This commit is contained in:
GlenDC 2014-01-16 22:27:09 +01:00
commit 553ad833ed
51 changed files with 1023 additions and 843 deletions

View file

@ -92,6 +92,7 @@ module.exports = class God
for scriptNote in @world.scriptNotes
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
@firstWorld = false
@testWorld = null
getUserCodeMap: ->
userCodeMap = {}

View file

@ -4,6 +4,7 @@ AudioPlayer = require 'lib/AudioPlayer'
LevelSession = require 'models/LevelSession'
ThangType = require 'models/ThangType'
app = require 'application'
World = require 'lib/world/world'
# This is an initial stab at unifying loading and setup into a single place which can
# monitor everything and keep a LoadingScreen visible overall progress.
@ -70,17 +71,17 @@ module.exports = class LevelLoader extends CocoClass
onSupermodelLoadedOne: (e) =>
@notifyProgress()
if e.model.type() is 'ThangType'
thangType = e.model
options = {async: true}
if thangType.get('name') is 'Wizard'
options.colorConfig = me.get('wizard')?.colorConfig or {}
building = thangType.buildSpriteSheet options
if building
@spriteSheetsToBuild += 1
thangType.on 'build-complete', =>
@spriteSheetsBuilt += 1
@notifyProgress()
# if e.model.type() is 'ThangType'
# thangType = e.model
# options = {async: true}
# if thangType.get('name') is 'Wizard'
# options.colorConfig = me.get('wizard')?.colorConfig or {}
# building = thangType.buildSpriteSheet options
# if building
# @spriteSheetsToBuild += 1
# thangType.on 'build-complete', =>
# @spriteSheetsBuilt += 1
# @notifyProgress()
onSupermodelLoadedAll: =>
@trigger 'loaded-supermodel'
@ -112,6 +113,48 @@ module.exports = class LevelLoader extends CocoClass
tempSession.save(patch, {patch: true})
@sessionDenormalized = true
# World init
initWorld: ->
return if @world
@world = new World @level.get('name')
serializedLevel = @level.serialize(@supermodel)
@world.loadFromLevel serializedLevel, false
@buildSpriteSheets()
buildSpriteSheets: ->
thangTypes = {}
thangTypes[tt.get('name')] = tt for tt in @supermodel.getModels(ThangType)
colorConfigs = @world.getTeamColors()
thangsProduced = {}
baseOptions = {resolutionFactor: 4, async: true}
for thang in @world.thangs
continue unless thang.spriteName
thangType = thangTypes[thang.spriteName]
options = thang.getSpriteOptions(colorConfigs)
options.async = true
thangsProduced[thang.spriteName] = true
@buildSpriteSheet(thangType, options)
for thangName, thangType of thangTypes
continue if thangsProduced[thangName]
thangType.spriteOptions = {resolutionFactor: 4, async: true}
@buildSpriteSheet(thangType, thangType.spriteOptions)
buildSpriteSheet: (thangType, options) ->
if thangType.get('name') is 'Wizard'
options.colorConfig = me.get('wizard')?.colorConfig or {}
building = thangType.buildSpriteSheet options
return unless building
console.log 'Building:', thangType.get('name'), options
@spriteSheetsToBuild += 1
thangType.on 'build-complete', =>
@spriteSheetsBuilt += 1
@notifyProgress()
# Initial Sound Loading
loadAudio: ->
@ -159,7 +202,9 @@ module.exports = class LevelLoader extends CocoClass
notifyProgress: ->
Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress()
@trigger 'ready-to-init-world' if @allDone()
@initWorld() if @allDone()
# @trigger 'ready-to-init-world' if @allDone()
@trigger 'loaded-all' if @progress() is 1
destroy: ->
@supermodel.off 'loaded-one', @onSupermodelLoadedOne

View file

@ -72,10 +72,11 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
toString: -> "<CocoSprite: #{@thang?.id}>"
spriteSheetKey: ->
"#{@thangType.get('name')} - #{@options.resolutionFactor}"
buildSpriteSheet: -> @thangType.getSpriteSheet @options
buildSpriteSheet: ->
options = @thang?.getSpriteOptions() or {}
options.colorConfig = @options.colorConfig if @options.colorConfig
options.async = false
@thangType.getSpriteSheet options
buildFromSpriteSheet: (spriteSheet) ->
if spriteSheet

View file

@ -115,10 +115,9 @@ module.exports = class Mark extends CocoClass
buildSprite: ->
#console.log "building", @name, "with thangtype", @thangType
options = resolutionFactor: 4
CocoSprite = require './CocoSprite'
markSprite = new CocoSprite @thangType, options
markSprite.queueAction "idle"
markSprite = new CocoSprite @thangType, @thangType.spriteOptions
markSprite.queueAction 'idle'
@mark = markSprite.displayObject
update: (pos=null) ->

View file

@ -87,7 +87,7 @@ module.exports = class SpriteBoss extends CocoClass
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection")
createSpriteOptions: (options) ->
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible, world: @world
createIndieSprites: (indieSprites, withWizards) ->
unless @indieSprites
@ -179,15 +179,11 @@ module.exports = class SpriteBoss extends CocoClass
wallSprites = (sprite for thangID, sprite of @sprites when sprite.thangType?.get('name') is 'Dungeon Wall')
walls = (sprite.thang for sprite in wallSprites)
@world.calculateBounds()
if @wallGrid and @wallGrid.width is @world.width and @wallGrid.height is @world.height
@wallGrid.update walls
else
@wallGrid = new Grid walls, @world.size()...
wallGrid = new Grid walls, @world.size()...
for wallSprite in wallSprites
wallSprite.updateActionDirection @wallGrid
wallSprite.updateActionDirection wallGrid
wallSprite.updateScale()
wallSprite.updatePosition()
# TODO: there's some bug whereby a new wall isn't drawn properly when cached the first time
#console.log @wallGrid.toString()
@spriteLayers["Obstacle"].uncache() if @spriteLayers["Obstacle"].cacheID # might have changed sizes
@spriteLayers["Obstacle"].cache()

View file

@ -36,7 +36,7 @@ module.exports = class Thang
Thang.lastIDNums[spriteName] = if Thang.lastIDNums[spriteName]? then Thang.lastIDNums[spriteName] + 1 else 0
id = spriteName + (Thang.lastIDNums[spriteName] or '')
id
@resetThangIDs: -> Thang.lastIDNums = {}
constructor: (@world, @spriteName, @id) ->
@ -156,4 +156,11 @@ module.exports = class Thang
for prop, val of o.finalState
# TODO: take some (but not all) of deserialize logic from ThangState to handle other types
t[prop] = val
t
t
getSpriteOptions: ->
colorConfigs = @world?.getTeamColors() or {}
options = {}
if @team and colorConfigs[@team]
options.colorConfig = {team: colorConfigs[@team]}
options

View file

@ -252,10 +252,9 @@ module.exports = class World
# Code hotspot; optimize it
if @frames.length < @totalFrames then worldShouldBeOverBeforeSerialization
[transferableObjects, nontransferableObjects] = [0, 0]
o = {name: @name, totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}}
o = {name: @name, totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}}
o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or []
o[prop] = @[prop] for prop in @trackedProperties or []
for thangID, methods of @userCodeMap
serializedMethods = o.userCodeMap[thangID] = {}
for methodName, method of methods
@ -348,7 +347,8 @@ module.exports = class World
perf.t0 = now()
w = new World o.name, o.userCodeMap, classMap
[w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory]
[w.showCoordinates, w.showGrid, w.showPaths, w.indieSprites] = [o.showCoordinates, o.showGrid, o.showPaths, o.indieSprites]
w[prop] = val for prop, val of o.trackedProperties
perf.t1 = now()
w.thangs = (Thang.deserialize(thang, w, classMap) for thang in o.thangs)
w.setThang thang for thang in w.thangs
@ -457,3 +457,9 @@ module.exports = class World
lastAction = action
@actionsForThangCache[cacheKey] = actions
return actions
getTeamColors: ->
teamConfigs = @teamConfigs or {}
colorConfigs = {}
colorConfigs[teamName] = config.color for teamName, config of teamConfigs
colorConfigs

View file

@ -203,6 +203,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
tome_select_a_thang: "Select Someone for "
tome_available_spells: "Available Spells"
hud_continue: "Continue (press shift-space)"
spell_saved: "Spell Saved"
admin:
av_title: "Admin Views"

View file

@ -1,76 +1,76 @@
module.exports = nativeDescription: "فارسی", englishDescription: "Persian", translation:
common:
loading: "Loading..."
# saving: "Saving..."
# sending: "Sending..."
loading: "...در حال بارگذاری"
saving: "...در حال ذخیره سازی"
sending: "...در حال ارسال"
# modal:
# close: "Close"
# okay: "Okay"
modal:
close: "بستن"
okay: "تایید"
# not_found:
# page_not_found: "Page not found"
not_found:
page_not_found: "صفحه پیدا نشد"
# nav:
# sign_up: "Create Account"
# log_in: "Log In"
# log_out: "Log Out"
# play: "Levels"
# editor: "Editor"
# blog: "Blog"
# forum: "Forum"
# admin: "Admin"
# home: "Home"
# contribute: "Contribute"
# legal: "Legal"
# about: "About"
# contact: "Contact"
# twitter_follow: "Follow"
# employers: "Employers"
nav:
sign_up: "ایجاد حساب کاربری"
log_in: "ورود"
log_out: "خروج"
play: "سطوح"
editor: "ویرایشگر"
blog: "بلاگ"
forum: "انجمن"
admin: "مدیر"
home: "خانه"
contribute: "همکاری"
legal: "موارد قانونی"
about: "درباره"
contact: "تماس "
twitter_follow: "دنبال کردن"
employers: "Employers"
# forms:
# name: "Name"
# email: "Email"
# message: "Message"
# cancel: "Cancel"
# save: "Save"
forms:
name: "نام"
email: "ایمیل"
message: "پیام"
cancel: "لغو"
save: "ذخیره "
# versions:
# save_version_title: "Save New Version"
# commit_message: "Commit Message"
# new_major_version: "New Major Version"
# cla_prefix: "To save changes, first you must agree to our"
# cla_url: "CLA"
# cla_suffix: "."
# cla_agree: "I AGREE"
versions:
save_version_title: "ذخیره کردن نسخه جدید"
commit_message: "Commit Message"
new_major_version: "New Major Version"
cla_prefix: "To save changes, first you must agree to our"
cla_url: "CLA"
cla_suffix: "."
cla_agree: "موافقم"
# login:
# login_modal_title: "Log In"
# log_in: "Log In"
# sign_up: "create new account"
# or: ", or "
# recover: "recover account"
login:
login_modal_title: "ورود"
log_in: "ورود"
sign_up: "ایجاد حساب کاربری"
or: ", یا "
recover: "بازیابی حساب کاربری"
# recover:
# recover_account_title: "Recover Account"
# send_password: "Send Recovery Password"
recover:
recover_account_title: "بازیابی حساب کاربری"
send_password: "ارسال رمز عبور بازیابی شده"
# signup:
# create_account_title: "Create Account to Save Progress"
# description: "It's free. Just need a couple things and you'll be good to go:"
# email_announcements: "Receive announcements by email"
# coppa: "13+ or non-USA "
# coppa_why: "(Why?)"
# creating: "Creating Account..."
# sign_up: "Sign Up"
# or: "or "
# log_in: "log in with password"
signup:
create_account_title: "ایجاد حساب کاربری برای ذخیره سازی پیشرفت ها"
description: ":ثبت نام رایگان است و برای شروع مقداری اطلاعات لازم داریم"
email_announcements: "دریافت اطلاعیه ها توسط ایمیل"
coppa: "حداقل ۱۳ سال دارم یا امریکایی نیستم"
coppa_why: "(چرا؟)"
creating: "...در حال ایجاد حساب کاربری"
sign_up: "ثبت نام"
or: " یا"
log_in: "ورود به وسیله رمز عبور"
# home:
# slogan: "Learn to Code JavaScript by Playing a Game"
# no_ie: "CodeCombat does not run in Internet Explorer 9 or older. Sorry!"
# no_mobile: "CodeCombat wasn't designed for mobile devices and may not work!"
# play: "Play"
home:
slogan: "کد نویسی به زبان جاوااسکریپت را با بازی بیاموزید"
no_ie: "متاسفیم اما بازی بر روی مرورگر های اینترنت اکسپلورر نسخه ۹ به قبل اجرا نمی شود"
no_mobile: "این بازی برای دستگاه های موبایل طراحی نشده است و بر روی آن ها اجرا نمی شود"
play: "شروع بازی"
# play:
# choose_your_level: "Choose Your Level"

View file

