Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-12-20 20:02:03 -08:00
commit 69118206ab
26 changed files with 149 additions and 37 deletions

View file

@ -58,7 +58,7 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
console.error 'Unable to save G+ token key', e console.error 'Unable to save G+ token key', e
@accessToken = e @accessToken = e
@trigger 'logged-in' @trigger 'logged-in'
loginCodeCombat: -> loginCodeCombat: ->
# email and profile data loaded separately # email and profile data loaded separately
gapi.client.request(path: plusURL, callback: @onPersonEntityReceived) gapi.client.request(path: plusURL, callback: @onPersonEntityReceived)
@ -71,7 +71,8 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
for gpProp, userProp of userPropsToSave for gpProp, userProp of userPropsToSave
keys = gpProp.split('.') keys = gpProp.split('.')
value = r value = r
value = value[key] for key in keys for key in keys
value = value[key]
if value and not me.get(userProp) if value and not me.get(userProp)
@shouldSave = true @shouldSave = true
me.set(userProp, value) me.set(userProp, value)

View file

@ -459,6 +459,44 @@ class SlugPropsObject extends TreemaNode.nodeMap.object
return res if @workingSchema.properties?[res]? return res if @workingSchema.properties?[res]?
_.string.slugify(res) _.string.slugify(res)
class TaskTreema extends TreemaNode.nodeMap.string
buildValueForDisplay: (valEl) ->
@taskCheckbox = $('<input type="checkbox">').prop 'checked', @data.complete
task = $("<span>#{@data.name}</span>")
valEl.append(@taskCheckbox).append(task)
@taskCheckbox.on 'change', @onTaskChanged
buildValueForEditing: (valEl, data) ->
@nameInput = @buildValueForEditingSimply(valEl, data.name)
@nameInput.parent().prepend(@taskCheckbox)
onTaskChanged: (e) =>
@markAsChanged()
@saveChanges()
@flushChanges()
@broadcastChanges()
onEditInputBlur: (e) =>
@markAsChanged()
@saveChanges()
if @isValid() then @display() if @isEditing() else @nameInput.focus().select()
@flushChanges()
@broadcastChanges()
saveChanges: (oldData) ->
@data ?= {}
@data.name = @nameInput.val() if @nameInput
@data.complete = Boolean(@taskCheckbox.prop 'checked')
destroy: ->
@taskCheckbox.off()
super()
#class CheckboxTreema extends TreemaNode.nodeMap.boolean
# TODO: try this out
module.exports.setup = -> module.exports.setup = ->
TreemaNode.setNodeSubclass('date-time', DateTimeTreema) TreemaNode.setNodeSubclass('date-time', DateTimeTreema)
TreemaNode.setNodeSubclass('version', VersionTreema) TreemaNode.setNodeSubclass('version', VersionTreema)
@ -475,3 +513,5 @@ module.exports.setup = ->
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
TreemaNode.setNodeSubclass 'task', TaskTreema
#TreemaNode.setNodeSubclass 'checkbox', CheckboxTreema

View file

