From 76b08f35bb3fb572e3bf8f149d27f21370315af4 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 21 Aug 2014 15:44:51 -0700 Subject: [PATCH 01/98] Removed duplicate goal schemas from the deprecated script goal properties. --- app/schemas/models/level.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index d2d4e01aa..2fa229ee1 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -110,8 +110,8 @@ NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes letterbox: {type: 'boolean', title: 'Letterbox', description: 'Turn letterbox mode on or off. Disables surface and playback controls.'} goals: c.object {title: 'Goals (Old)', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'}, - add: c.array {title: 'Add', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'}, GoalSchema - remove: c.array {title: 'Remove', description: 'Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead.'}, GoalSchema + add: c.array {title: 'Add', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'}, {} + remove: c.array {title: 'Remove', description: 'Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead.'}, {} playback: c.object {title: 'Playback', description: 'Control the playback of the level.'}, playing: {type: 'boolean', title: 'Set Playing', description: 'Set whether playback is playing or paused.'} From e200889f0bb708e52b36399f6975b11378db429a Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 22 Aug 2014 11:11:05 -0700 Subject: [PATCH 02/98] Initial refactoring to use the new version of Treema. --- app/treema-ext.coffee | 44 +++++---- app/views/account/JobProfileTreemaView.coffee | 16 ++-- .../level/components/ComponentsTabView.coffee | 13 +-- .../level/scripts/ScriptsTabView.coffee | 22 ++--- .../level/systems/SystemsTabView.coffee | 17 ++-- .../editor/level/thangs/ThangsTabView.coffee | 10 +- app/views/editor/level/treema_nodes.coffee | 93 ++++++++++--------- bower.json | 2 +- 8 files changed, 109 insertions(+), 108 deletions(-) diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index d1f76b9a9..909237d5e 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -5,13 +5,14 @@ locale = require 'locale/locale' class DateTimeTreema extends TreemaNode.nodeMap.string valueClass: 'treema-date-time' - buildValueForDisplay: (el) -> el.text(moment(@data).format('llll')) + buildValueForDisplay: (el, data) -> el.text(moment(data).format('llll')) buildValueForEditing: (valEl) -> @buildValueForEditingSimply valEl, null, 'date' class VersionTreema extends TreemaNode valueClass: 'treema-version' - buildValueForDisplay: (valEl) -> @buildValueForDisplaySimply(valEl, "#{@data.major}.#{@data.minor}") + buildValueForDisplay: (valEl, data) -> + @buildValueForDisplaySimply(valEl, "#{data.major}.#{data.minor}") class LiveEditingMarkup extends TreemaNode.nodeMap.ace valueClass: 'treema-markdown treema-multiline treema-ace' @@ -85,7 +86,7 @@ class SoundFileTreema extends TreemaNode.nodeMap.string getFiles: -> @settings[@soundCollection]?.models or [] - buildValueForDisplay: (valEl) -> + buildValueForDisplay: (valEl, data) -> mimetype = "audio/#{@keyForParent}" pickButton = $('') .click(=> filepicker.pick {mimetypes:[mimetype]}, @onFileChosen) @@ -116,17 +117,17 @@ class SoundFileTreema extends TreemaNode.nodeMap.string .text(filename) menu.append(li) menu.click (e) => - @data = $(e.target).data('fullPath') or @data + @data = $(e.target).data('fullPath') or data @reset() dropdown.append(menu) valEl.append(pickButton) - if @data + if data valEl.append(playButton) valEl.append(stopButton) valEl.append(dropdown) # if files.length and @canEdit() - if @data - path = @data.split('/') + if data + path = data.split('/') name = path[path.length-1] valEl.append($('').text(name)) @@ -136,7 +137,7 @@ class SoundFileTreema extends TreemaNode.nodeMap.string @refreshDisplay() playFile: => - @src = "/file/#{@data}" + @src = "/file/#{@getData()}" if @instance @instance.play() @@ -183,14 +184,14 @@ class ImageFileTreema extends TreemaNode.nodeMap.string return if $(e.target).closest('.btn').length super(arguments...) - buildValueForDisplay: (valEl) -> + buildValueForDisplay: (valEl, data) -> mimetype = 'image/*' pickButton = $(' Upload Picture') .click(=> filepicker.pick {mimetypes:[mimetype]}, @onFileChosen) valEl.append(pickButton) - if @data - valEl.append $('').attr('src', "/file/#{@data}") + if data + valEl.append $('').attr('src', "/file/#{data}") onFileChosen: (InkBlob) => if not @settings.filePath @@ -226,8 +227,8 @@ class CodeLanguagesObjectTreema extends TreemaNode.nodeMap.object (key for key in _.keys(codeLanguages) when not @data[key]?) class CodeLanguageTreema extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: _.keys(codeLanguages), minLength: 0, delay: 0, autoFocus: true) valEl @@ -236,8 +237,8 @@ class CodeTreema extends TreemaNode.nodeMap.ace super(arguments...) @schema.aceTabSize = 4 - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) if not @schema.aceMode and mode = codeLanguages[@keyForParent] @editor.getSession().setMode mode valEl @@ -307,15 +308,15 @@ class LatestVersionReferenceNode extends TreemaNode @url = "/db/#{parts[1]}" @model = require('models/' + _.string.classify(parts[1])) - buildValueForDisplay: (valEl) -> - val = if @data then @formatDocument(@data) else 'None' + buildValueForDisplay: (valEl, data) -> + val = if data then @formatDocument(data) else 'None' @buildValueForDisplaySimply(valEl, val) - buildValueForEditing: (valEl) -> + buildValueForEditing: (valEl, data) -> valEl.html(@searchValueTemplate) input = valEl.find('input') 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" @@ -357,7 +358,7 @@ class LatestVersionReferenceNode extends TreemaNode formatDocument: (docOrModel) -> return @modelToString(docOrModel) if docOrModel instanceof CocoModel return 'Unknown' unless @settings.supermodel? - m = CocoModel.getReferencedModel(@data, @schema) + m = CocoModel.getReferencedModel(@getData(), @schema) urlGoingFor = m.url() m = @settings.supermodel.getModel(urlGoingFor) if @instance and not m @@ -387,9 +388,6 @@ class LatestVersionReferenceNode extends TreemaNode e.preventDefault() @navigateSearch(-1) - onDeletePressed: (e) -> - super(arguments...) - navigateSearch: (offset) -> selected = @getSelectedResultEl() func = if offset > 0 then 'next' else 'prev' diff --git a/app/views/account/JobProfileTreemaView.coffee b/app/views/account/JobProfileTreemaView.coffee index 1ddde82e1..c2f6ccd92 100644 --- a/app/views/account/JobProfileTreemaView.coffee +++ b/app/views/account/JobProfileTreemaView.coffee @@ -107,25 +107,25 @@ JobProfileTreemaView.commonCities = commonCities = ['Tokyo', 'Jakarta', 'Seoul', autoFocus = true # Not working right now, possibly a Treema bower thing. class SkillTagNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: commonSkills, minLength: 1, delay: 0, autoFocus: autoFocus) valEl class LinkNameNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: commonLinkNames, minLength: 0, delay: 0, autoFocus: autoFocus) valEl class CityNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: commonCities, minLength: 1, delay: 0, autoFocus: autoFocus) valEl class CountryNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: commonCountries, minLength: 1, delay: 0, autoFocus: autoFocus) valEl diff --git a/app/views/editor/level/components/ComponentsTabView.coffee b/app/views/editor/level/components/ComponentsTabView.coffee index 3cfb953b8..10cd608aa 100644 --- a/app/views/editor/level/components/ComponentsTabView.coffee +++ b/app/views/editor/level/components/ComponentsTabView.coffee @@ -81,15 +81,16 @@ module.exports = class ComponentsTabView extends CocoView class LevelComponentNode extends TreemaObjectNode valueClass: 'treema-level-component' collection: false - buildValueForDisplay: (valEl) -> - count = if @data.count is 1 then @data.thangs[0] else ((if @data.count >= 100 then '100+' else @data.count) + ' Thangs') - if @data.original.match ':' - name = 'Old: ' + @data.original.replace('systems/', '') + buildValueForDisplay: (valEl, data) -> + count = if data.count is 1 then data.thangs[0] else ((if data.count >= 100 then '100+' else data.count) + ' Thangs') + if data.original.match ':' + name = 'Old: ' + data.original.replace('systems/', '') else 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}" @buildValueForDisplaySimply valEl, "#{name} (#{count})" onEnterPressed: -> - Backbone.Mediator.publish 'edit-level-component', original: @data.original, majorVersion: @data.majorVersion + data = @getData() + Backbone.Mediator.publish 'edit-level-component', original: data.original, majorVersion: data.majorVersion diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index cf412a649..e8f0a53f2 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -121,8 +121,8 @@ class ScriptsNode extends TreemaArrayNode class ScriptNode extends TreemaObjectNode valueClass: 'treema-script' collection: false - buildValueForDisplay: (valEl) -> - val = @data.id or @data.channel + buildValueForDisplay: (valEl, data) -> + val = data.id or data.channel s = "#{val}" @buildValueForDisplaySimply valEl, s @@ -152,15 +152,15 @@ class PropertiesNode extends TreemaObjectNode class EventPropsNode extends TreemaNode.nodeMap.string valueClass: 'treema-event-props' - arrayToString: -> (@data or []).join('.') + arrayToString: -> (@getData() or []).join('.') - buildValueForDisplay: (valEl) -> + buildValueForDisplay: (valEl, data) -> joined = @arrayToString() joined = '(unset)' if not joined.length @buildValueForDisplaySimply valEl, joined - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) channel = @getRoot().data.channel channelSchema = Backbone.Mediator.channelSchemas[channel] autocompleteValues = [] @@ -182,11 +182,11 @@ class EventPrereqsNode extends TreemaNode.nodeMap.array newTreema.childrenTreemas.eventProps?.edit() class EventPrereqNode extends TreemaNode.nodeMap.object - buildValueForDisplay: (valEl) -> - eventProp = (@data.eventProps or []).join('.') + buildValueForDisplay: (valEl, data) -> + eventProp = (data.eventProps or []).join('.') eventProp = '(unset)' unless eventProp.length statements = [] - for key, value of @data + for key, value of data continue if key is 'eventProps' comparison = @schema.properties[key].title value = value.toString() @@ -196,8 +196,8 @@ class EventPrereqNode extends TreemaNode.nodeMap.object @buildValueForDisplaySimply valEl, s class ChannelNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) autocompleteValues = ({label: val?.title or key, value: key} for key, val of Backbone.Mediator.channelSchemas) valEl.find('input').autocomplete(source: autocompleteValues, minLength: 0, delay: 0, autoFocus: true) valEl diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index 89fa16bf1..a1c272cdc 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -134,23 +134,25 @@ class LevelSystemNode extends TreemaObjectNode @collection = @system?.attributes?.configSchema?.properties? grabDBComponent: -> - unless _.isString @data.original + data = @getData() + unless _.isString data.original return alert('Press the "Add System" button at the bottom instead of the "+". Sorry.') - @system = @settings.supermodel.getModelByOriginalAndMajorVersion(LevelSystem, @data.original, @data.majorVersion) - console.error 'Couldn\'t find system for', @data.original, @data.majorVersion, 'from models', @settings.supermodel.models unless @system + @system = @settings.supermodel.getModelByOriginalAndMajorVersion(LevelSystem, data.original, data.majorVersion) + console.error 'Couldn\'t find system for', data.original, data.majorVersion, 'from models', @settings.supermodel.models unless @system getChildSchema: (key) -> return @system.attributes.configSchema if key is 'config' return super(key) - buildValueForDisplay: (valEl) -> - return super valEl unless @data.original and @system + buildValueForDisplay: (valEl, data) -> + return super valEl unless data.original and @system name = "#{@system.get('name')} v#{@system.get('version').major}" @buildValueForDisplaySimply valEl, "#{name}" onEnterPressed: (e) -> super e - Backbone.Mediator.publish 'edit-level-system', original: @data.original, majorVersion: @data.majorVersion + data = @getData() + Backbone.Mediator.publish 'edit-level-system', original: data.original, majorVersion: data.majorVersion open: (depth) -> super depth @@ -161,5 +163,4 @@ class LevelSystemNode extends TreemaObjectNode class LevelSystemConfigurationNode extends TreemaObjectNode valueClass: 'treema-level-system-configuration' - buildValueForDisplay: (valEl) -> - return + buildValueForDisplay: -> return diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 748e1a575..6607c279d 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -512,9 +512,9 @@ class ThangNode extends TreemaObjectNode collection: false @thangNameMap: {} @thangKindMap: {} - buildValueForDisplay: (valEl) -> - pos = _.find(@data.components, (c) -> c.config?.pos?)?.config.pos # TODO: hack - s = "#{@data.thangType}" + buildValueForDisplay: (valEl, data) -> + pos = _.find(data.components, (c) -> c.config?.pos?)?.config.pos # TODO: hack + s = "#{data.thangType}" if isObjectID s unless name = ThangNode.thangNameMap[s] thangType = _.find @settings.supermodel.getModels(ThangType), (m) -> m.get('original') is s and m.get('kind') @@ -523,7 +523,7 @@ class ThangNode extends TreemaObjectNode kind = ThangNode.thangKindMap[s] @$el.addClass "treema-#{kind}" s = name - s += ' - ' + @data.id if @data.id isnt s + s += ' - ' + data.id if data.id isnt s if pos s += " (#{Math.round(pos.x)}, #{Math.round(pos.y)})" else @@ -531,4 +531,4 @@ class ThangNode extends TreemaObjectNode @buildValueForDisplaySimply valEl, s onEnterPressed: -> - Backbone.Mediator.publish 'edit-level-thang', thangID: @data.id + Backbone.Mediator.publish 'edit-level-thang', thangID: @getData().id diff --git a/app/views/editor/level/treema_nodes.coffee b/app/views/editor/level/treema_nodes.coffee index 5baac355a..29e2952b1 100644 --- a/app/views/editor/level/treema_nodes.coffee +++ b/app/views/editor/level/treema_nodes.coffee @@ -11,12 +11,12 @@ module.exports.WorldPointNode = class WorldPointNode extends TreemaNode.nodeMap. console.error 'Point Treema node needs a World included in the settings.' unless @settings.world? console.error 'Point Treema node needs a RootView included in the settings.' unless @settings.view? - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) onClick: (e) -> @@ -24,7 +24,7 @@ module.exports.WorldPointNode = class WorldPointNode extends TreemaNode.nodeMap. if btn.length then @openMap() else super(arguments...) openMap: -> - modal = new WorldSelectModal(world: @settings.world, dataType: 'point', default: @data, supermodel: @settings.supermodel) + modal = new WorldSelectModal(world: @settings.world, dataType: 'point', default: @getData(), supermodel: @settings.supermodel) modal.callback = @callback @settings.view.openModalView modal @@ -42,12 +42,12 @@ class WorldRegionNode extends TreemaNode.nodeMap.object console.error 'Region Treema node needs a World included in the settings.' unless @settings.world? console.error 'Region Treema node needs a RootView included in the settings.' unless @settings.view? - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) onClick: (e) -> @@ -77,12 +77,12 @@ module.exports.WorldViewportNode = class WorldViewportNode extends TreemaNode.no console.error 'Viewport Treema node needs a World included in the settings.' unless @settings.world? console.error 'Viewport Treema node needs a RootView included in the settings.' unless @settings.view? - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) onClick: (e) -> @@ -92,7 +92,8 @@ module.exports.WorldViewportNode = class WorldViewportNode extends TreemaNode.no openMap: -> # can't really get the bounds from this data, so will have to hack this solution options = world: @settings.world, dataType: 'ratio-region' - options.defaultFromZoom = @data if @data?.target?.x? + data = @getData() + options.defaultFromZoom = data if data?.target?.x? options.supermodel = @settings.supermodel modal = new WorldSelectModal(options) modal.callback = @callback @@ -118,12 +119,12 @@ module.exports.WorldBoundsNode = class WorldBoundsNode extends TreemaNode.nodeMa console.error 'Bounds Treema node needs a World included in the settings.' unless @settings.world? console.error 'Bounds Treema node needs a RootView included in the settings.' unless @settings.view? - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('.treema-shortened').prepend(makeButton()) onClick: (e) -> @@ -131,7 +132,7 @@ module.exports.WorldBoundsNode = class WorldBoundsNode extends TreemaNode.nodeMa if btn.length then @openMap() else super(arguments...) openMap: -> - bounds = @data or [{x: 0, y: 0}, {x: 100, y: 80}] + bounds = @getData() or [{x: 0, y: 0}, {x: 100, y: 80}] modal = new WorldSelectModal(world: @settings.world, dataType: 'region', default: bounds, supermodel: @settings.supermodel) modal.callback = @callback @settings.view.openModalView modal @@ -142,71 +143,71 @@ module.exports.WorldBoundsNode = class WorldBoundsNode extends TreemaNode.nodeMa @set '/1', {x: shorten(e.points[1].x), y: shorten(e.points[1].y)} module.exports.ThangNode = class ThangNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: @settings.thangIDs, minLength: 0, delay: 0, autoFocus: true) valEl module.exports.TeamNode = class TeamNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: @settings.teams, minLength: 0, delay: 0, autoFocus: true) valEl module.exports.SuperteamNode = class SuperteamNode extends TreemaNode.nodeMap.string - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) valEl.find('input').autocomplete(source: @settings.superteams, minLength: 0, delay: 0, autoFocus: true) valEl module.exports.RadiansNode = class RadiansNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) - deg = @data / Math.PI * 180 + buildValueForDisplay: (valEl, data) -> + super(valEl, data) + deg = data / Math.PI * 180 valEl.text valEl.text() + "rad (#{deg.toFixed(0)}˚)" module.exports.MetersNode = class MetersNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.text valEl.text() + 'm' module.exports.KilogramsNode = class KilogramsNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.text valEl.text() + 'kg' module.exports.SecondsNode = class SecondsNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.text valEl.text() + 's' module.exports.MillisecondsNode = class MillisecondsNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.text valEl.text() + 'ms' module.exports.SpeedNode = class SpeedNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.text valEl.text() + 'm/s' module.exports.AccelerationNode = class AccelerationNode extends TreemaNode.nodeMap.number - buildValueForDisplay: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) valEl.text valEl.text() + 'm/s^2' module.exports.ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.string valueClass: 'treema-thang-type' constructor: (args...) -> super args... - @thangType = _.find @settings.supermodel.getModels(ThangType), (m) => m.get('original') is @data if @data - #console.log 'ThangTypeNode found ThangType', @thangType, 'for data', @data + data = @getData() + @thangType = _.find @settings.supermodel.getModels(ThangType), (m) => m.get('original') is data if data buildValueForDisplay: (valEl) -> @buildValueForDisplaySimply(valEl, @thangType?.get('name') or 'None') - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForEditing: (valEl, data) -> + super(valEl, data) thangTypeNames = (m.get('name') for m in @settings.supermodel.getModels ThangType) input = valEl.find('input').autocomplete(source: thangTypeNames, minLength: 0, delay: 0, autoFocus: true) input.val(@thangType?.get('name') or 'None') @@ -222,8 +223,8 @@ module.exports.ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.st module.exports.ItemThangTypeNode = class ThangTypeNode extends ThangTypeNode valueClass: 'treema-item-thang-type' - buildValueForEditing: (valEl) -> - super(valEl) + buildValueForDisplay: (valEl, data) -> + super(valEl, data) thangTypeNames = (m.get('name') for m in @settings.supermodel.getModels ThangType when m.get('kind') is 'Item') input = valEl.find('input').autocomplete(source: thangTypeNames, minLength: 0, delay: 0, autoFocus: true) input.val(@thangType?.get('name') or 'None') diff --git a/bower.json b/bower.json index 8ac33ff3c..63865ba7b 100644 --- a/bower.json +++ b/bower.json @@ -40,7 +40,7 @@ "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", "jquery.tablesorter": "~2.15.13", - "treema": "~0.0.14", + "treema": "~0.1.0", "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", "jquery.browser": "~0.0.6", From f15135efe1029f7afd1b2f10e273a90cb4b717f0 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 22 Aug 2014 11:40:31 -0700 Subject: [PATCH 03/98] Fixed a terrible bug. --- .../component/ThangComponentsEditView.coffee | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index eb72a84f4..9c5678694 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -25,6 +25,7 @@ module.exports = class ThangComponentsEditView extends CocoView constructor: (options) -> super options + @originalsLoaded = {} @components = options.components or [] @components = $.extend true, [], @components # just to be sure @lastComponentLength = @components.length @@ -36,6 +37,10 @@ module.exports = class ThangComponentsEditView extends CocoView loadComponents: (components) -> for componentRef in components + # just to handle if ever somehow the same component is loaded twice, through bad data and alike + continue if @originalsLoaded[componentRef.original] + @originalsLoaded[componentRef.original] = componentRef.original + levelComponent = new LevelComponent(componentRef) url = "/db/level.component/#{componentRef.original}/version/#{componentRef.majorVersion}" levelComponent.setURL(url) @@ -143,7 +148,9 @@ module.exports = class ThangComponentsEditView extends CocoView @reportChanges() updateComponentsList: -> - @componentsTreema?.set('/', $.extend(true, [], @components)) + # Before I was setting the data to the existing treema but then we had some + # nasty sorting/callback bugs. This is less efficient, but it's also less bug prone. + @buildComponentsTreema() onComponentsAdded: -> return unless @componentsTreema @@ -157,11 +164,14 @@ module.exports = class ThangComponentsEditView extends CocoView for componentRef in _.values(componentMap) componentModel = @supermodel.getModelByOriginalAndMajorVersion( LevelComponent, componentRef.original, componentRef.majorVersion) + if not componentModel?.loaded + @loadComponents([componentRef]) + continue for dependency in componentModel?.get('dependencies') or [] if not componentMap[dependency.original] component = @supermodel.getModelByOriginalAndMajorVersion( LevelComponent, dependency.original, dependency.majorVersion) - if not component + if not component?.loaded @loadComponents([dependency]) # will run onComponentsAdded once more when the model loads else @@ -204,7 +214,7 @@ module.exports = class ThangComponentsEditView extends CocoView makeThangComponentConfigView: (thangComponent) -> component = @supermodel.getModelByOriginal(LevelComponent, thangComponent.original) - return unless component + return unless component?.loaded config = thangComponent.config ? {} configView = new ThangComponentConfigView({ supermodel: @supermodel From e7dd88498d420304c8bd5774c00ef9376e3116ae Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 22 Aug 2014 14:52:35 -0700 Subject: [PATCH 04/98] Made more clear when thang types and level thangs are missing dependencies. --- app/models/Level.coffee | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index ad3458692..604dd40ce 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -13,7 +13,7 @@ module.exports = class Level extends CocoModel # Figure out Components o.levelComponents = _.cloneDeep (lc.attributes for lc in supermodel.getModels LevelComponent) - @sortThangComponents o.thangs, o.levelComponents + @sortThangComponents o.thangs, o.levelComponents, 'Level Thang' @fillInDefaultComponentConfiguration o.thangs, o.levelComponents # Figure out Systems @@ -23,7 +23,7 @@ module.exports = class Level extends CocoModel # Figure out ThangTypes' Components o.thangTypes = (original: tt.get('original'), name: tt.get('name'), components: $.extend(true, [], tt.get('components')) for tt in supermodel.getModels ThangType) - @sortThangComponents o.thangTypes, o.levelComponents + @sortThangComponents o.thangTypes, o.levelComponents, 'ThangType' @fillInDefaultComponentConfiguration o.thangTypes, o.levelComponents o @@ -54,7 +54,7 @@ module.exports = class Level extends CocoModel for thangComponent in levelThang.components configs[thangComponent.original] = thangComponent - for defaultThangComponent in thangType.get('components') + for defaultThangComponent in thangType.get('components') or [] if levelThangComponent = configs[defaultThangComponent.original] # Take the ThangType default Components and merge level-specific Component config into it copy = $.extend true, {}, defaultThangComponent.config @@ -98,7 +98,7 @@ module.exports = class Level extends CocoModel visit system for system in levelSystems sorted - sortThangComponents: (thangs, levelComponents) -> + sortThangComponents: (thangs, levelComponents, parentType) -> # Here we have to sort the Components by their dependencies. # It's a bit tricky though, because we don't have either soft dependencies or priority levels. # Example: Programmable must come last, since it has to override any Component-provided methods that any other Component might have created. Can't enumerate all soft dependencies. @@ -119,7 +119,10 @@ module.exports = class Level extends CocoModel else for d in lc.dependencies or [] c2 = _.find thang.components, {original: d.original} - console.error thang.id or thang.name, 'couldn\'t find dependent Component', d.original, 'from', lc.name unless c2 + unless c2 + dependent = _.find levelComponents, {original: d.original} + dependent = dependent?.name or d.original + console.error parentType, thang.id or thang.name, 'does not have dependent Component', dependent, 'from', lc.name visit c2 if c2 if lc.name is 'Collides' allied = _.find levelComponents, {name: 'Allied'} From fe2faefa5d7d1987b11193984e6c689547b061ad Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 22 Aug 2014 14:52:42 -0700 Subject: [PATCH 05/98] Pulling in a couple treema fixes. --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 63865ba7b..9eace2938 100644 --- a/bower.json +++ b/bower.json @@ -40,7 +40,7 @@ "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", "jquery.tablesorter": "~2.15.13", - "treema": "~0.1.0", + "treema": "~0.1.2", "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", "jquery.browser": "~0.0.6", From 1c5db3f2b7579d87110158d3486d41e75543d4e6 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 23 Aug 2014 11:07:52 -0700 Subject: [PATCH 06/98] First round of getting the site to use the new defaults system, in particular the job profile view. --- app/models/Achievement.coffee | 1 + app/models/CocoModel.coffee | 43 +++++----- app/models/Level.coffee | 1 + app/models/User.coffee | 40 +-------- app/schemas/models/user.coffee | 82 +++++++++++-------- app/templates/account/profile.jade | 6 +- app/templates/game-menu/options-view.jade | 2 +- app/templates/play/level/level_loading.jade | 2 +- .../level/systems/AddLevelSystemModal.coffee | 1 + app/views/user/JobProfileView.coffee | 31 +++---- bower.json | 2 +- server/achievements/Achievement.coffee | 1 + server/levels/Level.coffee | 6 -- .../levels/components/LevelComponent.coffee | 6 -- server/levels/sessions/LevelSession.coffee | 7 -- server/levels/systems/LevelSystem.coffee | 6 -- server/users/User.coffee | 8 -- server/users/user_handler.coffee | 2 +- test/app/models/User.spec.coffee | 25 ++++++ .../ThangComponentsEditView.spec.coffee | 6 +- .../views/user/JobProfileView.demo.coffee | 2 +- 21 files changed, 129 insertions(+), 151 deletions(-) diff --git a/app/models/Achievement.coffee b/app/models/Achievement.coffee index 723b2b0bf..47b5391d6 100644 --- a/app/models/Achievement.coffee +++ b/app/models/Achievement.coffee @@ -11,6 +11,7 @@ module.exports = class Achievement extends CocoModel # TODO logic is duplicated in Mongoose Achievement schema getExpFunction: -> + # TODO DEFAULTS kind = @get('function')?.kind or jsonschema.properties.function.default.kind parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters return utils.functionCreators[kind](parameters) if kind of utils.functionCreators diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index de02d9f1d..6f67a10e9 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -9,15 +9,12 @@ class CocoModel extends Backbone.Model notyErrors: true @schema: null - getMe: -> @me or @me = require('lib/auth').me - initialize: (attributes, options) -> super(arguments...) options ?= {} @setProjection options.project if not @constructor.className console.error("#{@} needs a className set.") - @addSchemaDefaults() @on 'sync', @onLoaded, @ @on 'error', @onError, @ @on 'add', @onLoaded, @ @@ -45,14 +42,31 @@ class CocoModel extends Backbone.Model @loadFromBackup() getNormalizedURL: -> "#{@urlRoot}/#{@id}" - + + attributesWithDefaults: undefined + + get: (attribute, withDefault=false) -> + if withDefault + if @attributesWithDefaults is undefined then @buildAttributesWithDefaults() + return @attributesWithDefaults[attribute] + else + super(attribute) + set: -> + delete @attributesWithDefaults inFlux = @loading or not @loaded @markToRevert() unless inFlux or @_revertAttributes res = super(arguments...) @saveBackup() if @saveBackups and (not inFlux) and @hasLocalChanges() res + buildAttributesWithDefaults: -> + t0 = new Date() + clone = $.extend true, {}, @attributes + TreemaNode.utils.populateDefaults(clone, @schema()) + @attributesWithDefaults = clone + console.debug "Populated defaults for #{@attributes.name or @type()} in #{new Date() - t0}ms" + loadFromBackup: -> return unless @saveBackups existing = storage.load @id @@ -161,28 +175,12 @@ class CocoModel extends Backbone.Model if @isPublished() then throw new Error('Can\'t publish what\'s already-published. Can\'t kill what\'s already dead.') @set 'permissions', (@get('permissions') or []).concat({access: 'read', target: 'public'}) - addSchemaDefaults: -> - return if @addedSchemaDefaults - @addedSchemaDefaults = true - for prop, defaultValue of @constructor.schema.default or {} - continue if @get(prop)? - #console.log 'setting', prop, 'to', defaultValue, 'from attributes.default' - @set prop, defaultValue - for prop, sch of @constructor.schema.properties or {} - continue if @get(prop)? - continue if prop is 'emails' # hack, defaults are handled through User.coffee's email-specific methods. - #console.log 'setting', prop, 'to', sch.default, 'from sch.default' if sch.default? - @set prop, sch.default if sch.default? - if @loaded - @loadFromBackup() - @isObjectID: (s) -> s.length is 24 and s.match(/[a-f0-9]/gi)?.length is 24 hasReadAccess: (actor) -> # actor is a User object - - actor ?= @getMe() + actor ?= me return true if actor.isAdmin() if @get('permissions')? for permission in @get('permissions') @@ -193,8 +191,7 @@ class CocoModel extends Backbone.Model hasWriteAccess: (actor) -> # actor is a User object - - actor ?= @getMe() + actor ?= me return true if actor.isAdmin() if @get('permissions')? for permission in @get('permissions') diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 604dd40ce..9214556df 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -135,6 +135,7 @@ module.exports = class Level extends CocoModel visit comp thang.components = sorted + # TODO DEFAULTS fillInDefaultComponentConfiguration: (thangs, levelComponents) -> for thang in thangs for component in thang.components or [] diff --git a/app/models/User.coffee b/app/models/User.coffee index 7b492cdba..eebb3d88c 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -9,13 +9,6 @@ module.exports = class User extends CocoModel urlRoot: '/db/user' notyErrors: false - defaults: - points: 0 - - initialize: -> - super() - @migrateEmails() - onLoaded: -> CocoModel.pollAchievements() # Check for achievements on login super arguments... @@ -24,14 +17,9 @@ module.exports = class User extends CocoModel permissions = @attributes['permissions'] or [] return 'admin' in permissions - isAnonymous: -> - @get 'anonymous' - - displayName: -> - @get('name') or 'Anoner' - - lang: -> - @get('preferredLanguage') or 'en-US' + isAnonymous: -> @get('anonymous', true) + displayName: -> @get('name', true) + lang: -> @get('preferredLanguage', true) getPhotoURL: (size=80, useJobProfilePhoto=false, useEmployerPageAvatar=false) -> photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null @@ -57,33 +45,13 @@ module.exports = class User extends CocoModel done response.name getEnabledEmails: -> - @migrateEmails() - emails = _.clone(@get('emails')) or {} - emails = _.defaults emails, @schema().properties.emails.default - (emailName for emailName, emailDoc of emails when emailDoc.enabled) + (emailName for emailName, emailDoc of @get('emails', true) when emailDoc.enabled) setEmailSubscription: (name, enabled) -> newSubs = _.clone(@get('emails')) or {} (newSubs[name] ?= {}).enabled = enabled @set 'emails', newSubs - emailMap: - announcement: 'generalNews' - developer: 'archmageNews' - tester: 'adventurerNews' - level_creator: 'artisanNews' - article_editor: 'scribeNews' - translator: 'diplomatNews' - support: 'ambassadorNews' - notification: 'anyNotes' - - migrateEmails: -> - return if @attributes.emails or not @attributes.emailSubscriptions - oldSubs = @get('emailSubscriptions') or [] - newSubs = {} - newSubs[newSubName] = {enabled: oldSubName in oldSubs} for oldSubName, newSubName of @emailMap - @set('emails', newSubs) - isEmailSubscriptionEnabled: (name) -> (@get('emails') or {})[name]?.enabled a = 5 diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index be4478820..ea283a63b 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -3,6 +3,20 @@ emailSubscriptions = ['announcement', 'tester', 'level_creator', 'developer', 'a UserSchema = c.object title: 'User' + default: + visa: 'Authorized to work in the US' + music: true + name: 'Anoner' + autocastDelay: 5000 + emails: {} + permissions: [] + anonymous: true + points: 0 + preferredLanguage: 'en-US' + aceConfig: {} + simulatedBy: 0 + simulatedFor: 0 + jobProfile: {} c.extendNamedProperties UserSchema # let's have the name be the first property @@ -31,7 +45,6 @@ visa = c.shortString title: 'US Work Status' description: 'Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)' enum: ['Authorized to work in the US', 'Need visa sponsorship'] - default: 'Authorized to work in the US' _.extend UserSchema.properties, email: c.shortString({title: 'Email', format: 'email'}) @@ -47,12 +60,12 @@ _.extend UserSchema.properties, wizardColor1: c.pct({title: 'Wizard Clothes Color'}) volume: c.pct({title: 'Volume'}) - music: {type: 'boolean', default: true} - autocastDelay: {type: 'integer', 'default': 5000} - lastLevel: {type: 'string'} + music: { type: 'boolean' } + autocastDelay: { type: 'integer' } + lastLevel: { type: 'string' } emailSubscriptions: c.array {uniqueItems: true}, {'enum': emailSubscriptions} - emails: c.object {title: 'Email Settings', default: {generalNews: {enabled: true}, anyNotes: {enabled: true}, recruitNotes: {enabled: true}}}, + emails: c.object {title: 'Email Settings', default: generalNews: {enabled: true}, anyNotes: {enabled: true}, recruitNotes: {enabled: true} }, # newsletters generalNews: {$ref: '#/definitions/emailSubscription'} adventurerNews: {$ref: '#/definitions/emailSubscription'} @@ -68,9 +81,9 @@ _.extend UserSchema.properties, employerNotes: {$ref: '#/definitions/emailSubscription'} # server controlled - permissions: c.array {'default': []}, c.shortString() + permissions: c.array {}, c.shortString() dateCreated: c.date({title: 'Date Joined'}) - anonymous: {type: 'boolean', 'default': true} + anonymous: {type: 'boolean' } testGroupNumber: {type: 'integer', minimum: 0, maximum: 256, exclusiveMaximum: true} mailChimp: {type: 'object'} hourOfCode: {type: 'boolean'} @@ -84,36 +97,36 @@ _.extend UserSchema.properties, emailHash: {type: 'string'} #Internationalization stuff - preferredLanguage: {type: 'string', default: 'en', 'enum': c.getLanguageCodeArray()} + preferredLanguage: {type: 'string', 'enum': c.getLanguageCodeArray()} signedCLA: c.date({title: 'Date Signed the CLA'}) wizard: c.object {}, colorConfig: c.object {additionalProperties: c.colorConfig()} - aceConfig: c.object {}, - language: {type: 'string', 'default': 'javascript', 'enum': ['javascript', 'coffeescript', 'python', 'clojure', 'lua', 'io']} - keyBindings: {type: 'string', 'default': 'default', 'enum': ['default', 'vim', 'emacs']} - invisibles: {type: 'boolean', 'default': false} - indentGuides: {type: 'boolean', 'default': false} - behaviors: {type: 'boolean', 'default': false} - liveCompletion: {type: 'boolean', 'default': true} + aceConfig: c.object { default: { language: 'javascript', keyBindings: 'default', invisibles: false, indentGuides: false, behaviors: false, liveCompletion: true }}, + language: {type: 'string', 'enum': ['javascript', 'coffeescript', 'python', 'clojure', 'lua', 'io']} + keyBindings: {type: 'string', 'enum': ['default', 'vim', 'emacs']} + invisibles: {type: 'boolean' } + indentGuides: {type: 'boolean' } + behaviors: {type: 'boolean' } + liveCompletion: {type: 'boolean' } - simulatedBy: {type: 'integer', minimum: 0, default: 0} - simulatedFor: {type: 'integer', minimum: 0, default: 0} - - jobProfile: c.object {title: 'Job Profile', required: ['lookingFor', 'jobTitle', 'active', 'name', 'city', 'country', 'skills', 'experience', 'shortDescription', 'longDescription', 'visa', 'work', 'education', 'projects', 'links']}, - lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], default: 'Full-time', description: 'What kind of developer position do you want?'} - jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"', default: 'Software Developer'} + simulatedBy: {type: 'integer', minimum: 0 } + simulatedFor: {type: 'integer', minimum: 0 } + + jobProfile: c.object {title: 'Job Profile', default: { active: false, lookingFor: 'Full-time', jobTitle: 'Software Developer', city: 'Defaultsville, CA', country: 'USA', skills: ['javascript'], shortDescription: 'Programmer seeking to build great software.', longDescription: '* I write great code.\n* You need great code?\n* Great!' }}, + lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], description: 'What kind of developer position do you want?'} + jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"' } active: {title: 'Open to Offers', type: 'boolean', description: 'Want interview offers right now?'} updated: c.date {title: 'Last Updated', description: 'How fresh your profile appears to employers. Profiles go inactive after 4 weeks.'} name: c.shortString {title: 'Name', description: 'Name you want employers to see, like "Nick Winter".'} - city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', default: 'Defaultsville, CA', format: 'city'} - country: c.shortString {title: 'Country', description: 'Country you want to work in (or live in now), like "USA" or "France".', default: 'USA', format: 'country'} - skills: c.array {title: 'Skills', description: 'Tag relevant developer skills in order of proficiency.', default: ['javascript'], minItems: 1, maxItems: 30, uniqueItems: true}, + city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', format: 'city'} + country: c.shortString {title: 'Country', description: 'Country you want to work in (or live in now), like "USA" or "France".', format: 'country'} + skills: c.array {title: 'Skills', description: 'Tag relevant developer skills in order of proficiency', maxItems: 30, uniqueItems: true}, {type: 'string', minLength: 1, maxLength: 50, description: 'Ex.: "objective-c", "mongodb", "rails", "android", "javascript"', format: 'skill'} experience: {type: 'integer', title: 'Years of Experience', minimum: 0, description: 'How many years of professional experience (getting paid) developing software do you have?'} - shortDescription: {type: 'string', maxLength: 140, title: 'Short Description', description: 'Who are you, and what are you looking for? 140 characters max.', default: 'Programmer seeking to build great software.'} - longDescription: {type: 'string', maxLength: 600, title: 'Description', description: 'Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max.', format: 'markdown', default: '* I write great code.\n* You need great code?\n* Great!'} + shortDescription: {type: 'string', maxLength: 140, title: 'Short Description', description: 'Who are you, and what are you looking for? 140 characters max.' } + longDescription: {type: 'string', maxLength: 600, title: 'Description', description: 'Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max.', format: 'markdown' } visa: visa work: c.array {title: 'Work Experience', description: 'List your relevant work experience, most recent first.'}, c.object {title: 'Job', description: 'Some work experience you had.', required: ['employer', 'role', 'duration']}, @@ -129,14 +142,14 @@ _.extend UserSchema.properties, description: {type: 'string', title: 'Description', description: 'Highlight anything about this educational experience. (140 chars; optional)', maxLength: 140} projects: c.array {title: 'Projects (Top 3)', description: 'Highlight your projects to amaze employers.', maxItems: 3}, c.object {title: 'Project', description: 'A project you created.', required: ['name', 'description', 'picture'], default: {name: 'My Project', description: 'A project I worked on.', link: 'http://example.com', picture: ''}}, - name: c.shortString {title: 'Project Name', description: 'What was the project called?', default: 'My Project'} - description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, default: 'A project I worked on.', format: 'markdown'} + name: c.shortString {title: 'Project Name', description: 'What was the project called?' } + description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, format: 'markdown'} picture: {type: 'string', title: 'Picture', format: 'image-file', description: 'Upload a 230x115px or larger image showing off the project.'} - link: c.url {title: 'Link', description: 'Link to the project.', default: 'http://example.com'} + link: c.url {title: 'Link', description: 'Link to the project.'} links: c.array {title: 'Personal and Social Links', description: 'Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog.'}, - c.object {title: 'Link', description: 'A link to another site you want to highlight, like your GitHub, your LinkedIn, or your blog.', required: ['name', 'link']}, + c.object {title: 'Link', description: 'A link to another site you want to highlight, like your GitHub, your LinkedIn, or your blog.', required: ['name', 'link'], default: {link: 'http://example.com'}}, name: {type: 'string', maxLength: 30, title: 'Link Name', description: 'What are you linking to? Ex: "Personal Website", "GitHub"', format: 'link-name'} - link: c.url {title: 'Link', description: 'The URL.', default: 'http://example.com'} + link: c.url {title: 'Link', description: 'The URL.' } photoURL: {type: 'string', format: 'image-file', title: 'Profile Picture', description: 'Upload a 256x256px or larger image if you want to show a different profile picture to employers than your normal avatar.'} curated: c.object {title: 'Curated', required: ['shortDescription', 'mainTag', 'location', 'education', 'workHistory', 'phoneScreenFilter', 'schoolFilter', 'locationFilter', 'roleFilter', 'seniorityFilter']}, shortDescription: @@ -169,7 +182,7 @@ _.extend UserSchema.properties, description: 'Should this candidate be prominently featured on the site?' jobProfileApproved: {title: 'Job Profile Approved', type: 'boolean', description: 'Whether your profile has been approved by CodeCombat.'} jobProfileApprovedDate: c.date {title: 'Approved date', description: 'The date that the candidate was approved'} - jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: 'CodeCombat\'s notes on the candidate.', format: 'markdown', default: ''} + jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: 'CodeCombat\'s notes on the candidate.', format: 'markdown' } employerAt: c.shortString {description: 'If given employer permissions to view job candidates, for which employer?'} signedEmployerAgreement: c.object {}, linkedinID: c.shortString {title: 'LinkedInID', description: 'The user\'s LinkedIn ID when they signed the contract.'} @@ -252,10 +265,11 @@ _.extend UserSchema.properties, c.extendBasicProperties UserSchema, 'user' -c.definitions = - emailSubscription = +UserSchema.definitions = + emailSubscription: c.object { default: { enabled: true, count: 0 } }, { enabled: {type: 'boolean'} lastSent: c.date() count: {type: 'integer'} + } module.exports = UserSchema diff --git a/app/templates/account/profile.jade b/app/templates/account/profile.jade index a178413ce..bbe9daba6 100644 --- a/app/templates/account/profile.jade +++ b/app/templates/account/profile.jade @@ -117,7 +117,7 @@ block content button.btn.btn-success.btn-block.save-section(data-i18n="common.save") Save .editable-section#basic-info-container - - var editableDefaults = editing && profile.city == jobProfileSchema.properties.city.default + - var editableDefaults = editing && !rawProfile.city div(class="editable-display" + (editableDefaults ? " edit-example-text" : ""), title="Click to edit your basic info") .editable-icon.glyphicon.glyphicon-pencil if editableDefaults @@ -224,7 +224,7 @@ block content #short-description-container.editable-section .editable-display(title="Click to write your tagline") .editable-icon.glyphicon.glyphicon-pencil - if editing && (!profile.shortDescription || profile.shortDescription == jobProfileSchema.properties.shortDescription.default) + if editing && !rawProfile.shortDescription h3.edit-label(data-i18n="account_profile.short_description_header") Write a short description of yourself p.edit-example-text(data-i18n="account_profile.short_description_blurb") Add a tagline to help an employer quickly learn more about you. @@ -269,7 +269,7 @@ block content #long-description-container.editable-section .editable-display(title="Click to start writing your longer description") .editable-icon.glyphicon.glyphicon-pencil - - var modified = profile.longDescription && profile.longDescription != jobProfileSchema.properties.longDescription.default + - var modified = rawProfile.longDescription if editing && !modified h3.edit-label(data-i18n="account_profile.long_description_header") Describe your desired position p.edit-example-text(data-i18n="account_profile.long_description_blurb") Tell employers how awesome you are and what role you want. diff --git a/app/templates/game-menu/options-view.jade b/app/templates/game-menu/options-view.jade index ee4010dd2..bddd86185 100644 --- a/app/templates/game-menu/options-view.jade +++ b/app/templates/game-menu/options-view.jade @@ -3,7 +3,7 @@ .editable-icon.glyphicon.glyphicon-pencil img.profile-photo(src=me.getPhotoURL(230)) .form-group - input#player-name.profile-caption(name="playerName", type="text", value=me.get('name') || 'Anoner') + input#player-name.profile-caption(name="playerName", type="text", value=me.get('name', true)) .form h3(data-i18n="options.general_options") General Options diff --git a/app/templates/play/level/level_loading.jade b/app/templates/play/level/level_loading.jade index 03d357f75..895524bce 100644 --- a/app/templates/play/level/level_loading.jade +++ b/app/templates/play/level/level_loading.jade @@ -39,7 +39,7 @@ strong.tip.rare(data-i18n='play_level.tip_first_language') The most disastrous thing that you can ever learn is your first programming language. - Alan Kay strong.tip.rare span(data-i18n='play_level.tip_harry') Yer a Wizard, - span= me.get('name') || 'Anoner' + span= me.get('name', true) .errors diff --git a/app/views/editor/level/systems/AddLevelSystemModal.coffee b/app/views/editor/level/systems/AddLevelSystemModal.coffee index d11071e4c..3d435caf1 100644 --- a/app/views/editor/level/systems/AddLevelSystemModal.coffee +++ b/app/views/editor/level/systems/AddLevelSystemModal.coffee @@ -61,6 +61,7 @@ module.exports = class AddLevelSystemModal extends ModalView levelSystem = original: s.get('original') ? id majorVersion: s.get('version').major ? 0 + # TODO DEFAULTS config: $.extend(true, {}, s.get('configSchema').default ? {}) @extantSystems.push levelSystem Backbone.Mediator.publish 'level-system-added', system: levelSystem diff --git a/app/views/user/JobProfileView.coffee b/app/views/user/JobProfileView.coffee index 836b75676..5d097e802 100644 --- a/app/views/user/JobProfileView.coffee +++ b/app/views/user/JobProfileView.coffee @@ -72,6 +72,14 @@ module.exports = class JobProfileView extends UserView finishInit: -> return unless @userID @uploadFilePath = "db/user/#{@userID}" + + if @user?.get('firstName') + jobProfile = @user.get('jobProfile') + jobProfile ?= {} + if not jobProfile.name + jobProfile.name = (@user.get('firstName') + ' ' + @user.get('lastName')).trim() + @user.set('jobProfile', jobProfile) + @highlightedContainers = [] if me.isAdmin() or 'employer' in me.get('permissions') $.post "/db/user/#{me.id}/track/view_candidate" @@ -223,16 +231,8 @@ module.exports = class JobProfileView extends UserView context = super() context.userID = @userID context.linkedInAuthorized = @authorizedWithLinkedIn - context.jobProfileSchema = me.schema().properties.jobProfile - if @user and not jobProfile = @user.get 'jobProfile' - jobProfile = {} - for prop, schema of context.jobProfileSchema.properties - jobProfile[prop] = _.clone schema.default if schema.default? - for prop in context.jobProfileSchema.required - jobProfile[prop] ?= {string: '', boolean: false, number: 0, integer: 0, array: []}[context.jobProfileSchema.properties[prop].type] - @user.set 'jobProfile', jobProfile - jobProfile.name ?= (@user.get('firstName') + ' ' + @user.get('lastName')).trim() if @user?.get('firstName') - context.profile = jobProfile + context.profile = @user.get('jobProfile', true) + context.rawProfile = @user.get('jobProfile') or {} context.user = @user context.myProfile = @isMe() context.allowedToViewJobProfile = @user and (me.isAdmin() or 'employer' in me.get('permissions') or (context.myProfile && !me.get('anonymous'))) @@ -244,7 +244,7 @@ module.exports = class JobProfileView extends UserView context.marked = marked context.moment = moment context.iconForLink = @iconForLink - if links = jobProfile?.links + if links = context.profile.links links = ($.extend(true, {}, link) for link in links) link.icon = @iconForLink link for link in links context.profileLinks = _.sortBy links, (link) -> not link.icon # icons first @@ -290,8 +290,8 @@ module.exports = class JobProfileView extends UserView aceUseWrapMode: true callbacks: {change: @onRemarkChanged} @remarkTreema = @$el.find('#remark-treema').treema treemaOptions - @remarkTreema.build() - @remarkTreema.open(3) + @remarkTreema?.build() + @remarkTreema?.open(3) onRemarkChanged: (e) => return unless @remarkTreema.isValid() @@ -440,7 +440,8 @@ module.exports = class JobProfileView extends UserView $(@).remove() if isEmpty @ resetOnce = false # We have to clear out arrays if we're going to redo them serialized = form.serializeArray() - jobProfile = @user.get 'jobProfile' + jobProfile = @user.get('jobProfile') or {} + jobProfile[prop] ?= [] for prop in ['links', 'skills', 'work', 'education', 'projects'] rootPropertiesSeen = {} for field in serialized keyChain = @extractFieldKeyChain field.name @@ -449,6 +450,7 @@ module.exports = class JobProfileView extends UserView for key, i in keyChain rootPropertiesSeen[key] = true unless i break if i is keyChain.length - 1 + parent[key] ?= {} child = parent[key] if _.isArray(child) and not resetOnce child = parent[key] = [] @@ -467,6 +469,7 @@ module.exports = class JobProfileView extends UserView if section.hasClass('projects-container') and not section.find('.array-item').length jobProfile.projects = [] section.addClass 'saving' + @user.set('jobProfile', jobProfile) @saveEdits true extractFieldKeyChain: (key) -> diff --git a/bower.json b/bower.json index 9eace2938..682d0d2a3 100644 --- a/bower.json +++ b/bower.json @@ -40,7 +40,7 @@ "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", "jquery.tablesorter": "~2.15.13", - "treema": "~0.1.2", + "treema": "https://github.com/codecombat/treema.git#release/0.1.0", "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", "jquery.browser": "~0.0.6", diff --git a/server/achievements/Achievement.coffee b/server/achievements/Achievement.coffee index 07a492a7c..4b0f38bc7 100644 --- a/server/achievements/Achievement.coffee +++ b/server/achievements/Achievement.coffee @@ -25,6 +25,7 @@ AchievementSchema.methods.stringifyQuery = -> @set('query', JSON.stringify(@get('query'))) if typeof @get('query') != 'string' AchievementSchema.methods.getExpFunction = -> + # TODO DEFAULTS kind = @get('function')?.kind or jsonschema.properties.function.default.kind parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters return utils.functionCreators[kind](parameters) if kind of utils.functionCreators diff --git a/server/levels/Level.coffee b/server/levels/Level.coffee index 05cbdeb8f..dc2020557 100644 --- a/server/levels/Level.coffee +++ b/server/levels/Level.coffee @@ -12,12 +12,6 @@ LevelSchema.plugin(plugins.VersionedPlugin) LevelSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']}) LevelSchema.plugin(plugins.PatchablePlugin) -LevelSchema.pre 'init', (next) -> - return next() unless jsonschema.properties? - for prop, sch of jsonschema.properties - @set(prop, _.cloneDeep(sch.default)) if sch.default? - next() - LevelSchema.post 'init', (doc) -> if _.isString(doc.get('nextLevel')) doc.set('nextLevel', undefined) diff --git a/server/levels/components/LevelComponent.coffee b/server/levels/components/LevelComponent.coffee index 6fb467908..ffd7b6468 100644 --- a/server/levels/components/LevelComponent.coffee +++ b/server/levels/components/LevelComponent.coffee @@ -13,10 +13,4 @@ LevelComponentSchema.plugin plugins.VersionedPlugin LevelComponentSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'description', 'system']} LevelComponentSchema.plugin plugins.PatchablePlugin -LevelComponentSchema.pre 'init', (next) -> - return next() unless jsonschema.properties? - for prop, sch of jsonschema.properties - @set(prop, _.cloneDeep sch.default) if sch.default? - next() - module.exports = LevelComponent = mongoose.model('level.component', LevelComponentSchema) diff --git a/server/levels/sessions/LevelSession.coffee b/server/levels/sessions/LevelSession.coffee index 329914f01..b9ae9cb5d 100644 --- a/server/levels/sessions/LevelSession.coffee +++ b/server/levels/sessions/LevelSession.coffee @@ -14,13 +14,6 @@ LevelSessionSchema = new mongoose.Schema({ LevelSessionSchema.plugin(plugins.PermissionsPlugin) LevelSessionSchema.plugin(AchievablePlugin) -LevelSessionSchema.pre 'init', (next) -> - # TODO: refactor this into a set of common plugins for all models? - return next() unless jsonschema.properties? - for prop, sch of jsonschema.properties - @set(prop, _.cloneDeep(sch.default)) if sch.default? - next() - previous = {} LevelSessionSchema.post 'init', (doc) -> diff --git a/server/levels/systems/LevelSystem.coffee b/server/levels/systems/LevelSystem.coffee index b553cdd89..87c4a2bab 100644 --- a/server/levels/systems/LevelSystem.coffee +++ b/server/levels/systems/LevelSystem.coffee @@ -12,10 +12,4 @@ LevelSystemSchema.plugin(plugins.VersionedPlugin) LevelSystemSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']}) LevelSystemSchema.plugin(plugins.PatchablePlugin) -LevelSystemSchema.pre 'init', (next) -> - return next() unless jsonschema.properties? - for prop, sch of jsonschema.properties - @set(prop, _.cloneDeep sch.default) if sch.default? - next() - module.exports = LevelSystem = mongoose.model('level.system', LevelSystemSchema) diff --git a/server/users/User.coffee b/server/users/User.coffee index 988107f9e..a0e7fe18a 100644 --- a/server/users/User.coffee +++ b/server/users/User.coffee @@ -15,14 +15,6 @@ UserSchema = new mongoose.Schema({ 'default': Date.now }, {strict: false}) -UserSchema.pre('init', (next) -> - return next() unless jsonschema.properties? - for prop, sch of jsonschema.properties - continue if prop is 'emails' # defaults may change, so don't carry them over just yet - @set(prop, sch.default) if sch.default? - next() -) - UserSchema.post('init', -> @set('anonymous', false) if @get('email') ) diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index d96d86cf8..97a13fe78 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -161,7 +161,7 @@ UserHandler = class UserHandler extends Handler post: (req, res) -> return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body) return @sendBadInputError(res, 'Must have an anonymous user to post with.') unless req.user - return @sendBadInputError(res, 'Existing users cannot create new ones.') unless req.user.get('anonymous') + return @sendBadInputError(res, 'Existing users cannot create new ones.') if req.user.get('anonymous') is false req.body._id = req.user._id if req.user.get('anonymous') @put(req, res) diff --git a/test/app/models/User.spec.coffee b/test/app/models/User.spec.coffee index 35b87f450..c6012896e 100644 --- a/test/app/models/User.spec.coffee +++ b/test/app/models/User.spec.coffee @@ -14,3 +14,28 @@ describe 'UserModel', -> me.set 'points', 50 expect(me.level()).toBe User.levelFromExp 50 + + describe 'user emails', -> + it 'has anyNotes, generalNews and recruitNotes enabled by default', -> + u = new User() + expect(u.get('emails')).toBeUndefined() + defaultEmails = u.get('emails', true) + expect(defaultEmails.anyNotes.enabled).toBe(true) + expect(defaultEmails.generalNews.enabled).toBe(true) + expect(defaultEmails.recruitNotes.enabled).toBe(true) + + it 'maintains defaults of other emails when one is explicitly set', -> + u = new User() + u.setEmailSubscription('recruitNotes', false) + defaultEmails = u.get('emails', true) + expect(defaultEmails.anyNotes?.enabled).toBe(true) + expect(defaultEmails.generalNews?.enabled).toBe(true) + expect(defaultEmails.recruitNotes.enabled).toBe(false) + + it 'does not populate raw data for other emails when one is explicitly set', -> + u = new User() + u.setEmailSubscription('recruitNotes', false) + u.buildAttributesWithDefaults() + emails = u.get('emails') + expect(emails.anyNotes).toBeUndefined() + expect(emails.generalNews).toBeUndefined() diff --git a/test/app/views/editor/component/ThangComponentsEditView.spec.coffee b/test/app/views/editor/component/ThangComponentsEditView.spec.coffee index dba8d8443..6862a0a03 100644 --- a/test/app/views/editor/component/ThangComponentsEditView.spec.coffee +++ b/test/app/views/editor/component/ThangComponentsEditView.spec.coffee @@ -6,14 +6,14 @@ responses = '/db/level.component/B/version/0': { system: 'System' original: 'B' - majorVersion: 0 + version: {major: 0, minor:0} name: 'B (depends on A)' dependencies: [{original:'A', majorVersion: 0}] } '/db/level.component/A/version/0': { system: 'System' original: 'A' - majorVersion: 0 + version: {major: 0, minor:0} name: 'A' configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} } } @@ -21,7 +21,7 @@ responses = componentC = new LevelComponent({ system: 'System' original: 'C' - majorVersion: 0 + version: {major: 0, minor:0} name: 'C (depends on B)' dependencies: [{original:'B', majorVersion: 0}] }) diff --git a/test/demo/views/user/JobProfileView.demo.coffee b/test/demo/views/user/JobProfileView.demo.coffee index 5ab643f87..6832fdee7 100644 --- a/test/demo/views/user/JobProfileView.demo.coffee +++ b/test/demo/views/user/JobProfileView.demo.coffee @@ -1,4 +1,4 @@ -JobProfileView = require 'views/account/JobProfileView' +JobProfileView = require 'views/user/JobProfileView' responses = '/db/user/joe/nameToID':'512ef4805a67a8c507000001' From 0721b492164d34a34f2aabda3dc32bc6c33ac1bd Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 23 Aug 2014 15:51:59 -0700 Subject: [PATCH 07/98] Refactoring defaults. --- app/application.coffee | 2 +- app/initialize.coffee | 6 +-- app/lib/utils.coffee | 2 +- app/models/User.coffee | 1 - app/schemas/models/achievement.coffee | 24 +++++---- app/schemas/models/earned_achievement.coffee | 5 +- app/schemas/models/level.coffee | 51 +++++++++++--------- app/views/kinds/RootView.coffee | 2 +- app/views/play/level/PlayLevelView.coffee | 2 +- 9 files changed, 53 insertions(+), 42 deletions(-) diff --git a/app/application.coffee b/app/application.coffee index 9f23e9f91..b4685e643 100644 --- a/app/application.coffee +++ b/app/application.coffee @@ -42,7 +42,7 @@ Application = initialize: -> @linkedinHandler = new LinkedInHandler() preload(COMMON_FILES) $.i18n.init { - lng: me?.lang() ? 'en' + lng: me.get('preferredLanguage', true) fallbackLng: 'en' resStore: locale #debug: true diff --git a/app/initialize.coffee b/app/initialize.coffee index a27c1f7a2..c384c196f 100644 --- a/app/initialize.coffee +++ b/app/initialize.coffee @@ -67,9 +67,9 @@ setUpDefinitions = -> setUpMoment = -> {me} = require 'lib/auth' - moment.lang me.lang(), {} - me.on 'change', (me) -> - moment.lang me.lang(), {} if me._previousAttributes.preferredLanguage isnt me.get 'preferredLanguage' + moment.lang me.get('preferredLanguage', true), {} + me.on 'change:preferredLanguage', (me) -> + moment.lang me.get('preferredLanguage', true), {} initializeServices = -> services = [ diff --git a/app/lib/utils.coffee b/app/lib/utils.coffee index 17167422d..5a4e78594 100644 --- a/app/lib/utils.coffee +++ b/app/lib/utils.coffee @@ -46,7 +46,7 @@ toHex = (n) -> h = '0'+h if h.length is 1 h -module.exports.i18n = (say, target, language=me.lang(), fallback='en') -> +module.exports.i18n = (say, target, language=me.get('preferredLanguage', true), fallback='en') -> generalResult = null fallbackResult = null fallforwardResult = null # If a general language isn't available, the first specific one will do diff --git a/app/models/User.coffee b/app/models/User.coffee index eebb3d88c..d9af11f11 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -19,7 +19,6 @@ module.exports = class User extends CocoModel isAnonymous: -> @get('anonymous', true) displayName: -> @get('name', true) - lang: -> @get('preferredLanguage', true) getPhotoURL: (size=80, useJobProfilePhoto=false, useEmployerPageAvatar=false) -> photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null diff --git a/app/schemas/models/achievement.coffee b/app/schemas/models/achievement.coffee index 35bd36bba..4f5181f92 100644 --- a/app/schemas/models/achievement.coffee +++ b/app/schemas/models/achievement.coffee @@ -38,15 +38,20 @@ c.extendNamedProperties AchievementSchema c.extendBasicProperties AchievementSchema, 'achievement' c.extendSearchableProperties AchievementSchema +AchievementSchema.default = + worth: 10 + description: 'Probably the coolest you\'ll ever get.' + difficulty: 1 + recalculable: true + function: {} + _.extend AchievementSchema.properties, query: #type:'object' $ref: '#/definitions/' + MongoFindQuerySchema.id worth: c.float - default: 10 collection: {type: 'string'} - description: c.shortString - default: 'Probably the coolest you\'ll ever get.' + description: c.shortString() userField: c.shortString() related: c.objectId(description: 'Related entity') icon: {type: 'string', format: 'image-file', title: 'Icon', description: 'Image should be a 100x100 transparent png.'} @@ -55,27 +60,26 @@ _.extend AchievementSchema.properties, description: 'For categorizing and display purposes' difficulty: c.int description: 'The higher the more difficult' - default: 1 proportionalTo: type: 'string' description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations' recalculable: type: 'boolean' description: 'Needs to be set to true before it is elligible for recalculation.' - default: true function: type: 'object' description: 'Function that gives total experience for X amount achieved' properties: - kind: {enum: ['linear', 'logarithmic', 'quadratic'], default: 'linear'} + kind: {enum: ['linear', 'logarithmic', 'quadratic'] } parameters: type: 'object' + default: { a: 1, b: 1, c: 1 } properties: - a: {type: 'number', default: 1} - b: {type: 'number', default: 1} - c: {type: 'number', default: 1} + a: {type: 'number' } + b: {type: 'number' } + c: {type: 'number' } additionalProperties: true - default: {kind: 'linear', parameters: a: 1} + default: {kind: 'linear', parameters: {}} required: ['kind', 'parameters'] additionalProperties: false i18n: c.object diff --git a/app/schemas/models/earned_achievement.coffee b/app/schemas/models/earned_achievement.coffee index 12451ceac..04cfe9b91 100644 --- a/app/schemas/models/earned_achievement.coffee +++ b/app/schemas/models/earned_achievement.coffee @@ -3,6 +3,9 @@ c = require './../schemas' module.exports = EarnedAchievementSchema = type: 'object' + default: + previouslyAchievedAmount: 0 + properties: user: c.objectId links: @@ -26,5 +29,5 @@ module.exports = changed: type: 'date' achievedAmount: type: 'number' earnedPoints: type: 'number' - previouslyAchievedAmount: {type: 'number', default: 0} + previouslyAchievedAmount: {type: 'number'} notified: type: 'boolean' diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index 2fa229ee1..ae05899c2 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -11,7 +11,7 @@ side = {title: 'Side', description: 'A side.', type: 'string', 'enum': ['left', thang = {title: 'Thang', description: 'The name of a Thang.', type: 'string', maxLength: 30, format: 'thang'} eventPrereqValueTypes = ['boolean', 'integer', 'number', 'null', 'string'] # not 'object' or 'array' -EventPrereqSchema = c.object {title: 'Event Prerequisite', format: 'event-prereq', description: 'Script requires that the value of some property on the event triggering it to meet some prerequisite.', 'default': {eventProps: []}, required: ['eventProps']}, +EventPrereqSchema = c.object {title: 'Event Prerequisite', format: 'event-prereq', description: 'Script requires that the value of some property on the event triggering it to meet some prerequisite.', required: ['eventProps']}, eventProps: c.array {'default': ['thang'], format: 'event-value-chain', maxItems: 10, title: 'Event Property', description: 'A chain of keys in the event, like "thang.pos.x" to access event.thang.pos.x.'}, c.shortString(title: 'Property', description: 'A key in the event property key chain.') equalTo: c.object {type: eventPrereqValueTypes, title: '==', description: 'Script requires the event\'s property chain value to be equal to this value.'} notEqualTo: c.object {type: eventPrereqValueTypes, title: '!=', description: 'Script requires the event\'s property chain value to *not* be equal to this value.'} @@ -30,7 +30,7 @@ GoalSchema = c.object {title: 'Goal', description: 'A goal that the player can a id: c.shortString(title: 'ID', description: 'Unique identifier for this goal, like \"defeat-dragons\".', pattern: '^[a-z0-9-]+$') # unique somehow? worldEndsAfter: {title: 'World Ends After', description: 'When included, ends the world this many seconds after this goal succeeds or fails.', type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 300, default: 3} howMany: {title: 'How Many', description: 'When included, require only this many of the listed goal targets instead of all of them.', type: 'integer', minimum: 1} - hiddenGoal: {title: 'Hidden', description: 'Hidden goals don\'t show up in the goals area for the player until they\'re failed. (Usually they\'re obvious, like "don\'t die".)', 'type': 'boolean', default: false} + hiddenGoal: {title: 'Hidden', description: 'Hidden goals don\'t show up in the goals area for the player until they\'re failed. (Usually they\'re obvious, like "don\'t die".)', 'type': 'boolean' } team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.') killThangs: c.array {title: 'Kill Thangs', description: 'A list of Thang IDs the player should kill, or team names.', uniqueItems: true, minItems: 1, 'default': ['ogres']}, thang saveThangs: c.array {title: 'Save Thangs', description: 'A list of Thang IDs the player should save, or team names', uniqueItems: true, minItems: 1, 'default': ['humans']}, thang @@ -70,26 +70,26 @@ ResponseSchema = c.object {title: 'Dialogue Button', description: 'A button to b buttonClass: c.shortString(title: 'Button Class', description: 'CSS class that will be added to the button, like "btn-primary".') i18n: {type: 'object', format: 'i18n', props: ['text'], description: 'Help translate this button'} -PointSchema = c.object {title: 'Point', description: 'An {x, y} coordinate point.', format: 'point2d', required: ['x', 'y']}, - x: {title: 'x', description: 'The x coordinate.', type: 'number', 'default': 15} - y: {title: 'y', description: 'The y coordinate.', type: 'number', 'default': 20} +PointSchema = c.object {title: 'Point', description: 'An {x, y} coordinate point.', format: 'point2d', default: { x:15, y:20 }}, + x: {title: 'x', description: 'The x coordinate.', type: 'number'} + y: {title: 'y', description: 'The y coordinate.', type: 'number'} SpriteCommandSchema = c.object {title: 'Thang Command', description: 'Make a target Thang move or say something, or select/deselect it.', required: ['id'], default: {id: 'Captain Anya'}}, id: thang select: {title: 'Select', description: 'Select or deselect this Thang.', type: 'boolean'} - say: c.object {title: 'Say', description: 'Make this Thang say a message.', required: ['text']}, + say: c.object {title: 'Say', description: 'Make this Thang say a message.', required: ['text'], default: { mood: 'explain' }}, blurb: c.shortString(title: 'Blurb', description: 'A very short message to display above this Thang\'s head. Plain text.', maxLength: 50) - mood: c.shortString(title: 'Mood', description: 'The mood with which the Thang speaks.', 'enum': ['explain', 'debrief', 'congrats', 'attack', 'joke', 'tip', 'alarm'], 'default': 'explain') + mood: c.shortString(title: 'Mood', description: 'The mood with which the Thang speaks.', 'enum': ['explain', 'debrief', 'congrats', 'attack', 'joke', 'tip', 'alarm']) text: {title: 'Text', description: 'A short message to display in the dialogue area. Markdown okay.', type: 'string', maxLength: 400} - sound: c.object {title: 'Sound', description: 'A dialogue sound file to accompany the message.', required: ['mp3', 'ogg']}, + sound: c.object {title: 'Sound', description: 'A dialogue sound file to accompany the message.', required: ['mp3', 'ogg'] }, mp3: c.shortString(title: 'MP3', format: 'sound-file') ogg: c.shortString(title: 'OGG', format: 'sound-file') - preload: {title: 'Preload', description: 'Whether to load this sound file before the level can begin (typically for the first dialogue of a level).', type: 'boolean', 'default': false} + preload: {title: 'Preload', description: 'Whether to load this sound file before the level can begin (typically for the first dialogue of a level).', type: 'boolean' } responses: c.array {title: 'Buttons', description: 'An array of buttons to include with the dialogue, with which the user can respond.'}, ResponseSchema i18n: {type: 'object', format: 'i18n', props: ['blurb', 'text'], description: 'Help translate this message'} - move: c.object {title: 'Move', description: 'Tell the Thang to move.', required: ['target'], default: {target: {x: 20, y: 20}, duration: 500}}, - target: _.extend _.cloneDeep(PointSchema), {title: 'Target', description: 'Target point to which the Thang will move.'} - duration: {title: 'Duration', description: 'Number of milliseconds over which to move, or 0 for an instant move.', type: 'integer', minimum: 0, default: 500, format: 'milliseconds'} + move: c.object {title: 'Move', description: 'Tell the Thang to move.', required: ['target'], default: {target: {}, duration: 500}}, + target: _.extend _.cloneDeep(PointSchema), {title: 'Target', description: 'Target point to which the Thang will move.', default: {x: 20, y: 20}} + duration: {title: 'Duration', description: 'Number of milliseconds over which to move, or 0 for an instant move.', type: 'integer', minimum: 0, format: 'milliseconds'} NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes that should be sent out as a result of this script triggering.', displayProperty: 'name'}, name: {title: 'Name', description: 'Short name describing the script, like \"Anya greets the player\", for your convenience.', type: 'string'} @@ -116,7 +116,7 @@ NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes playback: c.object {title: 'Playback', description: 'Control the playback of the level.'}, playing: {type: 'boolean', title: 'Set Playing', description: 'Set whether playback is playing or paused.'} scrub: c.object {title: 'Scrub', description: 'Scrub the level playback time to a certain point.', default: {offset: 2, duration: 1000, toRatio: 0.5}}, - offset: {type: 'integer', title: 'Offset', description: 'Number of frames by which to adjust the scrub target time.', default: 2} + offset: {type: 'integer', title: 'Offset', description: 'Number of frames by which to adjust the scrub target time.'} duration: {type: 'integer', title: 'Duration', description: 'Number of milliseconds over which to scrub time.', minimum: 0, format: 'milliseconds'} toRatio: {type: 'number', title: 'To Progress Ratio', description: 'Set playback time to a target playback progress ratio.', minimum: 0, maximum: 1} toTime: {type: 'number', title: 'To Time', description: 'Set playback time to a target playback point, in seconds.', minimum: 0} @@ -156,7 +156,7 @@ ScriptSchema = c.object { id: c.shortString(title: 'ID', description: 'A unique ID that other scripts can rely on in their Happens After prereqs, for sequencing.') # uniqueness? channel: c.shortString(title: 'Event', format: 'event-channel', description: 'Event channel this script might trigger for, like "world:won".') eventPrereqs: c.array {title: 'Event Checks', description: 'Logical checks on the event for this script to trigger.', format: 'event-prereqs'}, EventPrereqSchema - repeats: {title: 'Repeats', description: 'Whether this script can trigger more than once during a level.', enum: [true, false, 'session'], 'default': false} + repeats: {title: 'Repeats', description: 'Whether this script can trigger more than once during a level.', enum: [true, false, 'session']} scriptPrereqs: c.array {title: 'Happens After', description: 'Scripts that need to fire first.'}, c.shortString(title: 'ID', description: 'A unique ID of a script.') notAfter: c.array {title: 'Not After', description: 'Do not run this script if any of these scripts have run.'}, @@ -189,7 +189,7 @@ LevelSystemSchema = c.object { }, original: c.objectId(title: 'Original', description: 'A reference to the original System being configured.', format: 'hidden') config: c.object {title: 'Configuration', description: 'System-specific configuration properties.', additionalProperties: true, format: 'level-system-configuration'} - majorVersion: {title: 'Major Version', description: 'Which major version of the System is being used.', type: 'integer', minimum: 0, default: 0, format: 'hidden'} + majorVersion: {title: 'Major Version', description: 'Which major version of the System is being used.', type: 'integer', minimum: 0, format: 'hidden'} GeneralArticleSchema = c.object { title: 'Article' @@ -211,16 +211,18 @@ LevelSchema = c.object { 'default': name: 'Ineffable Wizardry' description: 'This level is indescribably flarmy.' - documentation: {specificArticles: [], generalArticles: []} + documentation: {} scripts: [] thangs: [] + systems: [] + victory: {} } c.extendNamedProperties LevelSchema # let's have the name be the first property _.extend LevelSchema.properties, - description: {title: 'Description', description: 'A short explanation of what this level is about.', type: 'string', maxLength: 65536, 'default': 'This level is indescribably flarmy!', format: 'markdown'} + description: {title: 'Description', description: 'A short explanation of what this level is about.', type: 'string', maxLength: 65536, format: 'markdown'} documentation: c.object {title: 'Documentation', description: 'Documentation articles relating to this level.', required: ['specificArticles', 'generalArticles'], 'default': {specificArticles: [], generalArticles: []}}, - specificArticles: c.array {title: 'Specific Articles', description: 'Specific documentation articles that live only in this level.', uniqueItems: true, 'default': []}, SpecificArticleSchema - generalArticles: c.array {title: 'General Articles', description: 'General documentation articles that can be linked from multiple levels.', uniqueItems: true, 'default': []}, GeneralArticleSchema + specificArticles: c.array {title: 'Specific Articles', description: 'Specific documentation articles that live only in this level.', uniqueItems: true }, SpecificArticleSchema + generalArticles: c.array {title: 'General Articles', description: 'General documentation articles that can be linked from multiple levels.', uniqueItems: true}, GeneralArticleSchema background: c.objectId({format: 'hidden'}) nextLevel: { type: 'object', @@ -230,10 +232,13 @@ _.extend LevelSchema.properties, description: 'Reference to the next level players will play after beating this one.' } employerDescription: { type:'string', format: 'markdown', title: 'Employer Description' } - scripts: c.array {title: 'Scripts', description: 'An array of scripts that trigger based on what the player does and affect things outside of the core level simulation.', 'default': []}, ScriptSchema - thangs: c.array {title: 'Thangs', description: 'An array of Thangs that make up the level.', 'default': []}, LevelThangSchema - systems: c.array {title: 'Systems', description: 'Levels are configured by changing the Systems attached to them.', uniqueItems: true, default: []}, LevelSystemSchema # TODO: uniqueness should be based on 'original', not whole thing - victory: c.object {title: 'Victory Screen', default: {}, properties: {'body': {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'}, i18n: {type: 'object', format: 'i18n', props: ['body'], description: 'Help translate this victory message'}}} + scripts: c.array {title: 'Scripts', description: 'An array of scripts that trigger based on what the player does and affect things outside of the core level simulation.'}, ScriptSchema + thangs: c.array {title: 'Thangs', description: 'An array of Thangs that make up the level.' }, LevelThangSchema + systems: c.array {title: 'Systems', description: 'Levels are configured by changing the Systems attached to them.', uniqueItems: true }, LevelSystemSchema # TODO: uniqueness should be based on 'original', not whole thing + victory: c.object {title: 'Victory Screen'}, { + body: {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'}, + i18n: {type: 'object', format: 'i18n', props: ['body'], description: 'Help translate this victory message'} + } i18n: {type: 'object', format: 'i18n', props: ['name', 'description'], description: 'Help translate this level'} icon: {type: 'string', format: 'image-file', title: 'Icon'} banner: {type: 'string', format: 'image-file', title: 'Banner'} diff --git a/app/views/kinds/RootView.coffee b/app/views/kinds/RootView.coffee index 59603ccd1..3ae076ea1 100644 --- a/app/views/kinds/RootView.coffee +++ b/app/views/kinds/RootView.coffee @@ -89,7 +89,7 @@ module.exports = class RootView extends CocoView if $select.hasClass('fancified') $select.parent().find('.options, .trigger').remove() $select.unwrap().removeClass('fancified') - preferred = me.lang() + preferred = me.get('preferredLanguage', true) codes = _.keys(locale) genericCodes = _.filter codes, (code) -> _.find(codes, (code2) -> diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 604625705..7bde697d2 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -152,7 +152,7 @@ module.exports = class PlayLevelView extends RootView getRenderData: -> c = super() c.world = @world - if me.get('hourOfCode') and me.lang() is 'en-US' + if me.get('hourOfCode') and me.get('preferredLanguage', true) is 'en-US' # Show the Hour of Code footer explanation until it's been more than a day elapsed = (new Date() - new Date(me.get('dateCreated'))) c.explainHourOfCode = elapsed < 86400 * 1000 From 78a4000e34c571775a5646e609cf9eb39862adf6 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 23 Aug 2014 16:06:41 -0700 Subject: [PATCH 08/98] Refactoring defaults in schemas. --- app/schemas/models/level_component.coffee | 14 ++++---------- app/schemas/models/level_feedback.coffee | 2 +- app/schemas/models/level_session.coffee | 8 ++++---- app/schemas/models/level_system.coffee | 14 ++++---------- app/schemas/models/thang_component.coffee | 3 +-- 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/app/schemas/models/level_component.coffee b/app/schemas/models/level_component.coffee index 162abc617..fdef02787 100644 --- a/app/schemas/models/level_component.coffee +++ b/app/schemas/models/level_component.coffee @@ -16,7 +16,7 @@ systems = [ PropertyDocumentationSchema = c.object { title: 'Property Documentation' description: 'Documentation entry for a property this Component will add to its Thang which other Components might want to also use.' - 'default': + default: name: 'foo' type: 'object' description: 'The `foo` property can satisfy all the #{spriteName}\'s foobar needs. Use it wisely.' @@ -88,9 +88,6 @@ PropertyDocumentationSchema = c.object { DependencySchema = c.object { title: 'Component Dependency' description: 'A Component upon which this Component depends.' - 'default': - #original: ? - majorVersion: 0 required: ['original', 'majorVersion'] format: 'latest-version-reference' links: [{rel: 'db', href: '/db/level.component/{(original)}/version/{(majorVersion)}'}] @@ -114,6 +111,7 @@ LevelComponentSchema = c.object { codeLanguage: 'coffeescript' dependencies: [] # TODO: should depend on something by default propertyDocumentation: [] + configSchema: {} } c.extendNamedProperties LevelComponentSchema # let's have the name be the first property LevelComponentSchema.properties.name.pattern = c.classNamePattern @@ -123,13 +121,11 @@ _.extend LevelComponentSchema.properties, description: 'The short name of the System this Component belongs to, like \"ai\".' type: 'string' 'enum': systems - 'default': 'ai' description: title: 'Description' description: 'A short explanation of what this Component does.' type: 'string' maxLength: 2000 - 'default': 'This Component makes the Thang attack itself.' codeLanguage: type: 'string' title: 'Language' @@ -138,7 +134,6 @@ _.extend LevelComponentSchema.properties, code: title: 'Code' description: 'The code for this Component, as a CoffeeScript class. TODO: add link to documentation for how to write these.' - 'default': attackSelfCode type: 'string' format: 'coffee' js: @@ -146,14 +141,13 @@ _.extend LevelComponentSchema.properties, description: 'The transpiled JavaScript code for this Component' type: 'string' format: 'hidden' - dependencies: c.array {title: 'Dependencies', description: 'An array of Components upon which this Component depends.', 'default': [], uniqueItems: true}, DependencySchema - propertyDocumentation: c.array {title: 'Property Documentation', description: 'An array of documentation entries for each notable property this Component will add to its Thang which other Components might want to also use.', 'default': []}, PropertyDocumentationSchema + dependencies: c.array {title: 'Dependencies', description: 'An array of Components upon which this Component depends.', uniqueItems: true}, DependencySchema + propertyDocumentation: c.array {title: 'Property Documentation', description: 'An array of documentation entries for each notable property this Component will add to its Thang which other Components might want to also use.'}, PropertyDocumentationSchema configSchema: _.extend metaschema, {title: 'Configuration Schema', description: 'A schema for validating the arguments that can be passed to this Component as configuration.', default: {type: 'object', additionalProperties: false}} official: type: 'boolean' title: 'Official' description: 'Whether this is an official CodeCombat Component.' - 'default': false c.extendBasicProperties LevelComponentSchema, 'level.component' c.extendSearchableProperties LevelComponentSchema diff --git a/app/schemas/models/level_feedback.coffee b/app/schemas/models/level_feedback.coffee index ce0374d8b..fe7517057 100644 --- a/app/schemas/models/level_feedback.coffee +++ b/app/schemas/models/level_feedback.coffee @@ -2,7 +2,7 @@ c = require './../schemas' LevelFeedbackLevelSchema = c.object {required: ['original', 'majorVersion']}, { original: c.objectId({}) - majorVersion: {type: 'integer', minimum: 0, default: 0}} + majorVersion: {type: 'integer', minimum: 0 }} LevelFeedbackSchema = c.object { title: 'Feedback' diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 235052c53..480159024 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -18,11 +18,14 @@ LevelSessionLevelSchema = c.object {required: ['original', 'majorVersion'], link majorVersion: type: 'integer' minimum: 0 - default: 0 LevelSessionSchema = c.object title: 'Session' description: 'A single session for a given level.' + default: + codeLanguage: 'javascript' + submittedCodeLanguage: 'javascript' + playtime: 0 _.extend LevelSessionSchema.properties, # denormalization @@ -123,12 +126,10 @@ _.extend LevelSessionSchema.properties, codeLanguage: type: 'string' - default: 'javascript' playtime: type: 'number' title: 'Playtime' - default: 0 description: 'The total playtime on this session' teamSpells: @@ -167,7 +168,6 @@ _.extend LevelSessionSchema.properties, submittedCodeLanguage: type: 'string' - default: 'javascript' transpiledCode: type: 'object' diff --git a/app/schemas/models/level_system.coffee b/app/schemas/models/level_system.coffee index 13f97e545..218e4a0e0 100644 --- a/app/schemas/models/level_system.coffee +++ b/app/schemas/models/level_system.coffee @@ -21,7 +21,7 @@ class Jitter extends System PropertyDocumentationSchema = c.object { title: 'Property Documentation' description: 'Documentation entry for a property this System will add to its Thang which other Systems might want to also use.' - 'default': + default: name: 'foo' type: 'object' description: 'This System provides a "foo" property to satisfy all one\'s foobar needs. Use it wisely.' @@ -36,9 +36,6 @@ PropertyDocumentationSchema = c.object { DependencySchema = c.object { title: 'System Dependency' description: 'A System upon which this System depends.' - 'default': - #original: ? - majorVersion: 0 required: ['original', 'majorVersion'] format: 'latest-version-reference' links: [{rel: 'db', href: '/db/level.system/{(original)}/version/{(majorVersion)}'}] @@ -54,7 +51,7 @@ LevelSystemSchema = c.object { title: 'System' description: 'A System which can affect Level behavior.' required: ['name', 'description', 'code', 'dependencies', 'propertyDocumentation', 'codeLanguage'] - 'default': + default: name: 'JitterSystem' description: 'This System makes all idle, movable Thangs jitter around.' code: jitterSystemCode @@ -70,7 +67,6 @@ _.extend LevelSystemSchema.properties, description: 'A short explanation of what this System does.' type: 'string' maxLength: 2000 - 'default': 'This System doesn\'t do anything yet.' codeLanguage: type: 'string' title: 'Language' @@ -79,7 +75,6 @@ _.extend LevelSystemSchema.properties, code: title: 'Code' description: 'The code for this System, as a CoffeeScript class. TODO: add link to documentation for how to write these.' - 'default': jitterSystemCode type: 'string' format: 'coffee' js: @@ -87,14 +82,13 @@ _.extend LevelSystemSchema.properties, description: 'The transpiled JavaScript code for this System' type: 'string' format: 'hidden' - dependencies: c.array {title: 'Dependencies', description: 'An array of Systems upon which this System depends.', 'default': [], uniqueItems: true}, DependencySchema - propertyDocumentation: c.array {title: 'Property Documentation', description: 'An array of documentation entries for each notable property this System will add to its Level which other Systems might want to also use.', 'default': []}, PropertyDocumentationSchema + dependencies: c.array {title: 'Dependencies', description: 'An array of Systems upon which this System depends.', uniqueItems: true}, DependencySchema + propertyDocumentation: c.array {title: 'Property Documentation', description: 'An array of documentation entries for each notable property this System will add to its Level which other Systems might want to also use.'}, PropertyDocumentationSchema configSchema: _.extend metaschema, {title: 'Configuration Schema', description: 'A schema for validating the arguments that can be passed to this System as configuration.', default: {type: 'object', additionalProperties: false}} official: type: 'boolean' title: 'Official' description: 'Whether this is an official CodeCombat System.' - 'default': false c.extendBasicProperties LevelSystemSchema, 'level.system' c.extendSearchableProperties LevelSystemSchema diff --git a/app/schemas/models/thang_component.coffee b/app/schemas/models/thang_component.coffee index 97b5e18d8..9daf0b3dd 100644 --- a/app/schemas/models/thang_component.coffee +++ b/app/schemas/models/thang_component.coffee @@ -5,7 +5,7 @@ module.exports = ThangComponentSchema = c.object { description: 'Configuration for a Component that this Thang uses.' format: 'component-reference' required: ['original', 'majorVersion'] - 'default': + default: majorVersion: 0 config: {} links: [{rel: 'db', href: '/db/level.component/{(original)}/version/{(majorVersion)}'}] @@ -17,5 +17,4 @@ module.exports = ThangComponentSchema = c.object { description: 'Which major version of the Component is being used.' type: 'integer' minimum: 0 - default: 0 format: 'hidden' From 297b3e3e63bc2a63023cf68c4187b576c355253d Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sun, 24 Aug 2014 11:19:44 -0700 Subject: [PATCH 09/98] Last schema refactoring. --- app/schemas/models/thang_type.coffee | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/schemas/models/thang_type.coffee b/app/schemas/models/thang_type.coffee index 5b7e4851e..09e88fd21 100644 --- a/app/schemas/models/thang_type.coffee +++ b/app/schemas/models/thang_type.coffee @@ -101,7 +101,7 @@ ActionSchema = c.object {}, SoundSchema = c.sound({delay: {type: 'number'}}) _.extend ThangTypeSchema.properties, - raw: c.object {title: 'Raw Vector Data'}, + raw: c.object {title: 'Raw Vector Data', default: {shapes: {}, containers: {}, animations: {}}}, shapes: c.object {title: 'Shapes', additionalProperties: ShapeObjectSchema} containers: c.object {title: 'Containers', additionalProperties: ContainerObjectSchema} animations: c.object {title: 'Animations', additionalProperties: RawAnimationObjectSchema} @@ -131,19 +131,22 @@ _.extend ThangTypeSchema.properties, type: 'array' format: 'thang-color-group' items: {type: 'string'} - snap: c.object {title: 'Snap', description: 'In the level editor, snap positioning to these intervals.', required: ['x', 'y']}, + snap: c.object {title: 'Snap', description: 'In the level editor, snap positioning to these intervals.', required: ['x', 'y'], default: {x: 4, y: 4}}, x: title: 'Snap X' type: 'number' description: 'Snap to this many meters in the x-direction.' - default: 4 y: title: 'Snap Y' type: 'number' description: 'Snap to this many meters in the y-direction.' - default: 4 components: c.array {title: 'Components', description: 'Thangs are configured by changing the Components attached to them.', uniqueItems: true, format: 'thang-components-array'}, ThangComponentSchema # TODO: uniqueness should be based on 'original', not whole thing +ThangTypeSchema.required = ['kind'] + +ThangTypeSchema.deafult = + raw: {} + ThangTypeSchema.definitions = action: ActionSchema sound: SoundSchema From 1669871acc23fef7014f335eb0a2c8cb96b6c61a Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Mon, 25 Aug 2014 11:39:38 -0700 Subject: [PATCH 10/98] Fixed a demo. --- .../editor/component/ThangComponentsEditView.demo.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee index d41af9628..e8be1b896 100644 --- a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee +++ b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee @@ -4,7 +4,7 @@ responses = '/db/level.component/A/version/0': { system: 'System' original: 'A' - majorVersion: 0 + version: { major: 0, minor: 0 } name: 'A' configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} } @@ -12,17 +12,18 @@ responses = '/db/level.component/B/version/0': { system: 'System' original: 'B' - majorVersion: 0 + version: { major: 0, minor: 0 } name: 'B (depends on A)' dependencies: [{original:'A', majorVersion: 0}] } '/db/level.component/C/version/0': { system: 'System' original: 'C' - majorVersion: 0 + version: { major: 0, minor: 0 } name: 'C (depends on B)' dependencies: [{original:'B', majorVersion: 0}] } + '/db/thang.type': [] module.exports = -> view = new ThangComponentEditView({ From 1406a970eaad4837fa2175bd5b8b52384e094e38 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Mon, 25 Aug 2014 20:34:46 -0700 Subject: [PATCH 11/98] Bunch of fixes to get the level editor working again. --- app/lib/world/world.coffee | 2 +- app/models/Level.coffee | 6 +- app/schemas/models/level.coffee | 2 +- app/schemas/models/level_component.coffee | 2 +- .../thang-component-config-view.sass | 3 + .../component/thang-components-edit-view.sass | 3 + .../thang-component-config-view.jade | 2 +- app/treema-ext.coffee | 2 +- .../component/ThangComponentConfigView.coffee | 10 +++ .../component/ThangComponentsEditView.coffee | 77 +++++++++++++------ .../level/settings/SettingsTabView.coffee | 2 +- config.coffee | 2 +- .../ThangComponentsEditView.demo.coffee | 26 ++++++- 13 files changed, 102 insertions(+), 37 deletions(-) diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee index e76ab2707..4611e0930 100644 --- a/app/lib/world/world.coffee +++ b/app/lib/world/world.coffee @@ -209,7 +209,7 @@ module.exports = class World @thangMap = {} # Load new Thangs - toAdd = (@loadThangFromLevel thangConfig, level.levelComponents, level.thangTypes for thangConfig in level.thangs) + toAdd = (@loadThangFromLevel thangConfig, level.levelComponents, level.thangTypes for thangConfig in level.thangs ? []) @extraneousThangs = consolidateThangs toAdd if willSimulate # Combine walls, for example; serialize the leftovers later @addThang thang for thang in toAdd null diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 9214556df..9a967f7a4 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -95,7 +95,7 @@ module.exports = class Level extends CocoModel #console.log 'sorted systems adding', systemModel.name sorted.push {model: systemModel, config: _.cloneDeep system.config} originalsSeen[system.original] = true - visit system for system in levelSystems + visit system for system in levelSystems ? [] sorted sortThangComponents: (thangs, levelComponents, parentType) -> @@ -106,7 +106,7 @@ module.exports = class Level extends CocoModel # Decision? Just special case the sort logic in here until we have more examples than these two and decide how best to handle most of the cases then, since we don't really know the whole of the problem yet. # TODO: anything that depends on Programmable will break right now. - for thang in thangs + for thang in thangs ? [] sorted = [] visit = (c) -> return if c in sorted @@ -137,7 +137,7 @@ module.exports = class Level extends CocoModel # TODO DEFAULTS fillInDefaultComponentConfiguration: (thangs, levelComponents) -> - for thang in thangs + for thang in thangs ? [] for component in thang.components or [] continue unless lc = _.find levelComponents, {original: component.original} component.config ?= {} diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index ae05899c2..f1bfb29a5 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -207,7 +207,7 @@ GeneralArticleSchema = c.object { LevelSchema = c.object { title: 'Level' description: 'A spectacular level which will delight and educate its stalwart players with the sorcery of coding.' - required: ['name', 'description', 'scripts', 'thangs', 'documentation'] + required: ['name'] 'default': name: 'Ineffable Wizardry' description: 'This level is indescribably flarmy.' diff --git a/app/schemas/models/level_component.coffee b/app/schemas/models/level_component.coffee index fdef02787..9907210a6 100644 --- a/app/schemas/models/level_component.coffee +++ b/app/schemas/models/level_component.coffee @@ -103,7 +103,7 @@ LevelComponentSchema = c.object { title: 'Component' description: 'A Component which can affect Thang behavior.' required: ['system', 'name', 'description', 'code', 'dependencies', 'propertyDocumentation', 'codeLanguage'] - 'default': + default: system: 'ai' name: 'AttacksSelf' description: 'This Component makes the Thang attack itself.' diff --git a/app/styles/editor/component/thang-component-config-view.sass b/app/styles/editor/component/thang-component-config-view.sass index e2670469b..ab8da8432 100644 --- a/app/styles/editor/component/thang-component-config-view.sass +++ b/app/styles/editor/component/thang-component-config-view.sass @@ -4,3 +4,6 @@ .treema-root border: 0 padding: 0 5px + + .is-default-component + background-color: lightgray \ No newline at end of file diff --git a/app/styles/editor/component/thang-components-edit-view.sass b/app/styles/editor/component/thang-components-edit-view.sass index 77b57132d..c10c7c23f 100644 --- a/app/styles/editor/component/thang-components-edit-view.sass +++ b/app/styles/editor/component/thang-components-edit-view.sass @@ -29,6 +29,9 @@ bottom: 0 overflow: scroll + .treema-key, .treema-description + display: none + .dependent background-color: rgba(128, 64, 255, 0.10) diff --git a/app/templates/editor/component/thang-component-config-view.jade b/app/templates/editor/component/thang-component-config-view.jade index c1ef8c916..80535c740 100644 --- a/app/templates/editor/component/thang-component-config-view.jade +++ b/app/templates/editor/component/thang-component-config-view.jade @@ -1,5 +1,5 @@ .panel.panel-default - .panel-heading + .panel-heading(class=isDefaultComponent ? "is-default-component" : "") em #{component.system}. strong.panel-title.spr= component.name span#description.text-muted= component.description diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 909237d5e..cbdc5e579 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -416,7 +416,7 @@ class LevelComponentReferenceNode extends LatestVersionReferenceNode # 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 + canEdit: -> not @getData().original # only allow editing if the row's data hasn't been set yet LatestVersionReferenceNode.prototype.search = _.debounce(LatestVersionReferenceNode.prototype.search, 200) diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index b27ddb177..eda43b494 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -17,6 +17,7 @@ module.exports = class ThangComponentConfigView extends CocoView super options @component = options.component @config = options.config or {} + @additionalDefaults = options.additionalDefaults @world = options.world @level = options.level @callback = options.callback @@ -25,12 +26,18 @@ module.exports = class ThangComponentConfigView extends CocoView context = super(context) context.component = @component.attributes context.configProperties = [] + context.isDefaultComponent = @isDefaultComponent context afterRender: -> super() @buildTreema() + setIsDefaultComponent: (isDefaultComponent) -> + changed = @isDefaultComponent isnt isDefaultComponent + @isDefaultComponent = isDefaultComponent + @render() if changed + buildTreema: -> thangs = if @level? then @level.get('thangs') else [] thangIDs = _.filter(_.pluck(thangs, 'id')) @@ -39,6 +46,9 @@ module.exports = class ThangComponentConfigView extends CocoView superteams = _.union(teams, superteams) config = $.extend true, {}, @config schema = $.extend true, {}, @component.get('configSchema') + schema.default ?= {} + _.merge schema.default, @additionalDefaults if @additionalDefaults + if @level?.get('type') is 'hero' schema.required = [] treemaOptions = diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 9c5678694..02eba88cf 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -28,13 +28,18 @@ module.exports = class ThangComponentsEditView extends CocoView @originalsLoaded = {} @components = options.components or [] @components = $.extend true, [], @components # just to be sure + @setThangType options.thangType @lastComponentLength = @components.length @world = options.world @level = options.level @loadComponents(@components) # Need to grab the ThangTypes so that we can autocomplete items in inventory based on them. @itemThangTypes = @supermodel.loadCollection(new ItemThangTypeSearchCollection(), 'thangs').model - + + setThangType: (@thangType) -> + return unless componentRefs = @thangType?.get('components') + @loadComponents(componentRefs) + loadComponents: (components) -> for componentRef in components # just to handle if ever somehow the same component is loaded twice, through bad data and alike @@ -59,26 +64,20 @@ module.exports = class ThangComponentsEditView extends CocoView @addThangComponentConfigViews() buildComponentsTreema: -> + components = _.zipObject((c.original for c in @components), @components) + defaultValue = undefined + if thangTypeComponents = @thangType?.get('components', true) + defaultValue = _.zipObject((c.original for c in thangTypeComponents), thangTypeComponents) + treemaOptions = supermodel: @supermodel - schema: Level.schema.properties.thangs.items.properties.components - data: $.extend true, [], @components + schema: { + type: 'object' + default: defaultValue + additionalProperties: Level.schema.properties.thangs.items.properties.components.items + }, + data: $.extend true, {}, components callbacks: {select: @onSelectComponent, change: @onComponentsTreemaChanged} - noSortable: true - nodeClasses: - 'thang-components-array': ThangComponentsArrayNode - 'point2d': nodes.WorldPointNode - 'viewport': nodes.WorldViewportNode - 'bounds': nodes.WorldBoundsNode - 'radians': nodes.RadiansNode - 'team': nodes.TeamNode - 'superteam': nodes.SuperteamNode - 'meters': nodes.MetersNode - 'kilograms': nodes.KilogramsNode - 'seconds': nodes.SecondsNode - 'speed': nodes.SpeedNode - 'acceleration': nodes.AccelerationNode - 'item-thang-type': nodes.ItemThangTypeNode @componentsTreema = @$el.find('#thang-components-column .treema').treema treemaOptions @componentsTreema.build() @@ -91,7 +90,7 @@ module.exports = class ThangComponentsEditView extends CocoView componentMap[component.original] = component newComponentsList = [] - for component in @componentsTreema.data + for component in _.values(@componentsTreema.data) newComponentsList.push(componentMap[component.original] or component) @components = newComponentsList @@ -204,12 +203,26 @@ module.exports = class ThangComponentsEditView extends CocoView # 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 @componentsTreema.data + + componentRefs = _.merge {}, @componentsTreema.data + if thangTypeComponents = @thangType?.get('components') + thangComponentRefs = _.zipObject((c.original for c in thangTypeComponents), thangTypeComponents) + for thangTypeComponent in thangTypeComponents + if componentRef = componentRefs[thangTypeComponent.original] + componentRef.additionalDefaults = thangTypeComponent.config + else + modifiedRef = _.merge {}, thangTypeComponent + modifiedRef.additionalDefaults = modifiedRef.config + delete modifiedRef.additionalDefaults + componentRefs[thangTypeComponent.original] = modifiedRef + + for componentRef in _.values(componentRefs) subview = componentConfigViews[componentRef.original] if not subview subview = @makeThangComponentConfigView(componentRef) continue unless subview @registerSubView(subview) + subview.setIsDefaultComponent(not @componentsTreema.data[componentRef.original]) configsEl.append(subview.$el) makeThangComponentConfigView: (thangComponent) -> @@ -222,15 +235,29 @@ module.exports = class ThangComponentsEditView extends CocoView world: @world config: config component: component + additionalDefaults: thangComponent.additionalDefaults }) configView.render() @listenTo configView, 'changed', @onConfigChanged configView onConfigChanged: (e) -> + foundComponent = false for thangComponent in @components if thangComponent.original is e.component.get('original') thangComponent.config = e.config + foundComponent = true + break + + if not foundComponent + @components.push({ + original: e.component.get('original') + majorVersion: e.component.get('version').major + config: e.config + }) + @onComponentsChanged() + + @updateComponentsList() @reportChanges() onSelectComponent: (e, nodes) => @@ -239,28 +266,28 @@ module.exports = class ThangComponentsEditView extends CocoView # find dependent components dependents = {} - dependents[nodes[0].data.original] = true - componentsToCheck = [nodes[0].data.original] + dependents[nodes[0].getData().original] = true + componentsToCheck = [nodes[0].getData().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') + for dependency in otherComponent.get('dependencies', true) if dependents[dependency.original] dependents[otherComponentRef.original] = true componentsToCheck.push otherComponentRef.original # highlight them for child in _.values(@componentsTreema.childrenTreemas) - if dependents[child.data.original] + if dependents[child.getData().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 + if subview.component.get('original') is nodes[0].getData().original subview.$el[0].scrollIntoView() break diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index cf2c57196..04fd12b16 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -47,7 +47,7 @@ module.exports = class SettingsTabView extends CocoView @settingsTreema.open() getThangIDs: -> - (t.id for t in @level.get('thangs') when t.id isnt 'Interface') + (t.id for t in @level.get('thangs') ? [] when t.id isnt 'Interface') onSettingsChanged: (e) => $('.level-title').text @settingsTreema.data.name diff --git a/config.coffee b/config.coffee index 26c7687ca..62d05b3d8 100644 --- a/config.coffee +++ b/config.coffee @@ -7,7 +7,7 @@ exports.config = 'public': 'public' conventions: ignored: (path) -> startsWith(sysPath.basename(path), '_') - sourceMaps: false + sourceMaps: true files: javascripts: defaultExtension: 'coffee' diff --git a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee index e8be1b896..3d081bb0e 100644 --- a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee +++ b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee @@ -1,4 +1,5 @@ ThangComponentEditView = require('views/editor/component/ThangComponentsEditView') +ThangType = require 'models/ThangType' responses = '/db/level.component/A/version/0': { @@ -6,8 +7,13 @@ responses = original: 'A' version: { major: 0, minor: 0 } name: 'A' - configSchema: { type: 'object', properties: { propA: { type: 'number' }, propB: { type: 'string' }} } - + configSchema: { + type: 'object' + properties: { + propA: { type: 'number' } + propB: { type: 'string' } + } + } } '/db/level.component/B/version/0': { system: 'System' @@ -22,6 +28,16 @@ responses = version: { major: 0, minor: 0 } name: 'C (depends on B)' dependencies: [{original:'B', majorVersion: 0}] + configSchema: { + type: 'object' + default: { propC: 'Default property from component config' } + } + } + '/db/level.component/D/version/0': { + system: 'System' + original: 'D' + version: { major: 0, minor: 0 } + name: 'D (comes from ThangType components)' } '/db/thang.type': [] @@ -32,6 +48,12 @@ module.exports = -> { original: 'B', majorVersion: 0 } { original: 'C', majorVersion: 0 } ] + thangType: new ThangType({ + components: [ + { original: 'C', majorVersion: 0, config: {propD: 'Default property from thang type component.'} } + { original: 'D', majorVersion: 0 } + ] + }) }) view.render() From 6091eaea6f320592eb23456aca83ca0740611132 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 09:48:33 -0700 Subject: [PATCH 12/98] Bunch of fixes to the thang component edit view. --- .../component/ThangComponentConfigView.coffee | 8 +++--- .../component/ThangComponentsEditView.coffee | 27 ++++++++++++------- .../level/thangs/LevelThangEditView.coffee | 2 ++ .../ThangComponentsEditView.spec.coffee | 2 -- .../ThangComponentsEditView.demo.coffee | 4 +-- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index eda43b494..9c5b0c31d 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -10,9 +10,6 @@ module.exports = class ThangComponentConfigView extends CocoView template: template changed: false - events: - 'click .treema-shortened': -> console.log 'clicked treema root' - constructor: (options) -> super options @component = options.component @@ -35,6 +32,7 @@ module.exports = class ThangComponentConfigView extends CocoView setIsDefaultComponent: (isDefaultComponent) -> changed = @isDefaultComponent isnt isDefaultComponent + if isDefaultComponent then @config = undefined @isDefaultComponent = isDefaultComponent @render() if changed @@ -44,7 +42,6 @@ module.exports = class ThangComponentConfigView extends CocoView teams = _.filter(_.pluck(thangs, 'team')) superteams = _.filter(_.pluck(thangs, 'superteam')) superteams = _.union(teams, superteams) - config = $.extend true, {}, @config schema = $.extend true, {}, @component.get('configSchema') schema.default ?= {} _.merge schema.default, @additionalDefaults if @additionalDefaults @@ -54,7 +51,7 @@ module.exports = class ThangComponentConfigView extends CocoView treemaOptions = supermodel: @supermodel schema: schema - data: config + data: @config callbacks: {change: @onConfigEdited} world: @world view: @ @@ -83,6 +80,7 @@ module.exports = class ThangComponentConfigView extends CocoView @$el.find('.panel-body').hide() onConfigEdited: => + @config = @editThangTreema.data @changed = true @trigger 'changed', { component: @component, config: @data() } diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 02eba88cf..f12a0b331 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -114,6 +114,11 @@ module.exports = class ThangComponentsEditView extends CocoView for component in @components componentMap[component.original] = component + thangComponentMap = {} + if thangTypeComponents = @thangType?.get('components') + for thangTypeComponent in thangTypeComponents + thangComponentMap[thangTypeComponent.original] = thangTypeComponent + # Deleting components missing dependencies. while true removedSomething = false @@ -121,7 +126,7 @@ module.exports = class ThangComponentsEditView extends CocoView componentModel = @supermodel.getModelByOriginalAndMajorVersion( LevelComponent, componentRef.original, componentRef.majorVersion) for dependency in componentModel.get('dependencies') or [] - if not componentMap[dependency.original] + unless (componentMap[dependency.original] or thangComponentMap[dependency.original]) delete componentMap[componentRef.original] component = @supermodel.getModelByOriginal( LevelComponent, componentRef.original) @@ -140,7 +145,7 @@ module.exports = class ThangComponentsEditView extends CocoView # 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')] + unless (componentMap[subview.component.get('original')] or thangComponentMap[subview.component.get('original')]) @removeSubView(subview) @updateComponentsList() @@ -157,6 +162,10 @@ module.exports = class ThangComponentsEditView extends CocoView for component in @components componentMap[component.original] = component + if thangTypeComponents = @thangType?.get('components') + for thangTypeComponent in thangTypeComponents + componentMap[thangTypeComponent.original] = thangTypeComponent + # Go through the map, adding missing dependencies. while true addedSomething = false @@ -213,7 +222,7 @@ module.exports = class ThangComponentsEditView extends CocoView else modifiedRef = _.merge {}, thangTypeComponent modifiedRef.additionalDefaults = modifiedRef.config - delete modifiedRef.additionalDefaults + delete modifiedRef.config componentRefs[thangTypeComponent.original] = modifiedRef for componentRef in _.values(componentRefs) @@ -255,7 +264,12 @@ module.exports = class ThangComponentsEditView extends CocoView majorVersion: e.component.get('version').major config: e.config }) - @onComponentsChanged() + + for subview in _.values(@subviews) + continue unless subview instanceof ThangComponentConfigView + if subview.component.get('original') is e.component.get('original') + _.defer -> subview.setIsDefaultComponent(false) + break @updateComponentsList() @reportChanges() @@ -291,11 +305,6 @@ module.exports = class ThangComponentsEditView extends CocoView 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() diff --git a/app/views/editor/level/thangs/LevelThangEditView.coffee b/app/views/editor/level/thangs/LevelThangEditView.coffee index 7ed5dce9e..fcfddc37e 100644 --- a/app/views/editor/level/thangs/LevelThangEditView.coffee +++ b/app/views/editor/level/thangs/LevelThangEditView.coffee @@ -36,11 +36,13 @@ module.exports = class LevelThangEditView extends CocoView onLoaded: -> @render() afterRender: -> super() + thangType = @supermodel.getModelByOriginal(ThangType, @thangData.thangType) options = components: @thangData.components supermodel: @supermodel level: @level world: @world + thangType: thangType @thangComponentEditView = new ThangComponentsEditView options @listenTo @thangComponentEditView, 'components-changed', @onComponentsChanged diff --git a/test/app/views/editor/component/ThangComponentsEditView.spec.coffee b/test/app/views/editor/component/ThangComponentsEditView.spec.coffee index 6862a0a03..a2349a8d9 100644 --- a/test/app/views/editor/component/ThangComponentsEditView.spec.coffee +++ b/test/app/views/editor/component/ThangComponentsEditView.spec.coffee @@ -52,14 +52,12 @@ describe 'ThangComponentsEditView', -> # TODO: Figure out why this is breaking karma but not always it 'adds dependencies to its components list', -> -# jasmine.Ajax.requests.sendResponses(responses) componentOriginals = (c.original for c in view.components) expect('A' in componentOriginals).toBeTruthy() expect('B' in componentOriginals).toBeTruthy() expect('C' in componentOriginals).toBeTruthy() it 'removes components that are dependent on a removed component', -> -# jasmine.Ajax.requests.sendResponses(responses) view.components = (c for c in view.components when c.original isnt 'A') view.onComponentsChanged() expect(view.components.length).toBe(0) diff --git a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee index 3d081bb0e..8bab8d90f 100644 --- a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee +++ b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee @@ -50,8 +50,8 @@ module.exports = -> ] thangType: new ThangType({ components: [ - { original: 'C', majorVersion: 0, config: {propD: 'Default property from thang type component.'} } - { original: 'D', majorVersion: 0 } + { original: 'A', majorVersion: 0, config: {propD: 'Default property from thang type component.'} } + { original: 'D', majorVersion: 0, config: {prop1: 'one', prop2: 'two'} } ] }) }) From 3b6de071f77da56416e956e503bcc638537e9e82 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 09:55:59 -0700 Subject: [PATCH 13/98] Getting rid of a debug line. --- app/models/SuperModel.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index cd7db688b..524a3a754 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -60,7 +60,6 @@ module.exports = class SuperModel extends Backbone.Model @addCollection collection onCollectionSynced = (c) -> if collection.url is c.url - console.debug 'Registering collection', url, c @registerCollection c else console.warn 'Sync triggered for collection', c From fb06e582018e94e49f1369cd513cf2ce14a23fdf Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 10:14:36 -0700 Subject: [PATCH 14/98] Fixed a bug where CocoModel wouldn't unset properties on revert because saveBackup is now debounced and would reintroduce those properties. --- app/models/CocoModel.coffee | 1 + app/views/editor/level/settings/SettingsTabView.coffee | 1 + 2 files changed, 2 insertions(+) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index b8ea3012e..e9f97dc24 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -156,6 +156,7 @@ class CocoModel extends Backbone.Model @_revertAttributes = $.extend(true, {}, @attributes) revert: -> + @clear({silent: true}) @set(@_revertAttributes, {silent: true}) if @_revertAttributes @clearBackup() diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index 04fd12b16..8b0d6dadd 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -29,6 +29,7 @@ module.exports = class SettingsTabView extends CocoView schema = _.cloneDeep Level.schema schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings schema.required = _.intersection schema.required, @editableSettings + schema.default = _.pick schema.default, (value, key) => key in @editableSettings thangIDs = @getThangIDs() treemaOptions = filePath: "db/level/#{@level.get('original')}" From ef8ca9874a4eb87d546c51a8cfec40423cb25065 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 10:28:01 -0700 Subject: [PATCH 15/98] For the markdown editor node, tweaked ace height, and made sure preview div is hidden by default. --- app/styles/treema-ext.sass | 1 + app/treema-ext.coffee | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/styles/treema-ext.sass b/app/styles/treema-ext.sass index 4d920df86..ff40f9aa7 100644 --- a/app/styles/treema-ext.sass +++ b/app/styles/treema-ext.sass @@ -9,6 +9,7 @@ &, & > div.ace_editor width: 100% + min-height: 48px .buttons button float: left diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 88263943d..94c606684 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -27,7 +27,7 @@ class LiveEditingMarkup extends TreemaNode.nodeMap.ace @addPreviewToggle(buttonRow) @addImageUpload(buttonRow) super(valEl) - valEl.append($('
')) + valEl.append($('
').hide()) addImageUpload: (valEl) -> return unless me.isAdmin() From 5b5bceda3af980afdb1c564705d2769adce23aad Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 13:08:56 -0700 Subject: [PATCH 16/98] Components tab now incorporates components inherited from thang types. --- .../editor/level/components/ComponentsTabView.coffee | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/views/editor/level/components/ComponentsTabView.coffee b/app/views/editor/level/components/ComponentsTabView.coffee index 10cd608aa..d3cd60125 100644 --- a/app/views/editor/level/components/ComponentsTabView.coffee +++ b/app/views/editor/level/components/ComponentsTabView.coffee @@ -1,5 +1,6 @@ CocoView = require 'views/kinds/CocoView' template = require 'templates/editor/level/components_tab' +ThangType = require 'models/ThangType' LevelComponent = require 'models/LevelComponent' LevelComponentEditView = require './LevelComponentEditView' LevelComponentNewView = require './NewLevelComponentModal' @@ -27,7 +28,15 @@ module.exports = class ComponentsTabView extends CocoView refreshLevelThangsTreema: (thangsData) -> presentComponents = {} for thang in thangsData + componentMap = {} + thangType = @supermodel.getModelByOriginal ThangType, thang.thangType + for component in thangType.get('components') ? [] + componentMap[component.original] = component + for component in thang.components + componentMap[component.original] = component + + for component in _.values(componentMap) haveThisComponent = (presentComponents[component.original + '.' + (component.majorVersion ? 0)] ?= []) haveThisComponent.push thang.id if haveThisComponent.length < 100 # for performance when adding many Thangs return if _.isEqual presentComponents, @presentComponents From 39d0df5153e2e406be377cf8ddd0c0a08617b059 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 13:27:17 -0700 Subject: [PATCH 17/98] Fixed name ordering in the thang components editor. --- .../editor/component/ThangComponentsEditView.coffee | 13 +++++++++---- .../component/ThangComponentsEditView.demo.coffee | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index f12a0b331..f2ce6b905 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -78,6 +78,8 @@ module.exports = class ThangComponentsEditView extends CocoView }, data: $.extend true, {}, components callbacks: {select: @onSelectComponent, change: @onComponentsTreemaChanged} + nodeClasses: + 'object': ThangComponentsObjectNode @componentsTreema = @$el.find('#thang-components-column .treema').treema treemaOptions @componentsTreema.build() @@ -345,11 +347,14 @@ module.exports = class ThangComponentsEditView extends CocoView @onComponentsChanged() -class ThangComponentsArrayNode extends TreemaArrayNode - valueClass: 'treema-thang-components-array' - sort: true - +class ThangComponentsObjectNode extends TreemaObjectNode + getChildren: -> + children = super(arguments...) + children.sort(@sortFunction) + sortFunction: (a, b) => + a = a.value ? a.defaultData + b = b.value ? b.defaultData 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) diff --git a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee index 8bab8d90f..079fc50ef 100644 --- a/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee +++ b/test/demo/views/editor/component/ThangComponentsEditView.demo.coffee @@ -44,9 +44,9 @@ responses = module.exports = -> view = new ThangComponentEditView({ components: [ - { original: 'A', majorVersion: 0, config: {propA: 1, propB: 'string'} } { original: 'B', majorVersion: 0 } { original: 'C', majorVersion: 0 } + { original: 'A', majorVersion: 0, config: {propA: 1, propB: 'string'} } ] thangType: new ThangType({ components: [ From 2f72aa4f47866273dde5833ac2699c593276084b Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 13:48:57 -0700 Subject: [PATCH 18/98] Fixed adding new components in the thang components edit view. --- app/views/editor/component/ThangComponentsEditView.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index f2ce6b905..b1b399135 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -348,6 +348,8 @@ module.exports = class ThangComponentsEditView extends CocoView class ThangComponentsObjectNode extends TreemaObjectNode + addNewChild: -> @addNewChildForKey('') # HACK to get the object adding to act more like adding to an array + getChildren: -> children = super(arguments...) children.sort(@sortFunction) From ab669739a6b88fa2a94e8880a5f2d129c15416a7 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 14:15:21 -0700 Subject: [PATCH 19/98] Fixed a bug with the thang component edit view: labels of new components not showing up. --- app/models/SuperModel.coffee | 3 ++- app/treema-ext.coffee | 4 ++-- app/views/editor/component/ThangComponentsEditView.coffee | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 524a3a754..d16cd725f 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -90,7 +90,8 @@ module.exports = class SuperModel extends Backbone.Model getModelByOriginalAndMajorVersion: (ModelClass, original, majorVersion=0) -> _.find @models, (m) -> - m.get('original') is original and m.get('version').major is majorVersion and m.constructor.className is ModelClass.className + return unless v = m.get('version') + m.get('original') is original and v.major is majorVersion and m.constructor.className is ModelClass.className getModels: (ModelClass) -> # can't use instanceof. SuperModel gets passed between windows, and one window diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 94c606684..eedc965c7 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -359,8 +359,8 @@ class LatestVersionReferenceNode extends TreemaNode return @modelToString(docOrModel) if docOrModel instanceof CocoModel return 'Unknown' unless @settings.supermodel? m = CocoModel.getReferencedModel(@getData(), @schema) - urlGoingFor = m.url() - m = @settings.supermodel.getModel(urlGoingFor) + data = @getData() + m = @settings.supermodel.getModelByOriginalAndMajorVersion(m.constructor, data.original, data.majorVersion) if @instance and not m m = @instance m.url = -> urlGoingFor diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index b1b399135..30721bce0 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -362,8 +362,8 @@ class ThangComponentsObjectNode extends TreemaObjectNode 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 1 if a.get('system') > b.get('system') + return -1 if a.get('system') < b.get('system') + return 1 if a.get('name') > b.get('name') + return -1 if a.get('name') < b.get('name') return 0 From 162aa7633e3b6eb9b6d77c331c2ff0d173e29c80 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 15:40:51 -0700 Subject: [PATCH 20/98] Fixed the component and system settings treema, clearing out defaults for properties not being edited. --- app/views/editor/level/components/LevelComponentEditView.coffee | 1 + app/views/editor/level/systems/LevelSystemEditView.coffee | 1 + 2 files changed, 2 insertions(+) diff --git a/app/views/editor/level/components/LevelComponentEditView.coffee b/app/views/editor/level/components/LevelComponentEditView.coffee index c09600241..ffa90ce8b 100644 --- a/app/views/editor/level/components/LevelComponentEditView.coffee +++ b/app/views/editor/level/components/LevelComponentEditView.coffee @@ -49,6 +49,7 @@ module.exports = class LevelComponentEditView extends CocoView schema = _.cloneDeep LevelComponent.schema schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings schema.required = _.intersection schema.required, @editableSettings + schema.default = _.pick schema.default, (value, key) => key in @editableSettings treemaOptions = supermodel: @supermodel diff --git a/app/views/editor/level/systems/LevelSystemEditView.coffee b/app/views/editor/level/systems/LevelSystemEditView.coffee index b1bcf8f67..ebb056ed7 100644 --- a/app/views/editor/level/systems/LevelSystemEditView.coffee +++ b/app/views/editor/level/systems/LevelSystemEditView.coffee @@ -43,6 +43,7 @@ module.exports = class LevelSystemEditView extends CocoView schema = _.cloneDeep LevelSystem.schema schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings schema.required = _.intersection schema.required, @editableSettings + schema.default = _.pick schema.default, (value, key) => key in @editableSettings treemaOptions = supermodel: @supermodel From f1bf160755ae27fdc2616d6022d0768f41918bd2 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 15:42:03 -0700 Subject: [PATCH 21/98] Fixed merge conflict. --- app/schemas/models/user.coffee | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/schemas/models/user.coffee b/app/schemas/models/user.coffee index e834bece5..428cb7c0c 100644 --- a/app/schemas/models/user.coffee +++ b/app/schemas/models/user.coffee @@ -45,12 +45,7 @@ visa = c.shortString title: 'US Work Status' description: 'Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)' enum: ['Authorized to work in the US', 'Need visa sponsorship'] -<<<<<<< HEAD -======= - default: 'Authorized to work in the US' - ->>>>>>> master _.extend UserSchema.properties, email: c.shortString({title: 'Email', format: 'email'}) firstName: c.shortString({title: 'First Name'}) From 98752159ca5928b4338348d5902a917d569a4fd4 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 16:31:53 -0700 Subject: [PATCH 22/98] Made the SuperModel a little bit more clever about handling duplicate loaded models. --- app/models/SuperModel.coffee | 3 ++- test/app/models/SuperModel.spec.coffee | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index d16cd725f..809f66383 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -119,7 +119,8 @@ module.exports = class SuperModel extends Backbone.Model for model, i in collection.models cachedModel = @getModelByURL(model.getURL()) if cachedModel - collection.models[i] = cachedModel + clone = $.extend true, {}, model.attributes + cachedModel.set(clone, {silent: true}) else @registerModel(model) collection diff --git a/test/app/models/SuperModel.spec.coffee b/test/app/models/SuperModel.spec.coffee index 1e64d8d14..12219a584 100644 --- a/test/app/models/SuperModel.spec.coffee +++ b/test/app/models/SuperModel.spec.coffee @@ -50,3 +50,23 @@ describe 'SuperModel', -> _.defer -> expect(triggered).toBe(true) done() + + describe 'collection loading', -> + it 'combines models which are fetched from multiple sources', -> + s = new SuperModel() + + c1 = new ComponentsCollection() + c1.url = '/db/level.component?v=1' + s.loadCollection(c1, 'components') + + c2 = new ComponentsCollection() + c2.url = '/db/level.component?v=2' + s.loadCollection(c2, 'components') + + request = jasmine.Ajax.requests.sendResponses({ + '/db/level.component?v=1': [{"_id":"id","name":"Something"}] + '/db/level.component?v=2': [{"_id":"id","description":"This is something"}] + }) + + expect(s.models['/db/level.component/id'].get('name')).toBe('Something') + expect(s.models['/db/level.component/id'].get('description')).toBe('This is something') From 607a2a133ffa44ac22c01a34d222446f42ca2e63 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 26 Aug 2014 16:46:23 -0700 Subject: [PATCH 23/98] Made the SuperModel a little bit more clever about handling duplicate loaded models, hopefully fixed #1108. --- app/models/SuperModel.coffee | 1 + app/models/ThangType.coffee | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 809f66383..25f781034 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -121,6 +121,7 @@ module.exports = class SuperModel extends Backbone.Model if cachedModel clone = $.extend true, {}, model.attributes cachedModel.set(clone, {silent: true}) + console.debug "Updated cached model <#{cachedModel.get('name') or cachedModel.getURL()}> with new data" else @registerModel(model) collection diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index 8a8d990ea..4fe5fb30a 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -13,8 +13,6 @@ module.exports = class ThangType extends CocoModel initialize: -> super() @building = {} - @setDefaults() - @on 'sync', @setDefaults @spriteSheets = {} ## Testing memory clearing @@ -24,9 +22,6 @@ module.exports = class ThangType extends CocoModel # @_previousAttributes.raw = null #setTimeout f, 40000 - setDefaults: -> - @resetRawData() unless @get('raw') - resetRawData: -> @set('raw', {shapes: {}, containers: {}, animations: {}}) From ac2c39a266322c0396e8ea7733cb8155ab8a8f82 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Wed, 27 Aug 2014 09:49:37 -0700 Subject: [PATCH 24/98] Fixed an infinite loop bug in CoordinateGrid. --- app/lib/surface/CoordinateGrid.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/surface/CoordinateGrid.coffee b/app/lib/surface/CoordinateGrid.coffee index 1d796ada2..8712f3fc7 100644 --- a/app/lib/surface/CoordinateGrid.coffee +++ b/app/lib/surface/CoordinateGrid.coffee @@ -24,8 +24,8 @@ module.exports = class CoordinateGrid extends CocoClass toString: -> '' build: (worldSize) -> - worldWidth = worldSize[0] ? 80 - worldHeight = worldSize[1] ? 68 + worldWidth = worldSize[0] or 80 + worldHeight = worldSize[1] or 68 @gridContainer = new createjs.Container() @gridShape = new createjs.Shape() @gridContainer.addChild @gridShape From 7bdfcb4313688ca387af36a3bd6fd379e28bb0ed Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Wed, 27 Aug 2014 17:29:28 -0700 Subject: [PATCH 25/98] Fixed pasting component configs into the thang component config treema. --- app/views/editor/component/ThangComponentConfigView.coffee | 6 ++++++ app/views/editor/component/ThangComponentsEditView.coffee | 3 +-- app/views/editor/level/scripts/ScriptsTabView.coffee | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index 9c5b0c31d..320be5e6d 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -29,6 +29,11 @@ module.exports = class ThangComponentConfigView extends CocoView afterRender: -> super() @buildTreema() + + setConfig: (config) -> + @handlingChange = true + @editThangTreema.set('/', config) + @handlingChange = false setIsDefaultComponent: (isDefaultComponent) -> changed = @isDefaultComponent isnt isDefaultComponent @@ -80,6 +85,7 @@ module.exports = class ThangComponentConfigView extends CocoView @$el.find('.panel-body').hide() onConfigEdited: => + return if @handlingChange @config = @editThangTreema.data @changed = true @trigger 'changed', { component: @component, config: @data() } diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 30721bce0..1aa3aaab4 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -108,8 +108,7 @@ module.exports = class ThangComponentsEditView extends CocoView if @components.length < @lastComponentLength @onComponentsRemoved() - else - @onComponentsAdded() + @onComponentsAdded() onComponentsRemoved: -> componentMap = {} diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index e8f0a53f2..7b32358ce 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -88,7 +88,7 @@ module.exports = class ScriptsTabView extends CocoView @selectedScriptPath = newPath getThangIDs: -> - (t.id for t in @level.get('thangs') when t.id isnt 'Interface') + (t.id for t in @level.get('thangs') ? [] when t.id isnt 'Interface') onNewScriptAdded: (scriptNode) => return unless scriptNode From 87d455e98ba2afe40e6a668908be2f11fe123d00 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Wed, 27 Aug 2014 18:16:19 -0700 Subject: [PATCH 26/98] Wrote a script to migrate component config defaults to the new system. --- .../2014-08-27-component-config-defaults.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 scripts/mongodb/migrations/2014-08-27-component-config-defaults.js diff --git a/scripts/mongodb/migrations/2014-08-27-component-config-defaults.js b/scripts/mongodb/migrations/2014-08-27-component-config-defaults.js new file mode 100644 index 000000000..6d70f2f5b --- /dev/null +++ b/scripts/mongodb/migrations/2014-08-27-component-config-defaults.js @@ -0,0 +1,33 @@ +load('bower_components/lodash/dist/lodash.js'); +load('bower_components/underscore.string/dist/underscore.string.min.js'); +load('bower_components/tv4/tv4.js'); +load('bower_components/treema/treema-utils.js'); +print(typeof TreemaUtils); + +print(db.level.components.count()); +db.level.components.find().limit(3000000).forEach(function (levelComponent) { + thisTv4 = tv4.freshApi(); + thisTv4.addSchema('#', levelComponent.configSchema || {}); + var data = levelComponent.configSchema.default || {}; + TreemaUtils.populateRequireds(data, levelComponent.configSchema); + var props = levelComponent.configSchema.properties; + if(props) { + _.keys(levelComponent.configSchema.properties).forEach(function (key) { + if(data[key]) return; + var childSchema = props[key]; + var workingSchema = TreemaUtils.buildWorkingSchemas(childSchema, thisTv4)[0]; + if(workingSchema.default) + return data[key] = TreemaUtils.cloneDeep(workingSchema.default); + var type = props[key].type; + if(!type) return; + if(Array.isArray(type)) type = type[0]; + data[key] = TreemaUtils.defaultForType(type); + }) + } + delete levelComponent.configSchema.required; + levelComponent.configSchema.default = data; + print('\n\n--------------------', levelComponent.name); + print("SCHEMA: ", JSON.stringify(levelComponent.configSchema, null, '\t')); + +}); + From bcffc5811f947f9ec0d14f06fb64abdcc270db70 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Wed, 27 Aug 2014 18:17:47 -0700 Subject: [PATCH 27/98] Migrated the Level populating of component and system config defaults to the new Treema utility method. --- app/models/Level.coffee | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 9bee91881..b1f9f46ae 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -134,30 +134,17 @@ module.exports = class Level extends CocoModel visit comp thang.components = sorted - # TODO DEFAULTS fillInDefaultComponentConfiguration: (thangs, levelComponents) -> for thang in thangs ? [] for component in thang.components or [] continue unless lc = _.find levelComponents, {original: component.original} component.config ?= {} - @walkDefaults component.config, lc.configSchema.properties + TreemaNode.utils.populateDefaults(component.config, lc.configSchema) fillInDefaultSystemConfiguration: (levelSystems) -> for system in levelSystems ? [] system.config ?= {} - @walkDefaults system.config, system.model.configSchema.properties - - walkDefaults: (config, properties) -> - return unless properties - for prop, schema of properties - if schema.default? and config[prop] is undefined - #console.log 'Setting default of', config, 'for', prop, 'to', schema.default - config[prop] = schema.default - if schema.type is 'object' and config[prop] - @walkDefaults config[prop], schema.properties - else if schema.type is 'array' and config[prop] - for item in config[prop] or [] - @walkDefaults item, schema.items + TreemaNode.utils.populateDefaults(system.config, system.model.configSchema) dimensions: -> width = 0 From f8f67833d13ae214c7c019205132dd354d4a0713 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 09:52:46 -0700 Subject: [PATCH 28/98] Refactored the config defaults script to work on systems, too, and save results. --- ...27-component-and-system-config-defaults.js | 45 +++++++++++++++++++ .../2014-08-27-component-config-defaults.js | 33 -------------- 2 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 scripts/mongodb/migrations/2014-08-27-component-and-system-config-defaults.js delete mode 100644 scripts/mongodb/migrations/2014-08-27-component-config-defaults.js diff --git a/scripts/mongodb/migrations/2014-08-27-component-and-system-config-defaults.js b/scripts/mongodb/migrations/2014-08-27-component-and-system-config-defaults.js new file mode 100644 index 000000000..64346d8a1 --- /dev/null +++ b/scripts/mongodb/migrations/2014-08-27-component-and-system-config-defaults.js @@ -0,0 +1,45 @@ +load('bower_components/lodash/dist/lodash.js'); +load('bower_components/underscore.string/dist/underscore.string.min.js'); +load('bower_components/tv4/tv4.js'); +load('bower_components/treema/treema-utils.js'); + +print(db.level.components.count()); + +// This script can be modified later to also remove the old defaults in these components and systems. + +migrateDefault = function (doc) { + thisTv4 = tv4.freshApi(); + thisTv4.addSchema('#', doc.configSchema || {}); + var data = TreemaUtils.cloneDeep(doc.configSchema.default) || {}; + TreemaUtils.populateRequireds(data, doc.configSchema); + var props = doc.configSchema.properties; + if(props) { + _.keys(doc.configSchema.properties).forEach(function (key) { + if(data[key]) return; + var childSchema = props[key]; + var workingSchema = TreemaUtils.buildWorkingSchemas(childSchema, thisTv4)[0]; + if(workingSchema.default) + return data[key] = TreemaUtils.cloneDeep(workingSchema.default); + var type = props[key].type; + if(!type) return; + if(Array.isArray(type)) type = type[0]; + data[key] = TreemaUtils.defaultForType(type); + }) + } + delete doc.configSchema.required; + doc.configSchema.default = data; +// print('\n\n--------------------', doc.name); +// print("SCHEMA: ", JSON.stringify(doc.configSchema, null, '\t')); + if(doc.system) { + print('saving component', doc.name); + db.level.components.save(doc); + } + else { + print('saving system', doc.name); + db.level.systems.save(doc); + } +}; + +db.level.components.find({slug: {$exists: true}}).forEach(migrateDefault); +db.level.systems.find({slug: {$exists: true}}).forEach(migrateDefault); + diff --git a/scripts/mongodb/migrations/2014-08-27-component-config-defaults.js b/scripts/mongodb/migrations/2014-08-27-component-config-defaults.js deleted file mode 100644 index 6d70f2f5b..000000000 --- a/scripts/mongodb/migrations/2014-08-27-component-config-defaults.js +++ /dev/null @@ -1,33 +0,0 @@ -load('bower_components/lodash/dist/lodash.js'); -load('bower_components/underscore.string/dist/underscore.string.min.js'); -load('bower_components/tv4/tv4.js'); -load('bower_components/treema/treema-utils.js'); -print(typeof TreemaUtils); - -print(db.level.components.count()); -db.level.components.find().limit(3000000).forEach(function (levelComponent) { - thisTv4 = tv4.freshApi(); - thisTv4.addSchema('#', levelComponent.configSchema || {}); - var data = levelComponent.configSchema.default || {}; - TreemaUtils.populateRequireds(data, levelComponent.configSchema); - var props = levelComponent.configSchema.properties; - if(props) { - _.keys(levelComponent.configSchema.properties).forEach(function (key) { - if(data[key]) return; - var childSchema = props[key]; - var workingSchema = TreemaUtils.buildWorkingSchemas(childSchema, thisTv4)[0]; - if(workingSchema.default) - return data[key] = TreemaUtils.cloneDeep(workingSchema.default); - var type = props[key].type; - if(!type) return; - if(Array.isArray(type)) type = type[0]; - data[key] = TreemaUtils.defaultForType(type); - }) - } - delete levelComponent.configSchema.required; - levelComponent.configSchema.default = data; - print('\n\n--------------------', levelComponent.name); - print("SCHEMA: ", JSON.stringify(levelComponent.configSchema, null, '\t')); - -}); - From 94065a0c4c1b174de94930b20c1bd52adf35da31 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 09:59:07 -0700 Subject: [PATCH 29/98] Merged conflicts. --- .../editor/level/components/ComponentsTabView.coffee | 7 ------- app/views/editor/level/scripts/ScriptsTabView.coffee | 11 +---------- .../editor/level/settings/SettingsTabView.coffee | 10 +--------- app/views/editor/level/systems/SystemsTabView.coffee | 6 +----- app/views/editor/level/thangs/ThangsTabView.coffee | 6 +----- bower.json | 5 ----- 6 files changed, 4 insertions(+), 41 deletions(-) diff --git a/app/views/editor/level/components/ComponentsTabView.coffee b/app/views/editor/level/components/ComponentsTabView.coffee index f3f1595cb..98ba729f1 100644 --- a/app/views/editor/level/components/ComponentsTabView.coffee +++ b/app/views/editor/level/components/ComponentsTabView.coffee @@ -94,10 +94,3 @@ class LevelComponentNode extends TreemaObjectNode 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}" @buildValueForDisplaySimply valEl, "#{name} (#{count})" -<<<<<<< HEAD - - onEnterPressed: -> - data = @getData() - Backbone.Mediator.publish 'edit-level-component', original: data.original, majorVersion: data.majorVersion -======= ->>>>>>> master diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index cbd667939..654702f72 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -89,11 +89,7 @@ module.exports = class ScriptsTabView extends CocoView @selectedScriptPath = newPath getThangIDs: -> -<<<<<<< HEAD - (t.id for t in @level.get('thangs') ? [] when t.id isnt 'Interface') -======= - (t.id for t in @level.get('thangs')) ->>>>>>> master + (t.id for t in @level.get('thangs') ? []) onNewScriptAdded: (scriptNode) => return unless scriptNode @@ -169,13 +165,8 @@ class EventPropsNode extends TreemaNode.nodeMap.string joined = '(unset)' if not joined.length @buildValueForDisplaySimply valEl, joined -<<<<<<< HEAD buildValueForEditing: (valEl, data) -> super(valEl, data) -======= - buildValueForEditing: (valEl) -> - super(valEl) ->>>>>>> master channel = @getRoot().data.channel channelSchema = Backbone.Mediator.channelSchemas[channel] autocompleteValues = [] diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index 9bc1567b9..3e26ce262 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -30,12 +30,8 @@ module.exports = class SettingsTabView extends CocoView schema = _.cloneDeep Level.schema schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings schema.required = _.intersection schema.required, @editableSettings -<<<<<<< HEAD schema.default = _.pick schema.default, (value, key) => key in @editableSettings - thangIDs = @getThangIDs() -======= @thangIDs = @getThangIDs() ->>>>>>> master treemaOptions = filePath: "db/level/#{@level.get('original')}" supermodel: @supermodel @@ -53,11 +49,7 @@ module.exports = class SettingsTabView extends CocoView @settingsTreema.open() getThangIDs: -> -<<<<<<< HEAD - (t.id for t in @level.get('thangs') ? [] when t.id isnt 'Interface') -======= - (t.id for t in @level.get('thangs')) ->>>>>>> master + (t.id for t in @level.get('thangs') ? []) onSettingsChanged: (e) => $('.level-title').text @settingsTreema.data.name diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index 72b7645b5..e45884c4a 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -147,12 +147,8 @@ class LevelSystemNode extends TreemaObjectNode onEnterPressed: (e) -> super e -<<<<<<< HEAD data = @getData() - Backbone.Mediator.publish 'edit-level-system', original: data.original, majorVersion: data.majorVersion -======= - Backbone.Mediator.publish 'editor:edit-level-system', original: @data.original, majorVersion: @data.majorVersion ->>>>>>> master + Backbone.Mediator.publish 'editor:edit-level-system', original: data.original, majorVersion: data.majorVersion open: (depth) -> super depth diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index be7566419..3e699cd19 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -529,8 +529,4 @@ class ThangNode extends TreemaObjectNode @buildValueForDisplaySimply valEl, s onEnterPressed: -> -<<<<<<< HEAD - Backbone.Mediator.publish 'edit-level-thang', thangID: @getData().id -======= - Backbone.Mediator.publish 'editor:edit-level-thang', thangID: @data.id ->>>>>>> master + Backbone.Mediator.publish 'editor:edit-level-thang', thangID: @getData().id diff --git a/bower.json b/bower.json index d7db35979..42cb3edbb 100644 --- a/bower.json +++ b/bower.json @@ -39,13 +39,8 @@ "d3": "~3.4.4", "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", -<<<<<<< HEAD - "jquery.tablesorter": "~2.15.13", "treema": "https://github.com/codecombat/treema.git#release/0.1.0", -======= "jquery.tablesorter": "~2", - "treema": "~0.0.14", ->>>>>>> master "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", "jquery.browser": "~0.0.6", From 8b056cfb8f9884b4e6bb1b71622d9202a36cb809 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 11:24:45 -0700 Subject: [PATCH 30/98] Refactored the item equips view to limit items by slot and also moved the item name loading to the node class itself. --- app/collections/CocoCollection.coffee | 18 ++++-- app/templates/loading_error.jade | 7 +- .../component/ThangComponentsEditView.coffee | 2 - app/views/editor/level/treema_nodes.coffee | 64 +++++++++++++++++-- app/views/kinds/CocoView.coffee | 9 ++- .../collections/CocoCollection.spec.coffee | 12 ++++ 6 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 test/app/collections/CocoCollection.spec.coffee diff --git a/app/collections/CocoCollection.coffee b/app/collections/CocoCollection.coffee index bb051811c..33b53f111 100644 --- a/app/collections/CocoCollection.coffee +++ b/app/collections/CocoCollection.coffee @@ -4,10 +4,14 @@ module.exports = class CocoCollection extends Backbone.Collection loaded: false model: null - initialize: -> + initialize: (models, options) -> + options ?= {} + @model ?= options.model if not @model console.error @constructor.name, 'does not have a model defined. This will not do!' - super() + super(models, options) + @setProjection options.project + if options.url then @url = options.url @once 'sync', => @loaded = true model.loaded = true for model in @models @@ -15,7 +19,13 @@ module.exports = class CocoCollection extends Backbone.Collection getURL: -> return if _.isString @url then @url else @url() - fetch: -> - @jqxhr = super(arguments...) + fetch: (options) -> + options ?= {} + if @project + options.data ?= {} + options.data.project = @project.join(',') + @jqxhr = super(options) @loading = true @jqxhr + + setProjection: (@project) -> \ No newline at end of file diff --git a/app/templates/loading_error.jade b/app/templates/loading_error.jade index 2e9a8edfe..fdd566a45 100644 --- a/app/templates/loading_error.jade +++ b/app/templates/loading_error.jade @@ -24,8 +24,5 @@ else strong(data-i18n="loading_error.unknown") Unknown error. - if resourceIndex !== undefined - button.btn.btn-xs.retry-loading-resource(data-i18n="common.retry", data-resource-index=resourceIndex) Retry - if requestIndex !== undefined - button.btn.btn-xs.retry-loading-request(data-i18n="common.retry", data-request-index=requestIndex) Retry - \ No newline at end of file + button.btn.btn-xs.retry-loading-resource(data-i18n="common.retry", data-resource-index=resourceIndex) Retry + button.btn.btn-xs.skip-loading-resource(data-i18n="common.skip", data-resource-index=resourceIndex) Skip diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 1aa3aaab4..a8da82b18 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -33,8 +33,6 @@ module.exports = class ThangComponentsEditView extends CocoView @world = options.world @level = options.level @loadComponents(@components) - # Need to grab the ThangTypes so that we can autocomplete items in inventory based on them. - @itemThangTypes = @supermodel.loadCollection(new ItemThangTypeSearchCollection(), 'thangs').model setThangType: (@thangType) -> return unless componentRefs = @thangType?.get('components') diff --git a/app/views/editor/level/treema_nodes.coffee b/app/views/editor/level/treema_nodes.coffee index 29e2952b1..33db3c129 100644 --- a/app/views/editor/level/treema_nodes.coffee +++ b/app/views/editor/level/treema_nodes.coffee @@ -1,5 +1,7 @@ WorldSelectModal = require './modals/WorldSelectModal' ThangType = require '/models/ThangType' +LevelComponent = require 'models/LevelComponent' +CocoCollection = require 'collections/CocoCollection' makeButton = -> $('') shorten = (f) -> parseFloat(f.toFixed(1)) @@ -221,11 +223,63 @@ module.exports.ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.st else @data = null -module.exports.ItemThangTypeNode = class ThangTypeNode extends ThangTypeNode +module.exports.ItemThangTypeNode = ItemThangTypeNode = class ItemThangTypeNode extends TreemaNode.nodeMap.string valueClass: 'treema-item-thang-type' + @items: null + @itemsCollection: null + + constructor: -> + super(arguments...) + @getItems() + unless ItemThangTypeNode.itemsCollection.loaded + ItemThangTypeNode.itemsCollection.once('sync', @refreshDisplay, @) + buildValueForDisplay: (valEl, data) -> - super(valEl, data) - thangTypeNames = (m.get('name') for m in @settings.supermodel.getModels ThangType when m.get('kind') is 'Item') - input = valEl.find('input').autocomplete(source: thangTypeNames, minLength: 0, delay: 0, autoFocus: true) - input.val(@thangType?.get('name') or 'None') + @buildValueForDisplaySimply(valEl, @getCurrentItem() or '') valEl + + buildValueForEditing: (valEl, data) -> + super(valEl, data) + if ItemThangTypeNode.items + source = (item.name for item in ItemThangTypeNode.items when @keyForParent in item.slots) + input = valEl.find('input').autocomplete(source: source, minLength: 0, delay: 0, autoFocus: true) + input.val(@getCurrentItem() or '') + valEl + + getCurrentItem: -> + window.itemData = @getData() + window.items = ItemThangTypeNode.items + return null unless ItemThangTypeNode.items + original = @getData() + return null unless original + item = _.find ItemThangTypeNode.items, { original: original } + item?.name or '...' + + getItems: -> + return if ItemThangTypeNode.itemsCollection + ItemThangTypeNode.itemsCollection = new CocoCollection([], { + url: '/db/thang.type' + project:['name', 'components', 'original'] + model: ThangType + }) + res = ItemThangTypeNode.itemsCollection.fetch() + ItemThangTypeNode.itemsCollection.once 'sync', => @processItems(ItemThangTypeNode.itemsCollection) + + processItems: (itemCollection) -> + ItemThangTypeNode.items = [] + for itemThang in itemCollection.models + itemComponent = _.find itemThang.get('components'), {original: LevelComponent.ItemID} + continue unless itemComponent + slots = itemComponent.config?.slots + continue unless slots?.length + ItemThangTypeNode.items.push { + name: itemThang.get('name') + original: itemThang.get('original') + slots: slots + } + + saveChanges: -> + thangTypeName = @$el.find('input').val() + item = _.find(ItemThangTypeNode.items, {name: thangTypeName}) + return @remove() unless item + @data = item.original \ No newline at end of file diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index eb6d4859f..d3d44b6f5 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -17,7 +17,7 @@ module.exports = class CocoView extends Backbone.View events: 'click .retry-loading-resource': 'onRetryResource' - 'click .retry-loading-request': 'onRetryRequest' + 'click .skip-loading-resource': 'onSkipResource' subscriptions: {} shortcuts: {} @@ -148,6 +148,13 @@ module.exports = class CocoView extends Backbone.View res.load() @$el.find('.progress').show() $(e.target).closest('.loading-error-alert').remove() + + onSkipResource: (e) -> + res = @supermodel.getResource($(e.target).data('resource-index')) + return unless res and res.isFailed + res.markLoaded() + @$el.find('.progress').show() + $(e.target).closest('.loading-error-alert').remove() # Modals diff --git a/test/app/collections/CocoCollection.spec.coffee b/test/app/collections/CocoCollection.spec.coffee new file mode 100644 index 000000000..6f233c6ce --- /dev/null +++ b/test/app/collections/CocoCollection.spec.coffee @@ -0,0 +1,12 @@ +CocoCollection = require 'collections/CocoCollection' +LevelComponent = require 'models/LevelComponent' + +describe 'CocoCollection', -> + it 'can be given a project function to include a project query arg', -> + collection = new CocoCollection([], { + url: '/db/level.component' + project:['name', 'description'] + model: LevelComponent + }) + collection.fetch({data: {view: 'items'}}) + expect(jasmine.Ajax.requests.mostRecent().url).toBe('/db/level.component?view=items&project=name%2Cdescription') From e3bc9045d15c01992fd2acd63a743dc8fb846e6a Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 13:53:00 -0700 Subject: [PATCH 31/98] Couple tweaks. --- app/views/editor/component/ThangComponentConfigView.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index 320be5e6d..418dcd9cb 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -15,6 +15,7 @@ module.exports = class ThangComponentConfigView extends CocoView @component = options.component @config = options.config or {} @additionalDefaults = options.additionalDefaults + @isDefaultComponent = false @world = options.world @level = options.level @callback = options.callback @@ -86,9 +87,9 @@ module.exports = class ThangComponentConfigView extends CocoView onConfigEdited: => return if @handlingChange - @config = @editThangTreema.data + @config = @data() @changed = true - @trigger 'changed', { component: @component, config: @data() } + @trigger 'changed', { component: @component, config: @config } data: -> @editThangTreema.data From 154b91c753b92dc46bf84a6b1ff8e4c6cb1644d4 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 15:19:04 -0700 Subject: [PATCH 32/98] Changed LevelLoader to always deeply load components and thang types, not just for hero levels. --- app/lib/LevelLoader.coffee | 43 +++++++++++----------------- test/app/lib/LevelLoader.spec.coffee | 15 ---------- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 4298460c0..cfdb46ae9 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -155,21 +155,22 @@ module.exports = class LevelLoader extends CocoClass @worldNecessities = @worldNecessities.concat worldNecessities loadItemThangsEquippedByLevelThang: (levelThang) -> - return unless levelThang.components - for component in levelThang.components - if component.original is LevelComponent.EquipsID and inventory = component.config?.inventory - for itemThangType in _.values(inventory) - unless itemThangType - console.warn "Empty item in inventory for", levelThang - continue - url = "/db/thang.type/#{itemThangType}/version?project=name,components,original" - @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') + @loadItemThangsFromComponentList levelThang.components + + loadItemThangsEquippedByThangType: (thangType) -> + @loadItemThangsFromComponentList thangType.get('components') + + loadItemThangsFromComponentList: (components) -> + equipsThangComponent = _.find components, (c) -> c.original is LevelComponent.EquipsID + inventory = equipsThangComponent?.config?.inventory + for itemThangType in _.values inventory + url = "/db/thang.type/#{itemThangType}/version?project=name,components,original" + @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') onThangNamesLoaded: (thangNames) -> - if @level.get('type') is 'hero' - for thangType in thangNames.models - @loadDefaultComponentsForThangType(thangType) - @loadEquippedItemsInheritedFromThangType(thangType) + for thangType in thangNames.models + @loadDefaultComponentsForThangType(thangType) + @loadItemThangsEquippedByThangType(thangType) loadDefaultComponentsForThangType: (thangType) -> return unless components = thangType.get('components') @@ -177,23 +178,11 @@ module.exports = class LevelLoader extends CocoClass url = "/db/level.component/#{component.original}/version/#{component.majorVersion}" @worldNecessities.push @maybeLoadURL(url, LevelComponent, 'component') - loadEquippedItemsInheritedFromThangType: (thangType) -> - for levelThang in @level.get('thangs') or [] - if levelThang.thangType is thangType.get('original') - levelThang = $.extend true, {}, levelThang - @level.denormalizeThang(levelThang, @supermodel) - equipsComponent = _.find levelThang.components, {original: LevelComponent.EquipsID} - inventory = equipsComponent?.config?.inventory - continue unless inventory - for itemThangType in _.values inventory - url = "/db/thang.type/#{itemThangType}/version?project=name,components,original" - @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') - onWorldNecessityLoaded: (resource) -> index = @worldNecessities.indexOf(resource) - if (@level?.loading or (@level?.get('type') is 'hero')) and resource.name is 'thang' + if resource.name is 'thang' @loadDefaultComponentsForThangType(resource.model) - @loadEquippedItemsInheritedFromThangType(resource.model) + @loadItemThangsEquippedByThangType(resource.model) return unless index >= 0 @worldNecessities.splice(index, 1) diff --git a/test/app/lib/LevelLoader.spec.coffee b/test/app/lib/LevelLoader.spec.coffee index e3cb98dd1..2181e67f6 100644 --- a/test/app/lib/LevelLoader.spec.coffee +++ b/test/app/lib/LevelLoader.spec.coffee @@ -7,7 +7,6 @@ LevelLoader = require 'lib/LevelLoader' # LEVELS levelWithOgreWithMace = { - type: 'hero' thangs: [{ thangType: 'ogre' components: [{ @@ -19,14 +18,12 @@ levelWithOgreWithMace = { } levelWithShaman = { - type: 'hero' thangs: [{ thangType: 'shaman' }] } levelWithShamanWithSuperWand = { - type: 'hero' thangs: [{ thangType: 'shaman' components: [{ @@ -180,15 +177,3 @@ describe 'LevelLoader', -> requests = jasmine.Ajax.requests.all() urls = (r.url for r in requests) expect('/db/level.component/poisons/version/0' in urls).toBeTruthy() - - it 'does not load item thang types from thang type equips component configs which are overriden by level thang equips component configs', -> - new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) - - responses = - '/db/level/id': levelWithShamanWithSuperWand - '/db/thang.type/names': [thangTypeShamanWithWandEquipped] - - jasmine.Ajax.requests.sendResponses(responses) - requests = jasmine.Ajax.requests.all() - urls = (r.url for r in requests) - expect('/db/thang.type/wand/version?project=name,components,original' in urls).toBeFalsy() From 8f95ad278766bc012034f3cb9d7abd7683c59be7 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 15:42:46 -0700 Subject: [PATCH 33/98] Fixed a few bugs. --- app/schemas/models/thang_type.coffee | 2 +- app/views/editor/component/ThangComponentConfigView.coffee | 2 +- app/views/editor/thang/ThangTypeEditView.coffee | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/schemas/models/thang_type.coffee b/app/schemas/models/thang_type.coffee index 09e88fd21..5b089d5cf 100644 --- a/app/schemas/models/thang_type.coffee +++ b/app/schemas/models/thang_type.coffee @@ -144,7 +144,7 @@ _.extend ThangTypeSchema.properties, ThangTypeSchema.required = ['kind'] -ThangTypeSchema.deafult = +ThangTypeSchema.default = raw: {} ThangTypeSchema.definitions = diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index 418dcd9cb..edd82ee13 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -86,7 +86,7 @@ module.exports = class ThangComponentConfigView extends CocoView @$el.find('.panel-body').hide() onConfigEdited: => - return if @handlingChange + return if @destroyed or @handlingChange @config = @data() @changed = true @trigger 'changed', { component: @component, config: @config } diff --git a/app/views/editor/thang/ThangTypeEditView.coffee b/app/views/editor/thang/ThangTypeEditView.coffee index afc14ef33..57bba80c8 100644 --- a/app/views/editor/thang/ThangTypeEditView.coffee +++ b/app/views/editor/thang/ThangTypeEditView.coffee @@ -69,7 +69,7 @@ module.exports = class ThangTypeEditView extends RootView context getAnimationNames: -> - raw = _.keys(@thangType.get('raw').animations) + raw = _.keys(@thangType.get('raw', true).animations) raw = ("raw:#{name}" for name in raw) main = _.keys(@thangType.get('actions') or {}) main.concat(raw) From 608d114c5cc40ef46be0e8e05ca45100dc4447f7 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 15:54:05 -0700 Subject: [PATCH 34/98] Fixed metaschemas not being found. --- app/models/CocoModel.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 3fb7d7a79..c6518340e 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -72,7 +72,10 @@ class CocoModel extends Backbone.Model buildAttributesWithDefaults: -> t0 = new Date() clone = $.extend true, {}, @attributes - TreemaNode.utils.populateDefaults(clone, @schema()) + thisTV4 = tv4.freshApi() + thisTV4.addSchema('#', @schema()) + thisTV4.addSchema('metaschema', require('schemas/metaschema')) + TreemaNode.utils.populateDefaults(clone, @schema(), thisTV4) @attributesWithDefaults = clone console.debug "Populated defaults for #{@attributes.name or @type()} in #{new Date() - t0}ms" From 74ca7fe0cc634c7a4b6b310420968d1907c0e477 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 17:05:46 -0700 Subject: [PATCH 35/98] Migrated to Treema's use of 'schema' as the raw schema property, and 'workingSchema' as the flattened, dereferenced schema. --- app/treema-ext.coffee | 28 +++++++++---------- .../level/scripts/ScriptsTabView.coffee | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index eedc965c7..667438393 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -19,7 +19,7 @@ class LiveEditingMarkup extends TreemaNode.nodeMap.ace constructor: -> super(arguments...) - @schema.aceMode = 'ace/mode/markdown' + @workingSchema.aceMode = 'ace/mode/markdown' initEditor: (valEl) -> buttonRow = $('
') @@ -224,7 +224,7 @@ codeLanguages = class CodeLanguagesObjectTreema extends TreemaNode.nodeMap.object childPropertiesAvailable: -> - (key for key in _.keys(codeLanguages) when not @data[key]? and not (key is 'javascript' and @schema.skipJavaScript)) + (key for key in _.keys(codeLanguages) when not @data[key]? and not (key is 'javascript' and @workingSchema.skipJavaScript)) class CodeLanguageTreema extends TreemaNode.nodeMap.string buildValueForEditing: (valEl, data) -> @@ -235,25 +235,25 @@ class CodeLanguageTreema extends TreemaNode.nodeMap.string class CodeTreema extends TreemaNode.nodeMap.ace constructor: -> super(arguments...) - @schema.aceTabSize = 4 + @workingSchema.aceTabSize = 4 buildValueForEditing: (valEl, data) -> super(valEl, data) - if not @schema.aceMode and mode = codeLanguages[@keyForParent] + if not @workingSchema.aceMode and mode = codeLanguages[@keyForParent] @editor.getSession().setMode mode valEl class CoffeeTreema extends CodeTreema constructor: -> super(arguments...) - @schema.aceMode = 'ace/mode/coffee' - @schema.aceTabSize = 2 + @workingSchema.aceMode = 'ace/mode/coffee' + @workingSchema.aceTabSize = 2 class JavaScriptTreema extends CodeTreema constructor: -> super(arguments...) - @schema.aceMode = 'ace/mode/javascript' - @schema.aceTabSize = 4 + @workingSchema.aceMode = 'ace/mode/javascript' + @workingSchema.aceTabSize = 4 class InternationalizationNode extends TreemaNode.nodeMap.object @@ -276,11 +276,11 @@ class InternationalizationNode extends TreemaNode.nodeMap.object properties: {} } return i18nChildSchema unless @parent - unless @schema.props? + unless @workingSchema.props? console.warn 'i18n props array is empty! Filling with all parent properties by default' - @schema.props = (prop for prop,_ of @parent.schema.properties when prop isnt 'i18n') + @workingSchema.props = (prop for prop,_ of @parent.schema.properties when prop isnt 'i18n') - for i18nProperty in @schema.props + for i18nProperty in @workingSchema.props i18nChildSchema.properties[i18nProperty] = @parent.schema.properties[i18nProperty] return i18nChildSchema #this must be filled out in order for the i18n node to work @@ -301,7 +301,7 @@ class LatestVersionReferenceNode extends TreemaNode super(arguments...) # to dynamically build the search url, inspect the links url that should be included - links = @schema.links or [] + links = @workingSchema.links or [] link = (l for l in links when l.rel is 'db')[0] return unless link parts = (p for p in link.href.split('/') when p.length) @@ -358,7 +358,7 @@ class LatestVersionReferenceNode extends TreemaNode formatDocument: (docOrModel) -> return @modelToString(docOrModel) if docOrModel instanceof CocoModel return 'Unknown' unless @settings.supermodel? - m = CocoModel.getReferencedModel(@getData(), @schema) + m = CocoModel.getReferencedModel(@getData(), @workingSchema) data = @getData() m = @settings.supermodel.getModelByOriginalAndMajorVersion(m.constructor, data.original, data.majorVersion) if @instance and not m @@ -423,7 +423,7 @@ LatestVersionReferenceNode.prototype.search = _.debounce(LatestVersionReferenceN class SlugPropsObject extends TreemaNode.nodeMap.object getPropertyKey: -> res = super(arguments...) - return res if @schema.properties?[res]? + return res if @workingSchema.properties?[res]? _.string.slugify(res) module.exports.setup = -> diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index 654702f72..133866634 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -194,7 +194,7 @@ class EventPrereqNode extends TreemaNode.nodeMap.object statements = [] for key, value of data continue if key is 'eventProps' - comparison = @schema.properties[key].title + comparison = @workingSchema.properties[key].title value = value.toString() statements.push("#{comparison} #{value}") statements = statements.join(', ') From 5fd154ca8bdab3f610be22a69806f4dea0bbf67a Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Thu, 28 Aug 2014 17:57:39 -0700 Subject: [PATCH 36/98] Tweaked the level serialization, leaving in walkDefaults for now because there may still be some components that don't have defaults properly set up. Added a warn log for when this happens so it can be fixed. --- app/models/Level.coffee | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index b1f9f46ae..59487f703 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -140,12 +140,32 @@ module.exports = class Level extends CocoModel continue unless lc = _.find levelComponents, {original: component.original} component.config ?= {} TreemaNode.utils.populateDefaults(component.config, lc.configSchema) + @lastType = 'component' + @lastOriginal = component.original + @walkDefaults component.config, lc.configSchema.properties fillInDefaultSystemConfiguration: (levelSystems) -> for system in levelSystems ? [] system.config ?= {} TreemaNode.utils.populateDefaults(system.config, system.model.configSchema) + @lastType = 'system' + @lastOriginal = system.model.name + @walkDefaults system.config, system.model.configSchema.properties + walkDefaults: (config, properties) -> + # This function is redundant, but is the old implementation. + # Remove it and calls to it once we stop seeing these warnings. + return unless properties + for prop, schema of properties + if schema.default? and config[prop] is undefined + console.warn 'Setting default of', config, 'for', prop, 'to', schema.default, 'but this method is deprecated... check your config schema!', @lastType, @lastOriginal + config[prop] = schema.default + if schema.type is 'object' and config[prop] + @walkDefaults config[prop], schema.properties + else if schema.type is 'array' and config[prop] + for item in config[prop] or [] + @walkDefaults item, schema.items + dimensions: -> width = 0 height = 0 From a4b2333fd3dbd4a969fa966e40af5cbecaab0423 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Thu, 28 Aug 2014 22:32:55 -0700 Subject: [PATCH 37/98] Added Systems documentation. Fixed #1115. --- app/Router.coffee | 3 +- .../docs/components-documentation-view.sass | 27 ++++++++++++++ app/styles/docs/components.sass | 31 ---------------- .../docs/systems-documentation-view.sass | 27 ++++++++++++++ .../editor/level/documentation_tab.sass | 5 +++ ...ade => components-documentation-view.jade} | 19 +++++----- .../docs/systems-documentation-view.jade | 31 ++++++++++++++++ app/templates/editor/level/edit.jade | 12 +++++-- ...fee => ComponentsDocumentationView.coffee} | 19 +++++++--- .../docs/SystemsDocumentationView.coffee | 35 +++++++++++++++++++ app/views/editor/level/LevelEditView.coffee | 13 +++++-- .../level/scripts/ScriptsTabView.coffee | 2 +- .../level/settings/SettingsTabView.coffee | 2 +- 13 files changed, 175 insertions(+), 51 deletions(-) create mode 100644 app/styles/docs/components-documentation-view.sass delete mode 100644 app/styles/docs/components.sass create mode 100644 app/styles/docs/systems-documentation-view.sass create mode 100644 app/styles/editor/level/documentation_tab.sass rename app/templates/docs/{components.jade => components-documentation-view.jade} (74%) create mode 100644 app/templates/docs/systems-documentation-view.jade rename app/views/docs/{ComponentDocumentationView.coffee => ComponentsDocumentationView.coffee} (50%) create mode 100644 app/views/docs/SystemsDocumentationView.coffee diff --git a/app/Router.coffee b/app/Router.coffee index 58f93b3b5..1fc29d92a 100644 --- a/app/Router.coffee +++ b/app/Router.coffee @@ -47,7 +47,8 @@ module.exports = class CocoRouter extends Backbone.Router 'db/*path': 'routeToServer' 'demo(/*subpath)': go('DemoView') - 'docs/components': go('docs/ComponentDocumentationView') + 'docs/components': go('docs/ComponentsDocumentationView') + 'docs/systems': go('docs/SystemsDocumentationView') 'editor': go('CommunityView') diff --git a/app/styles/docs/components-documentation-view.sass b/app/styles/docs/components-documentation-view.sass new file mode 100644 index 000000000..0a5bdb7fc --- /dev/null +++ b/app/styles/docs/components-documentation-view.sass @@ -0,0 +1,27 @@ +#components-documentation-view + background-color: #e4cf8c + height: 100% + + #toggle-all-component-code + margin: 10px + + .container, .row + height: 100% + + .index-column, .documentation-column + overflow-x: hidden + + > ul + padding: 0px 20px 20px 20px + + .doc-name + color: rgb(139, 69, 19) + + .index-column + width: 25% + + .documentation-column + width: 75% + + .special-list, .doc-description, .code-block + list-style-type: none diff --git a/app/styles/docs/components.sass b/app/styles/docs/components.sass deleted file mode 100644 index 145a9dfa5..000000000 --- a/app/styles/docs/components.sass +++ /dev/null @@ -1,31 +0,0 @@ -#docs-components-view - color: saddlebrown - - .row - - - - .index-column, .documentation-column - overflow-x: hidden - min-height: 600px - - > ul - padding: 0px 20px 20px 20px - - .doc-name - color: rgb(139, 69, 19) - - .index-column - width: 25% - - .documentation-column - width: 75% - - .specialList - list-style-type: none - - .doc-description - list-style-type: none - - .codeBlock - list-style-type: none \ No newline at end of file diff --git a/app/styles/docs/systems-documentation-view.sass b/app/styles/docs/systems-documentation-view.sass new file mode 100644 index 000000000..8af063555 --- /dev/null +++ b/app/styles/docs/systems-documentation-view.sass @@ -0,0 +1,27 @@ +#systems-documentation-view + background-color: #e4cf8c + height: 100% + + #toggle-all-system-code + margin: 10px + + .container, .row + height: 100% + + .index-column, .documentation-column + overflow-x: hidden + + > ul + padding: 0px 20px 20px 20px + + .doc-name + color: rgb(139, 69, 19) + + .index-column + width: 25% + + .documentation-column + width: 75% + + .special-list, .doc-description, .code-block + list-style-type: none diff --git a/app/styles/editor/level/documentation_tab.sass b/app/styles/editor/level/documentation_tab.sass new file mode 100644 index 000000000..7996c36b1 --- /dev/null +++ b/app/styles/editor/level/documentation_tab.sass @@ -0,0 +1,5 @@ +#editor-level-documentation + height: 100% + + .tab-content + height: 100% diff --git a/app/templates/docs/components.jade b/app/templates/docs/components-documentation-view.jade similarity index 74% rename from app/templates/docs/components.jade rename to app/templates/docs/components-documentation-view.jade index 459ba0f00..6b4eb8169 100644 --- a/app/templates/docs/components.jade +++ b/app/templates/docs/components-documentation-view.jade @@ -1,13 +1,14 @@ -//extends /templates/base +.container + .pull-right + button.btn.btn-primary#toggle-all-component-code Toggle all code + .clearfix - -block content .row .col-xs-3.index-column.nano ul.nav.nav-list.list-group.nano-content for component in components a.doc-name(href="##{component.get('name')}") - li.list-group-item= component.get('name') + li.list-group-item= component.get('system') + '.' + component.get('name') ul // .list-group for different layout each doc in component.attributes.propertyDocumentation @@ -19,24 +20,26 @@ block content for component in components div(id="#{component.get('name')}" class="panel panel-defalt") div(class="panel-heading") - | #{component.get('name')} + strong= component.get('system') + '.' + component.get('name') div(class="panel-body") | #{component.get('description')} ul each doc in component.attributes.propertyDocumentation li.list-group-item(id="#{component.get('name')}#{doc.name}") | #{doc.name} - ul.specialList + ul.special-list if doc.description[codeLanguage] li!=marked(doc.description[codeLanguage]) else li!=marked(doc.description) li.panel-heading - a.codeBlock(data-toggle="collapse" data-parent="##{component.get('name')}" href="##{component.get('name')}Code") + a.code-block(data-toggle="collapse" data-parent="##{component.get('name')}" href="##{component.get('name')}Code") | Code div(id="#{component.get('name')}Code" class="panel-collapse collapse") div.panel-body pre | #{component.attributes.code} - + .clearfix + .clearfix + .clearfix \ No newline at end of file diff --git a/app/templates/docs/systems-documentation-view.jade b/app/templates/docs/systems-documentation-view.jade new file mode 100644 index 000000000..2f439e5cd --- /dev/null +++ b/app/templates/docs/systems-documentation-view.jade @@ -0,0 +1,31 @@ +.container + .pull-right + button.btn.btn-primary#toggle-all-system-code Toggle all code + .clearfix + + .row + .col-xs-3.index-column.nano + ul.nav.nav-list.list-group.nano-content + for system in systems + a.doc-name(href="##{system.get('name')}") + li.list-group-item= system.get('name') + .col-xs-9.documentation-column.nano + ul.nano-content + for system in systems + div(id="#{system.get('name')}" class="panel panel-defalt") + div(class="panel-heading") + | #{system.get('name')} + div(class="panel-body") + | #{system.get('description')} + ul + li.panel-heading + a.code-block(data-toggle="collapse" data-parent="##{system.get('name')}" href="##{system.get('name')}Code") + | Code + div(id="#{system.get('name')}Code" class="panel-collapse collapse") + div.panel-body + pre + | #{system.attributes.code} + + .clearfix + .clearfix + .clearfix \ No newline at end of file diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index d61965365..d65981797 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -36,7 +36,7 @@ block header li a(href="#related-achievements-view", data-toggle="tab") Achievements li - a(href="#docs-components-view", data-toggle="tab", data-i18n="editor.level_tab_docs") Documentation + a(href="#editor-level-documentation", data-toggle="tab", data-i18n="editor.level_tab_docs") Documentation .navbar-header span.navbar-brand #{level.attributes.name} @@ -125,7 +125,15 @@ block outer_content div.tab-pane#related-achievements-view - div.tab-pane#docs-components-view + div.tab-pane#editor-level-documentation + ul.nav.nav-pills.nav-justified + li + a(href="#components-documentation-view", data-toggle="pill", data-i18n="resources.components") Components + li + a(href="#systems-documentation-view", data-toggle="pill", data-i18n="resources.systems") Systems + div.tab-content + div.tab-pane#components-documentation-view + div.tab-pane#systems-documentation-view div#error-view diff --git a/app/views/docs/ComponentDocumentationView.coffee b/app/views/docs/ComponentsDocumentationView.coffee similarity index 50% rename from app/views/docs/ComponentDocumentationView.coffee rename to app/views/docs/ComponentsDocumentationView.coffee index 7418e333d..5265cdfce 100644 --- a/app/views/docs/ComponentDocumentationView.coffee +++ b/app/views/docs/ComponentsDocumentationView.coffee @@ -1,17 +1,21 @@ -#RootView = require 'views/kinds/RootView' CocoView = require 'views/kinds/CocoView' -template = require 'templates/docs/components' +template = require 'templates/docs/components-documentation-view' CocoCollection = require 'collections/CocoCollection' LevelComponent = require 'models/LevelComponent' class ComponentDocsCollection extends CocoCollection - url: '/db/level.component?project=name,description,dependencies,propertyDocumentation,code' + url: '/db/level.component?project=system,name,description,dependencies,propertyDocumentation,code' model: LevelComponent + comparator: 'system' -module.exports = class ComponentDocumentationView extends CocoView - id: 'docs-components-view' +module.exports = class ComponentsDocumentationView extends CocoView + id: 'components-documentation-view' template: template className: 'tab-pane' + collapsed: true + + events: + 'click #toggle-all-component-code': 'onToggleAllCode' constructor: (options) -> super(options) @@ -24,3 +28,8 @@ module.exports = class ComponentDocumentationView extends CocoView c.marked = marked c.codeLanguage = me.get('aceConfig')?.language ? 'javascript' c + + onToggleAllCode: (e) -> + @collapsed = not @collapsed + @$el.find('.collapse').collapse(if @collapsed then 'hide' else 'show') + @$el.find('#toggle-all-component-code').toggleClass 'active', not @collapsed diff --git a/app/views/docs/SystemsDocumentationView.coffee b/app/views/docs/SystemsDocumentationView.coffee new file mode 100644 index 000000000..bb016a8c1 --- /dev/null +++ b/app/views/docs/SystemsDocumentationView.coffee @@ -0,0 +1,35 @@ +CocoView = require 'views/kinds/CocoView' +template = require 'templates/docs/systems-documentation-view' +CocoCollection = require 'collections/CocoCollection' +LevelSystem = require 'models/LevelSystem' + +class SystemDocsCollection extends CocoCollection + url: '/db/level.system?project=name,description,code' + model: LevelSystem + comparator: 'name' + +module.exports = class SystemsDocumentationView extends CocoView + id: 'systems-documentation-view' + template: template + className: 'tab-pane' + collapsed: true + + events: + 'click #toggle-all-system-code': 'onToggleAllCode' + + constructor: (options) -> + super(options) + @systemDocs = new SystemDocsCollection() + @supermodel.loadCollection @systemDocs, 'systems' + + getRenderData: -> + c = super() + c.systems = @systemDocs.models + c.marked = marked + c.codeLanguage = me.get('aceConfig')?.language ? 'javascript' + c + + onToggleAllCode: (e) -> + @collapsed = not @collapsed + @$el.find('.collapse').collapse(if @collapsed then 'hide' else 'show') + @$el.find('#toggle-all-system-code').toggleClass 'active', not @collapsed diff --git a/app/views/editor/level/LevelEditView.coffee b/app/views/editor/level/LevelEditView.coffee index 567dd6d54..c0cfaef66 100644 --- a/app/views/editor/level/LevelEditView.coffee +++ b/app/views/editor/level/LevelEditView.coffee @@ -17,7 +17,8 @@ SaveVersionModal = require 'views/modal/SaveVersionModal' PatchesView = require 'views/editor/PatchesView' RelatedAchievementsView = require 'views/editor/level/RelatedAchievementsView' VersionHistoryView = require './modals/LevelVersionsModal' -ComponentDocsView = require 'views/docs/ComponentDocumentationView' +ComponentsDocumentationView = require 'views/docs/ComponentsDocumentationView' +SystemsDocumentationView = require 'views/docs/SystemsDocumentationView' module.exports = class LevelEditView extends RootView id: 'editor-level-view' @@ -41,6 +42,7 @@ module.exports = class LevelEditView extends RootView 'click #level-patch-button': 'startPatchingLevel' 'click #level-watch-button': 'toggleWatchLevel' 'click #pop-level-i18n-button': -> @level.populateI18N() + 'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab' 'mouseup .nav-tabs > li a': 'toggleTab' constructor: (options, @levelID) -> @@ -79,7 +81,8 @@ module.exports = class LevelEditView extends RootView @insertSubView new ComponentsTabView supermodel: @supermodel @insertSubView new SystemsTabView supermodel: @supermodel @insertSubView new RelatedAchievementsView supermodel: @supermodel, level: @level - @insertSubView new ComponentDocsView # Don't give it the supermodel, it'll pollute it! + @insertSubView new ComponentsDocumentationView # Don't give it the supermodel, it'll pollute it! + @insertSubView new SystemsDocumentationView # Don't give it the supermodel, it'll pollute it! Backbone.Mediator.publish 'editor:level-loaded', level: @level @showReadOnly() if me.get('anonymous') @@ -164,3 +167,9 @@ module.exports = class LevelEditView extends RootView li.parent().find('li').hide() li.show() console.log li.hasClass('active') + + onClickDocumentationTab: (e) -> + # It's either too late at night or something is going on with Bootstrap nested tabs, so we do the click instead of using .active. + return if @initializedDocs + @initializedDocs = true + @$el.find('a[href="#components-documentation-view"]').click() diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index b4e486de9..199b7c32e 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -113,7 +113,7 @@ module.exports = class ScriptsTabView extends CocoView onThangsEdited: (e) -> # Update in-place so existing Treema nodes refer to the same array. - @thangIDs.splice(0, @thangIDs.length, @getThangIDs()...) + @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) class ScriptsNode extends TreemaArrayNode diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index c593e9120..419dab048 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -58,7 +58,7 @@ module.exports = class SettingsTabView extends CocoView onThangsEdited: (e) -> # Update in-place so existing Treema nodes refer to the same array. - @thangIDs.splice(0, @thangIDs.length, @getThangIDs()...) + @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) class SettingsNode extends TreemaObjectNode From 8b7f3c8cbe6c4a1292d11e807520c7a57b8ef1f6 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 09:16:17 -0700 Subject: [PATCH 38/98] Set treema back to an actual version. --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 42cb3edbb..47f657aa0 100644 --- a/bower.json +++ b/bower.json @@ -39,7 +39,7 @@ "d3": "~3.4.4", "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", - "treema": "https://github.com/codecombat/treema.git#release/0.1.0", + "treema": "~0.1.5", // "https://github.com/codecombat/treema.git#branch-name", "jquery.tablesorter": "~2", "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", From 0236ca6fa93e5b562af381eceacf39b020eb0b7b Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 09:17:06 -0700 Subject: [PATCH 39/98] Apparently comments in json don't fly. --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 47f657aa0..8dd7495c1 100644 --- a/bower.json +++ b/bower.json @@ -39,7 +39,7 @@ "d3": "~3.4.4", "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", - "treema": "~0.1.5", // "https://github.com/codecombat/treema.git#branch-name", + "treema": "~0.1.5", "jquery.tablesorter": "~2", "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", From f93ef2005efc4d3059619ca045ab4709bfc7c71e Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 11:34:32 -0700 Subject: [PATCH 40/98] Level editor only folds in ThangType components into Thang component configs if the level is type 'hero'. --- app/views/editor/level/thangs/LevelThangEditView.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/editor/level/thangs/LevelThangEditView.coffee b/app/views/editor/level/thangs/LevelThangEditView.coffee index 8bc0455e4..42fca453a 100644 --- a/app/views/editor/level/thangs/LevelThangEditView.coffee +++ b/app/views/editor/level/thangs/LevelThangEditView.coffee @@ -42,7 +42,8 @@ module.exports = class LevelThangEditView extends CocoView supermodel: @supermodel level: @level world: @world - thangType: thangType + + if @level.get('type') is 'hero' then options.thangType = thangType @thangComponentEditView = new ThangComponentsEditView options @listenTo @thangComponentEditView, 'components-changed', @onComponentsChanged From 2dfe39aafdff68ded984bb7190d7a6acb8c38fda Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 12:28:45 -0700 Subject: [PATCH 41/98] Migrated Achievement model's getExpFunction to use the new defaults system. --- app/models/Achievement.coffee | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/models/Achievement.coffee b/app/models/Achievement.coffee index 47b5391d6..93434d728 100644 --- a/app/models/Achievement.coffee +++ b/app/models/Achievement.coffee @@ -9,12 +9,9 @@ module.exports = class Achievement extends CocoModel isRepeatable: -> @get('proportionalTo')? - # TODO logic is duplicated in Mongoose Achievement schema getExpFunction: -> - # TODO DEFAULTS - kind = @get('function')?.kind or jsonschema.properties.function.default.kind - parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters - return utils.functionCreators[kind](parameters) if kind of utils.functionCreators + func = @get('function', true) + return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators @styleMapping: 1: 'achievement-wood' From ba238137c3a7de1cb967ab86113293f38da8446e Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 12:29:11 -0700 Subject: [PATCH 42/98] Went back to pointing to the latest version of treema cause there are still changes to quickly grab. --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 8dd7495c1..84356e053 100644 --- a/bower.json +++ b/bower.json @@ -39,7 +39,7 @@ "d3": "~3.4.4", "jsondiffpatch": "~0.1.5", "nanoscroller": "~0.8.0", - "treema": "~0.1.5", + "treema": "https://github.com/codecombat/treema.git#master", "jquery.tablesorter": "~2", "bootstrap": "~3.1.1", "validated-backbone-mediator": "~0.1.3", From 434a0c071bcd7d1823b88a38b56e0210939f5e90 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 12:29:42 -0700 Subject: [PATCH 43/98] Few more bugs fixed for defaults. --- app/views/editor/level/systems/AddLevelSystemModal.coffee | 2 -- app/views/editor/level/systems/SystemsTabView.coffee | 8 ++++---- app/views/editor/level/thangs/ThangsTabView.coffee | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/views/editor/level/systems/AddLevelSystemModal.coffee b/app/views/editor/level/systems/AddLevelSystemModal.coffee index 20a00e886..84a0c0abf 100644 --- a/app/views/editor/level/systems/AddLevelSystemModal.coffee +++ b/app/views/editor/level/systems/AddLevelSystemModal.coffee @@ -48,8 +48,6 @@ module.exports = class AddLevelSystemModal extends ModalView levelSystem = original: s.get('original') ? id majorVersion: s.get('version').major ? 0 - # TODO DEFAULTS - config: $.extend(true, {}, s.get('configSchema').default ? {}) @extantSystems.push levelSystem Backbone.Mediator.publish 'level-system-added', system: levelSystem @renderAvailableSystems() diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index e45884c4a..29179e06a 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -81,9 +81,9 @@ module.exports = class SystemsTabView extends CocoView @removeSubView @levelSystemEditView if @levelSystemEditView @levelSystemEditView = null return - until selected.data.original + until (data = selected.getData()) and data.original selected = selected.parent - @editLevelSystem original: selected.data.original, majorVersion: selected.data.majorVersion + @editLevelSystem original: data.original, majorVersion: data.majorVersion onLevelSystemAdded: (e) -> @systemsTreema.insert '/', e.system @@ -106,13 +106,13 @@ module.exports = class SystemsTabView extends CocoView buildDefaultSystems: -> [ {original: '528112c00268d018e3000008', majorVersion: 0} # Event - {original: '5280f83b8ae1581b66000001', majorVersion: 0, config: {lifespan: 60}} # Existence + {original: '5280f83b8ae1581b66000001', majorVersion: 0} # Existence {original: '5281146f0268d018e3000014', majorVersion: 0} # Programming {original: '528110f30268d018e3000001', majorVersion: 0} # AI {original: '52810ffa33e01a6e86000012', majorVersion: 0} # Action {original: '528114b20268d018e3000017', majorVersion: 0} # Targeting {original: '528105f833e01a6e86000007', majorVersion: 0} # Collision - {original: '528113240268d018e300000c', majorVersion: 0, config: {gravity: 9.81}} # Movement + {original: '528113240268d018e300000c', majorVersion: 0} # Movement {original: '528112530268d018e3000007', majorVersion: 0} # Combat {original: '52810f4933e01a6e8600000c', majorVersion: 0} # Hearing {original: '528115040268d018e300001b', majorVersion: 0} # Vision diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index f00f52259..c3faaddfd 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -161,7 +161,7 @@ module.exports = class ThangsTabView extends CocoView thangsHeaderHeight = $('#thangs-header').height() oldHeight = $('#thangs-list').height() $('#thangs-list').height(oldHeight - thangsHeaderHeight) - if data.thangs.length + if data.thangs?.length @$el.find('#randomize-button').hide() initSurface: -> From f54c3236abe4904c7a749420c66f47965f0902fd Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 12:41:25 -0700 Subject: [PATCH 44/98] Refactored Achievement's getExpFunction to use the new defaults system, now on the server side. --- server/achievements/Achievement.coffee | 8 ++++---- server_setup.coffee | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/server/achievements/Achievement.coffee b/server/achievements/Achievement.coffee index 4b0f38bc7..649bc4527 100644 --- a/server/achievements/Achievement.coffee +++ b/server/achievements/Achievement.coffee @@ -4,6 +4,7 @@ log = require 'winston' utils = require '../../app/lib/utils' plugins = require('../plugins/plugins') AchievablePlugin = require '../plugins/achievements' +TreemaUtils = require '../../bower_components/treema/treema-utils.js' # `pre` and `post` are not called for update operations executed directly on the database, # including `Model.update`,`.findByIdAndUpdate`,`.findOneAndUpdate`, `.findOneAndRemove`,and `.findByIdAndRemove`.order @@ -25,10 +26,9 @@ AchievementSchema.methods.stringifyQuery = -> @set('query', JSON.stringify(@get('query'))) if typeof @get('query') != 'string' AchievementSchema.methods.getExpFunction = -> - # TODO DEFAULTS - kind = @get('function')?.kind or jsonschema.properties.function.default.kind - parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters - return utils.functionCreators[kind](parameters) if kind of utils.functionCreators + func = @get('function') ? {} + TreemaUtils.populateDefaults(func, jsonschema.properties.function) + return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators AchievementSchema.methods.isRecalculable = -> @get('recalculable') isnt false diff --git a/server_setup.coffee b/server_setup.coffee index 10a586713..8a76573ef 100644 --- a/server_setup.coffee +++ b/server_setup.coffee @@ -13,6 +13,7 @@ logging = require './server/commons/logging' config = require './server_config' auth = require './server/routes/auth' UserHandler = require './server/users/user_handler' +global.tv4 = require 'tv4' # required for TreemaUtils to work productionLogging = (tokens, req, res) -> status = res.statusCode From 0ba9db6c14d7539825fab50f948f8ce1038724d6 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 12:58:23 -0700 Subject: [PATCH 45/98] Fixed some server tests. --- server/users/user_handler.coffee | 6 +++++- test/server/common.coffee | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index 97a13fe78..e90f50306 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -402,6 +402,7 @@ UserHandler = class UserHandler extends Handler userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> + usersTotal += 1 userObjectID = user.get('_id') userStringID = userObjectID.toHexString() @@ -450,6 +451,7 @@ UserHandler = class UserHandler extends Handler userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> + usersTotal += 1 userObjectID = user.get '_id' userStringID = userObjectID.toHexString() # Extend query with a patch ownership test @@ -478,6 +480,7 @@ UserHandler = class UserHandler extends Handler userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> + usersTotal += 1 userObjectID = user.get '_id' userStringID = userObjectID.toHexString() # Extend query with a patch ownership test @@ -505,6 +508,7 @@ UserHandler = class UserHandler extends Handler userStream.on 'error', (err) -> log.error err userStream.on 'close', -> streamFinished = true userStream.on 'data', (user) -> + usersTotal += 1 userID = user.get('_id').toHexString() LevelSession.count {creator: userID, 'state.completed': true}, (err, count) -> @@ -513,7 +517,7 @@ UserHandler = class UserHandler extends Handler articleEdits: (done) -> Article = require '../articles/Article' - countEdits Article, done + countEdits Article, done levelEdits: (done) -> Level = require '../levels/Level' diff --git a/test/server/common.coffee b/test/server/common.coffee index fa1b04f4c..82531ee2b 100644 --- a/test/server/common.coffee +++ b/test/server/common.coffee @@ -20,6 +20,8 @@ GLOBAL.mongoose = require 'mongoose' mongoose.connect('mongodb://localhost/coco_unittest') path = require 'path' GLOBAL.testing = true +GLOBAL.tv4 = require 'tv4' # required for TreemaUtils to work +# _.str = require 'underscore.string' models_path = [ '../../server/articles/Article' From 68dcdaf2230d2173df6f533bd227e131bf6291d8 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 13:00:11 -0700 Subject: [PATCH 46/98] A couple achievment/item editor error fixes. --- app/views/editor/achievement/AchievementEditView.coffee | 2 ++ app/views/editor/thang/ThangTypeColorsTabView.coffee | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/editor/achievement/AchievementEditView.coffee b/app/views/editor/achievement/AchievementEditView.coffee index 524c14e2e..0825c9de6 100644 --- a/app/views/editor/achievement/AchievementEditView.coffee +++ b/app/views/editor/achievement/AchievementEditView.coffee @@ -41,6 +41,7 @@ module.exports = class AchievementEditView extends RootView change: @pushChangesToPreview @treema = @$el.find('#achievement-treema').treema(options) @treema.build() + @pushChangesToPreview() getRenderData: (context={}) -> context = super(context) @@ -54,6 +55,7 @@ module.exports = class AchievementEditView extends RootView @pushChangesToPreview() pushChangesToPreview: => + return unless @treema @$el.find('#achievement-view').empty() for key, value of @treema.data @achievement.set key, value diff --git a/app/views/editor/thang/ThangTypeColorsTabView.coffee b/app/views/editor/thang/ThangTypeColorsTabView.coffee index cdc624587..8f5884eda 100644 --- a/app/views/editor/thang/ThangTypeColorsTabView.coffee +++ b/app/views/editor/thang/ThangTypeColorsTabView.coffee @@ -14,7 +14,7 @@ module.exports = class ThangTypeColorsTabView extends CocoView super options @supermodel.loadModel @thangType, 'thang' @colorConfig = {hue: 0, saturation: 0.5, lightness: 0.5} - @spriteBuilder = new SpriteBuilder(@thangType) + @spriteBuilder = new SpriteBuilder(@thangType) if @thangType.get('raw') f = => @offset++ @updateMovieClip() @@ -54,7 +54,7 @@ module.exports = class ThangTypeColorsTabView extends CocoView @updateMovieClip() updateMovieClip: -> - return unless @currentColorGroupTreema + return unless @currentColorGroupTreema and @thangType.get('raw') actionDict = @thangType.getActions() animations = (a.animation for key, a of actionDict when a.animation) index = @offset % animations.length @@ -74,6 +74,7 @@ module.exports = class ThangTypeColorsTabView extends CocoView @stage.addChild @movieClip updateContainer: -> + return unless @thangType.get('raw') actionDict = @thangType.getActions() idle = actionDict.idle @stage.removeChild(@container) if @container From b4d9a787f0fffafd99dcc88d46580cbb32e0e3e5 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 16:28:07 -0700 Subject: [PATCH 47/98] Fixed the rest of the server tests. --- app/schemas/models/achievement.coffee | 2 +- test/server/functional/article.spec.coffee | 2 +- test/server/functional/level_component.spec.coffee | 8 +++----- test/server/functional/level_system.spec.coffee | 6 ++---- test/server/functional/patch.spec.coffee | 4 ++-- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/schemas/models/achievement.coffee b/app/schemas/models/achievement.coffee index 4f5181f92..fe45e03b5 100644 --- a/app/schemas/models/achievement.coffee +++ b/app/schemas/models/achievement.coffee @@ -73,7 +73,7 @@ _.extend AchievementSchema.properties, kind: {enum: ['linear', 'logarithmic', 'quadratic'] } parameters: type: 'object' - default: { a: 1, b: 1, c: 1 } + default: { a: 1, b: 0, c: 0 } properties: a: {type: 'number' } b: {type: 'number' } diff --git a/test/server/functional/article.spec.coffee b/test/server/functional/article.spec.coffee index e17f241ea..acc7aab2b 100644 --- a/test/server/functional/article.spec.coffee +++ b/test/server/functional/article.spec.coffee @@ -117,7 +117,7 @@ describe '/db/article', -> request.get {uri: url + '?project=true', json: {}}, (err, res, body) -> expect(res.statusCode).toBe(200) expect(body.length).toBe(2) - expect(body[0].created).toBeUndefined() + expect(body[0].body).toBeUndefined() expect(body[0].version).toBeDefined() # custom projection diff --git a/test/server/functional/level_component.spec.coffee b/test/server/functional/level_component.spec.coffee index 697c14df7..a0c3b33e8 100644 --- a/test/server/functional/level_component.spec.coffee +++ b/test/server/functional/level_component.spec.coffee @@ -65,11 +65,9 @@ describe 'LevelComponent', -> expect(body.code).toBe(components[0].code) expect(body.codeLanguage).toBe(components[0].codeLanguage) expect(body.__v).toBe(0) - expect(body.official).toBeDefined() expect(body.creator).toBeDefined() expect(body.original).toBeDefined() expect(body.created).toBeDefined() - expect(body.configSchema).toBeDefined() expect(body.dependencies).toBeDefined() expect(body.propertyDocumentation).toBeDefined() expect(body.version.isLatestMajor).toBe(true) @@ -83,7 +81,7 @@ describe 'LevelComponent', -> expect(res.statusCode).toBe(200) body = JSON.parse(body) expect(body._id).toBe(components[0]._id) - expect(body.official).toBe(false) + expect(body.official).toBeUndefined() done() it 'has system ai by default', (done) -> @@ -100,7 +98,7 @@ describe 'LevelComponent', -> loginJoe -> request.post {uri: url, json: components[0]}, (err, res, body) -> expect(res.statusCode).toBe(200) - expect(body.official).toBe(false) + expect(body.official).toBeUndefined() done() it 'official property is editable by an admin.', (done) -> @@ -118,7 +116,7 @@ describe 'LevelComponent', -> expect(res.statusCode).toBe(200) body = JSON.parse(body) expect(body._id).toBe(components[0]._id) - expect(body.official).toBe(false) + expect(body.official).toBeUndefined() expect(body.version.isLatestMinor).toBe(false) expect(body.version.isLatestMajor).toBe(false) done() diff --git a/test/server/functional/level_system.spec.coffee b/test/server/functional/level_system.spec.coffee index 7d5c1e488..669fe5c4c 100644 --- a/test/server/functional/level_system.spec.coffee +++ b/test/server/functional/level_system.spec.coffee @@ -73,11 +73,9 @@ describe 'LevelSystem', -> expect(body.code).toBe(systems[0].code) expect(body.codeLanguage).toBe(systems[0].codeLanguage) expect(body.__v).toBe(0) - expect(body.official).toBeDefined() expect(body.creator).toBeDefined() expect(body.original).toBeDefined() expect(body.created).toBeDefined() - expect(body.configSchema).toBeDefined() expect(body.dependencies).toBeDefined() expect(body.propertyDocumentation).toBeDefined() expect(body.version.isLatestMajor).toBe(true) @@ -91,7 +89,7 @@ describe 'LevelSystem', -> expect(res.statusCode).toBe(200) body = JSON.parse(body) expect(body._id).toBe(systems[0]._id) - expect(body.official).toBe(false) + expect(body.official).toBeUndefined() done() it 'official property isn\'t editable by an ordinary user.', (done) -> @@ -116,7 +114,7 @@ describe 'LevelSystem', -> expect(res.statusCode).toBe(200) body = JSON.parse(body) expect(body._id).toBe(systems[0]._id) - expect(body.official).toBe(false) + expect(body.official).toBeUndefined() expect(body.version.isLatestMinor).toBe(false) expect(body.version.isLatestMajor).toBe(false) done() diff --git a/test/server/functional/patch.spec.coffee b/test/server/functional/patch.spec.coffee index 08eaac60c..c17a81774 100644 --- a/test/server/functional/patch.spec.coffee +++ b/test/server/functional/patch.spec.coffee @@ -71,13 +71,13 @@ describe '/db/patch', -> it 'does not add duplicate watchers', (done) -> watchingURL = getURL("/db/article/#{articles[0]._id}/watch") request.put {uri: watchingURL, json: {on: true}}, (err, res, body) -> - expect(body.watchers.length).toBe(2) + expect(body.watchers.length).toBe(4) done() it 'allows removing yourself', (done) -> watchingURL = getURL("/db/article/#{articles[0]._id}/watch") request.put {uri: watchingURL, json: {on: false}}, (err, res, body) -> - expect(body.watchers.length).toBe(1) + expect(body.watchers.length).toBe(3) done() it 'allows the submitter to withdraw the pull request', (done) -> From 5503b3a2bc9dc7dbfcccbf3f1d336c29694e7daa Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 17:03:02 -0700 Subject: [PATCH 48/98] Fixed #1147. --- app/lib/surface/CocoSprite.coffee | 39 +++++++++++++++---- app/lib/surface/WizardSprite.coffee | 6 +-- .../play/level/modal/LevelGuideModal.coffee | 2 +- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 23e16cec8..478a71b0c 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -80,13 +80,14 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @age = 0 @stillLoading = true if @thangType.isFullyLoaded() - @setupSprite() + @setUpSprite() else @thangType.setProjection null @thangType.fetch() unless @thangType.loading - @listenToOnce(@thangType, 'sync', @setupSprite) + @listenToOnce(@thangType, 'sync', @setUpSprite) + @setUpPlaceholder() - setupSprite: -> + setUpSprite: -> for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say' AudioPlayer.preloadSoundReference sound for sound in sounds if @thangType.get('raster') @@ -97,7 +98,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass else result = @buildSpriteSheet() if _.isString result # async build - @listenToOnce @thangType, 'build-complete', @setupSprite + @listenToOnce @thangType, 'build-complete', @setUpSprite + @setUpPlaceholder() else @stillLoading = false @actions = @thangType.getActions() @@ -106,6 +108,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @queueAction 'idle' finishSetup: -> + @placeholder = null @updateBaseScale() @scaleFactorX = @thang.scaleFactorX if @thang?.scaleFactorX? @scaleFactorX = @thang.scaleFactor if @thang?.scaleFactor? @@ -127,6 +130,28 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @imageObject.regY = -reg.y @finishSetup() + setUpPlaceholder: -> + return if @placeholder or not @thang + shape = new createjs.Shape() + width = @thang.width * Camera.PPM + height = @thang.height * Camera.PPM * @options.camera.y2x + depth = @thang.depth * Camera.PPM * @options.camera.z2y * @options.camera.y2x + makeColor = (brightnessFactor) => (Math.round(c * brightnessFactor) for c in (healthColors[@thang.team] ? [180, 180, 180])) + topColor = "rgba(#{makeColor(0.85).join(', ')},1)" + mainColor = "rgba(#{makeColor(0.75).join(', ')},1)" + ellipse = @thang.shape in ['ellipsoid', 'disc'] + fn = if ellipse then 'drawEllipse' else 'drawRect' + shape.graphics.beginFill(mainColor)[fn](-width / 2, -height / 2, width, height).endFill() + if ellipse + shape.graphics.moveTo(-width / 2, 0).beginFill(mainColor).lineTo(-width / 2, -depth).lineTo(width / 2, -depth).lineTo(width / 2, 0).lineTo(-width / 2, 0).endFill() + else + shape.graphics.moveTo(-width / 2, 0).beginFill(mainColor).lineTo(-width / 2, -depth).lineTo(width / 2, -depth).lineTo(width / 2, 0).lineTo(-width / 2, 0).endFill() + shape.graphics.beginFill(topColor)[fn](-width / 2, -height / 2 - depth, width, height).endFill() + shape.layerPriority = @thang?.layerPriority ? @thangType.get 'layerPriority' + @setImageObject shape + @updatePosition true + @placeholder = shape + toString: -> "" buildSpriteSheet: -> @@ -311,15 +336,15 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass p1.z += bobOffset x: p1.x, y: p1.y, z: if @thang.isLand then 0 else p1.z - @thang.depth / 2 - updatePosition: (log) -> - return if @stillLoading + updatePosition: (whileLoading=false) -> + return if @stillLoading and not whileLoading return unless @thang?.pos and @options.camera? wop = @getWorldPosition() [p0, p1] = [@lastPos, @thang.pos] return if p0 and p0.x is p1.x and p0.y is p1.y and p0.z is p1.z and not @options.camera.tweeningZoomTo and not @thang.bobHeight sup = @options.camera.worldToSurface wop [@imageObject.x, @imageObject.y] = [sup.x, sup.y] - @lastPos = p1.copy?() or _.clone(p1) + @lastPos = p1.copy?() or _.clone(p1) unless whileLoading @hasMoved = true if @thangType.get('name') is 'Flag' and not @notOfThisWorld # Let the pending flags know we're here (but not this call stack, they need to delete themselves, and we may be iterating sprites). diff --git a/app/lib/surface/WizardSprite.coffee b/app/lib/surface/WizardSprite.coffee index c577dae28..d5bdfede2 100644 --- a/app/lib/surface/WizardSprite.coffee +++ b/app/lib/surface/WizardSprite.coffee @@ -82,7 +82,7 @@ module.exports = class WizardSprite extends IndieSprite shouldUpdate = not _.isEqual(newColorConfig, @options.colorConfig) @options.colorConfig = $.extend(true, {}, newColorConfig) if shouldUpdate - @setupSprite() + @setUpSprite() @playAction(@currentAction) if @currentAction onSpriteSelected: (e) -> @@ -205,8 +205,8 @@ module.exports = class WizardSprite extends IndieSprite @faceTarget() @update true - updatePosition: -> - return unless @options.camera + updatePosition: (whileLoading=false) -> + return if whileLoading or not @options.camera @thang.pos = @getCurrentPosition() @faceTarget() sup = @options.camera.worldToSurface x: @thang.pos.x, y: @thang.pos.y, z: @thang.pos.z - @thang.depth / 2 diff --git a/app/views/play/level/modal/LevelGuideModal.coffee b/app/views/play/level/modal/LevelGuideModal.coffee index 4f454b380..0d54a71a7 100644 --- a/app/views/play/level/modal/LevelGuideModal.coffee +++ b/app/views/play/level/modal/LevelGuideModal.coffee @@ -14,7 +14,7 @@ module.exports = class LevelGuideModal extends ModalView constructor: (options) -> @firstOnly = options.firstOnly - @docs = options?.docs + @docs = options?.docs ? {} general = @docs.generalArticles or [] specific = @docs.specificArticles or [] From b918f5adfb59812f976f751833c1e86ddf2e08fd Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 17:18:03 -0700 Subject: [PATCH 49/98] Fixed #1262. --- app/models/Level.coffee | 6 +-- headless_client.coffee | 1 + headless_client/test.js | 107 ++++++++++++---------------------------- 3 files changed, 35 insertions(+), 79 deletions(-) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 59487f703..3b08b67eb 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -139,7 +139,7 @@ module.exports = class Level extends CocoModel for component in thang.components or [] continue unless lc = _.find levelComponents, {original: component.original} component.config ?= {} - TreemaNode.utils.populateDefaults(component.config, lc.configSchema) + TreemaUtils.populateDefaults(component.config, lc.configSchema) @lastType = 'component' @lastOriginal = component.original @walkDefaults component.config, lc.configSchema.properties @@ -147,7 +147,7 @@ module.exports = class Level extends CocoModel fillInDefaultSystemConfiguration: (levelSystems) -> for system in levelSystems ? [] system.config ?= {} - TreemaNode.utils.populateDefaults(system.config, system.model.configSchema) + TreemaUtils.populateDefaults(system.config, system.model.configSchema) @lastType = 'system' @lastOriginal = system.model.name @walkDefaults system.config, system.model.configSchema.properties @@ -165,7 +165,7 @@ module.exports = class Level extends CocoModel else if schema.type is 'array' and config[prop] for item in config[prop] or [] @walkDefaults item, schema.items - + dimensions: -> width = 0 height = 0 diff --git a/headless_client.coffee b/headless_client.coffee index 04fdc109a..1c426bdba 100644 --- a/headless_client.coffee +++ b/headless_client.coffee @@ -42,6 +42,7 @@ Worker::removeEventListener = (what) -> if what is 'message' @onmessage = -> #This webworker api has only one event listener at a time. GLOBAL.tv4 = require('tv4').tv4 +GLOBAL.TreemaUtils = require './bower_components/treema/treema-utils.js' GLOBAL.marked = setOptions: -> store = {} GLOBAL.localStorage = diff --git a/headless_client/test.js b/headless_client/test.js index fdd51bf83..3c92b64e0 100644 --- a/headless_client/test.js +++ b/headless_client/test.js @@ -1,87 +1,42 @@ module.exports = { - "messageGenerated": 1396792689279, - "sessions": [ - { - "sessionID": "533a2c4893b95d9319a58049", - "submitDate": "2014-04-06T06:31:11.806Z", + "messageGenerated": 1409357317134, + "sessions": [{ + "sessionID": "539bb9f86e1d92310506f138", "team": "humans", - "code": { - "ogre-base": { - "chooseAction": "// This is the code for your base. Decide which unit to build each frame.\n// Units you build will go into the this.built array.\n// Destroy the enemy base within 60 seconds!\n// Check out the Guide at the top for more info.\n\n// Choose your hero! You can only build one hero.\nvar hero;\n//hero = 'ironjaw'; // A leaping juggernaut hero, type 'brawler'.\nhero = 'yugargen'; // A devious spellcaster hero, type 'shaman'.\nif(hero && !this.builtHero) {\n this.builtHero = this.build(hero);\n return;\n}\n\n// Munchkins are weak melee units with 1.25s build cooldown.\n// Throwers are fragile, deadly ranged units with 2.5s build cooldown.\nvar buildOrder = ['munchkin', 'thrower', 'munchkin', 'thrower', 'munchkin', 'thrower'];\nvar type = buildOrder[this.built.length % buildOrder.length];\n//this.say('Unit #' + this.built.length + ' will be a ' + type);\nthis.build(type);\n//this.say(\"Move\", {to:{x:20, y:30}});{x: 68, y: 29}{x: 70, y: 30}" + "transpiledCode": { + "mak-fod": {}, + "tharin": { + "chooseAction": "var __interceptThis=(function(){var G=this;return function($this,sandbox){if($this==G){return sandbox;}return $this;};})();\nreturn (function (__global) {\n var tmp0, tmp1;\n tmp1 = function () {\n 'use strict'; _aether.logCallStart(_aether._userInfo); var rjust, debug_coin, use_terrify, player, items, tmp93, tmp94, tmp95, tmp96, tmp97, tmp103, tmp104, tmp105, tmp106, tmp107, tmp118, tmp119, tmp120, tmp121, tmp122, tmp123, tmp124, tmp125, tmp126, tmp127, tmp128, tmp129, tmp130, tmp131, tmp132, tmp133, tmp134, tmp135, tmp136, tmp137, tmp138, tmp139, tmp140, tmp141, tmp142, tmp143, tmp144, tmp145, tmp146, tmp147, tmp148, tmp149, tmp150, tmp151, tmp152, tmp153, tmp154, tmp155, tmp156, tmp157, tmp158, tmp159, tmp160, tmp161, tmp162, tmp163, tmp164, tmp165; for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n _aether.logStatementStart([{ofs: 0, row: 0, col: 0}, {ofs: 234, row: 9, col: 2}]); rjust = function (s, n, fill) {\n var num_pad_chars, out, i, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16, tmp17, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp27, tmp28, tmp29, tmp30, tmp31; s = _aether.createAPIClone(_aether, s); n = _aether.createAPIClone(_aether, n); fill = _aether.createAPIClone(_aether, fill); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n _aether.logStatementStart([{ofs: 43, row: 1, col: 8}, {ofs: 45, row: 1, col: 10}]); tmp2 = ''; _aether.logStatement([{ofs: 43, row: 1, col: 8}, {ofs: 45, row: 1, col: 10}], _aether._userInfo, false);\n tmp6 = 'Math';\n tmp7 = tmp6 in __global;\n if (tmp7) {\n tmp4 = __global[tmp6];\n } else { _aether.logStatementStart([{ofs: 46, row: 1, col: 11}, {ofs: 50, row: 1, col: 15}]);\n tmp8 = 'ReferenceError';\n tmp9 = __global[tmp8];\n tmp10 = new tmp9('ReferenceError: ' + (tmp6 + ' is not defined'));\n throw tmp10;\n _aether.logStatement([{ofs: 46, row: 1, col: 11}, {ofs: 50, row: 1, col: 15}], _aether._userInfo, false); }\n tmp5 = 'floor';\n tmp12 = s;\n _aether.logStatementStart([{ofs: 59, row: 1, col: 24}, {ofs: 61, row: 1, col: 26}]); tmp13 = 10; _aether.logStatement([{ofs: 59, row: 1, col: 24}, {ofs: 61, row: 1, col: 26}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 57, row: 1, col: 22}, {ofs: 61, row: 1, col: 26}]); tmp11 = tmp12 * tmp13; _aether.logStatement([{ofs: 57, row: 1, col: 22}, {ofs: 61, row: 1, col: 26}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 46, row: 1, col: 11}, {ofs: 62, row: 1, col: 27}]); tmp3 = _aether.createAPIClone(_aether, tmp4[tmp5](_aether.restoreAPIClone(_aether, tmp11))); _aether.logStatement([{ofs: 46, row: 1, col: 11}, {ofs: 62, row: 1, col: 27}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 39, row: 1, col: 4}, {ofs: 63, row: 1, col: 28}]); s = tmp2 + tmp3; _aether.logStatement([{ofs: 39, row: 1, col: 4}, {ofs: 63, row: 1, col: 28}], _aether._userInfo, false);\n tmp14 = n;\n tmp16 = s;\n tmp17 = 'length';\n _aether.logStatementStart([{ofs: 92, row: 2, col: 28}, {ofs: 100, row: 2, col: 36}]); tmp15 = tmp16[tmp17]; _aether.logStatement([{ofs: 92, row: 2, col: 28}, {ofs: 100, row: 2, col: 36}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 68, row: 2, col: 4}, {ofs: 101, row: 2, col: 37}]); num_pad_chars = tmp14 - tmp15; _aether.logStatement([{ofs: 68, row: 2, col: 4}, {ofs: 101, row: 2, col: 37}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 106, row: 3, col: 4}, {ofs: 119, row: 3, col: 17}]); out = ''; _aether.logStatement([{ofs: 106, row: 3, col: 4}, {ofs: 119, row: 3, col: 17}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 128, row: 4, col: 8}, {ofs: 137, row: 4, col: 17}]); i = 0; _aether.logStatement([{ofs: 128, row: 4, col: 8}, {ofs: 137, row: 4, col: 17}], _aether._userInfo, false);\n tmp19 = i;\n tmp20 = num_pad_chars;\n _aether.logStatementStart([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}]); tmp18 = tmp19 < tmp20; _aether.logStatement([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}], _aether._userInfo, false);\n tmp25: {\n while (tmp18) {\n tmp26: {\n tmp27 = out;\n tmp28 = fill;\n _aether.logStatementStart([{ofs: 173, row: 5, col: 8}, {ofs: 190, row: 5, col: 25}]); out = tmp27 + tmp28; _aether.logStatement([{ofs: 173, row: 5, col: 8}, {ofs: 190, row: 5, col: 25}], _aether._userInfo, false);\n }\n tmp23 = i;\n tmp24 = 1;\n i = tmp23 + tmp24;\n tmp21 = i;\n tmp22 = num_pad_chars;\n _aether.logStatementStart([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}]); tmp18 = tmp21 < tmp22; _aether.logStatement([{ofs: 139, row: 4, col: 19}, {ofs: 156, row: 4, col: 36}], _aether._userInfo, false);\n }\n }\n tmp29 = out;\n tmp30 = s;\n _aether.logStatementStart([{ofs: 201, row: 7, col: 4}, {ofs: 215, row: 7, col: 18}]); out = tmp29 + tmp30; _aether.logStatement([{ofs: 201, row: 7, col: 4}, {ofs: 215, row: 7, col: 18}], _aether._userInfo, false);\n tmp31 = out;\n return _aether.restoreAPIClone(_aether, tmp31);\n }; _aether.logStatement([{ofs: 0, row: 0, col: 0}, {ofs: 234, row: 9, col: 2}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 236, row: 11, col: 0}, {ofs: 449, row: 15, col: 2}]); debug_coin = function (dist_and_coin) {\n var dist, coin, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64, tmp65, tmp66, tmp67, tmp68, tmp69, tmp70, tmp71, tmp72, tmp73, tmp74, tmp75, tmp76, tmp77, tmp78, tmp79, tmp80, tmp81, tmp82, tmp83, tmp84, tmp85, tmp86; dist_and_coin = _aether.createAPIClone(_aether, dist_and_coin); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp32 = dist_and_coin;\n _aether.logStatementStart([{ofs: 308, row: 12, col: 29}, {ofs: 309, row: 12, col: 30}]); tmp33 = 0; _aether.logStatement([{ofs: 308, row: 12, col: 29}, {ofs: 309, row: 12, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 283, row: 12, col: 4}, {ofs: 311, row: 12, col: 32}]); dist = tmp32[tmp33]; _aether.logStatement([{ofs: 283, row: 12, col: 4}, {ofs: 311, row: 12, col: 32}], _aether._userInfo, false);\n tmp34 = dist_and_coin;\n _aether.logStatementStart([{ofs: 341, row: 13, col: 29}, {ofs: 342, row: 13, col: 30}]); tmp35 = 1; _aether.logStatement([{ofs: 341, row: 13, col: 29}, {ofs: 342, row: 13, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 316, row: 13, col: 4}, {ofs: 344, row: 13, col: 32}]); coin = tmp34[tmp35]; _aether.logStatement([{ofs: 316, row: 13, col: 4}, {ofs: 344, row: 13, col: 32}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 358, row: 14, col: 13}]); tmp51 = ''; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 358, row: 14, col: 13}], _aether._userInfo, false);\n tmp55 = 'Math';\n tmp56 = tmp55 in __global;\n if (tmp56) {\n tmp53 = __global[tmp55];\n } else { _aether.logStatementStart([{ofs: 359, row: 14, col: 14}, {ofs: 363, row: 14, col: 18}]);\n tmp57 = 'ReferenceError';\n tmp58 = __global[tmp57];\n tmp59 = new tmp58('ReferenceError: ' + (tmp55 + ' is not defined'));\n throw tmp59;\n _aether.logStatement([{ofs: 359, row: 14, col: 14}, {ofs: 363, row: 14, col: 18}], _aether._userInfo, false); }\n tmp54 = 'floor';\n tmp60 = dist;\n _aether.logStatementStart([{ofs: 359, row: 14, col: 14}, {ofs: 375, row: 14, col: 30}]); tmp52 = _aether.createAPIClone(_aether, tmp53[tmp54](_aether.restoreAPIClone(_aether, tmp60))); _aether.logStatement([{ofs: 359, row: 14, col: 14}, {ofs: 375, row: 14, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 375, row: 14, col: 30}]); tmp49 = tmp51 + tmp52; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 375, row: 14, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 376, row: 14, col: 31}, {ofs: 379, row: 14, col: 34}]); tmp50 = ','; _aether.logStatement([{ofs: 376, row: 14, col: 31}, {ofs: 379, row: 14, col: 34}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 379, row: 14, col: 34}]); tmp47 = tmp49 + tmp50; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 379, row: 14, col: 34}], _aether._userInfo, false);\n tmp63 = 'Math';\n tmp64 = tmp63 in __global;\n if (tmp64) {\n tmp61 = __global[tmp63];\n } else { _aether.logStatementStart([{ofs: 380, row: 14, col: 35}, {ofs: 384, row: 14, col: 39}]);\n tmp65 = 'ReferenceError';\n tmp66 = __global[tmp65];\n tmp67 = new tmp66('ReferenceError: ' + (tmp63 + ' is not defined'));\n throw tmp67;\n _aether.logStatement([{ofs: 380, row: 14, col: 35}, {ofs: 384, row: 14, col: 39}], _aether._userInfo, false); }\n tmp62 = 'floor';\n tmp71 = coin;\n tmp72 = 'pos';\n _aether.logStatementStart([{ofs: 391, row: 14, col: 46}, {ofs: 399, row: 14, col: 54}]); tmp69 = tmp71[tmp72]; _aether.logStatement([{ofs: 391, row: 14, col: 46}, {ofs: 399, row: 14, col: 54}], _aether._userInfo, false);\n tmp70 = 'x';\n _aether.logStatementStart([{ofs: 391, row: 14, col: 46}, {ofs: 401, row: 14, col: 56}]); tmp68 = tmp69[tmp70]; _aether.logStatement([{ofs: 391, row: 14, col: 46}, {ofs: 401, row: 14, col: 56}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 380, row: 14, col: 35}, {ofs: 402, row: 14, col: 57}]); tmp48 = _aether.createAPIClone(_aether, tmp61[tmp62](_aether.restoreAPIClone(_aether, tmp68))); _aether.logStatement([{ofs: 380, row: 14, col: 35}, {ofs: 402, row: 14, col: 57}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 402, row: 14, col: 57}]); tmp45 = tmp47 + tmp48; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 402, row: 14, col: 57}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 403, row: 14, col: 58}, {ofs: 406, row: 14, col: 61}]); tmp46 = ','; _aether.logStatement([{ofs: 403, row: 14, col: 58}, {ofs: 406, row: 14, col: 61}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 406, row: 14, col: 61}]); tmp43 = tmp45 + tmp46; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 406, row: 14, col: 61}], _aether._userInfo, false);\n tmp75 = 'Math';\n tmp76 = tmp75 in __global;\n if (tmp76) {\n tmp73 = __global[tmp75];\n } else { _aether.logStatementStart([{ofs: 407, row: 14, col: 62}, {ofs: 411, row: 14, col: 66}]);\n tmp77 = 'ReferenceError';\n tmp78 = __global[tmp77];\n tmp79 = new tmp78('ReferenceError: ' + (tmp75 + ' is not defined'));\n throw tmp79;\n _aether.logStatement([{ofs: 407, row: 14, col: 62}, {ofs: 411, row: 14, col: 66}], _aether._userInfo, false); }\n tmp74 = 'floor';\n tmp83 = coin;\n tmp84 = 'pos';\n _aether.logStatementStart([{ofs: 418, row: 14, col: 73}, {ofs: 426, row: 14, col: 81}]); tmp81 = tmp83[tmp84]; _aether.logStatement([{ofs: 418, row: 14, col: 73}, {ofs: 426, row: 14, col: 81}], _aether._userInfo, false);\n tmp82 = 'y';\n _aether.logStatementStart([{ofs: 418, row: 14, col: 73}, {ofs: 428, row: 14, col: 83}]); tmp80 = tmp81[tmp82]; _aether.logStatement([{ofs: 418, row: 14, col: 73}, {ofs: 428, row: 14, col: 83}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 407, row: 14, col: 62}, {ofs: 429, row: 14, col: 84}]); tmp44 = _aether.createAPIClone(_aether, tmp73[tmp74](_aether.restoreAPIClone(_aether, tmp80))); _aether.logStatement([{ofs: 407, row: 14, col: 62}, {ofs: 429, row: 14, col: 84}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 429, row: 14, col: 84}]); tmp41 = tmp43 + tmp44; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 429, row: 14, col: 84}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 430, row: 14, col: 85}, {ofs: 433, row: 14, col: 88}]); tmp42 = ','; _aether.logStatement([{ofs: 430, row: 14, col: 85}, {ofs: 433, row: 14, col: 88}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 433, row: 14, col: 88}]); tmp39 = tmp41 + tmp42; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 433, row: 14, col: 88}], _aether._userInfo, false);\n tmp85 = coin;\n tmp86 = 'id';\n _aether.logStatementStart([{ofs: 434, row: 14, col: 89}, {ofs: 441, row: 14, col: 96}]); tmp40 = tmp85[tmp86]; _aether.logStatement([{ofs: 434, row: 14, col: 89}, {ofs: 441, row: 14, col: 96}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 441, row: 14, col: 96}]); tmp37 = tmp39 + tmp40; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 441, row: 14, col: 96}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 442, row: 14, col: 97}, {ofs: 445, row: 14, col: 100}]); tmp38 = '|'; _aether.logStatement([{ofs: 442, row: 14, col: 97}, {ofs: 445, row: 14, col: 100}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 356, row: 14, col: 11}, {ofs: 445, row: 14, col: 100}]); tmp36 = tmp37 + tmp38; _aether.logStatement([{ofs: 356, row: 14, col: 11}, {ofs: 445, row: 14, col: 100}], _aether._userInfo, false);\n return _aether.restoreAPIClone(_aether, tmp36);\n }; _aether.logStatement([{ofs: 236, row: 11, col: 0}, {ofs: 449, row: 15, col: 2}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 451, row: 17, col: 0}, {ofs: 543, row: 20, col: 2}]); use_terrify = function (player) {\n var tmp87, tmp88, tmp89, tmp90, tmp91, tmp92; player = _aether.createAPIClone(_aether, player); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp87 = player;\n tmp88 = 'terrify';\n _aether.logStatementStart([{ofs: 492, row: 18, col: 4}, {ofs: 508, row: 18, col: 20}]); tmp89 = _aether.createAPIClone(_aether, tmp87[tmp88]()); _aether.logStatement([{ofs: 492, row: 18, col: 4}, {ofs: 508, row: 18, col: 20}], _aether._userInfo, false);\n tmp90 = player;\n tmp91 = 'usedTerrify';\n _aether.logStatementStart([{ofs: 514, row: 19, col: 4}, {ofs: 540, row: 19, col: 30}]); tmp92 = true; _aether.logStatement([{ofs: 514, row: 19, col: 4}, {ofs: 540, row: 19, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 514, row: 19, col: 4}, {ofs: 539, row: 19, col: 29}]); tmp90[tmp91] = tmp92; _aether.logStatement([{ofs: 514, row: 19, col: 4}, {ofs: 539, row: 19, col: 29}], _aether._userInfo, false);\n return;\n }; _aether.logStatement([{ofs: 451, row: 17, col: 0}, {ofs: 543, row: 20, col: 2}], _aether._userInfo, false);\n player = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp93 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp94 = 'getItems';\n _aether.logStatementStart([{ofs: 633, row: 25, col: 0}, {ofs: 661, row: 25, col: 28}]); items = _aether.createAPIClone(_aether, tmp93[tmp94]()); _aether.logStatement([{ofs: 633, row: 25, col: 0}, {ofs: 661, row: 25, col: 28}], _aether._userInfo, false);\n tmp95 = items;\n tmp96 = 'filter';\n _aether.logStatementStart([{ofs: 683, row: 26, col: 21}, {ofs: 723, row: 26, col: 61}]); tmp97 = function (x) {\n var tmp98, tmp99, tmp100, tmp101, tmp102; x = _aether.createAPIClone(_aether, x); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp101 = x;\n tmp102 = 'bountyGold';\n _aether.logStatementStart([{ofs: 704, row: 26, col: 42}, {ofs: 716, row: 26, col: 54}]); tmp99 = tmp101[tmp102]; _aether.logStatement([{ofs: 704, row: 26, col: 42}, {ofs: 716, row: 26, col: 54}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 719, row: 26, col: 57}, {ofs: 720, row: 26, col: 58}]); tmp100 = 2; _aether.logStatement([{ofs: 719, row: 26, col: 57}, {ofs: 720, row: 26, col: 58}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 704, row: 26, col: 42}, {ofs: 720, row: 26, col: 58}]); tmp98 = tmp99 > tmp100; _aether.logStatement([{ofs: 704, row: 26, col: 42}, {ofs: 720, row: 26, col: 58}], _aether._userInfo, false);\n return _aether.restoreAPIClone(_aether, tmp98);\n }; _aether.logStatement([{ofs: 683, row: 26, col: 21}, {ofs: 723, row: 26, col: 61}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 662, row: 26, col: 0}, {ofs: 725, row: 26, col: 63}]); items = _aether.createAPIClone(_aether, tmp95[tmp96](_aether.restoreAPIClone(_aether, tmp97))); _aether.logStatement([{ofs: 662, row: 26, col: 0}, {ofs: 725, row: 26, col: 63}], _aether._userInfo, false);\n tmp105 = items;\n tmp106 = 'map';\n _aether.logStatementStart([{ofs: 744, row: 27, col: 18}, {ofs: 806, row: 27, col: 80}]); tmp107 = function (x) {\n var tmp108, tmp109, tmp110, tmp111, tmp112, tmp113, tmp114, tmp115, tmp116, tmp117; x = _aether.createAPIClone(_aether, x); for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp111 = rjust;\n tmp115 = player;\n tmp116 = 'distance';\n tmp117 = x;\n _aether.logStatementStart([{ofs: 772, row: 27, col: 46}, {ofs: 790, row: 27, col: 64}]); tmp112 = _aether.createAPIClone(_aether, tmp115[tmp116](_aether.restoreAPIClone(_aether, tmp117))); _aether.logStatement([{ofs: 772, row: 27, col: 46}, {ofs: 790, row: 27, col: 64}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 792, row: 27, col: 66}, {ofs: 793, row: 27, col: 67}]); tmp113 = 7; _aether.logStatement([{ofs: 792, row: 27, col: 66}, {ofs: 793, row: 27, col: 67}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 795, row: 27, col: 69}, {ofs: 798, row: 27, col: 72}]); tmp114 = '0'; _aether.logStatement([{ofs: 795, row: 27, col: 69}, {ofs: 798, row: 27, col: 72}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 766, row: 27, col: 40}, {ofs: 799, row: 27, col: 73}]); tmp109 = _aether.createAPIClone(_aether, tmp111(_aether.restoreAPIClone(_aether, tmp112), _aether.restoreAPIClone(_aether, tmp113), _aether.restoreAPIClone(_aether, tmp114))); _aether.logStatement([{ofs: 766, row: 27, col: 40}, {ofs: 799, row: 27, col: 73}], _aether._userInfo, false);\n tmp110 = x;\n _aether.logStatementStart([{ofs: 765, row: 27, col: 39}, {ofs: 803, row: 27, col: 77}]); tmp108 = [\n tmp109,\n tmp110\n ]; _aether.logStatement([{ofs: 765, row: 27, col: 39}, {ofs: 803, row: 27, col: 77}], _aether._userInfo, false);\n return _aether.restoreAPIClone(_aether, tmp108);\n }; _aether.logStatement([{ofs: 744, row: 27, col: 18}, {ofs: 806, row: 27, col: 80}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 734, row: 27, col: 8}, {ofs: 807, row: 27, col: 81}]); tmp103 = _aether.createAPIClone(_aether, tmp105[tmp106](_aether.restoreAPIClone(_aether, tmp107))); _aether.logStatement([{ofs: 734, row: 27, col: 8}, {ofs: 807, row: 27, col: 81}], _aether._userInfo, false);\n tmp104 = 'sort';\n _aether.logStatementStart([{ofs: 726, row: 27, col: 0}, {ofs: 815, row: 27, col: 89}]); items = _aether.createAPIClone(_aether, tmp103[tmp104]()); _aether.logStatement([{ofs: 726, row: 27, col: 0}, {ofs: 815, row: 27, col: 89}], _aether._userInfo, false);\n tmp118 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp119 = 'say';\n tmp121 = items;\n tmp122 = 'map';\n tmp123 = debug_coin;\n _aether.logStatementStart([{ofs: 825, row: 28, col: 9}, {ofs: 846, row: 28, col: 30}]); tmp120 = _aether.createAPIClone(_aether, tmp121[tmp122](_aether.restoreAPIClone(_aether, tmp123))); _aether.logStatement([{ofs: 825, row: 28, col: 9}, {ofs: 846, row: 28, col: 30}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 816, row: 28, col: 0}, {ofs: 847, row: 28, col: 31}]); tmp124 = _aether.createAPIClone(_aether, tmp118[tmp119](_aether.restoreAPIClone(_aether, tmp120))); _aether.logStatement([{ofs: 816, row: 28, col: 0}, {ofs: 847, row: 28, col: 31}], _aether._userInfo, false);\n tmp127 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp128 = 'usedTerrify';\n _aether.logStatementStart([{ofs: 854, row: 30, col: 4}, {ofs: 870, row: 30, col: 20}]); tmp126 = tmp127[tmp128]; _aether.logStatement([{ofs: 854, row: 30, col: 4}, {ofs: 870, row: 30, col: 20}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 853, row: 30, col: 3}, {ofs: 870, row: 30, col: 20}]); tmp125 = !tmp126; _aether.logStatement([{ofs: 853, row: 30, col: 3}, {ofs: 870, row: 30, col: 20}], _aether._userInfo, false);\n if (tmp125) {\n tmp132 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp133 = 'distance';\n tmp135 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp136 = 'getNearestEnemy';\n _aether.logStatementStart([{ofs: 895, row: 31, col: 21}, {ofs: 917, row: 31, col: 43}]); tmp134 = _aether.createAPIClone(_aether, tmp135[tmp136]()); _aether.logStatement([{ofs: 895, row: 31, col: 21}, {ofs: 917, row: 31, col: 43}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 881, row: 31, col: 7}, {ofs: 918, row: 31, col: 44}]); tmp130 = _aether.createAPIClone(_aether, tmp132[tmp133](_aether.restoreAPIClone(_aether, tmp134))); _aether.logStatement([{ofs: 881, row: 31, col: 7}, {ofs: 918, row: 31, col: 44}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 921, row: 31, col: 47}, {ofs: 922, row: 31, col: 48}]); tmp131 = 5; _aether.logStatement([{ofs: 921, row: 31, col: 47}, {ofs: 922, row: 31, col: 48}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 881, row: 31, col: 7}, {ofs: 922, row: 31, col: 48}]); tmp129 = tmp130 < tmp131; _aether.logStatement([{ofs: 881, row: 31, col: 7}, {ofs: 922, row: 31, col: 48}], _aether._userInfo, false);\n if (tmp129) {\n tmp138 = use_terrify;\n tmp139 = player;\n _aether.logStatementStart([{ofs: 941, row: 32, col: 15}, {ofs: 960, row: 32, col: 34}]); tmp137 = _aether.createAPIClone(_aether, tmp138(_aether.restoreAPIClone(_aether, tmp139))); _aether.logStatement([{ofs: 941, row: 32, col: 15}, {ofs: 960, row: 32, col: 34}], _aether._userInfo, false);\n _aether.logCallEnd(); return _aether.restoreAPIClone(_aether, tmp137);\n } else {\n ;\n }\n tmp143 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp144 = 'now';\n _aether.logStatementStart([{ofs: 975, row: 34, col: 7}, {ofs: 985, row: 34, col: 17}]); tmp141 = _aether.createAPIClone(_aether, tmp143[tmp144]()); _aether.logStatement([{ofs: 975, row: 34, col: 7}, {ofs: 985, row: 34, col: 17}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 988, row: 34, col: 20}, {ofs: 990, row: 34, col: 22}]); tmp142 = 40; _aether.logStatement([{ofs: 988, row: 34, col: 20}, {ofs: 990, row: 34, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 975, row: 34, col: 7}, {ofs: 990, row: 34, col: 22}]); tmp140 = tmp141 > tmp142; _aether.logStatement([{ofs: 975, row: 34, col: 7}, {ofs: 990, row: 34, col: 22}], _aether._userInfo, false);\n if (tmp140) {\n tmp146 = use_terrify;\n tmp147 = player;\n _aether.logStatementStart([{ofs: 1009, row: 35, col: 15}, {ofs: 1028, row: 35, col: 34}]); tmp145 = _aether.createAPIClone(_aether, tmp146(_aether.restoreAPIClone(_aether, tmp147))); _aether.logStatement([{ofs: 1009, row: 35, col: 15}, {ofs: 1028, row: 35, col: 34}], _aether._userInfo, false);\n _aether.logCallEnd(); return _aether.restoreAPIClone(_aether, tmp145);\n } else {\n ;\n }\n } else {\n ;\n }\n tmp149 = items;\n _aether.logStatementStart([{ofs: 1049, row: 39, col: 10}, {ofs: 1050, row: 39, col: 11}]); tmp150 = 0; _aether.logStatement([{ofs: 1049, row: 39, col: 10}, {ofs: 1050, row: 39, col: 11}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1043, row: 39, col: 4}, {ofs: 1051, row: 39, col: 12}]); tmp148 = tmp149[tmp150]; _aether.logStatement([{ofs: 1043, row: 39, col: 4}, {ofs: 1051, row: 39, col: 12}], _aether._userInfo, false);\n if (tmp148) {\n tmp151 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp152 = 'move';\n tmp158 = items;\n _aether.logStatementStart([{ofs: 1075, row: 40, col: 20}, {ofs: 1076, row: 40, col: 21}]); tmp159 = 0; _aether.logStatement([{ofs: 1075, row: 40, col: 20}, {ofs: 1076, row: 40, col: 21}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1069, row: 40, col: 14}, {ofs: 1077, row: 40, col: 22}]); tmp156 = tmp158[tmp159]; _aether.logStatement([{ofs: 1069, row: 40, col: 14}, {ofs: 1077, row: 40, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1078, row: 40, col: 23}, {ofs: 1079, row: 40, col: 24}]); tmp157 = 1; _aether.logStatement([{ofs: 1078, row: 40, col: 23}, {ofs: 1079, row: 40, col: 24}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1069, row: 40, col: 14}, {ofs: 1080, row: 40, col: 25}]); tmp154 = tmp156[tmp157]; _aether.logStatement([{ofs: 1069, row: 40, col: 14}, {ofs: 1080, row: 40, col: 25}], _aether._userInfo, false);\n tmp155 = 'pos';\n _aether.logStatementStart([{ofs: 1069, row: 40, col: 14}, {ofs: 1084, row: 40, col: 29}]); tmp153 = tmp154[tmp155]; _aether.logStatement([{ofs: 1069, row: 40, col: 14}, {ofs: 1084, row: 40, col: 29}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1059, row: 40, col: 4}, {ofs: 1085, row: 40, col: 30}]); tmp160 = _aether.createAPIClone(_aether, tmp151[tmp152](_aether.restoreAPIClone(_aether, tmp153))); _aether.logStatement([{ofs: 1059, row: 40, col: 4}, {ofs: 1085, row: 40, col: 30}], _aether._userInfo, false);\n } else {\n tmp161 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp162 = 'moveXY';\n _aether.logStatementStart([{ofs: 1112, row: 42, col: 16}, {ofs: 1114, row: 42, col: 18}]); tmp163 = 64; _aether.logStatement([{ofs: 1112, row: 42, col: 16}, {ofs: 1114, row: 42, col: 18}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1116, row: 42, col: 20}, {ofs: 1118, row: 42, col: 22}]); tmp164 = 40; _aether.logStatement([{ofs: 1116, row: 42, col: 20}, {ofs: 1118, row: 42, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 1100, row: 42, col: 4}, {ofs: 1119, row: 42, col: 23}]); tmp165 = _aether.createAPIClone(_aether, tmp161[tmp162](_aether.restoreAPIClone(_aether, tmp163), _aether.restoreAPIClone(_aether, tmp164))); _aether.logStatement([{ofs: 1100, row: 42, col: 4}, {ofs: 1119, row: 42, col: 23}], _aether._userInfo, false);\n }\n _aether.logCallEnd(); return;\n };\n tmp0 = 'chooseAction';\n __global[tmp0] = tmp1;\n}(this));" }, - "programmable-shaman": { - "chooseAction": "if (this.hero !== undefined) {\n this.hero = this.getNearest(enemy);\n}\n// Shamans are spellcasters with a weak magic attack\n// and three spells: 'shrink', 'grow', and 'poison-cloud'.\n// Shrink: target has 2/3 health, 1.5x speed for 5s.\n// Grow: target has double health, half speed for 5s.\n// Once per match, she can cast poison cloud, which does\n// 5 poison dps for 10s to enemies in a 10m radius.\nvar right = 0;\nif(right === 0){this.move({x: 70, y: 40});\n}\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\nif (enemies.length === 0) return; // Chill if all enemies are dead.\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\n\nif(this.canCast('shrink', enemy)) \n{\n this.castShrink(enemy);\n}\nelse\n{\n this.castGrow(friend);\n}\n\nvar enemiesinpoisonrange = 0;\nfor (var i = 0; i < enemies.lenght; ++i) {\n var enemi = enemies[i];\n if (this.distance(enemi) <= 10) {\n enemiesinpoisonrange++;\n }\n}\nif (enemiesinpoisonrange >= 7) {\n this.castPoisonCloud(enemy);\n}\n//if (this.distance(ogrebase) > 10) {\n// this.move({x: 70, y: 30});\n//}\n//this.say(\"Defend!\", {targetPos: {x: 45, y: 30}});\n\n//this.say(\"Defend!\", {targetPos: {x: 35, y: 30}});\n\n//this.say(\"Defend!\", {targetPos: {x: 25, y: 30}});\n\n//this.say(\"Attack!\", {to:{x:20, y:30}});\n\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(this.canCast('shrink', enemy)) this.castShrink(enemy);\n//if(this.canCast('grow', friend)) this.castGrow(friend);\n//if(this.canCast('poison-cloud', enemy)) this.castPoisonCloud(enemy);\n//this.attack(enemy);\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 45, y: 30}});\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 50, y: 40});" - }, - "programmable-brawler": { - "chooseAction": "// The Brawler is a huge melee hero with mighty mass.\n// this.throw() hurls an enemy behind him.\n// this.jumpTo() leaps to a target within 20m every 10s.\n// this.stomp() knocks everyone away, once per match.\n\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\nif (enemies.length === 0) return; // Chill if all enemies are dead.\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(!this.getCooldown('jump')) this.jumpTo(enemy.pos);\n//if(!this.getCooldown('stomp') && this.distance(enemy) < 10) this.stomp();\n//if(!this.getCooldown('throw')) this.throw(enemy);\n//this.attack(enemy);\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 60, y: 30}}));\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 50, y: 40});\n\n// You can store state on this across frames:\n//this.lastHealth = this.health;{x: 68, y: 29}{x: 70, y: 30}" - }, - "programmable-librarian": { - "chooseAction": "var enemies = this.getEnemies();\nif (enemies.length === 0)\n return;\nvar enemy = this.getNearest(enemies);\nvar friends = this.getFriends();\nvar friend = this.getNearest(friends);\nvar archer = this.getFriends(type, \"archer\");\nvar soldier = this.getFriends(type, \"soldier\");\nvar hero = this.getFriends(type, \"hushbaum\");\nvar rand = Math.random();\nvar xmove;\nvar ymove;\nfor (var i = 0; i < enemies.length / 3; i += 1) {\n var e = enemies[i];\n var ehealth = Math.floor(e.health);\n if (this.canCast(\"haste\", friend)) {\n this.say(\"Godspeed \" + friend.id + \"!\");\n this.castHaste(friend);\n }\n if (this.canCast(\"haste\", this)) {\n this.say(\"I am Godspeed!\");\n this.castHaste(this);\n }\n if (this.canCast(\"slow\", e)) {\n this.say(\"Chill Out \" + e.id + \"!\");\n this.castSlow(e);\n }\n if (this.distance(e) < 45) {\n this.attack(e);\n this.say(\"Attacking \" + e.id + \" life is \" + ehealth + \".\");\n }\n if (this.health < this.maxHealth * 0.75) {\n if (this.pos.x > 20) {\n this.move({\n x: this.pos.x - 20,\n y: this.pos.y\n });\n } else {\n this.move({\n x: this.pos.x + 20,\n y: this.pos.y\n });\n }\n }\n if (this.canCast(\"regen\", this)) {\n this.castRegen(this);\n this.say(\"I won't die today bitch!\");\n }\n if (friend.health < friend.maxHealth * 0.5) {\n if (this.canCast(\"regen\", friend)) {\n this.say(\"You won't die today \" + friend.id + \".\");\n this.castRegen(friend);\n }\n }\n}\n;" - }, - "programmable-tharin": { - "chooseAction": "// Tharin is a melee fighter with shield, warcry, and terrify skills.\n// this.shield() lets him take one-third damage while defending.\n// this.warcry() gives allies within 10m 30% haste for 5s, every 10s.\n// this.terrify() sends foes within 30m fleeing for 5s, once per match.\n\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\nif (enemies.length === 0) return; // Chill if all enemies are dead.\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(!this.getCooldown('warcry')) this.warcry();\n//if(!this.getCooldown('terrify')) this.terrify();\n//this.shield();\n//this.attack(enemy);\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 30, y: 30}}));\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 40, y: 40});\n\n// You can store state on this across frames:\n//this.lastHealth = this.health;" - }, - "human-base": { - "chooseAction": "// This is the code for your base. Decide which unit to build each frame.\n// Units you build will go into the this.built array.\n// Destroy the enemy base within 60 seconds!\n// Check out the Guide at the top for more info.\n\n// CHOOSE YOUR HERO! You can only build one hero.\nvar hero;\n//hero = 'tharin'; // A fierce knight with battlecry abilities.\nhero = 'hushbaum'; // A fiery spellcaster hero.\n\nif(hero && !this.builtHero) {\n this.builtHero = this.build(hero);\n return;\n}\n\n// Soldiers are hard-to-kill, low damage melee units with 2s build cooldown.\n// Archers are fragile but deadly ranged units with 2.5s build cooldown.\nvar buildOrder = ['soldier', 'soldier', 'archer', 'archer', 'soldier', 'soldier'];\nvar type = buildOrder[this.built.length % buildOrder.length];\nthis.say('Unit #' + this.built.length + ' will be a ' + type);\nthis.build(type);\n\n " - } + "coin-generator-9000": {} }, + "submittedCodeLanguage": "javascript", "teamSpells": { - "ogres": [ - "programmable-brawler/chooseAction", - "programmable-shaman/chooseAction", - "ogre-base/chooseAction" - ], - "humans": [ - "programmable-librarian/chooseAction", - "programmable-tharin/chooseAction", - "human-base/chooseAction" - ] + "common": ["coin-generator-9000/chooseAction"], + "humans": ["tharin/chooseAction"], + "ogres": ["mak-fod/chooseAction"] }, - "levelID": "dungeon-arena", - "creator": "5338c38c4811eff221de2347", - "creatorName": "iC0DE" - }, - { - "sessionID": "532a777c2042708b711a6c29", - "submitDate": "2014-03-20T05:45:54.691Z", + "levelID": "gold-rush", + "creatorName": "s-krackas", + "creator": "539b4c9cb4c4f93805452aba", + "totalScore": 36.389248829587835 + }, { + "sessionID": "53fdc33abfe0d7f308a6906a", "team": "ogres", - "code": { - "ogre-base": { - "chooseAction": "// This is the code for your base. Decide which unit to build each frame.\n// Units you build will go into the this.built array.\n// Destroy the enemy base within 60 seconds!\n// Check out the Guide at the top for more info.\n\n// Choose your hero! You can only build one hero.\nvar hero;\n//hero = 'ironjaw'; // A leaping juggernaut hero, type 'brawler'.\nhero = 'yugargen'; // A devious spellcaster hero, type 'shaman'.\nif(hero && !this.builtHero) {\n this.builtHero = this.build(hero);\n return;\n}\n\n// Munchkins are weak melee units with 1.25s build cooldown.\n// Throwers are fragile, deadly ranged units with 2.5s build cooldown.\nvar buildOrder = ['munchkin', 'munchkin', 'munchkin', 'thrower'];\nvar type = buildOrder[this.built.length % buildOrder.length];\n//this.say('Unit #' + this.built.length + ' will be a ' + type);\nthis.build(type);" - }, - "programmable-shaman": { - "chooseAction": "// Shamans are spellcasters with a weak magic attack\n// and three spells: 'shrink', 'grow', and 'poison-cloud'.\n// Shrink: target has 2/3 health, 1.5x speed for 5s.\n// Grow: target has double health, half speed for 5s.\n// Once per match, she can cast poison cloud, which does\n// 5 poison dps for 10s to enemies in a 10m radius.\n\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\nif (enemies.length === 0) {\n return; // Chill if all enemies are dead.\n}\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\nif (enemies.length > 5) {\n if(this.canCast('poison-cloud', enemy)) {\n this.castPoisonCloud(enemy);\n return;\n }\n}\n\nif (friends.length > 4) {\n this.attack(enemy); \n}\nfor (var i = 0; i < friends.length; ++i) {\n if (friends[i].health < 0) {\n continue;\n }\n if(friends[i].type == \"thrower\" && this.canCast('shrink', friends[i])) {\n this.castShrink(friends[i]);\n return;\n } \n if(friends[i].type == \"munchkin\" && this.canCast('grow', friends[i])) {\n this.castGrow(friends[i]);\n return;\n } \n}\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(this.canCast('shrink', enemy)) this.castShrink(enemy);\n//if(this.canCast('grow', friend)) this.castGrow(friend);\n//if(this.canCast('poison-cloud', enemy)) this.castPoisonCloud(enemy);\n//this.attack(enemy);\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 60, y: 30}}));\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 50, y: 40});" - }, - "programmable-brawler": { - "chooseAction": "// The Brawler is a huge melee hero with mighty mass.\n// this.throw() hurls an enemy behind him.\n// this.jumpTo() leaps to a target within 20m every 10s.\n// this.stomp() knocks everyone away, once per match.\n\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\nif (enemies.length === 0) return; // Chill if all enemies are dead.\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(!this.getCooldown('jump')) this.jumpTo(enemy.pos);\n//if(!this.getCooldown('stomp') && this.distance(enemy) < 10) this.stomp();\n//if(!this.getCooldown('throw')) this.throw(enemy);\n//this.attack(enemy);\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 60, y: 30}}));\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 50, y: 40});\n\n// You can store state on this across frames:\n//this.lastHealth = this.health;" - }, - "human-base": { - "chooseAction": "// This is the code for your base. Decide which unit to build each frame.\n// Units you build will go into the this.built array.\n// Destroy the enemy base within 60 seconds!\n// Check out the Guide at the top for more info.\n\n// CHOOSE YOUR HERO! You can only build one hero.\nvar hero;\nhero = 'tharin'; // A fierce knight with battlecry abilities.\n//hero = 'hushbaum'; // A fiery spellcaster hero.\n\nif(hero && !this.builtHero) {\n this.builtHero = this.build(hero);\n return;\n}\n\n// Soldiers are hard-to-kill, low damage melee units with 2s build cooldown.\n// Archers are fragile but deadly ranged units with 2.5s build cooldown.\nvar buildOrder = ['archer', 'archer', 'soldier', 'archer', 'soldier'];\nvar type = buildOrder[this.built.length % buildOrder.length];\n//this.say('Unit #' + this.built.length + ' will be a ' + type);\nthis.build(type);" - }, - "programmable-tharin": { - "chooseAction": "this.findTypeInRange = function(units, type) {\n for (var i = 0; i < units.length; ++i) {\n var unit = units[i];\n if (unit.type === type && this.distance(unit) < 20)\n return unit;\n }\n return null;\n};\n\nthis.findType = function(units, type) {\n for (var i = 0; i < units.length; ++i) {\n var unit = units[i];\n if (unit.type === type)\n return unit;\n }\n return null;\n};\n\nthis.findHeroInRange = function(units, range) {\n for (var i = 0; i < units.length; ++i) {\n var unit = units[i];\n if ((unit.type === 'shaman' || unit.type === 'brawler') && this.distance(unit) < range)\n return unit;\n }\n return null;\n};\n\n// Tharin is a melee fighter with shield, warcry, and terrify skills.\n// this.shield() lets him take one-third damage while defending.\n// this.warcry() gives allies within 10m 30% haste for 5s, every 10s.\n// this.terrify() sends foes within 30m fleeing for 5s, once per match.\n\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\n\n//Enemies\nvar enemyBase = this.findType(enemies, 'base');\nvar brawler = this.findTypeInRange(enemies, 'brawler');\nvar shaman = this.findTypeInRange(enemies, 'shaman');\n\nif (enemies.length === 0) return; // Chill if all enemies are dead.\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(!this.getCooldown('warcry')) this.warcry();\n//if(!this.getCooldown('terrify')) this.terrify();\n//this.shield();\n\nif((brawler || shaman) && !this.attackTime)\n{\n this.attackTime = true;\n if(brawler)\n this.say(\"Attack!\", {target: brawler});\n else if(shaman)\n this.say(\"Attack!\", {target: shaman});\n}\nelse if(this.health < 15 && this.getCooldown('terrify'))\n{\n this.terrify();\n}\nelse if(this.findHeroInRange(enemies, 30) && this.getCooldown('terrify'))\n{\n this.terrify();\n}\nelse if(this.health < 25)\n{\n this.shield();\n}\nelse if(brawler && this.distance(brawler) <=10)\n{\n this.attack(brawler);\n}\nelse\n{\n this.attack(enemy);\n}\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 30, y: 30}}));\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 40, y: 40});\n\n// You can store state on this across frames:\n//this.lastHealth = this.health;" - }, - "programmable-librarian": { - "chooseAction": "// The Librarian is a spellcaster with a fireball attack\n// plus three useful spells: 'slow', 'regen', and 'haste'.\n// Slow makes a target move and attack at half speed for 5s.\n// Regen makes a target heal 10 hp/s for 10s.\n// Haste speeds up a target by 4x for 5s, once per match.\n\nvar friends = this.getFriends();\nvar enemies = this.getEnemies();\nif (enemies.length === 0) return; // Chill if all enemies are dead.\nvar enemy = this.getNearest(enemies);\nvar friend = this.getNearest(friends);\n\n// Which one do you do at any given time? Only the last called action happens.\n//if(this.canCast('slow', enemy)) this.castSlow(enemy);\n//if(this.canCast('regen', friend)) this.castRegen(friend);\n//if(this.canCast('haste', friend)) this.castHaste(friend);\n//this.attack(enemy);\n\n// You can also command your troops with this.say():\n//this.say(\"Defend!\", {targetPos: {x: 30, y: 30}}));\n//this.say(\"Attack!\", {target: enemy});\n//this.say(\"Move!\", {targetPos: {x: 50, y: 40});" + "transpiledCode": { + "mak-fod": { + "chooseAction": "var __interceptThis=(function(){var G=this;return function($this,sandbox){if($this==G){return sandbox;}return $this;};})();\nreturn (function (__global) {\n var tmp0, tmp1;\n tmp1 = function () {\n 'use strict'; _aether.logCallStart(_aether._userInfo); var items, saystring, currdistance, currindex, i, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp18, tmp19, tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27, tmp28, tmp29, tmp30, tmp31, tmp32, tmp33, tmp34, tmp35, tmp36, tmp37, tmp38, tmp39, tmp40, tmp41, tmp42, tmp43, tmp44, tmp45, tmp46, tmp47, tmp48, tmp49, tmp50, tmp51, tmp52, tmp53, tmp54, tmp55, tmp56, tmp57, tmp58, tmp59, tmp60, tmp61, tmp62, tmp63, tmp64; for(var __argIndexer = 0; __argIndexer < arguments.length; ++__argIndexer) arguments[__argIndexer] = _aether.createAPIClone(_aether, arguments[__argIndexer]);\n tmp2 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp3 = 'getItems';\n _aether.logStatementStart([{ofs: 173, row: 4, col: 0}, {ofs: 201, row: 4, col: 28}]); items = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp2[tmp3]())); _aether.logStatement([{ofs: 173, row: 4, col: 0}, {ofs: 201, row: 4, col: 28}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 202, row: 5, col: 0}, {ofs: 221, row: 5, col: 19}]); saystring = ''; _aether.logStatement([{ofs: 202, row: 5, col: 0}, {ofs: 221, row: 5, col: 19}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 242, row: 6, col: 20}, {ofs: 243, row: 6, col: 21}]); tmp4 = 1; _aether.logStatement([{ofs: 242, row: 6, col: 20}, {ofs: 243, row: 6, col: 21}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 222, row: 6, col: 0}, {ofs: 244, row: 6, col: 22}]); currdistance = -tmp4; _aether.logStatement([{ofs: 222, row: 6, col: 0}, {ofs: 244, row: 6, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 245, row: 7, col: 0}, {ofs: 263, row: 7, col: 18}]); currindex = 0; _aether.logStatement([{ofs: 245, row: 7, col: 0}, {ofs: 263, row: 7, col: 18}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 268, row: 8, col: 4}, {ofs: 277, row: 8, col: 13}]); i = 0; _aether.logStatement([{ofs: 268, row: 8, col: 4}, {ofs: 277, row: 8, col: 13}], _aether._userInfo, false);\n tmp6 = i;\n tmp8 = items;\n tmp9 = 'length';\n _aether.logStatementStart([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}]); tmp7 = tmp8[tmp9]; _aether.logStatement([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}]); tmp5 = tmp6 < tmp7; _aether.logStatement([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n tmp16: {\n while (tmp5) {\n tmp17: {\n tmp19 = i;\n _aether.logStatementStart([{ofs: 325, row: 9, col: 21}, {ofs: 327, row: 9, col: 23}]); tmp20 = ''; _aether.logStatement([{ofs: 325, row: 9, col: 21}, {ofs: 327, row: 9, col: 23}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 308, row: 9, col: 4}, {ofs: 328, row: 9, col: 24}]); tmp18 = tmp19 + tmp20; _aether.logStatement([{ofs: 308, row: 9, col: 4}, {ofs: 328, row: 9, col: 24}], _aether._userInfo, false);\n tmp21 = saystring;\n tmp22 = tmp18;\n saystring = tmp21 + tmp22;\n tmp24 = currdistance;\n _aether.logStatementStart([{ofs: 353, row: 10, col: 24}, {ofs: 354, row: 10, col: 25}]); tmp26 = 1; _aether.logStatement([{ofs: 353, row: 10, col: 24}, {ofs: 354, row: 10, col: 25}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 352, row: 10, col: 23}, {ofs: 354, row: 10, col: 25}]); tmp25 = -tmp26; _aether.logStatement([{ofs: 352, row: 10, col: 23}, {ofs: 354, row: 10, col: 25}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 336, row: 10, col: 7}, {ofs: 354, row: 10, col: 25}]); tmp23 = tmp24 == tmp25; _aether.logStatement([{ofs: 336, row: 10, col: 7}, {ofs: 354, row: 10, col: 25}], _aether._userInfo, false);\n if (tmp23) {\n tmp27 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp28 = 'distance';\n tmp30 = items;\n tmp31 = i;\n _aether.logStatementStart([{ofs: 395, row: 11, col: 37}, {ofs: 403, row: 11, col: 45}]); tmp29 = tmp30[tmp31]; _aether.logStatement([{ofs: 395, row: 11, col: 37}, {ofs: 403, row: 11, col: 45}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 366, row: 11, col: 8}, {ofs: 405, row: 11, col: 47}]); currdistance = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp27[tmp28](_aether.restoreAPIClone(_aether, tmp29)))); _aether.logStatement([{ofs: 366, row: 11, col: 8}, {ofs: 405, row: 11, col: 47}], _aether._userInfo, false);\n } else {\n ;\n }\n tmp35 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp36 = 'distance';\n tmp38 = items;\n tmp39 = i;\n _aether.logStatementStart([{ofs: 433, row: 13, col: 21}, {ofs: 441, row: 13, col: 29}]); tmp37 = tmp38[tmp39]; _aether.logStatement([{ofs: 433, row: 13, col: 21}, {ofs: 441, row: 13, col: 29}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 419, row: 13, col: 7}, {ofs: 442, row: 13, col: 30}]); tmp33 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp35[tmp36](_aether.restoreAPIClone(_aether, tmp37)))); _aether.logStatement([{ofs: 419, row: 13, col: 7}, {ofs: 442, row: 13, col: 30}], _aether._userInfo, false);\n tmp34 = currdistance;\n _aether.logStatementStart([{ofs: 419, row: 13, col: 7}, {ofs: 457, row: 13, col: 45}]); tmp32 = tmp33 < tmp34; _aether.logStatement([{ofs: 419, row: 13, col: 7}, {ofs: 457, row: 13, col: 45}], _aether._userInfo, false);\n if (tmp32) {\n tmp40 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp41 = 'distance';\n tmp43 = items;\n tmp44 = i;\n _aether.logStatementStart([{ofs: 498, row: 14, col: 37}, {ofs: 506, row: 14, col: 45}]); tmp42 = tmp43[tmp44]; _aether.logStatement([{ofs: 498, row: 14, col: 37}, {ofs: 506, row: 14, col: 45}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 469, row: 14, col: 8}, {ofs: 508, row: 14, col: 47}]); currdistance = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp40[tmp41](_aether.restoreAPIClone(_aether, tmp42)))); _aether.logStatement([{ofs: 469, row: 14, col: 8}, {ofs: 508, row: 14, col: 47}], _aether._userInfo, false);\n currindex = i;\n } else {\n ;\n }\n }\n tmp14 = i;\n tmp15 = 1;\n i = tmp14 + tmp15;\n tmp10 = i;\n tmp12 = items;\n tmp13 = 'length';\n _aether.logStatementStart([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}]); tmp11 = tmp12[tmp13]; _aether.logStatement([{ofs: 283, row: 8, col: 19}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}]); tmp5 = tmp10 < tmp11; _aether.logStatement([{ofs: 279, row: 8, col: 15}, {ofs: 295, row: 8, col: 31}], _aether._userInfo, false);\n }\n }\n tmp45 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp46 = 'say';\n tmp47 = currindex;\n _aether.logStatementStart([{ofs: 546, row: 20, col: 0}, {ofs: 565, row: 20, col: 19}]); tmp48 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp45[tmp46](_aether.restoreAPIClone(_aether, tmp47)))); _aether.logStatement([{ofs: 546, row: 20, col: 0}, {ofs: 565, row: 20, col: 19}], _aether._userInfo, false);\n tmp50 = items;\n tmp51 = currindex;\n _aether.logStatementStart([{ofs: 571, row: 21, col: 4}, {ofs: 587, row: 21, col: 20}]); tmp49 = tmp50[tmp51]; _aether.logStatement([{ofs: 571, row: 21, col: 4}, {ofs: 587, row: 21, col: 20}], _aether._userInfo, false);\n if (tmp49) {\n tmp52 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp53 = 'move';\n tmp57 = items;\n tmp58 = currindex;\n _aether.logStatementStart([{ofs: 605, row: 22, col: 14}, {ofs: 621, row: 22, col: 30}]); tmp55 = tmp57[tmp58]; _aether.logStatement([{ofs: 605, row: 22, col: 14}, {ofs: 621, row: 22, col: 30}], _aether._userInfo, false);\n tmp56 = 'pos';\n _aether.logStatementStart([{ofs: 605, row: 22, col: 14}, {ofs: 625, row: 22, col: 34}]); tmp54 = tmp55[tmp56]; _aether.logStatement([{ofs: 605, row: 22, col: 14}, {ofs: 625, row: 22, col: 34}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 595, row: 22, col: 4}, {ofs: 626, row: 22, col: 35}]); tmp59 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp52[tmp53](_aether.restoreAPIClone(_aether, tmp54)))); _aether.logStatement([{ofs: 595, row: 22, col: 4}, {ofs: 626, row: 22, col: 35}], _aether._userInfo, false);\n } else {\n tmp60 = _aether.createAPIClone(_aether, __interceptThis(this, __global));\n tmp61 = 'moveXY';\n _aether.logStatementStart([{ofs: 653, row: 24, col: 16}, {ofs: 655, row: 24, col: 18}]); tmp62 = 18; _aether.logStatement([{ofs: 653, row: 24, col: 16}, {ofs: 655, row: 24, col: 18}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 657, row: 24, col: 20}, {ofs: 659, row: 24, col: 22}]); tmp63 = 36; _aether.logStatement([{ofs: 657, row: 24, col: 20}, {ofs: 659, row: 24, col: 22}], _aether._userInfo, false);\n _aether.logStatementStart([{ofs: 641, row: 24, col: 4}, {ofs: 660, row: 24, col: 23}]); tmp64 = _aether.convertToNativeType(_aether.createAPIClone(_aether, tmp60[tmp61](_aether.restoreAPIClone(_aether, tmp62), _aether.restoreAPIClone(_aether, tmp63)))); _aether.logStatement([{ofs: 641, row: 24, col: 4}, {ofs: 660, row: 24, col: 23}], _aether._userInfo, false);\n }\n _aether.logCallEnd(); return;\n };\n tmp0 = 'chooseAction';\n __global[tmp0] = tmp1;\n}(this));" } }, + "submittedCodeLanguage": "javascript", "teamSpells": { - "ogres": [ - "programmable-brawler/chooseAction", - "programmable-shaman/chooseAction", - "ogre-base/chooseAction" - ], - "humans": [ - "programmable-librarian/chooseAction", - "programmable-tharin/chooseAction", - "human-base/chooseAction" - ] + "common": ["coin-generator-9000/chooseAction"], + "humans": ["tharin/chooseAction"], + "ogres": ["mak-fod/chooseAction"] }, - "levelID": "dungeon-arena", - "creator": "53291a80b112e7240f324667", - "creatorName": "Imbal Oceanrage" - } -], - "taskID": "53415d71942d00aa43dbf3e9", - "receiptHandle": "cd50e44db7dbd4cc0bcce047aa822ba2fe3556cf" -} \ No newline at end of file + "levelID": "gold-rush", + "creatorName": "monkboll", + "creator": "53fdb782716abde208a0e7a4", + "totalScore": 24.174942315603005 + }] +} From 63f2c5db4f9d5896d3cb7268194ab68108f373e2 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 17:33:25 -0700 Subject: [PATCH 50/98] Fixed #1268. --- app/styles/editor/achievement/edit.sass | 2 +- app/styles/editor/article/edit.sass | 6 +++--- app/templates/editor/achievement/edit.jade | 6 +++--- app/templates/editor/article/edit.jade | 8 ++++---- app/views/editor/thang/ThangTypeEditView.coffee | 4 ++++ 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/styles/editor/achievement/edit.sass b/app/styles/editor/achievement/edit.sass index 157353e9a..499cc1794 100644 --- a/app/styles/editor/achievement/edit.sass +++ b/app/styles/editor/achievement/edit.sass @@ -4,7 +4,7 @@ .treema-root margin: 28px 0px 20px - button + .achievement-tool-button float: right margin-top: 15px margin-left: 10px diff --git a/app/styles/editor/article/edit.sass b/app/styles/editor/article/edit.sass index 1c8e5eba2..94743a383 100644 --- a/app/styles/editor/article/edit.sass +++ b/app/styles/editor/article/edit.sass @@ -2,11 +2,11 @@ .treema-root margin-bottom: 20px - button + .article-tool-button float: right - margin-top: 15px + margin-bottom: 15px margin-left: 10px textarea width: 92% - height: 300px \ No newline at end of file + height: 300px diff --git a/app/templates/editor/achievement/edit.jade b/app/templates/editor/achievement/edit.jade index 17bab8045..a7058b835 100644 --- a/app/templates/editor/achievement/edit.jade +++ b/app/templates/editor/achievement/edit.jade @@ -10,9 +10,9 @@ block content li.active | #{achievement.attributes.name} - button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-button Recalculate - button(data-i18n="common.delete", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#delete-button Delete - button(data-i18n="common.save", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#save-button Save + button.achievement-tool-button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-button Recalculate + button.achievement-tool-button(data-i18n="common.delete", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#delete-button Delete + button.achievement-tool-button(data-i18n="common.save", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#save-button Save h3(data-i18n="achievement.edit_achievement_title") Edit Achievement span diff --git a/app/templates/editor/article/edit.jade b/app/templates/editor/article/edit.jade index 76e8d255b..48f610ce2 100644 --- a/app/templates/editor/article/edit.jade +++ b/app/templates/editor/article/edit.jade @@ -10,10 +10,10 @@ block content li.active | #{article.attributes.name} - button(data-i18n="general.version_history").btn.btn-primary#history-button Version History - button(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary#revert-button Revert - button(data-i18n="article.edit_btn_preview", disabled=authorized === true ? undefined : "true").btn.btn-primary#preview-button Preview - button(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary#save-button Save + button.article-tool-button(data-i18n="general.version_history").btn.btn-primary#history-button Version History + button.article-tool-button(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert", disabled=authorized === true ? undefined : "true").btn.btn-primary#revert-button Revert + button.article-tool-button(data-i18n="article.edit_btn_preview", disabled=authorized === true ? undefined : "true").btn.btn-primary#preview-button Preview + button.article-tool-button(data-i18n="common.save", disabled=authorized === true ? undefined : "true").btn.btn-primary#save-button Save h3(data-i18n="article.edit_article_title") Edit Article span diff --git a/app/views/editor/thang/ThangTypeEditView.coffee b/app/views/editor/thang/ThangTypeEditView.coffee index 57bba80c8..3fcfc4832 100644 --- a/app/views/editor/thang/ThangTypeEditView.coffee +++ b/app/views/editor/thang/ThangTypeEditView.coffee @@ -59,6 +59,10 @@ module.exports = class ThangTypeEditView extends RootView @updateFileSize() @refreshAnimation = _.debounce @refreshAnimation, 500 + showLoading: ($el) -> + $el ?= @$el.find('.outer-content') + super($el) + getRenderData: (context={}) -> context = super(context) context.thangType = @thangType From cb14149816784be652ee8a7603d0aba5c33d44a6 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 17:52:47 -0700 Subject: [PATCH 51/98] Fixed #1286. Sort of. --- app/views/editor/level/scripts/ScriptsTabView.coffee | 9 ++++++++- app/views/editor/level/thangs/ThangsTabView.coffee | 5 ++++- app/views/play/level/PlayLevelView.coffee | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index 79484f0eb..2ad48d680 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -18,6 +18,11 @@ module.exports = class ScriptsTabView extends CocoView super options @world = options.world @files = options.files + $(window).on 'resize', @onWindowResize + + destroy: -> + $(window).off 'resize', @onWindowResize + super() onLoaded: -> onLevelLoaded: (e) -> @@ -115,6 +120,8 @@ module.exports = class ScriptsTabView extends CocoView # Update in-place so existing Treema nodes refer to the same array. @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) + onWindowResize: (e) => + @$el.find('#scripts-treema').collapse('show') if $('body').width() > 800 class ScriptsNode extends TreemaArrayNode nodeDescription: 'Script' @@ -165,7 +172,7 @@ class EventPropsNode extends TreemaNode.nodeMap.string joined = '(unset)' if not joined.length @buildValueForDisplaySimply valEl, joined - buildValueForEditing: (valEl, data) -> + buildValueForEditing: (valEl, data) -> super(valEl, data) channel = @getRoot().data.channel channelSchema = Backbone.Mediator.channelSchemas[channel] diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index c3faaddfd..8e27fe78c 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -104,6 +104,8 @@ module.exports = class ThangsTabView extends CocoView $('#thangs-list').height(oldHeight - thangsHeaderHeight - 40) else $('#thangs-list').height(oldHeight - thangsHeaderHeight - 80) + $('#all-thangs').collapse 'show' + $('#add-thangs-column').collapse 'show' undo: (e) -> if not @editThangView then @thangsTreema.undo() else @editThangView.undo() @@ -117,7 +119,7 @@ module.exports = class ThangsTabView extends CocoView $('.tab-content').mousedown @selectAddThang $('#thangs-list').bind 'mousewheel', @preventBodyScrollingInThangList @$el.find('#extant-thangs-filter button:first').button('toggle') - $(window).resize @onWindowResize + $(window).on 'resize', @onWindowResize @addThangsView = @insertSubView new AddThangsView world: @world @buildInterface() # refactor to not have this trigger when this view re-renders? if @thangsTreema.data.length @@ -182,6 +184,7 @@ module.exports = class ThangsTabView extends CocoView destroy: -> @selectAddThangType null @surface.destroy() + $(window).off 'resize', @onWindowResize $(document).unbind 'contextmenu', @preventDefaultContextMenu super() diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 28810a573..3323f77d3 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -84,7 +84,7 @@ module.exports = class PlayLevelView extends RootView @isEditorPreview = @getQueryVariable 'dev' @sessionID = @getQueryVariable 'session' - $(window).on('resize', @onWindowResize) + $(window).on 'resize', @onWindowResize @saveScreenshot = _.throttle @saveScreenshot, 30000 if @isEditorPreview @@ -536,6 +536,7 @@ module.exports = class PlayLevelView extends RootView @god?.destroy() @goalManager?.destroy() @scriptManager?.destroy() + $(window).off 'resize', @onWindowResize delete window.world # not sure where this is set, but this is one way to clean it up clearInterval(@pointerInterval) @bus?.destroy() From 21e0889a868f2aceb251241d0bf6b68bda9ace4a Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 18:02:29 -0700 Subject: [PATCH 52/98] All ace editors must die. I found that certain editor modes will leave a mess in memory, so when editors are destroyed, I also set their modes to '' which seems to take care of most of the issue. They still leave memory behind just a little bit, though. --- app/views/account/AccountSettingsView.coffee | 4 ++++ app/views/account/JobProfileTreemaView.coffee | 4 ++++ app/views/common/LevelSessionCodeView.coffee | 9 ++++++++- .../editor/component/ThangComponentConfigView.coffee | 4 ++++ .../editor/component/ThangComponentsEditView.coffee | 5 ++++- .../editor/level/components/ComponentsTabView.coffee | 4 ++++ .../level/components/LevelComponentEditView.coffee | 6 ++++-- app/views/editor/level/scripts/ScriptsTabView.coffee | 7 ++++++- app/views/editor/level/settings/SettingsTabView.coffee | 4 ++++ .../editor/level/systems/LevelSystemEditView.coffee | 6 ++++-- app/views/editor/level/systems/SystemsTabView.coffee | 4 ++++ app/views/editor/level/thangs/ThangsTabView.coffee | 1 + app/views/editor/thang/ThangTypeColorsTabView.coffee | 1 + app/views/kinds/CocoView.coffee | 7 +++++++ app/views/modal/ModelModal.coffee | 4 ++++ app/views/play/level/tome/SpellView.coffee | 2 ++ app/views/user/JobProfileView.coffee | 4 ++++ 17 files changed, 69 insertions(+), 7 deletions(-) diff --git a/app/views/account/AccountSettingsView.coffee b/app/views/account/AccountSettingsView.coffee index 7dabe37fa..c0c3fa707 100644 --- a/app/views/account/AccountSettingsView.coffee +++ b/app/views/account/AccountSettingsView.coffee @@ -234,3 +234,7 @@ module.exports = class AccountSettingsView extends RootView $(@).data 'saved-value', $(@).val() $('#settings-panes input:checkbox').each -> $(@).data 'saved-value', JSON.stringify $(@)[0].checked + + destroy: -> + @pictureTreema?.destroy() + super() \ No newline at end of file diff --git a/app/views/account/JobProfileTreemaView.coffee b/app/views/account/JobProfileTreemaView.coffee index c2f6ccd92..f5a8e3320 100644 --- a/app/views/account/JobProfileTreemaView.coffee +++ b/app/views/account/JobProfileTreemaView.coffee @@ -95,6 +95,10 @@ module.exports = class JobProfileTreemaView extends CocoView getData: -> return {} unless me.get('jobProfile') or @hasEditedProfile _.pick @jobProfileTreema.data, (value, key) => key in @editableSettings + + destroy: -> + @jobProfileTreema?.destroy() + super() JobProfileTreemaView.commonSkills = commonSkills = ['c#', 'java', 'javascript', 'php', 'android', 'jquery', 'python', 'c++', 'html', 'mysql', 'ios', 'asp.net', 'css', 'sql', 'iphone', '.net', 'objective-c', 'ruby-on-rails', 'c', 'ruby', 'sql-server', 'ajax', 'wpf', 'linux', 'database', 'django', 'vb.net', 'windows', 'facebook', 'r', 'html5', 'multithreading', 'ruby-on-rails-3', 'wordpress', 'winforms', 'node.js', 'spring', 'osx', 'performance', 'visual-studio-2010', 'oracle', 'swing', 'algorithm', 'git', 'linq', 'apache', 'web-services', 'perl', 'wcf', 'entity-framework', 'bash', 'visual-studio', 'sql-server-2008', 'hibernate', 'actionscript-3', 'angularjs', 'matlab', 'qt', 'ipad', 'sqlite', 'cocoa-touch', 'cocoa', 'flash', 'mongodb', 'codeigniter', 'jquery-ui', 'css3', 'tsql', 'google-maps', 'silverlight', 'security', 'delphi', 'vba', 'postgresql', 'jsp', 'shell', 'internet-explorer', 'google-app-engine', 'sockets', 'validation', 'scala', 'oop', 'unit-testing', 'xaml', 'parsing', 'twitter-bootstrap', 'google-chrome', 'http', 'magento', 'email', 'android-layout', 'flex', 'rest', 'maven', 'jsf', 'listview', 'date', 'winapi', 'windows-phone-7', 'facebook-graph-api', 'unix', 'url', 'c#-4.0', 'jquery-ajax', 'svn', 'symfony2', 'table', 'cakephp', 'firefox', 'ms-access', 'java-ee', 'jquery-mobile', 'python-2.7', 'tomcat', 'zend-framework', 'opencv', 'visual-c++', 'opengl', 'spring-mvc', 'sql-server-2005', 'authentication', 'search', 'xslt', 'servlets', 'pdf', 'animation', 'math', 'batch-file', 'excel-vba', 'iis', 'mod-rewrite', 'sharepoint', 'gwt', 'powershell', 'visual-studio-2012', 'haskell', 'grails', 'ubuntu', 'networking', 'nhibernate', 'design-patterns', 'testing', 'jpa', 'visual-studio-2008', 'core-data', 'user-interface', 'audio', 'backbone.js', 'gcc', 'mobile', 'design', 'activerecord', 'extjs', 'video', 'stored-procedures', 'optimization', 'drupal', 'image-processing', 'android-intent', 'logging', 'web-applications', 'razor', 'database-design', 'azure', 'vim', 'memory-management', 'model-view-controller', 'cordova', 'c++11', 'selenium', 'ssl', 'assembly', 'soap', 'boost', 'canvas', 'google-maps-api-3', 'netbeans', 'heroku', 'jsf-2', 'encryption', 'hadoop', 'linq-to-sql', 'dll', 'xpath', 'data-binding', 'windows-phone-8', 'phonegap', 'jdbc', 'python-3.x', 'twitter', 'mvvm', 'gui', 'web', 'jquery-plugins', 'numpy', 'deployment', 'ios7', 'emacs', 'knockout.js', 'graphics', 'joomla', 'unicode', 'windows-8', 'android-fragments', 'ant', 'command-line', 'version-control', 'yii', 'github', 'amazon-web-services', 'macros', 'ember.js', 'svg', 'opengl-es', 'django-models', 'solr', 'orm', 'blackberry', 'windows-7', 'ruby-on-rails-4', 'compiler', 'tcp', 'pdo', 'architecture', 'groovy', 'nginx', 'concurrency', 'paypal', 'iis-7', 'express', 'vbscript', 'google-chrome-extension', 'memory-leaks', 'rspec', 'actionscript', 'interface', 'fonts', 'oauth', 'ssh', 'tfs', 'junit', 'struts2', 'd3.js', 'coldfusion', '.net-4.0', 'jqgrid', 'asp-classic', 'https', 'plsql', 'stl', 'sharepoint-2010', 'asp.net-web-api', 'mysqli', 'sed', 'awk', 'internet-explorer-8', 'jboss', 'charts', 'scripting', 'matplotlib', 'laravel', 'clojure', 'entity-framework-4', 'intellij-idea', 'xml-parsing', 'sqlite3', '3d', 'io', 'mfc', 'devise', 'playframework', 'youtube', 'amazon-ec2', 'localization', 'cuda', 'jenkins', 'ssis', 'safari', 'doctrine2', 'vb6', 'amazon-s3', 'dojo', 'air', 'eclipse-plugin', 'android-asynctask', 'crystal-reports', 'cocos2d-iphone', 'dns', 'highcharts', 'ruby-on-rails-3.2', 'ado.net', 'sql-server-2008-r2', 'android-emulator', 'spring-security', 'cross-browser', 'oracle11g', 'bluetooth', 'f#', 'msbuild', 'drupal-7', 'google-apps-script', 'mercurial', 'xna', 'google-analytics', 'lua', 'parallel-processing', 'internationalization', 'java-me', 'mono', 'monotouch', 'android-ndk', 'lucene', 'kendo-ui', 'linux-kernel', 'terminal', 'phpmyadmin', 'makefile', 'ffmpeg', 'applet', 'active-directory', 'coffeescript', 'pandas', 'responsive-design', 'xhtml', 'silverlight-4.0', '.net-3.5', 'jaxb', 'ruby-on-rails-3.1', 'gps', 'geolocation', 'network-programming', 'windows-services', 'laravel-4', 'ggplot2', 'rss', 'webkit', 'functional-programming', 'wsdl', 'telerik', 'maven-2', 'cron', 'mapreduce', 'websocket', 'automation', 'windows-runtime', 'django-forms', 'tkinter', 'android-widget', 'android-activity', 'rubygems', 'content-management-system', 'doctrine', 'django-templates', 'gem', 'fluent-nhibernate', 'seo', 'meteor', 'serial-port', 'glassfish', 'documentation', 'cryptography', 'ef-code-first', 'extjs4', 'x86', 'wordpress-plugin', 'go', 'wix', 'linq-to-entities', 'oracle10g', 'cocos2d', 'selenium-webdriver', 'open-source', 'jtable', 'qt4', 'smtp', 'redis', 'jvm', 'openssl', 'timezone', 'nosql', 'erlang', 'playframework-2.0', 'machine-learning', 'mocking', 'unity3d', 'thread-safety', 'android-actionbar', 'jni', 'udp', 'jasper-reports', 'zend-framework2', 'apache2', 'internet-explorer-7', 'sqlalchemy', 'neo4j', 'ldap', 'jframe', 'youtube-api', 'filesystems', 'make', 'flask', 'gdb', 'cassandra', 'sms', 'g++', 'django-admin', 'push-notification', 'statistics', 'tinymce', 'locking', 'javafx', 'firefox-addon', 'fancybox', 'windows-phone', 'log4j', 'uikit', 'prolog', 'socket.io', 'icons', 'oauth-2.0', 'refactoring', 'sencha-touch', 'elasticsearch', 'symfony1', 'google-api', 'webserver', 'wpf-controls', 'microsoft-metro', 'gtk', 'flex4', 'three.js', 'gradle', 'centos', 'angularjs-directive', 'internet-explorer-9', 'sass', 'html5-canvas', 'interface-builder', 'programming-languages', 'gmail', 'jersey', 'twitter-bootstrap-3', 'arduino', 'requirejs', 'cmake', 'web-development', 'software-engineering', 'startups', 'entrepreneurship', 'social-media-marketing', 'writing', 'marketing', 'web-design', 'graphic-design', 'game-development', 'game-design', 'photoshop', 'illustrator', 'robotics', 'aws', 'devops', 'mathematica', 'bioinformatics', 'data-vis', 'ui', 'embedded-systems', 'codecombat'] diff --git a/app/views/common/LevelSessionCodeView.coffee b/app/views/common/LevelSessionCodeView.coffee index 277eb2e3e..1977d9b03 100644 --- a/app/views/common/LevelSessionCodeView.coffee +++ b/app/views/common/LevelSessionCodeView.coffee @@ -26,13 +26,16 @@ module.exports = class LevelSessionCodeView extends CocoView afterRender: -> super() + editors = [] @$el.find('.code').each (index, codeEl) -> height = parseInt($(codeEl).data('height')) $(codeEl).height(height) editor = ace.edit codeEl editor.setReadOnly true + editors.push editor aceSession = editor.getSession() aceSession.setMode 'ace/mode/javascript' + @editors = editors organizeCode: -> team = @session.get('team') or 'humans' @@ -47,4 +50,8 @@ module.exports = class LevelSessionCodeView extends CocoView name: spell height: height } - filteredSpells \ No newline at end of file + filteredSpells + + destroy: -> + for editor in @editors + @editors \ No newline at end of file diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index edd82ee13..6e6483a24 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -92,6 +92,10 @@ module.exports = class ThangComponentConfigView extends CocoView @trigger 'changed', { component: @component, config: @config } data: -> @editThangTreema.data + + destroy: -> + @editThangTreema?.destroy() + super() class ComponentConfigNode extends TreemaObjectNode nodeDescription: 'Component Property' diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index a8da82b18..867ca02de 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -342,7 +342,10 @@ module.exports = class ThangComponentsEditView extends CocoView @loadComponents(sparseComponents) @components = @components.concat(sparseComponents) @onComponentsChanged() - + + destroy: -> + @componentsTreema?.destroy() + super() class ThangComponentsObjectNode extends TreemaObjectNode addNewChild: -> @addNewChildForKey('') # HACK to get the object adding to act more like adding to an array diff --git a/app/views/editor/level/components/ComponentsTabView.coffee b/app/views/editor/level/components/ComponentsTabView.coffee index 98ba729f1..7162273de 100644 --- a/app/views/editor/level/components/ComponentsTabView.coffee +++ b/app/views/editor/level/components/ComponentsTabView.coffee @@ -81,6 +81,10 @@ module.exports = class ComponentsTabView extends CocoView onLevelComponentEditingEnded: (e) -> @removeSubView @levelComponentEditView @levelComponentEditView = null + + destroy: -> + @componentsTreema?.destroy() + super() class LevelComponentNode extends TreemaObjectNode valueClass: 'treema-level-component' diff --git a/app/views/editor/level/components/LevelComponentEditView.coffee b/app/views/editor/level/components/LevelComponentEditView.coffee index 3638a92d8..e25a39249 100644 --- a/app/views/editor/level/components/LevelComponentEditView.coffee +++ b/app/views/editor/level/components/LevelComponentEditView.coffee @@ -84,7 +84,7 @@ module.exports = class LevelComponentEditView extends CocoView @levelComponent.set 'configSchema', @configSchemaTreema.data buildCodeEditor: -> - @editor?.destroy() + @destroyAceEditor(@editor) editorEl = $('
').text(@levelComponent.get('code')).addClass('inner-editor') @$el.find('#component-code-editor').empty().append(editorEl) @editor = ace.edit(editorEl[0]) @@ -120,5 +120,7 @@ module.exports = class LevelComponentEditView extends CocoView button.find('> span').toggleClass('secret') destroy: -> - @editor?.destroy() + @destroyAceEditor(@editor) + @componentSettingsTreema?.destroy() + @configSchemaTreema?.destroy() super() diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index 79484f0eb..87f44402c 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -83,6 +83,7 @@ module.exports = class ScriptsTabView extends CocoView newPath = selected.getPath() return if newPath is @selectedScriptPath + @scriptTreema?.destroy() @scriptTreema = @$el.find('#script-treema').treema treemaOptions @scriptTreema.build() @scriptTreema.childrenTreemas?.noteChain?.open() @@ -114,7 +115,11 @@ module.exports = class ScriptsTabView extends CocoView onThangsEdited: (e) -> # Update in-place so existing Treema nodes refer to the same array. @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) - + + destroy: -> + @scriptTreema?.destroy() + @scriptTreemas?.destroy() + super() class ScriptsNode extends TreemaArrayNode nodeDescription: 'Script' diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index 15ebbb168..10990c960 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -60,6 +60,10 @@ module.exports = class SettingsTabView extends CocoView onThangsEdited: (e) -> # Update in-place so existing Treema nodes refer to the same array. @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) + + destroy: -> + @settingsTreema?.destroy() + super() class SettingsNode extends TreemaObjectNode diff --git a/app/views/editor/level/systems/LevelSystemEditView.coffee b/app/views/editor/level/systems/LevelSystemEditView.coffee index f0dd4fb2b..1a91a214d 100644 --- a/app/views/editor/level/systems/LevelSystemEditView.coffee +++ b/app/views/editor/level/systems/LevelSystemEditView.coffee @@ -78,7 +78,7 @@ module.exports = class LevelSystemEditView extends CocoView @levelSystem.set 'configSchema', @configSchemaTreema.data buildCodeEditor: -> - @editor?.destroy() + @destroyAceEditor(@editor) editorEl = $('
').text(@levelSystem.get('code')).addClass('inner-editor') @$el.find('#system-code-editor').empty().append(editorEl) @editor = ace.edit(editorEl[0]) @@ -114,5 +114,7 @@ module.exports = class LevelSystemEditView extends CocoView button.find('> span').toggleClass('secret') destroy: -> - @editor?.destroy() + @destroyAceEditor(@editor) + @systemSettingsTreema?.destroy() + @configSchemaTreema?.destroy() super() diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index 29179e06a..049d41bbf 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -121,6 +121,10 @@ module.exports = class SystemsTabView extends CocoView {original: '528114e60268d018e300001a', majorVersion: 0} # UI {original: '528114040268d018e3000011', majorVersion: 0} # Physics ] + + destroy: -> + @systemsTreema?.destroy() + super() class LevelSystemNode extends TreemaObjectNode valueClass: 'treema-level-system' diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index c3faaddfd..9b72fc643 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -183,6 +183,7 @@ module.exports = class ThangsTabView extends CocoView @selectAddThangType null @surface.destroy() $(document).unbind 'contextmenu', @preventDefaultContextMenu + @thangsTreema?.destroy() super() onViewSwitched: (e) -> diff --git a/app/views/editor/thang/ThangTypeColorsTabView.coffee b/app/views/editor/thang/ThangTypeColorsTabView.coffee index 8f5884eda..47c3f2594 100644 --- a/app/views/editor/thang/ThangTypeColorsTabView.coffee +++ b/app/views/editor/thang/ThangTypeColorsTabView.coffee @@ -21,6 +21,7 @@ module.exports = class ThangTypeColorsTabView extends CocoView @interval = setInterval f, 1000 destroy: -> + @colorGroups?.destroy() clearInterval @interval super() diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index d3d44b6f5..41e974a01 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -65,6 +65,13 @@ module.exports = class CocoView extends Backbone.View @destroy = doNothing $.noty.closeAll() + destroyAceEditor: (editor) -> + # convenience method to make sure the ace editor is as destroyed as can be + return unless editor + session = editor.getSession() + session.setMode '' + editor.destroy() + afterInsert: -> willDisappear: -> diff --git a/app/views/modal/ModelModal.coffee b/app/views/modal/ModelModal.coffee index a5fbc5ce5..482482ea0 100644 --- a/app/views/modal/ModelModal.coffee +++ b/app/views/modal/ModelModal.coffee @@ -69,3 +69,7 @@ module.exports = class ModelModal extends ModalView res.success (model, response, options) => return if @destroyed @hide() + + destroy: -> + @modelTreemas[model].destroy() for model of @modelTreemas + super() \ No newline at end of file diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index e5cded4f7..b0b7b55a5 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -80,6 +80,7 @@ module.exports = class SpellView extends CocoView createACE: -> # Test themes and settings here: http://ace.ajax.org/build/kitchen-sink.html aceConfig = me.get('aceConfig') ? {} + @destroyAceEditor(@ace) @ace = ace.edit @$el.find('.ace')[0] @aceSession = @ace.getSession() @aceDoc = @aceSession.getDocument() @@ -710,5 +711,6 @@ module.exports = class SpellView extends CocoView @ace?.destroy() @aceDoc?.off 'change', @onCodeChangeMetaHandler @aceSession?.selection.off 'changeCursor', @onCursorActivity + @destroyAceEditor(@ace) @debugView?.destroy() super() diff --git a/app/views/user/JobProfileView.coffee b/app/views/user/JobProfileView.coffee index 76903b141..8a176782e 100644 --- a/app/views/user/JobProfileView.coffee +++ b/app/views/user/JobProfileView.coffee @@ -586,3 +586,7 @@ module.exports = class JobProfileView extends UserView session = _.find @sessions.models, (session) -> session.id is sessionID modal = new JobProfileCodeModal({session:session}) @openModalView modal + + destroy: -> + @remarkTreema?.destroy() + super() \ No newline at end of file From 40aa95ba1606eb05d5f5828d287637e6ea824bdd Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 18:06:45 -0700 Subject: [PATCH 53/98] Fixed #1297. --- .../level/components/LevelComponentEditView.coffee | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/views/editor/level/components/LevelComponentEditView.coffee b/app/views/editor/level/components/LevelComponentEditView.coffee index e25a39249..20b8d19a2 100644 --- a/app/views/editor/level/components/LevelComponentEditView.coffee +++ b/app/views/editor/level/components/LevelComponentEditView.coffee @@ -68,10 +68,19 @@ module.exports = class LevelComponentEditView extends CocoView null buildConfigSchemaTreema: -> + configSchema = @levelComponent.get 'configSchema' + if configSchema.properties + # Alphabetize (#1297) + propertyNames = _.keys configSchema.properties + propertyNames.sort() + orderedProperties = {} + for prop in propertyNames + orderedProperties[prop] = configSchema.properties[prop] + configSchema.properties = orderedProperties treemaOptions = supermodel: @supermodel schema: LevelComponent.schema.properties.configSchema - data: @levelComponent.get 'configSchema' + data: configSchema readOnly: me.get('anonymous') callbacks: {change: @onConfigSchemaEdited} @configSchemaTreema = @$el.find('#config-schema-treema').treema treemaOptions From e5cf6c340b555a2c18d718a9024e78afb30d7ccf Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Fri, 29 Aug 2014 23:09:38 -0700 Subject: [PATCH 54/98] Fixed #1140. I just put it on the home page for now, but later we can put it somewhere where it makes sense. --- app/styles/home.sass | 14 +++++++++ app/templates/editor/level/edit.jade | 2 +- app/templates/home.jade | 30 +++++++++++++++++++ app/views/HomeView.coffee | 18 +++++++++++ server/levels/level_handler.coffee | 4 +-- .../sessions/level_session_handler.coffee | 19 ++++++++++++ 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/app/styles/home.sass b/app/styles/home.sass index 43c7a8b6a..60155888e 100644 --- a/app/styles/home.sass +++ b/app/styles/home.sass @@ -97,6 +97,12 @@ .code-wizard opacity: 0.5 + .language-play-count + text-transform: lowercase + position: absolute + left: 0 + top: 0 + .code-language cursor: pointer text-align: center @@ -145,6 +151,8 @@ background: transparent url(/images/pages/home/language_js.png) no-repeat padding-right: 150px + .language-play-count + right: -100px .code-wizard left: 120px @@ -152,6 +160,8 @@ background: transparent url(/images/pages/home/language_python.png) no-repeat padding-left: 150px + .language-play-count + left: 125px .code-wizard right: 120px @@ -177,6 +187,10 @@ .col-md-3 padding: 0px + .language-play-count + left: 15px + top: -15px + .code-language background: transparent url(/images/pages/home/language_background_small.png) no-repeat width: 250px diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index d65981797..5def3a60b 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -46,7 +46,7 @@ block header span.glyphicon-arrow-left.glyphicon li#redo-button(title="Redo (Ctrl+Shift+Z)") a - span.glyphicon-repeat.glyphicon + span.glyphicon-arrow-right.glyphicon if authorized li#commit-level-start-button a diff --git a/app/templates/home.jade b/app/templates/home.jade index 6d9d939df..a52067427 100644 --- a/app/templates/home.jade +++ b/app/templates/home.jade @@ -11,6 +11,11 @@ block content .code-wizard h2 JavaScript p(data-i18n="home.javascript_blurb") The language of the web. Great for writing websites, web apps, HTML5 games, and servers. + - var playCount = codeLanguageCountMap.javascript + if playCount + div.language-play-count + span.spr= playCount + span(data-i18n="resources.sessions") sessions .col-md-6 .code-language.beta#python(data-code-language='python') @@ -18,6 +23,11 @@ block content .code-language-beta h2 Python p(data-i18n="home.python_blurb") Simple yet powerful, Python is a great general purpose programming language. + - var playCount = codeLanguageCountMap.python + if playCount + div.language-play-count + span.spr= playCount + span(data-i18n="resources.sessions") sessions .secondary-code-languages.row .col-md-3 @@ -27,6 +37,11 @@ block content .code-language-beta h3 CoffeeScript p(data-i18n="home.coffeescript_blurb") Nicer JavaScript syntax. + - var playCount = codeLanguageCountMap.coffeescript + if playCount + div.language-play-count + span.spr= playCount + span(data-i18n="resources.sessions") sessions .col-md-3 .code-language.beta#clojure(data-code-language='clojure') @@ -35,6 +50,11 @@ block content .code-language-beta h3 Clojure p(data-i18n="home.clojure_blurb") A modern Lisp. + - var playCount = codeLanguageCountMap.clojure + if playCount + div.language-play-count + span.spr= playCount + span(data-i18n="resources.sessions") sessions .col-md-3 .code-language.beta#lua(data-code-language='lua') @@ -43,6 +63,11 @@ block content .code-language-beta h3 Lua p(data-i18n="home.lua_blurb") Game scripting language. + - var playCount = codeLanguageCountMap.lua + if playCount + div.language-play-count + span.spr= playCount + span(data-i18n="resources.sessions") sessions .col-md-3 .code-language.beta#io(data-code-language='io', title="Careful: Io is still quite buggy") @@ -51,6 +76,11 @@ block content .code-language-beta h3 Io p(data-i18n="home.io_blurb") Simple but obscure. + - var playCount = codeLanguageCountMap.io + if playCount + div.language-play-count + span.spr= playCount + span(data-i18n="resources.sessions") sessions .alert.alert-danger.lt-ie10 strong(data-i18n="home.no_ie") CodeCombat does not run in Internet Explorer 9 or older. Sorry! diff --git a/app/views/HomeView.coffee b/app/views/HomeView.coffee index 4323f5ea0..23f619e56 100644 --- a/app/views/HomeView.coffee +++ b/app/views/HomeView.coffee @@ -15,6 +15,7 @@ module.exports = class HomeView extends RootView constructor: -> super(arguments...) ThangType.loadUniversalWizard() + @getCodeLanguageCounts() getRenderData: -> c = super() @@ -28,6 +29,7 @@ module.exports = class HomeView extends RootView c.isEnglish = (me.get('preferredLanguage') or 'en').startsWith 'en' c.languageName = me.get('preferredLanguage') c.codeLanguage = (me.get('aceConfig') ? {}).language or 'javascript' + c.codeLanguageCountMap = @codeLanguageCountMap c afterRender: -> @@ -68,3 +70,19 @@ module.exports = class HomeView extends RootView lastButton = @$el.find('#multiplayer .game-mode-wrapper').delay(1000).addClass('hovered', 500).delay(500).removeClass('hovered', 500) $('#page-container').animate {scrollTop: firstButton.offset().top - 100, easing: 'easeInOutCubic'}, 500 @updateLanguageLogos codeLanguage + + getCodeLanguageCounts: -> + @codeLanguageCountMap = {} + success = (codeLanguageCounts) => + return if @destroyed + for codeLanguage in codeLanguageCounts + @codeLanguageCountMap[codeLanguage._id] = codeLanguage.sessions + @codeLanguageCountMap.javascript += @codeLanguageCountMap[null] + @render() if @supermodel.finished() + + codeLanguageCountsRequest = @supermodel.addRequestResource 'play_counts', { + url: '/db/level.session/-/code_language_counts' + method: 'POST' + success: success + }, 0 + codeLanguageCountsRequest.load() diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index 79946b116..df3c4c979 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -292,8 +292,8 @@ LevelHandler = class LevelHandler extends Handler if playCounts = @playCountCache[cacheKey] return @sendSuccess res, playCounts query = Session.aggregate [ - {$match: {levelID: {$in: levelIDs}}}, - {$group: {_id: "$levelID", playtime: {$sum: "$playtime"}, sessions: {$sum: 1}}}, + {$match: {levelID: {$in: levelIDs}}} + {$group: {_id: "$levelID", playtime: {$sum: "$playtime"}, sessions: {$sum: 1}}} {$sort: {sessions: -1}} ] query.exec (err, data) => diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee index 678b33e80..573e17466 100644 --- a/server/levels/sessions/level_session_handler.coffee +++ b/server/levels/sessions/level_session_handler.coffee @@ -9,6 +9,7 @@ class LevelSessionHandler extends Handler getByRelationship: (req, res, args...) -> return @getActiveSessions req, res if args.length is 2 and args[1] is 'active' + return @getCodeLanguageCounts req, res if args[1] is 'code_language_counts' super(arguments...) formatEntity: (req, document) -> @@ -33,4 +34,22 @@ class LevelSessionHandler extends Handler return true if ('employer' in req.user.get('permissions')) and (method ? req.method).toLowerCase() is 'get' super(arguments...) + getCodeLanguageCounts: (req, res) -> + if @codeLanguageCache and (new Date()) - @codeLanguageCountCachedSince > 86400 * 1000 # Dumb cache expiration + @codeLanguageCountCache = null + @codeLanguageCountCacheSince = null + if @codeLanguageCountCache + return @sendSuccess res, @codeLanguageCountCache + query = LevelSession.aggregate [ + #{$match: {codeLanguage: {$exists: true}}} # actually slows it down + {$group: {_id: "$codeLanguage", sessions: {$sum: 1}}} + {$sort: {sessions: -1}} + ] + query.exec (err, data) => + if err? then return @sendDatabaseError res, err + @codeLanguageCountCache = data + @codeLanguageCountCachedSince = new Date() + @sendSuccess res, data + + module.exports = new LevelSessionHandler() From ece96bce2ad5b9d4e72149e095b9e81a84a3febe Mon Sep 17 00:00:00 2001 From: 99133799 Date: Sat, 30 Aug 2014 10:31:24 +0300 Subject: [PATCH 55/98] Update ro.coffee --- app/locale/ro.coffee | 184 +++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 0a8d6cc97..5edfd7863 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -3,21 +3,21 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman loading: "Se incarcă..." saving: "Se salvează..." sending: "Se trimite..." -# send: "Send" + send: "Trimite" cancel: "Anulează" save: "Salvează" -# publish: "Publish" - create: "Crează" + publish: "Publica" + create: "Creează" delay_1_sec: "1 secundă" delay_3_sec: "3 secunde" delay_5_sec: "5 secunde" manual: "Manual" fork: "Fork" play: "Joacă" -# retry: "Retry" + retry: "Reîncearca" # watch: "Watch" # unwatch: "Unwatch" -# submit_patch: "Submit Patch" + submit_patch: "Înainteaza Patch" units: second: "secundă" @@ -26,14 +26,14 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman minutes: "minute" hour: "oră" hours: "ore" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + day: "zi" + days: "zile" + week: "săptămână" + weeks: "săptămâni" + month: "luna" + months: "luni" + year: "an" + years: "ani" modal: close: "Inchide" @@ -44,16 +44,16 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman nav: play: "Nivele" -# community: "Community" + community: "Communitate" editor: "Editor" blog: "Blog" forum: "Forum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "Cont" + profile: "Profil" + stats: "Statistică" + code: "Cod" admin: "Admin" - home: "Acasa" + home: "Acasă" contribute: "Contribuie" legal: "Confidențialitate și termeni" about: "Despre" @@ -89,8 +89,8 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman creating: "Se creează contul..." sign_up: "Înscrie-te" log_in: "loghează-te cu parola" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." + social_signup: "Sau, te poți inregistra cu Facebook sau G+:" + required: "Trebuie să te înregistrezi înaite să parcurgi acest drum." home: slogan: "Învață sa scrii cod jucându-te" @@ -103,12 +103,12 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman for_beginners: "Pentru Începători" multiplayer: "Multiplayer" for_developers: "Pentru dezvoltatori" -# javascript_blurb: "The language of the web. Great for writing websites, web apps, HTML5 games, and servers." -# python_blurb: "Simple yet powerful, Python is a great general purpose programming language." -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." + javascript_blurb: "Limbajul web-ului. Perfect pentru crearea website-urilor, web applicații, jocuri HTML5, si servere. The language of the web. Great for writing websites, web apps, HTML5 games, and servers." + python_blurb: "Simplu dar puternic, Python este un limbaj de uz general extraordinar!" + coffeescript_blurb: "JavaScript cu o syntaxă mai placută! Nicer JavaScript syntax." + clojure_blurb: "Un Lisp modern." + lua_blurb: "Limbaj de scripting pentru jocuri." + io_blurb: "Simplu dar obscur." play: choose_your_level: "Alege nivelul" @@ -126,8 +126,8 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman level_difficulty: "Dificultate: " play_as: "Alege-ți echipa" spectate: "Spectator" -# players: "players" -# hours_played: "hours played" + players: "jucători" + hours_played: "ore jucate" contact: contact_us: "Contact CodeCombat" @@ -139,8 +139,8 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman forum_page: "forumul nostru" forum_suffix: " în schimb." send: "Trimite Feedback" -# contact_candidate: "Contact Candidate" -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." + contact_candidate: "Contacteaza Candidatul" + recruitment_reminder: "Folosiți acest formular pentru a ajunge la candidații care va intereseaza pentru interviu. CodeCombat percepe 15% din salariu în primul an. Taxa este datorată la angajare și este rambursabilă pentru 90 de zile în cazul în care salariatul nu rămâne angajat. Cele part time, și angajați cu contract la distanță sunt gratuite, așa cum sunt stagiari." diplomat_suggestion: title: "Ajută-ne să traducem CodeCombat!" @@ -153,16 +153,16 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman wizard_settings: title: "Setări Wizard" customize_avatar: "Personalizează-ți Avatarul" -# active: "Active" -# color: "Color" -# group: "Group" + active: "Activ" + color: "Culoare" + group: "Grup" clothes: "Haine" trim: "Margine" cloud: "Nor" -# team: "Team" + team: "Echipa" spell: "Vrajă" boots: "Încălțăminte" - hue: "Culoare" + hue: "Nuanță" saturation: "Saturație" lightness: "Luminozitate" @@ -172,7 +172,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman autosave: "Modificările se salvează automat" me_tab: "Eu" picture_tab: "Poză" -# upload_picture: "Upload a picture" + upload_picture: "Uploadeaza o imagine" wizard_tab: "Wizard" password_tab: "Parolă" emails_tab: "Email-uri" @@ -181,16 +181,16 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman new_password: "Parolă nouă" new_password_verify: "Verifică" email_subscriptions: "Subscripție Email" -# email_subscriptions_none: "No Email Subscriptions." + email_subscriptions_none: "Nu ai subscripții Email." email_announcements: "Anunțuri" email_announcements_description: "Primește email-uri cu ultimele știri despre CodeCombat." email_notifications: "Notificări" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_notifications_summary: "Control pentru notificări email personalizate, legate de activitatea CodeCombat." + email_any_notes: "Orice Notificări" + email_any_notes_description: "Dezactivați pentru a opri toate e-mailurile de notificare a activității. Disable to stop all activity notification emails." + email_news: "Noutăți" + email_recruit_notes: "Oportunități de job-uri" + email_recruit_notes_description: "Daca joci foarte bine, este posibil sa te contactăm pentru obținerea unui loc (mai bun) de muncă." contributor_emails: "Contributor Class Emails" contribute_prefix: "Căutăm oameni să se alăture distracției! Intră pe " contribute_page: "pagina de contribuție" @@ -199,49 +199,49 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman error_saving: "Salvare erori" saved: "Modificări salvate" password_mismatch: "Parola nu se potrivește." -# password_repeat: "Please repeat your password." + password_repeat: "Te rugăm sa repeți parola." # job_profile: "Job Profile" # job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." # job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" + sample_profile: "Vezi un profil exemplu" + view_profile: "Vizualizează Profilul" account_profile: -# settings: "Settings" -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" + settings: "Setări" + edit_profile: "Modifica Profil" + done_editing: "Am terminat modificările." profile_for_prefix: "Profil pentru " profile_for_suffix: "" -# featured: "Featured" + featured: "Recomandate" # not_featured: "Not Featured" # looking_for: "Looking for:" # last_updated: "Last updated:" # contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" + active: "Caut oferte de interviu." + inactive: "Nu caut oferte" + complete: "complet" + next: "Urmatorul" + next_city: "oras?" + next_country: "alege țara." + next_name: "nume?" + next_short_description: "scrie o scurta descriere." + next_long_description: "descrie poziția dorită." + next_skills: "listeaza cel puțin cinci competențe." + next_work: "cronica istoricului dvs. de lucru." + next_education: "povesteste-ne de chinurile educaționale" + next_projects: "scoate in evidență pana la 3 proiecte la care ai lucrat." + next_links: "adăuga orice link-uri personale sau sociale." + next_photo: "adăuga o fotografie profesionala opțională." + next_active: "indica că esti deschis la oferte ca să apară în căutări." + example_blog: "Blog" + example_personal_site: "Site Personal" + links_header: "Link-uri Personale" + links_blurb: "Link către orice alte site-uri sau profiluri pe care doriți să se sublinieze, ca GitHub, LinkedIn, sau blog-ul personal." + links_name: "Nume Link" + links_name_help: "Catre ce faci link?" + links_link_blurb: "Link URL" + basics_header: "Actualizați informații de bază" + basics_active: "Deschis la Oferte" # basics_active_help: "Want interview offers right now?" # basics_job_title: "Desired Job Title" # basics_job_title_help: "What role are you looking for?" @@ -359,15 +359,15 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman done: "Gata" customize_wizard: "Personalizează Wizard-ul" home: "Acasă" -# stop: "Stop" -# game_menu: "Game Menu" + stop: "Stop" + game_menu: "Meniul Jocului" guide: "Ghid" restart: "Restart" goals: "Obiective" -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" + success: "Success!" + incomplete: "Incomplet" + timed_out: "Ai ramas fara timp" + failing: "Eşec" action_timeline: "Timeline-ul acțiunii" click_to_select: "Apasă pe o unitate pentru a o selecta." reload_title: "Reîncarcă tot codul?" @@ -397,7 +397,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman hud_continue: "Continuă (apasă shift-space)" spell_saved: "Vrajă salvată" skip_tutorial: "Sari peste (esc)" -# keyboard_shortcuts: "Key Shortcuts" + keyboard_shortcuts: "Scurtături Keyboard" loading_ready: "Gata!" tip_insert_positions: "Shift+Click oriunde pe harta pentru a insera punctul în editorul de vrăji." tip_toggle_play: "Pune sau scoate pauza cu Ctrl+P." @@ -421,7 +421,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman # tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." # tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" # tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" + tip_patience: "Să ai rabdare trebuie, tinere Padawan. - Yoda" # tip_documented_bug: "A documented bug is not a bug; it is a feature." # tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" # tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" @@ -444,9 +444,9 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman # inventory_caption: "Equip your hero" # choose_hero_caption: "Choose hero, language" # save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" + options_caption: "Configurarea setărilor" + guide_caption: "Documentație si sfaturi" + multiplayer_caption: "Joaca cu prieteni!" # inventory: # temp: "Temp" @@ -659,7 +659,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick poate să facă orice si a ales să dezvolte CodeCombat." jeremy_description: "Customer support mage, usability tester, and community organizer; probabil ca ați vorbit deja cu Jeremy." michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael este cel care ține serverele in picioare." -# matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." + matt_description: "Bicyclist, Software Engineer, cititor de fantezie eroică, cunoscator de unt de arahide, sorbitor de cafea." legal: page_title: "Aspecte Legale" @@ -830,9 +830,9 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman simulate_all: "RESETEAZĂ ȘI SIMULEAZĂ JOCURI" games_simulated_by: "Jocuri simulate de tine:" games_simulated_for: "Jocuri simulate pentru tine:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" + games_simulated: "Jocuri simulate" + games_played: "Jocuri jucate" + ratio: "Ratie" leaderboard: "Clasament" battle_as: "Luptă ca " summary_your: "Al tău " @@ -851,7 +851,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman no_ranked_matches_pre: "Nici un meci de clasament pentru " no_ranked_matches_post: " echipă! Joacă împotriva unor concurenți și revino apoi aici pentr a-ți plasa meciul in clasament." choose_opponent: "Alege un adversar" -# select_your_language: "Select your language!" + select_your_language: "Alege limbă!" tutorial_play: "Joacă Tutorial-ul" tutorial_recommended: "Recomandat dacă nu ai mai jucat niciodată înainte" tutorial_skip: "Sari peste Tutorial" From 41e9778b3c3e6c721e76e2937767d2224ad076a5 Mon Sep 17 00:00:00 2001 From: Tore Haugland Date: Sat, 30 Aug 2014 10:54:30 +0200 Subject: [PATCH 56/98] Updated locale\no with correct letter case on several translations --- app/locale/no.coffee | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/app/locale/no.coffee b/app/locale/no.coffee index f5333d761..52b7acdc6 100644 --- a/app/locale/no.coffee +++ b/app/locale/no.coffee @@ -71,9 +71,9 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr login: sign_up: "Lag konto" - log_in: "Logg Inn" + log_in: "Logg inn" # logging_in: "Logging In" - log_out: "Logg Ut" + log_out: "Logg ut" recover: "Gjenåpne konto" # recover: @@ -93,7 +93,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # required: "You need to log in before you can go that way." home: - slogan: "Lær å Kode ved å Spille et Spill" + slogan: "Lær å kode ved å spille et spill" no_ie: "CodeCombat kjører ikke på IE8 eller eldre. Beklager!" no_mobile: "CodeCombat ble ikke designet for mobile enheter, og vil muligens ikke virke!" play: "Spill" @@ -111,15 +111,15 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # io_blurb: "Simple but obscure." play: - choose_your_level: "Velg Ditt Nivå" + choose_your_level: "Velg ditt nivå" adventurer_prefix: "Du kan hoppe til hvilket som helts nivå under, eller diskutere nivåene på" adventurer_forum: "Eventyrerforumet" adventurer_suffix: "." - campaign_beginner: "Begynner Felttog" + campaign_beginner: "Begynner felttog" campaign_beginner_description: "... hvor du lærer trolldommen bak programmering." - campaign_dev: "Tilfeldig Vanskeligere Nivåer" + campaign_dev: "Tilfeldig vanskeligere nivåer" campaign_dev_description: "... hvor du lærer grensesnittet mens du stadig gjør mer vanskeligere utfordringer." - campaign_multiplayer: "Multispiller Arenaer" + campaign_multiplayer: "Multispiller arenaer" campaign_multiplayer_description: "... hvor du spiller direkte mot andre spillere." campaign_player_created: "Spillerlaget" campaign_player_created_description: "... hvor du kjemper mot kreativiteten til en av dine medspillende Artisan Trollmenn." @@ -138,14 +138,14 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr forum_prefix: "For allment tilgjengelige henvendelser, vennligst prøv " forum_page: "forumet vårt" forum_suffix: " i steden." - send: "Send Tilbakemelding" + send: "Send tilbakemelding" # contact_candidate: "Contact Candidate" # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." diplomat_suggestion: title: "Hjelp med oversettelse av CodeCombat!" sub_heading: "Vi trenger dine språkkunnskaper." - pitch_body: "Vi utvikler CodeCombat på engelsk, men vi vi har allerede spillere over hele verden. Mange av dem vil spille på Norsk, men snakker ikke Engelsk, så hvis du kan snakke begge språk, vennligst vurder å meld deg på som Diplomat og hjelp å oversette både CodeCombat web siden og alle nivåene til Norsk." + pitch_body: "Vi utvikler CodeCombat på engelsk, men vi vi har allerede spillere over hele verden. Mange av dem vil spille på norsk, men snakker ikke engelsk, så hvis du kan snakke begge språk, vennligst vurder å meld deg på som Diplomat og hjelp å oversette både CodeCombat web siden og alle nivåene til norsk." missing_translations: "Inntil vi har oversatt alt til norsk vil du se engelsk hvor norsk ikke er tilgjengelig." learn_more: "Lær mer om hvordan det er å være en Diplomat" subscribe_as_diplomat: "Meld deg på som Diplomat" @@ -178,7 +178,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr emails_tab: "Epost" # admin: "Admin" wizard_color: "Farge på trollmannens Klær" - new_password: "Nytt Passord" + new_password: "Nytt passord" new_password_verify: "Verifiser" email_subscriptions: "Epostabonnement" # email_subscriptions_none: "No Email Subscriptions." @@ -191,13 +191,13 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # email_news: "News" # email_recruit_notes: "Job Opportunities" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." - contributor_emails: "Bidragsyterklasse Epost" + contributor_emails: "Bidragsyterklasse epost" contribute_prefix: "Vi leter etter folk som vil delta på festen vår! Sjekk ut " contribute_page: "bidragssiden" contribute_suffix: " for å finne ut mer." - email_toggle: "Vis Alle" - error_saving: "Lagring Feilet" - saved: "Endringer Lagret" + email_toggle: "Vis alle" + error_saving: "Lagring feilet" + saved: "Endringer lagret" password_mismatch: "Passordene er ikke like." # password_repeat: "Please repeat your password." # job_profile: "Job Profile" @@ -379,21 +379,21 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr victory_sign_up_poke: "Vil du ha siste nytt på epost? Opprett en gratis konto, så vil vi holde deg oppdatert!" victory_rate_the_level: "Bedøm nivået: " # victory_return_to_ladder: "Return to Ladder" - victory_play_next_level: "Spill Neste Nivå" + victory_play_next_level: "Spill neste nivå" victory_go_home: "Gå Hjem" victory_review: "Fortell oss mer!" victory_hour_of_code_done: "Er du ferdig?" victory_hour_of_code_done_yes: "Ja, jeg er ferdig med min time i koding!" guide_title: "Guide" tome_minion_spells: "Din Minions' Trylleformularer" - tome_read_only_spells: "Kun-Lesbare Trylleformularer" - tome_other_units: "Andre Enheter" + tome_read_only_spells: "Kun-lesbare trylleformularer" + tome_other_units: "Andre enheter" tome_cast_button_castable: "Kast" tome_cast_button_casting: "Kaster" - tome_cast_button_cast: "Kast Trylleformular" + tome_cast_button_cast: "Kast trylleformular" tome_select_spell: "Velg et trylleformular" tome_select_a_thang: "Velg noe for å " - tome_available_spells: "Tilgjenglige Trylleformularer" + tome_available_spells: "Tilgjenglige trylleformularer" hud_continue: "Fortsett (trykk shift+mellomrom)" # spell_saved: "Spell Saved" # skip_tutorial: "Skip (esc)" From 635f0224e406972f907525840686e996bff8cabc Mon Sep 17 00:00:00 2001 From: Tore Haugland Date: Sat, 30 Aug 2014 11:06:10 +0200 Subject: [PATCH 57/98] Updated locale\no with a more common norwegian than a direct translation from English to make it easier to read for all ages --- app/locale/no.coffee | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/app/locale/no.coffee b/app/locale/no.coffee index 52b7acdc6..bfd304549 100644 --- a/app/locale/no.coffee +++ b/app/locale/no.coffee @@ -53,7 +53,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # stats: "Stats" # code: "Code" admin: "Administrator" - home: "Hjem" + home: "Hovedside" contribute: "Bidra" legal: "Juridisk" about: "Om" @@ -70,7 +70,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # cla_agree: "I AGREE" login: - sign_up: "Lag konto" + sign_up: "Lag ny konto" log_in: "Logg inn" # logging_in: "Logging In" log_out: "Logg ut" @@ -82,11 +82,11 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr signup: # create_account_title: "Create Account to Save Progress" - description: "Det er gratis. Trenger bare noen få avklaringer, så er du klar:" - email_announcements: "Motta kunngjøringer på epost" - coppa: "13+ eller ikke fra USA" + description: "Det er gratis. Trenger bare litt informasjon så er du klar:" + email_announcements: "Motta nyhetsbrev på epost" + coppa: "over 13 år eller bor ikke i USA" coppa_why: "(Hvorfor?)" - creating: "Oppretter Konto..." + creating: "Oppretter konto..." sign_up: "Registrer deg" log_in: "Logg inn med passord" # social_signup: "Or, you can sign up through Facebook or G+:" @@ -111,17 +111,17 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # io_blurb: "Simple but obscure." play: - choose_your_level: "Velg ditt nivå" - adventurer_prefix: "Du kan hoppe til hvilket som helts nivå under, eller diskutere nivåene på" + choose_your_level: "Velg brett" + adventurer_prefix: "Du kan velge hvilket som helst brett under, eller diskutere brettene på " adventurer_forum: "Eventyrerforumet" adventurer_suffix: "." - campaign_beginner: "Begynner felttog" + campaign_beginner: "Nybegynner brett" campaign_beginner_description: "... hvor du lærer trolldommen bak programmering." - campaign_dev: "Tilfeldig vanskeligere nivåer" - campaign_dev_description: "... hvor du lærer grensesnittet mens du stadig gjør mer vanskeligere utfordringer." - campaign_multiplayer: "Multispiller arenaer" + campaign_dev: "Vanskeligere brett (tilfeldige)" + campaign_dev_description: "... hvor du lærer hvordan du bruker skjermbildene mens du stadig løser mer vanskelige utfordringer." + campaign_multiplayer: "Flerspiller brett (arenaer)" campaign_multiplayer_description: "... hvor du spiller direkte mot andre spillere." - campaign_player_created: "Spillerlaget" + campaign_player_created: "Brett laget av andre brukere" campaign_player_created_description: "... hvor du kjemper mot kreativiteten til en av dine medspillende Artisan Trollmenn." level_difficulty: "Vanskelighetsgrad: " # play_as: "Play As" @@ -131,13 +131,13 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr contact: contact_us: "Kontakt CodeCombat" - welcome: "Bra å høre fra deg! Bruk dette skjemaet for å sende oss en epost." + welcome: "Kontakt oss gjerne, men vi må ha meldingen på engelsk! Bruk dette skjemaet for å sende oss en epost." contribute_prefix: "Hvis du er interessert i å bidra, sjekk ut vår " contribute_page: "bidragsside" contribute_suffix: "!" - forum_prefix: "For allment tilgjengelige henvendelser, vennligst prøv " - forum_page: "forumet vårt" - forum_suffix: " i steden." + forum_prefix: "Du kan også stille spørsmål i våre åpne " + forum_page: "diskusjonsgrupper" + forum_suffix: " om du ønsker det. For å få flest mulig svar er det lurt å skrive på engelsk" send: "Send tilbakemelding" # contact_candidate: "Contact Candidate" # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." @@ -169,7 +169,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr account_settings: title: "Kontoinnstillinger" not_logged_in: "Logg inn eller opprett en konto for å endre innstillingene dine." - autosave: "Endringer Lagres Automatisk" + autosave: "Endringer lagres automatisk" me_tab: "Meg" picture_tab: "Bilde" # upload_picture: "Upload a picture" @@ -177,7 +177,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr password_tab: "Passord" emails_tab: "Epost" # admin: "Admin" - wizard_color: "Farge på trollmannens Klær" + wizard_color: "Farge på trollmannens klær" new_password: "Nytt passord" new_password_verify: "Verifiser" email_subscriptions: "Epostabonnement" @@ -192,7 +192,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # email_recruit_notes: "Job Opportunities" # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." contributor_emails: "Bidragsyterklasse epost" - contribute_prefix: "Vi leter etter folk som vil delta på festen vår! Sjekk ut " + contribute_prefix: "Vi leter etter folk som vil delta på kodefesten vår! Sjekk ut " contribute_page: "bidragssiden" contribute_suffix: " for å finne ut mer." email_toggle: "Vis alle" @@ -357,8 +357,8 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr play_level: done: "Ferdig" - customize_wizard: "Spesiallag Trollmann" - home: "Hjem" + customize_wizard: "Tilpass trollmann" + home: "Hovedside" # stop: "Stop" # game_menu: "Game Menu" guide: "Guide" @@ -380,7 +380,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr victory_rate_the_level: "Bedøm nivået: " # victory_return_to_ladder: "Return to Ladder" victory_play_next_level: "Spill neste nivå" - victory_go_home: "Gå Hjem" + victory_go_home: "Gå til Hovedsiden" victory_review: "Fortell oss mer!" victory_hour_of_code_done: "Er du ferdig?" victory_hour_of_code_done_yes: "Ja, jeg er ferdig med min time i koding!" From caa372628bc04bf5646bf77e6fcc953f0eb3323d Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Sat, 30 Aug 2014 13:48:19 +0100 Subject: [PATCH 58/98] Fixed errors --- app/templates/game-menu/options-view.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/game-menu/options-view.jade b/app/templates/game-menu/options-view.jade index 8f3df7a89..f21090004 100644 --- a/app/templates/game-menu/options-view.jade +++ b/app/templates/game-menu/options-view.jade @@ -10,7 +10,7 @@ .form-group.slider-group label(for="option-volume") - span(data-i18n="options.volume") Volume + span(data-i18n="options.volume_label") Volume span.spr : span#option-volume-value= (me.get('volume') * 100).toFixed(0) + '%' #option-volume.slider @@ -18,7 +18,7 @@ .form-group.checkbox label(for="option-music") input#option-music(name="option-music", type="checkbox", checked=music) - span(data-i18n="options.music") Music + span(data-i18n="options.music_label") Music span.help-block(data-i18n="options.music_description") Turn background music on/off. .form-group.select-group From 93b1f782aaf176e6bc84964a88f2375322afc8e8 Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Sat, 30 Aug 2014 13:48:36 +0100 Subject: [PATCH 59/98] Added "Volume" and fixed error --- app/locale/en.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 14c559e90..f1752f6a3 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -460,6 +460,7 @@ options: general_options: "General Options" + volume_label: "Volume" music_label: "Music" music_description: "Turn background music on/off." autorun_label: "Autorun" From 1f864300763546caeb089dad8ec2a340424e2b88 Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Sat, 30 Aug 2014 14:04:31 +0100 Subject: [PATCH 60/98] Update pt-PT.coffee --- app/locale/pt-PT.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 83049398d..047fb80e5 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -383,7 +383,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: victory_go_home: "Ir para o Início" victory_review: "Conte-nos mais!" victory_hour_of_code_done: "Terminou?" - victory_hour_of_code_done_yes: "Sim, terminei a minha Hora de Código™!" + victory_hour_of_code_done_yes: "Sim, terminei a minha Hora do Código™!" guide_title: "Guia" tome_minion_spells: "Feitiços dos Seus Minions" tome_read_only_spells: "Feitiços Apenas de Leitura" @@ -655,7 +655,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: why_ending: "E vejam só, é gratuito. " why_ending_url: "Comece a enfeitiçar agora!" george_description: "CEO, homem de negócios, designer da web, designer de jogos e campeão dos programadores iniciantes de todo o lado." - scott_description: "Programador extraordinário, arquiteto de software, feiticeiro da cozinha e mestre das finanças. O Scott é sensato." + scott_description: "Programador extraordinário, arquiteto de software, feiticeiro da cozinha e mestre das finanças. O Scott é o sensato." nick_description: "Feiticeiro da programção, mago da motivação excêntrico e experimentador de pernas para o ar. O Nick pode fazer qualquer coisa e escolhe construir o CodeCombat." jeremy_description: "Mago do suporte ao cliente, testador do uso e organizador da comunidade; provavelmente já falou com o Jeremy." michael_description: "Programador, administrador do sistema e técnico de graduação prodígio, o Michael é a pessoa que mantém os nossos servidores online." From 1b0346a1b71c0ff09a2cc13090c4f7be3ad89ad7 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 08:24:14 -0700 Subject: [PATCH 61/98] Propagated volume label i18n. --- app/locale/ar.coffee | 1 + app/locale/bg.coffee | 1 + app/locale/ca.coffee | 1 + app/locale/cs.coffee | 1 + app/locale/da.coffee | 1 + app/locale/de-AT.coffee | 1 + app/locale/de-CH.coffee | 1 + app/locale/de-DE.coffee | 1 + app/locale/de.coffee | 1 + app/locale/el.coffee | 1 + app/locale/en-AU.coffee | 1 + app/locale/en-GB.coffee | 1 + app/locale/en-US.coffee | 1 + app/locale/es-419.coffee | 1 + app/locale/es-ES.coffee | 1 + app/locale/es.coffee | 1 + app/locale/fa.coffee | 1 + app/locale/fi.coffee | 1 + app/locale/fr.coffee | 1 + app/locale/he.coffee | 1 + app/locale/hi.coffee | 1 + app/locale/hu.coffee | 1 + app/locale/id.coffee | 1 + app/locale/it.coffee | 1 + app/locale/ja.coffee | 1 + app/locale/ko.coffee | 1 + app/locale/lt.coffee | 1 + app/locale/ms.coffee | 1 + app/locale/nb.coffee | 1 + app/locale/nl-BE.coffee | 1 + app/locale/nl-NL.coffee | 1 + app/locale/nl.coffee | 1 + app/locale/nn.coffee | 1 + app/locale/no.coffee | 1 + app/locale/pl.coffee | 1 + app/locale/pt-BR.coffee | 1 + app/locale/pt-PT.coffee | 1 + app/locale/pt.coffee | 1 + app/locale/ro.coffee | 1 + app/locale/ru.coffee | 1 + app/locale/sk.coffee | 1 + app/locale/sl.coffee | 1 + app/locale/sr.coffee | 1 + app/locale/sv.coffee | 1 + app/locale/th.coffee | 1 + app/locale/tr.coffee | 1 + app/locale/uk.coffee | 1 + app/locale/ur.coffee | 1 + app/locale/vi.coffee | 1 + app/locale/zh-HANS.coffee | 1 + app/locale/zh-HANT.coffee | 1 + app/locale/zh-WUU-HANS.coffee | 1 + app/locale/zh-WUU-HANT.coffee | 1 + app/locale/zh.coffee | 1 + 54 files changed, 54 insertions(+) diff --git a/app/locale/ar.coffee b/app/locale/ar.coffee index 010730af7..95ea4b96e 100644 --- a/app/locale/ar.coffee +++ b/app/locale/ar.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/bg.coffee b/app/locale/bg.coffee index 280e57c66..f8c6d22bd 100644 --- a/app/locale/bg.coffee +++ b/app/locale/bg.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "български език", englishDescri # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ca.coffee b/app/locale/ca.coffee index 98d0982ac..94cb6d785 100644 --- a/app/locale/ca.coffee +++ b/app/locale/ca.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/cs.coffee b/app/locale/cs.coffee index 9dcb046f6..b376c394a 100644 --- a/app/locale/cs.coffee +++ b/app/locale/cs.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/da.coffee b/app/locale/da.coffee index 63f1b56b5..7857a119d 100644 --- a/app/locale/da.coffee +++ b/app/locale/da.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/de-AT.coffee b/app/locale/de-AT.coffee index c9365f521..1d8dac402 100644 --- a/app/locale/de-AT.coffee +++ b/app/locale/de-AT.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: options: general_options: "Allgemeine Einstellungen" +# volume_label: "Volume" music_label: "Musik" music_description: "Schalte Hintergrundmusik an/aus." # autorun_label: "Autorun" diff --git a/app/locale/de-CH.coffee b/app/locale/de-CH.coffee index a0ac35ce7..7f29c727e 100644 --- a/app/locale/de-CH.coffee +++ b/app/locale/de-CH.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/de-DE.coffee b/app/locale/de-DE.coffee index 1c2f3e64d..2d93f7d76 100644 --- a/app/locale/de-DE.coffee +++ b/app/locale/de-DE.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: options: general_options: "Allgemeine Einstellungen" +# volume_label: "Volume" music_label: "Musik" music_description: "Schalte Hintergrundmusik an/aus." # autorun_label: "Autorun" diff --git a/app/locale/de.coffee b/app/locale/de.coffee index ae66eb7df..3d9fcf287 100644 --- a/app/locale/de.coffee +++ b/app/locale/de.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra options: general_options: "Allgemeine Einstellungen" +# volume_label: "Volume" music_label: "Musik" music_description: "Schalte Hintergrundmusik an/aus." # autorun_label: "Autorun" diff --git a/app/locale/el.coffee b/app/locale/el.coffee index 63a002c9e..cf1ea906b 100644 --- a/app/locale/el.coffee +++ b/app/locale/el.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/en-AU.coffee b/app/locale/en-AU.coffee index f22c6dbe2..fbf9410c8 100644 --- a/app/locale/en-AU.coffee +++ b/app/locale/en-AU.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/en-GB.coffee b/app/locale/en-GB.coffee index 925c908c6..b8cb8411a 100644 --- a/app/locale/en-GB.coffee +++ b/app/locale/en-GB.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/en-US.coffee b/app/locale/en-US.coffee index 8ad30dd89..1541deabd 100644 --- a/app/locale/en-US.coffee +++ b/app/locale/en-US.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/es-419.coffee b/app/locale/es-419.coffee index a4d767b13..e100142d2 100644 --- a/app/locale/es-419.coffee +++ b/app/locale/es-419.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/es-ES.coffee b/app/locale/es-ES.coffee index 64cb82fcb..2d4d41d12 100644 --- a/app/locale/es-ES.coffee +++ b/app/locale/es-ES.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/es.coffee b/app/locale/es.coffee index 62896d625..43674fe60 100644 --- a/app/locale/es.coffee +++ b/app/locale/es.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "español", englishDescription: "Spanish", t # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/fa.coffee b/app/locale/fa.coffee index 2a75ddb3e..2aea43f4e 100644 --- a/app/locale/fa.coffee +++ b/app/locale/fa.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/fi.coffee b/app/locale/fi.coffee index 6fb9c21d8..ad52c0713 100644 --- a/app/locale/fi.coffee +++ b/app/locale/fi.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/fr.coffee b/app/locale/fr.coffee index 913dda2c9..51c442ed0 100644 --- a/app/locale/fr.coffee +++ b/app/locale/fr.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/he.coffee b/app/locale/he.coffee index 380bbdc08..bc4fe3c6a 100644 --- a/app/locale/he.coffee +++ b/app/locale/he.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/hi.coffee b/app/locale/hi.coffee index 4d00d4dee..876aef695 100644 --- a/app/locale/hi.coffee +++ b/app/locale/hi.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/hu.coffee b/app/locale/hu.coffee index 1e530b4c7..7e19bfc87 100644 --- a/app/locale/hu.coffee +++ b/app/locale/hu.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/id.coffee b/app/locale/id.coffee index ee80dff66..cb115a690 100644 --- a/app/locale/id.coffee +++ b/app/locale/id.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/it.coffee b/app/locale/it.coffee index 84c168299..5c0d1ff76 100644 --- a/app/locale/it.coffee +++ b/app/locale/it.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ja.coffee b/app/locale/ja.coffee index 20202577b..dffa1fa47 100644 --- a/app/locale/ja.coffee +++ b/app/locale/ja.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ko.coffee b/app/locale/ko.coffee index e3a4ef045..da283f410 100644 --- a/app/locale/ko.coffee +++ b/app/locale/ko.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/lt.coffee b/app/locale/lt.coffee index e5ca1fffa..392736446 100644 --- a/app/locale/lt.coffee +++ b/app/locale/lt.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ms.coffee b/app/locale/ms.coffee index d9d6fa714..8090b977e 100644 --- a/app/locale/ms.coffee +++ b/app/locale/ms.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/nb.coffee b/app/locale/nb.coffee index 0c3b52b92..586d407d6 100644 --- a/app/locale/nb.coffee +++ b/app/locale/nb.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/nl-BE.coffee b/app/locale/nl-BE.coffee index dca53ac5d..57779c970 100644 --- a/app/locale/nl-BE.coffee +++ b/app/locale/nl-BE.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/nl-NL.coffee b/app/locale/nl-NL.coffee index 16d8706e0..2c3f0130b 100644 --- a/app/locale/nl-NL.coffee +++ b/app/locale/nl-NL.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/nl.coffee b/app/locale/nl.coffee index 51399f105..f2dd6e88f 100644 --- a/app/locale/nl.coffee +++ b/app/locale/nl.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/nn.coffee b/app/locale/nn.coffee index bcf5762e7..4a1daa98a 100644 --- a/app/locale/nn.coffee +++ b/app/locale/nn.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/no.coffee b/app/locale/no.coffee index bfd304549..a0c93f751 100644 --- a/app/locale/no.coffee +++ b/app/locale/no.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/pl.coffee b/app/locale/pl.coffee index befbc7ca9..f81940642 100644 --- a/app/locale/pl.coffee +++ b/app/locale/pl.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish options: # general_options: "General Options" +# volume_label: "Volume" music_label: "Muzyka" music_description: "Wł/Wył muzykę w tle." autorun_label: "Autostart" diff --git a/app/locale/pt-BR.coffee b/app/locale/pt-BR.coffee index 76e1efe86..148286599 100644 --- a/app/locale/pt-BR.coffee +++ b/app/locale/pt-BR.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index 047fb80e5..b82a76888 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: options: general_options: "Opções Gerais" +# volume_label: "Volume" music_label: "Música" music_description: "Ative ou desative a música de fundo." autorun_label: "Executar Automaticamente" diff --git a/app/locale/pt.coffee b/app/locale/pt.coffee index 2c9518552..b228f67f0 100644 --- a/app/locale/pt.coffee +++ b/app/locale/pt.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 0a8d6cc97..714774510 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index c5ea9cd1e..bb980ecd2 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/sk.coffee b/app/locale/sk.coffee index db7a48f7d..298ee9711 100644 --- a/app/locale/sk.coffee +++ b/app/locale/sk.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/sl.coffee b/app/locale/sl.coffee index 28c63cbf1..31d01e63b 100644 --- a/app/locale/sl.coffee +++ b/app/locale/sl.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/sr.coffee b/app/locale/sr.coffee index 2ec357ed0..863e22f26 100644 --- a/app/locale/sr.coffee +++ b/app/locale/sr.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/sv.coffee b/app/locale/sv.coffee index 7c733b528..37458eb60 100644 --- a/app/locale/sv.coffee +++ b/app/locale/sv.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/th.coffee b/app/locale/th.coffee index 661573d41..b6e1cefed 100644 --- a/app/locale/th.coffee +++ b/app/locale/th.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/tr.coffee b/app/locale/tr.coffee index 720488b3d..4c450173e 100644 --- a/app/locale/tr.coffee +++ b/app/locale/tr.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index d94e271e6..698572816 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "українська мова", englishDesc options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/ur.coffee b/app/locale/ur.coffee index 4df03e932..3f79f2ae0 100644 --- a/app/locale/ur.coffee +++ b/app/locale/ur.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/vi.coffee b/app/locale/vi.coffee index e451dfecf..d617889cf 100644 --- a/app/locale/vi.coffee +++ b/app/locale/vi.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index 9845fd8bc..f2b84e3e4 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/zh-HANT.coffee b/app/locale/zh-HANT.coffee index b2494cd5d..cf89fba0b 100644 --- a/app/locale/zh-HANT.coffee +++ b/app/locale/zh-HANT.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/zh-WUU-HANS.coffee b/app/locale/zh-WUU-HANS.coffee index b548ee9c8..e710f94b0 100644 --- a/app/locale/zh-WUU-HANS.coffee +++ b/app/locale/zh-WUU-HANS.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/zh-WUU-HANT.coffee b/app/locale/zh-WUU-HANT.coffee index 5e531075a..27d180c4f 100644 --- a/app/locale/zh-WUU-HANT.coffee +++ b/app/locale/zh-WUU-HANT.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" diff --git a/app/locale/zh.coffee b/app/locale/zh.coffee index 988c73d08..3429208e3 100644 --- a/app/locale/zh.coffee +++ b/app/locale/zh.coffee @@ -460,6 +460,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra # options: # general_options: "General Options" +# volume_label: "Volume" # music_label: "Music" # music_description: "Turn background music on/off." # autorun_label: "Autorun" From 051f1a77ae6e43d377a183c42f07c1908f3e47cf Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 09:19:41 -0700 Subject: [PATCH 62/98] Fixed #1152. --- app/models/SuperModel.coffee | 2 +- app/views/play/level/PlayLevelView.coffee | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/models/SuperModel.coffee b/app/models/SuperModel.coffee index 25f781034..07cafff46 100644 --- a/app/models/SuperModel.coffee +++ b/app/models/SuperModel.coffee @@ -83,7 +83,7 @@ module.exports = class SuperModel extends Backbone.Model getModelByURL: (modelURL) -> modelURL = modelURL() if _.isFunction(modelURL) return @models[modelURL] or null - + getModelByOriginal: (ModelClass, original) -> _.find @models, (m) -> m.get('original') is original and m.constructor.className is ModelClass.className diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 3323f77d3..acb6aa82f 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -52,6 +52,7 @@ module.exports = class PlayLevelView extends RootView 'level:focus-dom': 'onFocusDom' 'level:disable-controls': 'onDisableControls' 'level:enable-controls': 'onEnableControls' + 'god:world-load-progress-changed': 'onWorldLoadProgressChanged' 'god:new-world-created': 'onNewWorld' 'god:streaming-world-updated': 'onNewWorld' 'god:infinite-loop': 'onInfiniteLoop' @@ -195,6 +196,18 @@ module.exports = class PlayLevelView extends RootView @world = @levelLoader.world @level = @levelLoader.level @otherSession = @levelLoader.opponentSession + @worldLoadFakeResources = [] # first element (0) is 1%, last (100) is 100% + for percent in [1 .. 100] + @worldLoadFakeResources.push @supermodel.addSomethingResource "world_simulation_#{percent}%", 1 + + onWorldLoadProgressChanged: (e) -> + return unless @worldLoadFakeResources + @lastWorldLoadPercent ?= 0 + worldLoadPercent = Math.floor 100 * e.progress + for percent in [@lastWorldLoadPercent + 1 .. worldLoadPercent] by 1 + @worldLoadFakeResources[percent - 1].markLoaded() + @lastWorldLoadPercent = worldLoadPercent + @worldFakeLoadResources = null if worldLoadPercent is 100 # Done, don't need to watch progress any more. loadOpponentTeam: (myTeam) -> opponentSpells = [] From 6f2de166c04f3afafdf3f4d0208ff7f08ae8a090 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Fri, 29 Aug 2014 18:22:21 -0700 Subject: [PATCH 63/98] Removed destroying scripts, since they don't have ace editors, and it was causing a bug. --- app/views/editor/level/scripts/ScriptsTabView.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/editor/level/scripts/ScriptsTabView.coffee b/app/views/editor/level/scripts/ScriptsTabView.coffee index 4bc5ab970..c7be53716 100644 --- a/app/views/editor/level/scripts/ScriptsTabView.coffee +++ b/app/views/editor/level/scripts/ScriptsTabView.coffee @@ -90,7 +90,7 @@ module.exports = class ScriptsTabView extends CocoView newPath = selected.getPath() return if newPath is @selectedScriptPath - @scriptTreema?.destroy() + #@scriptTreema?.destroy() # TODO: get this to work @scriptTreema = @$el.find('#script-treema').treema treemaOptions @scriptTreema.build() @scriptTreema.childrenTreemas?.noteChain?.open() From 5f11a5ecca5bd9211b43504eccb31a8ea0cdad45 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 30 Aug 2014 09:47:23 -0700 Subject: [PATCH 64/98] Selected component in the thang components edit view shows up with a blue header. --- app/styles/editor/component/thang-components-edit-view.sass | 4 +++- app/views/editor/component/ThangComponentsEditView.coffee | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/styles/editor/component/thang-components-edit-view.sass b/app/styles/editor/component/thang-components-edit-view.sass index c10c7c23f..ffc9821b8 100644 --- a/app/styles/editor/component/thang-components-edit-view.sass +++ b/app/styles/editor/component/thang-components-edit-view.sass @@ -51,4 +51,6 @@ right: 0 left: 20px overflow: scroll - + + .selected-component .panel-heading + background-color: lightblue \ No newline at end of file diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 867ca02de..45fd6820e 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -275,6 +275,7 @@ module.exports = class ThangComponentsEditView extends CocoView onSelectComponent: (e, nodes) => @componentsTreema.$el.find('.dependent').removeClass('dependent') + @$el.find('.selected-component').removeClass('selected-component') return unless nodes.length is 1 # find dependent components @@ -302,6 +303,7 @@ module.exports = class ThangComponentsEditView extends CocoView continue unless subview instanceof ThangComponentConfigView if subview.component.get('original') is nodes[0].getData().original subview.$el[0].scrollIntoView() + subview.$el.addClass('selected-component') break onChangeExtantComponents: => From 85a9a558efe6009fbe2380d31c6d3e23b1ea4426 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 13:43:56 -0700 Subject: [PATCH 65/98] Fixed #1329. --- app/lib/aether_utils.coffee | 48 +++++++++++++++++++ app/lib/simulator/Simulator.coffee | 22 +-------- app/models/ThangType.coffee | 2 +- app/schemas/models/level_session.coffee | 4 +- .../play/common/LadderSubmissionView.coffee | 21 ++------ app/views/play/level/tome/Spell.coffee | 25 ++-------- scripts/transpile.coffee | 21 ++------ 7 files changed, 64 insertions(+), 79 deletions(-) create mode 100644 app/lib/aether_utils.coffee diff --git a/app/lib/aether_utils.coffee b/app/lib/aether_utils.coffee new file mode 100644 index 000000000..de07fdb57 --- /dev/null +++ b/app/lib/aether_utils.coffee @@ -0,0 +1,48 @@ +Aether.addGlobal 'Vector', require 'lib/world/vector' +Aether.addGlobal '_', _ + +module.exports.createAetherOptions = (options) -> + throw new Error 'Specify a function name to create an Aether instance' unless options.functionName + throw new Error 'Specify a code language to create an Aether instance' unless options.codeLanguage + + aetherOptions = + functionName: options.functionName + protectAPI: not options.skipProtectAPI + includeFlow: false + yieldConditionally: options.functionName is 'plan' + globals: ['Vector', '_'] + problems: + jshint_W040: {level: 'ignore'} + jshint_W030: {level: 'ignore'} # aether_NoEffect instead + jshint_W038: {level: 'ignore'} # eliminates hoisting problems + jshint_W091: {level: 'ignore'} # eliminates more hoisting problems + jshint_E043: {level: 'ignore'} # https://github.com/codecombat/codecombat/issues/813 -- since we can't actually tell JSHint to really ignore things + jshint_Unknown: {level: 'ignore'} # E043 also triggers Unknown, so ignore that, too + aether_MissingThis: {level: 'error'} + #functionParameters: # TODOOOOO + executionLimit: 1 * 1000 * 1000 + language: options.codeLanguage + parameters = functionParameters[options.functionName] + unless parameters + console.warn "Unknown method #{options.functionName}: please add function parameters to lib/aether_utils.coffee." + parameters = [] + if options.functionParameters and not _.isEqual options.functionParameters, parameters + console.error "Update lib/aether_utils.coffee with the right function parameters for #{options.functionName} (had: #{parameters} but this gave #{options.functionParameters}." + parameters = options.functionParameters + aetherOptions.functionParameters = parameters.slice() + #console.log 'creating aether with options', aetherOptions + return aetherOptions + +# TODO: figure out some way of saving this info dynamically so that we don't have to hard-code it: #1329 +functionParameters = + hear: ['speaker', 'message', 'data'] + makeBid: ['tileGroupLetter'] + findCentroids: ['centroids'] + isFriend: ['name'] + evaluateBoard: ['board', 'player'] + getPossibleMoves: ['board'] + minimax_alphaBeta: ['board', 'player', 'depth', 'alpha', 'beta'] + + chooseAction: [] + plan: [] + initializeCentroids: [] diff --git a/app/lib/simulator/Simulator.coffee b/app/lib/simulator/Simulator.coffee index 0e5a6631e..01cff2057 100644 --- a/app/lib/simulator/Simulator.coffee +++ b/app/lib/simulator/Simulator.coffee @@ -3,9 +3,7 @@ CocoClass = require 'lib/CocoClass' LevelLoader = require 'lib/LevelLoader' GoalManager = require 'lib/world/GoalManager' God = require 'lib/God' - -Aether.addGlobal 'Vector', require 'lib/world/vector' -Aether.addGlobal '_', _ +{createAetherOptions} = require 'lib/aether_utils' module.exports = class Simulator extends CocoClass constructor: (@options) -> @@ -400,23 +398,7 @@ module.exports = class Simulator extends CocoClass aether.transpile '' createAether: (methodName, method, useProtectAPI, codeLanguage) -> - aetherOptions = - functionName: methodName - protectAPI: useProtectAPI - includeFlow: false - yieldConditionally: methodName is 'plan' - globals: ['Vector', '_'] - problems: - jshint_W040: {level: 'ignore'} - jshint_W030: {level: 'ignore'} # aether_NoEffect instead - aether_MissingThis: {level: 'error'} - #functionParameters: # TODOOOOO - executionLimit: 1 * 1000 * 1000 - language: codeLanguage - if methodName is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] - if methodName is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] - if methodName is 'findCentroids' then aetherOptions.functionParameters = ['centroids'] - #console.log 'creating aether with options', aetherOptions + aetherOptions = createAetherOptions functionName: methodName, codeLanguage: codeLanguage, skipProtectAPI: not useProtectAPI return new Aether aetherOptions class SimulationTask diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index 4fe5fb30a..be48d2e74 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -62,7 +62,7 @@ module.exports = class ThangType extends CocoModel options buildSpriteSheet: (options) -> - return false unless @isFullyLoaded() + return false unless @isFullyLoaded() and @get 'raw' @options = @fillOptions options key = @spriteSheetKey(@options) if ss = @spriteSheets[key] then return ss diff --git a/app/schemas/models/level_session.coffee b/app/schemas/models/level_session.coffee index 480159024..7fa0bbb15 100644 --- a/app/schemas/models/level_session.coffee +++ b/app/schemas/models/level_session.coffee @@ -122,7 +122,7 @@ _.extend LevelSessionSchema.properties, type: 'object' additionalProperties: type: 'string' - format: 'javascript' + format: 'code' codeLanguage: type: 'string' @@ -165,6 +165,7 @@ _.extend LevelSessionSchema.properties, type: 'object' additionalProperties: type: 'string' + format: 'code' submittedCodeLanguage: type: 'string' @@ -175,6 +176,7 @@ _.extend LevelSessionSchema.properties, type: 'object' additionalProperties: type: 'string' + format: 'code' isRanking: type: 'boolean' diff --git a/app/views/play/common/LadderSubmissionView.coffee b/app/views/play/common/LadderSubmissionView.coffee index 4cf4b7e29..580afba8a 100644 --- a/app/views/play/common/LadderSubmissionView.coffee +++ b/app/views/play/common/LadderSubmissionView.coffee @@ -1,5 +1,6 @@ CocoView = require 'views/kinds/CocoView' template = require 'templates/play/common/ladder_submission' +{createAetherOptions} = require 'lib/aether_utils' module.exports = class LadderSubmissionView extends CocoView className: 'ladder-submission-view' @@ -72,29 +73,15 @@ module.exports = class LadderSubmissionView extends CocoView transpileSession: -> submittedCode = @session.get('code') - language = @session.get('codeLanguage') or 'javascript' - @session.set('submittedCodeLanguage', language) + codeLanguage = @session.get('codeLanguage') or 'javascript' + @session.set('submittedCodeLanguage', codeLanguage) @session.save() # TODO: maybe actually use a callback to make sure this works? transpiledCode = {} for thang, spells of submittedCode transpiledCode[thang] = {} for spellID, spell of spells unless _.contains(@session.get('teamSpells')[@session.get('team')], thang + '/' + spellID) then continue - #DRY this - aetherOptions = - problems: {} - language: language - functionName: spellID - functionParameters: [] - yieldConditionally: spellID is 'plan' - globals: ['Vector', '_'] - protectAPI: true - includeFlow: false - executionLimit: 1 * 1000 * 1000 - if spellID is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] - if spellID is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] - if spellID is 'findCentroids' then aetherOptions.functionParameters = ['centroids'] - + aetherOptions = createAetherOptions functionName: spellID, codeLanguage: codeLanguage aether = new Aether aetherOptions transpiledCode[thang][spellID] = aether.transpile spell transpiledCode diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee index ff2dc39e6..5d56e131d 100644 --- a/app/views/play/level/tome/Spell.coffee +++ b/app/views/play/level/tome/Spell.coffee @@ -1,9 +1,7 @@ SpellView = require './SpellView' SpellListTabEntryView = require './SpellListTabEntryView' {me} = require 'lib/auth' - -Aether.addGlobal 'Vector', require 'lib/world/vector' -Aether.addGlobal '_', _ +{createAetherOptions} = require 'lib/aether_utils' module.exports = class Spell loaded: false @@ -125,25 +123,8 @@ module.exports = class Spell createAether: (thang) -> aceConfig = me.get('aceConfig') ? {} writable = @permissions.readwrite.length > 0 - aetherOptions = - problems: - jshint_W040: {level: 'ignore'} - jshint_W030: {level: 'ignore'} # aether_NoEffect instead - jshint_W038: {level: 'ignore'} # eliminates hoisting problems - jshint_W091: {level: 'ignore'} # eliminates more hoisting problems - jshint_E043: {level: 'ignore'} # https://github.com/codecombat/codecombat/issues/813 -- since we can't actually tell JSHint to really ignore things - jshint_Unknown: {level: 'ignore'} # E043 also triggers Unknown, so ignore that, too - aether_MissingThis: {level: 'error'} - language: @language - functionName: @name - functionParameters: @parameters - yieldConditionally: thang.plan? - globals: ['Vector', '_'] - # TODO: Gridmancer doesn't currently work with protectAPI, so hack it off - protectAPI: not (@skipProtectAPI or window.currentView?.level.get('name').match('Gridmancer')) and writable # If anyone can write to this method, we must protect it. - includeFlow: false - executionLimit: 1 * 1000 * 1000 - #console.log 'creating aether with options', aetherOptions + skipProtectAPI = @skipProtectAPI or not writable + aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI aether = new Aether aetherOptions workerMessage = function: 'createAether' diff --git a/scripts/transpile.coffee b/scripts/transpile.coffee index 5c39b414b..53e66e43a 100644 --- a/scripts/transpile.coffee +++ b/scripts/transpile.coffee @@ -8,9 +8,8 @@ async = require 'async' serverSetup = require '../server_setup' Level = require '../server/levels/Level' LevelSession = require '../server/levels/sessions/LevelSession' +{createAetherOptions} = require '../app/lib/aether_utils' -Aether.addGlobal 'Vector', require '../app/lib/world/vector' -Aether.addGlobal '_', _ i = 0 transpileLevelSession = (sessionID, cb) -> query = LevelSession.findOne('_id': sessionID).select('team teamSpells submittedCode submittedCodeLanguage').lean() @@ -27,23 +26,9 @@ transpileLevelSession = (sessionID, cb) -> transpiledCode[thang] = {} for spellID, spell of spells spellName = thang + '/' + spellID - - if session.teamSpells and not (spellName in session.teamSpells[session.team]) then continue + continue if session.teamSpells and not (spellName in session.teamSpells[session.team]) #console.log "Transpiling spell #{spellName}" - aetherOptions = - problems: {} - language: session.submittedCodeLanguage - functionName: spellID - functionParameters: [] - yieldConditionally: spellID is 'plan' - globals: ['Vector', '_'] - protectAPI: true - includeFlow: false - executionLimit: 1 * 1000 * 1000 - if spellID is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] - if spellID is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] - if spellID is 'findCentroids' then aetherOptions.functionParameters = ['centroids'] - + aetherOptions = createAetherOptions functionName: spellID, codeLanguage: session.submittedCodeLanguage aether = new Aether aetherOptions transpiledCode[thang][spellID] = aether.transpile spell conditions = From a8644d030edc090416d2b64330841f7d353b0afc Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 14:30:53 -0700 Subject: [PATCH 66/98] Fixed #1355. --- app/lib/LevelLoader.coffee | 1 + app/templates/editor/level/edit.jade | 4 +++- app/views/editor/level/LevelEditView.coffee | 4 ++++ app/views/play/level/PlayLevelView.coffee | 15 +++++++++++---- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index cfdb46ae9..b412abf74 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -243,6 +243,7 @@ module.exports = class LevelLoader extends CocoClass resource = null for resource in @spriteSheetsToBuild break if e.thangType is resource.thangType + return console.error 'Did not find spriteSheetToBuildResource for', e unless resource resource.spriteSheetKeys = (k for k in resource.spriteSheetKeys when k isnt e.key) resource.markLoaded() if resource.spriteSheetKeys.length is 0 diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index 5def3a60b..3405164bd 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -64,7 +64,9 @@ block header li.dropdown-header Play As Which Team? li for team in ['humans', 'ogres'] - a.play-with-team-button(data-team=team)= team + a.play-with-team-button(data-team=team)= team + ' vs. AI' + for match in recentlyPlayedOpponents + a.play-with-team-button(data-team=match.yourTeam, data-opponent=match.opponentSessionID)= match.yourTeam + ' vs. ' + match.opponentName else li(title="⌃↩ or ⌘↩: Play preview of current level")#play-button diff --git a/app/views/editor/level/LevelEditView.coffee b/app/views/editor/level/LevelEditView.coffee index c0cfaef66..2df56a719 100644 --- a/app/views/editor/level/LevelEditView.coffee +++ b/app/views/editor/level/LevelEditView.coffee @@ -19,6 +19,7 @@ RelatedAchievementsView = require 'views/editor/level/RelatedAchievementsView' VersionHistoryView = require './modals/LevelVersionsModal' ComponentsDocumentationView = require 'views/docs/ComponentsDocumentationView' SystemsDocumentationView = require 'views/docs/SystemsDocumentationView' +storage = require 'lib/storage' module.exports = class LevelEditView extends RootView id: 'editor-level-view' @@ -68,6 +69,7 @@ module.exports = class LevelEditView extends RootView context.level = @level context.authorized = me.isAdmin() or @level.hasWriteAccess(me) context.anonymous = me.get('anonymous') + context.recentlyPlayedOpponents = storage.load('recently-played-matches')?[@levelID] ? [] context afterRender: -> @@ -98,6 +100,7 @@ module.exports = class LevelEditView extends RootView onPlayLevel: (e) -> team = $(e.target).data('team') + opponentSessionID = $(e.target).data('opponent') sendLevel = => @childWindow.Backbone.Mediator.publish 'level:reload-from-data', level: @level, supermodel: @supermodel if @childWindow and not @childWindow.closed @@ -107,6 +110,7 @@ module.exports = class LevelEditView extends RootView # Create a new Window with a blank LevelView scratchLevelID = @level.get('slug') + '?dev=true' scratchLevelID += "&team=#{team}" if team + scratchLevelID += "&opponent=#{opponentSessionID}" if opponentSessionID if me.get('name') is 'Nick' @childWindow = window.open("/play/level/#{scratchLevelID}", 'child_window', 'width=2560,height=1080,left=0,top=-1600,location=1,menubar=1,scrollbars=1,status=0,titlebar=1,toolbar=1', true) else diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index acb6aa82f..2cd3e1479 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -3,9 +3,7 @@ template = require 'templates/play/level' {me} = require 'lib/auth' ThangType = require 'models/ThangType' utils = require 'lib/utils' - -# temp hard coded data -World = require 'lib/world/world' +storage = require 'lib/storage' # tools Surface = require 'lib/surface/Surface' @@ -280,14 +278,23 @@ module.exports = class PlayLevelView extends RootView # Everything is now loaded return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early - # Save latest level played in local storage + # Save latest level played. if not (@levelLoader.level.get('type') in ['ladder', 'ladder-tutorial']) me.set('lastLevel', @levelID) me.save() + @saveRecentMatch() if @otherSession @levelLoader.destroy() @levelLoader = null @initSurface() + saveRecentMatch: -> + allRecentlyPlayedMatches = storage.load('recently-played-matches') ? {} + recentlyPlayedMatches = allRecentlyPlayedMatches[@levelID] ? [] + allRecentlyPlayedMatches[@levelID] = recentlyPlayedMatches + recentlyPlayedMatches.unshift yourTeam: me.team, otherSessionID: @otherSession.id, opponentName: @otherSession.get('creatorName') unless _.find recentlyPlayedMatches, otherSessionID: @otherSession.id + recentlyPlayedMatches.splice(8) + storage.save 'recently-played-matches', allRecentlyPlayedMatches + initSurface: -> surfaceCanvas = $('canvas#surface', @$el) @surface = new Surface(@world, surfaceCanvas, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview) From 4d24233b0d66b5497274c681b5b6df10f6e477bf Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 15:19:42 -0700 Subject: [PATCH 67/98] Fixed #1356. --- server/routes/mail.coffee | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/server/routes/mail.coffee b/server/routes/mail.coffee index 4d905b4d1..38c159bab 100644 --- a/server/routes/mail.coffee +++ b/server/routes/mail.coffee @@ -139,7 +139,7 @@ sendReminderEmailToCandidate = (candidate, sendEmailCallback) -> log.error "Error sending candidate update reminder email: #{err} with result #{result}" if err sendEmailCallback null ### End Approved Candidate Update Reminder Task ### - + ### Unapproved Candidate Finish Reminder Task ### unapprovedCandidateFinishProfileTask = -> mailTaskName = "unapprovedCandidateFinishProfileTask" @@ -187,13 +187,13 @@ findAllUnapprovedCandidatesWithinTimeRange = (cb) -> User.find(findParameters).select(selection).lean().exec cb ignoredCandidateFilter = (candidate, cb) -> - findParameters = + findParameters = "user": candidate._id "contactName": "Ignore" UserRemark.count findParameters, (err, results) -> if err? then return true return cb Boolean(results.length) - + unapprovedCandidateFilter = (candidate, sentEmailFilterCallback) -> if candidate.emails?.anyNotes?.enabled is false or candidate.emails?.recruitNotes?.enabled is false return sentEmailFilterCallback true @@ -234,7 +234,7 @@ sendReminderEmailToUnapprovedCandidate = (candidate, sendEmailCallback) -> log.error "Error sending candidate finish profile reminder email: #{err} with result #{result}" if err sendEmailCallback null ### End Unapproved Candidate Finish Reminder Task ### - + ### Internal Candidate Update Reminder Email ### internalCandidateUpdateTask = -> mailTaskName = "internalCandidateUpdateTask" @@ -408,7 +408,7 @@ sendEmployerNewCandidatesAvailableEmail = (employer, cb) -> cb null ### End Employer New Candidates Available Email ### - + ### Task Emails ### emailUserRemarkTaskRemindersTask = -> mailTaskName = "emailUserRemarkTaskRemindersTask" @@ -428,7 +428,7 @@ emailUserRemarkTaskReminders = (cb) -> asyncContext = "currentTime": currentTime "mailTaskName": @mailTaskName - + async.waterfall [ findAllIncompleteUserRemarkTasksDue.bind(asyncContext) processRemarksIntoTasks.bind(asyncContext) @@ -437,9 +437,9 @@ emailUserRemarkTaskReminders = (cb) -> (tasksToRemind, cb) -> async.each tasksToRemind, sendUserRemarkTaskEmail.bind(asyncContext), cb ], cb - + findAllIncompleteUserRemarkTasksDue = (cb) -> - findParameters = + findParameters = tasks: $exists: true $elemMatch: @@ -449,12 +449,12 @@ findAllIncompleteUserRemarkTasksDue = (cb) -> $ne: 'Completed' selection = "contact user tasks" UserRemark.find(findParameters).select(selection).lean().exec cb - + processRemarksIntoTasks = (remarks, cb) -> tasks = [] for remark in remarks for task in remark.tasks - taskObject = + taskObject = date: task.date action: task.action contact: remark.contact @@ -476,7 +476,7 @@ taskReminderAlreadySentThisWeekFilter = (task, cb) -> MailSent.count findParameters, (err, count) -> if err? then return cb true return cb Boolean(count) - + sendUserRemarkTaskEmail = (task, cb) -> mailTaskName = @mailTaskName User.findOne("_id":task.contact).select("email").lean().exec (err, contact) -> @@ -548,8 +548,8 @@ isRequestFromDesignatedCronHandler = (req, res) -> handleLadderUpdate = (req, res) -> log.info('Going to see about sending ladder update emails.') - requestIsFromDesignatedCronHandler = isRequestFromDesignatedCronHandler req, res - return unless requestIsFromDesignatedCronHandler or DEBUGGING + requestIsFromDesignatedCronHandler = DEBUGGING or isRequestFromDesignatedCronHandler req, res + return unless requestIsFromDesignatedCronHandler res.send('Great work, Captain Cron! I can take it from here.') res.end() @@ -564,7 +564,7 @@ handleLadderUpdate = (req, res) -> endTime = startTime + 15 * 60 * 1000 # Debugging: make sure there's something to send findParameters = {submitted: true, submitDate: {$gt: new Date(startTime), $lte: new Date(endTime)}} # TODO: think about putting screenshots in the email - selectString = 'creator team levelName levelID totalScore matches submitted submitDate scoreHistory' + selectString = 'creator team levelName levelID totalScore matches submitted submitDate scoreHistory level.original' query = LevelSession.find(findParameters) .select(selectString) .lean() @@ -600,7 +600,7 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> defeat = _.last defeats victory = _.last victories - sendEmail = (defeatContext, victoryContext) -> + sendEmail = (defeatContext, victoryContext, levelVersionsContext) -> # TODO: do something with the preferredLanguage? context = email_id: sendwithus.templates.ladder_update_email @@ -621,6 +621,7 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> score_history_graph_url: getScoreHistoryGraphURL session, daysAgo defeat: defeatContext victory: victoryContext + levelVersions: levelVersionsContext log.info "Sending ladder update email to #{context.recipient.address} with #{context.email_data.wins} wins and #{context.email_data.losses} losses since #{daysAgo} day(s) ago." sendwithus.api.send context, (err, result) -> log.error "Error sending ladder update email: #{err} with result #{result}" if err @@ -639,7 +640,9 @@ sendLadderUpdateEmail = (session, now, daysAgo) -> log.error "Couldn't find victorious opponent: #{err}" victoriousOpponent = null defeatContext = {opponent_name: victoriousOpponent?.name ? 'Anoner', url: urlForMatch(defeat)} if defeat - sendEmail defeatContext, victoryContext + + Level.find({original: session.level.original, created: {$gt: session.submitDate}}).select('created commitMessage version').sort('-created').lean().exec (err, levelVersions) -> + sendEmail defeatContext, victoryContext, (if levelVersions.length then levelVersions else null) if defeat User.findOne({_id: defeat.opponents[0].userID}).select('name').lean().exec onFetchedVictoriousOpponent From 24d3d189dfe438706fda3ccaf445447001e98845 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 17:09:57 -0700 Subject: [PATCH 68/98] Made espionage mode simpler, and added an admin user lookup. --- app/templates/admin.jade | 28 +++++++++++-------- app/views/admin/MainAdminView.coffee | 42 ++++++++++++++++++++++------ server/routes/auth.coffee | 23 +++++---------- server/users/user_handler.coffee | 23 ++++++++++++++- 4 files changed, 79 insertions(+), 37 deletions(-) diff --git a/app/templates/admin.jade b/app/templates/admin.jade index 79061e10d..1ba14abab 100644 --- a/app/templates/admin.jade +++ b/app/templates/admin.jade @@ -2,17 +2,23 @@ extends /templates/base block content - h3 Espionage mode - h5 Please enter the email/username of the person you want to spy on - .form - .form-group - label.control-label Email - input#user-email - .form-group - label.control-label Username - input#user-username - - button.btn.btn-primary.btn-large#enter-espionage-mode 007 + .form-horizontal + .form-group + label.control-label.col-sm-2(for="espionage-name-or-email") Espionage + .col-sm-4 + input.form-control#espionage-name-or-email(placeholder="Email or username", type="text") + .col-sm-1 + button.btn.btn-primary.btn-large#enter-espionage-mode 007 + label.control-label.col-sm-5(for="espionage-name-or-email") + em you are currently #{me.get('name')} at #{me.get('email')} + .form-group + label.control-label.col-sm-2(for="user-search") User Search + .col-sm-4 + input.form-control#user-search(placeholder="Email, username, name, whatever", type="text") + .col-sm-1 + button.btn.btn-primary.btn-large#user-search-button Search + #user-search-result + h3(data-i18n="admin.av_title") Admin Views diff --git a/app/views/admin/MainAdminView.coffee b/app/views/admin/MainAdminView.coffee index 33bed066e..62c5d1ed5 100644 --- a/app/views/admin/MainAdminView.coffee +++ b/app/views/admin/MainAdminView.coffee @@ -5,32 +5,56 @@ template = require 'templates/admin' module.exports = class MainAdminView extends RootView id: 'admin-view' template: template + lastUserSearchValue: '' events: + 'keyup': 'checkForFormSubmissionEnterPress' 'click #enter-espionage-mode': 'enterEspionageMode' + 'click #user-search-button': 'searchForUser' 'click #increment-button': 'incrementUserAttribute' + checkForFormSubmissionEnterPress: (e) -> + if e.which is 13 and @$el.find('#espionage-name-or-email').val() isnt '' + @enterEspionageMode() + return + if @$el.find('#user-search').val() isnt @lastUserSearchValue + @searchForUser() + enterEspionageMode: -> - userEmail = $('#user-email').val().toLowerCase() - username = $('#user-username').val().toLowerCase() - - postData = - usernameLower: username - emailLower: userEmail - + userNameOrEmail = @$el.find('#espionage-name-or-email').val().toLowerCase() $.ajax type: 'POST', url: '/auth/spy' - data: postData + data: {nameOrEmailLower: userNameOrEmail} success: @espionageSuccess error: @espionageFailure espionageSuccess: (model) -> window.location.reload() - espionageFailure: (jqxhr, status,error)-> + espionageFailure: (jqxhr, status, error)-> console.log "There was an error entering espionage mode: #{error}" + searchForUser: -> + return @onSearchRequestSuccess [] unless @lastUserSearchValue = @$el.find('#user-search').val().toLowerCase() + $.ajax + type: 'POST', + url: '/db/user/-/admin_search' + data: {search: @lastUserSearchValue} + success: @onSearchRequestSuccess + error: @onSearchRequestFailure + + onSearchRequestSuccess: (users) => + result = '' + if users.length + result = ("#{user._id}#{_.escape(user.name or 'Anoner')}#{_.escape(user.email)}" for user in users) + result = "#{result.join('\n')}
" + @$el.find('#user-search-result').html(result) + + onSearchRequestFailure: (jqxhr, status, error) => + return if @destroyed + console.warn "There was an error looking up #{@lastUserSearchValue}:", error + incrementUserAttribute: (e) -> val = $('#increment-field').val() me.set(val, me.get(val) + 1) diff --git a/server/routes/auth.coffee b/server/routes/auth.coffee index 5bb1cf1a2..666954496 100644 --- a/server/routes/auth.coffee +++ b/server/routes/auth.coffee @@ -32,23 +32,14 @@ module.exports.setup = (app) -> app.post '/auth/spy', (req, res, next) -> if req?.user?.isAdmin() - - username = req.body.usernameLower - emailLower = req.body.emailLower - if emailLower - query = {'emailLower': emailLower} - else if username - query = {'nameLower': username} - else - return errors.badInput res, 'You need to supply one of emailLower or username' - + target = req.body.nameOrEmailLower + return errors.badInput res, 'Specify a username or email to espionage.' unless target + query = $or: [{nameLower: target}, {emailLower: target}] User.findOne query, (err, user) -> if err? then return errors.serverError res, 'There was an error finding the specified user' - unless user then return errors.badInput res, 'The specified user couldn\'t be found' - req.logIn user, (err) -> - if err? then return errors.serverError res, 'There was an error logging in with the specified' + if err? then return errors.serverError res, 'There was an error logging in with the specified user' res.send(UserHandler.formatEntity(req, user)) return res.end() else @@ -117,7 +108,7 @@ module.exports.setup = (app) -> ) ) - app.get '/auth/unsubscribe', (req, res) -> + app.get '/auth/unsubscribe', (req, res) -> req.query.email = decodeURIComponent(req.query.email) email = req.query.email unless req.query.email @@ -132,7 +123,7 @@ module.exports.setup = (app) -> return errors.serverError res, 'Database failure.' if err res.send "Unsubscribed #{req.query.email} from CodeCombat emails for #{session.levelName} #{session.team} ladder updates. Sorry to see you go!

Ladder preferences

" res.end() - + User.findOne({emailLower: req.query.email.toLowerCase()}).exec (err, user) -> if not user return errors.notFound res, "No user found with email '#{req.query.email}'" @@ -147,7 +138,7 @@ module.exports.setup = (app) -> else if req.query.employerNotes emails.employerNotes ?= {} emails.employerNotes.enabled = false - + msg = "Unsubscribed #{req.query.email} from employer emails." else msg = "Unsubscribed #{req.query.email} from all CodeCombat emails. Sorry to see you go!" diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index e90f50306..f3d40b649 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -13,6 +13,7 @@ LevelSession = require '../levels/sessions/LevelSession' LevelSessionHandler = require '../levels/sessions/level_session_handler' EarnedAchievement = require '../achievements/EarnedAchievement' UserRemark = require './remarks/UserRemark' +{isID} = require '../lib/utils' serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset'] candidateProperties = [ @@ -187,6 +188,7 @@ UserHandler = class UserHandler extends Handler return @getRecentlyPlayed(req, res, args[0]) if args[1] is 'recently_played' return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2] return @getRemark(req, res, args[0]) if args[1] is 'remark' + return @searchForUser(req, res) if args[1] is 'admin_search' return @sendNotFoundError(res) super(arguments...) @@ -388,6 +390,25 @@ UserHandler = class UserHandler extends Handler return @sendNotFoundError res unless remark? @sendSuccess res, remark + searchForUser: (req, res) -> + # TODO: also somehow search the CLAs to find a match amongst those fields and to find GitHub ids + return @sendUnauthorizedError(res) unless req.user.isAdmin() + search = req.body.search + query = email: {$exists: true}, $or: [ + {emailLower: search} + {nameLower: search} + ] + query.$or.push {_id: mongoose.Types.ObjectId(search) if isID search} + if search.length > 5 + searchParts = search.split(/[.+@]/) + if searchParts.length > 1 + query.$or.push {emailLower: {$regex: '^' + searchParts[0]}} + projection = name: 1, email: 1, dateCreated: 1 + User.find(query).select(projection).lean().exec (err, users) => + return @sendDatabaseError res, err if err + @sendSuccess res, users + + countEdits = (model, done) -> statKey = User.statsMapping.edits[model.modelName] return done(new Error 'Could not resolve statKey for model') unless statKey? @@ -578,7 +599,7 @@ UserHandler = class UserHandler extends Handler thangTypeTranslationPatches: (done) -> countPatchesByUsersInMemory {'target.collection': 'thang_type'}, isTranslationPatch, User.statsMapping.translations['thang.type'], done - + recalculateStats: (statName, done) => done new Error 'Recalculation handler not found' unless statName of @statRecalculators @statRecalculators[statName] done From 6549b4ccc0ffeba14e5a17fd0618415bf57f5946 Mon Sep 17 00:00:00 2001 From: George Saines Date: Sat, 30 Aug 2014 18:51:02 -0700 Subject: [PATCH 69/98] updating the diplomat list --- app/views/contribute/DiplomatView.coffee | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/app/views/contribute/DiplomatView.coffee b/app/views/contribute/DiplomatView.coffee index 1f075803f..214e36253 100644 --- a/app/views/contribute/DiplomatView.coffee +++ b/app/views/contribute/DiplomatView.coffee @@ -39,54 +39,54 @@ module.exports = class DiplomatView extends ContributeClassView 'en-US': [] # English (US), English (US) 'en-GB': [] # English (UK), English (UK) 'en-AU': [] # English (AU), English (AU) - ru: ['fess89', 'ser-storchak', 'Mr A', 'a1ip'] # русский язык, Russian - de: ['Dirk', 'faabsen', 'HiroP0', 'Anon', 'bkimminich'] # Deutsch, German + ru: ['fess89', 'ser-storchak', 'Mr A', 'a1ip', 'iulianR', 'EagleTA', 'kisik21', 'Shpionus', 'kerradus', 'ImmortalJoker'] # русский язык, Russian + de: ['Dirk', 'faabsen', 'HiroP0', 'Anon', 'bkimminich', 'bahuma20', 'djsmith85', 'greyhusky'] # Deutsch, German 'de-DE': [] # Deutsch (Deutschland), German (Germany) 'de-AT': [] # Deutsch (Österreich), German (Austria) 'de-CH': [] # Deutsch (Schweiz), German (Switzerland) es: [] # español, Spanish - 'es-419': ['Jesús Ruppel', 'Matthew Burt', 'Mariano Luzza'] # español (América Latina), Spanish (Latin America) - 'es-ES': ['Matthew Burt', 'DanielRodriguezRivero', 'Anon', 'Pouyio'] # español (ES), Spanish (Spain) - zh: ['Adam23', 'spacepope', 'yangxuan8282', 'Cheng Zheng'] # 中文, Chinese + 'es-419': ['Jesús Ruppel', 'Matthew Burt', 'Mariano Luzza', '2xG', ] # español (América Latina), Spanish (Latin America) + 'es-ES': ['Matthew Burt', 'DanielRodriguezRivero', 'Anon', 'Pouyio', '3rr3s3v3n', 'OviiiOne', 'Vindurrin'] # español (ES), Spanish (Spain) + zh: ['Adam23', 'spacepope', 'yangxuan8282', 'Cheng Zheng', 'yfdyh000', 'julycoolwind', 'Vic020', 'onion7878', 'BonnieBBS', '1c7', 'benojan', 'ZephyrSails'] # 中文, Chinese 'zh-HANS': [] # 简体中文, Chinese (Simplified) 'zh-HANT': [] # 繁体中文, Chinese (Traditional) 'zh-WUU-HANS': [] # 吴语, Wuu (Simplified) 'zh-WUU-HANT': [] # 吳語, Wuu (Traditional) - fr: ['Xeonarno', 'Elfisen', 'Armaldio', 'MartinDelille', 'pstweb', 'veritable', 'jaybi', 'xavismeh', 'Anon', 'Feugy'] # français, French + fr: ['Xeonarno', 'Elfisen', 'Armaldio', 'MartinDelille', 'pstweb', 'veritable', 'jaybi', 'xavismeh', 'Anon', 'Feugy', 'dc55028', 'ChrisLightman', 'Oaugereau'] # français, French ja: ['g1itch', 'kengos', 'treby'] # 日本語, Japanese - ar: [] # العربية, Arabic + ar: ['ahmed80dz', '5y'] # العربية, Arabic pt: [] # português, Portuguese - 'pt-BR': ['Gutenberg Barros', 'Kieizroe', 'Matthew Burt', 'brunoporto', 'cassiocardoso'] # português do Brasil, Portuguese (Brazil) - 'pt-PT': ['Matthew Burt', 'ReiDuKuduro', 'Imperadeiro98'] # Português (Portugal), Portuguese (Portugal) - pl: ['Anon', 'Kacper Ciepielewski'] # język polski, Polish - it: ['flauta'] # italiano, Italian - tr: ['Nazım Gediz Aydındoğmuş', 'cobaimelan', 'wakeup'] # Türkçe, Turkish + 'pt-BR': ['Gutenberg Barros', 'Kieizroe', 'Matthew Burt', 'brunoporto', 'cassiocardoso', 'Bia41'] # português do Brasil, Portuguese (Brazil) + 'pt-PT': ['Matthew Burt', 'ReiDuKuduro', 'Imperadeiro98', 'batista', 'ProgramadorLucas', 'gutierri'] # Português (Portugal), Portuguese (Portugal) + pl: ['Anon', 'Kacper Ciepielewski', 'TigroTigro', 'kvasnyk'] # język polski, Polish + it: ['flauta', 'AlessioPaternoster'] # italiano, Italian + tr: ['Nazım Gediz Aydındoğmuş', 'cobaimelan', 'wakeup', 'gediz', 'ilisyus'] # Türkçe, Turkish nl: ['Glen De Cauwsemaecker', 'Guido Zuidhof', 'Ruben Vereecken', 'Jasper D\'haene'] # Nederlands, Dutch 'nl-BE': [] # Nederlands (België), Dutch (Belgium) 'nl-NL': [] # Nederlands (Nederland), Dutch (Netherlands) fa: ['Reza Habibi (Rehb)'] # فارسی, Persian cs: ['vanous'] # čeština, Czech - sv: [] # Svenska, Swedish - id: [] # Bahasa Indonesia, Indonesian + sv: ['iamhj'] # Svenska, Swedish + id: ['mlewisno-oberlin'] # Bahasa Indonesia, Indonesian el: ['Stergios'] # ελληνικά, Greek ro: [] # limba română, Romanian vi: ['An Nguyen Hoang Thien'] # Tiếng Việt, Vietnamese - hu: ['ferpeter', 'csuvsaregal', 'atlantisguru', 'Anon'] # magyar, Hungarian + hu: ['ferpeter', 'csuvsaregal', 'atlantisguru', 'Anon', 'kinez', 'bbeasmile'] # magyar, Hungarian th: ['Kamolchanok Jittrepit'] # ไทย, Thai - da: ['Einar Rasmussen', 'sorsjen', 'Randi Hillerøe', 'Anon'] # dansk, Danish - ko: [] # 한국어, Korean + da: ['Einar Rasmussen', 'sorsjen', 'Randi Hillerøe', 'Anon', 'Silwing', 'Rahazan', 'marc-portier'] # dansk, Danish + ko: ['Melondonut'] # 한국어, Korean sk: ['Anon'] # slovenčina, Slovak sl: [] # slovenščina, Slovene fi: [] # suomi, Finnish bg: [] # български език, Bulgarian - no: ['bardeh'] # Norsk, Norwegian + no: ['bardeh', 'torehaug'] # Norsk, Norwegian nn: [] # Norwegian (Nynorsk), Norwegian Nynorsk nb: [] # Norsk Bokmål, Norwegian (Bokmål) - he: [] # עברית, Hebrew + he: ['OverProgram', 'monetita'] # עברית, Hebrew lt: [] # lietuvių kalba, Lithuanian sr: [] # српски, Serbian - uk: ['fess89'] # українська мова, Ukrainian + uk: ['fess89', 'ImmortalJoker', 'gorodsb', 'endrilian', 'OlenaGapak'] # українська мова, Ukrainian hi: [] # मानक हिन्दी, Hindi ur: [] # اُردُو, Urdu ms: [] # Bahasa Melayu, Bahasa Malaysia - ca: [] # Català, Catalan + ca: ['ArniMcFrag'] # Català, Catalan From 77e7efebebed635af54ea87b0fbefdfb9ece89d5 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 19:30:33 -0700 Subject: [PATCH 70/98] Fixed #1385: no more redundant fallback localization files. --- app/locale/de.coffee | 999 ----------------------- app/locale/es.coffee | 999 ----------------------- app/locale/locale.coffee | 5 - app/locale/nl-NL.coffee | 72 +- app/locale/nl.coffee | 999 ----------------------- app/locale/pt.coffee | 999 ----------------------- app/locale/zh.coffee | 999 ----------------------- app/templates/contribute/diplomat.jade | 2 +- app/views/contribute/DiplomatView.coffee | 21 +- server/routes/languages.coffee | 6 + 10 files changed, 51 insertions(+), 5050 deletions(-) delete mode 100644 app/locale/de.coffee delete mode 100644 app/locale/es.coffee delete mode 100644 app/locale/nl.coffee delete mode 100644 app/locale/pt.coffee delete mode 100644 app/locale/zh.coffee diff --git a/app/locale/de.coffee b/app/locale/de.coffee deleted file mode 100644 index 3d9fcf287..000000000 --- a/app/locale/de.coffee +++ /dev/null @@ -1,999 +0,0 @@ -module.exports = nativeDescription: "Deutsch", englishDescription: "German", translation: - common: - loading: "Lade..." - saving: "Speichere..." - sending: "Übertrage..." - send: "Senden" - cancel: "Abbrechen" - save: "Speichern" - publish: "Publiziere" - create: "Erstelle" - delay_1_sec: "1 Sekunde" - delay_3_sec: "3 Sekunden" - delay_5_sec: "5 Sekunden" - manual: "Manuell" - fork: "Fork" - play: "Abspielen" - retry: "Erneut versuchen" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" - - units: - second: "Sekunde" - seconds: "Sekunden" - minute: "Minute" - minutes: "Minuten" - hour: "Stunde" - hours: "Stunden" - day: "Tag" - days: "Tage" - week: "Woche" - weeks: "Wochen" - month: "Monat" - months: "Monate" - year: "Jahr" - years: "Jahre" - - modal: - close: "Schließen" - okay: "Okay" - - not_found: - page_not_found: "Seite nicht gefunden" - - nav: - play: "Spielen" -# community: "Community" - editor: "Editor" - blog: "Blog" - forum: "Forum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" - admin: "Administration" - home: "Home" - contribute: "Helfen" - legal: "Rechtliches" - about: "Über" - contact: "Kontakt" - twitter_follow: "Twitter" - employers: "Mitarbeiter" - - versions: - save_version_title: "Neue Version speichern" - new_major_version: "Neue Hauptversion" - cla_prefix: "Damit Änderungen gespeichert werden können, musst du unsere Lizenzbedingungen (" - cla_url: "CLA" - cla_suffix: ") akzeptieren." - cla_agree: "Ich stimme zu" - - login: - sign_up: "Registrieren" - log_in: "Einloggen" - logging_in: "Logge ein" - log_out: "Ausloggen" - recover: "Account wiederherstellen" - - recover: - recover_account_title: "Account Wiederherstellung" - send_password: "Wiederherstellungskennwort senden" - - signup: - create_account_title: "Account anlegen, um Fortschritt zu speichern" - description: "Es ist kostenlos. Nur noch ein paar Dinge, dann kannst Du loslegen." - email_announcements: "Erhalte Benachrichtigungen per Email" - coppa: "Älter als 13 oder nicht aus den USA" - coppa_why: "(Warum?)" - creating: "Erzeuge Account..." - sign_up: "Neuen Account anlegen" - log_in: "mit Passwort einloggen" - social_signup: "oder, du registriest dich über Facebook oder G+:" -# required: "You need to log in before you can go that way." - - home: - slogan: "Lerne spielend Programmieren" - no_ie: "CodeCombat läuft nicht im IE8 oder älteren Browsern. Tut uns Leid!" - no_mobile: "CodeCombat ist nicht für Mobilgeräte optimiert und funktioniert möglicherweise nicht." - play: "Spielen" - old_browser: "Oh! Dein Browser ist zu alt für CodeCombat. Sorry!" - old_browser_suffix: "Du kannst es trotzdem versuchen, aber es wird wahrscheinlich nicht funktionieren." - campaign: "Kampagne" - for_beginners: "Für Anfänger" - multiplayer: "Mehrspieler" - for_developers: "Für Entwickler" - javascript_blurb: "Die Sprache des Web. Geeignet für die Erstellung von Webseiten, WebApps, HTML5 Spielen und Servern.." -# python_blurb: "Simple yet powerful, Python is a great general purpose programming language." - coffeescript_blurb: "Schönere JavaScript Syntax." - clojure_blurb: "Ein modernes Lisp." - lua_blurb: "Skriptsprache für Spiele." -# io_blurb: "Simple but obscure." - - play: - choose_your_level: "Wähle dein Level" - adventurer_prefix: "Du kannst zu jedem Level springen oder diskutiere die Level " - adventurer_forum: "im Abenteurerforum" - adventurer_suffix: "." - campaign_beginner: "Anfängerkampagne" - campaign_beginner_description: "... in der Du die Zauberei der Programmierung lernst." - campaign_dev: "Beliebiges schwierigeres Level" - campaign_dev_description: "... in welchem Du die Bedienung erlernst, indem Du etwas schwierigeres machst." - campaign_multiplayer: "Multiplayerarena" - campaign_multiplayer_description: "... in der Du Kopf-an-Kopf gegen andere Spieler programmierst." - campaign_player_created: "Von Spielern erstellt" - campaign_player_created_description: "... in welchem Du gegen die Kreativität eines Artisan Zauberers kämpfst." - level_difficulty: "Schwierigkeit: " - play_as: "Spiele als " - spectate: "Zuschauen" -# players: "players" -# hours_played: "hours played" - - contact: - contact_us: "Kontaktiere CodeCombat" - welcome: "Schön von Dir zu hören! Benutze dieses Formular um uns eine Email zu schicken." - contribute_prefix: "Wenn Du Interesse hast, uns zu unterstützen dann sieh dir die " - contribute_page: "Unterstützer Seite" - contribute_suffix: " an!" - forum_prefix: "Für alle öffentlichen Themen, benutze stattdessen " - forum_page: "unser Forum" - forum_suffix: "." - send: "Sende Feedback" - contact_candidate: "Kontaktiere Kandidaten" - recruitment_reminder: "Benutzen Sie dieses Formular um Kontakt zu Kandidaten aufzunehmen, an denen Sie interessiert sind. Bedenken Sie das CodeCombat 15% des ersten Jahresgehaltes berechnet. Diese Gebühr wird fällig wenn Sie den Kandidaten einstellen und ist für 90 Tage rückerstattungsfähig, sollte der Mitarbeiter nicht eingestellt bleiben. Mitarbeiter die für Teilzeit, Remote oder eine Auftragsarbeit eingestellt werden sind kostenlos, das gilt auch für Praktikanten." - - diplomat_suggestion: - title: "Hilf CodeCombat zu übersetzen!" - sub_heading: "Wir brauchen Deine Sprachfähigkeiten." - pitch_body: "Wir entwickeln CodeCombat in Englisch, aber wir haben Spieler in der ganzen Welt. Viele von ihnen wollen in Deutsch spielen, sprechen aber kein Englisch. Wenn Du also beide Sprachen beherrscht, melde Dich an um ein Diplomat zu werden und hilf die Website und die Levels zu Deutsch zu übersetzen." - missing_translations: "Solange wir nicht alles ins Deutsche übesetzt haben, siehst Du die englische Übersetzung, wo Deutsch leider noch nicht zur Verfügung steht." - learn_more: "Finde heraus, wie Du ein Diplomat werden kannst" - subscribe_as_diplomat: "Schreibe dich als Diplomat ein" - - wizard_settings: - title: "Zauberer Einstellungen" - customize_avatar: "Individualisiere deinen Avatar" - active: "Aktiv" - color: "Farbe" - group: "Gruppe" - clothes: "Kleidung" - trim: "Applikationen" - cloud: "Wolke" - team: "Team" - spell: "Zauber" - boots: "Stiefel" - hue: "Farbton" - saturation: "Sättigung" - lightness: "Helligkeit" - - account_settings: - title: "Accounteinstellungen" - not_logged_in: "Logge Dich ein oder lege einen Account an, um deine Einstellungen ändern zu können." - autosave: "Sichere Änderungen automatisch" - me_tab: "Ich" - picture_tab: "Bild" - upload_picture: "Ein Bild hochladen" - wizard_tab: "Zauberer" - password_tab: "Passwort" - emails_tab: "Emails" - admin: "Admin" - wizard_color: "Die Farbe der Kleidung des Zauberers" - new_password: "Neues Passwort" - new_password_verify: "Passwort verifizieren" - email_subscriptions: "Email Abonnements" - email_subscriptions_none: "Keine Email Abonnements." - email_announcements: "Ankündigungen" - email_announcements_description: "Erhalte regelmäßig Ankündigungen zu deinem Account." - email_notifications: "Benachrichtigungen" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" - email_recruit_notes: "Job-Angebote" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." - contributor_emails: "Unterstützer Email" - contribute_prefix: "Wir suchen nach Leuten, die mitmachen! Schau dir die" - contribute_page: "Unterstützer Seite" - contribute_suffix: " an um mehr zu erfahren." - email_toggle: "Alles wählen" - error_saving: "Fehler beim Speichern" - saved: "Änderungen gespeichert" - password_mismatch: "Passwörter stimmen nicht überein." - password_repeat: "Bitte wiederhole dein Passwort." - job_profile: "Jobprofil" - job_profile_approved: "Dein Jobprofil wurde von CodeCombat freigegeben. Arbeitgeber können dieses solange einsehen, bis du es als inaktiv markiert oder wenn innerhalb von vier Wochen keine Änderung daran vorgenommen wurde." - job_profile_explanation: "Hi! Fülle dies aus und wir melden uns bei dir bezüglich des Auffindens eines Jobs als Programmierer" - sample_profile: "Ein Beispielprofil ansehen" - view_profile: "Dein Profil ansehen" - - account_profile: - settings: "Einstellungen" - edit_profile: "Profil editieren" -# done_editing: "Done Editing" - profile_for_prefix: "Profil von " - profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" - looking_for: "Suche nach:" - last_updated: "zuletzt geändert:" - contact: "Kontakt" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" - next_city: "Stadt?" - next_country: "Wähle dein Land." - next_name: "Name?" - next_short_description: "Schreibe eine kurze Beschreibung." - next_long_description: "Beschreibe deine gewünschte Position" - next_skills: "Liste mindestens fünf Fähigkeiten." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." - next_projects: "Zeige bis zu 3 Projekte an denen du gearbeitet hast." -# next_links: "add any personal or social links." - next_photo: "Füge ein optionales professionelles Foto hinzu." -# next_active: "mark yourself open to offers to show up in searches." - example_blog: "Blog" - example_personal_site: "Persönliche Seite" - links_header: "Persönliche Links" - links_blurb: "Verlinke zu anderen Seiten oder Profilen die du hervorheben möchtest, wie z.B. dein GitHub, dein LinkedIn oder deinen Blog." - links_name: "Link-Name" -# links_name_help: "What are you linking to?" - links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" - basics_city: "Stadt" - basics_city_help: "Stadt in der du arbeiten möchtest (oder jetzt lebst)." - basics_country: "Land" - basics_country_help: "Land in dem du arbeiten möchtest (oder jetzt lebst)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" - basics_looking_for_full_time: "Vollzeit" - basics_looking_for_part_time: "Teilzeit" - basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" - basics_looking_for_internship: "Praktikum" -# basics_looking_for_help: "What kind of developer position do you want?" - name_header: "Trage deinen Namen ein" - name_anonymous: "Anonymer Entwickler" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." - skills_header: "Fähigkeiten" -# skills_help: "Tag relevant developer skills in order of proficiency." - long_description_header: "Beschreibe deine gewünschte Position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." - work_experience: "Berufserfahrung" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." - work_employer: "Arbeitgeber" - work_employer_help: "Name deines Arbeitgebers." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" - work_description: "Beschreibung" - work_description_help: "Was hast du dort gemacht (140 Zeichen; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." - education_school: "Schule" - education_school_help: "Name deiner Schule." - education_degree: "Abschluss" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" - education_duration_help: "Wann?" - education_description: "Beschreibung" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" - projects: "Projekte" - projects_header: "Füge 3 Projekte hinzu" - projects_header_2: "Projekte (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." - project_name: "Projekt Name" - project_name_help: "Wie wurde das Projekt genannt?" - project_description: "Beschreibung" - project_description_help: "Beschreibe kurz das Projekt." - project_picture: "Bild" - project_picture_help: "Lade ein 230x115px oder größeres Bild hoch, welches das Projekt darstellt." - project_link: "Link" - project_link_help: "Verlinke zu dem Projekt." - player_code: "Spieler Code" - - employers: -# hire_developers_not_credentials: "Hire developers, not credentials." -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." - what: "Was ist CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." - candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" - candidate_role: "Rolle" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" - - play_level: - done: "Fertig" - customize_wizard: "Bearbeite den Zauberer" - home: "Startseite" -# stop: "Stop" - game_menu: "Spielmenü" - guide: "Hilfe" - restart: "Neustart" - goals: "Ziele" - success: "Erfolgreich!" - incomplete: "Unvollständig" - timed_out: "Zeit abgelaufen" -# failing: "Failing" - action_timeline: "Aktionszeitstrahl" - click_to_select: "Klicke auf eine Einheit, um sie auszuwählen." - reload_title: "Gesamten Code neu laden?" - reload_really: "Bist Du sicher, dass Du das Level neu beginnen willst?" - reload_confirm: "Alles neu laden" - victory_title_prefix: "" - victory_title_suffix: " Abgeschlossen" - victory_sign_up: "Melde Dich an, um Fortschritte zu speichern" - victory_sign_up_poke: "Möchtest Du Neuigkeiten per Mail erhalten? Erstelle einen kostenlosen Account und wir halten Dich auf dem Laufenden." - victory_rate_the_level: "Bewerte das Level: " - victory_return_to_ladder: "Zurück zur Rangliste" - victory_play_next_level: "Spiel das nächste Level" - victory_go_home: "Geh auf die Startseite" - victory_review: "Erzähl uns davon!" - victory_hour_of_code_done: "Bist Du fertig?" - victory_hour_of_code_done_yes: "Ja, ich bin mit meiner Code-Stunde fertig!" - guide_title: "Anleitung" - tome_minion_spells: "Die Zaubersprüche Deiner Knechte" - tome_read_only_spells: "Nur-lesen Zauberspüche" - tome_other_units: "Andere Einheiten" - tome_cast_button_castable: "Führe aus" - tome_cast_button_casting: "Ausführen" - tome_cast_button_cast: "Zauberspuch ausführen" - tome_select_spell: "Wähle einen Zauber" - tome_select_a_thang: "Wähle jemanden aus, um " - tome_available_spells: "Verfügbare Zauber" - hud_continue: "Weiter (drücke Shift + Leertaste)" - spell_saved: "Zauber gespeichert" - skip_tutorial: "Überspringen (Esc)" - keyboard_shortcuts: "Tastenkürzel" - loading_ready: "Bereit!" - tip_insert_positions: "Halte 'Umschalt' gedrückt und klicke auf die Karte um die Koordinaten einzufügen." - tip_toggle_play: "Wechsel zwischen Play und Pause mit Strg+P." - tip_scrub_shortcut: "Spule vor und zurück mit Strg+[ und Strg+]" - tip_guide_exists: "Klicke auf die Anleitung am oberen Ende der Seite für nützliche Informationen" - tip_open_source: "CodeCombat ist 100% quelloffen!" - tip_beta_launch: "CodeCombat startete seine Beta im Oktober 2013." - tip_js_beginning: "JavaScript ist nur der Anfang." - tip_think_solution: "Denke über die Lösung nach, nicht über das Problem." - tip_theory_practice: "In der Theorie gibt es keinen Unterschied zwischen Theorie und Praxis. In der Praxis schon. - Yogi Berra" - tip_error_free: "Es gibt zwei Wege fehlerfreie Programme zu schreiben; nur der Dritte funktioniert. - Alan Perlis" - tip_debugging_program: "Wenn Debugging der Prozess zum Fehler entfernen ist, dann muss Programmieren der Prozess sein Fehler zu machen. - Edsger W. Dijkstra" - tip_forums: "Gehe zum Forum und sage uns was du denkst!" - tip_baby_coders: "In der Zukunft werden sogar Babies Erzmagier sein." - tip_morale_improves: "Das Laden wird weiter gehen bis die Stimmung sich verbessert." - tip_all_species: "Wir glauben an gleiche Chancen für alle Arten Programmieren zu lernen." -# tip_reticulating: "Reticulating spines." - tip_harry: "Du bist ein Zauberer, " - tip_great_responsibility: "Mit großen Programmierfähigkeiten kommt große Verantwortung." - tip_munchkin: "Wenn du dein Gemüse nicht isst, besucht dich ein Zwerg während du schläfst." - tip_binary: "Es gibt auf der Welt nur 10 Arten von Menschen: die, welche Binär verstehen und die, welche nicht." - tip_commitment_yoda: "Ein Programmier muss die größte Hingabe haben, den ernstesten Verstand. ~ Yoda" - tip_no_try: "Tu. Oder tu nicht. Es gibt kein Versuchen. - Yoda" - tip_patience: "Geduld du musst haben, junger Padawan. - Yoda" - tip_documented_bug: "Ein dokumentierter Fehler ist kein Fehler; er ist ein Merkmal." - tip_impossible: "Es wirkt immer unmöglich bis es vollbracht ist. - Nelson Mandela" - tip_talk_is_cheap: "Reden ist billig. Zeig mir den Code. - Linus Torvalds" - tip_first_language: "Das schwierigste, das du jemals lernen wirst, ist die erste Programmiersprache. - Alan Kay" - tip_hardware_problem: "Q: Wie viele Programmierer braucht man um eine Glühbirne auszuwechseln? A: Keine, es ist ein Hardware-Problem." - time_current: "Aktuell" - time_total: "Total" - time_goto: "Gehe zu" - infinite_loop_try_again: "Erneut versuchen" -# infinite_loop_reset_level: "Reset Level" - infinite_loop_comment_out: "Meinen Code auskommentieren" - -# game_menu: -# inventory_tab: "Inventory" -# choose_hero_tab: "Restart Level" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" -# multiplayer_tab: "Multiplayer" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" - -# inventory: -# temp: "Temp" - -# choose_hero: -# temp: "Temp" - -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" - - options: - general_options: "Allgemeine Einstellungen" -# volume_label: "Volume" - music_label: "Musik" - music_description: "Schalte Hintergrundmusik an/aus." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." - editor_config: "Editor Einstellungen" - editor_config_title: "Editor Einstellungen" - editor_config_level_language_label: "Sprache für dieses Level" - editor_config_level_language_description: "Lege die Programmiersprache für dieses bestimmte Level fest." - editor_config_default_language_label: "Voreinstellung Programmiersprache" - editor_config_default_language_description: "Definiere die Programmiersprache in der du programmieren möchtest wenn du ein neues Level beginnst." - editor_config_keybindings_label: "Tastenbelegung" - editor_config_keybindings_default: "Standard (Ace)" - editor_config_keybindings_description: "Fügt zusätzliche Tastenkombinationen, bekannt aus anderen Editoren, hinzu" - editor_config_livecompletion_label: "Live Auto-Vervollständigung" - editor_config_livecompletion_description: "Zeigt Vorschläge der Auto-Vervollständigung an während du tippst." - editor_config_invisibles_label: "Zeige unsichtbare Zeichen" - editor_config_invisibles_description: "Zeigt unsichtbare Zeichen wie Leertasten an." - editor_config_indentguides_label: "Zeige Einrückungshilfe" - editor_config_indentguides_description: "Zeigt vertikale Linien an um Einrückungen besser zu sehen." - editor_config_behaviors_label: "Intelligentes Verhalten" - editor_config_behaviors_description: "Vervollständigt automatisch Klammern und Anführungszeichen." - -# guide: -# temp: "Temp" - - multiplayer: - multiplayer_title: "Mehrspieler Einstellungen" -# multiplayer_toggle: "Enable multiplayer" - multiplayer_toggle_description: "Erlaube anderen an deinem Spiel teilzunehmen." - multiplayer_link_description: "Gib diesen Link jedem, der mitmachen will." - multiplayer_hint_label: "Hinweis:" - multiplayer_hint: " Klick den Link, um alles auszuwählen, dann drück ⌘-C oder Strg-C um den Link zu kopieren." - multiplayer_coming_soon: "Mehr Multiplayerfeatures werden kommen!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." - - keyboard_shortcuts: - keyboard_shortcuts: "Tastaturkürzel" - space: "Leertaste" - enter: "Eingabetaste" -# escape: "Escape" -# shift: "Shift" -# cast_spell: "Cast current spell." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." - - admin: - av_title: "Administrator Übersicht" - av_entities_sub_title: "Entitäten" - av_entities_users_url: "Benutzer" - av_entities_active_instances_url: "Aktive Instanzen" - av_entities_employer_list_url: "Arbeitgeberliste" - av_other_sub_title: "Sonstige" - av_other_debug_base_url: "Base (um base.jade zu debuggen)" - u_title: "Benutzerliste" - lg_title: "Letzte Spiele" -# clas: "CLAs" - - community: - main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." - find_us: "Finde uns auf diesen Seiten" -# contribute_to_the_project: "Contribute to the project" - - editor: - main_title: "CodeCombat Editoren" - article_title: "Artikel Editor" - thang_title: "Thang Editor" - level_title: "Level Editor" -# achievement_title: "Achievement Editor" - back: "Zurück" - revert: "Zurücksetzen" - revert_models: "Models zurücksetzen." - pick_a_terrain: "Wähle ein Terrain" - small: "Klein" -# grassy: "Grassy" - fork_title: "Forke neue Version" - fork_creating: "Erzeuge Fork..." -# randomize: "Randomize" - more: "Mehr" - wiki: "Wiki" - live_chat: "Live Chat" - level_some_options: "Einige Einstellungsmöglichkeiten?" - level_tab_thangs: "Thangs" - level_tab_scripts: "Skripte" - level_tab_settings: "Einstellungen" - level_tab_components: "Komponenten" - level_tab_systems: "Systeme" - level_tab_thangs_title: "Aktuelle Thangs" - level_tab_thangs_all: "Alle" - level_tab_thangs_conditions: "Startbedingungen" - level_tab_thangs_add: "Thangs hinzufügen" - delete: "Löschen" - duplicate: "Duplizieren" - level_settings_title: "Einstellungen" - level_component_tab_title: "Aktuelle Komponenten" - level_component_btn_new: "neue Komponente erstellen" - level_systems_tab_title: "Aktuelle Systeme" - level_systems_btn_new: "neues System erstellen" - level_systems_btn_add: "System hinzufügen" - level_components_title: "Zurück zu allen Thangs" - level_components_type: "Typ" - level_component_edit_title: "Komponente bearbeiten" - level_component_config_schema: "Konfigurationsschema" - level_component_settings: "Einstellungen" - level_system_edit_title: "System bearbeiten" - create_system_title: "neues System erstellen" - new_component_title: "Neue Komponente erstellen" - new_component_field_system: "System" - new_article_title: "Erstelle einen neuen Artikel" - new_thang_title: "Erstelle einen neuen Thang-Typen" - new_level_title: "Erstelle ein neues Level" - new_article_title_login: "Melde dich an um einen neuen Artikel zu erstellen" - new_thang_title_login: "Melde dich an um einen neuen Thang-Typen zu erstellen" - new_level_title_login: "Melde dich an um ein neues Level zu erstellen" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" - article_search_title: "Durchsuche Artikel hier" - thang_search_title: "Durchsuche Thang-Typen hier" - level_search_title: "Durchsuche Levels hier" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" - - article: - edit_btn_preview: "Vorschau" - edit_article_title: "Artikel bearbeiten" - - general: - and: "und" - name: "Name" - date: "Datum" - body: "Inhalt" - version: "Version" - commit_msg: "Commit Nachricht" - version_history: "Versionshistorie" - version_history_for: "Versionsgeschichte für: " - result: "Ergebnis" - results: "Ergebnisse" - description: "Beschreibung" - or: "oder" - subject: "Betreff" - email: "Email" - password: "Passwort" - message: "Nachricht" - code: "Code" - ladder: "Rangliste" - when: "Wann" - opponent: "Gegner" - rank: "Rang" - score: "Punktzahl" - win: "Sieg" - loss: "Niederlage" - tie: "Unentschieden" - easy: "Einfach" - medium: "Mittel" - hard: "Schwer" - player: "Spieler" - - about: - who_is_codecombat: "Wer ist CodeCombat?" - why_codecombat: "Warum CodeCombat?" - who_description_prefix: "gründeten CodeCombat im Jahre 2013 zusammen. Wir entwickelten außerdem " - who_description_suffix: ", die meist benutzte (#1) Web and iOS Applikation 2008 zum Lernen des Schreibens von chinesischen und japanischen Schriftzeichen." - who_description_ending: "Nun ist es an der Zeit, den Leuten das Programmieren beizubringen." - why_paragraph_1: "Als er Skritter machte, wusste George nicht wie man programmiert und war permanent darüber frustriert, dass er seine Ideen nicht umsetzen konnte. Danach versuchte er es zu lernen, aber das ging ihm zu langsam. Sein Mitbewohner versuchte Codecademy, als er sich umorientierte und aufhörte zu lehren, aber \"langweilte sich\". Jede Woche begann ein neuer Freund mit Codecademy und ließ es dann wieder bleiben. Wir erkannten, dass es das gleiche Problem war, welches wir mit Skritter gelöst hatten: Leute lernen eine Fähigkeit mittels langsamer, intersiver Lerneinheiten, wobei sie schnelle, umfassende Übung bräuchten. Wir kennen Abhilfe." - why_paragraph_2: "Programmieren lernen? Du brauchst keine Stunden. Du musst einen Haufen Code schreiben und dabei Spaß haben." - why_paragraph_3_prefix: "Darum geht's beim Programmieren. Es soll Spaß machen. Nicht so einen Spaß wie" - why_paragraph_3_italic: "jau, 'ne Plakette" - why_paragraph_3_center: "sondern Spaß wie" - why_paragraph_3_italic_caps: "NEIN MUTTI ICH MUSS NOCH DEN LEVEL BEENDEN !" - why_paragraph_3_suffix: "Deshalb ist CodeCombat ein Multiplayerspiel und kein spielähnlicher Kurs. Wir werden nicht aufhören bis du nicht mehr aufhören kannst -- nur diesmal ist das eine gute Sache." - why_paragraph_4: "Wenn dich Spiele süchtig machen, dass lass dich von diesem süchtig machen und werde ein Zauberer des Technologiezeitalters." - why_ending: "Und hey, es kostet nichts. " - why_ending_url: "Beginne jetzt zu zaubern!" - george_description: "CEO, Businesstyp, Web Designer, Game Designer und Champion der Programmieranfänger überall." - scott_description: "Außergewöhnlicher Programmierer, Softwarearchitekt, Küchenzauberer und Finanzmeister. Scott ist der Vernünftige." - nick_description: "Programmierzauberer, exzentrischer Motivationskünstler und Auf-den-Kopf-stell-Experimentierer. Nick könnte alles mögliche tun und entschied CodeCombat zu bauen." - jeremy_description: "Kundendienstmagier, Usability Tester und Community-Organisator. Wahrscheinlich hast du schon mit Jeremy gesprochen." - michael_description: "Programmierer, Systemadministrator und studentisch technisches Wunderkind, Michael hält unsere Server am Laufen." -# matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." - - legal: - page_title: "Rechtliches" - opensource_intro: "CodeCombat ist Free-to-Play und vollständig Open Source." - opensource_description_prefix: "Schau dir " - github_url: "unsere GitHub-Seite" - opensource_description_center: " an und mach mit wenn Du möchtest! CodeCombat baut auf duzenden Open Source Projekten auf, und wir lieben sie. Schau dir die Liste in " - archmage_wiki_url: "unserem Erzmagier-Wiki" - opensource_description_suffix: " an, welche Software dieses Spiel möglich macht." - practices_title: "Best Practices" - practices_description: "Dies sind unsere Versprechen an dich, den Spieler, in weniger Fachchinesisch." - privacy_title: "Datenschutz" - privacy_description: "Wir werden deine persönlichen Daten nicht verkaufen. Letztenendes beabsichtigen wir, durch Vermittlung von Jobs zu verdienen, aber sei versichert, dass wir nicht deine persönlichen Daten ohne deine ausdrückliche Einwilligung interessierten Firmen zur Verfügung stellen werden." - security_title: "Datensicherheit" - security_description: "Wir streben an, deine persönlichen Daten sicher zu verwahren. Als Open Source Projekt ist unsere Site frei zugänglich für jedermann, auch um unsere Sicherheitsmaßnahmen in Augenschein zu nehmen und zu verbessern." - email_title: "Email" - email_description_prefix: "Wir werden dich nicht mit Spam überschwemmen. Mittels" - email_settings_url: "deiner Emaileinstellungen" - email_description_suffix: "oder durch von uns gesendete Links kannst du jederzeit deine Einstellungen ändern und Abonnements kündigen." - cost_title: "Kosten" - cost_description: "CodeCombat ist zur Zeit 100% kostenlos! Eines unserer Hauptziele ist, es dabei zu belassen, so dass es so viele Leute wie möglich spielen können, unabhängig davon in welcher Lebenssituation sie sich befinden. Falls dunkle Wolken aufziehen, könnten wir manche Inhalte im Rahmen eines Abonnements anbieten, aber lieber nicht. Mit etwas Glück können wir die Firma erhalten durch:" - recruitment_title: "Recruiting" - recruitment_description_prefix: "Hier bei CodeCombat kannst du ein mächtiger Zauberer werden, nicht nur im Spiel, sondern auch in der Realität." - url_hire_programmers: "Niemand kann schnell genug Programmierer einstellen." - recruitment_description_suffix: "So wenn du deine Fähigkeiten entwickelt hast und zustimmst, werden wir deine besten Leistungen den tausenden Arbeitgebern demonstrieren, welche nur auf die Gelegentheit warten, dich einzustellen. Sie bezahlen uns ein bisschen, und sie bezahlen dir " - recruitment_description_italic: "jede Menge" - recruitment_description_ending: ", die Seite bleibt kostenlos und jeder ist glücklich. So der Plan." - copyrights_title: "Copyrights und Lizenzen" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" - mit_license_url: "MIT Lizenz" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" - art_music: "Musik" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." - nutshell_title: "Zusammenfassung" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." - canonical: "Die englische Version dieses Dokuments ist die definitive, kanonische Version. Sollte es Unterschiede zwischen den Übersetzungen geben, dann hat das englische Dokument Vorrang." - - contribute: -# page_title: "Contributing" - character_classes_title: "Charakter Klassen" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." - class_attributes: "Klassenattribute" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" - join_url_email: "Emaile uns" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." - contact_us_url: "Kontaktiere uns" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." - diligent_scribes: "Unsere fleißgen Schreiber:" - powerful_archmages: "Unsere mächtigen Erzmagier:" - creative_artisans: "Unsere kreativen Handwerker:" - brave_adventurers: "Unsere mutigen Abenteurer:" - translating_diplomats: "Unsere übersetzenden Diplomaten:" - helpful_ambassadors: "Unsere hilfreichen Botschafter:" - - classes: - archmage_title: "Erzmagier" - archmage_title_description: "(Programmierer)" - artisan_title: "Handwerker" - artisan_title_description: "(Level Entwickler)" - adventurer_title: "Abenteurer" - adventurer_title_description: "(Level Spieltester)" - scribe_title: "Schreiber" - scribe_title_description: "(Artikel Editor)" - diplomat_title: "Diplomat" - diplomat_title_description: "(Übersetzer)" - ambassador_title: "Botschafter" - ambassador_title_description: "(Support)" - - ladder: - please_login: "Bitte logge dich zunächst ein, bevor du ein Ladder-Game spielst." - my_matches: "Meine Matches" - simulate: "Simuliere" -# simulation_explanation: "By simulating games you can get your game ranked faster!" - simulate_games: "Simuliere Spiele!" -# simulate_all: "RESET AND SIMULATE GAMES" - games_simulated_by: "Spiele die durch dich simuliert worden:" - games_simulated_for: "Spiele die für dich simuliert worden:" - games_simulated: "simulierte Spiele" - games_played: "gespielte Spiele" - ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " - summary_your: "Deine " - summary_matches: "Matches - " - summary_wins: " Siege, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." - choose_opponent: "Wähle einen Gegner" -# select_your_language: "Select your language!" - tutorial_play: "Spiele Tutorial" - tutorial_recommended: "Empfohlen, wenn du noch nie zuvor gespielt hast." - tutorial_skip: "Überspringe Tutorial" -# tutorial_not_sure: "Not sure what's going on?" - tutorial_play_first: "Spiele zuerst das Tutorial." - simple_ai: "Einfache KI" - warmup: "Aufwärmen" - vs: "VS" - friends_playing: "spielende Freunde" - log_in_for_friends: "Melde dich an um mit deinen Freunden zu spielen!" - social_connect_blurb: "Verbinde und spiele gegen deine Freunde!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" - tournament_ends: "Turnier endet" - tournament_ended: "Turnier beendet" - tournament_rules: "Turnier-Regeln" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" - rules: "Regeln" - winners: "Gewinner" - - ladder_prizes: - title: "Turnierpreise" -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." - blurb_4: "Zwei Teams heißt die doppelte Anzahl zu gewinnender Preise!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" - rank: "Rang" - prizes: "Gewinne" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" - license: "Lizenz" - oreilly: "Ebook deiner Wahl" - - loading_error: - could_not_load: "Fehler beim Laden vom Server" - connection_failure: "Verbindung fehlgeschlagen." - unauthorized: "Du musst angemeldet sein. Hast du Cookies ausgeschaltet?" - forbidden: "Sie haben nicht die nötigen Berechtigungen." - not_found: "Nicht gefunden." - not_allowed: "Methode nicht erlaubt." - timeout: "Server timeout." - conflict: "Resourcen Konflikt." - bad_input: "Falsche Eingabe." - server_error: "Server Fehler." - unknown: "Unbekannter Fehler." - - resources: -# sessions: "Sessions" - your_sessions: "Meine Sessions" - level: "Level" - social_network_apis: "Social Network APIs" - facebook_status: "Facebook Status" - facebook_friends: "Facebook Freunde" - facebook_friend_sessions: "Facebook Freunde Sessions" - gplus_friends: "G+ Freunde" - gplus_friend_sessions: "G+ Freunde Sessions" -# leaderboard: "Leaderboard" - user_schema: "Benutzerschema" - user_profile: "Benutzerprofil" -# patches: "Patches" -# patched_model: "Source Document" - model: "Model" - system: "System" -# systems: "Systems" - component: "Komponente" - components: "Komponenten" - thang: "Thang" - thangs: "Thangs" - level_session: "Deine Session" - opponent_session: "Gegner-Session" - article: "Artikel" - user_names: "Benutzernamen" - thang_names: "Thang Namen" - files: "Dateien" - top_simulators: "Top Simulatoren" -# source_document: "Source Document" - document: "Dokument" - sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" - candidate_sessions: "Kandidat-Sessions" - user_remark: "Benutzerkommentar" -# user_remarks: "User Remarks" - versions: "Versionen" - items: "Gegenstände" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" - - delta: - added: "hinzugefügt" - modified: "modifiziert" - deleted: "gelöscht" -# moved_index: "Moved Index" -# text_diff: "Text Diff" - merge_conflict_with: "MERGE KONFLIKT MIT" - no_changes: "Keine Änderungen" - -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." - -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" - -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." diff --git a/app/locale/es.coffee b/app/locale/es.coffee deleted file mode 100644 index 43674fe60..000000000 --- a/app/locale/es.coffee +++ /dev/null @@ -1,999 +0,0 @@ -module.exports = nativeDescription: "español", englishDescription: "Spanish", translation: - common: - loading: "Cargando..." - saving: "Guardando..." - sending: "Enviando..." - send: "Enviar..." - cancel: "Cancelar" - save: "Guardar" -# publish: "Publish" - create: "Crear..." - delay_1_sec: "1 segundo" - delay_3_sec: "3 segundos" - delay_5_sec: "5 segundos" - manual: "Manual" -# fork: "Fork" - play: "Jugar" - retry: "Reintentar" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" - - units: - second: "Segundo" - seconds: "Segundos" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" - - modal: - close: "Cerrar" - okay: "OK" - - not_found: - page_not_found: "Pagina no encontrada" - - nav: - play: "Jugar" -# community: "Community" - editor: "Editor" - blog: "Blog" - forum: "Foro" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" - admin: "Admin" - home: "Inicio" - contribute: "Contribuir" - legal: "Legal" - about: "Sobre" - contact: "Contacto" - twitter_follow: "Seguir" - employers: "Empleados" - - versions: - save_version_title: "Guardar Nueva Versión" - new_major_version: "New Major Version" - cla_prefix: "Para poder guardar los cambios, primero debes aceptar nuestra" - cla_url: "CLA" - cla_suffix: "." - cla_agree: "ACEPTO" - - login: - sign_up: "Crear Cuenta" - log_in: "Iniciar Sesión" -# logging_in: "Logging In" - log_out: "Cerrar Sesión" - recover: "recuperar cuenta" - - recover: - recover_account_title: "Recuperar cuenta" - send_password: "Enviar contraseña olvidada" - - signup: - create_account_title: "Crea una cuenta para guardar el progreso" - description: "Es gratis. Solo necesitas un par de cosas y estarás listo para comenzar:" - email_announcements: "Recibe noticias por email" - coppa: "más de 13 años o fuera de los Estados Unidos" - coppa_why: "¿Por qué?" - creating: "Creando Cuenta..." - sign_up: "Registrarse" - log_in: "Inicia sesión con tu contraseña" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." - - home: - slogan: "Aprende a programar jugando" - no_ie: "CodeCombat no funciona en Internet Explorer 9 o versiones anteriores. ¡Lo sentimos!" - no_mobile: "¡CodeCombat no fue diseñado para dispositivos móviles y quizás no funcione!" - play: "Jugar" -# old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" -# old_browser_suffix: "You can try anyway, but it probably won't work." - campaign: "Campaña" - for_beginners: "Para principiantes" - multiplayer: "Multijugador" - for_developers: "Para desarrolladores" -# javascript_blurb: "The language of the web. Great for writing websites, web apps, HTML5 games, and servers." -# python_blurb: "Simple yet powerful, Python is a great general purpose programming language." -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." - - play: - choose_your_level: "Elige tu nivel" - adventurer_prefix: "Puedes saltar a cualquier nivel de abajo, o discutir los niveles en " - adventurer_forum: "el foro del aventurero" - adventurer_suffix: "." - campaign_beginner: "Campaña para principiantes" - campaign_beginner_description: "... en la que aprendes la hechicería de la programación." - campaign_dev: "Niveles aleatorios más difíciles" - campaign_dev_description: "... en los que aprendes sobre la interfaz mientras haces algo un poco más difícil." - campaign_multiplayer: "Arenas Multijugador" - campaign_multiplayer_description: "... en las que programas cara-a-cara contra otros jugadores." - campaign_player_created: "Creados-Por-Jugadores" - campaign_player_created_description: "... en los que luchas contra la creatividad de tus compañeros Hechiceros Artesanales." - level_difficulty: "Dificultad: " - play_as: "Juega como " -# spectate: "Spectate" -# players: "players" -# hours_played: "hours played" - - contact: - contact_us: "Contacta a CodeCombat" - welcome: "¡Qué bueno es escucharte! Usa este formulario para enviarnos un mensaje" - contribute_prefix: "¡Si estas interesado en contribuir, chequea nuestra " - contribute_page: "página de contribución" - contribute_suffix: "!" - forum_prefix: "Para cualquier cosa pública, por favor prueba " - forum_page: "nuestro foro" - forum_suffix: " en su lugar." - send: "Enviar Comentario" -# contact_candidate: "Contact Candidate" -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." - - diplomat_suggestion: - title: "¡Ayuda a traducir CodeCombat!" - sub_heading: "Necesitamos tus habilidades de idioma." - pitch_body: "Desarrollamos CodeCombat en inglés, pero ya tenemos jugadores por todo el mundo. Muchos de ellos quieren jugar en español pero no hablan inglés, así que si puedes hablar ambos, por favor considera registrarte pare ser un Diplomático y ayudar a traducir tanto el sitio de CodeCombat como todos los niveles al español." - missing_translations: "Hasta que podamos traducir todo al español, verás inglés cuando el español no esté disponible." - learn_more: "Aprende más sobre ser un Diplomático" - subscribe_as_diplomat: "Suscribete como un Diplomático" - - wizard_settings: - title: "Configuración del mago" - customize_avatar: "Personaliza tu avatar" -# active: "Active" -# color: "Color" -# group: "Group" - clothes: "Ropa" - trim: "Trim" -# cloud: "Cloud" -# team: "Team" - spell: "Spell" - boots: "Botas" - hue: "Hue" - saturation: "Saturación" - lightness: "Brillo" - - account_settings: - title: "Configuración de la Cuenta" - not_logged_in: "Inicia sesión o crea una cuenta para cambiar tu configuración." - autosave: "Los cambios se guardan Automáticamente" - me_tab: "Yo" - picture_tab: "Imagen" -# upload_picture: "Upload a picture" - wizard_tab: "Hechicero" - password_tab: "Contraseña" - emails_tab: "Correos" - admin: "Administrador" - wizard_color: "Color de Ropas del Hechicero" - new_password: "Nueva Contraseña" - new_password_verify: "Verificar" - email_subscriptions: "Suscripciones de Email" -# email_subscriptions_none: "No Email Subscriptions." - email_announcements: "Noticias" - email_announcements_description: "Recibe correos electrónicos con las últimas noticias y desarrollos de CodeCombat." - email_notifications: "Notificación" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." - contributor_emails: "Correos Para Colaboradores" - contribute_prefix: "¡Buscamos gente que se una a nuestro comunidad! Comprueba la " - contribute_page: "página de colaboraciones" - contribute_suffix: " para saber más." - email_toggle: "Activar todo" - error_saving: "Error al guardar" - saved: "Cambios guardados" - password_mismatch: "La contraseña no coincide" -# password_repeat: "Please repeat your password." -# job_profile: "Job Profile" -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - - account_profile: -# settings: "Settings" -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" - profile_for_prefix: "Perfil de " -# profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" - -# employers: -# hire_developers_not_credentials: "Hire developers, not credentials." -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" - - play_level: - done: "Hecho" - customize_wizard: "Personalizar Mago" - home: "Inicio" -# stop: "Stop" -# game_menu: "Game Menu" - guide: "Guía" - restart: "Reiniciar" - goals: "Objetivos" -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" - action_timeline: "Cronología de Acción" - click_to_select: "Click en una unidad para seleccionarla" - reload_title: "¿Recargar todo el código?" - reload_really: "¿Estas seguro que quieres reiniciar el nivel?" - reload_confirm: "Recargarlo todo" -# victory_title_prefix: "" - victory_title_suffix: " Completo" - victory_sign_up: "Regístrate para recibir actualizaciones." - victory_sign_up_poke: "¿Buscas recivir las últimas noticias en tu email? Create una cuente gratuita y recibe la correspondencia." - victory_rate_the_level: "Puntúa este nivel: " -# victory_return_to_ladder: "Return to Ladder" - victory_play_next_level: "Jugar el siguiente nivel" - victory_go_home: "Ir a Inicio" - victory_review: "¡Cuéntanos más!" - victory_hour_of_code_done: "¿Has acabado?" - victory_hour_of_code_done_yes: "Si, ¡He terminado con mi hora de código!" - guide_title: "Guía" - tome_minion_spells: "Hechizos de tus Secuaces" - tome_read_only_spells: "Hechizos de Sólo Lectura" - tome_other_units: "Otras Unidades" - tome_cast_button_castable: "Invocable" - tome_cast_button_casting: "Invocando" - tome_cast_button_cast: "Invocar" - tome_select_spell: "Selecciona un Hechizo" - tome_select_a_thang: "Selecciona Alguien para " - tome_available_spells: "Hechizos Disponibles" - hud_continue: "Continuar (presionar shift+space)" - spell_saved: "Hechizo guardado" - skip_tutorial: "Saltar (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# tip_insert_positions: "Shift+Click a point on the map to insert it into the spell editor." -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide at the top of the page for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_js_beginning: "JavaScript is just the beginning." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" - - game_menu: -# inventory_tab: "Inventory" -# choose_hero_tab: "Restart Level" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" - multiplayer_tab: "Multijugador" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" - -# inventory: -# temp: "Temp" - -# choose_hero: -# temp: "Temp" - -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" - -# options: -# general_options: "General Options" -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." - -# guide: -# temp: "Temp" - - multiplayer: - multiplayer_title: "Configuración de Multijugador" -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." - multiplayer_link_description: "Da este enlace a cualquiera para que se te una." - multiplayer_hint_label: "Consejo:" - multiplayer_hint: " Cliquea el enlace para seleccionar todo, luego presiona ⌘-C o Ctrl-C para copiar el enlace." - multiplayer_coming_soon: "¡Más características de multijugador por venir!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." - -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# cast_spell: "Cast current spell." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." - - admin: -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" - av_entities_users_url: "Usuarios" -# av_entities_active_instances_url: "Active Instances" -# av_entities_employer_list_url: "Employer List" - av_other_sub_title: "Otros" -# av_other_debug_base_url: "Base (for debugging base.jade)" - u_title: "Lista de usuario" - lg_title: "Últimos juegos" -# clas: "CLAs" - -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# contribute_to_the_project: "Contribute to the project" - - editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" - level_title: "Editor de nivel" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# randomize: "Randomize" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# delete: "Delete" -# duplicate: "Duplicate" - level_settings_title: "Ajustes" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" - level_components_type: "Tipo" -# level_component_edit_title: "Edit Component" -# level_component_config_schema: "Config Schema" - level_component_settings: "Ajustes" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" - new_component_field_system: "Sistema" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" - - article: - edit_btn_preview: "Previsualizar" - edit_article_title: "Editar artículo" - - general: - and: "y" - name: "Nombre" -# date: "Date" - body: "Cuerpo" - version: "Versión" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " - result: "Resultado" - results: "Resultados" - description: "Descripción" - or: "o" -# subject: "Subject" - email: "Email" - password: "Contraseña" - message: "Mensaje" - code: "Código" -# ladder: "Ladder" - when: "Cuando" -# opponent: "Opponent" -# rank: "Rank" - score: "Puntuación" - win: "Victoria" - loss: "Pérdida" -# tie: "Tie" - easy: "Fácil" - medium: "Medio" - hard: "Difíficl" -# player: "Player" - - about: - who_is_codecombat: "¿Quién es CodeCombat?" - why_codecombat: "¿Por qué CodeCombat?" -# who_description_prefix: "together started CodeCombat in 2013. We also created " -# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." -# who_description_ending: "Now it's time to teach people to write code." -# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." -# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_3_italic: "yay a badge" -# why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." -# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# why_ending: "And hey, it's free. " -# why_ending_url: "Start wizarding now!" -# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." -# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." -# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." -# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." -# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." -# matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." - -# legal: -# page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." -# opensource_description_prefix: "Check out " -# github_url: "our GitHub" -# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " -# archmage_wiki_url: "our Archmage wiki" -# opensource_description_suffix: "for a list of the software that makes this game possible." -# practices_title: "Respectful Best Practices" -# practices_description: "These are our promises to you, the player, in slightly less legalese." -# privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." -# security_title: "Security" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." -# email_title: "Email" -# email_description_prefix: "We will not inundate you with spam. Through" -# email_settings_url: "your email settings" -# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." -# cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." -# copyrights_title: "Copyrights and Licenses" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" -# mit_license_url: "MIT license" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." - - contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" - artisan_join_step1: "Leer la documentación." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." -# diligent_scribes: "Our Diligent Scribes:" -# powerful_archmages: "Our Powerful Archmages:" -# creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" -# translating_diplomats: "Our Translating Diplomats:" -# helpful_ambassadors: "Our Helpful Ambassadors:" - -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" - - ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " - summary_wins: " Victorias, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" - rank_submitting: "Enviando..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" - tutorial_play: "Jugar Tutorial" -# tutorial_recommended: "Recommended if you've never played before" - tutorial_skip: "Saltar Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" - warmup: "Calentamiento" -# vs: "VS" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" - -# ladder_prizes: -# title: "Tournament Prizes" -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." - -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" - -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" - -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." - -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" - -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." diff --git a/app/locale/locale.coffee b/app/locale/locale.coffee index 34e59f3f7..b94876bfa 100644 --- a/app/locale/locale.coffee +++ b/app/locale/locale.coffee @@ -9,14 +9,11 @@ module.exports = 'en-GB': require './en-GB' # English (UK), English (UK) 'en-AU': require './en-AU' # English (AU), English (AU) ru: require './ru' # русский язык, Russian - de: require './de' # Deutsch, German 'de-DE': require './de-DE' # Deutsch (Deutschland), German (Germany) 'de-AT': require './de-AT' # Deutsch (Österreich), German (Austria) 'de-CH': require './de-CH' # Deutsch (Schweiz), German (Switzerland) - es: require './es' # español, Spanish 'es-419': require './es-419' # español (América Latina), Spanish (Latin America) 'es-ES': require './es-ES' # español (ES), Spanish (Spain) - zh: require './zh' # 中文, Chinese 'zh-HANS': require './zh-HANS' # 简体中文, Chinese (Simplified) 'zh-HANT': require './zh-HANT' # 繁体中文, Chinese (Traditional) 'zh-WUU-HANS': require './zh-WUU-HANS' # 吴语, Wuu (Simplified) @@ -24,13 +21,11 @@ module.exports = fr: require './fr' # français, French ja: require './ja' # 日本語, Japanese ar: require './ar' # العربية, Arabic - pt: require './pt' # português, Portuguese 'pt-BR': require './pt-BR' # português do Brasil, Portuguese (Brazil) 'pt-PT': require './pt-PT' # Português (Portugal), Portuguese (Portugal) pl: require './pl' # język polski, Polish it: require './it' # italiano, Italian tr: require './tr' # Türkçe, Turkish - nl: require './nl' # Nederlands, Dutch 'nl-BE': require './nl-BE' # Nederlands (België), Dutch (Belgium) 'nl-NL': require './nl-NL' # Nederlands (Nederland), Dutch (Netherlands) fa: require './fa' # فارسی, Persian diff --git a/app/locale/nl-NL.coffee b/app/locale/nl-NL.coffee index 2c3f0130b..f4f1ca0fe 100644 --- a/app/locale/nl-NL.coffee +++ b/app/locale/nl-NL.coffee @@ -15,9 +15,9 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription fork: "Fork" play: "Spelen" retry: "Probeer opnieuw" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" + watch: "Volgen" + unwatch: "Ontvolgen" + submit_patch: "Correctie Opsturen" units: second: "seconde" @@ -44,11 +44,11 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription nav: play: "Levels" -# community: "Community" + community: "Gemeenschap" editor: "Editor" blog: "Blog" forum: "Forum" -# account: "Account" + account: "Lidmaatschap" # profile: "Profile" # stats: "Stats" # code: "Code" @@ -172,7 +172,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription autosave: "Aanpassingen Automatisch Opgeslagen" me_tab: "Ik" picture_tab: "Afbeelding" -# upload_picture: "Upload a picture" + upload_picture: "Je afbeelding opsturen" wizard_tab: "Tovenaar" password_tab: "Wachtwoord" emails_tab: "Emails" @@ -185,12 +185,12 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription email_announcements: "Aankondigingen" email_announcements_description: "Verkrijg emails over het laatste nieuws en de ontwikkelingen bij CodeCombat." email_notifications: "Notificaties" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." + email_notifications_summary: "Instellingen voor gepersonaliseerde, automatische meldingen via e-mail omtrent je activiteit op CodeCombat." + email_any_notes: "Alle Meldingen" + email_any_notes_description: "Zet alle activiteit-meldingen via e-mail af." # email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_recruit_notes: "Job Aanbiedingen" + email_recruit_notes_description: "Als je zeer goed speelt, zouden we je wel eens kunnen contacteren om je een (betere) job aan te bieden." contributor_emails: "Medewerker Klasse emails" contribute_prefix: "We zoeken mensen om met ons te komen feesten! Bekijk de " contribute_page: "bijdragepagina" @@ -203,8 +203,8 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription job_profile: "Job Profiel" job_profile_approved: "Jouw job profiel werd goedgekeurd door CodeCombat. Werkgevers zullen het kunnen bekijken totdat je het inactief zet of als er geen verandering in komt voor vier weken." job_profile_explanation: "Hey! Vul dit in en we zullen je contacteren om je een job als softwareontwikkelaar te helpen vinden." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" + sample_profile: "Bekijk een voorbeeld kandidaat-profiel" + view_profile: "Bekijk je eigen kandidaat-profiel" account_profile: # settings: "Settings" @@ -394,7 +394,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription tome_select_spell: "Selecteer een Spreuk" tome_select_a_thang: "Selecteer Iemand voor " tome_available_spells: "Beschikbare spreuken" - hud_continue: "Ga verder (druk shift-space)" + hud_continue: "Ga verder (druk shift-spatie)" spell_saved: "Spreuk Opgeslagen" skip_tutorial: "Overslaan (esc)" # keyboard_shortcuts: "Key Shortcuts" @@ -412,9 +412,9 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription tip_debugging_program: "Als debuggen het proces is om bugs te verwijderen, dan moet programmeren het proces zijn om ze erin te stoppen. - Edsger W. Dijkstra" tip_forums: "Ga naar de forums en vertel ons wat je denkt!" tip_baby_coders: "Zelfs babies zullen in de toekomst een Tovenaar zijn." - tip_morale_improves: "Het spel zal blijven laden tot de moreel verbeterd." + tip_morale_improves: "Het spel zal blijven laden tot de moreel verbetert." tip_all_species: "Wij geloven in gelijke kansen voor alle wezens om te leren programmeren." -# tip_reticulating: "Reticulating spines." + tip_reticulating: "Paden aan het verknopen." tip_harry: "Je bent een tovenaar, " tip_great_responsibility: "Met een groots talent voor programmeren komt een grootse debug verantwoordelijkheid." tip_munchkin: "Als je je groentjes niet opeet zal een munchkin je ontvoeren terwijl je slaapt." @@ -477,7 +477,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # editor_config_livecompletion_label: "Live Autocompletion" # editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." editor_config_invisibles_label: "Toon onzichtbare" - editor_config_invisibles_description: "Toon onzichtbare whitespace karakters." + editor_config_invisibles_description: "Toon onzichtbare (spatie) karakters." editor_config_indentguides_label: "Toon inspringing regels" editor_config_indentguides_description: "Toon verticale hulplijnen om de zichtbaarheid te verbeteren." editor_config_behaviors_label: "Slim gedrag" @@ -529,8 +529,8 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription lg_title: "Laatste Spelletjes" clas: "CLAs" -# community: -# main_title: "CodeCombat Community" + community: + main_title: "CodeCombat Gemeenschap" # introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" # level_editor_prefix: "Use the CodeCombat" # level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" @@ -861,13 +861,13 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription simple_ai: "Simpele AI" warmup: "Opwarming" vs: "tegen" -# friends_playing: "Friends Playing" + friends_playing: "Spelende Vrienden" # log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" + social_connect_blurb: "Koppel je sociaal netwerk om tegen je vrienden te spelen!" + invite_friends_to_battle: "Nodig je vrienden uit om deel te nemen aan het gevecht!" + fight: "Aanvallen!" + watch_victory: "Aanschouw je overwinning!" + defeat_the: "Versla de" # tournament_ends: "Tournament ends" # tournament_ended: "Tournament ended" # tournament_rules: "Tournament Rules" @@ -926,19 +926,19 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription patches: "Patches" # patched_model: "Source Document" model: "Model" -# system: "System" + system: "Systeem" # systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" + component: "Component" + components: "Componenten" + thang: "Thang" + thangs: "Thangs" + level_session: "Jouw Sessie" + opponent_session: "Sessie van tegenstander" + article: "Artikel" + user_names: "Gebruikersnamen" # thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" + files: "Bestanden" + top_simulators: "Top Simulatoren" # source_document: "Source Document" # document: "Document" # sprite_sheet: "Sprite Sheet" diff --git a/app/locale/nl.coffee b/app/locale/nl.coffee deleted file mode 100644 index f2dd6e88f..000000000 --- a/app/locale/nl.coffee +++ /dev/null @@ -1,999 +0,0 @@ -module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", translation: - common: - loading: "Bezig met laden..." - saving: "Opslaan..." - sending: "Verzenden..." - send: "Verzend" - cancel: "Annuleren" - save: "Opslaan" - publish: "Publiceren" - create: "Creëer" - delay_1_sec: "1 seconde" - delay_3_sec: "3 secondes" - delay_5_sec: "5 secondes" - manual: "Handleiding" - fork: "Fork" - play: "Spelen" - retry: "Probeer opnieuw" - watch: "Volgen" - unwatch: "Ontvolgen" - submit_patch: "Correctie Opsturen" - - units: - second: "seconde" - seconds: "seconden" - minute: "minuut" - minutes: "minuten" - hour: "uur" - hours: "uren" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" - - modal: - close: "Sluiten" - okay: "Oké" - - not_found: - page_not_found: "Pagina niet gevonden" - - nav: - play: "Levels" - community: "Gemeenschap" - editor: "Editor" - blog: "Blog" - forum: "Forum" - account: "Lidmaatschap" -# profile: "Profile" -# stats: "Stats" -# code: "Code" - admin: "Administrator" - home: "Home" - contribute: "Bijdragen" - legal: "Legaal" - about: "Over Ons" - contact: "Contact" - twitter_follow: "Volgen" - employers: "Werkgevers" - - versions: - save_version_title: "Nieuwe versie opslaan" - new_major_version: "Nieuwe hoofd versie" - cla_prefix: "Om bewerkingen op te slaan, moet je eerst akkoord gaan met onze" - cla_url: "CLA" - cla_suffix: "." - cla_agree: "IK GA AKKOORD" - - login: - sign_up: "Account maken" - log_in: "Inloggen" - logging_in: "Bezig met inloggen" - log_out: "Uitloggen" - recover: "account herstellen" - - recover: - recover_account_title: "Herstel Account" - send_password: "Verzend nieuw wachtwoord" - - signup: - create_account_title: "Maak een account aan om je vooruitgang op te slaan" - description: "Het is gratis. We hebben maar een paar dingen nodig en dan kan je aan de slag:" - email_announcements: "Ontvang aankondigingen via email" - coppa: "13+ of niet uit de VS" - coppa_why: "(Waarom?)" - creating: "Account aanmaken..." - sign_up: "Aanmelden" - log_in: "inloggen met wachtwoord" - social_signup: "Of je kunt je registreren met Facebook of G+:" -# required: "You need to log in before you can go that way." - - home: - slogan: "Leer programmeren door het spelen van een spel" - no_ie: "CodeCombat werkt niet in IE8 of ouder. Sorry!" - no_mobile: "CodeCombat is niet gemaakt voor mobiele apparaten en werkt misschien niet!" - play: "Speel" - old_browser: "Uh oh, jouw browser is te oud om CodeCombat te kunnen spelen, Sorry!" - old_browser_suffix: "Je kan toch proberen, maar het zal waarschijnlijk niet werken!" - campaign: "Campagne" - for_beginners: "Voor Beginners" - multiplayer: "Multiplayer" - for_developers: "Voor ontwikkelaars" -# javascript_blurb: "The language of the web. Great for writing websites, web apps, HTML5 games, and servers." -# python_blurb: "Simple yet powerful, Python is a great general purpose programming language." -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." - - play: - choose_your_level: "Kies Je Level" - adventurer_prefix: "Je kunt meteen naar een van de levels hieronder springen, of de levels bespreken op " - adventurer_forum: "het Avonturiersforum" - adventurer_suffix: "." - campaign_beginner: "Beginnercampagne" - campaign_beginner_description: "... waarin je de toverkunst van het programmeren leert." - campaign_dev: "Willekeurige moeilijkere levels" - campaign_dev_description: "... waarin je de interface leert kennen terwijl je wat moeilijkers doet." - campaign_multiplayer: "Multiplayer Arena's" - campaign_multiplayer_description: "... waarin je direct tegen andere spelers speelt." - campaign_player_created: "Door-spelers-gemaakt" - campaign_player_created_description: "... waarin je ten strijde trekt tegen de creativiteit van andere Ambachtelijke Tovenaars." - level_difficulty: "Moeilijkheidsgraad: " - play_as: "Speel als " - spectate: "Toeschouwen" -# players: "players" -# hours_played: "hours played" - - contact: - contact_us: "Contact opnemen met CodeCombat" - welcome: "Goed om van je te horen! Gebruik dit formulier om ons een e-mail te sturen." - contribute_prefix: "Als je interesse hebt om bij te dragen, bekijk onze " - contribute_page: "pagina over bijdragen" - contribute_suffix: "!" - forum_prefix: "Voor iets publiekelijks, probeer dan " - forum_page: "ons forum" - forum_suffix: "." - send: "Feedback Verzonden" - contact_candidate: "Contacteer Kandidaat" - recruitment_reminder: "Gebruik dit formulier om kandidaten te contacteren voor wie je een interesse hebt om te interviewen. Vergeet niet dat CodeCombat een honorarium vraagt van 18% op het eerste-jaarssalaris. Dit honorarium moet betaald worden als de kandidaat wordt aangenomen en kon tot na 90 dagen terugbetaald worden als deze ontslagen wordt in deze periode. Deeltijds-, contract- en thuiswerkers worden van dit honorarium vrijgesteld, alsook interims." - - diplomat_suggestion: - title: "Help CodeCombat vertalen!" - sub_heading: "We hebben je taalvaardigheden nodig." - pitch_body: "We ontwikkelen CodeCombat in het Engels, maar we hebben al spelers van over de hele wereld. Veel van hen willen in het Nederlands spelen, maar kunnen geen Engels. Dus als je beiden spreekt, overweeg a.u.b. om je aan te melden als Diplomaat en help zowel de CodeCombat website als alle levels te vertalen naar het Nederlands." - missing_translations: "Totdat we alles hebben vertaald naar het Nederlands zul je Engels zien waar Nederlands niet beschikbaar is." - learn_more: "Meer informatie over het zijn van een Diplomaat" - subscribe_as_diplomat: "Abonneren als Diplomaat" - - wizard_settings: - title: "Tovenaar instellingen" - customize_avatar: "Bewerk je avatar" - active: "Actief" - color: "Kleur" - group: "Groep" - clothes: "Kleren" - trim: "Trim" - cloud: "Wolk" - team: "Team" - spell: "Spreuk" - boots: "Laarzen" - hue: "Hue" - saturation: "Saturatie" - lightness: "Helderheid" - - account_settings: - title: "Account Instellingen" - not_logged_in: "Log in of maak een account aan om je instellingen aan te passen." - autosave: "Aanpassingen Automatisch Opgeslagen" - me_tab: "Ik" - picture_tab: "Afbeelding" - upload_picture: "Je afbeelding opsturen" - wizard_tab: "Tovenaar" - password_tab: "Wachtwoord" - emails_tab: "Emails" - admin: "Administrator" - wizard_color: "Tovenaar Kleding Kleur" - new_password: "Nieuw Wachtwoord" - new_password_verify: "Verifieer" - email_subscriptions: "E-mail Abonnementen" -# email_subscriptions_none: "No Email Subscriptions." - email_announcements: "Aankondigingen" - email_announcements_description: "Verkrijg emails over het laatste nieuws en de ontwikkelingen bij CodeCombat." - email_notifications: "Meldingen" - email_notifications_summary: "Instellingen voor gepersonaliseerde, automatische meldingen via e-mail omtrent je activiteit op CodeCombat." - email_any_notes: "Alle Meldingen" - email_any_notes_description: "Zet alle activiteit-meldingen via e-mail af." -# email_news: "News" - email_recruit_notes: "Job Aanbiedingen" - email_recruit_notes_description: "Als je zeer goed speelt, zouden we je wel eens kunnen contacteren om je een (betere) job aan te bieden." - contributor_emails: "Medewerker Klasse emails" - contribute_prefix: "We zoeken mensen om met ons te komen feesten! Bekijk de " - contribute_page: "bijdragepagina" - contribute_suffix: " om meer te weten te komen." - email_toggle: "Vink alles aan/af" - error_saving: "Fout Tijdens Het Opslaan" - saved: "Aanpassingen Opgeslagen" - password_mismatch: "Het wachtwoord komt niet overeen." -# password_repeat: "Please repeat your password." - job_profile: "Job Profiel" - job_profile_approved: "Jouw job profiel werd goedgekeurd door CodeCombat. Werkgevers zullen het kunnen bekijken totdat je het inactief zet of als er geen verandering in komt voor vier weken." - job_profile_explanation: "Hey! Vul dit in en we zullen je contacteren om je een job als softwareontwikkelaar te helpen vinden." - sample_profile: "Bekijk een voorbeeld kandidaat-profiel" - view_profile: "Bekijk je eigen kandidaat-profiel" - - account_profile: -# settings: "Settings" -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" - profile_for_prefix: "Profiel voor " - profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" - looking_for: "Zoekt naar:" - last_updated: "Laatst aangepast:" - contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." - work_experience: "Werk ervaring" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" - education: "Opleiding" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" - our_notes: "Onze notities" -# remarks: "Remarks" - projects: "Projecten" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" - - employers: -# hire_developers_not_credentials: "Hire developers, not credentials." -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." - candidate_name: "Naam" - candidate_location: "Locatie" - candidate_looking_for: "Zoekt naar" - candidate_role: "Rol" - candidate_top_skills: "Beste vaardigheden" - candidate_years_experience: "Jaren ervaring" - candidate_last_updated: "Laatst aangepast" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" - - play_level: - done: "Klaar" - customize_wizard: "Pas Tovenaar aan" - home: "Home" -# stop: "Stop" -# game_menu: "Game Menu" - guide: "Handleiding" - restart: "Herstarten" - goals: "Doelen" -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" - action_timeline: "Actie tijdlijn" - click_to_select: "Klik op een eenheid om deze te selecteren." - reload_title: "Alle Code Herladen?" - reload_really: "Weet je zeker dat je dit level tot het begin wilt herladen?" - reload_confirm: "Herlaad Alles" - victory_title_prefix: "" - victory_title_suffix: " Compleet" - victory_sign_up: "Schrijf je in om je vooruitgang op te slaan" - victory_sign_up_poke: "Wil je jouw code opslaan? Maak een gratis account aan!" - victory_rate_the_level: "Beoordeel het level: " - victory_return_to_ladder: "Keer terug naar de ladder" - victory_play_next_level: "Speel Volgend Level" - victory_go_home: "Ga naar Home" - victory_review: "Vertel ons meer!" - victory_hour_of_code_done: "Ben Je Klaar?" - victory_hour_of_code_done_yes: "Ja, ik ben klaar met mijn Hour of Code!" - guide_title: "Handleiding" - tome_minion_spells: "Jouw Minions' Spreuken" - tome_read_only_spells: "Read-Only Spreuken" - tome_other_units: "Andere Eenheden" - tome_cast_button_castable: "Uitvoeren" - tome_cast_button_casting: "Aan het uitvoeren" - tome_cast_button_cast: "Spreuk uitvoeren" - tome_select_spell: "Selecteer een Spreuk" - tome_select_a_thang: "Selecteer Iemand voor " - tome_available_spells: "Beschikbare spreuken" - hud_continue: "Ga verder (druk shift-spatie)" - spell_saved: "Spreuk Opgeslagen" - skip_tutorial: "Overslaan (esc)" -# keyboard_shortcuts: "Key Shortcuts" - loading_ready: "Klaar!" - tip_insert_positions: "Shift+Klik een punt op de kaart om het toe te voegen aan je spreuk editor." - tip_toggle_play: "Verwissel speel/pauze met Ctrl+P." - tip_scrub_shortcut: "Ctrl+[ en Ctrl+] om terug te spoelen en vooruit te spoelen." - tip_guide_exists: "Klik op de handleiding bovenaan het scherm voor nuttige informatie." - tip_open_source: "CodeCombat is 100% open source!" - tip_beta_launch: "CodeCombat lanceerde zijn beta versie in Oktober, 2013." - tip_js_beginning: "JavaScript is nog maar het begin." - tip_think_solution: "Denk aan de oplossing, niet aan het probleem." - tip_theory_practice: "In theorie is er geen verschil tussen de theorie en de praktijk; in de praktijk is er wel een verschil. - Yogi Berra" - tip_error_free: "Er zijn twee manieren om fout-vrije code te schrijven, maar enkele de derde manier werkt. - Alan Perlis" - tip_debugging_program: "Als debuggen het proces is om bugs te verwijderen, dan moet programmeren het proces zijn om ze erin te stoppen. - Edsger W. Dijkstra" - tip_forums: "Ga naar de forums en vertel ons wat je denkt!" - tip_baby_coders: "Zelfs babies zullen in de toekomst een Tovenaar zijn." - tip_morale_improves: "Het spel zal blijven laden tot de moreel verbetert." - tip_all_species: "Wij geloven in gelijke kansen voor alle wezens om te leren programmeren." - tip_reticulating: "Paden aan het verknopen." - tip_harry: "Je bent een tovenaar, " - tip_great_responsibility: "Met een groots talent voor programmeren komt een grootse debug verantwoordelijkheid." - tip_munchkin: "Als je je groentjes niet opeet zal een munchkin je ontvoeren terwijl je slaapt." - tip_binary: "Er zijn 10 soorten mensen in de wereld: Mensen die binair kunnen tellen en mensen die dat niet kunnen." - tip_commitment_yoda: "Een programmeur moet de grootste inzet hebben, een meest serieuze geest. ~ Yoda" - tip_no_try: "Doe het. Of doe het niet. Je kunt niet proberen. - Yoda" - tip_patience: "Geduld moet je hebben, jonge Padawan. - Yoda" - tip_documented_bug: "Een gedocumenteerde fout is geen fout; het is deel van het programma." - tip_impossible: "Het lijkt altijd onmogelijk tot het gedaan wordt. - Nelson Mandela" - tip_talk_is_cheap: "Je kunt het goed uitleggen, maar toon me de code. - Linus Torvalds" - tip_first_language: "Het ergste dat je kan leren is je eerste programmeertaal. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." - time_current: "Nu:" - time_total: "Maximum:" - time_goto: "Ga naar:" - infinite_loop_try_again: "Probeer opnieuw" - infinite_loop_reset_level: "Level resetten" - infinite_loop_comment_out: "Mijn code weg commentariëren" - - game_menu: -# inventory_tab: "Inventory" -# choose_hero_tab: "Restart Level" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" - multiplayer_tab: "Multiplayer" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" - -# inventory: -# temp: "Temp" - -# choose_hero: -# temp: "Temp" - -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" - - options: -# general_options: "General Options" -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." - editor_config: "Editor Configuratie" - editor_config_title: "Editor Configuratie" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." - editor_config_keybindings_label: "Toets instellingen" - editor_config_keybindings_default: "Standaard (Ace)" - editor_config_keybindings_description: "Voeg extra shortcuts toe van de gebruikelijke editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." - editor_config_invisibles_label: "Toon onzichtbare" - editor_config_invisibles_description: "Toon onzichtbare (spatie) karakters." - editor_config_indentguides_label: "Toon inspringing regels" - editor_config_indentguides_description: "Toon verticale hulplijnen om de zichtbaarheid te verbeteren." - editor_config_behaviors_label: "Slim gedrag" - editor_config_behaviors_description: "Automatisch aanvullen van (gekrulde) haakjes en aanhalingstekens." - -# guide: -# temp: "Temp" - - multiplayer: - multiplayer_title: "Multiplayer Instellingen" -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." - multiplayer_link_description: "Geef deze url aan iemand om hem/haar te laten meedoen met jou." - multiplayer_hint_label: "Hint:" - multiplayer_hint: " Klik de link om alles te selecteren, druk dan op Apple-C of Ctrl-C om de link te kopiëren." - multiplayer_coming_soon: "Binnenkort komen er meer Multiplayermogelijkheden!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." - -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# cast_spell: "Cast current spell." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." - - admin: - av_title: "Administrator panels" - av_entities_sub_title: "Entiteiten" - av_entities_users_url: "Gebruikers" - av_entities_active_instances_url: "Actieve instanties" -# av_entities_employer_list_url: "Employer List" - av_other_sub_title: "Andere" - av_other_debug_base_url: "Base (om base.jade te debuggen)" - u_title: "Gebruikerslijst" - lg_title: "Laatste Spelletjes" - clas: "CLAs" - - community: - main_title: "CodeCombat Gemeenschap" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# contribute_to_the_project: "Contribute to the project" - - editor: - main_title: "CodeCombat Editors" - article_title: "Artikel Editor" - thang_title: "Thang Editor" - level_title: "Level Editor" -# achievement_title: "Achievement Editor" - back: "Terug" - revert: "Keer wijziging terug" - revert_models: "keer wijziging model terug" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" - fork_title: "Kloon naar nieuwe versie" - fork_creating: "Kloon aanmaken..." -# randomize: "Randomize" - more: "Meer" - wiki: "Wiki" - live_chat: "Live Chat" - level_some_options: "Enkele opties?" - level_tab_thangs: "Elementen" - level_tab_scripts: "Scripts" - level_tab_settings: "Instellingen" - level_tab_components: "Componenten" - level_tab_systems: "Systemen" - level_tab_thangs_title: "Huidige Elementen" - level_tab_thangs_all: "Alles" - level_tab_thangs_conditions: "Start Condities" - level_tab_thangs_add: "Voeg element toe" - delete: "Verwijder" - duplicate: "Dupliceer" - level_settings_title: "Instellingen" - level_component_tab_title: "Huidige Componenten" - level_component_btn_new: "Maak een nieuwe component aan" - level_systems_tab_title: "Huidige Systemen" - level_systems_btn_new: "Maak een nieuw systeem aan" - level_systems_btn_add: "Voeg Systeem toe" - level_components_title: "Terug naar Alle Elementen" - level_components_type: "Type" - level_component_edit_title: "Wijzig Component" - level_component_config_schema: "Schema" - level_component_settings: "Instellingen" - level_system_edit_title: "Wijzig Systeem" - create_system_title: "Maak een nieuw Systeem aan" - new_component_title: "Maak een nieuwe Component aan" - new_component_field_system: "Systeem" - new_article_title: "Maak een Nieuw Artikel" - new_thang_title: "Maak een Nieuw Thang Type" - new_level_title: "Maak een Nieuw Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" - article_search_title: "Zoek Artikels Hier" - thang_search_title: "Zoek Thang Types Hier" - level_search_title: "Zoek Levels Hier" -# achievement_search_title: "Search Achievements" - read_only_warning2: "Pas op, je kunt geen aanpassingen opslaan hier, want je bent niet ingelogd." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" - - article: - edit_btn_preview: "Voorbeeld" - edit_article_title: "Wijzig Artikel" - - general: - and: "en" - name: "Naam" -# date: "Date" - body: "Inhoud" - version: "Versie" - commit_msg: "Commit Bericht" - version_history: "Versie geschiedenis" - version_history_for: "Versie geschiedenis voor: " - result: "Resultaat" - results: "Resultaten" - description: "Beschrijving" - or: "of" - subject: "Onderwerp" - email: "Email" - password: "Wachtwoord" - message: "Bericht" - code: "Code" - ladder: "Ladder" - when: "Wanneer" - opponent: "Tegenstander" - rank: "Rang" - score: "Score" - win: "Win" - loss: "Verlies" - tie: "Gelijkstand" - easy: "Gemakkelijk" - medium: "Medium" - hard: "Moeilijk" - player: "Speler" - - about: - who_is_codecombat: "Wie is CodeCombat?" - why_codecombat: "Waarom CodeCombat?" - who_description_prefix: "hebben samen CodeCombat opgericht in 2013. We creëerden ook " - who_description_suffix: "en in 2008, groeide het uit tot de #1 web en iOS applicatie om Chinese en Japanse karakters te leren schrijven." - who_description_ending: "Nu is het tijd om mensen te leren programmeren." - why_paragraph_1: "Tijdens het maken van Skritter wist George niet hoe hij moest programmeren en was hij constant gefrustreerd doordat hij zijn ideeën niet kon verwezelijken. Nadien probeerde hij te studeren maar de lessen gingen te traag. Ook zijn huisgenoot wou opnieuw studeren en stopte met lesgeven. Hij probeerde Codecademy maar was al snel \"verveeld\". Iedere week startte een andere vriend met Codecademy, met telkens als resultaat dat hij/zij vrij snel met de lessen stopte. We realiseerden ons dat het hetzelfde probleem was zoals we al eerder hadden opgelost met Skritter: mensen leren iets via langzame en intensieve lessen, terwijl ze eigenlijk beter een snelle en uitgebreide opleiding nodig hebben. Wij weten hoe dat op te lossen." - why_paragraph_2: "Wil je leren programmeren? Je hebt geen lessen nodig. Je moet vooral veel code schrijven en je amuseren terwijl je dit doet." - why_paragraph_3_prefix: "Dat is waar programmeren om draait. Het moet tof zijn. Niet tof zoals" - why_paragraph_3_italic: "joepie een medaille" - why_paragraph_3_center: "maar tof zoals" - why_paragraph_3_italic_caps: "NEE MAMA IK MOET DIT LEVEL AF MAKEN!" - why_paragraph_3_suffix: "Dat is waarom CodeCombat een multiplayergame is, en niet zomaar lessen gegoten in spelformaat. We zullen niet stoppen totdat jij niet meer kan stoppen--maar deze keer, is dat iets goeds." - why_paragraph_4: "Als je verslaafd gaat zijn aan een spel, dan is het beter om hieraan verslaafd te raken en een tovenaar van het technisch tijdperk te worden." - why_ending: "En hallo, het is gratis." - why_ending_url: "Start nu met toveren!" - george_description: "CEO, zakenman, web designer, game designer, en kampioen van alle beginnende programmeurs." - scott_description: "Extraordinaire programmeur, software ontwikkelaar, keukenprins en heer en meester van financiën. Scott is het meeste voor reden vatbaar." - nick_description: "Getalenteerde programmeur, excentriek gemotiveerd, een rasechte experimenteerder. Nick kan alles en kiest ervoor om CodeCombat te ontwikkelen." - jeremy_description: "Klantenservice Manager, usability tester en gemeenschapsorganisator; Je hebt waarschijnlijk al gesproken met Jeremy." - michael_description: "Programmeur, sys-admin, en technisch wonderkind, Michael is de persoon die onze servers draaiende houdt." -# matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." - - legal: - page_title: "Legaal" - opensource_intro: "CodeCombat is gratis en volledig open source." - opensource_description_prefix: "Bekijk " - github_url: "onze GitHub" - opensource_description_center: "en help ons als je wil! CodeCombat is gebouwd met de hulp van tientallen open source projecten, en wij zijn er gek op. Bekijk ook " - archmage_wiki_url: "onze Tovenaar wiki" - opensource_description_suffix: "voor een lijst van de software die dit spel mogelijk maakt." - practices_title: "Goede Respectvolle gewoonten" - practices_description: "Dit zijn onze beloften aan u, de speler, in een iets minder juridische jargon." - privacy_title: "Privacy" - privacy_description: "We zullen nooit jouw persoonlijke informatie verkopen. We willen in verloop van tijd geld verdienen dankzij aanwervingen, maar je mag op je beide oren slapen dat wij nooit jouw persoonlijke informatie zullen verspreiden aan geïnteresseerde bedrijven zonder dat jij daar expliciet mee akkoord gaat." - security_title: "Beveiliging" - security_description: "We streven ernaar om jouw persoonlijke informatie veilig te bewaren. Onze website is open en beschikbaar voor iedereen, opdat ons beveiliging systeem kan worden nagekeken en geoptimaliseerd door iedereen die dat wil. Dit alles is mogelijk doordat we volledig open source en transparant zijn." - email_title: "E-mail" - email_description_prefix: "We zullen je niet overspoelen met spam. Door" - email_settings_url: "jouw e-mail instellingen" - email_description_suffix: "of via urls in de emails die wij verzenden, kan je jouw instellingen wijzigen en ten allen tijden uitschrijven." - cost_title: "Kosten" - cost_description: "Momenteel is CodeCombat 100% gratis! Één van onze doestellingen is om dit zo te houden, opdat zoveel mogelijk mensen kunnen spelen, onafhankelijk van waar je leeft of wie je bent. Als het financieel moeilijker wordt, kan het mogelijk zijn dat we gaan beginnen met abonnementen of een prijs zetten op bepaalde zaken, maar we streven ernaar om dit te voorkomen. Met een beetje geluk zullen we dit voor altijd kunnen garanderen met:" - recruitment_title: "Aanwervingen" - recruitment_description_prefix: "Hier bij CodeCombat, ga je ontplooien tot een krachtige tovenoor-niet enkel virtueel, maar ook in het echt." - url_hire_programmers: "Niemand kan snel genoeg programmeurs aanwerven" - recruitment_description_suffix: "dus eenmaal je jouw vaardigheden hebt aangescherp en ermee akkoord gaat, zullen we jouw beste programmeer prestaties voorstellen aan duizenden werkgevers die niet kunnen wachten om jou aan te werven. Zij betalen ons een beetje, maar betalen jou" - recruitment_description_italic: "enorm veel" - recruitment_description_ending: "de site blijft volledig gratis en iedereen is gelukkig. Dat is het plan." - copyrights_title: "Auteursrechten en licenties" - contributor_title: "Licentieovereenkomst voor vrijwilligers" - contributor_description_prefix: "Alle bijdragen, zowel op de website als op onze GitHub repository, vallen onder onze" - cla_url: "CLA" - contributor_description_suffix: "waarmee je moet akkoord gaan voordat wij jouw bijdragen kunnen gebruiken." - code_title: "Code - MIT" - code_description_prefix: "Alle code in het bezit van CodeCombat of aanwezig op codecombat.com, zowel in de GitHub respository als in de codecombat.com database, is erkend onder de" - mit_license_url: "MIT licentie" - code_description_suffix: "Dit geldt ook voor code in Systemen en Componenten dat publiek is gemaakt met als doel het maken van levels." - art_title: "Art/Music - Creative Commons " - art_description_prefix: "Alle gemeenschappelijke inhoud valt onder de" - cc_license_url: "Creative Commons Attribution 4.0 Internationale Licentie" - art_description_suffix: "Gemeenschappelijke inhoud is alles dat algemeen verkrijgbaar is bij CodeCombat met als doel levels te maken. Dit omvat:" - art_music: "Muziek" - art_sound: "Geluid" - art_artwork: "Illustraties" - art_sprites: "Sprites" - art_other: "Eender wat en al het creatief werk dat niet als code aanzien wordt en verkrijgbaar is bij het aanmaken van levels." - art_access: "Momenteel is er geen universeel en gebruiksvriendelijk systeem voor het ophalen van deze assets. In het algemeen, worden deze opgehaald via de links zoals gebruikt door de website. Contacteer ons voor assistentie, of help ons met de website uit te breiden en de assets bereikbaarder te maken." - art_paragraph_1: "Voor toekenning, gelieve de naam en link naar codecombat.com te plaatsen waar dit passend is voor de vorm waarin het voorkomt. Bijvoorbeeld:" - use_list_1: "Wanneer gebruikt in een film of een ander spel, voeg codecombat.com toe in de credits." - use_list_2: "Wanneer toegepast op een website, inclusief een link naar het gebruik, bijvoorbeeld onderaan een afbeelding. Of in een algemene webpagina waar je eventueel ook andere Creative Commons werken en open source software vernoemd die je gebruikt op de website. Iets dat al duidelijk gerelateerd is met CodeCombat, zoals een blog artikel dat CodeCombat vernoemd, heeft geen aparte vermelding nodig." - art_paragraph_2: "Wanneer de gebruikte inhoud is gemaakt door een gebruiker van codecombat.com, vernoem hem/haar in plaats van ons en volg toekenningsaanwijzingen als deze in de beschrijving van de bron staan." - rights_title: "Rechten Voorbehouden" - rights_desc: "Alle rechten zijn voorbehouden voor de Levels zelf. Dit omvat:" - rights_scripts: "Scripts" - rights_unit: "Eenheid Configuratie" - rights_description: "Beschrijvingen" - rights_writings: "Literaire werken" - rights_media: "Media (geluid, muziek) en eender welke creatieve inhoud, specifiek gemaakt voor dat level en niet verkrijgbaar bij het maken van levels." - rights_clarification: "Om het duidelijk te maken, iets dat beschikbaar is in de Level editor voor het maken van levels, valt onder de CC licentie. Terwijl de inhoud gemaakt met de Level Editor of geüpload in de loop van de creatie van de levels, hier niet onder vallen." - nutshell_title: "In een notendop" - nutshell_description: "Alle middelen die wij aanbieden in de Level Editor zijn gratis te gebruiken om levels aan te maken. Wij behouden ons echter het recht voor om levels die gemaakt zijn op codecombat.com te beperken, en hier in de toekomst geld voor te vragen, moest dat ooit gebeuren." - canonical: "De Engelse versie van dit document is de definitieve en kanonieke versie. Bij verschillen tussen vertalingen heeft de Engelse versie voorrang." - - contribute: - page_title: "Bijdragen" - character_classes_title: "Karakterklassen" - introduction_desc_intro: "We hebben hoge verwachtingen over CodeCombat." - introduction_desc_pref: "We willen zijn waar programmeurs van alle niveaus komen om te leren en samen te spelen, anderen introduceren aan de wondere wereld van code, en de beste delen van de gemeenschap te reflecteren. We kunnen en willen dit niet alleen doen; wat projecten zoals GitHub, Stack Overflow en Linux groots en succesvol maken, zijn de mensen die deze software gebruiken en verbeteren. Daartoe, " - introduction_desc_github_url: "CodeCombat is volledig open source" - introduction_desc_suf: ", en we streven ernaar om op zoveel mogelijk manieren het mogelijk te maken voor u om deel te nemen en dit project van zowel jou als ons te maken." - introduction_desc_ending: "We hopen dat je met ons meedoet!" - introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy en Matt" - alert_account_message_intro: "Hallo!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." - archmage_summary: "Geïnteresserd in het werken aan game graphics, user interface design, database- en serverorganisatie, multiplayer networking, physics, geluid of game engine prestaties? Wil jij helpen een game te bouwen wat anderen leert waar jij goed in bent? We moeten nog veel doen en als jij een ervaren programmeur bent en wil ontwikkelen voor CodeCombat, dan is dit de klasse voor jou. We zouden graag je hulp hebben bij het maken van de beste programmeergame ooit." - archmage_introduction: "Een van de beste aspecten aan het maken van spelletjes is dat zij zoveel verschillende zaken omvatten. Visualisaties, geluid, real-time netwerken, sociale netwerken, en natuurlijk enkele veelvoorkomende aspecten van programmeren, van low-level database beheer en server administratie tot gebruiksvriendelijke interfaces maken. Er is veel te doen, en als jij een ervaren programmeur bent met de motivatie om je volledig te verdiepen in de details van CodeCombat, dan ben je de tovenaar die wij zoeken! We zouden graag jouw hulp krijgen bij het bouwen van het allerbeste programmeerspel ooit." - class_attributes: "Klasse kenmerken" - archmage_attribute_1_pref: "Ervaring met " - archmage_attribute_1_suf: ", of de wil om het te leren. De meeste van onze code is in deze taal. Indien je een fan van Ruby of Python bent, zal je je meteen thuis voelen! Het is zoals JavaScript, maar met een mooiere syntax." - archmage_attribute_2: "Ervaring in programmeren en individueel initiatief. We kunnen jou helpen bij het opstarten, maar kunnen niet veel tijd spenderen om je op te leiden." - how_to_join: "Hoe deel te nemen" - join_desc_1: "Iedereen kan helpen! Bekijk onze " - join_desc_2: "om te starten, en vink het vierkantje hieronder aan om jezelf te abonneren als dappere tovenaar en het laatste magische nieuws te ontvangen. Wil je met ons praten over wat er te doen is of hoe je nog meer kunt helpen? " - join_desc_3: ", of vind ons in " - join_desc_4: "en we bekijken het verder vandaar!" - join_url_email: "E-mail ons" - join_url_hipchat: "ons publiek (Engelstalig) HipChat kanaal" - more_about_archmage: "Leer meer over hoe je een Machtige Tovenaar kan worden" - archmage_subscribe_desc: "Ontvang e-mails met nieuwe programmeer mogelijkheden en aankondigingen." - artisan_summary_pref: "Wil je levels ontwerpen en CodeCombat's arsenaal vergroten? Mensen spelen sneller door onze content dan wij bij kunnen houden! Op dit moment is onze level editor nog wat beperkt, dus wees daarvan bewust. Het maken van levels zal een uitdaging zijn met een grote kans op fouten. Als jij een visie van campagnes hebt van for-loops tot" - artisan_summary_suf: ", dan is dit de klasse voor jou." - artisan_introduction_pref: "We moeten meer levels bouwen! Mensen schreeuwen om meer inhoud, en er zijn ook maar zoveel levels dat wij kunnen maken. Momenteel is jouw werkplaats level een; onze level editor wordt zelfs door ons amper gebruikt, dus wees voorzichtig. Indien je een visie hebt van een campagne, gaande van for-loops tot" - artisan_introduction_suf: ", dan is deze klasse waarschijnlijk iets voor jou." - artisan_attribute_1: "Enige ervaring in het maken van vergelijkbare inhoud. Bijvoorbeeld ervaring in het gebruiken van Blizzard's level editor. Maar dit is niet vereist!" - artisan_attribute_2: "Tot in het detail testen en opnieuw proberen staat voor jou gelijk aan plezier. Om goede levels te maken, moet je het door anderen laten spelen en bereid zijn om een hele boel aan te passen." - artisan_attribute_3: "Momenteel heb je nog veel geduld nodig, doordat onze editor nog vrij ruw is en op je zenuwen kan werken. Samenwerken met een Avonturier kan jou ook veel helpen." - artisan_join_desc: "Gebruik de Level Editor min of meer in deze volgorde:" - artisan_join_step1: "Lees de documentatie." - artisan_join_step2: "Maak een nieuw level en bestudeer reeds bestaande levels." - artisan_join_step3: "Praat met ons in ons publieke (Engelstalige) HipChat kanaal voor hulp. (optioneel)" - artisan_join_step4: "Maak een bericht over jouw level op ons forum voor feedback." - more_about_artisan: "Leer meer over hoe je een Creatieve Ambachtsman kan worden." - artisan_subscribe_desc: "Ontvang e-mails met nieuws over de Level Editor." - adventurer_summary: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien, want het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou." - adventurer_introduction: "Laten we duidelijk zijn over je rol: jij bent de tank. Jij krijgt de zware klappen te verduren. We hebben mensen nodig om spiksplinternieuwe levels uit te proberen en te kijken hoe deze beter kunnen. Je zult veel afzien.Het maken van een goede game is een lang proces en niemand doet het de eerste keer goed. Als jij dit kan verduren en een hoog uihoudingsvermogen hebt, dan is dit de klasse voor jou." - adventurer_attribute_1: "Een wil om te leren. Jij wilt leren hoe je programmeert en wij willen het jou leren. Je zal overigens zelf het meeste leren doen." - adventurer_attribute_2: "Charismatisch. Wees netjes maar duidelijk over wat er beter kan en geef suggesties over hoe het beter kan." - adventurer_join_pref: "Werk samen met een Ambachtsman of recruteer er een, of tik het veld hieronder aan om e-mails te ontvangen wanneer er nieuwe levels zijn om te testen. We zullen ook berichten over levels die beoordeeld moeten worden op onze netwerken zoals" - adventurer_forum_url: "ons forum" - adventurer_join_suf: "dus als je liever op deze manier wordt geïnformeerd, schrijf je daar in!" - more_about_adventurer: "Leer meer over hoe je een Dappere Avonturier kunt worden." - adventurer_subscribe_desc: "Ontvang e-mails wanneer er nieuwe levels zijn die getest moeten worden." - scribe_summary_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn die spelers kunnen nakijken. Op die manier zal een Ambachtsman een link kunnen geven naar een artikel dat past bij een level. Net zoiets als het " - scribe_summary_suf: " heeft gebouwd. Als jij het leuk vindt programmeerconcepten uit te leggen, dan is deze klasse iets voor jou." - scribe_introduction_pref: "CodeCombat is meer dan slechts een aantal levels, het zal ook een bron van kennis zijn en een wiki met programmeerconcepten waar levels op in kunnen gaan. Op die manier zal niet elke Ambachtsman in detail hoeven uit te leggen wat een vergelijkingsoperator is, maar een link kunnen geven naar een artikel die deze informatie al verduidelijkt voor speler. Net zoiets als het " - scribe_introduction_url_mozilla: "Mozilla Developer Network" - scribe_introduction_suf: " heeft gebouwd. Als jij het leuk vindt om programmeerconcepten uit te leggen in Markdown-vorm, dan is deze klasse wellicht iets voor jou." - scribe_attribute_1: "Taalvaardigheid is praktisch alles wat je nodig hebt. Je moet niet enkel bedreven zijn in grammatica en spelling, maar ook moeilijke ideeën kunnen overbrengen aan anderen." - contact_us_url: "Contacteer ons" - scribe_join_description: "vertel ons wat over jezelf, je ervaring met programmeren en over wat voor soort dingen je graag zou schrijven. Verder zien we wel!" - more_about_scribe: "Leer meer over het worden van een ijverige Klerk." - scribe_subscribe_desc: "Ontvang e-mails met aankondigingen over het schrijven van artikelen." - diplomat_summary: "Er is grote interesse voor CodeCombat in landen waar geen Engels wordt gesproken! We zijn op zoek naar vertalers die tijd willen spenderen aan het vertalen van de site's corpus aan woorden zodat CodeCombat zo snel mogelijk toegankelijk wordt voor de hele wereld. Als jij wilt helpen om CodeCombat internationaal maken, dan is dit de klasse voor jou." - diplomat_introduction_pref: "Dus, als er iets is wat we geleerd hebben van de " - diplomat_launch_url: "release in oktober" - diplomat_introduction_suf: "dan is het wel dat er een enorme belangstelling is voor CodeCombat in andere landen, vooral Brazilië! We zijn een groep van vertalers aan het creëren dat ijverig de ene set woorden in de andere omzet om CodeCombat zo toegankelijk mogelijk te maken in de hele wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou." - diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide talen goed te begrijpen!" - diplomat_join_pref_github: "Vind van jouw taal het locale bestand " - diplomat_github_url: "op GitHub" - diplomat_join_suf_github: ", edit het online, en submit een pull request. Daarnaast kun je hieronder aanvinken als je up-to-date wilt worden gehouden met nieuwe internationalisatie-ontwikkelingen." - more_about_diplomat: "Leer meer over het worden van een geweldige Diplomaat" - diplomat_subscribe_desc: "Ontvang e-mails over i18n ontwikkelingen en levels om te vertalen." - ambassador_summary: "We proberen een gemeenschap te bouwen en elke gemeenschap heeft een supportteam nodig wanneer er problemen zijn. We hebben chats, e-mails en sociale netwerken zodat onze gebruikers het spel kunnen leren kennen. Als jij mensen wilt helpen betrokken te raken, plezier te hebben en wat te leren programmeren, dan is dit wellicht de klasse voor jou." - ambassador_introduction: "We zijn een gemeenschap aan het uitbouwen, en jij maakt er deel van uit. We hebben Olark chatkamers, emails, en sociale netwerken met veel andere mensen waarmee je kan praten en hulp aan kan vragen over het spel of om bij te leren. Als jij mensen wil helpen en te werken nabij de hartslag van CodeCombat in het bijsturen van onze toekomstvisie, dan is dit de geknipte klasse voor jou!" - ambassador_attribute_1: "Communicatieskills. Problemen die spelers hebben kunnen identificeren en ze helpen deze op te lossen. Verder zul je ook de rest van ons geïnformeerd houden over wat de spelers zeggen, wat ze leuk vinden, wat ze minder vinden en waar er meer van moet zijn!" - ambassador_join_desc: "vertel ons wat over jezelf, wat je hebt gedaan en wat je graag zou doen. We zien verder wel!" - ambassador_join_note_strong: "Opmerking" - ambassador_join_note_desc: "Een van onze topprioriteiten is om een multiplayer te bouwen waar spelers die moeite hebben een level op te lossen een tovenaar met een hoger level kunnen oproepen om te helpen. Dit zal een goede manier zijn voor ambassadeurs om hun ding te doen. We houden je op de hoogte!" - more_about_ambassador: "Leer meer over het worden van een behulpzame Ambassadeur" - ambassador_subscribe_desc: "Ontvang e-mails met updates over ondersteuning en multiplayer-ontwikkelingen." - changes_auto_save: "Veranderingen worden automatisch opgeslagen wanneer je het vierkantje aan- of afvinkt." - diligent_scribes: "Onze ijverige Klerks:" - powerful_archmages: "Onze machtige Tovenaars:" - creative_artisans: "Onze creatieve Ambachtslieden:" - brave_adventurers: "Onze dappere Avonturiers:" - translating_diplomats: "Onze vertalende Diplomaten:" - helpful_ambassadors: "Onze behulpzame Ambassadeurs:" - - classes: - archmage_title: "Tovenaar" - archmage_title_description: "(Programmeur)" - artisan_title: "Ambachtsman" - artisan_title_description: "(Level Bouwer)" - adventurer_title: "Avonturier" - adventurer_title_description: "(Level Tester)" - scribe_title: "Klerk" - scribe_title_description: "(Redacteur)" - diplomat_title: "Diplomaat" - diplomat_title_description: "(Vertaler)" - ambassador_title: "Ambassadeur" - ambassador_title_description: "(Ondersteuning)" - - ladder: - please_login: "Log alstublieft eerst in voordat u een ladderspel speelt." - my_matches: "Mijn Wedstrijden" - simulate: "Simuleer" - simulation_explanation: "Door spellen te simuleren kan je zelf sneller beoordeeld worden!" - simulate_games: "Simuleer spellen!" - simulate_all: "RESET EN SIMULEER SPELLEN" - games_simulated_by: "Door jou gesimuleerde spellen:" - games_simulated_for: "Voor jou gesimuleerde spellen:" - games_simulated: "Spellen gesimuleerd" - games_played: "Spellen gespeeld" - ratio: "Verhouding" - leaderboard: "Leaderboard" - battle_as: "Vecht als " - summary_your: "Jouw " - summary_matches: "Wedstrijden - " - summary_wins: " Overwinningen, " - summary_losses: " Nederlagen" - rank_no_code: "Geen nieuwe code om te Beoordelen!" - rank_my_game: "Beoordeel mijn spel!" - rank_submitting: "Verzenden..." - rank_submitted: "Verzonden voor Beoordeling" - rank_failed: "Beoordeling mislukt" - rank_being_ranked: "Spel wordt Beoordeeld" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" - code_being_simulated: "Uw nieuwe code wordt gesimuleerd door andere spelers om te beoordelen. Dit wordt vernieuwd zodra nieuwe matches binnenkomen." - no_ranked_matches_pre: "Geen beoordeelde wedstrijden voor het" - no_ranked_matches_post: " team! Speel tegen enkele tegenstanders en kom terug hier om uw spel te laten beoordelen." - choose_opponent: "Kies een tegenstander" -# select_your_language: "Select your language!" - tutorial_play: "Speel de Tutorial" - tutorial_recommended: "Aanbevolen als je nog niet eerder hebt gespeeld" - tutorial_skip: "Sla Tutorial over" - tutorial_not_sure: "Niet zeker wat er aan de hand is?" - tutorial_play_first: "Speel eerst de Tutorial." - simple_ai: "Simpele AI" - warmup: "Opwarming" - vs: "tegen" - friends_playing: "Spelende Vrienden" -# log_in_for_friends: "Log in to play with your friends!" - social_connect_blurb: "Koppel je sociaal netwerk om tegen je vrienden te spelen!" - invite_friends_to_battle: "Nodig je vrienden uit om deel te nemen aan het gevecht!" - fight: "Aanvallen!" - watch_victory: "Aanschouw je overwinning!" - defeat_the: "Versla de" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" - -# ladder_prizes: -# title: "Tournament Prizes" -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - - loading_error: - could_not_load: "Fout bij het laden van de server" - connection_failure: "Verbinding mislukt." - unauthorized: "Je moet ingelogd zijn. Heb je de cookies uitgeschakeld?" - forbidden: "Je hebt hier geen toestemming voor." - not_found: "Niet gevonden." - not_allowed: "Methode niet toegestaan." - timeout: "Server timeout." - conflict: "Conflict van resources" - bad_input: "Slechte input." - server_error: "Fout van de server." - unknown: "Onbekende fout." - - resources: -# sessions: "Sessions" - your_sessions: "Jouw sessies." - level: "Level" - social_network_apis: "Sociale netwerk APIs" - facebook_status: "Facebook Status" - facebook_friends: "Facebook vrienden" - facebook_friend_sessions: "Sessies van Facebook vrienden" - gplus_friends: "G+ vrienden" - gplus_friend_sessions: "Sessies van G+ vrienden" - leaderboard: "Scorebord" - user_schema: "Gebruikersschema" - user_profile: "Gebruikersprofiel" - patches: "Patches" -# patched_model: "Source Document" - model: "Model" - system: "Systeem" -# systems: "Systems" - component: "Component" - components: "Componenten" - thang: "Thang" - thangs: "Thangs" - level_session: "Jouw Sessie" - opponent_session: "Sessie van tegenstander" - article: "Artikel" - user_names: "Gebruikersnamen" -# thang_names: "Thang Names" - files: "Bestanden" - top_simulators: "Top Simulatoren" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" - -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" - -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." - -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" - -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." diff --git a/app/locale/pt.coffee b/app/locale/pt.coffee deleted file mode 100644 index b228f67f0..000000000 --- a/app/locale/pt.coffee +++ /dev/null @@ -1,999 +0,0 @@ -module.exports = nativeDescription: "português", englishDescription: "Portuguese", translation: - common: - loading: "Carregando..." -# saving: "Saving..." - sending: "Enviando..." -# send: "Send" - cancel: "Cancelar" -# save: "Save" -# publish: "Publish" -# create: "Create" - delay_1_sec: "1 segundo" - delay_3_sec: "3 segundos" - delay_5_sec: "5 segundos" - manual: "Manual" -# fork: "Fork" - play: "Jogar" -# retry: "Retry" -# watch: "Watch" -# unwatch: "Unwatch" -# submit_patch: "Submit Patch" - -# units: -# second: "second" -# seconds: "seconds" -# minute: "minute" -# minutes: "minutes" -# hour: "hour" -# hours: "hours" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" - - modal: - close: "Fechar" - okay: "Ok" - - not_found: - page_not_found: "Página não encontrada" - - nav: - play: "Jogar" -# community: "Community" - editor: "Editor" - blog: "Blog" - forum: "Fórum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" - admin: "Administrador" - home: "Início" - contribute: "Contribuir" - legal: "Legal" - about: "Sobre" - contact: "Contate-nos" - twitter_follow: "Seguir" -# employers: "Employers" - -# versions: -# save_version_title: "Save New Version" -# new_major_version: "New Major Version" -# cla_prefix: "To save changes, first you must agree to our" -# cla_url: "CLA" -# cla_suffix: "." -# cla_agree: "I AGREE" - - login: - sign_up: "Criar conta" - log_in: "Entrar" -# logging_in: "Logging In" - log_out: "Sair" - recover: "recuperar sua conta" - -# recover: -# recover_account_title: "Recover Account" -# send_password: "Send Recovery Password" - - signup: -# create_account_title: "Create Account to Save Progress" - description: "É grátis. Precisamos apenas de umas coisinhas e você estará pronto para seguir:" - email_announcements: "Receber notícias por email." - coppa: "acima de 13 anos ou não estadunidense" - coppa_why: "(Por quê?)" - creating: "Criando a nova conta..." - sign_up: "Criar conta" - log_in: "Entre com a senha" -# social_signup: "Or, you can sign up through Facebook or G+:" -# required: "You need to log in before you can go that way." - - home: - slogan: "Aprenda a programar enquanto se diverte com um jogo." - no_ie: "CodeCombat não roda em versões mais antigas que o Internet Explorer 10. Desculpe!" - no_mobile: "CodeCombat não foi projetado para dispositivos móveis e pode não funcionar!" - play: "Jogar" -# old_browser: "Uh oh, your browser is too old to run CodeCombat. Sorry!" -# old_browser_suffix: "You can try anyway, but it probably won't work." -# campaign: "Campaign" -# for_beginners: "For Beginners" -# multiplayer: "Multiplayer" -# for_developers: "For Developers" -# javascript_blurb: "The language of the web. Great for writing websites, web apps, HTML5 games, and servers." -# python_blurb: "Simple yet powerful, Python is a great general purpose programming language." -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." - - play: - choose_your_level: "Escolha seu estágio" - adventurer_prefix: "Você pode ir para qualquer um dos estágios abaixo, ou discutir sobre eles no " - adventurer_forum: "Fórum do Aventureiro" - adventurer_suffix: "." - campaign_beginner: "Campanha Iniciante" - campaign_beginner_description: "... na qual você aprenderá a magia da programação." - campaign_dev: "Fases Difíceis Aleatórias" - campaign_dev_description: "... nas quais você aprenderá a interface enquanto faz algo um pouco mais difícil." - campaign_multiplayer: "Arenas Multijogador" - campaign_multiplayer_description: "... nas quais você programará cara-a-cara contra outros jogadores." - campaign_player_created: "Criados por Jogadores" - campaign_player_created_description: "... nos quais você batalhará contra a criatividade dos seus companheiros feiticeiros Artesãos." - level_difficulty: "Dificuldade: " -# play_as: "Play As" -# spectate: "Spectate" -# players: "players" -# hours_played: "hours played" - - contact: - contact_us: "Contate-nos" - welcome: "É bom escutar suas opiniões! Use este formulário para nos enviar um email." - contribute_prefix: "Se você se interessar em contribuir conosco, dê uma conferida na nossa " - contribute_page: "página de contribuição" - contribute_suffix: "!" - forum_prefix: "Para algo público, por favor acesse " - forum_page: "nosso fórum" - forum_suffix: " ao invés disso." - send: "Enviar opinião" -# contact_candidate: "Contact Candidate" -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." - - diplomat_suggestion: - title: "Ajude a traduzir o CodeCombat!" - sub_heading: "Nós precisamos de suas habilidades linguísticas." - pitch_body: "Desenvolvemos o CodeCombat em Inglês, mas já temos jogadores de todo o mundo. Muitos deles querem jogar em Português Brasileiro mas não falam Inglês, por isso, se você conhece os dois idiomas, por favor, considere inscrever-se para ser um Diplomata e ajudar a traduzir tanto o site do CodeCombat quanto todos os estágios para o Português Brasileiro." - missing_translations: "Até que possamos traduzir tudo para o Português Brasileiro, você lerá em Inglês quando a versão em Português Brasileiro ainda não estiver disponível." - learn_more: "Saiba mais sobre ser um Diplomata" - subscribe_as_diplomat: "Assinar como um Diplomata" - -# wizard_settings: -# title: "Wizard Settings" -# customize_avatar: "Customize Your Avatar" -# active: "Active" -# color: "Color" -# group: "Group" -# clothes: "Clothes" -# trim: "Trim" -# cloud: "Cloud" -# team: "Team" -# spell: "Spell" -# boots: "Boots" -# hue: "Hue" -# saturation: "Saturation" -# lightness: "Lightness" - - account_settings: - title: "Configurações da Conta" - not_logged_in: "Entre com seu usuário e senha ou crie uma conta para poder alterar suas configurações." - autosave: "As alterações serão salvas automaticamente." - me_tab: "Eu" - picture_tab: "Foto" -# upload_picture: "Upload a picture" - wizard_tab: "Feiticeiro" - password_tab: "Senha" - emails_tab: "Emails" -# admin: "Admin" - wizard_color: "Cor das Roupas do Feiticeiro" - new_password: "Nova Senha" - new_password_verify: "Confirmação" - email_subscriptions: "Assinaturas para Notícias por Email" -# email_subscriptions_none: "No Email Subscriptions." - email_announcements: "Notícias" - email_announcements_description: "Receba emails com as últimas notícias e desenvolvimentos do CodeCombat." -# email_notifications: "Notifications" -# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." -# email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." - contributor_emails: "Emails para as Classes de Contribuidores" - contribute_prefix: "Estamos procurando pessoas para se juntar à nossa turma! Confira a nossa " - contribute_page: "página de contribuição" - contribute_suffix: " para saber mais." - email_toggle: "Ativar todos" - error_saving: "Erro no salvamento" - saved: "Alterações Salvas" - password_mismatch: "As senhas não estão iguais" -# password_repeat: "Please repeat your password." -# job_profile: "Job Profile" -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - - account_profile: -# settings: "Settings" -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" - profile_for_prefix: "Perfil de " -# profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" - -# employers: -# hire_developers_not_credentials: "Hire developers, not credentials." -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" - - play_level: - done: "Pronto" - customize_wizard: "Personalize o feiticeiro" - home: "Início" -# stop: "Stop" -# game_menu: "Game Menu" - guide: "Guia" - restart: "Reiniciar" - goals: "Objetivos" -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" - action_timeline: "Linha do Tempo das Ações" - click_to_select: "Clique em um personagem para selecioná-lo." - reload_title: "Recarregar Todo o Código?" - reload_really: "Você tem certeza que quer reiniciar o estágio?" - reload_confirm: "Recarregar Tudo" -# victory_title_prefix: "" - victory_title_suffix: " Completado!" - victory_sign_up: "Assine para atualizações" - victory_sign_up_poke: "Quer receber as últimas novidades por email? Crie uma conta grátis e nós o manteremos informado!" - victory_rate_the_level: "Avalie o estágio: " -# victory_return_to_ladder: "Return to Ladder" - victory_play_next_level: "Jogar o próximo estágio" - victory_go_home: "Ir à página inicial" - victory_review: "Diga-nos mais!" - victory_hour_of_code_done: "Terminou?" - victory_hour_of_code_done_yes: "Sim, eu terminei minha Hora da Programação!" - guide_title: "Guia" - tome_minion_spells: "Magias dos seus subordinados" - tome_read_only_spells: "Magias não editáveis" - tome_other_units: "Outras Unidades" - tome_cast_button_castable: "Lançar" - tome_cast_button_casting: "Conjurando" - tome_cast_button_cast: "Feitiço" - tome_select_spell: "Selecione um Feitiço" - tome_select_a_thang: "Selecione alguém para " - tome_available_spells: "Feitiços Disponíveis" - hud_continue: "Continue (tecle Shift+Space)" -# spell_saved: "Spell Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# tip_insert_positions: "Shift+Click a point on the map to insert it into the spell editor." -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide at the top of the page for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_js_beginning: "JavaScript is just the beginning." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" - - game_menu: -# inventory_tab: "Inventory" -# choose_hero_tab: "Restart Level" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" - multiplayer_tab: "Multiplayer" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" - -# inventory: -# temp: "Temp" - -# choose_hero: -# temp: "Temp" - -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" - -# options: -# general_options: "General Options" -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." - -# guide: -# temp: "Temp" - - multiplayer: - multiplayer_title: "Configurações do Multiplayer" -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." - multiplayer_link_description: "Passe este link para quem você quiser que se una à partida." - multiplayer_hint_label: "Dica:" - multiplayer_hint: " Clique no link para selecionar tudo, então dê Ctrl+C ou ⌘+C para copiar o link. " - multiplayer_coming_soon: "Mais novidades no multiplayer estão chegando!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." - -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# cast_spell: "Cast current spell." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." - -# admin: -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" -# av_entities_active_instances_url: "Active Instances" -# av_entities_employer_list_url: "Employer List" -# av_other_sub_title: "Other" -# av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# lg_title: "Latest Games" -# clas: "CLAs" - -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# contribute_to_the_project: "Contribute to the project" - -# editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# randomize: "Randomize" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# delete: "Delete" -# duplicate: "Duplicate" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" -# level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" - -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" - - general: -# and: "and" - name: "Nome" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" - or: "ou" -# subject: "Subject" - email: "Email" -# password: "Password" - message: "Mensagem" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" - -# about: -# who_is_codecombat: "Who is CodeCombat?" -# why_codecombat: "Why CodeCombat?" -# who_description_prefix: "together started CodeCombat in 2013. We also created " -# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." -# who_description_ending: "Now it's time to teach people to write code." -# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." -# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_3_italic: "yay a badge" -# why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." -# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# why_ending: "And hey, it's free. " -# why_ending_url: "Start wizarding now!" -# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." -# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." -# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." -# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." -# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." -# matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." - -# legal: -# page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." -# opensource_description_prefix: "Check out " -# github_url: "our GitHub" -# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " -# archmage_wiki_url: "our Archmage wiki" -# opensource_description_suffix: "for a list of the software that makes this game possible." -# practices_title: "Respectful Best Practices" -# practices_description: "These are our promises to you, the player, in slightly less legalese." -# privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." -# security_title: "Security" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." -# email_title: "Email" -# email_description_prefix: "We will not inundate you with spam. Through" -# email_settings_url: "your email settings" -# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." -# cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." -# copyrights_title: "Copyrights and Licenses" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" -# mit_license_url: "MIT license" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." - -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." -# diligent_scribes: "Our Diligent Scribes:" -# powerful_archmages: "Our Powerful Archmages:" -# creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" -# translating_diplomats: "Our Translating Diplomats:" -# helpful_ambassadors: "Our Helpful Ambassadors:" - -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" - -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# vs: "VS" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" - -# ladder_prizes: -# title: "Tournament Prizes" -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." - -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" - -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" - -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." - -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" - -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." diff --git a/app/locale/zh.coffee b/app/locale/zh.coffee deleted file mode 100644 index 3429208e3..000000000 --- a/app/locale/zh.coffee +++ /dev/null @@ -1,999 +0,0 @@ -module.exports = nativeDescription: "中文", englishDescription: "Chinese", translation: - common: - loading: "加载中..." - saving: "正在保存..." - sending: "在发送中。。。" - send: "发送" - cancel: "退出" - save: "保存" - publish: "发布" - create: "创建" - delay_1_sec: "1 秒" - delay_3_sec: "3 秒" - delay_5_sec: "5 秒" - manual: "手册" - fork: "Fork" - play: "玩" - retry: "重试" - watch: "关注" - unwatch: "取消关注" - submit_patch: "提交补丁" - - units: - second: "秒" - seconds: "秒" - minute: "分" - minutes: "分" - hour: "时" - hours: "时" - day: "日" - days: "日" - week: "星期" - weeks: "星期" - month: "月" - months: "月" - year: "年" - years: "年" - - modal: - close: "关闭" - okay: "好" - - not_found: - page_not_found: "找不到网页" - - nav: - play: "玩" - community: "社区" - editor: "编辑" - blog: "博客" - forum: "论坛" - account: "账号" - profile: "账户资料" - stats: "状态" - code: "编码" - admin: "超级管理员" - home: "首页" - contribute: "贡献" - legal: "法律" - about: "关于" - contact: "联系我们" - twitter_follow: "关注" - employers: "招募" - - versions: - save_version_title: "保存新版本" - new_major_version: "最新主要版本" - cla_prefix: "要保存更改, 首先你必须要同意我们的" -# cla_url: "CLA" -# cla_suffix: "." - cla_agree: "我同意" - - login: - sign_up: "注册" - log_in: "登录" - logging_in: "登录中..." - log_out: "登出" - recover: "找回账户" - - recover: - recover_account_title: "帐户恢复" - send_password: "发送恢复密码" - - signup: - create_account_title: "创建新帐户保存游戏进度" - description: "免费啊。先跟你讲两点你就可以开始了" - email_announcements: "收到邮件宣告" - coppa: "13岁+ 或 非美国国籍 " - coppa_why: "为什么?" - creating: "账户在创新中" - sign_up: "注册" - log_in: "以密码登录" - social_signup: "或者, 你可以通过Facebook 或者 G+ 注册:" -# required: "You need to log in before you can go that way." - - home: - slogan: "通过游戏学习编程" - no_ie: "抱歉!Internet Explorer 9等更旧的预览器打不开此网站" - no_mobile: "CodeCombat暂时没有手机版本,可能无法运行!" - play: "玩" - old_browser: "啊噢...你的浏览器太旧啦,CodeCombat无法运行了...抱歉!" - old_browser_suffix: "你可以继续尝试下去,但是这种尝试八成没用的。。更新浏览器吧。" -# campaign: "Campaign" -# for_beginners: "For Beginners" -# multiplayer: "Multiplayer" -# for_developers: "For Developers" -# javascript_blurb: "The language of the web. Great for writing websites, web apps, HTML5 games, and servers." -# python_blurb: "Simple yet powerful, Python is a great general purpose programming language." -# coffeescript_blurb: "Nicer JavaScript syntax." -# clojure_blurb: "A modern Lisp." -# lua_blurb: "Game scripting language." -# io_blurb: "Simple but obscure." - - play: - choose_your_level: "选取难度" -# adventurer_prefix: "You can jump to any level below, or discuss the levels on " -# adventurer_forum: "the Adventurer forum" -# adventurer_suffix: "." -# campaign_beginner: "Beginner Campaign" -# campaign_beginner_description: "... in which you learn the wizardry of programming." -# campaign_dev: "Random Harder Levels" -# campaign_dev_description: "... in which you learn the interface while doing something a little harder." -# campaign_multiplayer: "Multiplayer Arenas" -# campaign_multiplayer_description: "... in which you code head-to-head against other players." -# campaign_player_created: "Player-Created" -# campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." - level_difficulty: "难度" -# play_as: "Play As" -# spectate: "Spectate" -# players: "players" -# hours_played: "hours played" - - contact: - contact_us: "联系我们" -# welcome: "Good to hear from you! Use this form to send us email. " -# contribute_prefix: "If you're interested in contributing, check out our " -# contribute_page: "contribute page" -# contribute_suffix: "!" -# forum_prefix: "For anything public, please try " - forum_page: "我们的论坛" -# forum_suffix: " instead." - send: "意见反馈" -# contact_candidate: "Contact Candidate" -# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." - - diplomat_suggestion: - title: "帮我们翻译CodeCombat" - sub_heading: "我们需要您的语言技能" - pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Chinese but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Chinese." -# missing_translations: "Until we can translate everything into {English}, you'll see English when {English} isn't available." -# learn_more: "Learn more about being a Diplomat" -# subscribe_as_diplomat: "Subscribe as a Diplomat" - - wizard_settings: - title: "巫师设定" - customize_avatar: "设置你的头像" - active: "启用" - color: "颜色" - group: "类别" - clothes: "衣服" - trim: "条纹" - cloud: "云" - team: "队伍" - spell: "魔法球" - boots: "鞋子" - hue: "色彩" - saturation: "饱和度" - lightness: "亮度" - - account_settings: - title: "账户设置" - not_logged_in: "请先登录或创建账户" - autosave: "变更已自动保存" - me_tab: "我" - picture_tab: "图片" - upload_picture: "上传图片" - wizard_tab: "巫师" - password_tab: "密码" - emails_tab: "邮箱" - admin: "管理员" - wizard_color: "巫师服装颜色" - new_password: "新密码" - new_password_verify: "验证" - email_subscriptions: "邮件订阅" -# email_subscriptions_none: "No Email Subscriptions." - email_announcements: "声明" - email_announcements_description: "获取有关CodeCombat的最新消息" - email_notifications: "通知" - email_notifications_summary: "控制个性化的自动邮件提醒,提醒您在CodeCombat的活动" - email_any_notes: "任何通知" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_news: "News" - email_recruit_notes: "工作机会" - email_recruit_notes_description: "如果你玩得的确不赖,我们会联系你,或许给你一份(更好的)工作" - contributor_emails: "贡献者邮件" - contribute_prefix: "我们正在寻求更多的参与者。参见 " - contribute_page: "贡献页" - contribute_suffix: " 寻找更多" -# email_toggle: "Toggle All" - error_saving: "保存失败" - saved: "已保存" - password_mismatch: "密码不对应" -# password_repeat: "Please repeat your password." -# job_profile: "Job Profile" -# job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." -# job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." -# sample_profile: "See a sample profile" -# view_profile: "View Your Profile" - -# account_profile: -# settings: "Settings" -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" -# profile_for_prefix: "Profile for " -# profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" -# looking_for: "Looking for:" -# last_updated: "Last updated:" -# contact: "Contact" -# active: "Looking for interview offers now" -# inactive: "Not looking for offers right now" -# complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." -# next_name: "name?" -# next_short_description: "write a short description." -# next_long_description: "describe your desired position." -# next_skills: "list at least five skills." -# next_work: "chronicle your work history." -# next_education: "recount your educational ordeals." -# next_projects: "show off up to three projects you've worked on." -# next_links: "add any personal or social links." -# next_photo: "add an optional professional photo." -# next_active: "mark yourself open to offers to show up in searches." -# example_blog: "Blog" -# example_personal_site: "Personal Site" -# links_header: "Personal Links" -# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." -# links_name: "Link Name" -# links_name_help: "What are you linking to?" -# links_link_blurb: "Link URL" -# basics_header: "Update basic info" -# basics_active: "Open to Offers" -# basics_active_help: "Want interview offers right now?" -# basics_job_title: "Desired Job Title" -# basics_job_title_help: "What role are you looking for?" -# basics_city: "City" -# basics_city_help: "City you want to work in (or live in now)." -# basics_country: "Country" -# basics_country_help: "Country you want to work in (or live in now)." -# basics_visa: "US Work Status" -# basics_visa_help: "Are you authorized to work in the US, or do you need visa sponsorship? (If you live in Canada or Australia, mark authorized.)" -# basics_looking_for: "Looking For" -# basics_looking_for_full_time: "Full-time" -# basics_looking_for_part_time: "Part-time" -# basics_looking_for_remote: "Remote" -# basics_looking_for_contracting: "Contracting" -# basics_looking_for_internship: "Internship" -# basics_looking_for_help: "What kind of developer position do you want?" -# name_header: "Fill in your name" -# name_anonymous: "Anonymous Developer" -# name_help: "Name you want employers to see, like 'Nick Winter'." -# short_description_header: "Write a short description of yourself" -# short_description_blurb: "Add a tagline to help an employer quickly learn more about you." -# short_description: "Tagline" -# short_description_help: "Who are you, and what are you looking for? 140 characters max." -# skills_header: "Skills" -# skills_help: "Tag relevant developer skills in order of proficiency." -# long_description_header: "Describe your desired position" -# long_description_blurb: "Tell employers how awesome you are and what role you want." -# long_description: "Self Description" -# long_description_help: "Describe yourself to potential employers. Keep it short and to the point. We recommend outlining the position that would most interest you. Tasteful markdown okay; 600 characters max." -# work_experience: "Work Experience" -# work_header: "Chronicle your work history" -# work_years: "Years of Experience" -# work_years_help: "How many years of professional experience (getting paid) developing software do you have?" -# work_blurb: "List your relevant work experience, most recent first." -# work_employer: "Employer" -# work_employer_help: "Name of your employer." -# work_role: "Job Title" -# work_role_help: "What was your job title or role?" -# work_duration: "Duration" -# work_duration_help: "When did you hold this gig?" -# work_description: "Description" -# work_description_help: "What did you do there? (140 chars; optional)" -# education: "Education" -# education_header: "Recount your academic ordeals" -# education_blurb: "List your academic ordeals." -# education_school: "School" -# education_school_help: "Name of your school." -# education_degree: "Degree" -# education_degree_help: "What was your degree and field of study?" -# education_duration: "Dates" -# education_duration_help: "When?" -# education_description: "Description" -# education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" -# our_notes: "CodeCombat's Notes" -# remarks: "Remarks" -# projects: "Projects" -# projects_header: "Add 3 projects" -# projects_header_2: "Projects (Top 3)" -# projects_blurb: "Highlight your projects to amaze employers." -# project_name: "Project Name" -# project_name_help: "What was the project called?" -# project_description: "Description" -# project_description_help: "Briefly describe the project." -# project_picture: "Picture" -# project_picture_help: "Upload a 230x115px or larger image showing off the project." -# project_link: "Link" -# project_link_help: "Link to the project." -# player_code: "Player Code" - -# employers: -# hire_developers_not_credentials: "Hire developers, not credentials." -# get_started: "Get Started" -# already_screened: "We've already technically screened all our candidates" -# filter_further: ", but you can also filter further:" -# filter_visa: "Visa" -# filter_visa_yes: "US Authorized" -# filter_visa_no: "Not Authorized" -# filter_education_top: "Top School" -# filter_education_other: "Other" -# filter_role_web_developer: "Web Developer" -# filter_role_software_developer: "Software Developer" -# filter_role_mobile_developer: "Mobile Developer" -# filter_experience: "Experience" -# filter_experience_senior: "Senior" -# filter_experience_junior: "Junior" -# filter_experience_recent_grad: "Recent Grad" -# filter_experience_student: "College Student" -# filter_results: "results" -# start_hiring: "Start hiring." -# reasons: "Three reasons you should hire through us:" -# everyone_looking: "Everyone here is looking for their next opportunity." -# everyone_looking_blurb: "Forget about 20% LinkedIn InMail response rates. Everyone that we list on this site wants to find their next position and will respond to your request for an introduction." -# weeding: "Sit back; we've done the weeding for you." -# weeding_blurb: "Every player that we list has been screened for technical ability. We also perform phone screens for select candidates and make notes on their profiles to save you time." -# pass_screen: "They will pass your technical screen." -# pass_screen_blurb: "Review each candidate's code before reaching out. One employer found that 5x as many of our devs passed their technical screen than hiring from Hacker News." -# make_hiring_easier: "Make my hiring easier, please." -# what: "What is CodeCombat?" -# what_blurb: "CodeCombat is a multiplayer browser programming game. Players write code to control their forces in battle against other developers. Our players have experience with all major tech stacks." -# cost: "How much do we charge?" -# cost_blurb: "We charge 15% of first year's salary and offer a 100% money back guarantee for 90 days. We don't charge for candidates who are already actively being interviewed at your company." -# candidate_name: "Name" -# candidate_location: "Location" -# candidate_looking_for: "Looking For" -# candidate_role: "Role" -# candidate_top_skills: "Top Skills" -# candidate_years_experience: "Yrs Exp" -# candidate_last_updated: "Last Updated" -# candidate_who: "Who" -# featured_developers: "Featured Developers" -# other_developers: "Other Developers" -# inactive_developers: "Inactive Developers" - -# play_level: -# done: "Done" -# customize_wizard: "Customize Wizard" -# home: "Home" -# stop: "Stop" -# game_menu: "Game Menu" -# guide: "Guide" -# restart: "Restart" -# goals: "Goals" -# success: "Success!" -# incomplete: "Incomplete" -# timed_out: "Ran out of time" -# failing: "Failing" -# action_timeline: "Action Timeline" -# click_to_select: "Click on a unit to select it." -# reload_title: "Reload All Code?" -# reload_really: "Are you sure you want to reload this level back to the beginning?" -# reload_confirm: "Reload All" -# victory_title_prefix: "" -# victory_title_suffix: " Complete" -# victory_sign_up: "Sign Up to Save Progress" -# victory_sign_up_poke: "Want to save your code? Create a free account!" -# victory_rate_the_level: "Rate the level: " -# victory_return_to_ladder: "Return to Ladder" -# victory_play_next_level: "Play Next Level" -# victory_go_home: "Go Home" -# victory_review: "Tell us more!" -# victory_hour_of_code_done: "Are You Done?" -# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" -# guide_title: "Guide" -# tome_minion_spells: "Your Minions' Spells" -# tome_read_only_spells: "Read-Only Spells" -# tome_other_units: "Other Units" -# tome_cast_button_castable: "Cast Spell" -# tome_cast_button_casting: "Casting" -# tome_cast_button_cast: "Spell Cast" -# tome_select_spell: "Select a Spell" -# tome_select_a_thang: "Select Someone for " -# tome_available_spells: "Available Spells" -# hud_continue: "Continue (shift+space)" -# spell_saved: "Spell Saved" -# skip_tutorial: "Skip (esc)" -# keyboard_shortcuts: "Key Shortcuts" -# loading_ready: "Ready!" -# tip_insert_positions: "Shift+Click a point on the map to insert it into the spell editor." -# tip_toggle_play: "Toggle play/paused with Ctrl+P." -# tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward." -# tip_guide_exists: "Click the guide at the top of the page for useful info." -# tip_open_source: "CodeCombat is 100% open source!" -# tip_beta_launch: "CodeCombat launched its beta in October, 2013." -# tip_js_beginning: "JavaScript is just the beginning." -# tip_think_solution: "Think of the solution, not the problem." -# tip_theory_practice: "In theory, there is no difference between theory and practice. But in practice, there is. - Yogi Berra" -# tip_error_free: "There are two ways to write error-free programs; only the third one works. - Alan Perlis" -# tip_debugging_program: "If debugging is the process of removing bugs, then programming must be the process of putting them in. - Edsger W. Dijkstra" -# tip_forums: "Head over to the forums and tell us what you think!" -# tip_baby_coders: "In the future, even babies will be Archmages." -# tip_morale_improves: "Loading will continue until morale improves." -# tip_all_species: "We believe in equal opportunities to learn programming for all species." -# tip_reticulating: "Reticulating spines." -# tip_harry: "Yer a Wizard, " -# tip_great_responsibility: "With great coding skill comes great debug responsibility." -# tip_munchkin: "If you don't eat your vegetables, a munchkin will come after you while you're asleep." -# tip_binary: "There are only 10 types of people in the world: those who understand binary, and those who don't." -# tip_commitment_yoda: "A programmer must have the deepest commitment, the most serious mind. ~ Yoda" -# tip_no_try: "Do. Or do not. There is no try. - Yoda" -# tip_patience: "Patience you must have, young Padawan. - Yoda" -# tip_documented_bug: "A documented bug is not a bug; it is a feature." -# tip_impossible: "It always seems impossible until it's done. - Nelson Mandela" -# tip_talk_is_cheap: "Talk is cheap. Show me the code. - Linus Torvalds" -# tip_first_language: "The most disastrous thing that you can ever learn is your first programming language. - Alan Kay" -# tip_hardware_problem: "Q: How many programmers does it take to change a light bulb? A: None, it's a hardware problem." -# time_current: "Now:" -# time_total: "Max:" -# time_goto: "Go to:" -# infinite_loop_try_again: "Try Again" -# infinite_loop_reset_level: "Reset Level" -# infinite_loop_comment_out: "Comment Out My Code" - -# game_menu: -# inventory_tab: "Inventory" -# choose_hero_tab: "Restart Level" -# save_load_tab: "Save/Load" -# options_tab: "Options" -# guide_tab: "Guide" -# multiplayer_tab: "Multiplayer" -# inventory_caption: "Equip your hero" -# choose_hero_caption: "Choose hero, language" -# save_load_caption: "... and view history" -# options_caption: "Configure settings" -# guide_caption: "Docs and tips" -# multiplayer_caption: "Play with friends!" - -# inventory: -# temp: "Temp" - -# choose_hero: -# temp: "Temp" - -# save_load: -# granularity_saved_games: "Saved" -# granularity_change_history: "History" - -# options: -# general_options: "General Options" -# volume_label: "Volume" -# music_label: "Music" -# music_description: "Turn background music on/off." -# autorun_label: "Autorun" -# autorun_description: "Control automatic code execution." -# editor_config: "Editor Config" -# editor_config_title: "Editor Configuration" -# editor_config_level_language_label: "Language for This Level" -# editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" -# editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." -# editor_config_keybindings_label: "Key Bindings" -# editor_config_keybindings_default: "Default (Ace)" -# editor_config_keybindings_description: "Adds additional shortcuts known from the common editors." -# editor_config_livecompletion_label: "Live Autocompletion" -# editor_config_livecompletion_description: "Displays autocomplete suggestions while typing." -# editor_config_invisibles_label: "Show Invisibles" -# editor_config_invisibles_description: "Displays invisibles such as spaces or tabs." -# editor_config_indentguides_label: "Show Indent Guides" -# editor_config_indentguides_description: "Displays vertical lines to see indentation better." -# editor_config_behaviors_label: "Smart Behaviors" -# editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes." - -# guide: -# temp: "Temp" - -# multiplayer: -# multiplayer_title: "Multiplayer Settings" -# multiplayer_toggle: "Enable multiplayer" -# multiplayer_toggle_description: "Allow others to join your game." -# multiplayer_link_description: "Give this link to anyone to have them join you." -# multiplayer_hint_label: "Hint:" -# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." -# multiplayer_coming_soon: "More multiplayer features to come!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." - -# keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# shift: "Shift" -# cast_spell: "Cast current spell." -# run_real_time: "Run in real time." -# continue_script: "Continue past current script." -# skip_scripts: "Skip past all skippable scripts." -# toggle_playback: "Toggle play/pause." -# scrub_playback: "Scrub back and forward through time." -# single_scrub_playback: "Scrub back and forward through time by a single frame." -# scrub_execution: "Scrub through current spell execution." -# toggle_debug: "Toggle debug display." -# toggle_grid: "Toggle grid overlay." -# toggle_pathfinding: "Toggle pathfinding overlay." -# beautify: "Beautify your code by standardizing its formatting." -# maximize_editor: "Maximize/minimize code editor." -# move_wizard: "Move your Wizard around the level." - -# admin: -# av_title: "Admin Views" -# av_entities_sub_title: "Entities" -# av_entities_users_url: "Users" -# av_entities_active_instances_url: "Active Instances" -# av_entities_employer_list_url: "Employer List" -# av_other_sub_title: "Other" -# av_other_debug_base_url: "Base (for debugging base.jade)" -# u_title: "User List" -# lg_title: "Latest Games" -# clas: "CLAs" - -# community: -# main_title: "CodeCombat Community" -# introduction: "Check out the ways you can get involved below and decide what sounds the most fun. We look forward to working with you!" -# level_editor_prefix: "Use the CodeCombat" -# level_editor_suffix: "to create and edit levels. Users have created levels for their classes, friends, hackathons, students, and siblings. If create a new level sounds intimidating you can start by forking one of ours!" -# thang_editor_prefix: "We call units within the game 'thangs'. Use the" -# thang_editor_suffix: "to modify the CodeCombat source artwork. Allow units to throw projectiles, alter the direction of an animation, change a unit's hit points, or upload your own vector sprites." -# article_editor_prefix: "See a mistake in some of our docs? Want to make some instructions for your own creations? Check out the" -# article_editor_suffix: "and help CodeCombat players get the most out of their playtime." -# find_us: "Find us on these sites" -# contribute_to_the_project: "Contribute to the project" - -# editor: -# main_title: "CodeCombat Editors" -# article_title: "Article Editor" -# thang_title: "Thang Editor" -# level_title: "Level Editor" -# achievement_title: "Achievement Editor" -# back: "Back" -# revert: "Revert" -# revert_models: "Revert Models" -# pick_a_terrain: "Pick A Terrain" -# small: "Small" -# grassy: "Grassy" -# fork_title: "Fork New Version" -# fork_creating: "Creating Fork..." -# randomize: "Randomize" -# more: "More" -# wiki: "Wiki" -# live_chat: "Live Chat" -# level_some_options: "Some Options?" -# level_tab_thangs: "Thangs" -# level_tab_scripts: "Scripts" -# level_tab_settings: "Settings" -# level_tab_components: "Components" -# level_tab_systems: "Systems" -# level_tab_thangs_title: "Current Thangs" -# level_tab_thangs_all: "All" -# level_tab_thangs_conditions: "Starting Conditions" -# level_tab_thangs_add: "Add Thangs" -# delete: "Delete" -# duplicate: "Duplicate" -# level_settings_title: "Settings" -# level_component_tab_title: "Current Components" -# level_component_btn_new: "Create New Component" -# level_systems_tab_title: "Current Systems" -# level_systems_btn_new: "Create New System" -# level_systems_btn_add: "Add System" -# level_components_title: "Back to All Thangs" -# level_components_type: "Type" -# level_component_edit_title: "Edit Component" -# level_component_config_schema: "Config Schema" -# level_component_settings: "Settings" -# level_system_edit_title: "Edit System" -# create_system_title: "Create New System" -# new_component_title: "Create New Component" -# new_component_field_system: "System" -# new_article_title: "Create a New Article" -# new_thang_title: "Create a New Thang Type" -# new_level_title: "Create a New Level" -# new_article_title_login: "Log In to Create a New Article" -# new_thang_title_login: "Log In to Create a New Thang Type" -# new_level_title_login: "Log In to Create a New Level" -# new_achievement_title: "Create a New Achievement" -# new_achievement_title_login: "Log In to Create a New Achievement" -# article_search_title: "Search Articles Here" -# thang_search_title: "Search Thang Types Here" -# level_search_title: "Search Levels Here" -# achievement_search_title: "Search Achievements" -# read_only_warning2: "Note: you can't save any edits here, because you're not logged in." -# no_achievements: "No achievements have been added for this level yet." -# achievement_query_misc: "Key achievement off of miscellanea" -# achievement_query_goals: "Key achievement off of level goals" -# level_completion: "Level Completion" - -# article: -# edit_btn_preview: "Preview" -# edit_article_title: "Edit Article" - - general: -# and: "and" - name: "名字" -# date: "Date" -# body: "Body" -# version: "Version" -# commit_msg: "Commit Message" -# version_history: "Version History" -# version_history_for: "Version History for: " -# result: "Result" -# results: "Results" -# description: "Description" - or: "或" -# subject: "Subject" - email: "邮箱" -# password: "Password" - message: "留言" -# code: "Code" -# ladder: "Ladder" -# when: "When" -# opponent: "Opponent" -# rank: "Rank" -# score: "Score" -# win: "Win" -# loss: "Loss" -# tie: "Tie" -# easy: "Easy" -# medium: "Medium" -# hard: "Hard" -# player: "Player" - -# about: -# who_is_codecombat: "Who is CodeCombat?" -# why_codecombat: "Why CodeCombat?" -# who_description_prefix: "together started CodeCombat in 2013. We also created " -# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." -# who_description_ending: "Now it's time to teach people to write code." -# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." -# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." -# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" -# why_paragraph_3_italic: "yay a badge" -# why_paragraph_3_center: "but fun like" -# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" -# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." -# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." -# why_ending: "And hey, it's free. " -# why_ending_url: "Start wizarding now!" -# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." -# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." -# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." -# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." -# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." -# matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." - -# legal: -# page_title: "Legal" -# opensource_intro: "CodeCombat is free to play and completely open source." -# opensource_description_prefix: "Check out " -# github_url: "our GitHub" -# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See " -# archmage_wiki_url: "our Archmage wiki" -# opensource_description_suffix: "for a list of the software that makes this game possible." -# practices_title: "Respectful Best Practices" -# practices_description: "These are our promises to you, the player, in slightly less legalese." -# privacy_title: "Privacy" -# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent." -# security_title: "Security" -# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems." -# email_title: "Email" -# email_description_prefix: "We will not inundate you with spam. Through" -# email_settings_url: "your email settings" -# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time." -# cost_title: "Cost" -# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:" -# recruitment_title: "Recruitment" -# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life." -# url_hire_programmers: "No one can hire programmers fast enough" -# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you" -# recruitment_description_italic: "a lot" -# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan." -# copyrights_title: "Copyrights and Licenses" -# contributor_title: "Contributor License Agreement" -# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our" -# cla_url: "CLA" -# contributor_description_suffix: "to which you should agree before contributing." -# code_title: "Code - MIT" -# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the" -# mit_license_url: "MIT license" -# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels." -# art_title: "Art/Music - Creative Commons " -# art_description_prefix: "All common content is available under the" -# cc_license_url: "Creative Commons Attribution 4.0 International License" -# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:" -# art_music: "Music" -# art_sound: "Sound" -# art_artwork: "Artwork" -# art_sprites: "Sprites" -# art_other: "Any and all other non-code creative works that are made available when creating Levels." -# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible." -# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:" -# use_list_1: "If used in a movie or another game, include codecombat.com in the credits." -# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution." -# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any." -# rights_title: "Rights Reserved" -# rights_desc: "All rights are reserved for Levels themselves. This includes" -# rights_scripts: "Scripts" -# rights_unit: "Unit configuration" -# rights_description: "Description" -# rights_writings: "Writings" -# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels." -# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not." -# nutshell_title: "In a Nutshell" -# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening." -# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence." - -# contribute: -# page_title: "Contributing" -# character_classes_title: "Character Classes" -# introduction_desc_intro: "We have high hopes for CodeCombat." -# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " -# introduction_desc_github_url: "CodeCombat is totally open source" -# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." -# introduction_desc_ending: "We hope you'll join our party!" -# introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy and Matt" -# alert_account_message_intro: "Hey there!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." -# archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." -# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." -# class_attributes: "Class Attributes" -# archmage_attribute_1_pref: "Knowledge in " -# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." -# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." -# how_to_join: "How To Join" -# join_desc_1: "Anyone can help out! Just check out our " -# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " -# join_desc_3: ", or find us in our " -# join_desc_4: "and we'll go from there!" -# join_url_email: "Email us" -# join_url_hipchat: "public HipChat room" -# more_about_archmage: "Learn More About Becoming an Archmage" -# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." -# artisan_summary_pref: "Want to design levels and expand CodeCombat's arsenal? People are playing through our content at a pace faster than we can build! Right now, our level editor is barebone, so be wary. Making levels will be a little challenging and buggy. If you have visions of campaigns spanning for-loops to" -# artisan_summary_suf: ", then this class is for you." -# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" -# artisan_introduction_suf: ", then this class might be for you." -# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" -# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." -# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" -# artisan_join_desc: "Use the Level Editor in these steps, give or take:" -# artisan_join_step1: "Read the documentation." -# artisan_join_step2: "Create a new level and explore existing levels." -# artisan_join_step3: "Find us in our public HipChat room for help." -# artisan_join_step4: "Post your levels on the forum for feedback." -# more_about_artisan: "Learn More About Becoming an Artisan" -# artisan_subscribe_desc: "Get emails on level editor updates and announcements." -# adventurer_summary: "Let us be clear about your role: you are the tank. You are going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class is for you." -# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." -# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." -# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." -# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" -# adventurer_forum_url: "our forum" -# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" -# more_about_adventurer: "Learn More About Becoming an Adventurer" -# adventurer_subscribe_desc: "Get emails when there are new levels to test." -# scribe_summary_pref: "CodeCombat is not just going to be a bunch of levels. It will also be a resource of programming knowledge that players can hook into. That way, each Artisan can link to a detailed article that for the player's edification: documentation akin to what the " -# scribe_summary_suf: " has built. If you enjoy explaining programming concepts, then this class is for you." -# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " -# scribe_introduction_url_mozilla: "Mozilla Developer Network" -# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." -# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." -# contact_us_url: "Contact us" -# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" -# more_about_scribe: "Learn More About Becoming a Scribe" -# scribe_subscribe_desc: "Get emails about article writing announcements." -# diplomat_summary: "There is a large interest in CodeCombat in other countries that do not speak English! We are looking for translators who are willing to spend their time translating the site's corpus of words so that CodeCombat is accessible across the world as soon as possible. If you'd like to help getting CodeCombat international, then this class is for you." -# diplomat_introduction_pref: "So, if there's one thing we learned from the " -# diplomat_launch_url: "launch in October" -# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." -# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" -# diplomat_join_pref_github: "Find your language locale file " -# diplomat_github_url: "on GitHub" -# diplomat_join_suf_github: ", edit it online, and submit a pull request. Also, check this box below to keep up-to-date on new internationalization developments!" -# more_about_diplomat: "Learn More About Becoming a Diplomat" -# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate." -# ambassador_summary: "We are trying to build a community, and every community needs a support team when there are troubles. We have got chats, emails, and social networks so that our users can get acquainted with the game. If you want to help people get involved, have fun, and learn some programming, then this class is for you." -# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you." -# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!" -# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!" -# ambassador_join_note_strong: "Note" -# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!" -# more_about_ambassador: "Learn More About Becoming an Ambassador" -# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments." -# changes_auto_save: "Changes are saved automatically when you toggle checkboxes." -# diligent_scribes: "Our Diligent Scribes:" -# powerful_archmages: "Our Powerful Archmages:" -# creative_artisans: "Our Creative Artisans:" -# brave_adventurers: "Our Brave Adventurers:" -# translating_diplomats: "Our Translating Diplomats:" -# helpful_ambassadors: "Our Helpful Ambassadors:" - -# classes: -# archmage_title: "Archmage" -# archmage_title_description: "(Coder)" -# artisan_title: "Artisan" -# artisan_title_description: "(Level Builder)" -# adventurer_title: "Adventurer" -# adventurer_title_description: "(Level Playtester)" -# scribe_title: "Scribe" -# scribe_title_description: "(Article Editor)" -# diplomat_title: "Diplomat" -# diplomat_title_description: "(Translator)" -# ambassador_title: "Ambassador" -# ambassador_title_description: "(Support)" - -# ladder: -# please_login: "Please log in first before playing a ladder game." -# my_matches: "My Matches" -# simulate: "Simulate" -# simulation_explanation: "By simulating games you can get your game ranked faster!" -# simulate_games: "Simulate Games!" -# simulate_all: "RESET AND SIMULATE GAMES" -# games_simulated_by: "Games simulated by you:" -# games_simulated_for: "Games simulated for you:" -# games_simulated: "Games simulated" -# games_played: "Games played" -# ratio: "Ratio" -# leaderboard: "Leaderboard" -# battle_as: "Battle as " -# summary_your: "Your " -# summary_matches: "Matches - " -# summary_wins: " Wins, " -# summary_losses: " Losses" -# rank_no_code: "No New Code to Rank" -# rank_my_game: "Rank My Game!" -# rank_submitting: "Submitting..." -# rank_submitted: "Submitted for Ranking" -# rank_failed: "Failed to Rank" -# rank_being_ranked: "Game Being Ranked" -# rank_last_submitted: "submitted " -# help_simulate: "Help simulate games?" -# code_being_simulated: "Your new code is being simulated by other players for ranking. This will refresh as new matches come in." -# no_ranked_matches_pre: "No ranked matches for the " -# no_ranked_matches_post: " team! Play against some competitors and then come back here to get your game ranked." -# choose_opponent: "Choose an Opponent" -# select_your_language: "Select your language!" -# tutorial_play: "Play Tutorial" -# tutorial_recommended: "Recommended if you've never played before" -# tutorial_skip: "Skip Tutorial" -# tutorial_not_sure: "Not sure what's going on?" -# tutorial_play_first: "Play the Tutorial first." -# simple_ai: "Simple AI" -# warmup: "Warmup" -# vs: "VS" -# friends_playing: "Friends Playing" -# log_in_for_friends: "Log in to play with your friends!" -# social_connect_blurb: "Connect and play against your friends!" -# invite_friends_to_battle: "Invite your friends to join you in battle!" -# fight: "Fight!" -# watch_victory: "Watch your victory" -# defeat_the: "Defeat the" -# tournament_ends: "Tournament ends" -# tournament_ended: "Tournament ended" -# tournament_rules: "Tournament Rules" -# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details" -# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details" -# tournament_blurb_blog: "on our blog" -# rules: "Rules" -# winners: "Winners" - -# ladder_prizes: -# title: "Tournament Prizes" -# blurb_1: "These prizes will be awarded according to" -# blurb_2: "the tournament rules" -# blurb_3: "to the top human and ogre players." -# blurb_4: "Two teams means double the prizes!" -# blurb_5: "(There will be two first place winners, two second-place winners, etc.)" -# rank: "Rank" -# prizes: "Prizes" -# total_value: "Total Value" -# in_cash: "in cash" -# custom_wizard: "Custom CodeCombat Wizard" -# custom_avatar: "Custom CodeCombat avatar" -# heap: "for six months of \"Startup\" access" -# credits: "credits" -# one_month_coupon: "coupon: choose either Rails or HTML" -# one_month_discount: "discount, 30% off: choose either Rails or HTML" -# license: "license" -# oreilly: "ebook of your choice" - -# loading_error: -# could_not_load: "Error loading from server" -# connection_failure: "Connection failed." -# unauthorized: "You need to be signed in. Do you have cookies disabled?" -# forbidden: "You do not have the permissions." -# not_found: "Not found." -# not_allowed: "Method not allowed." -# timeout: "Server timeout." -# conflict: "Resource conflict." -# bad_input: "Bad input." -# server_error: "Server error." -# unknown: "Unknown error." - -# resources: -# sessions: "Sessions" -# your_sessions: "Your Sessions" -# level: "Level" -# social_network_apis: "Social Network APIs" -# facebook_status: "Facebook Status" -# facebook_friends: "Facebook Friends" -# facebook_friend_sessions: "Facebook Friend Sessions" -# gplus_friends: "G+ Friends" -# gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" -# user_schema: "User Schema" -# user_profile: "User Profile" -# patches: "Patches" -# patched_model: "Source Document" -# model: "Model" -# system: "System" -# systems: "Systems" -# component: "Component" -# components: "Components" -# thang: "Thang" -# thangs: "Thangs" -# level_session: "Your Session" -# opponent_session: "Opponent Session" -# article: "Article" -# user_names: "User Names" -# thang_names: "Thang Names" -# files: "Files" -# top_simulators: "Top Simulators" -# source_document: "Source Document" -# document: "Document" -# sprite_sheet: "Sprite Sheet" -# employers: "Employers" -# candidates: "Candidates" -# candidate_sessions: "Candidate Sessions" -# user_remark: "User Remark" -# user_remarks: "User Remarks" -# versions: "Versions" -# items: "Items" -# wizard: "Wizard" -# achievement: "Achievement" -# clas: "CLAs" -# play_counts: "Play Counts" - -# delta: -# added: "Added" -# modified: "Modified" -# deleted: "Deleted" -# moved_index: "Moved Index" -# text_diff: "Text Diff" -# merge_conflict_with: "MERGE CONFLICT WITH" -# no_changes: "No Changes" - -# user: -# stats: "Stats" -# singleplayer_title: "Singleplayer Levels" -# multiplayer_title: "Multiplayer Levels" -# achievements_title: "Achievements" -# last_played: "Last Played" -# status: "Status" -# status_completed: "Completed" -# status_unfinished: "Unfinished" -# no_singleplayer: "No Singleplayer games played yet." -# no_multiplayer: "No Multiplayer games played yet." -# no_achievements: "No Achievements earned yet." -# favorite_prefix: "Favorite language is " -# favorite_postfix: "." - -# achievements: -# last_earned: "Last Earned" -# amount_achieved: "Amount" -# achievement: "Achievement" -# category_contributor: "Contributor" -# category_miscellaneous: "Miscellaneous" -# category_levels: "Levels" -# category_undefined: "Uncategorized" -# current_xp_prefix: "" -# current_xp_postfix: " in total" -# new_xp_prefix: "" -# new_xp_postfix: " earned" -# left_xp_prefix: "" -# left_xp_infix: " until level " -# left_xp_postfix: "" - -# account: -# recently_played: "Recently Played" -# no_recent_games: "No games played during the past two weeks." diff --git a/app/templates/contribute/diplomat.jade b/app/templates/contribute/diplomat.jade index 2ddc3c53b..53e8d0cd7 100644 --- a/app/templates/contribute/diplomat.jade +++ b/app/templates/contribute/diplomat.jade @@ -55,7 +55,7 @@ block content // TODO: collect CodeCombat userids for these guys so we can include a tiled list ul.diplomats each stats, languageCode in languageStats - if !(languageCode.indexOf('-') != -1 && stats.completion < 0.02 && !stats.diplomats.length) + if !(stats.completion < 0.02 && !stats.diplomats.length) li a(href=stats.githubURL) span= stats.englishDescription diff --git a/app/views/contribute/DiplomatView.coffee b/app/views/contribute/DiplomatView.coffee index 214e36253..f63286183 100644 --- a/app/views/contribute/DiplomatView.coffee +++ b/app/views/contribute/DiplomatView.coffee @@ -40,30 +40,25 @@ module.exports = class DiplomatView extends ContributeClassView 'en-GB': [] # English (UK), English (UK) 'en-AU': [] # English (AU), English (AU) ru: ['fess89', 'ser-storchak', 'Mr A', 'a1ip', 'iulianR', 'EagleTA', 'kisik21', 'Shpionus', 'kerradus', 'ImmortalJoker'] # русский язык, Russian - de: ['Dirk', 'faabsen', 'HiroP0', 'Anon', 'bkimminich', 'bahuma20', 'djsmith85', 'greyhusky'] # Deutsch, German - 'de-DE': [] # Deutsch (Deutschland), German (Germany) - 'de-AT': [] # Deutsch (Österreich), German (Austria) - 'de-CH': [] # Deutsch (Schweiz), German (Switzerland) - es: [] # español, Spanish + 'de-DE': ['Dirk', 'faabsen', 'HiroP0', 'Anon', 'bkimminich', 'bahuma20', 'domenukk', 'dkundel'] # Deutsch (Deutschland), German (Germany) + 'de-AT': ['djsmith85'] # Deutsch (Österreich), German (Austria) + 'de-CH': ['greyhusky'] # Deutsch (Schweiz), German (Switzerland) 'es-419': ['Jesús Ruppel', 'Matthew Burt', 'Mariano Luzza', '2xG', ] # español (América Latina), Spanish (Latin America) 'es-ES': ['Matthew Burt', 'DanielRodriguezRivero', 'Anon', 'Pouyio', '3rr3s3v3n', 'OviiiOne', 'Vindurrin'] # español (ES), Spanish (Spain) - zh: ['Adam23', 'spacepope', 'yangxuan8282', 'Cheng Zheng', 'yfdyh000', 'julycoolwind', 'Vic020', 'onion7878', 'BonnieBBS', '1c7', 'benojan', 'ZephyrSails'] # 中文, Chinese - 'zh-HANS': [] # 简体中文, Chinese (Simplified) - 'zh-HANT': [] # 繁体中文, Chinese (Traditional) + 'zh-HANS': ['Adam23', 'spacepope', 'yangxuan8282', 'Cheng Zheng', 'yfdyh000', 'julycoolwind', 'Vic020', 'onion7878', 'BonnieBBS', '1c7', 'ZephyrSails'] # 简体中文, Chinese (Simplified) + 'zh-HANT': ['gintau', 'Adam23'] # 繁体中文, Chinese (Traditional) 'zh-WUU-HANS': [] # 吴语, Wuu (Simplified) - 'zh-WUU-HANT': [] # 吳語, Wuu (Traditional) + 'zh-WUU-HANT': ['benojan'] # 吳語, Wuu (Traditional) fr: ['Xeonarno', 'Elfisen', 'Armaldio', 'MartinDelille', 'pstweb', 'veritable', 'jaybi', 'xavismeh', 'Anon', 'Feugy', 'dc55028', 'ChrisLightman', 'Oaugereau'] # français, French ja: ['g1itch', 'kengos', 'treby'] # 日本語, Japanese ar: ['ahmed80dz', '5y'] # العربية, Arabic - pt: [] # português, Portuguese 'pt-BR': ['Gutenberg Barros', 'Kieizroe', 'Matthew Burt', 'brunoporto', 'cassiocardoso', 'Bia41'] # português do Brasil, Portuguese (Brazil) 'pt-PT': ['Matthew Burt', 'ReiDuKuduro', 'Imperadeiro98', 'batista', 'ProgramadorLucas', 'gutierri'] # Português (Portugal), Portuguese (Portugal) pl: ['Anon', 'Kacper Ciepielewski', 'TigroTigro', 'kvasnyk'] # język polski, Polish it: ['flauta', 'AlessioPaternoster'] # italiano, Italian tr: ['Nazım Gediz Aydındoğmuş', 'cobaimelan', 'wakeup', 'gediz', 'ilisyus'] # Türkçe, Turkish - nl: ['Glen De Cauwsemaecker', 'Guido Zuidhof', 'Ruben Vereecken', 'Jasper D\'haene'] # Nederlands, Dutch - 'nl-BE': [] # Nederlands (België), Dutch (Belgium) - 'nl-NL': [] # Nederlands (Nederland), Dutch (Netherlands) + 'nl-BE': ['Glen De Cauwsemaecker', 'Ruben Vereecken'] # Nederlands (België), Dutch (Belgium) + 'nl-NL': ['Jasper D\'haene', 'Guido Zuidhof'] # Nederlands (Nederland), Dutch (Netherlands) fa: ['Reza Habibi (Rehb)'] # فارسی, Persian cs: ['vanous'] # čeština, Czech sv: ['iamhj'] # Svenska, Swedish diff --git a/server/routes/languages.coffee b/server/routes/languages.coffee index b29cc8fdb..e6f411a04 100644 --- a/server/routes/languages.coffee +++ b/server/routes/languages.coffee @@ -26,6 +26,11 @@ module.exports.languageCodesLower = languageCodesLower = (code.toLowerCase() for # Keep keys lower-case for matching and values with second subtag uppercase like i18next expects languageAliases = 'en': 'en-US' + 'de': 'de-DE' + 'es': 'es-ES' + 'zh': 'zh-HANS' + 'pt': 'pt-PT' + 'nl': 'nl-NL' 'zh-cn': 'zh-HANS' 'zh-hans-cn': 'zh-HANS' @@ -39,6 +44,7 @@ languageAliases = 'zh-mo': 'zh-HANT' 'zh-hant-mo': 'zh-HANT' + module.exports.languageCodeFromAcceptedLanguages = languageCodeFromAcceptedLanguages = (acceptedLanguages) -> for lang in acceptedLanguages ? [] code = languageAliases[lang.toLowerCase()] From aa54cc32d1fc947e6a080269783f702911bb5387 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 20:27:58 -0700 Subject: [PATCH 71/98] Fixed #1458. --- app/Router.coffee | 1 + app/styles/common/top_nav.sass | 53 +++++++++++++++++++-------------- app/styles/home.sass | 42 +++++++------------------- app/templates/base.jade | 24 ++++----------- app/templates/home.jade | 12 ++++---- app/views/admin/BaseView.coffee | 6 ++++ app/views/kinds/RootView.coffee | 1 - 7 files changed, 61 insertions(+), 78 deletions(-) create mode 100644 app/views/admin/BaseView.coffee diff --git a/app/Router.coffee b/app/Router.coffee index 1fc29d92a..b826fcd89 100644 --- a/app/Router.coffee +++ b/app/Router.coffee @@ -31,6 +31,7 @@ module.exports = class CocoRouter extends Backbone.Router 'admin/files': go('admin/FilesView') 'admin/level-sessions': go('admin/LevelSessionsView') 'admin/users': go('admin/UsersView') + 'admin/base': go('admin/BaseView') 'beta': go('HomeView') diff --git a/app/styles/common/top_nav.sass b/app/styles/common/top_nav.sass index 42e7fce47..f63c32476 100644 --- a/app/styles/common/top_nav.sass +++ b/app/styles/common/top_nav.sass @@ -45,6 +45,9 @@ a.disabled padding: 4px 20px 0px 20px margin-left: -20px + .navbar-nav + float: right + .navbuttontext, .fancy-select .trigger font-size: 20px font-weight: 400 @@ -190,32 +193,38 @@ a.disabled color: #ebebeb padding: 8px 20px + .navbar-toggle + display: none -#mobile-nav - display: none @media only screen and (max-width: 800px) #top-nav - display: none - #mobile-nav display: inline - a.navbar-brand - padding: 4px 20px 0px 20px - button.navbar-toggle background: #483a2d border: 2px solid #2f261d - span.icon-bar - background: #F9E612 - - ul li - font-family: 'Bangers', cursive - font-weight: normal - color: #fff - font-size: 25px - margin-top: 5px - margin-bottom: 5px - .header-font - color: #fff - .footer-link-text - width: 100% - display: ineline + display: inline-block + + span.icon-bar + background: #F9E612 + + a.navbar-brand + padding: 4px 20px 0px 20px + margin-left: 0 + + .navbar-nav + float: none + margin: 0 0 20px 0 + overflow: visible + + .dropdown-menu + background-color: white + position: absolute + + .btn, .fancy-select + margin-bottom: 10px + + .btn, .fancy-select + float: none + + .fancy-select .options + right: auto diff --git a/app/styles/home.sass b/app/styles/home.sass index 60155888e..c41497a2d 100644 --- a/app/styles/home.sass +++ b/app/styles/home.sass @@ -7,24 +7,6 @@ text-align: center margin-top: 0 - #front-screenshot - margin: 15px 0 40px 150px - - #trailer-wrapper - position: relative - margin: 0 auto 40px - width: 950px - iframe - display: block - margin: 0 auto - position: relative - top: 8px - img - position: absolute - left: 0 - top: 0 - pointer-events: none - .game-mode-wrapper position: relative margin-bottom: 60px @@ -244,29 +226,27 @@ #home-view #site-slogan font-size: 30px - #trailer-wrapper - display: none - #front-screenshot - display: none - #mobile-trailer-wrapper - display: inline-block - - width: 100% - iframe - display: block - margin: 0 auto + margin-bottom: 30px + .code-languages + .col-sm-6, .col-sm-3 + margin-top: 30px + .code-language + margin: 0px auto .game-mode-wrapper width: 100% img width: 100% .play-text position: absolute - right: 45px - bottom: 0px + right: 15px + bottom: -15px color: $yellow font-size: 50px font-family: Bangers @include transition(color .10s linear) + .code-language-logo + right: 0px + top: 5px h1 text-align: center diff --git a/app/templates/base.jade b/app/templates/base.jade index 076900376..2d36b5d0e 100644 --- a/app/templates/base.jade +++ b/app/templates/base.jade @@ -1,33 +1,19 @@ body #fb-root block header - .nav.navbar.navbar-fixed-top#mobile-nav + .nav.navbar.navbar-fixed-top#top-nav .content.clearfix .navbar-header - button.navbar-toggle(type="button" data-toggle="collapse" data-target="#collapsible-navbar") + button.navbar-toggle(type="button" data-toggle="collapse" data-target=".navbar-nav.collapse") span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar - - a.navbar-brand(href='/') - img(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat") - .collapse.navbar-collapse#collapsible-navbar - ul.nav.navbar-nav - li.play - a.header-font(href='/play', data-i18n="nav.play") Levels - li - a.header-font(href='/community', data-i18n="nav.community") Community - - .nav.navbar.navbar-fixed-top#top-nav - .content.clearfix - .navbar-header + a.navbar-brand(href='/') img(src="/images/pages/base/logo.png", title="CodeCombat - Learn how to code by playing a game", alt="CodeCombat") - select.language-dropdown - - ul(class='navbar-link-text').nav.navbar-nav.pull-right + ul(class='navbar-link-text').nav.navbar-nav.navbar-collapse.collapse li.play a.header-font(href='/play', data-i18n="nav.play") Levels li @@ -65,6 +51,8 @@ body span(data-i18n="login.log_in") Log In span.spr.spl / span(data-i18n="login.sign_up") Create Account + li + select.language-dropdown block outer_content #outer-content-wrapper(class=showBackground ? 'show-background' : '') diff --git a/app/templates/home.jade b/app/templates/home.jade index a52067427..16698fb66 100644 --- a/app/templates/home.jade +++ b/app/templates/home.jade @@ -6,7 +6,7 @@ block content .code-languages .primary-code-languages.row - .col-md-6 + .col-sm-6 .code-language#javascript(data-code-language='javascript') .code-wizard h2 JavaScript @@ -17,7 +17,7 @@ block content span.spr= playCount span(data-i18n="resources.sessions") sessions - .col-md-6 + .col-sm-6 .code-language.beta#python(data-code-language='python') .code-wizard .code-language-beta @@ -30,7 +30,7 @@ block content span(data-i18n="resources.sessions") sessions .secondary-code-languages.row - .col-md-3 + .col-sm-3 .code-language.beta#coffeescript(data-code-language='coffeescript') .code-language-logo .code-wizard @@ -43,7 +43,7 @@ block content span.spr= playCount span(data-i18n="resources.sessions") sessions - .col-md-3 + .col-sm-3 .code-language.beta#clojure(data-code-language='clojure') .code-language-logo .code-wizard @@ -56,7 +56,7 @@ block content span.spr= playCount span(data-i18n="resources.sessions") sessions - .col-md-3 + .col-sm-3 .code-language.beta#lua(data-code-language='lua') .code-language-logo .code-wizard @@ -69,7 +69,7 @@ block content span.spr= playCount span(data-i18n="resources.sessions") sessions - .col-md-3 + .col-sm-3 .code-language.beta#io(data-code-language='io', title="Careful: Io is still quite buggy") .code-language-logo .code-wizard diff --git a/app/views/admin/BaseView.coffee b/app/views/admin/BaseView.coffee new file mode 100644 index 000000000..ca58690c0 --- /dev/null +++ b/app/views/admin/BaseView.coffee @@ -0,0 +1,6 @@ +RootView = require 'views/kinds/RootView' +template = require 'templates/base' + +module.exports = class BaseView extends RootView + id: 'base-view' + template: template diff --git a/app/views/kinds/RootView.coffee b/app/views/kinds/RootView.coffee index 63045eb4d..02723f8ea 100644 --- a/app/views/kinds/RootView.coffee +++ b/app/views/kinds/RootView.coffee @@ -66,7 +66,6 @@ module.exports = class RootView extends CocoView #location.hash = '' #location.hash = hash @renderScrollbar() - #@$('.antiscroll-wrap').antiscroll() # not yet, buggy getRenderData: -> c = super() From a284c8eb378bc9bb527196331ee91ab1406e8873 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 21:16:01 -0700 Subject: [PATCH 72/98] Fixed #1428. --- app/styles/editor/level/component/edit.sass | 5 ++++- app/styles/editor/level/system/edit.sass | 5 ++++- app/templates/editor/level/component/edit.jade | 8 ++++---- app/templates/editor/level/system/edit.jade | 6 ++++-- .../level/components/LevelComponentEditView.coffee | 9 +++++++-- .../editor/level/systems/LevelSystemEditView.coffee | 9 +++++++-- 6 files changed, 30 insertions(+), 12 deletions(-) diff --git a/app/styles/editor/level/component/edit.sass b/app/styles/editor/level/component/edit.sass index e4301dbde..978c286e5 100644 --- a/app/styles/editor/level/component/edit.sass +++ b/app/styles/editor/level/component/edit.sass @@ -26,4 +26,7 @@ left: 0 right: 0 bottom: 0 - top: 0px \ No newline at end of file + top: 0px + + #patch-component-button + display: none diff --git a/app/styles/editor/level/system/edit.sass b/app/styles/editor/level/system/edit.sass index f8ca710d1..92f015d9e 100644 --- a/app/styles/editor/level/system/edit.sass +++ b/app/styles/editor/level/system/edit.sass @@ -26,4 +26,7 @@ left: 0 right: 0 bottom: 0 - top: 0px \ No newline at end of file + top: 0px + + #patch-system-button + display: none diff --git a/app/templates/editor/level/component/edit.jade b/app/templates/editor/level/component/edit.jade index 66a8375c2..86e11d051 100644 --- a/app/templates/editor/level/component/edit.jade +++ b/app/templates/editor/level/component/edit.jade @@ -13,6 +13,10 @@ nav.navbar.navbar-default(role='navigation') span.navbar-brand= editTitle ul.nav.navbar-nav.navbar-right + if !component.hasWriteAccess() + li#patch-component-button + a(data-i18n="[title]common.submit_patch") + span.glyphicon-floppy-disk.glyphicon li.dropdown a(data-toggle='dropdown') span.glyphicon-chevron-down.glyphicon @@ -27,10 +31,6 @@ nav.navbar.navbar-default(role='navigation') span.unwatch.secret span.glyphicon.glyphicon-eye-close span.spl Unwatch - - if !component.hasWriteAccess() - li#patch-component-button - a(data-i18n="common.submit_patch") Submit Patch if !me.get('anonymous') li#create-new-component-button a(data-i18n="editor.level_component_b_new") Create New Component diff --git a/app/templates/editor/level/system/edit.jade b/app/templates/editor/level/system/edit.jade index 555709fca..f5c64f6ab 100644 --- a/app/templates/editor/level/system/edit.jade +++ b/app/templates/editor/level/system/edit.jade @@ -11,6 +11,10 @@ nav.navbar.navbar-default(role='navigation') a(href="#system-patches" data-toggle="tab" data-i18n="resources.patches")#system-patches-tab Patches ul.nav.navbar-nav.navbar-right + if !me.isAdmin() + li#patch-system-button + a(data-i18n="[title]common.submit_patch") + span.glyphicon-floppy-disk.glyphicon li.dropdown a(data-toggle='dropdown') span.glyphicon-chevron-down.glyphicon @@ -24,8 +28,6 @@ nav.navbar.navbar-default(role='navigation') span.unwatch.secret span.glyphicon.glyphicon-eye-close span.spl Unwatch - li#patch-system-button - a(data-i18n="common.submit_patch") Submit Patch if me.isAdmin() li#create-new-system a(data-i18n="editor.level_system_btn_new") Create New System diff --git a/app/views/editor/level/components/LevelComponentEditView.coffee b/app/views/editor/level/components/LevelComponentEditView.coffee index 20b8d19a2..898c88f0f 100644 --- a/app/views/editor/level/components/LevelComponentEditView.coffee +++ b/app/views/editor/level/components/LevelComponentEditView.coffee @@ -42,6 +42,7 @@ module.exports = class LevelComponentEditView extends CocoView @buildCodeEditor() @patchesView = @insertSubView(new PatchesView(@levelComponent), @$el.find('.patches-view')) @$el.find('#component-watch-button').find('> span').toggleClass('secret') if @levelComponent.watching() + @updatePatchButton() buildSettingsTreema: -> data = _.pick @levelComponent.attributes, (value, key) => key in @editableSettings @@ -65,7 +66,7 @@ module.exports = class LevelComponentEditView extends CocoView # Make sure it validates first? for key, value of @componentSettingsTreema.data @levelComponent.set key, value unless key is 'js' # will compile code if needed - null + @updatePatchButton() buildConfigSchemaTreema: -> configSchema = @levelComponent.get 'configSchema' @@ -91,6 +92,7 @@ module.exports = class LevelComponentEditView extends CocoView onConfigSchemaEdited: => @levelComponent.set 'configSchema', @configSchemaTreema.data + @updatePatchButton() buildCodeEditor: -> @destroyAceEditor(@editor) @@ -108,7 +110,10 @@ module.exports = class LevelComponentEditView extends CocoView onEditorChange: => return if @destroyed @levelComponent.set 'code', @editor.getValue() - null + @updatePatchButton() + + updatePatchButton: -> + @$el.find('#patch-component-button').toggle Boolean @levelComponent.hasLocalChanges() endEditing: (e) -> Backbone.Mediator.publish 'editor:level-component-editing-ended', component: @levelComponent diff --git a/app/views/editor/level/systems/LevelSystemEditView.coffee b/app/views/editor/level/systems/LevelSystemEditView.coffee index 1a91a214d..e50a37e4a 100644 --- a/app/views/editor/level/systems/LevelSystemEditView.coffee +++ b/app/views/editor/level/systems/LevelSystemEditView.coffee @@ -37,6 +37,7 @@ module.exports = class LevelSystemEditView extends CocoView @buildConfigSchemaTreema() @buildCodeEditor() @patchesView = @insertSubView(new PatchesView(@levelSystem), @$el.find('.patches-view')) + @updatePatchButton() buildSettingsTreema: -> data = _.pick @levelSystem.attributes, (value, key) => key in @editableSettings @@ -59,7 +60,7 @@ module.exports = class LevelSystemEditView extends CocoView # Make sure it validates first? for key, value of @systemSettingsTreema.data @levelSystem.set key, value unless key is 'js' # will compile code if needed - null + @updatePatchButton() buildConfigSchemaTreema: -> treemaOptions = @@ -76,6 +77,7 @@ module.exports = class LevelSystemEditView extends CocoView onConfigSchemaEdited: => @levelSystem.set 'configSchema', @configSchemaTreema.data + @updatePatchButton() buildCodeEditor: -> @destroyAceEditor(@editor) @@ -92,7 +94,10 @@ module.exports = class LevelSystemEditView extends CocoView onEditorChange: => @levelSystem.set 'code', @editor.getValue() - null + @updatePatchButton() + + updatePatchButton: -> + @$el.find('#patch-system-button').toggle Boolean @levelSystem.hasLocalChanges() endEditing: (e) -> Backbone.Mediator.publish 'editor:level-system-editing-ended', system: @levelSystem From 5f132e55ba508d461c100fc249e86ee077858446 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sat, 30 Aug 2014 23:04:45 -0700 Subject: [PATCH 73/98] Fixed #1414. Also started lazily loading certain level editor tabs. --- app/locale/en.coffee | 1 + app/schemas/subscriptions/editor.coffee | 3 +- .../editor/level/level-feedback-view.sass | 9 ++++ app/templates/editor/level/edit.jade | 5 ++ .../editor/level/level-feedback-view.jade | 17 ++++++ .../editor/level/related-achievements.jade | 5 +- .../docs/ComponentsDocumentationView.coffee | 13 +++++ .../docs/SystemsDocumentationView.coffee | 13 +++++ app/views/editor/level/LevelEditView.coffee | 8 +-- .../editor/level/LevelFeedbackView.coffee | 53 +++++++++++++++++++ .../level/RelatedAchievementsView.coffee | 18 +++++-- server/levels/level_handler.coffee | 21 +++++--- 12 files changed, 146 insertions(+), 20 deletions(-) create mode 100644 app/styles/editor/level/level-feedback-view.sass create mode 100644 app/templates/editor/level/level-feedback-view.jade create mode 100644 app/views/editor/level/LevelFeedbackView.coffee diff --git a/app/locale/en.coffee b/app/locale/en.coffee index f1752f6a3..f18a579fa 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -953,6 +953,7 @@ achievement: "Achievement" clas: "CLAs" play_counts: "Play Counts" + feedback: "Feedback" delta: added: "Added" diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee index 196723623..99b47d6db 100644 --- a/app/schemas/subscriptions/editor.coffee +++ b/app/schemas/subscriptions/editor.coffee @@ -5,7 +5,8 @@ module.exports = major: {type: 'boolean'} commitMessage: {type: 'string'} - 'editor:view-switched': c.object {title: 'Level View Switched', description: 'Published whenever the view switches'} + 'editor:view-switched': c.object {title: 'Level View Switched', description: 'Published whenever the view switches'}, + targetURL: {type: 'string'} 'editor:level-component-editing-ended': c.object {required: ['component']}, component: {type: 'object'} diff --git a/app/styles/editor/level/level-feedback-view.sass b/app/styles/editor/level/level-feedback-view.sass new file mode 100644 index 000000000..6ebc29d38 --- /dev/null +++ b/app/styles/editor/level/level-feedback-view.sass @@ -0,0 +1,9 @@ +#level-feedback-view + height: 100% + + .user-feedback-list + height: 90% + height: -webkit-calc(100% - 80px) + height: calc(100% - 80px) + overflow-y: scroll + overflow-x: hidden diff --git a/app/templates/editor/level/edit.jade b/app/templates/editor/level/edit.jade index 3405164bd..f6d2fb663 100644 --- a/app/templates/editor/level/edit.jade +++ b/app/templates/editor/level/edit.jade @@ -37,6 +37,9 @@ block header a(href="#related-achievements-view", data-toggle="tab") Achievements li a(href="#editor-level-documentation", data-toggle="tab", data-i18n="editor.level_tab_docs") Documentation + li + a(href="#level-feedback-view", data-toggle="tab") + .glyphicon.glyphicon-star .navbar-header span.navbar-brand #{level.attributes.name} @@ -137,6 +140,8 @@ block outer_content div.tab-pane#components-documentation-view div.tab-pane#systems-documentation-view + div.tab-pane#level-feedback-view + div#error-view block footer diff --git a/app/templates/editor/level/level-feedback-view.jade b/app/templates/editor/level/level-feedback-view.jade new file mode 100644 index 000000000..b2ed47634 --- /dev/null +++ b/app/templates/editor/level/level-feedback-view.jade @@ -0,0 +1,17 @@ +h2 Average Rating: #{averageRating.toFixed(2)}, #{totalRatings} ratings +ul.user-feedback-list.list-group + for feedback in allFeedback + li.list-group-item + each i in Array(feedback.rating) + .glyphicon.glyphicon-star + if feedback.rating < 5 + each i in Array(5 - feedback.rating) + .glyphicon.glyphicon-star-empty + span.spl.spr - + em= moment(new Date(feedback.created)).fromNow() + span.spl.spr - + a(href="/user/#{feedback.creator}") + strong= feedback.creatorName + if feedback.review + span.spr : + span= feedback.review diff --git a/app/templates/editor/level/related-achievements.jade b/app/templates/editor/level/related-achievements.jade index e0312d779..3d0ff9c34 100644 --- a/app/templates/editor/level/related-achievements.jade +++ b/app/templates/editor/level/related-achievements.jade @@ -1,9 +1,6 @@ - button.btn.btn-primary#new-achievement-button(disabled=me.isAdmin() === true ? undefined : "true" data-i18n="editor.new_achievement_title") Create a New Achievement -if achievements.loading - h2(data-i18n="common.loading") Loading... -else if ! achievements.models.length +if !achievements.models.length .panel .panel-body p(data-i18n="editor.no_achievements") No achievements added for this level yet. diff --git a/app/views/docs/ComponentsDocumentationView.coffee b/app/views/docs/ComponentsDocumentationView.coffee index 5265cdfce..8d5b01be7 100644 --- a/app/views/docs/ComponentsDocumentationView.coffee +++ b/app/views/docs/ComponentsDocumentationView.coffee @@ -17,10 +17,19 @@ module.exports = class ComponentsDocumentationView extends CocoView events: 'click #toggle-all-component-code': 'onToggleAllCode' + subscriptions: + 'editor:view-switched': 'onViewSwitched' + constructor: (options) -> super(options) @componentDocs = new ComponentDocsCollection() + @loadDocs() unless options.lazy + + loadDocs: -> + return if @loadingDocs @supermodel.loadCollection @componentDocs, 'components' + @loadingDocs = true + @render() getRenderData: -> c = super() @@ -33,3 +42,7 @@ module.exports = class ComponentsDocumentationView extends CocoView @collapsed = not @collapsed @$el.find('.collapse').collapse(if @collapsed then 'hide' else 'show') @$el.find('#toggle-all-component-code').toggleClass 'active', not @collapsed + + onViewSwitched: (e) -> + return unless e.targetURL is '#editor-level-documentation' + @loadDocs() diff --git a/app/views/docs/SystemsDocumentationView.coffee b/app/views/docs/SystemsDocumentationView.coffee index bb016a8c1..8c3e251fe 100644 --- a/app/views/docs/SystemsDocumentationView.coffee +++ b/app/views/docs/SystemsDocumentationView.coffee @@ -17,10 +17,19 @@ module.exports = class SystemsDocumentationView extends CocoView events: 'click #toggle-all-system-code': 'onToggleAllCode' + subscriptions: + 'editor:view-switched': 'onViewSwitched' + constructor: (options) -> super(options) @systemDocs = new SystemDocsCollection() + @loadDocs() unless options.lazy + + loadDocs: -> + return if @loadingDocs @supermodel.loadCollection @systemDocs, 'systems' + @loadingDocs = true + @render() getRenderData: -> c = super() @@ -33,3 +42,7 @@ module.exports = class SystemsDocumentationView extends CocoView @collapsed = not @collapsed @$el.find('.collapse').collapse(if @collapsed then 'hide' else 'show') @$el.find('#toggle-all-system-code').toggleClass 'active', not @collapsed + + onViewSwitched: (e) -> + return unless e.targetURL is '#editor-level-documentation' + @loadDocs() diff --git a/app/views/editor/level/LevelEditView.coffee b/app/views/editor/level/LevelEditView.coffee index 2df56a719..a6d6af179 100644 --- a/app/views/editor/level/LevelEditView.coffee +++ b/app/views/editor/level/LevelEditView.coffee @@ -19,6 +19,7 @@ RelatedAchievementsView = require 'views/editor/level/RelatedAchievementsView' VersionHistoryView = require './modals/LevelVersionsModal' ComponentsDocumentationView = require 'views/docs/ComponentsDocumentationView' SystemsDocumentationView = require 'views/docs/SystemsDocumentationView' +LevelFeedbackView = require 'views/editor/level/LevelFeedbackView' storage = require 'lib/storage' module.exports = class LevelEditView extends RootView @@ -76,15 +77,16 @@ module.exports = class LevelEditView extends RootView super() return unless @supermodel.finished() @$el.find('a[data-toggle="tab"]').on 'shown.bs.tab', (e) => - Backbone.Mediator.publish 'editor:view-switched', {} + Backbone.Mediator.publish 'editor:view-switched', {targetURL: $(e.target).attr('href')} @insertSubView new ThangsTabView world: @world, supermodel: @supermodel, level: @level @insertSubView new SettingsTabView supermodel: @supermodel @insertSubView new ScriptsTabView world: @world, supermodel: @supermodel, files: @files @insertSubView new ComponentsTabView supermodel: @supermodel @insertSubView new SystemsTabView supermodel: @supermodel @insertSubView new RelatedAchievementsView supermodel: @supermodel, level: @level - @insertSubView new ComponentsDocumentationView # Don't give it the supermodel, it'll pollute it! - @insertSubView new SystemsDocumentationView # Don't give it the supermodel, it'll pollute it! + @insertSubView new ComponentsDocumentationView lazy: true # Don't give it the supermodel, it'll pollute it! + @insertSubView new SystemsDocumentationView lazy: true # Don't give it the supermodel, it'll pollute it! + @insertSubView new LevelFeedbackView level: @level Backbone.Mediator.publish 'editor:level-loaded', level: @level @showReadOnly() if me.get('anonymous') diff --git a/app/views/editor/level/LevelFeedbackView.coffee b/app/views/editor/level/LevelFeedbackView.coffee new file mode 100644 index 000000000..64e2ba8c7 --- /dev/null +++ b/app/views/editor/level/LevelFeedbackView.coffee @@ -0,0 +1,53 @@ +CocoView = require 'views/kinds/CocoView' +CocoCollection = require 'collections/CocoCollection' +template = require 'templates/editor/level/level-feedback-view' +Level = require 'models/Level' +LevelFeedback = require 'models/LevelFeedback' + +class LevelFeedbackCollection extends CocoCollection + model: LevelFeedback + initialize: (models, options) -> + super models, options + @url = "/db/level/#{options.level.get('slug')}/all_feedback" + + comparator: (a, b) -> + score = 0 + score -= 9001900190019001 if a.get('creator') is me.id + score += 9001900190019001 if b.get('creator') is me.id + score -= new Date(a.get 'created') + score -= -(new Date(b.get 'created')) + score -= 900190019001 if a.get('review') + score += 900190019001 if b.get('review') + if score < 0 then -1 else (if score > 0 then 1 else 0) + +module.exports = class LevelFeedbackView extends CocoView + id: 'level-feedback-view' + template: template + className: 'tab-pane' + + subscriptions: + 'editor:view-switched': 'onViewSwitched' + + constructor: (options) -> + super options + + getRenderData: (context={}) -> + context = super(context) + if @allFeedback + context.allFeedback = (m.attributes for m in @allFeedback.models when @allFeedback.models.length < 20 or m.get('review')) + context.averageRating = _.reduce((m.get('rating') for m in @allFeedback.models), (acc, x) -> acc + x) / @allFeedback.models.length + context.totalRatings = @allFeedback.models.length + else + context.allFeedback = [] + context.averageRating = 0 + context.totalRatings = 0 + context.loading = true + context.moment = moment + context + + onViewSwitched: (e) -> + # Lazily load. + return unless e.targetURL is '#level-feedback-view' + unless @allFeedback + @allFeedback = @supermodel.loadCollection(new LevelFeedbackCollection(null, level: @options.level), 'feedback').model + @render() diff --git a/app/views/editor/level/RelatedAchievementsView.coffee b/app/views/editor/level/RelatedAchievementsView.coffee index ccb9969eb..c4bf75e3c 100644 --- a/app/views/editor/level/RelatedAchievementsView.coffee +++ b/app/views/editor/level/RelatedAchievementsView.coffee @@ -13,17 +13,20 @@ module.exports = class RelatedAchievementsView extends CocoView events: 'click #new-achievement-button': 'makeNewAchievement' + subscriptions: + 'editor:view-switched': 'onViewSwitched' + constructor: (options) -> super options @level = options.level @relatedID = @level.get('original') @achievements = new RelatedAchievementsCollection @relatedID - @supermodel.loadCollection @achievements, 'achievements' - onLoaded: -> - console.debug 'related achievements loaded' - @achievements.loading = false - super() + loadAchievements: -> + return if @loadingAchievements + @supermodel.loadCollection @achievements, 'achievements' + @loadingAchievements = true + @render() getRenderData: -> c = super() @@ -38,3 +41,8 @@ module.exports = class RelatedAchievementsView extends CocoView modal = new NewAchievementModal model: Achievement, modelLabel: 'Achievement', level: @level modal.once 'model-created', @onNewAchievementSaved @openModalView modal + + onViewSwitched: (e) -> + # Lazily load. + return unless e.targetURL is '#related-achievements-view' + @loadAchievements() diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index df3c4c979..f51f6574f 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -37,6 +37,7 @@ LevelHandler = class LevelHandler extends Handler return @getMyLeaderboardRank(req, res, args[0]) if args[1] is 'leaderboard_rank' return @getMySessions(req, res, args[0]) if args[1] is 'my_sessions' return @getFeedback(req, res, args[0]) if args[1] is 'feedback' + return @getAllFeedback(req, res, args[0]) if args[1] is 'all_feedback' return @getRandomSessionPair(req, res, args[0]) if args[1] is 'random_session_pair' return @getLeaderboardFacebookFriends(req, res, args[0]) if args[1] is 'leaderboard_facebook_friends' return @getLeaderboardGPlusFriends(req, res, args[0]) if args[1] is 'leaderboard_gplus_friends' @@ -266,18 +267,24 @@ LevelHandler = class LevelHandler extends Handler if map.length != 2 then return @sendDatabaseError res, 'There aren\'t sessions of 2 teams, so cannot choose random opponents!' @sendSuccess res, sessions - getFeedback: (req, res, id) -> + getFeedback: (req, res, levelID) -> return @sendNotFoundError(res) unless req.user - @fetchLevelByIDAndHandleErrors id, req, res, (err, level) => + @doGetFeedback req, res, levelID, false + + getAllFeedback: (req, res, levelID) -> + @doGetFeedback req, res, levelID, true + + doGetFeedback: (req, res, levelID, multiple) -> + @fetchLevelByIDAndHandleErrors levelID, req, res, (err, level) => feedbackQuery = - creator: mongoose.Types.ObjectId(req.user.id.toString()) 'level.original': level.original.toString() 'level.majorVersion': level.version.major - - Feedback.findOne(feedbackQuery).exec (err, doc) => + feedbackQuery.creator = mongoose.Types.ObjectId(req.user.id.toString()) unless multiple + fn = if multiple then 'find' else 'findOne' + Feedback[fn](feedbackQuery).exec (err, result) => return @sendDatabaseError(res, err) if err - return @sendNotFoundError(res) unless doc? - @sendSuccess(res, doc) + return @sendNotFoundError(res) unless result? + @sendSuccess(res, result) getPlayCountsBySlugs: (req, res) -> # This is hella slow (4s on my box), so relying on some dumb caching for it. From ef9a983e300d2f85464a272cc2e31b2f39fb78ca Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sun, 31 Aug 2014 12:21:25 -0700 Subject: [PATCH 74/98] Made ItemView more general. Upgraded Bootstrap from 3.1 to 3.2. Fixed a few bugs. --- app/models/ThangType.coffee | 76 +++++++++---------- app/styles/base.sass | 8 ++ app/styles/game-menu/inventory-view.sass | 7 +- app/styles/game-menu/item-view.sass | 4 +- .../editor/level/level-feedback-view.jade | 2 +- app/templates/game-menu/item-view.jade | 19 +++-- .../play/level/modal/keyboard_shortcuts.jade | 40 +++++----- app/treema-ext.coffee | 1 - .../editor/level/LevelFeedbackView.coffee | 2 +- .../editor/thang/ThangTypeEditView.coffee | 1 + app/views/game-menu/InventoryView.coffee | 6 +- app/views/game-menu/ItemView.coffee | 10 ++- bower.json | 2 +- 13 files changed, 99 insertions(+), 79 deletions(-) diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index be48d2e74..6dd21c750 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -200,6 +200,7 @@ module.exports = class ThangType extends CocoModel $('').attr('src', src) getPortraitSource: (spriteOptionsOrKey, size=100) -> + return @getPortraitURL() if @get 'rasterIcon' stage = @getPortraitStage(spriteOptionsOrKey, size) stage?.toDataURL() @@ -274,43 +275,42 @@ module.exports = class ThangType extends CocoModel return itemComponentRef?.config?.slots or [] getFrontFacingStats: -> - stats = [] - for component in @get('components') or [] + components = @get('components') or [] + unless itemConfig = _.find(components, original: LevelComponent.ItemID)?.config + console.warn @get('name'), 'is not an item, but you are asking for its stats.' + return props: [], stats: {} + props = itemConfig.programmableProperties ? [] + props = props.concat itemConfig.moreProgrammableProperties ? [] + stats = {} + for stat, modifiers of itemConfig.stats ? {} + stats[stat] = @formatStatDisplay stat, modifiers + for stat in itemConfig.extraHUDProperties ? [] + stats[stat] = null # Find it in the other Components. + for component in components continue unless config = component.config - if config.attackDamage - stats.push { name: 'Attack Damage', value: config.attackDamage } - if config.attackRange - stats.push { name: 'Attack Range', value: "#{config.attackRange}m" } - if config.cooldown - stats.push { name: 'Cooldown', value: "#{config.cooldown}s" } - if config.maxSpeed - stats.push { name: 'Speed', value: "#{config.maxSpeed}m/s" } - if config.maxAcceleration - stats.push { name: 'Acceleration', value: "#{config.maxAcceleration}m/s^2" } - if config.stats - for stat, value of config.stats - if value.factor - value = "x#{value.factor}" - if value.addend and value.addend > 0 - value = "+#{value.addend}" - if value.addend and value.addend < 0 - value = "#{value.addend}" - if value.setTo - value = "=#{value.setTo}" - if stat is 'maxHealth' - stats.push { name: 'Health', value: value } - if stat is 'healthReplenishRate' - stats.push { name: 'Regen', value: value } - if config.programmableProperties - props = config.programmableProperties - if props.length - stats.push { name: 'Allows', value: props.join(', ') } - if config.visualRange - value = config.visualRange - if value is 9001 then value is "Infinite" - stats.push { name: 'Visual Range', value: "#{value}m"} + for stat, value of stats when not value? + value = config[stat] + continue unless value? + stats[stat] = @formatStatDisplay stat, setTo: value if config.programmableSnippets - snippets = config.programmableSnippets - if snippets.length - stats.push { name: 'Snippets', value: snippets.join(', ') } - stats + props = props.concat config.programmableSnippets + props: props, stats: stats + + formatStatDisplay: (name, modifiers) -> + name = {maxHealth: 'Health', maxSpeed: 'Speed', healthReplenishRate: 'Regeneration'}[name] ? name + name = _.string.humanize name + format = '' + format = 'm' if /(range|radius|distance)$/i.test name + format ||= 's' if /cooldown$/i.test name + format ||= 'm/s' if /speed$/i.test name + format ||= '/s' if /(regeneration| rate)$/i.test name + value = modifiers.setTo + value = value.join ', ' if _.isArray value + display = [] + display.push "#{value}#{format}" if value? + display.push "+#{modifiers.addend}#{format}" if modifiers.addend > 0 + display.push "#{modifiers.addend}#{format}" if modifiers.addend < 0 + display.push "x#{modifiers.factor}" if modifiers.factor? and modifiers.factor isnt 1 + display = display.join ', ' + display = display.replace /9001m?/, 'Infinity' + name: name, display: display diff --git a/app/styles/base.sass b/app/styles/base.sass index 443073605..56c1a37bd 100644 --- a/app/styles/base.sass +++ b/app/styles/base.sass @@ -302,3 +302,11 @@ a[data-toggle="coco-modal"] bottom: 0px right: 0px z-index: 1001 + +kbd + padding: 2px 4px + font-size: 90% + color: #fff + background-color: #333 + border-radius: 3px + @include box-shadow(inset 0 -1px 0 rgba(0, 0, 0, .25)) diff --git a/app/styles/game-menu/inventory-view.sass b/app/styles/game-menu/inventory-view.sass index afb9e6f02..18dee227a 100644 --- a/app/styles/game-menu/inventory-view.sass +++ b/app/styles/game-menu/inventory-view.sass @@ -1,9 +1,6 @@ $selected-area-height: 150px @import "../bootstrap/mixins" -//#inventory-view * - - #inventory-view position: relative height: 600px @@ -80,6 +77,8 @@ $selected-area-height: 150px padding: 10px height: 100% width: 49% + display: flex + align-items: center img width: 100px @@ -102,4 +101,4 @@ $selected-area-height: 150px height: $swap-item-height font-size: 24px line-height: 24px - display: inline-block \ No newline at end of file + display: inline-block diff --git a/app/styles/game-menu/item-view.sass b/app/styles/game-menu/item-view.sass index 259334682..d37b85e57 100644 --- a/app/styles/game-menu/item-view.sass +++ b/app/styles/game-menu/item-view.sass @@ -8,7 +8,7 @@ .item-info margin-left: 45px - + ul margin-top: 5px - padding-left: 20px + diff --git a/app/templates/editor/level/level-feedback-view.jade b/app/templates/editor/level/level-feedback-view.jade index b2ed47634..be266b474 100644 --- a/app/templates/editor/level/level-feedback-view.jade +++ b/app/templates/editor/level/level-feedback-view.jade @@ -11,7 +11,7 @@ ul.user-feedback-list.list-group em= moment(new Date(feedback.created)).fromNow() span.spl.spr - a(href="/user/#{feedback.creator}") - strong= feedback.creatorName + strong= feedback.creatorName || 'Anoner' if feedback.review span.spr : span= feedback.review diff --git a/app/templates/game-menu/item-view.jade b/app/templates/game-menu/item-view.jade index 04dc9e350..c22c34e27 100644 --- a/app/templates/game-menu/item-view.jade +++ b/app/templates/game-menu/item-view.jade @@ -2,11 +2,20 @@ img(src=item.getPortraitURL()).img-thumbnail div.item-info if includes.name strong= item.get('name') - if includes.stats - - var stats = item.getFrontFacingStats() - ul - for stat in stats - li #{stat.name}: #{stat.value} + if includes.stats || (includes.props && props.length) + ul.list-unstyled + if includes.stats + for stat, prop in stats + if stat.display == 'true' + li= stat.name + else + li #{stat.name}: #{stat.display} + if includes.props && props.length + li Grants: + for prop in props + | + code= prop + .clearfix \ No newline at end of file diff --git a/app/templates/play/level/modal/keyboard_shortcuts.jade b/app/templates/play/level/modal/keyboard_shortcuts.jade index 06815d09e..afca41d17 100644 --- a/app/templates/play/level/modal/keyboard_shortcuts.jade +++ b/app/templates/play/level/modal/keyboard_shortcuts.jade @@ -6,71 +6,71 @@ block modal-header-content block modal-body-content dl.dl-horizontal dt(title=shift + "+" + enter) - code ⇧+#{enter} + kbd ⇧+#{enter} dd(data-i18n="keyboard_shortcuts.cast_spell") Cast current spell. dl.dl-horizontal dt(title=ctrlName + "+" + shift + "+" + enter) - code #{ctrl}+⇧+#{enter} + kbd #{ctrl}+⇧+#{enter} dd(data-i18n="keyboard_shortcuts.run_real_time") Run in real time. dl.dl-horizontal dt(title=shift + "+" + space) - code ⇧+#{space} + kbd ⇧+#{space} dd(data-i18n="keyboard_shortcuts.continue_script") Continue past current script. dl.dl-horizontal dt(title=escapeKey) - code Esc + kbd Esc dd(data-i18n="keyboard_shortcuts.skip_scripts") Skip past all skippable scripts. dl.dl-horizontal dt(title=ctrlName + "+P") - code #{ctrl}+P + kbd #{ctrl}+P dd(data-i18n="keyboard_shortcuts.toggle_playback") Toggle play/pause. dl.dl-horizontal dt(title=ctrlName + "+[, " + ctrlName + "+]") - code #{ctrl}+[ + kbd #{ctrl}+[ | , - code #{ctrl}+] + kbd #{ctrl}+] dd(data-i18n="keyboard_shortcuts.scrub_playback") Scrub back and forward through time. dl.dl-horizontal dt(title=ctrlName + "+[, " + ctrlName + "+]") - code #{ctrl}+⇧+[ + kbd #{ctrl}+⇧+[ | , - code #{ctrl}+⇧+] + kbd #{ctrl}+⇧+] dd(data-i18n="keyboard_shortcuts.single_scrub_playback") Scrub back and forward through time by a single frame. dl.dl-horizontal dt(title=ctrlName + "+" + altName + "+[, " + ctrlName + "+" + altName + "+]") - code #{ctrl}+#{alt}+[ + kbd #{ctrl}+#{alt}+[ | , - code #{ctrl}+#{alt}+] + kbd #{ctrl}+#{alt}+] dd(data-i18n="keyboard_shortcuts.scrub_execution") Scrub through current spell execution. dl.dl-horizontal dt(title=ctrlName + "+\\") - code #{ctrl}+\ + kbd #{ctrl}+\ dd(data-i18n="keyboard_shortcuts.toggle_debug") Toggle debug display. dl.dl-horizontal dt(title=ctrlName + "+G") - code #{ctrl}+G + kbd #{ctrl}+G dd(data-i18n="keyboard_shortcuts.toggle_grid") Toggle grid overlay. dl.dl-horizontal dt(title=ctrlName + "+O") - code #{ctrl}+O + kbd #{ctrl}+O dd(data-i18n="keyboard_shortcuts.toggle_pathfinding") Toggle pathfinding overlay. dl.dl-horizontal dt(title=ctrlName + "+" + shift + "+B") - code #{ctrl}+⇧+B + kbd #{ctrl}+⇧+B dd(data-i18n="keyboard_shortcuts.beautify") Beautify your code by standardizing its formatting. dl.dl-horizontal dt(title=ctrlName + "+" + shift + "+M") - code #{ctrl}+⇧+M + kbd #{ctrl}+⇧+M dd(data-i18n="keyboard_shortcuts.maximize_editor") Maximize/minimize code editor. dl.dl-horizontal dt(title="Arrow keys") - code ← + kbd ← | , - code → + kbd → | , - code ↑ + kbd ↑ | , - code ↓ + kbd ↓ dd(data-i18n="keyboard_shortcuts.move_wizard") Move your Wizard around the level. block modal-footer-content diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 667438393..92217b89c 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -363,7 +363,6 @@ class LatestVersionReferenceNode extends TreemaNode m = @settings.supermodel.getModelByOriginalAndMajorVersion(m.constructor, data.original, data.majorVersion) if @instance and not m m = @instance - m.url = -> urlGoingFor @settings.supermodel.registerModel(m) return 'Unknown' unless m return @modelToString(m) diff --git a/app/views/editor/level/LevelFeedbackView.coffee b/app/views/editor/level/LevelFeedbackView.coffee index 64e2ba8c7..360528d55 100644 --- a/app/views/editor/level/LevelFeedbackView.coffee +++ b/app/views/editor/level/LevelFeedbackView.coffee @@ -35,7 +35,7 @@ module.exports = class LevelFeedbackView extends CocoView context = super(context) if @allFeedback context.allFeedback = (m.attributes for m in @allFeedback.models when @allFeedback.models.length < 20 or m.get('review')) - context.averageRating = _.reduce((m.get('rating') for m in @allFeedback.models), (acc, x) -> acc + x) / @allFeedback.models.length + context.averageRating = _.reduce((m.get('rating') for m in @allFeedback.models), (acc, x) -> acc + (x ? 5)) / (@allFeedback.models.length or 1) context.totalRatings = @allFeedback.models.length else context.allFeedback = [] diff --git a/app/views/editor/thang/ThangTypeEditView.coffee b/app/views/editor/thang/ThangTypeEditView.coffee index 3fcfc4832..09b1af817 100644 --- a/app/views/editor/thang/ThangTypeEditView.coffee +++ b/app/views/editor/thang/ThangTypeEditView.coffee @@ -88,6 +88,7 @@ module.exports = class ThangTypeEditView extends RootView @insertSubView(new ThangTypeColorsTabView(@thangType)) @patchesView = @insertSubView(new PatchesView(@thangType), @$el.find('.patches-view')) @showReadOnly() if me.get('anonymous') + @updatePortrait() initComponents: => options = diff --git a/app/views/game-menu/InventoryView.coffee b/app/views/game-menu/InventoryView.coffee index ab74fa46d..32faff66c 100644 --- a/app/views/game-menu/InventoryView.coffee +++ b/app/views/game-menu/InventoryView.coffee @@ -25,7 +25,7 @@ module.exports = class InventoryView extends CocoView super(arguments...) @items = new CocoCollection([], {model: ThangType}) @equipment = options.equipment or @options.session?.get('heroConfig')?.inventory or {} - @items.url = '/db/thang.type?view=items&project=name,description,components,original' + @items.url = '/db/thang.type?view=items&project=name,description,components,original,rasterIcon' @supermodel.loadCollection(@items, 'items') onLoaded: -> @@ -201,7 +201,7 @@ module.exports = class InventoryView extends CocoView showSelectedSlotItem: (item) -> if not @selectedEquippedItemView @selectedEquippedItemView = new ItemView({ - item: item, includes: {name: true, stats: true}}) + item: item, includes: {name: true, stats: true, props: true}}) @insertSubView(@selectedEquippedItemView, @$el.find('#selected-equipped-item .item-view-stub')) else @@ -215,7 +215,7 @@ module.exports = class InventoryView extends CocoView showSelectedAvailableItem: (item) -> if not @selectedAvailableItemView @selectedAvailableItemView = new ItemView({ - item: item, includes: {name: true, stats: true}}) + item: item, includes: {name: true, stats: true, props: true}}) @insertSubView(@selectedAvailableItemView, @$el.find('#selected-available-item .item-view-stub')) else diff --git a/app/views/game-menu/ItemView.coffee b/app/views/game-menu/ItemView.coffee index 8ff1b3612..0c143460b 100644 --- a/app/views/game-menu/ItemView.coffee +++ b/app/views/game-menu/ItemView.coffee @@ -3,9 +3,9 @@ template = require 'templates/game-menu/item-view' module.exports = class ItemView extends CocoView className: 'item-view' - + template: template - + initialize: (options) -> super(arguments...) @item = options.item @@ -15,7 +15,11 @@ module.exports = class ItemView extends CocoView c = super() c.item = @item c.includes = @includes + if @includes.props or @includes.stats + {props, stats} = @item.getFrontFacingStats() + c.props = props + c.stats = stats c - + afterRender: -> @$el.data('item-id', @item.id) diff --git a/bower.json b/bower.json index 84356e053..b5b07df5e 100644 --- a/bower.json +++ b/bower.json @@ -41,7 +41,7 @@ "nanoscroller": "~0.8.0", "treema": "https://github.com/codecombat/treema.git#master", "jquery.tablesorter": "~2", - "bootstrap": "~3.1.1", + "bootstrap": "~3.2.0", "validated-backbone-mediator": "~0.1.3", "jquery.browser": "~0.0.6", "zatanna": "~0.0.6", From 9b2ee45a4d082013ce1e23b27c9bb8ca72a9c719 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sun, 31 Aug 2014 12:27:52 -0700 Subject: [PATCH 75/98] Added DPS to item view. --- app/models/ThangType.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee index 6dd21c750..c74373c57 100644 --- a/app/models/ThangType.coffee +++ b/app/models/ThangType.coffee @@ -292,6 +292,9 @@ module.exports = class ThangType extends CocoModel value = config[stat] continue unless value? stats[stat] = @formatStatDisplay stat, setTo: value + if stat is 'attackDamage' + dps = (value / (config.cooldown or 0.5)).toFixed(1) + stats[stat].display += " (#{dps} DPS)" if config.programmableSnippets props = props.concat config.programmableSnippets props: props, stats: stats From dc81e0d84b04f102e3c5d794d5e5008607eea327 Mon Sep 17 00:00:00 2001 From: MajkPascal Date: Sun, 31 Aug 2014 22:31:28 +0200 Subject: [PATCH 76/98] Update pl.coffee --- app/locale/pl.coffee | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/locale/pl.coffee b/app/locale/pl.coffee index f81940642..ba39fc531 100644 --- a/app/locale/pl.coffee +++ b/app/locale/pl.coffee @@ -44,14 +44,14 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish nav: play: "Graj" -# community: "Community" + community: "Społeczność" editor: "Edytor" blog: "Blog" forum: "Forum" -# account: "Account" -# profile: "Profile" -# stats: "Stats" -# code: "Code" + account: "Konto" + profile: "Profil" + stats: "Statystyki" + code: "Kod" admin: "Admin" home: "Główna" contribute: "Współpraca" @@ -90,7 +90,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish sign_up: "Zarejestruj" log_in: "zaloguj się używając hasła" social_signup: "lub zaloguj się używając konta Facebook lub G+:" -# required: "You need to log in before you can go that way." + required: "Musisz się zalogować zanim przejdziesz dalej." home: slogan: "Naucz się programowania grając" @@ -126,7 +126,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish level_difficulty: "Poziom trudności: " play_as: "Graj jako " spectate: "Oglądaj" -# players: "players" + players: "graczy" # hours_played: "hours played" contact: @@ -153,13 +153,13 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish wizard_settings: title: "Ustawienia czarodzieja" customize_avatar: "Personalizuj swój awatar" -# active: "Active" -# color: "Color" -# group: "Group" + active: "Aktywuj" + color: "Kolor" + group: "Rodzaj" clothes: "Ubrania" trim: "Dodatki" cloud: "Chmura" -# team: "Team" + team: "Drużyna" spell: "Zaklęcie" boots: "Buty" hue: "Odcień" @@ -172,7 +172,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish autosave: "Zmiany zapisują się automatycznie" me_tab: "Ja" picture_tab: "Zdjęcie" -# upload_picture: "Upload a picture" + upload_picture: "Wgraj zdjęcie" wizard_tab: "Czarodziej" password_tab: "Hasło" emails_tab: "Powiadomienia" @@ -210,7 +210,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish settings: "Ustawienia" edit_profile: "Edytuj Profil" # done_editing: "Done Editing" - profile_for_prefix: "Profil" + profile_for_prefix: "Profil " profile_for_suffix: "" # featured: "Featured" # not_featured: "Not Featured" @@ -852,7 +852,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish no_ranked_matches_pre: "Brak ocenionych pojedynków dla drużyny " no_ranked_matches_post: " ! Zagraj przeciwko kilku oponentom i wróc tutaj, aby uzyskać ocenę gry." choose_opponent: "Wybierz przeciwnika" -# select_your_language: "Select your language!" + select_your_language: "Wybierz swój język!" tutorial_play: "Rozegraj samouczek" tutorial_recommended: "Zalecane, jeśli wcześniej nie grałeś" tutorial_skip: "Pomiń samouczek" From 495247661ba6113e6320b10b79b9dca9c93e39e9 Mon Sep 17 00:00:00 2001 From: George Saines Date: Sun, 31 Aug 2014 15:21:30 -0700 Subject: [PATCH 77/98] bringing contribute avatars up to speed --- .../pages/contribute/archmage/alex_small.png | Bin 8050 -> 0 bytes .../pages/contribute/archmage/becca_small.png | Bin 7835 -> 0 bytes .../pages/contribute/archmage/ben_small.png | Bin 9154 -> 0 bytes .../pages/contribute/archmage/brad_small.png | Bin 9790 -> 0 bytes .../pages/contribute/archmage/chloe_small.png | Bin 6787 -> 0 bytes .../pages/contribute/archmage/dan_small.png | Bin 8849 -> 0 bytes .../contribute/archmage/deepak_small.png | Bin 7813 -> 0 bytes .../pages/contribute/archmage/glen_small.png | Bin 12057 -> 0 bytes .../pages/contribute/archmage/ken_small.png | Bin 10687 -> 0 bytes .../pages/contribute/archmage/laura_small.png | Bin 8487 -> 0 bytes .../contribute/archmage/mischa_small.png | Bin 6717 -> 0 bytes .../pages/contribute/archmage/paul_small.png | Bin 8648 -> 0 bytes .../contribute/archmage/rachel_small.png | Bin 8846 -> 0 bytes .../contribute/archmage/ronald_small.png | Bin 9411 -> 0 bytes .../contribute/archmage/sebastien_small.png | Bin 8277 -> 0 bytes .../contribute/archmage/shiying_small.png | Bin 8900 -> 0 bytes .../pages/contribute/archmage/tom_small.png | Bin 9604 -> 0 bytes .../pages/contribute/artisan/andrew_small.png | Bin 8857 -> 0 bytes .../contribute/artisan/axandre_small.png | Bin 9109 -> 0 bytes .../pages/contribute/artisan/derek_small.png | Bin 9942 -> 0 bytes .../contribute/artisan/katharine_small.png | Bin 9426 -> 0 bytes .../pages/contribute/artisan/rob_small.png | Bin 7320 -> 0 bytes app/views/contribute/ArchmageView.coffee | 37 +++++++++--------- app/views/contribute/ArtisanView.coffee | 14 +++---- 24 files changed, 26 insertions(+), 25 deletions(-) delete mode 100644 app/assets/images/pages/contribute/archmage/alex_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/becca_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/ben_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/brad_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/chloe_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/dan_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/deepak_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/glen_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/ken_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/laura_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/mischa_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/paul_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/rachel_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/ronald_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/sebastien_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/shiying_small.png delete mode 100644 app/assets/images/pages/contribute/archmage/tom_small.png delete mode 100644 app/assets/images/pages/contribute/artisan/andrew_small.png delete mode 100644 app/assets/images/pages/contribute/artisan/axandre_small.png delete mode 100644 app/assets/images/pages/contribute/artisan/derek_small.png delete mode 100644 app/assets/images/pages/contribute/artisan/katharine_small.png delete mode 100644 app/assets/images/pages/contribute/artisan/rob_small.png diff --git a/app/assets/images/pages/contribute/archmage/alex_small.png b/app/assets/images/pages/contribute/archmage/alex_small.png deleted file mode 100644 index fd5db1b239c2891f8f80958ff6272fa178508fb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8050 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 A8UO$Q diff --git a/app/assets/images/pages/contribute/archmage/becca_small.png b/app/assets/images/pages/contribute/archmage/becca_small.png deleted file mode 100644 index fa182573a0d4ed5b43e6e05534312b89cf58de69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7835 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 diff --git a/app/assets/images/pages/contribute/archmage/ben_small.png b/app/assets/images/pages/contribute/archmage/ben_small.png deleted file mode 100644 index bdb8f2e085cb782a44673d04732a5e7307cdc0a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9154 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 Y0Oh-psMc3=KmY&$07*qoM6N<$g6l(bssI20 diff --git a/app/assets/images/pages/contribute/archmage/chloe_small.png b/app/assets/images/pages/contribute/archmage/chloe_small.png deleted file mode 100644 index 706138e261f6302ed8e28417c4cb18101d07c7e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6787 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 diff --git a/app/assets/images/pages/contribute/archmage/dan_small.png b/app/assets/images/pages/contribute/archmage/dan_small.png deleted file mode 100644 index b2cdd83e6e48e641f7493a41c530693e18108f5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8849 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 TuXZW^00000NkvXXu0mjfRPzf< diff --git a/app/assets/images/pages/contribute/archmage/deepak_small.png b/app/assets/images/pages/contribute/archmage/deepak_small.png deleted file mode 100644 index 8c541a4d95855f792ede90bb7008ff4eb44b3c75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7813 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 XD}%9dMwSm!00000NkvXXu0mjfW!DFO diff --git a/app/assets/images/pages/contribute/archmage/glen_small.png b/app/assets/images/pages/contribute/archmage/glen_small.png deleted file mode 100644 index e0b299abd64a9321fded6c540cd6c10a69caa483..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12057 zcmV+!FXqsRP)qBd&bP7wqb5NPasccb^c=ic|1{=d6H5a1%(aycCQHe8#1~A+h6=D2N|cpu zup-%LLo!#u_)Jm&Q|@obQUoxDBCZQy4A&5huhQ-t1b{a~KxEB&tMJ#^u%XMP0-;z1 zgCTmo(TrKE2SJPbqUUiGGHA#|QJ0O0_j#hRc!yo=b7U3g1F;H=VH&d$y^*y9wtF3q zZTHxp{p2&}!oQY)5D*+Me4pb6wuYqLf`YMhS~GF}=H43NsI^5427@qeuZP3oz>RzM zpm)OtdhOJGKbcJ6!uj(UIC&DG#YF*3cXD1>Op$z3be&Ux!i9ay;Dsnck*w|*dx$=F z)z}nub_aoS{MQN)0^?D(3gLJO1O`J(QEU|qhPlj{TOv8QM-w_QTHD+4UrKmX<)7Z#FT+o=!V8G|<9BAszA~t0~I99|@CYK(kuRV4x zY0PxBoc5L$*Q@mMv&(_8q1#Oq<%ZFy2g5+LQZteOaXM_xW(*da#OHtbCx3#P+S*Gx zzhXhB&zwPlq_Q`cKrvMm$IRB4)$e)S7Btn{5Q=7TgV~Hus|nk_7=p7gkLVdYV)??J zP$vIbx|Y#_>BO090f_M`fbkIRjwz`P1|enFJ8!m%j^^q;-9D8Pptx#^2-ekz&$eva z4wuV`$&s@dK79;xlL4d>F%*lEqtE9uh=vz2J3frTqiE(5zS4{y6fWp1)szZv7h=P)*WR`!Jq0B!U#oxe`(!=dCXyUXU8Csi_fGLq>p6pazO3 z(&Uu0$YgSeMhaKASw~M4FCqisW*i*8x&qWGzO;T3)5={pNKG2#oIOFsA#zFn@yt zLMUQIZ36@iib9q+8%!Y*&kF0{s9&*;*`p4mLMEI{=FmVO?D5z^G8i9gs?lEdSLwQs z$pP;51Bk4mp5acde71j`3!mz*#cMMeT@H#JxCqv=1(+l$!vnTW8EUbj_SUG9L#q9UC%HcNn5Yz{Q@ z-l7+biDX7ln#Usn0y?FupjUM3=#bRz=-!%zamQs6 zdE~-o1Ws6m&kK3YXkW+*>uB2-$I^&YmocKTjg5A6cr1ACbOh;A8s;0+F57AU@a4uV z#>)j`OOsuI5zk6s1eUUhYVt)+r%7=2awSbNG(^9dn$@Q>QhBLrPPfF}4u@6HlATmr zUw=OVvJC@=-^5E#eHXJ6qqzC@kD+J7dhvh$o{ct()mA~CBAbek;S_#%L%kE?yeIZu}p=%db!fryb3VX6o~TKt~Q78 z8Iwd$C+9L)O5`xkN=c>>aFczycF8~Dh5pU0i|c2;~Z@WkViW6zpl$Vn&|lXv7=|}2oGCvCY{5%SPs2*16?c7Mgb;M^h@Vr7$|inZkrKmt2@Y2j$btk z(a2gAjF0!#RDdxR&ggz;vzB;0OI9<#n88pWA*>-nzcU&o^>xe6I^4X~hnx(`^J5+X z1_yR+9r{M*U@#S1!qEGNO8z1i#q8*LWRmg9G=6)#*#C0Qhq;Ka9bqn=B-;L;BA-v)j?^ z6yqIL{fW;a(iswu_4Tl zT@e5G_*>xg_+T`dF)=ZvJ4dZ-wcXu0Syikt*{sh=-<$TzHK`Zrrv6ExT?rzjwRxF`^`5g7>px9 zf?;-cPV!Ot6t-sOQNz~Zre3{1i=j#C)$=AS)tWKgXhr8_8cy;rJ3gL7Y{rD46O!-v z*JxTDWpt!U0>wnmy}ix`r`0HY$~HR2d_1QGh0Bu!$ir94LcX9?Q!9aSF9n4EcUUiP zc~SzWOtb?}Rjq>p#w=|KH(gvjA;vPMY(7CPNz~X)bQO*n1sE)I=Nc@iA%VeQFqKh9$Ej12)IF3aU1QNGWg*6i znz+I9BuGXVQfT!`->s$NcYDmDntb|jr{#slRN-*q7>M1;P6>#te%6wJb}`A5uKqFICz z`XKFhcX-Ksjd<~V3>n!$vt?}TDdm5X={(}3RKn4Wh)eiA1EI)XousuIP?IjgNT&%d z70^+Wg~M*dc+!S*r%#hmkk0=fH;!YMF9rumsJzL# zfY(m}lR?Q}c`PNW60ymQ1kO3qLL3A(FG`KQzAY!f5J^VAWH}X1mt^A6$t1j_Aeo}I zB}o2bU^0P!8H{K@UeyG>ivmK<=vkTnmQ@Ly+X;~Mj0MwW@d8BN>QX_d2Fm$*lA}(k zii=A{v_~m8n>KE)wqW`+B;)Ov{Pr~4tbHn?e^GeuKSY4)a6)YhsM!A zq~x$ZyTgO$24eycHuHqTqQ)TYE%r615IB&`QG+Ci13358O`Y3sy0lCWCyj>-L?K=a zZl(Bx8+=nIcMOA2Ryn%rH{H@KqJ1$r#9p7nsS&gA9YuMly3Wew6W^*flpBp@!$z4)icRQ_OfJrHes^X~ob+YE| zwO05DEv7OCiyb|KG+ZSgshwQsA!!?zui*{hYBA0tt>o+bZ{eE9*RM3V6 z>d}gITxwyUIH|}qkiN*YP&+kNQ%wyzeLm^6ob_#_&-i_LSxTiybjb6#B4PAI7f2CF z;DuunGmj9}aF9o87Q0L))jEbIrQA9=XV(`n4DjPJrlwmHfbIfmzkhMRh-K@NdBPSXCaBLj|Q~#J3}!C z2gyo?cYpx7u~o`t$+YF=498k0LaY`LHG}CQ(;X|zRqdjRok|o{K-eks$lfAPELaAJ z28ua+1e(vn4MctJpBsi_pFn`p%>_I9fS?F3k7M@QAl#bXIf%}kTJTck!N@S`EA z9`rhzh>48fJ`x+PrjjyBWk^*~z5X?pMj|KqF%~3RV6^of3uZ(RtN_BHz+@yR)Yd@B zkL*xVU$L@d|X*3fUE2OqWLyBlzzK!tQ z_<3|qci?mX=65Q-``v&1A!g_2E_%ELQeP|pyhe$e64cm7DNtR|)R95qjmL9*8d=M8 zfSD2I6L|pur%*(CWt=6jdCk%JPH{0;= zo)2MaVMg@aBK;MN60oBaMmLKadtLbvlF0Tm!Rd?4$T={N$2&Kh9$$DP8CETq{7H&`xa7mR@r5BVnE|m1hZ7 z7TxkVf~4O;Jb!jNj9QN$jdflG6KRag*=a{>qZB1mqo&en^mo)@DY8V4*N2WqFPa(} z(b?VsyRG`r>;s>?kG>zl*$d|}7h1x>lV{R;{VA!9*8WRX-M`wXp>MD&0>w^l$_J=t20Lmn#s{40Cwg#;cA5f?kG z>eSJ`S~KFL#7<785eVhi94OYh?AY7igD>8*7kjt#RQ=A=2ae&z_XhFOp~1EFPGT|6 z2yba(i!s4T(s(9WsLeoQ9J zNwy+U7T=IlM}~3-eg2E{F}xi}BW-kG|NdLaK|9gEWs{ypk|`uoCAw(vNmf5Uw}AQi z1q_@X#K3?w-}%A)JMjQ*YMw2T!e2l2Dh2}6k}soUZ|UDG4@7ms>8czMQ;w^Yk-(^U z9|(+*wVTjGmH&f1b@=;%D5g^d5%gL$)0Z}qzr1su3xD(WqV}*~BQPGnCX*b>BKPCk zS$j-ZJWi63)s;cq$CroKX+eVgisJko)G(j>-3MT`lpN(0Nzs$%h6NzH(Z=LFx>}oX z^PZdFB$|7GVFPfW~~WOoijV^egF1uR8NKF?2J_iWrC zzF$Fm4}L*paM^i>35XtY?4c?nqBHWG0Pc)Kiy5tty{uwfqlG9p@v9l%DXL(;OxCu0 zEeLB&o=>I)V`yv=M+Psz>GdOK%c@(_A_a7#Q?vNxJI4fQq9OeaxOw-D%hpQ^1_SWo zn};wsJgNgjSO@u+BLnB~%Xg0o(9|^fY5g4sAyQMdHeC}1REwrg7EBgIJfB@zFYkN`4?U)jaD|+h~GA}bm+hk^&+{;pZE4{Mr(aZp`Mm~l-(lLb(UhSvdM7! zwE^E$G%NmJTZ-(xYn}dcxq$4~rci>Mp{$Hv(6r>qu24SS_)7`fc zUpJ`Rr`PR=FBySx;us7u$ts?D`WeYO2m~E95`(g%XQ!{yP9`CK+mcCQZ^K6YvDbeT zhJ|4mr`{KJ&^mT)=ZTUJ;Uua}KiN-Br_ai)P-aE@uPJ4fjg5%P9G^{B_z&(mNqN*z zT%=99sDXh&l~zjmiwHJb+}PapVg2Wk(v&8)EX6vi$=-iQa+n?c(*)XIotIaIXryZyF9o^kwMWwhF)xcdrDpabv&ZAt(n55Tthx8h z#d1}w!${F8m-~7t`%Lav99)0L;;pF+)?;Sd#DlQR^bam4E%DG(ec$~g_RDjN)da+oR)-Mns z6-rBW?rcRjG}=VRR3^xANI;=M;93E)U27ri(BoD72mfz!S|3|5H~+)ee-Fr}RcP&+0iseCn`$Ies5%*dDQh87X*v09TJ+?rRTz}N{>uGp z`t9L?vz2s74Zf?@QGWeTC`=KSuaRS}GCV*QqNh^_;CrSFAp?v57L9@AQShbqk16H?53S zlvbIvRXZj3MUL1#deFMB6(H(~6y=euGC6@O7cKKE?Buy{D&u-BO5MuRA>ta#Vi8qo zv8-o#U-p|F9{?amb!?!T!kUScKJO@-P{ui`>lF+|J!#b`qx4W1<1=$O{Qgrw`>lWwPysm5c_~Z0hof@2@&ZAi`N2K8uA)^^P_i7z`wotqx z)_qy40Wr6h#~^IBoc%2iO@aiT#cUNljiuy~Du6Hm-+t<+DhOJi#FXK8rwEV&-k&Rq zfiA0=Cr9Uz$>p%6p(p^cZLr7 zdUAvFG}3B-NQp*LVkW}}1BfDdvsD<2&6Y19t@$U9v)9mX_(FNw<$>Xx=#^hC1LM!X z{X-R0o~qhQ4WzvriS<5b1{Rj2={Jx6=^x>%fAKBx`tII3)Yu%@+v!w!o#}&p8E0Y{ zn6E$axA?O!e_m%TOjinpGF|aGST$*VBTtm$1}}@oE0Tzf?h40$^#GA?0F~klc~~=< zt)9bLOc!0wlf#jV+VU!$2zK#kbzmbOI}m&t*j2mo#^Y zrQ}$n%mUPWRz}n{54-U?psZ>N>;3tJK7S=&pvOt^34x-_js3|huM0r9_cBd!qx^P% zt!NY;tz*mL-=(rE-`c=g0p8}LS&S?IP6CFl(&I3pllYr&j)cT{*c#bNxKsR` z=!^^BTEP?jm|B;rWUtw3$}B+jr`i0K@gJ8BNO?t=%6NRent$VlVn71n9ByiN2tc@T ze)5agRSMzW&1;EFgfhAwsyOJbmS$<++N@3!;@-XxS|U(q1h9$_Pykwp8W1AO2&b&@ z5jZ^duK@DTbZ!QPI|3&Vxg!W_j8&$CrYO+8t;=!I&S`fTKR7_DTZc(-ZMEz#gQr+Y zh7+_`?6@1e9^BNw5&k-_PVUCyNwJMgNOQ5QW(ziSw!>|A z2+vy}Diu*uaeajk*g|s4ICPPYU&e&F zVcfEF-`i#vb7UjbL;vn)FY7FheZ!X@{1lR@GzN!8aq{eiRn@J(w;Lb3bvJhP_X$76 z$7f4Wj?W$7+fP5IHZc2`wL#uUw?4E2gdacn(*qn6Ko)I0lJQSIH~`2sa4R zM?;MT!{IDG(dQPc9%v`YF&O{tkuRx19XBc$wuDe5if|-`&{9;qR!WP%UJV%ewcS=y zX6&Qb!oL2*cT_q3`r#95KJ615-6BR}i)S$Mh2`tc3XH`%?42%$f;a zq~~)dW2IH**mX|-N|Z~6R69n@T0B}MhsDu5^Kg0vLY-DuKFDBSw^Nsv&`R006vd}h z&}rFmS?O_S;5wxEM$u4uMFGQBWHT=x{FP1icKPTOh z^xZ>25zPJF&tAfx{rk^XSP4_q!K0@zJ-=8rnaH2B@3>{x4b`ngbBi2z0JaXMD$cRZ zMoL!Aj=x&{S*S!ZMQmmL|MS27VFf6@+8W%mfA6wZP@Ztq7Uh`bf#*sIGlzK^byKuqcV$boACh%P0)mQtgc zbjH*$wkXYda$v4H5#g_v#hu)*I1EOVNlqyzCWK4i@GSFa6@uJkQ;V2 zh2p(48-FR(tEKC;E`qsQ9}>_yX2H2OGq%*(&}29B zUCmanP`5(pgk|C%1d(3q_emX%XI5{7q!70r?#CIG};^ljzdkQE1;m>CMbnL zd4PH~$0)p(h|=p>zTS4zb^%OROOv?yW^{T+jA>{`A~{Ii-|JGx{@MD*Lm8o{v|u#X z)nPJ|L?)FIYda?Ow^f9-{zc@uQq^v|FqY@u*8F6iwcPY6u7*{jiRkI`t!)9Mz6O(TIgz1@et9;EU?$JAqhHNhOyEc3DK~?*+gKPHt@4qEa>q2iGm6kNkkFPXJO42 z;ivCs!a4B_lf2Uy$asz2ytE%%b{BP)MyCm{4MlZAV11KAT|QPWyb92xYXM}{PuSVg z;@VHXuzNCA6yqD~+!pw#hor+MH0?^Et}lbd({|+JCP8Pz#o}npV%*O+fokEew>rqz z7}3|(g3TRm7@b?7o-0{hJS_#6S^5FT2@Z=vEb_9w#eq7fSsaFGg4eBLztCF4OLEw8 z2U!FsEjgOzG{;G@it~#Zj1rJhP0wa9?!V`V>-ljHN555%T*@F8eEE4KrBN~l)cdTc&pTkq8nKZ8;cb2?f?pgz zDfU`!=D2{Z#X-MoauMkCx^9^?H&YW2Y6Zes5u|a$&6gz${hnF@27_|&%y~?YFCcQ3 zg3a{uw`^x?jc~5B3Cm>_#tUDrkkwf8SgvUmV6X}PsHhoBwh{$(xA}!~YA_ZM9k;^Q zmxj}yM{Q?TJd49;G729vkuRW*oOW}g4R$*}v6EC;ff}3wKHgk*o3Iw{u?rCi6z*|s zEldU6`Qp?Qr($^wQ2@qw{mv!|w9gAl8eGg`Jd(pSea`DbGKIv-8u>#TH{cUD?i3Ry zGUVe+hcD-ORfbCdLOhZI zWn;+g{#x{RH4=v!1=Z}xgyAt1;BLub+n0i35qk#Y>_15>DV(qrG4Cjy&Jwl=3%Pm2 zIy9H%nTBU(@%pLLh$NB~YK9Y&9LumHRRU91Z#)>qv=pu4blEEI+_6pUxr_}>FNteUGi;!L4MeM1>q}|I=U`gVWV% zsX!MP?^eYrKc3>rD>;N=x(FB#(eCGv)-ji24v!PNH@3m!s!Wd;$U)DOuWK`85ucWx zOvAL*(4R((-X{r(2KxV^nV;GTm9jeh^`u}b=UOqXv^UgaYj>x39Ze<)7=3!YxC|C- z3H$mt;;xU&7Xd?ETag!n>}>L2Q%8eXpLf~R0WW(L zk6@c6tCPa)+)sX|j({M&1%(oL z>zbRy#+D(n3Do$<4xYu(z*xnxHn-Su&qfz1M5zgem-6_BBVjck#bAAy!1{294N2-a z&krSVAY{OW#`@)|$8`0Bfnz70yrO_q17kybJ=V9@UD5%$7jMc$&}J?OE7|o&i{jaU z<)9p+UZ0Gcg>~Gy{e~+#49b)=aCBJsit@tr#G{1kJQ6wZyyF*#l6Y6M+*Ccu=HK2@ zgAJY%fsjkVUrm`YVW!xL>b`ZyRuuAij0VQA6kaM1CdGq}37qg{QI_m8ex%S8tEY{f z4d`vDyW|6!$;pqBV{J6%VI}{{4@B{Am!4F^`_MTXa#16ic*(zliIh`8fZ}mFFAD^= z2DXF)|8fMU35=y+tOAHnQzQS@1}A)Wqj22k$SQt#G=d9rr6l9d4hKF<{-oKdXMz|- zhvy0wOy!J3l|{@@Fl)2hv2)iBtluClY!Hjah}H^rSpfY3QCXU(?431Ph^(fLDPtFG z8JoHq(9`5wRl_iCEmGq+Df0b@Gbfz9w=0Wn4^d~a74(+EJuhQL!D?HRl`sfw1?LCG zg~P6zswb(sf1OiA{7RW3fBnX25+_GXR?%2v#(e}>S8a8-dV%QmdlPXSqMsD)H7J@( zYj(AhXkbu$^*)S|#SET4gG5~QRct9=TMHoSI#>)wcZ-+QiGNiMB%F($d{^VkX8$CF`M8Vvmpe3z?))t~@m7yIXC`E;q>Qx&PMy(OS4FiXwD)y7@DT#zVsoTp$H+w_aj9?;z=^MNWrE9#!gh`pe;mgF9TBH zUzP(#0pwLB@Z;!v@8%A;9o7}8DVCISLDhiJmR4kOoBW^ze(sZa_7eq)S#ta*o~}VG zC;@-ubOd8lsY|v$is-mjF}|8J|ysyU+JB#mT%H73#U3_y>s`XczOnr5l}4w8@HNLvA-v%w~o1@EsnuecH_Tg3(H7eAi9 zI2c?)@hlDxhmpzVvA~amHIUP{I?+nOZYx#sE?Wx0U<&7_l4@Nh>b$gh%9U9Ut1>e* z>nCy)-rnF@)*={)STenOZB0Juy|kHnZ5EqJy_|W=CZ4>>e#LAp;-*g~=rcQJE|{^c z&ka8>`LvW167WVp)v4AjZlSwaVV*xRn-L>|0XgO2b(wMN2DhNGCfmySJ_ceoYrs4O zi2r*mLe4*bQ81*mjkIt=P^MTE3+OZ?@t3V8)X8*8f|Fx+h8VUV;wX}?c#>Kh>hH?DcLxO<}m5!VS<%A6;V~ zMg8w*C?=DAb8A~GEEWrn96HKxzn}o3CJpOqDq{h*h;Tf$CJ>ma7)0(xTwR{{{7Nt{ z1}Yhp8GamU+<{Gd$m)Crtlyo6+fxu=@jzCN+!$wBxuIh6b8NZMCl}M0Tgr&N%Vt8O z*NWRW)na2EkIev=G6|h6Dnvjq7)rADFwxUu)K5Bgx?#F5*23EWK=ejMN3-?AU%??d&|!4r7Rgs-5QM}XI~MFR_2dZ z0)U?_mEp%lkX0?0_tAWAiWMS}XC`5&uo?~q!tbS1}sPmyWOXr$t)(i6$Aj>sB9S* zbK2bV8GuZ_Ai(4ItD&@asbwNm!Ml8Ba)w46KW)LLm_a-`g10^W8Jrrih$kD^-6m|^ z=oZ!@3qzT|FBwoF9rMh2yT~LsS)$Tl-?jLbR9w#+&e6;D@HNEWjZSF22<3;5AoR)FHCu!x@#wXj6~q%BX4Qxp&K;wR)N zk0Z)i_$fCMFoK3wIq95x>l?vz1rUO=$$7how%0ei#B;M)(()FJk-rKi5Q*toqt|J{ zBV8_{B_pPCvQ^UWemotku!@#C2X5WeNOdk^WKinVsZb}1t5RN`Ne_#<&Dl~`1{QYrZlp;Ri0 zBP%4zDN!;l5frJ2BuHWi2-^U&uRXml)BBS1-S_V6*F8N8fEf^QDvyFD)w%73w`N}Pt)@98ohDu zmy|CQ^q_o!4>Y?+mhil4+S`I)vs$U6t!=~S=xaC{y%li2$H1J~Wgu`ZFYA^2k%x}c z!w(&!AOFppG&8#>KZb(WS*^L*0o`twxZt6|K5G4^e@L&M`7Y&i8B%HE0-x|Zynkso ztVIXJH=Fjw<)Yp|fI8aS1OV;4p>I2B049}Ai~aJ-s@Ut4i&QE+&3(YRUtnNf-32gU z5#Qzw*F_#*JALZ6K2P0_ocJEsp#x+?GjDeSfB$~^!dL%@UiphZlc0E2;eA5|=FII{ zj7m)}%g)ON!H*r-Pec3m2|#YQ;W*fE9HC0wX6ENP4vU*Y~hTc5jCDpM^& z21A;4+w4wy`YT_fcAtkTDHLKWVMTgwSFPpD4nTl~J)>Snol^AHzh2}(Nk*rQE-!rf>;e&ZZX&u-A8ml%{6bg~&#`}|2= z)VHC5q8|PA-=?vL9vAPcg)~)`K2#_)xJs}5pL0|ymp66ljg)m(0`lSeAJVB)kCDyc zpi1xx8M}{*AM_o5l={XVq{YdLbnX0GG{aifz{}(M&hmvwkf3>QPOg-nSqN9g=R%b_ zKk;#a0l%`eysW+N_8BSQHR9#-Up)E1=~ky9E@iS+8yX#>LytU3!$%*keOM_`H8d$+ zCipzpu1-oY^kMmyfV`?YB$``Vqk-9JaUx?bF3ir@KB8C(ZvX{bHJ6~7D-$$*d4iS} z<|vg~Z+0FD(CN``6JdMnx`AR@rw|CJ1A)XN8t!2_X{UW72ZW^< ztS&P6N5#Hb!^u;jWm=k@qR`STo9GHf!m&mR5-n@&B4^jH(SQHJUuoZe_T6vJ~dXmT&4Ksi&ul{M~)j(cMoSeM98$Wd<|bo6CWf6bekGoCZcZ zlcV#Ornk&NHjA=elz`lr3$o_8X@2Sk^|CfNSyol%LpXUuYwreN@ZQrqDz-z?WD9ID z4Ac_)oN9cIe*BZ43jnO;kkGQ@M-B_eiuS?9i*)*lC+?bcNH7pgz(Qhk=e6%Y^&3x- zr+b(>y1L2M;vs{*b=zYCh-z$(_i@ceEcAjfG($6OF&M??29uRs#vHkAM8UN%#p6jb z8md&Z^io@!pDtdxB7UA;U#GsF9&*@iWH1?Mo0hYS{H^8-TRvN5VASEv%X$@?W?6&3aq%k#4okA#&#XqEGz zwU|DN`!dTpcPn`aqKgZ7vC6#Xb2};E_fl|qky6Pxb@vaDg*y$3!lsmpNSN>jV1$*_ z!b*K&R4vBH?H%D{&qtAHjIy~L3oAkSVu1o39b`6}cLj*`Y?dOiIF(8z?X%UjwVFfb zJSDr%3TLRO^wgconEGRQY3|x;h25u>Sb}#Vxw>14#HgPwnDmri#xT~Rzp_oJ3IW^XUSBG3WXwT zRge<=`c4`ESYg2jL*Y6w(2DAN8Qm50$vUN}r=s|Vtq}~!1=POCfsh`I3s>g!k=3g# z+bydB#?8LqKYmM%xY@~!rr6RN%0tG;~((qr< z(){uYTU~zJnwS8uT)O4{gPI@*0D(;}hbCY^$)DTqnA>r}b80DI90vv{2L=Mv7ig;k z2Aj!bB3ElG**P;sLuG+Sp%l|o(=slz31p?azPMVHjo%RcOm#ep3wjXXlkgje6&?F?c_%Y z(Re${;V^9UByV3b`RHCTikzrvaK97go&Xzp^V~r`Qpq_7GenD7?829P)li%;B!@gzmShIE4(cyCo zp{Lh#b=T3*0K|)TIw9na(_sb%*7A*JV**Vh6$WE69-#FSTgMXTH*qEKXyGVoU~r)3 zOAu9Y_G~m5ca5fEOjCM4lUdKutgh@1g>X5nH0oWg17(|r)ZlyD~WsVJxu^pY#&h11?F z_*mtTIvP+_g&u1YJh3b`GI7Xx17OS?Vk!>|`Ted5re zH>_qlZ8aOvan4Iz!XiY7%2ASZrdsyi+e2Qjmp+b$e$36zaoAj=A`3@gZjW04WHXk? zW-3x9lc7ke!eDDZdulJH6`A+yLecR8TLLtS6PHbY7vQMs;{rok1*mdyVUgC=$^m?u zw3@r#pFOY+_>@0^Kiukf@EN_my&|&uxSNrqDJX@7lTLuqSohkjb^HBqu7<<8d)-35 zy_s_d2;gWNXW6Db&NXhQ0TgWTZE*N{AJ?Qk3ujK748`7al+`rSVM@RT=Zy^-*}~y1 z_7;ofuJr?cBcIDtxl$IsL%uJt^Oqs`$JNYxt#OSG91TK{PBcWJI%jHC3zAM<1=wUX zHI2ZUH6h?i?R>dxP78IV@NcI%oeu7sO#mi+&3y+5qNf(lyp2_5oFmE^GPdF-9F2>W z8oHMky;bwL-6D%`^Z7)`jSW;AipK=7p-|}l1wpzWX`XcmCMW<`w8e#7f+ z61_O4s#o&s`X@`lhl zBw5%avO*mxlHlkiQOm*|HoLGK2q&Wo!K9W?P8>bDD`V>tge-&IYy?u2;Q2dPX=7Fo z#Y!bp6p2I{o&M?jA<9B1I4(n9ICWXT0~aIYl~ER&nAku`0SJauVL`Gy5-G&RFfIVG zsSY0lR3_DunidclRDcdYRVtyho?~Gb>ns69#$vHVIR*jN05IhDGUAef2mHk*kr#kC zv)jVktI`Q-TCeQnw=%L4bmg$uKC@VPZx+QxyBH2;s%xpu1KX^w)i@~+LWf`rYCTKV zxz;o5bw$Qo2_!5=ntku`)uFwR-L3=jub)2V4P~ltOa#m0(kdWq)Pim|46k)Jz?GBUJ?zJ}BZmR=XpN*b$Dzixb==D{~ z^7(L{ZAv`P+z(ED+`I$31C0qmS4=`k?W&U=ZL`t?_B1&RQV@#ZC!+=W_B+bdOpI5I zA3mbx6BsT=n2aeym;mwCJMU=z;DyK9o1Q;Xw$PQlQvf3rfqi;sTdNo$JkXJ(i!14! z_>}Sc!9vJwHPkyy|Hj|6d);(oe=p_BRl0gTN1YBMooF`^QnTx;2AYl3gmEz7rChaA zS4zR;x-Nf5J=Zg+sMI3)lfqmpnrVNVjgOb6a5^Uh-sN%9@qmXu;d0Qgq9uAiRH9by zv%k%$TA=Y`qs%?e)G=uqQ4&nQT&$H$@+`PeYMuP<7W(Ihm5Bt`SL5`9iCM}O>z)7A z&^j%p4RzeUSwr}0(}lzzt)}VyxnRRF73ciPgFW<#(QZ20ricOk@i=U>lFV!8X?3|d za?5j2l5MJR_Z=X}D>~a0+Ry`HV~R;;^YqJ`3p5ps(m#4)oX*G0bn2O}(Es>ojppOE zGRg>rD4xWv1BwqpF0%A0DRKjlr)y9-yv>kp=9sE_ID zNUvWg$f^nm=&)7Xui~PpAyx}}P4}0D%J+{t#6wf@uW)bp&l(8^Ax zk$?7ke@q5n4}B|gfi78Ulw})erPlbLG4ZD~r@mPOP!+0b@B5CQBIoD6PH+FsbbW~Q zxl#Iu4|LMv8{elJE43Lr>3q$S^z*|u8MK7g$@dPGo#C{eug zeX^YVG93%J=u)r-$gd5y(hnzZXxY9j{6R?ivWl!d>Bm-9R#xes3~s#H=2)PGs5Ef2J2Z#;B*Q#?zmeew%)%%j{)|Bb(2YTL%bA{4!GqaB%FC4CL7L|3&|* z%R>J*sbu;`+l(|e^VgK0R_>UR=SRD+Ser)b&{1DjS7_8J+qwGz5X?{dro*DAk(|(E z3x}r&RV9sGU~PPI*jMN17o$n?q&}cO_O%Efvlva#n-_J1SnBL|6asNOgIl#2Q8>h? zU#VCZR-?Ui=D*RW4s_G+vEcnojX3V5x7NvKG1EwA{ka31bn2X` z-}``YCnq?AjdQ29adi|I_w{ru@qb?*%`Pm6*Kbe7>HvYGhqit=w=94OB^BDb7BOm7 z9(_?#-l~9Nqjd6;won(7Ma@;F$8%Se3EcnzRQcdQkJ#%QEcU`;QnQYcfqlx9(^y_>K-Wbo<&(_xZ}?^-gf*lE&|i7ya_Pi**UvW>L@Ww(Tej3bC1W+?c7|-PK|f*6{*g zTl{{s5Lzl&-sE8C`E0HvocQF_6dfHKQ@EHfT3QLf_|w;?=->U;=!Rnin6$?OMv>8L z**8Z=D0OVrkZhtPW3!mVf8V*eKqvS2Hw6m{(M(ZL7rUtgq-wU(uTsW|y_DIWjt~v? z_xx96?KxHZF{S3n!Gqc=4Y(it>g1%j@cCGd0v;P3@Ymm{Gart!vkudEpNGD7`Upk& z7=}`k4h5X_^wA!Ac%PR}@OS!+%ImZiTccyWKKkmf9i(b$ovtjcQ7cDqeoy`J|K%e+lvQ19U(ADdcs{b;Or$qz`-W&5PXYV^eo__M7)4cnZ*~B?QzC^f_4R^}6 zwzg77o05BAQUh*rTUMv~ygeNwkKICt0}5qaSzD*S_~kS$=WH~YG}8|+hiQfx%x0`| zD4n2-3qht4izei$wJ5#v{uR2u5~kIxiGHzapufHrp-|q!?+r8+PEnuFMIDM@*(#^$ zy*VW^!+6SPKm8lp^dl(A>py=}^jTQ66B955!;c>r==mOl62IRlOIt(H?X*xlrD)+h z@4rt&`}#!{8113s$LPb07qt;Sj2x~P%JiS!nxiYCo+ZQA8f8tb+o@bNP=E^blS?=0 z+%?6~=hbR50KjiF`dTjt-NS(6zkPj%KFP`GADrwHl>#lyJ$9_FRC1XuBOX`4GO|TG z?54(Rd+xrm5NRD!^FDLx1T6+*Y$?iQ<}`<;!vo6gYbbwA{OYns3tv68kIr5R(ff-V zow?I)6I-C&M;3P*wRrt(EfvaV*Qv}JZE*i>X6qMrsd$Ml#f zROk~7%v)12&8H1^vFcJlZb z`F$ScHtS8sK$J54=F^MHV&Ys$1=vPcrWfeO+_LWbDy>Cpm0XP3B-I)iilI+Gd7K8i zl`&{cxO?sG5A>78hko?d2ebFOg}f|ZJaBMW6Iv*ipwWR&Hv1f{gc4dwrDh%VlimUy zJ^{vZNSWxI5%+W4-m)1eK7#5%LF3$~U^q@mHJdjX4Gff-6{e(>@MSwR6cFp4fU4Y1 z2L~Y*zJ?yUJTo#)Mkpl;j&rTH2a zf%hLeykGbnNjFmqt9R75L&JT-(%Q*Gj~qQfKYh2hj;@P?j%=>1EouPE`1Hxcw6qr1 z`j09x0+GWb`)GD~O;|j)(_jR)l3gpq8~4h0pyNvJv91Xg7X(3n>d`~2X_kkDvk_T_R#2msFec3PXYL>^07019fuG27T54foYbHRwaDS~Uc9K7M?dx;nhV zf62BSsx3GGK$~6jJr!lC*72;gj=|neWf=BUuz2KNQpIajK1zbxE$KUvskc|9)5D3%~B z0{#Z8Q@EJE5-HryAo@JW5!ag>an0Uq7V-`GqDLKtTVbu5V}Zt!X$=f4O#zDm4Yj%) zbfCYh?jR=)jfmd?%zVB`k+{O?>t!QNW!d2t%|h6=BKx0gh?3DoT3EU%HoV8y=g2Eo z!MVt4v4u)iC3XqNlTwHxNH}(2u>Ra*159g1p&aNV{D#bz=9WX^T3{(+s!{#AUEjFp zEQGZL(dKE9Td>fL>?|j)O|$dN(qkMg$@Q%+T$^GIwNQwKGu+!H`~`jwA?)L5W^|xe z0RW?c!LR^MP#{~vVxmCFl8M$>bG6sEi zigUsVYjCnLvMu?6F`hpiL)R2raF$;TGruvtalu;S4Yht1JMN`QHD zVMQNgd~LTZWY+-U4xM9AKv~`#YuEBxRCE}*gw#Y_7_0`))9-85W(We1Og_)teMsaQ z!jZF8gagLq(i}z~S`=UPV)zg<)Lou!- ze4q`lyS~UbHrOZlSvGLCeoCiO{!ki1&)RnewxYCJqH&0ON&q%A`JIc`m17mkb=f`y z+@h|!1YgqCR)1SPj*ULz7|5jnAo_#LWe$Gp9y}u#{F;iFc6AzH){<9XlOwH5tAP9|1@i~%jp!~L%RVO zz7zKpvc(KBt%-FZF?pKLDHOv%;zm+g(9>0NIl7Ao;ltshFBfmjhzWSJOKXjh)xU``V#mKWXWZ9i9Bf~(vNI*H?5xb_67Vb9EFyksfvZvGIuAxZ*kOofE*VASkl>R zQH)BB`dg!AWG9M^Dpl1LDEYI=*=4EG|A@7QY#k-nskV;cbgH%o!(o78>? zNeWU5P|};;LK;i+dSN@|t=76#QbAREK;>tRz70xKE^nxzo!JZDvFCuO4NqB!tJ{Wf z7Z+dL;#O%Xd@cM`0n+=IW=$__Nmtf}<(~kke#Hj)ePbV`5*aviH&KP!%CWh<6xf~u z!kRR}o6lS=HPt9we6ztN36i9uysGhTr&Akr+N$Y=y>Wz52b|820PMzaT;H74EQ1kc zP|5@MEg+hvSCU=^MZB(-3%qeysoumnyByrG01Q9^7@7l?fT*GEWMgnJ+f}(=DK_hjJ8uXlS3`D|zioaZz1~6eV3b4-*ex{GQ($I@efi9Oa5%nefG`k)e9)KGlE{YjJutrt>nZLZ8lsMl4l%t)XmZs=Ww^YGS*`o3p2Fe~jF)kjajCC7vn|8x`U3OJo0A_F}3on~?0LHgSS&VE<|9O|+ zPbZEaqbDCbNeB0j@CDdKr!YSjcgV^cE-P%}uo4I!K!CbL2x~VAi)jW5OHK$HBXrn9 z^8pejJV>XHu?YZ@%49_47mt&`p5926B~B}Zx@ug4=4vb(E`jvO4N zK%h(9uAQ#k^a_jlJgkL5IlE&(bdT6RsTKLWUi#E0o}`D5A160+db1~S$^|T;(HYhb ztt0$X>D)nG07bS=x0%{nOzh+f8Yni((}9jwisY)B3I|hqY+_v8P#0~%D#CSR@ePiR zfjHG;r!VejEoLPOXOxJ~z#h+Fscs3#C+5GyU(|2XJI9HWF0hBtX);ei&Z=Y zKqfP_n6rw1$W*AuYZgGr*3)VxyTwG;*9w|oO_dz|&RDznJd)*Hg#R0GnkZeYYE{`) zb?^ulfj!1C7T8DV!I1y)+KlvAz$Sp$-)a$kd2cyEYA{76S9#l+&L7PfYD_UWKpu4s zNG!|=>u9Pt3{1PvD{ixnCK8lPYQ76;!WaYcmsyM?AVqCI{g?#JektMNH|&e6-is2J`+-sRveJmCb za68Pj5K}-YvHD&L#`$wIjkULs-E0tA4nd=>v*wqo8!72XyOmD&+Uc`{N-@%9sqcRx zSEBcHH|zBDnaJO26;unXWe%cm3n2r=RX2v`RkgZ3n*UIyJ2|$M6T#7PykdwQ))NmtwCy6saNGc5jp<2(fi8`iDHl`S>)kM+tl9)>cP_BiNlw%YRw7WGLI?6(aFM!tLJ!lA4@EPPg zY!Mw>EU@{Dg*YpLVi7hMhrHc3a_~7@5ly+R)atOXe=>@5msD%KJZ~UrfZg5Qg5F$e z^;yiOSn0LQG#XsJ)u!97e`W0XbZbEYoH=L zZ#EQ-)-B9}I6M@KD&-6Y6VKpK7B?n3&}*P>pPiP&3db)p{dBV+-SxMNQ47bQCbN;! zxw3d(mH2HUL$k4UI@H<9v}3HJr9PKgY>k%CtbMSYq@bMR#renRuua}OsH^-MsD-Vy z-DTx~!$L`>)XVc#ExPix`Gi#<@l+OeHVI_}BP2Pz1~5oiH?tTljE3XP&d83cQXc;$o=_IjvuOuC5Xd7U*Id zJUW1h)g}I&65Y&23)JVclG9q>`6xSbKPP3#mH~_yKj9D<6gK2@F^%2&^F{#1Tq38V zDl_@qTQdMGdoEO_nV@1(UUtxO%`SkF<~&GIK2seq2yj5-^3fRpM{hZp(1&V-&YvI@ z{;o--E3Gzz@D?jB0692j+yfX<9}16{8Yh#rA9Q3rmlfY*A(&=QLKF8Cs(u8|XdLXc z&~TuI=7Jdpu_6RM6DbI~;J3mr1k)vw6$`J^Ea)T26esTFW!^Zy6k&g2qW;#MJ?jwE zH?f+k1H5mmpMyOhH_$rPW66u@ zP(@B0Gvaw93rkCSD>-XYt>fRwFn4rhMBD%=9{9^Az@PQSJP868e!=Yo_>JBoHiUhw zVbYJ-ECw3uvk1Q47jV+G`IHbcoahqM7p4Xkz}pQ059kBH!0$+hdoz-v)l`o26?Ywl zH8$iK2ycVBsi-!0l}7q)!ijGiIkJ|#ce6-pfJj}O>}^Ix3XSC`$p93PuvSR}M~=}$ zp86yjcOUBh2}|ngZSho6Je*Nj$qsKX1MT4 z&0}LrS=T^`uw^)yx*XPx7i{4G02BrR<%$(m*m*j?7-36slGjnM?aQe=TSc0#hSxO* zE{@}_Fc@}m{++Z?vOw=nR#urv%l;YSKD^_3UV_jTJ(LB>rM!z&HJ8+}BO#J~Xc^o?*b-1#Wr`a_% zENAAxqc?7~>A`_w7t1BVIwFZ?`deIHmMk=AU_ef*nYnOQ`t_)GtHon+?U4_o-34;l z>#eKI9KW)*E}mL&C(X(sulnKIUm(|D{LHI>;iOU2uNf(RCwC#U1@Aw*L6O#@55O*&D9wE`qf0u+u7^hW~}Xo9wAffh&t6#dfzNxJ=Mfw*W=)J2LY zX#+d0?O2H&FOs#{5=lvxNKsrwa`t`i`R-fh&2WYsQXHE~eSnKu-ppIhclLMRxkMk* zhx8#eC*A#Ul8=w|4)M#lJO+6TwfNUF^6Nz&(=ScVU;Ge|&4X}?U!LS~asvRgLa*@n zF^|&>(Da9Ztgi`w$m3}qp7-I90Oz|5%;^sSSq;QDWFS_N)np`}!@|Q#7L$QGY!=e- znHNfBis$m;)nw3<(`=$#u|$P(nG)GNmC6+=6w6|JDqEmzp(wsovXrM83{ad+6UcA3 z3Ju7WNh zT+nc!gC6MfH+9za&N-PY2#2hgDnw6+noLH$h{75X_U+_=089rs&5i|-wc92aBQ&uP z{y;#{y-#33E54_E-qY)&v2JfmARw$2Qq2IwGkHq!cWEBl2nnFL+4mqab68AG_X%Je z&>a5FdX67j0&UUI*BO-W-1mUUImQJg(L)sV@Nlr{?*RbJ5i|iBfbbMR0FYJ*8e`Dw z1W;z#cfeQSdwi!vWQu8PMh!IO(RN2)#`i%%zNdiUv0LcSPKs}8Gy&@!DPGq## zGJM}^2x|xGP(R0eNbHH-{c2dN*loPd#*&oeOgh9D-y~t9{>}~t!A}Q7mXi4GW@+2k z<5Lqlo9u`enVB#P0HU7!E;~K;z)l7sDBf>`GVEt&Lvi)}olvZyHYvj5ci99mUe@BE z(_Z87FEP;5OK~-EWI(+49UxlmfFwRXwnKoRgsqT7#QbNj&8^Z10PxrYyH;t!RuI6s zIK4gA|mR4Cf6%;4G0DD-Qap$Hvf3aUC$&{uMI#W>+GVMmSP*#87mn?xuRnS80|P?|82EJBg$gfWE+8mT|`it+sD22SqEQQlKQmQKU+A6^K{RGHKMb&+F$y~t( zBXWsx(x+&L77&T)Nd_P$m_E(#!RS^AAYs)1=V}8p5AX*4P5~Bx+RLvBNH`~$F2&JL zbLfn4JtSWbhU`a9-glWgN)?Dy00z_^=IVN+${@4FLO(n=LuaQ~1OS_*(Xlb{nog%_ z_V(?TzynZPu!dN30L-BwKOGqxPyvauhTx8)_L?-&*7lFGwD|`TE!^t>AWHpcCX%6> zksRg8MCEl{nAhu}zMfv%wR<=D{eJ4~>?FJ0PCEt$S~{1N70%pwEH5t$pk}70X(hBm zk;vM?5j3cnOOvTmB!{s?yL+m6;9NMVO=7paw$)Y*F|yA97^phjO_yn&ZE{wae@kmQ zgVjnqhX(1yCq7OcUf+g~4*)TsLHTT!Vv#VNK7E?boxecEVsT9kV*RC@&P-|6Y-_ws zy-q!$E@@up?&G!wq=-a|FJ>-TrbfkDFzZWS{<4txfB5cq>GqvFHQVPG7U_-RS<2`0 z^w7bF$Yx(V2mq5asU#T;Mlv&)crQ#@GD#)g_V(q=^!BC8Di{{C6gA~|{{ZdSv12qf zHASzz`f9aw!dg7T*MQOO(37oFx&HvEuqDjuEc9N|#P4g7a5}nXtK@RK1t3CLe3C}4gsbKEyQ#9!919zIdnL|x&8$L!Bc6S^@)PO zy6U2aSqn}ceT0rbe3-fe0UGGIw3*R6hUm%feKdOePBU z1ZjS5UNA(cGc)Reeft>nP65Wcg+vDF(&cvqC<^)fKK_4zZd|`1_S?(HIQ7I6G{3k= zr=Nd8{07H>_Bd=dvRWyU^how zGn8cpLr8GUd@fIKoINYfEzlJZfSFk!a1B--Hj9sfB)}`T9Ahslm)|W1C;(i^#D_0uYU8YH~_QD;Q+tIKNmZ)<|_D0wAMob^v5O-MlqT?`cd_ zGs5w@nE6W_7UOsTf5*U%#yJ%O1FeO*eCzBvD#$=@YYq9@$M<;NiPME}UIGN>AsDQc zWJqQ@l@_nc*erK8c6^g_Ygo3L@U!hD%N1=j2b=Ysb zbyk=#8#{dXi;M=~BZEGQusIB83WRD$p`s%vhtBCjiK3at*>-`=CZ~C+zw!4{M0YaOGUJ0Ts~Fupl+X2)FESJ5j6Yr0({mSQYf2h5 z=7p2D-x}+t@m>!t#U#_`uwsf(?KIUzOr+ohX(pT!0O9jui}f?Nu}lwo^wID9a>Bcv z0TB+}<*?HEaFON{tFvJf73tWn9vbvHnDG|Dyv-08_}sNC^n^Bm1^yzVsV$WYExrI;7V z)QMezCYgPiW&c4)YhzIbq8 z707w^Efcc~w9KY2mCM!a*T;US$KxfVCgB|N+QnG7yFj^OT7Y6OTPaaM(WgMY7Jy;9 zze*=NJT5-@JXtDvG8A=kLRFEO>ENiv!1h@$CP0{&fuTf-e)`tCf_bZJOF6pA;c+CB zqThIMjLe$I>JJ{@Lm%z;(sCk4pa0lk15;Pz{azFMZ?s>GbUsU;IlN!&7p3)FUOIbqC!VL_YC=WjnF54 z`mI&lCx7=JY2w_gGyw$gGMS?W99OY2d zBmk)=dy}b~`v!Yy@5hdl)v@}qguFo~wteQQKcJ(>PHgCejvRWJj(+O%WDoW20CJ{UB_$dS`dThmXBThL-IsqrJ$e%z?cZOs z?NhrBQ$D;v3oraLrEg!|&4_tv0og2weYMqAbVIlLs2zp zy|~>?7M{@)v)pGE!{l?=1Ss#^S#IgiU^d2IIo$M;qnDDE5{($FG{*j6?k9i0>623S z`!`-7W*wW(;)Ulj~YgqWJXD@ADRM#&QM9sZ6V$S$(g$%f*T#`roMoOTxdSS(hQ(3U!SWf0NxAXd{KEobY+o&v9~c|YzrWU zy4i)0su;8c_ZpZ{xX8{mn$5IA#}4eGAD_8IFQ1)SwavoEd-BA+KTm`5j{304>p8^m@(}(NiLw z-JI#+|04`;H57)hDB4jdlbE}etD6JnvbHDE+7cfkHwsX-qgkV{BBPPy$fU!hQ!jkD zuTzAqYf&#dZTxm>dQmV+apJuJ&zgBkoB;&zim!QKq`#&7Mk^?(Vug$iG+qgTWje5} z0MV8Ykb^YR5J(u3^gM%7Fu)#5();|cu83&KvPRT`Z3qIuRDgo$2lplB)=3T#X&vDI zjdJ8=X(1|t2I71aO7pqqS%4b-v(ATjwi_TGZ4T1t-bYzeGKsPs=(Om?v`OS7$S2kV zMF9p*6E&?f8Krjo{otf3wCg9IqjElD|kJG_aTUN{!Y zE?NO-6%4JvRGJ7ZdiChKde6^_817L!(DwDu-02FLFoCNw-wXG;xy3sJIA z63>As6P}}qG+n*9AZkT@fvsO80ZHjkfB6or#Hxi(gtuV^qfxJZM2=P#8h>3h7-Dl7 zOUnU-5s&mt@s-s2p|lG(z~4ku`=o83qiB8dO#3ip>bIwKqO|AX)ci>9cn}< zOXyIzD+{NMx|iEwCGQrrZ^HhbfBgzw5nxxRinLqEVQ~!pNnd_T8^HVOjerK?#0k6;B zK_?E5@z_HSYqbT(BAgViL$E8On7ai!#W?gQfgAD zBut|AW2uI`o29+Oebn3OrE?QEg|k=8m{OZUHjbg(F~rRDMj1S`;$y580k5m6mKrzM zwnM8mdJD7CYes`cEuS83D{%Te2N|5{rASMDu35H@Vk$amTVi=KezuQ_XJ$zb z)lM?MPThWK_H;WnM753jU0yN_Imz%|l^J%6RO#Ia7+R+;OiEYthg!i@3}_0#nO~07 zfTINgn$R2YiqVX0rhBj0YHu|d_j#yvElQQSxK>Nr0|?Mo966@-7$0i<;3h)WT&c9_ z3oJ00rgcg$&=`+vfDY^)5WkOVq9h0V6}#0!?WFqUEmUSk@>Dt)pu%DjT{MM!G2c$I zmb}uAC`)aOtl#M&eW!HMrup=`=BiL`ePx5|vm5h+01w)rjLm{k*?`H?ULUe`Ra+Pk z!=Qt7L1#n0Z5bePTzyLM9fq;08PIDYCy(6l5+_{JTwt-(Iw0^7(M{$B8zrkIk0@?H zXq07^Y-0AwY8D#y*L1%-wdH`2_77u1VjLI&WBFW#0h}IdguZt)N`GHbc>5 zYnq6Xj*?R;jg=djVe^6%^6EQGRM}*Y-&O%pr%USGYtOd0Cx>3CN@2S|!T!#ipQP!9 zl}-I$5jf>lt2tWpt?#UZjex&`y zH@y!f$<3^j>{im76Qm6TQl3w57!W1tYUQc6MfR7b{ys9xDvvw^nsV;yO-eV&Ia*>O z{}2DiezbYZu6@Is)(}O=vjJ#2ShIT>@B(rD0AXC7*flSB>)#MbAqMz-j zGCTId|J+vZSKE_ja))eR>Inp5iz_P^?@2=*+_MLz612)V>4;h>7LZjxk)zVPVKP40 zsoG6pR|_REa5paKaR>>r*O)ApvSQ<~QjMxzHa0a9z^|RZMN_wy=$X$SrUxF`Ck9N| z?2S1JFGnaDOQ|MJOWuHo%vLi6nfW_D(kDKHUVQBu{p6*0g$4=-S}AWxlNCpfTgzA^ z3_anMd+M+h7{!+tl(GpK3wp981b+k{nV+ySU%xOhaeBjm@SmTSi(?*T=gzc^l6EQ9 zF224%hLH}+Ym&rxI@Stcc~0+m43y*{^X@AVqdCh;A5H~#PaEx4Td$!Gy0kcDVJc~(IZiN9Ss4(M#ZGR2m*{q# z$z;^NyD(wDIzKH;O%ZqSc;6jswDH65UN|#J-}~X4^w#BBRV##fayV#kV1P0!v#N$V z%;q&pEC%vBtg7iCqI5%bn;#aM?@EHB~fG( zc8bSyY{qIPqv@J}@wD<8U8xU^R};-^^V9U7?>QES*Hsk~y@xeB>P%t>zBP%M=QS8+J2Aqp=;mwdM6ROYiJL<0+jHOyGEmbe}|a{JWg>Ph!U^KD+Fn2c9)g1GU!f&oesM8(?>^l zlh5Okg|S4hpF2nIT%D+oz8+<5i8eG@493?KFwoWc*u#fKt8gvt?j552i=%XLSqd48 zZ!S`TPqx0gg*7(PZg$p=%DN^?Yc9&=S|@`p(>|Y%!VC&pz7{mS7$NmjH`njz0|30t z7`&<>YBsI$ZH9HjUd3ER)2k!6vYu2h6jRmR*{On|9OL5;KTN?u7rp%ItBQarGj|?c zWwICyby^STH&33Rk>SRPQ>WESH|FCHOQk|;cN)a<@G zOcqR6sgNUAS8aD`tcUrE*9Lz=^cPw*(R^dYW-E1DayQ!N8fJ4uD zbfq@Pj0UK7{_N%N(HJum4^uuBUpM46vO~9+^kQ}jLfoXR2?dLR46?si^cjSfc#Liu zu@6xvGaZ+VCjPnxW&tZapauKf!h%{&Xa4^SgAz4pBC6lV`#zSa6GtCu2t+6nrDb8y z%VafKsFJJD-JDdU%bRuNNmgj?BQ6G}LdKl*_yM#+n=IyeTccd0D3Y#{>PHM_i|9h# z#mu!b!vF--UN4?|*Vn9$0H#A4m9<6iJJB+n0d?>`+#Hku2(%=}wn!{SiRD?Ljl$-e z%}t+av6%IIZ0U(xt#&MoxXHxYUSzWnb?V5dGtjteAOGG$9@gR_Yg0U#62G^YO{;+E z3Ut!#xp}dEih>gZ@?*t^d_(KR>w$>G;*?0$PG-PL0zRXM`Yb`3%PkVRe=m3FDeW-Q za*vJrV^U;=)thnSN(miJ#>Tmb_7R3cHbrKKTdbadheaW2SQ}(Zs=cxgD&?!bz_7+_ z6Qlg}a5$M&yQ^kgCQ3ubtSl6=sTyU!0Z=LsEItMV0zw1uV~K>g25*O3T|5ASapa+g z=)ZpUGv!cEvtoS9DEp2fCA{sGo9C|uM2b;W7=-XHR*X}!lopbrizF%ScTv34O70cu zB9`K5vFNY%0#{#n)rKpQi(`RV-2rLF2*O4romhutEdq#*n4DmVWhtyV#(GnKq-3^C zOR;JY5DHpJ7foU6sQ3(_gb7!oDI5a;V0jA#81NAT-(lezXmd|@m+)`lh#Iw>0$l1n z-#sHEGOBTbsulz^xyLBh+jN!+L=~5l6H+M54qnH$Hq>h;XCy~@wzG)15C+3^H3Otx zL!^ic7m_BW!K+nv%(&Ri5Tq48N%0-)X$MXTCihObdQSj?mI0WuwjjiXBd;e2ECv99 z@3Qf^<2^vZ_Z^7*SYvh#4QT=Sicxzv3*8Fdow_C>3NQmEBbQ&l0i|?OgEW{W?_aY!*x`VG$1EVXSRYi6#_C{|kAP^H(ZCtF_nk1@@8SGqmj@%~SM0RSLo Vr~Pu6T~h!6002ovPDHLkV1j;6EvWzi diff --git a/app/assets/images/pages/contribute/archmage/mischa_small.png b/app/assets/images/pages/contribute/archmage/mischa_small.png deleted file mode 100644 index 26cfae9e3b96b8c36ec0c293189996e80241fc7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6717 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 a6JP+?da%K~_1cgC0000w8v64fbkq3PcbkD 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̤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 JPDHLkV1nCe@0tJr diff --git a/app/assets/images/pages/contribute/archmage/sebastien_small.png b/app/assets/images/pages/contribute/archmage/sebastien_small.png deleted file mode 100644 index 27d3f418952cbd64acf126645620d53f2d97b7d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8277 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 Tg4ivq00000NkvXXu0mjf%g?5* diff --git a/app/assets/images/pages/contribute/archmage/shiying_small.png b/app/assets/images/pages/contribute/archmage/shiying_small.png deleted file mode 100644 index 10d7b270ca45ffc32717c7e4a72ec07ec2302807..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8900 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 diff --git a/app/assets/images/pages/contribute/artisan/andrew_small.png b/app/assets/images/pages/contribute/artisan/andrew_small.png deleted file mode 100644 index d3674ded9f4717b085b1e76973931e071d31894d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8857 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 TFk$I-00000NkvXXu0mjfwA`4E diff --git a/app/assets/images/pages/contribute/artisan/derek_small.png b/app/assets/images/pages/contribute/artisan/derek_small.png deleted file mode 100644 index 49ed07efe6956eb6445b833db8354dbe6360e500..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9942 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 UVC0r>OaK4?07*qoM6N<$f=K_{R{#J2 diff --git a/app/assets/images/pages/contribute/artisan/katharine_small.png b/app/assets/images/pages/contribute/artisan/katharine_small.png deleted file mode 100644 index 4bd2a2721b95e9785d47fe88b514c3db40f30237..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9426 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 diff --git a/app/views/contribute/ArchmageView.coffee b/app/views/contribute/ArchmageView.coffee index e9847448a..4fd0e7494 100644 --- a/app/views/contribute/ArchmageView.coffee +++ b/app/views/contribute/ArchmageView.coffee @@ -10,25 +10,26 @@ module.exports = class ArchmageView extends ContributeClassView {id: '52ccfc9bd3eb6b5a4100b60d', name: 'Glen De Cauwsemaecker', github: 'GlenDC'} {id: '52bfc3ecb7ec628868001297', name: 'Tom Steinbrecher', github: 'TomSteinbrecher'} {id: '5272806093680c5817033f73', name: 'Sébastien Moratinos', github: 'smoratinos'} - {name: 'deepak1556', avatar: 'deepak', github: 'deepak1556'} - {name: 'Ronnie Cheng', avatar: 'ronald', github: 'rhc2104'} - {name: 'Chloe Fan', avatar: 'chloe', github: 'chloester'} - {name: 'Rachel Xiang', avatar: 'rachel', github: 'rdxiang'} - {name: 'Dan Ristic', avatar: 'dan', github: 'dristic'} - {name: 'Brad Dickason', avatar: 'brad', github: 'bdickason'} - {name: 'Rebecca Saines', avatar: 'becca'} - {name: 'Laura Watiker', avatar: 'laura', github: 'lwatiker'} - {name: 'Shiying Zheng', avatar: 'shiying', github: 'shiyingzheng'} - {name: 'Mischa Lewis-Norelle', avatar: 'mischa', github: 'mlewisno'} - {name: 'Paul Buser', avatar: 'paul'} - {name: 'Benjamin Stern', avatar: 'ben'} - {name: 'Alex Cotsarelis', avatar: 'alex'} - {name: 'Ken Stanley', avatar: 'ken'} - {name: 'devast8a', avatar: '', github: 'devast8a'} - {name: 'phansch', avatar: '', github: 'phansch'} - {name: 'Zach Martin', avatar: '', github: 'zachster01'} + {id: '52d133893dc46cbe15001179', name: 'Deepak Raj', github: 'deepak1556'} + {id: '52699df70e404d591b000af7', name: 'Ronnie Cheng', github: 'rhc2104'} + {id: '5260b4c3ae8ec6795e000019', name: 'Chloe Fan', github: 'chloester'} + {id: '52d726ab3c70cec90c008f2d', name: 'Rachel Xiang', github: 'rdxiang'} + {id: '5286706d93df39a952001574', name: 'Dan Ristic', github: 'dristic'} + {id: '52cec8620b0d5c1b4c0039e6', name: 'Brad Dickason', github: 'bdickason'} + {id: '540397e2bc5b69a40e9c2fb1', name: 'Rebecca Saines'} + {id: '525ae40248839d81090013f2', name: 'Laura Watiker', github: 'lwatiker'} + {id: '540395e9fe56769115d7da86', name: 'Shiying Zheng', github: 'shiyingzheng'} + {id: '5403964dfe56769115d7da96', name: 'Mischa Lewis-Norelle', github: 'mlewisno'} + {id: '52b8be459e47006b4100094b', name: 'Paul Buser'} + {id: '540396effe56769115d7daa8', name: 'Benjamin Stern'} + {id: '5403974b11058b4213074779', name: 'Alex Cotsarelis'} + {id: '54039780fe56769115d7dab5', name: 'Ken Stanley'} + {id: '531258b5e0789d4609614110', name: 'Ruben Vereecken', github: 'rubenvereecken'} + {id: '5276ad5dcf83207a2801d3b4', name: 'Zach Martin', github: 'zachster01'} + {id: '530df0cbc06854403ba67c15', name: 'Alexandru Caciulescu', github: 'Darredevil'} {name: 'David Golds', avatar: ''} {name: 'gabceb', avatar: '', github: 'gabceb'} {name: 'MDP66', avatar: '', github: 'MDP66'} - {name: 'Alexandru Caciulescu', avatar: '', github: 'Darredevil'} + {name: 'devast8a', avatar: '', github: 'devast8a'} + {name: 'phansch', avatar: '', github: 'phansch'} ] diff --git a/app/views/contribute/ArtisanView.coffee b/app/views/contribute/ArtisanView.coffee index 40aa5bba0..c64e66be5 100644 --- a/app/views/contribute/ArtisanView.coffee +++ b/app/views/contribute/ArtisanView.coffee @@ -8,18 +8,18 @@ module.exports = class ArtisanView extends ContributeClassView contributorClassName: 'artisan' contributors: [ + {id: '5276ad5dcf83207a2801d3b4', name: 'Zach Martin', github: 'zachster01'} + {id: '530df0cbc06854403ba67c15', name: 'Alexandru Caciulescu', github: 'Darredevil'} + {id: '54038e91d3dbccd212505dee', name: 'Robert Moreton'} + {id: '54038f0bbc5b69a40e9c2a01', name: 'Andrew Witcher'} + {id: '54038f6b11058b4213074320', name: 'Axandre Oge'} + {id: '5403905c0557f27b0c3384be', name: 'Katharine Chan'} + {id: '5403908e0557f27b0c3384d9', name: 'Derek Wong'} {name: 'Sootn', avatar: ''} - {name: 'Zach Martin', avatar: '', github: 'zachster01'} {name: 'Aftermath', avatar: ''} {name: 'mcdavid1991', avatar: ''} {name: 'dwhittaker', avatar: ''} {name: 'Zacharias Fisches', avatar: ''} {name: 'Tom Setliff', avatar: ''} - {name: 'Robert Moreton', avatar: 'rob'} - {name: 'Andrew Witcher', avatar: 'andrew'} - {name: 'Axandre Oge', avatar: 'axandre'} - {name: 'Katharine Chan', avatar: 'katharine'} - {name: 'Derek Wong', avatar: 'derek'} - {name: 'Alexandru Caciulescu', avatar: '', github: 'Darredevil'} {name: 'Prabh Simran Singh Baweja', avatar: '', github: 'prabh27'} ] From 1329d31a63bad6ca96648a295d0765cad3495757 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sun, 31 Aug 2014 15:08:52 -0700 Subject: [PATCH 78/98] Hot-swapping inventory works now. --- app/lib/LevelLoader.coffee | 14 ++++++++++---- app/lib/surface/path.coffee | 2 +- app/models/Level.coffee | 9 +++++---- app/schemas/subscriptions/play.coffee | 2 ++ app/views/EmployersView.coffee | 2 +- app/views/game-menu/GameMenuModal.coffee | 2 ++ app/views/game-menu/InventoryView.coffee | 12 +++++++----- app/views/kinds/ModalView.coffee | 5 +++-- app/views/kinds/RootView.coffee | 4 ++-- app/views/play/level/PlayLevelView.coffee | 15 +++++++++++++-- 10 files changed, 46 insertions(+), 21 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index b412abf74..f326ed6b1 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -66,15 +66,21 @@ module.exports = class LevelLoader extends CocoClass session = new LevelSession().setURL url @sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false}) @session = @sessionResource.model - @listenToOnce @session, 'sync', -> - @session.url = -> '/db/level.session/' + @id - @loadDependenciesForSession(@session) + if @session.loaded + @loadDependenciesForSession @session + else + @listenToOnce @session, 'sync', -> + @session.url = -> '/db/level.session/' + @id + @loadDependenciesForSession @session if @opponentSessionID opponentSession = new LevelSession().setURL "/db/level_session/#{@opponentSessionID}" @opponentSessionResource = @supermodel.loadModel(opponentSession, 'opponent_session') @opponentSession = @opponentSessionResource.model - @listenToOnce @opponentSession, 'sync', @loadDependenciesForSession + if @opponentSession.loaded + @loadDependenciesForSession @opponentSession + else + @listenToOnce @opponentSession, 'sync', @loadDependenciesForSession loadDependenciesForSession: (session) -> return if @levelID is 'sky-span' # TODO diff --git a/app/lib/surface/path.coffee b/app/lib/surface/path.coffee index 05654f906..be17ae42f 100644 --- a/app/lib/surface/path.coffee +++ b/app/lib/surface/path.coffee @@ -143,7 +143,7 @@ module.exports.Trailmaster = class Trailmaster i = 0 sprites = [] sprite = @sprites[thang.id] - return sprites unless sprite? + return sprites unless sprite?.actions lastPos = @camera.surfaceToWorld x: sprite.imageObject.x, y: sprite.imageObject.y minDistance = Math.pow(CLONE_INTERVAL * Camera.MPP, 2) actions = @world.actionsForThang(thang.id) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 3b08b67eb..6c61ee214 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -38,17 +38,17 @@ module.exports = class Level extends CocoModel denormalizeThang: (levelThang, supermodel, session) -> levelThang.components ?= [] - thangType = supermodel.getModelByOriginal(ThangType, levelThang.thangType) - # Empty out placeholder Components and store their values if we're the hero placeholder. placeholders = {} if levelThang.id is 'Hero Placeholder' - for thangComponent in levelThang.components ? [] + for thangComponent in levelThang.components placeholders[thangComponent.original] = thangComponent - levelThang.components = [] + levelThang.components = [] # We have stored the placeholder values, so we can inherit everything else. heroThangType = session?.get('heroConfig')?.thangType levelThang.thangType = heroThangType if heroThangType + thangType = supermodel.getModelByOriginal(ThangType, levelThang.thangType) + configs = {} for thangComponent in levelThang.components configs[thangComponent.original] = thangComponent @@ -79,6 +79,7 @@ module.exports = class Level extends CocoModel if levelThang.id is 'Hero Placeholder' and equips = _.find levelThang.components, {original: LevelComponent.EquipsID} inventory = session?.get('heroConfig')?.inventory + equips.config ?= {} equips.config.inventory = $.extend true, {}, inventory if inventory diff --git a/app/schemas/subscriptions/play.coffee b/app/schemas/subscriptions/play.coffee index 4c7c05fa5..9ffedd3b6 100644 --- a/app/schemas/subscriptions/play.coffee +++ b/app/schemas/subscriptions/play.coffee @@ -152,3 +152,5 @@ module.exports = timedOut: {type: 'boolean'} 'level:edit-wizard-settings': c.object {} + + 'level:inventory-changed': c.object {} diff --git a/app/views/EmployersView.coffee b/app/views/EmployersView.coffee index d9dcd5a89..bb1292c93 100644 --- a/app/views/EmployersView.coffee +++ b/app/views/EmployersView.coffee @@ -20,7 +20,7 @@ module.exports = class EmployersView extends RootView 'change #select_all_checkbox': 'handleSelectAllChange' 'click .get-started-button': 'openSignupModal' 'click .navbar-brand': 'restoreBodyColor' - 'click #login-link': 'onClickAuthbutton' + 'click #login-link': 'onClickAuthButton' 'click #filter-link': 'swapFolderIcon' 'click #create-alert-button': 'createFilterAlert' 'click .deletion-col': 'deleteFilterAlert' diff --git a/app/views/game-menu/GameMenuModal.coffee b/app/views/game-menu/GameMenuModal.coffee index cfb49a4db..c23e7881c 100644 --- a/app/views/game-menu/GameMenuModal.coffee +++ b/app/views/game-menu/GameMenuModal.coffee @@ -13,6 +13,7 @@ module.exports = class GameMenuModal extends ModalView template: template modalWidthPercent: 95 id: 'game-menu-modal' + instant: true constructor: (options) -> super options @@ -28,6 +29,7 @@ module.exports = class GameMenuModal extends ModalView afterRender: -> super() + @$el.toggleClas @insertSubView new submenuView @options for submenuView in submenuViews (if @options.showDevBits then @subviews.inventory_view else @subviews.choose_hero_view).$el.addClass 'active' diff --git a/app/views/game-menu/InventoryView.coffee b/app/views/game-menu/InventoryView.coffee index 32faff66c..b51fd3631 100644 --- a/app/views/game-menu/InventoryView.coffee +++ b/app/views/game-menu/InventoryView.coffee @@ -240,8 +240,10 @@ module.exports = class InventoryView extends CocoView onHidden: -> inventory = @getCurrentEquipmentConfig() heroConfig = @options.session.get('heroConfig') ? {} - unless _.isEqual inventory, heroConfig.inventory - heroConfig.inventory = inventory - heroConfig.thangType ?= '529ffbf1cf1818f2be000001' # Temp: assign Tharin as the hero - @options.session.set 'heroConfig', heroConfig - @options.session.patch() + return if _.isEqual inventory, heroConfig.inventory + heroConfig.inventory = inventory + heroConfig.thangType ?= '529ffbf1cf1818f2be000001' # Temp: assign Tharin as the hero + @options.session.set 'heroConfig', heroConfig + @options.session.patch success: -> + _.defer -> + Backbone.Mediator.publish 'level:inventory-changed', {} diff --git a/app/views/kinds/ModalView.coffee b/app/views/kinds/ModalView.coffee index b2fecf1ce..5fd527b9d 100644 --- a/app/views/kinds/ModalView.coffee +++ b/app/views/kinds/ModalView.coffee @@ -6,6 +6,7 @@ module.exports = class ModalView extends CocoView closesOnClickOutside: true modalWidthPercent: null plain: false + instant: false events: 'click a': 'toggleModal' @@ -17,7 +18,7 @@ module.exports = class ModalView extends CocoView constructor: (options) -> options ?= {} - @className = @className.replace ' fade', '' if options.instant + @className = @className.replace ' fade', '' if options.instant or @instant @closeButton = options.closeButton if options.closeButton? @modalWidthPercent = options.modalWidthPercent if options.modalWidthPercent super options @@ -35,7 +36,7 @@ module.exports = class ModalView extends CocoView if Backbone.history.fragment is "employers" $(@$el).find(".background-wrapper").each -> $(this).addClass("employer-modal-background-wrapper").removeClass("background-wrapper") - + if @modalWidthPercent @$el.find('.modal-dialog').css width: "#{@modalWidthPercent}%" @$el.on 'hide.bs.modal', => diff --git a/app/views/kinds/RootView.coffee b/app/views/kinds/RootView.coffee index 02723f8ea..ebd44c0b5 100644 --- a/app/views/kinds/RootView.coffee +++ b/app/views/kinds/RootView.coffee @@ -25,7 +25,7 @@ module.exports = class RootView extends CocoView 'click #logout-button': 'logoutAccount' 'change .language-dropdown': 'onLanguageChanged' 'click .toggle-fullscreen': 'toggleFullscreen' - 'click .auth-button': 'onClickAuthbutton' + 'click .auth-button': 'onClickAuthButton' 'click a': 'toggleModal' 'click button': 'toggleModal' 'click li': 'toggleModal' @@ -50,7 +50,7 @@ module.exports = class RootView extends CocoView subview = new WizardSettingsModal {} @openModalView subview - onClickAuthbutton: -> + onClickAuthButton: -> AuthModal = require 'views/modal/AuthModal' @openModalView new AuthModal {} diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 2cd3e1479..a5c38c4cd 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -64,6 +64,7 @@ module.exports = class PlayLevelView extends RootView 'level:loading-view-unveiled': 'onLoadingViewUnveiled' 'playback:real-time-playback-started': 'onRealTimePlaybackStarted' 'playback:real-time-playback-ended': 'onRealTimePlaybackEnded' + 'level:inventory-changed': 'onInventoryChanged' events: 'click #level-done-button': 'onDonePressed' @@ -349,7 +350,7 @@ module.exports = class PlayLevelView extends RootView @setLevel e.level, e.supermodel if isReload @scriptManager.setScripts(e.level.get('scripts')) - Backbone.Mediator.publish 'tome:cast-spell' # a bit hacky + Backbone.Mediator.publish 'tome:cast-spell', {} # a bit hacky onLevelReloadThangType: (e) -> tt = e.thangType @@ -358,7 +359,17 @@ module.exports = class PlayLevelView extends RootView for key, val of tt.attributes model.attributes[key] = val break - Backbone.Mediator.publish 'tome:cast-spell' + Backbone.Mediator.publish 'tome:cast-spell', {} + + onInventoryChanged: (e) -> + # Doesn't work because the new inventory ThangTypes may not be loaded. + #@setLevel @level, @supermodel + #Backbone.Mediator.publish 'tome:cast-spell', {} + # We'll just make a new PlayLevelView instead + Backbone.Mediator.publish 'router:navigate', { + route: window.location.pathname, + viewClass: PlayLevelView, + viewArgs: [{supermodel: @supermodel}, @levelID]} onWindowResize: (s...) -> $('#pointer').css('opacity', 0.0) From 040ac4ff4ef606b7ed05e53b0aba423ddb4872bc Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sun, 31 Aug 2014 16:05:15 -0700 Subject: [PATCH 79/98] Fixed bug with black flashes during script playback. --- app/lib/surface/Surface.coffee | 1 + server/levels/sessions/level_session_handler.coffee | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 315a1a1d2..f15556120 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -578,6 +578,7 @@ module.exports = Surface = class Surface extends CocoClass @playing = false # Will start when countdown is done. onRealTimePlaybackEnded: (e) -> + return unless @realTime @realTime = false @onResize() @spriteBoss.selfWizardSprite?.toggle true diff --git a/server/levels/sessions/level_session_handler.coffee b/server/levels/sessions/level_session_handler.coffee index 573e17466..7d7e2886d 100644 --- a/server/levels/sessions/level_session_handler.coffee +++ b/server/levels/sessions/level_session_handler.coffee @@ -14,7 +14,7 @@ class LevelSessionHandler extends Handler formatEntity: (req, document) -> documentObject = super(req, document) - if req.user.isAdmin() or req.user.id is document.creator or ('employer' in req.user.get('permissions')) + if req.user.isAdmin() or req.user.id is document.creator or ('employer' in (req.user.get('permissions') ? [])) return documentObject else return _.omit documentObject, @privateProperties @@ -31,7 +31,7 @@ class LevelSessionHandler extends Handler hasAccessToDocument: (req, document, method=null) -> return true if req.method is 'GET' and document.get('totalScore') - return true if ('employer' in req.user.get('permissions')) and (method ? req.method).toLowerCase() is 'get' + return true if ('employer' in (req.user.get('permissions') ? [])) and (method ? req.method).toLowerCase() is 'get' super(arguments...) getCodeLanguageCounts: (req, res) -> From 3e0518cd7033200306a06569edd54a619c4d4a6b Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 09:11:10 -0700 Subject: [PATCH 80/98] Fixed a few bugs and added a bunch of defensive handling for permissions not existing without new defaults. Fixed longstanding mistake with signup ignoring email newsletter setting. --- app/lib/forms.coffee | 4 ++-- app/models/CocoModel.coffee | 27 ++++++++++------------ app/models/LevelSession.coffee | 2 +- app/models/User.coffee | 5 +--- app/templates/account/settings.jade | 2 +- app/templates/base.jade | 2 +- app/views/EmployersView.coffee | 6 ++--- app/views/admin/CandidatesView.coffee | 8 +++---- app/views/editor/ForkModal.coffee | 2 +- app/views/modal/AuthModal.coffee | 2 +- app/views/modal/EmployerSignupModal.coffee | 2 +- app/views/play/level/tome/Spell.coffee | 2 +- app/views/user/JobProfileView.coffee | 8 +++---- server/users/user_handler.coffee | 8 +++---- 14 files changed, 35 insertions(+), 45 deletions(-) diff --git a/app/lib/forms.coffee b/app/lib/forms.coffee index 86fe1530d..f319be0f2 100644 --- a/app/lib/forms.coffee +++ b/app/lib/forms.coffee @@ -31,7 +31,7 @@ module.exports.applyErrorsToForm = (el, errors, warning=false) -> module.exports.setErrorToField = setErrorToField = (el, message, warning=false) -> formGroup = el.closest('.form-group') unless formGroup.length - return console.error "#{el} did not contain a form group" + return console.error el, " did not contain a form group, so couldn't show message:", message kind = if warning then 'warning' else 'error' formGroup.addClass "has-#{kind}" @@ -40,7 +40,7 @@ module.exports.setErrorToField = setErrorToField = (el, message, warning=false) module.exports.setErrorToProperty = setErrorToProperty = (el, property, message, warning=false) -> input = $("[name='#{property}']", el) unless input.length - return console.error "#{property} not found in #{el}" + return console.error "#{property} not found in", el, "so couldn't show message:", message setErrorToField input, message, warning diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index c6518340e..44a99bee3 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -51,16 +51,16 @@ class CocoModel extends Backbone.Model @loadFromBackup() getNormalizedURL: -> "#{@urlRoot}/#{@id}" - + attributesWithDefaults: undefined - + get: (attribute, withDefault=false) -> if withDefault if @attributesWithDefaults is undefined then @buildAttributesWithDefaults() return @attributesWithDefaults[attribute] else super(attribute) - + set: -> delete @attributesWithDefaults inFlux = @loading or not @loaded @@ -180,13 +180,13 @@ class CocoModel extends Backbone.Model clone isPublished: -> - for permission in @get('permissions') or [] + for permission in (@get('permissions', true) ? []) return true if permission.target is 'public' and permission.access is 'read' false publish: -> if @isPublished() then throw new Error('Can\'t publish what\'s already-published. Can\'t kill what\'s already dead.') - @set 'permissions', (@get('permissions') or []).concat({access: 'read', target: 'public'}) + @set 'permissions', @get('permissions', true).concat({access: 'read', target: 'public'}) @isObjectID: (s) -> s.length is 24 and s.match(/[a-f0-9]/gi)?.length is 24 @@ -195,10 +195,9 @@ class CocoModel extends Backbone.Model # actor is a User object actor ?= me return true if actor.isAdmin() - if @get('permissions')? - for permission in @get('permissions') - if permission.target is 'public' or actor.get('_id') is permission.target - return true if permission.access in ['owner', 'read'] + for permission in (@get('permissions', true) ? []) + if permission.target is 'public' or actor.get('_id') is permission.target + return true if permission.access in ['owner', 'read'] return false @@ -206,16 +205,14 @@ class CocoModel extends Backbone.Model # actor is a User object actor ?= me return true if actor.isAdmin() - if @get('permissions')? - for permission in @get('permissions') - if permission.target is 'public' or actor.get('_id') is permission.target - return true if permission.access in ['owner', 'write'] + for permission in (@get('permissions', true) ? []) + if permission.target is 'public' or actor.get('_id') is permission.target + return true if permission.access in ['owner', 'write'] return false getOwner: -> - return null unless permissions = @get 'permissions' - ownerPermission = _.find permissions, access: 'owner' + ownerPermission = _.find @get('permissions', true), access: 'owner' ownerPermission?.target getDelta: -> diff --git a/app/models/LevelSession.coffee b/app/models/LevelSession.coffee index 34b03be6d..ba87aef23 100644 --- a/app/models/LevelSession.coffee +++ b/app/models/LevelSession.coffee @@ -13,7 +13,7 @@ module.exports = class LevelSession extends CocoModel @set('state', state) updatePermissions: -> - permissions = @get 'permissions' + permissions = @get 'permissions', true permissions = (p for p in permissions when p.target isnt 'public') if @get('multiplayer') permissions.push {target: 'public', access: 'write'} diff --git a/app/models/User.coffee b/app/models/User.coffee index d9af11f11..25071ac26 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -13,10 +13,7 @@ module.exports = class User extends CocoModel CocoModel.pollAchievements() # Check for achievements on login super arguments... - isAdmin: -> - permissions = @attributes['permissions'] or [] - return 'admin' in permissions - + isAdmin: -> 'admin' in @get('permissions', true) isAnonymous: -> @get('anonymous', true) displayName: -> @get('name', true) diff --git a/app/templates/account/settings.jade b/app/templates/account/settings.jade index cbef47a7c..d07b97d16 100644 --- a/app/templates/account/settings.jade +++ b/app/templates/account/settings.jade @@ -32,7 +32,7 @@ block content .form - var name = me.get('name') || ''; - var email = me.get('email'); - - var admin = me.get('permissions').indexOf('admin') != -1; + - var admin = me.get('permissions', true).indexOf('admin') != -1; .form-group label.control-label(for="name", data-i18n="general.name") Name input#name.form-control(name="name", type="text", value="#{name}") diff --git a/app/templates/base.jade b/app/templates/base.jade index 2d36b5d0e..a80d84c89 100644 --- a/app/templates/base.jade +++ b/app/templates/base.jade @@ -67,7 +67,7 @@ body .footer.clearfix .content p.footer-link-text - if pathname == "/" || (me.get('permissions') || []).indexOf('employer') != -1 + if pathname == "/" || (me.get('permissions', true)).indexOf('employer') != -1 a(href='/employers', title='Home', tabindex=-1, data-i18n="nav.employers") Employers else a(href='/', title='Home', tabindex=-1, data-i18n="nav.home") Home diff --git a/app/views/EmployersView.coffee b/app/views/EmployersView.coffee index bb1292c93..4e9c45e5f 100644 --- a/app/views/EmployersView.coffee +++ b/app/views/EmployersView.coffee @@ -197,9 +197,7 @@ module.exports = class EmployersView extends RootView ctx.numberOfCandidates = ctx.featuredCandidates.length ctx - isEmployer: -> - userPermissions = me.get('permissions') ? [] - _.contains userPermissions, 'employer' + isEmployer: -> 'employer' in me.get('permissions', true) setUpScrolling: => $('.nano').nanoScroller() @@ -209,7 +207,7 @@ module.exports = class EmployersView extends RootView # $('.nano').nanoScroller({scrollTo: $(window.location.hash)}) checkForEmployerSignupHash: => - if window.location.hash is '#employerSignupLoggingIn' and not ('employer' in me.get('permissions')) and not me.isAdmin() + if window.location.hash is '#employerSignupLoggingIn' and not ('employer' in me.get('permissions', true)) and not me.isAdmin() @openModalView new EmployerSignupModal window.location.hash = '' diff --git a/app/views/admin/CandidatesView.coffee b/app/views/admin/CandidatesView.coffee index 3216be2dc..39ddca817 100644 --- a/app/views/admin/CandidatesView.coffee +++ b/app/views/admin/CandidatesView.coffee @@ -52,9 +52,7 @@ module.exports = class CandidatesView extends RootView ctx._ = _ ctx - isEmployer: -> - userPermissions = me.get('permissions') ? [] - _.contains userPermissions, "employer" + isEmployer: -> 'employer' in me.get('permissions', true) setUpScrolling: -> $(".nano").nanoScroller() @@ -64,9 +62,9 @@ module.exports = class CandidatesView extends RootView $(".nano").nanoScroller({scrollTo:$(window.location.hash)}) checkForEmployerSignupHash: => - if window.location.hash is "#employerSignupLoggingIn" and not ("employer" in me.get("permissions")) + if window.location.hash is "#employerSignupLoggingIn" and not ("employer" in me.get('permissions', true)) @openModalView new EmployerSignupModal - window.location.hash = "" + window.location.hash = '' sortTable: -> # http://mottie.github.io/tablesorter/docs/example-widget-bootstrap-theme.html diff --git a/app/views/editor/ForkModal.coffee b/app/views/editor/ForkModal.coffee index 99e4170ea..63399693f 100644 --- a/app/views/editor/ForkModal.coffee +++ b/app/views/editor/ForkModal.coffee @@ -31,7 +31,7 @@ module.exports = class ForkModal extends ModalView newModel.unset 'parent' newModel.set 'commitMessage', "Forked from #{@model.get('name')}" newModel.set 'name', @$el.find('#fork-model-name').val() - if @model.get 'permissions' + if @model.schema.properties.permissions newModel.set 'permissions', [access: 'owner', target: me.id] newPathPrefix = "editor/#{@editorPath}/" res = newModel.save() diff --git a/app/views/modal/AuthModal.coffee b/app/views/modal/AuthModal.coffee index 67abf2537..251a45a8a 100644 --- a/app/views/modal/AuthModal.coffee +++ b/app/views/modal/AuthModal.coffee @@ -75,7 +75,7 @@ module.exports = class AuthModal extends ModalView userObject.name = @suggestedName if @suggestedName for key, val of me.attributes when key in ['preferredLanguage', 'testGroupNumber', 'dateCreated', 'wizardColor1', 'name', 'music', 'volume', 'emails'] userObject[key] ?= val - subscribe = @$el.find('#signup-subscribe').prop('checked') + subscribe = @$el.find('#subscribe').prop('checked') userObject.emails ?= {} userObject.emails.generalNews ?= {} userObject.emails.generalNews.enabled = subscribe diff --git a/app/views/modal/EmployerSignupModal.coffee b/app/views/modal/EmployerSignupModal.coffee index 2c5306356..ab13e309b 100644 --- a/app/views/modal/EmployerSignupModal.coffee +++ b/app/views/modal/EmployerSignupModal.coffee @@ -57,7 +57,7 @@ module.exports = class EmployerSignupModal extends ModalView getRenderData: -> context = super() context.userIsAuthorized = @authorizedWithLinkedIn - context.userHasSignedContract = 'employer' in me.get('permissions') + context.userHasSignedContract = 'employer' in me.get('permissions', true) context.userIsAnonymous = context.me.get('anonymous') context.sentMoreInfoEmail = @sentMoreInfoEmail context diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee index 5d56e131d..cfde25fe7 100644 --- a/app/views/play/level/tome/Spell.coffee +++ b/app/views/play/level/tome/Spell.coffee @@ -159,5 +159,5 @@ module.exports = class Spell return true if @spectateView # Use transpiled code for both teams if we're just spectating. return true if @isEnemySpell() # Use transpiled for enemy spells. # Players without permissions can't view the raw code. - return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions')) + return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions', true)) false diff --git a/app/views/user/JobProfileView.coffee b/app/views/user/JobProfileView.coffee index 8a176782e..9edc938fd 100644 --- a/app/views/user/JobProfileView.coffee +++ b/app/views/user/JobProfileView.coffee @@ -72,7 +72,7 @@ module.exports = class JobProfileView extends UserView finishInit: -> return unless @userID @uploadFilePath = "db/user/#{@userID}" - + if @user?.get('firstName') jobProfile = @user.get('jobProfile') jobProfile ?= {} @@ -81,7 +81,7 @@ module.exports = class JobProfileView extends UserView @user.set('jobProfile', jobProfile) @highlightedContainers = [] - if me.isAdmin() or 'employer' in me.get('permissions') + if me.isAdmin() or 'employer' in me.get('permissions', true) $.post "/db/user/#{me.id}/track/view_candidate" $.post "/db/user/#{@userID}/track/viewed_by_employer" unless me.isAdmin() @sessions = @supermodel.loadCollection(new LevelSessionsCollection(@userID), 'candidate_sessions').model @@ -235,7 +235,7 @@ module.exports = class JobProfileView extends UserView context.rawProfile = @user.get('jobProfile') or {} context.user = @user context.myProfile = @isMe() - context.allowedToViewJobProfile = @user and (me.isAdmin() or 'employer' in me.get('permissions') or (context.myProfile && !me.get('anonymous'))) + context.allowedToViewJobProfile = @user and (me.isAdmin() or 'employer' in me.get('permissions', true) or (context.myProfile && !me.get('anonymous'))) context.allowedToEditJobProfile = @user and (me.isAdmin() or (context.myProfile && !me.get('anonymous'))) context.profileApproved = @user?.get 'jobProfileApproved' context.progress = @progress ? @updateProgress() @@ -589,4 +589,4 @@ module.exports = class JobProfileView extends UserView destroy: -> @remarkTreema?.destroy() - super() \ No newline at end of file + super() diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index f3d40b649..40eed11ea 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -224,7 +224,7 @@ UserHandler = class UserHandler extends Handler res.end() getLevelSessionsForEmployer: (req, res, userID) -> - return @sendUnauthorizedError(res) unless req.user._id+'' is userID or req.user.isAdmin() or ('employer' in req.user.get('permissions')) + return @sendUnauthorizedError(res) unless req.user._id+'' is userID or req.user.isAdmin() or ('employer' in (req.user.get('permissions') ? [])) query = creator: userID, levelID: {$in: ['gridmancer', 'greed', 'dungeon-arena', 'brawlwood', 'gold-rush']} projection = 'levelName levelID team playtime codeLanguage submitted code totalScore teamSpells level' LevelSession.find(query).select(projection).exec (err, documents) => @@ -280,7 +280,7 @@ UserHandler = class UserHandler extends Handler return @sendMethodNotAllowed res unless req.method is 'POST' isMe = userID is req.user._id + '' isAuthorized = isMe or req.user.isAdmin() - isAuthorized ||= ('employer' in req.user.get('permissions')) and (activityName in ['viewed_by_employer', 'contacted_by_employer']) + isAuthorized ||= ('employer' in (req.user.get('permissions') ? [])) and (activityName in ['viewed_by_employer', 'contacted_by_employer']) return @sendUnauthorizedError res unless isAuthorized updateUser = (user) => activity = user.trackActivity activityName, increment @@ -303,7 +303,7 @@ UserHandler = class UserHandler extends Handler if not profileData.id or not profileData.positions or not profileData.emailAddress or not profileData.firstName or not profileData.lastName return errors.badInput(res, 'You need to have a more complete profile to sign up for this service.') @modelClass.findById(req.user.id).exec (err, user) => - if user.get('employerAt') or user.get('signedEmployerAgreement') or 'employer' in user.get('permissions') + if user.get('employerAt') or user.get('signedEmployerAgreement') or 'employer' in (user.get('permissions') ? []) return errors.conflict(res, 'You already have signed the agreement!') #TODO: Search for the current position employerAt = _.filter(profileData.positions.values, 'isCurrent')[0]?.company.name ? 'Not available' @@ -322,7 +322,7 @@ UserHandler = class UserHandler extends Handler res.end() getCandidates: (req, res) -> - authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions')) + authorized = req.user.isAdmin() or ('employer' in (req.user.get('permissions') ? [])) months = if req.user.isAdmin() then 12 else 2 since = (new Date((new Date()) - months * 30.4 * 86400 * 1000)).toISOString() query = {'jobProfile.updated': {$gt: since}} From 6df5ca00b2ff2aa523a06d4c4e60a731b1693a89 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 13:51:30 -0700 Subject: [PATCH 81/98] Fixed #69. --- app/lib/LevelLoader.coffee | 28 +++--- app/schemas/models/level_component.coffee | 2 +- app/views/account/JobProfileTreemaView.coffee | 4 +- .../component/ThangComponentConfigView.coffee | 7 +- .../editor/level/LevelFeedbackView.coffee | 10 +- app/views/editor/level/treema_nodes.coffee | 98 ++++++++++--------- 6 files changed, 81 insertions(+), 68 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index f326ed6b1..620912307 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -116,7 +116,7 @@ module.exports = class LevelLoader extends CocoClass flagThang = thangType: '53fa25f25bc220000052c2be', id: 'Placeholder Flag', components: [] for thang in (@level.get('thangs') or []).concat [flagThang] thangIDs.push thang.thangType - @loadItemThangsEquippedByLevelThang(thang) + @loadThangsRequiredByLevelThang(thang) for comp in thang.components or [] componentVersions.push _.pick(comp, ['original', 'majorVersion']) @@ -160,23 +160,27 @@ module.exports = class LevelLoader extends CocoClass @worldNecessities = @worldNecessities.concat worldNecessities - loadItemThangsEquippedByLevelThang: (levelThang) -> - @loadItemThangsFromComponentList levelThang.components + loadThangsRequiredByLevelThang: (levelThang) -> + @loadThangsRequiredFromComponentList levelThang.components - loadItemThangsEquippedByThangType: (thangType) -> - @loadItemThangsFromComponentList thangType.get('components') + loadThangsRequiredByThangType: (thangType) -> + @loadThangsRequiredFromComponentList thangType.get('components') - loadItemThangsFromComponentList: (components) -> - equipsThangComponent = _.find components, (c) -> c.original is LevelComponent.EquipsID - inventory = equipsThangComponent?.config?.inventory - for itemThangType in _.values inventory - url = "/db/thang.type/#{itemThangType}/version?project=name,components,original" + loadThangsRequiredFromComponentList: (components) -> + requiredThangTypes = [] + for component in components when component.config + if component.original is LevelComponent.EquipsID + requiredThangTypes.push itemThangType for itemThangType in _.values (component.config.inventory ? {}) + else if component.config.requiredThangTypes + requiredThangTypes = requiredThangTypes.concat component.config.requiredThangTypes + for thangType in requiredThangTypes + url = "/db/thang.type/#{thangType}/version?project=name,components,original" @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') onThangNamesLoaded: (thangNames) -> for thangType in thangNames.models @loadDefaultComponentsForThangType(thangType) - @loadItemThangsEquippedByThangType(thangType) + @loadThangsRequiredByThangType(thangType) loadDefaultComponentsForThangType: (thangType) -> return unless components = thangType.get('components') @@ -188,7 +192,7 @@ module.exports = class LevelLoader extends CocoClass index = @worldNecessities.indexOf(resource) if resource.name is 'thang' @loadDefaultComponentsForThangType(resource.model) - @loadItemThangsEquippedByThangType(resource.model) + @loadThangsRequiredByThangType(resource.model) return unless index >= 0 @worldNecessities.splice(index, 1) diff --git a/app/schemas/models/level_component.coffee b/app/schemas/models/level_component.coffee index 9907210a6..40e55800e 100644 --- a/app/schemas/models/level_component.coffee +++ b/app/schemas/models/level_component.coffee @@ -143,7 +143,7 @@ _.extend LevelComponentSchema.properties, format: 'hidden' dependencies: c.array {title: 'Dependencies', description: 'An array of Components upon which this Component depends.', uniqueItems: true}, DependencySchema propertyDocumentation: c.array {title: 'Property Documentation', description: 'An array of documentation entries for each notable property this Component will add to its Thang which other Components might want to also use.'}, PropertyDocumentationSchema - configSchema: _.extend metaschema, {title: 'Configuration Schema', description: 'A schema for validating the arguments that can be passed to this Component as configuration.', default: {type: 'object', additionalProperties: false}} + configSchema: _.extend metaschema, {title: 'Configuration Schema', description: 'A schema for validating the arguments that can be passed to this Component as configuration.', default: {type: 'object'}} official: type: 'boolean' title: 'Official' diff --git a/app/views/account/JobProfileTreemaView.coffee b/app/views/account/JobProfileTreemaView.coffee index f5a8e3320..3c3b3a7f6 100644 --- a/app/views/account/JobProfileTreemaView.coffee +++ b/app/views/account/JobProfileTreemaView.coffee @@ -82,7 +82,7 @@ module.exports = class JobProfileTreemaView extends CocoView {name: 'Provide your name.', weight: 1, fn: modified 'name'} {name: 'Choose your city.', weight: 1, fn: modified 'city'} {name: 'Pick your country.', weight: 0, fn: exists 'country'} - {name: 'List at least five skills.', weight: 2, fn: -> jobProfile.skills.length >= 5} + {name: 'List at least five skills.', weight: 2, fn: -> jobProfile.skills?.length >= 5} {name: 'Write a short description to summarize yourself at a glance.', weight: 2, fn: modified 'shortDescription'} {name: 'Fill in your main description to sell yourself and describe the work you\'re looking for.', weight: 3, fn: modified 'longDescription'} {name: 'List your work experience.', weight: 3, fn: listStarted 'work', ['role', 'employer']} @@ -95,7 +95,7 @@ module.exports = class JobProfileTreemaView extends CocoView getData: -> return {} unless me.get('jobProfile') or @hasEditedProfile _.pick @jobProfileTreema.data, (value, key) => key in @editableSettings - + destroy: -> @jobProfileTreema?.destroy() super() diff --git a/app/views/editor/component/ThangComponentConfigView.coffee b/app/views/editor/component/ThangComponentConfigView.coffee index 6e6483a24..6b447ab5a 100644 --- a/app/views/editor/component/ThangComponentConfigView.coffee +++ b/app/views/editor/component/ThangComponentConfigView.coffee @@ -30,7 +30,7 @@ module.exports = class ThangComponentConfigView extends CocoView afterRender: -> super() @buildTreema() - + setConfig: (config) -> @handlingChange = true @editThangTreema.set('/', config) @@ -51,7 +51,7 @@ module.exports = class ThangComponentConfigView extends CocoView schema = $.extend true, {}, @component.get('configSchema') schema.default ?= {} _.merge schema.default, @additionalDefaults if @additionalDefaults - + if @level?.get('type') is 'hero' schema.required = [] treemaOptions = @@ -77,6 +77,7 @@ module.exports = class ThangComponentConfigView extends CocoView 'seconds': nodes.SecondsNode 'speed': nodes.SpeedNode 'acceleration': nodes.AccelerationNode + 'thang-type': nodes.ThangTypeNode 'item-thang-type': nodes.ItemThangTypeNode @editThangTreema = @$el.find('.treema').treema treemaOptions @@ -92,7 +93,7 @@ module.exports = class ThangComponentConfigView extends CocoView @trigger 'changed', { component: @component, config: @config } data: -> @editThangTreema.data - + destroy: -> @editThangTreema?.destroy() super() diff --git a/app/views/editor/level/LevelFeedbackView.coffee b/app/views/editor/level/LevelFeedbackView.coffee index 360528d55..fa6a0884a 100644 --- a/app/views/editor/level/LevelFeedbackView.coffee +++ b/app/views/editor/level/LevelFeedbackView.coffee @@ -33,16 +33,16 @@ module.exports = class LevelFeedbackView extends CocoView getRenderData: (context={}) -> context = super(context) + context.moment = moment + context.allFeedback = [] + context.averageRating = 0 + context.totalRatings = 0 if @allFeedback context.allFeedback = (m.attributes for m in @allFeedback.models when @allFeedback.models.length < 20 or m.get('review')) - context.averageRating = _.reduce((m.get('rating') for m in @allFeedback.models), (acc, x) -> acc + (x ? 5)) / (@allFeedback.models.length or 1) + context.averageRating = _.reduce((m.get('rating') for m in @allFeedback.models), (acc, x) -> acc + (x ? 5)) / (@allFeedback.models.length) context.totalRatings = @allFeedback.models.length else - context.allFeedback = [] - context.averageRating = 0 - context.totalRatings = 0 context.loading = true - context.moment = moment context onViewSwitched: (e) -> diff --git a/app/views/editor/level/treema_nodes.coffee b/app/views/editor/level/treema_nodes.coffee index 33db3c129..989adfa22 100644 --- a/app/views/editor/level/treema_nodes.coffee +++ b/app/views/editor/level/treema_nodes.coffee @@ -200,6 +200,9 @@ module.exports.AccelerationNode = class AccelerationNode extends TreemaNode.node module.exports.ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.string valueClass: 'treema-thang-type' + @thangTypes: null + @thangTypesCollection: null + constructor: (args...) -> super args... data = @getData() @@ -223,63 +226,68 @@ module.exports.ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.st else @data = null -module.exports.ItemThangTypeNode = ItemThangTypeNode = class ItemThangTypeNode extends TreemaNode.nodeMap.string - valueClass: 'treema-item-thang-type' - @items: null - @itemsCollection: null +module.exports.ThangTypeNode = ThangTypeNode = class ThangTypeNode extends TreemaNode.nodeMap.string + valueClass: 'treema-thang-type' + @thangTypesCollection: null # Lives in ThangTypeNode parent class + @thangTypes: null # Lives in ThangTypeNode or subclasses constructor: -> super(arguments...) - @getItems() - unless ItemThangTypeNode.itemsCollection.loaded - ItemThangTypeNode.itemsCollection.once('sync', @refreshDisplay, @) - + @getThangTypes() + unless ThangTypeNode.thangTypesCollection.loaded + ThangTypeNode.thangTypesCollection.once('sync', @refreshDisplay, @) + buildValueForDisplay: (valEl, data) -> - @buildValueForDisplaySimply(valEl, @getCurrentItem() or '') + @buildValueForDisplaySimply(valEl, @getCurrentThangType() or '') valEl - + buildValueForEditing: (valEl, data) -> super(valEl, data) - if ItemThangTypeNode.items - source = (item.name for item in ItemThangTypeNode.items when @keyForParent in item.slots) - input = valEl.find('input').autocomplete(source: source, minLength: 0, delay: 0, autoFocus: true) - input.val(@getCurrentItem() or '') + input = valEl.find 'input' + if @constructor.thangTypes + source = (thangType.name for thangType in @constructor.thangTypes when @filterThangType thangType) + input.autocomplete(source: source, minLength: 0, delay: 0, autoFocus: true) + input.val(@getCurrentThangType() or '') valEl - - getCurrentItem: -> - window.itemData = @getData() - window.items = ItemThangTypeNode.items - return null unless ItemThangTypeNode.items - original = @getData() - return null unless original - item = _.find ItemThangTypeNode.items, { original: original } - item?.name or '...' - - getItems: -> - return if ItemThangTypeNode.itemsCollection - ItemThangTypeNode.itemsCollection = new CocoCollection([], { + + filterThangType: (thangType) -> true + + getCurrentThangType: -> + return null unless @constructor.thangTypes + return null unless original = @getData() + thangType = _.find @constructor.thangTypes, { original: original } + thangType?.name or '...' + + getThangTypes: -> + return if ThangTypeNode.thangTypesCollection + ThangTypeNode.thangTypesCollection = new CocoCollection([], { url: '/db/thang.type' project:['name', 'components', 'original'] model: ThangType }) - res = ItemThangTypeNode.itemsCollection.fetch() - ItemThangTypeNode.itemsCollection.once 'sync', => @processItems(ItemThangTypeNode.itemsCollection) - - processItems: (itemCollection) -> - ItemThangTypeNode.items = [] - for itemThang in itemCollection.models - itemComponent = _.find itemThang.get('components'), {original: LevelComponent.ItemID} - continue unless itemComponent - slots = itemComponent.config?.slots - continue unless slots?.length - ItemThangTypeNode.items.push { - name: itemThang.get('name') - original: itemThang.get('original') - slots: slots - } + res = ThangTypeNode.thangTypesCollection.fetch() + ThangTypeNode.thangTypesCollection.once 'sync', => @processThangTypes(ThangTypeNode.thangTypesCollection) + + processThangTypes: (thangTypeCollection) -> + @constructor.thangTypes = [] + @processThangType thangType for thangType in thangTypeCollection.models + + processThangType: (thangType) -> + @constructor.thangTypes.push name: thangType.get('name'), original: thangType.get('original') saveChanges: -> thangTypeName = @$el.find('input').val() - item = _.find(ItemThangTypeNode.items, {name: thangTypeName}) - return @remove() unless item - @data = item.original \ No newline at end of file + thangType = _.find(@constructor.thangTypes, {name: thangTypeName}) + return @remove() unless thangType + @data = thangType.original + +module.exports.ItemThangTypeNode = ItemThangTypeNode = class ItemThangTypeNode extends ThangTypeNode + valueClass: 'treema-item-thang-type' + + filterThangType: (thangType) -> + @keyForParent in thangType.slots + + processThangType: (thangType) -> + return unless itemComponent = _.find thangType.get('components'), {original: LevelComponent.ItemID} + return unless itemComponent.config?.slots?.length + @constructor.thangTypes.push name: thangType.get('name'), original: thangType.get('original'), slots: itemComponent.config.slots From 195e027b538f3185571d33927c9ad57c9b9ae6f3 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 14:27:47 -0700 Subject: [PATCH 82/98] Fix for null/undefined objects in the diffs. --- app/lib/deltas.coffee | 4 +--- app/views/modal/SaveVersionModal.coffee | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/lib/deltas.coffee b/app/lib/deltas.coffee index 55743f345..00d941a1e 100644 --- a/app/lib/deltas.coffee +++ b/app/lib/deltas.coffee @@ -89,7 +89,7 @@ expandFlattenedDelta = (delta, left, schema) -> delta module.exports.makeJSONDiffer = -> - hasher = (obj) -> obj.name || obj.id || obj._id || JSON.stringify(_.keys(obj)) + hasher = (obj) -> if obj? then obj.name or obj.id or obj._id or JSON.stringify(_.keys(obj)) else 'null' jsondiffpatch.create({objectHash: hasher}) module.exports.getConflicts = (headDeltas, pendingDeltas) -> @@ -175,5 +175,3 @@ prunePath = (delta, path) -> prunePath delta[path[0]], path.slice(1) unless delta[path[0]] is undefined keys = (k for k in _.keys(delta[path[0]]) when k isnt '_t') delete delta[path[0]] if keys.length is 0 - - diff --git a/app/views/modal/SaveVersionModal.coffee b/app/views/modal/SaveVersionModal.coffee index 77c5556a7..8a8c50c9f 100644 --- a/app/views/modal/SaveVersionModal.coffee +++ b/app/views/modal/SaveVersionModal.coffee @@ -37,7 +37,7 @@ module.exports = class SaveVersionModal extends ModalView deltaView = new DeltaView({model: @model}) @insertSubView(deltaView, changeEl) catch e - console.error 'Couldn\'t create delta view:', e + console.error 'Couldn\'t create delta view:', e, e.stack @$el.find('.commit-message input').attr('placeholder', $.i18n.t('general.commit_msg')) onSubmitForm: (e) -> From a28269c5312700e8298fb79da43c10ac9208325b Mon Sep 17 00:00:00 2001 From: Imperadeiro98 Date: Mon, 1 Sep 2014 23:10:14 +0100 Subject: [PATCH 83/98] Update en.coffee I don't know if is necessary to change something in codecombat / app / templates / play.jade --- app/locale/en.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/locale/en.coffee b/app/locale/en.coffee index f18a579fa..0be68564c 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -123,6 +123,8 @@ campaign_multiplayer_description: "... in which you code head-to-head against other players." campaign_player_created: "Player-Created" campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." + campaign_classic_algorithms: "Classic Algorithms" + campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Difficulty: " play_as: "Play As" spectate: "Spectate" From 72eb59b7c3f5a3f8bc41967fcdf7275aedfa40ec Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 16:14:50 -0700 Subject: [PATCH 84/98] Fixed a few bugs. --- app/lib/surface/CocoSprite.coffee | 3 ++- app/lib/surface/Surface.coffee | 13 ++++++++----- app/lib/surface/WizardSprite.coffee | 1 + app/schemas/models/thang_type.coffee | 4 ++-- app/views/editor/level/thangs/ThangsTabView.coffee | 1 - 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index 478a71b0c..d0a192515 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -136,7 +136,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass width = @thang.width * Camera.PPM height = @thang.height * Camera.PPM * @options.camera.y2x depth = @thang.depth * Camera.PPM * @options.camera.z2y * @options.camera.y2x - makeColor = (brightnessFactor) => (Math.round(c * brightnessFactor) for c in (healthColors[@thang.team] ? [180, 180, 180])) + brightnessFuzzFactor = 1 + 0.1 * (Math.random() - 0.5) + makeColor = (brightnessFactor) => (Math.round(c * brightnessFuzzFactor * brightnessFactor) for c in (healthColors[@thang.team] ? [180, 180, 180])) topColor = "rgba(#{makeColor(0.85).join(', ')},1)" mainColor = "rgba(#{makeColor(0.75).join(', ')},1)" ellipse = @thang.shape in ['ellipsoid', 'disc'] diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index f15556120..a3762e125 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -428,15 +428,18 @@ module.exports = Surface = class Surface extends CocoClass pageHeight = $('#page-container').height() - $('#control-bar-view').outerHeight() - $('#playback-view').outerHeight() newWidth = Math.min pageWidth, pageHeight * aspectRatio newHeight = newWidth / aspectRatio + else if $('#editor-level-thangs-tab-view') + newWidth = $('#canvas-wrapper').width() + newHeight = newWidth / aspectRatio else newWidth = 0.55 * pageWidth newHeight = newWidth / aspectRatio - @canvas.width newWidth - @canvas.height newHeight return unless newWidth > 0 and newHeight > 0 - #if InstallTrigger? # Firefox rendering performance goes down as canvas size goes up - # newWidth = Math.min 924, newWidth - # newHeight = Math.min 589, newHeight + ##if InstallTrigger? # Firefox rendering performance goes down as canvas size goes up + ## newWidth = Math.min 924, newWidth + ## newHeight = Math.min 589, newHeight + #@canvas.width newWidth + #@canvas.height newHeight @canvas.attr width: newWidth, height: newHeight @stage.scaleX *= newWidth / oldWidth @stage.scaleY *= newHeight / oldHeight diff --git a/app/lib/surface/WizardSprite.coffee b/app/lib/surface/WizardSprite.coffee index d5bdfede2..21b3b453c 100644 --- a/app/lib/surface/WizardSprite.coffee +++ b/app/lib/surface/WizardSprite.coffee @@ -199,6 +199,7 @@ module.exports = class WizardSprite extends IndieSprite wizard.beginMoveTween() endMoveTween: => + return if @destroyed @thang.action = if @editing then 'cast' else 'idle' @thang.actionActivated = @thang.action is 'cast' @reachedTarget = true diff --git a/app/schemas/models/thang_type.coffee b/app/schemas/models/thang_type.coffee index 5b089d5cf..9ac0319c3 100644 --- a/app/schemas/models/thang_type.coffee +++ b/app/schemas/models/thang_type.coffee @@ -1,7 +1,7 @@ c = require './../schemas' ThangComponentSchema = require './thang_component' -ThangTypeSchema = c.object() +ThangTypeSchema = c.object default: {kind: 'Misc'} c.extendNamedProperties ThangTypeSchema # name first ShapeObjectSchema = c.object {title: 'Shape'}, @@ -144,7 +144,7 @@ _.extend ThangTypeSchema.properties, ThangTypeSchema.required = ['kind'] -ThangTypeSchema.default = +ThangTypeSchema.default = raw: {} ThangTypeSchema.definitions = diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index e54624ebb..282820873 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -318,7 +318,6 @@ module.exports = class ThangsTabView extends CocoView physicalConfig = {pos: {x: 10, y: 10, z: 1}} if physicalOriginal = _.find(defaultComponents ? [], original: componentOriginals['physics.Physical']) physicalConfig.pos.z = physicalOriginal.config?.pos?.z ? 1 # Get the z right - console.log physicalOriginal, defaultComponents, componentOriginals['physics.Physical'], physicalConfig [ {original: componentOriginals['existence.Exists'], majorVersion: 0, config: {}} {original: componentOriginals['physics.Physical'], majorVersion: 0, config: physicalConfig} From 6085b70dfc943c3deb2880170859c4963622d521 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 16:22:04 -0700 Subject: [PATCH 85/98] Fixed a bounds offset issue in the Thang Editor colors tab. --- app/views/editor/thang/ThangTypeColorsTabView.coffee | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/editor/thang/ThangTypeColorsTabView.coffee b/app/views/editor/thang/ThangTypeColorsTabView.coffee index 47c3f2594..69651b1ae 100644 --- a/app/views/editor/thang/ThangTypeColorsTabView.coffee +++ b/app/views/editor/thang/ThangTypeColorsTabView.coffee @@ -67,11 +67,12 @@ module.exports = class ThangTypeColorsTabView extends CocoView @spriteBuilder.setOptions options @spriteBuilder.buildColorMaps() @movieClip = @spriteBuilder.buildMovieClip animation - larger = Math.min(400 / @movieClip.nominalBounds.width, 400 / @movieClip.nominalBounds.height) + bounds = @movieClip.frameBounds?[0] ? @movieClip.nominalBounds + larger = Math.min(400 / bounds.width, 400 / bounds.height) @movieClip.scaleX = larger @movieClip.scaleY = larger - @movieClip.regX = @movieClip.nominalBounds.x - @movieClip.regY = @movieClip.nominalBounds.y + @movieClip.regX = bounds.x + @movieClip.regY = bounds.y @stage.addChild @movieClip updateContainer: -> From b7d9e7a0956fe9b32e076a312adaf61c133d411b Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 20:07:50 -0700 Subject: [PATCH 86/98] Fixed some click/add behavior in level editor. Fixed level editor canvas resizing. Fixed some dungeon terrain generation issues. Certain Thangs can be placed on top of other Thangs. --- app/lib/LevelLoader.coffee | 1 + .../level/modals/TerrainRandomizeModal.coffee | 54 ++++++++++--------- .../editor/level/thangs/ThangsTabView.coffee | 24 +++++---- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index 620912307..bef0af952 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -167,6 +167,7 @@ module.exports = class LevelLoader extends CocoClass @loadThangsRequiredFromComponentList thangType.get('components') loadThangsRequiredFromComponentList: (components) -> + return unless components requiredThangTypes = [] for component in components when component.config if component.original is LevelComponent.EquipsID diff --git a/app/views/editor/level/modals/TerrainRandomizeModal.coffee b/app/views/editor/level/modals/TerrainRandomizeModal.coffee index fa6da998a..607ed92c6 100644 --- a/app/views/editor/level/modals/TerrainRandomizeModal.coffee +++ b/app/views/editor/level/modals/TerrainRandomizeModal.coffee @@ -9,11 +9,11 @@ clusters = { } 'trees': { 'thangs': ['Tree 1', 'Tree 2', 'Tree 3', 'Tree 4'] - 'margin': 0 + 'margin': 0.5 } 'shrubs': { 'thangs': ['Shrub 1', 'Shrub 2', 'Shrub 3'] - 'margin': 0 + 'margin': 0.5 } 'houses': { 'thangs': ['House 1', 'House 2', 'House 3', 'House 4'] @@ -280,7 +280,7 @@ module.exports = class TerrainRandomizeModal extends ModalView 'id': @getRandomThang(clusters['torch'].thangs) 'pos': { 'x': i + preset.borderSize - 'y': presetSize.y - preset.borderSize + 'y': presetSize.y - preset.borderSize / 2 } 'margin': clusters['torch'].margin } @@ -289,7 +289,7 @@ module.exports = class TerrainRandomizeModal extends ModalView 'id': @getRandomThang(clusters['chains'].thangs) 'pos': { 'x': i + preset.borderSize - 'y': presetSize.y - preset.borderSize + 'y': presetSize.y - preset.borderSize / 2 } 'margin': clusters['chains'].margin } @@ -346,53 +346,57 @@ module.exports = class TerrainRandomizeModal extends ModalView continue buildRoom: (preset, presetSize, room) -> + grid = preset.borderSize while true rect = { - 'width':presetSize.sizeFactor * (room.width[0] + preset.borderSize * _.random(0, (room.width[1] - room.width[0])/preset.borderSize)) - 'height':presetSize.sizeFactor * (room.height[0] + preset.borderSize * _.random(0, (room.height[1] - room.height[0])/preset.borderSize)) + 'width':presetSize.sizeFactor * (room.width[0] + grid * _.random(0, (room.width[1] - room.width[0])/grid)) + 'height':presetSize.sizeFactor * (room.height[0] + grid * _.random(0, (room.height[1] - room.height[0])/grid)) } + # This logic isn't quite right--it makes the rooms bigger than intended--but it's snapping correctly, which is fine for now. + rect.width = Math.round((rect.width - grid) / (2 * grid)) * 2 * grid + grid + rect.height = Math.round((rect.height - grid) / (2 * grid)) * 2 * grid + grid roomThickness = _.random(room.thickness[0], room.thickness[1]) - rect.x = _.random(rect.width/2 + preset.borderSize * (roomThickness+1.5), presetSize.x - rect.width/2 - preset.borderSize * (roomThickness+1.5)) - rect.y = _.random(rect.height/2 + preset.borderSize * (roomThickness+2.5), presetSize.y - rect.height/2 - preset.borderSize * (roomThickness+3.5)) + rect.x = _.random(rect.width/2 + grid * (roomThickness+1.5), presetSize.x - rect.width/2 - grid * (roomThickness+1.5)) + rect.y = _.random(rect.height/2 + grid * (roomThickness+2.5), presetSize.y - rect.height/2 - grid * (roomThickness+3.5)) # Snap room walls to the wall grid. - rect.x = Math.round((rect.x - preset.borderSize / 2) / preset.borderSize) * preset.borderSize + preset.borderSize / 2 - rect.y = Math.round((rect.y - preset.borderSize / 2) / preset.borderSize) * preset.borderSize + preset.borderSize / 2 + rect.x = Math.round((rect.x - grid / 2) / grid) * grid + rect.y = Math.round((rect.y - grid / 2) / grid) * grid break if @addRect { 'x': rect.x 'y': rect.y - 'width': rect.width + 2.5 * roomThickness * preset.borderSize - 'height': rect.height + 2.5 * roomThickness * preset.borderSize + 'width': rect.width + 2.5 * roomThickness * grid + 'height': rect.height + 2.5 * roomThickness * grid } - xRange = _.range(rect.x - rect.width/2 + preset.borderSize, rect.x + rect.width/2, preset.borderSize) + xRange = _.range(rect.x - rect.width/2 + grid, rect.x + rect.width/2, grid) topDoor = _.random(1) > 0.5 topDoorX = xRange[_.random(0, xRange.length-1)] bottomDoor = if not topDoor then true else _.random(1) > 0.5 bottomDoorX = xRange[_.random(0, xRange.length-1)] for t in _.range(0, roomThickness+1) - for i in _.range(rect.x - rect.width/2 - (t-1) * preset.borderSize, rect.x + rect.width/2 + t * preset.borderSize, preset.borderSize) + for i in _.range(rect.x - rect.width/2 - (t-1) * grid, rect.x + rect.width/2 + t * grid, grid) # Bottom wall thang = { 'id': @getRandomThang(clusters[room.cluster].thangs) 'pos': { 'x': i - 'y': rect.y - rect.height/2 - t * preset.borderSize + 'y': rect.y - rect.height/2 - t * grid } 'margin': clusters[room.cluster].margin } if i is bottomDoorX and bottomDoor thang.id = @getRandomThang(clusters['doors'].thangs) - thang.pos.y -= preset.borderSize/3 + thang.pos.y -= grid/3 @addThang thang unless i is bottomDoorX and t isnt roomThickness and bottomDoor - if t is roomThickness and i isnt rect.x - rect.width/2 - (t-1) * preset.borderSize and preset.type is 'dungeon' - if ( i isnt bottomDoorX and i isnt bottomDoorX + preset.borderSize ) or not bottomDoor + if t is roomThickness and i isnt rect.x - rect.width/2 - (t-1) * grid and preset.type is 'dungeon' + if ( i isnt bottomDoorX and i isnt bottomDoorX + grid ) or not bottomDoor @addThang { 'id': @getRandomThang(clusters['torch'].thangs) 'pos': { - 'x': thang.pos.x - preset.borderSize / 2 - 'y': thang.pos.y + preset.borderSize / 2 + 'x': thang.pos.x - grid / 2 + 'y': thang.pos.y + grid } 'margin': clusters['torch'].margin } @@ -402,22 +406,22 @@ module.exports = class TerrainRandomizeModal extends ModalView 'id': @getRandomThang(clusters[room.cluster].thangs) 'pos': { 'x': i - 'y': rect.y + rect.height/2 + t * preset.borderSize + 'y': rect.y + rect.height/2 + t * grid } 'margin': clusters[room.cluster].margin } if i is topDoorX and topDoor thang.id = @getRandomThang(clusters['doors'].thangs) - thang.pos.y -= preset.borderSize + thang.pos.y -= grid @addThang thang unless i is topDoorX and t isnt roomThickness and topDoor for t in _.range(0, roomThickness) - for i in _.range(rect.y - rect.height/2 - t * preset.borderSize, rect.y + rect.height/2 + (t+1) * preset.borderSize, preset.borderSize) + for i in _.range(rect.y - rect.height/2 - t * grid, rect.y + rect.height/2 + (t+1) * grid, grid) # Left wall @addThang { 'id': @getRandomThang(clusters[room.cluster].thangs) 'pos': { - 'x': rect.x - rect.width/2 - t * preset.borderSize + 'x': rect.x - rect.width/2 - t * grid 'y': i } 'margin': clusters[room.cluster].margin @@ -427,7 +431,7 @@ module.exports = class TerrainRandomizeModal extends ModalView @addThang { 'id': @getRandomThang(clusters[room.cluster].thangs) 'pos': { - 'x': rect.x + rect.width/2 + t * preset.borderSize + 'x': rect.x + rect.width/2 + t * grid 'y': i } 'margin': clusters[room.cluster].margin diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 282820873..35a4b00ac 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -20,6 +20,9 @@ componentOriginals = 'existence.Exists': '524b4150ff92f1f4f8000024' 'physics.Physical': '524b75ad7fc0f6d519000001' +# Let us place these on top of other Thangs +overlappableThangTypeNames = ['Torch', 'Chains', 'Bird', 'Cloud 1', 'Cloud 2', 'Cloud 3', 'Waterfall', 'Obstacle'] + class ThangTypeSearchCollection extends CocoCollection url: '/db/thang.type?project=original,name,version,slug,kind,components' model: ThangType @@ -190,7 +193,7 @@ module.exports = class ThangsTabView extends CocoView super() onViewSwitched: (e) -> - @selectAddThang() + @selectAddThang null, true @surface?.spriteBoss?.selectSprite null, null onSpriteMouseDown: (e) -> @@ -255,9 +258,12 @@ module.exports = class ThangsTabView extends CocoView if e.thang and (key.alt or key.meta) # We alt-clicked, so create a clone addThang @selectAddThangType e.thang.spriteName, @selectedExtantThang - else if e.thang and not (@addThangSprite and @addThangType is 'Blood Torch Test') # TODO: figure out which Thangs can be placed on other Thangs + else if @justAdded() + console.log 'Skipping double insert due to extra selection event, since we just added', (new Date() - @lastAddTime), 'ms ago.' + null + else if e.thang and not (@addThangSprite and @addThangType.get('name') in overlappableThangTypeNames) # We clicked on a Thang (or its Treema), so select the Thang - @selectAddThang null + @selectAddThang null, true @selectedExtantThangClickTime = new Date() treemaThang = _.find @thangsTreema.childrenTreemas, (treema) => treema.data.id is @selectedExtantThang.id if treemaThang @@ -270,16 +276,13 @@ module.exports = class ThangsTabView extends CocoView else if @addThangSprite # We clicked on the background when we had an add Thang selected, so add it @addThang @addThangType, @addThangSprite.thang.pos + @lastAddTime = new Date() - # Commented out this bit so the extant thangs treema editor can select invisible thangs like arrows. - # Couldn't spot any bugs... But if there are any, better come up with a better solution. -# else -# # We clicked on the background, so deselect anything selected -# @thangsTreema.deselectAll() + justAdded: -> @lastAddTime and (new Date() - @lastAddTime) < 150 - selectAddThang: (e) => + selectAddThang: (e, forceDeselect=false) => return if e? and $(e.target).closest('#thang-search').length # Ignore if you're trying to search thangs - return unless e? and $(e.target).closest('#editor-level-thangs-tab-view').length or key.isPressed('esc') + return unless (e? and $(e.target).closest('#editor-level-thangs-tab-view').length) or key.isPressed('esc') or forceDeselect if e then target = $(e.target) else target = @$el.find('.add-thangs-palette') # pretend to click on background if no event return true if target.attr('id') is 'surface' target = target.closest('.add-thang-palette-icon') @@ -428,7 +431,6 @@ module.exports = class ThangsTabView extends CocoView thangID = Thang.nextID(thangType.get('name'), @world) until thangID and not @thangsTreema.get "id=#{thangID}" if @cloneSourceThang components = _.cloneDeep @thangsTreema.get "id=#{@cloneSourceThang.id}/components" - @selectAddThang null else if @level.get('type') is 'hero' components = [] # Load them all from default ThangType Components else From 9094c0ce1932b8a525009182ff3f574ee6f10ce0 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 20:22:46 -0700 Subject: [PATCH 87/98] Add hero placeholder to terrain generator on hero levels. --- .../level/modals/TerrainRandomizeModal.coffee | 28 +++++++++++++++++++ .../editor/level/thangs/ThangsTabView.coffee | 8 ++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/app/views/editor/level/modals/TerrainRandomizeModal.coffee b/app/views/editor/level/modals/TerrainRandomizeModal.coffee index 607ed92c6..31958e8d0 100644 --- a/app/views/editor/level/modals/TerrainRandomizeModal.coffee +++ b/app/views/editor/level/modals/TerrainRandomizeModal.coffee @@ -3,6 +3,10 @@ template = require 'templates/editor/level/modal/terrain_randomize' CocoModel = require 'models/CocoModel' clusters = { + 'hero': { + 'thangs': ['Hero Placeholder'] + 'margin': 1 + } 'rocks': { 'thangs': ['Rock 1', 'Rock 2', 'Rock 3', 'Rock 4', 'Rock 5', 'Rock Cluster 1', 'Rock Cluster 2', 'Rock Cluster 3'] 'margin': 1 @@ -97,6 +101,14 @@ presets = { 'thickness': [2,2] 'cluster': 'dungeon_wall' } + 'hero': { + 'num': [1, 1] + 'width': 2 + 'height': 2 + 'clusters': { + 'hero': [1, 1] + } + } 'Barrels': { 'num': [1,1] 'width': [8, 12] @@ -130,6 +142,14 @@ presets = { 'thickness': [2,2] 'cluster': 'indoor_wall' } + 'hero': { + 'num': [1, 1] + 'width': 2 + 'height': 2 + 'clusters': { + 'hero': [1, 1] + } + } 'furniture': { 'num':[1,2] 'width': 15 @@ -148,6 +168,14 @@ presets = { 'borderThickness':3 'floors':'grass_floor' 'decorations': { + 'hero': { + 'num': [1, 1] + 'width': 2 + 'height': 2 + 'clusters': { + 'hero': [1, 1] + } + } 'house': { 'num':[1,2] #min-max 'width': 15 diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 35a4b00ac..8a1e07508 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -259,7 +259,7 @@ module.exports = class ThangsTabView extends CocoView # We alt-clicked, so create a clone addThang @selectAddThangType e.thang.spriteName, @selectedExtantThang else if @justAdded() - console.log 'Skipping double insert due to extra selection event, since we just added', (new Date() - @lastAddTime), 'ms ago.' + # Skip double insert due to extra selection event null else if e.thang and not (@addThangSprite and @addThangType.get('name') in overlappableThangTypeNames) # We clicked on a Thang (or its Treema), so select the Thang @@ -426,7 +426,11 @@ module.exports = class ThangsTabView extends CocoView addThang: (thangType, pos, batchInsert=false) -> @$el.find('#randomize-button').hide() if batchInsert - thangID = "Random #{thangType.get('name')} #{@thangsBatch.length}" + if thangType.get('name') is 'Hero Placeholder' + thangID = 'Hero Placeholder' + return if @level.get('type') isnt 'hero' or @thangsTreema.get "id=#{thangID}" + else + thangID = "Random #{thangType.get('name')} #{@thangsBatch.length}" else thangID = Thang.nextID(thangType.get('name'), @world) until thangID and not @thangsTreema.get "id=#{thangID}" if @cloneSourceThang From 632559f7dbd904177071361827559a0546e1b1ca Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 20:53:53 -0700 Subject: [PATCH 88/98] Refactored LevelLoader to load sessions after levels, so that we can do the right thing with hero levels' sessions. Made levels default to type: 'hero'. --- app/lib/LevelLoader.coffee | 63 +++++++++++++++------------- app/models/Level.coffee | 2 +- app/schemas/models/level.coffee | 1 + test/app/lib/LevelLoader.spec.coffee | 24 ++++++----- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index bef0af952..ff7135f2d 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -35,7 +35,6 @@ module.exports = class LevelLoader extends CocoClass @worldNecessities = [] @listenTo @supermodel, 'resource-loaded', @onWorldNecessityLoaded - @loadSession() @loadLevel() @loadAudio() @playJingle() @@ -44,14 +43,19 @@ module.exports = class LevelLoader extends CocoClass else @listenToOnce @supermodel, 'loaded-all', @onSupermodelLoaded - playJingle: -> - return if @headless - # Apparently the jingle, when it tries to play immediately during all this loading, you can't hear it. - # Add the timeout to fix this weird behavior. - f = -> - jingles = ['ident_1', 'ident_2'] - AudioPlayer.playInterfaceSound jingles[Math.floor Math.random() * jingles.length] - setTimeout f, 500 + # Supermodel (Level) Loading + + loadLevel: -> + @level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID + if @level.loaded + @onLevelLoaded() + else + @level = @supermodel.loadModel(@level, 'level').model + @listenToOnce @level, 'sync', @onLevelLoaded + + onLevelLoaded: -> + @loadSession() + @populateLevel() # Session Loading @@ -83,29 +87,19 @@ module.exports = class LevelLoader extends CocoClass @listenToOnce @opponentSession, 'sync', @loadDependenciesForSession loadDependenciesForSession: (session) -> - return if @levelID is 'sky-span' # TODO - # TODO: I think this runs afoul of https://github.com/codecombat/codecombat/issues/1108 - # TODO: this shouldn't happen when it's not a hero level, but we don't have level loaded yet, - # and the sessions are being created with default hero config regardless of whether it's a hero level. - if heroConfig = session.get('heroConfig') - url = "/db/thang.type/#{heroConfig.thangType}/version?project=name,components,original" + return unless @level.get('type', true) is 'hero' + heroConfig = session.get('heroConfig') + unless heroConfig + heroConfig = {inventory: {}, thangType: '529ffbf1cf1818f2be000001'} # Temp: assign Tharin as the hero + session.set 'heroConfig', heroConfig + url = "/db/thang.type/#{heroConfig.thangType}/version?project=name,components,original" + @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') + + for itemThangType in _.values(heroConfig.inventory) + url = "/db/thang.type/#{itemThangType}/version?project=name,components,original" @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') - for itemThangType in _.values(heroConfig.inventory) - url = "/db/thang.type/#{itemThangType}/version?project=name,components,original" - @worldNecessities.push @maybeLoadURL(url, ThangType, 'thang') - # Supermodel (Level) Loading - - loadLevel: -> - @level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID - if @level.loaded - @populateLevel() - else - @level = @supermodel.loadModel(@level, 'level').model - @listenToOnce @level, 'sync', @onLevelLoaded - - onLevelLoaded: -> - @populateLevel() + # Grabbing the rest of the required data for the level populateLevel: -> thangIDs = [] @@ -332,6 +326,15 @@ module.exports = class LevelLoader extends CocoClass # Initial Sound Loading + playJingle: -> + return if @headless + # Apparently the jingle, when it tries to play immediately during all this loading, you can't hear it. + # Add the timeout to fix this weird behavior. + f = -> + jingles = ['ident_1', 'ident_2'] + AudioPlayer.playInterfaceSound jingles[Math.floor Math.random() * jingles.length] + setTimeout f, 500 + loadAudio: -> return if @headless AudioPlayer.preloadInterfaceSounds ['victory'] diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 6c61ee214..be7364bac 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -30,7 +30,7 @@ module.exports = class Level extends CocoModel denormalize: (supermodel, session) -> o = $.extend true, {}, @attributes - if @get('type') is 'hero' + if @get('type', true) is 'hero' # TOOD: figure out if/when/how we are doing this for non-Hero levels that aren't expecting denormalization. for levelThang in o.thangs @denormalizeThang(levelThang, supermodel, session) diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index e5cf9db10..139d3a9f6 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -212,6 +212,7 @@ LevelSchema = c.object { thangs: [] systems: [] victory: {} + type: 'hero' } c.extendNamedProperties LevelSchema # let's have the name be the first property _.extend LevelSchema.properties, diff --git a/test/app/lib/LevelLoader.spec.coffee b/test/app/lib/LevelLoader.spec.coffee index 2181e67f6..a9f69bb18 100644 --- a/test/app/lib/LevelLoader.spec.coffee +++ b/test/app/lib/LevelLoader.spec.coffee @@ -112,12 +112,14 @@ describe 'LevelLoader', -> requests = jasmine.Ajax.requests.all() urls = (r.url for r in requests) expect('/db/level.component/jumps/version/0' in urls).toBeTruthy() - + it 'is idempotent', -> levelLoader = new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) # first load Tharin by the 'normal' session load - responses = { '/db/level_session/id': sessionWithTharinWithHelmet } + responses = '/db/level/id': levelWithOgreWithMace + jasmine.Ajax.requests.sendResponses(responses) + responses = '/db/level_session/id': sessionWithTharinWithHelmet jasmine.Ajax.requests.sendResponses(responses) numRequestsBefore = jasmine.Ajax.requests.count() @@ -126,21 +128,21 @@ describe 'LevelLoader', -> levelLoader.loadDependenciesForSession(session) levelLoader.loadDependenciesForSession(session) levelLoader.loadDependenciesForSession(session) - numRequestsAfter = jasmine.Ajax.requests.count() - expect(numRequestsBefore).toBe(numRequestsAfter) - + numRequestsAfter = jasmine.Ajax.requests.count() + expect(numRequestsAfter).toBe(numRequestsBefore) + it 'loads thangs for items that the level thangs have in their Equips component configs', -> new LevelLoader({supermodel:supermodel = new SuperModel(), sessionID: 'id', levelID: 'id'}) - - responses = { - '/db/level/id': levelWithOgreWithMace + + responses = { + '/db/level/id': levelWithOgreWithMace } jasmine.Ajax.requests.sendResponses(responses) requests = jasmine.Ajax.requests.all() urls = (r.url for r in requests) expect('/db/thang.type/mace/version?project=name,components,original' in urls).toBeTruthy() - + it 'loads components which are inherited by level thangs from thang type default components', -> new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) @@ -152,7 +154,7 @@ describe 'LevelLoader', -> requests = jasmine.Ajax.requests.all() urls = (r.url for r in requests) expect('/db/level.component/physical/version/0' in urls).toBeTruthy() - + it 'loads item thang types which are inherited by level thangs from thang type default equips component configs', -> new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) @@ -164,7 +166,7 @@ describe 'LevelLoader', -> requests = jasmine.Ajax.requests.all() urls = (r.url for r in requests) expect('/db/thang.type/wand/version?project=name,components,original' in urls).toBeTruthy() - + it 'loads components for item thang types which are inherited by level thangs from thang type default equips component configs', -> new LevelLoader({supermodel:new SuperModel(), sessionID: 'id', levelID: 'id'}) From e615dde6e245d5e8abf287d3d87fe1165a47a1e7 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 21:10:45 -0700 Subject: [PATCH 89/98] Show inventory view now, for hero levels. --- app/templates/game-menu/game-menu-modal.jade | 7 ++----- app/views/game-menu/GameMenuModal.coffee | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/templates/game-menu/game-menu-modal.jade b/app/templates/game-menu/game-menu-modal.jade index a8d8fb6ed..0876d23d5 100644 --- a/app/templates/game-menu/game-menu-modal.jade +++ b/app/templates/game-menu/game-menu-modal.jade @@ -8,8 +8,9 @@ block modal-body-content - if (!showDevBits) { // Not done yet. - submenus.splice(4, 1); - submenus.splice(2, 1); - - submenus.splice(0, 1); - } + - if (!showInventory) + - submenus.splice(0, 1); ul.nav.nav-tabs for submenu, index in submenus li(class=index ? "" : "active") @@ -19,10 +20,6 @@ block modal-body-content .tab-content for submenu, index in submenus .tab-pane(id=submenu + '-view') - h3= submenu + submenu + submenu - p - | Lorem ipsum dolor sit amet, charetra varius quam sit amet vulputate. - | Quisque mauris augue, molestie tincidunt condimentum vitae, gravida a libero. .clearfix block modal-footer \ No newline at end of file diff --git a/app/views/game-menu/GameMenuModal.coffee b/app/views/game-menu/GameMenuModal.coffee index c23e7881c..b2d1aca45 100644 --- a/app/views/game-menu/GameMenuModal.coffee +++ b/app/views/game-menu/GameMenuModal.coffee @@ -18,6 +18,7 @@ module.exports = class GameMenuModal extends ModalView constructor: (options) -> super options @options.showDevBits = me.isAdmin() or /https?:\/\/localhost/.test(window.location.href) + @options.showInventory = @options.level.get('type', true) is 'hero' events: 'change input.select': 'onSelectionChanged' @@ -25,13 +26,14 @@ module.exports = class GameMenuModal extends ModalView getRenderData: (context={}) -> context = super(context) context.showDevBits = @options.showDevBits + context.showInventory = @options.showInventory context afterRender: -> super() @$el.toggleClas @insertSubView new submenuView @options for submenuView in submenuViews - (if @options.showDevBits then @subviews.inventory_view else @subviews.choose_hero_view).$el.addClass 'active' + (if @options.showInventory then @subviews.inventory_view else @subviews.choose_hero_view).$el.addClass 'active' onHidden: -> subview.onHidden?() for subviewKey, subview of @subviews From af42bafb150ed004894a28837a14c7025e9523d8 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 21:58:22 -0700 Subject: [PATCH 90/98] Added default goals. Don't show System versions in the Systems list if they're 0. --- app/schemas/models/level.coffee | 4 ++++ app/views/editor/level/systems/SystemsTabView.coffee | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index 139d3a9f6..92b5036c2 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -213,6 +213,10 @@ LevelSchema = c.object { systems: [] victory: {} type: 'hero' + goals: [ + {id: 'ogres-die', name: 'Ogres must die.', killThangs: ['ogres'], worldEndsAfter: 3} + {id: 'humans-survive', name: 'Humans must survive.', saveThangs: ['humans'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true} + ] } c.extendNamedProperties LevelSchema # let's have the name be the first property _.extend LevelSchema.properties, diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index 049d41bbf..eff550260 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -121,7 +121,7 @@ module.exports = class SystemsTabView extends CocoView {original: '528114e60268d018e300001a', majorVersion: 0} # UI {original: '528114040268d018e3000011', majorVersion: 0} # Physics ] - + destroy: -> @systemsTreema?.destroy() super() @@ -146,8 +146,9 @@ class LevelSystemNode extends TreemaObjectNode buildValueForDisplay: (valEl, data) -> return super valEl unless data.original and @system - name = "#{@system.get('name')} v#{@system.get('version').major}" - @buildValueForDisplaySimply valEl, "#{name}" + name = @system.get 'name' + name += " v#{@system.get('version').major}" if @system.get('version').major + @buildValueForDisplaySimply valEl, name onEnterPressed: (e) -> super e From 7af2690c477e694968de5ed4dffe5a10ea3d9086 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 1 Sep 2014 22:20:48 -0700 Subject: [PATCH 91/98] Propagated i18h. --- app/locale/ar.coffee | 3 +++ app/locale/bg.coffee | 3 +++ app/locale/ca.coffee | 3 +++ app/locale/cs.coffee | 3 +++ app/locale/da.coffee | 3 +++ app/locale/de-AT.coffee | 3 +++ app/locale/de-CH.coffee | 3 +++ app/locale/de-DE.coffee | 3 +++ app/locale/el.coffee | 3 +++ app/locale/en-AU.coffee | 3 +++ app/locale/en-GB.coffee | 3 +++ app/locale/en-US.coffee | 3 +++ app/locale/es-419.coffee | 3 +++ app/locale/es-ES.coffee | 3 +++ app/locale/fa.coffee | 3 +++ app/locale/fi.coffee | 3 +++ app/locale/fr.coffee | 3 +++ app/locale/he.coffee | 3 +++ app/locale/hi.coffee | 3 +++ app/locale/hu.coffee | 3 +++ app/locale/id.coffee | 3 +++ app/locale/it.coffee | 3 +++ app/locale/ja.coffee | 3 +++ app/locale/ko.coffee | 3 +++ app/locale/lt.coffee | 3 +++ app/locale/ms.coffee | 3 +++ app/locale/nb.coffee | 3 +++ app/locale/nl-BE.coffee | 3 +++ app/locale/nl-NL.coffee | 3 +++ app/locale/nn.coffee | 3 +++ app/locale/no.coffee | 3 +++ app/locale/pl.coffee | 3 +++ app/locale/pt-BR.coffee | 3 +++ app/locale/pt-PT.coffee | 3 +++ app/locale/ro.coffee | 3 +++ app/locale/ru.coffee | 3 +++ app/locale/sk.coffee | 3 +++ app/locale/sl.coffee | 3 +++ app/locale/sr.coffee | 3 +++ app/locale/sv.coffee | 3 +++ app/locale/th.coffee | 3 +++ app/locale/tr.coffee | 3 +++ app/locale/uk.coffee | 3 +++ app/locale/ur.coffee | 3 +++ app/locale/vi.coffee | 3 +++ app/locale/zh-HANS.coffee | 3 +++ app/locale/zh-HANT.coffee | 3 +++ app/locale/zh-WUU-HANS.coffee | 3 +++ app/locale/zh-WUU-HANT.coffee | 3 +++ 49 files changed, 147 insertions(+) diff --git a/app/locale/ar.coffee b/app/locale/ar.coffee index 95ea4b96e..f4539e605 100644 --- a/app/locale/ar.coffee +++ b/app/locale/ar.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/bg.coffee b/app/locale/bg.coffee index f8c6d22bd..ab12284b9 100644 --- a/app/locale/bg.coffee +++ b/app/locale/bg.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "български език", englishDescri # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "български език", englishDescri # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/ca.coffee b/app/locale/ca.coffee index 94cb6d785..6a30f72a8 100644 --- a/app/locale/ca.coffee +++ b/app/locale/ca.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr campaign_multiplayer_description: "... on programes cara a cara contra altres jugadors." campaign_player_created: "Creats pel Jugador" campaign_player_created_description: "... on lluites contra la creativitat dels teus companys Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Dificultat: " play_as: "Jugar com" spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/cs.coffee b/app/locale/cs.coffee index b376c394a..efd3ee7ae 100644 --- a/app/locale/cs.coffee +++ b/app/locale/cs.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr campaign_multiplayer_description: "...ve které programujete proti jiným hráčům." campaign_player_created: "Uživatelsky vytvořené úrovně" campaign_player_created_description: "...ve kterých bojujete proti kreativitě ostatních Zdatných Kouzelníků." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Obtížnost: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/da.coffee b/app/locale/da.coffee index 7857a119d..69640ab50 100644 --- a/app/locale/da.coffee +++ b/app/locale/da.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans campaign_multiplayer_description: "... hvor du koder ansigt-til-ansigt imod andre spillere." campaign_player_created: "Spillerkreerede" campaign_player_created_description: "... hvor du kæmper mod dine med-Kunsthåndværker-troldmænds kreativitet." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Sværhedsgrad: " play_as: "Spil Som " spectate: "Observér" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/de-AT.coffee b/app/locale/de-AT.coffee index 1d8dac402..7d934f8a8 100644 --- a/app/locale/de-AT.coffee +++ b/app/locale/de-AT.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: campaign_multiplayer_description: "... in der Du Kopf-an-Kopf gegen andere Spieler programmierst." campaign_player_created: "Von Spielern erstellt" campaign_player_created_description: "... in welchem Du gegen die Kreativität eines Artisan Zauberers kämpfst." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Schwierigkeit: " play_as: "Spiele als " spectate: "Zuschauen" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription: # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" delta: added: "hinzugefügt" diff --git a/app/locale/de-CH.coffee b/app/locale/de-CH.coffee index 7f29c727e..1544bf85a 100644 --- a/app/locale/de-CH.coffee +++ b/app/locale/de-CH.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge campaign_multiplayer_description: "... i dene du Chopf a Chopf geg anderi Spieler spielsch." campaign_player_created: "Vo Spieler erstellti Level" campaign_player_created_description: "... i dene du gege d Kreativität vome Handwerker Zauberer kämpfsch." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Schwierigkeit: " play_as: "Spiel als" spectate: "Zueluege" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/de-DE.coffee b/app/locale/de-DE.coffee index 2d93f7d76..5046d545e 100644 --- a/app/locale/de-DE.coffee +++ b/app/locale/de-DE.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: campaign_multiplayer_description: "... in der Du Kopf-an-Kopf gegen andere Spieler programmierst." campaign_player_created: "Von Spielern erstellt" campaign_player_created_description: "... in welchem Du gegen die Kreativität eines Artisan Zauberers kämpfst." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Schwierigkeit: " play_as: "Spiele als " spectate: "Zuschauen" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription: # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" delta: added: "hinzugefügt" diff --git a/app/locale/el.coffee b/app/locale/el.coffee index cf1ea906b..f98e92e8b 100644 --- a/app/locale/el.coffee +++ b/app/locale/el.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre campaign_multiplayer_description: "... στο οποίο μπορείτε να προγραμματίσετε σώμα με σώμα έναντι άλλων παικτών." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Δυσκολία: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/en-AU.coffee b/app/locale/en-AU.coffee index fbf9410c8..0435f05b4 100644 --- a/app/locale/en-AU.coffee +++ b/app/locale/en-AU.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/en-GB.coffee b/app/locale/en-GB.coffee index b8cb8411a..18cea4ed4 100644 --- a/app/locale/en-GB.coffee +++ b/app/locale/en-GB.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/en-US.coffee b/app/locale/en-US.coffee index 1541deabd..6e8a10869 100644 --- a/app/locale/en-US.coffee +++ b/app/locale/en-US.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/es-419.coffee b/app/locale/es-419.coffee index e100142d2..2d595c3dd 100644 --- a/app/locale/es-419.coffee +++ b/app/locale/es-419.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip campaign_multiplayer_description: "... en las que programas cara-a-cara contra otros jugadores." campaign_player_created: "Creados-Por-Jugadores" campaign_player_created_description: "... en los que luchas contra la creatividad de tus compañeros Hechiceros Artesanales." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Dificultad: " play_as: "Jugar Como " spectate: "Observar" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/es-ES.coffee b/app/locale/es-ES.coffee index 2d4d41d12..708209cf3 100644 --- a/app/locale/es-ES.coffee +++ b/app/locale/es-ES.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis campaign_multiplayer_description: "... en las que tu código se enfrentará al de otros jugadores." campaign_player_created: "Creaciones de los Jugadores" campaign_player_created_description: "... en las que luchas contra la creatividad de tus compañeros Magos Artesanos." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Dificultad: " play_as: "Jugar como" spectate: "Observar" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" delta: added: "Añadido" diff --git a/app/locale/fa.coffee b/app/locale/fa.coffee index 2aea43f4e..546186670 100644 --- a/app/locale/fa.coffee +++ b/app/locale/fa.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", campaign_multiplayer_description: "... جایی که کد رو به رو شدن با بقیه بازیکنان رو مینویسید." campaign_player_created: "ایجاد بازیکن" campaign_player_created_description: "... جایی که در مقابل خلاقیت نیرو هاتون قرار میگیرید جادوگران آرتیزان." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "سختی: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian", # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/fi.coffee b/app/locale/fi.coffee index ad52c0713..4a00e59e4 100644 --- a/app/locale/fi.coffee +++ b/app/locale/fi.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/fr.coffee b/app/locale/fr.coffee index 51c442ed0..e46ce838a 100644 --- a/app/locale/fr.coffee +++ b/app/locale/fr.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "français", englishDescription: "French", t campaign_multiplayer_description: "... dans laquelle vous coderez en face à face contre d'autres joueurs." campaign_player_created: "Niveaux créés par les joueurs" campaign_player_created_description: "... Dans laquelle vous serez confrontés à la créativité des votres.Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Difficulté: " play_as: "Jouer comme " spectate: "Spectateur" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" delta: added: "Ajouté" diff --git a/app/locale/he.coffee b/app/locale/he.coffee index bc4fe3c6a..3dd3b47b8 100644 --- a/app/locale/he.coffee +++ b/app/locale/he.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", campaign_multiplayer_description: "..." campaign_player_created: "תוצרי השחקנים" campaign_player_created_description: "... שבהם תילחם נגד היצירתיות של בעלי-המלאכה." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "רמת קושי: " play_as: "שחק בתור " spectate: "צופה" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew", # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/hi.coffee b/app/locale/hi.coffee index 876aef695..22a3b4280 100644 --- a/app/locale/hi.coffee +++ b/app/locale/hi.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/hu.coffee b/app/locale/hu.coffee index 7e19bfc87..c5dff3e03 100644 --- a/app/locale/hu.coffee +++ b/app/locale/hu.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t campaign_multiplayer_description: "... amelyekben a kódod felveheti a versenyt más játékosok kódjával" campaign_player_created: "Játékosok pályái" campaign_player_created_description: "...melyekben Művészi Varázsló társaid ellen kűzdhetsz." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Nehézség: " play_as: "Játssz mint" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/id.coffee b/app/locale/id.coffee index cb115a690..4af035a4a 100644 --- a/app/locale/id.coffee +++ b/app/locale/id.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/it.coffee b/app/locale/it.coffee index 5c0d1ff76..f481ebbab 100644 --- a/app/locale/it.coffee +++ b/app/locale/it.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t campaign_multiplayer_description: "... nelle quali programmi faccia a faccia contro altri giocatori." campaign_player_created: "Creati dai giocatori" campaign_player_created_description: "... nei quali affronterai la creatività dei tuoi compagni Stregoni Artigiani." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Difficoltà: " play_as: "Gioca come " spectate: "Spettatore" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/ja.coffee b/app/locale/ja.coffee index dffa1fa47..2c6f283bb 100644 --- a/app/locale/ja.coffee +++ b/app/locale/ja.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "難易度: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese", # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/ko.coffee b/app/locale/ko.coffee index da283f410..73e89e210 100644 --- a/app/locale/ko.coffee +++ b/app/locale/ko.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t campaign_multiplayer_description: "... 이곳에서 당신은 다른 인간 플레이어들과 직접 결투할 수 있습니다." campaign_player_created: "사용자 직접 제작" campaign_player_created_description: "... 당신 동료가 고안한 레벨에 도전하세요 마법사 장인." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "난이도: " play_as: "Play As " spectate: "관중모드" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/lt.coffee b/app/locale/lt.coffee index 392736446..f9b886ca7 100644 --- a/app/locale/lt.coffee +++ b/app/locale/lt.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/ms.coffee b/app/locale/ms.coffee index 8090b977e..dfe726098 100644 --- a/app/locale/ms.coffee +++ b/app/locale/ms.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/nb.coffee b/app/locale/nb.coffee index 586d407d6..ad155850e 100644 --- a/app/locale/nb.coffee +++ b/app/locale/nb.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg campaign_multiplayer_description: "... hvor du spiller direkte mot andre spillere." campaign_player_created: "Spiller-Lagde" campaign_player_created_description: "... hvor du kjemper mot kreativiteten til en av dine medspillende Artisan Trollmenn." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Vanskelighetsgrad: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/nl-BE.coffee b/app/locale/nl-BE.coffee index 57779c970..d842bbd39 100644 --- a/app/locale/nl-BE.coffee +++ b/app/locale/nl-BE.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: campaign_multiplayer_description: "... waarin je direct tegen andere spelers speelt." campaign_player_created: "Door-spelers-gemaakt" campaign_player_created_description: "... waarin je ten strijde trekt tegen de creativiteit van andere Ambachtelijke Tovenaars." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Moeilijkheidsgraad: " play_as: "Speel als " spectate: "Toeschouwen" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription: # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/nl-NL.coffee b/app/locale/nl-NL.coffee index f4f1ca0fe..bb9afa07b 100644 --- a/app/locale/nl-NL.coffee +++ b/app/locale/nl-NL.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription campaign_multiplayer_description: "... waarin je direct tegen andere spelers speelt." campaign_player_created: "Door-spelers-gemaakt" campaign_player_created_description: "... waarin je ten strijde trekt tegen de creativiteit van andere Ambachtelijke Tovenaars." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Moeilijkheidsgraad: " play_as: "Speel als " spectate: "Toeschouwen" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/nn.coffee b/app/locale/nn.coffee index 4a1daa98a..20586ece2 100644 --- a/app/locale/nn.coffee +++ b/app/locale/nn.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/no.coffee b/app/locale/no.coffee index a0c93f751..0faf5283a 100644 --- a/app/locale/no.coffee +++ b/app/locale/no.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr campaign_multiplayer_description: "... hvor du spiller direkte mot andre spillere." campaign_player_created: "Brett laget av andre brukere" campaign_player_created_description: "... hvor du kjemper mot kreativiteten til en av dine medspillende Artisan Trollmenn." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Vanskelighetsgrad: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/pl.coffee b/app/locale/pl.coffee index ba39fc531..6f5e8d568 100644 --- a/app/locale/pl.coffee +++ b/app/locale/pl.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish campaign_multiplayer_description: "... w których konkurujesz z innymi graczami." campaign_player_created: "Stworzone przez graczy" campaign_player_created_description: "... w których walczysz przeciwko dziełom Czarodziejów Rękodzielnictwa" +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Poziom trudności: " play_as: "Graj jako " spectate: "Oglądaj" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/pt-BR.coffee b/app/locale/pt-BR.coffee index 148286599..6ffe3a56c 100644 --- a/app/locale/pt-BR.coffee +++ b/app/locale/pt-BR.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: campaign_multiplayer_description: "... nas quais você programará cara-a-cara contra outros jogadores." campaign_player_created: "Criados por Jogadores" campaign_player_created_description: "... nos quais você batalhará contra a criatividade dos seus companheiros feiticeiros Artesãos." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Dificuldade: " play_as: "Jogar Como " spectate: "Assistir" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription: # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index b82a76888..dcf88281f 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: campaign_multiplayer_description: "... onde programa frente-a-frente contra outros jogadores." campaign_player_created: "Criados por Jogadores" campaign_player_created_description: "... onde combate contra a criatividade dos seus colegas Feiticeiros Artesãos." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Dificuldade: " play_as: "Jogar Como" spectate: "Espectar" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription: achievement: "Conquista" clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" delta: added: "Adicionados/as" diff --git a/app/locale/ro.coffee b/app/locale/ro.coffee index 714774510..4f8408661 100644 --- a/app/locale/ro.coffee +++ b/app/locale/ro.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman campaign_multiplayer_description: "... în care te lupți cap-la-cap contra alti jucători." campaign_player_created: "Create de jucători" campaign_player_created_description: "... în care ai ocazia să testezi creativitatea colegilor tai Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Dificultate: " play_as: "Alege-ți echipa" spectate: "Spectator" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index bb980ecd2..d6b578b9d 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi campaign_multiplayer_description: "... в которых вы соревнуетесь в программировании с другими игроками." campaign_player_created: "Уровни игроков" campaign_player_created_description: "... в которых вы сражаетесь с креативностью ваших друзей Ремесленников." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Сложность: " play_as: "Играть за " spectate: "Наблюдать" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" delta: added: "Добавлено" diff --git a/app/locale/sk.coffee b/app/locale/sk.coffee index 298ee9711..6cf8d53c4 100644 --- a/app/locale/sk.coffee +++ b/app/locale/sk.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", campaign_multiplayer_description: "... v ktorej si zmeriaš svoje programátorské sily proti ostatným hráčom." campaign_player_created: "Hráčmi vytvorené úrovne" campaign_player_created_description: "... v ktorých sa popasuješ s kreativitou svojich kúzelníckych súdruhov." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Obtiažnosť." play_as: "Hraj ako" spectate: "Sleduj" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak", # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/sl.coffee b/app/locale/sl.coffee index 31d01e63b..8ed46d8aa 100644 --- a/app/locale/sl.coffee +++ b/app/locale/sl.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/sr.coffee b/app/locale/sr.coffee index 863e22f26..4b7423811 100644 --- a/app/locale/sr.coffee +++ b/app/locale/sr.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian campaign_multiplayer_description: "... у којима кодираш 1 на 1 мечеве против осталих играча." campaign_player_created: "Направљено од стране играча" campaign_player_created_description: "... у којима се бориш против креативности својих колега." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Тежина: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/sv.coffee b/app/locale/sv.coffee index 37458eb60..bf5d40751 100644 --- a/app/locale/sv.coffee +++ b/app/locale/sv.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr campaign_multiplayer_description: "... i vilken du tävlar i kodande mot andra spelare" campaign_player_created: "Spelarskapade" campaign_player_created_description: "... i vilken du tävlar mot kreativiteten hos andra Hantverkare." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Svårighetsgrad: " play_as: "Spela som " spectate: "Titta på" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/th.coffee b/app/locale/th.coffee index b6e1cefed..5abe55703 100644 --- a/app/locale/th.coffee +++ b/app/locale/th.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/tr.coffee b/app/locale/tr.coffee index 4c450173e..56e46cd76 100644 --- a/app/locale/tr.coffee +++ b/app/locale/tr.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t campaign_multiplayer_description: "Diğer oyuncularla kafa kafaya verip kodlamak için..." campaign_player_created: "Oyuncuların Oluşturdukları" campaign_player_created_description: "Zanaatkâr Büyücülerin yaratıcılıklarına karşı mücadele etmek için..." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Zorluk: " play_as: "Olarak Oyna" spectate: "İzleyici olarak katıl" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/uk.coffee b/app/locale/uk.coffee index 698572816..95b602b6e 100644 --- a/app/locale/uk.coffee +++ b/app/locale/uk.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "українська мова", englishDesc campaign_multiplayer_description: "... в яких ви програмуєте віч-на-віч із іншими гравцями." campaign_player_created: "Рівні, створені гравцями" campaign_player_created_description: "... у яких ви змагаєтесь у креативності із вашими друзями-Архітекторами." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Складність: " play_as: "Грати як" spectate: "Спостерігати" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "українська мова", englishDesc # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/ur.coffee b/app/locale/ur.coffee index 3f79f2ae0..10c1c74fb 100644 --- a/app/locale/ur.coffee +++ b/app/locale/ur.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu", # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/vi.coffee b/app/locale/vi.coffee index d617889cf..9705d0f1b 100644 --- a/app/locale/vi.coffee +++ b/app/locale/vi.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # campaign_multiplayer_description: "... in which you code head-to-head against other players." campaign_player_created: "Tạo người chơi" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "Khó: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/zh-HANS.coffee b/app/locale/zh-HANS.coffee index f2b84e3e4..a3b911f4c 100644 --- a/app/locale/zh-HANS.coffee +++ b/app/locale/zh-HANS.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese campaign_multiplayer_description: "……在这里你可以与其他玩家进行代码肉搏战。" campaign_player_created: "创建玩家" campaign_player_created_description: "……在这里你可以与你的小伙伴的创造力战斗 技术指导." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "难度:" play_as: "Play As" spectate: "旁观他人的游戏" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/zh-HANT.coffee b/app/locale/zh-HANT.coffee index cf89fba0b..22c279adf 100644 --- a/app/locale/zh-HANT.coffee +++ b/app/locale/zh-HANT.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese campaign_multiplayer_description: "...在這裡你可以和其他玩家進行對戰。" campaign_player_created: "玩家建立的關卡" campaign_player_created_description: "...挑戰同伴的創意 技術指導." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "難度" # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/zh-WUU-HANS.coffee b/app/locale/zh-WUU-HANS.coffee index e710f94b0..814760356 100644 --- a/app/locale/zh-WUU-HANS.coffee +++ b/app/locale/zh-WUU-HANS.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # campaign_multiplayer_description: "... in which you code head-to-head against other players." # campaign_player_created: "Player-Created" # campaign_player_created_description: "... in which you battle against the creativity of your fellow Artisan Wizards." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." # level_difficulty: "Difficulty: " # play_as: "Play As" # spectate: "Spectate" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" diff --git a/app/locale/zh-WUU-HANT.coffee b/app/locale/zh-WUU-HANT.coffee index 27d180c4f..06badf071 100644 --- a/app/locale/zh-WUU-HANT.coffee +++ b/app/locale/zh-WUU-HANT.coffee @@ -123,6 +123,8 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio campaign_multiplayer_description: "……徠箇搭爾好搭別人代碼捉跤。" campaign_player_created: "造玩家" campaign_player_created_description: "……徠箇搭爾好搭爾夥計造起來個賭打 技術相幫." +# campaign_classic_algorithms: "Classic Algorithms" +# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science." level_difficulty: "難度:" play_as: "Play As" spectate: "望別人攪遊戲" @@ -953,6 +955,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio # achievement: "Achievement" # clas: "CLAs" # play_counts: "Play Counts" +# feedback: "Feedback" # delta: # added: "Added" From 9c841991fdab78a80cd21f7a8bec6d030e36bf17 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 2 Sep 2014 11:07:58 -0700 Subject: [PATCH 92/98] Added links to the versions modal. --- app/templates/modal/versions.jade | 3 ++- app/views/modal/VersionsModal.coffee | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/templates/modal/versions.jade b/app/templates/modal/versions.jade index 56c9b0f71..f99f9402c 100755 --- a/app/templates/modal/versions.jade +++ b/app/templates/modal/versions.jade @@ -19,7 +19,8 @@ block modal-body-content tr td input(type="checkbox", value=data._id).select - td #{data.version.major}.#{data.version.minor} + td + a(href="/editor/#{page}/#{data._id}") #{data.version.major}.#{data.version.minor} td= moment(data.created).format('l') td= data.creator td #{data.commitMessage} diff --git a/app/views/modal/VersionsModal.coffee b/app/views/modal/VersionsModal.coffee index b9c93bb95..ab63fd569 100755 --- a/app/views/modal/VersionsModal.coffee +++ b/app/views/modal/VersionsModal.coffee @@ -1,6 +1,5 @@ ModalView = require 'views/kinds/ModalView' template = require 'templates/modal/versions' -tableTemplate = require 'templates/kinds/table' DeltaView = require 'views/editor/DeltaView' PatchModal = require 'views/editor/PatchModal' nameLoader = require 'lib/NameLoader' From 4b9bf22e226abc171e1c7ed289eecdb43f69544a Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 2 Sep 2014 11:29:24 -0700 Subject: [PATCH 93/98] Thang editor now sets some default Components when you change the kind of a ThangType. --- app/models/LevelComponent.coffee | 6 ++- app/schemas/subscriptions/editor.coffee | 3 ++ .../component/ThangComponentsEditView.coffee | 47 +++++++++++++++---- .../editor/level/thangs/ThangsTabView.coffee | 14 ++---- .../editor/thang/ThangTypeEditView.coffee | 4 ++ 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/app/models/LevelComponent.coffee b/app/models/LevelComponent.coffee index 6353e9684..6c0dbbc65 100644 --- a/app/models/LevelComponent.coffee +++ b/app/models/LevelComponent.coffee @@ -3,10 +3,14 @@ CocoModel = require './CocoModel' module.exports = class LevelComponent extends CocoModel @className: 'LevelComponent' @schema: require 'schemas/models/level_component' - + @EquipsID: '53e217d253457600003e3ebb' @ItemID: '53e12043b82921000051cdf9' @AttacksID: '524b7ba57fc0f6d519000016' + @PhysicalID: '524b75ad7fc0f6d519000001' + @ExistsID: '524b4150ff92f1f4f8000024' + @LandID: '524b7aff7fc0f6d519000006' + @CollidesID: '524b7b857fc0f6d519000012' urlRoot: '/db/level.component' set: (key, val, options) -> diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee index 99b47d6db..214404d5c 100644 --- a/app/schemas/subscriptions/editor.coffee +++ b/app/schemas/subscriptions/editor.coffee @@ -45,3 +45,6 @@ module.exports = 'editor:random-terrain-generated': c.object {required: ['thangs']}, thangs: c.array {}, {type: 'object'} + + 'editor:thang-type-kind-changed': c.object {required: ['kind']}, + kind: {type: 'string'} diff --git a/app/views/editor/component/ThangComponentsEditView.coffee b/app/views/editor/component/ThangComponentsEditView.coffee index 45fd6820e..dc8d2e0fb 100644 --- a/app/views/editor/component/ThangComponentsEditView.coffee +++ b/app/views/editor/component/ThangComponentsEditView.coffee @@ -12,6 +12,28 @@ nodes = require '../level/treema_nodes' ThangType = require 'models/ThangType' CocoCollection = require 'collections/CocoCollection' +LC = (componentName, config) -> original: LevelComponent[componentName + 'ID'], majorVersion: 0, config: config +DEFAULT_COMPONENTS = + Unit: [LC('Equips')] + Floor: [ + LC('Exists', stateless: true) + LC('Physical', width: 20, height: 17, depth: 2, shape: 'sheet', pos: {x: 10, y: 8.5, z: 1}) + LC('Land') + ] + Wall: [ + LC('Exists', stateless: true) + LC('Physical', width: 4, height: 4, depth: 12, shape: 'box', pos: {x: 2, y: 2, z: 6}) + LC('Collides', collisionType: 'static', collisionCategory: 'obstacles', mass: 1000, fixedRotation: true, restitution: 1) + ] + Doodad: [ + LC('Exists', stateless: true) + LC('Physical') + LC('Collides', collisionType: 'static', fixedRotation: true) + ] + Misc: [LC('Exists'), LC('Physical')] + Mark: [] + Item: [LC('Item')] + class ItemThangTypeSearchCollection extends CocoCollection url: '/db/thang.type?view=items&project=original,name,version,slug,kind,components' model: ThangType @@ -20,6 +42,9 @@ module.exports = class ThangComponentsEditView extends CocoView id: 'thang-components-edit-view' template: template + subscriptions: + 'editor:thang-type-kind-changed': 'onThangTypeKindChanged' + events: 'click #add-components-button': 'onAddComponentsButtonClicked' @@ -33,11 +58,11 @@ module.exports = class ThangComponentsEditView extends CocoView @world = options.world @level = options.level @loadComponents(@components) - + setThangType: (@thangType) -> return unless componentRefs = @thangType?.get('components') @loadComponents(componentRefs) - + loadComponents: (components) -> for componentRef in components # just to handle if ever somehow the same component is loaded twice, through bad data and alike @@ -73,7 +98,7 @@ module.exports = class ThangComponentsEditView extends CocoView type: 'object' default: defaultValue additionalProperties: Level.schema.properties.thangs.items.properties.components.items - }, + }, data: $.extend true, {}, components callbacks: {select: @onSelectComponent, change: @onComponentsTreemaChanged} nodeClasses: @@ -256,7 +281,7 @@ module.exports = class ThangComponentsEditView extends CocoView thangComponent.config = e.config foundComponent = true break - + if not foundComponent @components.push({ original: e.component.get('original') @@ -269,7 +294,7 @@ module.exports = class ThangComponentsEditView extends CocoView if subview.component.get('original') is e.component.get('original') _.defer -> subview.setIsDefaultComponent(false) break - + @updateComponentsList() @reportChanges() @@ -344,18 +369,24 @@ module.exports = class ThangComponentsEditView extends CocoView @loadComponents(sparseComponents) @components = @components.concat(sparseComponents) @onComponentsChanged() - + + onThangTypeKindChanged: (e) -> + return unless defaultComponents = DEFAULT_COMPONENTS[e.kind] + for component in defaultComponents when not _.find(@components, original: component.original) + @components.push component + @onComponentsAdded() + destroy: -> @componentsTreema?.destroy() super() class ThangComponentsObjectNode extends TreemaObjectNode addNewChild: -> @addNewChildForKey('') # HACK to get the object adding to act more like adding to an array - + getChildren: -> children = super(arguments...) children.sort(@sortFunction) - + sortFunction: (a, b) => a = a.value ? a.defaultData b = b.value ? b.defaultData diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 8a1e07508..0d5edc1aa 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -15,11 +15,6 @@ ComponentsCollection = require 'collections/ComponentsCollection' MOVE_MARGIN = 0.15 MOVE_SPEED = 13 -# Essential component original ids -componentOriginals = - 'existence.Exists': '524b4150ff92f1f4f8000024' - 'physics.Physical': '524b75ad7fc0f6d519000001' - # Let us place these on top of other Thangs overlappableThangTypeNames = ['Torch', 'Chains', 'Bird', 'Cloud 1', 'Cloud 2', 'Cloud 3', 'Waterfall', 'Obstacle'] @@ -229,8 +224,7 @@ module.exports = class ThangsTabView extends CocoView @surface.camera.dragDisabled = false return unless @selectedExtantThang and e.thang?.id is @selectedExtantThang?.id pos = @selectedExtantThang.pos - physicalOriginal = componentOriginals['physics.Physical'] - path = "id=#{@selectedExtantThang.id}/components/original=#{physicalOriginal}" # TODO: hack + path = "id=#{@selectedExtantThang.id}/components/original=#{LevelComponent.PhysicalID}" physical = @thangsTreema.get path return if not physical or (physical.config.pos.x is pos.x and physical.config.pos.y is pos.y) @thangsTreema.set path + '/config/pos', x: pos.x, y: pos.y, z: pos.z @@ -319,11 +313,11 @@ module.exports = class ThangsTabView extends CocoView createEssentialComponents: (defaultComponents) -> physicalConfig = {pos: {x: 10, y: 10, z: 1}} - if physicalOriginal = _.find(defaultComponents ? [], original: componentOriginals['physics.Physical']) + if physicalOriginal = _.find(defaultComponents ? [], original: LevelComponent.PhysicalID) physicalConfig.pos.z = physicalOriginal.config?.pos?.z ? 1 # Get the z right [ - {original: componentOriginals['existence.Exists'], majorVersion: 0, config: {}} - {original: componentOriginals['physics.Physical'], majorVersion: 0, config: physicalConfig} + {original: LevelComponent.ExistsID, majorVersion: 0, config: {}} + {original: LevelComponent.PhysicalID, majorVersion: 0, config: physicalConfig} ] createAddThang: -> diff --git a/app/views/editor/thang/ThangTypeEditView.coffee b/app/views/editor/thang/ThangTypeEditView.coffee index 09b1af817..186304b2d 100644 --- a/app/views/editor/thang/ThangTypeEditView.coffee +++ b/app/views/editor/thang/ThangTypeEditView.coffee @@ -380,6 +380,7 @@ module.exports = class ThangTypeEditView extends RootView el = @$el.find('#thang-type-treema') @treema = @$el.find('#thang-type-treema').treema(options) @treema.build() + @lastKind = data.kind pushChangesToPreview: => # TODO: This doesn't delete old Treema keys you deleted @@ -389,6 +390,9 @@ module.exports = class ThangTypeEditView extends RootView @refreshAnimation() @updateDots() @updatePortrait() + if (kind = @treema.data.kind) isnt @lastKind + @lastKind = kind + Backbone.Mediator.publish 'editor:thang-type-kind-changed', kind: kind onSelectNode: (e, selected) => selected = selected[0] From 13b066f6aac27c8b4dd17e700509854f68dccd2a Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 2 Sep 2014 11:53:03 -0700 Subject: [PATCH 94/98] Fixed not being able to drag thangs around every other attempt. --- app/views/editor/level/thangs/ThangsTabView.coffee | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 8a1e07508..c2d9b0d35 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -408,6 +408,11 @@ module.exports = class ThangsTabView extends CocoView thang.isSelectable = not thang.isLand for thang in @world.thangs # let us select walls and such @surface?.setWorld @world @selectAddThangType @addThangType, @cloneSourceThang if @addThangType # make another addThang sprite, since the World just refreshed + + # update selection, since the thangs have been remade + if @selectedExtantThang + @selectedExtantSprite = @surface.spriteBoss.sprites[@selectedExtantThang.id] + @selectedExtantThang = @selectedExtantSprite.thang Backbone.Mediator.publish 'editor:thangs-edited', thangs: @world.thangs onTreemaThangSelected: (e, selectedTreemas) => From b521aee322b6c28d98b0429aa235bef34b35c6b8 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 2 Sep 2014 12:33:34 -0700 Subject: [PATCH 95/98] Optimized level serialization a little so that level editing is a bit less painful. --- app/models/Level.coffee | 10 +++++----- app/views/editor/level/thangs/ThangsTabView.coffee | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/models/Level.coffee b/app/models/Level.coffee index be7364bac..fc49d8dfb 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -12,12 +12,12 @@ module.exports = class Level extends CocoModel o = @denormalize supermodel, session # Figure out Components - o.levelComponents = _.cloneDeep (lc.attributes for lc in supermodel.getModels LevelComponent) + o.levelComponents = $.extend true, [], (lc.attributes for lc in supermodel.getModels LevelComponent) @sortThangComponents o.thangs, o.levelComponents, 'Level Thang' @fillInDefaultComponentConfiguration o.thangs, o.levelComponents # Figure out Systems - systemModels = _.cloneDeep (ls.attributes for ls in supermodel.getModels LevelSystem) + systemModels = $.extend true, [], (ls.attributes for ls in supermodel.getModels LevelSystem) o.systems = @sortSystems o.systems, systemModels @fillInDefaultSystemConfiguration o.systems @@ -93,7 +93,7 @@ module.exports = class Level extends CocoModel system2 = _.find levelSystems, {original: d.original} visit system2 #console.log 'sorted systems adding', systemModel.name - sorted.push {model: systemModel, config: _.cloneDeep system.config} + sorted.push {model: systemModel, config: $.extend true, {}, system.config} originalsSeen[system.original] = true visit system for system in levelSystems ? [] sorted @@ -140,7 +140,7 @@ module.exports = class Level extends CocoModel for component in thang.components or [] continue unless lc = _.find levelComponents, {original: component.original} component.config ?= {} - TreemaUtils.populateDefaults(component.config, lc.configSchema) + TreemaUtils.populateDefaults(component.config, lc.configSchema, tv4) @lastType = 'component' @lastOriginal = component.original @walkDefaults component.config, lc.configSchema.properties @@ -148,7 +148,7 @@ module.exports = class Level extends CocoModel fillInDefaultSystemConfiguration: (levelSystems) -> for system in levelSystems ? [] system.config ?= {} - TreemaUtils.populateDefaults(system.config, system.model.configSchema) + TreemaUtils.populateDefaults(system.config, system.model.configSchema, tv4) @lastType = 'system' @lastOriginal = system.model.name @walkDefaults system.config, system.model.configSchema.properties diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index c2d9b0d35..0fa9dc1cf 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -412,7 +412,7 @@ module.exports = class ThangsTabView extends CocoView # update selection, since the thangs have been remade if @selectedExtantThang @selectedExtantSprite = @surface.spriteBoss.sprites[@selectedExtantThang.id] - @selectedExtantThang = @selectedExtantSprite.thang + @selectedExtantThang = @selectedExtantSprite?.thang Backbone.Mediator.publish 'editor:thangs-edited', thangs: @world.thangs onTreemaThangSelected: (e, selectedTreemas) => From 1edf3de651c026c8fd9a585660b0932c5e01894b Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Tue, 2 Sep 2014 14:06:37 -0700 Subject: [PATCH 96/98] Fixed double clicking sometimes firing when you're just dragging units around in the level editor. --- app/views/editor/level/thangs/ThangsTabView.coffee | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index eef86dc9d..0231c7993 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -38,6 +38,7 @@ module.exports = class ThangsTabView extends CocoView 'editor:view-switched': 'onViewSwitched' 'sprite:dragged': 'onSpriteDragged' 'sprite:mouse-up': 'onSpriteMouseUp' + 'sprite:mouse-down': 'onSpriteMouseDown' 'sprite:double-clicked': 'onSpriteDoubleClicked' 'surface:stage-mouse-up': 'onStageMouseUp' 'editor:random-terrain-generated': 'onRandomTerrainGenerated' @@ -192,6 +193,7 @@ module.exports = class ThangsTabView extends CocoView @surface?.spriteBoss?.selectSprite null, null onSpriteMouseDown: (e) -> + @dragged = false # Sprite clicks happen after stage clicks, but we need to know whether a sprite is being clicked. # clearTimeout @backgroundAddClickTimeout # if e.originalEvent.nativeEvent.button == 2 @@ -206,6 +208,7 @@ module.exports = class ThangsTabView extends CocoView onSpriteDragged: (e) -> return unless @selectedExtantThang and e.thang?.id is @selectedExtantThang?.id + @dragged = true @surface.camera.dragDisabled = true {stageX, stageY} = e.originalEvent wop = @surface.camera.screenToWorld x: stageX, y: stageY @@ -230,7 +233,7 @@ module.exports = class ThangsTabView extends CocoView @thangsTreema.set path + '/config/pos', x: pos.x, y: pos.y, z: pos.z onSpriteDoubleClicked: (e) -> - return unless e.thang + return unless e.thang and not @dragged @editThang thangID: e.thang.id onRandomTerrainGenerated: (e) -> From ba53cb06febf3aec6ae2aaaa2d8eda15e99cb9f9 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 2 Sep 2014 14:16:36 -0700 Subject: [PATCH 97/98] Tagged Levels and ThangTypes by terrains. Terrains now control default pathfinding in AI System. --- app/schemas/models/level.coffee | 1 + app/schemas/models/thang_type.coffee | 1 + app/schemas/schemas.coffee | 2 ++ app/schemas/subscriptions/editor.coffee | 10 +++++++++- .../level/modals/TerrainRandomizeModal.coffee | 9 ++++++--- .../level/settings/SettingsTabView.coffee | 12 ++++++++++-- .../editor/level/systems/SystemsTabView.coffee | 18 ++++++++++++++++++ .../editor/thang/ThangTypeEditView.coffee | 2 ++ 8 files changed, 49 insertions(+), 6 deletions(-) diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index 92b5036c2..993eed92f 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -245,6 +245,7 @@ _.extend LevelSchema.properties, banner: {type: 'string', format: 'image-file', title: 'Banner'} goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema type: c.shortString(title: 'Type', description: 'What kind of level this is.', 'enum': ['campaign', 'ladder', 'ladder-tutorial', 'hero']) + 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']) c.extendBasicProperties LevelSchema, 'level' diff --git a/app/schemas/models/thang_type.coffee b/app/schemas/models/thang_type.coffee index 9ac0319c3..c01c41878 100644 --- a/app/schemas/models/thang_type.coffee +++ b/app/schemas/models/thang_type.coffee @@ -106,6 +106,7 @@ _.extend ThangTypeSchema.properties, containers: c.object {title: 'Containers', additionalProperties: ContainerObjectSchema} animations: c.object {title: 'Animations', additionalProperties: RawAnimationObjectSchema} kind: c.shortString {enum: ['Unit', 'Floor', 'Wall', 'Doodad', 'Misc', 'Mark', 'Item'], default: 'Misc', title: 'Kind'} + terrains: c.array {title: 'Terrains', description: 'If specified, limits this ThangType to levels with matching terrains.', uniqueItems: true}, c.terrainString actions: c.object {title: 'Actions', additionalProperties: {$ref: '#/definitions/action'}} soundTriggers: c.object {title: 'Sound Triggers', additionalProperties: c.array({}, {$ref: '#/definitions/sound'})}, diff --git a/app/schemas/schemas.coffee b/app/schemas/schemas.coffee index ea6881729..0eb37689d 100644 --- a/app/schemas/schemas.coffee +++ b/app/schemas/schemas.coffee @@ -200,3 +200,5 @@ me.activity = me.object {description: 'Stats on an activity'}, first: me.date() last: me.date() count: {type: 'integer', minimum: 0} + +me.terrainString = me.shortString {enum: ['Grass', 'Dungeon', 'Indoor'], title: 'Terrain', description: 'Which terrain type this is.'} diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee index 214404d5c..865f51071 100644 --- a/app/schemas/subscriptions/editor.coffee +++ b/app/schemas/subscriptions/editor.coffee @@ -43,8 +43,16 @@ module.exports = 'level:reload-thang-type': c.object {required: ['thangType']}, thangType: {type: 'object'} - 'editor:random-terrain-generated': c.object {required: ['thangs']}, + 'editor:random-terrain-generated': c.object {required: ['thangs', 'terrain']}, thangs: c.array {}, {type: 'object'} + terrain: c.terrainString + + 'editor:terrain-changed': c.object {required: ['terrain']}, + terrain: + oneOf: [ + c.terrainString + {type: ['null', 'undefined']} + ] 'editor:thang-type-kind-changed': c.object {required: ['kind']}, kind: {type: 'string'} diff --git a/app/views/editor/level/modals/TerrainRandomizeModal.coffee b/app/views/editor/level/modals/TerrainRandomizeModal.coffee index 31958e8d0..94a772706 100644 --- a/app/views/editor/level/modals/TerrainRandomizeModal.coffee +++ b/app/views/editor/level/modals/TerrainRandomizeModal.coffee @@ -87,6 +87,7 @@ clusters = { presets = { 'dungeon': { + 'terrainName': 'Dungeon' 'type':'dungeon' 'borders':'dungeon_wall' 'borderNoise':0 @@ -128,6 +129,7 @@ presets = { } } 'indoor': { + 'terrainName': 'Indoor' 'type':'indoor' 'borders':'indoor_wall' 'borderNoise':0 @@ -161,6 +163,7 @@ presets = { } } 'grassy': { + 'terrainName': 'Grass' 'type':'grassy' 'borders':'trees' 'borderNoise':1 @@ -223,8 +226,8 @@ thangSizes = { 'borderSize': { 'x':4 'y':4 - } -} + }} + module.exports = class TerrainRandomizeModal extends ModalView id: 'terrain-randomize-modal' @@ -243,7 +246,7 @@ module.exports = class TerrainRandomizeModal extends ModalView presetType = target.attr 'data-preset-type' presetSize = target.attr 'data-preset-size' @randomizeThangs presetType, presetSize - Backbone.Mediator.publish 'editor:random-terrain-generated', thangs: @thangs + Backbone.Mediator.publish 'editor:random-terrain-generated', thangs: @thangs, terrain: presets[presetType].terrainName @hide() randomizeThangs: (presetName, presetSize) -> diff --git a/app/views/editor/level/settings/SettingsTabView.coffee b/app/views/editor/level/settings/SettingsTabView.coffee index 10990c960..fc6e02d91 100644 --- a/app/views/editor/level/settings/SettingsTabView.coffee +++ b/app/views/editor/level/settings/SettingsTabView.coffee @@ -13,12 +13,13 @@ module.exports = class SettingsTabView extends CocoView # not thangs or scripts or the backend stuff editableSettings: [ 'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals', - 'type', 'showsGuide', 'banner', 'employerDescription' + 'type', 'terrain', 'showsGuide', 'banner', 'employerDescription' ] subscriptions: 'editor:level-loaded': 'onLevelLoaded' 'editor:thangs-edited': 'onThangsEdited' + 'editor:random-terrain-generated': 'onRandomTerrainGenerated' constructor: (options) -> super options @@ -47,6 +48,7 @@ module.exports = class SettingsTabView extends CocoView @settingsTreema = @$el.find('#settings-treema').treema treemaOptions @settingsTreema.build() @settingsTreema.open() + @lastTerrain = data.terrain getThangIDs: -> (t.id for t in @level.get('thangs') ? []) @@ -56,11 +58,17 @@ module.exports = class SettingsTabView extends CocoView for key in @editableSettings continue if @settingsTreema.data[key] is undefined @level.set key, @settingsTreema.data[key] + if (terrain = @settingsTreema.data.terrain) isnt @lastTerrain + @lastTerrain = terrain + Backbone.Mediator.publish 'editor:terrain-changed', terrain: terrain onThangsEdited: (e) -> # Update in-place so existing Treema nodes refer to the same array. @thangIDs?.splice(0, @thangIDs.length, @getThangIDs()...) - + + onRandomTerrainGenerated: (e) -> + @settingsTreema.set '/terrain', e.terrain + destroy: -> @settingsTreema?.destroy() super() diff --git a/app/views/editor/level/systems/SystemsTabView.coffee b/app/views/editor/level/systems/SystemsTabView.coffee index eff550260..78809330b 100644 --- a/app/views/editor/level/systems/SystemsTabView.coffee +++ b/app/views/editor/level/systems/SystemsTabView.coffee @@ -17,6 +17,7 @@ module.exports = class SystemsTabView extends CocoView 'editor:edit-level-system': 'editLevelSystem' 'editor:level-system-editing-ended': 'onLevelSystemEditingEnded' 'editor:level-loaded': 'onLevelLoaded' + 'editor:terrain-changed': 'onTerrainChanged' events: 'click #add-system-button': 'addLevelSystem' @@ -103,6 +104,20 @@ module.exports = class SystemsTabView extends CocoView @removeSubView @levelSystemEditView @levelSystemEditView = null + onTerrainChanged: (e) -> + defaultPathfinding = e.terrain in ['Dungeon', 'Indoor'] + return unless AI = @systemsTreema.get 'original=528110f30268d018e3000001' + return if AI.config?.findsPaths is defaultPathfinding + AI.config ?= {} + AI.config.findsPaths = defaultPathfinding + @systemsTreema.set 'original=528110f30268d018e3000001', AI + noty { + text: "AI System defaulted pathfinding to #{defaultPathfinding} for terrain #{e.terrain}." + layout: 'topCenter' + timeout: 5000 + type: 'information' + } + buildDefaultSystems: -> [ {original: '528112c00268d018e3000008', majorVersion: 0} # Event @@ -120,6 +135,9 @@ module.exports = class SystemsTabView extends CocoView {original: '528111b30268d018e3000004', majorVersion: 0} # Alliance {original: '528114e60268d018e300001a', majorVersion: 0} # UI {original: '528114040268d018e3000011', majorVersion: 0} # Physics + {original: '52ae4f02a4dcd4415200000b', majorVersion: 0} # Display + {original: '52e953e81b2028d102000004', majorVersion: 0} # Effect + {original: '52f1354370fb890000000005', majorVersion: 0} # Magic ] destroy: -> diff --git a/app/views/editor/thang/ThangTypeEditView.coffee b/app/views/editor/thang/ThangTypeEditView.coffee index 186304b2d..6d74534ab 100644 --- a/app/views/editor/thang/ThangTypeEditView.coffee +++ b/app/views/editor/thang/ThangTypeEditView.coffee @@ -393,6 +393,8 @@ module.exports = class ThangTypeEditView extends RootView if (kind = @treema.data.kind) isnt @lastKind @lastKind = kind Backbone.Mediator.publish 'editor:thang-type-kind-changed', kind: kind + if kind in ['Doodad', 'Floor', 'Wall'] and not @treema.data.terrains + @treema.set '/terrains', ['Grass', 'Dungeon', 'Indoor'] # So editors know to set them. onSelectNode: (e, selected) => selected = selected[0] From 523cbfd38e02f05bf421c72b984a19602ed1a170 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 2 Sep 2014 14:27:13 -0700 Subject: [PATCH 98/98] Fixed a typo in the achievements:new event schema. --- app/schemas/subscriptions/misc.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/schemas/subscriptions/misc.coffee b/app/schemas/subscriptions/misc.coffee index d279489ba..fbaa4b813 100644 --- a/app/schemas/subscriptions/misc.coffee +++ b/app/schemas/subscriptions/misc.coffee @@ -25,7 +25,7 @@ module.exports = viewClass: {type: 'function'} viewArgs: {type: 'array'} - 'achievements:new': c.object {required: 'earnedAchievements'}, + 'achievements:new': c.object {required: ['earnedAchievements']}, earnedAchievements: {type: 'object'} 'ladder:game-submitted': c.object {required: ['session', 'level']},