@ -1,15 +1,15 @@
module.exports = nativeDescription: "français", englishDescription: "French", translation:
common:
loading: "Chargement..."
# saving: "Saving..."
# sending: "Sending..."
saving: "Sauvegarde..."
sending: "Envoi..."
modal:
close: "Fermer"
okay: "Ok"
not_found:
page_not_found: "Page absente"
page_not_found: "Page non trouvée"
nav:
sign_up: "Créer un compte"
@ -26,37 +26,37 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
about: "À propos"
contact: "Contact"
twitter_follow: "Suivre"
# employers: "Employers"
employers: "Employeurs"
forms:
name: "Nom"
email: "Courriel"
message: "Message"
cancel: "Annuler"
# save: "Save"
save: "Sauvegarder"
# versions:
# save_version_title: "Save New Version"
# commit_message: "Commit Message"
# new_major_version: "New Major Version"
# cla_prefix: "To save changes, first you must agree to our"
# cla_url: "CLA"
# cla_suffix: "."
# cla_agree: "I AGREE"
versions:
save_version_title: "Enregistrer une nouvelle version"
commit_message: "Message de mise à jour"
new_major_version: "Nouvelle version majeure"
cla_prefix: "Pour enregistrer vos modifications vous devez d'abord accepter notre"
cla_url: "Licence de Copyright"
cla_suffix: "."
cla_agree: "J'accepte"
login:
# login_modal_title: "Log In"
login_modal_title: "Connexion"
log_in: "Connexion"
sign_up: "Créer un nouveau compte"
or: ", ou "
recover: "récupérer son compte"
# recover:
# recover_account_title: "Recover Account"
# send_password: "Send Recovery Password"
recover:
recover_account_title: "Récupérer son compte"
send_password: "Envoyer le mot de passe de récupération"
signup:
# create_account_title: "Create Account to Save Progress"
create_account_title: "Créer un compte pour sauvegarder votre progression"
description: "C'est gratuit. Simplement quelques informations et vous pourrez commencer :"
email_announcements: "Recevoir les annonces par courriel"
coppa: "13+ ou hors É-U"
@ -67,24 +67,24 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
log_in: "se connecter avec votre mot de passe"
home:
slogan: "Appenez à coder en JavaScript en jouant"
slogan: "Apprenez à coder en JavaScript en jouant"
no_ie: "CodeCombat ne fonctionnera pas sous Internet Explorer 9 ou moins. Désolé !"
no_mobile: "CodeCombat n'a pas été créé pour les plateformes mobile donc il est possible qu'il ne fonctionne pas correctement ! "
play: "Jouer"
play:
choose_your_level: "Choisir Votre Niveau"
choose_your_level: "Choisissez votre niveau"
adventurer_prefix: "Vous pouvez passer à n'importe quel niveau ci-dessous, ou discuter des niveaux sur "
adventurer_forum: "le forum de l'Aventurier"
adventurer_suffix: "."
campaign_beginner: "Campagne du Débutant"
campaign_beginner_description: "... dans laquelle vous apprendrez le côté magique de la programmation."
campaign_beginner_description: "... dans laquelle vous apprendrez la magie de la programmation."
campaign_dev: "Niveaux aléatoires plus difficiles"
campaign_dev_description: "... dans lesquels vous apprendrez à utiliser l'interface en faisant quelque chose d'un petit peu plus dur."
campaign_multiplayer: "Campagne multi-joueurs"
campaign_multiplayer_description: "... dans laquelle vous codez en face à face contre d'autre joueurs."
campaign_player_created: "Joueur créé"
campaign_player_created_description: "... Dans laquelle vous vous battez contre la créativité de votre compagnon <a href=\"/contribute#artisan\">Artisan Wizards</a>."
campaign_player_created_description: "... Dans laquelle vous serz confrontés à la créativité des votres.<a href=\"/contribute#artisan\">Artisan Wizards</a>."
level_difficulty: "Difficulté: "
contact:
@ -93,7 +93,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
contribute_prefix: "Si vous voulez contribuer, consultez notre "
contribute_page: "page des contributions"
contribute_suffix: "!"
forum_prefix: "Pour toute chose publique, veuillez essayer "
forum_prefix: "Pour tout sujet d'ordre publique, merci d'utiliser "
forum_page: "notre forum"
forum_suffix: " à la place."
sending: "Envoi..."
@ -107,9 +107,9 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
learn_more: "Apprenez en plus sur les Traducteurs"
subscribe_as_diplomat: "Souscrire en tant que traducteur"
# wizard_settings:
# title: "Wizard Settings"
# customize_avatar: "Customize Your Avatar"
wizard_settings:
title: "Paramètres du Wizard"
customize_avatar: "Personnaliser votre avatar"
account_settings:
title: "Préférences du compte"
@ -123,7 +123,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
gravatar_select: "Sélectionnez la photo Gravatar à utiliser"
gravatar_add_photos: "Ajouter des vignettes et des photos sur un compte Gravatar pour votre e-mail pour choisir une image."
gravatar_add_more_photos: "Ajouter plus de photos à votre compte Gravatar pour y accéder ici."
wizard_color: "Couleur des vêtements du magicien"
wizard_color: "Couleur des vêtements du Magicien"
new_password: "Nouveau mot de passe"
new_password_verify: "Vérifier"
email_subscriptions: "Abonnements"
@ -131,35 +131,35 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
email_announcements_description: "Recevoir des mails sur les dernières actualités et sur le développement de CodeCombat."
contributor_emails: "Emails des contributeurs"
contribute_prefix: "Nous recherchons des personnes pour se joindre à notre groupe! Consultez la "
contribute_page: "page de contrubition"
contribute_page: "page de contribution"
contribute_suffix: " pour en savoir plus."
email_toggle: "Tout basculer"
saving: "Enregistrement..."
error_saving: "Probleme d'enregistrement"
saved: "Changement sauvegardés"
password_mismatch: "Le mot de passe ne corresponds pas."
password_mismatch: "Le mot de passe ne correspond pas."
account_profile:
edit_settings: "Editer les préférences"
profile_for_prefix: "Profil pour "
# profile_for_suffix: ""
profile_for_suffix: ""
profile: "Profil"
user_not_found: "Aucun utilisateur trouvé. Vérifier l'URL?"
gravatar_not_found_mine: "Nous n'avons pas pu trouver votre profil associé à: "
# gravatar_not_found_email_suffix: "."
gravatar_not_found_email_suffix: "."
gravatar_signup_prefix: "S'incrire à "
gravatar_signup_suffix: " to get set up!"
gravatar_signup_suffix: "pour commencer !"
gravatar_not_found_other: "Hélas, il n'y a pas de profil associé à l'adresse électronique de cette personne."
gravatar_contact: "Contact"
gravatar_websites: "Sites"
gravatar_accounts: "As Seen On"
gravatar_profile_link: "Profil Gravatar coplet"
gravatar_profile_link: "Profil Gravatar complet"
play_level:
level_load_error: "Le niveau ne peut pas être chargé."
done: "Fait"
grid: "Grille"
customize_wizard: "Customiser le magicien"
customize_wizard: "Personnaliser le magicien"
home: "Accueil"
guide: "Guide"
multiplayer: "Multijoueur"
@ -167,297 +167,300 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
goals: "Objectifs"
action_timeline: "Action sur la ligne de temps"
click_to_select: "Clique sur une unité pour la sélectionner."
reload_title: "Recharger tous les codes?"
reload_title: "Recharger tout le code?"
reload_really: "Êtes-vous sûr de vouloir recharger ce niveau et retourner au début?"
reload_confirm: "Tout recharger"
# victory_title_prefix: ""
victory_title_prefix: ""
victory_title_suffix: " Terminé"
victory_sign_up: "Inscrivez-vous pour les mises à jour"
victory_sign_up_poke: "Vous voulez les dernieres actualités par mail? Créez un compte gratuitement et nous vous tiendrons au courant!"
victory_sign_up: "Inscrivez-vous pour recevoir les mises à jour"
victory_sign_up_poke: "Vous voulez recevoir les dernières actualités par mail? Créez un compte gratuitement et nous vous tiendrons informés!"
victory_rate_the_level: "Notez ce niveau: "
victory_play_next_level: "Jouer au prochain niveau"
victory_go_home: "Retourner à l'&ccueil"
victory_review: "Dites-nous en plus!"
victory_hour_of_code_done: "Êtes-vous prêts"
victory_hour_of_code_done_yes: "Oui, J'en ai terminé avec mon heure de code!"
victory_hour_of_code_done: "Déjà fini ?"
victory_hour_of_code_done_yes: "Oui, j'ai fini mon heure de code!"
multiplayer_title: "Préférences multijoueurs"
multiplayer_link_description: "Donnez ce lien à tous ceux qui pourraient nous rejoindre."
multiplayer_link_description: "Partage ce lien pour que tes amis viennent jouer avec toi."
multiplayer_hint_label: "Astuce:"
multiplayer_hint: " Cliquez sur le lien pour tout sélectionner, puis appuyer sur Pomme-C ou Ctrl-C pour copier le lien."
multiplayer_coming_soon: "Plus de fonctionnalités multijoueurs sont à venir"
guide_title: "Guide"
tome_minion_spells: "Les sorts de vos larbins"
tome_minion_spells: "Les sorts de vos soldats"
tome_read_only_spells: "Sorts en lecture-seule"
tome_other_units: "Autres unités"
tome_cast_button_castable: "Cast"
tome_cast_button_casting: "Casting"
tome_cast_button_cast: "Spell Cast"
tome_autocast_delay: "Delai d'autocast"
tome_cast_button_castable: "Jeter le sort"
tome_cast_button_casting: "Sort en court"
tome_cast_button_cast: "Sort jeté"
tome_autocast_delay: "Temps de recharge"
tome_autocast_1: "1 seconde"
tome_autocast_3: "3 secondes"
tome_autocast_5: "5 secondes"
tome_autocast_manual: "Manuel"
tome_select_spell: "Choisissez un sort"
tome_select_a_thang: "Sélectionnez quelqu'un pour "
tome_select_a_thang: "Sélectionnez une unité pour"
tome_available_spells: "Sorts diponibles"
hud_continue: "Continuer (appuie sur shift-espace)"
# admin:
# av_title: "Admin Views"
# av_entities_sub_title: "Entities"
# av_entities_users_url: "Users"
# av_entities_active_instances_url: "Active Instances"
# av_other_sub_title: "Other"
# av_other_debug_base_url: "Base (for debugging base.jade)"
# u_title: "User List"
# lg_title: "Latest Games"
admin:
av_title: "Vues d'administrateurs"
av_entities_sub_title: "Entités"
av_entities_users_url: "Utilisateurs"
av_entities_active_instances_url: "Instances actives"
av_other_sub_title: "Autre"
av_other_debug_base_url: "Base (pour debugger base.jade)"
u_title: "Liste des utilisateurs"
lg_title: "Dernières parties"
editor:
# main_title: "CodeCombat Editors"
# main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!"
# article_title: "Article Editor"
# article_description: "Write articles that give players overviews of programming concepts which can be used across a variety of levels and campaigns."
# thang_title: "Thang Editor"
# thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics."
# level_title: "Level Editor"
# level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!"
# security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, "
# contact_us: "contact us!"
# hipchat_prefix: "You can also find us in our"
# hipchat_url: "HipChat room."
main_title: "Editeurs CodeCombat"
main_description: "Créé tes propres niveaux, campagnes, unités et contenu éducatif. Nous vous fournissons tous les outils dont vous avez besoin!"
article_title: "Editeur d'article"
article_description: "Ecris des articles qui donnent aux joueurs un aperçu des concepts de programmation qui peuvent être utilisés dans différents niveaux et campagnes."
thang_title: "Editeur Thang"
thang_description: "Créé des unités, definis leur comportement de base, graphisme et son. Pour l'instant ne supporte que l'importation d'images vectorielles exportées de Flash."
level_title: "Editeur de niveau"
level_description: "Inclus les outils de script, le téléversement de vidéos, et l'élaboration de logiques personnalisées pour créer toute sorte de niveaux. Tout ce que nous utilisons nous même!"
security_notice: "Plusieurs caractèristiques majeurs de cet éditeur ne sont pas encore activées par défaut. Quand nous aurons améliorés la sécurité de ces éléments, ils seront rendus disponibles. Si vous voulez les utiliser plus rapidement, "
contact_us: "contactez nous!"
hipchat_prefix: "Vous pouvez aussi nous trouver dans notre "
hipchat_url: "conversation HipChat."
level_btn_save: "Sauver"
# level_btn_fork: "Fork"
# level_btn_play: "Play"
# level_some_options: "Some Options?"
# level_options_1_second: "1 second"
# level_options_3_seconds: "3 seconds"
# level_options_5_seconds: "5 seconds"
# level_options_manual: "Manual"
# level_tab_thangs: "Thangs"
# level_tab_scripts: "Scripts"
# level_tab_settings: "Settings"
# level_tab_components: "Components"
# level_tab_systems: "Systems"
# level_tab_thangs_title: "Current Thangs"
# level_tab_thangs_conditions: "Starting Conditions"
# level_tab_thangs_add: "Add Thangs"
# level_settings_title: "Settings"
# level_component_tab_title: "Current Components"
# level_component_btn_new: "Create New Component"
# level_systems_tab_title: "Current Systems"
# level_systems_btn_new: "Create New System"
# level_systems_btn_add: "Add System"
# level_components_title: "Back to All Thangs"
# level_components_type: "Type"
level_btn_fork: "Fork"
level_btn_play: "Jouer"
level_some_options: "Quelques options?"
level_options_1_second: "1 seconde"
level_options_3_seconds: "3 secondes"
level_options_5_seconds: "5 secondes"
level_options_manual: "Manuel"
level_tab_thangs: "Thangs"
level_tab_scripts: "Scripts"
level_tab_settings: "Paramètres"
level_tab_components: "Composants"
level_tab_systems: "Systemes"
level_tab_thangs_title: "Thangs actuels"
level_tab_thangs_conditions: "Conditions de départ"
level_tab_thangs_add: "ajouter des Thangs"
level_settings_title: "Paramètres"
level_component_tab_title: "Composants actuels"
level_component_btn_new: "Créer un nouveau composant"
level_systems_tab_title: "Systemes actuels"
level_systems_btn_new: "Créer un nouveau système"
level_systems_btn_add: "Ajouter un système"
level_components_title: "Retourner à tous les Thangs"
level_components_type: "Type"
level_component_edit_title: "Editer le composant"
# level_system_edit_title: "Edit System"
# create_system_title: "Create New System"
# create_system_field_name: "Name"
# create_system_btn_cancel: "Cancel"
# create_system_btn_create: "Create"
level_system_edit_title: "Editer le systeme"
create_system_title: "Créer un nouveau système"
create_system_field_name: "Nom"
create_system_btn_cancel: "Annuler"
create_system_btn_create: "Créer"
new_component_title: "Créer un nouveau composant"
new_component_field_system: "Système"
new_component_field_name: "Nom"
new_component_btn_cancel: "Annuler"
new_component_btn_create: "Créer"
# level:
# index_table_results: "Results"
# index_table_name: "Name"
# index_table_description: "Description"
# index_table_version: "Version"
level:
index_table_results: "Résultats"
index_table_name: "Nom"
index_table_description: "Description"
index_table_version: "Version"
# article:
# index_table_results: "Results"
# index_table_name: "Name"
# index_table_body: "Body"
# index_table_version: "Version"
# edit_btn_preview: "Preview"
# edit_btn_save: "Save"
# edit_article_title: "Edit Article"
article:
index_table_results: "Résultats"
index_table_name: "Nom"
index_table_body: "Corps"
index_table_version: "Version"
edit_btn_save: "Sauvegarder"
edit_btn_preview: "Prévisualiser"
edit_article_title: "Editer l'article"
# general:
# and: "and"
# about:
# who_is_codecombat: "Who is CodeCombat?"
# why_codecombat: "Why CodeCombat?"
# who_description_prefix: "together started CodeCombat in 2013. We also created "
# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters."
# who_description_ending: "Now it's time to teach people to write code."
# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that."
# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it."
# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like"
# why_paragraph_3_italic: "yay a badge"
# why_paragraph_3_center: "but fun like"
# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!"
# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing."
# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age."
# why_ending: "And hey, it's free. "
# why_ending_url: "Start wizarding now!"
# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere."
# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one."
# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat."
# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy."
# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online."
general:
and: "et"
# legal:
# page_title: "Legal"
# opensource_intro: "CodeCombat is free to play and completely open source."
# opensource_description_prefix: "Check out "
# github_url: "our GitHub"
# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See "
# archmage_wiki_url: "our Archmage wiki"
# opensource_description_suffix: "for a list of the software that makes this game possible."
# practices_title: "Respectful Best Practices"
# practices_description: "These are our promises to you, the player, in slightly less legalese."
# privacy_title: "Privacy"
# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent."
# security_title: "Security"
# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems."
# email_title: "Email"
# email_description_prefix: "We will not inundate you with spam. Through"
# email_settings_url: "your email settings"
# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time."
# cost_title: "Cost"
# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:"
# recruitment_title: "Recruitment"
# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizardnot just in the game, but also in real life."
# url_hire_programmers: "No one can hire programmers fast enough"
# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you"
# recruitment_description_italic: "a lot"
# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan."
# copyrights_title: "Copyrights and Licenses"
# contributor_title: "Contributor License Agreement"
# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"
# cla_url: "CLA"
# contributor_description_suffix: "to which you should agree before contributing."
# code_title: "Code - MIT"
# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the"
# mit_license_url: "MIT license"
# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels."
# art_title: "Art/Music - Creative Commons "
# art_description_prefix: "All common content is available under the"
# cc_license_url: "Creative Commons Attribution 4.0 International License"
# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:"
# art_music: "Music"
# art_sound: "Sound"
# art_artwork: "Artwork"
# art_sprites: "Sprites"
# art_other: "Any and all other non-code creative works that are made available when creating Levels."
# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible."
# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:"
# use_list_1: "If used in a movie or another game, include codecombat.com in the credits."
# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution."
# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any."
# rights_title: "Rights Reserved"
# rights_desc: "All rights are reserved for Levels themselves. This includes"
# rights_scripts: "Scripts"
# rights_unit: "Unit configuration"
# rights_description: "Description"
# rights_writings: "Writings"
# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels."
# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not."
# nutshell_title: "In a Nutshell"
# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening."
# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence."
about:
who_is_codecombat: "Qui est CodeCombat?"
why_codecombat: "Pourquoi CodeCombat?"
who_description_prefix: "ont lancés CodeCombat ensemble en 2013. Nous avons aussi créé "
who_description_suffix: "en 2008, l'améliorant jusqu'au rang de première application web et iOS pour apprendre à écrire les caractères chinois et japonnais."
who_description_ending: "Maintenant nous apprenons aux gens à coder."
why_paragraph_1: "En développant Skritter, George ne savait pas programmer et été frustré de ne pas pouvoir implémenter ses idées. Ensuite, il essaya d'apprendre, mais les cours n'étaient pas assez rapides. Son colocataire, voulant se requalifier et arreter d'apprendre, essaya Codecademy, mais \"s'ennuya.\" Chaque semaine un nouvel ami commença Codecademy, puis abandonna. Nous nous sommes rendus compte que nous avions résolus le même problème avec Skritter: les gens apprennant grâce à des cours lents et intensifs quand nous avons besoin d'expérience rapide et intensive. Nous savons comment remédier à ça."
why_paragraph_2: "Besoin d'apprendre à développer? Vous n'avez pas besoin de cours. Vous avez besoin d'écrire beaucoup de code et de vous amuser en le faisant."
why_paragraph_3_prefix: "C'est ce dont il s'agit en programmation. Ca doit être amusant. Pas amusant comme"
why_paragraph_3_italic: "génial un badge"
why_paragraph_3_center: "mais amusant comme"
why_paragraph_3_italic_caps: "NAN MAMAN JE DOIS FINIR MON NIVEAU!"
why_paragraph_3_suffix: "C'est pourquoi CodeCombat est un jeu multijoueur, pas un cours avec une lesson jouée. Nous n'arrêterons pas avant que vous puissiez plus arrêter--mais cette fois, c'est une bonne chose."
why_paragraph_4: "Si vous devez devenir accro à un jeu, accrochez-vous à celui-ci et devenez un des mages de l'age de la technologie."
why_ending: "Et oh, c'est gratuit. "
why_ending_url: "Commence ton apprentissage maintenant!"
george_description: "PDG, homme d'affaire, web designer, game designer, et champion des programmeurs débutants."
scott_description: "Programmeur extraordinaire, architecte logiciel, assistant cuisinier, et maitre de la finance. Scott est le raisonnable."
nick_description: "Assistant programmeur, mage à la motivation excentrique, and technicien sans dessus-dessous. Nick peut faire n'importe quoi mais il a choisit CodeCombat."
jeremy_description: "Mage de l'assistance client, testeurs de maniabilité, et community manager; vous avez probablement déjà parlé avec Jeremy."
michael_description: "Programmeur, administrateur réseau, and l'enfant prodige du premier cycle, Michael est la personne qui maintient nos serveurs en ligne."
# contribute:
# page_title: "Contributing"
# introduction_desc_intro: "We have high hopes for CodeCombat."
# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, "
# introduction_desc_github_url: "CodeCombat is totally open source"
# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours."
# introduction_desc_ending: "We hope you'll join our party!"
# introduction_desc_signature: "- Nick, George, Scott, Michael, and Jeremy"
# alert_account_message_intro: "Hey there!"
# alert_account_message_pref: "To subscribe for class emails, you'll need to "
# alert_account_message_suf: "first."
# alert_account_message_create_url: "create an account"
# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever."
# class_attributes: "Class Attributes"
# archmage_attribute_1_pref: "Knowledge in "
# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax."
# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you."
# how_to_join: "How To Join"
# join_desc_1: "Anyone can help out! Just check out our "
# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? "
# join_desc_3: ", or find us in our "
# join_desc_4: "and we'll go from there!"
# join_url_email: "Email us"
# join_url_hipchat: "public HipChat room"
# more_about_archmage: "Learn More About Becoming A Powerful Archmage"
# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements."
# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to"
# artisan_introduction_suf: "to then this class might be for you."
# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!"
# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix."
# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!"
# artisan_join_desc: "Use the Level Editor in these steps, give or take:"
# artisan_join_step1: "Read the documentation."
# artisan_join_step2: "Create a new level and explore existing levels."
# artisan_join_step3: "Find us in our public HipChat room for help."
# artisan_join_step4: "Post your levels on the forum for feedback."
# more_about_artisan: "Learn More About Becoming A Creative Artisan"
# artisan_subscribe_desc: "Get emails on level editor updates and announcements."
# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you."
# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though."
# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve."
# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like"
# adventurer_forum_url: "our forum"
# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!"
# more_about_adventurer: "Learn More About Becoming A Brave Adventurer"
# adventurer_subscribe_desc: "Get emails when there are new levels to test."
# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the "
# scribe_introduction_url_mozilla: "Mozilla Developer Network"
# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you."
# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others."
# contact_us_url: "Contact us"
# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!"
# more_about_scribe: "Learn More About Becoming A Diligent Scribe"
# scribe_subscribe_desc: "Get emails about article writing announcements."
# diplomat_introduction_pref: "So, if there's one thing we learned from the "
# diplomat_launch_url: "launch in October"
# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries, particularly Brazil! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you."
# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!"
# diplomat_join_pref: "We've started a lot of initial translations at "
# diplomat_doc_url: "this forum post"
# diplomat_join_suf: "so check it out and add things for your language. Also, check this box below to keep up-to-date on new internationalization developments!"
# more_about_diplomat: "Learn More About Becoming A Great Diplomat"
# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate."
# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you."
# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!"
# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!"
# ambassador_join_note_strong: "Note"
# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!"
# more_about_ambassador: "Learn More About Becoming A Helpful Ambassador"
# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments."
# counselor_introduction_1: "Do you have life experience? A different perspective on things that can help us decide how to shape CodeCombat? Of all these roles, this will probably take the least time, but individually you may make the most difference. We're on the lookout for wisened sages, particularly in areas like: teaching, game development, open source project management, technical recruiting, entrepreneurship, or design."
# counselor_introduction_2: "Or really anything that is relevant to the development of CodeCombat. If you have knowledge and want to share it to help grow this project, then this class might be for you."
# counselor_attribute_1: "Experience, in any of the areas above or something you think might be helpful."
# counselor_attribute_2: "A little bit of free time!"
# counselor_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll put you in our contact list and be in touch when we could use advice (not too often)."
# more_about_counselor: "Learn More About Becoming A Valuable Counselor"
# changes_auto_save: "Changes are saved automatically when you toggle checkboxes."
# diligent_scribes: "Our Diligent Scribes:"
# powerful_archmages: "Our Powerful Archmages:"
# creative_artisans: "Our Creative Artisans:"
# brave_adventurers: "Our Brave Adventurers:"
# translating_diplomats: "Our Translating Diplomats:"
# helpful_ambassadors: "Our Helpful Ambassadors:"
legal:
page_title: "Judiciaire"
opensource_intro: "CodeCombat est complètement gratuit et open source."
opensource_description_prefix: "Regardez "
github_url: "notre GitHub"
opensource_description_center: "et aidez nous si vous voulez! CodeCombat est construit sur plusieurs projets open source, et nous les aimons. Regardez "
archmage_wiki_url: "notre wiki des Archimage"
opensource_description_suffix: "pour trouver la liste des logiciels qui rendent ce jeu possible."
practices_title: "Bonnes pratiques"
practices_description: "Ce sont les promesses que nous vous faisons à vous, le joueur, en jargon un peu juridique."
privacy_title: "Vie privée"
privacy_description: "Nous ne vendrons aucunes de vos informations personnelles. Nous comptons faire de l'argent éventuellement avec le recrutement, mais soyez assuré que nous ne fournir aucunes de vos informations personnelles à des compagnies intéressées sans votre consentement explicite."
security_title: "Sécurité"
security_description: "Nous faisons tout notre possible pour conserver la confidentialité de vos informations personnelles. En tant que projet ope source, notre site est ouvert à tous ceux qui souhaite examiner et améliorer nos systèmes de sécurité."
email_title: "Email"
email_description_prefix: "Nous ne vous innonderons pas d'email. Grâce à"
email_settings_url: "vos paramètres d'email "
email_description_suffix: "ou avec des liens disponibles dans nos emails, vous pouvez changer vos préférences ou vous désinscrire à tous moments."
cost_title: "Coût"
cost_description: "Pour l'instant, CodeCombat est gratuit à 100%! Un de nos principaux objectifs est que ça le reste, pour qu'autant de gens possible puissent y jouer, indépendamment de leu niveaude vie. Si le ciel s'assombrit, nous devrons peut-être rendre les inscriptions payantes ou une partie du contenu, mais nous ne le souhaitons pas. Avec un peu de chance, nous serons capable de soutenir l'entreprise avec :"
recruitment_title: "Recrutement"
recruitment_description_prefix: "Ici chez CodeCombat, vous allez devenir un magicien puissant, pas seulement dans le jeu, mais aussi dans la vie réelle."
url_hire_programmers: "Personne ne peut recruter des développeurs aussi vite"
recruitment_description_suffix: "donc une fois que vous aurez aiguisé votre savoir-faire et si vous l'acceptez, nous montrerons vos meilleurs codes aux milliers d'employeurs qui attendent une chance de vous recruter. Ils nous payent un peu pour ensuite vous payer"
recruitment_description_italic: "beaucoup"
recruitment_description_ending: "le site reste gratuit et tout le monde est content. C'est le plan."
copyrights_title: "Copyrights et Licenses"
contributor_title: "Contributor License Agreement"
contributor_description_prefix: "Toutes contributions, sur le siet et sur le répertoire GitHub, sont sujette à nos"
cla_url: "CLA"
contributor_description_suffix: "auxquels vous devez agréer avant de contribuer."
code_title: "Code - MIT"
code_description_prefix: "Tout code possédé CodeCombat ou hébergé sur codecombat.com, sur le répertoire GitHub ou dans la base de donnée de codecombat.com, est sous la licence"
mit_license_url: "MIT"
code_description_suffix: "Cela inclus le code dans Systèmes et Composants qui sont rendu disponible par CodeCombat dans le but de créer des niveaux."
art_title: "Art/Musique - Creative Commons "
art_description_prefix: "Tout le contenu commun est sous licence"
cc_license_url: "Creative Commons Attribution 4.0 International"
art_description_suffix: "Le contenu commun est tout ce qui est rendu disponible par CodeCombat afin de créer des niveaux. Cela inclus :"
art_music: "la musique"
art_sound: "le son"
art_artwork: "les artwork"
art_sprites: "les sprites"
art_other: "tout le reste du contenu non-code qui est rendu accessible lors de la création de niveaux."
art_access: "Pour l'instant il n'y a aucun système universel et facile pour rassembler ces ressources. En général, accédez y par les URL comme le fait le site, contactez nous pour de l'aide, ou aidez nous à agrandir le site pour rendre ces ressources plus facilement accessibles."
art_paragraph_1: "Pour l'attribution, s'il vous plait, nommez et liez codecombat.com près de la source utilisée ou dans un endroit approprié. Par example:"
use_list_1: "Si utilisé dans un film ou un autre jeu, incluez codecombat.com dans le générique."
use_list_2: "Si utilisé sur un site web, incluez un lien près de l'utilisation, par exemple sous une image, ou sur une page d'attribution générale où vous pourrez aussi mentionner les autres travaux en Creative Commons et les logiciels open source utilisés sur votre site. Quelque chose qui fait clairement référence à CodeCombat, comme un article de blog mentionnant CodeCombat, n'a pas besoin d'attribution séparée."
art_paragraph_2: "Si le contenu utilisé n'est pas créé par CodeCombat mais par un autre utilisateur de codecombat.com, attribuez le à cet utilisateur, et suivez les recommandation fournies dans la ressource de la description s'il y en a."
rights_title: "Droits réservés"
rights_desc: "Toues les droits sont réservés pour les niveaux eux même. Cela inclus"
rights_scripts: "les scripts"
rights_unit: "la configuration unitaire"
rights_description: "la description"
rights_writings: "l'écriture"
rights_media: "les médias (sons, musiques) et tous les autres contenu créatif créés spécialement pour ce niveau et pas rendu généralement accessible en créant des niveaux."
rights_clarification: "Pour clarifier, tout ce qui est rendu accessible dans l'éditeur de niveaux dans le but de créer des niveaux est sous licence CC, tandis que le contenu créé avec l'éditeur de niveaux ou téléversé dans le but de créer un niveau de l'est pas."
nutshell_title: "En un mot"
nutshell_description: "Chaque ressource que nous fournissons dans l'éditeur de niveau est libre d'utilisation pour créer des niveaux. Mais nous nous réservons le droit de restreindre la distribution des niveaux créés (qui sont créés sur codecombat.com) ils peuvent donc devenir payant dans le futur, si c'est ce qui doit arriver."
canonical: "La version de ce document est la version définitive et canonique. En cas d'irrégularités dans les traductions, le document anglais l'emporte."
# classes:
# archmage_title: "Archmage"
# archmage_title_description: "(Coder)"
# artisan_title: "Artisan"
# artisan_title_description: "(Level Builder)"
# adventurer_title: "Adventurer"
# adventurer_title_description: "(Level Playtester)"
# scribe_title: "Scribe"
# scribe_title_description: "(Article Editor)"
# diplomat_title: "Diplomat"
# diplomat_title_description: "(Translator)"
# ambassador_title: "Ambassador"
# ambassador_title_description: "(Support)"
# counselor_title: "Counselor"
# counselor_title_description: "(Expert/Teacher)"
contribute:
page_title: "Contribution"
introduction_desc_intro: "Nous avons beaucoup d'espoir pour CodeCombat."
introduction_desc_pref: "Nous voulons être l'endroit où les développeurs de tous horizons viennent pour apprendre et jouer ensemble, présenter les autres au monde du développement, et reflèter le meilleur de la communauté. Nous ne pouvons et ne voulons pas faire ça seuls ; ce qui rend les projets comme GitHub, Stack Overflow et Linux super est que les gens qui l'utilisent le construisent. Dans ce but, "
introduction_desc_github_url: "CodeCombat est totalement open source"
introduction_desc_suf: ", et nous avons pour objectif de fournir autant de manières possibles pour que vous participiez et fassiez de ce projet autant votre que notre."
introduction_desc_ending: "Nous espérons que vous allez joindre notre aventure!"
introduction_desc_signature: "- Nick, George, Scott, Michael et Jeremy"
alert_account_message_intro: "Et tiens!"
alert_account_message_pref: "Pour s'inscire à la newsletter, vous devez d'abord "
alert_account_message_suf: "."
alert_account_message_create_url: "créer un compte"
archmage_introduction: "L'une des meileurs parties de la création d'un jeu est qu'il regroupe tant de choses différentes. Graphismes, sons, réseau en temps réel, réseaux sociaux, et bien sur bien d'autres aspects de la programmation, de la gestion bas niveau de base de données, et de l'administration de serveur à l'élaboration d'interfaces utilisateur. Il y a tant à faire, et si vous êtes un programmeur expérimenté avec une aspiration à vraiment plonger dans le fond de CodeCombat, cette classe est faite pour vous. Nous aimerions avoir votre aide pour le meileur jeu de développement de tous les temps."
class_attributes: "Attributs de classe"
archmage_attribute_1_pref: "Connaissance en "
archmage_attribute_1_suf: ", ou le désir d'apprendre. La plupart de notre code est écrit avec ce langage. Si vous êtes fan de Ruby ou Python, vous vous sentirez chez vous. C'est du JavaScript, mais avec une syntaxe plus sympatique."
archmage_attribute_2: "De l'expérience en développement et en initiatives personnelles. Nous vous aiderons à vous orienter, mais nous ne pouvons pas passer plus de temps à vous entrainer."
how_to_join: "Comment nous rejoindre"
join_desc_1: "N'importe qui peut aider! Vous avez seulement besoin de regarder "
join_desc_2: "pour commencer, et cocher la case ci-dessous pour vous marquer comme un archimage courageux et obtenir les dernières nouvelles par email. Envie de discuter de ce qu'il y a à faire ou de comment être plus impliqué? "
join_desc_3: ", ou trouvez nous dans nos "
join_desc_4: "et nous partirons de là!"
join_url_email: "Contactez nous"
join_url_hipchat: "conversation publique HipChat"
more_about_archmage: "En apprendre plus sur devenir un puissant archimage"
archmage_subscribe_desc: "Obtenir des emails sur les nouvelles possibilités de développement et des annonces."
artisan_introduction_pref: "Nous devons créer des niveaux additionnels! Les gens veulent plus de contenu, et nous ne pouvons pas tous lse créer nous même. Maintenant votre station de travail est au niveau un ; notre éditeur de niveaux est à peine utilisable même par ces créateurs, donc méfiez vous. Si vous avez des idées sur la boucle for de"
artisan_introduction_suf: "cette classe est faite pour vous."
artisan_attribute_1: "Une expérience dans la création de contenu comme celui-ci serait un plus, comme utiliser l'éditeur de niveaux de Blizzard. Mais ce n'est pas nécessaire!"
artisan_attribute_2: "Vous aspirez à faire beaucoup de tests et d'itérations. Pour faire de bons niveaux, vous aurez besoin de les porposer aux autres et les regarder les jouer, et être prêt à trouver un grand nombre de choses à corriger."
artisan_attribute_3: "Pour l'heure, endurance en par with an Adventurer. Notre éditeur de niveaux est vraiment préliminaire et frustant à l'utilisation. Vous êtes prévenus!"
artisan_join_desc: "Utilisez le créateur de niveaux pour ces étapes, donnez ou prennez :"
artisan_join_step1: "Lire la documentation."
artisan_join_step2: "Créé un nouveau niveau et explore les niveaux existants."
artisan_join_step3: "Retrouvez nous dans notre conversation HipChat pour obtenir de l'aide."
artisan_join_step4: "Postez vos niveaux dans le forum pour avoir des retours."
more_about_artisan: "Learn More About Becoming A Creative Artisan"
artisan_subscribe_desc: "Recevoir des emails sur les annonces et mises à jour de l'éditeur de niveaux."
adventurer_introduction: "Soyons clair à propos de votre role : vous êtes le tank. Vous allez subir beaucoup de dommages. Nous avons besoins de gens pour essayer les nouveaux niveaux et aider à identifier comment améliorer les choses. La douleur sera énorme; faire de bons jeux est une longue tâche et personne n'y arrive du premier coup. Si vous pouvez résister et avez un gros score de constitution, alors cette classe est faite pour vous."
adventurer_attribute_1: "Une soif d'apprendre. Vous voulez apprendre à développer et nous voulons vous apprendre. Vous allez toutefois faire la plupart de l'apprentissage."
adventurer_attribute_2: "Charismatique. Soyez doux mais exprimez vous sur ce qui a besoin d'être amélioré, et faite des propositions sur comment l'améliorer."
adventurer_join_pref: "Soit être ensemble avec (ou recruter!) un artisan et travailler avec lui, ou cocher la case ci-dessous pour recevoir des emails quand il y aura de nouveaux niveaux à tester. Nous parlon aussi des niveaux à réviser sur notre réseau"
adventurer_forum_url: "notre forum"
adventurer_join_suf: "si vous préférez être avertis ainsi, inscrivez-vous ici!"
more_about_adventurer: "En apprendre plus sur devenir un aventurier brave"
adventurer_subscribe_desc: "Recevoir un email lorqu'il y a de nouveaux niveaux à tester."
scribe_introduction_pref: "CodeCombat n'est pas seulement un ramassi de niveaux. Il contiendra aussi des ressources pour la connaissance, un wiki des concepts de programmation que les niveaux pourront illustrer. Dans ce sens au lieu que chaque Artisan ai à décrire en détail ce qu'est un opérateur de comparaison, ils peuvent seulement lier leur niveau à l'article qui les décrit et qui a été écrit pour aider les joueurs. Quelque chose dans le sens de ce que le "
scribe_introduction_url_mozilla: "Mozilla Developer Network"
scribe_introduction_suf: " a développé. Si votre représentation de l'amusement est l'articulation des conceptes de programmation au format Markdown, alors cette classe est pour vous."
scribe_attribute_1: "Une qualification dans les mots est quasiment la seule chose dont vous aurez besoin. Pas seulement la grammaire et l'orthographe, mais être capable de lier des idées ensembles."
contact_us_url: "Contactez nous"
scribe_join_description: "parlez nous un peu de vous, votre expérience en programmation et quels sujets vous souhaitez traiter. Nous partirons de là!"
more_about_scribe: "En apprendre plus sur devenir un scribe dilligent"
scribe_subscribe_desc: "Recevoir des emails sur les annonces d'écriture d'article."
diplomat_introduction_pref: "Si nous avons appris quelque chose du "
diplomat_launch_url: "lancement en octobre"
diplomat_introduction_suf: "c'est la où il y a un intéret mesurable sur CodeCombat dans d'autres pays, particulièrement au Brésil! Nous créons une équipe de traducteurs pour changer une liste de mots en une autre pour que CodeCombat soit autant accessible que possible à travers le monde. Si vous aimez avoir des aperçu des prochains contenus et avoir les niveaux dans votre langue le plus tôt possible, alors cette classe est faite pour vous."
diplomat_attribute_1: "Des facilités en anglais et dans la langue que vous souhaitez traduire. Pour transmettre des idées complexes, c'est important d'avoir une solide compréhension des deux!"
diplomat_join_pref: "Nous avons commencé plusieurs traductions sur "
diplomat_doc_url: "ce post du forum"
diplomat_join_suf: "regardez les et ajoutez des choses à votre langue. Aussi, cochez la case ci-dessous pour rester à jour sur les nouveaux développement d'i18n!"
more_about_diplomat: "En apprendre plus sur devenir un bon diplomate"
diplomat_subscribe_desc: "Recevoir des emails sur le développement i18n et les niveaux à traduire."
ambassador_introduction: "C'est la communauté que nous avons construite, et vous en êtes les connexions. Nous avons une discussion Olark, des emails et les réseaux sociaux avec plusieurs personnes avec qui parler et l'aide est avec grâce au jeu et vient du jeu lui même. Si vous voulez aider les gens prenez part à l'aventure et amusez-vous, ayant un bon feeling de CodeCombat et d'où nous allons, alors cette classe est faite pour vous."
ambassador_attribute_1: "Compétences en communication. Etre capable d'identifier les problèmes que les joueurs ont et les aider à les résoudre. Aussi, nous garder informé de ce que les joueurs disent, ce qu'ils aiment et n'aiment pas et autres choses de ce type!"
ambassador_join_desc: "parlez nous un peu de vous, ce que vous avez fait et ce qui vous souhaiterez faire. Nous partirons de ça!"
ambassador_join_note_strong: "Note"
ambassador_join_note_desc: "Une de nos piorités est développer un jeu multijoueur où les joueurs qui ont du mal à réussir un niveau peuvent demander de l'aide à un joueur de plus haut niveau. Ce sera un bon moyen pour que les ambassadeurs fassent leur travail. Nous vous garderons en ligne!"
more_about_ambassador: "En apprendre plus sur devenir un ambassadeur utile"
ambassador_subscribe_desc: "Recevoir des emails sur les mises à jour de l'aide et les développements multijoueur."
counselor_introduction_1: "Avez-vous une expérience de vie? Un autre point de vue qui peut nous aider à décider comment diriger CodeCombat? De tous ces roles, ce sera probablement celui qui prend le moins de temps, mais vous ferez la différence. Nous recherchons des sages, particulièrement dans les domaines de : l'apprentissage, le développement de jeux, la gestion de projets open source, le recrutement technique, l'entrepreunariat, ou la conception."
counselor_introduction_2: "Ou vraiment chaque chose qui concerne le développement de CodeCombat. Si vous avez des connaissances et que vous voulez les partager pour aider le projet à avancer, alors cette classe est faite pour vous."
counselor_attribute_1: "De l'expérience, dans un des domaines ci-dessus ou quelque chose que vous pensez utile."
counselor_attribute_2: "Un peu de temps libre!"
counselor_join_desc: "parlez nous un peu de vous, ce que vous avez fait et ce que vous voudrez faire. Nous vous mettrons dans notre liste de contacts et resterons en contact quand nous pourrons utiliser vos conseils (pas trop souvent)."
more_about_counselor: "En apprendre plus sur devenir un conseiller précieux"
changes_auto_save: "Les changements sont sauvegardés automatiquement quand vous changez l'état des cases à cocher."
diligent_scribes: "Nos scribes diligents :"
powerful_archmages: "Nos puissants archimages :"
creative_artisans: "Nos artisans créatifs :"
brave_adventurers: "Nos braves aventuriers :"
translating_diplomats: "Nos diplomates de la traduction :"
helpful_ambassadors: "Nos ambassadeurs utiles :"
classes:
archmage_title: "Archimage"
archmage_title_description: "(Développeur)"
artisan_title: "Artisan"
artisan_title_description: "(Créateur de niveau)"
adventurer_title: "Aventurier"
adventurer_title_description: "(Testeur de jouabilité)"
scribe_title: "Scribe"
scribe_title_description: "(Editeur d'article)"
diplomat_title: "Diplomate"
diplomat_title_description: "(Tarducteur)"
ambassador_title: "Ambassadeur"
ambassador_title_description: "(Aide)"
counselor_title: "Conseiller"
counselor_title_description: "(Expert/Professeur)"