@ -62,9 +62,9 @@ module.exports = class SpriteParser
break break
continue unless container.bounds and instructions.length continue unless container.bounds and instructions.length
@addContainer {c: instructions, b: container.bounds}, container.name @addContainer {c: instructions, b: container.bounds}, container.name
childrenMovieClips = [] childrenMovieClips = []
for movieClip, index in movieClips for movieClip, index in movieClips
lastBounds = null lastBounds = null
# fill in bounds which are null... # fill in bounds which are null...
@ -73,7 +73,7 @@ module.exports = class SpriteParser
movieClip.frameBounds[boundsIndex] = _.clone(lastBounds) movieClip.frameBounds[boundsIndex] = _.clone(lastBounds)
else else
lastBounds = bounds lastBounds = bounds
localGraphics = @getGraphicsFromBlock(movieClip, source) localGraphics = @getGraphicsFromBlock(movieClip, source)
[shapeKeys, localShapes] = @getShapesFromBlock movieClip, source [shapeKeys, localShapes] = @getShapesFromBlock movieClip, source
localContainers = @getContainersFromMovieClip movieClip, source, true localContainers = @getContainersFromMovieClip movieClip, source, true
@ -90,7 +90,7 @@ module.exports = class SpriteParser
bounds: movieClip.bounds bounds: movieClip.bounds
frameBounds: movieClip.frameBounds frameBounds: movieClip.frameBounds
}, movieClip.name }, movieClip.name
for movieClip in movieClips for movieClip in movieClips
if movieClip.name not in childrenMovieClips if movieClip.name not in childrenMovieClips
for bounds in movieClip.frameBounds for bounds in movieClip.frameBounds
@ -390,7 +390,7 @@ module.exports = class SpriteParser
name = node.callee.property?.name name = node.callee.property?.name
return unless name in ['get', 'to', 'wait'] return unless name in ['get', 'to', 'wait']
return if name is 'get' and callExpressions.length # avoid Ease calls in the tweens return if name is 'get' and callExpressions.length # avoid Ease calls in the tweens
flattenedRanges = _.flatten [a.range for a in node.arguments] flattenedRanges = _.flatten [(a.range for a in node.arguments)]
range = [_.min(flattenedRanges), _.max(flattenedRanges)] range = [_.min(flattenedRanges), _.max(flattenedRanges)]
# Replace 'this.<local>' references with just the 'name' # Replace 'this.<local>' references with just the 'name'
argsSource = @subSourceFromRange(range, source) argsSource = @subSourceFromRange(range, source)

View file

@ -326,7 +326,7 @@ module.exports = Lank = class Lank extends CocoClass
newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1 newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1
newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1 newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1
if @thang?.spriteName is 'Beam' if @layer?.name is 'Land' or @thang?.spriteName is 'Beam'
@scaleFactorX = newScaleFactorX @scaleFactorX = newScaleFactorX
@scaleFactorY = newScaleFactorY @scaleFactorY = newScaleFactorY
else if @thang and (newScaleFactorX isnt @targetScaleFactorX or newScaleFactorY isnt @targetScaleFactorY) else if @thang and (newScaleFactorX isnt @targetScaleFactorX or newScaleFactorY isnt @targetScaleFactorY)

View file

@ -65,12 +65,12 @@ module.exports = class SingularSprite extends createjs.Sprite
@regY = -reg.y * scale @regY = -reg.y * scale
@scaleX = @scaleY = 1 / @resolutionFactor @scaleX = @scaleY = 1 / @resolutionFactor
if @camera and @thangType.get('name') in floors
@baseScaleY *= @camera.y2x
@scaleX *= -1 if action.flipX @scaleX *= -1 if action.flipX
@scaleY *= -1 if action.flipY @scaleY *= -1 if action.flipY
@baseScaleX = @scaleX @baseScaleX = @scaleX
@baseScaleY = @scaleY @baseScaleY = @scaleY
if @camera and @thangType.get('name') in floors
@baseScaleY *= @camera.y2x
@currentAnimation = actionName @currentAnimation = actionName
return return

View file

@ -46,7 +46,8 @@ module.exports = class System
hashString: (s) -> hashString: (s) ->
return @hashes[s] if s of @hashes return @hashes[s] if s of @hashes
hash = 0 hash = 0
hash = hash * 31 + s.charCodeAt(i) for i in [0 ... Math.min(s.length, 100)] for i in [0 ... Math.min(s.length, 100)]
hash = hash * 31 + s.charCodeAt(i)
hash = @hashes[s] = hash % 3.141592653589793 hash = @hashes[s] = hash % 3.141592653589793
hash hash

View file

@ -480,6 +480,8 @@
forum_prefix: "For anything public, please try " forum_prefix: "For anything public, please try "
forum_page: "our forum" forum_page: "our forum"
forum_suffix: " instead." forum_suffix: " instead."
faq_prefix: "There's also a"
faq: "FAQ"
subscribe_prefix: "If you need help figuring out a level, please" subscribe_prefix: "If you need help figuring out a level, please"
subscribe: "buy a CodeCombat subscription" subscribe: "buy a CodeCombat subscription"
subscribe_suffix: "and we'll be happy to help you with your code." subscribe_suffix: "and we'll be happy to help you with your code."

