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 { module.exports = ThangComponentSchema = c.object {
title: 'Component' title: 'Component'
description: 'Configuration for a Component that this Thang uses.' description: 'Configuration for a Component that this Thang uses.'
format: 'thang-component' format: 'component-reference'
required: ['original', 'majorVersion'] required: ['original', 'majorVersion']
'default': 'default':
majorVersion: 0 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 #extant-components-column
left: 0 left: 0
width: 20% width: 300px
.treema .treema
position: absolute position: absolute
@ -29,26 +29,23 @@
bottom: 0 bottom: 0
overflow: scroll overflow: scroll
#add-component-column .dependent
right: 0 background-color: rgba(128, 64, 255, 0.10)
width: 20%
.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 position: absolute
top: 75px top: 80px
left: 10px
right: 0px
bottom: 0 bottom: 0
right: 0
left: 20px
overflow: scroll 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 .editor
&, #level-editor-top-nav h1, h2, h3, h4, h5, h6, a
// min-width: 1024px font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important
a
font-family: helvetica, arial, sans serif
position: absolute position: absolute
top: 0px top: 0px
@ -48,7 +45,7 @@
border-radius: 0 border-radius: 0
.navbar-right .navbar-right
// not sure why bootstrap puts a big negative margin in, but this overrides it // not sure why bootstrap puts a big negative margin in, but this overrides it
margin-right: 10px margin-right: 10px !important
float: right float: right
.dropdown-menu .dropdown-menu
@ -130,9 +127,7 @@
margin-top: -10px margin-top: -10px
padding-top: 10px padding-top: 10px
// keeps the editor tabs a certain height #level-editor-tabs, #thang-type-edit-view .tab-content
#level-editor-tabs
position: absolute position: absolute
left: 15px left: 15px
right: 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 &, & > div.ace_editor
width: 100% width: 100%
& > div.ace_editor
height: 400px
.treema-markdown.treema-display, .treema-coffee.treema-display, .treema-javascript.treema-display .treema-markdown.treema-display, .treema-coffee.treema-display, .treema-javascript.treema-display
width: 100% width: 100%
border: 3px inset rgba(0, 100, 100, 0.2) 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) super(valEl)
if not @schema.aceMode and mode = codeLanguages[@keyForParent] if not @schema.aceMode and mode = codeLanguages[@keyForParent]
@editor.getSession().setMode mode @editor.getSession().setMode mode
@editor.on('change', @onEditorChange)
valEl valEl
onEditorChange: =>
@saveChanges()
@flushChanges()
@getRoot().broadcastChanges()
class CoffeeTreema extends CodeTreema class CoffeeTreema extends CodeTreema
constructor: -> constructor: ->
super(arguments...) super(arguments...)
@ -308,26 +302,19 @@ class LatestVersionReferenceNode extends TreemaNode
input.focus().keyup @search input.focus().keyup @search
input.attr('placeholder', @formatDocument(@data)) if @data input.attr('placeholder', @formatDocument(@data)) if @data
buildSearchURL: (term) -> "#{@url}?term=#{term}&project=true"
search: => search: =>
term = @getValEl().find('input').val() term = @getValEl().find('input').val()
return if term is @lastTerm return if term is @lastTerm
# HACK while search is broken
if @collection
@lastTerm = term
@searchCallback()
return
@getSearchResultsEl().empty() if @lastTerm and not term @getSearchResultsEl().empty() if @lastTerm and not term
return unless term return unless term
@lastTerm = term @lastTerm = term
@getSearchResultsEl().empty().append('Searching') @getSearchResultsEl().empty().append('Searching')
@collection = new LatestVersionCollection([], model: @model) @collection = new LatestVersionCollection([], model: @model)
# HACK while search is broken @collection.url = @buildSearchURL(term)
# @collection.url = "#{@url}?term=#{term}&project=true"
@collection.url = "#{@url}?term=#{''}&project=true"
@collection.fetch() @collection.fetch()
@collection.once 'sync', @searchCallback, @ @collection.once 'sync', @searchCallback, @
@ -338,10 +325,6 @@ class LatestVersionReferenceNode extends TreemaNode
row = $('<div></div>').addClass('treema-search-result-row') row = $('<div></div>').addClass('treema-search-result-row')
text = @formatDocument(model) text = @formatDocument(model)
continue unless text? continue unless text?
# HACK while search is broken
continue unless text.toLowerCase().indexOf(@lastTerm.toLowerCase()) >= 0
row.addClass('treema-search-selected') if first row.addClass('treema-search-selected') if first
first = false first = false
row.text(text) row.text(text)
@ -354,9 +337,10 @@ class LatestVersionReferenceNode extends TreemaNode
getSearchResultsEl: -> @getValEl().find('.treema-search-results') getSearchResultsEl: -> @getValEl().find('.treema-search-results')
getSelectedResultEl: -> @getValEl().find('.treema-search-selected') getSelectedResultEl: -> @getValEl().find('.treema-search-selected')
modelToString: (model) -> model.get('name')
formatDocument: (docOrModel) -> formatDocument: (docOrModel) ->
doc = docOrModel.attributes or docOrModel return @modelToString(docOrModel) if docOrModel instanceof CocoModel
return doc.name if doc.name?
return 'Unknown' unless @settings.supermodel? return 'Unknown' unless @settings.supermodel?
m = CocoModel.getReferencedModel(@data, @schema) m = CocoModel.getReferencedModel(@data, @schema)
urlGoingFor = m.url() urlGoingFor = m.url()
@ -366,7 +350,7 @@ class LatestVersionReferenceNode extends TreemaNode
m.url = -> urlGoingFor m.url = -> urlGoingFor
@settings.supermodel.registerModel(m) @settings.supermodel.registerModel(m)
return 'Unknown' unless m return 'Unknown' unless m
return m.get('name') return @modelToString(m)
saveChanges: -> saveChanges: ->
selected = @getSelectedResultEl() selected = @getSelectedResultEl()
@ -379,10 +363,12 @@ class LatestVersionReferenceNode extends TreemaNode
@instance = fullValue @instance = fullValue
onDownArrowPressed: (e) -> onDownArrowPressed: (e) ->
return super(arguments...) unless @isEditing()
@navigateSearch(1) @navigateSearch(1)
e.preventDefault() e.preventDefault()
onUpArrowPressed: (e) -> onUpArrowPressed: (e) ->
return super(arguments...) unless @isEditing()
e.preventDefault() e.preventDefault()
@navigateSearch(-1) @navigateSearch(-1)
@ -411,6 +397,13 @@ class LatestVersionReferenceNode extends TreemaNode
selected = @getSelectedResultEl() selected = @getSelectedResultEl()
return not selected.length 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) LatestVersionReferenceNode.prototype.search = _.debounce(LatestVersionReferenceNode.prototype.search, 200)
@ -431,6 +424,7 @@ module.exports.setup = ->
TreemaNode.setNodeSubclass('javascript', JavaScriptTreema) TreemaNode.setNodeSubclass('javascript', JavaScriptTreema)
TreemaNode.setNodeSubclass('image-file', ImageFileTreema) TreemaNode.setNodeSubclass('image-file', ImageFileTreema)
TreemaNode.setNodeSubclass('latest-version-reference', LatestVersionReferenceNode) TreemaNode.setNodeSubclass('latest-version-reference', LatestVersionReferenceNode)
TreemaNode.setNodeSubclass('component-reference', LevelComponentReferenceNode)
TreemaNode.setNodeSubclass('i18n', InternationalizationNode) TreemaNode.setNodeSubclass('i18n', InternationalizationNode)
TreemaNode.setNodeSubclass('sound-file', SoundFileTreema) TreemaNode.setNodeSubclass('sound-file', SoundFileTreema)
TreemaNode.setNodeSubclass 'slug-props', SlugPropsObject TreemaNode.setNodeSubclass 'slug-props', SlugPropsObject