View file

@ -1,12 +1,12 @@
module.exports = nativeDescription: "日本語", englishDescription: "Japanese", translation:
common:
loading: "ロード中"
# saving: "Saving..."
# sending: "Sending..."
saving: "保存中..."
sending: "送信中..."
modal:
close: "閉じる"
okay: ""
okay: "OK"
not_found:
page_not_found: "ページが見つかりません"
@ -23,7 +23,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
home: "ホーム"
contribute: "貢献"
legal: "規約"
about: "について"
about: "CoCoについて"
contact: "お問い合わせ"
twitter_follow: "フォロー"
# employers: "Employers"
@ -33,50 +33,50 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
email: "メールアドレス"
message: "メッセージ"
cancel: "キャンセル"
# save: "Save"
save: "保存"
# versions:
# save_version_title: "Save New Version"
# commit_message: "Commit Message"
# new_major_version: "New Major Version"
# cla_prefix: "To save changes, first you must agree to our"
versions:
save_version_title: "新しいバージョンを保存"
commit_message: "コミットメッセージ"
new_major_version: "メジャーバージョンを新しくする"
cla_prefix: "変更を適用するには, 私達のCLAに同意する必要があります。"
# cla_url: "CLA"
# cla_suffix: "."
# cla_agree: "I AGREE"
cla_agree: "同意する"
login:
# login_modal_title: "Log In"
login_modal_title: "ログイン"
log_in: "ログイン"
sign_up: "アカウント登録"
or: "または"
recover: "パスワードを忘れた場合はこちら"
# recover:
# recover_account_title: "Recover Account"
# send_password: "Send Recovery Password"
recover:
recover_account_title: "パスワードを忘れた場合"
send_password: "送信する"
signup:
# create_account_title: "Create Account to Save Progress"
# description: "It's free. Just need a couple things and you'll be good to go:"
create_account_title: "進行状況保存用のアカウント作成"
description: "無料でご登録いただけます。"
email_announcements: "メールでお知らせを受け取る"
coppa: "13歳以上または米国以外"
# coppa_why: "(Why?)"
coppa_why: "(COPPAって?)"
creating: "アカウントを作成しています..."
sign_up: "アカウント登録"
or: "または"
log_in: "パスワードでログイン"
home:
slogan: "ゲームをプレイしてを学びましょう"
no_ie: "申し訳ありませんが、ご使用のブラウザ(IE8以下)はサポートされていません。"
no_mobile: "CodeCombatはモバイルデバイス向けに設計されていないので、動作しない可能があります。"
slogan: "ゲームをプレイしてJavaScriptを学びましょう"
no_ie: "大変申し訳ありませんが、ご利用のブラウザIE8以下はサポートされていません。(ChromeやFirefoxをご利用ください)"
no_mobile: "CodeCombat は携帯端末向けに制作されていないため、動作しない可能性があります。"
play: "ゲームスタート"
play:
choose_your_level: "レベル選択"
# adventurer_prefix: "You can jump to any level below, or discuss the levels on "
adventurer_prefix: "別のレベルに移動することができます。レベルについて議論するにはこちら: "
adventurer_forum: "冒険者の掲示板"
# adventurer_suffix: "."
adventurer_suffix: ""
campaign_beginner: "初心者のキャンペーン"
# campaign_beginner_description: "... in which you learn the wizardry of programming."
# campaign_dev: "Random Harder Levels"
@ -88,28 +88,28 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
# level_difficulty: "Difficulty: "
contact:
# contact_us: "Contact CodeCombat"
# welcome: "Good to hear from you! Use this form to send us email. "
# contribute_prefix: "If you're interested in contributing, check out our "
# contribute_page: "contribute page"
# contribute_suffix: "!"
# forum_prefix: "For anything public, please try "
# forum_page: "our forum"
# forum_suffix: " instead."
contact_us: "お問い合わせ"
welcome: "あなたからの連絡に感謝します。私達にメールを送信するには、このフォームを使ってください。"
contribute_prefix: "もしあなたが開発への貢献に興味がある場合: "
contribute_page: "このページをチェック"
contribute_suffix: "!"
forum_prefix: "公開で様々な人と議論したい場合は "
forum_page: "こちらのフォーラム"
forum_suffix: " でお願いします。"
sending: "送信中"
send: "フィードバックを送信"
diplomat_suggestion:
title: "を翻訳しましょう!"
title: "CodeCombatを翻訳しましょう!"
sub_heading: "あなたの言語力が必要です。"
pitch_body: "CodeCombatは英語で開発されていますが、ユーザーはいたるところいます。日本語版でプレイしたい方がいますが、ゲームのたくさんはまだ英語です。もし日本語と英語の両方が出来ますなら、CodeCombatのレベルやサイトのすべてを翻訳に助力、Diplomat(翻訳者)として登録するご検討をお願いいたします"
missing_translations: "ゲームは翻訳し終わらない間、日本語が利用できない場合は英語で表示されます。"
learn_more: "について情報"
subscribe_as_diplomat: "Diplomatとして登録"
pitch_body: "CodeCombatは英語で開発されています。日本語でプレイしたい方がたくさんいますが、ゲームの多くはまだ英語のままです。もし、あなたが英語が得意であれば、Diplomat翻訳者として登録し、CodeCombatのレベルやサイトの翻訳にご協力ください"
missing_translations: "翻訳が完了していない部分は、英語で表示されます。"
learn_more: "Diplomat について情報"
subscribe_as_diplomat: "Diplomat登録"
# wizard_settings:
# title: "Wizard Settings"
# customize_avatar: "Customize Your Avatar"
wizard_settings:
title: "ウィザードの設定"
customize_avatar: "アバターのカスタマイズ"
account_settings:
title: "アカウント設定"
@ -120,36 +120,36 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
wizard_tab: "魔法使い"
password_tab: "パスワード"
emails_tab: "メール"
gravatar_select: "グラバター画像"
gravatar_select: "Gravatar"
# gravatar_add_photos: "Add thumbnails and photos to a Gravatar account for your email to choose an image."
# gravatar_add_more_photos: "Add more photos to your Gravatar account to access them here."
wizard_color: "魔法使い服の色"
wizard_color: "ウィザードの色"
new_password: "新パスワード"
new_password_verify: "新パスワードを再入力"
# email_subscriptions: "Email Subscriptions"
email_subscriptions: "ニュースレターの購読"
email_announcements: "お知らせ"
# email_announcements_description: "Get emails on the latest news and developments at CodeCombat."
# contributor_emails: "Contributor Class Emails"
# contribute_prefix: "We're looking for people to join our party! Check out the "
# contribute_page: "contribute page"
# contribute_suffix: " to find out more."
# email_toggle: "Toggle All"
email_announcements_description: "CodeCombatの最新のニュースや進展をメールで受け取る"
contributor_emails: "開発を手伝ってくれる人向けのメール"
contribute_prefix: "私達は開発を手伝ってくれる人を探しています。 詳しくは "
contribute_page: "こちらのページ"
contribute_suffix: " を確認して下さい。"
email_toggle: "すべて"
saving: "セーブ中"
error_saving: "セーブエラーが発生しました"
error_saving: "セーブ中にエラーが発生しました"
saved: "変更しました"
password_mismatch: "パスワードが違います"
account_profile:
edit_settings: "設定"
# profile_for_prefix: "Profile for "
# profile_for_suffix: ""
profile_for_prefix: ""
profile_for_suffix: "のプロフィール"
profile: "プロフィール"
user_not_found: "ユーザーが見つかりません。を間違って入力していないか確認してください。"
gravatar_not_found_mine: "プロフィールが見つかりません:"
# gravatar_not_found_email_suffix: "."
# gravatar_signup_prefix: "Sign up at "
# gravatar_signup_suffix: " to get set up!"
# gravatar_not_found_other: "Alas, there's no profile associated with this person's email address."
user_not_found: "ユーザーが見つかりません。URLを間違って入力していないか確認してください。"
gravatar_not_found_mine: ""
gravatar_not_found_email_suffix: " のメールアドレスは Gravatar で見つけることができませんでした。"
gravatar_signup_prefix: ""
gravatar_signup_suffix: " を登録"
gravatar_not_found_other: "このメールアドレスには プロフィールが関連付けられていません。"
# gravatar_contact: "Contact"
# gravatar_websites: "Websites"
# gravatar_accounts: "As Seen On"
@ -171,39 +171,39 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
reload_really: "レベルをリセットします。よろしいですか?"
reload_confirm: "リセットする"
# victory_title_prefix: ""
victory_title_suffix: "完了"
# victory_sign_up: "Sign Up to Save Progress"
# victory_sign_up_poke: "Want to save your code? Create a free account!"
# victory_rate_the_level: "Rate the level: "
# victory_play_next_level: "Play Next Level"
# victory_go_home: "Go Home"
# victory_review: "Tell us more!"
victory_title_suffix: "クリア"
victory_sign_up: "進行状況を保存するにはアカウント登録をしてください"
victory_sign_up_poke: "あなたのコードを保存してみませんか? 無料アカウント登録!"
victory_rate_the_level: "このレベルの評価: "
victory_play_next_level: "次のレベル"
victory_go_home: "ホームに戻る"
victory_review: "フィードバック"
# victory_hour_of_code_done: "Are You Done?"
# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code!"
# multiplayer_title: "Multiplayer Settings"
# multiplayer_link_description: "Give this link to anyone to have them join you."
# multiplayer_hint_label: "Hint:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link."
# multiplayer_coming_soon: "More multiplayer features to come!"
# guide_title: "Guide"
# tome_minion_spells: "Your Minions' Spells"
# tome_read_only_spells: "Read-Only Spells"
# tome_other_units: "Other Units"
multiplayer_title: "マルチプレイ設定"
multiplayer_link_description: "このURLを一緒にプレイしたい人に教えてください。"
multiplayer_hint_label: "ヒント:"
multiplayer_hint: " リンクを選択後、 ⌘-C(MacOS) or Ctrl-C(Windows) でリンクをコピーできます。"
multiplayer_coming_soon: "今後より多くのマルチプレイ機能が追加されます。"
guide_title: "ガイド"
tome_minion_spells: "操作できるキャラクターの呪文"
tome_read_only_spells: "読込専用の呪文"
tome_other_units: "その他のユニット"
# tome_cast_button_castable: "Cast"
# tome_cast_button_casting: "Casting"
# tome_cast_button_cast: "Spell Cast"
# tome_autocast_delay: "Autocast Delay"
tome_autocast_1: ""
tome_autocast_3: ""
tome_autocast_5: ""
# tome_autocast_manual: "Manual"
tome_autocast_delay: "自動実行待機時間"
tome_autocast_1: "1"
tome_autocast_3: "3"
tome_autocast_5: "5"
tome_autocast_manual: "手動"
tome_select_spell: "呪文を選択"
# tome_select_a_thang: "Select Someone for "
tome_available_spells: "利用できる呪文"
hud_continue: "続く Shift+Spaceキー"
# admin:
# av_title: "Admin Views"
admin:
av_title: "管理画面"
# av_entities_sub_title: "Entities"
# av_entities_users_url: "Users"
# av_entities_active_instances_url: "Active Instances"
@ -446,18 +446,18 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
# translating_diplomats: "Our Translating Diplomats:"
# helpful_ambassadors: "Our Helpful Ambassadors:"
# classes:
classes:
# archmage_title: "Archmage"
# archmage_title_description: "(Coder)"
archmage_title_description: "(コーダー)"
# artisan_title: "Artisan"
# artisan_title_description: "(Level Builder)"
# adventurer_title: "Adventurer"
# adventurer_title_description: "(Level Playtester)"
artisan_title_description: "(レベルの製作者)"
adventurer_title: "Adventurer"
adventurer_title_description: "(レベルのテストプレイヤー)"
# scribe_title: "Scribe"
# scribe_title_description: "(Article Editor)"
scribe_title_description: "(記事の編集者)"
# diplomat_title: "Diplomat"
# diplomat_title_description: "(Translator)"
diplomat_title_description: "(翻訳者)"
# ambassador_title: "Ambassador"
# ambassador_title_description: "(Support)"
ambassador_title_description: "(サポート)"
# counselor_title: "Counselor"
# counselor_title_description: "(Expert/Teacher)"

