Compare commits
52
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
240a14da58 | ||
|
|
c441d75529 | ||
|
|
1bc89c1c89 | ||
|
|
43202e9917 | ||
|
|
05bc1a271d | ||
|
|
da1419d6b6 | ||
|
|
841fac38d3 | ||
|
|
2b8e23b32f | ||
|
|
5eae2bb488 | ||
|
|
6fa18c764b | ||
|
|
c9796a142d | ||
|
|
0718590f56 | ||
|
|
51f566ebf1 | ||
|
|
d2d904be7f | ||
|
|
088994f366 | ||
|
|
184b64a6af | ||
|
|
4aca035709 | ||
|
|
ebf3443e96 | ||
|
|
fd5372b35d | ||
|
|
f464558cd2 | ||
|
|
e7cd3f0549 | ||
|
|
545408a7e7 | ||
|
|
83156c30c7 | ||
|
|
636a8d061c | ||
|
|
2dbda99986 | ||
|
|
7d83552013 | ||
|
|
35bb12de6a | ||
|
|
1d1772e961 | ||
|
|
6787063ac7 | ||
|
|
9e09b8d87c | ||
|
|
2c77972073 | ||
|
|
31f4594928 | ||
|
|
eb9ac15de8 | ||
|
|
e922f3c5cc | ||
|
|
a8d08b93c5 | ||
|
|
64e118a2c8 | ||
|
|
6671541dcb | ||
|
|
962165dc8e | ||
|
|
f9b65dd892 | ||
|
|
979586d9d3 | ||
|
|
c522e60e28 | ||
|
|
f9cd38947d | ||
|
|
99b6cf2861 | ||
|
|
d925a17940 | ||
|
|
a2bbdf4645 | ||
|
|
93c98e16ac | ||
|
|
88e57b1200 | ||
|
|
1e96cf2d6c | ||
|
|
ccaf8e9842 | ||
|
|
e1f8282049 | ||
|
|
d873c4b7f1 | ||
|
|
3529bccf3a |
+7
-2
@@ -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', dest:'./dist/'}
|
||||
{expand: true, cwd:'./build/static', src:['**/*.html', '!**/index.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,7 +77,10 @@ module.exports = (grunt)->
|
||||
]
|
||||
build_to_dist:
|
||||
files: [
|
||||
{expand:true, cwd:'./build/static/', src:['**/*', '!**/*.map', '!**/*.js'], dest:'./dist'}
|
||||
expand: true
|
||||
cwd: './build/static/'
|
||||
src: ['**/*', '!**/*.map', '!**/*.js', '!index.html']
|
||||
dest: './dist'
|
||||
]
|
||||
common_source:
|
||||
files: [
|
||||
@@ -135,6 +138,8 @@ module.exports = (grunt)->
|
||||
|
||||
sass:
|
||||
all:
|
||||
options:
|
||||
sourcemap: 'none'
|
||||
files:
|
||||
'./build/static/main.css': [ './build/imports.scss' ]
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 267 B |
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 300 B |
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "crafting-guide",
|
||||
"version": "3.2.2",
|
||||
"version": "3.2.4",
|
||||
"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.
@@ -0,0 +1,353 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,18 @@
|
||||
//-
|
||||
//- 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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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,6 +9,9 @@
|
||||
|
||||
module.exports = class BaseModel extends Backbone.Model
|
||||
|
||||
@_loadingQueue = []
|
||||
@_isDraining = false
|
||||
|
||||
constructor: (attributes={}, options={})->
|
||||
options.logEvents ?= true
|
||||
super attributes, options
|
||||
@@ -19,10 +22,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
|
||||
|
||||
@loading = null
|
||||
@state = c.modelState.unloaded
|
||||
|
||||
Object.defineProperties this,
|
||||
isUnloaded: { get:-> @state is c.modelState.unloaded }
|
||||
@@ -60,12 +63,23 @@ module.exports = class BaseModel extends Backbone.Model
|
||||
|
||||
@state = c.modelState.loading
|
||||
@trigger c.event.request, this
|
||||
@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
|
||||
|
||||
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.catch (e)-> # do nothing
|
||||
return @loading
|
||||
@@ -86,3 +100,30 @@ 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,6 +24,7 @@ module.exports = class CraftingPlan
|
||||
@_rawScores = {}
|
||||
@_scores = {}
|
||||
@_steps = steps
|
||||
@_tools = null
|
||||
@_want = want
|
||||
|
||||
@_numberSteps()
|
||||
@@ -37,19 +38,32 @@ 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
|
||||
if not stack?
|
||||
throw new Error 'stack should not be null here'
|
||||
|
||||
continue if recipe.isPassThroughFor stack.itemSlug
|
||||
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,6 +63,22 @@ 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,10 +27,11 @@ module.exports = class CraftingStep
|
||||
|
||||
addToolsTo: (targetInventory)->
|
||||
for stack in @_recipe.tools
|
||||
targetInventory.add stack.itemSlug, stack.quantity
|
||||
targetInventory.add stack.itemSlug, stack.quantity, insert:true
|
||||
|
||||
completeInto: (targetInventory)->
|
||||
for stack in @_recipe.output
|
||||
continue if @_recipe.isPassThroughFor stack.itemSlug
|
||||
quantity = @_recipe.getQuantityProduced stack.itemSlug
|
||||
targetInventory.add stack.itemSlug, quantity * @_multiplier
|
||||
|
||||
@@ -67,4 +68,6 @@ module.exports = class CraftingStep
|
||||
@_inventory = new SimpleInventory
|
||||
for stack in @_recipe.input
|
||||
qualifiedSlug = @_modPack.qualifySlug stack.itemSlug
|
||||
@_inventory.add qualifiedSlug, @multiplier * @_recipe.getQuantityRequired stack.itemSlug
|
||||
required = @_recipe.getQuantityRequired(stack.itemSlug) - @_recipe.getQuantityProduced(stack.itemSlug)
|
||||
required = if required > 0 then required * @multiplier else 1
|
||||
@_inventory.add qualifiedSlug, required
|
||||
|
||||
@@ -22,57 +22,43 @@ module.exports = class Craftsman extends BaseModel
|
||||
@::PLAN_STEP_INCREMENT = 39
|
||||
|
||||
@::STAGE =
|
||||
WAITING: 'waiting'
|
||||
EMPTY: 'empty'
|
||||
READY: 'ready'
|
||||
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 =
|
||||
paused: false
|
||||
stage: @STAGE.WAITING
|
||||
stage: @STAGE.READY
|
||||
stageCount: 0
|
||||
super attributes, {}
|
||||
|
||||
@_modPack = modPack
|
||||
|
||||
reset = _.debounce (=> @reset()), 100
|
||||
reevaluatePlans = _.debounce (=> @reevaluatePlans()), 100
|
||||
|
||||
@_have = new Inventory modPack:@_modPack
|
||||
@_have.on c.event.change, reevaluatePlans
|
||||
@_have.on c.event.change, => @_resetStage()
|
||||
|
||||
@_want = new Inventory modPack:@_modPack
|
||||
@_want.on c.event.change, reset
|
||||
@_want.on c.event.change, => @_resetStage()
|
||||
|
||||
@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
|
||||
|
||||
@stage = @STAGE.WAITING
|
||||
@stageCount = 0
|
||||
|
||||
@_scheduleNextWork()
|
||||
@_resetStage()
|
||||
|
||||
work: ->
|
||||
return if @_want.isEmpty
|
||||
@@ -130,7 +116,7 @@ module.exports = class Craftsman extends BaseModel
|
||||
Object.defineProperties @prototype,
|
||||
|
||||
complete:
|
||||
get: -> @stage in [@STAGE.COMPLETE, @STAGE.INVALID]
|
||||
get: -> @stage in [@STAGE.COMPLETE, @STAGE.INVALID, @STAGE.OUTDATED]
|
||||
|
||||
have:
|
||||
get: -> @_have
|
||||
@@ -143,8 +129,19 @@ 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,6 +98,12 @@ 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
|
||||
@@ -107,12 +113,29 @@ 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 Inventory
|
||||
inventory = new SimpleInventory
|
||||
inventory.addInventory this
|
||||
return inventory
|
||||
|
||||
@@ -79,7 +79,7 @@ module.exports = class SimpleInventory
|
||||
|
||||
if qualifiedSlug?
|
||||
newSlugs.push qualifiedSlug
|
||||
newStacks.push new Stack itemSlug:qualifiedSlug, quantity:stack.quantity
|
||||
newStacks.push new SimpleStack 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 Inventory.Delimiters.Stack
|
||||
stacks = data.split SimpleInventory.Delimiters.Stack
|
||||
for stackText in stacks
|
||||
stackParts = stackText.split Inventory.Delimiters.Item
|
||||
stackParts = stackText.split SimpleInventory.Delimiters.Item
|
||||
if stackParts.length is 2
|
||||
quantity = parseInt stackParts[0], 10
|
||||
itemSlug = ItemSlug.slugify stackParts[1]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
###
|
||||
#
|
||||
# Crafting Guide - event_recorder.coffee
|
||||
#
|
||||
# Copyright (c) 2014-2015 by Redwood Labs
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
###
|
||||
#
|
||||
|
||||
util = require 'util'
|
||||
|
||||
|
||||
@@ -29,10 +29,10 @@ module.exports = class Inventory extends BaseModel
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
add: (itemSlug, quantity=1)->
|
||||
add: (itemSlug, quantity=1, options={})->
|
||||
return this unless quantity > 0
|
||||
|
||||
@_add itemSlug, quantity
|
||||
@_add itemSlug, quantity, options
|
||||
@trigger c.event.add, this, itemSlug, quantity
|
||||
@trigger c.event.change, this
|
||||
return this
|
||||
@@ -141,6 +141,18 @@ 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)->
|
||||
@@ -209,7 +221,8 @@ module.exports = class Inventory extends BaseModel
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_add: (itemSlug, quantity=1)->
|
||||
_add: (itemSlug, quantity=1, options={})->
|
||||
options.insert ?= false
|
||||
return unless itemSlug?
|
||||
return unless quantity > 0
|
||||
|
||||
@@ -218,7 +231,10 @@ 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
|
||||
@_itemSlugs.push itemSlug
|
||||
if options.insert
|
||||
@_itemSlugs.unshift itemSlug
|
||||
else
|
||||
@_itemSlugs.push itemSlug
|
||||
@_sort()
|
||||
else
|
||||
stack.quantity += quantity
|
||||
|
||||
@@ -29,8 +29,6 @@ module.exports = class Mod extends BaseModel
|
||||
@_modVersions = []
|
||||
@_tutorials = []
|
||||
|
||||
@once c.event.sync, => @_verifyActiveModVersion()
|
||||
|
||||
# Class Methods ##################################################################################
|
||||
|
||||
@Version: Version =
|
||||
@@ -95,6 +93,12 @@ 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
|
||||
@@ -129,6 +133,7 @@ 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
|
||||
@@ -143,6 +148,9 @@ 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
|
||||
@@ -201,6 +209,8 @@ module.exports = class Mod extends BaseModel
|
||||
@_parser ?= new ModParser model:this
|
||||
@_parser.parse text
|
||||
|
||||
@_verifyActiveModVersion()
|
||||
|
||||
return null # prevent calling `set`
|
||||
|
||||
url: ->
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#
|
||||
|
||||
BaseModel = require '../base_model'
|
||||
Inventory = require './inventory'
|
||||
Mod = require './mod'
|
||||
ModVersionParser = require '../parsing/mod_version_parser'
|
||||
Recipe = require './recipe'
|
||||
SimpleInventory = require '../crafting/simple_inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -24,6 +25,12 @@ 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
|
||||
|
||||
@@ -75,7 +82,7 @@ module.exports = class ModPack extends BaseModel
|
||||
result.modSlug = @_mods[0].slug
|
||||
result.modVersion = @_mods[0].activeVersion
|
||||
|
||||
craftingUrlInventory = new Inventory modPack:this
|
||||
craftingUrlInventory = new SimpleInventory modPack:this
|
||||
if item?.multiblock?
|
||||
craftingUrlInventory.addInventory item.multiblock.inventory
|
||||
else
|
||||
@@ -165,12 +172,18 @@ module.exports = class ModPack extends BaseModel
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_onModVersionLoaded: (modVersion)->
|
||||
working = true
|
||||
@eachMod (mod)->
|
||||
mod.eachModVersion (modVersion)->
|
||||
working = working and (modVersion.isUnloaded or modVersion.isLoading)
|
||||
mods = @getAllMods()
|
||||
return true unless mods.length > 0
|
||||
|
||||
return if working
|
||||
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
|
||||
|
||||
@trigger c.event.change, this
|
||||
@trigger c.event.sync, this
|
||||
|
||||
@@ -59,6 +59,10 @@ 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
|
||||
|
||||
@@ -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,16 +111,7 @@ module.exports = class Recipe extends BaseModel
|
||||
return result
|
||||
|
||||
isPassThroughFor: (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
|
||||
return @getQuantityProduced(itemSlug) is @getQuantityRequired(itemSlug)
|
||||
|
||||
produces: (itemSlug)->
|
||||
if not @_produces?
|
||||
@@ -221,7 +212,11 @@ module.exports = class Recipe extends BaseModel
|
||||
_computeQuantities: (pattern)->
|
||||
quantityMap = {}
|
||||
|
||||
for c in pattern.split ''
|
||||
index = 0
|
||||
while index < pattern.length
|
||||
c = pattern[index]
|
||||
index += 1
|
||||
|
||||
continue if c is '.'
|
||||
continue if c is ' '
|
||||
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
#
|
||||
# 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]
|
||||
@@ -1,121 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,112 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,134 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,55 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,165 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,50 +0,0 @@
|
||||
#
|
||||
# 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(', ')
|
||||
@@ -1,59 +0,0 @@
|
||||
#
|
||||
# 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 []
|
||||
@@ -1,67 +0,0 @@
|
||||
#
|
||||
# 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'
|
||||
@@ -1,54 +0,0 @@
|
||||
#
|
||||
# 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 []
|
||||
@@ -1,62 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,58 +0,0 @@
|
||||
#
|
||||
# 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'
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# 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'
|
||||
@@ -1,41 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,38 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,48 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,143 +0,0 @@
|
||||
#
|
||||
# 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
|
||||
@@ -1,125 +0,0 @@
|
||||
#
|
||||
# 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 []
|
||||
@@ -1,51 +0,0 @@
|
||||
#
|
||||
# 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'
|
||||
@@ -1,63 +0,0 @@
|
||||
#
|
||||
# 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'
|
||||
@@ -40,6 +40,7 @@ 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
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#
|
||||
# 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
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
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'
|
||||
@@ -47,6 +47,7 @@ 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,7 +7,6 @@
|
||||
|
||||
.content.view__browse_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.tile_container
|
||||
@@ -10,14 +10,32 @@
|
||||
|
||||
.right {
|
||||
.tile_container {
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-between;
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
|
||||
.view__mod_tile {
|
||||
max-width : calc(50% - 12px); // $size-margin-large / 2
|
||||
flex : 1 1 calc(50% - 12px); // $size-margin-large / 2
|
||||
margin-right : $size-margin-medium;
|
||||
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,7 +5,6 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
AdsenseController = require '../common/adsense/adsense_controller'
|
||||
ModTileController = require './mod_tile/mod_tile_controller'
|
||||
PageController = require '../page_controller'
|
||||
|
||||
@@ -32,8 +31,6 @@ module.exports = class BrowsePageController extends PageController
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
|
||||
|
||||
@$tileContainer = @$('.tile_container')
|
||||
|
||||
refresh: ->
|
||||
|
||||
@@ -6,16 +6,14 @@
|
||||
//
|
||||
|
||||
.view__mod_tile {
|
||||
min-width : $size-minecraft-block * 9;
|
||||
min-width : $size-minecraft-block * 9;
|
||||
|
||||
display : flex;
|
||||
justify-content : stretch;
|
||||
align-items : stretch;
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
max-width: 100%;
|
||||
|
||||
@include floating-panel;
|
||||
flex: 1 1 auto;
|
||||
|
||||
border : $size-border-medium solid $color-background-panel;
|
||||
color : $color-text;
|
||||
text-decoration : none;
|
||||
@@ -39,6 +37,7 @@
|
||||
overflow : hidden;
|
||||
|
||||
.title {
|
||||
max-width: 0;
|
||||
margin-bottom : $size-margin-medium;
|
||||
|
||||
font-family : $font-family-header;
|
||||
@@ -86,3 +85,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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,6 +12,7 @@ BaseController = require '../../base_controller'
|
||||
module.exports = class ModTileController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.tagName = 'section'
|
||||
options.templateName = 'browse_page/mod_tile'
|
||||
super options
|
||||
|
||||
|
||||
@@ -6,20 +6,64 @@
|
||||
//
|
||||
|
||||
.view__adsense {
|
||||
.skyscraper {
|
||||
height : $size-skyscraper-height;
|
||||
width : $size-skyscraper-width;
|
||||
position : relative;
|
||||
left : 50%;
|
||||
margin : $size-margin-large 0;
|
||||
position : relative;
|
||||
transform : translate(-50%);
|
||||
|
||||
border : 0.1em solid $color-gray-faint;
|
||||
margin : $size-margin-large 0;
|
||||
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;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-top : 0;
|
||||
}
|
||||
}
|
||||
|
||||
ins {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view__mod_tile + .view__adsense {
|
||||
margin-top : 0;
|
||||
}
|
||||
|
||||
@@ -5,63 +5,132 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class AdsenseController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'common/adsense'
|
||||
super options
|
||||
module.exports = class AdsenseController
|
||||
|
||||
constructor: ->
|
||||
@_adsEnabled = global.env in c.productionEnvs
|
||||
@_adCount = 0
|
||||
@_adsPlaced = 0
|
||||
@_waiting = false
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
fillAdPositions: ->
|
||||
if @_adCount > 0
|
||||
return unless @$sidebar and @$mainBody
|
||||
return unless @_adCount < @_computeMaxAds()
|
||||
return unless @_adCount < c.adsense.slotIds.length
|
||||
adType = @_computeAdType()
|
||||
maxAds = c.adsense[adType].slotIds.length - @_adsPlaced
|
||||
return if maxAds is 0
|
||||
|
||||
$adContainer = $('<div class="skyscraper"></div>')
|
||||
@$el.append $adContainer
|
||||
logger.verbose => "Looking to place #{maxAds} more #{adType} ads..."
|
||||
|
||||
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 adType is 'skyscraper'
|
||||
placedNewAd = @_createSkyscraperPositions()
|
||||
else
|
||||
placedNewAd = @_createBannerPositions adType
|
||||
|
||||
@_adCount += 1
|
||||
_.defer => @fillAdPositions()
|
||||
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"
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$mainBody = $('.page > .content > .right')
|
||||
@$sidebar = $('.page > .content > .left')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
reset: ->
|
||||
@_adsPlaced = 0
|
||||
@_waitForPageReadiness()
|
||||
super
|
||||
|
||||
# Private Methods #############################################################################
|
||||
|
||||
_computeMaxAds: ->
|
||||
return null unless @$sidebar
|
||||
result = Math.floor @$sidebar.height() / (c.adsense.skyscraper.height + c.adsense.skyscraper.margin)
|
||||
_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
|
||||
|
||||
return result
|
||||
|
||||
_loadAds: ->
|
||||
_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: ->
|
||||
return unless @_adsEnabled
|
||||
|
||||
logger.info "Loading #{@_adCount} Google AdSense ads"
|
||||
logger.info "Loading Google AdSense ads"
|
||||
try
|
||||
global.adsbygoogle ||= []
|
||||
global.adsbygoogle.push {}
|
||||
@@ -70,11 +139,12 @@ module.exports = class AdsenseController extends BaseController
|
||||
|
||||
_waitForPageReadiness: ->
|
||||
return if @_waiting
|
||||
adCount = @_computeAdCount @_computeAdType()
|
||||
|
||||
if @$sidebar.height() < c.adsense[@model].height / 2
|
||||
if adCount is 0
|
||||
logger.verbose "Adsense is waiting for room to insert ads"
|
||||
@_waiting = true
|
||||
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.duration.normal
|
||||
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.adsense.readinessCheckInterval
|
||||
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 an item...
|
||||
.button.item-selector: .bezel: p add<span class="full"> an item</span>...
|
||||
.button.clear: .bezel: p clear
|
||||
|
||||
@@ -38,12 +38,24 @@
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
.item-selector {
|
||||
flex : 85%;
|
||||
flex : 80%;
|
||||
margin-right : $size-margin-medium;
|
||||
|
||||
@include screen-size(mobile) {
|
||||
flex: 50%;
|
||||
|
||||
.full {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clear {
|
||||
flex : 15%;
|
||||
flex : 20%;
|
||||
|
||||
@include screen-size(mobile) {
|
||||
flex: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,8 +73,12 @@
|
||||
.quantity {
|
||||
padding-right : $size-margin-medium;
|
||||
input {
|
||||
width: 3em;
|
||||
font-size : $font-size-xlarge;
|
||||
width : 3em;
|
||||
font-size : $font-size-xlarge;
|
||||
|
||||
@include screen-size(mobile) {
|
||||
width : 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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: center;
|
||||
align-items: flex-start;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block;
|
||||
@@ -22,9 +22,19 @@
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
line-height: $size-minecraft-block;
|
||||
overflow-x: hidden;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include screen-size(mobile) {
|
||||
.view__item_tile {
|
||||
a {
|
||||
p {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@
|
||||
}
|
||||
|
||||
&.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 the name of an item')
|
||||
input(placeholder='enter an item name')
|
||||
img.close(src='/images/close.png')
|
||||
.results
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
}
|
||||
|
||||
.view__item_selector_popup {
|
||||
max-width: $size-minecraft-block * 20;
|
||||
height : 66%;
|
||||
|
||||
background : $color-background-panel;
|
||||
@@ -35,6 +36,7 @@
|
||||
|
||||
input {
|
||||
flex: 1 1 100%;
|
||||
min-width: $font-size-large * 10;
|
||||
|
||||
background: none;
|
||||
border: 0;
|
||||
@@ -50,8 +52,15 @@
|
||||
|
||||
.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.down
|
||||
when c.key.downArrow
|
||||
@_selectNext()
|
||||
return false
|
||||
when c.key.up
|
||||
when c.key.upArrow
|
||||
@_selectPrevious()
|
||||
return false
|
||||
|
||||
@@ -82,6 +82,7 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
@model.hint = @$hintField.val()
|
||||
@tryRefresh()
|
||||
@onResultSelected @_elementControllers[0]
|
||||
return false
|
||||
|
||||
onPopupClicked: (event)->
|
||||
return false
|
||||
@@ -90,6 +91,7 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
@_session.resolve controller.model.slug
|
||||
@_session = null
|
||||
@_close()
|
||||
return false
|
||||
|
||||
onResultSelected: (controller)->
|
||||
for c in @_elementControllers
|
||||
@@ -98,6 +100,8 @@ module.exports = class ItemSelectorController extends BaseController
|
||||
if controller?
|
||||
controller.selected = true
|
||||
|
||||
return false
|
||||
|
||||
onScreenClicked: (event)->
|
||||
@_close()
|
||||
return false
|
||||
@@ -169,6 +173,8 @@ 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]
|
||||
@@ -179,7 +185,13 @@ 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]
|
||||
@@ -190,6 +202,10 @@ 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.css 'scrollTop', top
|
||||
@$resultsContainer.animate {scrollTop:top}, c.duration.snap
|
||||
|
||||
@@ -128,3 +128,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include screen-size(mobile) {
|
||||
.view__markdown_section {
|
||||
.creating {
|
||||
padding-bottom: $size-margin-xlarge;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,16 +60,15 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onCancelClicked: (event)->
|
||||
event.preventDefault()
|
||||
@model = @_originalModel
|
||||
@resetToDefaultState()
|
||||
|
||||
@_imageListController.reset()
|
||||
@_updatePreview()
|
||||
return false
|
||||
|
||||
onEditClicked: (event)->
|
||||
event.preventDefault()
|
||||
return unless @editable
|
||||
return false unless @editable
|
||||
|
||||
@_originalModel = @model
|
||||
@state = @State.waiting
|
||||
@@ -81,22 +80,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 =>
|
||||
@@ -109,9 +108,11 @@ module.exports = class MarkdownSectionController extends BaseController
|
||||
.then =>
|
||||
@state = @State.viewing
|
||||
|
||||
return false
|
||||
|
||||
onTextChanged: (event)->
|
||||
event.preventDefault()
|
||||
@model = @$textarea.val()
|
||||
return false
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
display: flex;
|
||||
|
||||
.input {
|
||||
flex: 1 1 50%;
|
||||
flex: 0 0 auto;
|
||||
|
||||
align-items : center;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.view__crafting_grid {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.tool {
|
||||
margin-top : $size-margin-small;
|
||||
|
||||
@@ -24,7 +28,7 @@
|
||||
}
|
||||
|
||||
.output {
|
||||
flex: 1 1 50%;
|
||||
flex: 1 1 100%;
|
||||
margin-left: $size-margin-medium;
|
||||
|
||||
display : flex;
|
||||
@@ -42,4 +46,4 @@
|
||||
margin-top : $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@
|
||||
//-
|
||||
|
||||
.view__stack
|
||||
.quantity: input
|
||||
.quantity
|
||||
input
|
||||
.hover
|
||||
.icon: img(src='')
|
||||
.name: a
|
||||
.action.first(style="display: none")
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
.quantity {
|
||||
padding-right : $size-margin-medium;
|
||||
position : relative;
|
||||
text-align : right;
|
||||
|
||||
input {
|
||||
@@ -32,6 +33,38 @@
|
||||
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 {
|
||||
@@ -84,3 +117,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include screen-size(mobile) {
|
||||
.view__stack.editable {
|
||||
.quantity {
|
||||
input {
|
||||
width : 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ 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'
|
||||
@@ -107,6 +108,7 @@ 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
|
||||
@@ -124,6 +126,7 @@ module.exports = class StackController extends BaseController
|
||||
@$quantityField.val quantityText
|
||||
|
||||
@_refreshButtons()
|
||||
@_refreshHover()
|
||||
|
||||
if @_editable
|
||||
@$el.addClass 'editable'
|
||||
@@ -169,6 +172,20 @@ 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
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//-
|
||||
//- 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
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,5 @@
|
||||
//-
|
||||
|
||||
.view__video
|
||||
iframe(width="356", height="267", src="", frameborder="0", allowfullscreen="true")
|
||||
iframe(src="", frameborder="0", allowfullscreen="true")
|
||||
.caption: p
|
||||
@@ -11,6 +11,10 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
iframe {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.caption {
|
||||
margin-top: $size-margin-small;
|
||||
|
||||
|
||||
@@ -5,16 +5,8 @@
|
||||
//- 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
|
||||
@@ -26,19 +18,19 @@
|
||||
p.
|
||||
Not sure what you'd like to make? Try one of these:
|
||||
.items
|
||||
a(href="/craft/potion_of_strength_ii_1_30")
|
||||
a(href="/" data-slug="potion_of_strength_ii_1_30")
|
||||
img(src="/data/minecraft/items/potion_of_strength_ii_1_30/icon.png")
|
||||
a(href="/craft/quarry")
|
||||
a(href="/" data-slug="quarry")
|
||||
img(src="/data/buildcraft/items/quarry/icon.png")
|
||||
a(href="/craft/quantumsuit_bodyarmor:quantumsuit_boots:quantumsuit_helmet:quantumsuit_leggings")
|
||||
a(href="/" data-slug="quantumsuit_bodyarmor:quantumsuit_boots:quantumsuit_helmet:quantumsuit_leggings")
|
||||
img(src="/data/ic2_classic/items/quantumsuit_helmet/icon.png")
|
||||
a(href="/craft/solar_panel_vi")
|
||||
a(href="/" data-slug="solar_panel_vi")
|
||||
img(src="/data/solar_flux/items/solar_panel_vi/icon.png")
|
||||
a(href="/craft/crystal_chest")
|
||||
a(href="/" data-slug="crystal_chest")
|
||||
img(src="/data/iron_chests/items/crystal_chest/icon.png")
|
||||
a(href="/craft/digital_miner")
|
||||
a(href="/" data-slug="digital_miner")
|
||||
img(src="/data/mekanism/items/digital_miner/icon.png")
|
||||
a(href="/craft/2.tesseract")
|
||||
a(href="/" data-slug="2.tesseract")
|
||||
img(src="/data/thermal_expansion/items/tesseract/icon.png")
|
||||
|
||||
section.want
|
||||
@@ -51,8 +43,6 @@
|
||||
.panel
|
||||
.view__inventory.large.editable
|
||||
|
||||
section.view__craftsman_working
|
||||
|
||||
section.need
|
||||
h2 Need to Gather
|
||||
.panel
|
||||
@@ -60,4 +50,6 @@
|
||||
|
||||
section.steps
|
||||
h2 Steps
|
||||
.panel
|
||||
.panel
|
||||
|
||||
section.view__craftsman_working
|
||||
|
||||
@@ -49,4 +49,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.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,19 +5,21 @@
|
||||
# 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'
|
||||
@@ -33,10 +35,9 @@ module.exports = class CraftPageController extends PageController
|
||||
@_router = options.router
|
||||
@_storage = options.storage
|
||||
|
||||
# c.event Methods ################################################################################
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onHaveInventoryChanged: ->
|
||||
logger.warning => "storing have: #{@model.craftsman.have.unparse()}"
|
||||
@_storage.store 'crafting-plan:have', @model.craftsman.have.unparse()
|
||||
|
||||
onMoveNeedToHave: (itemSlug)->
|
||||
@@ -50,27 +51,39 @@ 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: ->
|
||||
return 'Craft'
|
||||
description = @model.craftsman.want.toDescription()
|
||||
return null unless description?
|
||||
return "Crafting Plan for #{description}"
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
|
||||
|
||||
@_wantInventoryController = @addChild InventoryController, '.want .view__inventory',
|
||||
firstButtonType: 'remove'
|
||||
imageLoader: @_imageLoader
|
||||
@@ -89,7 +102,6 @@ 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
|
||||
@@ -102,6 +114,7 @@ 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')
|
||||
@@ -112,25 +125,37 @@ 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': 'routeLinkClick'
|
||||
'click .instructions a': 'onSampleClicked'
|
||||
|
||||
# Private Methods ################################################################################
|
||||
|
||||
@@ -138,7 +163,7 @@ module.exports = class CraftPageController extends PageController
|
||||
controller.model.addToolsTo @model.craftsman.want
|
||||
|
||||
_completeStep: (controller)->
|
||||
controller.model.completeInto @model.craftsman.have
|
||||
controller.markComplete @model.craftsman.have
|
||||
|
||||
_isAddingToolsPossible: (controller)->
|
||||
tools = controller.model.recipe.tools
|
||||
@@ -160,6 +185,12 @@ 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
|
||||
|
||||
@@ -172,11 +203,13 @@ 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, @$workingSection]
|
||||
visibleSections.push el for el in [@$wantSection, @$haveSection, @$workingSection]
|
||||
else if not @model.craftsman.complete
|
||||
visibleSections.push el for el in [@$wantSection, @$workingSection]
|
||||
visibleSections.push el for el in [@$wantSection, @$haveSection, @$workingSection]
|
||||
else
|
||||
visibleSections.push el for el in [@$haveSection, @$needSection, @$stepsSection, @$wantSection]
|
||||
visibleSections.push el for el in [@$haveSection, @$wantSection, @$workingSection]
|
||||
if @model.craftsman.plan?
|
||||
visibleSections.push el for el in [@$needSection, @$stepsSection]
|
||||
|
||||
@hide $el for $el in allSections
|
||||
@show $el for $el in visibleSections
|
||||
@@ -209,3 +242,28 @@ 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,11 +5,16 @@
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_group.section
|
||||
.content
|
||||
.view__craftsman_working
|
||||
.panel
|
||||
.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,32 +6,78 @@ All rights reserved.
|
||||
*/
|
||||
|
||||
.view__craftsman_working {
|
||||
.content {
|
||||
background: $color-white;
|
||||
box-shadow: none;
|
||||
|
||||
.panel {
|
||||
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;
|
||||
font-family : $font-family-header;
|
||||
font-size : $font-size-large;
|
||||
text-align : center;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
p {
|
||||
font-family: $font-family-header;
|
||||
font-size: $font-size-medium;
|
||||
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%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,48 +12,94 @@ 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
|
||||
|
||||
@model.on c.event.change, => @_refreshStatusText()
|
||||
@_hideTimer = null
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onButtonClicked: ->
|
||||
@trigger c.event.click
|
||||
@model.reset()
|
||||
@model.work()
|
||||
return false
|
||||
|
||||
# BaseController Methods #######################################################################
|
||||
|
||||
onDidModelChange: ->
|
||||
@refresh()
|
||||
|
||||
onDidRender: ->
|
||||
@$message = @$('.message p')
|
||||
@$count = @$('.count p')
|
||||
@$waiting = @$('.waiting')
|
||||
@$button = @$('.button')
|
||||
@$count = @$('.count p')
|
||||
@$message = @$('.message p')
|
||||
@$outdated = @$('.outdated')
|
||||
@$waiting = @$('.waiting')
|
||||
|
||||
@_controls = [@$button, @$count, @$message, @$outdated, @$waiting]
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_refreshStatusText()
|
||||
super
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshStatusText: ->
|
||||
return unless @$message? and @$count?
|
||||
|
||||
@show @$waiting
|
||||
button = count = message = outdated = waiting = null
|
||||
|
||||
switch @model.stage
|
||||
when Craftsman::STAGE.WAITING
|
||||
@$message.html 'Preparing crafting calculation...'
|
||||
@$count.html ''
|
||||
when Craftsman::STAGE.READY
|
||||
message = 'Ready to compute crafting plan!'
|
||||
count = 'Click "Calculate" to continue.'
|
||||
button = true
|
||||
when Craftsman::STAGE.GRAPHING
|
||||
@$message.html 'Researching recipes...'
|
||||
@$count.html "Found #{@model.stageCount} recipes so far..."
|
||||
message = 'Researching recipes...'
|
||||
count = "Found #{@model.stageCount} recipes so far..."
|
||||
watiing = true
|
||||
when Craftsman::STAGE.PLANNING
|
||||
@$message.html 'Figuring out possible crafting plans...'
|
||||
@$count.html "Found #{@model.stageCount} possibilities so far..."
|
||||
message = 'Figuring out possible crafting plans...'
|
||||
count = "Found #{@model.stageCount} possibilities so far..."
|
||||
watiing = true
|
||||
when Craftsman::STAGE.ANALYZING
|
||||
@$message.html 'Looking for the best plan...'
|
||||
@$count.html "Finished checking #{@model.stageCount} so far..."
|
||||
message = 'Looking for the best plan...'
|
||||
count = "Finished checking #{@model.stageCount} so far..."
|
||||
watiing = true
|
||||
when Craftsman::STAGE.COMPLETE
|
||||
@$message.html 'All done!'
|
||||
@$count.html ''
|
||||
message = 'Crafting plan is complete.'
|
||||
when Craftsman::STAGE.INVALID
|
||||
@$message.html 'Couldn\'t make a crafting plan'
|
||||
@$count.html ''
|
||||
@hide @waiting
|
||||
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
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .button': 'onButtonClicked'
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
.view__inventory
|
||||
.view__recipe
|
||||
.buttons
|
||||
.button.complete: .bezel: p complete step
|
||||
.button.tool: .bezel: p make this tool
|
||||
a.button.tool: .bezel: p <span class="full">show plan for </span>tools
|
||||
.button.complete: .bezel: p complete<span class="full"> step</span>
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
.view__inventory {
|
||||
flex: 50%;
|
||||
margin-left: $size-margin-large;
|
||||
|
||||
.buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.view__recipe {
|
||||
@@ -52,5 +56,51 @@
|
||||
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,6 +8,7 @@
|
||||
BaseController = require '../../base_controller'
|
||||
InventoryController = require '../../common/inventory/inventory_controller'
|
||||
RecipeController = require '../../common/recipe/recipe_controller'
|
||||
SimpleInventory = require '../../../models/crafting/simple_inventory'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -29,16 +30,19 @@ 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: ->
|
||||
@@ -63,6 +67,9 @@ 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: ->
|
||||
@@ -83,11 +90,19 @@ 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
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
|
||||
display: flex;
|
||||
|
||||
@include screen-size(mobile) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-content {
|
||||
flex: 1 1 100%;
|
||||
|
||||
@@ -73,13 +77,11 @@
|
||||
background: $color-gray-light;
|
||||
border: $size-border-medium solid $color-black;
|
||||
border-left: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
p {
|
||||
transform: rotate(-90deg);
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translateX(-50%) rotate(-90deg) translateX(10%);
|
||||
|
||||
font-family: $font-family-header;
|
||||
font-size: $label-size;
|
||||
|
||||
@@ -19,12 +19,20 @@
|
||||
> .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;
|
||||
|
||||
@@ -38,6 +46,7 @@
|
||||
.action {
|
||||
flex: 1 1 auto;
|
||||
align-self: center;
|
||||
margin-top: $size-margin-large;
|
||||
margin-bottom: $size-margin-large;
|
||||
|
||||
display: flex;
|
||||
@@ -55,3 +64,13 @@
|
||||
height: $size-minecraft-block;
|
||||
}
|
||||
}
|
||||
|
||||
@include screen-size(mobile) {
|
||||
.view__footer {
|
||||
> .content {
|
||||
.left, .center, .right {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,4 +21,6 @@
|
||||
.button.search: .bezel: p Search
|
||||
.button.login: .bezel: p Login
|
||||
|
||||
.breadcrumbs
|
||||
.breadcrumb-bar
|
||||
.breadcrumbs
|
||||
.extra-nav
|
||||
|
||||
@@ -23,24 +23,22 @@
|
||||
|
||||
img {
|
||||
position : relative;
|
||||
top : -$size-minecraft-block * 0.5;
|
||||
left : -$size-minecraft-block * 0.5;
|
||||
height : $size-minecraft-block * 2.0;
|
||||
width : $size-minecraft-block * 2.0;
|
||||
height : $size-minecraft-block * 1.5;
|
||||
width : $size-minecraft-block * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
flex: 1 1 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.addthis_sharing_toolbox {
|
||||
flex : 0 0 auto;
|
||||
min-height : 37px;
|
||||
min-height : 37px; // actual size of addthis component
|
||||
min-width : 144px;
|
||||
margin-right : $size-margin-small;
|
||||
|
||||
|
||||
&.placeholder {
|
||||
border : 1px solid orange;
|
||||
text-align : center;
|
||||
@@ -62,36 +60,103 @@
|
||||
align-items : center;
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-around;
|
||||
justify-content : center;
|
||||
padding : $size-margin-medium $size-margin-medium;
|
||||
padding-top : 0;
|
||||
|
||||
.button {
|
||||
flex : 1 1 20%;
|
||||
min-width : 4 * $size-minecraft-block;
|
||||
max-width : 5 * $size-minecraft-block;
|
||||
margin-top: $size-margin-medium;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
.breadcrumb-bar {
|
||||
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;
|
||||
|
||||
padding: $size-margin-small $size-margin-medium;
|
||||
display: flex;
|
||||
|
||||
&:before {
|
||||
content: "⟩";
|
||||
margin-right: $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;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.extra-nav {
|
||||
flex: 1 1 25%;
|
||||
align-self: flex-end;
|
||||
|
||||
span {
|
||||
margin: 0 $size-margin-medium;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
BaseController = require '../base_controller'
|
||||
ItemSelectorController = require '../common/item_selector/item_selector_controller'
|
||||
UrlParams = require '../url_params'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
@@ -43,9 +44,11 @@ module.exports = class HeaderController extends BaseController
|
||||
@router.navigate '/news', trigger:true
|
||||
return false
|
||||
|
||||
onSearch: ->
|
||||
@_selector.launch()
|
||||
onSearch: (event, hint='')->
|
||||
@_selector.launch hint
|
||||
.then (itemSlug)=>
|
||||
return unless itemSlug?
|
||||
|
||||
itemDisplay = @_modPack.findItemDisplay itemSlug
|
||||
@router.navigate itemDisplay.itemUrl, trigger:true
|
||||
return false
|
||||
@@ -62,8 +65,11 @@ 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)->
|
||||
@@ -75,6 +81,8 @@ module.exports = class HeaderController extends BaseController
|
||||
@$loginButtonLabel.text if global.site.user? then "Logout" else "Login"
|
||||
|
||||
@_refreshBreadcrumbs()
|
||||
@_refreshExtraNav()
|
||||
@_refreshSelectedNavButton()
|
||||
@_refreshTitle()
|
||||
super
|
||||
|
||||
@@ -82,15 +90,21 @@ 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 .breadcrumbs a': 'routeLinkClick'
|
||||
'click .craft': 'onCraft'
|
||||
'click .browse': 'onBrowse'
|
||||
'click .login': 'onLogin'
|
||||
'click .news': 'onNews'
|
||||
'click .search': 'onSearch'
|
||||
'click .breadcrumb-bar 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 ?= []
|
||||
@@ -106,6 +120,23 @@ 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
|
||||
|
||||
@@ -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="width=480")
|
||||
meta(name="viewport" content="user-scalable=no, width=device-width")
|
||||
|
||||
body
|
||||
.view__feedback
|
||||
@@ -21,7 +21,7 @@ html
|
||||
.page
|
||||
.view__header
|
||||
|
||||
.content(style="display: none")
|
||||
.content.hidden
|
||||
.content-loading
|
||||
img(src="/images/wait.gif")
|
||||
|
||||
@@ -32,4 +32,6 @@ html
|
||||
|
||||
include ../includes/_google_adsense.jade
|
||||
include ../includes/_google_analytics.jade
|
||||
include ../includes/_google_sitelinks.jade
|
||||
include ../includes/_twitter_follow.jade
|
||||
|
||||
|
||||
@@ -36,19 +36,22 @@ html {
|
||||
}
|
||||
|
||||
& > *:first-child {
|
||||
flex: 0 0 50%;
|
||||
flex: 1 1 100%;
|
||||
|
||||
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;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
.content.view__item_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.about
|
||||
@@ -17,7 +16,7 @@
|
||||
.title
|
||||
a.sourceMod
|
||||
.button-row
|
||||
.button.craftingPlan: .bezel: p View Crafting Plan
|
||||
.button.craftingPlan: .bezel: p View <span class="full">Crafting </span>Plan
|
||||
.right
|
||||
p Official Link:
|
||||
a.officialLink project page
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
.button {
|
||||
flex: 1 1 100%;
|
||||
max-width: 66%;
|
||||
max-width: $size-minecraft-block * 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,3 +58,50 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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,7 +5,6 @@
|
||||
# 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'
|
||||
@@ -42,6 +41,7 @@ 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,6 +68,13 @@ 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
|
||||
@@ -83,9 +90,11 @@ module.exports = class ItemPageController extends PageController
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper', router: @_router
|
||||
onDidModelChange: ->
|
||||
@trigger c.event.change
|
||||
super
|
||||
|
||||
onDidRender: ->
|
||||
options = imageLoader:@_imageLoader, modPack:@_modPack, router:@_router, show:false
|
||||
@_multiblockController = @addChild MultiblockViewerController, '.view__multiblock_viewer', options
|
||||
@_similarItemsController = @addChild ItemGroupController, '.view__item_group.similar', options
|
||||
@@ -101,11 +110,12 @@ module.exports = class ItemPageController extends PageController
|
||||
endEditing: => @_endEditingDescription()
|
||||
enterFeedback: @_enterFeedback
|
||||
|
||||
@$aboutImage = @$('.about img')
|
||||
@$name = @$('.about .title')
|
||||
@$officialLink = @$('.about a.officialLink')
|
||||
@$sourceModLink = @$('.about a.sourceMod')
|
||||
@$aboutLinks = @$('.about .right')
|
||||
@$aboutImage = @$('.about img')
|
||||
@$craftingPlanButton = @$('.button.craftingPlan')
|
||||
@$name = @$('.about .title')
|
||||
@$officialLink = @$('.about a.officialLink')
|
||||
@$sourceModLink = @$('.about a.sourceMod')
|
||||
@$aboutLinks = @$('.about .right')
|
||||
|
||||
@$multiblockSection = @$('section.multiblock')
|
||||
@$recipeContainer = @$('section.recipes .panel')
|
||||
@@ -135,6 +145,15 @@ 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()
|
||||
@@ -317,7 +336,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,3 +75,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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,8 +20,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
order: 10;
|
||||
}
|
||||
|
||||
.pattern {
|
||||
flex : 0 1 33%;
|
||||
order: 20;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
@@ -36,9 +41,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - mod_editor_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__mod_editor_page
|
||||
.left
|
||||
|
||||
.right
|
||||
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// Crafting Guide - mod_editor_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__mod_editor_page {
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#
|
||||
# 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 ##############################################################################
|
||||
@@ -1,5 +1,5 @@
|
||||
//-
|
||||
//- Crafting Guide - item_page.jade
|
||||
//- Crafting Guide - mod_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
.content.view__mod_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.about
|
||||
@@ -17,8 +16,11 @@
|
||||
.title
|
||||
.author
|
||||
.description
|
||||
.modpack
|
||||
span.checkbox
|
||||
span.label I have this mod in my modpack
|
||||
.version
|
||||
span in my mod pack
|
||||
span I'm using version:
|
||||
select
|
||||
.warning.
|
||||
<b>PLEASE NOTE:</b> By selecting "no", crafting plans, search, and other features will ignore items
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Crafting Guide - item_page.scss
|
||||
// Crafting Guide - mod_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
@@ -49,8 +49,44 @@
|
||||
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-large;
|
||||
margin-top: $size-margin-medium;
|
||||
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
@@ -91,4 +127,43 @@
|
||||
@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,7 +5,6 @@
|
||||
# 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'
|
||||
@@ -29,6 +28,14 @@ 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
|
||||
@@ -58,8 +65,6 @@ 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')
|
||||
@@ -67,15 +72,18 @@ 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()
|
||||
@@ -86,6 +94,7 @@ module.exports = class ModPageController extends BaseController
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'change .version select': 'onVersionChanged'
|
||||
'click .modpack': 'onToggleEnabled'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
@@ -107,6 +116,19 @@ 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
|
||||
@@ -124,9 +146,10 @@ module.exports = class ModPageController extends BaseController
|
||||
router: @_router
|
||||
title: if group is Item.Group.Other then 'Items' else group
|
||||
|
||||
@_groupControllers.push controller
|
||||
@$itemGroups.append controller.$el
|
||||
controller.render()
|
||||
_.defer =>
|
||||
@_groupControllers.push controller
|
||||
@$itemGroups.append controller.$el
|
||||
controller.render()
|
||||
else
|
||||
controller.modVersion = modVersion
|
||||
controller.model = items
|
||||
@@ -163,19 +186,16 @@ 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
Reference in New Issue
Block a user