Refactor website with new design & structure
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
#
|
||||
# Crafting Guide - base_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
templates = require './templates'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class BaseController extends Backbone.View
|
||||
|
||||
constructor: (options={})->
|
||||
@_children = []
|
||||
@_model = null
|
||||
@_parent = options.parent
|
||||
@_rendered = false
|
||||
|
||||
@router = options.router
|
||||
|
||||
@tryRefresh = _.debounce (=> @_tryRefresh()), 100
|
||||
|
||||
@_loadTemplate options.templateName
|
||||
super options
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
addChild: (Controller, atSelector, options={})->
|
||||
if not _.isFunction Controller then throw new Error "Controller must be a constructor"
|
||||
|
||||
options.el = @$(atSelector)[0]
|
||||
options.parent = this
|
||||
|
||||
child = new Controller options
|
||||
child.render options
|
||||
@_children.push child
|
||||
return child
|
||||
|
||||
hide: ($el)->
|
||||
$el ?= @$el
|
||||
$el.css 'display', 'none'
|
||||
|
||||
refresh: ->
|
||||
logger.verbose => "#{this} refreshing"
|
||||
|
||||
remove: ->
|
||||
@$el.remove()
|
||||
@off()
|
||||
|
||||
routeLinkClick: (event)->
|
||||
event.preventDefault()
|
||||
href = $(event.target).attr 'href'
|
||||
href ?= $(event.currentTarget).attr 'href'
|
||||
logger.info "Re-routing link to internal navigation: #{href}"
|
||||
|
||||
if href? and href.match /^http/
|
||||
window.location.href = href
|
||||
else
|
||||
@router.navigate href, trigger:true
|
||||
|
||||
show: ($el)->
|
||||
$el ?= @$el
|
||||
$el.css 'display', ''
|
||||
|
||||
unrender: ->
|
||||
@undelegateEvents()
|
||||
@$el.empty()
|
||||
@_rendered = false
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onDidModelChange: ->
|
||||
@tryRefresh()
|
||||
|
||||
onDidModelSync: ->
|
||||
@tryRefresh()
|
||||
|
||||
onWillRender: -> # do nothing
|
||||
|
||||
onDidRender: -> # do nothing
|
||||
|
||||
onWillShow: -> # do nothing
|
||||
|
||||
onDidShow: -> # do nothing
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
if oldModel?.on?
|
||||
@stopListening oldModel
|
||||
if newModel?.on?
|
||||
@listenTo newModel, 'sync', (e)=> @onDidModelSync e
|
||||
@listenTo newModel, 'change', (e)=> @onDidModelChange e
|
||||
return newModel
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
model:
|
||||
get: -> @_model
|
||||
set: (newModel)->
|
||||
return if @model is newModel
|
||||
|
||||
newModel = @onWillChangeModel @_model, newModel
|
||||
@_model = newModel
|
||||
@tryRefresh()
|
||||
|
||||
rendered:
|
||||
get: -> @_rendered
|
||||
|
||||
user:
|
||||
get: -> @_user
|
||||
set: (newUser)->
|
||||
@_user = newUser
|
||||
@tryRefresh()
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return {}
|
||||
|
||||
render: (options={})->
|
||||
options.force ?= false
|
||||
options.show ?= true
|
||||
return this unless not @_rendered or options.force
|
||||
|
||||
if not @_template?
|
||||
logger.error => "Default render called for #{@constructor.name} without a template"
|
||||
return this
|
||||
|
||||
data = (@model?.toHash? and @model.toHash()) or @model or {}
|
||||
logger.verbose => "#{this} rendering with data: #{data}"
|
||||
$renderedEl = $($(@_template(data))[0])
|
||||
|
||||
@onWillRender()
|
||||
@unrender()
|
||||
|
||||
@$el.append $renderedEl.children()
|
||||
@$el.addClass $renderedEl.attr 'class'
|
||||
@$el.data $renderedEl.data()
|
||||
@delegateEvents()
|
||||
|
||||
@_rendered = true
|
||||
@onDidRender()
|
||||
|
||||
@refresh()
|
||||
|
||||
return this
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
toString: ->
|
||||
return "#{@constructor.name}.#{@cid}"
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_loadTemplate: (templateName)->
|
||||
if templateName?
|
||||
@_template = templates[templateName]
|
||||
|
||||
_tryRefresh: ->
|
||||
return unless @_rendered
|
||||
@refresh()
|
||||
@@ -0,0 +1,13 @@
|
||||
//-
|
||||
//- Crafting Guide - browse_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__browse_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.tile_container
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Crafting Guide - browse_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__browse_page {
|
||||
@include layout-body-with-sidebar;
|
||||
|
||||
.right {
|
||||
.tile_container {
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-between;
|
||||
|
||||
.view__mod_tile {
|
||||
max-width : calc(50% - 12px); // $size-margin-large / 2
|
||||
margin-bottom : $size-margin-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Crafting Guide - browse_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
AdsenseController = require '../common/adsense/adsense_controller'
|
||||
ModTileController = require './mod_tile/mod_tile_controller'
|
||||
PageController = require '../page_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class BrowsePageController extends PageController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.modPack then throw new Error 'options.modPack is required'
|
||||
options.templateName = 'browse_page'
|
||||
super options
|
||||
|
||||
@_modPack = options.modPack
|
||||
@_tileControllers = []
|
||||
|
||||
# PageController Overrides #####################################################################
|
||||
|
||||
getTitle: ->
|
||||
return "Browse"
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
|
||||
|
||||
@$tileContainer = @$('.tile_container')
|
||||
|
||||
refresh: ->
|
||||
@_refreshModTiles()
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshModTiles: ->
|
||||
index = 0
|
||||
mods = @_modPack.getAllMods()
|
||||
for mod in mods
|
||||
controller = @_tileControllers[index]
|
||||
if not controller
|
||||
controller = new ModTileController model:mod, router:@router
|
||||
controller.render()
|
||||
@$tileContainer.append controller.$el
|
||||
@_tileControllers.push controller
|
||||
else
|
||||
controller.model = mod
|
||||
index++
|
||||
|
||||
while @_tileControllers.length > mods.length
|
||||
controller = @_tileControllers.pop()
|
||||
controller.remove()
|
||||
@@ -0,0 +1,14 @@
|
||||
//-
|
||||
//- Crafting Guide - mod_tile.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__mod_tile
|
||||
a
|
||||
.left
|
||||
img
|
||||
.right
|
||||
p.title
|
||||
p.description
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Crafting Guide - mod_tile.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__mod_tile {
|
||||
min-width : $size-minecraft-block * 9;
|
||||
|
||||
display : flex;
|
||||
justify-content : stretch;
|
||||
align-items : stretch;
|
||||
|
||||
a {
|
||||
max-width: 100%;
|
||||
|
||||
@include floating-panel;
|
||||
border : $size-border-medium solid $color-background-panel;
|
||||
color : $color-text;
|
||||
text-decoration : none;
|
||||
|
||||
display : flex;
|
||||
padding : $size-margin-medium;
|
||||
|
||||
& > .left {
|
||||
flex: 0 0 $size-minecraft-block * 4;
|
||||
margin-right: $size-margin-medium;
|
||||
|
||||
img {
|
||||
width: $size-minecraft-block * 4;
|
||||
max-height: $size-minecraft-block * 4;
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
flex : 1 1 100%;
|
||||
|
||||
overflow : hidden;
|
||||
|
||||
.title {
|
||||
margin-bottom : $size-margin-medium;
|
||||
|
||||
font-family : $font-family-header;
|
||||
font-size : $font-size-xlarge;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $font-size-medium * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $color-blue-faint;
|
||||
border-color: $color-link-normal;
|
||||
|
||||
& > .right {
|
||||
.title {
|
||||
color: $color-link-normal;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
a {
|
||||
& > .left {
|
||||
img {
|
||||
@include grayscale;
|
||||
opacity: 0.25;
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
.title {
|
||||
color: $color-gray-light;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $color-gray-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#
|
||||
# Crafting Guide - mod_tile_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ModTileController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'browse_page/mod_tile'
|
||||
super options
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$link = @$('a')
|
||||
@$logoImage = @$('img')
|
||||
@$title = @$('p.title')
|
||||
@$description = @$('p.description')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$link.attr 'href', c.url.mod modSlug:@model.slug
|
||||
@$logoImage.attr 'src', c.url.modIcon modSlug:@model.slug
|
||||
@$title.text @model.name
|
||||
@$description.text @model.description
|
||||
|
||||
if @model.enabled
|
||||
@$el.removeClass 'disabled'
|
||||
else
|
||||
@$el.addClass 'disabled'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
_.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
@@ -0,0 +1,8 @@
|
||||
//-
|
||||
//- Crafting Guide - adsense.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__adsense
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Crafting Guide - adsense.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__adsense {
|
||||
.skyscraper {
|
||||
height : $size-skyscraper-height;
|
||||
width : $size-skyscraper-width;
|
||||
position : relative;
|
||||
|
||||
border : 0.1em solid $color-gray-faint;
|
||||
margin : $size-margin-large 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top : 0;
|
||||
}
|
||||
|
||||
ins {
|
||||
position: absolute; top: 0; right: 0; bottom: 0; left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#
|
||||
# Crafting Guide - adsense_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class AdsenseController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'common/adsense'
|
||||
super options
|
||||
|
||||
@_adsEnabled = global.env in c.productionEnvs
|
||||
@_adCount = 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
|
||||
|
||||
$adContainer = $('<div class="skyscraper"></div>')
|
||||
@$el.append $adContainer
|
||||
|
||||
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()
|
||||
|
||||
@_adCount += 1
|
||||
_.defer => @fillAdPositions()
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$mainBody = $('.page > .content > .right')
|
||||
@$sidebar = $('.page > .content > .left')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_waitForPageReadiness()
|
||||
super
|
||||
|
||||
# Private Methods #############################################################################
|
||||
|
||||
_computeMaxAds: ->
|
||||
return null unless @$sidebar
|
||||
result = Math.floor @$sidebar.height() / (c.adsense.skyscraper.height + c.adsense.skyscraper.margin)
|
||||
return result
|
||||
|
||||
_loadAds: ->
|
||||
return unless @_adsEnabled
|
||||
|
||||
logger.info "Loading #{@_adCount} Google AdSense ads"
|
||||
try
|
||||
global.adsbygoogle ||= []
|
||||
global.adsbygoogle.push {}
|
||||
catch e
|
||||
logger.warning "Could not load Adsense ad: #{e}"
|
||||
|
||||
_waitForPageReadiness: ->
|
||||
return if @_waiting
|
||||
|
||||
if @$sidebar.height() < c.adsense[@model].height / 2
|
||||
logger.verbose "Adsense is waiting for room to insert ads"
|
||||
@_waiting = true
|
||||
_.delay (=> @_waiting = false; @_waitForPageReadiness()), c.duration.normal
|
||||
else
|
||||
logger.verbose "Adsense is ready to fill ads"
|
||||
@fillAdPositions()
|
||||
@@ -0,0 +1,20 @@
|
||||
//-
|
||||
//- Crafting Guide - crafting_grid.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__crafting_grid
|
||||
.row
|
||||
.view__slot
|
||||
.view__slot
|
||||
.view__slot
|
||||
.row
|
||||
.view__slot
|
||||
.view__slot
|
||||
.view__slot
|
||||
.row
|
||||
.view__slot
|
||||
.view__slot
|
||||
.view__slot
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Crafting Guide - crafting_grid.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__crafting_grid {
|
||||
background : $color-gray-light;
|
||||
border : $size-border-medium solid $color-gray-medium;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.row {
|
||||
flex: 1 1 auto;
|
||||
|
||||
display: flex;
|
||||
|
||||
.view__slot {
|
||||
flex: 1 1 auto;
|
||||
|
||||
background : $color-white;
|
||||
margin : $size-border-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Crafting Guide - crafting_grid_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
SlotController = require '../slot/slot_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftingGridController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
# options.model should be a recipe
|
||||
options.templateName = 'common/crafting_grid'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
@_modPack.on c.event.change, => @tryRefresh()
|
||||
|
||||
# BaseController Methods #######################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_slotControllers = []
|
||||
for el in @$('.view__slot')
|
||||
controller = new SlotController el:el, imageLoader:@_imageLoader, modPack:@_modPack, router:@_router
|
||||
controller.render()
|
||||
@_slotControllers.push controller
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
for controller, index in @_slotControllers
|
||||
controller.model = @model?.getStackAtSlot(index)
|
||||
|
||||
super
|
||||
@@ -0,0 +1,13 @@
|
||||
//-
|
||||
//- Crafting Guide - inventory.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__inventory
|
||||
.item_container
|
||||
.empty_placeholder nothing
|
||||
.buttons
|
||||
.button.item-selector: .bezel: p add an item...
|
||||
.button.clear: .bezel: p clear
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Crafting Guide - inventory.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__inventory {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.empty_placeholder {
|
||||
color : $color-gray-light;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
margin : $size-margin-large 0;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.item_container {
|
||||
flex: 0 1 100%;
|
||||
|
||||
display: table;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Editable Variant ////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.view__inventory.editable {
|
||||
.buttons {
|
||||
display : flex;
|
||||
flex-direction : row;
|
||||
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
.item-selector {
|
||||
flex : 85%;
|
||||
margin-right : $size-margin-medium;
|
||||
}
|
||||
|
||||
.clear {
|
||||
flex : 15%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Large variant ///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.view__inventory.large {
|
||||
.view__stack {
|
||||
& > * {
|
||||
&:not(:last-child) {
|
||||
padding-bottom : $size-margin-small;
|
||||
}
|
||||
}
|
||||
|
||||
.quantity {
|
||||
padding-right : $size-margin-medium;
|
||||
input {
|
||||
width: 3em;
|
||||
font-size : $font-size-xlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding-right : $size-margin-medium;
|
||||
|
||||
img {
|
||||
height : $size-minecraft-block * 1.5;
|
||||
width : $size-minecraft-block * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
a {
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-xlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
padding-left : $size-margin-small;
|
||||
|
||||
.button {
|
||||
width : $size-button-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
#
|
||||
# Crafting Guide - inventory_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
ItemSelectorController = require '../item_selector/item_selector_controller'
|
||||
StackController = require '../stack/stack_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
###
|
||||
Events:
|
||||
clear(this)
|
||||
change(this)
|
||||
add(this, itemSlug)
|
||||
button:first(this, itemSlug)
|
||||
button:second(this, itemSlug)
|
||||
###
|
||||
module.exports = class InventoryController extends BaseController
|
||||
|
||||
@MAX_QUANTITY = 9999
|
||||
|
||||
@ONLY_DIGITS = /^[0-9]*$/
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
@_editable = options.editable ?= true
|
||||
@_icon = options.icon ?= '/images/chest_front.png'
|
||||
@_firstButtonType = options.firstButtonType ?= null
|
||||
@_isAcceptable = options.isAcceptable ?= null
|
||||
@_secondButtonType = options.secondButtonType ?= null
|
||||
@_shouldEnableButton = options.shouldEnableButton ?= (model, button)-> true
|
||||
|
||||
options.templateName = 'common/inventory'
|
||||
super options
|
||||
|
||||
@_stackControllers = []
|
||||
|
||||
@listenTo @_modPack, c.event.change, => @tryRefresh()
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onClearButtonClicked: ->
|
||||
return unless @model?
|
||||
return if @$clearButton.hasClass 'disabled'
|
||||
|
||||
@model.clear()
|
||||
@trigger c.event.clear, this
|
||||
@trigger c.event.change, this
|
||||
|
||||
onFirstButtonClicked: (stackController)->
|
||||
@trigger c.event.button.first, this, stackController?.model?.itemSlug
|
||||
|
||||
onItemSelectorButtonClicked: ->
|
||||
return unless @model?
|
||||
|
||||
@_selector.launch()
|
||||
.then (itemSlug)=>
|
||||
return unless itemSlug?
|
||||
|
||||
@model.add itemSlug, 1
|
||||
@trigger c.event.add, this, itemSlug
|
||||
@trigger c.event.change, this
|
||||
|
||||
onSecondButtonClicked: (stackController)->
|
||||
@trigger c.event.button.second, this, stackController?.model?.itemSlug
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_selector = @addChild ItemSelectorController, null, isAcceptable: @_isAcceptable, modPack: @_modPack
|
||||
|
||||
@$clearButton = @$('.button.clear')
|
||||
@$emptyPlaceholder = @$('.empty_placeholder')
|
||||
@$icon = @$('.icon')
|
||||
@$itemContainer = @$('.item_container')
|
||||
@$title = @$('h2 p')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
if @_editable
|
||||
@_selector.show()
|
||||
@show @$clearButton
|
||||
else
|
||||
@_selector.hide()
|
||||
@hide @$clearButton
|
||||
|
||||
@$icon.attr 'src', @_icon
|
||||
|
||||
@_refreshStacks()
|
||||
|
||||
if not @model or @model.isEmpty
|
||||
@show @$emptyPlaceholder
|
||||
@$clearButton.addClass 'disabled'
|
||||
else
|
||||
@hide @$emptyPlaceholder
|
||||
@$clearButton.removeClass 'disabled'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .button.item-selector': 'onItemSelectorButtonClicked'
|
||||
'click .button.clear': 'onClearButtonClicked'
|
||||
'click button.first': 'onFirstButtonClicked'
|
||||
'click button.second': 'onSecondButtonClicked'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshStacks: ->
|
||||
@_stackControllers ?= []
|
||||
index = 0
|
||||
|
||||
if @model?
|
||||
@model.each (stack)=>
|
||||
controller = @_stackControllers[index]
|
||||
if not controller?
|
||||
controller = new StackController
|
||||
editable: @_editable
|
||||
firstButtonType: @_firstButtonType
|
||||
imageLoader: @_imageLoader
|
||||
model: stack
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
secondButtonType: @_secondButtonType
|
||||
shouldEnableButton: @_shouldEnableButton
|
||||
controller.on c.event.change, => @trigger c.event.change, this
|
||||
controller.on c.event.button.first, (c)=> @onFirstButtonClicked(c)
|
||||
controller.on c.event.button.second, (c)=> @onSecondButtonClicked(c)
|
||||
controller.render()
|
||||
|
||||
@_stackControllers.push controller
|
||||
@$itemContainer.append controller.$el
|
||||
else
|
||||
controller.model = stack
|
||||
index += 1
|
||||
|
||||
while @_stackControllers.length > index
|
||||
@_stackControllers.pop().remove()
|
||||
@@ -0,0 +1,10 @@
|
||||
//-
|
||||
//- Crafting Guide - item_group.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
section.view__item_group
|
||||
h2
|
||||
.panel
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Crafting Guide - item_group.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_group {
|
||||
.panel {
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-between;
|
||||
|
||||
.view__item_tile {
|
||||
margin-bottom: $size-margin-medium;
|
||||
width: calc(33% - 8px); // $size-margin-xsmall * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#
|
||||
# Crafting Guide - item_group_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
ItemController = require './item_tile/item_tile_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ItemGroupController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
options.model ?= []
|
||||
options.templateName = 'common/item_group'
|
||||
options.title ?= ''
|
||||
options.tagName = 'section'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
@_title = options.title
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
|
||||
title:
|
||||
get: -> @_title
|
||||
|
||||
set: (newTitle)->
|
||||
@_title = newTitle
|
||||
@tryRefresh()
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$title = @$('h2')
|
||||
@$items = @$('.panel')
|
||||
super
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
newModel ?= []
|
||||
return super oldModel, newModel
|
||||
|
||||
refresh: ->
|
||||
if @model.length > 0
|
||||
@$title.html @_title
|
||||
@_refreshItems()
|
||||
@show()
|
||||
else
|
||||
@hide()
|
||||
|
||||
super
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshItems: ->
|
||||
@_itemControllers ?= []
|
||||
controllerIndex = 0
|
||||
|
||||
for item in @model
|
||||
controller = @_itemControllers[controllerIndex]
|
||||
if not controller?
|
||||
controller = new ItemController
|
||||
imageLoader: @_imageLoader
|
||||
model: item
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
@_itemControllers.push controller
|
||||
@$items.append controller.$el
|
||||
controller.render()
|
||||
else
|
||||
controller.model = item
|
||||
controllerIndex += 1
|
||||
|
||||
while @_itemControllers.length > controllerIndex
|
||||
@_itemControllers.pop().remove()
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - item_tile.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_tile
|
||||
a
|
||||
img
|
||||
p.itemName
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Crafting Guide - item_tile.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_tile {
|
||||
a {
|
||||
overflow: visible;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block;
|
||||
width: $size-minecraft-block;
|
||||
margin-right: $size-margin-small;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
line-height: $size-minecraft-block;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#
|
||||
# Crafting Guide - item_group_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ItemTileController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
options.templateName = 'common/item_group/item_tile'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$icon = @$('img')
|
||||
@$name = @$('.itemName')
|
||||
@$nameLink = @$('a')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
display = @_modPack.findItemDisplay @model.slug
|
||||
|
||||
@_imageLoader.load display.iconUrl, @$icon
|
||||
@$name.html display.itemName
|
||||
@$nameLink.attr 'href', display.itemUrl
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - element.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__element
|
||||
img
|
||||
.name
|
||||
.modName
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Crafting Guide - element.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__element {
|
||||
display : flex;
|
||||
align-items: center;
|
||||
|
||||
background: $color-white;
|
||||
padding: $size-margin-xsmall $size-margin-medium;
|
||||
|
||||
& > * {
|
||||
margin-right: $size-margin-medium;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
flex : 0 0 auto;
|
||||
|
||||
height : $size-minecraft-block;
|
||||
width : $size-minecraft-block;
|
||||
}
|
||||
|
||||
.name {
|
||||
flex : 1 1 100%;
|
||||
|
||||
color : $color-text;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
}
|
||||
|
||||
.modName {
|
||||
flex : 0 0 auto;
|
||||
|
||||
color : $color-text-secondary;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-small;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background : $color-gray-faint;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
flex : 0 0 auto;
|
||||
background : $color-active-hover;
|
||||
|
||||
.name {
|
||||
color : $color-text-light;
|
||||
}
|
||||
|
||||
.modName {
|
||||
color : $color-text-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#
|
||||
# Crafting Guide - element.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ElementController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.onClicked ?= (controller)-> # do nothing
|
||||
options.onSelected ?= (controller)-> # do nothing
|
||||
options.templateName = 'common/item_selector/element'
|
||||
options.useAnimations = false
|
||||
super options
|
||||
|
||||
@onClicked = options.onClicked
|
||||
@onSelected = options.onSelected
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
isSelected: ->
|
||||
return @_selected
|
||||
|
||||
setSelected: (newSelected)->
|
||||
oldSelected = @_selected
|
||||
return if newSelected is oldSelected
|
||||
|
||||
@_selected = newSelected
|
||||
@tryRefresh()
|
||||
|
||||
@trigger c.event.change + ':selected', this, oldSelected, newSelected
|
||||
@trigger c.event.change, this
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
selected: {get:@prototype.isSelected, set:@prototype.setSelected}
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$icon = @$('img')
|
||||
@$name = @$('.name')
|
||||
@$modName = @$('.modName')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$icon.attr 'src', @model.iconUrl
|
||||
@$name.html @model.itemName
|
||||
@$modName.html @model.modName
|
||||
|
||||
if @_selected
|
||||
@$el.addClass 'selected'
|
||||
else
|
||||
@$el.removeClass 'selected'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click': '_onClick'
|
||||
'mouseenter': '_onMouseEnter'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_onClick: (event)->
|
||||
event.preventDefault()
|
||||
@onClicked this
|
||||
|
||||
_onMouseEnter: (event)->
|
||||
event.preventDefault()
|
||||
@onSelected this
|
||||
@@ -0,0 +1,14 @@
|
||||
//-
|
||||
//- Crafting Guide - item_selector.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_selector
|
||||
.view__item_selector_popup
|
||||
.search
|
||||
img(src='/images/search.png')
|
||||
input(placeholder='enter the name of an item')
|
||||
img.close(src='/images/close.png')
|
||||
.results
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Crafting Guide - item_selector.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_selector {
|
||||
}
|
||||
|
||||
.view__item_selector_popup {
|
||||
height : 66%;
|
||||
|
||||
background : $color-background-panel;
|
||||
border : 1px solid $color-gray-medium;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.search {
|
||||
flex: 0 0 auto;
|
||||
|
||||
border-bottom: 1px solid $color-gray-medium;
|
||||
|
||||
display: flex;
|
||||
padding: $size-margin-small;
|
||||
|
||||
& > :not(:first-child) {
|
||||
margin-left: $size-margin-medium;
|
||||
}
|
||||
|
||||
img {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
input {
|
||||
flex: 1 1 100%;
|
||||
|
||||
background: none;
|
||||
border: 0;
|
||||
font-family: $font-family-input;
|
||||
font-size: $font-size-large;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
img.close {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.results {
|
||||
flex: 1 1 100%;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
#
|
||||
# Crafting Guide - item_selector_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
ItemSelector = require '../../../models/site/item_selector'
|
||||
ElementController = require './element/element_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ItemSelectorController extends BaseController
|
||||
|
||||
constructor: (options)->
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
options.isAcceptable ?= null
|
||||
options.model ?= new ItemSelector {}, modPack:options.modPack, isAcceptable:options.isAcceptable
|
||||
options.onChoseItem ?= (item)-> # do nothing
|
||||
options.templateName = 'common/item_selector'
|
||||
super options
|
||||
|
||||
@_modPack = options.modPack
|
||||
@_session = null
|
||||
|
||||
@_elementControllers = []
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
launch: (hint='')->
|
||||
if @_session
|
||||
@_session.resolve null
|
||||
@_session = null
|
||||
|
||||
@model.hint = hint
|
||||
if @rendered then @refresh()
|
||||
|
||||
@_disableWindowScrolling()
|
||||
|
||||
@$page.addClass 'blur'
|
||||
@$screen.append @$popup
|
||||
@$screen.css 'display', ''
|
||||
|
||||
@$popup.off c.event.click
|
||||
@$popup.on c.event.click, (event)=> @onPopupClicked(event)
|
||||
|
||||
@$hintField.off 'keyup input'
|
||||
@$hintField.on 'keyup', (event)=> @onHintKeyPress(event)
|
||||
@$hintField.on 'input', (event)=> @onHintChanged(event)
|
||||
@$hintField.focus()
|
||||
|
||||
@$closeButton.one 'click', (event)=> @_close()
|
||||
|
||||
@$screen.one c.event.click, (event)=> @onScreenClicked(event)
|
||||
|
||||
@_session = w.defer()
|
||||
return @_session.promise
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onClose: (event)->
|
||||
@_close()
|
||||
return false
|
||||
|
||||
onHintKeyPress: (event)->
|
||||
switch event.which
|
||||
when c.key.escape
|
||||
@_close()
|
||||
return false
|
||||
when c.key.enter
|
||||
@_chooseSelected()
|
||||
return false
|
||||
when c.key.down
|
||||
@_selectNext()
|
||||
return false
|
||||
when c.key.up
|
||||
@_selectPrevious()
|
||||
return false
|
||||
|
||||
onHintChanged: (event)->
|
||||
@model.hint = @$hintField.val()
|
||||
@tryRefresh()
|
||||
@onResultSelected @_elementControllers[0]
|
||||
|
||||
onPopupClicked: (event)->
|
||||
return false
|
||||
|
||||
onResultClicked: (controller)->
|
||||
@_session.resolve controller.model.slug
|
||||
@_session = null
|
||||
@_close()
|
||||
|
||||
onResultSelected: (controller)->
|
||||
for c in @_elementControllers
|
||||
c.selected = false
|
||||
|
||||
if controller?
|
||||
controller.selected = true
|
||||
|
||||
onScreenClicked: (event)->
|
||||
@_close()
|
||||
return false
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$page = $('.page') # find the shared, global page
|
||||
@$screen = $('.view__screen') # find the shared, global screen
|
||||
|
||||
@$closeButton = @$('img.close')
|
||||
@$popup = @$('.view__item_selector_popup')
|
||||
@$hintField = @$('.view__item_selector_popup input')
|
||||
@$searchInput = @$('.search input')
|
||||
@$resultsContainer = @$('.results')
|
||||
|
||||
@$popup.detach()
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$hintField.val @model.hint
|
||||
@_refreshResults()
|
||||
super
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_chooseSelected: ->
|
||||
for controller in @_elementControllers
|
||||
if controller.selected
|
||||
@onResultClicked controller
|
||||
return
|
||||
|
||||
_close: ->
|
||||
@_enableWindowScrolling()
|
||||
@$page.removeClass 'blur'
|
||||
@$screen.css 'display', 'none'
|
||||
@$popup.detach()
|
||||
@model.hint = ''
|
||||
|
||||
_disableWindowScrolling: ->
|
||||
@_windowScrollPosition = $(window).scrollTop()
|
||||
$('body').addClass('scrollDisabled').css('margin-top', -@_windowScrollPosition)
|
||||
|
||||
_enableWindowScrolling: ->
|
||||
$('body').removeClass('scrollDisabled').css('margin-top', 0)
|
||||
$(window).scrollTop @_windowScrollPosition
|
||||
|
||||
_refreshResults: ->
|
||||
index = 0
|
||||
|
||||
for itemSlug in @model.results
|
||||
displayModel = @_modPack.findItemDisplay itemSlug
|
||||
controller = @_elementControllers[index]
|
||||
if not controller?
|
||||
controller = new ElementController
|
||||
model: displayModel
|
||||
onClicked: (c)=> @onResultClicked(c)
|
||||
onSelected: (c)=> @onResultSelected(c)
|
||||
controller.render show:false
|
||||
@_elementControllers[index] = controller
|
||||
@$resultsContainer.append controller.$el
|
||||
else
|
||||
controller.model = displayModel
|
||||
|
||||
controller.show()
|
||||
index += 1
|
||||
|
||||
while @_elementControllers.length > index
|
||||
@_elementControllers.pop().remove()
|
||||
|
||||
_selectNext: ->
|
||||
for i in [0...(@_elementControllers.length - 1)] by 1
|
||||
controller = @_elementControllers[i]
|
||||
nextController = @_elementControllers[i + 1]
|
||||
|
||||
if controller.selected
|
||||
controller.selected = false
|
||||
nextController.selected = true
|
||||
@_showElement nextController.$el
|
||||
return
|
||||
|
||||
_selectPrevious: ->
|
||||
for i in [1...@_elementControllers.length] by 1
|
||||
previousController = @_elementControllers[i - 1]
|
||||
controller = @_elementControllers[i]
|
||||
|
||||
if controller.selected
|
||||
previousController.selected = true
|
||||
controller.selected = false
|
||||
@_showElement previousController.$el
|
||||
return
|
||||
|
||||
_showElement: ($el)->
|
||||
top = $el.position().top + @$resultsContainer.scrollTop()
|
||||
@$resultsContainer.css 'scrollTop', top
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//-
|
||||
//- Crafting Guide - markdown_image.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__markdown_image
|
||||
.loaded
|
||||
.thumbnail: img
|
||||
.dynamic
|
||||
.button: .bezel: p
|
||||
.fileName: p
|
||||
.error: p
|
||||
form: input(type="file", accept="image/gif,image/jpeg,image/png" multiple="false")
|
||||
.loading
|
||||
p checking for image...
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Crafting Guide - markdown_image.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__markdown_image {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.loaded {
|
||||
display: flex;
|
||||
|
||||
.thumbnail {
|
||||
flex: 0 0 auto;
|
||||
height: $size-minecraft-block * 4;
|
||||
width: $size-minecraft-block * 4;
|
||||
margin-right: $size-margin-medium;
|
||||
|
||||
background: $color-gray-faint;
|
||||
border: 1px solid $color-gray-light;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: $size-minecraft-block * 3;
|
||||
max-height: $size-minecraft-block * 3;
|
||||
}
|
||||
}
|
||||
|
||||
.dynamic {
|
||||
flex: 1 1 100%;
|
||||
|
||||
& > :not(:last-child) {
|
||||
margin-bottom: $size-margin-medium;
|
||||
}
|
||||
|
||||
.fileName {
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: $color-error;
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
height: $size-minecraft-block * 4;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
color: $color-gray-light;
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
###
|
||||
Crafting Guide - markdown_image_controller.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require '../../../../base_controller'
|
||||
MarkdownImage = require '../../../../../models/site/markdown_image'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MarkdownImageController extends BaseController
|
||||
|
||||
@::MAX_FILE_SIZE = 750 * 1024
|
||||
@::MAX_HEIGHT = 600
|
||||
@::MAX_WIDTH = 740
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.templateName = 'common/markdown_section/markdown_image_list/markdown_image'
|
||||
super
|
||||
|
||||
@_errorMessage = null
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onButtonClicked: ->
|
||||
@$input.click()
|
||||
|
||||
onFileChanged: ->
|
||||
if @_reader? then @_reader.abort = true
|
||||
|
||||
file = @$input.prop('files')[0]
|
||||
return unless file?
|
||||
|
||||
if file.size > @MAX_FILE_SIZE
|
||||
logger.warning "The choosen file, #{file.name}, is too large: #{file.size}"
|
||||
@errorMessage = "Choose a file less than #{@MAX_FILE_SIZE / 1024}kB"
|
||||
@_clearFile()
|
||||
return
|
||||
|
||||
reader = @_reader = new FileReader
|
||||
reader.abort = false
|
||||
|
||||
reader.onload = =>
|
||||
if reader.abort
|
||||
logger.info "aborted reading #{file.name}"
|
||||
return
|
||||
|
||||
@_reader = null
|
||||
|
||||
$image = $("<img src=\"#{reader.result}\">")
|
||||
$image.load =>
|
||||
if $image[0].naturalHeight > @MAX_HEIGHT
|
||||
logger.warning "The choosen file, #{file.name}, is too tall: #{$image[0].naturalHeight}"
|
||||
@errorMessage = "Choose an image less than #{@MAX_HEIGHT}px tall"
|
||||
@_clearFile()
|
||||
return
|
||||
|
||||
if $image[0].naturalWidth > @MAX_WIDTH
|
||||
logger.warning "The choosen file, #{file.name}, is too wide: #{$image[0].naturalWidth}"
|
||||
@errorMessage = "Choose an image less than #{@MAX_WIDTH}px wide"
|
||||
@_clearFile()
|
||||
return
|
||||
|
||||
index = reader.result.indexOf ','
|
||||
meta = reader.result.substring 0, index
|
||||
encodedData = reader.result.substring index + 1
|
||||
|
||||
match = meta.match /data:(.*);(.*)/
|
||||
mimeType = match[1]
|
||||
encoding = match[2]
|
||||
|
||||
@model.encodedData = encodedData
|
||||
logger.info "loaded #{encodedData.length} bytes from #{file.name} as #{mimeType}"
|
||||
@errorMessage = null
|
||||
@_clearFile()
|
||||
|
||||
logger.info "starting to read local file: #{file.name}"
|
||||
reader.readAsDataURL file
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getErrorMessage: ->
|
||||
return @_errorMessage
|
||||
|
||||
setErrorMessage: (newErrorMessage)->
|
||||
oldErrorMessage = @_errorMessage
|
||||
return if newErrorMessage is oldErrorMessage
|
||||
|
||||
@_errorMessage = newErrorMessage
|
||||
@trigger Event.change + ':errorMessage', this, oldErrorMessage, newErrorMessage
|
||||
@trigger Event.change, this
|
||||
|
||||
@tryRefresh()
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
errorMessage: { get:@prototype.getErrorMessage, set:@prototype.setErrorMessage }
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$image = @$('img')
|
||||
@$fileName = @$('.fileName p')
|
||||
@$buttonLabel = @$('.button p')
|
||||
@$form = @$('form')
|
||||
@$input = @$('input')
|
||||
@$errorContainer = @$('.error')
|
||||
@$errorMessage = @$('.error p')
|
||||
@$loaded = @$('.loaded')
|
||||
@$loading = @$('.loading')
|
||||
|
||||
super
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
@errorMessage = null
|
||||
super oldModel, newModel
|
||||
|
||||
refresh: ->
|
||||
if @model.mimeType? and @model.encodedData?
|
||||
@$image.attr 'src', "data:#{@model.mimeType};base64,#{@model.encodedData}"
|
||||
else
|
||||
@$image.attr 'src', '/images/unknown.png'
|
||||
|
||||
@$fileName.html @model.fileName
|
||||
|
||||
if @model.encodedData?
|
||||
@$buttonLabel.html 'Update'
|
||||
else
|
||||
@$buttonLabel.html 'Choose'
|
||||
|
||||
if @model.status is MarkdownImage::Status.checking
|
||||
@hide @$loaded
|
||||
@show @$loading
|
||||
else
|
||||
@hide @$loading
|
||||
@show @$loaded
|
||||
|
||||
if @errorMessage?
|
||||
@$errorMessage.html @errorMessage
|
||||
@show @$errorContainer
|
||||
else if @model.status is MarkdownImage::Status.empty
|
||||
@$errorMessage.html 'please choose an image'
|
||||
@show @$errorContainer
|
||||
else
|
||||
@hide @$errorContainer
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .button': 'onButtonClicked'
|
||||
'change input': 'onFileChanged'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_clearFile: ->
|
||||
@$form[0].reset()
|
||||
@@ -0,0 +1,10 @@
|
||||
//-
|
||||
//- Crafting Guide - markdown_image_list.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__markdown_image_list
|
||||
h3 Images Referenced
|
||||
.image_container
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Crafting Guide - markdown_image_list.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__markdown_image_list {
|
||||
position: relative; min-width: 33em; width: 100%;
|
||||
|
||||
border-bottom: 0.1em solid $color-gray-medium;
|
||||
margin: 2em 0;
|
||||
padding: 0 0 2em 0;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.image_container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -$size-margin-large;
|
||||
|
||||
& > * {
|
||||
width: calc(50% - 24px); // $size-margin-large
|
||||
margin-right: $size-margin-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
#
|
||||
# Crafting Guide - markdown_image_list_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../../base_controller'
|
||||
MarkdownImageController = require './markdown_image/markdown_image_controller'
|
||||
MarkdownImageList = require '../../../../models/site/markdown_image_list'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MarkdownImageListController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.model ?= new MarkdownImageList {}, {client:options.client}
|
||||
options.templateName = 'common/markdown_section/markdown_image_list'
|
||||
super options
|
||||
|
||||
@imageBase = ''
|
||||
@_valid = null
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
fetchImages: ->
|
||||
@model.fetchImages()
|
||||
|
||||
getImageUrlForFile: (fileName)->
|
||||
@model.getFile(fileName).imageUrl
|
||||
|
||||
reset: ->
|
||||
@model.reset()
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getImageBase: ->
|
||||
return @_imageBase
|
||||
|
||||
setImageBase: (newImageBase)->
|
||||
oldImageBase = @_imageBase
|
||||
return unless oldImageBase isnt newImageBase
|
||||
|
||||
@_imageBase = newImageBase
|
||||
@trigger c.event.change + ':imageBase', this, oldImageBase, newImageBase
|
||||
@trigger c.event.change, this
|
||||
|
||||
getMarkdownText: ->
|
||||
return @model.markdownText
|
||||
|
||||
setMarkdownText: (markdownText)->
|
||||
@model.markdownText = markdownText
|
||||
|
||||
getValid: ->
|
||||
return @_valid
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
imageBase: {get:@prototype.getImageBase, set:@prototype.setImageBase}
|
||||
markdownText: {get:@prototype.getMarkdownText, set:@prototype.setMarkdownText}
|
||||
valid: {get:@prototype.getValid}
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidModelChange: ->
|
||||
super
|
||||
@_setValid @model.valid
|
||||
|
||||
onDidRender: ->
|
||||
@$imageContainer = @$('.image_container')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_controllers ?= []
|
||||
index = 0
|
||||
|
||||
for image in @model.all
|
||||
controller = @_controllers[index]
|
||||
if not controller?
|
||||
controller = new MarkdownImageController model:image
|
||||
@_controllers.push controller
|
||||
@$imageContainer.append controller.$el
|
||||
controller.render()
|
||||
else
|
||||
controller.model = image
|
||||
|
||||
index += 1
|
||||
|
||||
while @_controllers.length > index
|
||||
@_controllers.pop().remove()
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_setValid: (newValid)->
|
||||
oldValid = @_valid
|
||||
return if newValid is oldValid
|
||||
|
||||
@_valid = newValid
|
||||
@trigger c.event.change + ':valid', this, oldValid, newValid
|
||||
@trigger c.event.change
|
||||
@@ -0,0 +1,38 @@
|
||||
//-
|
||||
//- Crafting Guide - markdown_section.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__markdown_section
|
||||
h2
|
||||
.panel
|
||||
.waiting: img(src='/images/wait.gif')
|
||||
.markdown
|
||||
.creating
|
||||
p Please help by adding a description!
|
||||
.editor
|
||||
.instructions.
|
||||
Edit using <a href="https://help.github.com/articles/github-flavored-markdown/" target="new">GitHub
|
||||
Flavored Markdown</a>. Use <tt>[[Item Name]]</tt> to link to another item and <tt></tt>
|
||||
to add an image.
|
||||
.text
|
||||
textarea
|
||||
.sizer
|
||||
.view__markdown_image_list
|
||||
.footer
|
||||
.question: a(href="javascript:void;") Confused? Click here to ask a question!
|
||||
.error: p
|
||||
.buttons
|
||||
.button.edit: .bezel: p edit
|
||||
.button.save: .bezel: p save
|
||||
.button.preview: .bezel: p preview
|
||||
.button.cancel: .bezel: p cancel
|
||||
.button.return: .bezel: p return
|
||||
.confirming
|
||||
p Thanks for your help!
|
||||
p Your changes will appear on the site in a few minutes.
|
||||
.appologizing
|
||||
p Sorry, but something went wrong.
|
||||
p Please try again after a few moments.
|
||||
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// Crafting Guide - markdown_section.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__markdown_section {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.waiting {
|
||||
display : flex;
|
||||
justify-content : center;
|
||||
|
||||
padding : $size-margin-xlarge;
|
||||
}
|
||||
|
||||
.appologizing, .confirming, .creating {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
justify-content : center;
|
||||
padding : $size-margin-xlarge;
|
||||
|
||||
p {
|
||||
color : $color-gray-medium;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
text-align : center;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.creating {
|
||||
padding-bottom: $size-margin-large;
|
||||
|
||||
p {
|
||||
color: $color-gray-light;
|
||||
font-size: $font-size-xlarge;
|
||||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.instructions {
|
||||
margin-bottom : $size-margin-medium;
|
||||
|
||||
color : $color-gray-medium;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-small;
|
||||
}
|
||||
|
||||
.text {
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
position : relative;
|
||||
|
||||
textarea, .sizer {
|
||||
font-family : $font-family-input;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $font-size-medium;
|
||||
overflow : hidden;
|
||||
padding : $size-margin-small;
|
||||
resize : none;
|
||||
white-space : pre-wrap;
|
||||
}
|
||||
|
||||
textarea {
|
||||
position : absolute; width: 100%; height: 100%;
|
||||
|
||||
background: none;
|
||||
z-index: $layer-normal;
|
||||
}
|
||||
|
||||
.sizer {
|
||||
min-height: $size-minecraft-block * 4;
|
||||
opacity: 0;
|
||||
z-index: $layer-normal - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
display : flex;
|
||||
align-items : flex-end;
|
||||
margin-top : $size-margin-large;
|
||||
|
||||
.question {
|
||||
flex : 1 1 100%;
|
||||
|
||||
a {
|
||||
color : $color-gray-light;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
text-decoration : underline;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
flex: 1 1 100%;
|
||||
align-self: center;
|
||||
|
||||
p {
|
||||
color : $color-error;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
font-style : italic;
|
||||
text-align : center;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
flex : 0 0 auto;
|
||||
|
||||
display : flex;
|
||||
flex-direction : row-reverse;
|
||||
align-items : center;
|
||||
|
||||
.button {
|
||||
margin-left : $size-margin-medium;
|
||||
width : $size-minecraft-block * 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
#
|
||||
# Crafting Guide - markdown_section_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
convertMarkdown = require 'marked'
|
||||
MarkdownImageListController = require './markdown_image_list/markdown_image_list_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MarkdownSectionController extends BaseController
|
||||
|
||||
@::State =
|
||||
appologizing: 'applogizing'
|
||||
confirming: 'confirming'
|
||||
creating: 'creating'
|
||||
editing: 'editing'
|
||||
previewing: 'previewing'
|
||||
viewing: 'viewing'
|
||||
waiting: 'waiting'
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
options.model ?= null
|
||||
options.templateName = 'common/markdown_section'
|
||||
@_state = @State.waiting
|
||||
super options
|
||||
|
||||
@_client = options.client
|
||||
@_confirmationMessage = options.confirmationMessage
|
||||
@_confirmDuration = options.confirmationDuration or 5000
|
||||
@_enterFeedback = options.enterFeedback or -> # do nothing
|
||||
@_imageBase = options.imageBase or ''
|
||||
@_modPack = options.modPack
|
||||
@_title = options.title or 'Description'
|
||||
|
||||
# @_beginEditing must be a function returning a promise which resolves when all actions necessary prior to an
|
||||
# editing session have been completed (e.g., loading the latest content from a remote server). The promise must
|
||||
# reject if an error occured which should prevent editing.
|
||||
@_beginEditing = options.beginEditing
|
||||
|
||||
# @_endEditing must be a function returning a promise which resolves when the model of this controller has been
|
||||
# saved however is appropriate. The promise must reject if saving failed for some reason.
|
||||
@_endEditing = options.endEditing
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
resetToDefaultState: ->
|
||||
if @model?
|
||||
@state = @State.viewing
|
||||
else
|
||||
if @editable?
|
||||
@state = @State.creating
|
||||
else
|
||||
@state = @State.waiting
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onCancelClicked: (event)->
|
||||
event.preventDefault()
|
||||
@model = @_originalModel
|
||||
@resetToDefaultState()
|
||||
|
||||
@_imageListController.reset()
|
||||
@_updatePreview()
|
||||
|
||||
onEditClicked: (event)->
|
||||
event.preventDefault()
|
||||
return unless @editable
|
||||
|
||||
@_originalModel = @model
|
||||
@state = @State.waiting
|
||||
@_beginEditing()
|
||||
.then =>
|
||||
@state = @State.editing
|
||||
.catch (e)=>
|
||||
logger.warning "cannot begin editing: #{e.stack}"
|
||||
@state = @State.appologizing
|
||||
w(true).delay(@_confirmDuration).then => @resetToDefaultState()
|
||||
|
||||
onQuestionClicked: (event)->
|
||||
event.preventDefault()
|
||||
@_enterFeedback ''
|
||||
|
||||
onPreviewClicked: (event)->
|
||||
event.preventDefault()
|
||||
@_updatePreview()
|
||||
@state = @State.previewing
|
||||
|
||||
onReturnClicked: (event)->
|
||||
event.preventDefault()
|
||||
@state = @State.editing
|
||||
|
||||
onSaveClicked: (event)->
|
||||
event.preventDefault()
|
||||
|
||||
@state = @State.waiting
|
||||
@_endEditing()
|
||||
.then =>
|
||||
@state = @State.confirming
|
||||
.catch (e)=>
|
||||
message = if e.stack? then e.stack else e
|
||||
logger.error "failed to end editing: #{message}"
|
||||
@state = @State.appologizing
|
||||
.delay @_confirmDuration
|
||||
.then =>
|
||||
@state = @State.viewing
|
||||
|
||||
onTextChanged: (event)->
|
||||
event.preventDefault()
|
||||
@model = @$textarea.val()
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
isEditable: ->
|
||||
return _.isFunction(@_beginEditing) and _.isFunction(@_endEditing)
|
||||
|
||||
getImageBase: ->
|
||||
return @_imageBase
|
||||
|
||||
setImageBase: (newImageBase)->
|
||||
oldImageBase = @_imageBase
|
||||
return unless newImageBase isnt oldImageBase
|
||||
|
||||
@_imageBase = newImageBase
|
||||
@trigger c.event.change + ':imageBase', this, oldImageBase, newImageBase
|
||||
@trigger c.event.change, this
|
||||
|
||||
getImageFiles: ->
|
||||
return @_imageListController.model.all
|
||||
|
||||
getState: ->
|
||||
return @_state
|
||||
|
||||
setState: (newState)->
|
||||
oldState = @_state
|
||||
return if oldState is newState
|
||||
@_state = newState
|
||||
|
||||
logger.verbose => "MarkdownSectionController.#{@cid} changed state from #{oldState} to #{newState}"
|
||||
@trigger c.event.change + ':state', this, oldState, newState
|
||||
@tryRefresh()
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
editable: { get:@prototype.isEditable }
|
||||
imageBase: { get:@prototype.getImageBase, set:@prototype.setImageBase }
|
||||
imageFiles: { get:@prototype.getImageFiles }
|
||||
state: { get:@prototype.getState, set:@prototype.setState }
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_imageListController = @addChild MarkdownImageListController, '.view__markdown_image_list',
|
||||
client: @_client
|
||||
imageBase: @_imageBase
|
||||
@on c.event.change + ':imageBase', => @_imageListController.model.imageBase = @_imageBase
|
||||
@listenTo @_imageListController, c.event.change + ':valid', => @tryRefresh()
|
||||
|
||||
@$errorPanel = @$('.error')
|
||||
@$errorText = @$('.error p')
|
||||
@$markdownPanel = @$('.markdown')
|
||||
@$previewButton = @$('.button.preview')
|
||||
@$questionPanel = @$('.question')
|
||||
@$saveButton = @$('.button.save')
|
||||
@$sizer = @$('.sizer')
|
||||
@$textarea = @$('textarea')
|
||||
@$title = @$('h2')
|
||||
|
||||
@resetToDefaultState()
|
||||
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$title.html @_title
|
||||
@_imageListController.markdownText = @model
|
||||
@_imageListController.fetchImages() if @state is @State.editing
|
||||
|
||||
@_updateButtonStates()
|
||||
@_updateSizer()
|
||||
@_updatePreview()
|
||||
@_updateStateVisibility()
|
||||
|
||||
super
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
if @rendered then @$textarea.val newModel
|
||||
super oldModel, newModel
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .markdown a': 'routeLinkClick'
|
||||
'click .button.cancel': 'onCancelClicked'
|
||||
'click .button.edit': 'onEditClicked'
|
||||
'click .button.preview': 'onPreviewClicked'
|
||||
'click .button.return': 'onReturnClicked'
|
||||
'click .button.save': 'onSaveClicked'
|
||||
'click .question a': 'onQuestionClicked'
|
||||
'input textarea': 'onTextChanged'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_convertImageLinks: (text)->
|
||||
text.replace /\<img src="([^"]*)"/g, (match, fileName)=>
|
||||
return "<img src=\"#{@_imageListController.getImageUrlForFile(fileName)}\""
|
||||
|
||||
_convertWikiLinks: (text)->
|
||||
text.replace /\[\[([^\]]*)\]\]/g, (match, name)=>
|
||||
result = match
|
||||
item = @_modPack.findItemByName name
|
||||
if item?
|
||||
display = @_modPack.findItemDisplay item.slug
|
||||
result = "[#{name}](#{display.itemUrl})"
|
||||
|
||||
return result
|
||||
|
||||
_updateButtonStates: ->
|
||||
for $button in [ @$previewButton, @$saveButton ]
|
||||
if @_imageListController.valid
|
||||
$button.prop 'disabled', false
|
||||
else
|
||||
$button.prop 'disabled', true
|
||||
|
||||
_updateSizer: ->
|
||||
text = @model
|
||||
if @model?
|
||||
text = text.replace /\n/g, '<br>'
|
||||
|
||||
@$sizer.html text
|
||||
|
||||
_updatePreview: ->
|
||||
text = @model
|
||||
if @model?
|
||||
text = @_convertWikiLinks text
|
||||
text = convertMarkdown text
|
||||
text = @_convertImageLinks text
|
||||
|
||||
@$markdownPanel.html text
|
||||
|
||||
_updateStateVisibility: ->
|
||||
return if @_lastUpdatedState is @state
|
||||
@_lastUpdatedState = @state
|
||||
|
||||
logger.verbose => "Updating markdown section visibility for state: #{@state}"
|
||||
|
||||
elements =
|
||||
appologizingPanel: @$('.appologizing')
|
||||
cancelButton: @$('.button.cancel')
|
||||
confirmingPanel: @$('.confirming')
|
||||
creatingPanel: @$('.creating')
|
||||
editButton: @$('.button.edit')
|
||||
editorPanel: @$('.editor')
|
||||
footerPanel: @$('.footer')
|
||||
imageList: @$('.view__markdown_image_list')
|
||||
markdownPanel: @$markdownPanel
|
||||
previewButton: @$previewButton
|
||||
returnButton: @$('.button.return')
|
||||
saveButton: @$saveButton
|
||||
waitingPanel: @$('.waiting')
|
||||
|
||||
visible = {}
|
||||
errorText = ''
|
||||
|
||||
if @state is @State.appologizing
|
||||
visible = appologizingPanel:true
|
||||
else if @state is @State.confirming
|
||||
visible = confirmingPanel:true
|
||||
else if @state is @State.creating
|
||||
visible = footerPanel:true, creatingPanel:true, editButton:true
|
||||
else if @state is @State.editing
|
||||
visible = footerPanel:true, editorPanel:true, cancelButton:true, previewButton:true, imageList:true
|
||||
else if @state is @State.previewing
|
||||
visible = footerPanel:true, markdownPanel:true, returnButton:true, saveButton:true
|
||||
errorText = "remember: your changes aren't saved yet!"
|
||||
else if @state is @State.viewing
|
||||
visible = markdownPanel:true
|
||||
if @editable then _.extend visible, {footerPanel:true, editButton:true}
|
||||
else # assume any unknown state is the same as "waiting"
|
||||
visible = waitingPanel:true
|
||||
|
||||
for name, $el of elements when not visible[name]
|
||||
@hide $el
|
||||
for name, $el of elements when visible[name]
|
||||
@show $el
|
||||
|
||||
if @state is @State.editing then @$textarea.focus()
|
||||
|
||||
@$errorText.html errorText
|
||||
if errorText.length > 0
|
||||
@show @$errorPanel
|
||||
@hide @$questionPanel
|
||||
else
|
||||
@hide @$errorPanel
|
||||
@show @$questionPanel
|
||||
@@ -0,0 +1,14 @@
|
||||
//-
|
||||
//- Crafting Guide - recipe.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__recipe
|
||||
.input
|
||||
.view__crafting_grid
|
||||
.tool
|
||||
.output
|
||||
.view__slot
|
||||
.multiplier
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Crafting Guide - recipe.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__recipe {
|
||||
display: flex;
|
||||
|
||||
.input {
|
||||
flex: 1 1 50%;
|
||||
|
||||
align-items : center;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.tool {
|
||||
margin-top : $size-margin-small;
|
||||
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.output {
|
||||
flex: 1 1 50%;
|
||||
margin-left: $size-margin-medium;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
justify-content : center;
|
||||
align-items : center;
|
||||
|
||||
.view__slot {
|
||||
border: $size-border-medium solid $color-gray-medium;
|
||||
}
|
||||
|
||||
.multiplier {
|
||||
font-family : $font-family-header;
|
||||
font-size : $font-size-large;
|
||||
margin-top : $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# Crafting Guide - recipe_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
CraftingGridController = require '../crafting_grid/crafting_grid_controller'
|
||||
SlotController = require '../slot/slot_controller'
|
||||
{StringBuilder} = require 'crafting-guide-common'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class RecipeController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
options.templateName = 'common/recipe'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_multiplier = options.multiplier
|
||||
@_router = options.router
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_gridController = @addChild CraftingGridController, '.view__crafting_grid',
|
||||
modPack: @_modPack
|
||||
imageLoader: @_imageLoader
|
||||
router: @_router
|
||||
|
||||
@_outputSlotController = @addChild SlotController, '.output .view__slot',
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@$multiplier = @$('.multiplier')
|
||||
@$outputImg = @$('.output img')
|
||||
@$outputLink = @$('.output a')
|
||||
@$outputQuantity = @$('.quantity')
|
||||
@$toolContainer = @$('.tool')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_gridController.model = @model
|
||||
@_outputSlotController.model = @model?.output?[0]
|
||||
|
||||
@_refreshMultiplier()
|
||||
@_refreshTools()
|
||||
super
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
multiplier:
|
||||
get: ->
|
||||
@_multiplier ?= 1
|
||||
return @_multiplier
|
||||
|
||||
set: (newMultiplier)->
|
||||
oldMultiplier = @_multiplier
|
||||
return if newMultiplier is oldMultiplier
|
||||
|
||||
@_multiplier = newMultiplier
|
||||
@_refreshMultiplier()
|
||||
|
||||
@trigger Event.change + ':multiplier', this, oldMultiplier, newMultiplier
|
||||
@trigger Event.change, this
|
||||
|
||||
# Backbone.View Methods ########################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshMultiplier: ->
|
||||
if @multiplier > 1
|
||||
@$multiplier.html "x#{@multiplier}"
|
||||
else
|
||||
@$multiplier.html ''
|
||||
|
||||
_refreshTools: ->
|
||||
@$toolContainer.empty()
|
||||
return unless @model?
|
||||
|
||||
builder = new StringBuilder
|
||||
builder.loop @model.tools, delimiter:', ', onEach:(b, stack)=>
|
||||
display = @_modPack.findItemDisplay stack.itemSlug
|
||||
b.push "<a href=\"#{display.itemUrl}\">#{display.itemName}</a>"
|
||||
@$toolContainer.html builder.toString()
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - slot.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__slot
|
||||
a
|
||||
img(src='/images/empty.png')
|
||||
.quantity
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Crafting Guide - slot.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__slot {
|
||||
position : relative;
|
||||
height : ($size-minecraft-block * 1.5) + ($size-margin-xsmall * 2);
|
||||
width : ($size-minecraft-block * 1.5) + ($size-margin-xsmall * 2);
|
||||
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content : center;
|
||||
padding : $size-margin-xsmall;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block * 1.5;
|
||||
width: $size-minecraft-block * 1.5;
|
||||
}
|
||||
|
||||
.quantity {
|
||||
position : absolute; right: 2px; bottom: 0;
|
||||
color : white;
|
||||
font-family : $font-family-header;
|
||||
font-size : $font-size-large;
|
||||
text-shadow : -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#
|
||||
# Crafting Guide - slot_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class SlotController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
# options.model should be a Stack
|
||||
options.templateName = 'common/slot'
|
||||
options.useAnimations = false
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$link = @$('a')
|
||||
@$image = @$('img')
|
||||
@$quantity = @$('.quantity')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
if @model?
|
||||
display = @_modPack.findItemDisplay @model.itemSlug
|
||||
@$link.attr 'href', display.itemUrl
|
||||
|
||||
@_imageLoader.load display.iconUrl, @$image
|
||||
|
||||
if @model.quantity > 1
|
||||
@$quantity.html @model.quantity
|
||||
else
|
||||
@$quantity.html ''
|
||||
else
|
||||
@$link.removeAttr 'href'
|
||||
@$quantity.html ''
|
||||
|
||||
@$image.attr 'src', '/images/empty.png'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
@@ -0,0 +1,15 @@
|
||||
//-
|
||||
//- Crafting Guide - stack.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__stack
|
||||
.quantity: input
|
||||
.icon: img(src='')
|
||||
.name: a
|
||||
.action.first(style="display: none")
|
||||
.button: .bezel: p
|
||||
.action.second(style="display: none")
|
||||
.button: .bezel: p
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Crafting Guide - stack.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__stack {
|
||||
// using table-row display type to allow the quantity to always line up, even when the length is different
|
||||
display : table-row;
|
||||
max-width: 100%;
|
||||
|
||||
& > * {
|
||||
display : table-cell;
|
||||
vertical-align : middle;
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-bottom : $size-margin-xsmall;
|
||||
}
|
||||
}
|
||||
|
||||
.quantity {
|
||||
padding-right : $size-margin-medium;
|
||||
text-align : right;
|
||||
|
||||
input {
|
||||
width: 4em;
|
||||
|
||||
background : none;
|
||||
border : none;
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
text-align : right;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding-right : $size-margin-small;
|
||||
|
||||
img {
|
||||
height : $size-minecraft-block;
|
||||
width : $size-minecraft-block;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
padding-right : $size-margin-large;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
a {
|
||||
// use absolute positioning to pull the name out of the layout so that a long name won't cause the row to
|
||||
// be too long. Instead, this element will truncate itself with an ellipsis.
|
||||
position: absolute; top: 50%; width: 100%;
|
||||
transform: translateY(-50%);
|
||||
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $size-minecraft-block;
|
||||
overflow : hidden;
|
||||
text-overflow : ellipsis;
|
||||
white-space : nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.action {
|
||||
padding-left : $size-margin-small;
|
||||
|
||||
.button {
|
||||
width : $size-button-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Editable ////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
.view__stack.editable {
|
||||
.quantity {
|
||||
input {
|
||||
width : 3em;
|
||||
|
||||
border-bottom : 1px dashed $color-gray-dark;
|
||||
font-family : $font-family-input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#
|
||||
# Crafting Guide - stack_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
###
|
||||
Events:
|
||||
change(this)
|
||||
change:quantity(this, oldQuantity, newQuantity)
|
||||
button:first(this, type)
|
||||
button:second(this, type)
|
||||
###
|
||||
module.exports = class StackController extends BaseController
|
||||
|
||||
@::MAX_QUANTITY = 9999
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
|
||||
options.templateName = 'common/stack'
|
||||
super options
|
||||
|
||||
@_editable = options.editable ?= false
|
||||
@_firstButtonType = options.firstButtonType ?= null
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_secondButtonType = options.secondButtonType ?= null
|
||||
@_shouldEnableButton = options.shouldEnableButton ?= (model, button)-> true
|
||||
|
||||
@_modPack.on c.event.change, => @tryRefresh()
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onFirstButtonClicked: (event)->
|
||||
@trigger c.event.button.first, this, @_firstButtonType
|
||||
return false
|
||||
|
||||
onQuantityFieldBlur: ->
|
||||
return unless @_editable
|
||||
|
||||
oldQuantity = @_priorValue
|
||||
quantityText = @$quantityField.val().trim()
|
||||
if quantityText.length is 0
|
||||
newQuantity = oldQuantity
|
||||
else if not quantityText.match /^[0-9]*$/
|
||||
newQuantity = 1
|
||||
else
|
||||
newQuantity = parseInt quantityText, 10
|
||||
if _.isNaN newQuantity then newQuantity = 1
|
||||
|
||||
newQuantity = Math.min newQuantity, @MAX_QUANTITY
|
||||
newQuantity = Math.max 1, newQuantity
|
||||
|
||||
@$quantityField.val "#{newQuantity}"
|
||||
if newQuantity? and newQuantity isnt oldQuantity
|
||||
@$quantityField.removeClass 'error'
|
||||
@$quantityField.removeClass 'error-new'
|
||||
|
||||
@model.quantity = newQuantity
|
||||
@trigger c.event.change + ':quantity', this, oldQuantity, newQuantity
|
||||
@trigger c.event.change, this
|
||||
|
||||
onQuantityFieldChanged: ->
|
||||
return unless @_editable
|
||||
|
||||
quantityText = @$quantityField.val().trim()
|
||||
if not quantityText.match /^[0-9]*$/
|
||||
@$quantityField.addClass 'error', 0
|
||||
@$quantityField.addClass 'error-new', 0
|
||||
@$quantityField.removeClass 'error-new'
|
||||
else
|
||||
@$quantityField.removeClass 'error'
|
||||
@$quantityField.removeClass 'error-new'
|
||||
|
||||
onQuantityFieldFocused: ->
|
||||
if @_editable
|
||||
@$quantityField.addClass 'editing'
|
||||
@_priorValue = @model.quantity
|
||||
@$quantityField.val ''
|
||||
else
|
||||
@$quantityField.blur()
|
||||
|
||||
onQuantityKeyUp: (event)->
|
||||
return unless @_editable
|
||||
|
||||
if event.which is c.key.return
|
||||
@$quantityField.blur()
|
||||
|
||||
onSecondButtonClicked: (event)->
|
||||
event.preventDefault()
|
||||
@trigger c.event.button.second, this, @_secondButtonType
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$firstAction = @$('.action.first')
|
||||
@$firstButton = @$('.action.first .button')
|
||||
@$image = @$('.icon img')
|
||||
@$nameLink = @$('.name a')
|
||||
@$quantityField = @$('.quantity input')
|
||||
@$secondAction = @$('.action.second')
|
||||
@$secondButton = @$('.action.second .button')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
if not @model? then throw new Error "must have a model to render"
|
||||
|
||||
display = @_modPack.findItemDisplay @model.itemSlug
|
||||
|
||||
@_imageLoader.load display.iconUrl, @$image
|
||||
@$nameLink.html display.itemName
|
||||
@$nameLink.attr 'href', display.itemUrl
|
||||
|
||||
quantityText = if @model.quantity > 10000 then "#{@model.quantity / 1000}k" else "#{@model.quantity}"
|
||||
@$quantityField.val quantityText
|
||||
|
||||
@_refreshButtons()
|
||||
|
||||
if @_editable
|
||||
@$el.addClass 'editable'
|
||||
@$quantityField.css 'width', ''
|
||||
@$quantityField.removeAttr 'readonly'
|
||||
else
|
||||
@$el.removeClass 'editable'
|
||||
@$quantityField.css 'width', "#{quantityText}".length * 0.66 + "em"
|
||||
@$quantityField.attr 'readonly', 'readonly'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'blur .quantity input': 'onQuantityFieldBlur'
|
||||
'click .name a': 'routeLinkClick'
|
||||
'click .action.first .button': 'onFirstButtonClicked'
|
||||
'click .action.second .button': 'onSecondButtonClicked'
|
||||
'focus .quantity input': 'onQuantityFieldFocused'
|
||||
'input .quantity input': 'onQuantityFieldChanged'
|
||||
'keyup .quantity input': 'onQuantityKeyUp'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshButtons: ->
|
||||
@_updateButtonType @$firstButton, @_firstButtonType
|
||||
@_updateButtonType @$secondButton, @_secondButtonType
|
||||
|
||||
actions = [@$firstAction, @$secondAction]
|
||||
buttons = [@$firstButton, @$secondButton]
|
||||
|
||||
for buttonType, index in [@_firstButtonType, @_secondButtonType]
|
||||
display = if @_shouldEnableButton(@model, index) then '' else 'none'
|
||||
display = if not buttonType? then 'none' else display
|
||||
actions[index].css 'display', display
|
||||
|
||||
$label = buttons[index].find 'p'
|
||||
switch buttonType
|
||||
when 'check' then $label.text '✓'
|
||||
when 'down' then $label.text '⬇︎'
|
||||
when 'remove' then $label.text '-'
|
||||
when 'up' then $label.text '⬆︎'
|
||||
|
||||
_updateButtonType: ($button, type)->
|
||||
for currentType in ['check', 'down', 'remove', 'up']
|
||||
$button.removeClass currentType
|
||||
|
||||
if type?
|
||||
$button.css 'display', null
|
||||
$button.addClass type
|
||||
else
|
||||
$button.css 'display', 'none'
|
||||
@@ -0,0 +1,10 @@
|
||||
//-
|
||||
//- Crafting Guide - video.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__video
|
||||
iframe(width="356", height="267", src="", frameborder="0", allowfullscreen="true")
|
||||
.caption: p
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Crafting Guide - video.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__video {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.caption {
|
||||
margin-top: $size-margin-small;
|
||||
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Crafting Guide - video_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class VideoController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.templateName = 'common/video'
|
||||
super options
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$iframe = @$('iframe')
|
||||
@$caption = @$('.caption p')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$caption.html @model.name
|
||||
@$iframe.attr 'src', @_createYouTubeUrl()
|
||||
super
|
||||
|
||||
# Private ######################################################################################
|
||||
|
||||
_createYouTubeUrl: ->
|
||||
return "http://www.youtube.com/embed/#{@model.youTubeId}?modestbranding=1&autohide=1&showinfo=0"
|
||||
@@ -0,0 +1,63 @@
|
||||
//-
|
||||
//- Crafting Guide - craft_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- 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
|
||||
h2 Instructions
|
||||
.panel
|
||||
p.
|
||||
Crafting Guide provides step-by-step instructions for thousands of items from Minecraft and many
|
||||
popular mods. Just click "Add an item..." down below.
|
||||
p.
|
||||
Not sure what you'd like to make? Try one of these:
|
||||
.items
|
||||
a(href="/craft/potion_of_strength_ii_1_30")
|
||||
img(src="/data/minecraft/items/potion_of_strength_ii_1_30/icon.png")
|
||||
a(href="/craft/quarry")
|
||||
img(src="/data/buildcraft/items/quarry/icon.png")
|
||||
a(href="/craft/quantumsuit_bodyarmor:quantumsuit_boots:quantumsuit_helmet:quantumsuit_leggings")
|
||||
img(src="/data/ic2_classic/items/quantumsuit_helmet/icon.png")
|
||||
a(href="/craft/solar_panel_vi")
|
||||
img(src="/data/solar_flux/items/solar_panel_vi/icon.png")
|
||||
a(href="/craft/crystal_chest")
|
||||
img(src="/data/iron_chests/items/crystal_chest/icon.png")
|
||||
a(href="/craft/digital_miner")
|
||||
img(src="/data/mekanism/items/digital_miner/icon.png")
|
||||
a(href="/craft/2.tesseract")
|
||||
img(src="/data/thermal_expansion/items/tesseract/icon.png")
|
||||
|
||||
section.want
|
||||
h2 Items to Make
|
||||
.panel
|
||||
.view__inventory.large.editable
|
||||
|
||||
section.have
|
||||
h2 Already in Inventory
|
||||
.panel
|
||||
.view__inventory.large.editable
|
||||
|
||||
section.view__craftsman_working
|
||||
|
||||
section.need
|
||||
h2 Need to Gather
|
||||
.panel
|
||||
.view__inventory.large
|
||||
|
||||
section.steps
|
||||
h2 Steps
|
||||
.panel
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Crafting Guide - craft_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__craft_page {
|
||||
@include layout-body-with-sidebar;
|
||||
|
||||
.instructions {
|
||||
.panel {
|
||||
padding : $size-margin-large $size-margin-xlarge;
|
||||
|
||||
p {
|
||||
margin-bottom : $size-margin-large;
|
||||
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-large;
|
||||
line-height : $font-size-large * 1.5;
|
||||
}
|
||||
|
||||
.items {
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-around;
|
||||
margin-top : $size-margin-medium;
|
||||
margin-right : -$size-margin-medium;
|
||||
|
||||
a {
|
||||
flex : 0 0 auto;
|
||||
margin-top : $size-margin-medium;
|
||||
margin-right : $size-margin-medium;
|
||||
|
||||
background : $color-gray-faint;
|
||||
border : 1px solid $color-gray-light;
|
||||
padding : $size-margin-medium;
|
||||
|
||||
img {
|
||||
height : $size-minecraft-block * 1.5;
|
||||
width : $size-minecraft-block * 1.5;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $color-gray-light;
|
||||
border-color: $color-gray-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
#
|
||||
# Crafting Guide - craft_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# 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'
|
||||
StepController = require './step/step_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftPageController extends PageController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
if not options.storage? then throw new Error 'options.storage is required'
|
||||
|
||||
options.model ?= new CraftPage modPack:options.modPack
|
||||
options.templateName = 'craft_page'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
@_storage = options.storage
|
||||
|
||||
# c.event Methods ################################################################################
|
||||
|
||||
onHaveInventoryChanged: ->
|
||||
logger.warning => "storing have: #{@model.craftsman.have.unparse()}"
|
||||
@_storage.store 'crafting-plan:have', @model.craftsman.have.unparse()
|
||||
|
||||
onMoveNeedToHave: (itemSlug)->
|
||||
quantity = @_needInventoryController.model.quantityOf itemSlug
|
||||
@model.craftsman.have.add itemSlug, quantity
|
||||
|
||||
onRemoveFromHaveInventory: (itemSlug)->
|
||||
@model.craftsman.have.remove itemSlug
|
||||
|
||||
onRemoveFromWant: (itemSlug)->
|
||||
@model.craftsman.want.remove itemSlug
|
||||
@onWantInventoryChanged()
|
||||
|
||||
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'
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
|
||||
|
||||
@_wantInventoryController = @addChild InventoryController, '.want .view__inventory',
|
||||
firstButtonType: 'remove'
|
||||
imageLoader: @_imageLoader
|
||||
isAcceptable: (item)=> item.isCraftable
|
||||
modPack: @_modPack
|
||||
model: @model.craftsman.want
|
||||
router: @_router
|
||||
@_wantInventoryController.on c.event.button.first, (c, s)=> @onRemoveFromWant(s)
|
||||
@_wantInventoryController.on c.event.change, (c)=> @onWantInventoryChanged()
|
||||
|
||||
@_haveInventoryController = @addChild InventoryController, '.have .view__inventory',
|
||||
firstButtonType: 'down'
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
model: @model.craftsman.have
|
||||
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
|
||||
firstButtonType: 'up'
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
model: null
|
||||
router: @_router
|
||||
@_needInventoryController.on c.event.button.first, (c, s)=> @onMoveNeedToHave(s)
|
||||
|
||||
@_workingSectionController = @addChild CraftsmanWorkingController, '.view__craftsman_working',
|
||||
model: @model.craftsman
|
||||
|
||||
@$haveSection = @$('section.have')
|
||||
@$instructionsSection = @$('section.instructions')
|
||||
@$makeSection = @$('section.make')
|
||||
@$needSection = @$('section.need')
|
||||
@$stepsContainer = @$('section.steps .panel')
|
||||
@$stepsSection = @$('section.steps')
|
||||
@$toolsSection = @$('section.tools')
|
||||
@$workingSection = @$('.view__craftsman_working')
|
||||
|
||||
super
|
||||
|
||||
onWillRender: ->
|
||||
@model.craftsman.have.clear()
|
||||
@model.craftsman.have.parse @_storage.load('crafting-plan:have')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_needInventoryController.model = @model.craftsman.plan?.need
|
||||
@_refreshSectionVisibility()
|
||||
@_refreshSteps()
|
||||
@_adsenseController.fillAdPositions()
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ########################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .instructions a': 'routeLinkClick'
|
||||
|
||||
# Private Methods ################################################################################
|
||||
|
||||
_addTools: (controller)->
|
||||
controller.model.addToolsTo @model.craftsman.want
|
||||
|
||||
_completeStep: (controller)->
|
||||
controller.model.completeInto @model.craftsman.have
|
||||
|
||||
_isAddingToolsPossible: (controller)->
|
||||
tools = controller.model.recipe.tools
|
||||
return false unless tools.length > 0
|
||||
|
||||
have = @model.craftsman.have
|
||||
want = @model.craftsman.want
|
||||
|
||||
for stack in tools
|
||||
return false if have.hasAtLeast stack.itemSlug
|
||||
return false if want.hasAtLeast stack.itemSlug
|
||||
|
||||
return true
|
||||
|
||||
_isStepCompletable: (controller)->
|
||||
recipe = controller.model.recipe
|
||||
for stack in recipe.output
|
||||
return false if @model.craftsman.want.hasAtLeast stack.itemSlug
|
||||
|
||||
return true
|
||||
|
||||
_refreshSectionVisibility: ->
|
||||
return unless @_rendered
|
||||
|
||||
allSections = [
|
||||
@$instructionsSection, @$haveSection, @$needSection, @$stepsSection, @$toolsSection, @$workingSection
|
||||
]
|
||||
|
||||
visibleSections = []
|
||||
|
||||
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]
|
||||
else if not @model.craftsman.complete
|
||||
visibleSections.push el for el in [@$wantSection, @$workingSection]
|
||||
else
|
||||
visibleSections.push el for el in [@$haveSection, @$needSection, @$stepsSection, @$wantSection]
|
||||
|
||||
@hide $el for $el in allSections
|
||||
@show $el for $el in visibleSections
|
||||
|
||||
_refreshSteps: ->
|
||||
steps = @model.craftsman.plan?.steps or []
|
||||
@_stepControllers ?= []
|
||||
index = 0
|
||||
|
||||
for step in steps
|
||||
controller = @_stepControllers[index]
|
||||
if not controller?
|
||||
controller = new StepController
|
||||
canAddTools: (controller)=> @_isAddingToolsPossible controller
|
||||
canComplete: (controller)=> @_isStepCompletable controller
|
||||
onComplete: (controller)=> @_completeStep controller
|
||||
onAddTools: (controller)=> @_addTools controller
|
||||
imageLoader: @_imageLoader
|
||||
model: step
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
controller.render()
|
||||
|
||||
@$stepsContainer.append controller.$el
|
||||
@_stepControllers.push controller
|
||||
else
|
||||
controller.model = step
|
||||
|
||||
index += 1
|
||||
|
||||
while @_stepControllers.length > index
|
||||
@_stepControllers.pop().remove()
|
||||
@@ -0,0 +1,15 @@
|
||||
//-
|
||||
//- Crafting Guide - craftsman_working.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__item_group.section
|
||||
.content
|
||||
.waiting
|
||||
img(src='/images/wait.gif')
|
||||
.message
|
||||
p
|
||||
.count
|
||||
p
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Crafting Table - crafting_table.scss
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
.view__craftsman_working {
|
||||
.content {
|
||||
background: $color-white;
|
||||
box-shadow: none;
|
||||
|
||||
align-items : center;
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
padding : $size-margin-xlarge;
|
||||
|
||||
.waiting {
|
||||
margin-bottom: $size-margin-xlarge;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: $size-margin-medium;
|
||||
|
||||
p {
|
||||
font-family: $font-family-header;
|
||||
font-size: $font-size-large;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
p {
|
||||
font-family: $font-family-header;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#
|
||||
# Crafting Guide - craftsman_working_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
Craftsman = require '../../../models/crafting/craftsman'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftsmanWorkingController extends BaseController
|
||||
|
||||
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()
|
||||
|
||||
# BaseController Methods #######################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$message = @$('.message p')
|
||||
@$count = @$('.count p')
|
||||
@$waiting = @$('.waiting')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_refreshStatusText()
|
||||
super
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshStatusText: ->
|
||||
return unless @$message? and @$count?
|
||||
|
||||
@show @$waiting
|
||||
switch @model.stage
|
||||
when Craftsman::STAGE.WAITING
|
||||
@$message.html 'Preparing crafting calculation...'
|
||||
@$count.html ''
|
||||
when Craftsman::STAGE.GRAPHING
|
||||
@$message.html 'Researching recipes...'
|
||||
@$count.html "Found #{@model.stageCount} recipes so far..."
|
||||
when Craftsman::STAGE.PLANNING
|
||||
@$message.html 'Figuring out possible crafting plans...'
|
||||
@$count.html "Found #{@model.stageCount} possibilities so far..."
|
||||
when Craftsman::STAGE.ANALYZING
|
||||
@$message.html 'Looking for the best plan...'
|
||||
@$count.html "Finished checking #{@model.stageCount} so far..."
|
||||
when Craftsman::STAGE.COMPLETE
|
||||
@$message.html 'All done!'
|
||||
@$count.html ''
|
||||
when Craftsman::STAGE.INVALID
|
||||
@$message.html 'Couldn\'t make a crafting plan'
|
||||
@$count.html ''
|
||||
@hide @waiting
|
||||
@@ -0,0 +1,15 @@
|
||||
//-
|
||||
//- Crafting Guide - step.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__step
|
||||
h3
|
||||
.main_content
|
||||
.view__inventory
|
||||
.view__recipe
|
||||
.buttons
|
||||
.button.complete: .bezel: p complete step
|
||||
.button.tool: .bezel: p make this tool
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Crafting Guide - step.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid $color-gray-light;
|
||||
margin-top: $size-margin-large;
|
||||
padding-top: $size-margin-small;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
& > .buttons {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.main_content {
|
||||
display: flex;
|
||||
|
||||
.view__inventory {
|
||||
flex: 50%;
|
||||
margin-left: $size-margin-large;
|
||||
}
|
||||
|
||||
.view__recipe {
|
||||
flex: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
display: flex;
|
||||
|
||||
.button {
|
||||
flex: 1 1 50%;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#
|
||||
# Crafting Guide - step_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
InventoryController = require '../../common/inventory/inventory_controller'
|
||||
RecipeController = require '../../common/recipe/recipe_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class StepController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
options.templateName = 'craft_page/step'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
@canAddTools = options.canAddTools or (controller)-> true
|
||||
@canComplete = options.canComplete or (controller)-> true
|
||||
@onComplete = options.onComplete or (controller)-> # do nothing
|
||||
@onAddTools = options.onAddTools or (controller)-> # do nothing
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onCompleteButtonClicked: (event)->
|
||||
return if @$completeButton.hasClass 'disabled'
|
||||
@onComplete this
|
||||
|
||||
onToolButtonClicked: (event)->
|
||||
return if @$toolButton.hasClass 'disabled'
|
||||
@onAddTools this
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@inventoryController = @addChild InventoryController, '.view__inventory',
|
||||
editable: false
|
||||
imageLoader: @_imageLoader
|
||||
model: @model.inventory
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@recipeController = @addChild RecipeController, '.view__recipe',
|
||||
imageLoader: @_imageLoader
|
||||
model: @model.recipe
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@$completeButton = @$('.button.complete')
|
||||
@$header = @$('h3')
|
||||
@$toolButton = @$('.button.tool')
|
||||
@$toolButtonLabel = @$('.button.tool p')
|
||||
super
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
if not newModel? then throw new Error 'model cannot be null'
|
||||
return super
|
||||
|
||||
refresh: ->
|
||||
itemDisplay = @_modPack.findItemDisplay @model.recipe.output[0].itemSlug
|
||||
@$header.html "#{@model.number}. #{itemDisplay.itemName}"
|
||||
|
||||
@inventoryController.model = @model.inventory
|
||||
@recipeController.model = @model.recipe
|
||||
@recipeController.multiplier = @model.multiplier
|
||||
|
||||
@_refreshCompleteButton()
|
||||
@_refreshToolButton()
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .button.complete': 'onCompleteButtonClicked'
|
||||
'click .button.tool': 'onToolButtonClicked'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshToolButton: ->
|
||||
if @canAddTools this
|
||||
@$toolButton.removeClass 'disabled'
|
||||
else
|
||||
@$toolButton.addClass 'disabled'
|
||||
|
||||
_refreshCompleteButton: ->
|
||||
if @canComplete this
|
||||
@$completeButton.removeClass 'disabled'
|
||||
else
|
||||
@$completeButton.addClass 'disabled'
|
||||
@@ -0,0 +1,19 @@
|
||||
//-
|
||||
//- Crafting Guide - feedback.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__feedback
|
||||
.form-content
|
||||
input(name="name", placeholder="name (optional)")
|
||||
input(name="email", placeholder="email (optional)")
|
||||
|
||||
label(name="comment") Comment:
|
||||
textarea(name="comment")
|
||||
.error: p Sending failed. Please try again later.
|
||||
.button.send: .bezel: p send
|
||||
|
||||
.tab
|
||||
p Feedback
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Crafting Guide - feedback.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__feedback {
|
||||
min-height: $size-minecraft-block * 10;
|
||||
z-index: $layer-dialog;
|
||||
|
||||
background: white;
|
||||
box-shadow: $size-margin-xxsmall $size-margin-xxsmall $size-margin-xsmall $color-shadow;
|
||||
opacity: 0;
|
||||
|
||||
display: flex;
|
||||
|
||||
.form-content {
|
||||
flex: 1 1 100%;
|
||||
|
||||
border: $size-border-medium solid $color-black;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: $size-margin-medium;
|
||||
|
||||
& > :not(:last-child) {
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: $size-margin-medium;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
|
||||
font-family: $font-family-input;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-top: $size-margin-small;
|
||||
margin-bottom: $size-margin-small !important;
|
||||
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: $font-size-medium * 10;
|
||||
flex: 1 1 100%;
|
||||
|
||||
border: $size-border-small solid $color-gray-light;
|
||||
font-family: $font-family-input;
|
||||
font-size: $font-size-medium;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: $color-error;
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-small;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
$label-size: 25px;
|
||||
|
||||
flex: 0 0 auto;
|
||||
width: $label-size + 2 * $size-margin-medium;
|
||||
|
||||
background: $color-gray-light;
|
||||
border: $size-border-medium solid $color-black;
|
||||
border-left: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
p {
|
||||
transform: rotate(-90deg);
|
||||
|
||||
font-family: $font-family-header;
|
||||
font-size: $label-size;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#
|
||||
# Crafting Guide - feedback_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../base_controller'
|
||||
EmailClient = require '../../models/site/email_client'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class FeedbackController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.model ?= new EmailClient
|
||||
options.templateName = 'feedback'
|
||||
super options
|
||||
|
||||
Object.defineProperty this, 'isOpen', get:-> @$el.offset().left is 0
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
enterFeedback: (message)->
|
||||
promise = w(true)
|
||||
if not @isOpen then promise = @onToggle()
|
||||
promise.then => @$commentField.val message
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onSendClicked: ->
|
||||
return unless @$commentField.val().length > 0
|
||||
@$sendButton.addClass 'disabled'
|
||||
|
||||
message =
|
||||
url: window.location.href
|
||||
name: @$nameField.val()
|
||||
email: @$emailField.val()
|
||||
comment: @$commentField.val()
|
||||
|
||||
@model.send('feedback', message)
|
||||
.then =>
|
||||
@onToggle()
|
||||
@$error.css display: 'none'
|
||||
.catch (error)=>
|
||||
@$error.css display: ''
|
||||
.then =>
|
||||
@$sendButton.removeClass 'disabled'
|
||||
|
||||
onTextChanged: (event)->
|
||||
if @$commentField.val().length > 0
|
||||
@$sendButton.removeClass 'disabled'
|
||||
else
|
||||
@$sendButton.addClass 'disabled'
|
||||
|
||||
onToggle: ->
|
||||
w.promise (resolve)=>
|
||||
@$screen.off 'click'
|
||||
|
||||
if @isOpen
|
||||
@$screen.css display:'none'
|
||||
@$page.removeClass 'blur'
|
||||
@$el.animate {left:-@_closeWidth}, duration:c.duration.fast, complete:=>
|
||||
@$commentField.val ''
|
||||
@$('input, textarea').blur()
|
||||
resolve()
|
||||
else
|
||||
@$page.addClass 'blur'
|
||||
@$screen.on 'click', => @onToggle()
|
||||
@$screen.css display:'block'
|
||||
|
||||
@$el.animate {left:0}, duration:c.duration.fast, complete:=>
|
||||
if @$nameField.val().length is 0
|
||||
@$nameField.focus()
|
||||
else if @$emailField.val().length is 0
|
||||
@$emailField.focus()
|
||||
else
|
||||
@$commentField.focus()
|
||||
|
||||
resolve()
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$page = $('.page')
|
||||
@$screen = $('.view__screen')
|
||||
|
||||
@$commentField = @$('textarea[name="comment"]')
|
||||
@$emailField = @$('input[name="email"]')
|
||||
@$error = @$('.error')
|
||||
@$nameField = @$('input[name="name"]')
|
||||
@$sendButton = @$('.button.send')
|
||||
|
||||
@$el.css left:-@$el.outerWidth() * 1.25, opacity: 1.0;
|
||||
|
||||
@_closeWidth = @$el.outerWidth() - @$('.tab').outerWidth()
|
||||
@$el.delay(c.duration.slow).animate {left:-@_closeWidth}, c.duration.normal
|
||||
@onTextChanged()
|
||||
|
||||
@$error.css display:'none'
|
||||
@$sendButton.addClass 'disabled'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Methods ########################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click .tab': 'onToggle'
|
||||
'click .button': 'onSendClicked'
|
||||
'input textarea': 'onTextChanged'
|
||||
'keyup textarea': 'onTextChanged'
|
||||
@@ -0,0 +1,42 @@
|
||||
//-
|
||||
//- Crafting Guide - footer.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__footer
|
||||
.divider.top
|
||||
|
||||
.content
|
||||
.left
|
||||
h2 About
|
||||
p.
|
||||
Crafting Guide gives step-by-step instructions for making anything in Minecraft or its many mods. Just
|
||||
say what you'd like to make, what you already have, it will do the rest, giving you a list of raw
|
||||
materials and instructions of which items to make in the proper order. You can even ask it to include
|
||||
the materials and instructions for all the tools you'll need along the way!
|
||||
|
||||
.center
|
||||
h2 Donate
|
||||
p.
|
||||
Crafting Guide is free for all, but if you find it helpful, donations in any amount are gratefully
|
||||
accepted.
|
||||
.action
|
||||
form(action="https://www.paypal.com/cgi-bin/webscr", class="centered", method="post", target="_top")
|
||||
input(type="hidden", name="cmd", value="_s-xclick")
|
||||
input(type="hidden", name="hosted_button_id", value="GCB2TYZJYLAE6")
|
||||
input(type="image", src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif", border="0",
|
||||
name="submit" alt="PayPal - The safer, easier way to pay online!")
|
||||
img(src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif", width="1", height="1")
|
||||
|
||||
.right
|
||||
h2 Get Involved
|
||||
p.
|
||||
Crafting Guide is completely open-source, and you can help! Whether you want to write a recipe book
|
||||
(all simple JSON), or implement new features, just head over to GitHub to get started.
|
||||
.action
|
||||
iframe(src="http://ghbtns.com/github-btn.html?user=andrewminer&repo=crafting-guide&type=fork&size=large",
|
||||
allowtransparency="true", frameborder="0", scrolling="0", width="100", height="32")
|
||||
|
||||
.divider.bottom
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Crafting Guide - footer.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
background: $color-background-footer;
|
||||
|
||||
.divider.top {
|
||||
background: $color-dirt url('/images/grass_side.png');
|
||||
height: $size-minecraft-block;
|
||||
}
|
||||
|
||||
> .content {
|
||||
display: flex;
|
||||
|
||||
color: $color-text-light;
|
||||
|
||||
.left, .center, .right {
|
||||
flex: 1 1 33%;
|
||||
margin: $size-margin-large $size-margin-large $size-margin-large 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
p {
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $font-size-medium * 1.25;
|
||||
text-align : justify;
|
||||
}
|
||||
|
||||
.action {
|
||||
flex: 1 1 auto;
|
||||
align-self: center;
|
||||
margin-bottom: $size-margin-large;
|
||||
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
margin-left: $size-margin-large;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.divider.bottom {
|
||||
background: $color-bedrock url('/images/bedrock.png');
|
||||
height: $size-minecraft-block;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#
|
||||
# Crafting Guide - footer_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class FooterController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'footer'
|
||||
super options
|
||||
@@ -0,0 +1,24 @@
|
||||
//-
|
||||
//- Crafting Guide - header.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__header
|
||||
.top-row
|
||||
a.logo(href="/")
|
||||
img(src="/images/site-icon.png")
|
||||
|
||||
h1 Minecraft Crafting Guide
|
||||
|
||||
.addthis_sharing_toolbox
|
||||
|
||||
.divider
|
||||
.button.craft: .bezel: p Craft
|
||||
.button.browse: .bezel: p Browse
|
||||
.button.news: .bezel: p News
|
||||
.button.search: .bezel: p Search
|
||||
.button.login: .bezel: p Login
|
||||
|
||||
.breadcrumbs
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Crafting Guide - header.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__header {
|
||||
background: $color-gray-light;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
.top-row {
|
||||
display : flex;
|
||||
align-items : center;
|
||||
|
||||
a {
|
||||
flex : 0 0 auto;
|
||||
height : $size-minecraft-block * 1.5;
|
||||
width : $size-minecraft-block * 1.5;
|
||||
margin-right : $size-margin-medium;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.addthis_sharing_toolbox {
|
||||
flex : 0 0 auto;
|
||||
min-height : 37px;
|
||||
min-width : 144px;
|
||||
margin-right : $size-margin-small;
|
||||
|
||||
|
||||
&.placeholder {
|
||||
border : 1px solid orange;
|
||||
text-align : center;
|
||||
|
||||
&:before {
|
||||
color : orange;
|
||||
content : "ADD THIS";
|
||||
font-family : $font-family-header;
|
||||
font-size : $font-size-large;
|
||||
line-height : 37px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
background : $color-planks url('/images/planks.png');
|
||||
|
||||
align-items : center;
|
||||
display : flex;
|
||||
flex-wrap : wrap;
|
||||
justify-content : space-around;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
background: $color-screen;
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
|
||||
padding: $size-margin-small $size-margin-medium;
|
||||
|
||||
&:before {
|
||||
content: "⟩";
|
||||
margin-right: $size-margin-medium;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
span {
|
||||
margin: 0 $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#
|
||||
# Crafting Guide - header_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../base_controller'
|
||||
ItemSelectorController = require '../common/item_selector/item_selector_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class HeaderController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
options.model ?= {}
|
||||
options.templateName = 'header'
|
||||
super options
|
||||
|
||||
@_modPack = options.modPack
|
||||
|
||||
global.site.on c.event.change + ':user', => @tryRefresh()
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onCraft: ->
|
||||
@router.navigate '/craft', trigger:true
|
||||
return false
|
||||
|
||||
onBrowse: ->
|
||||
@router.navigate '/browse', trigger:true
|
||||
return false
|
||||
|
||||
onLogin: ->
|
||||
if global.site.user?
|
||||
global.site.logout()
|
||||
else
|
||||
@router.navigate '/login', trigger:true
|
||||
return false
|
||||
|
||||
onNews: ->
|
||||
@router.navigate '/news', trigger:true
|
||||
return false
|
||||
|
||||
onSearch: ->
|
||||
@_selector.launch()
|
||||
.then (itemSlug)=>
|
||||
itemDisplay = @_modPack.findItemDisplay itemSlug
|
||||
@router.navigate itemDisplay.itemUrl, trigger:true
|
||||
return false
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_selector = @addChild ItemSelectorController, null, modPack: @_modPack
|
||||
|
||||
if global.env in c.productionEnvs
|
||||
addThisUrl = 'http://s7.addthis.com/js/300/addthis_widget.js?pubid=ra-54f3c9717b2e530d'
|
||||
$('body').append "<script async=\"async\" src=\"#{addThisUrl}\"></script>"
|
||||
else
|
||||
@$('.addthis_sharing_toolbox').addClass 'placeholder'
|
||||
|
||||
@$breadcrumbs = @$('.breadcrumbs')
|
||||
@$loginButtonLabel = @$('.button.login p')
|
||||
@$title = $('title')
|
||||
super
|
||||
|
||||
onWillChangeModel: (oldModel, newModel)->
|
||||
if oldModel? then @stopListening oldModel.controller
|
||||
@listenTo newModel.controller, c.event.change, => @tryRefresh()
|
||||
return newModel
|
||||
|
||||
refresh: ->
|
||||
@$loginButtonLabel.text if global.site.user? then "Logout" else "Login"
|
||||
|
||||
@_refreshBreadcrumbs()
|
||||
@_refreshTitle()
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
_.extend super,
|
||||
'click .craft': 'onCraft'
|
||||
'click .browse': 'onBrowse'
|
||||
'click .login': 'onLogin'
|
||||
'click .news': 'onNews'
|
||||
'click .search': 'onSearch'
|
||||
'click .breadcrumbs a': 'routeLinkClick'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshBreadcrumbs: ->
|
||||
breadcrumbs = @model?.controller?.getBreadcrumbs()
|
||||
breadcrumbs ?= []
|
||||
|
||||
if breadcrumbs.length > 0
|
||||
@$breadcrumbs.empty()
|
||||
|
||||
for crumb, index in breadcrumbs
|
||||
if index isnt 0 then @$breadcrumbs.append $("<span>⟩</span>")
|
||||
@$breadcrumbs.append crumb
|
||||
|
||||
@show @$breadcrumbs
|
||||
else
|
||||
@hide @$breadcrumbs
|
||||
|
||||
_refreshTitle: ->
|
||||
title = @model?.controller?.getTitle()
|
||||
if title
|
||||
@$title.text c.text.titleWithText text: title
|
||||
else
|
||||
@$title.text c.text.title()
|
||||
@@ -0,0 +1,103 @@
|
||||
#
|
||||
# Crafting Guide - image_loader.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ImageLoader
|
||||
|
||||
constructor: (options={})->
|
||||
options.defaultUrl ?= null
|
||||
|
||||
if not options.defaultUrl?
|
||||
options.onLoading ?= -> @hide()
|
||||
options.onLoad ?= -> @show()
|
||||
|
||||
@defaultUrl = options.defaultUrl
|
||||
@onLoading = options.onLoading
|
||||
@onLoad = options.onLoad
|
||||
|
||||
@_images = {}
|
||||
|
||||
if @defaultUrl
|
||||
@_defaultImage = new Image
|
||||
@_defaultImage.src = @defaultUrl
|
||||
|
||||
# Class Methods ################################################################################
|
||||
|
||||
@load: (imageUrl, $el, options={}) ->
|
||||
loader = new ImageLoader options
|
||||
loader.load $el, options
|
||||
return loader
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
isLoaded: (imageUrl)->
|
||||
data = @_images[imageUrl]
|
||||
return false unless data?
|
||||
return data.isLoaded
|
||||
|
||||
load: (imageUrl, $el)->
|
||||
return if not $el
|
||||
|
||||
data = @preload imageUrl
|
||||
if $el.attr('src')?
|
||||
return if $el.attr('src').indexOf(imageUrl) isnt -1
|
||||
|
||||
if @onLoading? then @onLoading.call $el
|
||||
$el.data 'isLoading', true
|
||||
$el.data 'isLoaded', false
|
||||
|
||||
if data.isLoaded
|
||||
@_loadImageIntoElement data.imageUrl, $el
|
||||
else
|
||||
if @defaultUrl?
|
||||
$el.attr 'src', @defaultUrl
|
||||
else
|
||||
$el.removeAttr 'src'
|
||||
|
||||
if data.elements.indexOf($el) is -1 then data.elements.push $el
|
||||
|
||||
return this
|
||||
|
||||
preload: (imageUrl)->
|
||||
data = @_images[imageUrl]
|
||||
if not data?
|
||||
data = imageUrl:imageUrl, elements:[], image:new Image, isLoaded:false
|
||||
@_images[imageUrl] = data
|
||||
|
||||
data.image = new Image
|
||||
data.image.onload = => @_onImageLoaded data
|
||||
data.image.onerror = => @_onImageError data
|
||||
data.image.src = imageUrl
|
||||
|
||||
return data
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_onImageLoaded: (data)->
|
||||
for $el in data.elements
|
||||
@_loadImageIntoElement data.imageUrl, $el
|
||||
|
||||
data.elements = []
|
||||
data.isLoaded = true
|
||||
|
||||
_onImageError: (data)->
|
||||
if @defaultUrl?
|
||||
data.imageUrl = @defaultUrl
|
||||
|
||||
for $el in data.elements
|
||||
@_loadImageIntoElement @defaultUrl, $el
|
||||
|
||||
data.elements = []
|
||||
data.isLoaded = true
|
||||
|
||||
_loadImageIntoElement: (imageUrl, $el)->
|
||||
$el.data 'isLoading', false
|
||||
$el.data 'isLoaded', true
|
||||
$el.attr 'src', imageUrl
|
||||
|
||||
if @onLoad? then @onLoad.call $el
|
||||
@@ -0,0 +1,35 @@
|
||||
//-
|
||||
//- Crafting Guide - index.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title The Ultimate Minecraft Crafting Guide
|
||||
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")
|
||||
|
||||
body
|
||||
.view__feedback
|
||||
.view__screen(style="display: none")
|
||||
|
||||
.page
|
||||
.view__header
|
||||
|
||||
.content(style="display: none")
|
||||
.content-loading
|
||||
img(src="/images/wait.gif")
|
||||
|
||||
.view__footer
|
||||
|
||||
script(src="/external.js")
|
||||
script(src="/internal.js")
|
||||
|
||||
include ../includes/_google_adsense.jade
|
||||
include ../includes/_google_analytics.jade
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Crafting Guide - index.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
html {
|
||||
font-size : $size-rem;
|
||||
|
||||
body {
|
||||
background : $color-background-window url('/images/ice.png');
|
||||
width : 100%;
|
||||
z-index : $layer-normal;
|
||||
|
||||
display : flex;
|
||||
justify-content : center;
|
||||
|
||||
.view__feedback {
|
||||
position: fixed; top: 50%; left: 0; width: $size-minecraft-block * 10;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.view__screen {
|
||||
position : fixed; top: 0; right: 0; bottom: 0; left: 0;
|
||||
z-index : $layer-screen;
|
||||
|
||||
background: $color-screen;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
& > :not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > *:first-child {
|
||||
flex: 0 0 50%;
|
||||
|
||||
box-shadow : $size-margin-small $size-margin-small $size-margin-large $color-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
.page {
|
||||
margin : $size-margin-large 0;
|
||||
max-width : $size-page-width;
|
||||
width : 100%;
|
||||
|
||||
background : $color-background-page;
|
||||
box-shadow : $size-margin-small $size-margin-small $size-margin-large $color-shadow;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
|
||||
> .content {
|
||||
padding : $size-margin-medium;
|
||||
}
|
||||
|
||||
> .content-loading {
|
||||
margin : $size-margin-xlarge 0;
|
||||
|
||||
display : flex;
|
||||
align-items : center;
|
||||
justify-content : center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//-
|
||||
//- Crafting Guide - item_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__item_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.about
|
||||
.left
|
||||
img
|
||||
.center
|
||||
.title
|
||||
a.sourceMod
|
||||
.button-row
|
||||
.button.craftingPlan: .bezel: p View Crafting Plan
|
||||
.right
|
||||
p Official Link:
|
||||
a.officialLink project page
|
||||
|
||||
section.description.view__markdown_section
|
||||
|
||||
section.multiblock
|
||||
h2 Multiblock Construction
|
||||
.panel
|
||||
.view__multiblock_viewer
|
||||
|
||||
section.recipes
|
||||
h2 Recipes
|
||||
.panel
|
||||
|
||||
section.videos
|
||||
h2 Videos
|
||||
.panel
|
||||
|
||||
section.view__item_group.usedToMake
|
||||
|
||||
section.view__item_group.usedAsToolToMake
|
||||
|
||||
section.view__item_group.similar
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Crafting Guide - item_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__item_page {
|
||||
@include layout-body-with-sidebar;
|
||||
|
||||
.about {
|
||||
margin-bottom: $size-margin-large;
|
||||
display: flex;
|
||||
|
||||
& > .left {
|
||||
margin-right: $size-margin-large;
|
||||
|
||||
flex: 0 0 auto;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block * 5;
|
||||
width: $size-minecraft-block * 5;
|
||||
}
|
||||
}
|
||||
|
||||
& > .center {
|
||||
flex: 1 1 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-family: $font-family-header;
|
||||
font-size: $font-size-xxlarge;
|
||||
}
|
||||
|
||||
a.sourceMod {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
flex: 1 1 100%;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
|
||||
.button {
|
||||
flex: 1 1 100%;
|
||||
max-width: 66%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
#
|
||||
# Crafting Guide - item_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# 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'
|
||||
ItemPage = require '../../models/site/item_page'
|
||||
ItemSlug = require '../../models/game/item_slug'
|
||||
MarkdownSectionController = require '../common/markdown_section/markdown_section_controller'
|
||||
MultiblockViewerController = require './multiblock_viewer/multiblock_viewer_controller'
|
||||
PageController = require '../page_controller'
|
||||
RecipeDetailController = require './recipe_detail/recipe_detail_controller'
|
||||
VideoController = require '../common/video/video_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ItemPageController extends PageController
|
||||
|
||||
@::FILE_UPLOAD_DELAY = 250
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.client? then throw new Error 'options.client is required'
|
||||
if not options.itemSlug? then throw new Error 'options.itemSlug is required'
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
|
||||
options.model ?= new ItemPage modPack:options.modPack
|
||||
options.templateName ?= 'item_page'
|
||||
|
||||
super options
|
||||
|
||||
@_client = options.client
|
||||
@_descriptionFile = null
|
||||
@_enterFeedback = options.enterFeedback
|
||||
@_imageLoader = options.imageLoader
|
||||
@_itemSlug = options.itemSlug
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
@_modPack.on c.event.change, =>
|
||||
@tryRefresh()
|
||||
@trigger c.event.change
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
craftingPlanButtonClicked: ->
|
||||
display = @_modPack.findItemDisplay @model.item.slug
|
||||
@_router.navigate display.craftingUrl, trigger:true
|
||||
return false
|
||||
|
||||
# PageController Overrides #####################################################################
|
||||
|
||||
getBreadcrumbs: ->
|
||||
return [] unless @_itemSlug?
|
||||
|
||||
display = @_modPack.findItemDisplay @_itemSlug
|
||||
return [] unless display.itemName? and display.modName
|
||||
|
||||
return [
|
||||
$("<a href='/browse'>Browse</a>")
|
||||
$("<a href='#{c.url.mod modSlug:display.modSlug}'>#{display.modName}</a>")
|
||||
$("<b>#{display.itemName}</b>")
|
||||
]
|
||||
|
||||
getMetaDescription: ->
|
||||
return null unless @_itemSlug?
|
||||
display = @_modPack.findItemDisplay @_itemSlug
|
||||
return c.text.itemDescription display
|
||||
|
||||
getTitle: ->
|
||||
return null unless @_itemSlug?
|
||||
|
||||
display = @_modPack.findItemDisplay @_itemSlug
|
||||
return null unless display.itemName? and display.modName?
|
||||
|
||||
return "#{display.itemName} from #{display.modName}"
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper', router: @_router
|
||||
|
||||
options = imageLoader:@_imageLoader, modPack:@_modPack, router:@_router, show:false
|
||||
@_multiblockController = @addChild MultiblockViewerController, '.view__multiblock_viewer', options
|
||||
@_similarItemsController = @addChild ItemGroupController, '.view__item_group.similar', options
|
||||
@_usedAsToolToMakeController = @addChild ItemGroupController, '.view__item_group.usedAsToolToMake ', options
|
||||
@_usedToMakeController = @addChild ItemGroupController, '.view__item_group.usedToMake', options
|
||||
|
||||
@_descriptionController = @addChild MarkdownSectionController, '.view__markdown_section',
|
||||
client: @_client
|
||||
editable: true
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
beginEditing: => @_beginEditingDescription()
|
||||
endEditing: => @_endEditingDescription()
|
||||
enterFeedback: @_enterFeedback
|
||||
|
||||
@$aboutImage = @$('.about img')
|
||||
@$name = @$('.about .title')
|
||||
@$officialLink = @$('.about a.officialLink')
|
||||
@$sourceModLink = @$('.about a.sourceMod')
|
||||
@$aboutLinks = @$('.about .right')
|
||||
|
||||
@$multiblockSection = @$('section.multiblock')
|
||||
@$recipeContainer = @$('section.recipes .panel')
|
||||
@$recipesSection = @$('section.recipes')
|
||||
@$recipesSectionTitle = @$('section.recipes h2')
|
||||
@$similarSection = @$('section.similar')
|
||||
@$usedAsToolToMakeSection = @$('section.usedAsToolToMake')
|
||||
@$usedToMakeSection = @$('section.usedToMake')
|
||||
@$videosContainer = @$('section.videos .panel')
|
||||
@$videosSection = @$('section.videos')
|
||||
@$videosSectionTitle = @$('section.videos h2')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_resolveItemSlug()
|
||||
|
||||
if @model.item?
|
||||
display = @_modPack.findItemDisplay @model.item.slug
|
||||
@_imageLoader.load display.iconUrl, @$aboutImage
|
||||
@$name.text display.itemName
|
||||
|
||||
@_descriptionController.imageBase = c.url.itemImageDir display
|
||||
|
||||
if @model.item.officialUrl?
|
||||
@$officialLink.attr 'href', @model.item.officialUrl
|
||||
@show @$aboutLinks
|
||||
else
|
||||
@hide @$aboutLinks
|
||||
|
||||
@show()
|
||||
else
|
||||
@hide()
|
||||
|
||||
@_refreshSourceMod()
|
||||
@_refreshDescription()
|
||||
@_refreshMultiblock()
|
||||
@_refreshRecipes()
|
||||
@_refreshSimilarItems()
|
||||
@_refreshUsedAsToolToMake()
|
||||
@_refreshUsedToMake()
|
||||
@_refreshVideos()
|
||||
|
||||
super
|
||||
|
||||
setUser: (user)->
|
||||
super user
|
||||
@_descriptionController.user = user if @_descriptionController?
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a.craftingPlan': 'routeLinkClick'
|
||||
'click a.sourceMod': 'routeLinkClick'
|
||||
'click .markdown a': 'routeLinkClick'
|
||||
'click .button.craftingPlan': 'craftingPlanButtonClicked'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_beginEditingDescription: ->
|
||||
if not @user?
|
||||
global.site.login()
|
||||
return w.reject new Error 'must be logged in to edit'
|
||||
|
||||
if not @model.item?
|
||||
return w.reject new Error 'must have an item'
|
||||
|
||||
pathArgs = modSlug:@_itemSlug.mod, itemSlug:@_itemSlug.item
|
||||
attributes =
|
||||
fileName: c.gitHub.file.itemDescription.fileName pathArgs
|
||||
path: c.gitHub.file.itemDescription.path pathArgs
|
||||
|
||||
@_descriptionFile = new EditableFile attributes, client:@_client
|
||||
@_descriptionFile.fetch()
|
||||
.then =>
|
||||
if @_descriptionFile.encodedData?.length > 0
|
||||
@model.item.parse @_descriptionFile.getDecodedData 'utf8'
|
||||
else
|
||||
@model.item.description = ''
|
||||
|
||||
@_descriptionController.model = @model.item.description
|
||||
|
||||
_endEditingDescription: ->
|
||||
oldDescription = @model.item.description
|
||||
promises = []
|
||||
|
||||
saveList = []
|
||||
for imageFile in @_descriptionController.imageFiles
|
||||
saveList.push
|
||||
file: imageFile
|
||||
message: "User-submitted image for #{@model.item.name} from #{global.hostName}"
|
||||
|
||||
@model.item.description = @_descriptionController.model
|
||||
@_descriptionFile.setDecodedData @model.item.unparse()
|
||||
saveList.push
|
||||
file: @_descriptionFile
|
||||
message: "User-submitted text for #{@model.item.name} from #{global.hostName}"
|
||||
|
||||
saveNextFile = (fileList)->
|
||||
return w(true) if fileList.length is 0
|
||||
{file, message} = fileList.shift()
|
||||
file.save message
|
||||
.delay @FILE_UPLOAD_DELAY
|
||||
.then ->
|
||||
saveNextFile fileList
|
||||
|
||||
saveNextFile saveList
|
||||
.catch (e)=>
|
||||
@model.item.description = oldDescription
|
||||
throw e
|
||||
|
||||
_refreshDescription: ->
|
||||
if @model.item?.description?.length > 0
|
||||
@_descriptionController.model = @model.item.description
|
||||
@_descriptionController.resetToDefaultState()
|
||||
|
||||
_refreshMultiblock: ->
|
||||
if @model.item?.multiblock?
|
||||
@_multiblockController.model = @model.item.multiblock
|
||||
@show @$multiblockSection
|
||||
else
|
||||
@hide @$multiblockSection
|
||||
|
||||
_refreshRecipes: ->
|
||||
@_recipeControllers ?= []
|
||||
index = 0
|
||||
|
||||
recipes = @model.findRecipes()
|
||||
if recipes?.length > 0
|
||||
@$recipesSectionTitle.html if recipes.length is 1 then 'Recipe' else 'Recipes'
|
||||
|
||||
for recipe in @model.findRecipes()
|
||||
controller = @_recipeControllers[index]
|
||||
if not controller?
|
||||
controller = new RecipeDetailController
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
model: recipe
|
||||
router: @_router
|
||||
@_recipeControllers.push controller
|
||||
@$recipeContainer.append controller.$el
|
||||
controller.render()
|
||||
else
|
||||
controller.model = recipe
|
||||
index++
|
||||
|
||||
@show @$recipesSection
|
||||
else
|
||||
@hide @$recipesSection
|
||||
|
||||
while @_recipeControllers.length > index
|
||||
@_recipeControllers.pop().remove()
|
||||
|
||||
_refreshSimilarItems: ->
|
||||
group = @model.item?.group
|
||||
if group? and group isnt Item.Group.Other
|
||||
@_similarItemsController.title = "Other #{group}"
|
||||
@_similarItemsController.model = @model.findSimilarItems()
|
||||
else
|
||||
@_similarItemsController.model = null
|
||||
|
||||
_refreshSourceMod: ->
|
||||
mod = @model.item?.modVersion?.mod
|
||||
if mod?.name?.length > 0
|
||||
@$sourceModLink.attr 'href', c.url.mod modSlug:mod.slug
|
||||
@$sourceModLink.text mod.name
|
||||
|
||||
@show @$sourceModLink
|
||||
else
|
||||
@hide @$sourceModLink
|
||||
|
||||
_refreshUsedAsToolToMake: ->
|
||||
@_usedAsToolToMakeController.title = 'Used as Tool to Make'
|
||||
@_usedAsToolToMakeController.model = @model.findToolForRecipes()
|
||||
|
||||
_refreshUsedToMake: ->
|
||||
@_usedToMakeController.title = 'Used to Make'
|
||||
@_usedToMakeController.model = @model.findComponentInItems()
|
||||
|
||||
_refreshVideos: ->
|
||||
@_videoControllers ?= []
|
||||
index = 0
|
||||
|
||||
videos = @model?.item?.videos or []
|
||||
if videos? and videos.length > 0
|
||||
@$videosSectionTitle.html if videos.length is 1 then 'Video' else 'Videos'
|
||||
|
||||
for video in videos
|
||||
controller = @_videoControllers[index]
|
||||
if not controller?
|
||||
controller = new VideoController model:video
|
||||
@_videoControllers.push controller
|
||||
controller.render()
|
||||
@$videosContainer.append controller.$el
|
||||
else
|
||||
controller.model = video
|
||||
index++
|
||||
|
||||
@show @$videosSection
|
||||
else
|
||||
@hide @$videosSection
|
||||
|
||||
while @_videoControllers.length > index
|
||||
@_videoControllers.pop().remove()
|
||||
|
||||
_resolveItemSlug: ->
|
||||
return if @model.item?
|
||||
|
||||
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
|
||||
return
|
||||
|
||||
@model.item = item
|
||||
item.fetch()
|
||||
item.on c.event.sync, => @refresh()
|
||||
@@ -0,0 +1,9 @@
|
||||
//-
|
||||
//- Crafting Guide - multiblock.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__multiblock
|
||||
img.outline(src="/images/outline.png")
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Crafting Guide - multiblock.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
$animate-slow: 400ms;
|
||||
$animate-fast: 100ms;
|
||||
|
||||
.view__multiblock {
|
||||
position: relative;
|
||||
|
||||
.block {
|
||||
position: absolute;
|
||||
transition: all $animate-slow cubic-bezier(0.615, -0.090, 0.010, 1.090);
|
||||
}
|
||||
|
||||
.outline {
|
||||
position: absolute;
|
||||
opacity: 0.0;
|
||||
transition: opacity $animate-fast;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
###
|
||||
Crafting Guide - multiblock_controller.coffee
|
||||
|
||||
Copyright (c) 2014-2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
BaseController = require '../../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MultiblockController extends BaseController
|
||||
|
||||
@::HOVER_TIMEOUT = 5000 # ms
|
||||
|
||||
@::IMAGE_SIZE = 48 # px
|
||||
|
||||
@::X_LAYER_H_SHIFT = -24 # px
|
||||
@::Z_LAYER_H_SHIFT = +24 # px
|
||||
|
||||
@::X_LAYER_V_SHIFT = -12 # px
|
||||
@::Y_LAYER_V_SHIFT = -30 # px
|
||||
@::Z_LAYER_V_SHIFT = -12 # px
|
||||
|
||||
@::MAX_DROP_DELAY = 250 # ms
|
||||
|
||||
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 = 'item_page/multiblock_viewer/multiblock'
|
||||
super options
|
||||
|
||||
@_currentLayer = 0
|
||||
@_blockCache = null
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_onHovering = options.onHovering ?= (itemDisplay)-> # do nothing
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
goBackLayer: ->
|
||||
@_currentLayer = Math.max 0, @_currentLayer - 1
|
||||
@_refreshAllBlocks()
|
||||
|
||||
goNextLayer: ->
|
||||
@_currentLayer = Math.min @model.height - 1, @_currentLayer + 1
|
||||
@_refreshAllBlocks()
|
||||
|
||||
hasBackLayer: ->
|
||||
return false unless @model?
|
||||
return @_currentLayer > 0
|
||||
|
||||
hasNextLayer: ->
|
||||
return false unless @model?
|
||||
return @_currentLayer < @model.height - 1
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
currentLayer:
|
||||
get: -> return @_currentLayer
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$outline = @$('.outline')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@$el.css 'height':@_computeMinHeight(), 'width':@_computeMinWidth()
|
||||
|
||||
@_refreshBlockCache()
|
||||
@_refreshAllBlocks()
|
||||
super
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_computePosition: (x, y, z, options={})->
|
||||
if not @model? then return left:0, top:-@IMAGE_SIZE, 'z-index':0, opacity:0.0
|
||||
|
||||
options.hideUpperLayers ?= false
|
||||
pxWidth = @$el.width()
|
||||
pxHeight = @$el.height()
|
||||
|
||||
left = ((pxWidth - @IMAGE_SIZE) / 2) + (x * @X_LAYER_H_SHIFT) + (z * @Z_LAYER_H_SHIFT)
|
||||
|
||||
if options.hideUpperLayers and y > @_currentLayer
|
||||
top = 0
|
||||
opacity = 0.0
|
||||
else
|
||||
top = (pxHeight - @IMAGE_SIZE) + (x * @X_LAYER_V_SHIFT) + (z * @Z_LAYER_V_SHIFT) + (y * @Y_LAYER_V_SHIFT)
|
||||
opacity = 1.0
|
||||
|
||||
zindex = ((@model.width - x) + (@model.depth - z) + ((@model.width * @model.depth) * y)) * 2
|
||||
|
||||
return left:left, top:top, 'z-index':zindex, opacity:opacity
|
||||
|
||||
_computeMinHeight: ->
|
||||
return 0 unless @model?
|
||||
|
||||
minHeight = Math.max(Math.abs(@model.width * @X_LAYER_V_SHIFT), Math.abs(@model.depth * @Z_LAYER_V_SHIFT))
|
||||
minHeight += Math.abs(@model.height * @Y_LAYER_V_SHIFT)
|
||||
minHeight += 2 * @IMAGE_SIZE
|
||||
return minHeight
|
||||
|
||||
_computeMinWidth: ->
|
||||
return 0 unless @model?
|
||||
|
||||
leftPosition = @_computePosition @model.width, 0, 0
|
||||
rightPosition = @_computePosition 0, 0, @model.depth
|
||||
return rightPosition.left - leftPosition.left
|
||||
|
||||
_endHovering: ->
|
||||
@$outline.css opacity:0.0
|
||||
@_hoverTimer = null
|
||||
@_onHovering null
|
||||
|
||||
_randomDropDelay: ->
|
||||
return Math.random() * @MAX_DROP_DELAY
|
||||
|
||||
_refreshBlockCache: ->
|
||||
@_blockCache = null
|
||||
return unless @model?
|
||||
|
||||
@_blockCache = width:@model.width, height:@model.height, depth:@model.depth
|
||||
for x in [0...@model.width]
|
||||
@_blockCache[x] ?= {}
|
||||
for y in [0...@model.height]
|
||||
@_blockCache[x][y] ?= {}
|
||||
for z in [0...@model.depth]
|
||||
@_blockCache[x][y][z] ?= null
|
||||
|
||||
for el in @$('.block')
|
||||
$el = $(el)
|
||||
@_blockCache[$el.data('x')][$el.data('y')][$el.data('z')] = $el
|
||||
|
||||
_refreshBlock: (x, y, z)->
|
||||
return unless @model?
|
||||
|
||||
stack = @model.getStackAt x, y, z
|
||||
$block = @_blockCache[x][y][z]
|
||||
position = @_computePosition x, y, z, hideUpperLayers:true
|
||||
move = false
|
||||
|
||||
if stack?
|
||||
itemDisplay = @_modPack.findItemDisplay stack.itemSlug
|
||||
|
||||
if not $block?
|
||||
$block = $("<img alt='#{itemDisplay.itemName}' class='block' />")
|
||||
$block.css position, top:-@IMAGE_SIZE, opacity:0.0
|
||||
@_registerHoverHandlers $block, y, itemDisplay
|
||||
@_imageLoader.load itemDisplay.iconUrl, $block
|
||||
@_blockCache[x][y][z] = $block
|
||||
@$el.append $block
|
||||
move = true
|
||||
else
|
||||
$block.attr 'src', itemDisplay.iconUrl
|
||||
move = true
|
||||
else if $block?
|
||||
$block.attr 'src', ''
|
||||
move = true
|
||||
|
||||
if move then _.delay (-> $block.css position), @_randomDropDelay()
|
||||
|
||||
_refreshAllBlocks: ->
|
||||
return unless @model?
|
||||
if @_endHovering? then @_endHovering()
|
||||
|
||||
for x in [0...@model.width]
|
||||
for y in [0...@model.height]
|
||||
for z in [0...@model.depth]
|
||||
@_refreshBlock(x, y, z)
|
||||
|
||||
_registerHoverHandlers: ($el, y, itemDisplay)->
|
||||
startHovering = =>
|
||||
return unless y is @_currentLayer
|
||||
zIndex = parseInt($el.css('z-index')) + 1
|
||||
zIndex = if _.isNaN(zIndex) then 0 else zIndex
|
||||
|
||||
@$outline.css opacity:1.0, top:$el.css('top'), left:$el.css('left'), 'z-index':zIndex
|
||||
@_onHovering itemDisplay
|
||||
|
||||
if @_hoverTimer? then clearTimeout @_hoverTimer
|
||||
@_hoverTimer = setTimeout (=> @_endHovering()), @HOVER_TIMEOUT
|
||||
|
||||
$el.hover startHovering, (->)
|
||||
@@ -0,0 +1,22 @@
|
||||
//-
|
||||
//- Crafting Guide - multiblock_viewer.jade
|
||||
//-
|
||||
//- Copyright (c) 2015 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__multiblock_viewer
|
||||
.left
|
||||
h3 Complete Materials
|
||||
.view__inventory.complete
|
||||
|
||||
h3 Current Layer
|
||||
.view__inventory.layer
|
||||
.right
|
||||
.buttons
|
||||
.button.back: .bezel: p back
|
||||
.button.next: .bezel: p next
|
||||
.view__multiblock
|
||||
.caption
|
||||
img
|
||||
p
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Crafting Guide - multiblock_viewer.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__multiblock_viewer {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
|
||||
h3 {
|
||||
margin-bottom: $size-margin-small;
|
||||
margin-left: 0;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: $size-margin-large;
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
border-right: 1px solid $color-gray-light;
|
||||
flex: 1 0 40%;
|
||||
|
||||
.view__inventory {
|
||||
margin-left: $size-margin-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 0 1 60%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.buttons {
|
||||
flex: 0 0 auto;
|
||||
margin-bottom: $size-margin-medium;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.button {
|
||||
flex: 1 1 50%;
|
||||
margin-left: $size-margin-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.view__multiblock {
|
||||
flex: 0 0 auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.caption {
|
||||
flex: 0 0 auto;
|
||||
margin-top: $size-margin-medium;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
margin-right : $size-margin-medium;
|
||||
height : $size-minecraft-block;
|
||||
width : $size-minecraft-block;
|
||||
|
||||
|
||||
border : none;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# Crafting Guide - multiblock_viewer_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
InventoryController = require '../../common/inventory/inventory_controller'
|
||||
MultiblockController = require './multiblock/multiblock_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class MultiblockViewerController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
options.templateName = 'item_page/multiblock_viewer'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_hoverTimer = null
|
||||
@_router = options.router
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onBackClicked: ->
|
||||
return if @$backButton.hasClass 'disabled'
|
||||
@multiblockController.goBackLayer()
|
||||
@refresh()
|
||||
|
||||
onNextClicked: ->
|
||||
return if @$nextButton.hasClass 'disabled'
|
||||
@multiblockController.goNextLayer()
|
||||
@refresh()
|
||||
|
||||
onBlockHovered: (itemDisplay)->
|
||||
if itemDisplay?
|
||||
@$captionText.html itemDisplay.itemName
|
||||
@_imageLoader.load itemDisplay.iconUrl, @$captionIcon
|
||||
else
|
||||
@$captionText.html ' '
|
||||
@$captionIcon.attr 'src', '/images/empty.png'
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@completeInventoryController = @addChild InventoryController, '.view__inventory.complete',
|
||||
editable: false
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@layerInventoryController = @addChild InventoryController, '.view__inventory.layer',
|
||||
editable: false
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@multiblockController = @addChild MultiblockController, '.view__multiblock',
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
onHovering: (itemDisplay)=> @onBlockHovered(itemDisplay)
|
||||
|
||||
@$backButton = @$('.button.back')
|
||||
@$nextButton = @$('.button.next')
|
||||
@$captionText = @$('.caption p')
|
||||
@$captionIcon = @$('.caption img')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
if @model?
|
||||
@completeInventoryController.model = @model.inventory
|
||||
@layerInventoryController.model = @model.getLayerInventory @multiblockController.currentLayer
|
||||
@multiblockController.model = @model
|
||||
|
||||
if @multiblockController.hasBackLayer()
|
||||
@$backButton.removeClass 'disabled'
|
||||
else
|
||||
@$backButton.addClass 'disabled'
|
||||
|
||||
if @multiblockController.hasNextLayer()
|
||||
@$nextButton.removeClass 'disabled'
|
||||
else
|
||||
@$nextButton.addClass 'disabled'
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
_.extend super,
|
||||
'click .button.back': 'onBackClicked'
|
||||
'click .button.next': 'onNextClicked'
|
||||
@@ -0,0 +1,19 @@
|
||||
//-
|
||||
//- Crafting Guide - recipe_detail.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__recipe_detail
|
||||
.input
|
||||
h3 Ingredients
|
||||
.view__inventory
|
||||
|
||||
.pattern
|
||||
.view__crafting_grid
|
||||
a.tool
|
||||
|
||||
.output
|
||||
h3 Produces
|
||||
.view__inventory
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Crafting Guide - recipe_detail.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__recipe_detail {
|
||||
display : flex;
|
||||
|
||||
.input, .output{
|
||||
flex : 0 1 33%;
|
||||
|
||||
h3 {
|
||||
margin-bottom : 1em;
|
||||
}
|
||||
|
||||
.view__inventory {
|
||||
margin-left: $size-margin-large;
|
||||
}
|
||||
}
|
||||
|
||||
.pattern {
|
||||
flex : 0 1 33%;
|
||||
|
||||
display : flex;
|
||||
flex-direction : column;
|
||||
align-items : center;
|
||||
|
||||
.tool {
|
||||
margin-top : $size-margin-small;
|
||||
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
text-align : center;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top : 1px solid $color-gray-light;
|
||||
margin-top : $size-margin-medium;
|
||||
padding-top : $size-margin-large;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#
|
||||
# Crafting Guide - recipe_detail_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
CraftingGridController = require '../../common/crafting_grid/crafting_grid_controller'
|
||||
Inventory = require '../../../models/game/inventory'
|
||||
InventoryController = require '../../common/inventory/inventory_controller'
|
||||
{StringBuilder} = require 'crafting-guide-common'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class RecipeDetailController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
options.templateName = 'item_page/recipe_detail'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@gridController = @addChild CraftingGridController, '.view__crafting_grid',
|
||||
imageLoader: @_imageLoader
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@inputController = @addChild InventoryController, '.input .view__inventory',
|
||||
editable: false
|
||||
imageLoader: @_imageLoader
|
||||
model: new Inventory
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@outputController = @addChild InventoryController, '.output .view__inventory',
|
||||
editable: false
|
||||
imageLoader: @_imageLoader
|
||||
model: new Inventory
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
|
||||
@$toolContainer = @$('.tool')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
if @model?
|
||||
@gridController.model = @model
|
||||
@_refreshInputs()
|
||||
@_refreshOutputs()
|
||||
@_refreshTools()
|
||||
@show()
|
||||
else
|
||||
@hide()
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Methods ########################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshInputs: ->
|
||||
inputs = @inputController.model
|
||||
inputs.clear()
|
||||
|
||||
if @model?
|
||||
for stack in @model.input
|
||||
inputs.add stack.itemSlug, @model.getQuantityRequired stack.itemSlug
|
||||
|
||||
|
||||
_refreshOutputs: ->
|
||||
outputs = @outputController.model
|
||||
outputs.clear()
|
||||
|
||||
if @model?
|
||||
for stack in @model.output
|
||||
outputs.add stack.itemSlug, @model.getQuantityProduced stack.itemSlug
|
||||
|
||||
_refreshTools: ->
|
||||
@$toolContainer.empty()
|
||||
return unless @model?
|
||||
|
||||
builder = new StringBuilder
|
||||
builder.loop @model.tools, delimiter:', ', onEach:(b, stack)=>
|
||||
display = @_modPack.findItemDisplay stack.itemSlug
|
||||
b.push "<a href=\"#{display.itemUrl}\">#{display.itemName}</a>"
|
||||
@$toolContainer.html builder.toString()
|
||||
@@ -0,0 +1,80 @@
|
||||
//-
|
||||
//- Crafting Guide - login_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__login_page
|
||||
.ready-to-login
|
||||
.left
|
||||
h1 Login with GitHub
|
||||
p.
|
||||
Use your GitHub credentials to login and make changes on Crafting Guide. No GitHub account? Don't
|
||||
worry, you can get one for free. <a class="read-more" href="#">read more...</a>
|
||||
|
||||
.expandable-container.read-more
|
||||
.content
|
||||
p.
|
||||
All changes you make on the site get submitted directly to our GitHub repo, and then published
|
||||
through the same automated deployment process as changes made by anyone else. That way, anyone
|
||||
can contribute easily: whether developer or end user.
|
||||
p.
|
||||
To log in, we redirect you to GitHub's site, and once you're signed in, they'll send you back
|
||||
here. We never see your credentials, and only get the minimum set of permissions necessary to
|
||||
submit changes to the Crafting Guide repo on your behalf.
|
||||
p.
|
||||
GitHub offers free accounts, so even if you don't already have one, you've got nothing to lose!
|
||||
|
||||
.actions
|
||||
.button.login: .bezel: p Login with GitHub
|
||||
|
||||
.right
|
||||
img(src="/images/github-login-large.png")
|
||||
|
||||
.fetching-token
|
||||
.left
|
||||
h1 Completing log in...
|
||||
p Hang on just a second while we finish logging you in...
|
||||
.actions
|
||||
img(src="/images/wait.gif")
|
||||
.right
|
||||
img(src="/images/github-login-large.png")
|
||||
|
||||
.invalid-callback
|
||||
.left
|
||||
h1 Oops!
|
||||
p.
|
||||
Sorry about that, but it looks like something went wrong with your GitHub login. Click the button below
|
||||
to try again, or use the Feedback button on the left to let us know, and we'll try to help.
|
||||
|
||||
.actions
|
||||
.button.login: .bezel: p Login with GitHub
|
||||
.right
|
||||
img(src="/images/github-login-large.png")
|
||||
|
||||
.logged-in
|
||||
.left
|
||||
h1 All set!
|
||||
p.
|
||||
You are logged in to CraftingGuide using the following GitHub account:
|
||||
.user
|
||||
.left
|
||||
img
|
||||
.right
|
||||
.name
|
||||
.email
|
||||
p.withRedirect.
|
||||
You will be redirected shortly.
|
||||
.actions
|
||||
.button.logout: .bezel: p Logout
|
||||
.right
|
||||
img.float-right(src="/images/github-login-large.png")
|
||||
|
||||
.server-down
|
||||
.left
|
||||
h1 Sorry!
|
||||
p.
|
||||
Our interactive editing service is down right now, but you can always submit a pull request over at the
|
||||
<a href="https://github.com/andrewminer/crafting-guide">Crafting Guide GitHub Repository</a>. We'll
|
||||
have the editing service back up soon!
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// Crafting Guide - login_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__login_page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
& > div {
|
||||
max-width: 75%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > .left {
|
||||
flex: 1 1 66%;
|
||||
margin-right: $size-margin-large;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
p {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
line-height: $font-size-medium * 1.25;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: $size-margin-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
padding: 0 $size-margin-xlarge;
|
||||
|
||||
p {
|
||||
font-family: $font-family-header;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.expandable-container {
|
||||
margin-top: $size-margin-medium;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 500ms;
|
||||
}
|
||||
|
||||
.user {
|
||||
margin: $size-margin-large;
|
||||
display: flex;
|
||||
|
||||
@include floating-panel;
|
||||
padding: $size-margin-medium;
|
||||
|
||||
.left {
|
||||
margin-right: $size-margin-small;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block * 2;
|
||||
width: $size-minecraft-block * 2;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
.name {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-xlarge;
|
||||
}
|
||||
|
||||
.email {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-small;
|
||||
margin-top: $size-margin-xsmall;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > .right {
|
||||
flex: 0 0 33%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#
|
||||
# Crafting Guide - login_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
PageController = require '../page_controller'
|
||||
GitHubUser = require '../../models/site/github_user'
|
||||
{CraftingGuideClient} = require 'crafting-guide-common'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class LoginPageController extends PageController
|
||||
|
||||
@::State =
|
||||
ReadyToLogin: 'ready-to-login'
|
||||
ServerDown: 'server-down'
|
||||
InvalidCallback: 'invalid-callback'
|
||||
FetchingToken: 'fetching-token'
|
||||
LoggedIn: 'logged-in'
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'login_page'
|
||||
|
||||
@_client = options.client
|
||||
@_params = options.params
|
||||
@_storage = options.storage
|
||||
|
||||
@_accessTokenLookupFailed = false
|
||||
|
||||
@_client.on c.event.change + ':status', => @tryRefresh()
|
||||
|
||||
super options
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onExpandReadMore: (event)->
|
||||
@$readMoreLink.css display: 'none'
|
||||
@$readMoreContainer.css 'max-height':@$readMoreContent.height()
|
||||
return false
|
||||
|
||||
onLoginButtonClicked: (event)->
|
||||
@_redirectToGitHub()
|
||||
return false
|
||||
|
||||
onLogoutButtonClicked: (event)->
|
||||
global.site.logout()
|
||||
return false
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getLoginSecurityToken: ->
|
||||
@_storage.load 'loginSecurityToken'
|
||||
|
||||
setLoginSecurityToken: (value)->
|
||||
@_storage.store 'loginSecurityToken', value
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
loginSecurityToken: {get:@prototype.getLoginSecurityToken, set:@prototype.setLoginSecurityToken}
|
||||
|
||||
# PageController Overrides #####################################################################
|
||||
|
||||
getTitle: ->
|
||||
return "Login"
|
||||
|
||||
# BaseController Methods #######################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$readMoreContainer = @$('.expandable-container.read-more')
|
||||
@$readMoreContent = @$('.read-more .content')
|
||||
@$readMoreLink = @$('a.read-more')
|
||||
@$redirectMessage = @$('.withRedirect')
|
||||
@$userAvatar = @$('.user img')
|
||||
@$userEmail = @$('.user .email')
|
||||
@$userName = @$('.user .name')
|
||||
|
||||
if (not @user?) and (@_params?.state?) and (@_params?.state is @loginSecurityToken)
|
||||
@_client.completeGitHubLogin code:@_params.code
|
||||
.then (response)=>
|
||||
attributes = response.json.data.user
|
||||
if attributes?
|
||||
global.site.user = new GitHubUser attributes
|
||||
global.site.resumeAfterLogin()
|
||||
.catch (error)=>
|
||||
logger.error "Failed to get access token: #{error}"
|
||||
@_accessTokenLookupFailed = true
|
||||
@refresh()
|
||||
.done()
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
state = @_computeState()
|
||||
@$el.find('> *').css display: 'none'
|
||||
@$(".#{state}").css display: ''
|
||||
|
||||
if state is @State.LoggedIn
|
||||
@$userAvatar.attr 'src', @user.avatarUrl
|
||||
@$userEmail.text @user.email
|
||||
@$userName.text @user.name
|
||||
|
||||
postLoginUrl = @_storage.load 'post-login-url'
|
||||
@$redirectMessage.css display:(if postLoginUrl? then 'none' else '')
|
||||
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a.read-more': 'onExpandReadMore'
|
||||
'click .button.login': 'onLoginButtonClicked'
|
||||
'click .button.logout': 'onLogoutButtonClicked'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_computeState: ->
|
||||
if @user? then return @State.LoggedIn
|
||||
|
||||
if @_client.status is CraftingGuideClient.Status.Down then return @State.ServerDown
|
||||
|
||||
if @_accessTokenLookupFailed then return @State.InvalidCallback
|
||||
|
||||
if @_params?.state?
|
||||
if @_params.state is @loginSecurityToken
|
||||
return @State.FetchingToken
|
||||
else
|
||||
return @State.InvalidCallback
|
||||
|
||||
return @State.ReadyToLogin
|
||||
|
||||
_compeleteLogin: (code)->
|
||||
@_client.completeGitHubLogin code:code
|
||||
.then (response)=>
|
||||
global.site.user = new GitHubUser response.json
|
||||
.catch (error)=>
|
||||
@_accessTokenLookupFailed = true
|
||||
@refresh()
|
||||
.done()
|
||||
|
||||
_redirectToGitHub: ->
|
||||
@loginSecurityToken = _.uuid()
|
||||
clientId = c.login.clientIds[global.env]
|
||||
window.location.href = c.login.authorizeUrl clientId:clientId, state:@loginSecurityToken
|
||||
@@ -0,0 +1,36 @@
|
||||
//-
|
||||
//- Crafting Guide - item_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__mod_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.about
|
||||
.left
|
||||
img
|
||||
.center
|
||||
.title
|
||||
.author
|
||||
.description
|
||||
.version
|
||||
span in my mod pack
|
||||
select
|
||||
.warning.
|
||||
<b>PLEASE NOTE:</b> By selecting "no", crafting plans, search, and other features will ignore items
|
||||
and recipes from this mod.
|
||||
.right
|
||||
p Official Links:
|
||||
a.homePage project page
|
||||
a.documentation documentation
|
||||
a.download download
|
||||
|
||||
section.tutorials
|
||||
h2 Tutorials
|
||||
.panel
|
||||
|
||||
.itemGroups
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// Crafting Guide - item_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__mod_page {
|
||||
@include layout-body-with-sidebar;
|
||||
|
||||
.about {
|
||||
margin-bottom: $size-margin-xlarge;
|
||||
|
||||
display: flex;
|
||||
|
||||
.left {
|
||||
flex: 0 0 auto;
|
||||
margin-right: $size-margin-large;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block * 5;
|
||||
width: $size-minecraft-block * 5;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
flex: 1 1 100%;
|
||||
margin-right: $size-margin-large;
|
||||
|
||||
.title {
|
||||
font-family: $font-family-header;
|
||||
font-size: $font-size-xxlarge;
|
||||
}
|
||||
|
||||
.author {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
|
||||
&:before {
|
||||
content: "by: "
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
line-height: $font-size-medium * 1.25;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-top: $size-margin-large;
|
||||
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
|
||||
span {
|
||||
flex: 0 0 auto;
|
||||
|
||||
margin-right: $size-margin-medium;
|
||||
}
|
||||
|
||||
select {
|
||||
flex: 1 1 100%;
|
||||
|
||||
font-family: $font-family-control;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.warning {
|
||||
margin-top: $size-margin-medium;
|
||||
|
||||
color: $color-error;
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-small;
|
||||
line-height: $font-size-small * 1.25;
|
||||
|
||||
b {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 0 0 auto;
|
||||
@include layout-link-list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
#
|
||||
# Crafting Guide - mod_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# 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'
|
||||
Mod = require '../../models/game/mod'
|
||||
TutorialController = require './tutorial/tutorial_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class ModPageController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
if not options.router? then throw new Error 'options.router is required'
|
||||
options.templateName = 'mod_page'
|
||||
super options
|
||||
|
||||
@_imageLoader = options.imageLoader
|
||||
@_modPack = options.modPack
|
||||
@_router = options.router
|
||||
|
||||
# Event Methods ################################################################################
|
||||
|
||||
onVersionChanged: ->
|
||||
@model.activeVersion = @$versionSelector.val()
|
||||
return false
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
|
||||
effectiveModVersion:
|
||||
get: ->
|
||||
modVersion = @model.activeModVersion
|
||||
modVersion ?= @model.getModVersion Mod.Version.Latest
|
||||
modVersion.fetch() if modVersion?
|
||||
return modVersion
|
||||
|
||||
# PageController Overrides #####################################################################
|
||||
|
||||
getBreadcrumbs: ->
|
||||
return [
|
||||
$('<a href="/browse">Browse</a>')
|
||||
$("<b>#{@model.name}</b>")
|
||||
]
|
||||
|
||||
getTitle: ->
|
||||
return @model.name
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model: 'skyscraper'
|
||||
|
||||
@$author = @$('.about .author')
|
||||
@$description = @$('.about .description')
|
||||
@$documentationLink = @$('.about .documentation')
|
||||
@$downloadLink = @$('.about .download')
|
||||
@$homePageLink = @$('.about .homePage')
|
||||
@$itemGroups = @$(".itemGroups")
|
||||
@$logo = @$('.about img')
|
||||
@$title = @$('.about .title')
|
||||
@$tutorialsContainer = @$('section.tutorials .panel')
|
||||
@$tutorialsSection = @$('section.tutorials')
|
||||
@$versionSelector = @$('.version select')
|
||||
@$warning = @$('.warning')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
@_refreshAboutBlock()
|
||||
@_refreshItemGroups()
|
||||
@_refreshTutorials()
|
||||
@_refreshVersionSelector()
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'change .version select': 'onVersionChanged'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshAboutBlock: ->
|
||||
if @model?
|
||||
@$author.text @model.author
|
||||
@$description.text @model.description
|
||||
@$documentationLink.attr 'href', @model.documentationUrl
|
||||
@$downloadLink.attr 'href', @model.downloadUrl
|
||||
@$homePageLink.attr 'href', @model.homePageUrl
|
||||
@$logo.attr 'src', c.url.modIcon modSlug:@model.slug
|
||||
@$title.text @model.name
|
||||
else
|
||||
@$author.text ''
|
||||
@$description.text ''
|
||||
@$documentationLink.attr 'href', ''
|
||||
@$downloadLink.attr 'href', ''
|
||||
@$homePageLink.attr 'href', ''
|
||||
@$logo.attr 'src', ''
|
||||
@$title.text ''
|
||||
|
||||
_refreshItemGroups: ->
|
||||
@_groupControllers ?= []
|
||||
groupIndex = 0
|
||||
modVersion = @effectiveModVersion
|
||||
|
||||
if modVersion?
|
||||
modVersion.eachGroup (group)=>
|
||||
controller = @_groupControllers[groupIndex]
|
||||
items = modVersion.allItemsInGroup group
|
||||
if not controller?
|
||||
controller = new ItemGroupController
|
||||
imageLoader: @_imageLoader
|
||||
model: items
|
||||
modPack: @_modPack
|
||||
router: @_router
|
||||
title: if group is Item.Group.Other then 'Items' else group
|
||||
|
||||
@_groupControllers.push controller
|
||||
@$itemGroups.append controller.$el
|
||||
controller.render()
|
||||
else
|
||||
controller.modVersion = modVersion
|
||||
controller.model = items
|
||||
controller.refresh()
|
||||
groupIndex++
|
||||
|
||||
while @_groupControllers.length > groupIndex + 1
|
||||
@_groupControllers.pop().remove()
|
||||
|
||||
_refreshTutorials: ->
|
||||
@_tutorialControllers ?= []
|
||||
index = 0
|
||||
tutorials = @model.tutorials
|
||||
|
||||
if tutorials.length > 0
|
||||
for tutorial in tutorials
|
||||
controller = @_tutorialControllers[index]
|
||||
if not controller?
|
||||
controller = new TutorialController model: tutorial, router: @_router
|
||||
@_tutorialControllers.push controller
|
||||
@$tutorialsContainer.append controller.$el
|
||||
controller.render()
|
||||
else
|
||||
controller.model = tutorial
|
||||
|
||||
index += 1
|
||||
|
||||
while @_tutorialControllers.length > index
|
||||
@_tutorialControllers.pop().remove()
|
||||
|
||||
@show @$tutorialsSection
|
||||
else
|
||||
@hide @$tutorialsSection
|
||||
|
||||
_refreshVersionSelector: ->
|
||||
modVersions = @model.modVersions
|
||||
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
|
||||
$option.attr 'selected', 'true'
|
||||
@$versionSelector.append $option
|
||||
@$versionSelector.css display:''
|
||||
@@ -0,0 +1,11 @@
|
||||
//-
|
||||
//- Crafting Guide - tutorial.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__tutorial
|
||||
a
|
||||
img
|
||||
p
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Crafting Guide - tutorial.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__tutorial {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
a {
|
||||
flex: 0 0 auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
height: $size-minecraft-block * 4;
|
||||
margin-bottom: $size-margin-xsmall;
|
||||
width: $size-minecraft-block * 4;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: $font-family-text;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Crafting Guide - tutorial_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require '../../base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class TutorialController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.model? then throw new Error 'options.model is required'
|
||||
options.templateName = 'mod_page/tutorial'
|
||||
super options
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@$icon = @$('img')
|
||||
@$link = @$('a')
|
||||
@$title = @$('p')
|
||||
super
|
||||
|
||||
refresh: ->
|
||||
templateData = modSlug:@model.modSlug, tutorialSlug:@model.slug
|
||||
@$icon.attr 'src', c.url.tutorialIcon templateData
|
||||
@$link.attr 'href', c.url.tutorial templateData
|
||||
@$title.html @model.name
|
||||
super
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
return _.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
@@ -0,0 +1,162 @@
|
||||
//-
|
||||
//- Crafting Guide - news_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.content.view__news_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
section
|
||||
h3 2015-02-11
|
||||
.panel
|
||||
p.
|
||||
I've been using <a href="/browse/modular_powersuits">Modular PowerSuits</a> myself lately, and I
|
||||
noticed that the newest version (finally!) comes with default recipes. So, I've updated
|
||||
Crafting Guide to support both the newest vanilla recipes as well as those from Thermal
|
||||
Expansion. You can switch back and forth on the <a href="/configure">Configure</a> page.
|
||||
|
||||
section
|
||||
h3 2015-02-09
|
||||
.panel
|
||||
p.
|
||||
Back before the winter holidays, the extremely patient <a href="https://github.com/tdallmann">
|
||||
tdallmann</a> completed support for <a href="/browse/hydraulicraft/">Hydraulicraft</a>, and I'm
|
||||
afraid I'm only now getting it up on the site! It looks like a neat mod based all around the
|
||||
science of hydraulics with a nicely stepped set of devices to be made at each level. Many thanks
|
||||
to tddallmann for adding it to Crafting Guide!
|
||||
p.
|
||||
I also received a submission for adding <a href="/browse/opencomputers">OpenComputers</a> from
|
||||
<a href="https://github.com/yut23">yut23</a>. Should be a big help to those aspiring Minecraft
|
||||
programmers who want a bit more challege than is provided by <a href="/browse/computercraft">
|
||||
ComputerCraft</a>! Thanks, yut23!
|
||||
|
||||
section
|
||||
h3 2015-11-25
|
||||
.panel
|
||||
p.
|
||||
With many thanks to <a href="https://github.com/pkmnfrk">Mike Caron</a>, I'm pleased to announce
|
||||
support for <a href="/browse/storage_drawers">Storage Drawers</a>!
|
||||
|
||||
section
|
||||
h3 2015-12-13
|
||||
.panel
|
||||
p.
|
||||
I'm super excited to announce that I've added full multi-block support for Crafting Guide! For
|
||||
example, check out a <a href="/browse/big_reactors/5_core_passive_reactor">5-Core Reactor</a>
|
||||
from Big Reactors, or the <a href="/browse/railcraft/steel_tank_5x5x5">5x5x5 Steel Tank</a> from
|
||||
Railcraft. I'm really pleased with how it turned out!
|
||||
|
||||
section
|
||||
h3 2015-11-17
|
||||
.panel
|
||||
p.
|
||||
And today, it's <a href="/browse/extra_cells">Extra Cells</a>!
|
||||
|
||||
section
|
||||
h3 2015-11-16
|
||||
.panel
|
||||
p.
|
||||
It's a small one, but also highly requested... Here's <a
|
||||
href="/browse/computercraft">ComputerCraft</a>!
|
||||
|
||||
section
|
||||
h3 2015-11-14
|
||||
.panel
|
||||
p.
|
||||
One more small one over the weekend... <a href="/browse/advanced_solar_panels">Advanced Solar
|
||||
Panels</a> has been added to crafting guide!
|
||||
|
||||
section
|
||||
h3 2015-11-14
|
||||
.panel
|
||||
p.
|
||||
For a long while now, <a href="/browse/galacticraft">Galacticraft</a> has been the most
|
||||
requested mod, and it's finally here! Enjoy!
|
||||
|
||||
section
|
||||
h3 2015-11-10
|
||||
.panel
|
||||
p.
|
||||
Remember me? They guy who runs the site. Yeah, I'm back, and with big news! The site now has
|
||||
a completely new crafting algorithm. Instead of just taking the first crafting plan it comes up
|
||||
with, it tries to compute every possible plan, rates them, and then picks the best one. This
|
||||
also clears up a lot of bugs related to the old algorithm. The one drawback is that some items
|
||||
can take a bit longer to compute than with the old way, but I think you'll find it's worth it!
|
||||
p.
|
||||
On a personal note, I think it's clear that I don't have nearly as much time to dedicate to the
|
||||
site as I used to. That's mostly because I started a new job which keeps me away from working
|
||||
on the site so much. I hope to be able to get into a more regular pace now things my personal
|
||||
life has settled down again.
|
||||
|
||||
section
|
||||
h3 2015-07-25
|
||||
.panel
|
||||
p.
|
||||
You may have noticed things have been very quiet with the site for the past month... well I'm
|
||||
afraid that's because I haven't been working on it. Instead, I've been hunting for a new job.
|
||||
Now that I've got that all squared away (I'll be starting at Looker in Santa Cruz on Monday),
|
||||
I'll have nights and weekends to continue working on the site. Thanks for being patient!
|
||||
|
||||
section
|
||||
h3 2015-06-12
|
||||
.panel
|
||||
p.
|
||||
Today I launched the second major part of the item description editor: image uploads! As you
|
||||
enter your description, use the markdown notation for an image <tt>  </tt> and a
|
||||
empty image preview will show up underneath the editor. Click the button to select an image from
|
||||
your computer. Just be sure to keep the images to a reasonable size (< 750kB, and < 740px by
|
||||
600px).
|
||||
|
||||
section
|
||||
h3 2015-05-13
|
||||
.panel
|
||||
p.
|
||||
As of today, you can now add and edit item descriptions right on Crafting Guide. This feature is
|
||||
starting small with just item descriptions, but eventually all the content on the site will be
|
||||
editable. All you need is a (free) GitHub account, and you're ready to go!
|
||||
p.
|
||||
For those more comfortable with a regular text editor, all the content is still in GitHub under
|
||||
the <a href="https://github.com/andrewminer/crafting-guide-data">crafting-guide-data</a> repo.
|
||||
Just follow the instructions, and you can submit content via pull request instead of using the
|
||||
website's editor.
|
||||
|
||||
section
|
||||
h3 2015-04-30
|
||||
.panel
|
||||
p.
|
||||
I've been working on a big new feature for a while now, but I didn't want to leave everyone
|
||||
without anything new for so long... So, I've just finished adding <a
|
||||
href="/browse/minefactory_reloaded">MineFactory Reloaded</a>! Enjoy!
|
||||
|
||||
section
|
||||
h3 2015-04-20
|
||||
.panel
|
||||
p.
|
||||
Okay... big changes on the crafting page! Instead of having four little boxes for the various
|
||||
parts of the crafting plan, the new page shows all the steps as one long list. In addition, you
|
||||
get a lot more control over the plan. Now, you can even mark individual steps as complete to get
|
||||
a new crafting plan which doesn't include those items.
|
||||
p.
|
||||
As an example, let's say you're thinking about upgrading your <a
|
||||
href="/browse/simply_jetpacks/reinforced_jetpack">Reinforced Jetpack</a> to a shiny new <a
|
||||
href="/browse/simply_jetpacks/resonant_jetpack">Resonant Jetpack</a>, you can just mark off the
|
||||
step for the Reinforced Jetpack, and get only the materials and steps for doing the upgrade.
|
||||
I'm really pleased with the new crafting page, and I think you're going to love it!
|
||||
|
||||
section
|
||||
h3 2015-04-11
|
||||
.panel
|
||||
p.
|
||||
I've just finished adding <a href="/browse/simply_jetpacks">Simply Jetpacks</a> by Tonius. It
|
||||
seems like a simple mod on the surface, but what with all the upgrades and technology tiers, it
|
||||
takes an amazing <a href="/craft/flux_infused_jetplate">87 steps</a> to make the highest-tier
|
||||
jetpack!
|
||||
p.
|
||||
I've also added <a href="/browse/solar_flux">Solar Flux</a> by Nauktis. Again, another
|
||||
deceptively simple mod, but the <a href="/browse/solar_flux/solar_panel_vi/">most powerful solar
|
||||
panel</a> kicks out an impressive 4096 RF/t! I just hope your mine has been productive... among
|
||||
other things, you'll need 45 Diamonds, 110 Gold Ore, 1568 Copper Ore, and 6023 Iron Ore!
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Crafting Guide - news_page.scss
|
||||
//
|
||||
// Copyright © 2014-2016 by Redwood Labs
|
||||
// All rights reserved.
|
||||
//
|
||||
|
||||
.view__news_page {
|
||||
@include layout-body-with-sidebar;
|
||||
|
||||
section {
|
||||
&:not(:first-child) {
|
||||
margin-top : $size-margin-large;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom : $size-margin-small;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family : $font-family-text;
|
||||
font-size : $font-size-medium;
|
||||
line-height : $font-size-medium * 1.5;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom : $size-margin-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#
|
||||
# Crafting Guide - news_page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
AdsenseController = require '../common/adsense/adsense_controller'
|
||||
PageController = require '../page_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class NewsPageController extends PageController
|
||||
|
||||
constructor: (options={})->
|
||||
options.templateName = 'news_page'
|
||||
super options
|
||||
|
||||
# PageController Overrides #####################################################################
|
||||
|
||||
getTitle: ->
|
||||
return "News"
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
onDidRender: ->
|
||||
@_adsenseController = @addChild AdsenseController, '.view__adsense', model:'skyscraper'
|
||||
|
||||
# Backbone.View Overrides ######################################################################
|
||||
|
||||
events: ->
|
||||
_.extend super,
|
||||
'click a': 'routeLinkClick'
|
||||
@@ -0,0 +1,54 @@
|
||||
#
|
||||
# Crafting Guide - page_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require './base_controller'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class PageController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
super options
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
getBreadcrumbs: ->
|
||||
return []
|
||||
|
||||
getMetaDescription: ->
|
||||
# subclasses should override this to return the page-specific text for the meta description tag
|
||||
return null
|
||||
|
||||
getTitle: ->
|
||||
# subclasses should override this to return the page-specific portion of the title
|
||||
return null
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
refresh: ->
|
||||
@_refreshMetaDescription()
|
||||
@_refreshTitle()
|
||||
super
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_refreshMetaDescription: ->
|
||||
description = @getMetaDescription()
|
||||
description ?= ''
|
||||
|
||||
if description.length > 0
|
||||
$('meta[name="description"]').remove()
|
||||
$('head').append "<meta name=\"description\" content=\"#{description}\">"
|
||||
|
||||
_refreshTitle: ->
|
||||
title = @getTitle()
|
||||
title = if title? then title.trim() else ''
|
||||
|
||||
if title.length > 0
|
||||
$('title').html c.text.titleWithText text:title
|
||||
else
|
||||
$('title').html c.text.title()
|
||||
@@ -0,0 +1,169 @@
|
||||
#
|
||||
# Crafting Guide - router.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BrowsePageController = require './browse_page/browse_page_controller'
|
||||
CraftPageController = require './craft_page/craft_page_controller'
|
||||
ItemPageController = require './item_page/item_page_controller'
|
||||
ItemSlug = require '../models/game/item_slug'
|
||||
LoginPageController = require './login_page/login_page_controller'
|
||||
ModPageController = require './mod_page/mod_page_controller'
|
||||
NewsPageController = require './news_page/news_page_controller'
|
||||
TutorialPageController = require './tutorial_page/tutorial_page_controller'
|
||||
UrlParams = require './url_params'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class Router extends Backbone.Router
|
||||
|
||||
constructor: (siteController, options={})->
|
||||
if not siteController? then throw new Error "siteController is required"
|
||||
|
||||
@_siteController = siteController
|
||||
@_lastReported = null
|
||||
|
||||
super options
|
||||
|
||||
# Backbone.Router Overrides ####################################################################
|
||||
|
||||
navigate: ->
|
||||
super
|
||||
@_recordPageView()
|
||||
|
||||
routes:
|
||||
'': 'route__home'
|
||||
'/': 'route__home'
|
||||
'/index.html': 'route__home'
|
||||
|
||||
'browse': 'route__browse'
|
||||
'browse/': 'route__browse'
|
||||
'browse/index.html': 'route__browse'
|
||||
|
||||
'browse/:modSlug': 'route__browseMod'
|
||||
'browse/:modSlug/': 'route__browseMod'
|
||||
'browse/:modSlug/index.html': 'route__browseMod'
|
||||
|
||||
'browse/:modSlug/:itemSlug': 'route__browseModItem'
|
||||
'browse/:modSlug/:itemSlug/': 'route__browseModItem'
|
||||
'browse/:modSlug/:itemSlug/index.html': 'route__browseModItem'
|
||||
|
||||
'browse/:modSlug/tutorials/:tutorialSlug': 'route__browseTutorial'
|
||||
'browse/:modSlug/tutorials/:tutorialSlug/': 'route__browseTutorial'
|
||||
'browse/:modSlug/tutorials/:tutorialSlug/index.html': 'route__browseTutorial'
|
||||
|
||||
'configure': 'route__configure'
|
||||
'configure/': 'route__configure'
|
||||
'configure/index.html': 'route__configure'
|
||||
|
||||
'craft': 'route__craft'
|
||||
'craft/': 'route__craft'
|
||||
'craft/index.html': 'route__craft'
|
||||
|
||||
'craft/:text': 'route__craft'
|
||||
'craft/:text/': 'route__craft'
|
||||
'craft/:text/index.html': 'route__craft'
|
||||
|
||||
'login': 'route__login'
|
||||
'login/': 'route__login'
|
||||
'login/index.html': 'route__login'
|
||||
|
||||
'news': 'route__news'
|
||||
'news/': 'route__news'
|
||||
'news/index.html': 'route__news'
|
||||
|
||||
'item/:itemSlug': 'deprecated__item'
|
||||
'crafting/(:text)': 'deprecated__crafting'
|
||||
'mod/:modSlug': 'deprecated__mod'
|
||||
'mod/:modSlug/:itemSlug': 'deprecated__modItem'
|
||||
|
||||
# Route Methods ################################################################################
|
||||
|
||||
route__home: ->
|
||||
params = new UrlParams recipeName:{type:'string'}, count:{type:'integer'}
|
||||
if params.recipeName?
|
||||
@deprecated__v1_root params
|
||||
return
|
||||
|
||||
@route__craft()
|
||||
|
||||
route__browse: ->
|
||||
@_siteController.setPage 'browse', new BrowsePageController @_makeOptions {}
|
||||
|
||||
route__browseMod: (modSlug)->
|
||||
controller = new ModPageController @_makeOptions()
|
||||
controller.model = @_siteController.modPack.getMod modSlug
|
||||
@_siteController.setPage 'browseMod', controller
|
||||
|
||||
route__browseModItem: (modSlug, itemSlug)->
|
||||
slug = new ItemSlug modSlug, itemSlug
|
||||
controller = new ItemPageController @_makeOptions {itemSlug:slug}
|
||||
@_siteController.setPage 'browseModItem', controller
|
||||
|
||||
route__browseTutorial: (modSlug, tutorialSlug)->
|
||||
controller = new TutorialPageController @_makeOptions {modSlug:modSlug, tutorialSlug:tutorialSlug}
|
||||
@_siteController.setPage 'browseTutorial', controller
|
||||
|
||||
route__configure: ->
|
||||
@route__craft()
|
||||
|
||||
route__craft: (text)->
|
||||
controller = new CraftPageController @_makeOptions {}
|
||||
controller.model.params = inventoryText:text
|
||||
@_siteController.setPage 'craft', controller
|
||||
|
||||
route__login: ->
|
||||
params = new UrlParams code:{type:'string'}, state:{type:'string'}
|
||||
controller = new LoginPageController @_makeOptions {params:params}
|
||||
@_siteController.setPage 'login', controller
|
||||
|
||||
route__news: ->
|
||||
controller = new NewsPageController @_makeOptions {}
|
||||
@_siteController.setPage 'news', controller
|
||||
|
||||
# Deprecated Route Methods #####################################################################
|
||||
|
||||
deprecated__crafting: (text)->
|
||||
controller = new CraftPageController @_makeOptions {}
|
||||
controller.model.params = inventoryText:text
|
||||
@_siteController.setPage 'crafting', controller
|
||||
|
||||
deprecated__item: (itemSlug)->
|
||||
controller = new ItemPageController @_makeOptions {itemSlug:ItemSlug.slugify(itemSlug)}
|
||||
@_siteController.setPage 'item', controller
|
||||
|
||||
deprecated__modItem: (modSlug, itemSlug)->
|
||||
slug = new ItemSlug modSlug, itemSlug
|
||||
controller = new ItemPageController @_makeOptions {itemSlug:slug}
|
||||
@_siteController.setPage 'item', controller
|
||||
|
||||
deprecated__mod: (modSlug)->
|
||||
controller = new ModPageController @_makeOptions {}
|
||||
controller.model = @_siteController.modPack.getMod modSlug
|
||||
@_siteController.setPage 'mod', controller
|
||||
|
||||
deprecated__v1_root: (params)->
|
||||
text = ''
|
||||
if params.recipeName?
|
||||
if params.count? and params.count > 1
|
||||
text = "#{params.count}.#{_.slugify(params.recipeName)}"
|
||||
else
|
||||
text = _.slugify params.recipeName
|
||||
|
||||
@navigate c.url.crafting(inventoryText:text), trigger:true
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_makeOptions: (options={})->
|
||||
return _.extend options, @_siteController.controllerOptions
|
||||
|
||||
_recordPageView: ->
|
||||
pathname = window.location.pathname
|
||||
|
||||
if global.env is 'production' and ga?
|
||||
logger.info -> "Recording GA page view: #{pathname}"
|
||||
ga 'send', 'pageview', pathname
|
||||
else
|
||||
logger.info -> "Suppressing GA page view: #{pathname}"
|
||||
@@ -0,0 +1,163 @@
|
||||
#
|
||||
# Crafting Guide - site_controller.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
BaseController = require './base_controller'
|
||||
FeedbackController = require './feedback/feedback_controller'
|
||||
FooterController = require './footer/footer_controller'
|
||||
GitHubUser = require '../models/site/github_user'
|
||||
HeaderController = require './header/header_controller'
|
||||
ImageLoader = require './image_loader'
|
||||
Mod = require '../models/game/mod'
|
||||
ModPack = require '../models/game/mod_pack'
|
||||
Router = require './router'
|
||||
Storage = require './storage'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class SiteController extends BaseController
|
||||
|
||||
constructor: (options={})->
|
||||
options.el = 'html'
|
||||
super options
|
||||
|
||||
@client = options.client
|
||||
@imageLoader = new ImageLoader defaultUrl:'/images/unknown.png'
|
||||
@modPack = new ModPack
|
||||
@router = new Router this
|
||||
@storage = new Storage storage:global.localStorage
|
||||
|
||||
@_currentPage = null
|
||||
@_currentPageController = null
|
||||
@_user = null
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
loadDefaultModPack: ->
|
||||
makeResponder = (m)-> return ->
|
||||
m.activeModVersion.fetch() if m.activeModVersion?
|
||||
|
||||
for modSlug, modData of c.defaultMods
|
||||
mod = new Mod slug:modSlug
|
||||
mod.on c.event.change + ':activeModVersion', makeResponder mod
|
||||
@storage.register "mod:#{mod.slug}", mod, 'activeVersion', modData.defaultVersion
|
||||
mod.fetch()
|
||||
|
||||
@modPack.addMod mod
|
||||
|
||||
if global.env isnt 'prerender'
|
||||
@modPack.once c.event.sync, =>
|
||||
@$pageContent.css 'display', ''
|
||||
@$pageContentLoading.css 'display', 'none'
|
||||
|
||||
loadCurrentUser: ->
|
||||
@client.fetchCurrentUser()
|
||||
.then (response)=>
|
||||
userData = response.json?.data?.user
|
||||
@user = new GitHubUser userData if userData?
|
||||
.catch (error)=>
|
||||
if error?.response?.statusCode isnt 401
|
||||
logger.error "Failed to get current user: #{error}"
|
||||
else
|
||||
logger.info "User is not logged in"
|
||||
.done()
|
||||
|
||||
login: ->
|
||||
@storage.store 'post-login-url', window.location.pathname
|
||||
@router.navigate c.url.login(), trigger:true
|
||||
|
||||
logout: ->
|
||||
@client.logout()
|
||||
.then =>
|
||||
@storage.store 'loginSecurityToken', null
|
||||
@user = null
|
||||
.catch (error)->
|
||||
logger.error -> "Failed to log out: #{error}"
|
||||
.done()
|
||||
|
||||
resumeAfterLogin: ->
|
||||
url = @storage.load 'post-login-url'
|
||||
url = if (not url? or url is 'null') then null else url
|
||||
if url? then @router.navigate url, trigger:true
|
||||
@storage.store 'post-login-url', null
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getControllerOptions: ->
|
||||
client: @client
|
||||
imageLoader: @imageLoader
|
||||
modPack: @modPack
|
||||
router: @router
|
||||
storage: @storage
|
||||
enterFeedback: (message)=> @_feedbackController.enterFeedback(message)
|
||||
user: @user
|
||||
|
||||
getUser: ->
|
||||
return @_user
|
||||
|
||||
setUser: (newUser)->
|
||||
oldUser = @_user
|
||||
return if newUser is oldUser
|
||||
|
||||
@_user = newUser
|
||||
logger.info -> "User changed to: #{newUser?.name}"
|
||||
|
||||
@trigger c.event.change + ':user', this, oldUser, newUser
|
||||
@trigger c.event.change, this
|
||||
|
||||
if @_currentPageController? then @_currentPageController.user = newUser
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
controllerOptions: { get:@::getControllerOptions }
|
||||
user: { get:@::getUser, set:@::setUser }
|
||||
|
||||
# BaseController Overrides #####################################################################
|
||||
|
||||
render: ->
|
||||
@_footerController = @addChild FooterController, '.view__footer', @controllerOptions
|
||||
@_headerController = @addChild HeaderController, '.view__header', @controllerOptions
|
||||
|
||||
@$pageContent = @$('.page > .content')
|
||||
@$pageContentLoading = @$('.page > .content-loading')
|
||||
|
||||
@_feedbackController = @addChild FeedbackController, '.view__feedback'
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
setPage: (page, controller)->
|
||||
return if @_currentPageController is controller
|
||||
|
||||
logger.info -> "changing to page controller: #{controller.constructor.name}"
|
||||
|
||||
if @_currentPageController
|
||||
@_currentPageController.unrender()
|
||||
@_resetGlobals()
|
||||
|
||||
@_currentPage = page
|
||||
@_currentPageController = controller
|
||||
|
||||
$pageContent = $('.page > .content')
|
||||
$pageContent.removeClass()
|
||||
$pageContent.addClass 'content'
|
||||
|
||||
window.scrollTo 0, 0
|
||||
|
||||
@_headerController.model = controller: controller
|
||||
|
||||
@_currentPageController.user = @user
|
||||
@_currentPageController.onWillShow()
|
||||
@_currentPageController.$el = $pageContent
|
||||
@_currentPageController.render()
|
||||
|
||||
_resetGlobals: ->
|
||||
if global.env in c.productionEnvs
|
||||
for key, value of global
|
||||
if key.indexOf('google') isnt -1
|
||||
delete global[key]
|
||||
|
||||
delete global.adsByGoogle
|
||||
$('script[src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"]').remove()
|
||||
$('body').append('<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>')
|
||||
@@ -0,0 +1,62 @@
|
||||
#
|
||||
# Crafting Guide - storage.coffee
|
||||
#
|
||||
# Copyright © 2014-2016 by Redwood Labs
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class Storage
|
||||
|
||||
constructor: (options={})->
|
||||
options.storage ?= window.sessionStorage
|
||||
|
||||
@storage = options.storage
|
||||
@_models = {}
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
load: (key)->
|
||||
value = @storage.getItem key
|
||||
logger.verbose -> "loaded #{value} from #{key}"
|
||||
return value
|
||||
|
||||
store: (key, value)->
|
||||
@storage.setItem key, value
|
||||
logger.verbose -> "stored #{value} into #{key}"
|
||||
|
||||
register: (key, model, property, defaultValue=null)->
|
||||
modelData = @_models[key]
|
||||
if not modelData? then modelData = @_models[key] = model:model, properties:{}
|
||||
makeStoreMethod = (k, m, p)=> return => @_storeProperty(k, m, p)
|
||||
|
||||
return if modelData.properties[property]?
|
||||
modelData.properties[property] = property
|
||||
|
||||
@_loadProperty key, model, property, defaultValue
|
||||
model.on "change:#{property}", makeStoreMethod(key, model, property)
|
||||
|
||||
unregister: (key, property)->
|
||||
modelData = @_models[key]
|
||||
return unless modelData?
|
||||
delete modelData.properties[key]
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_loadProperty: (key, model, property, defaultValue=null)->
|
||||
loadedValue = @load "#{key}:#{property}"
|
||||
if loadedValue?
|
||||
value = JSON.parse loadedValue
|
||||
else
|
||||
value = defaultValue
|
||||
|
||||
return unless value?
|
||||
model[property] = value
|
||||
|
||||
if value? and not loadedValue?
|
||||
@_storeProperty key, model, property
|
||||
|
||||
_storeProperty: (key, model, property)->
|
||||
value = JSON.stringify model[property]
|
||||
@store "#{key}:#{property}", value
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
|
||||
//-
|
||||
//- Crafting Guide - tutorial_page.jade
|
||||
//-
|
||||
//- Copyright © 2014-2016 by Redwood Labs
|
||||
//- All rights reserved.
|
||||
//-
|
||||
|
||||
.view__tutorial_page
|
||||
.left
|
||||
.view__adsense
|
||||
|
||||
.right
|
||||
.about
|
||||
.left
|
||||
img
|
||||
.right
|
||||
.title
|
||||
.sourceMod a tutorial for <a></a>
|
||||
.officialLink from <a></a>
|
||||
|
||||
.tutorial-sections
|
||||
|
||||
section.videos
|
||||
h2
|
||||
.panel
|
||||
.video-container
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user