View file

@ -1,8 +1,8 @@
module.exports = nativeDescription: "português do Brasil", englishDescription: "Portuguese (Brazil)", translation:
common:
loading: "Carregando..."
# saving: "Saving..."
# sending: "Sending..."
saving: "Salvando..."
sending: "Enviando..."
modal:
close: "Fechar"
@ -33,7 +33,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
email: "Email"
message: "Mensagem"
cancel: "Cancelar"
# save: "Save"
save: "Salvar"
# versions:
# save_version_title: "Save New Version"
@ -45,18 +45,18 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
# cla_agree: "I AGREE"
login:
# login_modal_title: "Log In"
login_modal_title: "Entrar"
log_in: "Entrar"
sign_up: "Criar uma nova conta"
or: ", ou "
recover: "recuperar sua conta"
# recover:
# recover_account_title: "Recover Account"
# send_password: "Send Recovery Password"
recover:
recover_account_title: "Recuperar conta"
send_password: "Recuperar senha"
signup:
# create_account_title: "Create Account to Save Progress"
create_account_title: "Criar conta para salvar progresso"
description: "É grátis. Precisamos apenas de umas coisinhas e você estará pronto para seguir:"
email_announcements: "Receber notícias por email."
coppa: "acima de 13 anos ou não estadunidense"
@ -107,9 +107,9 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
learn_more: "Saiba mais sobre ser um Diplomata"
subscribe_as_diplomat: "Assinar como um Diplomata"
# wizard_settings:
# title: "Wizard Settings"
# customize_avatar: "Customize Your Avatar"
wizard_settings:
title: "Configurações do Feiticeiro"
customize_avatar: "Personalize o seu Avatar"
account_settings:
title: "Configurações da Conta"