View file

@ -1,6 +1,48 @@
c = require './../schemas' c = require './../schemas'
ThangComponentSchema = require './thang_component' ThangComponentSchema = require './thang_component'
defaultTasks = [
'Name the level.'
'Create a Referee stub, if needed.'
'Build the level.'
'Set up goals.'
'Choose the Existence System lifespan and frame rate.'
'Choose the UI System paths and coordinate hover if needed.'
'Choose the AI System pathfinding and Vision System line of sight.'
'Write the sample code.'
'Do basic set decoration.'
'Adjust script camera bounds.'
'Choose music file in Introduction script.'
'Add to a campaign.'
'Publish for playtesting.'
'Choose level options like required/restricted gear.'
'Create achievements, including unlocking next level.'
'Playtest with a slow/tough hero.'
'Playtest with a fast/weak hero.'
'Playtest with a couple random seeds.'
'Make sure the level ends promptly on success and failure.'
'Remove/simplify unnecessary doodad collision.'
'Release to adventurers.'
'Write the description.'
'Translate the sample code comments.'
'Add Io/Clojure/Lua/CoffeeScript.'
'Write the guide.'
'Write a loading tip, if needed.'
'Populate i18n.'
'Mark whether it requires a subscription (after adventurer week).'
'Release to everyone.'
'Check completion/engagement/problem analytics.'
'Do any custom scripting, if needed.'
'Do thorough set decoration.'
'Add a walkthrough video.'
]
SpecificArticleSchema = c.object() SpecificArticleSchema = c.object()
c.extendNamedProperties SpecificArticleSchema # name first c.extendNamedProperties SpecificArticleSchema # name first
SpecificArticleSchema.properties.body = {type: 'string', title: 'Content', description: 'The body content of the article, in Markdown.', format: 'markdown'} SpecificArticleSchema.properties.body = {type: 'string', title: 'Content', description: 'The body content of the article, in Markdown.', format: 'markdown'}
@ -252,6 +294,7 @@ _.extend LevelSchema.properties,
terrain: c.terrainString terrain: c.terrainString
showsGuide: c.shortString(title: 'Shows Guide', description: 'If the guide is shown at the beginning of the level.', 'enum': ['first-time', 'always']) showsGuide: c.shortString(title: 'Shows Guide', description: 'If the guide is shown at the beginning of the level.', 'enum': ['first-time', 'always'])
requiresSubscription: {title: 'Requires Subscription', description: 'Whether this level is available to subscribers only.', type: 'boolean'} requiresSubscription: {title: 'Requires Subscription', description: 'Whether this level is available to subscribers only.', type: 'boolean'}
tasks: c.array {title: 'Tasks', description: 'Tasks to be completed for this level.', default: (name: t for t in defaultTasks)}, c.task
c.extendBasicProperties LevelSchema, 'level' c.extendBasicProperties LevelSchema, 'level'
c.extendSearchableProperties LevelSchema c.extendSearchableProperties LevelSchema

View file

@ -228,3 +228,7 @@ me.RewardSchema = (descriptionFragment='earned by achievements') ->
levels: me.array {uniqueItems: true, description: "Levels #{descriptionFragment}."}, levels: me.array {uniqueItems: true, description: "Levels #{descriptionFragment}."},
me.stringID(links: [{rel: 'db', href: '/db/level/{($)}/version'}], title: 'Level', description: 'A reference to the earned Level.', format: 'latest-version-original-reference') me.stringID(links: [{rel: 'db', href: '/db/level/{($)}/version'}], title: 'Level', description: 'A reference to the earned Level.', format: 'latest-version-original-reference')
gems: me.int {description: "Gems #{descriptionFragment}."} gems: me.int {description: "Gems #{descriptionFragment}."}
me.task = me.object {title: 'Task', description: 'A task to be completed', format: 'task', default: {name: 'TODO', complete: false}},
name: {title: 'Name', description: 'What must be done?', type: 'string'}
complete: {title: 'Complete', description: 'Whether this task is done.', type: 'boolean', format: 'checkbox'}