View file

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

View file

@ -1,12 +1,12 @@
CocoView = require 'views/kinds/CocoView' CocoView = require 'views/kinds/CocoView'
template = require 'templates/editor/level/thang/edit' template = require 'templates/editor/level/thang/edit'
ThangComponentEditView = require 'views/editor/component/ThangComponentEditView' ThangComponentsEditView = require 'views/editor/component/ThangComponentsEditView'
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
module.exports = class LevelThangEditView extends CocoView module.exports = class LevelThangEditView extends CocoView
### ###
In the level editor, is the bar at the top when editing a single thang. 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. ThangType editor view.
### ###
@ -43,7 +43,7 @@ module.exports = class LevelThangEditView extends CocoView
world: @world world: @world
callback: @onComponentsChanged callback: @onComponentsChanged
@thangComponentEditView = new ThangComponentEditView options @thangComponentEditView = new ThangComponentsEditView options
@insertSubView @thangComponentEditView @insertSubView @thangComponentEditView
thangTypeNames = (m.get('name') for m in @supermodel.getModels ThangType) 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) 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' DocumentFiles = require 'collections/DocumentFiles'
RootView = require 'views/kinds/RootView' RootView = require 'views/kinds/RootView'
ThangComponentEditView = require 'views/editor/component/ThangComponentEditView' ThangComponentsEditView = require 'views/editor/component/ThangComponentsEditView'
ThangTypeVersionsModal = require './ThangTypeVersionsModal' ThangTypeVersionsModal = require './ThangTypeVersionsModal'
ThangTypeColorsTabView = require './ThangTypeColorsTabView' ThangTypeColorsTabView = require './ThangTypeColorsTabView'
PatchesView = require 'views/editor/PatchesView' PatchesView = require 'views/editor/PatchesView'
SaveVersionModal = require 'views/modal/SaveVersionModal' 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} CENTER = {x: 200, y: 300}
module.exports = class ThangTypeEditView extends RootView module.exports = class ThangTypeEditView extends RootView
id: 'editor-thang-type-edit-view' id: 'thang-type-edit-view'
className: 'editor'
template: template template: template
startsLoading: true startsLoading: true
resolution: 4 resolution: 4
@ -49,7 +50,6 @@ module.exports = class ThangTypeEditView extends RootView
@thangType = @supermodel.loadModel(@thangType, 'thang').model @thangType = @supermodel.loadModel(@thangType, 'thang').model
@thangType.saveBackups = true @thangType.saveBackups = true
@listenToOnce @thangType, 'sync', -> @listenToOnce @thangType, 'sync', ->
console.log 'files for?', @thangType.id, @thangType.get 'name'
@files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model @files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model
@refreshAnimation = _.debounce @refreshAnimation, 500 @refreshAnimation = _.debounce @refreshAnimation, 500
@ -82,7 +82,7 @@ module.exports = class ThangTypeEditView extends RootView
components: @thangType.get('components') ? [] components: @thangType.get('components') ? []
supermodel: @supermodel supermodel: @supermodel
callback: @onComponentsChanged callback: @onComponentsChanged
@thangComponentEditView = new ThangComponentEditView options @thangComponentEditView = new ThangComponentsEditView options
@insertSubView @thangComponentEditView @insertSubView @thangComponentEditView
onComponentsChanged: (components) => onComponentsChanged: (components) =>

View file

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

View file

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

View file

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

View file

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