View file

@ -4,10 +4,11 @@ SpriteBuilder = require 'lib/sprites/SpriteBuilder'
module.exports = class ThangType extends CocoModel
@className: "ThangType"
urlRoot: "/db/thang.type"
building: 0
building: {}
initialize: ->
super()
@building = {}
@setDefaults()
@on 'sync', @setDefaults
@spriteSheets = {}
@ -24,7 +25,7 @@ module.exports = class ThangType extends CocoModel
getActions: ->
return @actions or @buildActions()
buildActions: ->
@actions = _.cloneDeep(@get('actions'))
for name, action of @actions
@ -33,12 +34,12 @@ module.exports = class ThangType extends CocoModel
relatedAction.name = action.name + "_" + relatedName
@actions[relatedAction.name] = relatedAction
@actions
getSpriteSheet: (options) ->
options = @fillOptions options
key = @spriteSheetKey(options)
return @spriteSheets[key] or @buildSpriteSheet(options)
fillOptions: (options) ->
options ?= {}
options = _.clone options
@ -47,20 +48,23 @@ module.exports = class ThangType extends CocoModel
options
buildSpriteSheet: (options) ->
@options = @fillOptions options
key = @spriteSheetKey(@options)
return if @building[key]
@initBuild(options)
# @options.portraitOnly = true
@addGeneralFrames() unless @options.portraitOnly
@addPortrait()
@finishBuild()
@building[key] = true
result = @finishBuild()
return result
initBuild: (options) ->
@buildActions() if not @actions
@options = @fillOptions options
@vectorParser = new SpriteBuilder(@, options)
@builder = new createjs.SpriteSheetBuilder()
@builder.padding = 2
@frames = {}
addPortrait: ->
# The portrait is built very differently than the other animations, so it gets a separate function.
return unless @actions
@ -98,7 +102,7 @@ module.exports = class ThangType extends CocoModel
next = action.goesTo if action.goesTo
next = false if action.loops is false
@builder.addAnimation name, frames, next
for name, action of @actions when action.container and not action.animation
continue if name is 'portrait'
scale = @options.resolutionFactor * (action.scale or @get('scale') or 1)
@ -131,15 +135,16 @@ module.exports = class ThangType extends CocoModel
@builder.buildAsync()
@builder.on 'complete', @onBuildSpriteSheetComplete, @, true, key
return true
console.warn 'Building', @get('name'), 'and blocking the main thread. LevelLoader should have it built asynchronously instead.'
console.warn 'Building', @get('name'), 'and blocking the main thread.'
spriteSheet = @builder.build()
@spriteSheets[key] = spriteSheet
spriteSheet
onBuildSpriteSheetComplete: (e, key) ->
@spriteSheets[key] = e.target.spriteSheet
@trigger 'build-complete'
@builder = null
@vectorParser = null
spriteSheetKey: (options) ->
colorConfigs = []
@ -183,7 +188,7 @@ module.exports = class ThangType extends CocoModel
createjs.Ticker.removeEventListener 'tick', @tick
@tick = null
stage
uploadGenericPortrait: (callback) ->
src = @getPortraitSource()
return callback?() unless src
@ -198,4 +203,3 @@ module.exports = class ThangType extends CocoModel
onFileUploaded: =>
console.log 'Image uploaded'

View file

@ -35,22 +35,41 @@
font-size: 12px
#wizard-settings-tab-view
.form-horizontal
#color-settings
float: left
width: 400px
margin-left: 50px
width: 600px
margin-left: 30px
canvas
float: left
border: 2px solid black
margin: 20px
.selector
position: relative
top: 10px
left: 10px
h2 input
margin-left: 10px
.form-horizontal .control-group
.color-group
clear: both
padding-bottom: 10px
margin-bottom: 10px
border-bottom: 1px solid gray
.name-cell
float: left
width: 100px
padding-top: 2px
input
margin-right: 10px
position: relative
top: -3px
.checkbox-cell
float: left
width: 40px
.slider-cell
margin-bottom: 10px
float: left
width: 120px
.selector
width: 100px

View file

@ -1,41 +1,12 @@
@import "bootstrap/variables"
#editor-level-view
.images
margin: 30px 0
text-align: center
div
float: left
width: 25%
padding: 10px 40px
box-sizing: border-box
img
width: 100%
#image-modal
.modal-body
max-height: 100%
height: 80%
width: 80%
margin-left: -40%
img
display: block
margin: 0 auto
max-height: 80%
#after-images
clear: both
ul#prereqs
margin: 20px 40px
.editor-column
width: 33%
box-sizing: border-box
padding-right: 20px
float: left
h3
text-decoration: underline
margin-bottom: 2px
#editor-links .btn
margin-right: 20px
margin-bottom: 30px
//.screencast-wrapper
// text-align: center
// background: $gray
// object
// display: inline-block

View file

@ -18,7 +18,13 @@
position: absolute
top: 0
height: 100%
.save-status
display: none
position: relative
padding-top: 2px
padding-left: 10px
.firepad
width: 100%
height: 100%

View file

@ -1,21 +1,22 @@
canvas#tinting-display(width=200, height=200).img-rounded
.form-horizontal
#color-settings
for group in colorGroups
.color-group(data-name=group.name)
h2
span(data-i18n=group.dasherized)= group.humanized
div.name-cell
input(type='checkbox', checked=group.exists).color-group-checkbox
.sliders
.control-group
label.control-label(for=group.humanized+"_hue", data-i18n="hue") Hue
span(data-i18n=group.dasherized)= group.humanized
div.sliders
div.slider-cell
label(for=group.humanized+"_hue", data-i18n="hue") Hue
.controls
.selector(id=group.humanized+"_hue", name=group.name+'.hue', data-key='hue')
.control-group
label.control-label(for=group.humanized+"_saturation", data-i18n="saturation") Saturation
div.slider-cell
label(for=group.humanized+"_saturation", data-i18n="saturation") Saturation
.controls
.selector(id=group.humanized+"_saturation", name=group.name+'.saturation', data-key='saturation')
.control-group
label.control-label(for=group.humanized+"_lightness", data-i18n="lightness") Lightness
div.slider-cell
label(for=group.humanized+"_lightness", data-i18n="lightness") Lightness
.controls
.selector(id=group.humanized+"_lightness", name=group.name+'.lightness', data-key='lightness')
.selector(id=group.humanized+"_lightness", name=group.name+'.lightness', data-key='lightness')
div.clearfix

