mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-25 00:28:31 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
ee07d53671
58 changed files with 837 additions and 249 deletions
|
@ -72,6 +72,12 @@ module.exports = class CocoRouter extends Backbone.Router
|
||||||
|
|
||||||
'github/*path': 'routeToServer'
|
'github/*path': 'routeToServer'
|
||||||
|
|
||||||
|
'i18n': go('i18n/I18NHomeView')
|
||||||
|
'i18n/thang/:handle': go('i18n/I18NEditThangTypeView')
|
||||||
|
'i18n/component/:handle': go('i18n/I18NEditComponentView')
|
||||||
|
'i18n/level/:handle': go('i18n/I18NEditLevelView')
|
||||||
|
'i18n/achievement/:handle': go('i18n/I18NEditAchievementView')
|
||||||
|
|
||||||
'legal': go('LegalView')
|
'legal': go('LegalView')
|
||||||
|
|
||||||
'multiplayer': go('MultiplayerView')
|
'multiplayer': go('MultiplayerView')
|
||||||
|
|
|
@ -175,3 +175,7 @@ prunePath = (delta, path) ->
|
||||||
prunePath delta[path[0]], path.slice(1) unless delta[path[0]] is undefined
|
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')
|
keys = (k for k in _.keys(delta[path[0]]) when k isnt '_t')
|
||||||
delete delta[path[0]] if keys.length is 0
|
delete delta[path[0]] if keys.length is 0
|
||||||
|
|
||||||
|
module.exports.DOC_SKIP_PATHS = [
|
||||||
|
'_id','version', 'commitMessage', 'parent', 'created',
|
||||||
|
'slug', 'index', '__v', 'patches', 'creator', 'js', 'watchers']
|
|
@ -28,7 +28,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
about: "Sobre Nosaltres"
|
about: "Sobre Nosaltres"
|
||||||
contact: "Contacta"
|
contact: "Contacta"
|
||||||
twitter_follow: "Segueix-nos"
|
twitter_follow: "Segueix-nos"
|
||||||
# teachers: "Teachers"
|
teachers: "Profesors"
|
||||||
|
|
||||||
modal:
|
modal:
|
||||||
close: "Tancar"
|
close: "Tancar"
|
||||||
|
@ -56,7 +56,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
account: "Conta" # Tooltip on account button from /play
|
account: "Conta" # Tooltip on account button from /play
|
||||||
settings: "Configuració" # Tooltip on settings button from /play
|
settings: "Configuració" # Tooltip on settings button from /play
|
||||||
next: "Següent" # Go from choose hero to choose inventory before playing a level
|
next: "Següent" # Go from choose hero to choose inventory before playing a level
|
||||||
# change_hero: "Change Hero" # Go back from choose inventory to choose hero
|
change_hero: "Canviar heroi" # Go back from choose inventory to choose hero
|
||||||
choose_inventory: "Equipar objectes"
|
choose_inventory: "Equipar objectes"
|
||||||
older_campaigns: "Campanyes antigues"
|
older_campaigns: "Campanyes antigues"
|
||||||
anonymous: "Jugador anònim"
|
anonymous: "Jugador anònim"
|
||||||
|
@ -132,7 +132,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
body: "Cos"
|
body: "Cos"
|
||||||
version: "Versió"
|
version: "Versió"
|
||||||
# commit_msg: "Commit Message"
|
# commit_msg: "Commit Message"
|
||||||
# version_history: "Version History"
|
version_history: "Historial de versions"
|
||||||
# version_history_for: "Version History for: "
|
# version_history_for: "Version History for: "
|
||||||
result: "Resultat"
|
result: "Resultat"
|
||||||
results: "Resultats"
|
results: "Resultats"
|
||||||
|
@ -148,8 +148,8 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# opponent: "Opponent"
|
# opponent: "Opponent"
|
||||||
# rank: "Rank"
|
# rank: "Rank"
|
||||||
score: "Puntuació"
|
score: "Puntuació"
|
||||||
# win: "Win"
|
win: "Guanyats"
|
||||||
# loss: "Loss"
|
loss: "Perduts"
|
||||||
# tie: "Tie"
|
# tie: "Tie"
|
||||||
easy: "Fàcil"
|
easy: "Fàcil"
|
||||||
medium: "Intermedi"
|
medium: "Intermedi"
|
||||||
|
@ -175,7 +175,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
play_level:
|
play_level:
|
||||||
done: "Fet"
|
done: "Fet"
|
||||||
home: "Inici"
|
home: "Inici"
|
||||||
# skip: "Skip"
|
skip: "Ometre"
|
||||||
game_menu: "Menu de joc"
|
game_menu: "Menu de joc"
|
||||||
guide: "Guia"
|
guide: "Guia"
|
||||||
# restart: "Restart"
|
# restart: "Restart"
|
||||||
|
@ -200,10 +200,10 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# victory_play_skip: "Skip Ahead"
|
# victory_play_skip: "Skip Ahead"
|
||||||
victory_play_next_level: "Jugar el següent nivell"
|
victory_play_next_level: "Jugar el següent nivell"
|
||||||
# victory_play_more_practice: "More Practice"
|
# victory_play_more_practice: "More Practice"
|
||||||
# victory_play_too_easy: "Too Easy"
|
victory_play_too_easy: "Massa fàcil"
|
||||||
# victory_play_just_right: "Just Right"
|
# victory_play_just_right: "Just Right"
|
||||||
# victory_play_too_hard: "Too Hard"
|
victory_play_too_hard: "Massa difícil"
|
||||||
# victory_saving_progress: "Saving Progress"
|
victory_saving_progress: "Desa progrés"
|
||||||
victory_go_home: "Tornar a l'inici" # Only in old-style levels.
|
victory_go_home: "Tornar a l'inici" # Only in old-style levels.
|
||||||
victory_review: "Diguens més!" # Only in old-style levels.
|
victory_review: "Diguens més!" # Only in old-style levels.
|
||||||
# victory_hour_of_code_done: "Are You Done?"
|
# victory_hour_of_code_done: "Are You Done?"
|
||||||
|
@ -302,7 +302,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# attack: "Damage" # Can also translate as "Attack"
|
# attack: "Damage" # Can also translate as "Attack"
|
||||||
health: "Salut"
|
health: "Salut"
|
||||||
speed: "Velocitat"
|
speed: "Velocitat"
|
||||||
# skills: "Skills"
|
skills: "Habilitats"
|
||||||
|
|
||||||
save_load:
|
save_load:
|
||||||
granularity_saved_games: "Desats"
|
granularity_saved_games: "Desats"
|
||||||
|
@ -482,7 +482,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# revert: "Revert"
|
# revert: "Revert"
|
||||||
# revert_models: "Revert Models"
|
# revert_models: "Revert Models"
|
||||||
# pick_a_terrain: "Pick A Terrain"
|
# pick_a_terrain: "Pick A Terrain"
|
||||||
# small: "Small"
|
small: "Petit"
|
||||||
# grassy: "Grassy"
|
# grassy: "Grassy"
|
||||||
# fork_title: "Fork New Version"
|
# fork_title: "Fork New Version"
|
||||||
# fork_creating: "Creating Fork..."
|
# fork_creating: "Creating Fork..."
|
||||||
|
@ -492,11 +492,11 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
live_chat: "Xat en directe"
|
live_chat: "Xat en directe"
|
||||||
# level_some_options: "Some Options?"
|
# level_some_options: "Some Options?"
|
||||||
# level_tab_thangs: "Thangs"
|
# level_tab_thangs: "Thangs"
|
||||||
# level_tab_scripts: "Scripts"
|
level_tab_scripts: "Scripts"
|
||||||
level_tab_settings: "Configuració"
|
level_tab_settings: "Configuració"
|
||||||
level_tab_components: "Components"
|
level_tab_components: "Components"
|
||||||
level_tab_systems: "Sistemes"
|
level_tab_systems: "Sistemes"
|
||||||
# level_tab_docs: "Documentation"
|
level_tab_docs: "Documentació"
|
||||||
# level_tab_thangs_title: "Current Thangs"
|
# level_tab_thangs_title: "Current Thangs"
|
||||||
level_tab_thangs_all: "Tot"
|
level_tab_thangs_all: "Tot"
|
||||||
# level_tab_thangs_conditions: "Starting Conditions"
|
# level_tab_thangs_conditions: "Starting Conditions"
|
||||||
|
@ -508,13 +508,13 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
level_component_tab_title: "Components actuals"
|
level_component_tab_title: "Components actuals"
|
||||||
# level_component_btn_new: "Create New Component"
|
# level_component_btn_new: "Create New Component"
|
||||||
level_systems_tab_title: "Sistemes actuals"
|
level_systems_tab_title: "Sistemes actuals"
|
||||||
# level_systems_btn_new: "Create New System"
|
level_systems_btn_new: "Crea un nou sistema"
|
||||||
level_systems_btn_add: "Afegir sistema"
|
level_systems_btn_add: "Afegir sistema"
|
||||||
# level_components_title: "Back to All Thangs"
|
# level_components_title: "Back to All Thangs"
|
||||||
# level_components_type: "Type"
|
# level_components_type: "Type"
|
||||||
# level_component_edit_title: "Edit Component"
|
# level_component_edit_title: "Edit Component"
|
||||||
# level_component_config_schema: "Config Schema"
|
# level_component_config_schema: "Config Schema"
|
||||||
# level_component_settings: "Settings"
|
level_component_settings: "Configuració"
|
||||||
# level_system_edit_title: "Edit System"
|
# level_system_edit_title: "Edit System"
|
||||||
create_system_title: "Crea un nou sistema"
|
create_system_title: "Crea un nou sistema"
|
||||||
# new_component_title: "Create New Component"
|
# new_component_title: "Create New Component"
|
||||||
|
@ -537,9 +537,9 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# achievement_query_goals: "Key achievement off of level goals"
|
# achievement_query_goals: "Key achievement off of level goals"
|
||||||
# level_completion: "Level Completion"
|
# level_completion: "Level Completion"
|
||||||
|
|
||||||
# article:
|
article:
|
||||||
# edit_btn_preview: "Preview"
|
edit_btn_preview: "Vista previa"
|
||||||
# edit_article_title: "Edit Article"
|
edit_article_title: "Editar l'article"
|
||||||
|
|
||||||
contribute:
|
contribute:
|
||||||
# page_title: "Contributing"
|
# page_title: "Contributing"
|
||||||
|
@ -783,8 +783,8 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# merge_conflict_with: "MERGE CONFLICT WITH"
|
# merge_conflict_with: "MERGE CONFLICT WITH"
|
||||||
no_changes: "Sense canvis"
|
no_changes: "Sense canvis"
|
||||||
|
|
||||||
# guide:
|
guide:
|
||||||
# temp: "Temp"
|
temp: "Temp"
|
||||||
|
|
||||||
multiplayer:
|
multiplayer:
|
||||||
multiplayer_title: "Configuració multijugador" # We'll be changing this around significantly soon. Until then, it's not important to translate.
|
multiplayer_title: "Configuració multijugador" # We'll be changing this around significantly soon. Until then, it's not important to translate.
|
||||||
|
@ -808,13 +808,13 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# practices_description: "These are our promises to you, the player, in slightly less legalese."
|
# practices_description: "These are our promises to you, the player, in slightly less legalese."
|
||||||
privacy_title: "Privacitat"
|
privacy_title: "Privacitat"
|
||||||
# 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."
|
# 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_title: "Seguretat"
|
||||||
# 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."
|
# 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_title: "Email"
|
||||||
# email_description_prefix: "We will not inundate you with spam. Through"
|
# email_description_prefix: "We will not inundate you with spam. Through"
|
||||||
# email_settings_url: "your email settings"
|
# 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."
|
# 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_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:"
|
# 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_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."
|
# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizard–not just in the game, but also in real life."
|
||||||
|
@ -900,7 +900,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
profile_for_prefix: "Perfil de "
|
profile_for_prefix: "Perfil de "
|
||||||
profile_for_suffix: ""
|
profile_for_suffix: ""
|
||||||
featured: "Destacat"
|
featured: "Destacat"
|
||||||
# not_featured: "Not Featured"
|
not_featured: "Sense destacar"
|
||||||
looking_for: "Buscant:"
|
looking_for: "Buscant:"
|
||||||
last_updated: "Ultima actualització:"
|
last_updated: "Ultima actualització:"
|
||||||
contact: "Contacta"
|
contact: "Contacta"
|
||||||
|
@ -1059,7 +1059,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# av_entities_user_code_problems_list_url: "User Code Problems List"
|
# av_entities_user_code_problems_list_url: "User Code Problems List"
|
||||||
av_other_sub_title: "Altres"
|
av_other_sub_title: "Altres"
|
||||||
# av_other_debug_base_url: "Base (for debugging base.jade)"
|
# av_other_debug_base_url: "Base (for debugging base.jade)"
|
||||||
# u_title: "User List"
|
u_title: "Llista d'usuaris"
|
||||||
# ucp_title: "User Code Problems"
|
# ucp_title: "User Code Problems"
|
||||||
# lg_title: "Latest Games"
|
# lg_title: "Latest Games"
|
||||||
# clas: "CLAs"
|
# clas: "CLAs"
|
||||||
|
|
|
@ -536,6 +536,7 @@
|
||||||
achievement_query_misc: "Key achievement off of miscellanea"
|
achievement_query_misc: "Key achievement off of miscellanea"
|
||||||
achievement_query_goals: "Key achievement off of level goals"
|
achievement_query_goals: "Key achievement off of level goals"
|
||||||
level_completion: "Level Completion"
|
level_completion: "Level Completion"
|
||||||
|
pop_i18n: "Populate I18N"
|
||||||
|
|
||||||
article:
|
article:
|
||||||
edit_btn_preview: "Preview"
|
edit_btn_preview: "Preview"
|
||||||
|
|
|
@ -13,6 +13,10 @@ module.exports = class Achievement extends CocoModel
|
||||||
func = @get('function', true)
|
func = @get('function', true)
|
||||||
return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators
|
return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators
|
||||||
|
|
||||||
|
save: ->
|
||||||
|
@populateI18N()
|
||||||
|
super(arguments...)
|
||||||
|
|
||||||
@styleMapping:
|
@styleMapping:
|
||||||
1: 'achievement-wood'
|
1: 'achievement-wood'
|
||||||
2: 'achievement-stone'
|
2: 'achievement-stone'
|
||||||
|
|
|
@ -233,7 +233,7 @@ class CocoModel extends Backbone.Model
|
||||||
|
|
||||||
getDelta: ->
|
getDelta: ->
|
||||||
differ = deltasLib.makeJSONDiffer()
|
differ = deltasLib.makeJSONDiffer()
|
||||||
differ.diff @_revertAttributes, @attributes
|
differ.diff(_.omit(@_revertAttributes, deltasLib.DOC_SKIP_PATHS), _.omit(@attributes, deltasLib.DOC_SKIP_PATHS))
|
||||||
|
|
||||||
getDeltaWith: (otherModel) ->
|
getDeltaWith: (otherModel) ->
|
||||||
differ = deltasLib.makeJSONDiffer()
|
differ = deltasLib.makeJSONDiffer()
|
||||||
|
@ -272,9 +272,11 @@ class CocoModel extends Backbone.Model
|
||||||
sum = 0
|
sum = 0
|
||||||
data ?= $.extend true, {}, @attributes
|
data ?= $.extend true, {}, @attributes
|
||||||
schema ?= @schema() or {}
|
schema ?= @schema() or {}
|
||||||
|
addedI18N = false
|
||||||
if schema.properties?.i18n and _.isPlainObject(data) and not data.i18n?
|
if schema.properties?.i18n and _.isPlainObject(data) and not data.i18n?
|
||||||
data.i18n = {'-':'-'} # mongoose doesn't work with empty objects
|
data.i18n = {'-':{'-':'-'}} # mongoose doesn't work with empty objects
|
||||||
sum += 1
|
sum += 1
|
||||||
|
addedI18N = true
|
||||||
|
|
||||||
if _.isPlainObject data
|
if _.isPlainObject data
|
||||||
for key, value of data
|
for key, value of data
|
||||||
|
@ -287,6 +289,7 @@ class CocoModel extends Backbone.Model
|
||||||
if schema.items and _.isArray data
|
if schema.items and _.isArray data
|
||||||
sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data
|
sum += @populateI18N(value, schema.items, path+'/'+index) for value, index in data
|
||||||
|
|
||||||
|
@set('i18n', data.i18n) if addedI18N and not path # need special case for root i18n
|
||||||
@updateI18NCoverage()
|
@updateI18NCoverage()
|
||||||
sum
|
sum
|
||||||
|
|
||||||
|
@ -343,10 +346,8 @@ class CocoModel extends Backbone.Model
|
||||||
|
|
||||||
updateI18NCoverage: ->
|
updateI18NCoverage: ->
|
||||||
i18nObjects = @findI18NObjects()
|
i18nObjects = @findI18NObjects()
|
||||||
console.log 'i18n objects', i18nObjects
|
return unless i18nObjects.length
|
||||||
langCodeArrays = (_.keys(i18n) for i18n in i18nObjects)
|
langCodeArrays = (_.keys(i18n) for i18n in i18nObjects)
|
||||||
console.log 'lang code arrays', langCodeArrays
|
|
||||||
window.codes = langCodeArrays
|
|
||||||
@set('i18nCoverage', _.intersection(langCodeArrays...))
|
@set('i18nCoverage', _.intersection(langCodeArrays...))
|
||||||
|
|
||||||
findI18NObjects: (data, results) ->
|
findI18NObjects: (data, results) ->
|
||||||
|
|
|
@ -12,6 +12,7 @@ module.exports = class LevelComponent extends CocoModel
|
||||||
@LandID: '524b7aff7fc0f6d519000006'
|
@LandID: '524b7aff7fc0f6d519000006'
|
||||||
@CollidesID: '524b7b857fc0f6d519000012'
|
@CollidesID: '524b7b857fc0f6d519000012'
|
||||||
@PlansID: '524b7b517fc0f6d51900000d'
|
@PlansID: '524b7b517fc0f6d51900000d'
|
||||||
|
@ProgrammableID: '524b7b5a7fc0f6d51900000e'
|
||||||
urlRoot: '/db/level.component'
|
urlRoot: '/db/level.component'
|
||||||
|
|
||||||
set: (key, val, options) ->
|
set: (key, val, options) ->
|
||||||
|
|
|
@ -78,10 +78,7 @@ _.extend AchievementSchema.properties,
|
||||||
default: {kind: 'linear', parameters: {}}
|
default: {kind: 'linear', parameters: {}}
|
||||||
required: ['kind', 'parameters']
|
required: ['kind', 'parameters']
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
i18n: c.object
|
i18n: {type: 'object', format: 'i18n', props: ['name', 'description'], description: 'Help translate this achievement'}
|
||||||
format: 'i18n'
|
|
||||||
props: ['name', 'description']
|
|
||||||
description: 'Help translate this achievement'
|
|
||||||
rewards: c.RewardSchema 'awarded by this achievement'
|
rewards: c.RewardSchema 'awarded by this achievement'
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,5 +90,6 @@ _.extend AchievementSchema, # Let's have these on the bottom
|
||||||
AchievementSchema.definitions = {}
|
AchievementSchema.definitions = {}
|
||||||
AchievementSchema.definitions['mongoQueryOperator'] = MongoQueryOperatorSchema
|
AchievementSchema.definitions['mongoQueryOperator'] = MongoQueryOperatorSchema
|
||||||
AchievementSchema.definitions['mongoFindQuery'] = MongoFindQuerySchema
|
AchievementSchema.definitions['mongoFindQuery'] = MongoFindQuerySchema
|
||||||
|
c.extendTranslationCoverageProperties AchievementSchema
|
||||||
|
|
||||||
module.exports = AchievementSchema
|
module.exports = AchievementSchema
|
||||||
|
|
|
@ -23,6 +23,12 @@ PropertyDocumentationSchema = c.object {
|
||||||
required: ['name', 'type', 'description']
|
required: ['name', 'type', 'description']
|
||||||
},
|
},
|
||||||
name: {type: 'string', title: 'Name', description: 'Name of the property.'}
|
name: {type: 'string', title: 'Name', description: 'Name of the property.'}
|
||||||
|
i18n: { type: 'object', format: 'i18n', props: ['description', 'context'], description: 'Help translate this property'}
|
||||||
|
context: {
|
||||||
|
type: 'object'
|
||||||
|
title: 'Example template context'
|
||||||
|
additionalProperties: { type: 'string' }
|
||||||
|
}
|
||||||
codeLanguages: c.array {title: 'Specific Code Languages', description: 'If present, then only the languages specified will show this documentation. Leave unset for language-independent documentation.', format: 'code-languages-array'}, c.shortString(title: 'Code Language', description: 'A specific code language to show this documentation for.', format: 'code-language')
|
codeLanguages: c.array {title: 'Specific Code Languages', description: 'If present, then only the languages specified will show this documentation. Leave unset for language-independent documentation.', format: 'code-languages-array'}, c.shortString(title: 'Code Language', description: 'A specific code language to show this documentation for.', format: 'code-language')
|
||||||
# not actual JS types, just whatever they describe...
|
# not actual JS types, just whatever they describe...
|
||||||
type: c.shortString(title: 'Type', description: 'Intended type of the property.')
|
type: c.shortString(title: 'Type', description: 'Intended type of the property.')
|
||||||
|
@ -84,6 +90,7 @@ PropertyDocumentationSchema = c.object {
|
||||||
}
|
}
|
||||||
{title: 'Description', type: 'string', description: 'Description of the return value.', maxLength: 1000}
|
{title: 'Description', type: 'string', description: 'Description of the return value.', maxLength: 1000}
|
||||||
]
|
]
|
||||||
|
i18n: { type: 'object', format: 'i18n', props: ['description'], description: 'Help translate this return value'}
|
||||||
|
|
||||||
DependencySchema = c.object {
|
DependencySchema = c.object {
|
||||||
title: 'Component Dependency'
|
title: 'Component Dependency'
|
||||||
|
@ -155,5 +162,6 @@ c.extendSearchableProperties LevelComponentSchema
|
||||||
c.extendVersionedProperties LevelComponentSchema, 'level.component'
|
c.extendVersionedProperties LevelComponentSchema, 'level.component'
|
||||||
c.extendPermissionsProperties LevelComponentSchema, 'level.component'
|
c.extendPermissionsProperties LevelComponentSchema, 'level.component'
|
||||||
c.extendPatchableProperties LevelComponentSchema
|
c.extendPatchableProperties LevelComponentSchema
|
||||||
|
c.extendTranslationCoverageProperties LevelComponentSchema
|
||||||
|
|
||||||
module.exports = LevelComponentSchema
|
module.exports = LevelComponentSchema
|
||||||
|
|
|
@ -116,6 +116,7 @@ _.extend ThangTypeSchema.properties,
|
||||||
rotationType: {title: 'Rotation', type: 'string', enum: ['isometric', 'fixed', 'free']}
|
rotationType: {title: 'Rotation', type: 'string', enum: ['isometric', 'fixed', 'free']}
|
||||||
matchWorldDimensions: {title: 'Match World Dimensions', type: 'boolean'}
|
matchWorldDimensions: {title: 'Match World Dimensions', type: 'boolean'}
|
||||||
shadow: {title: 'Shadow Diameter', type: 'number', format: 'meters', description: 'Shadow diameter in meters'}
|
shadow: {title: 'Shadow Diameter', type: 'number', format: 'meters', description: 'Shadow diameter in meters'}
|
||||||
|
description: { type:'string', format: 'markdown', title: 'Description' }
|
||||||
layerPriority:
|
layerPriority:
|
||||||
title: 'Layer Priority'
|
title: 'Layer Priority'
|
||||||
type: 'integer'
|
type: 'integer'
|
||||||
|
@ -144,6 +145,7 @@ _.extend ThangTypeSchema.properties,
|
||||||
type: 'number'
|
type: 'number'
|
||||||
description: 'Snap to this many meters in the y-direction.'
|
description: 'Snap to this many meters in the y-direction.'
|
||||||
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
|
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
|
||||||
|
i18n: {type: 'object', format: 'i18n', props: ['name', 'description'], description: 'Help translate this ThangType\'s name and description.'}
|
||||||
|
|
||||||
ThangTypeSchema.required = []
|
ThangTypeSchema.required = []
|
||||||
|
|
||||||
|
@ -158,5 +160,6 @@ c.extendBasicProperties ThangTypeSchema, 'thang.type'
|
||||||
c.extendSearchableProperties ThangTypeSchema
|
c.extendSearchableProperties ThangTypeSchema
|
||||||
c.extendVersionedProperties ThangTypeSchema, 'thang.type'
|
c.extendVersionedProperties ThangTypeSchema, 'thang.type'
|
||||||
c.extendPatchableProperties ThangTypeSchema
|
c.extendPatchableProperties ThangTypeSchema
|
||||||
|
c.extendTranslationCoverageProperties ThangTypeSchema
|
||||||
|
|
||||||
module.exports = ThangTypeSchema
|
module.exports = ThangTypeSchema
|
||||||
|
|
|
@ -166,6 +166,7 @@ me.FunctionArgumentSchema = me.object {
|
||||||
required: ['name', 'type', 'example', 'description']
|
required: ['name', 'type', 'example', 'description']
|
||||||
},
|
},
|
||||||
name: {type: 'string', pattern: me.identifierPattern, title: 'Name', description: 'Name of the function argument.'}
|
name: {type: 'string', pattern: me.identifierPattern, title: 'Name', description: 'Name of the function argument.'}
|
||||||
|
i18n: { type: 'object', format: 'i18n', props: ['description'], description: 'Help translate this argument'}
|
||||||
# not actual JS types, just whatever they describe...
|
# not actual JS types, just whatever they describe...
|
||||||
type: me.shortString(title: 'Type', description: 'Intended type of the argument.')
|
type: me.shortString(title: 'Type', description: 'Intended type of the argument.')
|
||||||
example:
|
example:
|
||||||
|
|
|
@ -1,3 +1,27 @@
|
||||||
#guide-view
|
#guide-view, #settings-treema .treema-markdown
|
||||||
h3
|
.nav-tabs
|
||||||
text-decoration: underline
|
height: 41px
|
||||||
|
|
||||||
|
.tab-content
|
||||||
|
padding-top: 20px
|
||||||
|
margin-bottom: 50px
|
||||||
|
|
||||||
|
li:not(.active) a[data-toggle="tab"]
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
img
|
||||||
|
display: block
|
||||||
|
margin: 0 auto
|
||||||
|
|
||||||
|
img + em
|
||||||
|
display: block
|
||||||
|
margin: 0 auto
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
hr
|
||||||
|
border-color: #5c5c5c
|
||||||
|
width: 80%
|
||||||
|
|
||||||
|
table
|
||||||
|
width: 80%
|
||||||
|
margin: 20px 10%
|
12
app/styles/i18n/i18n-edit-model-view.sass
Normal file
12
app/styles/i18n/i18n-edit-model-view.sass
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.i18n-edit-model-view
|
||||||
|
#patch-submit
|
||||||
|
margin-top: 5px
|
||||||
|
|
||||||
|
td
|
||||||
|
width: 40%
|
||||||
|
|
||||||
|
.outer-content
|
||||||
|
padding: 10px
|
||||||
|
|
||||||
|
select
|
||||||
|
margin-bottom: 10px
|
|
@ -1,25 +0,0 @@
|
||||||
#docs-modal .modal-dialog, #settings-treema .treema-markdown
|
|
||||||
width: 800px
|
|
||||||
|
|
||||||
.tab-content
|
|
||||||
padding-top: 20px
|
|
||||||
|
|
||||||
li:not(.active) a[data-toggle="tab"]
|
|
||||||
cursor: pointer
|
|
||||||
|
|
||||||
img
|
|
||||||
display: block
|
|
||||||
margin: 0 auto
|
|
||||||
|
|
||||||
img + em
|
|
||||||
display: block
|
|
||||||
margin: 0 auto
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
hr
|
|
||||||
border-color: #5c5c5c
|
|
||||||
width: 80%
|
|
||||||
|
|
||||||
table
|
|
||||||
width: 80%
|
|
||||||
margin: 20px 10%
|
|
|
@ -34,6 +34,8 @@ nav.navbar.navbar-default(role='navigation')
|
||||||
if !me.get('anonymous')
|
if !me.get('anonymous')
|
||||||
li#create-new-component-button
|
li#create-new-component-button
|
||||||
a(data-i18n="editor.level_component_b_new") Create New Component
|
a(data-i18n="editor.level_component_b_new") Create New Component
|
||||||
|
li
|
||||||
|
a(data-i18n="editor.pop_i18n")#pop-component-i18n-button Populate i18n
|
||||||
|
|
||||||
li.divider
|
li.divider
|
||||||
li.dropdown-header Info
|
li.dropdown-header Info
|
||||||
|
|
|
@ -56,6 +56,8 @@ block header
|
||||||
a(data-i18n="common.fork")#fork-start-button Fork
|
a(data-i18n="common.fork")#fork-start-button Fork
|
||||||
li(class=anonymous ? "disabled": "")
|
li(class=anonymous ? "disabled": "")
|
||||||
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert
|
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert
|
||||||
|
li(class=anonymous ? "disabled": "")
|
||||||
|
a(data-i18n="editor.pop_i18n")#pop-level-i18n-button Populate i18n
|
||||||
li.divider
|
li.divider
|
||||||
li.dropdown-header Info
|
li.dropdown-header Info
|
||||||
li#history-button
|
li#history-button
|
||||||
|
|
|
@ -6,13 +6,15 @@ block modal-body-content
|
||||||
.button.close(type="button", data-dismiss="modal", aria-hidden="true") ×
|
.button.close(type="button", data-dismiss="modal", aria-hidden="true") ×
|
||||||
.tabbable.tabs-left
|
.tabbable.tabs-left
|
||||||
- var submenus = ["inventory", "choose-hero", "save-load", "options", "guide", "multiplayer"]
|
- var submenus = ["inventory", "choose-hero", "save-load", "options", "guide", "multiplayer"]
|
||||||
- if (!showDevBits) { // Not done yet.
|
- if (!showsGuide) {
|
||||||
- submenus.splice(4, 1);
|
- submenus.splice(4, 1);
|
||||||
|
- }
|
||||||
|
- if (!showDevBits) { // Not done yet.
|
||||||
- submenus.splice(2, 1);
|
- submenus.splice(2, 1);
|
||||||
- }
|
- }
|
||||||
- if (!showInventory)
|
- if (!showInventory)
|
||||||
- submenus.splice(0, 1);
|
- submenus.splice(0, 1);
|
||||||
ul.nav.nav-tabs
|
ul.nav.nav-tabs#game-menu-nav
|
||||||
for submenu, index in submenus
|
for submenu, index in submenus
|
||||||
li(class=index ? "" : "active")
|
li(class=index ? "" : "active")
|
||||||
a(href='#' + submenu + '-view', data-toggle='tab')
|
a(href='#' + submenu + '-view', data-toggle='tab')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
h3 Guide, and stuff
|
ul.nav.nav-tabs
|
||||||
|
for doc in docs
|
||||||
div(data-i18n="guide.temp") Temp
|
li
|
||||||
|
a(data-target="#docs_tab_#{doc.slug}", data-toggle="tab") #{doc.name}
|
||||||
p ... and more stuff
|
div.tab-content
|
||||||
|
for doc in docs
|
||||||
p Just put the existing guide in there pretty much as it is. Also add tab for keyboard shortcuts.
|
div.tab-pane(id="docs_tab_#{doc.slug}")!= doc.html
|
||||||
|
|
71
app/templates/i18n/i18n-edit-model-view.jade
Normal file
71
app/templates/i18n/i18n-edit-model-view.jade
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
extends /templates/base
|
||||||
|
|
||||||
|
block header
|
||||||
|
if model.loading
|
||||||
|
nav.navbar.navbar-default(role='navigation')
|
||||||
|
.container-fluid
|
||||||
|
ul.nav.navbar-nav
|
||||||
|
li
|
||||||
|
a(href="/i18n")
|
||||||
|
span.glyphicon-home.glyphicon
|
||||||
|
|
||||||
|
else
|
||||||
|
nav.navbar.navbar-default(role='navigation')
|
||||||
|
ul.nav.navbar-nav
|
||||||
|
li
|
||||||
|
a(href="/i18n")
|
||||||
|
span.glyphicon-home.glyphicon
|
||||||
|
|
||||||
|
.navbar-header
|
||||||
|
span.navbar-brand #{model.get('name')}
|
||||||
|
|
||||||
|
ul.nav.navbar-nav.navbar-right
|
||||||
|
li
|
||||||
|
button.btn.btn-info.btn-sm.pull-right#patch-submit(disabled=model.hasLocalChanges() ? null : 'disabled', value=model.id) Submit Changes
|
||||||
|
|
||||||
|
li.dropdown
|
||||||
|
|
||||||
|
a(data-toggle='dropdown')
|
||||||
|
span.glyphicon-chevron-down.glyphicon
|
||||||
|
|
||||||
|
ul.dropdown-menu
|
||||||
|
li.dropdown-header Actions
|
||||||
|
|
||||||
|
li(class=anonymous ? "disabled": "")
|
||||||
|
a(data-toggle="coco-modal", data-target="modal/RevertModal", data-i18n="editor.revert")#revert-button Revert
|
||||||
|
|
||||||
|
li.divider
|
||||||
|
li.dropdown-header Info
|
||||||
|
li#history-button
|
||||||
|
a(href='#', data-i18n="general.version_history") Version History
|
||||||
|
li.divider
|
||||||
|
li.dropdown-header Help
|
||||||
|
li
|
||||||
|
a(href='https://github.com/codecombat/codecombat/wiki', data-i18n="editor.wiki", target="_blank") Wiki
|
||||||
|
li
|
||||||
|
a(href='http://www.hipchat.com/g3plnOKqa', data-i18n="editor.live_chat", target="_blank") Live Chat
|
||||||
|
li
|
||||||
|
a(href='http://discourse.codecombat.com/category/diplomat', data-i18n="nav.forum", target="_blank") Forum
|
||||||
|
li
|
||||||
|
a(data-toggle="coco-modal", data-target="modal/ContactModal", data-i18n="nav.contact") Email
|
||||||
|
|
||||||
|
block outer_content
|
||||||
|
.outer-content
|
||||||
|
|
||||||
|
select.form-control#language-select
|
||||||
|
|
||||||
|
table.table
|
||||||
|
for row in translationList
|
||||||
|
tr(data-format=row.format || '')
|
||||||
|
th= row.title
|
||||||
|
td.english-value-row
|
||||||
|
div= row.enValue
|
||||||
|
td.to-value-row
|
||||||
|
if row.format === 'markdown'
|
||||||
|
div(data-index=row.index.toString())= row.toValue
|
||||||
|
else
|
||||||
|
input.input-sm.form-control.translation-input(data-index=row.index.toString(), value=row.toValue)
|
||||||
|
|
||||||
|
div#error-view
|
||||||
|
.clearfix
|
||||||
|
block footer
|
23
app/templates/i18n/i18n-home-view.jade
Normal file
23
app/templates/i18n/i18n-home-view.jade
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
extends /templates/base
|
||||||
|
|
||||||
|
block content
|
||||||
|
.progress
|
||||||
|
.progress-bar.progress-bar-info(role="progressbar" aria-valuenow=progress aria-valuemin="0" aria-valuemax="100" style="width: "+progress+"%")= progress+"%"
|
||||||
|
|
||||||
|
table.table.table-condensed
|
||||||
|
tr
|
||||||
|
th
|
||||||
|
select#language-select.form-control.input-sm
|
||||||
|
|
||||||
|
th Type
|
||||||
|
th Specifically Covered
|
||||||
|
th Generally Covered
|
||||||
|
|
||||||
|
for model in collection.models
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
a(href=model.i18nURLBase+model.get('slug'))= model.get('name')
|
||||||
|
td= model.constructor.className
|
||||||
|
td(class=model.specificallyCovered ? 'success' : 'danger')= model.specificallyCovered ? 'Yes' : 'No'
|
||||||
|
td(class=model.generallyCovered ? 'success' : 'danger')= model.generallyCovered ? 'Yes' : 'No'
|
||||||
|
|
|
@ -21,9 +21,6 @@ h4.title
|
||||||
|
|
||||||
button.btn.btn-xs.btn-inverse.banner#game-menu-button(title="Show game menu", data-i18n="play_level.game_menu") Game Menu
|
button.btn.btn-xs.btn-inverse.banner#game-menu-button(title="Show game menu", data-i18n="play_level.game_menu") Game Menu
|
||||||
|
|
||||||
if showsGuide
|
|
||||||
button.btn.btn-xs.btn-success.banner#docs-button(title="Show level instructions", data-i18n="play_level.guide") Guide
|
|
||||||
|
|
||||||
if spectateGame
|
if spectateGame
|
||||||
button.btn.btn-xs.btn-inverse.banner#next-game-button(title="Next Game", data-i18n="play_level.next-game") Next game!
|
button.btn.btn-xs.btn-inverse.banner#next-game-button(title="Next Game", data-i18n="play_level.next-game") Next game!
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
extends /templates/modal/modal_base
|
|
||||||
|
|
||||||
block modal-header-content
|
|
||||||
h3(data-i18n="play_level.guide_title") Guide
|
|
||||||
|
|
||||||
block modal-body-content
|
|
||||||
ul.nav.nav-tabs
|
|
||||||
for doc in docs
|
|
||||||
li
|
|
||||||
a(data-target="#docs_tab_#{doc.slug}", data-toggle="tab") #{doc.name}
|
|
||||||
div.tab-content
|
|
||||||
for doc in docs
|
|
||||||
div.tab-pane(id="docs_tab_#{doc.slug}")!= doc.html
|
|
||||||
|
|
||||||
block modal-footer-content
|
|
||||||
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close
|
|
|
@ -1 +1 @@
|
||||||
span.doc-title= doc.title
|
span.doc-title(data-property-name=doc.name)= doc.title
|
|
@ -267,6 +267,11 @@ class InternationalizationNode extends TreemaNode.nodeMap.object
|
||||||
res = (r for r in res when r[0] isnt '-')
|
res = (r for r in res when r[0] isnt '-')
|
||||||
res
|
res
|
||||||
|
|
||||||
|
populateData: ->
|
||||||
|
super()
|
||||||
|
if Object.keys(@data).length is 0
|
||||||
|
@data['-'] = {'-':'-'} # also to get around mongoose bug
|
||||||
|
|
||||||
getChildSchema: (key) ->
|
getChildSchema: (key) ->
|
||||||
#construct the child schema here
|
#construct the child schema here
|
||||||
|
|
||||||
|
@ -281,7 +286,12 @@ class InternationalizationNode extends TreemaNode.nodeMap.object
|
||||||
@workingSchema.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 @workingSchema.props
|
for i18nProperty in @workingSchema.props
|
||||||
i18nChildSchema.properties[i18nProperty] = @parent.schema.properties[i18nProperty]
|
parentSchemaProperties = @parent.schema.properties ? {}
|
||||||
|
for extraSchemas in [@parent.schema.oneOf, @parent.schema.anyOf]
|
||||||
|
for extraSchema in extraSchemas ? []
|
||||||
|
for prop, schema of extraSchema?.properties ? {}
|
||||||
|
parentSchemaProperties[prop] ?= schema
|
||||||
|
i18nChildSchema.properties[i18nProperty] = parentSchemaProperties[i18nProperty]
|
||||||
return i18nChildSchema
|
return i18nChildSchema
|
||||||
#this must be filled out in order for the i18n node to work
|
#this must be filled out in order for the i18n node to work
|
||||||
|
|
||||||
|
|
|
@ -93,14 +93,14 @@ module.exports = class DeltaView extends CocoView
|
||||||
treemaOptions = { schema: deltaData.schema or {}, readOnly: true }
|
treemaOptions = { schema: deltaData.schema or {}, readOnly: true }
|
||||||
|
|
||||||
if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
|
if _.isObject(deltaData.left) and leftEl = deltaEl.find('.old-value')
|
||||||
options = _.defaults {data: deltaData.left}, treemaOptions
|
options = _.defaults {data: _.merge({}, deltaData.left)}, treemaOptions
|
||||||
try
|
try
|
||||||
TreemaNode.make(leftEl, options).build()
|
TreemaNode.make(leftEl, options).build()
|
||||||
catch error
|
catch error
|
||||||
console.error "Couldn't show left details Treema for", deltaData.left, treemaOptions
|
console.error "Couldn't show left details Treema for", deltaData.left, treemaOptions
|
||||||
|
|
||||||
if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
|
if _.isObject(deltaData.right) and rightEl = deltaEl.find('.new-value')
|
||||||
options = _.defaults {data: deltaData.right}, treemaOptions
|
options = _.defaults {data: _.merge({}, deltaData.right)}, treemaOptions
|
||||||
try
|
try
|
||||||
TreemaNode.make(rightEl, options).build()
|
TreemaNode.make(rightEl, options).build()
|
||||||
catch error
|
catch error
|
||||||
|
|
|
@ -2,15 +2,13 @@ ModalView = require 'views/kinds/ModalView'
|
||||||
template = require 'templates/editor/patch_modal'
|
template = require 'templates/editor/patch_modal'
|
||||||
DeltaView = require 'views/editor/DeltaView'
|
DeltaView = require 'views/editor/DeltaView'
|
||||||
auth = require 'lib/auth'
|
auth = require 'lib/auth'
|
||||||
|
deltasLib = require 'lib/deltas'
|
||||||
|
|
||||||
module.exports = class PatchModal extends ModalView
|
module.exports = class PatchModal extends ModalView
|
||||||
id: 'patch-modal'
|
id: 'patch-modal'
|
||||||
template: template
|
template: template
|
||||||
plain: true
|
plain: true
|
||||||
modalWidthPercent: 60
|
modalWidthPercent: 60
|
||||||
@DOC_SKIP_PATHS = [
|
|
||||||
'_id','version', 'commitMessage', 'parent', 'created',
|
|
||||||
'slug', 'index', '__v', 'patches', 'creator', 'js', 'watchers']
|
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #withdraw-button': 'withdrawPatch'
|
'click #withdraw-button': 'withdrawPatch'
|
||||||
|
@ -54,7 +52,7 @@ module.exports = class PatchModal extends ModalView
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
return super() unless @supermodel.finished() and @deltaWorked
|
return super() unless @supermodel.finished() and @deltaWorked
|
||||||
@deltaView = new DeltaView({model:@pendingModel, headModel:@headModel, skipPaths: PatchModal.DOC_SKIP_PATHS})
|
@deltaView = new DeltaView({model:@pendingModel, headModel:@headModel, skipPaths: deltasLib.DOC_SKIP_PATHS})
|
||||||
changeEl = @$el.find('.changes-stub')
|
changeEl = @$el.find('.changes-stub')
|
||||||
@insertSubView(@deltaView, changeEl)
|
@insertSubView(@deltaView, changeEl)
|
||||||
super()
|
super()
|
||||||
|
|
|
@ -43,7 +43,7 @@ module.exports = class LevelEditView extends RootView
|
||||||
'click #components-tab': -> @subviews.editor_level_components_tab_view.refreshLevelThangsTreema @level.get('thangs')
|
'click #components-tab': -> @subviews.editor_level_components_tab_view.refreshLevelThangsTreema @level.get('thangs')
|
||||||
'click #level-patch-button': 'startPatchingLevel'
|
'click #level-patch-button': 'startPatchingLevel'
|
||||||
'click #level-watch-button': 'toggleWatchLevel'
|
'click #level-watch-button': 'toggleWatchLevel'
|
||||||
'click #pop-level-i18n-button': -> @level.populateI18N()
|
'click #pop-level-i18n-button': 'onPopulateI18N'
|
||||||
'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab'
|
'click a[href="#editor-level-documentation"]': 'onClickDocumentationTab'
|
||||||
'mouseup .nav-tabs > li a': 'toggleTab'
|
'mouseup .nav-tabs > li a': 'toggleTab'
|
||||||
|
|
||||||
|
@ -164,6 +164,11 @@ module.exports = class LevelEditView extends RootView
|
||||||
@level.watch(button.find('.watch').is(':visible'))
|
@level.watch(button.find('.watch').is(':visible'))
|
||||||
button.find('> span').toggleClass('secret')
|
button.find('> span').toggleClass('secret')
|
||||||
|
|
||||||
|
onPopulateI18N: ->
|
||||||
|
@level.populateI18N()
|
||||||
|
f = -> document.location.reload()
|
||||||
|
setTimeout(f, 200)
|
||||||
|
|
||||||
toggleTab: (e) ->
|
toggleTab: (e) ->
|
||||||
@renderScrollbar()
|
@renderScrollbar()
|
||||||
return unless $(document).width() <= 800
|
return unless $(document).width() <= 800
|
||||||
|
|
|
@ -20,6 +20,7 @@ module.exports = class LevelComponentEditView extends CocoView
|
||||||
'click #component-history-button': 'showVersionHistory'
|
'click #component-history-button': 'showVersionHistory'
|
||||||
'click #patch-component-button': 'startPatchingComponent'
|
'click #patch-component-button': 'startPatchingComponent'
|
||||||
'click #component-watch-button': 'toggleWatchComponent'
|
'click #component-watch-button': 'toggleWatchComponent'
|
||||||
|
'click #pop-component-i18n-button': 'onPopulateI18N'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -133,6 +134,10 @@ module.exports = class LevelComponentEditView extends CocoView
|
||||||
@levelComponent.watch(button.find('.watch').is(':visible'))
|
@levelComponent.watch(button.find('.watch').is(':visible'))
|
||||||
button.find('> span').toggleClass('secret')
|
button.find('> span').toggleClass('secret')
|
||||||
|
|
||||||
|
onPopulateI18N: ->
|
||||||
|
@levelComponent.populateI18N()
|
||||||
|
@render()
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
@destroyAceEditor(@editor)
|
@destroyAceEditor(@editor)
|
||||||
@componentSettingsTreema?.destroy()
|
@componentSettingsTreema?.destroy()
|
||||||
|
|
|
@ -5,6 +5,7 @@ LevelComponent = require 'models/LevelComponent'
|
||||||
LevelSystem = require 'models/LevelSystem'
|
LevelSystem = require 'models/LevelSystem'
|
||||||
DeltaView = require 'views/editor/DeltaView'
|
DeltaView = require 'views/editor/DeltaView'
|
||||||
PatchModal = require 'views/editor/PatchModal'
|
PatchModal = require 'views/editor/PatchModal'
|
||||||
|
deltasLib = require 'lib/deltas'
|
||||||
|
|
||||||
module.exports = class SaveLevelModal extends SaveVersionModal
|
module.exports = class SaveLevelModal extends SaveVersionModal
|
||||||
template: template
|
template: template
|
||||||
|
@ -40,7 +41,7 @@ module.exports = class SaveLevelModal extends SaveVersionModal
|
||||||
for changeEl, i in changeEls
|
for changeEl, i in changeEls
|
||||||
model = models[i]
|
model = models[i]
|
||||||
try
|
try
|
||||||
deltaView = new DeltaView({model: model, skipPaths: PatchModal.DOC_SKIP_PATHS})
|
deltaView = new DeltaView({model: model, skipPaths: deltasLib.DOC_SKIP_PATHS})
|
||||||
@insertSubView(deltaView, $(changeEl))
|
@insertSubView(deltaView, $(changeEl))
|
||||||
catch e
|
catch e
|
||||||
console.error 'Couldn\'t create delta view:', e
|
console.error 'Couldn\'t create delta view:', e
|
||||||
|
@ -95,6 +96,7 @@ module.exports = class SaveLevelModal extends SaveVersionModal
|
||||||
@showLoading()
|
@showLoading()
|
||||||
tuples = _.zip(modelsToSave, formsToSave)
|
tuples = _.zip(modelsToSave, formsToSave)
|
||||||
for [newModel, form] in tuples
|
for [newModel, form] in tuples
|
||||||
|
newModel.updateI18NCoverage() if newModel.get('i18nCoverage')
|
||||||
res = newModel.save()
|
res = newModel.save()
|
||||||
do (newModel, form) =>
|
do (newModel, form) =>
|
||||||
res.error =>
|
res.error =>
|
||||||
|
|
|
@ -45,6 +45,7 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
'click .play-with-level-button': 'onPlayLevel'
|
'click .play-with-level-button': 'onPlayLevel'
|
||||||
'click .play-with-level-parent': 'onPlayLevelSelect'
|
'click .play-with-level-parent': 'onPlayLevelSelect'
|
||||||
'keyup .play-with-level-input': 'onPlayLevelKeyUp'
|
'keyup .play-with-level-input': 'onPlayLevelKeyUp'
|
||||||
|
'click #pop-level-i18n-button': 'onPopulateLevelI18N'
|
||||||
|
|
||||||
subscriptions:
|
subscriptions:
|
||||||
'editor:thang-type-color-groups-changed': 'onColorGroupsChanged'
|
'editor:thang-type-color-groups-changed': 'onColorGroupsChanged'
|
||||||
|
@ -352,6 +353,7 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
saveNewThangType: (e) ->
|
saveNewThangType: (e) ->
|
||||||
newThangType = if e.major then @thangType.cloneNewMajorVersion() else @thangType.cloneNewMinorVersion()
|
newThangType = if e.major then @thangType.cloneNewMajorVersion() else @thangType.cloneNewMinorVersion()
|
||||||
newThangType.set('commitMessage', e.commitMessage)
|
newThangType.set('commitMessage', e.commitMessage)
|
||||||
|
newThangType.updateI18NCoverage() if newThangType.get('i18nCoverage')
|
||||||
|
|
||||||
res = newThangType.save()
|
res = newThangType.save()
|
||||||
return unless res
|
return unless res
|
||||||
|
@ -404,9 +406,9 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
|
|
||||||
pushChangesToPreview: =>
|
pushChangesToPreview: =>
|
||||||
return if @temporarilyIgnoringChanges
|
return if @temporarilyIgnoringChanges
|
||||||
# TODO: This doesn't delete old Treema keys you deleted
|
for key of @thangType.attributes
|
||||||
for key, value of @treema.data
|
continue if key is 'components'
|
||||||
@thangType.set(key, value)
|
@thangType.set(key, @treema.data[key])
|
||||||
@updateSelectBox()
|
@updateSelectBox()
|
||||||
@refreshAnimation()
|
@refreshAnimation()
|
||||||
@updateDots()
|
@updateDots()
|
||||||
|
@ -455,6 +457,10 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
showVersionHistory: (e) ->
|
showVersionHistory: (e) ->
|
||||||
@openModalView new ThangTypeVersionsModal thangType: @thangType, @thangTypeID
|
@openModalView new ThangTypeVersionsModal thangType: @thangType, @thangTypeID
|
||||||
|
|
||||||
|
onPopulateLevelI18N: ->
|
||||||
|
@thangType.populateI18N()
|
||||||
|
_.delay((-> document.location.reload()), 500)
|
||||||
|
|
||||||
openSaveModal: ->
|
openSaveModal: ->
|
||||||
@openModalView new SaveVersionModal model: @thangType
|
@openModalView new SaveVersionModal model: @thangType
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ module.exports = class GameMenuModal extends ModalView
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'change input.select': 'onSelectionChanged'
|
'change input.select': 'onSelectionChanged'
|
||||||
'shown.bs.tab .nav-tabs a': 'onTabShown'
|
'shown.bs.tab #game-menu-nav a': 'onTabShown'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -30,6 +30,8 @@ module.exports = class GameMenuModal extends ModalView
|
||||||
context = super(context)
|
context = super(context)
|
||||||
context.showDevBits = @options.showDevBits
|
context.showDevBits = @options.showDevBits
|
||||||
context.showInventory = @options.showInventory
|
context.showInventory = @options.showInventory
|
||||||
|
docs = @options.level.get('documentation') ? {}
|
||||||
|
context.showsGuide = docs.specificArticles?.length or docs.generalArticles?.length
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
|
|
|
@ -1,17 +1,58 @@
|
||||||
CocoView = require 'views/kinds/CocoView'
|
CocoView = require 'views/kinds/CocoView'
|
||||||
template = require 'templates/game-menu/guide-view'
|
template = require 'templates/game-menu/guide-view'
|
||||||
{me} = require 'lib/auth'
|
Article = require 'models/Article'
|
||||||
ThangType = require 'models/ThangType'
|
utils = require 'lib/utils'
|
||||||
|
|
||||||
module.exports = class GuideView extends CocoView
|
# let's implement this once we have the docs database schema set up
|
||||||
|
|
||||||
|
module.exports = class LevelGuideView extends CocoView
|
||||||
|
template: template
|
||||||
id: 'guide-view'
|
id: 'guide-view'
|
||||||
className: 'tab-pane'
|
className: 'tab-pane'
|
||||||
template: template
|
|
||||||
|
|
||||||
getRenderData: (context={}) ->
|
constructor: (options) ->
|
||||||
context = super(context)
|
@firstOnly = options.firstOnly
|
||||||
context
|
@docs = options?.docs ? options.level.get('documentation') ? {}
|
||||||
|
general = @docs.generalArticles or []
|
||||||
|
specific = @docs.specificArticles or []
|
||||||
|
|
||||||
|
articles = options.supermodel.getModels(Article)
|
||||||
|
articleMap = {}
|
||||||
|
articleMap[article.get('original')] = article for article in articles
|
||||||
|
general = (articleMap[ref.original] for ref in general)
|
||||||
|
general = (article.attributes for article in general when article)
|
||||||
|
|
||||||
|
@docs = specific.concat(general)
|
||||||
|
@docs = $.extend(true, [], @docs)
|
||||||
|
@docs = [@docs[0]] if @firstOnly and @docs[0]
|
||||||
|
doc.html = marked(utils.i18n doc, 'body') for doc in @docs
|
||||||
|
doc.name = (utils.i18n doc, 'name') for doc in @docs
|
||||||
|
doc.slug = _.string.slugify(doc.name) for doc in @docs
|
||||||
|
super()
|
||||||
|
|
||||||
|
getRenderData: ->
|
||||||
|
c = super()
|
||||||
|
c.docs = @docs
|
||||||
|
c
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
#Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-open', volume: 1 # no, only when the tab is activated
|
if @docs.length is 1
|
||||||
|
@$el.html(@docs[0].html)
|
||||||
|
else
|
||||||
|
# incredible hackiness. Getting bootstrap tabs to work shouldn't be this complex
|
||||||
|
@$el.find('.nav-tabs li:first').addClass('active')
|
||||||
|
@$el.find('.tab-content .tab-pane:first').addClass('active')
|
||||||
|
@$el.find('.nav-tabs a').click(@clickTab)
|
||||||
|
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-open', volume: 1
|
||||||
|
|
||||||
|
clickTab: (e) =>
|
||||||
|
@$el.find('li.active').removeClass('active')
|
||||||
|
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-tab-switch', volume: 1
|
||||||
|
|
||||||
|
afterInsert: ->
|
||||||
|
super()
|
||||||
|
Backbone.Mediator.publish 'level:docs-shown', {}
|
||||||
|
|
||||||
|
onHidden: ->
|
||||||
|
Backbone.Mediator.publish 'level:docs-hidden', {}
|
||||||
|
|
|
@ -336,6 +336,7 @@ module.exports = class InventoryView extends CocoView
|
||||||
gearByLevel =
|
gearByLevel =
|
||||||
'dungeons-of-kithgard': {feet: 'simple-boots'}
|
'dungeons-of-kithgard': {feet: 'simple-boots'}
|
||||||
'gems-in-the-deep': {feet: 'simple-boots'}
|
'gems-in-the-deep': {feet: 'simple-boots'}
|
||||||
|
'forgetful-gemsmith': {feet: 'simple-boots'}
|
||||||
'shadow-guard': {feet: 'simple-boots'}
|
'shadow-guard': {feet: 'simple-boots'}
|
||||||
'true-names': {feet: 'simple-boots', 'right-hand': 'longsword'}
|
'true-names': {feet: 'simple-boots', 'right-hand': 'longsword'}
|
||||||
'the-raised-sword': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic'}
|
'the-raised-sword': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic'}
|
||||||
|
@ -345,9 +346,11 @@ module.exports = class InventoryView extends CocoView
|
||||||
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'longsword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'longsword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||||
'closing-the-distance': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', eyes: 'crude-glasses'}
|
'closing-the-distance': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', eyes: 'crude-glasses'}
|
||||||
'the-final-kithmaze': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
'the-final-kithmaze': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||||
'kithgard-gates': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
|
'kithgard-gates': {feet: 'simple-boots', 'right-hand': 'builders-hammer', torso: 'leather-tunic'}
|
||||||
'defense-of-plainswood': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
|
'defense-of-plainswood': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
|
||||||
# TODO: figure out leather boots for plainswood (or next one?)
|
'winding-trail': {feet: 'leather-boots', 'right-hand': 'builders-hammer'}
|
||||||
|
'thornbush-farm': {feet: 'leather-boots', 'right-hand': 'builders-hammer', eyes: 'crude-glasses'}
|
||||||
|
'a-fiery-trap': {feet: 'leather-boots', 'right-hand': 'builders-hammer', eyes: 'crude-glasses'}
|
||||||
return unless necessaryGear = gearByLevel[@options.levelID]
|
return unless necessaryGear = gearByLevel[@options.levelID]
|
||||||
if @inserted
|
if @inserted
|
||||||
if @supermodel.finished()
|
if @supermodel.finished()
|
||||||
|
|
16
app/views/i18n/I18NEditAchievementView.coffee
Normal file
16
app/views/i18n/I18NEditAchievementView.coffee
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
I18NEditModelView = require './I18NEditModelView'
|
||||||
|
Achievement = require 'models/Achievement'
|
||||||
|
|
||||||
|
module.exports = class I18NEditAchievementView extends I18NEditModelView
|
||||||
|
id: "i18n-edit-component-view"
|
||||||
|
modelClass: Achievement
|
||||||
|
|
||||||
|
buildTranslationList: ->
|
||||||
|
lang = @selectedLanguage
|
||||||
|
|
||||||
|
# name, description
|
||||||
|
if i18n = @model.get('i18n')
|
||||||
|
if name = @model.get('name')
|
||||||
|
@wrapRow "Achievement name", ['name'], name, i18n[lang]?.name, []
|
||||||
|
if description = @model.get('description')
|
||||||
|
@wrapRow "Achievement description", ['description'], description, i18n[lang]?.description, []
|
47
app/views/i18n/I18NEditComponentView.coffee
Normal file
47
app/views/i18n/I18NEditComponentView.coffee
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
I18NEditModelView = require './I18NEditModelView'
|
||||||
|
LevelComponent = require 'models/LevelComponent'
|
||||||
|
|
||||||
|
module.exports = class I18NEditComponentView extends I18NEditModelView
|
||||||
|
id: "i18n-edit-component-view"
|
||||||
|
modelClass: LevelComponent
|
||||||
|
|
||||||
|
buildTranslationList: ->
|
||||||
|
lang = @selectedLanguage
|
||||||
|
|
||||||
|
propDocs = @model.get('propertyDocumentation')
|
||||||
|
|
||||||
|
for propDoc, propDocIndex in propDocs
|
||||||
|
|
||||||
|
#- Component property descriptions
|
||||||
|
if i18n = propDoc.i18n
|
||||||
|
path = ["propertyDocumentation", propDocIndex]
|
||||||
|
if _.isObject propDoc.description
|
||||||
|
for progLang, description of propDoc
|
||||||
|
@wrapRow "#{propDoc.name} description (#{progLang})", [progLang, 'description'], description, i18n[lang]?[progLang]?.description, path, 'markdown'
|
||||||
|
else if _.isString propDoc.description
|
||||||
|
@wrapRow "#{propDoc.name} description", ['description'], propDoc.description, i18n[lang]?.description, path, 'markdown'
|
||||||
|
if context = propDoc.context
|
||||||
|
for key, value of context
|
||||||
|
@wrapRow "#{propDoc.name} context value", ["context", key], value, i18n[lang]?.context[key], path
|
||||||
|
|
||||||
|
#- Component return value descriptions
|
||||||
|
if i18n = propDoc.returns?.i18n
|
||||||
|
path = ["propertyDocumentation", propDocIndex, "returns"]
|
||||||
|
d = propDoc.returns.description
|
||||||
|
if _.isObject d
|
||||||
|
for progLang, description of d
|
||||||
|
@wrapRow "#{propDoc.name} return val (#{progLang})", [progLang, 'description'], description, i18n[lang]?[progLang]?.description, path, 'markdown'
|
||||||
|
else if _.isString d
|
||||||
|
@wrapRow "#{propDoc.name} return val", ['description'], d, i18n[lang]?.description, path, 'markdown'
|
||||||
|
|
||||||
|
#- Component argument descriptions
|
||||||
|
if propDoc.args
|
||||||
|
for argDoc, argIndex in propDoc.args
|
||||||
|
if i18n = argDoc.i18n
|
||||||
|
path = ["propertyDocumentation", propDocIndex, 'args', argIndex]
|
||||||
|
if _.isObject argDoc.description
|
||||||
|
for progLang, description of argDoc
|
||||||
|
@wrapRow "#{propDoc.name} arg description #{argDoc.name} (#{progLang})", [progLang, 'description'], description, i18n[lang]?[progLang]?.description, path, 'markdown'
|
||||||
|
else if _.isString argDoc.description
|
||||||
|
@wrapRow "#{propDoc.name} arg description #{argDoc.name}", ['description'], argDoc.description, i18n[lang]?.description, path, 'markdown'
|
||||||
|
|
58
app/views/i18n/I18NEditLevelView.coffee
Normal file
58
app/views/i18n/I18NEditLevelView.coffee
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
I18NEditModelView = require './I18NEditModelView'
|
||||||
|
Level = require 'models/Level'
|
||||||
|
LevelComponent = require 'models/LevelComponent'
|
||||||
|
|
||||||
|
module.exports = class I18NEditLevelView extends I18NEditModelView
|
||||||
|
id: "i18n-edit-level-view"
|
||||||
|
modelClass: Level
|
||||||
|
|
||||||
|
buildTranslationList: ->
|
||||||
|
lang = @selectedLanguage
|
||||||
|
|
||||||
|
# name, description
|
||||||
|
if i18n = @model.get('i18n')
|
||||||
|
if name = @model.get('name')
|
||||||
|
@wrapRow "Level name", ['name'], name, i18n[lang]?.name, []
|
||||||
|
if description = @model.get('description')
|
||||||
|
@wrapRow "Level description", ['description'], description, i18n[lang]?.description, []
|
||||||
|
|
||||||
|
# goals
|
||||||
|
for goal, index in @model.get('goals') ? []
|
||||||
|
if i18n = goal.i18n
|
||||||
|
@wrapRow "Goal name", ['name'], goal.name, i18n[lang]?.name, ['goals', index]
|
||||||
|
|
||||||
|
# documentation
|
||||||
|
for doc, index in @model.get('documentation')?.specificArticles ? []
|
||||||
|
if i18n = doc.i18n
|
||||||
|
@wrapRow "Guide article name", ['name'], doc.name, i18n[lang]?.name, ['documentation', 'specificArticles', index]
|
||||||
|
@wrapRow "'#{doc.name}' description", ['description'], doc.description, i18n[lang]?.description, ['documentation', 'specificArticles', index], 'markdown'
|
||||||
|
|
||||||
|
# sprite dialogues
|
||||||
|
for script, scriptIndex in @model.get('scripts') ? []
|
||||||
|
for noteGroup, noteGroupIndex in script.noteChain ? []
|
||||||
|
for spriteCommand, spriteCommandIndex in noteGroup.sprites ? []
|
||||||
|
pathPrefix = ['scripts', scriptIndex, 'noteChain', noteGroupIndex, 'sprites', spriteCommandIndex, 'say']
|
||||||
|
|
||||||
|
if i18n = spriteCommand.say?.i18n
|
||||||
|
if spriteCommand.say.text
|
||||||
|
@wrapRow "Sprite text", ['text'], spriteCommand.say.text, i18n[lang]?.text, pathPrefix, 'markdown'
|
||||||
|
if spriteCommand.say.blurb
|
||||||
|
@wrapRow "Sprite blurb", ['blurb'], spriteCommand.say.blurb, i18n[lang]?.blurb, pathPrefix
|
||||||
|
|
||||||
|
for response, responseIndex in spriteCommand.say?.responses ? []
|
||||||
|
if i18n = response.i18n
|
||||||
|
@wrapRow "Response button", ['text'], response.text, i18n[lang]?.text, pathPrefix.concat(['responses', responseIndex])
|
||||||
|
|
||||||
|
# victory modal
|
||||||
|
if i18n = @model.get('victory')?.i18n
|
||||||
|
@wrapRow "Victory text", ['body'], @model.get('victory').body, i18n[lang]?.body, ['victory'], 'markdown'
|
||||||
|
|
||||||
|
# code comments
|
||||||
|
for thang, thangIndex in @model.get('thangs') ? []
|
||||||
|
for component, componentIndex in thang.components ? []
|
||||||
|
continue unless component.original is LevelComponent.ProgrammableID
|
||||||
|
for methodName, method of component.config?.programmableMethods ? {}
|
||||||
|
if (i18n = method.i18n) and (context = method.context)
|
||||||
|
for key, value of context
|
||||||
|
path = ['thangs', thangIndex, 'components', componentIndex, 'config', 'programmableMethods', methodName]
|
||||||
|
@wrapRow "Code comment", ["context", key], value, i18n[lang]?.context[key], path
|
159
app/views/i18n/I18NEditModelView.coffee
Normal file
159
app/views/i18n/I18NEditModelView.coffee
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
RootView = require 'views/kinds/RootView'
|
||||||
|
locale = require 'locale/locale'
|
||||||
|
Patch = require 'models/Patch'
|
||||||
|
template = require 'templates/i18n/i18n-edit-model-view'
|
||||||
|
deltasLib = require 'lib/deltas'
|
||||||
|
|
||||||
|
module.exports = class I18NEditModelView extends RootView
|
||||||
|
className: 'editor i18n-edit-model-view'
|
||||||
|
template: template
|
||||||
|
|
||||||
|
events:
|
||||||
|
'change .translation-input': 'onInputChanged'
|
||||||
|
'change #language-select': 'onLanguageSelectChanged'
|
||||||
|
'click #patch-submit': 'onSubmitPatch'
|
||||||
|
|
||||||
|
constructor: (options, @modelHandle) ->
|
||||||
|
super(options)
|
||||||
|
@model = new @modelClass(_id: @modelHandle)
|
||||||
|
@model = @supermodel.loadModel(@model, 'model').model
|
||||||
|
@model.saveBackups = true
|
||||||
|
@selectedLanguage = me.get('preferredLanguage', true)
|
||||||
|
|
||||||
|
showLoading: ($el) ->
|
||||||
|
$el ?= @$el.find('.outer-content')
|
||||||
|
super($el)
|
||||||
|
|
||||||
|
onLoaded: ->
|
||||||
|
super()
|
||||||
|
@model.markToRevert() unless @model.hasLocalChanges()
|
||||||
|
|
||||||
|
getRenderData: ->
|
||||||
|
c = super()
|
||||||
|
|
||||||
|
c.model = @model
|
||||||
|
c.selectedLanguage = @selectedLanguage
|
||||||
|
|
||||||
|
@translationList = []
|
||||||
|
if @supermodel.finished() then @buildTranslationList() else []
|
||||||
|
result.index = index for result, index in @translationList
|
||||||
|
c.translationList = @translationList
|
||||||
|
|
||||||
|
c
|
||||||
|
|
||||||
|
afterRender: ->
|
||||||
|
super()
|
||||||
|
|
||||||
|
@hush = true
|
||||||
|
$select = @$el.find('#language-select').empty()
|
||||||
|
@addLanguagesToSelect($select, @selectedLanguage)
|
||||||
|
@hush = false
|
||||||
|
editors = []
|
||||||
|
|
||||||
|
@$el.find('tr[data-format="markdown"]').each((index, el) =>
|
||||||
|
englishEditor = ace.edit(enEl=$(el).find('.english-value-row div')[0])
|
||||||
|
englishEditor.el = enEl
|
||||||
|
englishEditor.setReadOnly(true)
|
||||||
|
toEditor = ace.edit(toEl=$(el).find('.to-value-row div')[0])
|
||||||
|
toEditor.el = toEl
|
||||||
|
toEditor.on 'change', @onEditorChange
|
||||||
|
editors = editors.concat([englishEditor, toEditor])
|
||||||
|
)
|
||||||
|
|
||||||
|
for editor in editors
|
||||||
|
session = editor.getSession()
|
||||||
|
session.setTabSize 2
|
||||||
|
session.setMode 'ace/mode/markdown'
|
||||||
|
session.setNewLineMode = 'unix'
|
||||||
|
session.setUseSoftTabs true
|
||||||
|
editor.setOptions({ maxLines: Infinity })
|
||||||
|
|
||||||
|
onEditorChange: (event, editor) =>
|
||||||
|
return if @destroyed
|
||||||
|
index = $(editor.el).data('index')
|
||||||
|
rowInfo = @translationList[index]
|
||||||
|
value = editor.getValue()
|
||||||
|
@onTranslationChanged(rowInfo, value)
|
||||||
|
|
||||||
|
wrapRow: (title, key, enValue, toValue, path, format) ->
|
||||||
|
@translationList.push {
|
||||||
|
title: title,
|
||||||
|
key: key,
|
||||||
|
enValue: enValue,
|
||||||
|
toValue: toValue or '',
|
||||||
|
path: path
|
||||||
|
format: format
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTranslationList: -> [] # overwrite
|
||||||
|
|
||||||
|
onInputChanged: (e) ->
|
||||||
|
index = $(e.target).data('index')
|
||||||
|
rowInfo = @translationList[index]
|
||||||
|
value = $(e.target).val()
|
||||||
|
@onTranslationChanged(rowInfo, value)
|
||||||
|
|
||||||
|
onTranslationChanged: (rowInfo, value) ->
|
||||||
|
|
||||||
|
#- Navigate down to where the translation will live
|
||||||
|
base = @model.attributes
|
||||||
|
|
||||||
|
for seg in rowInfo.path
|
||||||
|
base = base[seg]
|
||||||
|
|
||||||
|
base = base.i18n
|
||||||
|
|
||||||
|
base[@selectedLanguage] ?= {}
|
||||||
|
base = base[@selectedLanguage]
|
||||||
|
|
||||||
|
if rowInfo.key.length > 1
|
||||||
|
for seg in rowInfo.key[..-2]
|
||||||
|
base[seg] ?= {}
|
||||||
|
base = base[seg]
|
||||||
|
|
||||||
|
#- Set the data in a non-kosher way
|
||||||
|
|
||||||
|
base[rowInfo.key[rowInfo.key.length-1]] = value
|
||||||
|
@model.saveBackup()
|
||||||
|
|
||||||
|
#- Enable patch submit button
|
||||||
|
|
||||||
|
@$el.find('#patch-submit').attr('disabled', null)
|
||||||
|
|
||||||
|
onLanguageSelectChanged: (e) ->
|
||||||
|
return if @hush
|
||||||
|
@selectedLanguage = $(e.target).val()
|
||||||
|
@render()
|
||||||
|
|
||||||
|
onSubmitPatch: (e) ->
|
||||||
|
|
||||||
|
delta = @model.getDelta()
|
||||||
|
flattened = deltasLib.flattenDelta(delta)
|
||||||
|
save = _.all(flattened, (delta) ->
|
||||||
|
return _.isArray(delta.o) and delta.o.length is 1 and 'i18n' in delta.dataPath
|
||||||
|
)
|
||||||
|
|
||||||
|
if save
|
||||||
|
modelToSave = @model.cloneNewMinorVersion()
|
||||||
|
modelToSave.updateI18NCoverage() if modelToSave.get('i18nCoverage')
|
||||||
|
|
||||||
|
else
|
||||||
|
modelToSave = new Patch()
|
||||||
|
modelToSave.set 'delta', @model.getDelta()
|
||||||
|
modelToSave.set 'target', {
|
||||||
|
'collection': _.string.underscored @model.constructor.className
|
||||||
|
'id': @model.id
|
||||||
|
}
|
||||||
|
|
||||||
|
if @modelClass.schema.properties.commitMessage
|
||||||
|
commitMessage = "Diplomat submission for lang #{@selectedLanguage}: #{flattened.length} change(s)."
|
||||||
|
modelToSave.set 'commitMessage', commitMessage
|
||||||
|
|
||||||
|
errors = modelToSave.validate()
|
||||||
|
button = $(e.target)
|
||||||
|
button.attr('disabled', 'disabled')
|
||||||
|
return button.text('Failed to Submit Changes') if errors
|
||||||
|
res = modelToSave.save()
|
||||||
|
return button.text('Failed to Submit Changes') unless res
|
||||||
|
res.error => button.text('Error Submitting Changes')
|
||||||
|
res.success => button.text('Submit Changes')
|
15
app/views/i18n/I18NEditThangTypeView.coffee
Normal file
15
app/views/i18n/I18NEditThangTypeView.coffee
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
I18NEditModelView = require './I18NEditModelView'
|
||||||
|
ThangType = require 'models/ThangType'
|
||||||
|
|
||||||
|
module.exports = class ThangTypeI18NView extends I18NEditModelView
|
||||||
|
id: "thang-type-i18n-view"
|
||||||
|
modelClass: ThangType
|
||||||
|
|
||||||
|
buildTranslationList: ->
|
||||||
|
lang = @selectedLanguage
|
||||||
|
@model.markToRevert() unless @model.hasLocalChanges()
|
||||||
|
i18n = @model.get('i18n')
|
||||||
|
if i18n
|
||||||
|
name = @model.get('name')
|
||||||
|
@wrapRow('Name', ['name'], name, i18n[lang]?.name, [])
|
||||||
|
@wrapRow('Description', ['description'], @model.get('description'), i18n[lang]?.description, [], 'markdown')
|
98
app/views/i18n/I18NHomeView.coffee
Normal file
98
app/views/i18n/I18NHomeView.coffee
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
RootView = require 'views/kinds/RootView'
|
||||||
|
template = require 'templates/i18n/i18n-home-view'
|
||||||
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
|
|
||||||
|
LevelComponent = require 'models/LevelComponent'
|
||||||
|
ThangType = require 'models/ThangType'
|
||||||
|
Level = require 'models/Level'
|
||||||
|
Achievement = require 'models/Achievement'
|
||||||
|
|
||||||
|
languages = _.keys(require 'locale/locale').sort()
|
||||||
|
PAGE_SIZE = 100
|
||||||
|
|
||||||
|
module.exports = class I18NHomeView extends RootView
|
||||||
|
id: "i18n-home-view"
|
||||||
|
template: template
|
||||||
|
|
||||||
|
events:
|
||||||
|
'change #language-select': 'onLanguageSelectChanged'
|
||||||
|
|
||||||
|
constructor: (options) ->
|
||||||
|
super(options)
|
||||||
|
@selectedLanguage = me.get('preferredLanguage', true)
|
||||||
|
|
||||||
|
#-
|
||||||
|
@aggregateModels = new Backbone.Collection()
|
||||||
|
@aggregateModels.comparator = (m) ->
|
||||||
|
return 2 if m.specificallyCovered
|
||||||
|
return 1 if m.generallyCovered
|
||||||
|
return 0
|
||||||
|
|
||||||
|
project = ['name', 'components.original', 'i18nCoverage', 'slug']
|
||||||
|
|
||||||
|
@thangTypes = new CocoCollection([], { url: '/db/thang.type?view=i18n-coverage', project: project, model: ThangType })
|
||||||
|
@components = new CocoCollection([], { url: '/db/level.component?view=i18n-coverage', project: project, model: LevelComponent })
|
||||||
|
@levels = new CocoCollection([], { url: '/db/level?view=i18n-coverage', project: project, model: Level })
|
||||||
|
@achievements = new CocoCollection([], { url: '/db/achievement?view=i18n-coverage', project: project, model: Achievement })
|
||||||
|
|
||||||
|
for c in [@thangTypes, @components, @levels, @achievements]
|
||||||
|
c.skip = 0
|
||||||
|
c.fetch({data: {skip: 0, limit: PAGE_SIZE}, cache:false})
|
||||||
|
@supermodel.loadCollection(c, 'documents')
|
||||||
|
@listenTo c, 'sync', @onCollectionSynced
|
||||||
|
|
||||||
|
|
||||||
|
onCollectionSynced: (collection) ->
|
||||||
|
for model in collection.models
|
||||||
|
model.i18nURLBase = switch model.constructor.className
|
||||||
|
when "ThangType" then "/i18n/thang/"
|
||||||
|
when "LevelComponent" then "/i18n/component/"
|
||||||
|
when "Achievement" then "/i18n/achievement/"
|
||||||
|
when "Level" then "/i18n/level/"
|
||||||
|
getMore = collection.models.length is PAGE_SIZE
|
||||||
|
@aggregateModels.add(collection.models)
|
||||||
|
@render()
|
||||||
|
|
||||||
|
if getMore
|
||||||
|
collection.skip += PAGE_SIZE
|
||||||
|
collection.fetch({data: {skip: collection.skip, limit: PAGE_SIZE}})
|
||||||
|
|
||||||
|
getRenderData: ->
|
||||||
|
c = super()
|
||||||
|
@updateCoverage()
|
||||||
|
c.languages = languages
|
||||||
|
c.selectedLanguage = @selectedLanguage
|
||||||
|
c.collection = @aggregateModels
|
||||||
|
|
||||||
|
covered = (m for m in @aggregateModels.models when m.specificallyCovered).length
|
||||||
|
total = @aggregateModels.models.length
|
||||||
|
c.progress = if total then parseInt(100 * covered / total) else 100
|
||||||
|
|
||||||
|
c
|
||||||
|
|
||||||
|
updateCoverage: ->
|
||||||
|
selectedBase = @selectedLanguage[..2]
|
||||||
|
relatedLanguages = (l for l in languages when l.startsWith(selectedBase) and l isnt @selectedLanguage)
|
||||||
|
for model in @aggregateModels.models
|
||||||
|
@updateCoverageForModel(model, relatedLanguages)
|
||||||
|
model.generallyCovered = true if @selectedLanguage.startsWith 'en'
|
||||||
|
@aggregateModels.sort()
|
||||||
|
|
||||||
|
updateCoverageForModel: (model, relatedLanguages) ->
|
||||||
|
model.specificallyCovered = true
|
||||||
|
model.generallyCovered = true
|
||||||
|
coverage = model.get('i18nCoverage')
|
||||||
|
|
||||||
|
if @selectedLanguage not in coverage
|
||||||
|
model.specificallyCovered = false
|
||||||
|
if not _.any((l in coverage for l in relatedLanguages))
|
||||||
|
model.generallyCovered = false
|
||||||
|
return
|
||||||
|
|
||||||
|
afterRender: ->
|
||||||
|
super()
|
||||||
|
@addLanguagesToSelect(@$el.find('#language-select'), @selectedLanguage)
|
||||||
|
|
||||||
|
onLanguageSelectChanged: (e) ->
|
||||||
|
@selectedLanguage = $(e.target).val()
|
||||||
|
@render()
|
|
@ -106,15 +106,20 @@ module.exports = class RootView extends CocoView
|
||||||
$select.parent().find('.options, .trigger').remove()
|
$select.parent().find('.options, .trigger').remove()
|
||||||
$select.unwrap().removeClass('fancified')
|
$select.unwrap().removeClass('fancified')
|
||||||
preferred = me.get('preferredLanguage', true)
|
preferred = me.get('preferredLanguage', true)
|
||||||
|
@addLanguagesToSelect($select, preferred)
|
||||||
|
$select.fancySelect().parent().find('.trigger').addClass('header-font')
|
||||||
|
$('body').attr('lang', preferred)
|
||||||
|
|
||||||
|
addLanguagesToSelect: ($select, initialVal) ->
|
||||||
|
initialVal ?= me.get('preferredLanguage', true)
|
||||||
codes = _.keys(locale)
|
codes = _.keys(locale)
|
||||||
genericCodes = _.filter codes, (code) ->
|
genericCodes = _.filter codes, (code) ->
|
||||||
_.find(codes, (code2) ->
|
_.find(codes, (code2) ->
|
||||||
code2 isnt code and code2.split('-')[0] is code)
|
code2 isnt code and code2.split('-')[0] is code)
|
||||||
for code, localeInfo of locale when not (code in genericCodes) or code is preferred
|
for code, localeInfo of locale when not (code in genericCodes) or code is initialVal
|
||||||
$select.append(
|
$select.append(
|
||||||
$('<option></option>').val(code).text(localeInfo.nativeDescription))
|
$('<option></option>').val(code).text(localeInfo.nativeDescription))
|
||||||
$select.val(preferred).fancySelect().parent().find('.trigger').addClass('header-font')
|
$select.val(initialVal)
|
||||||
$('body').attr('lang', preferred)
|
|
||||||
|
|
||||||
onLanguageChanged: ->
|
onLanguageChanged: ->
|
||||||
newLang = $('.language-dropdown').val()
|
newLang = $('.language-dropdown').val()
|
||||||
|
|
|
@ -4,6 +4,7 @@ DeltaView = require 'views/editor/DeltaView'
|
||||||
PatchModal = require 'views/editor/PatchModal'
|
PatchModal = require 'views/editor/PatchModal'
|
||||||
nameLoader = require 'lib/NameLoader'
|
nameLoader = require 'lib/NameLoader'
|
||||||
CocoCollection = require 'collections/CocoCollection'
|
CocoCollection = require 'collections/CocoCollection'
|
||||||
|
deltasLib = require 'lib/deltas'
|
||||||
|
|
||||||
class VersionsViewCollection extends CocoCollection
|
class VersionsViewCollection extends CocoCollection
|
||||||
url: ''
|
url: ''
|
||||||
|
@ -55,7 +56,7 @@ module.exports = class VersionsModal extends ModalView
|
||||||
@deltaView = new DeltaView({
|
@deltaView = new DeltaView({
|
||||||
model: earlierVersion
|
model: earlierVersion
|
||||||
comparisonModel: laterVersion
|
comparisonModel: laterVersion
|
||||||
skipPaths: PatchModal.DOC_SKIP_PATHS
|
skipPaths: deltasLib.DOC_SKIP_PATHS
|
||||||
loadModels: true
|
loadModels: true
|
||||||
})
|
})
|
||||||
@insertSubView(@deltaView, deltaEl)
|
@insertSubView(@deltaView, deltaEl)
|
||||||
|
|
|
@ -2,7 +2,6 @@ CocoView = require 'views/kinds/CocoView'
|
||||||
template = require 'templates/play/level/control_bar'
|
template = require 'templates/play/level/control_bar'
|
||||||
{me} = require 'lib/auth'
|
{me} = require 'lib/auth'
|
||||||
|
|
||||||
LevelGuideModal = require './modal/LevelGuideModal'
|
|
||||||
GameMenuModal = require 'views/game-menu/GameMenuModal'
|
GameMenuModal = require 'views/game-menu/GameMenuModal'
|
||||||
RealTimeCollection = require 'collections/RealTimeCollection'
|
RealTimeCollection = require 'collections/RealTimeCollection'
|
||||||
|
|
||||||
|
@ -16,16 +15,9 @@ module.exports = class ControlBarView extends CocoView
|
||||||
'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
|
'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #docs-button': ->
|
|
||||||
window.tracker?.trackEvent 'Clicked Docs', level: @level.get('name'), label: @level.get('name')
|
|
||||||
@showGuideModal()
|
|
||||||
|
|
||||||
'click #next-game-button': -> Backbone.Mediator.publish 'level:next-game-pressed', {}
|
'click #next-game-button': -> Backbone.Mediator.publish 'level:next-game-pressed', {}
|
||||||
|
|
||||||
'click #game-menu-button': 'showGameMenuModal'
|
'click #game-menu-button': 'showGameMenuModal'
|
||||||
|
|
||||||
'click': -> Backbone.Mediator.publish 'tome:focus-editor', {}
|
'click': -> Backbone.Mediator.publish 'tome:focus-editor', {}
|
||||||
|
|
||||||
'click .home a': 'onClickHome'
|
'click .home a': 'onClickHome'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
|
@ -69,30 +61,10 @@ module.exports = class ControlBarView extends CocoView
|
||||||
c.multiplayerSession = @multiplayerSession if @multiplayerSession
|
c.multiplayerSession = @multiplayerSession if @multiplayerSession
|
||||||
c.multiplayerPlayers = @multiplayerPlayers if @multiplayerPlayers
|
c.multiplayerPlayers = @multiplayerPlayers if @multiplayerPlayers
|
||||||
c.meID = me.id
|
c.meID = me.id
|
||||||
docs = @level.get('documentation') ? {}
|
|
||||||
c.showsGuide = docs.specificArticles?.length or docs.generalArticles?.length
|
|
||||||
c
|
c
|
||||||
|
|
||||||
afterRender: ->
|
|
||||||
super()
|
|
||||||
@guideHighlightInterval ?= setInterval @onGuideHighlight, 5 * 60 * 1000
|
|
||||||
|
|
||||||
destroy: ->
|
|
||||||
clearInterval @guideHighlightInterval if @guideHighlightInterval
|
|
||||||
super()
|
|
||||||
|
|
||||||
onGuideHighlight: =>
|
|
||||||
return if @destroyed or @guideShownOnce
|
|
||||||
@$el.find('#docs-button').hide().show('highlight', 4000)
|
|
||||||
|
|
||||||
showGuideModal: ->
|
|
||||||
options = {docs: @level.get('documentation'), supermodel: @supermodel}
|
|
||||||
@openModalView(new LevelGuideModal(options))
|
|
||||||
clearInterval @guideHighlightInterval
|
|
||||||
@guideHighlightInterval = null
|
|
||||||
|
|
||||||
showGameMenuModal: ->
|
showGameMenuModal: ->
|
||||||
@openModalView new GameMenuModal level: @level, session: @session
|
@openModalView new GameMenuModal level: @level, session: @session, supermodel: @supermodel
|
||||||
|
|
||||||
onClickHome: (e) ->
|
onClickHome: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
|
@ -142,37 +142,6 @@ module.exports = class PlayLevelView extends RootView
|
||||||
|
|
||||||
# CocoView overridden methods ###############################################
|
# CocoView overridden methods ###############################################
|
||||||
|
|
||||||
updateProgress: (progress) ->
|
|
||||||
super(progress)
|
|
||||||
return if @seenDocs
|
|
||||||
return if @isIPadApp()
|
|
||||||
return unless @levelLoader.session.loaded and @levelLoader.level.loaded
|
|
||||||
return unless showFrequency = @levelLoader.level.get('showsGuide')
|
|
||||||
session = @levelLoader.session
|
|
||||||
diff = new Date().getTime() - new Date(session.get('created')).getTime()
|
|
||||||
return if showFrequency is 'first-time' and diff > (5 * 60 * 1000)
|
|
||||||
articles = @levelLoader.supermodel.getModels Article
|
|
||||||
for article in articles
|
|
||||||
return unless article.loaded
|
|
||||||
@showGuide()
|
|
||||||
|
|
||||||
showGuide: ->
|
|
||||||
@seenDocs = true
|
|
||||||
LevelGuideModal = require './modal/LevelGuideModal'
|
|
||||||
options =
|
|
||||||
docs: @levelLoader.level.get('documentation')
|
|
||||||
supermodel: @supermodel
|
|
||||||
firstOnly: true
|
|
||||||
@openModalView(new LevelGuideModal(options), true)
|
|
||||||
onGuideOpened = (e) ->
|
|
||||||
@guideOpenTime = new Date()
|
|
||||||
onGuideClosed = (e) ->
|
|
||||||
application.tracker?.trackTiming new Date() - @guideOpenTime, 'Intro Guide Time', @levelID, @levelID, 100
|
|
||||||
@onLevelStarted()
|
|
||||||
Backbone.Mediator.subscribeOnce 'modal:opened', onGuideOpened, @
|
|
||||||
Backbone.Mediator.subscribeOnce 'modal:closed', onGuideClosed, @
|
|
||||||
return true
|
|
||||||
|
|
||||||
getRenderData: ->
|
getRenderData: ->
|
||||||
c = super()
|
c = super()
|
||||||
c.world = @world
|
c.world = @world
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
ModalView = require 'views/kinds/ModalView'
|
|
||||||
template = require 'templates/play/level/modal/docs'
|
|
||||||
Article = require 'models/Article'
|
|
||||||
utils = require 'lib/utils'
|
|
||||||
|
|
||||||
# let's implement this once we have the docs database schema set up
|
|
||||||
|
|
||||||
module.exports = class LevelGuideModal extends ModalView
|
|
||||||
template: template
|
|
||||||
id: 'docs-modal'
|
|
||||||
|
|
||||||
shortcuts:
|
|
||||||
'enter': 'hide'
|
|
||||||
|
|
||||||
constructor: (options) ->
|
|
||||||
@firstOnly = options.firstOnly
|
|
||||||
@docs = options?.docs ? {}
|
|
||||||
general = @docs.generalArticles or []
|
|
||||||
specific = @docs.specificArticles or []
|
|
||||||
|
|
||||||
articles = options.supermodel.getModels(Article)
|
|
||||||
articleMap = {}
|
|
||||||
articleMap[article.get('original')] = article for article in articles
|
|
||||||
general = (articleMap[ref.original] for ref in general)
|
|
||||||
general = (article.attributes for article in general when article)
|
|
||||||
|
|
||||||
@docs = specific.concat(general)
|
|
||||||
@docs = $.extend(true, [], @docs)
|
|
||||||
@docs = [@docs[0]] if @firstOnly and @docs[0]
|
|
||||||
doc.html = marked(utils.i18n doc, 'body') for doc in @docs
|
|
||||||
doc.name = (utils.i18n doc, 'name') for doc in @docs
|
|
||||||
doc.slug = _.string.slugify(doc.name) for doc in @docs
|
|
||||||
super()
|
|
||||||
|
|
||||||
getRenderData: ->
|
|
||||||
c = super()
|
|
||||||
c.docs = @docs
|
|
||||||
c
|
|
||||||
|
|
||||||
afterRender: ->
|
|
||||||
super()
|
|
||||||
if @docs.length is 1
|
|
||||||
@$el.find('.modal-body').html(@docs[0].html)
|
|
||||||
else
|
|
||||||
# incredible hackiness. Getting bootstrap tabs to work shouldn't be this complex
|
|
||||||
@$el.find('.nav-tabs li:first').addClass('active')
|
|
||||||
@$el.find('.tab-content .tab-pane:first').addClass('active')
|
|
||||||
@$el.find('.nav-tabs a').click(@clickTab)
|
|
||||||
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-open', volume: 1
|
|
||||||
|
|
||||||
clickTab: (e) =>
|
|
||||||
@$el.find('li.active').removeClass('active')
|
|
||||||
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-tab-switch', volume: 1
|
|
||||||
|
|
||||||
afterInsert: ->
|
|
||||||
super()
|
|
||||||
Backbone.Mediator.publish 'level:docs-shown', {}
|
|
||||||
|
|
||||||
onHidden: ->
|
|
||||||
Backbone.Mediator.publish 'level:docs-hidden', {}
|
|
|
@ -1,6 +1,7 @@
|
||||||
popoverTemplate = require 'templates/play/level/tome/spell_palette_entry_popover'
|
popoverTemplate = require 'templates/play/level/tome/spell_palette_entry_popover'
|
||||||
{downTheChain} = require 'lib/world/world_utils'
|
{downTheChain} = require 'lib/world/world_utils'
|
||||||
window.Vector = require 'lib/world/vector' # So we can document it
|
window.Vector = require 'lib/world/vector' # So we can document it
|
||||||
|
utils = require 'lib/utils'
|
||||||
|
|
||||||
safeJSONStringify = (input, maxDepth) ->
|
safeJSONStringify = (input, maxDepth) ->
|
||||||
recursion = (input, path, depth) ->
|
recursion = (input, path, depth) ->
|
||||||
|
@ -89,11 +90,26 @@ module.exports = class DocFormatter
|
||||||
if @doc.returns
|
if @doc.returns
|
||||||
toTranslate.push {obj: @doc.returns, prop: 'example'}, {obj: @doc.returns, prop: 'description'}
|
toTranslate.push {obj: @doc.returns, prop: 'example'}, {obj: @doc.returns, prop: 'description'}
|
||||||
for {obj, prop} in toTranslate
|
for {obj, prop} in toTranslate
|
||||||
|
# Translate into chosen code language.
|
||||||
if val = obj[prop]?[@options.language]
|
if val = obj[prop]?[@options.language]
|
||||||
obj[prop] = val
|
obj[prop] = val
|
||||||
else unless _.isString obj[prop]
|
else unless _.isString obj[prop]
|
||||||
obj[prop] = null
|
obj[prop] = null
|
||||||
|
|
||||||
|
# Translate into chosen spoken language.
|
||||||
|
if val = obj[prop]
|
||||||
|
context = @doc.context
|
||||||
|
obj[prop] = val = utils.i18n obj, prop
|
||||||
|
if @doc.i18n
|
||||||
|
spokenLanguage = me.get 'preferredLanguage'
|
||||||
|
while spokenLanguage
|
||||||
|
spokenLanguage = spokenLanguage.substr 0, spokenLanguage.lastIndexOf('-') if fallingBack?
|
||||||
|
if spokenLanguageContext = @doc.i18n[spokenLanguage]?.context
|
||||||
|
context = _.merge context, spokenLanguageContext
|
||||||
|
break
|
||||||
|
fallingBack = true
|
||||||
|
obj[prop] = _.template val, context if context
|
||||||
|
|
||||||
formatPopover: ->
|
formatPopover: ->
|
||||||
content = popoverTemplate doc: @doc, language: @options.language, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? []), writable: @options.writable, selectedMethod: @options.selectedMethod, cooldowns: @inferCooldowns(), item: @options.item
|
content = popoverTemplate doc: @doc, language: @options.language, value: @formatValue(), marked: marked, argumentExamples: (arg.example or arg.default or arg.name for arg in @doc.args ? []), writable: @options.writable, selectedMethod: @options.selectedMethod, cooldowns: @inferCooldowns(), item: @options.item
|
||||||
owner = if @doc.owner is 'this' then @options.thang else window[@doc.owner]
|
owner = if @doc.owner is 'this' then @options.thang else window[@doc.owner]
|
||||||
|
|
|
@ -22,6 +22,8 @@ module.exports = class Spell
|
||||||
@levelType = options.level.get('type', true)
|
@levelType = options.level.get('type', true)
|
||||||
|
|
||||||
p = options.programmableMethod
|
p = options.programmableMethod
|
||||||
|
@commentI18N = p.i18n
|
||||||
|
@commentContext = p.context
|
||||||
@languages = p.languages ? {}
|
@languages = p.languages ? {}
|
||||||
@languages.javascript ?= p.source
|
@languages.javascript ?= p.source
|
||||||
@name = p.name
|
@name = p.name
|
||||||
|
@ -61,6 +63,18 @@ module.exports = class Spell
|
||||||
setLanguage: (@language) ->
|
setLanguage: (@language) ->
|
||||||
#console.log 'setting language to', @language, 'so using original source', @languages[language] ? @languages.javascript
|
#console.log 'setting language to', @language, 'so using original source', @languages[language] ? @languages.javascript
|
||||||
@originalSource = @languages[language] ? @languages.javascript
|
@originalSource = @languages[language] ? @languages.javascript
|
||||||
|
# Translate comments chosen spoken language.
|
||||||
|
return unless @commentContext
|
||||||
|
context = $.extend true, {}, @commentContext
|
||||||
|
if @commentI18N
|
||||||
|
spokenLanguage = me.get 'preferredLanguage'
|
||||||
|
while spokenLanguage
|
||||||
|
spokenLanguage = spokenLanguage.substr 0, spokenLanguage.lastIndexOf('-') if fallingBack?
|
||||||
|
if spokenLanguageContext = @commentI18N[spokenLanguage]?.context
|
||||||
|
context = _.merge context, spokenLanguageContext
|
||||||
|
break
|
||||||
|
fallingBack = true
|
||||||
|
@originalSource = _.template @originalSource, context
|
||||||
|
|
||||||
addThang: (thang) ->
|
addThang: (thang) ->
|
||||||
if @thangs[thang.id]
|
if @thangs[thang.id]
|
||||||
|
|
|
@ -68,7 +68,8 @@
|
||||||
"node-gyp": "~0.13.0",
|
"node-gyp": "~0.13.0",
|
||||||
"aether": "~0.2.39",
|
"aether": "~0.2.39",
|
||||||
"JASON": "~0.1.3",
|
"JASON": "~0.1.3",
|
||||||
"JQDeferred": "~2.1.0"
|
"JQDeferred": "~2.1.0",
|
||||||
|
"jsondiffpatch": "0.1.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jade": "0.33.x",
|
"jade": "0.33.x",
|
||||||
|
|
|
@ -64,6 +64,7 @@ AchievementSchema.post 'save', -> @constructor.loadAchievements()
|
||||||
|
|
||||||
AchievementSchema.plugin(plugins.NamedPlugin)
|
AchievementSchema.plugin(plugins.NamedPlugin)
|
||||||
AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']})
|
AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']})
|
||||||
|
AchievementSchema.plugin plugins.TranslationCoveragePlugin
|
||||||
|
|
||||||
module.exports = Achievement = mongoose.model('Achievement', AchievementSchema, 'achievements')
|
module.exports = Achievement = mongoose.model('Achievement', AchievementSchema, 'achievements')
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,37 @@ class AchievementHandler extends Handler
|
||||||
modelClass: Achievement
|
modelClass: Achievement
|
||||||
|
|
||||||
# Used to determine which properties requests may edit
|
# Used to determine which properties requests may edit
|
||||||
editableProperties: ['name', 'query', 'worth', 'collection', 'description', 'userField', 'proportionalTo', 'icon', 'function', 'related', 'difficulty', 'category', 'rewards']
|
editableProperties: [
|
||||||
|
'name'
|
||||||
|
'query'
|
||||||
|
'worth'
|
||||||
|
'collection'
|
||||||
|
'description'
|
||||||
|
'userField'
|
||||||
|
'proportionalTo'
|
||||||
|
'icon'
|
||||||
|
'function'
|
||||||
|
'related'
|
||||||
|
'difficulty'
|
||||||
|
'category'
|
||||||
|
'rewards'
|
||||||
|
'i18n'
|
||||||
|
'i18nCoverage'
|
||||||
|
]
|
||||||
|
|
||||||
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
|
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
|
||||||
jsonSchema = require '../../app/schemas/models/achievement.coffee'
|
jsonSchema = require '../../app/schemas/models/achievement.coffee'
|
||||||
|
|
||||||
|
|
||||||
hasAccess: (req) ->
|
hasAccess: (req) ->
|
||||||
req.method is 'GET' or req.user?.isAdmin()
|
req.method in ['GET', 'PUT'] or req.user?.isAdmin()
|
||||||
|
|
||||||
|
hasAccessToDocument: (req, document, method=null) ->
|
||||||
|
method = (method or req.method).toLowerCase()
|
||||||
|
return true if method is 'get'
|
||||||
|
return true if req.user?.isAdmin()
|
||||||
|
return true if method is 'put' and @isJustFillingTranslations(req, document)
|
||||||
|
return
|
||||||
|
|
||||||
get: (req, res) ->
|
get: (req, res) ->
|
||||||
# /db/achievement?related=<ID>
|
# /db/achievement?related=<ID>
|
||||||
|
|
|
@ -7,6 +7,7 @@ Patch = require '../patches/Patch'
|
||||||
User = require '../users/User'
|
User = require '../users/User'
|
||||||
sendwithus = require '../sendwithus'
|
sendwithus = require '../sendwithus'
|
||||||
hipchat = require '../hipchat'
|
hipchat = require '../hipchat'
|
||||||
|
deltasLib = require '../../app/lib/deltas'
|
||||||
|
|
||||||
PROJECT = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
PROJECT = {original: 1, name: 1, version: 1, description: 1, slug: 1, kind: 1, created: 1, permissions: 1}
|
||||||
FETCH_LIMIT = 300
|
FETCH_LIMIT = 300
|
||||||
|
@ -32,10 +33,23 @@ module.exports = class Handler
|
||||||
hasAccess: (req) -> true
|
hasAccess: (req) -> true
|
||||||
hasAccessToDocument: (req, document, method=null) ->
|
hasAccessToDocument: (req, document, method=null) ->
|
||||||
return true if req.user?.isAdmin()
|
return true if req.user?.isAdmin()
|
||||||
|
|
||||||
|
if @modelClass.schema.uses_coco_translation_coverage and (method or req.method).toLowerCase() is 'put'
|
||||||
|
return true if @isJustFillingTranslations(req, document)
|
||||||
|
|
||||||
if @modelClass.schema.uses_coco_permissions
|
if @modelClass.schema.uses_coco_permissions
|
||||||
return document.hasPermissionsForMethod?(req.user, method or req.method)
|
return document.hasPermissionsForMethod?(req.user, method or req.method)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
isJustFillingTranslations: (req, document) ->
|
||||||
|
differ = deltasLib.makeJSONDiffer()
|
||||||
|
omissions = ['original'].concat(deltasLib.DOC_SKIP_PATHS)
|
||||||
|
delta = differ.diff(_.omit(document.toObject(), omissions), _.omit(req.body, omissions))
|
||||||
|
flattened = deltasLib.flattenDelta(delta)
|
||||||
|
_.all(flattened, (delta) ->
|
||||||
|
# sometimes coverage gets moved around... allow other changes to happen to i18nCoverage
|
||||||
|
return _.isArray(delta.o) and (('i18n' in delta.dataPath and delta.o.length is 1) or 'i18nCoverage' in delta.dataPath))
|
||||||
|
|
||||||
formatEntity: (req, document) -> document?.toObject()
|
formatEntity: (req, document) -> document?.toObject()
|
||||||
getEditableProperties: (req, document) ->
|
getEditableProperties: (req, document) ->
|
||||||
props = @editableProperties.slice()
|
props = @editableProperties.slice()
|
||||||
|
@ -89,8 +103,29 @@ module.exports = class Handler
|
||||||
|
|
||||||
specialParameters = ['term', 'project', 'conditions']
|
specialParameters = ['term', 'project', 'conditions']
|
||||||
|
|
||||||
|
if @modelClass.schema.uses_coco_translation_coverage and req.query.view is 'i18n-coverage'
|
||||||
|
# TODO: generalize view, project, limit and skip query parameters
|
||||||
|
projection = {}
|
||||||
|
if req.query.project
|
||||||
|
projection[field] = 1 for field in req.query.project.split(',')
|
||||||
|
query = {slug: {$exists: true}, i18nCoverage: {$exists: true}}
|
||||||
|
q = @modelClass.find(query, projection)
|
||||||
|
|
||||||
|
skip = parseInt(req.query.skip)
|
||||||
|
if skip? and skip < 1000000
|
||||||
|
q.skip(skip)
|
||||||
|
|
||||||
|
limit = parseInt(req.query.limit)
|
||||||
|
if limit? and limit < 1000
|
||||||
|
q.limit(limit)
|
||||||
|
|
||||||
|
q.exec (err, documents) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
documents = (@formatEntity(req, doc) for doc in documents)
|
||||||
|
@sendSuccess(res, documents)
|
||||||
|
|
||||||
# If the model uses coco search it's probably a text search
|
# If the model uses coco search it's probably a text search
|
||||||
if @modelClass.schema.uses_coco_search
|
else if @modelClass.schema.uses_coco_search
|
||||||
term = req.query.term
|
term = req.query.term
|
||||||
matchedObjects = []
|
matchedObjects = []
|
||||||
filters = if @modelClass.schema.uses_coco_versions or @modelClass.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}]
|
filters = if @modelClass.schema.uses_coco_versions or @modelClass.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}]
|
||||||
|
|
|
@ -12,6 +12,7 @@ LevelComponentSchema.plugin plugins.PermissionsPlugin
|
||||||
LevelComponentSchema.plugin plugins.VersionedPlugin
|
LevelComponentSchema.plugin plugins.VersionedPlugin
|
||||||
LevelComponentSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'searchStrings', 'description']}
|
LevelComponentSchema.plugin plugins.SearchablePlugin, {searchable: ['name', 'searchStrings', 'description']}
|
||||||
LevelComponentSchema.plugin plugins.PatchablePlugin
|
LevelComponentSchema.plugin plugins.PatchablePlugin
|
||||||
|
LevelComponentSchema.plugin plugins.TranslationCoveragePlugin
|
||||||
LevelComponentSchema.pre('save', (next) ->
|
LevelComponentSchema.pre('save', (next) ->
|
||||||
name = @get('name')
|
name = @get('name')
|
||||||
strings = _.str.humanize(name).toLowerCase().split(' ')
|
strings = _.str.humanize(name).toLowerCase().split(' ')
|
||||||
|
|
|
@ -14,6 +14,7 @@ LevelComponentHandler = class LevelComponentHandler extends Handler
|
||||||
'propertyDocumentation'
|
'propertyDocumentation'
|
||||||
'configSchema'
|
'configSchema'
|
||||||
'name'
|
'name'
|
||||||
|
'i18nCoverage'
|
||||||
]
|
]
|
||||||
|
|
||||||
getEditableProperties: (req, document) ->
|
getEditableProperties: (req, document) ->
|
||||||
|
|
|
@ -28,6 +28,7 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
'banner'
|
'banner'
|
||||||
'employerDescription'
|
'employerDescription'
|
||||||
'terrain'
|
'terrain'
|
||||||
|
'i18nCoverage'
|
||||||
]
|
]
|
||||||
|
|
||||||
postEditableProperties: ['name']
|
postEditableProperties: ['name']
|
||||||
|
|
|
@ -36,13 +36,22 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
||||||
'featureImage'
|
'featureImage'
|
||||||
'spriteType'
|
'spriteType'
|
||||||
'i18nCoverage'
|
'i18nCoverage'
|
||||||
|
'i18n'
|
||||||
|
'description'
|
||||||
]
|
]
|
||||||
|
|
||||||
hasAccess: (req) ->
|
hasAccess: (req) ->
|
||||||
req.method is 'GET' or req.user?.isAdmin()
|
req.method in ['GET', 'PUT'] or req.user?.isAdmin()
|
||||||
|
|
||||||
|
hasAccessToDocument: (req, document, method=null) ->
|
||||||
|
method = (method or req.method).toLowerCase()
|
||||||
|
return true if method is 'get'
|
||||||
|
return true if req.user?.isAdmin()
|
||||||
|
return true if method is 'put' and @isJustFillingTranslations(req, document)
|
||||||
|
return
|
||||||
|
|
||||||
get: (req, res) ->
|
get: (req, res) ->
|
||||||
if req.query.view in ['items', 'heroes']
|
if req.query.view in ['items', 'heroes', 'i18n-coverage']
|
||||||
projection = {}
|
projection = {}
|
||||||
if req.query.project
|
if req.query.project
|
||||||
projection[field] = 1 for field in req.query.project.split(',')
|
projection[field] = 1 for field in req.query.project.split(',')
|
||||||
|
@ -52,7 +61,19 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
||||||
else if req.query.view is 'heroes'
|
else if req.query.view is 'heroes'
|
||||||
query.kind = 'Unit'
|
query.kind = 'Unit'
|
||||||
query.original = {$in: _.values heroes} # TODO: replace with some sort of ThangType property later
|
query.original = {$in: _.values heroes} # TODO: replace with some sort of ThangType property later
|
||||||
ThangType.find(query, projection).exec (err, documents) =>
|
else if req.query.view is 'i18n-coverage'
|
||||||
|
query.i18nCoverage = {$exists: true}
|
||||||
|
|
||||||
|
q = ThangType.find(query, projection)
|
||||||
|
skip = parseInt(req.query.skip)
|
||||||
|
if skip? and skip < 1000000
|
||||||
|
q.skip(skip)
|
||||||
|
|
||||||
|
limit = parseInt(req.query.limit)
|
||||||
|
if limit? and limit < 1000
|
||||||
|
q.limit(limit)
|
||||||
|
|
||||||
|
q.exec (err, documents) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
documents = (@formatEntity(req, doc) for doc in documents)
|
documents = (@formatEntity(req, doc) for doc in documents)
|
||||||
@sendSuccess(res, documents)
|
@sendSuccess(res, documents)
|
||||||
|
|
|
@ -124,7 +124,7 @@ module.exports.getTwoGames = (req, res) ->
|
||||||
#if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.')
|
#if userIsAnonymous req then return errors.unauthorized(res, 'You need to be logged in to get games.')
|
||||||
humansGameID = req.body.humansGameID
|
humansGameID = req.body.humansGameID
|
||||||
ogresGameID = req.body.ogresGameID
|
ogresGameID = req.body.ogresGameID
|
||||||
ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span', 'dueling-grounds', 'cavern-survival']
|
ladderGameIDs = ['greed', 'criss-cross', 'brawlwood', 'dungeon-arena', 'gold-rush', 'sky-span', 'dueling-grounds', 'cavern-survival', 'intuit-tf2014']
|
||||||
levelID = _.sample ladderGameIDs
|
levelID = _.sample ladderGameIDs
|
||||||
unless ogresGameID and humansGameID
|
unless ogresGameID and humansGameID
|
||||||
#fetch random games here
|
#fetch random games here
|
||||||
|
|
|
@ -14,6 +14,7 @@ config = require './server_config'
|
||||||
auth = require './server/routes/auth'
|
auth = require './server/routes/auth'
|
||||||
UserHandler = require './server/users/user_handler'
|
UserHandler = require './server/users/user_handler'
|
||||||
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
||||||
|
global.jsondiffpatch = require 'jsondiffpatch'
|
||||||
|
|
||||||
productionLogging = (tokens, req, res) ->
|
productionLogging = (tokens, req, res) ->
|
||||||
status = res.statusCode
|
status = res.statusCode
|
||||||
|
|
|
@ -76,5 +76,6 @@ describe 'lib/FacebookHandler.coffee', ->
|
||||||
expect(params.gender).toBe(mockMe.gender)
|
expect(params.gender).toBe(mockMe.gender)
|
||||||
expect(params.email).toBe(mockMe.email)
|
expect(params.email).toBe(mockMe.email)
|
||||||
expect(params.facebookID).toBe(mockMe.id)
|
expect(params.facebookID).toBe(mockMe.id)
|
||||||
expect(request.method).toBe('PATCH')
|
#expect(request.method).toBe('PATCH') # PATCHes are PUTs until more proxy servers recognize PATCH
|
||||||
|
expect(request.method).toBe('PUT')
|
||||||
expect(_.string.startsWith(request.url, '/db/user/12345')).toBeTruthy()
|
expect(_.string.startsWith(request.url, '/db/user/12345')).toBeTruthy()
|
||||||
|
|
Loading…
Reference in a new issue