Refactor website with new design & structure
This commit is contained in:
@@ -1,31 +1,36 @@
|
||||
###
|
||||
Crafting Guide - main.coffee
|
||||
#
|
||||
# Crafting Guide - client.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
############################################################################################################
|
||||
|
||||
require './polyfill'
|
||||
# Allow Node.js-style `global` in addition to `window`
|
||||
if typeof(global) is 'undefined'
|
||||
window.global = window
|
||||
|
||||
$ = require 'jquery'
|
||||
CraftingGuideClient = require('crafting-guide-common').CraftingGuideClient
|
||||
CraftingGuideRouter = require './crafting_guide_router'
|
||||
FeedbackController = require './controllers/feedback_controller'
|
||||
Logger = require('crafting-guide-common').Logger
|
||||
_ = require './underscore_mixins'
|
||||
backbone = require 'backbone'
|
||||
marked = require 'marked'
|
||||
views = require './views'
|
||||
global._ = require '../common/underscore'
|
||||
global.$ = require 'jquery'
|
||||
global.c = require '../common/constants'
|
||||
global.π = Math.PI
|
||||
global.ε = 0.0001
|
||||
global.w = require 'when'
|
||||
|
||||
global.Backbone = Backbone = require 'backbone'
|
||||
Backbone.$ = $
|
||||
|
||||
{Logger} = require 'crafting-guide-common'
|
||||
global.logger = new Logger
|
||||
|
||||
marked = require 'marked'
|
||||
marked.setOptions
|
||||
sanitize: true
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
if typeof(global) is 'undefined'
|
||||
window.global = window
|
||||
global = window.global
|
||||
|
||||
global.hostName = window.location.hostname
|
||||
global.logger = new Logger
|
||||
|
||||
switch global.hostName
|
||||
when 'prerender.crafting-guide.com'
|
||||
global.env = 'prerender'
|
||||
@@ -46,25 +51,23 @@ switch global.hostName
|
||||
else
|
||||
throw new Error "cannot determine the environment of: #{window.location.hostname}"
|
||||
|
||||
client = _.extend new CraftingGuideClient(baseUrl:apiBaseUrl), backbone.Events
|
||||
########################################################################################################################
|
||||
|
||||
{CraftingGuideClient} = require 'crafting-guide-common'
|
||||
client = _.extend new CraftingGuideClient(baseUrl:apiBaseUrl), Backbone.Events
|
||||
client.onStatusChanged = (c, oldStatus, newStatus)->
|
||||
logger.info "Crafting Guide server status changed from #{oldStatus} to #{newStatus}"
|
||||
client.trigger 'change:status', client, oldStatus, newStatus
|
||||
client.trigger 'change', client
|
||||
|
||||
backbone.$ = $
|
||||
|
||||
marked.setOptions
|
||||
sanitize: true
|
||||
|
||||
global.feedbackController = new FeedbackController el:'.view__feedback'
|
||||
feedbackController.render()
|
||||
|
||||
global.router = new CraftingGuideRouter client:client
|
||||
global.router.loadCurrentUser()
|
||||
global.router.loadDefaultModPack()
|
||||
|
||||
backbone.history.start pushState:true
|
||||
client.checkStatus()
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
SiteController = require './site/site_controller'
|
||||
global.site = site = new SiteController client: client
|
||||
site.render()
|
||||
site.loadDefaultModPack()
|
||||
site.loadCurrentUser()
|
||||
|
||||
Backbone.history.start pushState:true
|
||||
logger.info -> "CraftingGuide is ready"
|
||||
@@ -1,7 +1,7 @@
|
||||
//-
|
||||
//- Crafting Guide - _google_adsense.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- Copyright (c) 2015-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
//-
|
||||
//- Crafting Guide - _google_analytics.jade
|
||||
//-
|
||||
//- Copyright (c) 2014-2015 by Redwood Labs
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
###
|
||||
Crafting Guide - base_model.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
$ = require 'jquery'
|
||||
backbone = require 'backbone'
|
||||
w = require 'when'
|
||||
{Event} = require '../constants'
|
||||
{ModelState} = require '../constants'
|
||||
#
|
||||
# Crafting Guide - base_model.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class BaseModel extends backbone.Model
|
||||
module.exports = class BaseModel extends Backbone.Model
|
||||
|
||||
constructor: (attributes={}, options={})->
|
||||
options.logEvents ?= true
|
||||
@@ -26,15 +20,15 @@ module.exports = class BaseModel extends backbone.Model
|
||||
Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name)
|
||||
|
||||
@logEvents = options.logEvents or false
|
||||
@state = ModelState.unloaded
|
||||
@state = c.modelState.unloaded
|
||||
|
||||
@loading = null
|
||||
|
||||
Object.defineProperties this,
|
||||
isUnloaded: { get:-> @state is ModelState.unloaded }
|
||||
isLoading: { get:-> @state is ModelState.loading }
|
||||
isLoaded: { get:-> @state is ModelState.loaded }
|
||||
isError: { get:-> @state is ModelState.error }
|
||||
isUnloaded: { get:-> @state is c.modelState.unloaded }
|
||||
isLoading: { get:-> @state is c.modelState.loading }
|
||||
isLoaded: { get:-> @state is c.modelState.loaded }
|
||||
isError: { get:-> @state is c.modelState.error }
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
@@ -42,18 +36,18 @@ module.exports = class BaseModel extends backbone.Model
|
||||
try
|
||||
@set @parse text
|
||||
|
||||
@state = ModelState.loaded
|
||||
@trigger Event.change, this
|
||||
@trigger Event.sync, this
|
||||
@state = c.modelState.loaded
|
||||
@trigger c.event.change, this
|
||||
@trigger c.event.sync, this
|
||||
logger.info => "#{@constructor.name}.#{@cid} loaded successfully"
|
||||
catch e
|
||||
logger.error -> "A parsing error occured: #{e.stack}"
|
||||
@onLoadFailed e.message, 'parsing failed', xhr
|
||||
|
||||
onLoadFailed: (error, status, xhr)->
|
||||
@state = ModelState.error
|
||||
@state = c.modelState.error
|
||||
logger.error => "#{@constructor.name}.#{@cid} failed to load: status:#{status}, message:#{error}"
|
||||
@trigger Event.error, this, error
|
||||
@trigger c.event.error, this, error
|
||||
|
||||
# Backbone.Model Overrides #####################################################################
|
||||
|
||||
@@ -64,8 +58,8 @@ module.exports = class BaseModel extends backbone.Model
|
||||
url = @url()
|
||||
logger.info => "#{@constructor.name}.#{@cid} reading from url: #{url}"
|
||||
|
||||
@state = ModelState.loading
|
||||
@trigger Event.request, this
|
||||
@state = c.modelState.loading
|
||||
@trigger c.event.request, this
|
||||
@loading = w.promise (resolve, reject)=>
|
||||
$.ajax
|
||||
url: url
|
||||
+6
-8
@@ -1,11 +1,9 @@
|
||||
###
|
||||
Crafting Guide - crafting_node.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
#
|
||||
# Crafting Guide - crafting_node.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#
|
||||
# Crafting Guide - crafting_node.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
fixtures = require './fixtures.test'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
root = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'crafting_node.coffee', ->
|
||||
|
||||
describe 'acceptVisitor', ->
|
||||
|
||||
it 'can provide a prefix, depth-first traversal', ->
|
||||
root = fixtures.makeTree 'test__iron_ingot'
|
||||
|
||||
visitor =
|
||||
nodes: []
|
||||
onEnterOtherNode: (node)-> visitor.nodes.push node
|
||||
|
||||
root.acceptVisitor visitor
|
||||
|
||||
(v.constructor.name.replace('Node', '') for v in visitor.nodes).should.eql [
|
||||
"Inventory", "Item", "Recipe", "Item", "Item", "Recipe", "Item", "Item", "Recipe", "Item", "Item"
|
||||
]
|
||||
|
||||
it 'can provide a postfix, depth-first traversal', ->
|
||||
root = fixtures.makeTree 'test__iron_ingot'
|
||||
|
||||
visitor =
|
||||
nodes: []
|
||||
onLeaveOtherNode: (node)-> visitor.nodes.push node
|
||||
|
||||
root.acceptVisitor visitor
|
||||
|
||||
(n.constructor.name.replace('Node', '') for n in visitor.nodes).should.eql [
|
||||
"Item", "Item", "Item", "Recipe", "Item", "Recipe", "Item", "Item", "Recipe", "Item", "Inventory"
|
||||
]
|
||||
+7
-8
@@ -1,12 +1,11 @@
|
||||
###
|
||||
Crafting Guide - crafting_plan.coffee
|
||||
#
|
||||
# Crafting Guide - crafting_plan.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require '../../underscore_mixins'
|
||||
SimpleInventory = require '../simple_inventory'
|
||||
SimpleInventory = require './simple_inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#
|
||||
# Crafting Guide - crafting_plan.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CraftingPlan = require './crafting_plan'
|
||||
fixtures = require './fixtures.test'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'crafting_plan.coffee', ->
|
||||
|
||||
it 'requires wanted item if gatherable', ->
|
||||
plans = fixtures.makePlans [1, 'test__coal']
|
||||
plans.length.should.equal 1
|
||||
|
||||
plan = plans[0]
|
||||
plan.computeRequired()
|
||||
plan.need.unparse().should.equal 'coal'
|
||||
plan.made.unparse().should.equal 'coal'
|
||||
|
||||
it 'can compute a single item with one single step plan', ->
|
||||
plans = fixtures.makePlans [1, 'test__charcoal']
|
||||
plans.length.should.equal 1
|
||||
|
||||
plan = plans[0]
|
||||
plan.computeRequired()
|
||||
plan.need.unparse().should.equal 'coal:8.oak_wood'
|
||||
plan.made.unparse().should.equal '8.charcoal'
|
||||
(s.toString() for s in plan.steps).should.eql ['1x 8 test__oak_wood,test__coal>.0. ... .1.>>8 test__charcoal']
|
||||
|
||||
it 'can compute a large quantity of a single item with one single step plan', ->
|
||||
plans = fixtures.makePlans [15, 'test__charcoal']
|
||||
plans.length.should.equal 1
|
||||
|
||||
plan = plans[0]
|
||||
plan.computeRequired()
|
||||
plan.need.unparse().should.equal '2.coal:16.oak_wood'
|
||||
plan.made.unparse().should.equal '16.charcoal'
|
||||
(s.toString() for s in plan.steps).should.eql ['2x 8 test__oak_wood,test__coal>.0. ... .1.>>8 test__charcoal']
|
||||
|
||||
it 'can compute a single item with multiple plans', ->
|
||||
plans = fixtures.makePlans [1, 'test__iron_ingot']
|
||||
plans.length.should.equal 2
|
||||
|
||||
plan = plans[0]
|
||||
plan.computeRequired()
|
||||
plan.need.unparse().should.equal 'coal:8.iron_ore:8.oak_wood'
|
||||
plan.made.unparse().should.equal '7.charcoal:8.iron_ingot'
|
||||
(s.toString() for s in plan.steps).should.eql [
|
||||
'1x 8 test__oak_wood,test__coal>.0. ... .1.>>8 test__charcoal'
|
||||
'1x 8 test__iron_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__iron_ingot'
|
||||
]
|
||||
|
||||
plan = plans[1]
|
||||
plan.computeRequired()
|
||||
plan.need.unparse().should.equal 'coal:8.iron_ore'
|
||||
plan.made.unparse().should.equal '8.iron_ingot'
|
||||
(s.toString() for s in plan.steps).should.eql [
|
||||
'1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot'
|
||||
]
|
||||
|
||||
it 'can compute multiple items with multiple plans', ->
|
||||
plans = fixtures.makePlans [1, 'test__copper_block'], [1, 'test__iron_sword']
|
||||
plans.length.should.equal 4
|
||||
|
||||
for plan in plans
|
||||
plan.computeRequired()
|
||||
plan.need.unparse().should.match /16.copper_ore.*8.iron_ore/
|
||||
plan.made.unparse().should.match /copper_block.*:iron_sword/
|
||||
|
||||
plan = plans[0]
|
||||
plan.need.unparse().should.match /3.coal.*9.oak_wood/
|
||||
plan.made.unparse().should.match /7.charcoal/
|
||||
"#{plan.steps[3]}".should.equal \
|
||||
'1x 8 test__iron_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__iron_ingot'
|
||||
"#{plan.steps[4]}".should.equal \
|
||||
'2x 8 test__copper_ore,test__coal>.0. ... .1.>test__furnace>8 test__copper_ingot'
|
||||
plan.steps.length.should.equal 7
|
||||
|
||||
plan = plans[1]
|
||||
plan.need.unparse().should.match /3.coal.*:oak_wood/
|
||||
plan.made.unparse().should.not.match /charcoal/
|
||||
"#{plan.steps[2]}".should.equal '1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot'
|
||||
"#{plan.steps[3]}".should.equal \
|
||||
'2x 8 test__copper_ore,test__coal>.0. ... .1.>test__furnace>8 test__copper_ingot'
|
||||
plan.steps.length.should.equal 6
|
||||
|
||||
plan = plans[2]
|
||||
plan.need.unparse().should.match /^coal.*9.oak_wood/
|
||||
plan.made.unparse().should.match /5.charcoal/
|
||||
"#{plan.steps[3]}".should.equal \
|
||||
'1x 8 test__iron_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__iron_ingot'
|
||||
"#{plan.steps[4]}".should.equal \
|
||||
'2x 8 test__copper_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__copper_ingot'
|
||||
plan.steps.length.should.equal 7
|
||||
|
||||
plan = plans[3]
|
||||
plan.need.unparse().should.match /2.coal.*9.oak_wood/
|
||||
plan.made.unparse().should.match /6.charcoal/
|
||||
"#{plan.steps[3]}".should.equal '1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot'
|
||||
"#{plan.steps[4]}".should.equal \
|
||||
'2x 8 test__copper_ore,test__charcoal>.0. ... .1.>test__furnace>8 test__copper_ingot'
|
||||
plan.steps.length.should.equal 7
|
||||
+7
-7
@@ -1,11 +1,11 @@
|
||||
###
|
||||
Crafting Guide - crafting_step.coffee
|
||||
#
|
||||
# Crafting Guide - crafting_step.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
SimpleInventory = require '../simple_inventory'
|
||||
SimpleInventory = require './simple_inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+12
-15
@@ -1,18 +1,15 @@
|
||||
###
|
||||
Crafting Guide - craftsman.coffee
|
||||
#
|
||||
# Crafting Guide - craftsman.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
BaseModel = require '../base_model'
|
||||
{Event} = require '../../constants'
|
||||
GraphBuilder = require './graph_builder'
|
||||
Inventory = require '../inventory'
|
||||
Inventory = require '../game/inventory'
|
||||
PlanBuilder = require './plan_builder'
|
||||
PlanEvaluator = require './plan_evaluator'
|
||||
w = require 'when'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -46,13 +43,13 @@ module.exports = class Craftsman extends BaseModel
|
||||
reevaluatePlans = _.debounce (=> @reevaluatePlans()), 100
|
||||
|
||||
@_have = new Inventory modPack:@_modPack
|
||||
@_have.on Event.change, reevaluatePlans
|
||||
@_have.on c.event.change, reevaluatePlans
|
||||
|
||||
@_want = new Inventory modPack:@_modPack
|
||||
@_want.on Event.change, reset
|
||||
@_want.on c.event.change, reset
|
||||
|
||||
@on Event.change + ':paused', reset
|
||||
@on Event.change + ':stage', => logger.info "Craftsman has started #{@stage}..."
|
||||
@on c.event.change + ':paused', reset
|
||||
@on c.event.change + ':stage', => logger.info "Craftsman has started #{@stage}..."
|
||||
@on 'scheduleNextWork', => @_scheduleNextWork()
|
||||
@reset()
|
||||
|
||||
@@ -124,7 +121,7 @@ module.exports = class Craftsman extends BaseModel
|
||||
|
||||
logger.info => "Craftsman has finished with plans: #{(p.toString() for p in @_plans).join('\n\n')}"
|
||||
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change, this
|
||||
@trigger 'scheduleNextWork'
|
||||
return @complete
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
#
|
||||
# Crafting Guide - fixtures.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
GraphBuilder = require './graph_builder'
|
||||
Inventory = require '../game/inventory'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
Mod = require '../game/mod'
|
||||
ModPack = require '../game/mod_pack'
|
||||
ModVersion = require '../game/mod_version'
|
||||
PlanBuilder = require './plan_builder'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
MOD_VERSION_FILE =
|
||||
"""
|
||||
schema: 1
|
||||
|
||||
item: Bed
|
||||
recipe:
|
||||
input: Oak Planks, Wool
|
||||
pattern: ... 000 111
|
||||
|
||||
item: Charcoal
|
||||
recipe:
|
||||
input: 8 Oak Wood, Coal
|
||||
pattern: .0. ... .1.
|
||||
quantity: 8
|
||||
|
||||
item: Crafting Table
|
||||
recipe:
|
||||
input: Oak Planks
|
||||
pattern: .00 .00 ...
|
||||
|
||||
item: Coal
|
||||
gatherable: yes
|
||||
|
||||
item: Cobblestone
|
||||
gatherable: yes
|
||||
|
||||
item: Copper Block
|
||||
recipe:
|
||||
input: Copper Ingot
|
||||
pattern: 000 000 000
|
||||
tools: Crafting Table
|
||||
|
||||
item: Copper Ingot
|
||||
recipe:
|
||||
input: 8 Copper Ore, Coal
|
||||
pattern: .0. ... .1.
|
||||
quantity: 8
|
||||
tools: Furnace
|
||||
recipe:
|
||||
input: 8 Copper Ore, Charcoal
|
||||
pattern: .0. ... .1.
|
||||
quantity: 8
|
||||
tools: Furnace
|
||||
recipe:
|
||||
input: Copper Block
|
||||
pattern: ... .0. ...
|
||||
quantity: 9
|
||||
|
||||
item: Copper Ore
|
||||
gatherable: yes
|
||||
|
||||
item: Furnace
|
||||
recipe:
|
||||
input: Cobblestone
|
||||
pattern: 000 0.0 000
|
||||
tools: Crafting Table
|
||||
|
||||
item: Iron Ore
|
||||
gatherable: yes
|
||||
|
||||
item: Iron Ingot
|
||||
recipe:
|
||||
input: 8 Iron Ore, Charcoal
|
||||
pattern: .0. ... .1.
|
||||
quantity: 8
|
||||
tools: Furnace
|
||||
recipe:
|
||||
input: 8 Iron Ore, Coal
|
||||
pattern: .0. ... .1.
|
||||
quantity: 8
|
||||
tools: Furnace
|
||||
|
||||
item: Iron Sword
|
||||
recipe:
|
||||
input: Iron Ingot, Stick
|
||||
pattern: .0. .0. .1.
|
||||
tools: Crafting Table
|
||||
|
||||
item: Lever
|
||||
recipe:
|
||||
input: Stick, Cobblestone
|
||||
pattern: .0. .1. ...
|
||||
|
||||
item: Oak Planks
|
||||
recipe:
|
||||
input: Oak Wood
|
||||
pattern: ... .0. ...
|
||||
quantity: 4
|
||||
|
||||
item: Oak Wood
|
||||
gatherable: yes
|
||||
|
||||
item: Stick
|
||||
recipe:
|
||||
input: Oak Planks
|
||||
pattern: .0. .0. ...
|
||||
quantity: 4
|
||||
|
||||
item: String
|
||||
gatherable: yes
|
||||
|
||||
item: Wool
|
||||
gatherable: yes
|
||||
recipe:
|
||||
input: String
|
||||
pattern: 00. 00. ...
|
||||
"""
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = fixtures =
|
||||
|
||||
makeGraphBuilder: ->
|
||||
return new GraphBuilder modPack:fixtures.makeModPack(), want:new Inventory
|
||||
|
||||
makeModPack: ->
|
||||
modPack = new ModPack
|
||||
|
||||
mod = new Mod name:'Test', slug:'test'
|
||||
modPack.addMod mod
|
||||
|
||||
modVersion = new ModVersion modSlug:'test', version:'0.0'
|
||||
modVersion.parse MOD_VERSION_FILE
|
||||
mod.addModVersion modVersion
|
||||
|
||||
return modPack
|
||||
|
||||
makePlans: (stacks...)->
|
||||
modPack = fixtures.makeModPack()
|
||||
|
||||
have = new Inventory
|
||||
want = new Inventory
|
||||
for stack in stacks
|
||||
want.add ItemSlug.slugify(stack[1]), stack[0]
|
||||
|
||||
graphBuilder = new GraphBuilder modPack:modPack, want:want
|
||||
graphBuilder.expandGraph()
|
||||
|
||||
planBuilder = new PlanBuilder graphBuilder.rootNode, modPack, want:want, have:have
|
||||
return planBuilder.producePlans()
|
||||
|
||||
makeTree: (itemSlug, quantity=1)->
|
||||
want = new Inventory
|
||||
want.add ItemSlug.slugify itemSlug
|
||||
|
||||
builder = new GraphBuilder modPack:fixtures.makeModPack(), want:want
|
||||
builder.expandGraph()
|
||||
|
||||
return builder.rootNode
|
||||
+8
-9
@@ -1,13 +1,12 @@
|
||||
###
|
||||
Crafting Guide - graph_builder.coffee
|
||||
#
|
||||
# Crafting Guide - graph_builder.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
Inventory = require '../inventory'
|
||||
Inventory = require '../game/inventory'
|
||||
InventoryNode = require './inventory_node'
|
||||
{Limits} = require '../../constants'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -17,7 +16,7 @@ module.exports = class GraphBuilder
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.want? then throw new Error 'options.want is required'
|
||||
|
||||
@_maximumGraphSize = Limits.maximumGraphSize
|
||||
@_maximumGraphSize = c.limits.maximumGraphSize
|
||||
@_modPack = options.modPack
|
||||
@_stepCount = 0
|
||||
@_want = options.want
|
||||
@@ -0,0 +1,77 @@
|
||||
#
|
||||
# Crafting Guide - graph_builder.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
fixtures = require './fixtures.test'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
builder = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'GraphBuilder.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
builder = fixtures.makeGraphBuilder()
|
||||
|
||||
describe 'expand', ->
|
||||
|
||||
it 'can work a few steps at a time', ->
|
||||
builder.want.add ItemSlug.slugify 'test__iron_sword'
|
||||
builder.expandGraph 9
|
||||
|
||||
builder.rootNode.depth.should.equal 6
|
||||
builder.rootNode.size.should.equal 13
|
||||
builder.complete.should.be.false
|
||||
|
||||
builder.expandGraph 9
|
||||
|
||||
builder.rootNode.depth.should.equal 8
|
||||
builder.rootNode.size.should.equal 18
|
||||
builder.complete.should.be.true
|
||||
|
||||
it 'works properly with an empty inventory', ->
|
||||
builder.expandGraph 100
|
||||
|
||||
builder.rootNode.depth.should.equal 1
|
||||
builder.rootNode.size.should.equal 1
|
||||
builder.complete.should.be.true
|
||||
|
||||
describe 'can build a tree for', ->
|
||||
|
||||
runSingleItemTreeBuildingTest = (slug, depth, size)->
|
||||
builder.want.add ItemSlug.slugify slug
|
||||
builder.expandGraph 100
|
||||
|
||||
builder.rootNode.depth.should.equal depth
|
||||
builder.rootNode.size.should.equal size
|
||||
builder.complete.should.be.true
|
||||
|
||||
it 'a gatherable item', ->
|
||||
runSingleItemTreeBuildingTest 'test__oak_wood', 2, 2
|
||||
|
||||
it 'a single-step item', ->
|
||||
runSingleItemTreeBuildingTest 'test__crafting_table', 6, 6
|
||||
|
||||
it 'an item with multiple inputs', ->
|
||||
runSingleItemTreeBuildingTest 'test__lever', 8, 9
|
||||
|
||||
it 'an item with multiple recipes', ->
|
||||
runSingleItemTreeBuildingTest 'test__iron_ingot', 6, 11
|
||||
|
||||
it 'an item with multiple inputs and multiple recipes', ->
|
||||
runSingleItemTreeBuildingTest 'test__iron_sword', 8, 18
|
||||
|
||||
it 'an item with one recursive recipe', ->
|
||||
runSingleItemTreeBuildingTest 'test__copper_ingot', 6, 15
|
||||
|
||||
it 'an item which gatherable and craftable', ->
|
||||
runSingleItemTreeBuildingTest 'test__wool', 4, 4
|
||||
|
||||
it 'an item which requires a gatherable and craftable item', ->
|
||||
runSingleItemTreeBuildingTest 'test__bed', 6, 7
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
###
|
||||
Crafting Guide - crafting_plan_node.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - inventory_node.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CraftingNode = require './crafting_node'
|
||||
ItemNode = require './item_node'
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
###
|
||||
Crafting Guide - item_node.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - item_node.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CraftingNode = require './crafting_node'
|
||||
RecipeNode = require './recipe_node'
|
||||
+8
-9
@@ -1,15 +1,14 @@
|
||||
###
|
||||
Crafting Plan - plan_builder.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - plan_builder.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CraftingNode = require './crafting_node'
|
||||
CraftingPlan = require './crafting_plan'
|
||||
CraftingStep = require './crafting_step'
|
||||
Inventory = require '../inventory'
|
||||
{Limits} = require '../../constants'
|
||||
Inventory = require '../game/inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -24,7 +23,7 @@ module.exports = class PlanBuilder
|
||||
|
||||
@_choiceNodes = []
|
||||
@_complete = false
|
||||
@_maxPlanCount = Limits.maximumPlanCount
|
||||
@_maxPlanCount = c.limits.maximumPlanCount
|
||||
@_modPack = modPack
|
||||
@_plans = []
|
||||
@_rootNode = rootNode
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Crafting Guide - plan_builder.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
fixtures = require './fixtures.test'
|
||||
PlanBuilder = require './plan_builder'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'plan_builder.coffee', ->
|
||||
|
||||
printPlan = (plan)->
|
||||
return '' unless plan?
|
||||
return ((s.recipe.slug.replace(/^.*>.*>/, '') for s in plan.steps;;)).join ' > '
|
||||
|
||||
it 'generates an empty plan for a gatherable item', ->
|
||||
plans = fixtures.makePlans [1, 'test__oak_wood']
|
||||
|
||||
plans.length.should.equal 1
|
||||
plans[0].length.should.equal 0
|
||||
|
||||
it 'can find a multi-step plan', ->
|
||||
plans = fixtures.makePlans [1, 'test__lever']
|
||||
|
||||
printPlan(plans[0]).should.equal '4 test__oak_planks > 4 test__stick > test__lever'
|
||||
plans.length.should.equal 1
|
||||
|
||||
it 'can find multiple plans', ->
|
||||
plans = fixtures.makePlans [1, 'test__iron_ingot']
|
||||
|
||||
printPlan(plans[0]).should.equal '8 test__charcoal > 8 test__iron_ingot'
|
||||
printPlan(plans[1]).should.equal '8 test__iron_ingot'
|
||||
plans.length.should.equal 2
|
||||
|
||||
it 'ignores invalid plans', ->
|
||||
plans = fixtures.makePlans [1, 'test__copper_block']
|
||||
|
||||
printPlan(plans[0]).should.equal '8 test__copper_ingot > test__copper_block'
|
||||
printPlan(plans[1]).should.equal '8 test__charcoal > 8 test__copper_ingot > test__copper_block'
|
||||
plans.length.should.equal 2
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
###
|
||||
Crafting Guide - plan_evaluator.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - plan_evaluator.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#
|
||||
# Crafting Guide - plan_evaluator.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CraftingPlan = require './crafting_plan'
|
||||
fixtures = require './fixtures.test'
|
||||
Inventory = require '../game/inventory'
|
||||
PlanEvaluator = require './plan_evaluator'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
criteria = evaluator = planA = planB = planC = plans = wanted = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'plan_evaluator.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
criteria = PlanEvaluator::CRITERIA.FEWEST_STEPS
|
||||
|
||||
describe 'findBestPlan', ->
|
||||
|
||||
it 'can find the shortest of two options', ->
|
||||
evaluator = new PlanEvaluator fixtures.makePlans [1, 'test__copper_block']
|
||||
evaluator.scorePlans()
|
||||
|
||||
bestPlan = evaluator.findBestPlan criteria
|
||||
bestPlan.getScore(criteria).should.equal 1.0
|
||||
|
||||
it 'can find the shortest of many options', ->
|
||||
evaluator = new PlanEvaluator fixtures.makePlans [1, 'test__copper_block'], [1, 'test__iron_sword']
|
||||
evaluator.scorePlans()
|
||||
|
||||
bestPlan = evaluator.findBestPlan criteria
|
||||
bestPlan.getScore(criteria).should.equal 1.0
|
||||
|
||||
it 'can break ties using the non-primary criteria', ->
|
||||
evaluator = new PlanEvaluator fixtures.makePlans [2, 'test__iron_ingot'], [1, 'test__charcoal']
|
||||
evaluator.scorePlans()
|
||||
|
||||
bestPlan = evaluator.findBestPlan criteria
|
||||
bestPlan.getScore(criteria).should.equal 1.0
|
||||
|
||||
(p.getRawScore('fewest steps') for p in evaluator._plans).should.eql [2, 2]
|
||||
(p.getRawScore('least materials') for p in evaluator._plans).should.eql [17, 18]
|
||||
|
||||
describe '_normalizeScores', ->
|
||||
|
||||
beforeEach ->
|
||||
modPack = fixtures.makeModPack()
|
||||
wanted = new Inventory modPack:modPack
|
||||
have = new Inventory modPack:modPack
|
||||
planA = new CraftingPlan modPack, wanted, have, []
|
||||
planB = new CraftingPlan modPack, wanted, have, []
|
||||
planC = new CraftingPlan modPack, wanted, have, []
|
||||
plans = [planA, planB, planC]
|
||||
evaluator = new PlanEvaluator plans
|
||||
criteria = PlanEvaluator::CRITERIA.FEWEST_STEPS
|
||||
|
||||
it 'does not set scores when none have been evaluated', ->
|
||||
evaluator._normalizeScores()
|
||||
(plan.hasRawScore(criteria) for plan in plans).should.eql [false, false, false]
|
||||
|
||||
it 'assigns a 1.0 when all plans equal', ->
|
||||
for plan in plans
|
||||
plan.setRawScore criteria, 5
|
||||
|
||||
evaluator._normalizeScores()
|
||||
(plan.getScore(criteria) for plan in plans).should.eql [1.0, 1.0, 1.0]
|
||||
|
||||
it 'computes correct scores from raw scores', ->
|
||||
planA.setRawScore criteria, 7
|
||||
planB.setRawScore criteria, 8
|
||||
planC.setRawScore criteria, 9
|
||||
|
||||
evaluator._normalizeScores()
|
||||
(plan.getScore(criteria) for plan in plans).should.eql [1.0, 0.5, 0.0]
|
||||
+8
-8
@@ -1,14 +1,14 @@
|
||||
###
|
||||
Crafting Guide - recipe_node.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - recipe_node.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CraftingNode = require './crafting_node'
|
||||
Item = require '../item'
|
||||
Item = require '../game/item'
|
||||
# ItemNode = require './item_node' # don't include here, causes a cycle
|
||||
ItemSlug = require '../item_slug'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+8
-9
@@ -1,13 +1,12 @@
|
||||
###
|
||||
Crafting Guide - inventory.coffee
|
||||
#
|
||||
# Crafting Guide - simple_inventory.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
ItemSlug = require './item_slug'
|
||||
SimpleStack = require './simple_stack'
|
||||
_ = require 'underscore'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
SimpleStack = require './simple_stack'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Crafting Guide - simple_stack.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
ItemSlug = require '../game/item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class Stack
|
||||
|
||||
constructor: (attributes={}, options={})->
|
||||
if not attributes.itemSlug? then throw new Error 'attributes.itemSlug is required'
|
||||
attributes.quantity ?= 1
|
||||
|
||||
@itemSlug = attributes.itemSlug
|
||||
@quantity = attributes.quantity
|
||||
|
||||
# Class Methods ################################################################################
|
||||
|
||||
@compare: (a, b)->
|
||||
if a? and not b? then return -1
|
||||
if not a? and b? then return +1
|
||||
if a.quantity isnt b.quantity
|
||||
return if a.quantity > b.quantity then -1 else +1
|
||||
return ItemSlug.compare a.itemSlug, b.itemSlug
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
return "#{@quantity} #{@itemSlug}"
|
||||
@@ -0,0 +1,36 @@
|
||||
###
|
||||
# Crafting Guide - event_recorder.coffee
|
||||
#
|
||||
# Copyright (c) 2014-2015 by Redwood Labs
|
||||
# All rights reserved.
|
||||
###
|
||||
|
||||
util = require 'util'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class EventRecorder
|
||||
|
||||
constructor: (model)->
|
||||
if not model? then throw new Error 'model is required'
|
||||
|
||||
@model = model
|
||||
@events = []
|
||||
|
||||
@model.on 'all', (event, model, args...)=>
|
||||
logger.verbose -> "#{model?.constructor?.name}(#{model?.cid}) emitted #{event}
|
||||
with args: #{util.inspect(args)}"
|
||||
@events.push id:model?.cid, event:event, args:args
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
reset: ->
|
||||
@events = []
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getNames: ->
|
||||
return (e.event for e in @events)
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
names: {get:@prototype.getNames}
|
||||
@@ -1,16 +1,13 @@
|
||||
###
|
||||
Crafting Guide - inventory.coffee
|
||||
#
|
||||
# Crafting Guide - inventory.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
ItemSlug = require './item_slug'
|
||||
Stack = require './stack'
|
||||
_ = require 'underscore'
|
||||
{Event} = require '../constants'
|
||||
{RequiredMods} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
ItemSlug = require './item_slug'
|
||||
Stack = require './stack'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -36,21 +33,21 @@ module.exports = class Inventory extends BaseModel
|
||||
return this unless quantity > 0
|
||||
|
||||
@_add itemSlug, quantity
|
||||
@trigger Event.add, this, itemSlug, quantity
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.add, this, itemSlug, quantity
|
||||
@trigger c.event.change, this
|
||||
return this
|
||||
|
||||
addInventory: (inventory)->
|
||||
inventory.each (stack)=> @_add stack.itemSlug, stack.quantity
|
||||
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change, this
|
||||
return this
|
||||
|
||||
clear: (options={})->
|
||||
@_stacks = {}
|
||||
@_itemSlugs = []
|
||||
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change, this
|
||||
|
||||
clone: ->
|
||||
inventory = new Inventory
|
||||
@@ -103,10 +100,10 @@ module.exports = class Inventory extends BaseModel
|
||||
@_stacks = {}
|
||||
for stack in newStacks
|
||||
@_stacks[stack.itemSlug] = stack
|
||||
@listenTo stack, Event.change, => @trigger Event.change, this
|
||||
@listenTo stack, c.event.change, => @trigger c.event.change, this
|
||||
|
||||
@_sort()
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change, this
|
||||
|
||||
pop: ->
|
||||
itemSlug = @_itemSlugs.pop()
|
||||
@@ -115,8 +112,8 @@ module.exports = class Inventory extends BaseModel
|
||||
stack = @_stacks[itemSlug]
|
||||
delete @_stacks[itemSlug]
|
||||
|
||||
@trigger Event.remove, this, stack.itemSlug, stack.quantity
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.remove, this, stack.itemSlug, stack.quantity
|
||||
@trigger c.event.change, this
|
||||
return stack
|
||||
|
||||
quantityOf: (itemSlug)->
|
||||
@@ -140,8 +137,8 @@ module.exports = class Inventory extends BaseModel
|
||||
delete @_stacks[itemSlug]
|
||||
@_itemSlugs = (s for s in @_itemSlugs when not ItemSlug.equal(s, itemSlug))
|
||||
|
||||
@trigger Event.remove, this, itemSlug, quantity
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.remove, this, itemSlug, quantity
|
||||
@trigger c.event.change, this
|
||||
return this
|
||||
|
||||
# Parsing Methods ##############################################################################
|
||||
@@ -184,18 +181,16 @@ module.exports = class Inventory extends BaseModel
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getIsEmpty: ->
|
||||
return @_itemSlugs.length is 0
|
||||
|
||||
getTotalQuantity: ->
|
||||
total = 0
|
||||
@each (stack)->
|
||||
total += stack.quantity
|
||||
return total
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
isEmpty: { get:@prototype.getIsEmpty }
|
||||
totalQuantity: { get:@prototype.getTotalQuantity }
|
||||
isEmpty:
|
||||
get: -> @_itemSlugs.length is 0
|
||||
|
||||
totalQuantity:
|
||||
get: ->
|
||||
total = 0
|
||||
@each (stack)->
|
||||
total += stack.quantity
|
||||
return total
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
@@ -221,7 +216,7 @@ module.exports = class Inventory extends BaseModel
|
||||
stack = @_stacks[itemSlug]
|
||||
if not stack?
|
||||
stack = new Stack itemSlug:itemSlug, quantity:quantity
|
||||
@listenTo stack, Event.change, => @trigger Event.change, this
|
||||
@listenTo stack, c.event.change, => @trigger c.event.change, this
|
||||
@_stacks[itemSlug] = stack
|
||||
@_itemSlugs.push itemSlug
|
||||
@_sort()
|
||||
@@ -0,0 +1,213 @@
|
||||
#
|
||||
# Crafting Guide - inventory.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
EventRecorder = require '../event_recorder'
|
||||
Inventory = require './inventory'
|
||||
Item = require './item'
|
||||
ItemSlug = require './item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
inventory = modPack = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'inventory.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
inventory = new Inventory {}, silent:false
|
||||
inventory.add ItemSlug.slugify('wool'), 4
|
||||
inventory.add ItemSlug.slugify('string'), 20
|
||||
inventory.add ItemSlug.slugify('boat')
|
||||
|
||||
describe 'add', ->
|
||||
|
||||
it 'can add to an empty inventory', ->
|
||||
inventory.add ItemSlug.slugify('iron_ingot'), 4
|
||||
stack = inventory._stacks['iron_ingot']
|
||||
stack.constructor.name.should.equal 'Stack'
|
||||
stack.itemSlug.qualified.should.equal 'iron_ingot'
|
||||
stack.quantity.should.equal 4
|
||||
|
||||
it 'can augment quantity of existing items', ->
|
||||
inventory.add ItemSlug.slugify('wool'), 2
|
||||
inventory.unparse().should.equal 'boat:20.string:6.wool'
|
||||
|
||||
it 'can add zero quantity', ->
|
||||
inventory.add ItemSlug.slugify('wool'), 0
|
||||
inventory.unparse().should.equal 'boat:20.string:4.wool'
|
||||
|
||||
it 'emits the proper events', ->
|
||||
events = new EventRecorder inventory
|
||||
inventory.add ItemSlug.slugify('iron_ingot'), 10
|
||||
events.names.should.eql [c.event.add, c.event.change]
|
||||
|
||||
describe 'addInventory', ->
|
||||
|
||||
it 'can add to an empty inventory', ->
|
||||
newInventory = new Inventory
|
||||
newInventory.addInventory inventory
|
||||
newInventory.unparse().should.equal 'boat:20.string:4.wool'
|
||||
|
||||
it 'can add a mix of new and existing items', ->
|
||||
newInventory = new Inventory
|
||||
newInventory.add ItemSlug.slugify('string'), 2
|
||||
newInventory.addInventory inventory
|
||||
newInventory.unparse().should.equal 'boat:22.string:4.wool'
|
||||
|
||||
describe 'clone', ->
|
||||
|
||||
it 'creates an empty inventory from an empty inventory', ->
|
||||
a = new Inventory
|
||||
b = a.clone()
|
||||
b._itemSlugs.should.eql []
|
||||
|
||||
it 'faithfully copies an existing inventory', ->
|
||||
copy = inventory.clone()
|
||||
copy.unparse().should.equal 'boat:20.string:4.wool'
|
||||
|
||||
describe 'each', ->
|
||||
|
||||
it 'works with an empty inventory', ->
|
||||
inventory = new Inventory
|
||||
result = []
|
||||
inventory.each (item)-> result.push item.name
|
||||
result.should.eql []
|
||||
|
||||
it 'works when items have only been added', ->
|
||||
result = []
|
||||
inventory.each (stack)-> result.push stack.itemSlug.qualified
|
||||
result.should.eql ['boat', 'string', 'wool']
|
||||
|
||||
it 'works when items have been augmented', ->
|
||||
inventory.add ItemSlug.slugify 'iron_ingot'
|
||||
inventory.add ItemSlug.slugify 'boat'
|
||||
inventory.add ItemSlug.slugify('wool'), 2
|
||||
|
||||
result = []
|
||||
inventory.each (stack)-> result.push stack.itemSlug.qualified
|
||||
result.should.eql ['boat', 'iron_ingot', 'string', 'wool']
|
||||
|
||||
describe 'hasAtLeast', ->
|
||||
|
||||
it 'works when the item is completely absent', ->
|
||||
answer = inventory.hasAtLeast 'chicken', 1
|
||||
answer.should.be.false
|
||||
|
||||
it 'always returns true for zero quantity', ->
|
||||
inventory.hasAtLeast('chicken', 0).should.be.true
|
||||
inventory.hasAtLeast('wool', 0).should.be.true
|
||||
|
||||
it 'works for a quantity above 1', ->
|
||||
inventory.hasAtLeast('wool', 3).should.be.true
|
||||
inventory.hasAtLeast('wool', 4).should.be.true
|
||||
inventory.hasAtLeast('wool', 5).should.be.false
|
||||
|
||||
describe 'localize', ->
|
||||
|
||||
before ->
|
||||
modPack =
|
||||
modSlug:
|
||||
wool: 'minecraft'
|
||||
string: 'minecraft'
|
||||
boat: 'minecraft'
|
||||
stone_gear: 'buildcraft'
|
||||
findItem: (slug)->
|
||||
return slug:new ItemSlug @modSlug[slug.item], slug.item
|
||||
|
||||
it 'replaces item slugs with qualified slugs', ->
|
||||
inventory.add ItemSlug.slugify 'stone_gear'
|
||||
inventory.modPack = modPack
|
||||
inventory.localize()
|
||||
|
||||
slugs = []
|
||||
inventory.each (stack)-> slugs.push stack.itemSlug.qualified
|
||||
slugs.should.eql [
|
||||
'minecraft__boat'
|
||||
'buildcraft__stone_gear'
|
||||
'minecraft__string'
|
||||
'minecraft__wool'
|
||||
]
|
||||
|
||||
it 'ignores qualified slugs', ->
|
||||
inventory.add ItemSlug.slugify 'buildcraft__stone_gear'
|
||||
inventory.modPack = modPack
|
||||
inventory.localize()
|
||||
|
||||
slugs = []
|
||||
inventory.each (stack)-> slugs.push stack.itemSlug.qualified
|
||||
slugs.should.eql [
|
||||
'minecraft__boat'
|
||||
'buildcraft__stone_gear'
|
||||
'minecraft__string'
|
||||
'minecraft__wool'
|
||||
]
|
||||
|
||||
describe 'parse', ->
|
||||
|
||||
beforeEach ->
|
||||
inventory = new Inventory {}, silent:false
|
||||
|
||||
it 'ignores an empty string', ->
|
||||
result = inventory.parse ''
|
||||
result.unparse().should.eql ''
|
||||
|
||||
it 'can parse a single item without quantity', ->
|
||||
result = inventory.parse 'wool'
|
||||
result.unparse().should.equal 'wool'
|
||||
|
||||
it 'can parse a single item with quantity', ->
|
||||
result = inventory.parse '4.wool'
|
||||
result.unparse().should.equal '4.wool'
|
||||
|
||||
it 'can parse multiple mixed-type items', ->
|
||||
result = inventory.parse '4.wool:10.string:boat'
|
||||
result.unparse().should.equal 'boat:10.string:4.wool'
|
||||
|
||||
describe 'pop', ->
|
||||
|
||||
it 'returns null for an empty inventory', ->
|
||||
inventory = new Inventory
|
||||
result = inventory.pop()
|
||||
expect(result).to.be.null
|
||||
|
||||
it 'completely removes the last item', ->
|
||||
stack = inventory.pop()
|
||||
stack.itemSlug.qualified.should.equal 'wool'
|
||||
stack.quantity.should.equal 4
|
||||
inventory.unparse().should.equal 'boat:20.string'
|
||||
|
||||
it 'triggers the right events', ->
|
||||
events = new EventRecorder inventory
|
||||
result = inventory.pop()
|
||||
events.names.should.eql [c.event.remove, c.event.change]
|
||||
|
||||
describe 'remove', ->
|
||||
|
||||
it 'does nothing when the item is absent', ->
|
||||
before = inventory.unparse()
|
||||
inventory.remove 'foo'
|
||||
after = inventory.unparse()
|
||||
|
||||
before.should.equal after
|
||||
|
||||
it 'throws when the item has insufficient quantity', ->
|
||||
expect(-> inventory.remove('wool', 10)).to.throw Error,
|
||||
'cannot remove 10: only 4 wool in this inventory'
|
||||
|
||||
it 'removes all items by default', ->
|
||||
inventory.remove 'wool'
|
||||
expect(inventory._stacks.wool).to.be.empty
|
||||
|
||||
it 'removes a quantity above 1', ->
|
||||
inventory.remove 'wool', 3
|
||||
inventory._stacks.wool.quantity.should.equal 1
|
||||
|
||||
it 'emits the proper events', ->
|
||||
events = new EventRecorder inventory
|
||||
inventory.remove 'wool'
|
||||
events.names.should.eql [c.event.change, c.event.remove, c.event.change]
|
||||
@@ -1,16 +1,14 @@
|
||||
###
|
||||
Crafting Guide - item.coffee
|
||||
#
|
||||
# Crafting Guide - item.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
BaseModel = require '../base_model'
|
||||
ItemSlug = require './item_slug'
|
||||
Recipe = require './recipe'
|
||||
{Event} = require '../constants'
|
||||
{StringBuilder} = require 'crafting-guide-common'
|
||||
{Url} = require '../constants'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -33,7 +31,7 @@ module.exports = class Item extends BaseModel
|
||||
options.logEvents ?= false
|
||||
super attributes, options
|
||||
|
||||
@on Event.change + ':modVersion', =>
|
||||
@on c.event.change + ':modVersion', =>
|
||||
@_isCraftable = null
|
||||
@slug.mod = @modVersion?.modSlug
|
||||
|
||||
@@ -47,7 +45,7 @@ module.exports = class Item extends BaseModel
|
||||
return 0
|
||||
|
||||
unparse: ->
|
||||
ItemParser = require './item_parser' # to avoid require cycles
|
||||
ItemParser = require '../parsing/item_parser' # to avoid require cycles
|
||||
@_parser ?= new ItemParser model:this
|
||||
return @_parser.unparse()
|
||||
|
||||
@@ -67,14 +65,14 @@ module.exports = class Item extends BaseModel
|
||||
# Backbone.Model Overrides #####################################################################
|
||||
|
||||
parse: (text)->
|
||||
ItemParser = require './item_parser' # to avoid require cycles
|
||||
ItemParser = require '../parsing/item_parser' # to avoid require cycles
|
||||
@_parser ?= new ItemParser model:this
|
||||
@_parser.parse text
|
||||
|
||||
return null # prevent calling `set`
|
||||
|
||||
url: ->
|
||||
return Url.itemData modSlug:@slug.mod, itemSlug:@slug.item
|
||||
return c.url.itemData modSlug:@slug.mod, itemSlug:@slug.item
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
###
|
||||
Crafting Guide - item_slug.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
{RequiredMods} = require '../constants'
|
||||
#
|
||||
# Crafting Guide - item_slug.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -68,33 +65,27 @@ module.exports = class ItemSlug
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getIsQualified: ->
|
||||
return @_mod?
|
||||
|
||||
getItem: ->
|
||||
return @_item
|
||||
|
||||
setItem: (item)->
|
||||
if not item? then throw new Error 'item is required'
|
||||
@_item = item
|
||||
|
||||
@mod = @mod # reset @_qualified
|
||||
|
||||
getMod: ->
|
||||
return @_mod
|
||||
|
||||
setMod: (mod)->
|
||||
@_mod = mod
|
||||
@_qualified = if @_mod? then _.composeSlugs(@_mod, @_item) else @_item
|
||||
|
||||
getQualified: ->
|
||||
return @_qualified
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
isQualified: { get:@prototype.getIsQualified }
|
||||
mod: { get:@prototype.getMod, set:@prototype.setMod }
|
||||
item: { get:@prototype.getItem, set:@prototype.setItem }
|
||||
qualified: { get:@prototype.getQualified }
|
||||
isQualified:
|
||||
get: -> @_mod?
|
||||
|
||||
item:
|
||||
get: -> @_item
|
||||
|
||||
set: (newItem)->
|
||||
if not newItem? then throw new Error 'item is required'
|
||||
@_item = newItem
|
||||
@mod = @mod # reset @_qualified
|
||||
|
||||
mod:
|
||||
get: -> @_mod
|
||||
|
||||
set: (newMod)->
|
||||
@_mod = newMod
|
||||
@_qualified = if @_mod? then _.composeSlugs(@_mod, @_item) else @_item
|
||||
|
||||
qualified:
|
||||
get: -> @_qualified
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
#
|
||||
# Crafting Guide - item_slug.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
ItemSlug = require './item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'item_slug.coffee', ->
|
||||
|
||||
describe 'constructor', ->
|
||||
|
||||
it 'can handle one argument', ->
|
||||
slug = new ItemSlug 'alpha'
|
||||
slug.item.should.equal 'alpha'
|
||||
expect(slug.mod).to.be.null
|
||||
slug.qualified.should.equal 'alpha'
|
||||
|
||||
it 'can handle two arguments', ->
|
||||
slug = new ItemSlug 'alpha', 'bravo'
|
||||
slug.mod.should.equal 'alpha'
|
||||
slug.item.should.equal 'bravo'
|
||||
slug.qualified.should.equal 'alpha__bravo'
|
||||
|
||||
it 'throws with zero arguments', ->
|
||||
f = -> new ItemSlug
|
||||
expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"'
|
||||
|
||||
it 'throws with more arguments', ->
|
||||
f = -> new ItemSlug 'alpha', 'bravo', 'charlie'
|
||||
expect(f).to.throw Error, 'expected arguments to be "modSlug, itemSlug" or just "itemSlug"'
|
||||
|
||||
describe 'ItemSlug.compare', ->
|
||||
|
||||
it 'sorts by item when both are qualified in the same mod', ->
|
||||
a = new ItemSlug 'alpha', 'bravo'
|
||||
b = new ItemSlug 'charlie', 'bravo'
|
||||
|
||||
ItemSlug.compare(a, b).should.equal -1
|
||||
ItemSlug.compare(b, a).should.equal +1
|
||||
|
||||
it 'sorts by item when not qualified', ->
|
||||
a = new ItemSlug 'alpha'
|
||||
b = new ItemSlug 'bravo'
|
||||
|
||||
ItemSlug.compare(a, b).should.equal -1
|
||||
ItemSlug.compare(b, a).should.equal +1
|
||||
|
||||
describe 'ItemSlug.equal', ->
|
||||
|
||||
it 'requires both to have the same mod', ->
|
||||
a = new ItemSlug 'alpha', 'bravo'
|
||||
b = new ItemSlug 'alpha', 'charlie'
|
||||
c = new ItemSlug 'alpha', 'bravo'
|
||||
|
||||
ItemSlug.equal(a, b).should.be.false
|
||||
ItemSlug.equal(a, c).should.be.true
|
||||
|
||||
it 'requires both to have the same item', ->
|
||||
a = new ItemSlug 'alpha', 'bravo'
|
||||
b = new ItemSlug 'alpha', 'charlie'
|
||||
c = new ItemSlug 'alpha', 'bravo'
|
||||
|
||||
ItemSlug.equal(a, b).should.be.false
|
||||
ItemSlug.equal(a, c).should.be.true
|
||||
|
||||
describe 'ItemSlug.slugify', ->
|
||||
|
||||
it 'can slugify a pure name', ->
|
||||
slug = ItemSlug.slugify 'Alpha Bravo (Charlie)'
|
||||
slug.item.should.equal 'alpha_bravo_charlie'
|
||||
expect(slug.mod).to.be.null
|
||||
|
||||
it 'can slugify a simple item slug', ->
|
||||
slug = ItemSlug.slugify 'alpha_bravo_charlie'
|
||||
slug.item.should.equal 'alpha_bravo_charlie'
|
||||
expect(slug.mod).to.be.null
|
||||
|
||||
it 'can slugify a fully-qualified slug', ->
|
||||
slug = ItemSlug.slugify 'alpha_bravo__charlie_delta'
|
||||
slug.mod.should.equal 'alpha_bravo'
|
||||
slug.item.should.equal 'charlie_delta'
|
||||
|
||||
describe 'matches', ->
|
||||
|
||||
it 'ignores mod when either is unqualified', ->
|
||||
a = new ItemSlug 'alpha', 'bravo'
|
||||
b = new ItemSlug 'bravo'
|
||||
c = new ItemSlug 'charlie'
|
||||
|
||||
a.matches(b).should.be.true
|
||||
a.matches(c).should.be.false
|
||||
|
||||
it 'observes differences in mod when all are qualified', ->
|
||||
a = new ItemSlug 'alpha', 'bravo'
|
||||
b = new ItemSlug 'charlie', 'bravo'
|
||||
c = new ItemSlug 'delta', 'echo'
|
||||
d = new ItemSlug 'alpha', 'bravo'
|
||||
|
||||
a.matches(b).should.be.false
|
||||
a.matches(c).should.be.false
|
||||
a.matches(d).should.be.true
|
||||
|
||||
describe 'isQualified', ->
|
||||
|
||||
it 'returns true only when the mod slug is set', ->
|
||||
a = new ItemSlug 'alpha', 'bravo'
|
||||
b = new ItemSlug 'charlie'
|
||||
a.isQualified.should.be.true
|
||||
b.isQualified.should.be.false
|
||||
|
||||
describe '[]', ->
|
||||
|
||||
it 'allows slugs as a key', ->
|
||||
slug = new ItemSlug 'alpha', 'bravo'
|
||||
data = {}
|
||||
data[slug] = 'foo'
|
||||
data['alpha__bravo'].should.equal 'foo'
|
||||
data[slug].should.equal 'foo'
|
||||
@@ -1,15 +1,11 @@
|
||||
###
|
||||
Crafting Guide - mod.coffee
|
||||
#
|
||||
# Crafting Guide - mod.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
BaseModel = require './base_model'
|
||||
{Event} = require '../constants'
|
||||
{RequiredMods} = require '../constants'
|
||||
{Url} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -33,7 +29,7 @@ module.exports = class Mod extends BaseModel
|
||||
@_modVersions = []
|
||||
@_tutorials = []
|
||||
|
||||
@once Event.sync, => @_verifyActiveModVersion()
|
||||
@once c.event.sync, => @_verifyActiveModVersion()
|
||||
|
||||
# Class Methods ##################################################################################
|
||||
|
||||
@@ -44,8 +40,8 @@ module.exports = class Mod extends BaseModel
|
||||
# Public Methods #################################################################################
|
||||
|
||||
compareTo: (that)->
|
||||
thisRequired = this.slug in RequiredMods
|
||||
thatRequired = that.slug in RequiredMods
|
||||
thisRequired = this.slug in c.requiredMods
|
||||
thatRequired = that.slug in c.requiredMods
|
||||
|
||||
if thisRequired isnt thatRequired
|
||||
return -1 if thisRequired
|
||||
@@ -76,8 +72,8 @@ module.exports = class Mod extends BaseModel
|
||||
@_activeVersion = version
|
||||
@_activateModVersion null
|
||||
|
||||
@trigger Event.change + ':activeVersion', this, @_activeVersion
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change + ':activeVersion', this, @_activeVersion
|
||||
@trigger c.event.change, this
|
||||
else
|
||||
for modVersion in @_modVersions
|
||||
if version is modVersion.version
|
||||
@@ -85,12 +81,15 @@ module.exports = class Mod extends BaseModel
|
||||
break
|
||||
|
||||
@_activeVersion = version
|
||||
@trigger Event.change + ':activeVersion', this, @_activeVersion
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change + ':activeVersion', this, @_activeVersion
|
||||
@trigger c.event.change, this
|
||||
|
||||
enabled:
|
||||
get: -> @_activeModVersion?
|
||||
|
||||
modVersions:
|
||||
get: -> @_modVersions[..]
|
||||
|
||||
tutorials:
|
||||
get: -> @getAllTutorials()
|
||||
|
||||
@@ -129,12 +128,12 @@ module.exports = class Mod extends BaseModel
|
||||
return if @_modVersions.indexOf(modVersion) isnt -1
|
||||
|
||||
@_modVersions.push modVersion
|
||||
@listenTo modVersion, Event.change, => @trigger Event.change, this
|
||||
@listenTo modVersion, c.event.change, => @trigger c.event.change, this
|
||||
modVersion.mod = this
|
||||
|
||||
@trigger Event.add + ':modVersion', modVersion, this
|
||||
@trigger Event.change + ':version', modVersion, this
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.add + ':modVersion', modVersion, this
|
||||
@trigger c.event.change + ':version', modVersion, this
|
||||
@trigger c.event.change, this
|
||||
|
||||
if not @activeVersion? then @activeVersion = modVersion.version
|
||||
if modVersion.version is @_activeVersion then @_activateModVersion modVersion
|
||||
@@ -198,21 +197,21 @@ module.exports = class Mod extends BaseModel
|
||||
# Backbone.Model Overrides #####################################################################
|
||||
|
||||
parse: (text)->
|
||||
ModParser = require './mod_parser' # to avoid require cycles
|
||||
ModParser = require '../parsing/mod_parser' # to avoid require cycles
|
||||
@_parser ?= new ModParser model:this
|
||||
@_parser.parse text
|
||||
|
||||
return null # prevent calling `set`
|
||||
|
||||
url: ->
|
||||
return Url.modData modSlug:@slug
|
||||
return c.url.modData modSlug:@slug
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_activateModVersion: (modVersion)->
|
||||
if @_activeModVersion? then @stopListening @_activeModVersion
|
||||
@_activeModVersion = modVersion
|
||||
@trigger Event.change + ':activeModVersion', this, @_activeModVersion
|
||||
@trigger c.event.change + ':activeModVersion', this, @_activeModVersion
|
||||
|
||||
logger.verbose => "#{@slug} switched to version #{@_activeVersion}"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#
|
||||
# Crafting Guide - mod.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Mod = require './mod'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
mod = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'mod.coffee', ->
|
||||
|
||||
beforeEach -> mod = new Mod name:'Test', slug:'test'
|
||||
|
||||
describe 'compareTo', ->
|
||||
|
||||
it 'lists required mods first', ->
|
||||
minecraft = new Mod name:'Minecraft', slug:'minecraft'
|
||||
mod.compareTo(minecraft).should.equal +1
|
||||
minecraft.compareTo(mod).should.equal -1
|
||||
|
||||
it 'sorts by name second', ->
|
||||
buildcraft = new Mod name:'Buildcraft', slug:'buildcraft'
|
||||
mod.compareTo(buildcraft).should.equal +1
|
||||
buildcraft.compareTo(mod).should.equal -1
|
||||
@@ -1,17 +1,14 @@
|
||||
###
|
||||
Crafting Guide - mod_pack.coffee
|
||||
#
|
||||
# Crafting Guide - mod_pack.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
{DefaultModVersions} = require '../constants'
|
||||
{Event} = require '../constants'
|
||||
Inventory = require './inventory'
|
||||
ModVersionParser = require './mod_version_parser'
|
||||
Recipe = require './recipe'
|
||||
{Url} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
Inventory = require './inventory'
|
||||
ModVersionParser = require '../parsing/mod_version_parser'
|
||||
Recipe = require './recipe'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -23,7 +20,7 @@ module.exports = class ModPack extends BaseModel
|
||||
@_mods = []
|
||||
@_cache = {}
|
||||
|
||||
@on Event.change, => @_cache = {}
|
||||
@on c.event.change, => @_cache = {}
|
||||
|
||||
# Item Methods #################################################################################
|
||||
|
||||
@@ -84,10 +81,10 @@ module.exports = class ModPack extends BaseModel
|
||||
else
|
||||
craftingUrlInventory.add itemSlug
|
||||
|
||||
result.craftingUrl = Url.crafting inventoryText:craftingUrlInventory.unparse()
|
||||
result.iconUrl = Url.itemIcon result
|
||||
result.itemUrl = Url.item result
|
||||
result.modName = @getMod(result.modSlug).name
|
||||
result.craftingUrl = c.url.crafting inventoryText:craftingUrlInventory.unparse()
|
||||
result.iconUrl = c.url.itemIcon result
|
||||
result.itemUrl = c.url.item result
|
||||
result.modName = @getMod(result.modSlug).name
|
||||
return result
|
||||
|
||||
qualifySlug: (itemSlug)->
|
||||
@@ -105,12 +102,12 @@ module.exports = class ModPack extends BaseModel
|
||||
|
||||
mod.modPack = this
|
||||
@_mods.push mod
|
||||
@listenTo mod, Event.change, (modVersion)=> @_onModVersionLoaded modVersion
|
||||
@trigger Event.add + ':mod', mod, this
|
||||
@listenTo mod, c.event.change, (modVersion)=> @_onModVersionLoaded modVersion
|
||||
@trigger c.event.add + ':mod', mod, this
|
||||
|
||||
@_mods.sort (a, b)-> a.compareTo b
|
||||
@trigger Event.sort + ':mod', this
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.sort + ':mod', this
|
||||
@trigger c.event.change, this
|
||||
|
||||
return this
|
||||
|
||||
@@ -175,5 +172,5 @@ module.exports = class ModPack extends BaseModel
|
||||
|
||||
return if working
|
||||
|
||||
@trigger Event.change, this
|
||||
@trigger Event.sync, this
|
||||
@trigger c.event.change, this
|
||||
@trigger c.event.sync, this
|
||||
@@ -0,0 +1,103 @@
|
||||
#
|
||||
# Crafting Guide - mod_pack.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Item = require './item'
|
||||
ItemSlug = require './item_slug'
|
||||
Mod = require './mod'
|
||||
ModPack = require './mod_pack'
|
||||
ModVersion = require './mod_version'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
buildcraft = industrialCraft = minecraft = modPack = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'mod_pack.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
minecraft = new Mod slug:'minecraft', name:'Minecraft'
|
||||
minecraft.addModVersion new ModVersion modSlug:minecraft.slug, version:'1.7.10'
|
||||
minecraft.activeModVersion.addItem new Item name:'Wool'
|
||||
minecraft.activeModVersion.addItem new Item name:'Bed', recipes:['']
|
||||
minecraft.activeModVersion.registerName ItemSlug.slugify('iron_chestplate'), 'Iron Chestplate'
|
||||
|
||||
buildcraft = new Mod slug:'buildcraft', name:'Buildcraft'
|
||||
buildcraft.addModVersion new ModVersion modSlug:buildcraft.slug, version:'6.2.6'
|
||||
buildcraft.activeModVersion.addItem new Item name:'Stone Gear', recipes:['']
|
||||
buildcraft.activeModVersion.addItem new Item name:'Wrench', recipes:['']
|
||||
buildcraft.activeVersion = Mod.Version.None
|
||||
|
||||
industrialCraft = new Mod slug:'industrial_craft', name:'Industrial Craft'
|
||||
industrialCraft.addModVersion new ModVersion modSlug:industrialCraft.slug, version:'2.0'
|
||||
industrialCraft.activeModVersion.addItem new Item name:'Resin'
|
||||
industrialCraft.activeModVersion.addItem new Item name:'Rubber'
|
||||
industrialCraft.activeModVersion.addItem new Item name:'Wrench', recipes:['']
|
||||
industrialCraft.activeVersion = Mod.Version.None
|
||||
|
||||
modPack = new ModPack
|
||||
modPack.addMod minecraft
|
||||
modPack.addMod buildcraft
|
||||
modPack.addMod industrialCraft
|
||||
|
||||
describe 'findItem', ->
|
||||
|
||||
it 'can find an item by partial slug', ->
|
||||
item = modPack.findItem ItemSlug.slugify 'wool'
|
||||
item.slug.qualified.should.equal 'minecraft__wool'
|
||||
|
||||
it 'can find an item by full slug', ->
|
||||
item = modPack.findItem ItemSlug.slugify 'minecraft__wool'
|
||||
item.name.should.equal 'Wool'
|
||||
|
||||
it 'can find an ambiguous item by full slug', ->
|
||||
buildcraft.activeVersion = Mod.Version.Latest
|
||||
industrialCraft.activeVersion = Mod.Version.Latest
|
||||
item = modPack.findItem ItemSlug.slugify 'industrial_craft__wrench'
|
||||
item.name.should.equal 'Wrench'
|
||||
item.modVersion.mod.name.should.equal 'Industrial Craft'
|
||||
|
||||
it 'can find an ambiguous item by partial slug', ->
|
||||
buildcraft.activeVersion = Mod.Version.Latest
|
||||
industrialCraft.activeVersion = Mod.Version.Latest
|
||||
item = modPack.findItem ItemSlug.slugify 'wrench'
|
||||
item.name.should.equal 'Wrench'
|
||||
item.modVersion.mod.name.should.equal 'Buildcraft'
|
||||
|
||||
describe 'findItemByName', ->
|
||||
|
||||
it 'finds the requested item', ->
|
||||
item = modPack.findItemByName 'Bed'
|
||||
item.name.should.equal 'Bed'
|
||||
|
||||
it 'ignores disabled mod versions', ->
|
||||
item = modPack.findItemByName 'Stone Gear'
|
||||
expect(item).to.be.null
|
||||
|
||||
describe 'findItemDisplay', ->
|
||||
|
||||
it 'returns all data for a regular Minecraft item', ->
|
||||
display = modPack.findItemDisplay ItemSlug.slugify 'bed'
|
||||
display.iconUrl.should.equal '/data/minecraft/items/bed/icon.png'
|
||||
display.itemUrl.should.equal '/browse/minecraft/bed/'
|
||||
display.itemName.should.equal 'Bed'
|
||||
display.modSlug.should.equal 'minecraft'
|
||||
|
||||
it 'returns all data for an item in an enabled mod', ->
|
||||
buildcraft.activeVersion = '6.2.6'
|
||||
display = modPack.findItemDisplay ItemSlug.slugify 'stone_gear'
|
||||
display.iconUrl.should.equal '/data/buildcraft/items/stone_gear/icon.png'
|
||||
display.itemUrl.should.equal '/browse/buildcraft/stone_gear/'
|
||||
display.itemName.should.equal 'Stone Gear'
|
||||
display.modSlug.should.equal 'buildcraft'
|
||||
|
||||
it 'assumes an unfound item is from Minecraft', ->
|
||||
display = modPack.findItemDisplay ItemSlug.slugify 'iron_chestplate'
|
||||
display.iconUrl.should.equal '/data/minecraft/items/iron_chestplate/icon.png'
|
||||
display.itemUrl.should.equal '/browse/minecraft/iron_chestplate/'
|
||||
display.itemName.should.equal 'Iron Chestplate'
|
||||
display.modSlug.should.equal 'minecraft'
|
||||
@@ -1,18 +1,14 @@
|
||||
###
|
||||
Crafting Guide - mod_version.coffee
|
||||
#
|
||||
# Crafting Guide - mod_version.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
Item = require './item'
|
||||
ItemSlug = require './item_slug'
|
||||
Recipe = require './recipe'
|
||||
_ = require 'underscore'
|
||||
{Event} = require '../constants'
|
||||
{RequiredMods} = require '../constants'
|
||||
{Url} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
Item = require './item'
|
||||
ItemSlug = require './item_slug'
|
||||
Recipe = require './recipe'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -183,14 +179,14 @@ module.exports = class ModVersion extends BaseModel
|
||||
# Backbone.Model Overrides #####################################################################
|
||||
|
||||
parse: (text)->
|
||||
ModVersionParser = require './mod_version_parser' # to avoid require cycles
|
||||
ModVersionParser = require '../parsing/mod_version_parser' # to avoid require cycles
|
||||
@_parser ?= new ModVersionParser model:this
|
||||
@_parser.parse text
|
||||
|
||||
return null # prevent calling `set`
|
||||
|
||||
url: ->
|
||||
return Url.modVersionData modSlug:@modSlug, modVersion:@version
|
||||
return c.url.modVersionData modSlug:@modSlug, modVersion:@version
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#
|
||||
# Crafting Guide - mod_version.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Item = require './item'
|
||||
ItemSlug = require './item_slug'
|
||||
ModVersion = require './mod_version'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
modVersion = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'mod_version.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
modVersion = new ModVersion modSlug:'test', version:'0.0'
|
||||
modVersion.addItem new Item name:'underscore', group:'punctuation'
|
||||
modVersion.addItem new Item name:'bravo', group:'letter'
|
||||
modVersion.addItem new Item name:'alpha', group:'letter'
|
||||
modVersion.addItem new Item name:'one', group:'number'
|
||||
modVersion.addItem new Item name:'two', group:'number'
|
||||
|
||||
describe 'constructor', ->
|
||||
|
||||
it 'requires a mod slug', ->
|
||||
expect(-> new ModVersion version:'0.0').to.throw Error, 'attributes.modSlug is required'
|
||||
|
||||
it 'requires a mod version', ->
|
||||
expect(-> new ModVersion modSlug:'test').to.throw Error, 'attributes.version is required'
|
||||
|
||||
describe 'addItem', ->
|
||||
|
||||
it 'refuses to add duplicates', ->
|
||||
modVersion.addItem new Item name:'Wool'
|
||||
expect(-> modVersion.addItem new Item name:'Wool').to.throw Error, 'duplicate item for Wool'
|
||||
|
||||
it 'adds an item indexed by its slug', ->
|
||||
modVersion.addItem new Item name:'Wool'
|
||||
modVersion._items.wool.name.should.equal 'Wool'
|
||||
|
||||
it 'sets the modVersion', ->
|
||||
modVersion.addItem new Item name:'Wool'
|
||||
modVersion._items.wool.modVersion.should.equal modVersion
|
||||
|
||||
describe 'eachGroup', ->
|
||||
|
||||
it 'returns all the groups in order', ->
|
||||
groupNames = []
|
||||
modVersion.eachGroup (groupName)-> groupNames.push groupName
|
||||
groupNames.should.eql ['letter', 'number', 'punctuation']
|
||||
|
||||
describe 'eachItemInGroup', ->
|
||||
|
||||
it 'returns immediately for unknown group', ->
|
||||
slugs = []
|
||||
modVersion.eachItemInGroup 'foobar', (item)-> slugs.push item.slug.qualified
|
||||
slugs.should.eql []
|
||||
|
||||
it 'calls callback for exactly the items in a group in order', ->
|
||||
slugs = []
|
||||
modVersion.eachItemInGroup 'letter', (item)-> slugs.push item.slug.qualified
|
||||
slugs.should.eql ['test__alpha', 'test__bravo']
|
||||
|
||||
slugs = []
|
||||
modVersion.eachItemInGroup 'number', (item)-> slugs.push item.slug.qualified
|
||||
slugs.should.eql ['test__one', 'test__two']
|
||||
|
||||
|
||||
describe 'findItemByName', ->
|
||||
|
||||
it 'locates items by slugified name', ->
|
||||
modVersion.addItem new Item name:'Crafting Table'
|
||||
modVersion.findItemByName('Crafting Table').slug.qualified.should.equal 'test__crafting_table'
|
||||
|
||||
describe 'findRecipes', ->
|
||||
|
||||
beforeEach ->
|
||||
modVersion = new ModVersion modSlug:'test', version:'1.0'
|
||||
modVersion.parse """
|
||||
schema:1
|
||||
|
||||
item: Cake
|
||||
recipe:; input: Milk, Sugar, Egg, Wheat; pattern: 000 121 333; extras: 3 Bucket
|
||||
recipe:; input: Milk, Cocoa Beans, Egg, Wheat; pattern: 000 121 333; extras: 3 Bucket
|
||||
recipe:; input: Cake Slice; pattern: 000 000 000; onlyIf: item Cake Slice
|
||||
item: Bucket
|
||||
recipe:; input: Iron Ingot; pattern: ... 0.0 .0.
|
||||
"""
|
||||
|
||||
it 'finds all recipes which list item as output', ->
|
||||
recipes = modVersion.findRecipes ItemSlug.slugify('test__bucket')
|
||||
(r.output[0].itemSlug.item for r in recipes).sort().should.eql ['bucket', 'cake', 'cake']
|
||||
|
||||
it 'skip recipes whose conditions are not met', ->
|
||||
recipes = modVersion.findRecipes ItemSlug.slugify('test__cake')
|
||||
recipes.length.should.equal 2
|
||||
@@ -1,12 +1,11 @@
|
||||
###
|
||||
Crafting Guide - multiblock.coffee
|
||||
#
|
||||
# Crafting Guide - multiblock.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require '../underscore_mixins'
|
||||
BaseModel = require './base_model'
|
||||
BaseModel = require '../base_model'
|
||||
Inventory = require './inventory'
|
||||
|
||||
########################################################################################################################
|
||||
@@ -0,0 +1,85 @@
|
||||
#
|
||||
# Crafting Guide - multiblock.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
ItemSlug = require './item_slug'
|
||||
Mod = require './mod'
|
||||
Multiblock = require './multiblock'
|
||||
Stack = require './stack'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'multiblock.coffee', ->
|
||||
|
||||
describe 'with a single block type', ->
|
||||
|
||||
beforeEach ->
|
||||
@input = [ new Stack itemSlug:ItemSlug.slugify('cobblestone'), quantity:1 ]
|
||||
|
||||
it 'correctly parses a 1x1x1 cube', ->
|
||||
model = new Multiblock input:@input, layers:['0']
|
||||
model.depth.should.equal 1
|
||||
model.height.should.equal 1
|
||||
model.width.should.equal 1
|
||||
model.getStackAt(0, 0, 0).toString().should.equal '1 cobblestone'
|
||||
|
||||
it 'correctly parses a solid 3x3x3 cube', ->
|
||||
model = new Multiblock input:@input, layers:['000 000 000', '000 000 000', '000 000 000']
|
||||
model.depth.should.equal 3
|
||||
model.height.should.equal 3
|
||||
model.width.should.equal 3
|
||||
|
||||
for x in [0..2]
|
||||
for y in [0..2]
|
||||
for z in [0..2]
|
||||
model.getStackAt(x, y, z).toString().should.equal '1 cobblestone'
|
||||
|
||||
it 'correctly parses a hollow 3x3x3 cube', ->
|
||||
model = new Multiblock input:@input, layers:['000 000 000', '000 0.0 000', '000 000 000']
|
||||
model.depth.should.equal 3
|
||||
model.height.should.equal 3
|
||||
model.width.should.equal 3
|
||||
|
||||
for x in [0..2]
|
||||
for y in [0..2]
|
||||
for z in [0..2]
|
||||
if x isnt 1 or y isnt 1 or z isnt 1
|
||||
model.getStackAt(x, y, z).toString().should.equal '1 cobblestone'
|
||||
else
|
||||
expect(model.getStackAt(x, y, z)).to.be.null
|
||||
|
||||
it 'correctly parses a 3x2x3 pyramid', ->
|
||||
model = new Multiblock input:@input, layers:['000 000 000', '.. .0 ..']
|
||||
model.depth.should.equal 3
|
||||
model.height.should.equal 2
|
||||
model.width.should.equal 3
|
||||
|
||||
for y in [0..1]
|
||||
for z in [0..2]
|
||||
for x in [0..2]
|
||||
if y is 1 and (x isnt 1 or z isnt 1)
|
||||
expect(model.getStackAt(x, y, z)).to.be.null
|
||||
else
|
||||
model.getStackAt(x, y, z).toString().should.equal '1 cobblestone'
|
||||
|
||||
describe 'with multiple block types', ->
|
||||
|
||||
beforeEach ->
|
||||
@input = [
|
||||
new Stack itemSlug:ItemSlug.slugify('cobblestone'), quantity:1
|
||||
new Stack itemSlug:ItemSlug.slugify('stone'), quantity:1
|
||||
new Stack itemSlug:ItemSlug.slugify('oak wood'), quantity:1
|
||||
]
|
||||
|
||||
it 'correctly parses a 1x3x1 column of different types', ->
|
||||
model = new Multiblock input:@input, layers:['0', '1', '2']
|
||||
model.depth.should.equal 1
|
||||
model.height.should.equal 3
|
||||
model.width.should.equal 1
|
||||
|
||||
model.getStackAt(0, 0, 0).toString().should.equal '1 cobblestone'
|
||||
model.getStackAt(0, 1, 0).toString().should.equal '1 stone'
|
||||
model.getStackAt(0, 2, 0).toString().should.equal '1 oak_wood'
|
||||
@@ -1,14 +1,13 @@
|
||||
###
|
||||
Crafting Guide - recipe.coffee
|
||||
#
|
||||
# Crafting Guide - recipe.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
BaseModel = require '../base_model'
|
||||
ItemSlug = require './item_slug'
|
||||
Stack = require './stack'
|
||||
{Event} = require '../constants'
|
||||
{StringBuilder} = require 'crafting-guide-common'
|
||||
|
||||
########################################################################################################################
|
||||
@@ -38,8 +37,8 @@ module.exports = class Recipe extends BaseModel
|
||||
|
||||
@_computeQuantities attributes.pattern
|
||||
|
||||
@on Event.change + ':modVersion', => @_slug = null
|
||||
@on Event.change + ':pattern', => @_patternCache = null
|
||||
@on c.event.change + ':modVersion', => @_slug = null
|
||||
@on c.event.change + ':pattern', => @_patternCache = null
|
||||
|
||||
# Class Methods ################################################################################
|
||||
|
||||
@@ -104,12 +103,9 @@ module.exports = class Recipe extends BaseModel
|
||||
if modPack?.findItemByName(@condition.noun)?
|
||||
result = true
|
||||
else if @condition.verb is 'mod'
|
||||
if not modPack?
|
||||
result = true
|
||||
else
|
||||
modPack.eachMod (mod)=>
|
||||
if mod.name is @condition.noun
|
||||
result = true
|
||||
modPack.eachMod (mod)->
|
||||
if mod.name is @condition.noun
|
||||
result = true
|
||||
|
||||
if @condition.inverted then result = not result
|
||||
return result
|
||||
@@ -0,0 +1,90 @@
|
||||
#
|
||||
# Crafting Guide - recipe.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Item = require './item'
|
||||
ItemSlug = require './item_slug'
|
||||
Recipe = require './recipe'
|
||||
Stack = require './stack'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
input = output = pattern = recipe = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'recipe.coffee', ->
|
||||
|
||||
describe 'constructor', ->
|
||||
|
||||
beforeEach ->
|
||||
input = [
|
||||
new Stack(itemSlug:new ItemSlug('iron_gear')),
|
||||
new Stack(itemSlug:new ItemSlug('gold_ingot'), quantity:4)
|
||||
]
|
||||
pattern = '.1. 101 .1.'
|
||||
|
||||
it 'requires input', ->
|
||||
expect(-> new Recipe slug:'gold_gear', pattern:pattern).to.throw Error, 'attributes.input is required'
|
||||
|
||||
it 'requires a pattern', ->
|
||||
expect(-> new Recipe slug:'gold_gear', input:input).to.throw Error, 'attributes.pattern is required'
|
||||
|
||||
it 'requires either outputs or a slug', ->
|
||||
f = -> new Recipe input:input, pattern:pattern
|
||||
expect(f).to.throw 'attributes.itemSlug or attributes.output is required'
|
||||
|
||||
it 'creates default output', ->
|
||||
recipe = new Recipe itemSlug:ItemSlug.slugify('gold_gear'), input:input, pattern:pattern
|
||||
recipe.output.length.should.equal 1
|
||||
recipe.output[0].itemSlug.qualified.should.equal 'gold_gear'
|
||||
recipe.output[0].quantity.should.equal 1
|
||||
|
||||
it 'assigns a default slug', ->
|
||||
recipe = new Recipe input:input, pattern:pattern, output:[new Stack itemSlug:ItemSlug.slugify('gold_gear')]
|
||||
recipe.itemSlug.qualified.should.equal 'gold_gear'
|
||||
|
||||
describe 'getStackAtSlot', ->
|
||||
|
||||
beforeEach ->
|
||||
input = [
|
||||
new Stack itemSlug:ItemSlug.slugify('iron_gear')
|
||||
new Stack itemSlug:ItemSlug.slugify('gold_ingot'), quantity:4
|
||||
]
|
||||
recipe = new Recipe itemSlug:'gold_gear', input:input, pattern:'.1. 101 .1.'
|
||||
|
||||
it 'returns the proper item for an early slot', ->
|
||||
stack = recipe.getStackAtSlot(1)
|
||||
stack.itemSlug.qualified.should.equal 'gold_ingot'
|
||||
stack.quantity.should.equal 4
|
||||
|
||||
it 'returns the proper item for a late slot', ->
|
||||
stack = recipe.getStackAtSlot(4)
|
||||
stack.itemSlug.qualified.should.equal 'iron_gear'
|
||||
stack.quantity.should.equal 1
|
||||
|
||||
it 'returns null for an invalid slot', ->
|
||||
expect(recipe.getStackAtSlot(12)).to.be.null
|
||||
|
||||
describe '_parsePattern', ->
|
||||
|
||||
beforeEach ->
|
||||
recipe = new Recipe
|
||||
itemSlug: 'oak_wood_planks',
|
||||
input: [new Stack itemSlug:new ItemSlug('oak_wood')],
|
||||
pattern:'... .0. ...'
|
||||
|
||||
it 'normalizes invalid characters', ->
|
||||
recipe._parsePattern('$$0 #() 010').should.equal '..0 ... 010'
|
||||
|
||||
it 'removes extra characters', ->
|
||||
recipe._parsePattern('000 000 000 000').should.equal '000 000 000'
|
||||
|
||||
it 'fills in missing characters', ->
|
||||
recipe._parsePattern('000000').should.equal '000 000 ...'
|
||||
|
||||
it 'fills in spaces', ->
|
||||
recipe._parsePattern('000000000').should.equal '000 000 000'
|
||||
@@ -1,9 +1,9 @@
|
||||
###
|
||||
Crafting Guide - simple_stack.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - simple_stack.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
ItemSlug = require './item_slug'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
###
|
||||
Crafting Guide - stack.coffee
|
||||
#
|
||||
# Crafting Guide - stack.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
BaseModel = require '../base_model'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+6
-7
@@ -1,11 +1,10 @@
|
||||
###
|
||||
Crafting Guide - command_parser_version_base.coffee
|
||||
#
|
||||
# Crafting Guide - command_parser_version_base.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
{StringBuilder} = require 'crafting-guide-common'
|
||||
|
||||
########################################################################################################################
|
||||
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Crafting Guide - command_parser_version_base.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CommandParserVersionBase = require './command_parser_version_base'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
parser = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'command_parser_version_base.coffee', ->
|
||||
|
||||
beforeEach -> parser = new CommandParserVersionBase model:{}
|
||||
|
||||
describe '_parseHereDoc', ->
|
||||
|
||||
it 'returns null for non-heredoc lines', ->
|
||||
result = parser._parseHereDoc 'foobar: baz'
|
||||
expect(result[1]).to.be.null
|
||||
|
||||
it 'identifies the right text for a real heredoc', ->
|
||||
parser._lines = ['command: <<-END', 'alpha', 'bravo', 'charlie', 'END', 'command1: arg2']
|
||||
parser._lineNumber = 1
|
||||
|
||||
result = parser._parseHereDoc parser._lines[0]
|
||||
result[0].should.equal 'command: '
|
||||
result[1].should.equal 'alpha\nbravo\ncharlie'
|
||||
|
||||
it 'identifies an empty heredoc', ->
|
||||
parser._lines = ['command: <<-END', 'END']
|
||||
parser._lineNumber = 1
|
||||
|
||||
result = parser._parseHereDoc parser._lines[0]
|
||||
result[0].should.equal 'command: '
|
||||
expect(result[1]).to.be.null
|
||||
|
||||
it 'trims smallest leading whitespace', ->
|
||||
parser._lines = ['command: <<-END', ' alpha', ' bravo', '', ' charlie', 'END', 'command1: arg2']
|
||||
parser._lineNumber = 1
|
||||
|
||||
result = parser._parseHereDoc parser._lines[0]
|
||||
result[0].should.equal 'command: '
|
||||
result[1].should.equal 'alpha\n bravo\n\ncharlie'
|
||||
@@ -1,12 +1,12 @@
|
||||
###
|
||||
Crafting Guide - item_parser.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - item_parser.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
VersionedParserBase = require './versioned_parser_base'
|
||||
ItemParserV1 = require './parser_versions/item_parser_v1'
|
||||
ItemParserV1 = require './item_parser_v1'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+6
-6
@@ -1,9 +1,9 @@
|
||||
###
|
||||
Crafting Guide - item_parser_v1.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - item_parser_v1.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CommandParserVersionBase = require './command_parser_version_base'
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#
|
||||
# Crafting Guide - item_parser_v1.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Item = require '../game/item'
|
||||
ItemParserV1 = require './item_parser_v1'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
baseText = item = parser = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'item_parser_v1.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
item = new Item name:'alpha'
|
||||
parser = new ItemParserV1 model:item
|
||||
|
||||
describe 'officialUrl', ->
|
||||
|
||||
it 'may be omitted', ->
|
||||
parser.parse 'schema: 1\nvideo: youtubeid, Video Alpha\ndescription: Bravo, Charlie'
|
||||
expect(item.officialUrl).to.be.null
|
||||
|
||||
it 'is assigned properly when given', ->
|
||||
parser.parse 'schema: 1\nofficialUrl: http://testurl.com'
|
||||
item.officialUrl.should.equal 'http://testurl.com'
|
||||
|
||||
it 'does not allow duplicate declarations', ->
|
||||
func = -> parser.parse 'schema: 1\nofficialUrl: http://testurl.com\nofficialUrl: http://testurl2.com'
|
||||
expect(func).to.throw Error, 'duplicate'
|
||||
|
||||
it 'does not allow an empty value if given', ->
|
||||
func = -> parser.parse 'schema: 1\nofficialUrl:'
|
||||
expect(func).to.throw Error, 'empty'
|
||||
|
||||
describe 'description', ->
|
||||
|
||||
it 'may be omitted', ->
|
||||
parser.parse 'schema: 1\nvideo: youTubeId, Video Alpha\nofficialUrl: http://testurl.com'
|
||||
expect(item.description).to.be.null
|
||||
|
||||
it 'is assigned properly when given', ->
|
||||
parser.parse 'schema: 1\ndescription: Alpha Bravo Charlie'
|
||||
item.description.should.equal 'Alpha Bravo Charlie'
|
||||
|
||||
it 'can be a heredoc', ->
|
||||
parser.parse 'schema: 1\ndescription: <<-END\nAlpha\nBravo\nCharlie\nEND'
|
||||
item.description.should.equal 'Alpha\nBravo\nCharlie'
|
||||
|
||||
it 'concatenates multiple declarations', ->
|
||||
parser.parse 'schema: 1\ndescription: Alpha\ndescription: Bravo'
|
||||
item.description.should.equal 'Alpha\nBravo'
|
||||
|
||||
describe 'video', ->
|
||||
|
||||
it 'may be omitted', ->
|
||||
parser.parse 'schema: 1\nofficialUrl: http://testurl.com\ndescription: Alpha Bravo Charlie'
|
||||
item.videos.should.eql []
|
||||
|
||||
it 'is assigned properly when given', ->
|
||||
parser.parse 'schema: 1\nvideo: youtubeid, Alpha Bravo'
|
||||
item.videos[0].should.eql youTubeId:'youtubeid', name:'Alpha Bravo'
|
||||
item.videos.length.should.equal 1
|
||||
|
||||
it 'may be included multiple times', ->
|
||||
parser.parse 'schema: 1\nvideo: youtubeid1, Alpha\nvideo: youtubeid2, Bravo'
|
||||
item.videos[0].should.eql youTubeId:'youtubeid1', name:'Alpha'
|
||||
item.videos[1].should.eql youTubeId:'youtubeid2', name:'Bravo'
|
||||
item.videos.length.should.equal 2
|
||||
|
||||
it 'requires a YouTubeId and name', ->
|
||||
func = -> parser.parse 'schema: 1\nvideo: alpha'
|
||||
expect(func).to.throw Error, 'requires a name'
|
||||
|
||||
describe 'unparsing', ->
|
||||
|
||||
it 'can round-trip a fully described item', ->
|
||||
text = """
|
||||
schema: 1
|
||||
|
||||
officialUrl: http://testurl.com
|
||||
|
||||
description: <<-END
|
||||
Alpha
|
||||
Bravo
|
||||
END
|
||||
|
||||
video: youtubeid1, Alpha Bravo
|
||||
video: youtubeid2, Charlie Delta
|
||||
|
||||
|
||||
"""
|
||||
parser.parse text
|
||||
parser.unparse().should.equal text
|
||||
@@ -1,12 +1,12 @@
|
||||
###
|
||||
Crafting Guide - mod_parser.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - mod_parser.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
VersionedParserBase = require './versioned_parser_base'
|
||||
ModParserV1 = require './parser_versions/mod_parser_v1'
|
||||
ModParserV1 = require './mod_parser_v1'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+9
-9
@@ -1,14 +1,14 @@
|
||||
###
|
||||
Crafting Guide - mod_parser_v2.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - mod_parser_v1.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CommandParserVersionBase = require './command_parser_version_base'
|
||||
Mod = require '../mod'
|
||||
ModVersion = require '../mod_version'
|
||||
Tutorial = require '../tutorial'
|
||||
Mod = require '../game/mod'
|
||||
ModVersion = require '../game/mod_version'
|
||||
Tutorial = require '../site/tutorial'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
###
|
||||
Crafting Guide - mod_version_parser.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - mod_version_parser.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
VersionedParserBase = require './versioned_parser_base'
|
||||
ModVersionParserV1 = require './parser_versions/mod_version_parser_v1'
|
||||
ModVersionParserV1 = require './mod_version_parser_v1'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+12
-13
@@ -1,18 +1,17 @@
|
||||
###
|
||||
Crafting Guide - mod_version_parser_v2.coffee
|
||||
#
|
||||
# Crafting Guide - mod_version_parser_v1.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
CommandParserVersionBase = require './command_parser_version_base'
|
||||
Item = require '../item'
|
||||
ItemSlug = require '../item_slug'
|
||||
ModVersion = require '../mod_version'
|
||||
Multiblock = require '../multiblock'
|
||||
Recipe = require '../recipe'
|
||||
Stack = require '../simple_stack'
|
||||
Item = require '../game/item'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
ModVersion = require '../game/mod_version'
|
||||
Multiblock = require '../game/multiblock'
|
||||
Recipe = require '../game/recipe'
|
||||
Stack = require '../game/simple_stack'
|
||||
{StringBuilder} = require 'crafting-guide-common'
|
||||
|
||||
########################################################################################################################
|
||||
@@ -0,0 +1,344 @@
|
||||
#
|
||||
# Crafting Guide - mod_version_parser_v1.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CommandParserVersionBase = require './command_parser_version_base'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
ModVersion = require '../game/mod_version'
|
||||
ModVersionParserV1 = require './mod_version_parser_v1'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
baseText = modVersion = parser = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'mod_version_parser_v1.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
modVersion = new ModVersion modSlug:'test', version:'0.0'
|
||||
parser = new ModVersionParserV1 model:modVersion
|
||||
|
||||
describe 'Item', ->
|
||||
|
||||
it 'allows multiple recipes', ->
|
||||
recipes = "item: Charlie;
|
||||
recipe:; input:Alpha; pattern:... .0. ...;
|
||||
recipe:; input:Bravo; pattern:... 0.0 ...;"
|
||||
modVersion = parser.parse recipes
|
||||
recipes = modVersion.findRecipes ItemSlug.slugify 'test__charlie'
|
||||
recipes[0].input[0].itemSlug.qualified.should.equal 'alpha'
|
||||
recipes[1].input[0].itemSlug.qualified.should.equal 'bravo'
|
||||
|
||||
describe 'name', ->
|
||||
|
||||
it 'adds the name when present', ->
|
||||
modVersion = parser.parse 'item: Charlie'
|
||||
modVersion._items.charlie.name.should.equal 'Charlie'
|
||||
|
||||
it 'requires a non-empty name', ->
|
||||
func = -> parser.parse 'item: \n'
|
||||
expect(func).to.throw Error, 'cannot be empty'
|
||||
|
||||
describe 'gatherable', ->
|
||||
|
||||
it 'adds "gatherable" when present', ->
|
||||
modVersion = parser.parse 'item: Alpha Bravo; gatherable: yes'
|
||||
modVersion._items.alpha_bravo.isGatherable.should.be.true
|
||||
|
||||
it 'does not allow a duplicate "gatherable" declaration', ->
|
||||
func = -> parser.parse 'item: Alpha Bravo; gatherable: yes; gatherable: yes'
|
||||
expect(func).to.throw Error, 'duplicate'
|
||||
|
||||
it 'requires "gatherable" to be "yes" or "no"', ->
|
||||
func = -> parser.parse 'item: Alpha Bravo; gatherable: true'
|
||||
expect(func).to.throw Error, 'gatherable must be'
|
||||
|
||||
it 'does not allow "gatherable" before "item"', ->
|
||||
func = -> parser.parse 'gatherable: yes; item: Alpha Bravo; gatherable: yes'
|
||||
expect(func).to.throw Error, '"gatherable" before "item"'
|
||||
|
||||
describe 'Multiblock', ->
|
||||
|
||||
it 'adds a "multiblock" when present', ->
|
||||
modVersion = parser.parse 'item: Alpha; multiblock:; input:Bravo; layer: 0'
|
||||
item = modVersion.findItemByName 'Alpha'
|
||||
item.multiblock.height.should.equal 1
|
||||
|
||||
it 'requires "item" be declared before "multiblock"', ->
|
||||
func = -> parser.parse "multiblock:"
|
||||
expect(func).to.throw Error, '"multiblock" before "item"'
|
||||
|
||||
it 'prohibits multiple "multiblock" commands per item"', ->
|
||||
func = -> parser.parse "item: Alpha; multiblock:; multiblock:"
|
||||
expect(func).to.throw Error, 'duplicate'
|
||||
|
||||
it 'prohibits multiblocks with no inputs', ->
|
||||
func = -> parser.parse "item: Alpha; multiblock:; layer: 000"
|
||||
expect(func).to.throw Error, 'at least one "input"'
|
||||
|
||||
describe 'layer', ->
|
||||
|
||||
it 'allows multiple "layer" commands', ->
|
||||
modVersion = parser.parse 'item: Alpha; multiblock:; input:Bravo, Charlie; layer: 01 10; layer: 10 01'
|
||||
item = modVersion.findItemByName 'Alpha'
|
||||
item.multiblock.depth.should.equal 2
|
||||
item.multiblock.height.should.equal 2
|
||||
item.multiblock.width.should.equal 2
|
||||
|
||||
it 'prohibits empty layers', ->
|
||||
func = -> parser.parse 'item: Alpha; multiblock:; input: Bravo; layer:; layer: 00 00'
|
||||
expect(func).to.throw Error, 'empty layer'
|
||||
|
||||
it 'prohibits multiblocks with no layers', ->
|
||||
func = -> parser.parse "item: Alpha; multiblock:; input: Bravo"
|
||||
expect(func).to.throw Error, 'at least one "layer"'
|
||||
|
||||
describe 'Recipe', ->
|
||||
|
||||
beforeEach -> baseText = 'item: Charlie; '
|
||||
|
||||
describe 'input', ->
|
||||
|
||||
it 'adds "input" when present', ->
|
||||
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern: ... 010 ...'
|
||||
charlieSlug = ItemSlug.slugify('test__charlie')
|
||||
slugs = (s.itemSlug.item for s in modVersion.findRecipes(charlieSlug)[0].input)
|
||||
slugs.should.eql ['alpha', 'bravo']
|
||||
|
||||
it 'requires an "input" declaration', ->
|
||||
func = -> parser.parse baseText + 'recipe:; pattern: ... .0. ...'
|
||||
expect(func).to.throw Error, 'the "input" declaration is required'
|
||||
|
||||
it 'does not allow a duplicate "input" declaration', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:....0....; input:Bravo'
|
||||
expect(func).to.throw Error, 'duplicate declaration of "input"'
|
||||
|
||||
it 'does not allow "input" before "recipe"', ->
|
||||
func = -> parser.parse baseText + 'input:Alpha, Bravo; recipe:; pattern:....0....'
|
||||
expect(func).to.throw Error, 'cannot declare "input" before "recipe"'
|
||||
|
||||
it 'registers slugs for each input name', ->
|
||||
modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...'
|
||||
(s.item for s in modVersion._slugs).should.eql ['charlie', 'delta', 'echo', 'foxtrot']
|
||||
|
||||
it 'correctly handles recipes which use the same input multiple times', ->
|
||||
modVersion = parser.parse baseText + 'recipe:; input:Alpha; pattern:.0..0....'
|
||||
recipe = _.values(modVersion._recipes)[0]
|
||||
recipe.getQuantityRequired(ItemSlug.slugify('alpha')).should.equal 2
|
||||
|
||||
it 'allows a quantity for each input', ->
|
||||
modVersion = parser.parse baseText + 'recipe:; input: 12 Delta, 3 Echo; pattern:... 0.1 ...'
|
||||
recipe = _.values(modVersion._recipes)[0]
|
||||
recipe.input[0].quantity.should.equal 12
|
||||
recipe.input[0].itemSlug.qualified.should.equal 'delta'
|
||||
recipe.input[1].quantity.should.equal 3
|
||||
recipe.input[1].itemSlug.qualified.should.equal 'echo'
|
||||
|
||||
describe 'pattern', ->
|
||||
|
||||
it 'adds "pattern" when present', ->
|
||||
modVersion = parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:... .0. .1.'
|
||||
modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].pattern.should.equal '... .0. .1.'
|
||||
|
||||
it 'requires a "pattern" declaration', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo'
|
||||
expect(func).to.throw Error, 'the "pattern" declaration is required'
|
||||
|
||||
it 'does not allow a duplicate "pattern" declaration', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern:....0..1.; pattern:01.......'
|
||||
expect(func).to.throw Error, 'duplicate declaration of "pattern"'
|
||||
|
||||
it 'requires pattern to be the right length', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:000'
|
||||
expect(func).to.throw Error, 'a pattern must have'
|
||||
|
||||
it 'requires pattern to only use proper characters', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:abc def ghi'
|
||||
expect(func).to.throw Error, 'a pattern must have'
|
||||
|
||||
it 'requires pattern to only refer to existing items', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha; pattern:... 010 ...'
|
||||
expect(func).to.throw Error, 'there is no item 1 in this recipe'
|
||||
|
||||
it 'requires all items to appear in the pattern', ->
|
||||
func = -> parser.parse baseText + 'recipe:; input:Alpha, Bravo; pattern: 000 0.0 000'
|
||||
expect(func).to.throw Error, 'Bravo is listed'
|
||||
|
||||
it 'does not allow "pattern" before "recipe"', ->
|
||||
func = -> parser.parse baseText + 'pattern:... .0. ...; recipe:; inputs:Alpha'
|
||||
expect(func).to.throw Error, 'cannot declare "pattern" before "recipe"'
|
||||
|
||||
describe 'quantity', ->
|
||||
|
||||
beforeEach ->
|
||||
baseText = 'item: Charlie; recipe:; input:Alpha; pattern:...0.0...; '
|
||||
|
||||
it 'adds "quantity" when present', ->
|
||||
modVersion = parser.parse baseText + 'quantity: 2'
|
||||
modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].output[0].quantity.should.equal 2
|
||||
|
||||
it 'does not allow a duplicate "quantity" declaration', ->
|
||||
func = -> parser.parse baseText + 'quantity:1; quantity:2'
|
||||
expect(func).to.throw Error, 'duplicate declaration of "quantity"'
|
||||
|
||||
it 'requires quantity to be an integer', ->
|
||||
func = -> parser.parse baseText + 'quantity:ten'
|
||||
expect(func).to.throw Error, 'quantity must be an integer'
|
||||
|
||||
it 'assumes a quantity of 1 by default', ->
|
||||
modVersion = parser.parse baseText
|
||||
modVersion.findRecipes(ItemSlug.slugify('test__charlie'))[0].output[0].quantity.should.equal 1
|
||||
|
||||
it 'does not allow "quantity" before recipe', ->
|
||||
func = -> parser.parse 'item:Bravo; quantity:12; recipe:;'
|
||||
expect(func).to.throw Error, 'cannot declare "quantity" before "recipe"'
|
||||
|
||||
describe 'onlyIf', ->
|
||||
|
||||
beforeEach ->
|
||||
baseText = 'item: Charlie; recipe:; input:Alpha; pattern:...0.0...; '
|
||||
|
||||
it 'understands the "item" verb', ->
|
||||
modVersion = parser.parse baseText + 'onlyIf: item Iron Ingot'
|
||||
recipes = []
|
||||
modVersion.eachRecipe (recipe)-> recipes.push recipe
|
||||
recipe = recipes[0]
|
||||
recipe.condition.should.eql verb:'item', noun:'Iron Ingot', inverted:false
|
||||
|
||||
it 'understands the "mod" verb', ->
|
||||
modVersion = parser.parse baseText + 'onlyIf: mod BuildCraft'
|
||||
recipes = []
|
||||
modVersion.eachRecipe (recipe)-> recipes.push recipe
|
||||
recipe = recipes[0]
|
||||
recipe.condition.should.eql verb:'mod', noun:'BuildCraft', inverted:false
|
||||
|
||||
it 'understands inverting verbs', ->
|
||||
modVersion = parser.parse baseText + 'onlyIf: not item Iron Ingot'
|
||||
recipes = []
|
||||
modVersion.eachRecipe (recipe)-> recipes.push recipe
|
||||
recipe = recipes[0]
|
||||
recipe.condition.should.eql verb:'item', noun:'Iron Ingot', inverted:true
|
||||
|
||||
it 'requires at least two words', ->
|
||||
func = -> parser.parse baseText + 'onlyIf: item'
|
||||
expect(func).to.throw Error, 'verb followed by a noun'
|
||||
|
||||
it 'only allows known verbs', ->
|
||||
func = -> parser.parse baseText + 'onlyIf: foo Iron Ingot'
|
||||
expect(func).to.throw Error, 'unknown verb'
|
||||
|
||||
describe 'output', ->
|
||||
|
||||
beforeEach ->
|
||||
baseText = 'item: Delta; item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; '
|
||||
|
||||
it 'adds a single item as the default output', ->
|
||||
modVersion = parser.parse baseText
|
||||
stack = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].output[0]
|
||||
stack.itemSlug.qualified.should.equal 'test__bravo'
|
||||
stack.quantity.should.equal 1
|
||||
|
||||
it 'can add multiple extras with quantities', ->
|
||||
modVersion = parser.parse baseText + 'extras:2 Delta, 4 Echo'
|
||||
output = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].output
|
||||
output[0].itemSlug.qualified.should.equal 'test__bravo'
|
||||
output[0].quantity.should.equal 1
|
||||
output[1].itemSlug.qualified.should.equal 'test__delta'
|
||||
output[1].quantity.should.equal 2
|
||||
output[2].itemSlug.qualified.should.equal 'echo'
|
||||
output[2].quantity.should.equal 4
|
||||
|
||||
it 'does not allow "extras" before "recipe"', ->
|
||||
func = -> parser.parse 'item:Bravo; extras:Charlie'
|
||||
expect(func).to.throw Error, 'cannot declare "extras" before "recipe"'
|
||||
|
||||
it 'registers slugs for each output name', ->
|
||||
modVersion = parser.parse baseText + 'extras:Delta, Echo'
|
||||
(s.qualified for s in modVersion._slugs).should.eql [
|
||||
'test__bravo', 'charlie', 'test__delta', 'echo'
|
||||
]
|
||||
|
||||
it 'does not allow a duplicate "extras" declaration', ->
|
||||
func = -> parser.parse baseText + 'extras:Echo; extras:Delta'
|
||||
expect(func).to.throw Error, 'duplicate declaration of "extras"'
|
||||
|
||||
describe 'tools', ->
|
||||
|
||||
beforeEach ->
|
||||
baseText = 'item:Bravo; recipe:; input:Charlie; pattern:... .0. ...; '
|
||||
|
||||
it 'can add a single tool', ->
|
||||
modVersion = parser.parse baseText + 'tools: Furnace'
|
||||
modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].tools[0].itemSlug.item.should.equal 'furnace'
|
||||
|
||||
it 'can add multiple tools', ->
|
||||
modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace'
|
||||
tools = modVersion.findRecipes(ItemSlug.slugify('test__bravo'))[0].tools
|
||||
tools[0].itemSlug.item.should.equal 'crafting_table'
|
||||
tools[1].itemSlug.item.should.equal 'furnace'
|
||||
|
||||
it 'registers slugs for each tool name', ->
|
||||
modVersion = parser.parse baseText + 'tools: Crafting Table, Furnace'
|
||||
(s.item for s in modVersion._slugs).should.eql ['bravo', 'charlie', 'crafting_table', 'furnace']
|
||||
|
||||
it 'does not allow a duplicate "tools" declaration', ->
|
||||
func = -> parser.parse baseText + 'tools:Crafting Table; tools:Furnace'
|
||||
expect(func).to.throw Error, 'duplicate declaration of "tools"'
|
||||
|
||||
describe "unparsing", ->
|
||||
|
||||
beforeEach ->
|
||||
baseText = """
|
||||
schema: 1
|
||||
|
||||
group: Agriculture
|
||||
|
||||
item: Apple
|
||||
|
||||
item: Baked Potato
|
||||
recipe:
|
||||
input: furnace fuel, Potato
|
||||
pattern: .1. ... .0.
|
||||
tools: Furnace
|
||||
|
||||
item: (filled) Canned Food
|
||||
recipe:
|
||||
input: 4 (Empty) Tin Can, Apple
|
||||
pattern: .1. .0. ...
|
||||
|
||||
item: Pyramid
|
||||
multiblock:
|
||||
input: Cobblestone
|
||||
layer: 000 000 000
|
||||
layer: ... .0. ...
|
||||
|
||||
group: Functional Blocks
|
||||
|
||||
item: Furnace
|
||||
recipe:
|
||||
input: Cobblestone
|
||||
pattern: 000 0.0 000
|
||||
tools: Crafting Table
|
||||
|
||||
update: Iron Ingot
|
||||
recipe:
|
||||
input: furnace fuel, Iron Dust
|
||||
pattern: .1. ... .0.
|
||||
tools: Furnace
|
||||
recipe:
|
||||
onlyIf: item Redstone Furnace
|
||||
input: Iron Ore
|
||||
pattern: ... .0. ...
|
||||
tools: Redstone Furnace
|
||||
"""
|
||||
|
||||
it 'can round-trip a data file', ->
|
||||
text = parser.unparse parser.parse baseText
|
||||
actual = CommandParserVersionBase.simplify text
|
||||
expected = CommandParserVersionBase.simplify baseText
|
||||
|
||||
actual.should.equal expected
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
###
|
||||
Crafting Guide - tutorial_parser.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - tutorial_parser.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
VersionedParserBase = require './versioned_parser_base'
|
||||
TutorialParserV1 = require './parser_versions/tutorial_parser_v1'
|
||||
TutorialParserV1 = require './tutorial_parser_v1'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+7
-7
@@ -1,12 +1,12 @@
|
||||
###
|
||||
Crafting Guide - tutorial_parser_v1.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
#
|
||||
# Crafting Guide - tutorial_parser_v1.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
CommandParserVersionBase = require './command_parser_version_base'
|
||||
Tutorial = require '../tutorial'
|
||||
Tutorial = require '../site/tutorial'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
+6
-8
@@ -1,11 +1,9 @@
|
||||
###
|
||||
Crafting Guide - versioned_parser_base.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
#
|
||||
# Crafting Guide - versioned_parser_base.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
###
|
||||
Crafting Guide - craft_page.coffee
|
||||
#
|
||||
# Crafting Guide - craft_page.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
Craftsman = require './crafting/craftsman'
|
||||
{Event} = require '../constants'
|
||||
Inventory = require './inventory'
|
||||
ModPack = require './mod_pack'
|
||||
BaseModel = require '../base_model'
|
||||
Craftsman = require '../crafting/craftsman'
|
||||
Inventory = require '../game/inventory'
|
||||
ModPack = require '../game/mod_pack'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -21,10 +20,10 @@ module.exports = class CraftPage extends BaseModel
|
||||
attributes.craftsman ?= new Craftsman attributes.modPack
|
||||
super attributes, options
|
||||
|
||||
@modPack.on Event.change, => @_consumeParams()
|
||||
@on Event.change + ':params', => @_consumeParams()
|
||||
@craftsman.on Event.change + ':stage', => @trigger Event.change, this
|
||||
@craftsman.on Event.change + ':complete', => @trigger Event.change, this
|
||||
@modPack.on c.event.change, => @_consumeParams()
|
||||
@on c.event.change + ':params', => @_consumeParams()
|
||||
@craftsman.on c.event.change + ':stage', => @trigger c.event.change, this
|
||||
@craftsman.on c.event.change + ':complete', => @trigger c.event.change, this
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
+47
-35
@@ -1,19 +1,17 @@
|
||||
###
|
||||
Crafting Guide - editable_file.coffee
|
||||
#
|
||||
# Crafting Guide - editable_file.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
{Event} = require '../constants'
|
||||
w = require 'when'
|
||||
BaseModel = require '../base_model'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class EditableFile extends BaseModel
|
||||
|
||||
@MimeTypes = MimeTypes = [
|
||||
@::MimeTypes = [
|
||||
{ pattern:/\.cg$/i, text:'text/plain' }
|
||||
{ pattern:/\.png$/i, text:'image/png' }
|
||||
{ pattern:/\.gif$/i, text:'image/gif' }
|
||||
@@ -21,7 +19,7 @@ module.exports = class EditableFile extends BaseModel
|
||||
{ pattern:/\.jpeg$/i, text:'image/jpeg' }
|
||||
]
|
||||
|
||||
@Status = Status = {
|
||||
@::Status = {
|
||||
'unknown': 'unknown'
|
||||
'checking': 'checking'
|
||||
'empty': 'empty'
|
||||
@@ -30,16 +28,17 @@ module.exports = class EditableFile extends BaseModel
|
||||
}
|
||||
|
||||
constructor: (attributes={}, options={})->
|
||||
if not options.client? then throw new Error 'options.client is required'
|
||||
attributes.fileName ?= ''
|
||||
attributes.path ?= ''
|
||||
attributes.sha ?= null
|
||||
attributes.status ?= Status.unknown
|
||||
super
|
||||
super attributes, options
|
||||
|
||||
@client = options.client
|
||||
@_client = options.client
|
||||
@_status = @Status.unknown
|
||||
@_encodedData = null
|
||||
|
||||
@on Event.change + ':fileName', => @_mimeType = null
|
||||
@on c.event.change + ':fileName', => @_mimeType = null
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
@@ -66,66 +65,79 @@ module.exports = class EditableFile extends BaseModel
|
||||
@_encodedData = newEncodedData
|
||||
@_updateStatusForNewData()
|
||||
|
||||
@trigger Event.change + ':encodedData', this, oldEncodedData, newEncodedData
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change + ':encodedData', this, oldEncodedData, newEncodedData
|
||||
@trigger c.event.change, this
|
||||
|
||||
getFullPath: ->
|
||||
return "#{@path}/#{@fileName}"
|
||||
|
||||
getMimeType: ->
|
||||
if not @_mimeType?
|
||||
for mimeType in MimeTypes
|
||||
for mimeType in @MimeTypes
|
||||
if mimeType.pattern.test @fileName
|
||||
@_mimeType = mimeType.text
|
||||
break
|
||||
|
||||
return @_mimeType
|
||||
|
||||
getStatus: ->
|
||||
return @_status
|
||||
|
||||
isValid: ->
|
||||
return @status in [ Status.clean, Status.dirty ]
|
||||
return @_status in [ @Status.clean, @Status.dirty ]
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
encodedData: { get:@prototype.getEncodedData, set:@prototype.setEncodedData }
|
||||
fullPath: { get:@prototype.getFullPath }
|
||||
imageUrl: { get:@prototype.getImageUrl }
|
||||
mimeType: { get:@prototype.getMimeType }
|
||||
status: { get:@prototype.getStatus }
|
||||
valid: { get:@prototype.isValid }
|
||||
|
||||
# BaseModel Overrides ##########################################################################
|
||||
|
||||
fetch: ->
|
||||
if not @client? then throw new Error 'EditableFile must be given a client to fetch with'
|
||||
if not @_client? then throw new Error 'EditableFile must be given a client to fetch with'
|
||||
|
||||
@status = Status.checking
|
||||
@client.fetchFile path:@fullPath
|
||||
@_updateStatus @Status.checking
|
||||
@_client.fetchFile path:@fullPath
|
||||
.then (response)=>
|
||||
data = response.json.data
|
||||
|
||||
if data.sha?
|
||||
@encodedData = data.content
|
||||
@sha = data.sha
|
||||
@_updateStatusForNewData()
|
||||
else
|
||||
@status = Status.empty
|
||||
@_updateStatus @Status.empty
|
||||
|
||||
return this
|
||||
|
||||
save: (commitMessage)->
|
||||
return w(true) if @status is Status.clean
|
||||
return w(true) if @_status is @Status.clean
|
||||
return w(true) unless @encodedData?
|
||||
if not @valid then return w.reject new Error "cannot save with an invalid status: #{@status}"
|
||||
if not @valid then return w.reject new Error "cannot save with an invalid status: #{@_status}"
|
||||
|
||||
commitMessage ?= "User-submitted update to #{@fileName} from #{global.hostName}"
|
||||
@client.updateFile content:@encodedData, message:commitMessage, path:@fullPath, sha:@sha
|
||||
@_client.updateFile content:@encodedData, message:commitMessage, path:@fullPath, sha:@sha
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_updateStatus: (newStatus)->
|
||||
oldStatus = @_status
|
||||
return if oldStatus is newStatus
|
||||
|
||||
@_status = newStatus
|
||||
@trigger c.event.change, this, oldStatus, newStatus
|
||||
@trigger c.event.change, this
|
||||
|
||||
_updateStatusForNewData: ->
|
||||
switch @status
|
||||
when Status.unknown
|
||||
@status = Status.dirty
|
||||
when Status.checking
|
||||
@status = Status.clean
|
||||
when Status.empty
|
||||
@status = Status.dirty
|
||||
when Status.clean
|
||||
@status = Status.dirty
|
||||
switch @_status
|
||||
when @Status.unknown
|
||||
@_updateStatus @Status.dirty
|
||||
when @Status.checking
|
||||
@_updateStatus @Status.clean
|
||||
when @Status.empty
|
||||
@_updateStatus @Status.dirty
|
||||
when @Status.clean
|
||||
@_updateStatus @Status.dirty
|
||||
@@ -0,0 +1,32 @@
|
||||
#
|
||||
# Crafting Guide - email_client.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
emailjs = require 'emailjs'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
emailjs.init 'user_sLxEH6RwPvzowjaIkfY6H'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class EmailClient
|
||||
|
||||
constructor: ->
|
||||
@serviceId = 'default_service'
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
send: (template, data={})->
|
||||
logger.info -> "sending email with template #{template} and data: #{JSON.stringify(data)}"
|
||||
|
||||
emailjs.send @serviceId, template, data
|
||||
.then (response)->
|
||||
logger.info -> "Email sent! status: #{response.status}, message: #{response.text}"
|
||||
return status:response.status, message:response.text
|
||||
.catch (error)->
|
||||
logger.error -> "Email failed! error: #{error}"
|
||||
throw error
|
||||
@@ -1,19 +1,18 @@
|
||||
###
|
||||
Crafting Guide - user.coffee
|
||||
#
|
||||
# Crafting Guide - github_user.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
{Url} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class GitHubUser extends BaseModel
|
||||
|
||||
constructor: (attributes={}, options={})->
|
||||
attributes.avatarUrl ?= null
|
||||
attributes.avatarUrl ?= attributes.avatar_url ?= null
|
||||
attributes.email ?= null
|
||||
attributes.login ?= null
|
||||
attributes.name ?= null
|
||||
@@ -1,14 +1,11 @@
|
||||
###
|
||||
Crafting Guide - item_page.coffee
|
||||
#
|
||||
# Crafting Guide - item_page.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
BaseModel = require './base_model'
|
||||
{Event} = require '../constants'
|
||||
{Url} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -42,13 +39,13 @@ module.exports = class ItemPage extends BaseModel
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_findRecipesMatching: (callback)->
|
||||
_findRecipesMatching: (isAcceptable)->
|
||||
return null unless @item?
|
||||
|
||||
result = {}
|
||||
@modPack.eachMod (mod)=>
|
||||
mod.eachRecipe (recipe)=>
|
||||
if callback(recipe)
|
||||
if isAcceptable recipe
|
||||
for outputStack in recipe.output
|
||||
outputItem = @modPack.findItem outputStack.itemSlug, includeDisabled:true
|
||||
result[outputItem.slug] = outputItem
|
||||
+27
-28
@@ -1,13 +1,12 @@
|
||||
###
|
||||
Crafting Guide - item_selector.coffee
|
||||
#
|
||||
# Crafting Guide - item_selector.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
ItemSlug = require './item_slug'
|
||||
_ = require 'underscore'
|
||||
BaseModel = require '../base_model'
|
||||
ItemSlug = require '../game/item_slug'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -26,25 +25,25 @@ module.exports = class ItemSelector extends BaseModel
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getHint: ->
|
||||
return @_hint
|
||||
|
||||
setHint: (newHint)->
|
||||
oldHint = @_hint
|
||||
return if newHint is oldHint
|
||||
|
||||
@_hint = newHint.toLowerCase()
|
||||
|
||||
@trigger Event.change + ':hint', this, oldHint, newHint
|
||||
@_refreshResults()
|
||||
@trigger Event.change, this
|
||||
|
||||
getResults: ->
|
||||
return @_results
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
hint: {get:@prototype.getHint, set:@prototype.setHint}
|
||||
results: {get:@prototype.getResults}
|
||||
|
||||
hint:
|
||||
get: ->
|
||||
return @_hint
|
||||
|
||||
set: (newHint)->
|
||||
oldHint = @_hint
|
||||
return if newHint is oldHint
|
||||
|
||||
@_hint = newHint.toLowerCase()
|
||||
|
||||
@trigger c.event.change + ':hint', this, oldHint, newHint
|
||||
@_refreshResults()
|
||||
@trigger c.event.change, this
|
||||
|
||||
results:
|
||||
get: ->
|
||||
return @_results
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
@@ -87,4 +86,4 @@ module.exports = class ItemSelector extends BaseModel
|
||||
newResults.sort ItemSlug.compare
|
||||
|
||||
@_results = newResults
|
||||
@trigger Event.change + ':results', this, oldResults, newResults
|
||||
@trigger c.event.change + ':results', this, oldResults, newResults
|
||||
+6
-7
@@ -1,11 +1,10 @@
|
||||
###
|
||||
Crafting Guide - markdown_image.coffee
|
||||
#
|
||||
# Crafting Guide - markdown_image.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
EditableFile = require './editable_file'
|
||||
|
||||
########################################################################################################################
|
||||
+14
-15
@@ -1,12 +1,11 @@
|
||||
###
|
||||
Crafting Guide - markdown_image_list.coffee
|
||||
#
|
||||
# Crafting Guide - markdown_image_list.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
{Event} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
MarkdownImage = require './markdown_image'
|
||||
|
||||
########################################################################################################################
|
||||
@@ -21,11 +20,11 @@ module.exports = class MarkdownImageList extends BaseModel
|
||||
@client = options.client
|
||||
@_images = {}
|
||||
|
||||
@on Event.change + ':imageBase', =>
|
||||
@on c.event.change + ':imageBase', =>
|
||||
for fileName, image of @_images
|
||||
image.path = @imageBase
|
||||
|
||||
@on Event.change + ':markdownText', => @_analyzeMarkdownText()
|
||||
@on c.event.change + ':markdownText', => @_analyzeMarkdownText()
|
||||
@_analyzeMarkdownText()
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
@@ -35,7 +34,7 @@ module.exports = class MarkdownImageList extends BaseModel
|
||||
|
||||
fetchImages: ->
|
||||
for fileName, image of @_images
|
||||
if image.status is MarkdownImage.Status.unknown
|
||||
if image.status is MarkdownImage::Status.unknown
|
||||
image.fetch()
|
||||
|
||||
reset: ->
|
||||
@@ -80,17 +79,17 @@ module.exports = class MarkdownImageList extends BaseModel
|
||||
image = @_images[fileName]
|
||||
if not image?
|
||||
image = new MarkdownImage {fileName:fileName, path:@imageBase}, {client:@client}
|
||||
@listenTo image, Event.change, => @trigger Event.change, this
|
||||
@trigger Event.add, this, image
|
||||
@listenTo image, c.event.change, => @trigger c.event.change, this
|
||||
@trigger c.event.add, this, image
|
||||
changed = true
|
||||
|
||||
newImages[fileName] = image
|
||||
|
||||
for fileName, image of @_images
|
||||
if not newImages[fileName]?
|
||||
@trigger Event.remove, this, image
|
||||
@trigger c.event.remove, this, image
|
||||
changed = true
|
||||
|
||||
if changed
|
||||
@_images = newImages
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change, this
|
||||
@@ -0,0 +1,65 @@
|
||||
#
|
||||
# Crafting Guide - markdown_image_list.test.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
MarkdownImageList = require './markdown_image_list'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
list = null
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
describe 'markdown_image_list.coffee', ->
|
||||
|
||||
beforeEach ->
|
||||
list = new MarkdownImageList {}, client:{fetchFile:sinon.stub().returns(w.promise(->))}
|
||||
|
||||
describe '_analyzeMarkdownText', ->
|
||||
|
||||
it 'finds nothing in a null string', ->
|
||||
list.all.should.eql []
|
||||
|
||||
it 'finds nothing in an empty string', ->
|
||||
list.markdownText = ''
|
||||
list.all.should.eql []
|
||||
|
||||
it 'finds nothing when there are no images in the markdown', ->
|
||||
list.markdownText = 'alpha bravo charlie delta'
|
||||
list.all.should.eql []
|
||||
|
||||
it 'finds a single image alone', ->
|
||||
list.markdownText = ''
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
|
||||
|
||||
it 'finds a single image inside a markdown document', ->
|
||||
list.markdownText = '# alpha\n bravo charlie\n\n|  |\n|:----------:|\ndelta'
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
|
||||
|
||||
it 'finds multiple images inside a markdown document', ->
|
||||
list.markdownText = '| Images |\n|:------:|\n|  |\n|  |\n|  |\n'
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png', 'charlie.png']
|
||||
|
||||
it 'adds new images when markdown changes', ->
|
||||
list.markdownText = ''
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
|
||||
|
||||
list.markdownText = ', '
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png']
|
||||
|
||||
it 'removes missing images when markdown changes', ->
|
||||
list.markdownText = ', '
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png', 'bravo.png']
|
||||
|
||||
list.markdownText = ''
|
||||
(i.fileName for i in list.all).sort().should.eql ['alpha.png']
|
||||
|
||||
it 'does not change remaining images when still present in changed markdown', ->
|
||||
list.markdownText = ''
|
||||
list.all[0].testField = 'foo'
|
||||
|
||||
list.markdownText = ' bravo'
|
||||
list.all[0].testField.should.equal 'foo'
|
||||
@@ -1,14 +1,11 @@
|
||||
###
|
||||
Crafting Guide - tutorial.coffee
|
||||
#
|
||||
# Crafting Guide - tutorial.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseModel = require './base_model'
|
||||
TutorialParser = require './tutorial_parser'
|
||||
_ = require 'underscore'
|
||||
{Url} = require '../constants'
|
||||
BaseModel = require '../base_model'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -26,11 +23,11 @@ module.exports = class Tutorial extends BaseModel
|
||||
# Backbone.Model Overrides #####################################################################
|
||||
|
||||
parse: (text)->
|
||||
TutorialParser = require './tutorial_parser' # to avoid require cycles
|
||||
TutorialParser = require '../parsing/tutorial_parser' # to avoid require cycles
|
||||
@_parser ?= new TutorialParser model:this
|
||||
@_parser.parse text
|
||||
|
||||
return null # prevent calling `set`
|
||||
|
||||
url: ->
|
||||
return Url.tutorialData modSlug:@modSlug, tutorialSlug:@slug
|
||||
return c.url.tutorialData modSlug:@modSlug, tutorialSlug:@slug
|
||||
@@ -0,0 +1,162 @@
|
||||
#
|
||||
# Crafting Guide - base_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
templates = require './templates'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class BaseController extends Backbone.View
|
||||
|
||||
constructor: (options={})->
|
||||
@_children = []
|
||||
@_model = null
|
||||
@_parent = options.parent
|
||||
@_rendered = false
|
||||
|
||||
@router = options.router
|
||||
|
||||
@tryRefresh = _.debounce (=> @_tryRefresh()), 100
|
||||
|
||||
@_loadTemplate options.templateName
|
||||
super options
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
addChild: (Controller, atSelector, options={})->
|
||||
if not _.isFunction Controller then throw new Error "Controller must be a constructor"
|
||||
|
||||
options.el = @$(atSelector)[0]
|
||||
options.parent = this
|
||||
|
||||
child = new Controller options
|
||||
child.render options
|
||||
@_children.push child
|
||||
return child
|
||||
|
||||
hide: ($el)->
|
||||
$el ?= @$el
|
||||
$el.css 'display', 'none'
|
||||
|
||||
refresh: ->
|
||||
logger.verbose => "#{this} refreshing"
|
||||
|
||||
remove: ->
|
||||
@$el.remove()
|
||||
@off()
|
||||
|
||||
routeLinkClick: (event)->
|
||||
event.preventDefault()
|
||||
href = $(event.target).attr 'href'
|
||||
href ?= $(event.currentTarget).attr 'href'
|
||||
logger.info "Re-routing link to internal navigation: #{href}"
|
||||
|
||||
if href? and href.match /^http/
|
||||
window.location.href = href
|
||||
else
|
||||
@router.navigate href, trigger:true
|
||||
|
||||
show: ($el)->
|
||||
$el ?= @$el
|
||||
$el.css 'display', ''
|
||||
|
||||
unrender: ->
|
||||
@undelegateEvents()
|
||||
@$el.empty()
|
||||
@_rendered = false
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onDidModelChange: ->
|
||||
@tryRefresh()
|
||||
|
||||
onDidModelSync: ->
|
||||
@tryRefresh()
|
||||
|
||||
onWillRender: -> # do nothing
|
||||
|
||||
onDidRender: -> # do nothing
|
||||
|
||||
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 newModel
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
model:
|
||||
get: -> @_model
|
||||
set: (newModel)->
|
||||
return if @model is newModel
|
||||
|
||||
newModel = @onWillChangeModel @_model, newModel
|
||||
@_model = newModel
|
||||
@tryRefresh()
|
||||
|
||||
rendered:
|
||||
get: -> @_rendered
|
||||
|
||||
user:
|
||||
get: -> @_user
|
||||
set: (newUser)->
|
||||
@_user = newUser
|
||||
@tryRefresh()
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return {}
|
||||
|
||||
render: (options={})->
|
||||
options.force ?= false
|
||||
options.show ?= true
|
||||
return this unless not @_rendered or options.force
|
||||
|
||||
if not @_template?
|
||||
logger.error => "Default render called for #{@constructor.name} without a template"
|
||||
return this
|
||||
|
||||
data = (@model?.toHash? and @model.toHash()) or @model or {}
|
||||
logger.verbose => "#{this} rendering with data: #{data}"
|
||||
$renderedEl = $($(@_template(data))[0])
|
||||
|
||||
@onWillRender()
|
||||
@unrender()
|
||||
|
||||
@$el.append $renderedEl.children()
|
||||
@$el.addClass $renderedEl.attr 'class'
|
||||
@$el.data $renderedEl.data()
|
||||
@delegateEvents()
|
||||
|
||||
@_rendered = true
|
||||
@onDidRender()
|
||||
|
||||
@refresh()
|
||||
|
||||
return this
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
return "#{@constructor.name}.#{@cid}"
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_loadTemplate: (templateName)->
|
||||
if templateName?
|
||||
@_template = templates[templateName]
|
||||
|
||||
_tryRefresh: ->
|
||||
return unless @_rendered
|
||||
@refresh()
|
||||
@@ -0,0 +1,13 @@
|
||||
//-
|
||||
//- Crafting Guide - browse_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__browse_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.tile_container
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Crafting Guide - browse_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__browse_page {
|
||||
@include layout-body-with-sidebar;
|
||||
|
||||
.right {
|
||||
.tile_container {
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-between;
|
||||
|
||||
.view__mod_tile {
|
||||
max-width : calc(50% - 12px); // $size-margin-large / 2
|
||||
margin-bottom : $size-margin-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Crafting Guide - browse_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
AdsenseController = require '../common/adsense/adsense_controller'
|
||||
ModTileController = require './mod_tile/mod_tile_controller'
|
||||
PageController = require '../page_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
|
||||
@_tileControllers = []
|
||||
|
||||
# PageController Overrides #####################################################################
|
||||
|
||||
getTitle: ->
|
||||
return "Browse"
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
|
||||
|
||||
@$tileContainer = @$('.tile_container')
|
||||
|
||||
refresh: ->
|
||||
@_refreshModTiles()
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshModTiles: ->
|
||||
index = 0
|
||||
mods = @_modPack.getAllMods()
|
||||
for mod in mods
|
||||
controller = @_tileControllers[index]
|
||||
if not controller
|
||||
controller = new ModTileController model:mod, router:@router
|
||||
controller.render()
|
||||
@$tileContainer.append controller.$el
|
||||
@_tileControllers.push controller
|
||||
else
|
||||
controller.model = mod
|
||||
index++
|
||||
|
||||
while @_tileControllers.length > mods.length
|
||||
controller = @_tileControllers.pop()
|
||||
controller.remove()
|
||||
@@ -0,0 +1,14 @@
|
||||
//-
|
||||
//- Crafting Guide - mod_tile.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__mod_tile
|
||||
a
|
||||
.left
|
||||
img
|
||||
.right
|
||||
p.title
|
||||
p.description
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Crafting Guide - mod_tile.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__mod_tile {
|
||||
min-width : $size-minecraft-block * 9;
|
||||
|
||||
display : flex;
|
||||
justify-content : stretch;
|
||||
align-items : stretch;
|
||||
|
||||
a {
|
||||
max-width: 100%;
|
||||
|
||||
@include floating-panel;
|
||||
border : $size-border-medium solid $color-background-panel;
|
||||
color : $color-text;
|
||||
text-decoration : none;
|
||||
|
||||
display : flex;
|
||||
padding : $size-margin-medium;
|
||||
|
||||
& > .left {
|
||||
flex: 0 0 $size-minecraft-block * 4;
|
||||
margin-right: $size-margin-medium;
|
||||
|
||||
img {
|
||||
width: $size-minecraft-block * 4;
|
||||
max-height: $size-minecraft-block * 4;
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
flex : 1 1 100%;
|
||||
|
||||
overflow : hidden;
|
||||
|
||||
.title {
|
||||
margin-bottom : $size-margin-medium;
|
||||
|
||||
font-family : $font-family-header;
|
||||
font-size : $font-size-xlarge;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $font-size-medium * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $color-blue-faint;
|
||||
border-color: $color-link-normal;
|
||||
|
||||
& > .right {
|
||||
.title {
|
||||
color: $color-link-normal;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
a {
|
||||
& > .left {
|
||||
img {
|
||||
@include grayscale;
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
.title {
|
||||
color: $color-gray-light;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $color-gray-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#
|
||||
# Crafting Guide - mod_tile_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ModTileController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'browse_page/mod_tile'
|
||||
super options
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$link = @$('a')
|
||||
@$logoImage = @$('img')
|
||||
@$title = @$('p.title')
|
||||
@$description = @$('p.description')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$link.attr 'href', c.url.mod modSlug:@model.slug
|
||||
@$logoImage.attr 'src', c.url.modIcon modSlug:@model.slug
|
||||
@$title.text @model.name
|
||||
@$description.text @model.description
|
||||
|
||||
if @model.enabled
|
||||
@$el.removeClass 'disabled'
|
||||
else
|
||||
@$el.addClass 'disabled'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
_.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
@@ -0,0 +1,8 @@
|
||||
//-
|
||||
//- Crafting Guide - adsense.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__adsense
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Crafting Guide - adsense.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__adsense {
|
||||
.skyscraper {
|
||||
height : $size-skyscraper-height;
|
||||
width : $size-skyscraper-width;
|
||||
position : relative;
|
||||
|
||||
border : 0.1em solid $color-gray-faint;
|
||||
margin : $size-margin-large 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top : 0;
|
||||
}
|
||||
|
||||
ins {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-28
@@ -1,46 +1,39 @@
|
||||
###
|
||||
Crafting Guide - adsense_controller.coffee
|
||||
#
|
||||
# Crafting Guide - adsense_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
$ = require 'jquery'
|
||||
BaseController = require './base_controller'
|
||||
_ = require 'underscore'
|
||||
{Adsense} = require '../constants'
|
||||
{Duration} = require '../constants'
|
||||
{ProductionEnvs} = require '../constants'
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class AdsenseController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = "adsense_sidebar_skyscraper"
|
||||
options.templateName = 'common/adsense'
|
||||
super options
|
||||
|
||||
@_adsEnabled = global.env in ProductionEnvs
|
||||
@_adHeight = 630
|
||||
@_adCount = 0
|
||||
@_waiting = false
|
||||
@_adsEnabled = global.env in c.productionEnvs
|
||||
@_adCount = 0
|
||||
@_waiting = false
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
fillAdPositions: ->
|
||||
if @_adCount > 0
|
||||
return unless @_adCount < Adsense.slotIds.length
|
||||
return unless @$sidebar? and @$mainBody?
|
||||
return unless @$sidebar.height() + @_adHeight < @$mainBody.height()
|
||||
return unless @$sidebar and @$mainBody
|
||||
return unless @_adCount < @_computeMaxAds()
|
||||
return unless @_adCount < c.adsense.slotIds.length
|
||||
|
||||
$adContainer = $('<div class="sidebar_skyscraper"></div>')
|
||||
$adContainer = $('<div class="skyscraper"></div>')
|
||||
@$el.append $adContainer
|
||||
|
||||
if @_adsEnabled
|
||||
$ad = $('<ins class="adsbygoogle"></ins>')
|
||||
$ad.attr 'data-ad-client', Adsense.clientId
|
||||
$ad.attr 'data-ad-slot', Adsense.slotIds[@_adCount]
|
||||
$ad.css 'height':'600px', 'width':'160px'
|
||||
$ad.attr 'data-ad-client', c.adsense.clientId
|
||||
$ad.attr 'data-ad-slot', c.adsense.slotIds[@_adCount]
|
||||
$adContainer.append $ad
|
||||
@_loadAds()
|
||||
|
||||
@@ -50,8 +43,8 @@ module.exports = class AdsenseController extends BaseController
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$mainBody = $('.mainBody')
|
||||
@$sidebar = $('.sidebar')
|
||||
@$mainBody = $('.page > .content > .right')
|
||||
@$sidebar = $('.page > .content > .left')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@@ -60,6 +53,11 @@ module.exports = class AdsenseController extends BaseController
|
||||
|
||||
# Private Methods #############################################################################
|
||||
|
||||
_computeMaxAds: ->
|
||||
return null unless @$sidebar
|
||||
result = Math.floor @$sidebar.height() / (c.adsense.skyscraper.height + c.adsense.skyscraper.margin)
|
||||
return result
|
||||
|
||||
_loadAds: ->
|
||||
return unless @_adsEnabled
|
||||
|
||||
@@ -73,8 +71,10 @@ module.exports = class AdsenseController extends BaseController
|
||||
_waitForPageReadiness: ->
|
||||
return if @_waiting
|
||||
|
||||
if @$mainBody.height() < @_adHeight / 2
|
||||
if @$sidebar.height() < c.adsense[@model].height / 2
|
||||
logger.verbose "Adsense is waiting for room to insert ads"
|
||||
@_waiting = true
|
||||
_.delay (=> @_waiting = false; @_waitForPageReadiness()), Duration.normal
|
||||
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.duration.normal
|
||||
else
|
||||
logger.verbose "Adsense is ready to fill ads"
|
||||
@fillAdPositions()
|
||||
@@ -0,0 +1,20 @@
|
||||
//-
|
||||
//- Crafting Guide - crafting_grid.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__crafting_grid
|
||||
.row
|
||||
.view__slot
|
||||
.view__slot
|
||||
.view__slot
|
||||
.row
|
||||
.view__slot
|
||||
.view__slot
|
||||
.view__slot
|
||||
.row
|
||||
.view__slot
|
||||
.view__slot
|
||||
.view__slot
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Crafting Guide - crafting_grid.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__crafting_grid {
|
||||
background : $color-gray-light;
|
||||
border : $size-border-medium solid $color-gray-medium;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.row {
|
||||
flex: 1 1 auto;
|
||||
|
||||
display: flex;
|
||||
|
||||
.view__slot {
|
||||
flex: 1 1 auto;
|
||||
|
||||
background : $color-white;
|
||||
margin : $size-border-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Crafting Guide - crafting_grid_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
SlotController = require '../slot/slot_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
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'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
# options.model should be a recipe
|
||||
options.templateName = 'common/crafting_grid'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
@_modPack.on c.event.change, => @tryRefresh()
|
||||
|
||||
# BaseController Methods #######################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_slotControllers = []
|
||||
for el in @$('.view__slot')
|
||||
controller = new SlotController el:el, imageLoader:@_imageLoader, modPack:@_modPack, router:@_router
|
||||
controller.render()
|
||||
@_slotControllers.push controller
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
for controller, index in @_slotControllers
|
||||
controller.model = @model?.getStackAtSlot(index)
|
||||
|
||||
super
|
||||
@@ -0,0 +1,13 @@
|
||||
//-
|
||||
//- Crafting Guide - inventory.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__inventory
|
||||
.item_container
|
||||
.empty_placeholder nothing
|
||||
.buttons
|
||||
.button.item-selector: .bezel: p add an item...
|
||||
.button.clear: .bezel: p clear
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Crafting Guide - inventory.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__inventory {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.empty_placeholder {
|
||||
color : $color-gray-light;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
margin : $size-margin-large 0;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.item_container {
|
||||
flex: 0 1 100%;
|
||||
|
||||
display: table;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Editable Variant ////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.view__inventory.editable {
|
||||
.buttons {
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
.item-selector {
|
||||
flex : 85%;
|
||||
margin-right : $size-margin-medium;
|
||||
}
|
||||
|
||||
.clear {
|
||||
flex : 15%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Large variant ///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.view__inventory.large {
|
||||
.view__stack {
|
||||
& > * {
|
||||
&:not(:last-child) {
|
||||
padding-bottom : $size-margin-small;
|
||||
}
|
||||
}
|
||||
|
||||
.quantity {
|
||||
padding-right : $size-margin-medium;
|
||||
input {
|
||||
width: 3em;
|
||||
font-size : $font-size-xlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding-right : $size-margin-medium;
|
||||
|
||||
img {
|
||||
height : $size-minecraft-block * 1.5;
|
||||
width : $size-minecraft-block * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
a {
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-xlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
padding-left : $size-margin-small;
|
||||
|
||||
.button {
|
||||
width : $size-button-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
#
|
||||
# Crafting Guide - inventory_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
ItemSelectorController = require '../item_selector/item_selector_controller'
|
||||
StackController = require '../stack/stack_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
###
|
||||
Events:
|
||||
clear(this)
|
||||
change(this)
|
||||
add(this, itemSlug)
|
||||
button:first(this, itemSlug)
|
||||
button:second(this, itemSlug)
|
||||
###
|
||||
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.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
@_editable = options.editable ?= true
|
||||
@_icon = options.icon ?= '/images/chest_front.png'
|
||||
@_firstButtonType = options.firstButtonType ?= null
|
||||
@_isAcceptable = options.isAcceptable ?= null
|
||||
@_secondButtonType = options.secondButtonType ?= null
|
||||
@_shouldEnableButton = options.shouldEnableButton ?= (model, button)-> true
|
||||
|
||||
options.templateName = 'common/inventory'
|
||||
super options
|
||||
|
||||
@_stackControllers = []
|
||||
|
||||
@listenTo @_modPack, c.event.change, => @tryRefresh()
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onClearButtonClicked: ->
|
||||
return unless @model?
|
||||
return if @$clearButton.hasClass 'disabled'
|
||||
|
||||
@model.clear()
|
||||
@trigger c.event.clear, this
|
||||
@trigger c.event.change, this
|
||||
|
||||
onFirstButtonClicked: (stackController)->
|
||||
@trigger c.event.button.first, this, stackController?.model?.itemSlug
|
||||
|
||||
onItemSelectorButtonClicked: ->
|
||||
return unless @model?
|
||||
|
||||
@_selector.launch()
|
||||
.then (itemSlug)=>
|
||||
return unless itemSlug?
|
||||
|
||||
@model.add itemSlug, 1
|
||||
@trigger c.event.add, this, itemSlug
|
||||
@trigger c.event.change, this
|
||||
|
||||
onSecondButtonClicked: (stackController)->
|
||||
@trigger c.event.button.second, this, stackController?.model?.itemSlug
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_selector = @addChild ItemSelectorController, null, isAcceptable: @_isAcceptable, modPack: @_modPack
|
||||
|
||||
@$clearButton = @$('.button.clear')
|
||||
@$emptyPlaceholder = @$('.empty_placeholder')
|
||||
@$icon = @$('.icon')
|
||||
@$itemContainer = @$('.item_container')
|
||||
@$title = @$('h2 p')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
if @_editable
|
||||
@_selector.show()
|
||||
@show @$clearButton
|
||||
else
|
||||
@_selector.hide()
|
||||
@hide @$clearButton
|
||||
|
||||
@$icon.attr 'src', @_icon
|
||||
|
||||
@_refreshStacks()
|
||||
|
||||
if not @model or @model.isEmpty
|
||||
@show @$emptyPlaceholder
|
||||
@$clearButton.addClass 'disabled'
|
||||
else
|
||||
@hide @$emptyPlaceholder
|
||||
@$clearButton.removeClass 'disabled'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .button.item-selector': 'onItemSelectorButtonClicked'
|
||||
'click .button.clear': 'onClearButtonClicked'
|
||||
'click button.first': 'onFirstButtonClicked'
|
||||
'click button.second': 'onSecondButtonClicked'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshStacks: ->
|
||||
@_stackControllers ?= []
|
||||
index = 0
|
||||
|
||||
if @model?
|
||||
@model.each (stack)=>
|
||||
controller = @_stackControllers[index]
|
||||
if not controller?
|
||||
controller = new StackController
|
||||
editable: @_editable
|
||||
firstButtonType: @_firstButtonType
|
||||
imageLoader: @_imageLoader
|
||||
model: stack
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
secondButtonType: @_secondButtonType
|
||||
shouldEnableButton: @_shouldEnableButton
|
||||
controller.on c.event.change, => @trigger c.event.change, this
|
||||
controller.on c.event.button.first, (c)=> @onFirstButtonClicked(c)
|
||||
controller.on c.event.button.second, (c)=> @onSecondButtonClicked(c)
|
||||
controller.render()
|
||||
|
||||
@_stackControllers.push controller
|
||||
@$itemContainer.append controller.$el
|
||||
else
|
||||
controller.model = stack
|
||||
index += 1
|
||||
|
||||
while @_stackControllers.length > index
|
||||
@_stackControllers.pop().remove()
|
||||
+1
-1
@@ -5,6 +5,6 @@
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_group.section.hideable.hidden
|
||||
section.view__item_group
|
||||
h2
|
||||
.panel
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Crafting Guide - item_group.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_group {
|
||||
.panel {
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-between;
|
||||
|
||||
.view__item_tile {
|
||||
margin-bottom: $size-margin-medium;
|
||||
width: calc(33% - 8px); // $size-margin-xsmall * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
-24
@@ -1,15 +1,12 @@
|
||||
###
|
||||
Crafting Guide - item_group_controller.coffee
|
||||
#
|
||||
# Crafting Guide - item_group_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
Item = require '../models/item'
|
||||
ItemController = require './item_controller'
|
||||
{Duration} = require '../constants'
|
||||
{Event} = require '../constants'
|
||||
BaseController = require '../../base_controller'
|
||||
ItemController = require './item_tile/item_tile_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -18,26 +15,28 @@ 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'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
options.model ?= []
|
||||
options.templateName = 'common/item_group'
|
||||
options.title ?= ''
|
||||
options.templateName = 'item_group'
|
||||
options.tagName = 'section'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_title = options.title
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
@_title = options.title
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getTitle: ->
|
||||
return @_title
|
||||
|
||||
setTitle: (title)->
|
||||
@_title = title
|
||||
@tryRefresh()
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
title: {get:@prototype.getTitle, set:@prototype.setTitle}
|
||||
|
||||
title:
|
||||
get: -> @_title
|
||||
|
||||
set: (newTitle)->
|
||||
@_title = newTitle
|
||||
@tryRefresh()
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
@@ -69,7 +68,11 @@ module.exports = class ItemGroupController extends BaseController
|
||||
for item in @model
|
||||
controller = @_itemControllers[controllerIndex]
|
||||
if not controller?
|
||||
controller = new ItemController imageLoader:@_imageLoader, model:item, modPack:@_modPack
|
||||
controller = new ItemController
|
||||
imageLoader: @_imageLoader
|
||||
model: item
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
@_itemControllers.push controller
|
||||
@$items.append controller.$el
|
||||
controller.render()
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - item_tile.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_tile
|
||||
a
|
||||
img
|
||||
p.itemName
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Crafting Guide - item_tile.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_tile {
|
||||
a {
|
||||
overflow: visible;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block;
|
||||
width: $size-minecraft-block;
|
||||
margin-right: $size-margin-small;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
line-height: $size-minecraft-block;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-14
@@ -1,26 +1,25 @@
|
||||
###
|
||||
Crafting Guide - item_controller.coffee
|
||||
#
|
||||
# Crafting Guide - item_group_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
_ = require 'underscore'
|
||||
BaseController = require '../../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ItemController extends BaseController
|
||||
module.exports = class ItemTileController 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'
|
||||
options.templateName = 'common/item_group/item_tile'
|
||||
super options
|
||||
|
||||
@imageLoader = options.imageLoader
|
||||
@modPack = options.modPack
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
@@ -31,9 +30,9 @@ module.exports = class ItemController extends BaseController
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
display = @modPack.findItemDisplay @model.slug
|
||||
display = @_modPack.findItemDisplay @model.slug
|
||||
|
||||
@imageLoader.load display.iconUrl, @$icon
|
||||
@_imageLoader.load display.iconUrl, @$icon
|
||||
@$name.html display.itemName
|
||||
@$nameLink.attr 'href', display.itemUrl
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - element.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__element
|
||||
img
|
||||
.name
|
||||
.modName
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Crafting Guide - element.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__element {
|
||||
display : flex;
|
||||
align-items: center;
|
||||
|
||||
background: $color-white;
|
||||
padding: $size-margin-xsmall $size-margin-medium;
|
||||
|
||||
& > * {
|
||||
margin-right: $size-margin-medium;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
flex : 0 0 auto;
|
||||
|
||||
height : $size-minecraft-block;
|
||||
width : $size-minecraft-block;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex : 1 1 100%;
|
||||
|
||||
color : $color-text;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
}
|
||||
|
||||
.modName {
|
||||
flex : 0 0 auto;
|
||||
|
||||
color : $color-text-secondary;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-small;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background : $color-gray-faint;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
flex : 0 0 auto;
|
||||
background : $color-active-hover;
|
||||
|
||||
.name {
|
||||
color : $color-text-light;
|
||||
}
|
||||
|
||||
.modName {
|
||||
color : $color-text-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-16
@@ -1,23 +1,21 @@
|
||||
###
|
||||
Crafting Guide - item_selector_element_controller.coffee
|
||||
#
|
||||
# Crafting Guide - element.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
_ = require 'underscore'
|
||||
{Event} = require '../constants'
|
||||
BaseController = require '../../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ItemSelectorElementController extends BaseController
|
||||
module.exports = class ElementController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.onClicked ?= (controller)-> # do nothing
|
||||
options.onSelected ?= (controller)-> # do nothing
|
||||
options.templateName = 'item_selector_element'
|
||||
options.templateName = 'common/item_selector/element'
|
||||
options.useAnimations = false
|
||||
super options
|
||||
|
||||
@@ -36,8 +34,8 @@ module.exports = class ItemSelectorElementController extends BaseController
|
||||
@_selected = newSelected
|
||||
@tryRefresh()
|
||||
|
||||
@trigger Event.change + ':selected', this, oldSelected, newSelected
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change + ':selected', this, oldSelected, newSelected
|
||||
@trigger c.event.change, this
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
selected: {get:@prototype.isSelected, set:@prototype.setSelected}
|
||||
@@ -46,8 +44,8 @@ module.exports = class ItemSelectorElementController extends BaseController
|
||||
|
||||
onDidRender: ->
|
||||
@$icon = @$('img')
|
||||
@$name = @$('.name p')
|
||||
@$modName = @$('.modName p')
|
||||
@$name = @$('.name')
|
||||
@$modName = @$('.modName')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@@ -77,4 +75,4 @@ module.exports = class ItemSelectorElementController extends BaseController
|
||||
|
||||
_onMouseEnter: (event)->
|
||||
event.preventDefault()
|
||||
@onSelected this
|
||||
@onSelected this
|
||||
@@ -0,0 +1,14 @@
|
||||
//-
|
||||
//- Crafting Guide - item_selector.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_selector
|
||||
.view__item_selector_popup
|
||||
.search
|
||||
img(src='/images/search.png')
|
||||
input(placeholder='enter the name of an item')
|
||||
img.close(src='/images/close.png')
|
||||
.results
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Crafting Guide - item_selector.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_selector {
|
||||
}
|
||||
|
||||
.view__item_selector_popup {
|
||||
height : 66%;
|
||||
|
||||
background : $color-background-panel;
|
||||
border : 1px solid $color-gray-medium;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.search {
|
||||
flex: 0 0 auto;
|
||||
|
||||
border-bottom: 1px solid $color-gray-medium;
|
||||
|
||||
display: flex;
|
||||
padding: $size-margin-small;
|
||||
|
||||
& > :not(:first-child) {
|
||||
margin-left: $size-margin-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1 1 100%;
|
||||
|
||||
background: none;
|
||||
border: 0;
|
||||
font-family: $font-family-input;
|
||||
font-size: $font-size-large;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
img.close {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.results {
|
||||
flex: 1 1 100%;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
+70
-70
@@ -1,18 +1,13 @@
|
||||
###
|
||||
Crafting Guide - item_selector_controller.coffee
|
||||
#
|
||||
# Crafting Guide - item_selector_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
BaseController = require './base_controller'
|
||||
{Duration} = require '../constants'
|
||||
{Event} = require '../constants'
|
||||
ItemSelector = require '../models/item_selector'
|
||||
ItemSelectorElementController = require './item_selector_element_controller'
|
||||
{Key} = require '../constants'
|
||||
BaseController = require '../../base_controller'
|
||||
ItemSelector = require '../../../models/site/item_selector'
|
||||
ElementController = require './element/element_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -23,28 +18,63 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
options.isAcceptable ?= null
|
||||
options.model ?= new ItemSelector {}, modPack:options.modPack, isAcceptable:options.isAcceptable
|
||||
options.onChoseItem ?= (item)-> # do nothing
|
||||
options.templateName = 'item_selector'
|
||||
options.templateName = 'common/item_selector'
|
||||
super options
|
||||
|
||||
@modPack = options.modPack
|
||||
@onChoseItem = options.onChoseItem
|
||||
@_modPack = options.modPack
|
||||
@_session = null
|
||||
|
||||
@_elementControllers = []
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
launch: (hint='')->
|
||||
if @_session
|
||||
@_session.resolve null
|
||||
@_session = null
|
||||
|
||||
@model.hint = hint
|
||||
if @rendered then @refresh()
|
||||
|
||||
@_disableWindowScrolling()
|
||||
|
||||
@$page.addClass 'blur'
|
||||
@$screen.append @$popup
|
||||
@$screen.css 'display', ''
|
||||
|
||||
@$popup.off c.event.click
|
||||
@$popup.on c.event.click, (event)=> @onPopupClicked(event)
|
||||
|
||||
@$hintField.off 'keyup input'
|
||||
@$hintField.on 'keyup', (event)=> @onHintKeyPress(event)
|
||||
@$hintField.on 'input', (event)=> @onHintChanged(event)
|
||||
@$hintField.focus()
|
||||
|
||||
@$closeButton.one 'click', (event)=> @_close()
|
||||
|
||||
@$screen.one c.event.click, (event)=> @onScreenClicked(event)
|
||||
|
||||
@_session = w.defer()
|
||||
return @_session.promise
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onClose: (event)->
|
||||
@_close()
|
||||
return false
|
||||
|
||||
onHintKeyPress: (event)->
|
||||
switch event.which
|
||||
when Key.Escape
|
||||
when c.key.escape
|
||||
@_close()
|
||||
return false
|
||||
when Key.Enter
|
||||
when c.key.enter
|
||||
@_chooseSelected()
|
||||
return false
|
||||
when Key.DownArrow
|
||||
when c.key.down
|
||||
@_selectNext()
|
||||
return false
|
||||
when Key.UpArrow
|
||||
when c.key.up
|
||||
@_selectPrevious()
|
||||
return false
|
||||
|
||||
@@ -53,39 +83,13 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
@tryRefresh()
|
||||
@onResultSelected @_elementControllers[0]
|
||||
|
||||
onLaunchButtonClicked: (event)->
|
||||
event.preventDefault()
|
||||
@model.hint = ''
|
||||
@refresh()
|
||||
|
||||
$('body').addClass 'stop-scrolling'
|
||||
|
||||
@$screen.append @$popup
|
||||
@$screen.css 'display', 'block'
|
||||
|
||||
@$popup.offset @$el.offset()
|
||||
@$popup.css 'width', @$el.css 'width'
|
||||
@$popup.off Event.click
|
||||
@$popup.on Event.click, (event)=> @onPopupClicked(event)
|
||||
@$popup.removeClass 'hiding'
|
||||
|
||||
@$hintField.off 'keyup input'
|
||||
@$hintField.on 'keyup', (event)=> @onHintKeyPress(event)
|
||||
@$hintField.on 'input', (event)=> @onHintChanged(event)
|
||||
|
||||
@$popup.off Event.transitionEnd
|
||||
@$popup.one Event.transitionEnd, => @onPopupDisplayed()
|
||||
@$screen.one Event.click, (event)=> @onScreenClicked(event)
|
||||
|
||||
onPopupClicked: (event)->
|
||||
return false
|
||||
|
||||
onPopupDisplayed: ->
|
||||
@$hintField.focus()
|
||||
|
||||
onResultClicked: (controller)->
|
||||
@_session.resolve controller.model.slug
|
||||
@_session = null
|
||||
@_close()
|
||||
@onChoseItem controller.model.slug
|
||||
|
||||
onResultSelected: (controller)->
|
||||
for c in @_elementControllers
|
||||
@@ -101,7 +105,10 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$page = $('.page') # find the shared, global page
|
||||
@$screen = $('.view__screen') # find the shared, global screen
|
||||
|
||||
@$closeButton = @$('img.close')
|
||||
@$popup = @$('.view__item_selector_popup')
|
||||
@$hintField = @$('.view__item_selector_popup input')
|
||||
@$searchInput = @$('.search input')
|
||||
@@ -115,17 +122,6 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
@_refreshResults()
|
||||
super
|
||||
|
||||
render: ->
|
||||
$buttonContent = @$el.html()
|
||||
super
|
||||
@$el.html $buttonContent
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click': 'onLaunchButtonClicked'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_chooseSelected: ->
|
||||
@@ -135,24 +131,28 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
return
|
||||
|
||||
_close: ->
|
||||
$('body').removeClass 'stop-scrolling'
|
||||
@_enableWindowScrolling()
|
||||
@$page.removeClass 'blur'
|
||||
@$screen.css 'display', 'none'
|
||||
@$popup.detach()
|
||||
@model.hint = ''
|
||||
|
||||
@$popup.addClass 'hiding'
|
||||
_disableWindowScrolling: ->
|
||||
@_windowScrollPosition = $(window).scrollTop()
|
||||
$('body').addClass('scrollDisabled').css('margin-top', -@_windowScrollPosition)
|
||||
|
||||
@$popup.off Event.transitionEnd
|
||||
@$popup.one Event.transitionEnd, =>
|
||||
@$screen.css 'display', 'none'
|
||||
@$popup.detach()
|
||||
@model.hint = ''
|
||||
_enableWindowScrolling: ->
|
||||
$('body').removeClass('scrollDisabled').css('margin-top', 0)
|
||||
$(window).scrollTop @_windowScrollPosition
|
||||
|
||||
_refreshResults: ->
|
||||
index = 0
|
||||
|
||||
for itemSlug in @model.results
|
||||
displayModel = @modPack.findItemDisplay itemSlug
|
||||
displayModel = @_modPack.findItemDisplay itemSlug
|
||||
controller = @_elementControllers[index]
|
||||
if not controller?
|
||||
controller = new ItemSelectorElementController
|
||||
controller = new ElementController
|
||||
model: displayModel
|
||||
onClicked: (c)=> @onResultClicked(c)
|
||||
onSelected: (c)=> @onResultSelected(c)
|
||||
@@ -192,4 +192,4 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
|
||||
_showElement: ($el)->
|
||||
top = $el.position().top + @$resultsContainer.scrollTop()
|
||||
@$resultsContainer.animate {scrollTop:top}, duration:Duration.snap
|
||||
@$resultsContainer.css 'scrollTop', top
|
||||
+4
-3
@@ -1,7 +1,7 @@
|
||||
//-
|
||||
//- Crafting Guide - markdown_image.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
.loaded
|
||||
.thumbnail: img
|
||||
.dynamic
|
||||
.button: .bezel: p
|
||||
.fileName: p
|
||||
.error: p
|
||||
button
|
||||
form: input(type="file", accept="image/gif,image/jpeg,image/png" multiple="false")
|
||||
.loading: p checking for image...
|
||||
.loading
|
||||
p checking for image...
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Crafting Guide - markdown_image.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__markdown_image {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.loaded {
|
||||
display: flex;
|
||||
|
||||
.thumbnail {
|
||||
flex: 0 0 auto;
|
||||
height: $size-minecraft-block * 4;
|
||||
width: $size-minecraft-block * 4;
|
||||
margin-right: $size-margin-medium;
|
||||
|
||||
background: $color-gray-faint;
|
||||
border: 1px solid $color-gray-light;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: $size-minecraft-block * 3;
|
||||
max-height: $size-minecraft-block * 3;
|
||||
}
|
||||
}
|
||||
|
||||
.dynamic {
|
||||
flex: 1 1 100%;
|
||||
|
||||
& > :not(:last-child) {
|
||||
margin-bottom: $size-margin-medium;
|
||||
}
|
||||
|
||||
.fileName {
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: $color-error;
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
height: $size-minecraft-block * 4;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
color: $color-gray-light;
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-28
@@ -5,23 +5,20 @@ Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
$ = require 'jquery'
|
||||
_ = require 'underscore'
|
||||
BaseController = require './base_controller'
|
||||
{Duration} = require '../constants'
|
||||
MarkdownImage = require '../models/markdown_image'
|
||||
BaseController = require '../../../../base_controller'
|
||||
MarkdownImage = require '../../../../../models/site/markdown_image'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MarkdownImageController extends BaseController
|
||||
|
||||
MAX_FILE_SIZE = 750 * 1024
|
||||
MAX_HEIGHT = 600
|
||||
MAX_WIDTH = 740
|
||||
@::MAX_FILE_SIZE = 750 * 1024
|
||||
@::MAX_HEIGHT = 600
|
||||
@::MAX_WIDTH = 740
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.templateName = 'markdown_image'
|
||||
options.templateName = 'common/markdown_section/markdown_image_list/markdown_image'
|
||||
super
|
||||
|
||||
@_errorMessage = null
|
||||
@@ -37,9 +34,9 @@ module.exports = class MarkdownImageController extends BaseController
|
||||
file = @$input.prop('files')[0]
|
||||
return unless file?
|
||||
|
||||
if file.size > MAX_FILE_SIZE
|
||||
if file.size > @MAX_FILE_SIZE
|
||||
logger.warning "The choosen file, #{file.name}, is too large: #{file.size}"
|
||||
@errorMessage = "Choose a file less than #{MAX_FILE_SIZE / 1024}kB"
|
||||
@errorMessage = "Choose a file less than #{@MAX_FILE_SIZE / 1024}kB"
|
||||
@_clearFile()
|
||||
return
|
||||
|
||||
@@ -55,15 +52,15 @@ module.exports = class MarkdownImageController extends BaseController
|
||||
|
||||
$image = $("<img src=\"#{reader.result}\">")
|
||||
$image.load =>
|
||||
if $image[0].naturalHeight > MAX_HEIGHT
|
||||
if $image[0].naturalHeight > @MAX_HEIGHT
|
||||
logger.warning "The choosen file, #{file.name}, is too tall: #{$image[0].naturalHeight}"
|
||||
@errorMessage = "Choose an image less than #{MAX_HEIGHT}px tall"
|
||||
@errorMessage = "Choose an image less than #{@MAX_HEIGHT}px tall"
|
||||
@_clearFile()
|
||||
return
|
||||
|
||||
if $image[0].naturalWidth > MAX_WIDTH
|
||||
if $image[0].naturalWidth > @MAX_WIDTH
|
||||
logger.warning "The choosen file, #{file.name}, is too wide: #{$image[0].naturalWidth}"
|
||||
@errorMessage = "Choose an image less than #{MAX_WIDTH}px wide"
|
||||
@errorMessage = "Choose an image less than #{@MAX_WIDTH}px wide"
|
||||
@_clearFile()
|
||||
return
|
||||
|
||||
@@ -106,7 +103,7 @@ module.exports = class MarkdownImageController extends BaseController
|
||||
onDidRender: ->
|
||||
@$image = @$('img')
|
||||
@$fileName = @$('.fileName p')
|
||||
@$button = @$('button')
|
||||
@$buttonLabel = @$('.button p')
|
||||
@$form = @$('form')
|
||||
@$input = @$('input')
|
||||
@$errorContainer = @$('.error')
|
||||
@@ -116,6 +113,10 @@ module.exports = class MarkdownImageController extends BaseController
|
||||
|
||||
super
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
@errorMessage = null
|
||||
super oldModel, newModel
|
||||
|
||||
refresh: ->
|
||||
if @model.mimeType? and @model.encodedData?
|
||||
@$image.attr 'src', "data:#{@model.mimeType};base64,#{@model.encodedData}"
|
||||
@@ -125,25 +126,25 @@ module.exports = class MarkdownImageController extends BaseController
|
||||
@$fileName.html @model.fileName
|
||||
|
||||
if @model.encodedData?
|
||||
@$button.html 'Update'
|
||||
@$buttonLabel.html 'Update'
|
||||
else
|
||||
@$button.html 'Choose'
|
||||
@$buttonLabel.html 'Choose'
|
||||
|
||||
if @model.status is MarkdownImage.Status.checking
|
||||
@$loaded.fadeOut duration:Duration.normal
|
||||
@$loading.fadeIn duration:Duration.normal, queue:true
|
||||
if @model.status is MarkdownImage::Status.checking
|
||||
@hide @$loaded
|
||||
@show @$loading
|
||||
else
|
||||
@$loading.fadeOut duration:Duration.normal
|
||||
@$loaded.fadeIn duration:Duration.normal, queue:true
|
||||
@hide @$loading
|
||||
@show @$loaded
|
||||
|
||||
if @errorMessage?
|
||||
@$errorMessage.html @errorMessage
|
||||
@$errorContainer.fadeIn duration:Duration.normal
|
||||
else if @model.status is MarkdownImage.Status.empty
|
||||
@show @$errorContainer
|
||||
else if @model.status is MarkdownImage::Status.empty
|
||||
@$errorMessage.html 'please choose an image'
|
||||
@$errorContainer.fadeIn duration:Duration.normal
|
||||
@show @$errorContainer
|
||||
else
|
||||
@$errorContainer.fadeOut duration:Duration.normal
|
||||
@hide @$errorContainer
|
||||
|
||||
super
|
||||
|
||||
@@ -151,7 +152,7 @@ module.exports = class MarkdownImageController extends BaseController
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click button': 'onButtonClicked'
|
||||
'click .button': 'onButtonClicked'
|
||||
'change input': 'onFileChanged'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
//-
|
||||
//- Crafting Guide - markdown_image_list.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__markdown_image_list
|
||||
h3 Images Referenced
|
||||
.image_container
|
||||
.image_container
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Crafting Guide - markdown_image_list.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__markdown_image_list {
|
||||
position: relative; min-width: 33em; width: 100%;
|
||||
|
||||
border-bottom: 0.1em solid $color-gray-medium;
|
||||
margin: 2em 0;
|
||||
padding: 0 0 2em 0;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.image_container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -$size-margin-large;
|
||||
|
||||
& > * {
|
||||
width: calc(50% - 24px); // $size-margin-large
|
||||
margin-right: $size-margin-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-15
@@ -1,14 +1,13 @@
|
||||
###
|
||||
Crafting Guide - markdown_image_list_controller.coffee
|
||||
#
|
||||
# Crafting Guide - markdown_image_list_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require './base_controller'
|
||||
{Event} = require '../constants'
|
||||
MarkdownImageController = require './markdown_image_controller'
|
||||
MarkdownImageList = require '../models/markdown_image_list'
|
||||
BaseController = require '../../../base_controller'
|
||||
MarkdownImageController = require './markdown_image/markdown_image_controller'
|
||||
MarkdownImageList = require '../../../../models/site/markdown_image_list'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -16,7 +15,7 @@ module.exports = class MarkdownImageListController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.model ?= new MarkdownImageList {}, {client:options.client}
|
||||
options.templateName = 'markdown_image_list'
|
||||
options.templateName = 'common/markdown_section/markdown_image_list'
|
||||
super options
|
||||
|
||||
@imageBase = ''
|
||||
@@ -43,8 +42,8 @@ module.exports = class MarkdownImageListController extends BaseController
|
||||
return unless oldImageBase isnt newImageBase
|
||||
|
||||
@_imageBase = newImageBase
|
||||
@trigger Event.change + ':imageBase', this, oldImageBase, newImageBase
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change + ':imageBase', this, oldImageBase, newImageBase
|
||||
@trigger c.event.change, this
|
||||
|
||||
getMarkdownText: ->
|
||||
return @model.markdownText
|
||||
@@ -96,5 +95,5 @@ module.exports = class MarkdownImageListController extends BaseController
|
||||
return if newValid is oldValid
|
||||
|
||||
@_valid = newValid
|
||||
@trigger Event.change + ':valid', this, oldValid, newValid
|
||||
@trigger Event.change
|
||||
@trigger c.event.change + ':valid', this, oldValid, newValid
|
||||
@trigger c.event.change
|
||||
@@ -0,0 +1,38 @@
|
||||
//-
|
||||
//- Crafting Guide - markdown_section.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__markdown_section
|
||||
h2
|
||||
.panel
|
||||
.waiting: img(src='/images/wait.gif')
|
||||
.markdown
|
||||
.creating
|
||||
p Please help by adding a description!
|
||||
.editor
|
||||
.instructions.
|
||||
Edit using <a href="https://help.github.com/articles/github-flavored-markdown/" target="new">GitHub
|
||||
Flavored Markdown</a>. Use <tt>[[Item Name]]</tt> to link to another item and <tt></tt>
|
||||
to add an image.
|
||||
.text
|
||||
textarea
|
||||
.sizer
|
||||
.view__markdown_image_list
|
||||
.footer
|
||||
.question: a(href="javascript:void;") Confused? Click here to ask a question!
|
||||
.error: p
|
||||
.buttons
|
||||
.button.edit: .bezel: p edit
|
||||
.button.save: .bezel: p save
|
||||
.button.preview: .bezel: p preview
|
||||
.button.cancel: .bezel: p cancel
|
||||
.button.return: .bezel: p return
|
||||
.confirming
|
||||
p Thanks for your help!
|
||||
p Your changes will appear on the site in a few minutes.
|
||||
.appologizing
|
||||
p Sorry, but something went wrong.
|
||||
p Please try again after a few moments.
|
||||
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// Crafting Guide - markdown_section.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__markdown_section {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.waiting {
|
||||
display : flex;
|
||||
justify-content : center;
|
||||
|
||||
padding : $size-margin-xlarge;
|
||||
}
|
||||
|
||||
.appologizing, .confirming, .creating {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
justify-content : center;
|
||||
padding : $size-margin-xlarge;
|
||||
|
||||
p {
|
||||
color : $color-gray-medium;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
text-align : center;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.creating {
|
||||
padding-bottom: $size-margin-large;
|
||||
|
||||
p {
|
||||
color: $color-gray-light;
|
||||
font-size: $font-size-xlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.instructions {
|
||||
margin-bottom : $size-margin-medium;
|
||||
|
||||
color : $color-gray-medium;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-small;
|
||||
}
|
||||
|
||||
.text {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
position : relative;
|
||||
|
||||
textarea, .sizer {
|
||||
font-family : $font-family-input;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $font-size-medium;
|
||||
overflow : hidden;
|
||||
padding : $size-margin-small;
|
||||
resize : none;
|
||||
white-space : pre-wrap;
|
||||
}
|
||||
|
||||
textarea {
|
||||
position : absolute; width: 100%; height: 100%;
|
||||
|
||||
background: none;
|
||||
z-index: $layer-normal;
|
||||
}
|
||||
|
||||
.sizer {
|
||||
min-height: $size-minecraft-block * 4;
|
||||
opacity: 0;
|
||||
z-index: $layer-normal - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display : flex;
|
||||
align-items : flex-end;
|
||||
margin-top : $size-margin-large;
|
||||
|
||||
.question {
|
||||
flex : 1 1 100%;
|
||||
|
||||
a {
|
||||
color : $color-gray-light;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
text-decoration : underline;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
flex: 1 1 100%;
|
||||
align-self: center;
|
||||
|
||||
p {
|
||||
color : $color-error;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
font-style : italic;
|
||||
text-align : center;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex : 0 0 auto;
|
||||
|
||||
display : flex;
|
||||
flex-direction : row-reverse;
|
||||
align-items : center;
|
||||
|
||||
.button {
|
||||
margin-left : $size-margin-medium;
|
||||
width : $size-minecraft-block * 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
-94
@@ -1,24 +1,19 @@
|
||||
###
|
||||
Crafting Guide - markdown_section_controller.coffee
|
||||
#
|
||||
# Crafting Guide - markdown_section_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
BaseController = require './base_controller'
|
||||
BaseController = require '../../base_controller'
|
||||
convertMarkdown = require 'marked'
|
||||
{Duration} = require '../constants'
|
||||
{Event} = require '../constants'
|
||||
MarkdownImageListController = require './markdown_image_list_controller'
|
||||
{Url} = require '../constants'
|
||||
w = require 'when'
|
||||
MarkdownImageListController = require './markdown_image_list/markdown_image_list_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MarkdownSectionController extends BaseController
|
||||
|
||||
@State = State =
|
||||
@::State =
|
||||
appologizing: 'applogizing'
|
||||
confirming: 'confirming'
|
||||
creating: 'creating'
|
||||
@@ -30,19 +25,17 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
constructor: (options={})->
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
options.model ?= null
|
||||
options.templateName = 'markdown_section'
|
||||
@_state = State.waiting
|
||||
options.templateName = 'common/markdown_section'
|
||||
@_state = @State.waiting
|
||||
super options
|
||||
|
||||
@on Event.animate.hide.finish, => @_updateStateVisibility()
|
||||
@on Event.animate.show.finish, => @_updateStateVisibility()
|
||||
|
||||
@client = options.client
|
||||
@confirmactionMessage = options.confirmationMessage
|
||||
@confirmDuration = options.confirmationDuration ?= 5000
|
||||
@imageBase = options.imageBase ?= ''
|
||||
@modPack = options.modPack
|
||||
@title = options.title ?= 'Description'
|
||||
@_client = options.client
|
||||
@_confirmationMessage = options.confirmationMessage
|
||||
@_confirmDuration = options.confirmationDuration or 5000
|
||||
@_enterFeedback = options.enterFeedback or -> # do nothing
|
||||
@_imageBase = options.imageBase or ''
|
||||
@_modPack = options.modPack
|
||||
@_title = options.title or 'Description'
|
||||
|
||||
# @_beginEditing must be a function returning a promise which resolves when all actions necessary prior to an
|
||||
# editing session have been completed (e.g., loading the latest content from a remote server). The promise must
|
||||
@@ -57,12 +50,12 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
|
||||
resetToDefaultState: ->
|
||||
if @model?
|
||||
@state = State.viewing
|
||||
@state = @State.viewing
|
||||
else
|
||||
if @editable?
|
||||
@state = State.creating
|
||||
@state = @State.creating
|
||||
else
|
||||
@state = State.waiting
|
||||
@state = @State.waiting
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
@@ -79,42 +72,42 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
return unless @editable
|
||||
|
||||
@_originalModel = @model
|
||||
@state = State.waiting
|
||||
@state = @State.waiting
|
||||
@_beginEditing()
|
||||
.then =>
|
||||
@state = State.editing
|
||||
@state = @State.editing
|
||||
.catch (e)=>
|
||||
logger.warning "cannot begin editing: #{e.stack}"
|
||||
@state = State.appologizing
|
||||
w(true).delay(@confirmDuration).then => @resetToDefaultState()
|
||||
@state = @State.appologizing
|
||||
w(true).delay(@_confirmDuration).then => @resetToDefaultState()
|
||||
|
||||
onQuestionClicked: (event)->
|
||||
event.preventDefault()
|
||||
global.feedbackController.enterFeedback ''
|
||||
@_enterFeedback ''
|
||||
|
||||
onPreviewClicked: (event)->
|
||||
event.preventDefault()
|
||||
@_updatePreview()
|
||||
@state = State.previewing
|
||||
@state = @State.previewing
|
||||
|
||||
onReturnClicked: (event)->
|
||||
event.preventDefault()
|
||||
@state = State.editing
|
||||
@state = @State.editing
|
||||
|
||||
onSaveClicked: (event)->
|
||||
event.preventDefault()
|
||||
|
||||
@state = State.waiting
|
||||
@state = @State.waiting
|
||||
@_endEditing()
|
||||
.then =>
|
||||
@state = State.confirming
|
||||
@state = @State.confirming
|
||||
.catch (e)=>
|
||||
message = if e.stack? then e.stack else e
|
||||
logger.error "failed to end editing: #{message}"
|
||||
@state = State.appologizing
|
||||
.delay @confirmDuration
|
||||
@state = @State.appologizing
|
||||
.delay @_confirmDuration
|
||||
.then =>
|
||||
@state = State.viewing
|
||||
@state = @State.viewing
|
||||
|
||||
onTextChanged: (event)->
|
||||
event.preventDefault()
|
||||
@@ -133,8 +126,8 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
return unless newImageBase isnt oldImageBase
|
||||
|
||||
@_imageBase = newImageBase
|
||||
@trigger Event.change + ':imageBase', this, oldImageBase, newImageBase
|
||||
@trigger Event.change, this
|
||||
@trigger c.event.change + ':imageBase', this, oldImageBase, newImageBase
|
||||
@trigger c.event.change, this
|
||||
|
||||
getImageFiles: ->
|
||||
return @_imageListController.model.all
|
||||
@@ -148,7 +141,7 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
@_state = newState
|
||||
|
||||
logger.verbose => "MarkdownSectionController.#{@cid} changed state from #{oldState} to #{newState}"
|
||||
@trigger Event.change + ':state', this, oldState, newState
|
||||
@trigger c.event.change + ':state', this, oldState, newState
|
||||
@tryRefresh()
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
@@ -160,19 +153,18 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_imageListController = @addChild MarkdownImageListController, '.image_list',
|
||||
client: @client
|
||||
imageBase: @imageBase
|
||||
@on Event.change + ':imageBase', => @_imageListController.model.imageBase = @imageBase
|
||||
@listenTo @_imageListController, Event.change + ':valid', => @tryRefresh()
|
||||
|
||||
if not global.feedbackController?
|
||||
@$('.question').hide duration:0
|
||||
@_imageListController = @addChild MarkdownImageListController, '.view__markdown_image_list',
|
||||
client: @_client
|
||||
imageBase: @_imageBase
|
||||
@on c.event.change + ':imageBase', => @_imageListController.model.imageBase = @_imageBase
|
||||
@listenTo @_imageListController, c.event.change + ':valid', => @tryRefresh()
|
||||
|
||||
@$errorPanel = @$('.error')
|
||||
@$errorText = @$('.error p')
|
||||
@$markdownPanel = @$('.markdown')
|
||||
@$previewButton = @$('button.preview')
|
||||
@$saveButton = @$('button.save')
|
||||
@$previewButton = @$('.button.preview')
|
||||
@$questionPanel = @$('.question')
|
||||
@$saveButton = @$('.button.save')
|
||||
@$sizer = @$('.sizer')
|
||||
@$textarea = @$('textarea')
|
||||
@$title = @$('h2')
|
||||
@@ -182,9 +174,9 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$title.html @title
|
||||
@$title.html @_title
|
||||
@_imageListController.markdownText = @model
|
||||
@_imageListController.fetchImages() if @state is State.editing
|
||||
@_imageListController.fetchImages() if @state is @State.editing
|
||||
|
||||
@_updateButtonStates()
|
||||
@_updateSizer()
|
||||
@@ -201,14 +193,14 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .markdown a': 'routeLinkClick'
|
||||
'click button.cancel': 'onCancelClicked'
|
||||
'click button.edit': 'onEditClicked'
|
||||
'click button.preview': 'onPreviewClicked'
|
||||
'click button.return': 'onReturnClicked'
|
||||
'click button.save': 'onSaveClicked'
|
||||
'click .question a': 'onQuestionClicked'
|
||||
'input textarea': 'onTextChanged'
|
||||
'click .markdown a': 'routeLinkClick'
|
||||
'click .button.cancel': 'onCancelClicked'
|
||||
'click .button.edit': 'onEditClicked'
|
||||
'click .button.preview': 'onPreviewClicked'
|
||||
'click .button.return': 'onReturnClicked'
|
||||
'click .button.save': 'onSaveClicked'
|
||||
'click .question a': 'onQuestionClicked'
|
||||
'input textarea': 'onTextChanged'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
@@ -219,9 +211,9 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
_convertWikiLinks: (text)->
|
||||
text.replace /\[\[([^\]]*)\]\]/g, (match, name)=>
|
||||
result = match
|
||||
item = @modPack.findItemByName name
|
||||
item = @_modPack.findItemByName name
|
||||
if item?
|
||||
display = @modPack.findItemDisplay item.slug
|
||||
display = @_modPack.findItemDisplay item.slug
|
||||
result = "[#{name}](#{display.itemUrl})"
|
||||
|
||||
return result
|
||||
@@ -257,53 +249,50 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
|
||||
elements =
|
||||
appologizingPanel: @$('.appologizing')
|
||||
buttonPanel: @$('.buttons')
|
||||
cancelButton: @$('button.cancel')
|
||||
cancelButton: @$('.button.cancel')
|
||||
confirmingPanel: @$('.confirming')
|
||||
creatingPanel: @$('.creating')
|
||||
editButton: @$('button.edit')
|
||||
editButton: @$('.button.edit')
|
||||
editorPanel: @$('.editor')
|
||||
errorPanel: @$('.error')
|
||||
imageList: @$('.image_list')
|
||||
footerPanel: @$('.footer')
|
||||
imageList: @$('.view__markdown_image_list')
|
||||
markdownPanel: @$markdownPanel
|
||||
previewButton: @$previewButton
|
||||
returnButton: @$('button.return')
|
||||
returnButton: @$('.button.return')
|
||||
saveButton: @$saveButton
|
||||
waitingPanel: @$('.waiting')
|
||||
|
||||
visible = {}
|
||||
errorText = ''
|
||||
|
||||
if @state is State.appologizing
|
||||
if @state is @State.appologizing
|
||||
visible = appologizingPanel:true
|
||||
else if @state is State.confirming
|
||||
else if @state is @State.confirming
|
||||
visible = confirmingPanel:true
|
||||
else if @state is State.creating
|
||||
visible = buttonPanel:true, creatingPanel:true, editButton:true
|
||||
else if @state is State.editing
|
||||
visible = buttonPanel:true, cancelButton:true, editorPanel:true, previewButton:true, imageList:true
|
||||
else if @state is State.previewing
|
||||
visible = buttonPanel:true, errorPanel:true, markdownPanel:true, returnButton:true, saveButton:true
|
||||
else if @state is @State.creating
|
||||
visible = footerPanel:true, creatingPanel:true, editButton:true
|
||||
else if @state is @State.editing
|
||||
visible = footerPanel:true, editorPanel:true, cancelButton:true, previewButton:true, imageList:true
|
||||
else if @state is @State.previewing
|
||||
visible = footerPanel:true, markdownPanel:true, returnButton:true, saveButton:true
|
||||
errorText = "remember: your changes aren't saved yet!"
|
||||
else if @state is State.viewing
|
||||
else if @state is @State.viewing
|
||||
visible = markdownPanel:true
|
||||
if @editable then _.extend visible, {buttonPanel:true, editButton:true}
|
||||
if @editable then _.extend visible, {footerPanel:true, editButton:true}
|
||||
else # assume any unknown state is the same as "waiting"
|
||||
visible = waitingPanel:true
|
||||
|
||||
toHide = ($el for name, $el of elements when not visible[name])
|
||||
toShow = ($el for name, $el of elements when visible[name])
|
||||
for name, $el of elements when not visible[name]
|
||||
@hide $el
|
||||
for name, $el of elements when visible[name]
|
||||
@show $el
|
||||
|
||||
$el.clearQueue() for name, $el of elements
|
||||
|
||||
if toHide.length > 0
|
||||
$el.fadeOut(duration:Duration.normal) for $el in toHide
|
||||
$el.delay(Duration.normal) for $el in toShow
|
||||
|
||||
$el.fadeIn(duration:Duration.normal) for $el in toShow
|
||||
|
||||
if @state is State.editing
|
||||
@once Event.animate.show.finish, =>
|
||||
@$textarea.focus()
|
||||
if @state is @State.editing then @$textarea.focus()
|
||||
|
||||
@$errorText.html errorText
|
||||
if errorText.length > 0
|
||||
@show @$errorPanel
|
||||
@hide @$questionPanel
|
||||
else
|
||||
@hide @$errorPanel
|
||||
@show @$questionPanel
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user