View file

@ -89,5 +89,6 @@ block content
li Polish - Anon
li Danish - Anon
li Slovak - Anon
li Persian - Reza Habibi(Rehb)
div.clearfix
div.clearfix

View file

@ -11,7 +11,7 @@ block content
p.lead Want to hire expert CodeCombat players?
p
| CodeCombat doesn't just have beginners. We also have expert software developers who play our
| CodeCombat doesn't just have beginners. We also have expert software developers who play our
a(href="http://blog.codecombat.com/beat-this-level-get-a-programming-job") developer challenge levels
| . If your company is seeking technical talent, then we'd be happy to help place candidates with you.

View file

@ -1,3 +1,5 @@
img(src="/images/level/code_editor_background.png").code-background
div.ace
.save-status(data-i18n="play_level.spell_saved") Spell Saved

View file

@ -49,7 +49,7 @@ module.exports = class WizardSettingsTabView extends RootView
@$el.find('.selector').each (i, slider) =>
[groupName, prop] = $(slider).attr('name').split('.')
value = 100 * (wizardSettings.colorConfig[groupName]?[prop] or 0)
value = 100 * (wizardSettings.colorConfig[groupName]?[prop] ? 0.5)
@initSlider $(slider), value, @onSliderChanged
@$el.find('.color-group').each (i, colorGroup) =>
@ -82,7 +82,9 @@ module.exports = class WizardSettingsTabView extends RootView
initStage: ->
@stage = new createjs.Stage(@$el.find('canvas')[0])
@updateMovieClip()
createjs.Ticker.setFPS 20
createjs.Ticker.addEventListener("tick", @stage)
updateMovieClip: ->
return unless @wizardThangType.loaded
wizardSettings = me.get('wizard') or {}
@ -92,13 +94,16 @@ module.exports = class WizardSettingsTabView extends RootView
options = {colorConfig: wizardSettings.colorConfig}
@spriteBuilder.setOptions options
@spriteBuilder.buildColorMaps()
portraitAction = @wizardThangType.get('actions')?.portrait
return unless portraitAction?.animation
@movieClip = @spriteBuilder.buildMovieClip portraitAction.animation
@movieClip.scaleY = @movieClip.scaleX = 2 * (portraitAction.scale or 1)
reg = portraitAction.positions?.registration
castAction = @wizardThangType.get('actions')?.cast
return unless castAction?.animation
@movieClip = @spriteBuilder.buildMovieClip castAction.animation
@movieClip.scaleY = @movieClip.scaleX = 1.7 * (castAction.scale or 1)
reg = castAction.positions?.registration
if reg
@movieClip.regX = reg.x
@movieClip.regY = reg.y
@stage.addChild @movieClip
@stage.update()
@stage.update()
destroy: ->
@stage?.removeAllEventListeners()

View file

@ -21,6 +21,7 @@ module.exports = class HUDView extends View
'dialogue-sound-completed': 'onDialogueSoundCompleted'
'thang-began-talking': 'onThangBeganTalking'
'thang-finished-talking': 'onThangFinishedTalking'
'god:new-world-created': 'onNewWorld'
events:
'click': -> Backbone.Mediator.publish 'focus-editor'
@ -61,6 +62,9 @@ module.exports = class HUDView extends View
onSpriteClearDialogue: ->
@clearSpeaker()
onNewWorld: (e) ->
@thang = e.world.thangMap[@thang.id] if @thang
setThang: (thang, thangType) ->
unless @speaker
if not thang? and not @thang? then return
@ -74,7 +78,7 @@ module.exports = class HUDView extends View
if not @thang
@hintNextSelectionTimeout = _.delay((=> @$el.find('.no-selection-message').slideDown('slow')), 10000)
return
@createAvatar thangType
@createAvatar thangType, @thang
@createProperties()
@createActions()
@update()
@ -84,7 +88,7 @@ module.exports = class HUDView extends View
return if speakerSprite is @speakerSprite
@speakerSprite = speakerSprite
@speaker = @speakerSprite.thang.id
@createAvatar @speakerSprite.thangType
@createAvatar @speakerSprite.thangType, @speakerSprite.thang
@$el.removeClass 'no-selection'
@switchToDialogueElements()
@ -98,8 +102,10 @@ module.exports = class HUDView extends View
@bubble = null
@update()
createAvatar: (thangType) ->
stage = thangType.getPortraitStage()
createAvatar: (thangType, thang) ->
options = thang.getSpriteOptions() or {}
options.async = false
stage = thangType.getPortraitStage options
wrapper = @$el.find '.thang-canvas-wrapper'
newCanvas = $(stage.canvas).addClass('thang-canvas')
wrapper.empty().append(newCanvas)

View file

@ -8,6 +8,7 @@ module.exports = class ThangAvatarView extends View
subscriptions:
'tome:problems-updated': "onProblemsUpdated"
'god:new-world-created': 'onNewWorld'
constructor: (options) ->
super options
@ -20,7 +21,9 @@ module.exports = class ThangAvatarView extends View
thangs = @supermodel.getModels(ThangType)
thangs = (t for t in thangs when t.get('name') is @thang.spriteName)
thang = thangs[0]
context.avatarURL = thang.getPortraitSource()
options = @thang?.getSpriteOptions() or {}
options.async = false
context.avatarURL = thang.getPortraitSource(options)
context.includeName = @includeName
context
@ -39,7 +42,7 @@ module.exports = class ThangAvatarView extends View
@$el.toggleClass 'selected', Boolean(selected)
onProblemsUpdated: (e) ->
return unless @thang.id of e.spell.thangs
return unless @thang?.id of e.spell.thangs
myProblems = []
for thangID, spellThang of e.spell.thangs when thangID is @thang.id
#aether = if e.isCast and spellThang.castAether then spellThang.castAether else spellThang.aether
@ -50,3 +53,6 @@ module.exports = class ThangAvatarView extends View
worstLevel = level
break
@setProblems myProblems.length, worstLevel
onNewWorld: (e) ->
@options.thang = @thang = e.world.thangMap[@thang.id] if @thang

View file

@ -17,7 +17,6 @@ module.exports = class Spell
@thangs = {}
@view = new SpellView {spell: @, session: @session}
@view.render() # Get it ready and code loaded in advance
console.log 'spell creates tab entry view', @supermodel
@tabView = new SpellListTabEntryView spell: @, supermodel: @supermodel
@tabView.render()

View file

@ -15,6 +15,7 @@ module.exports = class SpellListEntryView extends View
'tome:problems-updated': "onProblemsUpdated"
'level-disable-controls': 'onDisableControls'
'level-enable-controls': 'onEnableControls'
'god:new-world-created': 'onNewWorld'
events:
'click': 'onClick'
@ -96,3 +97,6 @@ module.exports = class SpellListEntryView extends View
# Should refactor the disabling list so we can target the spell list separately?
# Should not call it 'editor' any more?
@$el.toggleClass('disabled', disabled).find('*').prop('disabled', disabled)
onNewWorld: (e) ->
@lastSelectedThang = e.world.thangMap[@lastSelectedThang.id] if @lastSelectedThang

View file

@ -10,6 +10,7 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
subscriptions:
'tome:spell-loaded': "onSpellLoaded"
'tome:spell-changed': "onSpellChanged"
'god:new-world-created': 'onNewWorld'
events:
'click .spell-list-button': 'onDropdownClick'
@ -26,6 +27,9 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
super()
@$el.addClass 'spell-tab'
onNewWorld: (e) ->
@thang = e.world.thangMap[@thang.id] if @thang
setThang: (thang) ->
return if thang.id is @thang?.id
@thang = thang

View file

@ -14,7 +14,8 @@ module.exports = class SpellListView extends View
id: 'spell-list-view'
template: template
subscriptions: {}
subscriptions:
'god:new-world-created': 'onNewWorld'
constructor: (options) ->
super options
@ -64,6 +65,9 @@ module.exports = class SpellListView extends View
@$el.append entry.el
entry.render() # Render after appending so that we can access parent container for popover
onNewWorld: (e) ->
@thang = e.world.thangMap[@thang.id] if @thang
setThangAndSpell: (@thang, @spell) ->
@entries[0]?.setSelected false
@sortSpells()

View file

@ -51,6 +51,6 @@ module.exports = class SpellPaletteEntryView extends View
onFrameChanged: (e) ->
return unless e.selectedThang?.id is @thang.id
@thang = e.selectedThang # Update our thang to the current version
@doc = Docs.getDocsFor(@thang, [@doc.prop])[0]
@options.thang = @thang = e.selectedThang # Update our thang to the current version
@options.doc = @doc = Docs.getDocsFor(@thang, [@doc.prop])[0]
@$el.find("code.current-value").text(@doc.formatValue()) # Don't call any functions. (?? What does this mean?)

View file

@ -55,7 +55,7 @@ module.exports = class SpellPaletteView extends View
onFrameChanged: (e) ->
return unless e.selectedThang?.id is @thang.id
@thang = e.selectedThang # Update our thang to the current version
@options.thang = @thang = e.selectedThang # Update our thang to the current version
destroy: ->
super()

View file

@ -21,9 +21,11 @@ module.exports = class SpellView extends View
'god:user-code-problem': 'onUserCodeProblem'
'tome:manual-cast': 'onManualCast'
'tome:reload-code': 'onCodeReload'
'tome:spell-changed': 'onSpellChanged'
'level:session-will-save': 'onSessionWillSave'
'modal-closed': 'focus'
'focus-editor': 'focus'
events:
'click .ace': -> console.log 'clicked ace', @
@ -316,6 +318,16 @@ module.exports = class SpellView extends View
#else
# console.log "valid but not at end of line; recompile in", @autocastDelay + "ms"
onSpellChanged: (e) ->
@spellHasChanged = true
onSessionWillSave: (e) ->
setTimeout(=>
unless @spellHasChanged
@$el.find('.save-status').finish().show().fadeOut(2000)
, 1000)
@spellHasChanged = false
onUserCodeProblem: (e) ->
return @onInfiniteLoop e if e.problem.id is "runtime_InfiniteLoop"
return unless e.problem.userInfo.methodName is @spell.name

View file

@ -71,9 +71,7 @@ module.exports = class PlayLevelView extends View
window.tracker?.trackEvent 'Hour of Code Begin', {}
@isEditorPreview = @getQueryVariable "dev"
sessionID = @getQueryVariable "session"
@levelLoader = new LevelLoader(@levelID, @supermodel, sessionID)
@levelLoader.once 'ready-to-init-world', @onReadyToInitWorld
@sessionID = @getQueryVariable "session"
$(window).on('resize', @onWindowResize)
@supermodel.once 'error', =>
@ -81,9 +79,16 @@ module.exports = class PlayLevelView extends View
@$el.html('<div class="alert">' + msg + '</div>')
@saveScreenshot = _.throttle @saveScreenshot, 30000
@load() unless @isEditorPreview
setLevel: (@level, @supermodel) ->
@god?.level = @level.serialize @supermodel
@initWorld()
@load()
load: ->
@levelLoader = new LevelLoader(@levelID, @supermodel, @sessionID)
@levelLoader.once 'ready-to-init-world', @onReadyToInitWorld
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
getRenderData: ->
c = super()
@ -96,11 +101,12 @@ module.exports = class PlayLevelView extends View
@loadingScreen.show()
super()
onReadyToInitWorld: =>
onLevelLoaderLoaded: =>
@session = @levelLoader.session
@level = @levelLoader.level
@world = @levelLoader.world
@loadingScreen.destroy()
@initWorld()
@setTeam @world.teamForPlayer 1 # We don't know which player we are; this will go away--temp TODO
@initSurface()
@initGod()
@initGoalManager()
@ -285,12 +291,6 @@ module.exports = class PlayLevelView extends View
return if p.length
@$el.append($('<img src="/images/level/pointer.png" id="pointer">'))
initWorld: ->
@world ?= new World @level.get('name')
serializedLevel = @level.serialize(@supermodel)
@world.loadFromLevel serializedLevel, false
@setTeam @world.teamForPlayer 1 # We don't know which player we are; this will go away--temp TODO
initSurface: ->
surfaceCanvas = $('canvas#surface', @$el)
@surface = new Surface(@world, surfaceCanvas, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview)

View file

@ -32,7 +32,7 @@
"firepad": "~0.1.2",
"marked": "~0.3.0",
"moment": "~2.5.0",
"aether": "~0.0.1",
"aether": "~0.0.5",
"underscore.string": "~2.3.3",
"firebase": "~1.0.2"
},

View file