View file

@ -378,3 +378,8 @@ body > iframe[src^="https://apis.google.com"]
.progress-bar .progress-bar
background-color: lightblue background-color: lightblue
.treema-node input[type='checkbox']
@include scale(1.25)
width: auto
margin: 8px 15px 8px 15px

View file

@ -33,6 +33,9 @@
#components-treema #components-treema
z-index: 11 z-index: 11
.not-present
opacity: 0.75
.edit-component-container .edit-component-container
margin-left: 290px margin-left: 290px
position: absolute position: absolute

View file

@ -9,10 +9,11 @@ block content
li.active(data-i18n="#{currentEditor}") li.active(data-i18n="#{currentEditor}")
| #{currentEditor} | #{currentEditor}
if me.get('anonymous') if me.isAdmin() || !newModelsAdminOnly
a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="core/AuthModal", role="button", data-i18n="#{currentNewSignup}") Log in to Create a New Content if me.get('anonymous')
else a.btn.btn-primary.open-modal-button(data-toggle="coco-modal", data-target="core/AuthModal", role="button", data-i18n="#{currentNewSignup}") Log in to Create a New Something
a.btn.btn-primary.open-modal-button#new-model-button(data-i18n="#{currentNew}") Create a New Something else
a.btn.btn-primary.open-modal-button#new-model-button(data-i18n="#{currentNew}") Create a New Something
input#search(data-i18n="[placeholder]#{currentSearch}") input#search(data-i18n="[placeholder]#{currentSearch}")
hr hr
div.results div.results

View file

@ -9,6 +9,9 @@ block modal-body-content
span.spl(data-i18n="contact.forum_prefix") For anything public, please try span.spl(data-i18n="contact.forum_prefix") For anything public, please try
a(href="http://discourse.codecombat.com/", data-i18n="contact.forum_page") our forum a(href="http://discourse.codecombat.com/", data-i18n="contact.forum_page") our forum
span(data-i18n="contact.forum_suffix") instead. span(data-i18n="contact.forum_suffix") instead.
span.spl.spr(data-i18n="contact.faq_prefix") There's also a
a(data-i18n="contact.faq", href="http://discourse.codecombat.com/t/faq-check-before-posting/1027") FAQ
| .
if me.isPremium() if me.isPremium()
p(data-i18n="contact.subscriber_support") Since you're a CodeCombat subscriber, your email will get our priority support. p(data-i18n="contact.subscriber_support") Since you're a CodeCombat subscriber, your email will get our priority support.
else else

View file

@ -92,9 +92,9 @@ block header
li(class=anonymous ? "disabled": "") li(class=anonymous ? "disabled": "")
a(data-i18n="common.fork")#fork-start-button Fork a(data-i18n="common.fork")#fork-start-button Fork
li(class=anonymous ? "disabled": "") li(class=anonymous ? "disabled": "")
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=anonymous)#revert-button Revert
li(class=anonymous ? "disabled": "") li(class=anonymous ? "disabled": "")
a(data-toggle="coco-modal", data-target="editor/level/modals/GenerateTerrainModal", data-i18n="editor.generate_terrain").generate-terrain-button Generate Terrain a(data-toggle="coco-modal", data-target="editor/level/modals/GenerateTerrainModal", data-i18n="editor.generate_terrain", disabled=anonymous).generate-terrain-button Generate Terrain
li(class=anonymous ? "disabled": "") li(class=anonymous ? "disabled": "")
a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n
li.divider li.divider

View file

