Refactor website with new design & structure
This commit is contained in:
@@ -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"
|
||||
Reference in New Issue
Block a user