@ -90,7 +90,7 @@
"karma": "~0.10.9",
"karma-coverage": "~0.1.4"
},
"license": "Copyright © 2014 CodeCombat",
"license": "MIT for the code, and CC-BY for the art and music",
"private": true,
"engines": {
"node": "0.10.x"

View file

@ -5,6 +5,7 @@ User = require('./models/User')
UserHandler = require('./handlers/user')
config = require '../server_config'
nodemailer = require 'nodemailer'
errors = require './errors'
module.exports.setupRoutes = (app) ->
passport.serializeUser((user, done) -> done(null, user._id))
@ -32,9 +33,7 @@ module.exports.setupRoutes = (app) ->
passport.authenticate('local', (err, user, info) ->
return next(err) if err
if not user
res.status(401)
res.send([{message:info.message, property:info.property}])
return res.end()
return errors.unauthorized(res, [{message:info.message, property:info.property}])
req.logIn(user, (err) ->
return next(err) if (err)
@ -54,28 +53,25 @@ module.exports.setupRoutes = (app) ->
req.logout()
res.end()
)
app.post('/auth/reset', (req, res) ->
unless req.body.email
res.status(422)
res.send([{message:'Need an email specified.', property:email}])
return res.end()
return errors.badInput(res, [{message:'Need an email specified.', property:email}])
User.findOne({emailLower:req.body.email.toLowerCase()}).exec((err, user) ->
if not user
res.status(404)
res.send([{message:'not found.', property:'email'}])
return res.end()
return errors.notFound(res, [{message:'not found.', property:'email'}])
user.set('passwordReset', Math.random().toString(36).slice(2,7).toUpperCase())
user.save (err) =>
return returnServerError(res) if err
return errors.serverError(res) if err
if config.isProduction
transport = createSMTPTransport()
options = createMailOptions req.body.email, user.get('passwordReset')
transport.sendMail options, (error, response) ->
if error
console.error "Error sending mail: #{error.message or error}"
return returnServerError(res) if err
return errors.serverError(res) if err
else
return res.end()
else
@ -102,9 +98,3 @@ createSMTPTransport = ->
pass: config.mail.password
authMethod: "LOGIN"
smtpTransport
returnServerError = (res) ->
res.status(500)
res.send('Server error.')
res.end()

View file

@ -3,6 +3,7 @@ winston = require 'winston'
mongoose = require 'mongoose'
Grid = require 'gridfs-stream'
async = require 'async'
errors = require './errors'
testing = '--unittest' in process.argv
@ -42,9 +43,7 @@ module.exports.setupRoutes = (app) ->
catch error
winston.error("Error trying db method #{req.route.method} route #{parts} from #{name}: #{error}")
winston.error(error)
res.status(404)
res.write("Route #{req.path} not found.")
res.end()
errors.notFound(res, "Route #{req.path} not found.")
getSchema = (req, res, moduleName) ->
try
@ -55,6 +54,4 @@ getSchema = (req, res, moduleName) ->
catch error
winston.error("Error trying to grab schema from #{name}: #{error}")
res.status(404)
res.write("Schema #{moduleName} not found.")
res.end()
errors.notFound(res, "Schema #{moduleName} not found.")

View file

@ -1,31 +1,35 @@
module.exports.notFound = (req, res, message) ->
res.status(404)
message = "Route #{req.path} not found." unless message
res.write(message)
res.end()
module.exports.badMethod = (res) ->
res.status(405)
res.send('Method not allowed.')
res.end()
module.exports.custom = (res, code=500, message='Internal Server Error') ->
res.send code, message
res.end
module.exports.badInput = (res) ->
res.status(422)
res.send('Bad post input.')
res.end()
module.exports.unauthorized = (res, message='Unauthorized') ->
# TODO: The response MUST include a WWW-Authenticate header field
res.send 401, message
res.end
module.exports.conflict = (res) ->
res.status(409)
res.send('File exists.')
res.end()
module.exports.forbidden = (res, message='Forbidden') ->
res.send 403, message
res.end
module.exports.serverError = (res) ->
res.status(500)
res.send('Server error.')
res.end()
module.exports.notFound = (res, message='Not found.') ->
res.send 404, message
res.end
module.exports.unauthorized = (res) ->
res.status(403)
res.send('Unauthorized.')
res.end()
module.exports.badMethod = (res, message='Method Not Allowed') ->
# TODO: The response MUST include an Allow header containing a list of valid methods for the requested resource
res.send 405, message
res.end
module.exports.conflict = (res, message='Conflict. File exists') ->
res.send 409, message
res.end
module.exports.badInput = (res, message='Unprocessable Entity. Bad Input.') ->
res.send 422, message
res.end
module.exports.serverError = (res, message='Internal Server Error') ->
res.send 500, message
res.end

View file

@ -3,12 +3,13 @@ Grid = require 'gridfs-stream'
fs = require 'fs'
request = require 'request'
mongoose = require('mongoose')
errors = require './errors'
module.exports.setupRoutes = (app) ->
app.all '/file*', (req, res) ->
return fileGet(req, res) if req.route.method is 'get'
return filePost(req, res) if req.route.method is 'post'
return returnBadMethod(res)
return errors.badMethod(res)
fileGet = (req, res) ->
@ -27,16 +28,16 @@ fileGet = (req, res) ->
if isFolder
Grid.gfs.collection('media').find query, (err, cursor) ->
return returnServerError(res) if err
return errors.serverError(res) if err
results = cursor.toArray (err, results) ->
return returnServerError(res) if err
return errors.serverError(res) if err
res.setHeader('Content-Type', 'text/json')
res.send(results)
res.end()
else
Grid.gfs.collection('media').findOne query, (err, filedata) =>
return returnNotFound(req, res) if not filedata
return errors.notFound(res) if not filedata
readstream = Grid.gfs.createReadStream({_id: filedata._id, root:'media'})
if req.headers['if-modified-since'] is filedata.uploadDate
res.status(304)
@ -69,12 +70,13 @@ postFileSchema =
required: ['filename', 'mimetype', 'path']
filePost = (req, res) ->
return returnNotAllowed(req, res) unless req.user.isAdmin()
return errors.forbidden(res) unless req.user.isAdmin()
options = req.body
tv4 = require('tv4').tv4
valid = tv4.validate(options, postFileSchema)
hasSource = options.url or options.postName or options.b64png
return returnBadInput(res) if (not valid) or (not hasSource)
# TODO : give tv4.error to badInput
return errors.badInput(res) if (not valid) or (not hasSource)
return saveURL(req, res) if options.url
return saveFile(req, res) if options.postName
return savePNG(req, res) if options.b64png
@ -82,7 +84,7 @@ filePost = (req, res) ->
saveURL = (req, res) ->
options = createPostOptions(req)
checkExistence options, res, req.body.force, (err) ->
return returnServerError(res) if err
return errors.serverError(res) if err
writestream = Grid.gfs.createWriteStream(options)
request(req.body.url).pipe(writestream)
handleStreamEnd(res, writestream)
@ -100,7 +102,7 @@ saveFile = (req, res) ->
savePNG = (req, res) ->
options = createPostOptions(req)
checkExistence options, res, req.body.force, (err) ->
return returnServerError(res) if err
return errors.serverError(res) if err
writestream = Grid.gfs.createWriteStream(options)
img = new Buffer(req.body.b64png, 'base64')
streamBuffers = require 'stream-buffers'
@ -116,13 +118,13 @@ checkExistence = (options, res, force, done) ->
}
Grid.gfs.collection('media').find(q).toArray (err, files) ->
if files.length and not force
returnConflict(res)
errors.conflict(res)
done(true)
else if files.length
q = { _id: files[0]._id }
q.root = 'media'
Grid.gfs.remove q, (err) ->
return returnServerError(res) if err
return errors.serverError(res) if err
done()
else
done()
@ -133,7 +135,7 @@ handleStreamEnd = (res, stream) ->
res.end()
stream.on 'error', ->
return returnServerError(res)
return errors.serverError(res)
CHUNK_SIZE = 1024*256
@ -159,35 +161,3 @@ createPostOptions = (req) ->
options.metadata.description = req.body.description if req.body.description?
options
returnNotAllowed = (req, res, message) ->
res.status(403)
message = "Can't do that, Dave." unless message
res.write(message)
res.end()
returnNotFound = (req, res, message) ->
res.status(404)
message = "Route #{req.path} not found." unless message
res.write(message)
res.end()
returnBadMethod = (res) ->
res.status(405)
res.send('Method not allowed.')
res.end()
returnBadInput = (res) ->
res.status(422)
res.send('Bad post input.')
res.end()
returnConflict = (res) ->
res.status(409)
res.send('File exists.')
res.end()
returnServerError = (res) ->
res.status(500)
res.send('Server error.')
res.end()

View file

@ -12,7 +12,7 @@ folderGet = (req, res) ->
folder = req.path[7..]
userfolder = "/user-#{req.user.id}/"
folder = userfolder if folder is '/me/'
return errors.unauthorized(res) unless (folder is userfolder) or (req.user.isAdmin())
return errors.forbidden(res) unless (folder is userfolder) or (req.user.isAdmin())
mongoose.connection.db.collection 'media.files', (errors, collection) ->
collection.find({'metadata.path': folder}).toArray (err, results) ->

View file

@ -1,6 +1,7 @@
async = require 'async'
mongoose = require('mongoose')
Grid = require 'gridfs-stream'
errors = require '../errors'
module.exports = class Handler
# subclasses should override these properties
@ -38,17 +39,14 @@ module.exports = class Handler
props
# sending functions
sendUnauthorizedError: (res) -> @sendError(res, 403, "Unauthorized.")
sendNotFoundError: (res) -> @sendError(res, 404, 'Resource not found.')
sendMethodNotAllowed: (res) -> @sendError(res, 405, 'Method not allowed.')
sendBadInputError: (res, message) -> @sendError(res, 422, message)
sendDatabaseError: (res, err) -> @sendError(res, 500, 'Database error.')
sendUnauthorizedError: (res) -> errors.forbidden(res) #TODO: rename sendUnauthorizedError to sendForbiddenError
sendNotFoundError: (res) -> errors.notFound(res)
sendMethodNotAllowed: (res) -> errors.badMethod(res)
sendBadInputError: (res, message) -> errors.badInput(res, message)
sendDatabaseError: (res, err) -> errors.serverError(res, 'Database error, ' + err)
sendError: (res, code, message) ->
console.warn "Sending an error code", code, message
res.status(code)
res.send(message)
res.end()
errors.custom(res, code, message)
sendSuccess: (res, message) ->
res.send(message)

View file

@ -41,8 +41,7 @@ LevelHandler = class LevelHandler extends Handler
Session.findOne(sessionQuery).exec (err, doc) =>
return @sendDatabaseError(res, err) if err
if doc
res.send(doc)
res.end()
@sendSuccess(res, doc)
return
initVals = sessionQuery
@ -71,8 +70,7 @@ LevelHandler = class LevelHandler extends Handler
Feedback.findOne(feedbackQuery).exec (err, doc) =>
return @sendDatabaseError(res, err) if err
return @sendNotFoundError(res) unless doc?
res.send(doc)
res.end()
@sendSuccess(res, doc)
return
postEditableProperties: ['name']

View file

@ -139,8 +139,7 @@ UserHandler = class UserHandler extends Handler
req.user.set('signedCLA', doc.created)
req.user.save (err) ->
return @sendDatabaseError(res, err) if err
res.send({result:'success'})
res.end()
@sendSuccess(res, {result:'success'})
module.exports = new UserHandler()
@ -157,17 +156,14 @@ module.exports.setupMiddleware = (app) ->
loginUser = (req, res, user, send=true, next=null) ->
user.save((err) ->
if err
res.status(500)
return res.end()
return @sendDatabaseError(res, err)
req.logIn(user, (err) ->
if err
res.status(500)
return res.end()
return @sendDatabaseError(res, err)
if send
res.send(user)
return res.end()
return @sendSuccess(res, user)
next() if next
)
)

View file

@ -3,23 +3,31 @@ require './common'
describe '/file', ->
url = getURL('/file')
files = []
it 'deletes all the files first', (done) ->
options = {
uri:url
json: {
url: 'http://scotterickson.info/images/where-are-you.jpg'
filename: 'where-are-you.jpg'
mimetype: 'image/jpeg'
description: 'None!'
}
}
filepath = 'tmp/file' # TODO Warning hard coded path !!!
jsonOptions= {
path: 'my_path'
postName: 'my_buffer'
filename: 'ittybitty.data'
mimetype: 'application/octet-stream'
description: 'rando-info'
my_buffer_url: 'http://scotterickson.info/images/where-are-you.jpg'
}
it 'preparing test : deletes all the files first', (done) ->
dropGridFS ->
done()
it 'no admin users can\'t post files', (done) ->
options = {
uri:url
json: {
url: 'http://scotterickson.info/images/where-are-you.jpg'
filename: 'where-are-you.jpg'
mimetype: 'image/jpeg'
description: 'None!'
}
}
it 'can\'t be created by ordinary users.', (done) ->
func = (err, res, body) ->
expect(res.statusCode).toBe(403)
expect(body.metadata).toBeUndefined()
@ -27,87 +35,91 @@ describe '/file', ->
request.post(options, func)
# FIXME fatal error
xit 'posts good', (done) ->
options = {
uri:url
json: {
url: 'http://scotterickson.info/images/where-are-you.jpg'
filename: 'where-are-you.jpg'
mimetype: 'image/jpeg'
description: 'None!'
}
}
it 'can\'t be created if invalid (property path is required)', (done) ->
func = (err, res, body) ->
expect(res.statusCode).toBe(422)
done()
loginAdmin ->
request.post(options, func)
it 'can be created by an admin', (done) ->
func = (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.metadata.description).toBe('None!')
expect(body._id).toBeDefined()
expect(body.filename).toBe(options.json.filename)
expect(body.contentType).toBe(options.json.mimetype)
expect(body.length).toBeDefined()
expect(body.uploadDate).toBeDefined()
expect(body.metadata).toBeDefined()
expect(body.metadata.name).toBeDefined()
expect(body.metadata.path).toBe(options.json.path)
expect(body.metadata.creator).toBeDefined()
expect(body.metadata.description).toBe(options.json.description)
expect(body.md5).toBeDefined()
files.push(body)
done()
collection = mongoose.connection.db.collection('media.files')
collection.findOne {}, (err, result) ->
expect(result.metadata.name).toBe('Where are you')
expect(result.metadata.createdFor+'').toBe([]+'')
done()
options.json.path = filepath
request.post(options, func)
it 'gets good', (done) ->
it 'can be read by an admin.', (done) ->
request.get {uri:url+'/'+files[0]._id}, (err, res) ->
expect(res.statusCode).toBe(200)
expect(res.headers['content-type']).toBe('image/jpeg')
expect(res.headers['content-type']).toBe(files[0].contentType)
done()
it 'returns 404 for missing files', (done) ->
id = '000000000000000000000000'
request.get {uri:url+'/'+id}, (err, res) ->
expect(res.statusCode).toBe(404)
done()
it 'returns 404 for invalid ids', (done) ->
request.get {uri:url+'/thiswillnotwork'}, (err, res) ->
expect(res.statusCode).toBe(404)
done()
# FIXME fatal error
xit 'posts data directly', (done) ->
options = {
it 'can be created directly with form parameters', (done) ->
options2 = {
uri:url
}
func = (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
expect(body.metadata.description).toBe('rando-info')
expect(body._id).toBeDefined()
expect(body.filename).toBe(jsonOptions.filename)
expect(body.contentType).toBe(jsonOptions.mimetype)
expect(body.length).toBeDefined()
expect(body.uploadDate).toBeDefined()
expect(body.metadata).toBeDefined()
expect(body.metadata.name).toBeDefined()
expect(body.metadata.path).toBe(jsonOptions.path)
expect(body.metadata.creator).toBeDefined()
expect(body.metadata.description).toBe(jsonOptions.description)
expect(body.md5).toBeDefined()
files.push(body)
collection = mongoose.connection.db.collection('media.files')
collection.find({_id:mongoose.Types.ObjectId(body._id)}).toArray (err, results) ->
expect(results[0].metadata.name).toBe('Ittybitty')
done()
done()
# the only way I could figure out how to get request to do what I wanted...
r = request.post(options, func)
r = request.post(options2, func)
form = r.form()
form.append('postName', 'my_buffer')
form.append('filename', 'ittybitty.data')
form.append('mimetype', 'application/octet-stream')
form.append('description', 'rando-info')
form.append('my_buffer', request('http://scotterickson.info/images/where-are-you.jpg'))
form.append('path', jsonOptions.path)
form.append('postName', jsonOptions.postName)
form.append('filename', jsonOptions.filename)
form.append('mimetype', jsonOptions.mimetype)
form.append('description', jsonOptions.description)
form.append('my_buffer', request(jsonOptions.my_buffer_url))
it 'created directly, can be read', (done) ->
request.get {uri:url+'/'+files[1]._id}, (err, res) ->
expect(res.statusCode).toBe(200)
expect(res.headers['content-type']).toBe(files[1].contentType)
done()
it 'does not overwrite existing files', (done) ->
options = {
uri:url
json: {
url: 'http://scotterickson.info/images/scott.jpg'
filename: 'where-are-you.jpg'
mimetype: 'image/jpeg'
description: 'Face'
}
}
options.json.description = 'Face'
func = (err, res, body) ->
expect(res.statusCode).toBe(409)
@ -122,16 +134,7 @@ describe '/file', ->
request.post(options, func)
it 'does overwrite existing files if force is true', (done) ->
options = {
uri:url
json: {
url: 'http://scotterickson.info/images/scott.jpg'
filename: 'where-are-you.jpg'
mimetype: 'image/jpeg'
description: 'Face'
force: true
}
}
options.json.force = "true" # TODO ask why it's a string and not a boolean ?
func = (err, res, body) ->
expect(res.statusCode).toBe(200)
@ -146,6 +149,20 @@ describe '/file', ->
done()
request.post(options, func)
# TODO: test server errors, see what they do
it ' can\'t be requested with HTTP PUT method', (done) ->
request.put {uri:url}, (err, res) ->
expect(res.statusCode).toBe(405)
done()
it ' can\'t be requested with HTTP HEAD method', (done) ->
request.head {uri:url}, (err, res) ->
expect(res.statusCode).toBe(405)
done()
it ' can\'t be requested with HTTP DEL method', (done) ->
request.del {uri:url}, (err, res) ->
expect(res.statusCode).toBe(405)
done()
# TODO: test server errors, see what they do

View file

@ -21,11 +21,11 @@ describe '/db/article', ->
loginAdmin ->
request.post {uri:url, json:article}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.slug).not.toBeUndefined()
expect(body.body).not.toBeUndefined()
expect(body.name).not.toBeUndefined()
expect(body.original).not.toBeUndefined()
expect(body.creator).not.toBeUndefined()
expect(body.slug).toBeDefined()
expect(body.body).toBeDefined()
expect(body.name).toBeDefined()
expect(body.original).toBeDefined()
expect(body.creator).toBeDefined()
articles[0] = body
done()
@ -38,7 +38,7 @@ describe '/db/article', ->
expect(body.version.minor).toBe(1)
expect(body._id).not.toBe(articles[0]._id)
expect(body.parent).toBe(articles[0]._id)
expect(body.creator).not.toBeUndefined()
expect(body.creator).toBeDefined()
articles[1] = body
done()

View file

@ -16,18 +16,18 @@ describe '/db/campaign', ->
campaign.permissions = [access: 'owner', target: user._id]
request.post {uri:url, json:campaign}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.permissions).not.toBeUndefined()
expect(body.permissions).toBeDefined()
campaigns[0] = body
done()
it 'does not allow other users access', (done) ->
loginSam (user) ->
loginSam ->
request.get {uri:url+'/'+campaigns[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(403)
done()
it 'allows editing permissions.', (done) ->
loginJoe (user) ->
loginJoe ->
campaigns[0].permissions.push(access: 'read', target: 'public')
request.put {uri:url, json:campaigns[0]}, (err, res, body) ->
expect(res.statusCode).toBe(200)
@ -36,7 +36,7 @@ describe '/db/campaign', ->
done()
it 'allows anyone to access it through public permissions', (done) ->
loginSam (user) ->
loginSam ->
request.get {uri:url+'/'+campaigns[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
done()

View file

@ -15,7 +15,7 @@ describe 'Level', ->
done()
it 'can make a Level.', (done) ->
loginJoe (joe) ->
loginJoe ->
request.post {uri:url, json:level}, (err, res, body) ->
expect(res.statusCode).toBe(200)
done()

View file

@ -19,13 +19,13 @@ describe 'LevelComponent', ->
done()
it 'can\'t be created by ordinary users.', (done) ->
loginJoe (joe) ->
loginJoe ->
request.post {uri:url, json:component}, (err, res, body) ->
expect(res.statusCode).toBe(403)
done()
it 'can be created by an admin.', (done) ->
loginAdmin (joe) ->
loginAdmin ->
request.post {uri:url, json:component}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body._id).toBeDefined()
@ -43,13 +43,13 @@ describe 'LevelComponent', ->
done()
it 'have a unique name.', (done) ->
loginAdmin (joe) ->
loginAdmin ->
request.post {uri:url, json:component}, (err, res, body) ->
expect(res.statusCode).toBe(422)
done()
it 'can read by an admin.', (done) ->
loginAdmin (joe) ->
it 'can be read by an admin.', (done) ->
loginAdmin ->
request.get {uri:url+'/'+components[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
@ -57,7 +57,7 @@ describe 'LevelComponent', ->
done()
it 'can be read by ordinary users.', (done) ->
loginJoe (joe) ->
loginJoe ->
request.get {uri:url+'/'+components[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
@ -81,7 +81,7 @@ describe 'LevelComponent', ->
done()
it 'is unofficial by default', (done) ->
loginJoe (joe) ->
loginJoe ->
request.get {uri:url+'/'+components[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
@ -90,7 +90,7 @@ describe 'LevelComponent', ->
done()
it 'has system ai by default', (done) ->
loginJoe (joe) ->
loginJoe ->
request.get {uri:url+'/'+components[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
@ -100,14 +100,14 @@ describe 'LevelComponent', ->
it 'official property isn\'t editable by an ordinary user.', (done) ->
components[0].official = true
loginJoe (joe) ->
loginJoe ->
request.post {uri:url, json:components[0]}, (err, res, body) ->
expect(res.statusCode).toBe(403)
done()
it 'official property is editable by an admin.', (done) ->
components[0].official = true
loginAdmin (joe) ->
loginAdmin ->
request.post {uri:url, json:components[0]}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.official).toBe(true)
@ -123,4 +123,19 @@ describe 'LevelComponent', ->
expect(body.official).toBe(false)
expect(body.version.isLatestMinor).toBe(false)
expect(body.version.isLatestMajor).toBe(false)
done()
done()
xit ' can\'t be requested with HTTP PUT method', (done) ->
request.put {uri:url+'/'+components[0]._id}, (err, res) ->
expect(res.statusCode).toBe(404)
done()
it ' can\'t be requested with HTTP HEAD method', (done) ->
request.head {uri:url+'/'+components[0]._id}, (err, res) ->
expect(res.statusCode).toBe(404)
done()
it ' can\'t be requested with HTTP DEL method', (done) ->
request.del {uri:url+'/'+components[0]._id}, (err, res) ->
expect(res.statusCode).toBe(404)
done()

View file

@ -2,7 +2,7 @@ require '../common'
describe 'LevelSystem', ->
raw =
system =
name:'Bashing'
description:'Performs Thang bashing updates for Bashes Thangs.'
code: """class Bashing extends System
@ -10,30 +10,121 @@ describe 'LevelSystem', ->
super world
"""
language: 'coffeescript'
official: true
permissions:simplePermissions
systems = {}
url = getURL('/db/level.system')
it 'clears things first', (done) ->
it 'preparing test : deletes all LevelSystem first', (done) ->
clearModels [Level, LevelSystem], (err) ->
expect(err).toBeNull()
done()
it 'can make a LevelSystem, without setting official.', (done) ->
loginJoe (joe) ->
request.post {uri:url, json:systems}, (err, res, body) ->
it 'can\'t be created by ordinary users.', (done) ->
loginJoe ->
request.post {uri:url, json:system}, (err, res, body) ->
expect(res.statusCode).toBe(403)
done()
it 'can be created by an admin.', (done) ->
loginAdmin ->
request.post {uri:url, json:system}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.official).toBeUndefined()
expect(body._id).toBeDefined()
expect(body.name).toBe(system.name)
expect(body.description).toBe(system.description)
expect(body.code).toBe(system.code)
expect(body.language).toBe(system.language)
expect(body.__v).toBe(0)
expect(body.creator).toBeDefined()
expect(body.original).toBeDefined()
expect(body.created).toBeDefined()
expect(body.version).toBeDefined()
expect(body.permissions).toBeDefined()
systems[0] = body
done()
it 'can allows admins to edit the official property.', (done) ->
systems[0].official = true
loginAdmin (joe) ->
request.post {uri:url, json:systems[0]}, (err, res, body) ->
expect(body.official).toBe(true)
expect(res.statusCode).toBe(200)
it 'have a unique name.', (done) ->
loginAdmin ->
request.post {uri:url, json:system}, (err, res, body) ->
expect(res.statusCode).toBe(422)
done()
it 'can be read by an admin.', (done) ->
loginAdmin ->
request.get {uri:url+'/'+systems[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
expect(body._id).toBe(systems[0]._id)
done()
it 'can be read by ordinary users.', (done) ->
loginJoe ->
request.get {uri:url+'/'+systems[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
expect(body._id).toBe(systems[0]._id)
expect(body.name).toBe(systems[0].name)
expect(body.slug).toBeDefined()
expect(body.description).toBe(systems[0].description)
expect(body.code).toBe(systems[0].code)
expect(body.language).toBe(systems[0].language)
expect(body.__v).toBe(0)
expect(body.official).toBeDefined()
expect(body.creator).toBeDefined()
expect(body.original).toBeDefined()
expect(body.created).toBeDefined()
expect(body.configSchema).toBeDefined()
expect(body.dependencies).toBeDefined()
expect(body.propertyDocumentation).toBeDefined()
expect(body.version.isLatestMajor).toBe(true)
expect(body.version.isLatestMinor).toBe(true)
expect(body.permissions).toBeDefined()
done()
it 'is unofficial by default', (done) ->
loginJoe ->
request.get {uri:url+'/'+systems[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
expect(body._id).toBe(systems[0]._id)
expect(body.official).toBe(false)
done()
it 'official property isn\'t editable by an ordinary user.', (done) ->
systems[0].official = true
loginJoe ->
request.post {uri:url, json:systems[0]}, (err, res, body) ->
expect(res.statusCode).toBe(403)
done()
it 'official property is editable by an admin.', (done) ->
systems[0].official = true
loginAdmin ->
request.post {uri:url, json:systems[0]}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.official).toBe(true)
expect(body.original).toBe(systems[0].original)
expect(body.version.isLatestMinor).toBe(true)
expect(body.version.isLatestMajor).toBe(true)
systems[1] = body
request.get {uri:url+'/'+systems[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
body = JSON.parse(body)
expect(body._id).toBe(systems[0]._id)
expect(body.official).toBe(false)
expect(body.version.isLatestMinor).toBe(false)
expect(body.version.isLatestMajor).toBe(false)
done()
it ' can\'t be requested with HTTP HEAD method', (done) ->
request.head {uri:url+'/'+systems[0]._id}, (err, res) ->
expect(res.statusCode).toBe(404)
done()
it ' can\'t be requested with HTTP DEL method', (done) ->
request.del {uri:url+'/'+systems[0]._id}, (err, res) ->
expect(res.statusCode).toBe(404)
done()

View file

@ -1,8 +1,11 @@
require '../common'
request = require 'request'
urlUser = '/db/user'
describe 'POST /db/user', ->
request = require 'request'
it 'clears the db first', (done) ->
it 'preparing test : clears the db first', (done) ->
clearModels [User], (err) ->
throw err if err
done()
@ -13,13 +16,13 @@ describe 'POST /db/user', ->
expect(user.get('password')).toBeUndefined()
expect(user?.get('passwordHash')).not.toBeUndefined()
if user?.get('passwordHash')?
expect(user.get('passwordHash')[..5]).toBe('948c7e')
expect(user.get('passwordHash')[..5]).toBe('31dc3d')
expect(user.get('permissions').length).toBe(0)
done()
it 'serves the user through /db/user/id', (done) ->
unittest.getNormalJoe (user) ->
url = getURL('/db/user/'+user._id)
url = getURL(urlUser+'/'+user._id)
request.get url, (err, res, body) ->
expect(res.statusCode).toBe(200)
user = JSON.parse(body)
@ -40,7 +43,7 @@ describe 'POST /db/user', ->
loginJoe ->
unittest.getAdmin (user) ->
url = getURL('/db/user/'+user._id)
url = getURL(urlUser+'/'+user._id)
request.get url, (err, res, body) ->
expect(res.statusCode).toBe(200)
user = JSON.parse(body)
@ -55,7 +58,7 @@ describe 'PUT /db/user', ->
req = request.post getURL('/auth/logout'),
(err, res) ->
expect(res.statusCode).toBe(200)
req = request.put getURL('/db/user'),
req = request.put getURL(urlUser),
(err, res) ->
expect(res.statusCode).toBe(422)
expect(res.body).toBe('No input.')
@ -66,7 +69,7 @@ describe 'PUT /db/user', ->
it 'denies requests to edit someone who is not joe', (done) ->
unittest.getAdmin (admin) ->
req = request.put getURL('/db/user'),
req = request.put getURL(urlUser),
(err, res) ->
expect(res.statusCode).toBe(403)
done()
@ -74,7 +77,7 @@ describe 'PUT /db/user', ->
it 'denies invalid data', (done) ->
unittest.getNormalJoe (joe) ->
req = request.put getURL('/db/user'),
req = request.put getURL(urlUser),
(err, res) ->
expect(res.statusCode).toBe(422)
expect(res.body.indexOf('too long')).toBeGreaterThan(-1)
@ -87,13 +90,10 @@ ghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghl
it 'logs in as admin', (done) ->
loginAdmin -> done()
it 'denies non-existent ids', (done) ->
req = request.put getURL('/db/user'),
req = request.put getURL(urlUser),
(err, res) ->
expect(res.statusCode).toBe(404)
expect(res.body).toBe('Resource not found.')
done()
done()
form = req.form()
form.append('_id', '513108d4cb8b610000000004')
@ -102,7 +102,7 @@ ghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghl
it 'denies if the email being changed is already taken', (done) ->
unittest.getNormalJoe (joe) ->
unittest.getAdmin (admin) ->
req = request.put getURL('/db/user'), (err, res) ->
req = request.put getURL(urlUser), (err, res) ->
expect(res.statusCode).toBe(409)
expect(res.body.indexOf('already used')).toBeGreaterThan(-1)
done()
@ -112,7 +112,7 @@ ghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghl
it 'works', (done) ->
unittest.getNormalJoe (joe) ->
req = request.put getURL('/db/user'), (err, res) ->
req = request.put getURL(urlUser), (err, res) ->
expect(res.statusCode).toBe(200)
unittest.getUser('New@email.com', 'null', (joe) ->
expect(joe.get('name')).toBe('Wilhelm')
@ -124,8 +124,9 @@ ghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghlfarghlarghl
form.append('email', 'New@email.com')
form.append('name', 'Wilhelm')
describe 'GET /db/user', ->
request = require 'request'
it 'logs in as admin', (done) ->
req = request.post(getURL('/auth/login'), (error, response) ->
expect(response.statusCode).toBe(200)
@ -143,7 +144,7 @@ describe 'GET /db/user', ->
['sort', '-dateCreated']
]
options = {
url: getURL('/db/user')
url: getURL(urlUser)
qs: {
conditions: JSON.stringify(conditions)
}
@ -161,7 +162,7 @@ describe 'GET /db/user', ->
['lime', 20]
]
options = {
url: getURL('/db/user')
url: getURL(urlUser)
qs: {
conditions: JSON.stringify(conditions)
}

View file

@ -23,8 +23,8 @@ describe 'CampaignStatus', ->
.populate('campaign')
.exec (err, c) ->
expect(err).toBe(null)
expect(c.user.get('name')).not.toBeUndefined()
expect(c.campaign.get('name')).not.toBeUndefined()
expect(c.user.get('name')).toBeDefined()
expect(c.campaign.get('name')).toBeDefined()
done()
it 'rejects duplicates', (done) ->

View file

@ -27,5 +27,5 @@ describe 'LevelDraft', ->
throw err if err
LevelDraft.findOne {_id:draft._id}, (err, fetched) ->
expect(fetched.level.original).not.toBeUndefined()
expect(fetched.level.original).toBeDefined()
done()

View file

@ -259,7 +259,7 @@ describe 'VersionedPlugin', ->
expect(results.length).toBe(3)
expect(results[2].slug).toBeUndefined()
expect(results[1].slug).toBeUndefined()
expect(results[0].slug).not.toBeUndefined()
expect(results[0].slug).toBeDefined()
done()