@ -52,11 +52,11 @@ block header
span.glyphicon-chevron-down.glyphicon span.glyphicon-chevron-down.glyphicon
ul.dropdown-menu ul.dropdown-menu
li.dropdown-header(data-i18n="common.actions") Actions li.dropdown-header(data-i18n="common.actions") Actions
li(class=anonymous ? "disabled": "") li(class=!me.isAdmin() ? "disabled": "")
a(data-i18n="common.fork")#fork-start-button Fork a(data-i18n="common.fork")#fork-start-button Fork
li(class=anonymous ? "disabled": "") li(class=!authorized ? "disabled": "")
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=!authorized)#revert-button Revert
li(class=anonymous ? "disabled": "") li(class=!authorized ? "disabled": "")
a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n
li.divider li.divider
li.dropdown-header(data-i18n="common.info") Info li.dropdown-header(data-i18n="common.info") Info

View file

@ -199,6 +199,7 @@ module.exports = class CocoView extends Backbone.View
# special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value. # special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value.
elem = $(e.target) elem = $(e.target)
return unless elem.data('toggle') is 'coco-modal' return unless elem.data('toggle') is 'coco-modal'
return if elem.attr('disabled')
target = elem.data('target') target = elem.data('target')
Modal = require 'views/'+target Modal = require 'views/'+target
e.stopPropagation() e.stopPropagation()

View file

@ -6,7 +6,6 @@ module.exports = class ForkModal extends ModalView
id: 'fork-modal' id: 'fork-modal'
template: template template: template
instant: false instant: false
modalWidthPercent: 60
events: events:
'click #fork-model-confirm-button': 'forkModel' 'click #fork-model-confirm-button': 'forkModel'

View file

@ -14,6 +14,7 @@ module.exports = class AchievementSearchView extends SearchView
context.currentNew = 'editor.new_achievement_title' context.currentNew = 'editor.new_achievement_title'
context.currentNewSignup = 'editor.new_achievement_title_login' context.currentNewSignup = 'editor.new_achievement_title_login'
context.currentSearch = 'editor.achievement_search_title' context.currentSearch = 'editor.achievement_search_title'
context.newModelsAdminOnly = true
context.unauthorized = true unless me.isAdmin() context.unauthorized = true unless me.isAdmin()
@$el.i18n() @$el.i18n()
context context

View file

@ -14,5 +14,6 @@ module.exports = class ArticleSearchView extends SearchView
context.currentNew = 'editor.new_article_title' context.currentNew = 'editor.new_article_title'
context.currentNewSignup = 'editor.new_article_title_login' context.currentNewSignup = 'editor.new_article_title_login'
context.currentSearch = 'editor.article_search_title' context.currentSearch = 'editor.article_search_title'
context.newModelsAdminOnly = true
@$el.i18n() @$el.i18n()
context context

View file

@ -40,7 +40,7 @@ module.exports = class LevelEditView extends RootView
'click .play-with-team-button': 'onPlayLevel' 'click .play-with-team-button': 'onPlayLevel'
'click .play-with-team-parent': 'onPlayLevelTeamSelect' 'click .play-with-team-parent': 'onPlayLevelTeamSelect'
'click #commit-level-start-button': 'startCommittingLevel' 'click #commit-level-start-button': 'startCommittingLevel'
'click #fork-start-button': 'startForking' 'click li:not(.disabled) > #fork-start-button': 'startForking'
'click #level-history-button': 'showVersionHistory' 'click #level-history-button': 'showVersionHistory'
'click #undo-button': 'onUndo' 'click #undo-button': 'onUndo'
'mouseenter #undo-button': 'showUndoDescription' 'mouseenter #undo-button': 'showUndoDescription'
@ -50,7 +50,7 @@ module.exports = class LevelEditView extends RootView
'click #components-tab': -> @subviews.editor_level_components_tab_view.refreshLevelThangsTreema @level.get('thangs') 'click #components-tab': -> @subviews.editor_level_components_tab_view.refreshLevelThangsTreema @level.get('thangs')
'click #level-patch-button': 'startPatchingLevel' 'click #level-patch-button': 'startPatchingLevel'
'click #level-watch-button': 'toggleWatchLevel' 'click #level-watch-button': 'toggleWatchLevel'
'click #pop-level-i18n-button': 'onPopulateI18N' 'click li:not(.disabled) > #pop-level-i18n-button': 'onPopulateI18N'
'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab' 'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab'
'mouseup .nav-tabs > li a': 'toggleTab' 'mouseup .nav-tabs > li a': 'toggleTab'
@ -66,7 +66,7 @@ module.exports = class LevelEditView extends RootView
showLoading: ($el) -> showLoading: ($el) ->
$el ?= @$el.find('.outer-content') $el ?= @$el.find('.outer-content')
super($el) super($el)
getTitle: -> "LevelEditor - " + (@level.get('name') or '...') getTitle: -> "LevelEditor - " + (@level.get('name') or '...')
onLoaded: -> onLoaded: ->
@ -170,7 +170,7 @@ module.exports = class LevelEditView extends RootView
button = @$el.find('#level-watch-button') button = @$el.find('#level-watch-button')
@level.watch(button.find('.watch').is(':visible')) @level.watch(button.find('.watch').is(':visible'))
button.find('> span').toggleClass('secret') button.find('> span').toggleClass('secret')
onPopulateI18N: -> onPopulateI18N: ->
@level.populateI18N() @level.populateI18N()
f = -> document.location.reload() f = -> document.location.reload()

