Refactored and improved the ThangComponentsEditView and ThangComponentConfigView. Still some things to do.

This commit is contained in:
Scott Erickson 2014-08-07 18:49:39 -07:00
parent 9a1dd412c9
commit 6cac2371c1
25 changed files with 597 additions and 584 deletions

View file

@ -3,7 +3,7 @@ c = require './../schemas'
module.exports = ThangComponentSchema = c.object {
title: 'Component'
description: 'Configuration for a Component that this Thang uses.'
format: 'thang-component'
format: 'component-reference'
required: ['original', 'majorVersion']
'default':
majorVersion: 0

View file

@ -1,16 +0,0 @@
#component-config-column-view
h3
float: left
#add-remove-button
float: right
margin: 5px
#description
clear: both
.treema
position: absolute
left: 5px
right: 5px
top: 90px
bottom: 0
overflow: scroll

View file

@ -0,0 +1 @@
//.thang-component-config-view

View file

@ -19,7 +19,7 @@
#extant-components-column
left: 0
width: 20%
width: 300px
.treema
position: absolute
@ -29,26 +29,23 @@
bottom: 0
overflow: scroll
#add-component-column
right: 0
width: 20%
.dependent
background-color: rgba(128, 64, 255, 0.10)
.treema
#thang-components-config-column
border-left: 1px solid black
right: 0
left: 300px
min-width: 600px
h3
margin-left: 20px
#thang-component-configs
position: absolute
top: 75px
left: 10px
right: 0px
top: 80px
bottom: 0
right: 0
left: 20px
overflow: scroll
.treema-row
line-height: 24px
.add-button
margin-left: 10px
#component-config-column-view
border-left: 1px solid black
border-right: 1px solid black
right: 20%
left: 20%

View file

@ -1,9 +1,6 @@
#editor-level-view
&, #level-editor-top-nav
// min-width: 1024px
a
font-family: helvetica, arial, sans serif
.editor
h1, h2, h3, h4, h5, h6, a
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important
position: absolute
top: 0px
@ -48,7 +45,7 @@
border-radius: 0
.navbar-right
// not sure why bootstrap puts a big negative margin in, but this overrides it
margin-right: 10px
margin-right: 10px !important
float: right
.dropdown-menu
@ -130,9 +127,7 @@
margin-top: -10px
padding-top: 10px
// keeps the editor tabs a certain height
#level-editor-tabs
#level-editor-tabs, #thang-type-edit-view .tab-content
position: absolute
left: 15px
right: 15px

View file

@ -1,85 +0,0 @@
#editor-thang-type-edit-view
#save-button, #revert-button
float: right
margin-right: 20px
#portrait
float: right
.main-area
padding: 5px
& > div.slider-cell
margin-right: 5px
float: right
.file-controls
float: left
position: relative
top: 5px
left: 5px
button
margin-left: 5px
select
position: relative
top: 5px
input
display: none
.slider-cell
margin: 5px 0
float: left
width: 120px
.selector
display: inline-block
width: 100px
position: relative
top: 3px
#marker-button, #end-button
float: right
margin-right: 10px
position: relative
top: 15px
#canvas
float: right
width: 400px
border: 1px solid blue
background-color: lightgray
#settings-col
float: left
width: 550px
.treema-row img
max-width: 100%
#thang-type-treema
height: 400px
overflow: scroll
box-sizing: border-box
#thang-components-edit-view
position: absolute
top: 200px
bottom: 0
.treema-root
background-color: white
border-radius: 4px
#spritesheets
border: 1px solid green
max-width: 100%
max-height: 600px
overflow: scroll
clear: both
box-sizing: border-box
margin-bottom: 20px
canvas
background: darkseagreen
margin: 2px

View file

@ -0,0 +1,92 @@
#thang-type-edit-view
#portrait
float: left
margin: 5px
width: 100px
height: 100px
#settings-col
width: 560px
position: absolute
left: 0
top: 0
bottom: 0
.file-controls
float: left
position: relative
top: 5px
left: 5px
button
margin-left: 5px
input
display: none
.treema-row img
max-width: 100%
.treema-root
overflow: scroll
box-sizing: border-box
position: absolute
top: 160px
bottom: 20px
right: 20px
left: 20px
#display-col
position: absolute
width: 440px
left: 580px
top: 0
bottom: 0
overflow: hidden
& > div.slider-cell
margin-right: 5px
canvas
width: 400px
border: 1px solid blue
background-color: lightgray
select
margin-top: 10px
#marker-button, #end-button
margin: 10px 10px 10px 0
.slider-cell
margin: 5px 0
width: 195px
float: left
.selector
display: inline-block
width: 180px
position: relative
top: 3px
#thang-components-edit-view
position: absolute
top: 0px
bottom: 0
.treema-root
background-color: white
border-radius: 4px
#spritesheets
position: absolute
top: 0
bottom: 0
left: 0
right: 0
border: 1px solid green
overflow: scroll
canvas
background: darkseagreen

View file

@ -10,9 +10,6 @@
&, & > div.ace_editor
width: 100%
& > div.ace_editor
height: 400px
.treema-markdown.treema-display, .treema-coffee.treema-display, .treema-javascript.treema-display
width: 100%
border: 3px inset rgba(0, 100, 100, 0.2)

View file

@ -1,8 +0,0 @@
h3= component.name
//button.btn#add-remove-button Add Component
p#description= component.description
.treema
//p#other-props
// strong Other Config Properties:
// for prop in configProperties
// a(title=prop.description)= prop.name

View file

@ -1,11 +0,0 @@
#extant-components-column.column
h3 Components
p Click then press delete to remove.
.treema
#component-config-column-view.column
#add-component-column.column
h3 Add Components
p Click then press enter to add.
.treema

View file

@ -0,0 +1,7 @@
.panel.panel-default
.panel-heading
strong.panel-title= component.name
em.spl.spr (#{component.system})
span#description= component.description
.panel-body
.treema

View file

@ -0,0 +1,9 @@
#extant-components-column.column
h3 Components
p Click then press delete to remove.
.treema
.column#thang-components-config-column
h3 Component Configurations
#thang-component-configs

View file

@ -1,95 +0,0 @@
extends /templates/base
block content
div
ol.breadcrumb
li
a(href="/editor", data-i18n="editor.main_title") CodeCombat Editors
li
a(href="/editor/thang", data-i18n="editor.thang_title") Thang Editor
li.active
| #{thangType.attributes.name}
img#portrait.img-thumbnail
button.btn.btn-secondary#history-button(data-i18n="general.version_history") Version History
button.btn.btn-primary#save-button(data-i18n="common.save", disabled=authorized === true ? undefined : "true") Save
button.btn.btn-primary#revert-button(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true") Revert
h3 Edit Thang Type: "#{thangType.attributes.name}"
ul.nav.nav-tabs
li.active
a(href="#editor-thang-main-tab-view", data-toggle="tab") Main
li
a(href="#editor-thang-components-tab-view", data-toggle="tab") Components
li
a(href="#editor-thang-spritesheets-view", data-toggle="tab") Spritesheets
li
a(href="#editor-thang-colors-tab-view", data-toggle="tab")#color-tab Colors
li
a(href="#editor-thang-patches-view", data-toggle="tab")#patches-tab Patches
div.tab-content
div.tab-pane#editor-thang-colors-tab-view
div.tab-pane#editor-thang-main-tab-view.active
div.main-area.well
div.file-controls
select#animations-select
for animation in animations
option #{animation}
button(disabled=authorized === true ? undefined : "true").btn.btn-small.btn-primary#upload-button
i.icon-upload
button(disabled=authorized === true ? undefined : "true").btn.btn-small.btn-primary#clear-button
i.icon-remove
input#real-upload-button(type="file")
button.btn.btn-small.btn-primary#marker-button
i.icon-map-marker
button.btn.btn-small.btn-primary#end-button
i.icon-stop
div.slider-cell
| Rotation:
span.rotation-label
.selector#rotation-slider
div.slider-cell
| Scale:
span.scale-label
.selector#scale-slider
div.slider-cell
| Resolution:
span.resolution-label 4.0x
.selector#resolution-slider
div.slider-cell
| Health:
span.health-label 10hp
.selector#health-slider
.clearfix
div#settings-col
div#thang-type-treema
canvas#canvas(width="400", height="400")
.clearfix
div.tab-pane#editor-thang-components-tab-view
#thang-components-edit-view
div.tab-pane#editor-thang-spritesheets-view
div#spritesheets
div.tab-pane#editor-thang-patches-view
div.patches-view
div#error-view
.clearfix

View file

@ -0,0 +1,124 @@
extends /templates/base
block header
if thangType.loading
nav.navbar.navbar-default(role='navigation')#thang-editor-top-nav
.container-fluid
ul.nav.navbar-nav
li
a(href="/editor/thang")
span.glyphicon-home.glyphicon
else
nav.navbar.navbar-default(role='navigation')#thang-editor-top-nav
ul.nav.navbar-nav
li
a(href="/editor/thang")
span.glyphicon-home.glyphicon
ul.nav.navbar-nav.nav-tabs
li.active
a(href="#editor-thang-main-tab-view", data-toggle="tab") Main
li
a(href="#editor-thang-components-tab-view", data-toggle="tab") Components
li
a(href="#editor-thang-spritesheets-view", data-toggle="tab") Spritesheets
li
a(href="#editor-thang-colors-tab-view", data-toggle="tab")#color-tab Colors
li
a(href="#editor-thang-patches-view", data-toggle="tab")#patches-tab
span(data-i18n="resources.patches").spr Patches
- var patches = thangType.get('patches')
if patches && patches.length
span.badge= patches.length
.navbar-header
span.navbar-brand #{thangType.attributes.name}
ul.nav.navbar-nav.navbar-right
if authorized
li#save-button
a
span.glyphicon-floppy-disk.glyphicon
li.dropdown
a(data-toggle='dropdown')
span.glyphicon-chevron-down.glyphicon
ul.dropdown-menu
li.dropdown-header Actions
li(class=anonymous ? "disabled": "")
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert
li.divider
li.dropdown-header Info
li#history-button
a(href='#', data-i18n="general.version_history") Version History
li.divider
li.dropdown-header Help
li
a(href='https://github.com/codecombat/codecombat/wiki/Artisan-Home', data-i18n="editor.wiki", target="_blank") Wiki
li
a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat", target="_blank") Live Chat
li
a(href='http://discourse.codecombat.com/category/artisan', data-i18n="nav.forum", target="_blank") Forum
li
a(data-toggle="coco-modal", data-target="modal/ContactModal", data-i18n="nav.contact") Email
block outer_content
.outer-content
div.tab-content
div.tab-pane#editor-thang-colors-tab-view
div.tab-pane#editor-thang-main-tab-view.active
div#settings-col.well
img#portrait.img-thumbnail
div.file-controls
button(disabled=authorized === true ? undefined : "true").btn.btn-small.btn-info#upload-button
span.glyphicon.glyphicon-upload
span.spl Upload Animation
button(disabled=authorized === true ? undefined : "true").btn.btn-small.btn-danger#clear-button
span.glyphicon.glyphicon-remove
span.spl Clear Data
input#real-upload-button(type="file")
div#thang-type-treema
.clearfix
div#display-col.well
canvas#canvas(width="400", height="400")
select#animations-select.form-control
for animation in animations
option #{animation}
div
button.btn.btn-small.btn-primary#marker-button
i.icon-map-marker
span.spl Markers
button.btn.btn-small.btn-primary#end-button
i.icon-stop
span.spl Stop
div.slider-cell
| Rotation:
span.rotation-label
.selector#rotation-slider
div.slider-cell
| Scale:
span.scale-label
.selector#scale-slider
div.slider-cell
| Resolution:
span.resolution-label 4.0x
.selector#resolution-slider
div.slider-cell
| Health:
span.health-label 10hp
.selector#health-slider
div.tab-pane#editor-thang-components-tab-view
#thang-components-edit-view
div.tab-pane#editor-thang-spritesheets-view
div#spritesheets
div.tab-pane#editor-thang-patches-view
div.patches-view
div#error-view
.clearfix
block footer

View file

@ -225,14 +225,8 @@ class CodeTreema extends TreemaNode.nodeMap.ace
super(valEl)
if not @schema.aceMode and mode = codeLanguages[@keyForParent]
@editor.getSession().setMode mode
@editor.on('change', @onEditorChange)
valEl
onEditorChange: =>
@saveChanges()
@flushChanges()
@getRoot().broadcastChanges()
class CoffeeTreema extends CodeTreema
constructor: ->
super(arguments...)
@ -308,26 +302,19 @@ class LatestVersionReferenceNode extends TreemaNode
input.focus().keyup @search
input.attr('placeholder', @formatDocument(@data)) if @data
buildSearchURL: (term) -> "#{@url}?term=#{term}&project=true"
search: =>
term = @getValEl().find('input').val()
return if term is @lastTerm
# HACK while search is broken
if @collection
@lastTerm = term
@searchCallback()
return
@getSearchResultsEl().empty() if @lastTerm and not term
return unless term
@lastTerm = term
@getSearchResultsEl().empty().append('Searching')
@collection = new LatestVersionCollection([], model: @model)
# HACK while search is broken
# @collection.url = "#{@url}?term=#{term}&project=true"
@collection.url = "#{@url}?term=#{''}&project=true"
@collection.url = @buildSearchURL(term)
@collection.fetch()
@collection.once 'sync', @searchCallback, @
@ -338,10 +325,6 @@ class LatestVersionReferenceNode extends TreemaNode
row = $('<div></div>').addClass('treema-search-result-row')
text = @formatDocument(model)
continue unless text?
# HACK while search is broken
continue unless text.toLowerCase().indexOf(@lastTerm.toLowerCase()) >= 0
row.addClass('treema-search-selected') if first
first = false
row.text(text)
@ -354,9 +337,10 @@ class LatestVersionReferenceNode extends TreemaNode
getSearchResultsEl: -> @getValEl().find('.treema-search-results')
getSelectedResultEl: -> @getValEl().find('.treema-search-selected')
modelToString: (model) -> model.get('name')
formatDocument: (docOrModel) ->
doc = docOrModel.attributes or docOrModel
return doc.name if doc.name?
return @modelToString(docOrModel) if docOrModel instanceof CocoModel
return 'Unknown' unless @settings.supermodel?
m = CocoModel.getReferencedModel(@data, @schema)
urlGoingFor = m.url()
@ -366,7 +350,7 @@ class LatestVersionReferenceNode extends TreemaNode
m.url = -> urlGoingFor
@settings.supermodel.registerModel(m)
return 'Unknown' unless m
return m.get('name')
return @modelToString(m)
saveChanges: ->
selected = @getSelectedResultEl()
@ -379,10 +363,12 @@ class LatestVersionReferenceNode extends TreemaNode
@instance = fullValue
onDownArrowPressed: (e) ->
return super(arguments...) unless @isEditing()
@navigateSearch(1)
e.preventDefault()
onUpArrowPressed: (e) ->
return super(arguments...) unless @isEditing()
e.preventDefault()
@navigateSearch(-1)
@ -411,6 +397,13 @@ class LatestVersionReferenceNode extends TreemaNode
selected = @getSelectedResultEl()
return not selected.length
class LevelComponentReferenceNode extends LatestVersionReferenceNode
# HACK: this list of properties is needed by the thang components edit view and config views.
# need a better way to specify this, or keep the search models from bleeding into those
# supermodels.
buildSearchURL: (term) -> "#{@url}?term=#{term}&project=name,system,original,version,dependencies,configSchema,description"
modelToString: (model) -> model.get('system') + '.' + model.get('name')
canEdit: -> not @data.original # only allow editing if the row's data hasn't been set yet
LatestVersionReferenceNode.prototype.search = _.debounce(LatestVersionReferenceNode.prototype.search, 200)
@ -431,6 +424,7 @@ module.exports.setup = ->
TreemaNode.setNodeSubclass('javascript', JavaScriptTreema)
TreemaNode.setNodeSubclass('image-file', ImageFileTreema)
TreemaNode.setNodeSubclass('latest-version-reference', LatestVersionReferenceNode)
TreemaNode.setNodeSubclass('component-reference', LevelComponentReferenceNode)
TreemaNode.setNodeSubclass('i18n', InternationalizationNode)
TreemaNode.setNodeSubclass('sound-file', SoundFileTreema)
TreemaNode.setNodeSubclass 'slug-props', SlugPropsObject

View file

@ -1,28 +1,29 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/components/config'
template = require 'templates/editor/components/thang-component-config-view'
Level = require 'models/Level'
LevelComponent = require 'models/LevelComponent'
nodes = require '../level/treema_nodes'
module.exports = class ThangComponentConfigView extends CocoView
id: 'component-config-column-view'
className: 'thang-component-config-view'
template: template
className: 'column'
changed: false
events:
'click .treema-shortened': -> console.log 'clicked treema root'
constructor: (options) ->
super options
@component = options.component
@config = options.config or {}
@world = options.world
@level = options.level
@editing = options.editing
@callback = options.callback
getRenderData: (context={}) ->
context = super(context)
context.component = @component
context.component = @component.attributes
context.configProperties = []
context
@ -38,7 +39,7 @@ module.exports = class ThangComponentConfigView extends CocoView
superteams = _.union(teams, superteams)
treemaOptions =
supermodel: @supermodel
schema: @component.configSchema
schema: @component.attributes.configSchema
data: _.cloneDeep @config
callbacks: {change: @onConfigEdited}
world: @world
@ -58,16 +59,16 @@ module.exports = class ThangComponentConfigView extends CocoView
'seconds': nodes.SecondsNode
'speed': nodes.SpeedNode
'acceleration': nodes.AccelerationNode
treemaOptions.readOnly = not @editing
@editThangTreema = @$el.find('.treema').treema treemaOptions
@editThangTreema.build()
@editThangTreema.open(2)
@hideLoading()
if _.isEqual(@config, {}) and not @editThangTreema.canAddChild()
@$el.find('.panel-body').hide()
onConfigEdited: =>
@changed = true
@callback?(@data())
@trigger 'changed', { component: @component, config: @data() }
undo: ->
@editThangTreema.undo()

View file

@ -1,284 +0,0 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/components/main'
Level = require 'models/Level'
LevelComponent = require 'models/LevelComponent'
LevelSystem = require 'models/LevelSystem'
ComponentsCollection = require 'collections/ComponentsCollection'
ComponentConfigView = require './ThangComponentConfigView'
module.exports = class ThangComponentEditView extends CocoView
id: 'thang-components-edit-view'
template: template
constructor: (options) ->
super options
@components = options.components or []
@world = options.world
@level = options.level
@callback = options.callback
@componentCollection = @supermodel.loadCollection(new ComponentsCollection(), 'components').model
afterRender: ->
super()
return unless @supermodel.finished()
@buildExtantComponentTreema()
@buildAddComponentTreema()
buildExtantComponentTreema: ->
level = new Level()
treemaOptions =
supermodel: @supermodel
schema: level.schema().properties.thangs.items.properties.components
data: _.cloneDeep @components
callbacks: {select: @onSelectExtantComponent, change: @onChangeExtantComponents}
noSortable: true
nodeClasses:
'thang-components-array': ThangComponentsArrayNode
'thang-component': ThangComponentNode
@extantComponentsTreema = @$el.find('#extant-components-column .treema').treema treemaOptions
@extantComponentsTreema.build()
@$el.find('#extant-components-column .treema').droppable({
drop: =>
@onAddComponentEnterPressed @selectedRow
})
buildAddComponentTreema: ->
return unless @componentCollection and @extantComponentsTreema
extantComponents = @extantComponentsTreema.data
componentsUsedCount = extantComponents.length
return if @lastComponentsUsedCount is componentsUsedCount
@lastComponentsUsedCount = componentsUsedCount
components = (m.attributes for m in @componentCollection.models)
_.remove components, (comp) =>
_.find extantComponents, {original: comp.original} # already have this one added
components = _.sortBy components, (comp) -> comp.system + '.' + comp.name
treemaOptions =
supermodel: @supermodel
schema: {type: 'array', items: LevelComponent.schema}
data: ($.extend(true, {}, c) for c in components)
callbacks: {
select: @onSelectAddableComponent
enter: @onAddComponentEnterPressed
dblclick: @onAddComponentDoubleClicked
mouseenter: @onAddComponentMouseEnter
mouseleave: @onAddComponentMouseLeave
}
readOnly: true
noSortable: true
nodeClasses:
'array': ComponentArrayNode
'object': ComponentNode
# I have no idea why it's not building in the Thang Editor unless I defer
_.defer (=>
@addComponentsTreema = @$el.find('#add-component-column .treema').treema treemaOptions
@addComponentsTreema.build()
@$el.find('#add-component-column .treema-node').draggable({
revert: 'invalid'
helper: 'clone'
appendTo: 'body'
start: (e) ->
# Hack to ensure dragged treema node is selected
$(@).trigger('click') unless $(@).hasClass 'treema-selected'
})
@hideLoading()
), 500
onSelectAddableComponent: (e, selected) =>
@extantComponentsTreema.deselectAll()
@onComponentSelected(selected, false)
onSelectExtantComponent: (e, selected) =>
return if @updatingFromConfig
@addComponentsTreema.deselectAll()
@onComponentSelected(selected, true)
onComponentSelected: (selected, extant=true) ->
return if @alreadySaving # handle infinite loops
@alreadySaving = true
@closeExistingView()
@alreadySaving = false
return unless selected.length
# select dependencies.
node = selected[0]
original = node.data.original
toRemoveTreema = []
dependent_class = 'treema-dependent'
try
for index, child of @extantComponentsTreema.childrenTreemas
$(child.$el).removeClass(dependent_class)
for index, child of @extantComponentsTreema.childrenTreemas
if child.data.original == original # Here we assume that the treemas are sorted by their dependency.
break
dep_originals = (d.original for d in child.component.attributes.dependencies)
for dep_original in dep_originals
if original == dep_original
toRemoveTreema.push child
for dep_treema in toRemoveTreema
dep_treema.toggleSelect()
$(dep_treema.$el).addClass(dependent_class)
catch error
console.error error
return unless selected.length
row = selected[0]
@selectedRow = row
component = row.component?.attributes or row.data
config = if extant then row.data?.config else {}
@configView = new ComponentConfigView({
supermodel: @supermodel
level: @level
world: @world
config: config
component: component
editing: extant
callback: @onComponentConfigChanged
})
@insertSubView @configView
closeExistingView: ->
return unless @configView
data = @configView.data()
@selectedRow.set '/config', data if data and @configView.changed and @configView.editing
@removeSubView @configView
@configView = null
onComponentConfigChanged: (data) =>
@updatingFromConfig = true
@selectedRow.set '/config', data if data and @configView.changed and @configView.editing
@updatingFromConfig = false
onChangeExtantComponents: =>
@buildAddComponentTreema()
@reportChanges()
onAddComponentEnterPressed: (node) =>
if extantSystems
extantSystems =
(@supermodel.getModelByOriginalAndMajorVersion LevelSystem, sn.original, sn.majorVersion).attributes.name.toLowerCase() for idx, sn of @level.get('systems')
requireSystem = node.data.system.toLowerCase()
if requireSystem not in extantSystems
warn_element = 'Component <b>' + node.data.name + '</b> requires system <b>' + requireSystem + '</b> which is currently not specified in this level.'
noty({
text: warn_element,
layout: 'bottomLeft',
type: 'warning'
})
id = node.data._id
comp = _.find @componentCollection.models, id: id
unless comp
return console.error 'Couldn\'t find component for id', id, 'out of', @components.models
# Add all dependencies, recursively, unless we already have them
toAdd = comp.getDependencies(@componentCollection.models)
_.remove toAdd, (c1) =>
_.find @extantComponentsTreema.data, (c2) ->
c2.original is c1.get('original')
for c in toAdd.concat [comp]
@extantComponentsTreema.insert '/', {
original: c.get('original') ? id
majorVersion: c.get('version').major ? 0
}
# reselect newly added child treema in the extantComponentsTreema
for index, treema of @extantComponentsTreema.childrenTreemas
if treema.component.id is id
_.defer =>
treema.select()
@onSelectExtantComponent({}, [treema])
return
onAddComponentDoubleClicked: (e, treema) =>
@onAddComponentEnterPressed treema
onAddComponentMouseEnter: (e, treema) ->
treema.$el.find('.add-button').show()
onAddComponentMouseLeave: (e, treema) ->
treema.$el.find('.add-button').hide()
return
reportChanges: ->
@callback?($.extend(true, [], @extantComponentsTreema.data))
undo: ->
if @configView is null or @configView?.editing is false then @extantComponentsTreema.undo() else @configView.undo()
redo: ->
if @configView is null or @configView?.editing is false then @extantComponentsTreema.redo() else @configView.redo()
class ThangComponentsArrayNode extends TreemaArrayNode
valueClass: 'treema-thang-components-array'
editable: false
sort: true
canAddChild: -> false
sortFunction: (a, b) =>
a = @settings.supermodel.getModelByOriginalAndMajorVersion(LevelComponent, a.original, a.majorVersion)
b = @settings.supermodel.getModelByOriginalAndMajorVersion(LevelComponent, b.original, b.majorVersion)
return 1 if a.attributes.system > b.attributes.system
return -1 if a.attributes.system < b.attributes.system
return 1 if a.name > b.name
return -1 if a.name < b.name
return 0
class ThangComponentNode extends TreemaObjectNode
valueClass: 'treema-thang-component'
collection: false
constructor: ->
super(arguments...)
@grabDBComponent()
grabDBComponent: ->
@component = @settings.supermodel.getModelByOriginalAndMajorVersion LevelComponent, @data.original, @data.majorVersion
console.error 'Couldn\'t find comp for', @data.original, @data.majorVersion, 'from models', @settings.supermodel.models unless @component
buildValueForDisplay: (valEl) ->
return super valEl unless @data.original and @component
s = @component.get('system') + '.' + @component.get('name')
@buildValueForDisplaySimply valEl, s
class ComponentArrayNode extends TreemaArrayNode
editable: false
sort: true
canAddChild: -> false
sortFunction: (a, b) =>
return 1 if a.system > b.system
return -1 if a.system < b.system
return 1 if a.name > b.name
return -1 if a.name < b.name
return 0
class ComponentNode extends TreemaObjectNode
valueClass: 'treema-component'
addButtonTemplate: '<button type="button" class="add-button btn btn-default btn-xs"><span class="glyphicon glyphicon-plus"></span></button>'
collection: false
build: ->
super()
@$el.find('> .treema-row').append @addButtonTemplate
addButton = @$el.find('.add-button')
addButton.hide()
addButton.click =>
@callbacks.enter?(@)
@$el
buildValueForDisplay: (valEl) ->
s = @data.system + '.' + @data.name
@buildValueForDisplaySimply valEl, s
onEnterPressed: (args...) ->
super(args...)
@callbacks.enter?(@)

View file

@ -0,0 +1,282 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/components/thang-components-edit-view'
Level = require 'models/Level'
LevelComponent = require 'models/LevelComponent'
LevelSystem = require 'models/LevelSystem'
ComponentsCollection = require 'collections/ComponentsCollection'
ThangComponentConfigView = require './ThangComponentConfigView'
module.exports = class ThangComponentsEditView extends CocoView
id: 'thang-components-edit-view'
template: template
constructor: (options) ->
super options
@components = options.components or []
@components = $.extend true, [], @components # just to be sure
@lastComponentLength = @components.length
@world = options.world
@level = options.level
@callback = options.callback # TODO: Switch to 'trigger'
@loadComponents(@components)
loadComponents: (components) ->
for componentRef in components
levelComponent = new LevelComponent()
url = "/db/level.component/#{componentRef.original}/version/#{componentRef.majorVersion}"
levelComponent.setURL(url)
resource = @supermodel.loadModel levelComponent, 'component'
@listenToOnce resource, 'loaded', ->
return if @handlingChange
if @supermodel.finished()
@handlingChange = true
@onComponentsAdded()
@handlingChange = false
afterRender: ->
super()
return unless @supermodel.finished()
@buildExtantComponentsTreema()
@addThangComponentConfigViews()
buildExtantComponentsTreema: ->
treemaOptions =
supermodel: @supermodel
schema: Level.schema.properties.thangs.items.properties.components
data: $.extend true, [], @components
callbacks: {select: @onSelectComponent, change: @onComponentsTreemaChanged}
noSortable: true
nodeClasses:
'thang-components-array': ThangComponentsArrayNode
@extantComponentsTreema = @$el.find('#extant-components-column .treema').treema treemaOptions
@extantComponentsTreema.build()
onComponentsTreemaChanged: =>
return if @handlingChange
@handlingChange = true
componentMap = {}
for component in @components
componentMap[component.original] = component
newComponentsList = []
for component in @extantComponentsTreema.data
newComponentsList.push(componentMap[component.original] or component)
@components = newComponentsList
# update the components list here
@onComponentsChanged()
@handlingChange = false
onComponentsChanged: =>
# happens whenever the list of components changed, one way or another
# * if the treema gets changed
# * if components are added externally, like by a modal
# * if a dependency loads and is added to the list
# TODO: Disallow editing components in the list, otherwise this system breaks.
if @components.length < @lastComponentLength
@onComponentsRemoved()
else
@onComponentsAdded()
@lastComponentLength = @components.length
onComponentsRemoved: ->
componentMap = {}
for component in @components
componentMap[component.original] = component
# Deleting components missing dependencies.
while true
removedSomething = false
for componentRef in _.values(componentMap)
componentModel = @supermodel.getModelByOriginalAndMajorVersion(
LevelComponent, componentRef.original, componentRef.majorVersion)
for dependency in componentModel.get('dependencies') or []
if not componentMap[dependency.original]
delete componentMap[componentRef.original]
component = @supermodel.getModelByOriginal(
LevelComponent, componentRef.original)
noty {
text: "Removed dependent component: #{component.get('name')}"
layout: 'topCenter'
timeout: 5000
type: 'information'
}
removedSomething = true
break if removedSomething
break unless removedSomething
@components = _.values(componentMap)
# Delete individual component config views that are no longer included.
for subview in _.values(@subviews)
continue unless subview instanceof ThangComponentConfigView
if not componentMap[subview.component.get('original')]
@removeSubView(subview)
@updateComponentsList()
@reportChanges()
updateComponentsList: ->
@extantComponentsTreema?.set('/', $.extend(true, [], @components))
onComponentsAdded: ->
componentMap = {}
for component in @components
componentMap[component.original] = component
# Go through the map, adding missing dependencies.
while true
addedSomething = false
for componentRef in _.values(componentMap)
componentModel = @supermodel.getModelByOriginalAndMajorVersion(
LevelComponent, componentRef.original, componentRef.majorVersion)
for dependency in componentModel.get('dependencies') or []
if not componentMap[dependency.original]
component = @supermodel.getModelByOriginalAndMajorVersion(
LevelComponent, dependency.original, dependency.majorVersion)
if not component
@loadComponents([dependency])
# will run onComponentsAdded once more when the model loads
else
addedSomething = true
noty {
text: "Added dependency: #{component.get('name')}"
layout: 'topCenter'
timeout: 5000
type: 'information'
}
componentMap[dependency.original] = dependency
@components.push dependency
break unless addedSomething
# Sort the component list, reorder the component config views
@updateComponentsList()
@addThangComponentConfigViews()
@reportChanges()
addThangComponentConfigViews: ->
# Detach all component config views temporarily.
componentConfigViews = {}
for subview in _.values(@subviews)
continue unless subview instanceof ThangComponentConfigView
componentConfigViews[subview.component.get('original')] = subview
subview.$el.detach()
# Put back config views into the DOM based on the component list ordering,
# adding and registering new ones as needed.
configsEl = @$el.find('#thang-component-configs')
for componentRef in @extantComponentsTreema.data
subview = componentConfigViews[componentRef.original]
if not subview
subview = @makeThangComponentConfigView(componentRef)
continue unless subview
@registerSubView(subview)
configsEl.append(subview.$el)
makeThangComponentConfigView: (thangComponent) ->
component = @supermodel.getModelByOriginal(LevelComponent, thangComponent.original)
return unless component
config = thangComponent.config ? {}
configView = new ThangComponentConfigView({
supermodel: @supermodel
level: @level
world: @world
config: config
component: component
})
configView.render()
@listenTo configView, 'changed', @onConfigChanged
configView
onConfigChanged: (e) ->
for thangComponent in @components
if thangComponent.original is e.component.get('original')
thangComponent.config = e.config
@reportChanges()
onSelectComponent: (e, nodes) =>
@extantComponentsTreema.$el.find('.dependent').removeClass('dependent')
return unless nodes.length is 1
# find dependent components
dependents = {}
dependents[nodes[0].data.original] = true
componentsToCheck = [nodes[0].data.original]
while componentsToCheck.length
componentOriginal = componentsToCheck.pop()
for otherComponentRef in @components
continue if otherComponentRef.original is componentOriginal
continue if dependents[otherComponentRef.original]
otherComponent = @supermodel.getModelByOriginal(LevelComponent, otherComponentRef.original)
for dependency in otherComponent.get('dependencies')
if dependents[dependency.original]
dependents[otherComponentRef.original] = true
componentsToCheck.push otherComponentRef.original
# highlight them
for child in _.values(@extantComponentsTreema.childrenTreemas)
if dependents[child.data.original]
child.$el.addClass('dependent')
# scroll to the config
for subview in _.values(@subviews)
continue unless subview instanceof ThangComponentConfigView
if subview.component.get('original') is nodes[0].data.original
subview.$el[0].scrollIntoView()
break
onComponentConfigChanged: (data) =>
@updatingFromConfig = true
@selectedRow.set '/config', data if data and @configView.changed and @configView.editing
@updatingFromConfig = false
onChangeExtantComponents: =>
@buildAddComponentTreema()
@reportChanges()
onAddComponentEnterPressed: (node) =>
# TODO: Incorporate this logic when adding components
if extantSystems
extantSystems =
(@supermodel.getModelByOriginalAndMajorVersion LevelSystem, sn.original, sn.majorVersion).attributes.name.toLowerCase() for idx, sn of @level.get('systems')
requireSystem = node.data.system.toLowerCase()
if requireSystem not in extantSystems
warn_element = 'Component <b>' + node.data.name + '</b> requires system <b>' + requireSystem + '</b> which is currently not specified in this level.'
noty({
text: warn_element,
layout: 'bottomLeft',
type: 'warning'
})
reportChanges: ->
@callback?($.extend(true, [], @components))
# TODO: Fix these.
undo: ->
if @configView is null or @configView?.editing is false then @extantComponentsTreema.undo() else @configView.undo()
redo: ->
if @configView is null or @configView?.editing is false then @extantComponentsTreema.redo() else @configView.redo()
class ThangComponentsArrayNode extends TreemaArrayNode
valueClass: 'treema-thang-components-array'
sort: true
sortFunction: (a, b) =>
a = @settings.supermodel.getModelByOriginalAndMajorVersion(LevelComponent, a.original, a.majorVersion)
b = @settings.supermodel.getModelByOriginalAndMajorVersion(LevelComponent, b.original, b.majorVersion)
return 0 if not (a or b)
return 1 if not b
return -1 if not a
return 1 if a.attributes.system > b.attributes.system
return -1 if a.attributes.system < b.attributes.system
return 1 if a.name > b.name
return -1 if a.name < b.name
return 0

View file

@ -20,6 +20,7 @@ ComponentDocsView = require 'views/docs/ComponentDocumentationView'
module.exports = class LevelEditView extends RootView
id: 'editor-level-view'
className: 'editor'
template: template
cache: false

View file

@ -1,12 +1,12 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/level/thang/edit'
ThangComponentEditView = require 'views/editor/component/ThangComponentEditView'
ThangComponentsEditView = require 'views/editor/component/ThangComponentsEditView'
ThangType = require 'models/ThangType'
module.exports = class LevelThangEditView extends CocoView
###
In the level editor, is the bar at the top when editing a single thang.
Everything below is part of the ThangComponentEditView, which is shared with the
Everything below is part of the ThangComponentsEditView, which is shared with the
ThangType editor view.
###
@ -43,7 +43,7 @@ module.exports = class LevelThangEditView extends CocoView
world: @world
callback: @onComponentsChanged
@thangComponentEditView = new ThangComponentEditView options
@thangComponentEditView = new ThangComponentsEditView options
@insertSubView @thangComponentEditView
thangTypeNames = (m.get('name') for m in @supermodel.getModels ThangType)
input = @$el.find('#thang-type-link input').autocomplete(source: thangTypeNames, minLength: 0, delay: 0, autoFocus: true)

View file

@ -6,17 +6,18 @@ Camera = require 'lib/surface/Camera'
DocumentFiles = require 'collections/DocumentFiles'
RootView = require 'views/kinds/RootView'
ThangComponentEditView = require 'views/editor/component/ThangComponentEditView'
ThangComponentsEditView = require 'views/editor/component/ThangComponentsEditView'
ThangTypeVersionsModal = require './ThangTypeVersionsModal'
ThangTypeColorsTabView = require './ThangTypeColorsTabView'
PatchesView = require 'views/editor/PatchesView'
SaveVersionModal = require 'views/modal/SaveVersionModal'
template = require 'templates/editor/thang/edit'
template = require 'templates/editor/thang/thang-type-edit-view'
CENTER = {x: 200, y: 300}
module.exports = class ThangTypeEditView extends RootView
id: 'editor-thang-type-edit-view'
id: 'thang-type-edit-view'
className: 'editor'
template: template
startsLoading: true
resolution: 4
@ -49,7 +50,6 @@ module.exports = class ThangTypeEditView extends RootView
@thangType = @supermodel.loadModel(@thangType, 'thang').model
@thangType.saveBackups = true
@listenToOnce @thangType, 'sync', ->
console.log 'files for?', @thangType.id, @thangType.get 'name'
@files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model
@refreshAnimation = _.debounce @refreshAnimation, 500
@ -82,7 +82,7 @@ module.exports = class ThangTypeEditView extends RootView
components: @thangType.get('components') ? []
supermodel: @supermodel
callback: @onComponentsChanged
@thangComponentEditView = new ThangComponentEditView options
@thangComponentEditView = new ThangComponentsEditView options
@insertSubView @thangComponentEditView
onComponentsChanged: (components) =>

View file

@ -86,6 +86,8 @@ module.exports = class CocoView extends Backbone.View
render: ->
return @ unless me
view.destroy() for id, view of @subviews
@subviews = {}
super()
return @template if _.isString(@template)
@$el.html @template(@getRenderData())
@ -248,18 +250,30 @@ module.exports = class CocoView extends Backbone.View
# Subviews
insertSubView: (view, elToReplace=null) ->
key = view.id or (view.constructor.name+classCount++)
key = _.string.underscored(key) # handy for autocomplete in dev console
# used to insert views with ids
key = @makeSubViewKey(view)
@subviews[key].destroy() if key of @subviews
elToReplace ?= @$el.find('#'+view.id)
elToReplace.after(view.el).remove()
view.parent = @
@registerSubView(view, key)
view.render()
view.afterInsert()
view
registerSubView: (view, key) ->
# used to register views which are custom inserted into the view,
# like views where you add multiple instances of them
key = @makeSubViewKey(view)
view.parent = @
view.parentKey = key
@subviews[key] = view
view
makeSubViewKey: (view) ->
key = view.id or (view.constructor.name+classCount++)
key = _.string.underscored(key) # handy for autocomplete in dev console
key
removeSubView: (view) ->
view.$el.empty()
delete @subviews[view.parentKey]

View file

@ -40,7 +40,7 @@
"jsondiffpatch": "~0.1.5",
"nanoscroller": "~0.8.0",
"jquery.tablesorter": "~2.15.13",
"treema": "~0.0.9",
"treema": "~0.0.12",
"bootstrap": "~3.1.1",
"validated-backbone-mediator": "~0.1.3",
"jquery.browser": "~0.0.6",

View file

@ -1,6 +1,6 @@
describe 'require', ->
it 'has no modules that error when you import them', ->
modules = window.require.list();
modules = window.require.list()
for module in modules
try
require(module)

View file

@ -1,2 +0,0 @@
module.exports.sendTestResponses = (responseMap) ->