#
# Crafting Guide - item_page_controller.coffee
#
# Copyright © 2014-2017 by Redwood Labs
# All rights reserved.
#
EditableFile = require "../../models/site/editable_file"
{Item} = require("crafting-guide-common").models
ItemDisplay = require "../../models/site/item_display"
ItemGroupController = require "../common/item_group/item_group_controller"
ItemPage = require "../../models/site/item_page"
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"
w = require "when"
########################################################################################################################
module.exports = class ItemPageController extends PageController
@::FILE_UPLOAD_DELAY = 250
constructor: (options={})->
if not options.model?.constructor is ItemPage then throw new Error "options.model must be an ItemPage instance"
if not options.client? then throw new Error "options.client 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.templateName ?= "item_page"
super options
@_client = options.client
@_descriptionFile = null
@_enterFeedback = options.enterFeedback
@_imageLoader = options.imageLoader
@_modPack = options.modPack
@_router = options.router
@_triggerEditing = options.login
# Event Methods ################################################################################
craftingPlanButtonClicked: ->
tracker.trackEvent c.tracking.category.craft, "view-crafting-plan", @model.item.slug
display = @_modPack.findItemDisplay @model.item.slug
@_router.navigate display.craftingUrl, trigger:true
return false
# PageController Overrides #####################################################################
getBreadcrumbs: ->
return [
$("Browse")
$("#{@model.display.modName}")
$("#{display.name}")
]
getExtraNav: ->
item = @_modPack.chooseRandomItem()
return null unless item?
display = new ItemDisplay item
return $("Random Item")
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 #####################################################################
onDidModelChange: ->
@trigger c.event.change
super
onDidRender: ->
options = imageLoader:@_imageLoader, modPack:@_modPack, router:@_router, show:false
@_multiblockController = @addChild MultiblockViewerController, '.view__multiblock_viewer', options
@_similarItemsController = @addChild ItemGroupController, '.view__item_group.similar', options
@_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')
@$craftingPlanButton = @$('.button.craftingPlan')
@$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
if @model.item.isCraftable
@show @$craftingPlanButton
else
@hide @$craftingPlanButton
if @_triggerEditing
@_triggerEditing = false
@_descriptionController.onEditClicked {}
@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()