View file

@ -31,7 +31,7 @@ module.exports = class ComponentsTabView extends CocoView
thangType = @supermodel.getModelByOriginal ThangType, thang.thangType thangType = @supermodel.getModelByOriginal ThangType, thang.thangType
for component in thangType.get('components') ? [] for component in thangType.get('components') ? []
componentMap[component.original] = component componentMap[component.original] = component
for component in thang.components for component in thang.components
componentMap[component.original] = component componentMap[component.original] = component
@ -45,9 +45,10 @@ module.exports = class ComponentsTabView extends CocoView
componentModelMap = {} componentModelMap = {}
componentModelMap[comp.get('original')] = comp for comp in componentModels componentModelMap[comp.get('original')] = comp for comp in componentModels
components = ({original: key.split('.')[0], majorVersion: parseInt(key.split('.')[1], 10), thangs: value, count: value.length} for key, value of @presentComponents) components = ({original: key.split('.')[0], majorVersion: parseInt(key.split('.')[1], 10), thangs: value, count: value.length} for key, value of @presentComponents)
treemaData = _.sortBy components, (comp) -> components = components.concat ({original: c.get('original'), majorVersion: c.get('version').major, thangs: [], count: 0} for c in componentModels when not @presentComponents[c.get('original') + '.' + c.get('version').major])
comp = componentModelMap[comp.original] treemaData = _.sortBy components, (comp) =>
res = [comp.get('system'), comp.get('name')] component = componentModelMap[comp.original]
res = [(if comp.count then 0 else 1), component.get('system'), component.get('name')]
return res return res
treemaOptions = treemaOptions =
@ -82,7 +83,7 @@ module.exports = class ComponentsTabView extends CocoView
onLevelComponentEditingEnded: (e) -> onLevelComponentEditingEnded: (e) ->
@removeSubView @levelComponentEditView @removeSubView @levelComponentEditView
@levelComponentEditView = null @levelComponentEditView = null
destroy: -> destroy: ->
@componentsTreema?.destroy() @componentsTreema?.destroy()
super() super()
@ -98,4 +99,6 @@ class LevelComponentNode extends TreemaObjectNode
comp = _.find @settings.supermodel.getModels(LevelComponent), (m) => comp = _.find @settings.supermodel.getModels(LevelComponent), (m) =>
m.get('original') is data.original and m.get('version').major is data.majorVersion m.get('original') is data.original and m.get('version').major is data.majorVersion
name = "#{comp.get('system')}.#{comp.get('name')} v#{comp.get('version').major}" name = "#{comp.get('system')}.#{comp.get('name')} v#{comp.get('version').major}"
@buildValueForDisplaySimply valEl, "#{name} (#{count})" result = @buildValueForDisplaySimply valEl, "#{name} (#{count})"
result.addClass 'not-present' unless data.count
result

