Author SHA1 Message Date
Andrew Miner 9ddbd5cda7 Add remaining commands 2016-04-15 21:22:10 -07:00
Andrew Miner 31f6e12844 wip 2016-04-13 22:31:11 -07:00
Andrew Miner 14278c81df Add first stage of a new single-file parser
This first stage consists of the general framework for the parser as
a whole, a lexical analyzers to converts text into commands, and the
semantic anlyzer to convert commands into a complete data structure
representing the cumulative effect of running the commands.
2016-04-10 13:37:17 -07:00
116 changed files with 2122 additions and 2306 deletions
+2 -7
View File
@@ -54,7 +54,7 @@ module.exports = (grunt)->
mode: 'gzip'
files: [
{expand: true, cwd:'./build/static', src:'**/*.css', dest:'./dist/'}
{expand: true, cwd:'./build/static', src:['**/*.html', '!**/index.html'], dest:'./dist/'}
{expand: true, cwd:'./build/static', src:'**/*.html', dest:'./dist/'}
{expand: true, cwd:'./build/static', src:'**/*.ugly.js', dest:'./dist/', ext: '.js'}
{expand: true, cwd:'./build/static', src:'**/*.json', dest:'./dist/'}
{expand: true, cwd:'./build/static', src:'**/*.ttf', dest:'./dist/'}
@@ -77,10 +77,7 @@ module.exports = (grunt)->
]
build_to_dist:
files: [
expand: true
cwd: './build/static/'
src: ['**/*', '!**/*.map', '!**/*.js', '!index.html']
dest: './dist'
{expand:true, cwd:'./build/static/', src:['**/*', '!**/*.map', '!**/*.js'], dest:'./dist'}
]
common_source:
files: [
@@ -138,8 +135,6 @@ module.exports = (grunt)->
sass:
all:
options:
sourcemap: 'none'
files:
'./build/static/main.css': [ './build/imports.scss' ]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 B

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "crafting-guide",
"version": "3.2.4",
"version": "3.2.2",
"description": "A guide for building anything in Minecraft or its many mods",
"main": "./dist/index.js",
"repository": "http://github.com/andrewminer/crafting-guide",
Binary file not shown.
@@ -1,353 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ActiveLayerIndex</key>
<integer>0</integer>
<key>ApplicationVersion</key>
<array>
<string>com.omnigroup.OmniGraffle.MacAppStore</string>
<string>139.18</string>
</array>
<key>AutoAdjust</key>
<false/>
<key>BackgroundGraphic</key>
<dict>
<key>Bounds</key>
<string>{{0, 0}, {320, 320}}</string>
<key>Class</key>
<string>SolidGraphic</string>
<key>ID</key>
<integer>2</integer>
<key>Style</key>
<dict>
<key>shadow</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
<key>stroke</key>
<dict>
<key>Draws</key>
<string>NO</string>
</dict>
</dict>
</dict>
<key>BaseZoom</key>
<integer>0</integer>
<key>CanvasOrigin</key>
<string>{0, 0}</string>
<key>CanvasSize</key>
<string>{320, 320}</string>
<key>ColumnAlign</key>
<integer>1</integer>
<key>ColumnSpacing</key>
<real>36</real>
<key>CreationDate</key>
<string>2016-04-23 05:39:56 +0000</string>
<key>Creator</key>
<string>Andrew Miner</string>
<key>DisplayScale</key>
<string>1 pt = 1 px</string>
<key>GraphDocumentVersion</key>
<integer>8</integer>
<key>GraphicsList</key>
<array>
<dict>
<key>AllowConnections</key>
<string>NO</string>
<key>AllowLabelDrop</key>
<false/>
<key>AllowToConnect</key>
<false/>
<key>Class</key>
<string>LineGraphic</string>
<key>ID</key>
<integer>5</integer>
<key>Points</key>
<array>
<string>{160, 480}</string>
<string>{480, 160}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>Cap</key>
<integer>0</integer>
<key>Color</key>
<dict>
<key>a</key>
<string>0.2</string>
<key>b</key>
<string>0</string>
<key>g</key>
<string>1</string>
<key>r</key>
<string>1</string>
</dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>TailArrow</key>
<string>0</string>
<key>Width</key>
<real>120</real>
</dict>
</dict>
</dict>
<dict>
<key>AllowConnections</key>
<string>NO</string>
<key>AllowLabelDrop</key>
<false/>
<key>AllowToConnect</key>
<false/>
<key>Class</key>
<string>LineGraphic</string>
<key>ID</key>
<integer>4</integer>
<key>Points</key>
<array>
<string>{-160, 160}</string>
<string>{160, -160}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>Cap</key>
<integer>0</integer>
<key>Color</key>
<dict>
<key>a</key>
<string>0.2</string>
<key>b</key>
<string>0</string>
<key>g</key>
<string>1</string>
<key>r</key>
<string>1</string>
</dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>TailArrow</key>
<string>0</string>
<key>Width</key>
<real>120</real>
</dict>
</dict>
</dict>
<dict>
<key>AllowConnections</key>
<string>NO</string>
<key>AllowLabelDrop</key>
<false/>
<key>AllowToConnect</key>
<false/>
<key>Class</key>
<string>LineGraphic</string>
<key>ID</key>
<integer>3</integer>
<key>Points</key>
<array>
<string>{0, 320}</string>
<string>{320, 0}</string>
</array>
<key>Style</key>
<dict>
<key>stroke</key>
<dict>
<key>Cap</key>
<integer>0</integer>
<key>Color</key>
<dict>
<key>a</key>
<string>0.2</string>
<key>b</key>
<string>0</string>
<key>g</key>
<string>1</string>
<key>r</key>
<string>1</string>
</dict>
<key>HeadArrow</key>
<string>0</string>
<key>Legacy</key>
<true/>
<key>TailArrow</key>
<string>0</string>
<key>Width</key>
<real>120</real>
</dict>
</dict>
</dict>
</array>
<key>GridInfo</key>
<dict>
<key>GridSpacing</key>
<real>10</real>
<key>MajorGridSpacing</key>
<integer>10</integer>
<key>ShowsGrid</key>
<string>YES</string>
<key>SnapsToGrid</key>
<string>YES</string>
</dict>
<key>GuidesLocked</key>
<string>NO</string>
<key>GuidesVisible</key>
<string>YES</string>
<key>HPages</key>
<integer>1</integer>
<key>ImageCounter</key>
<integer>1</integer>
<key>KeepToScale</key>
<false/>
<key>Layers</key>
<array>
<dict>
<key>Lock</key>
<string>NO</string>
<key>Name</key>
<string>Layer 1</string>
<key>Print</key>
<string>YES</string>
<key>View</key>
<string>YES</string>
</dict>
</array>
<key>LayoutInfo</key>
<dict>
<key>Animate</key>
<string>NO</string>
<key>circoMinDist</key>
<real>18</real>
<key>circoSeparation</key>
<real>0.0</real>
<key>layoutEngine</key>
<string>dot</string>
<key>neatoSeparation</key>
<real>0.0</real>
<key>twopiSeparation</key>
<real>0.0</real>
</dict>
<key>LinksVisible</key>
<string>NO</string>
<key>MagnetsVisible</key>
<string>NO</string>
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
<string>2016-04-27 03:11:38 +0000</string>
<key>Modifier</key>
<string>Andrew Miner</string>
<key>NotesVisible</key>
<string>NO</string>
<key>Orientation</key>
<integer>2</integer>
<key>OriginVisible</key>
<string>NO</string>
<key>PageBreaks</key>
<string>NO</string>
<key>PrintInfo</key>
<dict>
<key>NSBottomMargin</key>
<array>
<string>float</string>
<string>41</string>
</array>
<key>NSHorizonalPagination</key>
<array>
<string>coded</string>
<string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
</array>
<key>NSLeftMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSPaperSize</key>
<array>
<string>size</string>
<string>{611.99998474121094, 792}</string>
</array>
<key>NSPrintReverseOrientation</key>
<array>
<string>int</string>
<string>0</string>
</array>
<key>NSRightMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
<key>NSTopMargin</key>
<array>
<string>float</string>
<string>18</string>
</array>
</dict>
<key>PrintOnePage</key>
<false/>
<key>ReadOnly</key>
<string>NO</string>
<key>RowAlign</key>
<integer>1</integer>
<key>RowSpacing</key>
<real>36</real>
<key>SheetTitle</key>
<string>Canvas 1</string>
<key>SmartAlignmentGuidesActive</key>
<string>YES</string>
<key>SmartDistanceGuidesActive</key>
<string>YES</string>
<key>UniqueID</key>
<integer>1</integer>
<key>UseEntirePage</key>
<false/>
<key>VPages</key>
<integer>1</integer>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
<integer>0</integer>
<key>ExpandedCanvases</key>
<array>
<dict>
<key>name</key>
<string>Canvas 1</string>
</dict>
</array>
<key>Frame</key>
<string>{{215, 0}, {2134, 1417}}</string>
<key>ListView</key>
<true/>
<key>OutlineWidth</key>
<integer>142</integer>
<key>RightSidebar</key>
<false/>
<key>ShowRuler</key>
<true/>
<key>Sidebar</key>
<true/>
<key>SidebarWidth</key>
<integer>120</integer>
<key>VisibleRegion</key>
<string>{{-840, -477}, {1999, 1275}}</string>
<key>Zoom</key>
<real>1</real>
<key>ZoomValues</key>
<array>
<array>
<string>Canvas 1</string>
<real>1</real>
<real>1</real>
</array>
</array>
</dict>
</dict>
</plist>
@@ -1,18 +0,0 @@
//-
//- Crafting Guide - _google_sitelinks.jade
//-
//- Copyright © 2014-2016 by Redwood Labs
//- All rights reserved.
//-
script(type="application/ld+json").
{
"@context": "http://schema.org",
"@type": "WebSite",
"url": "http://crafting-guide.com/",
"potentialAction": {
"@type": "SearchAction",
"target": "http://crafting-guide.com?search={search_term}",
"query-input": "required name=search_term"
}
}
-11
View File
@@ -1,11 +0,0 @@
script.
! function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0],
p = /^http:/.test(d.location) ? 'http' : 'https';
if (!d.getElementById(id)) {
js = d.createElement(s);
js.id = id;
js.src = p + '://platform.twitter.com/widgets.js';
fjs.parentNode.insertBefore(js, fjs);
}
}(document, 'script', 'twitter-wjs');
+9 -50
View File
@@ -9,9 +9,6 @@
module.exports = class BaseModel extends Backbone.Model
@_loadingQueue = []
@_isDraining = false
constructor: (attributes={}, options={})->
options.logEvents ?= true
super attributes, options
@@ -22,10 +19,10 @@ module.exports = class BaseModel extends Backbone.Model
continue if name is 'id'
Object.defineProperty this, name, get:makeGetter(name), set:makeSetter(name)
@fileCache = options.fileCache or null
@loading = null
@logEvents = options.logEvents or false
@state = c.modelState.unloaded
@state = c.modelState.unloaded
@loading = null
Object.defineProperties this,
isUnloaded: { get:-> @state is c.modelState.unloaded }
@@ -63,23 +60,12 @@ module.exports = class BaseModel extends Backbone.Model
@state = c.modelState.loading
@trigger c.event.request, this
loadFromServer = =>
w.promise (resolve, reject)=>
$.ajax
url: url
dataType: 'text'
success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr
error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr
if @fileCache?
@loading = @fileCache.loading.then =>
if @fileCache.hasFile url
return @_addToLoadingQueue @fileCache.getFile(url), 'success', {url:url}
else
@loading = loadFromServer()
else
@loading = loadFromServer()
@loading = w.promise (resolve, reject)=>
$.ajax
url: url
dataType: 'text'
success: (text, status, xhr)=> resolve @onLoadSucceeded text, status, xhr
error: (xhr, status, error)=> reject @onLoadFailed error, status, xhr
@loading.catch (e)-> # do nothing
return @loading
@@ -100,30 +86,3 @@ module.exports = class BaseModel extends Backbone.Model
toString: ->
return "#{@constructor.name}.#{@cid}"
# Private Methods ##############################################################################
_addToLoadingQueue: (text, status, xhr)->
deferred = w.defer()
BaseModel._loadingQueue.push resolve:deferred.resolve, func:(=> @onLoadSucceeded text, status, xhr)
@_drainLoadingQueue()
return deferred.promise
_drainLoadingQueue: ->
return if @_isDraining
@_isDraining = true
drainDelay = 50
drain = =>
toLoad = BaseModel._loadingQueue.shift()
if not toLoad?
@_isDraining = false
else
toLoad.func()
toLoad.resolve(true)
_.delay drain, drainDelay
_.delay drain, drainDelay
@@ -24,7 +24,6 @@ module.exports = class CraftingPlan
@_rawScores = {}
@_scores = {}
@_steps = steps
@_tools = null
@_want = want
@_numberSteps()
@@ -38,32 +37,19 @@ module.exports = class CraftingPlan
@_made = new SimpleInventory modPack:@_modPack
@_made.addInventory @_have
@_tools = new SimpleInventory modPack:@_modPack
for i in [@_steps.length-1..0] by -1
step = @_steps[i]
step.multiplier = 0
recipe = step.recipe
for stack in step.recipe.output
continue if recipe.isPassThroughFor stack.itemSlug
qualifiedSlug = @_modPack.qualifySlug stack.itemSlug
if not stack?
throw new Error 'stack should not be null here'
qualifiedSlug = @_modPack.qualifySlug stack.itemSlug
while @_need.quantityOf(qualifiedSlug) > 0
@_executeStep step
if step.multiplier > 0
for stack in step.recipe.output
qualifiedSlug = @_modPack.qualifySlug stack.itemSlug
continue unless recipe.isPassThroughFor stack.itemSlug
continue if @_made.hasAtLeast qualifiedSlug, 1
continue if @_need.hasAtLeast qualifiedSlug, 1
continue if @_tools.hasAtLeast qualifiedSlug, 1
@_need.add qualifiedSlug, 1
@_tools.add qualifiedSlug, 1
@_made.addInventory @_want
@_made.addInventory @_tools
@_pruneEmptySteps()
@_numberSteps()
@@ -63,22 +63,6 @@ describe 'crafting_plan.coffee', ->
'1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot'
]
it 'uses the correct amount of passthrough items', ->
plans = fixtures.makePlans [10, 'test__split_oak_wood'], [10, 'test__split_spruce_wood']
plan = plans[2]
plan.computeRequired()
plan.need.unparse().should.equal 'coal:8.iron_ore:6.oak_wood:5.spruce_wood'
plan.made.unparse().should.equal '4.iron_ingot:maul:2.oak_planks:10.split_oak_wood:10.split_spruce_wood:stick'
(s.toString() for s in plan.steps).should.eql [
'1x test__oak_wood>... .0. ...>>4 test__oak_planks'
'1x test__oak_planks>.0. .0. ...>>4 test__stick'
'1x 8 test__iron_ore,test__coal>.0. ... .1.>test__furnace>8 test__iron_ingot'
'1x test__iron_ingot,test__stick>001 001 ..1>test__crafting_table>test__maul'
'5x test__spruce_wood,test__maul>.1. .0. ...>>2 test__split_spruce_wood,test__maul'
'5x test__oak_wood,test__maul>.1. .0. ...>>2 test__split_oak_wood,test__maul'
]
it 'can compute multiple items with multiple plans', ->
plans = fixtures.makePlans [1, 'test__copper_block'], [1, 'test__iron_sword']
plans.length.should.equal 4
@@ -27,11 +27,10 @@ module.exports = class CraftingStep
addToolsTo: (targetInventory)->
for stack in @_recipe.tools
targetInventory.add stack.itemSlug, stack.quantity, insert:true
targetInventory.add stack.itemSlug, stack.quantity
completeInto: (targetInventory)->
for stack in @_recipe.output
continue if @_recipe.isPassThroughFor stack.itemSlug
quantity = @_recipe.getQuantityProduced stack.itemSlug
targetInventory.add stack.itemSlug, quantity * @_multiplier
@@ -68,6 +67,4 @@ module.exports = class CraftingStep
@_inventory = new SimpleInventory
for stack in @_recipe.input
qualifiedSlug = @_modPack.qualifySlug stack.itemSlug
required = @_recipe.getQuantityRequired(stack.itemSlug) - @_recipe.getQuantityProduced(stack.itemSlug)
required = if required > 0 then required * @multiplier else 1
@_inventory.add qualifiedSlug, required
@_inventory.add qualifiedSlug, @multiplier * @_recipe.getQuantityRequired stack.itemSlug
+23 -20
View File
@@ -22,43 +22,57 @@ module.exports = class Craftsman extends BaseModel
@::PLAN_STEP_INCREMENT = 39
@::STAGE =
EMPTY: 'empty'
READY: 'ready'
WAITING: 'waiting'
GRAPHING: 'examining recipes'
PLANNING: 'computing plans'
ANALYZING: 'analyzing plans'
COMPLETE: 'complete'
INVALID: 'invalid'
OUTDATED: 'outdated'
constructor: (modPack)->
if not modPack? then throw new Error 'modPack is required'
attributes =
stage: @STAGE.READY
paused: false
stage: @STAGE.WAITING
stageCount: 0
super attributes, {}
@_modPack = modPack
reset = _.debounce (=> @reset()), 100
reevaluatePlans = _.debounce (=> @reevaluatePlans()), 100
@_have = new Inventory modPack:@_modPack
@_have.on c.event.change, => @_resetStage()
@_have.on c.event.change, reevaluatePlans
@_want = new Inventory modPack:@_modPack
@_want.on c.event.change, => @_resetStage()
@_want.on c.event.change, reset
@on c.event.change + ':paused', reset
@on c.event.change + ':stage', => logger.info "Craftsman has started #{@stage}..."
@on 'scheduleNextWork', => @_scheduleNextWork()
@reset()
# Public Methods ###############################################################################
reevaluatePlans: ->
@_plans = null
@_planEvaluator = null
@stage = @STAGE.WAITING
@stageCount = 0
@_scheduleNextWork()
reset: ->
@_graphBuilder = null
@_planBuilder = null
@_planEvaluator = null
@_plans = null
@_resetStage()
@stage = @STAGE.WAITING
@stageCount = 0
@_scheduleNextWork()
work: ->
return if @_want.isEmpty
@@ -116,7 +130,7 @@ module.exports = class Craftsman extends BaseModel
Object.defineProperties @prototype,
complete:
get: -> @stage in [@STAGE.COMPLETE, @STAGE.INVALID, @STAGE.OUTDATED]
get: -> @stage in [@STAGE.COMPLETE, @STAGE.INVALID]
have:
get: -> @_have
@@ -129,19 +143,8 @@ module.exports = class Craftsman extends BaseModel
# Private Methods ##############################################################################
_resetStage: ->
@stageCount = 0
if @want.isEmpty
if @stage isnt @STAGE.EMPTY
@stage = @STAGE.EMPTY
@reset()
else if not @_plans?
@stage = @STAGE.READY
else
@stage = @STAGE.OUTDATED
_scheduleNextWork: ->
return if @paused
return if @want.isEmpty
return if @complete
@@ -98,12 +98,6 @@ MOD_VERSION_FILE =
input: Stick, Cobblestone
pattern: .0. .1. ...
item: Maul
recipe:
input: Iron Ingot, Stick
pattern: 001 001 ..1
tools: Crafting Table
item: Oak Planks
recipe:
input: Oak Wood
@@ -113,29 +107,12 @@ MOD_VERSION_FILE =
item: Oak Wood
gatherable: yes
item: Spruce Wood
gatherable: yes
item: Stick
recipe:
input: Oak Planks
pattern: .0. .0. ...
quantity: 4
item: Split Oak Wood
recipe:
extras: Maul
input: Oak Wood, Maul
pattern: .1. .0. ...
quantity: 2
item: Split Spruce Wood
recipe:
extras: Maul
input: Spruce Wood, Maul
pattern: .1. .0. ...
quantity: 2
item: String
gatherable: yes
@@ -44,7 +44,7 @@ module.exports = class SimpleInventory
@_itemSlugs = []
clone: ->
inventory = new SimpleInventory
inventory = new Inventory
inventory.addInventory this
return inventory
@@ -79,7 +79,7 @@ module.exports = class SimpleInventory
if qualifiedSlug?
newSlugs.push qualifiedSlug
newStacks.push new SimpleStack itemSlug:qualifiedSlug, quantity:stack.quantity
newStacks.push new Stack itemSlug:qualifiedSlug, quantity:stack.quantity
else
newSlugs.push itemSlug
newStacks.push stack
@@ -130,9 +130,9 @@ module.exports = class SimpleInventory
parse: (data)->
return this if not data? or data.length is 0
stacks = data.split SimpleInventory.Delimiters.Stack
stacks = data.split Inventory.Delimiters.Stack
for stackText in stacks
stackParts = stackText.split SimpleInventory.Delimiters.Item
stackParts = stackText.split Inventory.Delimiters.Item
if stackParts.length is 2
quantity = parseInt stackParts[0], 10
itemSlug = ItemSlug.slugify stackParts[1]
+3 -3
View File
@@ -1,9 +1,9 @@
#
###
# Crafting Guide - event_recorder.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# Copyright (c) 2014-2015 by Redwood Labs
# All rights reserved.
#
###
util = require 'util'
+4 -20
View File
@@ -29,10 +29,10 @@ module.exports = class Inventory extends BaseModel
# Public Methods ###############################################################################
add: (itemSlug, quantity=1, options={})->
add: (itemSlug, quantity=1)->
return this unless quantity > 0
@_add itemSlug, quantity, options
@_add itemSlug, quantity
@trigger c.event.add, this, itemSlug, quantity
@trigger c.event.change, this
return this
@@ -141,18 +141,6 @@ module.exports = class Inventory extends BaseModel
@trigger c.event.change, this
return this
toDescription: ->
return null if @isEmpty
return null unless @modPack?
item = @modPack.findItem @_itemSlugs[0]
extras = @_itemSlugs.length - 1
result = "#{item.name}"
if extras > 0 then result += " and #{extras} more..."
return result
# Parsing Methods ##############################################################################
parse: (data)->
@@ -221,8 +209,7 @@ module.exports = class Inventory extends BaseModel
# Private Methods ##############################################################################
_add: (itemSlug, quantity=1, options={})->
options.insert ?= false
_add: (itemSlug, quantity=1)->
return unless itemSlug?
return unless quantity > 0
@@ -231,10 +218,7 @@ module.exports = class Inventory extends BaseModel
stack = new Stack itemSlug:itemSlug, quantity:quantity
@listenTo stack, c.event.change, => @trigger c.event.change, this
@_stacks[itemSlug] = stack
if options.insert
@_itemSlugs.unshift itemSlug
else
@_itemSlugs.push itemSlug
@_itemSlugs.push itemSlug
@_sort()
else
stack.quantity += quantity
+2 -12
View File
@@ -29,6 +29,8 @@ module.exports = class Mod extends BaseModel
@_modVersions = []
@_tutorials = []
@once c.event.sync, => @_verifyActiveModVersion()
# Class Methods ##################################################################################
@Version: Version =
@@ -93,12 +95,6 @@ module.exports = class Mod extends BaseModel
# Item Methods #################################################################################
chooseRandomItem: ->
effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest
return null unless effectiveModVersion?
return effectiveModVersion.chooseRandomItem()
eachItem: (callback)->
effectiveModVersion = @_activeModVersion or @getModVersion Mod.Version.Latest
effectiveModVersion.eachItem callback
@@ -133,7 +129,6 @@ module.exports = class Mod extends BaseModel
@_modVersions.push modVersion
@listenTo modVersion, c.event.change, => @trigger c.event.change, this
modVersion.fileCache = this.fileCache
modVersion.mod = this
@trigger c.event.add + ':modVersion', modVersion, this
@@ -148,9 +143,6 @@ module.exports = class Mod extends BaseModel
for modVersion in @_modVersions
callback modVersion
getAllModVersions: ->
return @_modVersions[..]
getModVersion: (version)->
return null if version is Mod.Version.None
return @_modVersions[0] if version is Mod.Version.Latest
@@ -209,8 +201,6 @@ module.exports = class Mod extends BaseModel
@_parser ?= new ModParser model:this
@_parser.parse text
@_verifyActiveModVersion()
return null # prevent calling `set`
url: ->
+7 -20
View File
@@ -6,10 +6,9 @@
#
BaseModel = require '../base_model'
Mod = require './mod'
Inventory = require './inventory'
ModVersionParser = require '../parsing/mod_version_parser'
Recipe = require './recipe'
SimpleInventory = require '../crafting/simple_inventory'
########################################################################################################################
@@ -25,12 +24,6 @@ module.exports = class ModPack extends BaseModel
# Item Methods #################################################################################
chooseRandomItem: ->
return null unless @_mods.length > 0
modIndex = Math.floor Math.random() * @_mods.length
return @_mods[modIndex].chooseRandomItem()
findItem: (itemSlug, options={})->
options.includeDisabled ?= false
@@ -82,7 +75,7 @@ module.exports = class ModPack extends BaseModel
result.modSlug = @_mods[0].slug
result.modVersion = @_mods[0].activeVersion
craftingUrlInventory = new SimpleInventory modPack:this
craftingUrlInventory = new Inventory modPack:this
if item?.multiblock?
craftingUrlInventory.addInventory item.multiblock.inventory
else
@@ -172,18 +165,12 @@ module.exports = class ModPack extends BaseModel
# Private Methods ##############################################################################
_onModVersionLoaded: (modVersion)->
mods = @getAllMods()
return true unless mods.length > 0
working = true
@eachMod (mod)->
mod.eachModVersion (modVersion)->
working = working and (modVersion.isUnloaded or modVersion.isLoading)
for mod in mods
modVersions = mod.getAllModVersions()
return true unless modVersions.length > 0
continue if mod.activeVersion is Mod.Version.None
activeModVersion = mod.activeModVersion
return true unless activeModVersion?
return true if activeModVersion.isUnloaded
return true if activeModVersion.isLoading
return if working
@trigger c.event.change, this
@trigger c.event.sync, this
@@ -59,10 +59,6 @@ module.exports = class ModVersion extends BaseModel
return null if result.length is 0
return result
chooseRandomItem: ->
itemIndex = Math.floor Math.random() * @_slugs.length
return @_slugs[itemIndex]
eachItem: (callback)->
for slug in @_slugs
item = @findItem slug
+12 -7
View File
@@ -103,7 +103,7 @@ module.exports = class Recipe extends BaseModel
if modPack?.findItemByName(@condition.noun)?
result = true
else if @condition.verb is 'mod'
modPack.eachMod (mod)=>
modPack.eachMod (mod)->
if mod.name is @condition.noun
result = true
@@ -111,7 +111,16 @@ module.exports = class Recipe extends BaseModel
return result
isPassThroughFor: (itemSlug)->
return @getQuantityProduced(itemSlug) is @getQuantityRequired(itemSlug)
amountCreated = 0
for stack in @output
if stack.itemSlug.matches itemSlug
amountCreated += stack.quantity
for stack in @input
if stack.itemSlug.matches itemSlug
amountCreated -= stack.quantity
return amountCreated <= 0
produces: (itemSlug)->
if not @_produces?
@@ -212,11 +221,7 @@ module.exports = class Recipe extends BaseModel
_computeQuantities: (pattern)->
quantityMap = {}
index = 0
while index < pattern.length
c = pattern[index]
index += 1
for c in pattern.split ''
continue if c is '.'
continue if c is ' '
@@ -0,0 +1,143 @@
#
# Crafting Guide - lexical_analyzer.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserCommand = require './parser_command'
########################################################################################################################
module.exports = class LexicalAnalyzer
constructor: (fileName, rawText)->
if not fileName? then throw new Error 'fileName is required'
if not rawText? then throw new Error 'rawText is required'
@_commands = []
@_fileName = fileName
@_rawText = rawText
@_lines = rawText.split '\n'
@_lineIndex = -1
@_lineNumber = 0
# Class Methods ################################################################################
@::COMMAND = /\ *([^:]*):?(.*)/
@::COMMENT = /([^\\]?)#.*/
@::FILE = /^#FILE +(.*)/
# Public Methods ###############################################################################
next: ->
command = @peek()
@_commands.shift()
return command
peek: ->
while @_commands.length is 0
break if @_lineNumber > @_lines.length
@_parseLine()
return null unless @_commands.length > 0
return @_commands[0]
push: (command)->
@_commands.unshift command
# Property Methods #############################################################################
getIsFinished: ->
return @peek() is null
Object.defineProperties @prototype,
isFinished: { get:@::getIsFinished }
# Private Methods ##############################################################################
_currentLine: ->
return null if @_lineIndex >= @_lines.length
return @_lines[@_lineIndex]
_nextLine: ->
if @_lineIndex < @_lines.length
while true
@_lineIndex += 1
@_lineNumber += 1
break if @_lineIndex >= @_lines.length
line = @_lines[@_lineIndex]
fileMatch = line.match @FILE, '$1'
if fileMatch?
@_fileName = fileMatch[1]
@_lineNumber = 0
else
line = line.replace @COMMENT, '$1'
@_lines[@_lineIndex] = line
break if line.trim().length > 0
return @_currentLine()
_parseHereDoc: ->
line = @_currentLine()
hereDocIndex = line.indexOf '<<-'
return [line, null] unless hereDocIndex isnt -1
hereDocStopText = line[hereDocIndex+3...line.length]
line = line[0...hereDocIndex]
hereDocLines = []
while true
nextLine = @_nextLine()
break unless nextLine?
break if nextLine.trim() is hereDocStopText
hereDocLines.push nextLine
shortestIndent = Number.MAX_VALUE
for hereDocLine in hereDocLines
continue if hereDocLine.trim().length is 0
shortestIndent = Math.min hereDocLine.match(/( *).*/)[1].length, shortestIndent
for i in [0...hereDocLines.length]
hereDocLines[i] = hereDocLines[i][shortestIndent..]
return [line, null] unless hereDocLines.length > 0
return [line, hereDocLines.join('\n')]
_parseLine: ->
line = @_nextLine()
return unless line?
startingLineNumber = @_lineNumber
[line, lineHereDoc] = @_parseHereDoc()
lineParts = (part.trim() for part in line.split(';'))
for linePart, index in lineParts
continue if linePart.length is 0
hereDoc = lineHereDoc if index is lineParts.length - 1
match = @COMMAND.exec linePart
if not match? then throw new Error "Expected <command>: <args>, but found: \"#{linePart}\""
argText = if match[2] then match[2].trim() else ''
args = (arg.trim() for arg in argText.split(','))
args = (arg for arg in args when arg.length > 0)
if hereDoc?
if argText.length > 0 then argText += ', '
argText += hereDoc
args.push hereDoc
@_commands.push new ParserCommand
argText: argText
args: args
fileName: @_fileName
hereDoc: hereDoc
lineNumber: startingLineNumber
name: match[1]
@@ -0,0 +1,121 @@
#
# Crafting Guide - lexical_analyzer.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
LexicalAnalyzer = require './lexical_analyzer'
########################################################################################################################
lexer = null
createLexer = (text)->
return new LexicalAnalyzer 'file.cg', text
########################################################################################################################
describe 'lexical_analyzer.coffee', ->
describe 'a document with', ->
it 'no content returns null immediately', ->
lexer = createLexer ''
expect(lexer.next()).to.be.null
it 'only whitespace returns null immediately', ->
lexer = createLexer '\n\n \n \n\n'
expect(lexer.next()).to.be.null
it 'a single command returns only that command', ->
lexer = createLexer 'alpha: bravo'
lexer.next().name.should.equal 'alpha'
expect(lexer.next()).to.be.null
it 'multiple commands on a single line returns all commands', ->
lexer = createLexer 'alpha: bravo; charlie: delta; echo: foxtrot'
lexer.next().name.should.equal 'alpha'
lexer.next().name.should.equal 'charlie'
lexer.next().name.should.equal 'echo'
expect(lexer.next()).to.be.null
it 'multiple commands on separate lines returns all commands', ->
lexer = createLexer 'alpha: bravo \n charlie: delta \n echo: foxtrot'
lexer.next().name.should.equal 'alpha'
lexer.next().name.should.equal 'charlie'
lexer.next().name.should.equal 'echo'
expect(lexer.next()).to.be.null
describe 'a command with', ->
it 'only a name has no arguments', ->
command = createLexer('alpha:').next()
command.argText.should.equal ''
command.args.should.eql []
it 'one argument has only that argument', ->
command = createLexer('alpha: bravo').next()
command.argText.should.equal 'bravo'
command.args.should.eql ['bravo']
it 'multiple arguments has all of them', ->
command = createLexer('alpha: bravo, charlie, delta').next()
command.argText.should.equal 'bravo, charlie, delta'
command.args.should.eql ['bravo', 'charlie', 'delta']
it 'only a heredoc has it recorded in the right places', ->
command = createLexer('alpha: <<-END\nbravo charlie\nEND').next()
command.hereDoc.should.equal 'bravo charlie'
command.args.should.eql ['bravo charlie']
command.argText.should.equal 'bravo charlie'
it 'multiple args and a hereDoc is built correctly', ->
command = createLexer('alpha: bravo, charlie <<-END\ndelta echo\nfoxtrot\nEND').next()
command.hereDoc.should.equal 'delta echo\nfoxtrot'
command.argText.should.equal 'bravo, charlie, delta echo\nfoxtrot'
command.args.should.eql ['bravo', 'charlie', 'delta echo\nfoxtrot']
describe 'file name is assigned correctly when', ->
it 'no file markers are given', ->
lexer = createLexer 'alpha: bravo\ncharlie: delta'
lexer.next().fileName.should.equal 'file.cg'
lexer.next().fileName.should.equal 'file.cg'
it 'commands appear before the first file marker', ->
lexer = createLexer 'alpha: bravo\n#FILE file2.cg\ncharlie: delta'
lexer.next().fileName.should.equal 'file.cg'
lexer.next().fileName.should.equal 'file2.cg'
it 'multiple file markers are used', ->
lexer = createLexer '#FILE file1.cg\na: b\n#FILE file2.cg\nc: d\n#FILE file3.cg\ne: f'
lexer.next().fileName.should.equal 'file1.cg'
lexer.next().fileName.should.equal 'file2.cg'
lexer.next().fileName.should.equal 'file3.cg'
describe 'line numbers are assigned correctly when', ->
it 'commands are listed one after another', ->
lexer = createLexer 'alpha: bravo\ncharlie: delta\necho: foxtrot'
lexer.next().lineNumber.should.equal 1
lexer.next().lineNumber.should.equal 2
lexer.next().lineNumber.should.equal 3
it 'commands are separated by blank lines and comments', ->
lexer = createLexer '# foo\n\nalpha: bravo\n\ncharlie: delta\n\n# bar\n\necho: foxtrot'
lexer.next().lineNumber.should.equal 3
lexer.next().lineNumber.should.equal 5
lexer.next().lineNumber.should.equal 9
it 'commands include hereDocs', ->
lexer = createLexer 'alpha: <<-END\nbravo charlie\nEND\ndelta: \n\necho: '
lexer.next().lineNumber.should.equal 1
lexer.next().lineNumber.should.equal 4
lexer.next().lineNumber.should.equal 6
it 'file markers are used', ->
lexer = createLexer '#FILE file1.cg\nalpha: \n#FILE file2.cg\n\nbravo: \n\n#FILE file3.cg\n\n\ncharlie: '
lexer.next().lineNumber.should.equal 1
lexer.next().lineNumber.should.equal 2
lexer.next().lineNumber.should.equal 3
+112
View File
@@ -0,0 +1,112 @@
#
# Crafting Guide - parser.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
LexicalAnalyzer = require './lexical_analyzer'
ModPack = require '../game/mod_pack'
ParserData = require './parser_data'
########################################################################################################################
module.exports = class Parser
constructor: (modPack, fileName, rawText)->
@_extensions = []
@_fileName = fileName
@_lexer = new LexicalAnalyzer fileName, rawText
@_modPack = new ModPack
@_data = new ParserData
modPackData = @_data.create {}, 'modPack'
modPackData.instance = @_modPack
@_loadSchema()
# Public Methods ###############################################################################
next: ->
if not @_lexer.isFinished
@_processNextCommand()
else if not @_data.isValidated
@_validateNextEntry()
else if not @_data.isBuilt
@_buildNextEntry()
else if not @_data.isAssembled
@_assembleNextEntry()
# Property Methods #############################################################################
getIsFinished: ->
return @_lexer.isFinished and @_data.isValidated and @_data.isBuilt and @_data.isAssembled
getData: ->
return @_data
getModPack: ->
return @_modPack
Object.defineProperties @prototype,
data: { get:@::getData }
isFinished: { get:@::getIsFinished }
modPack: { get:@::getModPack }
# Private Methods ##############################################################################
_assembleNextEntry: ->
entry = @_data.popNextToAssemble()
return unless entry?
for extensions in @_extensions
continue unless extension.canAssemble entry
extension.assemble entry
return
_buildNextEntry: ->
entry = @_data.popNextToBuild()
return unless entry?
for extensions in @_extensions
continue unless extension.canBuild entry
extension.build entry
return
_loadSchema: ->
command = @_lexer.next()
if command.name isnt 'schema' then throw new Error "#{@_fileName} must start with the \"schema\" command"
schema = command.argText
if schema is '1'
@_use new require './parser_extensions/current_pe_v1'
@_use new require './parser_extensions/item_pe_v1'
@_use new require './parser_extensions/mod_pe_v1'
@_use new require './parser_extensions/mod_version_pe_v1'
@_use new require './parser_extensions/recipe_pe_v1'
else
throw new Error "unknown schema version: #{schema}"
_processNextCommand: ->
command = @_lexer.next()
return unless command?
for extension in @_extensions
continue unless extension.canExecute command
extension.execute command
return
@_data.addError command, "unknown command: #{command.name}"
_use: (parserExtension)->
parserExtension.state = @_state
@_extensions.push parserExtension
_validateNextEntry: ->
entry = @_data.popNextToValidate()
return unless entry?
for extensions in @_extensions
continue unless extension.canValidate entry
extension.validate entry
return
@@ -0,0 +1,33 @@
#
# Crafting Guide - parser_command.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
########################################################################################################################
module.exports = class ParserCommand
constructor: (options={})->
if not options.name? then throw 'options.name is required'
if not options.fileName? then throw new 'options.fileName is required'
if not options.lineNumber? then throw 'options.lineNumber is required'
{@name, @fileName, @lineNumber, @argText, @args, @hereDoc} = options
# Property Methods #############################################################################
getLocation: ->
return "#{@fileName}:#{@lineNumber}"
Object.defineProperties @prototype,
location: { get:@::getLocation }
# Object Overrides #############################################################################
toString: ->
result = "#{@fileName}:#{@lineNumber} #{@name}: #{@argText}"
if @hereDoc?
result += " <<- END\n#{@hereDoc}\nEND"
return result
@@ -0,0 +1,134 @@
#
# Crafting Guide - parser_data.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
########################################################################################################################
module.exports = class ParserData
constructor: ->
@clear()
@_toAssemble = []
@_toBuild = []
@_toValidate = []
# Public Methods ###############################################################################
addError: (command, message)->
if not command? then throw new Error 'command is required'
message ?= 'is not valid'
@_errors.push command:command, message:message
create: (command, type, id=null)->
id ?= _.uniqueId "#{type}-"
@clearCurrent type
typeData = @_findOrCreateType type
itemData = @_current = typeData['current'] = typeData[id] = id:id, command:command, type:type
@_toAssemble.push itemData
@_toBuild.push itemData
@_toValidate.push itemData
return itemData
clear: ->
@_current = null
@_data =
mod: {}
modVersion: {}
tutorial: {}
itemGroup: {}
item: {}
multiblock: {}
recipe: {}
@_errors = []
clearCurrent: (type)->
switch type
when 'mod'
delete @_data.mod.current
delete @_data.modVersion.current
delete @_data.tutorial.current
delete @_data.itemGroup.current
delete @_data.item.current
delete @_data.multiblock.current
delete @_data.recipe.current
when 'modVersion'
delete @_data.modVersion.current
delete @_data.tutorial.current
delete @_data.itemGroup.current
delete @_data.item.current
delete @_data.multiblock.current
delete @_data.recipe.current
when 'tutorial'
delete @_data.tutorial.current
when 'itemGroup'
delete @_data.itemGroup.current
delete @_data.item.current
delete @_data.multiblock.current
delete @_data.recipe.current
when 'item'
delete @_data.item.current
delete @_data.multiblock.current
delete @_data.recipe.current
when 'multiblock'
delete @_data.multiblock.current
when 'recipe'
delete @_data.recipe.current
get: (type, id)->
typeData = @_findOrCreateType type
itemData = typeData[id] or null
return itemData
getCurrent: (type)->
return @_current unless type?
typeData = @_findOrCreateType type
current = typeData['current'] or null
return current
popNextToAssemble: ->
return @_toAssemble.pop() or null
popNextToBuild: ->
return @_toBuild.pop() or null
popNextToValidate: ->
return @_toValidate.pop() or null
# Property Methods #############################################################################
getErrors: ->
return @_errors[..]
getIsAssembled: ->
return @_toAssemble.length > 0
getIsBuilt: ->
return @_toBuild.length > 0
getIsValidated: ->
return @_toValidate.length > 0
Object.defineProperties @prototype,
errors: { get:@::getErrors }
isAssembled: { get:@::getIsAssembled }
isBuilt: { get:@::getIsBuilt }
isValidated: { get:@::getIsValidated }
# Private Methods ##############################################################################
_findOrCreateType: (type)->
typeData = @_data[type]
if not typeData?
typeData = @_data[type] = {}
typeData.type = type
return typeData
@@ -0,0 +1,55 @@
#
# Crafting Guide - parser_data.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserCommand = require './parser_command'
ParserData = require './parser_data'
########################################################################################################################
command = state = null
########################################################################################################################
describe 'parser_data.coffee', ->
beforeEach ->
state = new ParserData
command = new ParserCommand name:'alpha', fileName:'file.cg', lineNumber:1
describe 'create', ->
it 'returns an object with the given id', ->
alpha = state.create command, 'alpha', 0
alpha.id.should.equal 0
alpha.command.location.should.equal 'file.cg:1'
it 'returns an object with a newly created id when none is given', ->
alpha = state.create command, 'alpha'
alpha.id.should.not.be.null
alpha.id.should.match /^alpha-[0-9]+$/
alpha.command.location.should.equal 'file.cg:1'
describe 'get', ->
it 'returns a previously created item', ->
alpha1 = state.create command, 'alpha', 0
alpha2 = state.get 'alpha', 0
alpha1.should.equal alpha2
it 'returns null when no such item exists', ->
alpha = state.create command, 'alpha', 0
expect(state.get 'alpha', 1).to.be.null
expect(state.get 'bravo', 0).to.be.null
describe 'getCurrent', ->
it 'returns the new item each time one is created', ->
alpha1 = state.create command, 'alpha', 0
state.getCurrent('alpha').id.should.equal 0
alpha2 = state.create command, 'alpha', 1
state.getCurrent('alpha').id.should.equal 1
@@ -0,0 +1,165 @@
#
# Crafting Guide - parser_extension.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
########################################################################################################################
module.exports = class ParserExtension
constructor: (data, type, commandFunctions)->
@data = data
@_commandFunctions = commandFunctions or {}
@_type = type or null
# Public Methods ###############################################################################
canAssemble: (entry)->
return entry.type is @_type
canBuild: (entry)->
return entry.type is @_type
canExecute: (command)->
_.isFunction @_commandFunctions[command.name]
canValidate: (entry)->
return entry.type is @_type
execute: (command)->
if not @_data? then throw new Error '@data must be defined before executing commands'
func = @_commandFunctions[command.name]
if not func then throw new Error "#{@constructor.name} cannot handle command: #{command.name}"
func.call this, command
return this
# Error Checking Helpers #######################################################################
alreadyExists: (command, type, id)->
obj = @data.get(type, id)
if obj?
@data.addError command, "a #{type} called #{id} has already been declared"
return true
return false
duplicateField: (command, obj, field)->
if obj[field]?
@data.addError command, "duplicate #{field}"
return true
return false
incompatibleField: (command, obj, field, otherCommandName)->
otherCommandName ?= field
if obj[field]?
@data.addError "#{command.name} cannot be used with #{otherCommandName}"
return true
return false
missingCurrent: (command, type)->
obj = @data.getCurrent type
if not obj?
@data.addError command, "you must declare a #{type} before using #{command.name}"
return true
return false
missingArgs: (command)->
if not command.args or command.args.length is 0
@data.addError command, "#{command.name} requires at least one item"
return true
return false
missingArgText: (command)->
if command.argText.length is 0
@data.addError command, "#{command.name} cannot be empty"
return true
return false
invalidVerb: (command, verb)->
if not verb in ['mod', 'item']
@data.addError command, "#{command.name} only applies to either `mod` or `item`"
return true
return false
tooFewArguments: (command, minimum)->
if command.args.length < minimum
@data.addError command, "#{command.name} requires at least #{minimum} arguments"
return true
return false
# Parsing Helpers ##############################################################################
parseBoolean: (command, text)->
return null unless text?
switch text.toLowerCase()
when "yes" then return true
when "no" then return false
@data.addError command, "#{command.name} requires \"yes\" or \"no\""
return null
parseInt: (command, text)->
return null unless text?
value = Number.parseInt text
if isNaN value
@addError command, "#{command.name} requires a number"
return null
return value
# Property Methods #############################################################################
getData: ->
return @_data
setData: (data)->
if not data? then throw new Error 'data cannot be undefined'
@_data = data
Object.defineProperties @prototype,
data: { get:@::getData, set:@::setData }
# Overridable Methods ##########################################################################
assmeble: (entry)->
throw new Error "#{@constructor.name} must override the `assemble` method"
build: (entry)->
throw new Error "#{@constructor.name} must override the `build` method"
validate: (entry)->
throw new Error "#{@constructor.name} must override the `validate` method"
# Private Methods ##############################################################################
_findBuildMethod: (entry)->
methodName = "_build_#{entry.type}"
method = this[methodName]
return null unless _.isFunction method
return method
_findCommandMethod: (command)->
methodName = "_command_#{command.name}"
method = this[methodName]
return null unless _.isFunction method
return method
_findValidateMethod: (command)->
methodName = "_validate_#{command.name}"
method = this[methodName]
return null unless _.isFunction method
return method
@@ -0,0 +1,50 @@
#
# Crafting Guide - current_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class CurrentParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, null,
description: @_command_description
name: @_command_name
officialUrl: @_command_officialUrl
video: @_command_video
# Command Methods ##############################################################################
_command_description: (command)->
current = @data.getCurrent()
return if @duplicateField command, current, 'description'
return if @missingArgText command
current.description = command.argText
_command_name: (command)->
current = @data.getCurrent()
return if @duplicateField command, current, 'name'
return if @missingArgText command
current.name = command.argText
_command_officialUrl: (command)->
current = @data.getCurrent()
return if @duplicateField command, current, 'officialUrl'
return if @missingArgText command
current.officialUrl = command.argText
_command_video: (command)->
current = @data.getCurrent()
return if @missingArgs command
return if @tooFewArguments command, 2
current.videos ?= []
current.videos.push youTubeId:command.args[0], caption:command.args[1..].join(', ')
@@ -0,0 +1,59 @@
#
# Crafting Guide - current_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
CurrentParserExtensionV1 = require './current_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = state = null
########################################################################################################################
describe 'current_pe_v1.coffee', ->
beforeEach ->
state = new ParserData
parser = new CurrentParserExtensionV1 state
state.create {}, 'alpha'
describe 'description', ->
it 'assigns to the current object', ->
parser.execute name:'description', argText:'bravo'
state.getCurrent().description.should.equal 'bravo'
state.errors.should.eql []
describe 'name', ->
it 'assigns to the current object', ->
parser.execute name:'name', argText:'bravo'
state.getCurrent().name.should.equal 'bravo'
state.errors.should.eql []
describe 'officialUrl', ->
it 'assigns to the current object', ->
parser.execute name:'officialUrl', argText:'bravo'
state.getCurrent().officialUrl.should.equal 'bravo'
state.errors.should.eql []
describe 'video', ->
it 'assigns to the current object', ->
parser.execute name:'video', args:['alpha', 'bravo']
videos = state.getCurrent().videos
videos[0].youTubeId.should.equal 'alpha'
videos[0].caption.should.equal 'bravo'
state.errors.should.eql []
it 'can assign multiple videos', ->
parser.execute name:'video', args:['alpha', 'bravo']
parser.execute name:'video', args:['charlie', 'delta']
state.getCurrent().videos.length.should.equal 2
state.errors.should.eql []
@@ -0,0 +1,67 @@
#
# Crafting Guide - item_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class ItemParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, 'item',
gatherable: @_command_gatherable
item: @_command_item
update: @_command_update
# ParserExtensions Overrides ###################################################################
assmeble: (entry)->
throw new Error "#{@constructor.name} must override the `assemble` method"
build: (entry)->
entry.instance = new
throw new Error "#{@constructor.name} must override the `build` method"
validate: (entry)-> # do nothing
# Command Methods ##############################################################################
_command_gatherable: (command)->
return if @missingArgText command
return if @missingCurrent command, 'item'
item = @data.getCurrent 'item'
return if @duplicateField command, item, 'gatherable'
gatherable = @parseBoolean command, command.argText
return unless gatherable?
item.gatherable = gatherable
_command_item: (command)->
return if @missingArgText command
return if @missingCurrent command, 'modVersion'
return if @alreadyExists command, 'item', command.argText
item = @data.create command, 'item', command.argText
item.name = command.argText
item.isUpdate = false
group = @data.getCurrent 'itemGroup'
if group? then item.group = group
item.modVersion = @data.getCurrent 'modVersion'
_command_update: (command)->
return if @missingArgText command
return if @missingCurrent command, 'modVersion'
return if @alreadyExists command, 'item', command.argText
item = @data.create command, 'item', command.argText
item.name = command.argText
item.isUpdate = true
item.modVersion = @data.getCurrent 'modVersion'
@@ -0,0 +1,54 @@
#
# Crafting Guide - item_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ItemParserExtensionV1 = require './item_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = data = null
########################################################################################################################
describe 'item_pe_v1.coffee', ->
beforeEach ->
data = new ParserData
parser = new ItemParserExtensionV1 data
data.create {}, 'modVersion', 0
describe 'gatherable', ->
it 'assigns to the current item', ->
data.create {}, 'item', 1
parser.execute name:'gatherable', argText:'yes'
data.getCurrent('item').gatherable.should.be.true
data.errors.should.eql []
describe 'item', ->
it 'creates a new item', ->
data.create {}, 'itemGroup', 2
parser.execute name:'item', argText:'alpha'
item = data.getCurrent 'item'
item.id.should.equal 'alpha'
item.isUpdate.should.equal false
item.name.should.equal 'alpha'
item.modVersion.id.should.equal 0
item.group.id.should.equal 2
data.getErrors().should.eql []
describe 'update', ->
it 'creates an item update', ->
parser.execute name:'update', argText:'alpha'
item = data.getCurrent 'item'
item.isUpdate.should.equal true
data.getErrors().should.eql []
@@ -0,0 +1,62 @@
#
# Crafting Guide - mod_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class ModParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, 'mod',
author: @_command_author
documentationUrl: @_command_documentationUrl
downloadUrl: @_command_downloadUrl
homePageUrl: @_command_homePageUrl
mod: @_command_mod
# Command Methods ##############################################################################
_command_author: (command)->
return if @missingCurrent command, 'mod'
mod = @data.getCurrent 'mod'
return if @duplicateField command, mod, 'author'
return if @missingArgText command
mod.author = command.argText
_command_documentationUrl: (command)->
current = @data.getCurrent()
return if @duplicateField command, current, 'documentationUrl'
return if @missingArgText command
current.documentationUrl = command.argText
_command_downloadUrl: (command)->
return if @missingCurrent command, 'mod'
mod = @data.getCurrent 'mod'
return if @duplicateField command, mod, 'downloadUrl'
return if @missingArgText command
mod.downloadUrl = command.argText
_command_homePageUrl: (command)->
return if @missingCurrent command, 'mod'
mod = @data.getCurrent 'mod'
return if @duplicateField command, mod, 'homePageUrl'
return if @missingArgText command
mod.homePageUrl = command.argText
_command_mod: (command)->
return if @missingArgText command
return if @alreadyExists command, 'mod', command.argText
@data.create command, 'mod', command.argText
@@ -0,0 +1,58 @@
#
# Crafting Guide - mod_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ModParserExtensionV1 = require './mod_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = data = null
########################################################################################################################
describe 'mod_pe_v1.coffee', ->
beforeEach ->
data = new ParserData
parser = new ModParserExtensionV1 data
data.create {}, 'mod', 0
describe 'author', ->
it 'assigns to the current mod', ->
parser.execute name:'author', argText:'alpha'
data.getCurrent('mod').author.should.equal 'alpha'
data.errors.should.eql []
describe 'documentationUrl', ->
it 'assigns to the current mod', ->
parser.execute name:'documentationUrl', argText:'alpha'
data.getCurrent('mod').documentationUrl.should.equal 'alpha'
data.errors.should.eql []
describe 'downloadUrl', ->
it 'assigns to the current mod', ->
parser.execute name:'downloadUrl', argText:'alpha'
data.getCurrent('mod').downloadUrl.should.equal 'alpha'
data.errors.should.eql []
describe 'homePageUrl', ->
it 'assigns to the current mod', ->
parser.execute name:'homePageUrl', argText:'alpha'
data.getCurrent('mod').homePageUrl.should.equal 'alpha'
data.errors.should.eql []
describe 'mod', ->
it 'creates a new mod', ->
parser.execute name:'mod', argText:'alpha'
mod = data.getCurrent 'mod'
mod.id.should.equal 'alpha'
@@ -0,0 +1,33 @@
#
# Crafting Guide - mod_version_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class ModVersionParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, 'modVersion',
group: @_command_group
version: @_command_version
# Command Methods ##############################################################################
_command_group: (command)->
return if @missingArgText command
return if @missingCurrent command, 'modVersion'
group = @data.create command, 'itemGroup', command.argText
group.modVersion = @data.getCurrent 'modVersion'
_command_version: (command)->
return if @missingArgText command
return if @missingCurrent command, 'mod'
modVersion = @data.create command, 'modVersion', command.argText
modVersion.mod = @data.getCurrent 'mod'
@@ -0,0 +1,41 @@
#
# Crafting Guide - mod_version_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ModVersionParserExtensionV1 = require './mod_version_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = data = null
########################################################################################################################
describe 'mod_version_pe_v1.coffee', ->
beforeEach ->
data = new ParserData
parser = new ModVersionParserExtensionV1 data
data.create {}, 'mod', 0
describe 'group', ->
it 'creates a new item group', ->
data.create {}, 'modVersion', 1
parser.execute name:'group', argText:'alpha'
itemGroup = data.getCurrent('itemGroup')
itemGroup.id.should.equal 'alpha'
itemGroup.modVersion.id.should.equal 1
describe 'version', ->
it 'creates a new mod version', ->
parser.execute name:'version', argText:'alpha'
modVersion = data.getCurrent 'modVersion'
modVersion.id.should.equal 'alpha'
modVersion.mod.id.should.equal 0
@@ -0,0 +1,38 @@
#
# Crafting Guide - multiblock_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class MultiblockParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, 'multiblock',
layer: @_command_layer
multiblock: @_command_multiblock
# Command Methods ##############################################################################
_command_layer: (command)->
return if @missingCurrent command, 'multiblock'
return if @missingArgText command
multiblock = @data.getCurrent 'multiblock'
multiblock.layers ?= []
multiblock.layers.push command.argText
_command_multiblock: (command)->
return if @missingCurrent command, 'item'
item = @data.getCurrent 'item'
return if @duplicateField command, item, 'multiblock'
return if @incompatibleField command, item, 'recipe'
return if @incompatibleField command, item, 'gatherable'
multiblock = @data.create command, 'multiblock'
multiblock.item = item
@@ -0,0 +1,48 @@
#
# Crafting Guide - multiblock_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
MultiblockParserExtensionV1 = require './multiblock_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = data = null
########################################################################################################################
describe 'multiblock_pe_v1.coffee', ->
beforeEach ->
data = new ParserData
parser = new MultiblockParserExtensionV1 data
data.create {}, 'item', 1
describe 'layer', ->
it 'creates a new layer in the current multiblock', ->
data.create {}, 'multiblock', 2
parser.execute name:'layer', argText:'alpha'
multiblock = data.getCurrent 'multiblock'
multiblock.layers.length.should.equal 1
multiblock.layers[0].should.equal 'alpha'
it 'can create multiple layers', ->
data.create {}, 'multiblock', 2
parser.execute name:'layer', argText:'alpha'
parser.execute name:'layer', argText:'bravo'
multiblock = data.getCurrent 'multiblock'
multiblock.layers.should.eql ['alpha', 'bravo']
describe 'multiblock', ->
it 'creates a new multiblock', ->
parser.execute name:'multiblock'
multiblock = data.getCurrent 'multiblock'
multiblock.item.id.should.equal 1
@@ -0,0 +1,143 @@
#
# Crafting Guide - recipe_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class RecipeParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, 'recipe',
extras: @_command_extras
ignoreDuringCrafting: @_command_ignoreDuringCrafting
input: @_command_input
onlyIf: @_command_onlyIf
pattern: @_command_pattern
recipe: @_command_recipe
quantity: @_command_quantity
tools: @_command_tools
# Class Methods ################################################################################
@::PATTERN = /[0-9. ]+/
@::STACK = /^([0-9]+) +(.+)$/
# Error Checking Helpers #######################################################################
invalidPattern: (command)->
match = command.argText.match @PATTERN
if not match?
@data.addError command, "invalid pattern: \"#{command.argText}\""
return true
return false
# Command Methods ##############################################################################
_command_extras: (command)->
return if @missingCurrent command, 'recipe'
return if @missingArgs command
recipe = @data.getCurrent 'recipe'
return if @duplicateField command, 'extras', recipe
recipe.extras = command.args
_command_ignoreDuringCrafting: (command)->
return if @missingArgText command
return if @missingCurrent command, 'recipe'
recipe = @data.getCurrent 'recipe'
return if @duplicateField command, recipe, 'ignoreDuringCrafting'
ignoreDuringCrafting = @parseBoolean command, command.argText
return unless ignoreDuringCrafting?
recipe.ignoreDuringCrafting = ignoreDuringCrafting
_command_input: (command)->
return if @missingCurrent command, 'recipe'
return if @missingArgs command
recipe = @data.getCurrent 'recipe'
multiblock = @data.getCurrent 'multiblock'
target = if multiblock? then multiblock else recipe
return if @duplicateField command, 'input', target
target.input = (@_parseStack(arg) for arg in command.args)
_command_onlyIf: (command)->
return if @missingCurrent command, 'recipe'
return if @missingArgText command
recipe = @data.getCurrent 'recipe'
return if @duplicateField command, recipe, 'condition'
words = command.argText.split ' '
inverted = false
if words[0] is 'not'
inverted = true
words.shift()
verb = words[0]
noun = words[1..].join ' '
return if @invalidVerb command, verb
recipe.condition = verb:verb, noun:noun, inverted:inverted
_command_pattern: (command)->
return if @missingCurrent command, 'recipe'
return if @missingArgText command
return if @invalidPattern command
recipe = @data.getCurrent 'recipe'
return if @duplicateField command, 'pattern', recipe
recipe.pattern = command.argText
_command_quantity: (command)->
return if @missingCurrent command, 'recipe'
return if @missingArgText command
value = @parseInt command, command.argText
return unless value?
recipe = @data.getCurrent 'recipe'
return if @duplicateField command, 'quantity', recipe
recipe.quantity = value
_command_recipe: (command)->
return if @missingCurrent command, 'item'
recipe = @data.create command, 'recipe'
recipe.item = @data.getCurrent 'item'
_command_tools: (command)->
return if @missingCurrent command, 'recipe'
return if @missingArgs command
recipe = @data.getCurrent 'recipe'
return if @duplicateField command, 'tools', recipe
recipe.tools = (@_parseStack(arg) for arg in command.args)
# Custom Parsers ###############################################################################
_parseStack: (text)->
match = text.match @STACK
return name:text, quantity:1 unless match?
quantity = parseFloat match[1]
if isNaN quantity then quantity = 1
name = match[2]
return name:name, quantity:quantity
@@ -0,0 +1,125 @@
#
# Crafting Guide - recipe_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
RecipeParserExtensionV1 = require './recipe_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = data = null
########################################################################################################################
describe 'recipe_pe_v1.coffee', ->
beforeEach ->
data = new ParserData
parser = new RecipeParserExtensionV1 data
data.create {}, 'item', 0
describe 'extras', ->
it 'assigns to the current recipe', ->
data.create {}, 'recipe'
parser.execute name:'extras', args:['bravo']
recipe = data.getCurrent 'recipe'
recipe.extras.should.eql ['bravo']
data.errors.should.eql []
describe 'ignoreDuringCrafting', ->
it 'assigns to the current recipe', ->
data.create {}, 'recipe'
parser.execute name:'ignoreDuringCrafting', argText:'yes'
recipe = data.getCurrent 'recipe'
recipe.ignoreDuringCrafting.should.equal true
data.errors.should.eql []
describe 'input', ->
it 'correctly reads a single item', ->
data.create {}, 'recipe'
parser.execute name:'input', args:['bravo']
recipe = data.getCurrent 'recipe'
recipe.input.should.eql [name:'bravo', quantity:1]
data.errors.should.eql []
it 'correctly reads multiple items', ->
data.create {}, 'recipe'
parser.execute name:'input', args:['bravo', 'charlie']
recipe = data.getCurrent 'recipe'
recipe.input.should.eql [{name:'bravo', quantity:1}, {name:'charlie', quantity:1}]
data.errors.should.eql []
it 'correctly reads a stack', ->
data.create {}, 'recipe'
parser.execute name:'input', args:['10 bravo']
recipe = data.getCurrent 'recipe'
recipe.input.should.eql [name:'bravo', quantity:10]
data.errors.should.eql []
describe 'onlyIf', ->
it 'assigns to the current recipe', ->
data.create {}, 'recipe'
parser.execute name:'onlyIf', argText:'not mod Alpha'
recipe = data.getCurrent 'recipe'
recipe.condition.should.eql verb:'mod', noun:'Alpha', inverted:true
data.errors.should.eql []
describe 'pattern', ->
it 'assigns to the current recipe', ->
data.create {}, 'recipe'
parser.execute name:'pattern', argText:'00. 00. ...'
recipe = data.getCurrent 'recipe'
recipe.pattern.should.equal '00. 00. ...'
data.errors.should.eql []
it 'rejects an invalid pattern', ->
data.create {}, 'recipe'
parser.execute name:'pattern', argText:'alpha'
recipe = data.getCurrent 'recipe'
expect(recipe.pattern).to.be.undefined
(e.message for e in data.errors).should.eql ['invalid pattern: "alpha"']
describe 'quantity', ->
it 'assigns to the current recipe', ->
data.create {}, 'recipe'
parser.execute name:'quantity', argText:'42'
recipe = data.getCurrent 'recipe'
recipe.quantity.should.equal 42
data.errors.should.eql []
describe 'recipe', ->
it 'creates a new recipe', ->
parser.execute name:'recipe'
recipe = data.getCurrent 'recipe'
recipe.item.id.should.equal 0
data.errors.should.eql []
describe 'tools', ->
it 'assigns to the current recipe', ->
data.create {}, 'recipe'
parser.execute name:'tools', args:['bravo']
recipe = data.getCurrent 'recipe'
recipe.tools.should.eql [name:'bravo', quantity:1]
data.errors.should.eql []
@@ -0,0 +1,51 @@
#
# Crafting Guide - tutorial_pe_v1.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
ParserExtension = require '../parser_extension'
########################################################################################################################
module.exports = class TutorialParserExtensionV1 extends ParserExtension
constructor: (data)->
super data, 'tutorial',
content: @_command_content
section: @_command_section
title: @_command_title
tutorial: @_command_tutorial
# Command Methods ##############################################################################
_command_content: (command)->
return if @missingCurrent command, 'tutorial'
return if @missingArgText command
section = @data.getCurrent 'section'
section.content = command.argText
_command_section: (command)->
return if @missingCurrent command, 'tutorial'
section = @data.create command, 'section'
section.tutorial = @data.getCurrent 'tutorial'
_command_title: (command)->
return if @missingCurrent command, 'section'
return if @missingArgText command
section = @data.getCurrent 'section'
return if @duplicateField command, section, 'title'
section.title = command.argText
_command_tutorial: (command)->
return if @missingCurrent command, 'modVersion'
return if @missingArgText command
tutorial = @data.create command, 'tutorial', command.argText
tutorial.name = command.argText
tutorial.modVersion = @data.getCurrent 'modVersion'
@@ -0,0 +1,63 @@
#
# Crafting Guide - tutorial_pe_v1.test.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
TutorialParserExtensionV1 = require './tutorial_pe_v1'
ParserData = require '../parser_data'
########################################################################################################################
parser = data = null
########################################################################################################################
describe 'tutorial_pe_v1.coffee', ->
beforeEach ->
data = new ParserData
parser = new TutorialParserExtensionV1 data
data.create {}, 'modVersion', 1
describe 'content', ->
it 'assigns content to the current section', ->
data.create {}, 'tutorial', 2
data.create {}, 'section', 3
parser.execute name:'content', argText:'alpha'
data.errors.should.eql []
section = data.getCurrent 'section'
section.content.should.equal 'alpha'
describe 'section', ->
it 'creates a section in the current tutorial', ->
data.create {}, 'tutorial', 2
parser.execute name:'section'
data.errors.should.eql []
section = data.getCurrent 'section'
section.tutorial.id.should.equal 2
describe 'title', ->
it 'assigns a title to the current tutorial', ->
data.create {}, 'tutorial', 2
data.create {}, 'section', 3
parser.execute name:'title', argText:'alpha'
data.errors.should.eql []
section = data.getCurrent 'section'
section.title.should.equal 'alpha'
describe 'tutorial', ->
it 'creates a new tutorial', ->
parser.execute name:'tutorial', argText:'alpha'
data.errors.should.eql []
tutorial = data.getCurrent 'tutorial'
tutorial.name.should.equal 'alpha'
-1
View File
@@ -40,7 +40,6 @@ module.exports = class CraftPage extends BaseModel
inventory.each (stack)=>
item = @modPack.findItem stack.itemSlug, enableAsNeeded:true
return unless item? and item.isCraftable
@craftsman.want.add stack.itemSlug, stack.quantity
inventory.remove stack.itemSlug
-86
View File
@@ -1,86 +0,0 @@
#
# Crafting Guide - file_cache.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
########################################################################################################################
module.exports = class FileCache extends Backbone.Events
@::FILE_TOKEN = '#file='
constructor: (url)->
@_ajax = $.ajax
@_files = {}
@_loading = w.resolve(true)
if url? then @loadArchive(url).catch(->)
# Public Methods ###############################################################################
getFile: (file)->
return null unless @hasFile file
return @_files[file]
hasFile: (file)->
return @_files[file]?
loadArchive: (url)->
if not _.isFunction(@_ajax) then throw new Error '$.ajax is required'
newLoading = w.promise (resolve, reject)=>
@_ajax
url: url
dataType: 'text'
success: (text, status, xhr)=> resolve @_onLoadSuccess text, status, xhr
error: (xhr, status, error)=> resolve @_onLoadError error, status, xhr
@_loading = w.join @_loading, newLoading
# Property Methods #############################################################################
Object.defineProperties @prototype,
loading:
get: -> return @_loading
# Private Methods ##############################################################################
_onLoadError: (error, status, xhr)->
console.error "Failed to load archive: #{xhr.responseText}"
return false
_onLoadSuccess: (text, status, xhr)->
return @_parseArchive text
_parseArchive: (text)->
file = null
tokenIndex = nameStartIndex = nameEndIndex = fileStartIndex = fileEndIndex = 0
fileCount = 0
while true
tokenIndex = text.indexOf @FILE_TOKEN, fileStartIndex
if tokenIndex is -1
if file?
@_files[file] = text.substring fileStartIndex, text.length
fileCount += 1
break
if file?
fileEndIndex = tokenIndex
@_files[file] = text.substring fileStartIndex, fileEndIndex
fileCount += 1
nameStartIndex = tokenIndex + @FILE_TOKEN.length
nameEndIndex = text.indexOf '\n', nameStartIndex
if nameEndIndex is -1
file = text.substring nameStartIndex, text.length
@_files[file] = ''
break
file = text.substring nameStartIndex, nameEndIndex
fileStartIndex = nameEndIndex + '\n'.length
return fileCount > 0
@@ -1,72 +0,0 @@
FileCache = require './file_cache'
########################################################################################################################
describe 'file_cache.coffee', ->
beforeEach ->
@cache = new FileCache
makeTestAjax = (text)->
return (options)->
options.success text, 'success', {url:options.url}
it 'can load an archive with a single file', ->
@cache._ajax = makeTestAjax '#file=alpha\nbravo charlie delta'
@cache.loadArchive 'http://test'
.then =>
@cache.hasFile('alpha').should.equal true
@cache.getFile('alpha').should.equal 'bravo charlie delta'
it 'can load an archive with multiple files', ->
@cache._ajax = makeTestAjax '#file=alpha\nbravo charlie\n#file=delta\necho foxtrot'
@cache.loadArchive 'http://test'
.then =>
@cache.hasFile('alpha').should.equal true
@cache.getFile('alpha').should.equal 'bravo charlie\n'
@cache.hasFile('delta').should.equal true
@cache.getFile('delta').should.equal 'echo foxtrot'
it 'can load an archive without newlines', ->
@cache._ajax = makeTestAjax '#file=alpha\nbravo charlie#file=delta\necho foxtrot'
@cache.loadArchive 'http://test'
.then =>
@cache.hasFile('alpha').should.equal true
@cache.getFile('alpha').should.equal 'bravo charlie'
@cache.hasFile('delta').should.equal true
@cache.getFile('delta').should.equal 'echo foxtrot'
it 'can cope with an empty file in the middle of an archive', ->
@cache._ajax = makeTestAjax '#file=alpha\nbravo charlie\n#file=delta\n#file=echo\nfoxtrot golf'
@cache.loadArchive 'http://test'
.then =>
@cache.hasFile('alpha').should.equal true
@cache.getFile('alpha').should.equal 'bravo charlie\n'
@cache.hasFile('delta').should.equal true
@cache.getFile('delta').should.equal ''
@cache.hasFile('echo').should.equal true
@cache.getFile('echo').should.equal 'foxtrot golf'
it 'can cope with an empty file at the end of an archive', ->
@cache._ajax = makeTestAjax '#file=alpha\nbravo charlie\n#file=delta'
@cache.loadArchive 'http://test'
.then =>
@cache.hasFile('alpha').should.equal true
@cache.getFile('alpha').should.equal 'bravo charlie\n'
@cache.hasFile('delta').should.equal true
@cache.getFile('delta').should.equal ''
it 'can load multiple, non-overlapping archives', ->
@cache._ajax = makeTestAjax '#file=alpha\nbravo charlie\n#file=delta\necho foxtrot'
@cache.loadArchive 'http://test'
@cache._ajax = makeTestAjax '#file=golf\nhotel india'
@cache.loadArchive 'http://test2'
.then =>
@cache.hasFile('alpha').should.equal true
@cache.getFile('alpha').should.equal 'bravo charlie\n'
@cache.hasFile('delta').should.equal true
@cache.getFile('delta').should.equal 'echo foxtrot'
@cache.hasFile('golf').should.equal true
@cache.getFile('golf').should.equal 'hotel india'
-1
View File
@@ -47,7 +47,6 @@ module.exports = class ItemPage extends BaseModel
mod.eachRecipe (recipe)=>
if isAcceptable recipe
for outputStack in recipe.output
continue if recipe.isPassThroughFor outputStack.itemSlug
outputItem = @modPack.findItem outputStack.itemSlug, includeDisabled:true
result[outputItem.slug] = outputItem
@@ -7,6 +7,7 @@
.content.view__browse_page
.left
.view__adsense
.right
.tile_container
+4 -22
View File
@@ -10,32 +10,14 @@
.right {
.tile_container {
display : flex;
flex-wrap : wrap;
display : flex;
flex-wrap : wrap;
justify-content : space-between;
.view__mod_tile {
flex : 1 1 calc(50% - 12px); // $size-margin-large / 2
margin-right : $size-margin-medium;
max-width : calc(50% - 12px); // $size-margin-large / 2
margin-bottom : $size-margin-large;
}
}
}
}
@include screen-size(tablet) {
.view__browse_page {
.right {
.tile_container {
flex-direction: column;
flex-wrap: nowrap;
align-items: stretch;
.view__mod_tile {
flex: 0 0 auto;
margin-right: 0;
width: 100%;
}
}
}
}
}
@@ -5,6 +5,7 @@
# All rights reserved.
#
AdsenseController = require '../common/adsense/adsense_controller'
ModTileController = require './mod_tile/mod_tile_controller'
PageController = require '../page_controller'
@@ -31,6 +32,8 @@ module.exports = class BrowsePageController extends PageController
# BaseController Overrides #####################################################################
onDidRender: ->
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
@$tileContainer = @$('.tile_container')
refresh: ->
@@ -6,14 +6,16 @@
//
.view__mod_tile {
min-width : $size-minecraft-block * 9;
min-width : $size-minecraft-block * 9;
display: flex;
display : flex;
justify-content : stretch;
align-items : stretch;
a {
@include floating-panel;
flex: 1 1 auto;
max-width: 100%;
@include floating-panel;
border : $size-border-medium solid $color-background-panel;
color : $color-text;
text-decoration : none;
@@ -37,7 +39,6 @@
overflow : hidden;
.title {
max-width: 0;
margin-bottom : $size-margin-medium;
font-family : $font-family-header;
@@ -85,20 +86,3 @@
}
}
}
@include screen-size(mobile) {
.view__mod_tile {
min-width: 0;
a {
& > .left {
flex: 0 0 $size-minecraft-block * 2;
img {
width: $size-minecraft-block * 2;
max-height: $size-minecraft-block * 2;
}
}
}
}
}
@@ -12,7 +12,6 @@ BaseController = require '../../base_controller'
module.exports = class ModTileController extends BaseController
constructor: (options={})->
options.tagName = 'section'
options.templateName = 'browse_page/mod_tile'
super options
+8 -52
View File
@@ -6,64 +6,20 @@
//
.view__adsense {
left : 50%;
margin : $size-margin-large 0;
position : relative;
transform : translate(-50%);
.skyscraper {
height : $size-skyscraper-height;
width : $size-skyscraper-width;
position : relative;
ins {
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
}
&.large-mobile-banner {
height : $size-ad-large-mobile-banner-height;
width : $size-ad-large-mobile-banner-width;
&.placeholder:before {
font-size : $font-size-large;
}
}
&.leaderboard {
height : $size-ad-leaderboard-height;
width : $size-ad-leaderboard-width;
&.placeholder:before {
font-size : $font-size-xxlarge;
}
}
&.skyscraper {
height : $size-ad-skyscraper-height;
width : $size-ad-skyscraper-width;
&.placeholder:before {
transform : rotate(90deg);
font-size : $font-size-xxlarge;
}
border : 0.1em solid $color-gray-faint;
margin : $size-margin-large 0;
&:first-child {
margin-top : 0;
}
}
&.placeholder {
border : $size-border-small solid $color-gray-faint;
display : flex;
align-items : center;
justify-content : center;
&:before {
color : $color-gray-faint;
content : "GOOGLE ADSENSE";
font-family : $font-family-control;
letter-spacing : $size-margin-small;
white-space : nowrap;
ins {
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
}
}
}
.view__mod_tile + .view__adsense {
margin-top : 0;
}
@@ -5,132 +5,63 @@
# All rights reserved.
#
BaseController = require '../../base_controller'
########################################################################################################################
module.exports = class AdsenseController
module.exports = class AdsenseController extends BaseController
constructor: (options={})->
options.templateName = 'common/adsense'
super options
constructor: ->
@_adsEnabled = global.env in c.productionEnvs
@_adsPlaced = 0
@_adCount = 0
@_waiting = false
# Public Methods ###############################################################################
fillAdPositions: ->
adType = @_computeAdType()
maxAds = c.adsense[adType].slotIds.length - @_adsPlaced
return if maxAds is 0
if @_adCount > 0
return unless @$sidebar and @$mainBody
return unless @_adCount < @_computeMaxAds()
return unless @_adCount < c.adsense.slotIds.length
logger.verbose => "Looking to place #{maxAds} more #{adType} ads..."
$adContainer = $('<div class="skyscraper"></div>')
@$el.append $adContainer
if adType is 'skyscraper'
placedNewAd = @_createSkyscraperPositions()
else
placedNewAd = @_createBannerPositions adType
if @_adsEnabled
$ad = $('<ins class="adsbygoogle"></ins>')
$ad.attr 'data-ad-client', c.adsense.clientId
$ad.attr 'data-ad-slot', c.adsense.slotIds[@_adCount]
$adContainer.append $ad
@_loadAds()
if @_adsPlaced < c.adsense[adType].slotIds.length
duration = if placedNewAd then 0 else c.adsense.readinessCheckInterval
_.delay (=> @fillAdPositions()), duration
else
logger.verbose => "All #{adType} ad positions filled"
@_adCount += 1
_.defer => @fillAdPositions()
reset: ->
@_adsPlaced = 0
# BaseController Overrides #####################################################################
onDidRender: ->
@$mainBody = $('.page > .content > .right')
@$sidebar = $('.page > .content > .left')
super
refresh: ->
@_waitForPageReadiness()
super
# Private Methods #############################################################################
_computeAdType: ->
return c.adsense.adTypeMap[c.screen.type.compute()]
_computeAdCount: (adType)->
result = c.adsense[adType].slotIds.length
if c.screen.type.compute() is c.screen.type.desktop
$sidebar = $('.page > .content > .left')
return 0 unless $sidebar?.length
available = $sidebar.height() + c.adsense.skyscraper.margin
available = Math.floor available / (c.adsense.skyscraper.height + c.adsense.skyscraper.margin)
result = Math.min available, result
_computeMaxAds: ->
return null unless @$sidebar
result = Math.floor @$sidebar.height() / (c.adsense.skyscraper.height + c.adsense.skyscraper.margin)
return result
_createSkyscraperPositions: ->
$sidebar = $('.page > .content > .left')
remaining = @_computeAdCount 'skyscraper'
remaining -= @_adsPlaced
placedNewAd = false
while remaining > 0
$el = $('<div class="view__adsense"></div>')
$el.addClass c.adsense.skyscraper.cssClass
if @_adsEnabled
$ad = $('<ins class="adsbygoogle"></ins>')
$ad.attr 'data-ad-client', c.adsense.clientId
$ad.attr 'data-ad-slot', c.adsense.skyscraper.slotIds[@_adsPlaced]
$el.append $ad
else
$el.addClass 'placeholder'
$sidebar.append $el
placedNewAd = true
@_adsPlaced += 1
remaining -= 1
logger.info => "Placed skyscraper ad ##{@_adsPlaced}"
@_loadAd()
return placedNewAd
_createBannerPositions: (adType)->
$pageContent = $('.page > .content > .right')
remaining = @_computeAdCount adType
remaining -= @_adsPlaced
sectionIndex = 0
placedNewAd = false
for sectionEl in $pageContent.find('section')
break unless remaining > 0
$section = $(sectionEl)
continue unless $section.is ':visible'
sectionIndex += 1
continue if sectionIndex % 2 is 0
continue if $section.next().hasClass 'view__adsense'
$el = $('<div class="view__adsense"></div>')
$el.addClass c.adsense[adType].cssClass
if @_adsEnabled
$ad = $('<ins class="adsbygoogle"></ins>')
$ad.attr 'data-ad-client', c.adsense.clientId
$ad.attr 'data-ad-slot', c.adsense[adType].slotIds[@_adsPlaced]
$el.append $ad
else
$el.addClass 'placeholder'
$section.after $el
placedNewAd = true
@_adsPlaced += 1
remaining -= 1
logger.info => "Placed #{adType} ad ##{@_adsPlaced}"
@_loadAd()
return placedNewAd
_isTooClose: ($priorEl, $el)->
return false if c.screen.type.compute() is c.screen.type.desktop
return false if not $priorEl?
priorBottom = $priorEl.position().top + $priorEl.height()
currentTop = $el.position().top
return priorBottom + c.adsense.minimumDistance > currentTop
_loadAd: ->
_loadAds: ->
return unless @_adsEnabled
logger.info "Loading Google AdSense ads"
logger.info "Loading #{@_adCount} Google AdSense ads"
try
global.adsbygoogle ||= []
global.adsbygoogle.push {}
@@ -139,12 +70,11 @@ module.exports = class AdsenseController
_waitForPageReadiness: ->
return if @_waiting
adCount = @_computeAdCount @_computeAdType()
if adCount is 0
if @$sidebar.height() < c.adsense[@model].height / 2
logger.verbose "Adsense is waiting for room to insert ads"
@_waiting = true
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.adsense.readinessCheckInterval
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.duration.normal
else
logger.verbose "Adsense is ready to fill ads"
@fillAdPositions()
@@ -9,5 +9,5 @@
.item_container
.empty_placeholder nothing
.buttons
.button.item-selector: .bezel: p add<span class="full"> an item</span>...
.button.item-selector: .bezel: p add an item...
.button.clear: .bezel: p clear
@@ -38,24 +38,12 @@
margin-top: $size-margin-large;
.item-selector {
flex : 80%;
flex : 85%;
margin-right : $size-margin-medium;
@include screen-size(mobile) {
flex: 50%;
.full {
display: none;
}
}
}
.clear {
flex : 20%;
@include screen-size(mobile) {
flex: 50%;
}
flex : 15%;
}
}
}
@@ -73,12 +61,8 @@
.quantity {
padding-right : $size-margin-medium;
input {
width : 3em;
font-size : $font-size-xlarge;
@include screen-size(mobile) {
width : 2em;
}
width: 3em;
font-size : $font-size-xlarge;
}
}
@@ -17,23 +17,3 @@
}
}
}
@include screen-size(tablet) {
.view__item_group {
.panel {
.view__item_tile {
width: calc(50% - 8px); // $size-margin-xsmall * 2
}
}
}
}
@include screen-size(mobile) {
.view__item_group {
.panel {
.view__item_tile {
width: 100%;
}
}
}
}
@@ -10,7 +10,7 @@
overflow: visible;
display: flex;
align-items: flex-start;
align-items: center;
img {
height: $size-minecraft-block;
@@ -22,19 +22,9 @@
font-family: $font-family-text;
font-size: $font-size-medium;
line-height: $size-minecraft-block;
overflow: hidden;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
@include screen-size(mobile) {
.view__item_tile {
a {
p {
white-space: normal;
}
}
}
}
@@ -48,6 +48,7 @@
}
&.selected {
flex : 0 0 auto;
background : $color-active-hover;
.name {
@@ -9,6 +9,6 @@
.view__item_selector_popup
.search
img(src='/images/search.png')
input(placeholder='enter an item name')
input(placeholder='enter the name of an item')
img.close(src='/images/close.png')
.results
@@ -9,7 +9,6 @@
}
.view__item_selector_popup {
max-width: $size-minecraft-block * 20;
height : 66%;
background : $color-background-panel;
@@ -36,7 +35,6 @@
input {
flex: 1 1 100%;
min-width: $font-size-large * 10;
background: none;
border: 0;
@@ -52,15 +50,8 @@
.results {
flex: 1 1 100%;
position: relative;
overflow-x: hidden;
overflow-y: scroll;
}
}
@include screen-size(mobile) {
.view__item_selector_popup {
position: fixed; top: 0; left: 0; height: 100%; width: 100%;
}
}
@@ -71,10 +71,10 @@ module.exports = class ItemSelectorController extends BaseController
when c.key.enter
@_chooseSelected()
return false
when c.key.downArrow
when c.key.down
@_selectNext()
return false
when c.key.upArrow
when c.key.up
@_selectPrevious()
return false
@@ -82,7 +82,6 @@ module.exports = class ItemSelectorController extends BaseController
@model.hint = @$hintField.val()
@tryRefresh()
@onResultSelected @_elementControllers[0]
return false
onPopupClicked: (event)->
return false
@@ -91,7 +90,6 @@ module.exports = class ItemSelectorController extends BaseController
@_session.resolve controller.model.slug
@_session = null
@_close()
return false
onResultSelected: (controller)->
for c in @_elementControllers
@@ -100,8 +98,6 @@ module.exports = class ItemSelectorController extends BaseController
if controller?
controller.selected = true
return false
onScreenClicked: (event)->
@_close()
return false
@@ -173,8 +169,6 @@ module.exports = class ItemSelectorController extends BaseController
@_elementControllers.pop().remove()
_selectNext: ->
return unless @_elementControllers.length > 0
for i in [0...(@_elementControllers.length - 1)] by 1
controller = @_elementControllers[i]
nextController = @_elementControllers[i + 1]
@@ -185,13 +179,7 @@ module.exports = class ItemSelectorController extends BaseController
@_showElement nextController.$el
return
controller = @_elementControllers[0]
controller.selected = true
@_showElement controller.$el
_selectPrevious: ->
return unless @_elementControllers.length > 0
for i in [1...@_elementControllers.length] by 1
previousController = @_elementControllers[i - 1]
controller = @_elementControllers[i]
@@ -202,10 +190,6 @@ module.exports = class ItemSelectorController extends BaseController
@_showElement previousController.$el
return
controller = @_elementControllers[@_elementControllers.length - 1]
controller.selected = true
@_showElement controller.$el
_showElement: ($el)->
top = $el.position().top + @$resultsContainer.scrollTop()
@$resultsContainer.animate {scrollTop:top}, c.duration.snap
@$resultsContainer.css 'scrollTop', top
@@ -128,15 +128,3 @@
}
}
}
@include screen-size(mobile) {
.view__markdown_section {
.creating {
padding-bottom: $size-margin-xlarge;
}
.footer {
display: none;
}
}
}
@@ -60,15 +60,16 @@ module.exports = class MarkdownSectionController extends BaseController
# Event Methods ################################################################################
onCancelClicked: (event)->
event.preventDefault()
@model = @_originalModel
@resetToDefaultState()
@_imageListController.reset()
@_updatePreview()
return false
onEditClicked: (event)->
return false unless @editable
event.preventDefault()
return unless @editable
@_originalModel = @model
@state = @State.waiting
@@ -80,22 +81,22 @@ module.exports = class MarkdownSectionController extends BaseController
@state = @State.appologizing
w(true).delay(@_confirmDuration).then => @resetToDefaultState()
return false
onQuestionClicked: (event)->
event.preventDefault()
@_enterFeedback ''
return false
onPreviewClicked: (event)->
event.preventDefault()
@_updatePreview()
@state = @State.previewing
return false
onReturnClicked: (event)->
event.preventDefault()
@state = @State.editing
return false
onSaveClicked: (event)->
event.preventDefault()
@state = @State.waiting
@_endEditing()
.then =>
@@ -108,11 +109,9 @@ module.exports = class MarkdownSectionController extends BaseController
.then =>
@state = @State.viewing
return false
onTextChanged: (event)->
event.preventDefault()
@model = @$textarea.val()
return false
# Property Methods #############################################################################
+3 -7
View File
@@ -9,16 +9,12 @@
display: flex;
.input {
flex: 0 0 auto;
flex: 1 1 50%;
align-items : center;
display : flex;
flex-direction : column;
.view__crafting_grid {
flex: 0 0 auto;
}
.tool {
margin-top : $size-margin-small;
@@ -28,7 +24,7 @@
}
.output {
flex: 1 1 100%;
flex: 1 1 50%;
margin-left: $size-margin-medium;
display : flex;
@@ -46,4 +42,4 @@
margin-top : $size-margin-medium;
}
}
}
}
+1 -3
View File
@@ -6,9 +6,7 @@
//-
.view__stack
.quantity
input
.hover
.quantity: input
.icon: img(src='')
.name: a
.action.first(style="display: none")
-43
View File
@@ -21,7 +21,6 @@
.quantity {
padding-right : $size-margin-medium;
position : relative;
text-align : right;
input {
@@ -33,38 +32,6 @@
font-size : $font-size-large;
text-align : right;
}
.hover {
$backgroundColor: fade-out($color-black, 0.25);
display: none;
position: absolute; left: 50%; top: -($font-size-large * 2);
transform: translateX(-50%);
background: $backgroundColor;
color: $color-white;
font-family: $font-family-control;
font-size: $font-size-medium;
padding: $size-margin-small;
z-index: $layer-dialog;
&:after {
position: absolute; top: 100%; left: calc(50% - 9px); // $font-size-large / 2
content: "";
width: 0;
height: 0;
border-style: solid;
border-width: $size-margin-medium $size-margin-medium 0 $size-margin-medium;
border-color: $backgroundColor transparent transparent transparent;
}
}
&:hover {
.hover.enabled {
display: block;
}
}
}
.icon {
@@ -117,13 +84,3 @@
}
}
}
@include screen-size(mobile) {
.view__stack.editable {
.quantity {
input {
width : 1.5em;
}
}
}
}
@@ -19,7 +19,6 @@ Events:
module.exports = class StackController extends BaseController
@::MAX_QUANTITY = 9999
@::STACK_SIZE = 64
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
@@ -108,7 +107,6 @@ module.exports = class StackController extends BaseController
@$image = @$('.icon img')
@$nameLink = @$('.name a')
@$quantityField = @$('.quantity input')
@$quantityHover = @$('.quantity .hover')
@$secondAction = @$('.action.second')
@$secondButton = @$('.action.second .button')
super
@@ -126,7 +124,6 @@ module.exports = class StackController extends BaseController
@$quantityField.val quantityText
@_refreshButtons()
@_refreshHover()
if @_editable
@$el.addClass 'editable'
@@ -172,20 +169,6 @@ module.exports = class StackController extends BaseController
when 'remove' then $label.text '-'
when 'up' then $label.text '⬆︎'
_refreshHover: ->
if @model.quantity > @STACK_SIZE
remainder = @model.quantity % @STACK_SIZE
stacks = (@model.quantity - remainder) / @STACK_SIZE
text = "#{stacks}x#{@STACK_SIZE}"
text += "+#{remainder}" if remainder > 0
@$quantityHover.text text
@$quantityHover.addClass 'enabled'
else
@$quantityHover.text ''
@$quantityHover.removeClass 'enabled'
_updateButtonType: ($button, type)->
for currentType in ['check', 'down', 'remove', 'up']
$button.removeClass currentType
@@ -1,18 +0,0 @@
//-
//- Crafting Guide - twitter.jade
//-
//- Copyright © 2014-2016 by Redwood Labs
//- All rights reserved.
//-
.view__twitter
.left
img(src="/images/twitter.png")
.right
.instructions Follow us for updates!
a(
href="https://twitter.com/Crafting_Guide"
class="twitter-follow-button"
data-show-count="false"
data-size="large"
) Follow @Crafting_Guide
@@ -1,62 +0,0 @@
//
// Crafting Guide - news_page.scss
//
// Copyright © 2014-2016 by Redwood Labs
// All rights reserved.
//
.view__twitter {
display: flex;
& > .left {
flex: 1 1 50%;
display: flex;
align-items: center;
justify-content: center;
img {
height: $size-minecraft-block * 4;
width: $size-minecraft-block * 4;
}
}
& > .right {
flex: 1 1 50%;
display: flex;
flex-direction: column;
align-items: center;
.instructions {
flex: 1 1 50%;
margin-bottom: $size-margin-medium;
margin-top: $size-margin-medium;
font-family: $font-family-header;
font-size: $font-size-xlarge;
text-align: center;
}
iframe {
flex: 1 1 50%;
}
}
}
@include screen-size(mobile) {
.view__twitter {
& > .left {
img {
height: $size-minecraft-block * 3;
width: $size-minecraft-block * 3;
}
}
& > .right {
.instructions {
font-size: $font-size-xlarge;
}
}
}
}
+1 -1
View File
@@ -6,5 +6,5 @@
//-
.view__video
iframe(src="", frameborder="0", allowfullscreen="true")
iframe(width="356", height="267", src="", frameborder="0", allowfullscreen="true")
.caption: p
-4
View File
@@ -11,10 +11,6 @@
align-items: center;
justify-content: center;
iframe {
max-width: 100%;
}
.caption {
margin-top: $size-margin-small;
+18 -10
View File
@@ -5,8 +5,16 @@
//- All rights reserved.
//-
- var quarryHref = '/craft/quarry'
- var quarry
- var quantumSuitHref = '/craft/quantumsuit_bodyarmor:quantumsuit_boots:quantumsuit_helmet:quantumsuit_leggings'
- var ic2Href = '/browse/industrial_craft_2'
- var solarPanelHref = '/craft/solar_panel_vi'
- var solarFluxHref = '/browse/solar_flux'
.content.view__craft_page
.left
.view__adsense
.right
section.instructions
@@ -18,19 +26,19 @@
p.
Not sure what you'd like to make? Try one of these:
.items
a(href="/" data-slug="potion_of_strength_ii_1_30")
a(href="/craft/potion_of_strength_ii_1_30")
img(src="/data/minecraft/items/potion_of_strength_ii_1_30/icon.png")
a(href="/" data-slug="quarry")
a(href="/craft/quarry")
img(src="/data/buildcraft/items/quarry/icon.png")
a(href="/" data-slug="quantumsuit_bodyarmor:quantumsuit_boots:quantumsuit_helmet:quantumsuit_leggings")
a(href="/craft/quantumsuit_bodyarmor:quantumsuit_boots:quantumsuit_helmet:quantumsuit_leggings")
img(src="/data/ic2_classic/items/quantumsuit_helmet/icon.png")
a(href="/" data-slug="solar_panel_vi")
a(href="/craft/solar_panel_vi")
img(src="/data/solar_flux/items/solar_panel_vi/icon.png")
a(href="/" data-slug="crystal_chest")
a(href="/craft/crystal_chest")
img(src="/data/iron_chests/items/crystal_chest/icon.png")
a(href="/" data-slug="digital_miner")
a(href="/craft/digital_miner")
img(src="/data/mekanism/items/digital_miner/icon.png")
a(href="/" data-slug="2.tesseract")
a(href="/craft/2.tesseract")
img(src="/data/thermal_expansion/items/tesseract/icon.png")
section.want
@@ -43,6 +51,8 @@
.panel
.view__inventory.large.editable
section.view__craftsman_working
section.need
h2 Need to Gather
.panel
@@ -50,6 +60,4 @@
section.steps
h2 Steps
.panel
section.view__craftsman_working
.panel
@@ -49,14 +49,4 @@
}
}
}
&.outdated {
section.need .panel {
background: $color-white url('/images/panel-background-outdated.png') repeat;
}
section.steps .panel {
background: $color-white url('/images/panel-background-outdated.png') repeat;
}
}
}
@@ -5,21 +5,19 @@
# All rights reserved.
#
AdsenseController = require '../common/adsense/adsense_controller'
BaseController = require '../base_controller'
CraftPage = require '../../models/site/craft_page'
Craftsman = require '../../models/crafting/craftsman'
CraftsmanWorkingController = require './craftsman_working/craftsman_working_controller'
InventoryController = require '../common/inventory/inventory_controller'
PageController = require '../page_controller'
SimpleInventory = require '../../models/crafting/simple_inventory'
StepController = require './step/step_controller'
########################################################################################################################
module.exports = class CraftPageController extends PageController
@::SCROLL_BUFFER = 8 # px
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'
@@ -35,9 +33,10 @@ module.exports = class CraftPageController extends PageController
@_router = options.router
@_storage = options.storage
# Event Methods ################################################################################
# c.event Methods ################################################################################
onHaveInventoryChanged: ->
logger.warning => "storing have: #{@model.craftsman.have.unparse()}"
@_storage.store 'crafting-plan:have', @model.craftsman.have.unparse()
onMoveNeedToHave: (itemSlug)->
@@ -51,39 +50,27 @@ module.exports = class CraftPageController extends PageController
@model.craftsman.want.remove itemSlug
@onWantInventoryChanged()
onSampleClicked: (event)->
$el = $(event.target)
while $el.length > 0
inventoryText = $el.attr 'data-slug'
break if inventoryText
$el = $el.parent()
if inventoryText?
sampleInventory = new SimpleInventory
sampleInventory.parse inventoryText
@model.craftsman.want.addInventory sampleInventory
@_scrollTo @$workingSection
return false
onWantInventoryChanged: ->
text = @model.craftsman.want.unparse()
url = c.url.crafting inventoryText:text
@router.navigate url
if @model.craftsman.want.isEmpty
@model.craftsman.have.clear()
# PageController Overrides #####################################################################
getMetaDescription: ->
return c.text.craftDescription()
getTitle: ->
description = @model.craftsman.want.toDescription()
return null unless description?
return "Crafting Plan for #{description}"
return 'Craft'
# BaseController Overrides #####################################################################
onDidRender: ->
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
@_wantInventoryController = @addChild InventoryController, '.want .view__inventory',
firstButtonType: 'remove'
imageLoader: @_imageLoader
@@ -102,6 +89,7 @@ module.exports = class CraftPageController extends PageController
router: @_router
@_haveInventoryController.on c.event.button.first, (controller, itemSlug)=>
@onRemoveFromHaveInventory itemSlug
@_haveInventoryController.on c.event.change, (c)=> @onHaveInventoryChanged()
@_needInventoryController = @addChild InventoryController, '.need .view__inventory',
editable: false
@@ -114,7 +102,6 @@ module.exports = class CraftPageController extends PageController
@_workingSectionController = @addChild CraftsmanWorkingController, '.view__craftsman_working',
model: @model.craftsman
@_workingSectionController.on c.event.click, => @_scrollTo @$workingSection
@$haveSection = @$('section.have')
@$instructionsSection = @$('section.instructions')
@@ -125,37 +112,25 @@ module.exports = class CraftPageController extends PageController
@$toolsSection = @$('section.tools')
@$workingSection = @$('.view__craftsman_working')
if c.screen.type.compute() is c.screen.type.mobile
@$('.view__inventory.large').removeClass 'large'
super
onWillRender: ->
@model.craftsman.have.clear()
@model.craftsman.have.parse @_storage.load('crafting-plan:have')
@model.craftsman.have.on c.event.change, => @onHaveInventoryChanged()
@model.craftsman.want.on c.event.change, => @refresh()
@model.craftsman.on c.event.change + ":stage", =>
return unless @model.craftsman.stage is Craftsman::STAGE.COMPLETE
@_scrollTo @$needSection
super
refresh: ->
@_needInventoryController.model = @model.craftsman.plan?.need
@_refreshOutdated()
@_refreshSectionVisibility()
@_refreshSteps()
@_adsenseController.fillAdPositions()
super
# Backbone.View Overrides ########################################################################
events: ->
return _.extend super,
'click .instructions a': 'onSampleClicked'
'click .instructions a': 'routeLinkClick'
# Private Methods ################################################################################
@@ -163,7 +138,7 @@ module.exports = class CraftPageController extends PageController
controller.model.addToolsTo @model.craftsman.want
_completeStep: (controller)->
controller.markComplete @model.craftsman.have
controller.model.completeInto @model.craftsman.have
_isAddingToolsPossible: (controller)->
tools = controller.model.recipe.tools
@@ -185,12 +160,6 @@ module.exports = class CraftPageController extends PageController
return true
_refreshOutdated: ->
if @model.craftsman.stage is Craftsman::STAGE.OUTDATED
@$el.addClass 'outdated'
else
@$el.removeClass 'outdated'
_refreshSectionVisibility: ->
return unless @_rendered
@@ -203,13 +172,11 @@ module.exports = class CraftPageController extends PageController
if @model.craftsman.want.isEmpty
visibleSections.push el for el in [@$instructionsSection, @$wantSection]
else if @model.craftsman.stage is Craftsman::STAGE.INVALID
visibleSections.push el for el in [@$wantSection, @$haveSection, @$workingSection]
visibleSections.push el for el in [@$wantSection, @$workingSection]
else if not @model.craftsman.complete
visibleSections.push el for el in [@$wantSection, @$haveSection, @$workingSection]
visibleSections.push el for el in [@$wantSection, @$workingSection]
else
visibleSections.push el for el in [@$haveSection, @$wantSection, @$workingSection]
if @model.craftsman.plan?
visibleSections.push el for el in [@$needSection, @$stepsSection]
visibleSections.push el for el in [@$haveSection, @$needSection, @$stepsSection, @$wantSection]
@hide $el for $el in allSections
@show $el for $el in visibleSections
@@ -242,28 +209,3 @@ module.exports = class CraftPageController extends PageController
while @_stepControllers.length > index
@_stepControllers.pop().remove()
_scrollTo: ($targetEl)->
@_$scrollTarget = $targetEl
if @_scrollTimer?
clearTimeout @_scrollTimer
@_scrollTimer = setTimeout(=>
$el = @_$scrollTarget
@_scrollTimer = null
minScrollPosition = $el.offset().top + $el.height() - $(window).height() - @SCROLL_BUFFER
maxScrollPosition = $el.offset().top + @SCROLL_BUFFER
scrollPosition = $(window).scrollTop()
scrollTarget = scrollPosition
if scrollPosition > maxScrollPosition
scrollTarget = maxScrollPosition
else if scrollPosition < minScrollPosition
scrollTarget = minScrollPosition
else
return
$('html, body').animate {scrollTop:scrollTarget}, c.duration.normal
, c.duration.slow)
@@ -5,16 +5,11 @@
//- All rights reserved.
//-
.view__craftsman_working
.panel
.view__item_group.section
.content
.waiting
img(src='/images/wait.gif')
.message
p
.count
p
.button: .bezel: p Calculate
.outdated
.message Your crafting plan is out of date!
.button: .bezel: p Recalculate
@@ -6,78 +6,32 @@ All rights reserved.
*/
.view__craftsman_working {
.panel {
.content {
background: $color-white;
box-shadow: none;
align-items : center;
display : flex;
flex-direction : column;
padding : $size-margin-xlarge;
.waiting {
margin-bottom : $size-margin-xlarge;
margin-bottom: $size-margin-xlarge;
}
.message {
margin-bottom : $size-margin-medium;
margin-bottom: $size-margin-medium;
p {
font-family : $font-family-header;
font-size : $font-size-large;
text-align : center;
font-family: $font-family-header;
font-size: $font-size-large;
}
}
.count {
p {
font-family : $font-family-header;
font-size : $font-size-medium;
text-align : center;
}
}
.button {
margin-top : $size-margin-large;
min-width: $size-minecraft-block * 6;
}
}
.outdated {
position: fixed; top: 0; left: 0; right: 0;
background : fade-out($color-white, 0.15) url('/images/panel-background-outdated.png');
border-bottom : $size-border-medium solid $color-black;
display : flex;
flex-direction : column;
align-items : center;
.message {
margin-top : $size-margin-medium;
font-family : $font-family-header;
font-size : $font-size-xlarge;
text-align : center;
}
.button {
width : $size-minecraft-block * 6;
margin : $size-margin-medium;
}
}
}
@include screen-size(not-mobile) {
.view__craftsman_working {
.panel {
.button {
margin-top : $size-margin-large;
height : $size-minecraft-block * 2;
width : $size-minecraft-block * 10;
p {
font-size : $font-size-xxlarge;
transform : translateY(-25%);
}
font-family: $font-family-header;
font-size: $font-size-medium;
}
}
}
@@ -12,94 +12,48 @@ Craftsman = require '../../../models/crafting/craftsman'
module.exports = class CraftsmanWorkingController extends BaseController
@::HIDE_TIMER_DURATION = 2000
constructor: (options={})->
if not options.model then throw new Error 'options.model is required'
options.templateName = 'craft_page/craftsman_working'
super options
@_hideTimer = null
# Event Methods ################################################################################
onButtonClicked: ->
@trigger c.event.click
@model.reset()
@model.work()
return false
@model.on c.event.change, => @_refreshStatusText()
# BaseController Methods #######################################################################
onDidModelChange: ->
@refresh()
onDidRender: ->
@$button = @$('.button')
@$count = @$('.count p')
@$message = @$('.message p')
@$outdated = @$('.outdated')
@$waiting = @$('.waiting')
@_controls = [@$button, @$count, @$message, @$outdated, @$waiting]
@$message = @$('.message p')
@$count = @$('.count p')
@$waiting = @$('.waiting')
super
refresh: ->
return unless @$message? and @$count?
button = count = message = outdated = waiting = null
switch @model.stage
when Craftsman::STAGE.READY
message = 'Ready to compute crafting plan!'
count = 'Click "Calculate" to continue.'
button = true
when Craftsman::STAGE.GRAPHING
message = 'Researching recipes...'
count = "Found #{@model.stageCount} recipes so far..."
watiing = true
when Craftsman::STAGE.PLANNING
message = 'Figuring out possible crafting plans...'
count = "Found #{@model.stageCount} possibilities so far..."
watiing = true
when Craftsman::STAGE.ANALYZING
message = 'Looking for the best plan...'
count = "Finished checking #{@model.stageCount} so far..."
watiing = true
when Craftsman::STAGE.COMPLETE
message = 'Crafting plan is complete.'
when Craftsman::STAGE.INVALID
message = 'Couldn\'t make a crafting plan!'
count = 'Please report this problem using the Feedback box.'
when Craftsman::STAGE.OUTDATED
message = 'Your crafting plan is out of date!'
count = 'Click "Calculate" to re-compute.'
button = true
outdated = true
@hide(control) for control in @_controls
if button?
@show @$button
if count?
@show @$count
@$count.html count
if message?
@show @$message
@$message.html message
if outdated?
@show @$outdated
if waiting?
@show @$waiting
@_refreshStatusText()
super
# Backbone.View Overrides ######################################################################
# Private Methods ##############################################################################
events: ->
return _.extend super,
'click .button': 'onButtonClicked'
_refreshStatusText: ->
return unless @$message? and @$count?
@show @$waiting
switch @model.stage
when Craftsman::STAGE.WAITING
@$message.html 'Preparing crafting calculation...'
@$count.html ''
when Craftsman::STAGE.GRAPHING
@$message.html 'Researching recipes...'
@$count.html "Found #{@model.stageCount} recipes so far..."
when Craftsman::STAGE.PLANNING
@$message.html 'Figuring out possible crafting plans...'
@$count.html "Found #{@model.stageCount} possibilities so far..."
when Craftsman::STAGE.ANALYZING
@$message.html 'Looking for the best plan...'
@$count.html "Finished checking #{@model.stageCount} so far..."
when Craftsman::STAGE.COMPLETE
@$message.html 'All done!'
@$count.html ''
when Craftsman::STAGE.INVALID
@$message.html 'Couldn\'t make a crafting plan'
@$count.html ''
@hide @waiting
+2 -2
View File
@@ -11,5 +11,5 @@
.view__inventory
.view__recipe
.buttons
a.button.tool: .bezel: p <span class="full">show plan for </span>tools
.button.complete: .bezel: p complete<span class="full"> step</span>
.button.complete: .bezel: p complete step
.button.tool: .bezel: p make this tool
+1 -51
View File
@@ -31,10 +31,6 @@
.view__inventory {
flex: 50%;
margin-left: $size-margin-large;
.buttons {
display: none;
}
}
.view__recipe {
@@ -56,51 +52,5 @@
margin-left: $size-margin-medium;
}
}
a {
border: 0;
.bezel {
border-top : 3px solid $color-button-highlight;
border-left : 3px solid $color-button-highlight;
border-bottom : 6px solid $color-button-shadow;
border-right : 3px solid $color-button-shadow;
}
}
}
&.complete {
opacity: 0.25;
h3 {
text-decoration: line-through;
}
}
}
@include screen-size(mobile) {
.view__step {
.main_content {
flex-direction: column-reverse;
.view__inventory {
flex: 0 0 auto;
margin: $size-margin-large $size-margin-large 0 $size-margin-large;
}
.view__recipe {
flex: 0 0 auto;
}
}
.buttons {
opacity: 1.0;
.button {
.full {
display: none;
}
}
}
}
}
}
@@ -8,7 +8,6 @@
BaseController = require '../../base_controller'
InventoryController = require '../../common/inventory/inventory_controller'
RecipeController = require '../../common/recipe/recipe_controller'
SimpleInventory = require '../../../models/crafting/simple_inventory'
########################################################################################################################
@@ -30,19 +29,16 @@ module.exports = class StepController extends BaseController
@onComplete = options.onComplete or (controller)-> # do nothing
@onAddTools = options.onAddTools or (controller)-> # do nothing
# Public Methods ###############################################################################
markComplete: (targetInventory)->
@$el.addClass 'complete'
@$completeButton.addClass 'disabled'
@model.completeInto targetInventory
# Event Methods ################################################################################
onCompleteButtonClicked: (event)->
return if @$completeButton.hasClass 'disabled'
@onComplete this
onToolButtonClicked: (event)->
return if @$toolButton.hasClass 'disabled'
@onAddTools this
# BaseController Overrides #####################################################################
onDidRender: ->
@@ -67,9 +63,6 @@ module.exports = class StepController extends BaseController
onWillChangeModel: (oldModel, newModel)->
if not newModel? then throw new Error 'model cannot be null'
if @rendered
@$el.removeClass 'complete'
@$completeButton.removeClass 'disabled'
return super
refresh: ->
@@ -90,19 +83,11 @@ module.exports = class StepController extends BaseController
events: ->
return _.extend super,
'click .button.complete': 'onCompleteButtonClicked'
'click .button.tool': 'onToolButtonClicked'
# Private Methods ##############################################################################
_refreshToolButton: ->
inventory = new SimpleInventory {}, modPack:@_modPack
for toolStack in @model.recipe.tools
inventory.add toolStack.itemSlug, toolStack.quantity
inventory.localize()
inventoryText = inventory.unparse()
@$toolButton.attr 'href', "/craft/#{inventoryText}"
@$toolButton.attr 'target', inventoryText
if @canAddTools this
@$toolButton.removeClass 'disabled'
else
+5 -7
View File
@@ -15,10 +15,6 @@
display: flex;
@include screen-size(mobile) {
display: none;
}
.form-content {
flex: 1 1 100%;
@@ -77,11 +73,13 @@
background: $color-gray-light;
border: $size-border-medium solid $color-black;
border-left: 0;
position: relative;
display: flex;
align-items: center;
justify-content: center;
p {
position: absolute; top: 50%; left: 50%;
transform: translateX(-50%) rotate(-90deg) translateX(10%);
transform: rotate(-90deg);
font-family: $font-family-header;
font-size: $label-size;
-19
View File
@@ -19,20 +19,12 @@
> .content {
display: flex;
@include screen-size(mobile) {
flex-direction: column;
}
color: $color-text-light;
.left, .center, .right {
flex: 1 1 33%;
margin: $size-margin-large $size-margin-large $size-margin-large 0;
@include screen-size(mobile) {
margin-left: $size-margin-large;
}
display: flex;
flex-direction: column;
@@ -46,7 +38,6 @@
.action {
flex: 1 1 auto;
align-self: center;
margin-top: $size-margin-large;
margin-bottom: $size-margin-large;
display: flex;
@@ -64,13 +55,3 @@
height: $size-minecraft-block;
}
}
@include screen-size(mobile) {
.view__footer {
> .content {
.left, .center, .right {
flex: 0 0 auto;
}
}
}
}
+1 -3
View File
@@ -21,6 +21,4 @@
.button.search: .bezel: p Search
.button.login: .bezel: p Login
.breadcrumb-bar
.breadcrumbs
.extra-nav
.breadcrumbs
+21 -86
View File
@@ -23,22 +23,24 @@
img {
position : relative;
height : $size-minecraft-block * 1.5;
width : $size-minecraft-block * 1.5;
top : -$size-minecraft-block * 0.5;
left : -$size-minecraft-block * 0.5;
height : $size-minecraft-block * 2.0;
width : $size-minecraft-block * 2.0;
}
}
h1 {
flex: 1 1 auto;
white-space: nowrap;
}
.addthis_sharing_toolbox {
flex : 0 0 auto;
min-height : 37px; // actual size of addthis component
min-height : 37px;
min-width : 144px;
margin-right : $size-margin-small;
&.placeholder {
border : 1px solid orange;
text-align : center;
@@ -60,103 +62,36 @@
align-items : center;
display : flex;
flex-wrap : wrap;
justify-content : center;
justify-content : space-around;
padding : $size-margin-medium $size-margin-medium;
padding-top : 0;
.button {
flex-grow : 1;
flex-shrink : 1;
min-width : 3 * $size-minecraft-block;
max-width : 5 * $size-minecraft-block;
margin-top : $size-margin-medium;
margin-right : $size-margin-small;
&:last-child {
margin-right: 0;
}
flex : 1 1 20%;
min-width : 4 * $size-minecraft-block;
max-width : 5 * $size-minecraft-block;
margin-top: $size-margin-medium;
}
}
.breadcrumb-bar {
.breadcrumbs {
background: $color-screen;
padding: 0 ($size-margin-medium + $size-margin-large);
font-family: $font-family-control;
font-size: $font-size-medium;
line-height: $font-size-medium + 2 * $size-margin-small;
display: flex;
padding: $size-margin-small $size-margin-medium;
.breadcrumbs {
flex: 1 1 75%;
text-indent: -$size-margin-large;
&:before {
content: "";
margin-right: $size-margin-medium;
}
a {
text-decoration: underline;
}
span {
margin: 0 $size-margin-medium;
}
&:before {
content: "";
margin-right: $size-margin-medium;
}
.extra-nav {
flex: 1 1 25%;
align-self: flex-end;
a {
text-decoration: underline;
}
text-align: right;
}
}
}
@include screen-size(desktop) {
.view__header {
.top-row {
a {
img {
top : -$size-minecraft-block * 0.5;
left : -$size-minecraft-block * 0.5;
height : $size-minecraft-block * 2.0;
width : $size-minecraft-block * 2.0;
}
}
}
}
}
@include screen-size(tablet) {
.view__header {
.top-row {
h1 {
font-size : $font-size-xlarge;
}
}
}
}
@include screen-size(mobile) {
.view__header {
.top-row {
display : none;
}
.divider {
.button.login {
display: none;
}
}
.breadcrumb-bar {
.extra-nav {
display: none;
}
span {
margin: 0 $size-margin-medium;
}
}
}
@@ -7,7 +7,6 @@
BaseController = require '../base_controller'
ItemSelectorController = require '../common/item_selector/item_selector_controller'
UrlParams = require '../url_params'
########################################################################################################################
@@ -44,11 +43,9 @@ module.exports = class HeaderController extends BaseController
@router.navigate '/news', trigger:true
return false
onSearch: (event, hint='')->
@_selector.launch hint
onSearch: ->
@_selector.launch()
.then (itemSlug)=>
return unless itemSlug?
itemDisplay = @_modPack.findItemDisplay itemSlug
@router.navigate itemDisplay.itemUrl, trigger:true
return false
@@ -65,11 +62,8 @@ module.exports = class HeaderController extends BaseController
@$('.addthis_sharing_toolbox').addClass 'placeholder'
@$breadcrumbs = @$('.breadcrumbs')
@$extraNav = @$('.extra-nav')
@$loginButtonLabel = @$('.button.login p')
@$title = $('title')
@_checkForSearchRequest()
super
onWillChangeModel: (oldModel, newModel)->
@@ -81,8 +75,6 @@ module.exports = class HeaderController extends BaseController
@$loginButtonLabel.text if global.site.user? then "Logout" else "Login"
@_refreshBreadcrumbs()
@_refreshExtraNav()
@_refreshSelectedNavButton()
@_refreshTitle()
super
@@ -90,21 +82,15 @@ module.exports = class HeaderController extends BaseController
events: ->
_.extend super,
'click .craft': 'onCraft'
'click .browse': 'onBrowse'
'click .login': 'onLogin'
'click .news': 'onNews'
'click .search': 'onSearch'
'click .breadcrumb-bar a': 'routeLinkClick'
'click .craft': 'onCraft'
'click .browse': 'onBrowse'
'click .login': 'onLogin'
'click .news': 'onNews'
'click .search': 'onSearch'
'click .breadcrumbs a': 'routeLinkClick'
# Private Methods ##############################################################################
_checkForSearchRequest: ->
params = new UrlParams search:{type:'string'}
return unless params.search?
_.delay (=> @onSearch {}, params.search), c.duration.slow
_refreshBreadcrumbs: ->
breadcrumbs = @model?.controller?.getBreadcrumbs()
breadcrumbs ?= []
@@ -120,23 +106,6 @@ module.exports = class HeaderController extends BaseController
else
@hide @$breadcrumbs
_refreshExtraNav: ->
extraNavContent = @model?.controller?.getExtraNav()
if extraNavContent?
@$extraNav.empty()
@$extraNav.append extraNavContent
@show @$extraNav
else
@hide @$extraNav
_refreshSelectedNavButton: ->
return unless @model?.page?
@$('.button').removeClass 'active'
@$(".button.#{@model.page}").addClass 'active'
_refreshTitle: ->
title = @model?.controller?.getTitle()
if title
+2 -4
View File
@@ -12,7 +12,7 @@ html
link(href="/images/favicon.png" rel="icon" type="image/png")
link(href="/main.css" rel="stylesheet" type="text/css" )
meta(charset="UTF-8")
meta(name="viewport" content="user-scalable=no, width=device-width")
meta(name="viewport" content="width=480")
body
.view__feedback
@@ -21,7 +21,7 @@ html
.page
.view__header
.content.hidden
.content(style="display: none")
.content-loading
img(src="/images/wait.gif")
@@ -32,6 +32,4 @@ html
include ../includes/_google_adsense.jade
include ../includes/_google_analytics.jade
include ../includes/_google_sitelinks.jade
include ../includes/_twitter_follow.jade
+3 -6
View File
@@ -36,22 +36,19 @@ html {
}
& > *:first-child {
flex: 1 1 100%;
flex: 0 0 50%;
box-shadow : $size-margin-small $size-margin-small $size-margin-large $color-shadow;
}
}
.page {
margin : $size-margin-large 0;
max-width : $size-page-width;
width : 100%;
@include screen-size(desktop) {
margin : $size-margin-large 0;
box-shadow : $size-margin-small $size-margin-small $size-margin-large $color-shadow;
}
background : $color-background-page;
box-shadow : $size-margin-small $size-margin-small $size-margin-large $color-shadow;
display : flex;
flex-direction : column;
+2 -1
View File
@@ -7,6 +7,7 @@
.content.view__item_page
.left
.view__adsense
.right
.about
@@ -16,7 +17,7 @@
.title
a.sourceMod
.button-row
.button.craftingPlan: .bezel: p View <span class="full">Crafting </span>Plan
.button.craftingPlan: .bezel: p View Crafting Plan
.right
p Official Link:
a.officialLink project page
+1 -48
View File
@@ -48,7 +48,7 @@
.button {
flex: 1 1 100%;
max-width: $size-minecraft-block * 10;
max-width: 66%;
}
}
}
@@ -58,50 +58,3 @@
}
}
}
@include screen-size(mobile) {
.view__item_page {
.about {
flex-direction: column;
& > .left {
margin: 0 $size-margin-medium;
display: flex;
flex-direction: column;
align-items: center;
img {
}
}
& > .center {
flex: 0 0 auto;
margin: 0 $size-margin-medium;
.sourceMod, .title {
text-align: center;
}
a.sourceMod {
}
.button-row {
flex: 0 0 auto;
margin: $size-margin-large 0;
.button {
max-width: $size-minecraft-block * 8;
.full {
display: none;
}
}
}
}
& > .right {
margin: 0 $size-margin-medium;
}
}
}
}
@@ -5,6 +5,7 @@
# All rights reserved.
#
AdsenseController = require '../common/adsense/adsense_controller'
EditableFile = require '../../models/site/editable_file'
Item = require '../../models/game/item'
ItemGroupController = require '../common/item_group/item_group_controller'
@@ -41,7 +42,6 @@ module.exports = class ItemPageController extends PageController
@_itemSlug = options.itemSlug
@_modPack = options.modPack
@_router = options.router
@_triggerEditing = options.login
@_modPack.on c.event.change, =>
@tryRefresh()
@@ -68,13 +68,6 @@ module.exports = class ItemPageController extends PageController
$("<b>#{display.itemName}</b>")
]
getExtraNav: ->
itemSlug = @_modPack.chooseRandomItem()
return null unless itemSlug?
itemDisplay = @_modPack.findItemDisplay itemSlug
return $("<a href='#{itemDisplay.itemUrl}'>Random Item</a>")
getMetaDescription: ->
return null unless @_itemSlug?
display = @_modPack.findItemDisplay @_itemSlug
@@ -90,11 +83,9 @@ module.exports = class ItemPageController extends PageController
# BaseController Overrides #####################################################################
onDidModelChange: ->
@trigger c.event.change
super
onDidRender: ->
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper', router: @_router
options = imageLoader:@_imageLoader, modPack:@_modPack, router:@_router, show:false
@_multiblockController = @addChild MultiblockViewerController, '.view__multiblock_viewer', options
@_similarItemsController = @addChild ItemGroupController, '.view__item_group.similar', options
@@ -110,12 +101,11 @@ module.exports = class ItemPageController extends PageController
endEditing: => @_endEditingDescription()
enterFeedback: @_enterFeedback
@$aboutImage = @$('.about img')
@$craftingPlanButton = @$('.button.craftingPlan')
@$name = @$('.about .title')
@$officialLink = @$('.about a.officialLink')
@$sourceModLink = @$('.about a.sourceMod')
@$aboutLinks = @$('.about .right')
@$aboutImage = @$('.about img')
@$name = @$('.about .title')
@$officialLink = @$('.about a.officialLink')
@$sourceModLink = @$('.about a.sourceMod')
@$aboutLinks = @$('.about .right')
@$multiblockSection = @$('section.multiblock')
@$recipeContainer = @$('section.recipes .panel')
@@ -145,15 +135,6 @@ module.exports = class ItemPageController extends PageController
else
@hide @$aboutLinks
if @model.item.isCraftable
@show @$craftingPlanButton
else
@hide @$craftingPlanButton
if @_triggerEditing
@_triggerEditing = false
@_descriptionController.onEditClicked {}
@show()
else
@hide()
@@ -336,7 +317,7 @@ module.exports = class ItemPageController extends PageController
item = @_modPack.findItem @_itemSlug, includeDisabled:true
if item?
if not ItemSlug.equal item.slug, @_itemSlug
@_router.navigate c.url.item(modSlug:item.slug.mod, itemSlug:item.slug.item), trigger:true
router.navigate c.url.item(modSlug:item.slug.mod, itemSlug:item.slug.item), trigger:true
return
@model.item = item
@@ -21,4 +21,4 @@ $animate-fast: 100ms;
opacity: 0.0;
transition: opacity $animate-fast;
}
}
}
@@ -75,20 +75,3 @@
}
}
}
@include screen-size(mobile) {
.view__multiblock_viewer {
flex-direction: column-reverse;
.left {
flex: 0 0 auto;
border: 0;
}
.right {
flex: 0 0 auto;
margin-bottom: $size-margin-large;
}
}
}
@@ -8,7 +8,7 @@
.view__recipe_detail {
display : flex;
.input, .output {
.input, .output{
flex : 0 1 33%;
h3 {
@@ -20,13 +20,8 @@
}
}
.input {
order: 10;
}
.pattern {
flex : 0 1 33%;
order: 20;
display : flex;
flex-direction : column;
@@ -41,48 +36,9 @@
}
}
.output {
order: 30;
}
&:not(:first-child) {
border-top : 1px solid $color-gray-light;
margin-top : $size-margin-medium;
padding-top : $size-margin-large;
}
}
@include screen-size(mobile) {
.view__recipe_detail {
flex-direction: column;
> :not(:last-child) {
margin-bottom: $size-margin-large;
}
.input, .output {
flex: 0 0 auto;
h3 {
}
.view__inventory {
}
}
.input {
order: 21;
}
.pattern {
flex: 0 0 auto;
.tool {
}
}
&:not(:first-child) {
}
}
}
@@ -1,11 +0,0 @@
//-
//- Crafting Guide - mod_editor_page.jade
//-
//- Copyright © 2014-2016 by Redwood Labs
//- All rights reserved.
//-
.content.view__mod_editor_page
.left
.right
@@ -1,9 +0,0 @@
//
// Crafting Guide - mod_editor_page.scss
//
// Copyright © 2014-2016 by Redwood Labs
// All rights reserved.
//
.view__mod_editor_page {
}
@@ -1,46 +0,0 @@
#
# Crafting Guide - mod_page_controller.coffee
#
# Copyright © 2014-2016 by Redwood Labs
# All rights reserved.
#
PageController = require '../page_controller'
########################################################################################################################
module.exports = class ModPageController extends PageController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.templateName = 'mod_editor_page'
super options
@_imageLoader = options.imageLoader
@_modPack = options.modPack
# Event Methods ################################################################################
# Property Methods #############################################################################
# PageController Overrides #####################################################################
getBreadcrumbs: ->
getTitle: ->
# BaseController Overrides #####################################################################
onDidRender: ->
super
refresh: ->
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
# Private Methods ##############################################################################
+3 -5
View File
@@ -1,5 +1,5 @@
//-
//- Crafting Guide - mod_page.jade
//- Crafting Guide - item_page.jade
//-
//- Copyright © 2014-2016 by Redwood Labs
//- All rights reserved.
@@ -7,6 +7,7 @@
.content.view__mod_page
.left
.view__adsense
.right
.about
@@ -16,11 +17,8 @@
.title
.author
.description
.modpack
span.checkbox
span.label I have this mod in my modpack
.version
span I'm using version:
span in my mod pack
select
.warning.
<b>PLEASE NOTE:</b> By selecting "no", crafting plans, search, and other features will ignore items
+3 -78
View File
@@ -1,5 +1,5 @@
//
// Crafting Guide - mod_page.scss
// Crafting Guide - item_page.scss
//
// Copyright © 2014-2016 by Redwood Labs
// All rights reserved.
@@ -49,44 +49,8 @@
line-height: $font-size-medium * 1.25;
}
.modpack {
margin-top : $size-margin-large;
cursor : pointer;
font-family : $font-family-control;
font-size : $font-size-medium;
display : flex;
.checkbox {
display : inline-block;
height : $font-size-medium;
margin-right : $size-margin-small;
position : relative;
width : $font-size-medium;
background : $color-white;
border : $size-border-small solid $color-black;
}
.label {
color: $color-gray-medium;
}
&.enabled {
.checkbox:before {
position: absolute; top: 0; left: 1.5px; // adjust for glyph position
content: "";
}
.label {
color: $color-black;
}
}
}
.version {
margin-top: $size-margin-medium;
margin-top: $size-margin-large;
font-family: $font-family-control;
font-size: $font-size-medium;
@@ -127,43 +91,4 @@
@include layout-link-list;
}
}
}
@include screen-size(mobile) {
.view__mod_page {
.about {
flex-direction: column;
& > * {
margin-left: $size-margin-medium;
margin-right: $size-margin-medium;
}
.left {
margin-bottom: $size-margin-large;
margin-left: $size-margin-medium;
margin-right: $size-margin-medium;
display : flex;
flex-direction : column;
align-items : center;
}
.center {
flex: 0 0 auto;
margin-bottom: $size-margin-xlarge;
margin-left: $size-margin-medium;
margin-right: $size-margin-medium;
.author, .title {
text-align: center;
}
}
.right {
margin-left: $size-margin-medium;
margin-right: $size-margin-medium;
}
}
}
}
}
@@ -5,6 +5,7 @@
# All rights reserved.
#
AdsenseController = require '../common/adsense/adsense_controller'
BaseController = require '../base_controller'
Item = require '../../models/game/item'
ItemGroupController = require '../common/item_group/item_group_controller'
@@ -28,14 +29,6 @@ module.exports = class ModPageController extends BaseController
# Event Methods ################################################################################
onToggleEnabled: ->
if @model.activeVersion is Mod.Version.None
@model.activeVersion = Mod.Version.Latest
else
@model.activeVersion = Mod.Version.None
return false
onVersionChanged: ->
@model.activeVersion = @$versionSelector.val()
return false
@@ -65,6 +58,8 @@ module.exports = class ModPageController extends BaseController
# BaseController Overrides #####################################################################
onDidRender: ->
@_adsenseController = @addChild AdsenseController, '.view__adsense', model: 'skyscraper'
@$author = @$('.about .author')
@$description = @$('.about .description')
@$documentationLink = @$('.about .documentation')
@@ -72,18 +67,15 @@ module.exports = class ModPageController extends BaseController
@$homePageLink = @$('.about .homePage')
@$itemGroups = @$(".itemGroups")
@$logo = @$('.about img')
@$modpackSection = @$('.modpack')
@$title = @$('.about .title')
@$tutorialsContainer = @$('section.tutorials .panel')
@$tutorialsSection = @$('section.tutorials')
@$versionSection = @$('.version')
@$versionSelector = @$('.version select')
@$warning = @$('.warning')
super
refresh: ->
@_refreshAboutBlock()
@_refreshEnabled()
@_refreshItemGroups()
@_refreshTutorials()
@_refreshVersionSelector()
@@ -94,7 +86,6 @@ module.exports = class ModPageController extends BaseController
events: ->
return _.extend super,
'change .version select': 'onVersionChanged'
'click .modpack': 'onToggleEnabled'
# Private Methods ##############################################################################
@@ -116,19 +107,6 @@ module.exports = class ModPageController extends BaseController
@$logo.attr 'src', ''
@$title.text ''
_refreshEnabled: ->
if @model.slug in c.requiredMods
@hide @$modpackSection
else
@show @$modpackSection
if @model.activeVersion is Mod.Version.None
@$modpackSection.removeClass 'enabled'
@show @$warning
else
@$modpackSection.addClass 'enabled'
@hide @$warning
_refreshItemGroups: ->
@_groupControllers ?= []
groupIndex = 0
@@ -146,10 +124,9 @@ module.exports = class ModPageController extends BaseController
router: @_router
title: if group is Item.Group.Other then 'Items' else group
_.defer =>
@_groupControllers.push controller
@$itemGroups.append controller.$el
controller.render()
@_groupControllers.push controller
@$itemGroups.append controller.$el
controller.render()
else
controller.modVersion = modVersion
controller.model = items
@@ -186,16 +163,19 @@ module.exports = class ModPageController extends BaseController
_refreshVersionSelector: ->
modVersions = @model.modVersions
if (@model.activeVersion is Mod.Version.None) or modVersions.length < 2
@hide @$versionSection
else
@show @$versionSection
selectedModVersion = @effectiveModVersion
@$versionSelector.empty()
$option = $("<option value=\"none\">no</option>")
if not @model.enabled
@$warning.css display:''
selectedModVersion = null
$option.attr 'selected', true
else
@$warning.css display:'none'
@$versionSelector.append $option
for modVersion in @model.modVersions
$option = $("<option value=\"#{modVersion.version}\">#{modVersion.version}</option>")
if modVersion is selectedModVersion

Some files were not shown because too many files have changed in this diff Show More