mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-29 10:35:51 -05:00
d532d9fe6e
Only for overall campaign editor analytics. Still need to add date pickers to individual level views. Moving campaign editor analytics to its own modal to support reloading with new dates. The date picker widget works poorly, but you can type in dates if necessary. Also moving analytics button up into the top right header, with a little stats glyph icon.
331 lines
12 KiB
CoffeeScript
331 lines
12 KiB
CoffeeScript
RootView = require 'views/core/RootView'
|
|
Campaign = require 'models/Campaign'
|
|
Level = require 'models/Level'
|
|
Achievement = require 'models/Achievement'
|
|
ThangType = require 'models/ThangType'
|
|
CampaignView = require 'views/play/CampaignView'
|
|
CocoCollection = require 'collections/CocoCollection'
|
|
treemaExt = require 'core/treema-ext'
|
|
utils = require 'core/utils'
|
|
RelatedAchievementsCollection = require 'collections/RelatedAchievementsCollection'
|
|
CampaignAnalyticsModal = require './CampaignAnalyticsModal'
|
|
CampaignLevelView = require './CampaignLevelView'
|
|
SaveCampaignModal = require './SaveCampaignModal'
|
|
|
|
achievementProject = ['related', 'rewards', 'name', 'slug']
|
|
thangTypeProject = ['name', 'original']
|
|
|
|
module.exports = class CampaignEditorView extends RootView
|
|
id: "campaign-editor-view"
|
|
template: require 'templates/editor/campaign/campaign-editor-view'
|
|
className: 'editor'
|
|
|
|
events:
|
|
'click #analytics-button': 'onClickAnalyticsButton'
|
|
'click #save-button': 'onClickSaveButton'
|
|
|
|
constructor: (options, @campaignHandle) ->
|
|
super(options)
|
|
@campaign = new Campaign({_id:@campaignHandle})
|
|
@supermodel.loadModel(@campaign, 'campaign')
|
|
|
|
# Save reference to data used by anlytics modal so it persists across modal open/closes.
|
|
@campaignAnalytics = {}
|
|
|
|
@levels = new CocoCollection([], {
|
|
model: Level
|
|
url: "/db/campaign/#{@campaignHandle}/levels"
|
|
project: Campaign.denormalizedLevelProperties
|
|
})
|
|
@supermodel.loadCollection(@levels, 'levels')
|
|
|
|
@achievements = new CocoCollection([], {
|
|
model: Achievement
|
|
url: "/db/campaign/#{@campaignHandle}/achievements"
|
|
project: achievementProject
|
|
})
|
|
@supermodel.loadCollection(@achievements, 'achievements')
|
|
|
|
@toSave = new Backbone.Collection()
|
|
@listenToOnce @campaign ,'sync', @loadThangTypeNames
|
|
@listenToOnce @campaign, 'sync', @onFundamentalLoaded
|
|
@listenToOnce @levels, 'sync', @onFundamentalLoaded
|
|
@listenToOnce @achievements, 'sync', @onFundamentalLoaded
|
|
|
|
loadThangTypeNames: ->
|
|
# Load the names of the ThangTypes that this level's Treema nodes might want to display.
|
|
originals = []
|
|
for level in _.values(@campaign.get('levels'))
|
|
originals = originals.concat(_.values(level.requiredGear)) if level.requiredGear
|
|
originals = originals.concat(_.values(level.restrictedGear)) if level.restrictedGear
|
|
originals = originals.concat(level.allowedHeroes) if level.allowedHeroes
|
|
originals = _.uniq _.flatten originals
|
|
for original in originals
|
|
thangType = new ThangType()
|
|
thangType.setProjection(thangTypeProject)
|
|
thangType.setURL("/db/thang.type/#{original}/version")
|
|
@supermodel.loadModel(thangType, 'thang')
|
|
|
|
onFundamentalLoaded: ->
|
|
# Load any levels which haven't been denormalized into our campaign.
|
|
return unless @campaign.loaded and @levels.loaded and @achievements.loaded
|
|
for level in _.values(@campaign.get('levels'))
|
|
continue if model = @levels.findWhere(original: level.original)
|
|
model = new Level({})
|
|
model.setProjection Campaign.denormalizedLevelProperties
|
|
model.setURL("/db/level/#{level.original}/version")
|
|
@levels.add @supermodel.loadModel(model, 'level').model
|
|
achievements = new RelatedAchievementsCollection level.original
|
|
achievements.setProjection achievementProject
|
|
@supermodel.loadCollection achievements, 'achievements'
|
|
@listenToOnce achievements, 'sync', ->
|
|
@achievements.add(achievements.models)
|
|
|
|
onLoaded: ->
|
|
@toSave.add @campaign if @campaign.hasLocalChanges()
|
|
campaignLevels = $.extend({}, @campaign.get('levels'))
|
|
for level in @levels.models
|
|
levelOriginal = level.get('original')
|
|
campaignLevel = campaignLevels[levelOriginal]
|
|
continue if not campaignLevel
|
|
|
|
$.extend campaignLevel, _.omit(level.attributes, '_id')
|
|
achievements = @achievements.where {'related': levelOriginal}
|
|
rewards = []
|
|
for achievement in achievements
|
|
for rewardType, rewardArray of achievement.get('rewards')
|
|
for reward in rewardArray
|
|
rewardObject = { achievement: achievement.id }
|
|
|
|
if rewardType is 'heroes'
|
|
rewardObject.hero = reward
|
|
thangType = new ThangType({}, {project: thangTypeProject})
|
|
thangType.setURL("/db/thang.type/#{reward}/version")
|
|
@supermodel.loadModel(thangType, 'thang')
|
|
|
|
if rewardType is 'levels'
|
|
rewardObject.level = reward
|
|
if not @levels.findWhere({original: reward})
|
|
level = new Level({}, {project: Campaign.denormalizedLevelProperties})
|
|
level.setURL("/db/level/#{reward}/version")
|
|
@supermodel.loadModel(level, 'level')
|
|
|
|
if rewardType is 'items'
|
|
rewardObject.item = reward
|
|
thangType = new ThangType({}, {project: thangTypeProject})
|
|
thangType.setURL("/db/thang.type/#{reward}/version")
|
|
@supermodel.loadModel(thangType, 'thang')
|
|
|
|
rewards.push rewardObject
|
|
campaignLevel.rewards = rewards
|
|
delete campaignLevel.unlocks
|
|
campaignLevel.campaign = @campaign.get 'slug'
|
|
campaignLevels[levelOriginal] = campaignLevel
|
|
|
|
@campaign.set('levels', campaignLevels)
|
|
|
|
for level in _.values campaignLevels
|
|
model = @levels.findWhere {original: level.original}
|
|
model.set key, level[key] for key in Campaign.denormalizedLevelProperties
|
|
@toSave.add model if model.hasLocalChanges()
|
|
@updateRewardsForLevel model, level.rewards
|
|
|
|
super()
|
|
|
|
getRenderData: ->
|
|
c = super()
|
|
c.campaign = @campaign
|
|
c
|
|
|
|
onClickAnalyticsButton: ->
|
|
@openModalView new CampaignAnalyticsModal {}, @campaignHandle, @campaignAnalytics
|
|
|
|
onClickSaveButton: ->
|
|
@toSave.set @toSave.filter (m) -> m.hasLocalChanges()
|
|
@openModalView new SaveCampaignModal({}, @toSave)
|
|
|
|
afterRender: ->
|
|
super()
|
|
treemaOptions =
|
|
schema: Campaign.schema
|
|
data: $.extend({}, @campaign.attributes)
|
|
filePath: "db/campaign/#{@campaign.get('_id')}"
|
|
callbacks:
|
|
change: @onTreemaChanged
|
|
select: @onTreemaSelectionChanged
|
|
dblclick: @onTreemaDoubleClicked
|
|
nodeClasses:
|
|
levels: LevelsNode
|
|
level: LevelNode
|
|
campaigns: CampaignsNode
|
|
campaign: CampaignNode
|
|
achievement: AchievementNode
|
|
supermodel: @supermodel
|
|
|
|
@treema = @$el.find('#campaign-treema').treema treemaOptions
|
|
@treema.build()
|
|
@treema.open()
|
|
@treema.childrenTreemas.levels?.open()
|
|
|
|
@campaignView = new CampaignView({editorMode: true, supermodel: @supermodel}, @campaignHandle)
|
|
@campaignView.highlightElement = _.noop # make it stop
|
|
@listenTo @campaignView, 'level-moved', @onCampaignLevelMoved
|
|
@listenTo @campaignView, 'adjacent-campaign-moved', @onAdjacentCampaignMoved
|
|
@listenTo @campaignView, 'level-clicked', @onCampaignLevelClicked
|
|
@listenTo @campaignView, 'level-double-clicked', @onCampaignLevelDoubleClicked
|
|
@insertSubView @campaignView
|
|
|
|
onTreemaChanged: (e, nodes) =>
|
|
for node in nodes
|
|
path = node.getPath()
|
|
if _.string.startsWith path, '/levels/'
|
|
parts = path.split('/')
|
|
original = parts[2]
|
|
level = @supermodel.getModelByOriginal Level, original
|
|
campaignLevel = @treema.get "/levels/#{original}"
|
|
|
|
@updateRewardsForLevel level, campaignLevel.rewards
|
|
|
|
level.set key, campaignLevel[key] for key in Campaign.denormalizedLevelProperties
|
|
@toSave.add level if level.hasLocalChanges()
|
|
|
|
@toSave.add @campaign
|
|
@campaign.set key, value for key, value of @treema.data
|
|
@campaignView.setCampaign(@campaign)
|
|
|
|
onTreemaDoubleClicked: (e, node) =>
|
|
path = node.getPath()
|
|
return unless _.string.startsWith path, '/levels/'
|
|
original = path.split('/')[2]
|
|
@openCampaignLevelView @supermodel.getModelByOriginal Level, original
|
|
|
|
onCampaignLevelMoved: (e) ->
|
|
path = "levels/#{e.levelOriginal}/position"
|
|
@treema.set path, e.position
|
|
|
|
onAdjacentCampaignMoved: (e) ->
|
|
path = "adjacentCampaigns/#{e.campaignID}/position"
|
|
@treema.set path, e.position
|
|
|
|
onCampaignLevelClicked: (levelOriginal) ->
|
|
return unless levelTreema = @treema.childrenTreemas?.levels?.childrenTreemas?[levelOriginal]
|
|
if key.ctrl or key.command
|
|
url = "/editor/level/#{levelTreema.data.slug}"
|
|
window.open url, '_blank'
|
|
levelTreema.select()
|
|
#levelTreema.open()
|
|
|
|
onCampaignLevelDoubleClicked: (levelOriginal) ->
|
|
@openCampaignLevelView @supermodel.getModelByOriginal Level, levelOriginal
|
|
|
|
openCampaignLevelView: (level) ->
|
|
@insertSubView campaignLevelView = new CampaignLevelView({}, level)
|
|
@listenToOnce campaignLevelView, 'hidden', => @$el.find('#campaign-view').show()
|
|
@$el.find('#campaign-view').hide()
|
|
|
|
updateRewardsForLevel: (level, rewards) ->
|
|
achievements = @supermodel.getModels(Achievement)
|
|
achievements = (a for a in achievements when a.get('related') is level.get('original'))
|
|
for achievement in achievements
|
|
rewardSubset = (r for r in rewards when r.achievement is achievement.id)
|
|
oldRewards = achievement.get 'rewards'
|
|
newRewards = {}
|
|
|
|
heroes = _.compact((r.hero for r in rewardSubset))
|
|
newRewards.heroes = heroes if heroes.length
|
|
|
|
items = _.compact((r.item for r in rewardSubset))
|
|
newRewards.items = items if items.length
|
|
|
|
levels = _.compact((r.level for r in rewardSubset))
|
|
newRewards.levels = levels if levels.length
|
|
|
|
newRewards.gems = oldRewards.gems if oldRewards.gems
|
|
achievement.set 'rewards', newRewards
|
|
if achievement.hasLocalChanges()
|
|
@toSave.add achievement
|
|
|
|
|
|
class LevelsNode extends TreemaObjectNode
|
|
valueClass: 'treema-levels'
|
|
@levels: {}
|
|
|
|
buildValueForDisplay: (valEl, data) ->
|
|
@buildValueForDisplaySimply valEl, ''+_.size(data)
|
|
|
|
childPropertiesAvailable: -> @childSource
|
|
|
|
childSource: (req, res) =>
|
|
s = new Backbone.Collection([], {model:Level})
|
|
s.url = '/db/level'
|
|
s.fetch({data: {term:req.term, project: Campaign.denormalizedLevelProperties.join(',')}})
|
|
s.once 'sync', (collection) =>
|
|
for level in collection.models
|
|
LevelsNode.levels[level.get('original')] = level
|
|
@settings.supermodel.registerModel level
|
|
mapped = ({label: r.get('name'), value: r.get('original')} for r in collection.models)
|
|
res(mapped)
|
|
|
|
|
|
class LevelNode extends TreemaObjectNode
|
|
valueClass: 'treema-level'
|
|
buildValueForDisplay: (valEl, data) ->
|
|
name = data.name
|
|
if data.requiresSubscription
|
|
name = "[P] " + name
|
|
|
|
status = ''
|
|
el = 'strong'
|
|
if data.adminOnly
|
|
status += " (disabled)"
|
|
el = 'span'
|
|
else if data.adventurer
|
|
status += " (adventurer)"
|
|
|
|
completion = ''
|
|
if data.tasks
|
|
completion = "#{(t for t in data.tasks when t.complete).length} / #{data.tasks.length}"
|
|
|
|
valEl.append $("<a href='/editor/level/#{_.string.slugify(data.name)}' class='spr'>(e)</a>")
|
|
valEl.append $("<#{el}></#{el}>").addClass('treema-shortened').text name
|
|
if status
|
|
valEl.append $('<em class="spl"></em>').text status
|
|
if completion
|
|
valEl.append $('<span class="completion"></span>').text completion
|
|
|
|
populateData: ->
|
|
return if @data.name?
|
|
data = _.pick LevelsNode.levels[@keyForParent].attributes, Campaign.denormalizedLevelProperties
|
|
_.extend @data, data
|
|
|
|
class CampaignsNode extends TreemaObjectNode
|
|
valueClass: 'treema-campaigns'
|
|
@campaigns: {}
|
|
|
|
buildValueForDisplay: (valEl, data) ->
|
|
@buildValueForDisplaySimply valEl, ''+_.size(data)
|
|
|
|
childPropertiesAvailable: -> @childSource
|
|
|
|
childSource: (req, res) =>
|
|
s = new Backbone.Collection([], {model:Campaign})
|
|
s.url = '/db/campaign'
|
|
s.fetch({data: {term:req.term, project: Campaign.denormalizedCampaignProperties}})
|
|
s.once 'sync', (collection) ->
|
|
CampaignsNode.campaigns[campaign.id] = campaign for campaign in collection.models
|
|
mapped = ({label: r.get('name'), value: r.id} for r in collection.models)
|
|
res(mapped)
|
|
|
|
|
|
class CampaignNode extends TreemaObjectNode
|
|
valueClass: 'treema-campaign'
|
|
buildValueForDisplay: (valEl, data) ->
|
|
@buildValueForDisplaySimply valEl, data.name
|
|
|
|
populateData: ->
|
|
return if @data.name?
|
|
data = _.pick CampaignsNode.campaigns[@keyForParent].attributes, Campaign.denormalizedCampaignProperties
|
|
_.extend @data, data
|
|
|
|
class AchievementNode extends treemaExt.IDReferenceNode
|
|
buildSearchURL: (term) -> "#{@url}?term=#{term}&project=#{achievementProject.join(',')}"
|