View file

@ -14,7 +14,8 @@ module.exports = class SettingsTabView extends CocoView
# not thangs or scripts or the backend stuff # not thangs or scripts or the backend stuff
editableSettings: [ editableSettings: [
'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals', 'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals',
'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription' 'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription',
'tasks'
] ]
subscriptions: subscriptions:

View file

@ -45,13 +45,13 @@ module.exports = class ThangTypeEditView extends RootView
'click #stop-button': 'stopAnimation' 'click #stop-button': 'stopAnimation'
'click #play-button': 'playAnimation' 'click #play-button': 'playAnimation'
'click #history-button': 'showVersionHistory' 'click #history-button': 'showVersionHistory'
'click #fork-start-button': 'startForking' 'click li:not(.disabled) > #fork-start-button': 'startForking'
'click #save-button': 'openSaveModal' 'click #save-button': 'openSaveModal'
'click #patches-tab': -> @patchesView.load() 'click #patches-tab': -> @patchesView.load()
'click .play-with-level-button': 'onPlayLevel' 'click .play-with-level-button': 'onPlayLevel'
'click .play-with-level-parent': 'onPlayLevelSelect' 'click .play-with-level-parent': 'onPlayLevelSelect'
'keyup .play-with-level-input': 'onPlayLevelKeyUp' 'keyup .play-with-level-input': 'onPlayLevelKeyUp'
'click #pop-level-i18n-button': 'onPopulateLevelI18N' 'click li:not(.disabled) > #pop-level-i18n-button': 'onPopulateLevelI18N'
onClickSetVectorIcon: -> onClickSetVectorIcon: ->

View file

@ -15,6 +15,7 @@ module.exports = class ThangTypeSearchView extends SearchView
context.currentNew = 'editor.new_thang_title' context.currentNew = 'editor.new_thang_title'
context.currentNewSignup = 'editor.new_thang_title_login' context.currentNewSignup = 'editor.new_thang_title_login'
context.currentSearch = 'editor.thang_search_title' context.currentSearch = 'editor.thang_search_title'
context.newModelsAdminOnly = true
@$el.i18n() @$el.i18n()
context context

View file

@ -312,7 +312,8 @@ module.exports = class SpellView extends CocoView
# Lock contiguous section of default code # Lock contiguous section of default code
# Only works for languages without closing delimeters on blocks currently # Only works for languages without closing delimeters on blocks currently
lines = @aceDoc.getAllLines() lines = @aceDoc.getAllLines()
lastRow = row for line, row in lines when not /^\s*$/.test(line) for line, row in lines when not /^\s*$/.test(line)
lastRow = row
if lastRow? if lastRow?
@readOnlyRanges.push new Range 0, 0, lastRow, lines[lastRow].length - 1 @readOnlyRanges.push new Range 0, 0, lastRow, lines[lastRow].length - 1

View file

@ -31,6 +31,7 @@ LevelHandler = class LevelHandler extends Handler
'i18nCoverage' 'i18nCoverage'
'loadingTip' 'loadingTip'
'requiresSubscription' 'requiresSubscription'
'tasks'
] ]
postEditableProperties: ['name'] postEditableProperties: ['name']
@ -72,7 +73,7 @@ LevelHandler = class LevelHandler extends Handler
Session.findOne(sessionQuery).exec (err, doc) => Session.findOne(sessionQuery).exec (err, doc) =>
return @sendDatabaseError(res, err) if err return @sendDatabaseError(res, err) if err
return @sendSuccess(res, doc) if doc? return @sendSuccess(res, doc) if doc?
return @sendPaymentRequiredError(res, err) if (not req.user.isPremium()) and level.get('requiresSubscription') return @sendPaymentRequiredError(res, err) if (not req.user.isPremium()) and level.get('requiresSubscription')
@createAndSaveNewSession sessionQuery, req, res @createAndSaveNewSession sessionQuery, req, res
createAndSaveNewSession: (sessionQuery, req, res) => createAndSaveNewSession: (sessionQuery, req, res) =>