Merge branch 'master' into production
This commit is contained in:
commit
76ee449693
66 changed files with 364 additions and 85 deletions
app
locale
ar.coffeebg.coffeeca.coffeecs.coffeeda.coffeede-AT.coffeede-CH.coffeede-DE.coffeeel.coffeeen-AU.coffeeen-GB.coffeeen-US.coffeeen.coffeees-419.coffeees-ES.coffeefa.coffeefi.coffeefr.coffeegl.coffeehe.coffeehi.coffeehu.coffeeid.coffeeit.coffeeja.coffeeko.coffeelt.coffeems.coffeenb.coffeenl-BE.coffeenl-NL.coffeenn.coffeeno.coffeepl.coffeept-BR.coffeept-PT.coffeero.coffeeru.coffeesk.coffeesl.coffeesr.coffeesv.coffeeth.coffeetr.coffeeuk.coffeeur.coffeevi.coffeezh-HANS.coffeezh-HANT.coffeezh-WUU-HANS.coffeezh-WUU-HANT.coffee
schemas/models
styles/play/level/tome
templates/play/level
views
account
play
server
payments
users
test/server/functional
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "български език", englishDescri
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalitza el teu bruixot"
|
customize_wizard: "Personalitza el teu bruixot"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Upravit Kouzelníka"
|
customize_wizard: "Upravit Kouzelníka"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Tilpas troldmand"
|
customize_wizard: "Tilpas troldmand"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Bearbeite den Zauberer"
|
customize_wizard: "Bearbeite den Zauberer"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Zauberer apasse"
|
customize_wizard: "Zauberer apasse"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
|
||||||
tip_hofstadters_law: "Hofstadter's Gesetz: Es dauert immer länger als erwartet, auch wenn du Hofstadter's Gesetz anwendest."
|
tip_hofstadters_law: "Hofstadter's Gesetz: Es dauert immer länger als erwartet, auch wenn du Hofstadter's Gesetz anwendest."
|
||||||
tip_premature_optimization: "Vorzeitige Optimierung ist die Wurzel allen Übels (oder mindestens des meisten) bei der Programmierung - Donald Knuth"
|
tip_premature_optimization: "Vorzeitige Optimierung ist die Wurzel allen Übels (oder mindestens des meisten) bei der Programmierung - Donald Knuth"
|
||||||
tip_brute_force: "Verwende im Zweifelsfall rohe Gewalt. - Ken Thompson"
|
tip_brute_force: "Verwende im Zweifelsfall rohe Gewalt. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Bearbeite den Zauberer"
|
customize_wizard: "Bearbeite den Zauberer"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Προσαρμογή Μάγου"
|
customize_wizard: "Προσαρμογή Μάγου"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Customise Wizard"
|
customize_wizard: "Customise Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@
|
||||||
tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Customize Wizard"
|
customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip
|
||||||
tip_hofstadters_law: "Ley de Hofstadter: Siempre toma más tiempo del que esperas, inclso cuando tienes en cuenta la ley de Hofstadter."
|
tip_hofstadters_law: "Ley de Hofstadter: Siempre toma más tiempo del que esperas, inclso cuando tienes en cuenta la ley de Hofstadter."
|
||||||
tip_premature_optimization: "La optimización prematura es la raíz de la maldad. - Donald Knuth"
|
tip_premature_optimization: "La optimización prematura es la raíz de la maldad. - Donald Knuth"
|
||||||
tip_brute_force: "Cuando tengas duda, usa la fuerza bruta. - Ken Thompson"
|
tip_brute_force: "Cuando tengas duda, usa la fuerza bruta. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalizar Hechicero"
|
customize_wizard: "Personalizar Hechicero"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
tip_premature_optimization: "La optimizacion prematura es la raiz de todo mal. - Donald Knuth"
|
tip_premature_optimization: "La optimizacion prematura es la raiz de todo mal. - Donald Knuth"
|
||||||
tip_brute_force: "Cuando haya dudas, usa la fuerza bruta. - Ken Thompson"
|
tip_brute_force: "Cuando haya dudas, usa la fuerza bruta. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalizar Mago"
|
customize_wizard: "Personalizar Mago"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
||||||
tip_hofstadters_law: "Loi de Hofstadter: Il faut toujours plus de temps que prévu, même si vous prenez en compte la loi de Hofstadter."
|
tip_hofstadters_law: "Loi de Hofstadter: Il faut toujours plus de temps que prévu, même si vous prenez en compte la loi de Hofstadter."
|
||||||
tip_premature_optimization: "L'optimisation prématurée est la racine de tous les maux. - Donald Knuth"
|
tip_premature_optimization: "L'optimisation prématurée est la racine de tous les maux. - Donald Knuth"
|
||||||
tip_brute_force: "En cas de doute, utiliser la force brute. - Ken Thompson"
|
tip_brute_force: "En cas de doute, utiliser la force brute. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personnaliser le magicien"
|
customize_wizard: "Personnaliser le magicien"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
tip_premature_optimization: "A optimizacion prematura é a raíz de todo mal. - Donald Knuth"
|
tip_premature_optimization: "A optimizacion prematura é a raíz de todo mal. - Donald Knuth"
|
||||||
tip_brute_force: "Cando hai dúbidas, usa a forza bruta. - Ken Thompson"
|
tip_brute_force: "Cando hai dúbidas, usa a forza bruta. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Persoalizar Mago"
|
customize_wizard: "Persoalizar Mago"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Varázsló testreszabása"
|
customize_wizard: "Varázsló testreszabása"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalizza il mago"
|
customize_wizard: "Personalizza il mago"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "魔法使いの設定"
|
customize_wizard: "魔法使いの設定"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "사용자 정의 마법사"
|
customize_wizard: "사용자 정의 마법사"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
|
||||||
tip_hofstadters_law: "Hofstadters Lov: Ting tar alltid lenger tid enn du tror, selv når du tar Hofstadters Lov med i beregningen."
|
tip_hofstadters_law: "Hofstadters Lov: Ting tar alltid lenger tid enn du tror, selv når du tar Hofstadters Lov med i beregningen."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Tilpass Trollmann"
|
customize_wizard: "Tilpass Trollmann"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Pas Tovenaar aan"
|
customize_wizard: "Pas Tovenaar aan"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Pas Tovenaar aan"
|
customize_wizard: "Pas Tovenaar aan"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Tilpass trollmann"
|
customize_wizard: "Tilpass trollmann"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Spersonalizuj czarodzieja"
|
customize_wizard: "Spersonalizuj czarodzieja"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
|
||||||
tip_hofstadters_law: "Lei de Hofstadter: Sempre demora mais do que você espera, mesmo quando você leva em consideração a Lei de Hofstadter."
|
tip_hofstadters_law: "Lei de Hofstadter: Sempre demora mais do que você espera, mesmo quando você leva em consideração a Lei de Hofstadter."
|
||||||
tip_premature_optimization: "Uma otimização permatura é a raíz de todos os males. - Donald Knuth"
|
tip_premature_optimization: "Uma otimização permatura é a raíz de todos os males. - Donald Knuth"
|
||||||
tip_brute_force: "Na dúvida, utilize força bruta. - Ken Thompson"
|
tip_brute_force: "Na dúvida, utilize força bruta. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalize o feiticeiro"
|
customize_wizard: "Personalize o feiticeiro"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
||||||
tip_hofstadters_law: "Lei de Hofstadter: Tudo demora sempre mais do que pensas, mesmo quando levas em conta a Lei de Hofstadter."
|
tip_hofstadters_law: "Lei de Hofstadter: Tudo demora sempre mais do que pensas, mesmo quando levas em conta a Lei de Hofstadter."
|
||||||
tip_premature_optimization: "Uma otimização permatura é a raíz de todo o mal. - Donald Knuth"
|
tip_premature_optimization: "Uma otimização permatura é a raíz de todo o mal. - Donald Knuth"
|
||||||
tip_brute_force: "Quando em dúvida, usa a força bruta. - Ken Thompson"
|
tip_brute_force: "Quando em dúvida, usa a força bruta. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalizar Feiticeiro"
|
customize_wizard: "Personalizar Feiticeiro"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Personalizează Wizard-ul"
|
customize_wizard: "Personalizează Wizard-ul"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
||||||
tip_hofstadters_law: "Закон Хофштадтера: Любое дело всегда длится дольше, чем ожидается, даже если учесть закон Хофштадтера."
|
tip_hofstadters_law: "Закон Хофштадтера: Любое дело всегда длится дольше, чем ожидается, даже если учесть закон Хофштадтера."
|
||||||
tip_premature_optimization: "Поспешная оптимизация - корень всех зол. - Donald Knuth"
|
tip_premature_optimization: "Поспешная оптимизация - корень всех зол. - Donald Knuth"
|
||||||
tip_brute_force: "Когда сомневаешься используй грубую силу. - Кен Томпсон"
|
tip_brute_force: "Когда сомневаешься используй грубую силу. - Кен Томпсон"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Настройки волшебника"
|
customize_wizard: "Настройки волшебника"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
@ -376,13 +377,13 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
||||||
blocks: "Блокирует" # As in "this shield blocks this much damage"
|
blocks: "Блокирует" # As in "this shield blocks this much damage"
|
||||||
skills: "Умения"
|
skills: "Умения"
|
||||||
available_for_purchase: "Доступно для покупки"
|
available_for_purchase: "Доступно для покупки"
|
||||||
level_to_unlock: "Разблокируется на уровне:" # ToDo: check in interface
|
level_to_unlock: "Разблокируется на уровне:"
|
||||||
restricted_to_certain_heroes: "Только определенные герои могут играть этот уровень."
|
restricted_to_certain_heroes: "Только определенные герои могут играть этот уровень."
|
||||||
|
|
||||||
skill_docs:
|
skill_docs:
|
||||||
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
|
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
|
||||||
# read_only: "read-only"
|
# read_only: "read-only"
|
||||||
action_name: "имя" # ToDo: check in interface
|
action_name: "имя"
|
||||||
action_cooldown: "Применяется"
|
action_cooldown: "Применяется"
|
||||||
action_specific_cooldown: "Восстановление"
|
action_specific_cooldown: "Восстановление"
|
||||||
action_damage: "Повреждения"
|
action_damage: "Повреждения"
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Прилагоди Чаробњака"
|
customize_wizard: "Прилагоди Чаробњака"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Skräddarsy trollkarl"
|
customize_wizard: "Skräddarsy trollkarl"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Sihirbazı Düzenle"
|
customize_wizard: "Sihirbazı Düzenle"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "українська мова", englishDesc
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Налаштування персонажа"
|
customize_wizard: "Налаштування персонажа"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "Tùy chỉnh Wizard"
|
customize_wizard: "Tùy chỉnh Wizard"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
||||||
tip_hofstadters_law: "侯世达定律:做事所花费的时间总是比你预期的要长,即使你的预期中考虑了侯世达定律。"
|
tip_hofstadters_law: "侯世达定律:做事所花费的时间总是比你预期的要长,即使你的预期中考虑了侯世达定律。"
|
||||||
tip_premature_optimization: "过早的优化是万恶之源。 - 高德纳"
|
tip_premature_optimization: "过早的优化是万恶之源。 - 高德纳"
|
||||||
tip_brute_force: "拿不准时就用穷举法。 - Ken Thompson"
|
tip_brute_force: "拿不准时就用穷举法。 - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "自定义向导"
|
customize_wizard: "自定义向导"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "自定義巫師"
|
customize_wizard: "自定義巫師"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
# customize_wizard: "Customize Wizard"
|
# customize_wizard: "Customize Wizard"
|
||||||
|
|
||||||
# game_menu:
|
# game_menu:
|
||||||
|
|
|
@ -296,6 +296,7 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
|
||||||
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
|
||||||
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
|
||||||
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
|
||||||
|
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
|
||||||
customize_wizard: "自設定獻路人"
|
customize_wizard: "自設定獻路人"
|
||||||
|
|
||||||
game_menu:
|
game_menu:
|
||||||
|
|
|
@ -278,6 +278,9 @@ _.extend UserSchema.properties,
|
||||||
planID: { enum: ['basic'] }
|
planID: { enum: ['basic'] }
|
||||||
subscriptionID: { type: 'string' }
|
subscriptionID: { type: 'string' }
|
||||||
token: { type: 'string' }
|
token: { type: 'string' }
|
||||||
|
couponID: { type: 'string' }
|
||||||
|
discountID: { type: 'string' }
|
||||||
|
free: { type: ['boolean', 'string'], format: 'date-time' }
|
||||||
}
|
}
|
||||||
|
|
||||||
c.extendBasicProperties UserSchema, 'user'
|
c.extendBasicProperties UserSchema, 'user'
|
||||||
|
|
|
@ -107,6 +107,9 @@
|
||||||
.executed
|
.executed
|
||||||
background-color: rgba(110, 110, 110, 0.12)
|
background-color: rgba(110, 110, 110, 0.12)
|
||||||
|
|
||||||
|
.locked-code
|
||||||
|
border: 1px dashed rgba(53, 45, 34, 0.5)
|
||||||
|
|
||||||
+keyframes(pulseRedBackground)
|
+keyframes(pulseRedBackground)
|
||||||
from
|
from
|
||||||
background-color: rgba(255, 45, 27, 0.4)
|
background-color: rgba(255, 45, 27, 0.4)
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
strong.tip.rare(data-i18n='play_level.tip_hofstadters_law') Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
|
strong.tip.rare(data-i18n='play_level.tip_hofstadters_law') Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
|
||||||
strong.tip.rare(data-i18n='play_level.tip_premature_optimization') Premature optimization is the root of all evil - Donald Knuth
|
strong.tip.rare(data-i18n='play_level.tip_premature_optimization') Premature optimization is the root of all evil - Donald Knuth
|
||||||
strong.tip.rare(data-i18n='play_level.tip_brute_force') When in doubt, use brute force. - Ken Thompson
|
strong.tip.rare(data-i18n='play_level.tip_brute_force') When in doubt, use brute force. - Ken Thompson
|
||||||
|
strong.tip.rare(data-i18n='play_level.tip_extrapolation') There are only two kinds of people: those that can extrapolate from incomplete data...
|
||||||
strong.tip.rare
|
strong.tip.rare
|
||||||
span(data-i18n='play_level.tip_harry') Yer a Wizard,
|
span(data-i18n='play_level.tip_harry') Yer a Wizard,
|
||||||
span= me.get('name', true)
|
span= me.get('name', true)
|
||||||
|
|
|
@ -30,6 +30,7 @@ module.exports = class PaymentsView extends RootView
|
||||||
onClickStartSubscription: (e) ->
|
onClickStartSubscription: (e) ->
|
||||||
@openModalView new SubscribeModal()
|
@openModalView new SubscribeModal()
|
||||||
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'payments view'
|
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'payments view'
|
||||||
|
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
|
||||||
|
|
||||||
onSubscribed: ->
|
onSubscribed: ->
|
||||||
document.location.reload()
|
document.location.reload()
|
||||||
|
|
|
@ -124,6 +124,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
@openModalView? new modal() unless window.currentModal
|
@openModalView? new modal() unless window.currentModal
|
||||||
if modal is SubscribeModal
|
if modal is SubscribeModal
|
||||||
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'world map loadded'
|
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'world map loadded'
|
||||||
|
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
|
||||||
, 2000
|
, 2000
|
||||||
|
|
||||||
onSubscribed: ->
|
onSubscribed: ->
|
||||||
|
@ -214,6 +215,7 @@ module.exports = class WorldMapView extends RootView
|
||||||
@openModalView new modal()
|
@openModalView new modal()
|
||||||
if modal is SubscribeModal
|
if modal is SubscribeModal
|
||||||
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'map level clicked'
|
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'map level clicked'
|
||||||
|
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
|
||||||
else if $(e.target).attr('disabled')
|
else if $(e.target).attr('disabled')
|
||||||
Backbone.Mediator.publish 'router:navigate', route: '/contribute/adventurer'
|
Backbone.Mediator.publish 'router:navigate', route: '/contribute/adventurer'
|
||||||
return
|
return
|
||||||
|
@ -222,11 +224,13 @@ module.exports = class WorldMapView extends RootView
|
||||||
else
|
else
|
||||||
@startLevel levelElement
|
@startLevel levelElement
|
||||||
window.tracker?.trackEvent 'Clicked Level', category: 'World Map', levelID: levelID, ['Google Analytics']
|
window.tracker?.trackEvent 'Clicked Level', category: 'World Map', levelID: levelID, ['Google Analytics']
|
||||||
|
window.tracker?.trackPageView "world-map/clicked-level/#{levelID}", ['Google Analytics']
|
||||||
|
|
||||||
onClickStartLevel: (e) ->
|
onClickStartLevel: (e) ->
|
||||||
levelElement = $(e.target).parents('.level-info-container')
|
levelElement = $(e.target).parents('.level-info-container')
|
||||||
@startLevel levelElement
|
@startLevel levelElement
|
||||||
window.tracker?.trackEvent 'Clicked Start Level', category: 'World Map', levelID: levelElement.data('level-id'), ['Google Analytics']
|
window.tracker?.trackEvent 'Clicked Start Level', category: 'World Map', levelID: levelElement.data('level-id'), ['Google Analytics']
|
||||||
|
window.tracker?.trackPageView "world-map/clicked-start-level/#{levelElement.data('level-id')}", ['Google Analytics']
|
||||||
|
|
||||||
startLevel: (levelElement) ->
|
startLevel: (levelElement) ->
|
||||||
@setupManager?.destroy()
|
@setupManager?.destroy()
|
||||||
|
|
|
@ -114,6 +114,7 @@ module.exports = class LevelLoadingView extends CocoView
|
||||||
@openModalView new modal()
|
@openModalView new modal()
|
||||||
if modal is SubscribeModal
|
if modal is SubscribeModal
|
||||||
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'level loading'
|
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'level loading'
|
||||||
|
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
|
||||||
|
|
||||||
onSubscribed: ->
|
onSubscribed: ->
|
||||||
document.location.reload()
|
document.location.reload()
|
||||||
|
|
|
@ -273,6 +273,15 @@ module.exports = class SpellView extends CocoView
|
||||||
return true for range in @readOnlyRanges when leftRange.intersects(range)
|
return true for range in @readOnlyRanges when leftRange.intersects(range)
|
||||||
false
|
false
|
||||||
|
|
||||||
|
intersectsRight = =>
|
||||||
|
rightRange = @ace.getSelectionRange().clone()
|
||||||
|
if rightRange.end.column < @aceDoc.getLine(rightRange.end.row).length
|
||||||
|
rightRange.setEnd rightRange.end.row, rightRange.end.column + 1
|
||||||
|
else if rightRange.start.row < @aceDoc.getLength() - 1
|
||||||
|
rightRange.setEnd rightRange.end.row + 1, 0
|
||||||
|
return true for range in @readOnlyRanges when rightRange.intersects(range)
|
||||||
|
false
|
||||||
|
|
||||||
preventReadonly = (next) ->
|
preventReadonly = (next) ->
|
||||||
return true if intersects()
|
return true if intersects()
|
||||||
next?()
|
next?()
|
||||||
|
@ -284,27 +293,16 @@ module.exports = class SpellView extends CocoView
|
||||||
wrapper => orig.apply obj, args
|
wrapper => orig.apply obj, args
|
||||||
obj[method]
|
obj[method]
|
||||||
|
|
||||||
finishRange = (row, startRow, startColumn) =>
|
if @lockedCodeMarkerID?
|
||||||
range = new Range startRow, startColumn, row, @aceSession.getLine(row).length - 1
|
@aceSession.removeMarker @lockedCodeMarkerID
|
||||||
range.start = @aceDoc.createAnchor range.start
|
@lockedCodeMarkerID = null
|
||||||
range.end = @aceDoc.createAnchor range.end
|
|
||||||
range.end.$insertRight = true
|
|
||||||
@readOnlyRanges.push range
|
|
||||||
|
|
||||||
# Create a read-only range for each chunk of text not separated by an empty line
|
|
||||||
@readOnlyRanges = []
|
@readOnlyRanges = []
|
||||||
startRow = startColumn = null
|
lines = @aceDoc.getAllLines()
|
||||||
for row in [0...@aceSession.getLength()]
|
lastRow = row for line, row in lines when not /^\s*$/.test(line)
|
||||||
unless /^\s*$/.test @aceSession.getLine(row)
|
if lastRow?
|
||||||
unless startRow? and startColumn?
|
@readOnlyRanges.push new Range 0, 0, lastRow, lines[lastRow].length - 1
|
||||||
startRow = row
|
@lockedCodeMarkerID = @aceSession.addMarker @readOnlyRanges[0], 'locked-code', 'fullLine'
|
||||||
startColumn = 0
|
|
||||||
else
|
|
||||||
if startRow? and startColumn?
|
|
||||||
finishRange row - 1, startRow, startColumn
|
|
||||||
startRow = startColumn = null
|
|
||||||
if startRow? and startColumn?
|
|
||||||
finishRange @aceSession.getLength() - 1, startRow, startColumn
|
|
||||||
|
|
||||||
# Override write operations that intersect with default code
|
# Override write operations that intersect with default code
|
||||||
interceptCommand @ace, 'onPaste', preventReadonly
|
interceptCommand @ace, 'onPaste', preventReadonly
|
||||||
|
@ -319,6 +317,9 @@ module.exports = class SpellView extends CocoView
|
||||||
if e.command.name in ['Backspace', 'throttle-backspaces'] and intersectsLeft()
|
if e.command.name in ['Backspace', 'throttle-backspaces'] and intersectsLeft()
|
||||||
@zatanna?.off?()
|
@zatanna?.off?()
|
||||||
return false
|
return false
|
||||||
|
if e.command.name is 'del' and intersectsRight()
|
||||||
|
@zatanna?.off?()
|
||||||
|
return false
|
||||||
if e.command.name in ['enter-skip-delimiters', 'Enter', 'Return']
|
if e.command.name in ['enter-skip-delimiters', 'Enter', 'Return']
|
||||||
if intersects()
|
if intersects()
|
||||||
e.editor.navigateDown 1
|
e.editor.navigateDown 1
|
||||||
|
|
|
@ -45,10 +45,12 @@ module.exports = class SubscribeModal extends ModalView
|
||||||
container: @$el
|
container: @$el
|
||||||
).on 'shown.bs.popover', =>
|
).on 'shown.bs.popover', =>
|
||||||
application.tracker?.trackEvent 'Subscription parent hover', {}
|
application.tracker?.trackEvent 'Subscription parent hover', {}
|
||||||
|
application.tracker?.trackPageView "subscription/parent-hover", ['Google Analytics']
|
||||||
|
|
||||||
onClickPurchaseButton: (e) ->
|
onClickPurchaseButton: (e) ->
|
||||||
@playSound 'menu-button-click'
|
@playSound 'menu-button-click'
|
||||||
application.tracker?.trackEvent 'Started subscription purchase', {}
|
application.tracker?.trackEvent 'Started subscription purchase', {}
|
||||||
|
application.tracker?.trackPageView "subscription/start-purchase", ['Google Analytics']
|
||||||
stripeHandler.open({
|
stripeHandler.open({
|
||||||
description: $.i18n.t('subscribe.stripe_description')
|
description: $.i18n.t('subscribe.stripe_description')
|
||||||
amount: @product.amount
|
amount: @product.amount
|
||||||
|
@ -69,6 +71,7 @@ module.exports = class SubscribeModal extends ModalView
|
||||||
|
|
||||||
onSubscriptionSuccess: ->
|
onSubscriptionSuccess: ->
|
||||||
application.tracker?.trackEvent 'Finished subscription purchase', {}
|
application.tracker?.trackEvent 'Finished subscription purchase', {}
|
||||||
|
application.tracker?.trackPageView "subscription/finish-purchase", ['Google Analytics']
|
||||||
Backbone.Mediator.publish 'subscribe-modal:subscribed', {}
|
Backbone.Mediator.publish 'subscribe-modal:subscribed', {}
|
||||||
@playSound 'victory'
|
@playSound 'victory'
|
||||||
@hide()
|
@hide()
|
||||||
|
|
48
server/payments/discount_handler.coffee
Normal file
48
server/payments/discount_handler.coffee
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Not paired with a document in the DB, just handles coordinating between
|
||||||
|
# the stripe property in the user with what's being stored in Stripe.
|
||||||
|
|
||||||
|
Handler = require '../commons/Handler'
|
||||||
|
config = require '../../server_config'
|
||||||
|
stripe = require('stripe')(config.stripe.secretKey)
|
||||||
|
|
||||||
|
class DiscountHandler extends Handler
|
||||||
|
logDiscountError: (req, msg) ->
|
||||||
|
console.warn "Discount Error: #{req.user.get('slug')} (#{req.user._id}): '#{msg}'"
|
||||||
|
|
||||||
|
discountUser: (req, user, done) ->
|
||||||
|
if (not user) or user.isAnonymous()
|
||||||
|
return done({res: 'User must not be anonymous.', code: 403})
|
||||||
|
|
||||||
|
couponID = req.body.stripe.couponID
|
||||||
|
if not couponID
|
||||||
|
@logDiscountError(req, 'Missing couponID.')
|
||||||
|
return done({res: 'Missing couponID.', code: 422})
|
||||||
|
|
||||||
|
stripe.coupons.retrieve couponID, (err, coupon) =>
|
||||||
|
if (err)
|
||||||
|
return done({res: 'No coupon with id '+couponID, code: 404})
|
||||||
|
|
||||||
|
if customerID = user.get('stripe')?.customerID
|
||||||
|
options = { coupon: coupon.id }
|
||||||
|
stripe.customers.update customerID, options, (err, customer) =>
|
||||||
|
if err
|
||||||
|
@logDiscountError(req, 'Error applying coupon to customer'+customerID)
|
||||||
|
return done({res: 'Error applying coupon to customer.', code: 500})
|
||||||
|
done()
|
||||||
|
|
||||||
|
else
|
||||||
|
# couponID will be set on the user by the handler
|
||||||
|
done()
|
||||||
|
|
||||||
|
removeDiscountFromCustomer: (req, user, done) ->
|
||||||
|
customerID = user.get('stripe').customerID
|
||||||
|
return done() unless customerID
|
||||||
|
|
||||||
|
stripe.customers.deleteDiscount customerID, (err, customer) =>
|
||||||
|
if err
|
||||||
|
console.log 'err?', err
|
||||||
|
@logDiscountError(req, 'Error removing coupon from customer ' + customerID)
|
||||||
|
return done({res: 'Error applying coupon to customer.', code: 500})
|
||||||
|
done()
|
||||||
|
|
||||||
|
module.exports = new DiscountHandler()
|
|
@ -166,29 +166,34 @@ PaymentHandler = class PaymentHandler extends Handler
|
||||||
handleStripePaymentPost: (req, res, timestamp, productID, token) ->
|
handleStripePaymentPost: (req, res, timestamp, productID, token) ->
|
||||||
|
|
||||||
# First, make sure we save the payment info as a Customer object, if we haven't already.
|
# First, make sure we save the payment info as a Customer object, if we haven't already.
|
||||||
if not req.user.get('stripe')?.customerID
|
if token
|
||||||
stripe.customers.create({
|
customerID = req.user.get('stripe')?.customerID
|
||||||
card: token
|
|
||||||
email: req.user.get('email')
|
if customerID
|
||||||
metadata: {
|
# old customer, new token. Save it.
|
||||||
id: req.user._id + ''
|
stripe.customers.update customerID, { card: token }, (err, customer) =>
|
||||||
slug: req.user.get('slug')
|
|
||||||
}
|
|
||||||
}).then(((customer) =>
|
|
||||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
|
||||||
stripeInfo.customerID = customer.id
|
|
||||||
req.user.set('stripe', stripeInfo)
|
|
||||||
req.user.save((err) =>
|
|
||||||
if err
|
|
||||||
@logPaymentError(req, 'Stripe customer id save db error. '+err)
|
|
||||||
return @sendDatabaseError(res, err)
|
|
||||||
@beginStripePayment(req, res, timestamp, productID)
|
@beginStripePayment(req, res, timestamp, productID)
|
||||||
)
|
|
||||||
),
|
else
|
||||||
(err) =>
|
newCustomer = {
|
||||||
@logPaymentError(req, 'Stripe customer creation error. '+err)
|
card: token
|
||||||
return @sendDatabaseError(res, err)
|
email: req.user.get('email')
|
||||||
)
|
metadata: { id: req.user._id + '', slug: req.user.get('slug') }
|
||||||
|
}
|
||||||
|
|
||||||
|
stripe.customers.create newCustomer, (err, customer) =>
|
||||||
|
if err
|
||||||
|
@logPaymentError(req, 'Stripe customer creation error. '+err)
|
||||||
|
return @sendDatabaseError(res, err)
|
||||||
|
|
||||||
|
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||||
|
stripeInfo.customerID = customer.id
|
||||||
|
req.user.set('stripe', stripeInfo)
|
||||||
|
req.user.save (err) =>
|
||||||
|
if err
|
||||||
|
@logPaymentError(req, 'Stripe customer id save db error. '+err)
|
||||||
|
return @sendDatabaseError(res, err)
|
||||||
|
@beginStripePayment(req, res, timestamp, productID)
|
||||||
|
|
||||||
else
|
else
|
||||||
@beginStripePayment(req, res, timestamp, productID)
|
@beginStripePayment(req, res, timestamp, productID)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
Handler = require '../commons/Handler'
|
Handler = require '../commons/Handler'
|
||||||
config = require '../../server_config'
|
config = require '../../server_config'
|
||||||
stripe = require('stripe')(config.stripe.secretKey)
|
stripe = require('stripe')(config.stripe.secretKey)
|
||||||
|
discountHandler = require './discount_handler'
|
||||||
|
|
||||||
subscriptions = {
|
subscriptions = {
|
||||||
basic: {
|
basic: {
|
||||||
|
@ -19,61 +20,52 @@ class SubscriptionHandler extends Handler
|
||||||
if (not req.user) or req.user.isAnonymous()
|
if (not req.user) or req.user.isAnonymous()
|
||||||
return done({res: 'You must be signed in to subscribe.', code: 403})
|
return done({res: 'You must be signed in to subscribe.', code: 403})
|
||||||
|
|
||||||
stripeToken = req.body.stripe?.token
|
token = req.body.stripe?.token
|
||||||
extantCustomerID = user.get('stripe')?.customerID
|
customerID = user.get('stripe')?.customerID
|
||||||
if not (stripeToken or extantCustomerID)
|
if not (token or customerID)
|
||||||
@logSubscriptionError(req, 'Missing stripe token or customer ID.')
|
@logSubscriptionError(req, 'Missing stripe token or customer ID.')
|
||||||
return done({res: 'Missing stripe token or customer ID.', code: 422})
|
return done({res: 'Missing stripe token or customer ID.', code: 422})
|
||||||
|
|
||||||
if stripeToken
|
if token
|
||||||
stripe.customers.create({
|
if customerID
|
||||||
card: stripeToken
|
stripe.customers.update customerID, { card: token }, (err, customer) =>
|
||||||
email: req.user.get('email')
|
@checkForExistingSubscription(req, user, customer, done)
|
||||||
metadata: {
|
|
||||||
id: req.user._id + ''
|
else
|
||||||
slug: req.user.get('slug')
|
newCustomer = {
|
||||||
|
card: token
|
||||||
|
email: req.user.get('email')
|
||||||
|
metadata: { id: req.user._id + '', slug: req.user.get('slug') }
|
||||||
}
|
}
|
||||||
}).then(((customer) =>
|
|
||||||
|
stripe.customers.create newCustomer, (err, customer) =>
|
||||||
|
if err
|
||||||
|
if err.type in ['StripeCardError', 'StripeInvalidRequestError']
|
||||||
|
return done({res: 'Card error', code: 402})
|
||||||
|
else
|
||||||
|
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
|
||||||
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
||||||
stripeInfo.customerID = customer.id
|
stripeInfo.customerID = customer.id
|
||||||
req.user.set('stripe', stripeInfo)
|
req.user.set('stripe', stripeInfo)
|
||||||
req.user.save((err) =>
|
req.user.save (err) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer id save db error. '+err)
|
@logSubscriptionError(req, 'Stripe customer id save db error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
@checkForExistingSubscription(req, user, customer, done)
|
@checkForExistingSubscription(req, user, customer, done)
|
||||||
)
|
|
||||||
),
|
|
||||||
(err) =>
|
|
||||||
if err.type in ['StripeCardError', 'StripeInvalidRequestError']
|
|
||||||
done({res: 'Card error', code: 402})
|
|
||||||
else
|
|
||||||
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
|
|
||||||
return done({res: 'Database error.', code: 500})
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
else
|
||||||
stripe.customers.retrieve(extantCustomerID, (err, customer) =>
|
stripe.customers.retrieve(customerID, (err, customer) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
|
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
else if not customer
|
@checkForExistingSubscription(req, user, customer, done)
|
||||||
# TODO: what actually happens when you try to retrieve a customer and it DNE?
|
|
||||||
@logSubscriptionError(req, 'Stripe customer id is missing! '+err)
|
|
||||||
stripeInfo = _.cloneDeep(req.user.get('stripe') ? {})
|
|
||||||
delete stripeInfo.customerID
|
|
||||||
req.user.set('stripe', stripeInfo)
|
|
||||||
req.user.save (err) =>
|
|
||||||
if err
|
|
||||||
@logSubscriptionError(req, 'Stripe customer id delete db error. '+err)
|
|
||||||
return done({res: 'Database error.', code: 500})
|
|
||||||
@subscribeUser(req, done)
|
|
||||||
else
|
|
||||||
@checkForExistingSubscription(req, user, customer, done)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
checkForExistingSubscription: (req, user, customer, done) ->
|
checkForExistingSubscription: (req, user, customer, done) ->
|
||||||
|
couponID = user.get('stripe')?.couponID
|
||||||
if subscription = customer.subscriptions?.data?[0]
|
if subscription = customer.subscriptions?.data?[0]
|
||||||
|
|
||||||
if subscription.cancel_at_period_end
|
if subscription.cancel_at_period_end
|
||||||
|
@ -87,30 +79,34 @@ class SubscriptionHandler extends Handler
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
options = { plan: 'basic', trial_end: subscription.current_period_end }
|
options = { plan: 'basic', trial_end: subscription.current_period_end }
|
||||||
|
options.coupon = couponID if couponID
|
||||||
stripe.customers.update req.user.get('stripe').customerID, options, (err, customer) =>
|
stripe.customers.update req.user.get('stripe').customerID, options, (err, customer) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
|
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
@updateUser(req, user, customer.subscriptions.data[0], false, done)
|
@updateUser(req, user, customer, false, done)
|
||||||
|
|
||||||
else
|
else
|
||||||
# can skip creating the subscription
|
# can skip creating the subscription
|
||||||
return @updateUser(req, user, customer.subscriptions.data[0], false, done)
|
return @updateUser(req, user, customer, false, done)
|
||||||
|
|
||||||
else
|
else
|
||||||
stripe.customers.update req.user.get('stripe').customerID, { plan: 'basic' }, (err, customer) =>
|
options = { plan: 'basic' }
|
||||||
|
options.coupon = couponID if couponID
|
||||||
|
stripe.customers.update req.user.get('stripe').customerID, options, (err, customer) =>
|
||||||
if err
|
if err
|
||||||
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
|
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
|
||||||
return done({res: 'Database error.', code: 500})
|
return done({res: 'Database error.', code: 500})
|
||||||
|
|
||||||
@updateUser(req, user, customer.subscriptions.data[0], true, done)
|
@updateUser(req, user, customer, true, done)
|
||||||
|
|
||||||
updateUser: (req, user, subscription, increment, done) ->
|
updateUser: (req, user, customer, increment, done) ->
|
||||||
|
subscription = customer.subscriptions.data[0]
|
||||||
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
|
stripeInfo = _.cloneDeep(user.get('stripe') ? {})
|
||||||
stripeInfo.planID = 'basic'
|
stripeInfo.planID = 'basic'
|
||||||
stripeInfo.subscriptionID = subscription.id
|
stripeInfo.subscriptionID = subscription.id
|
||||||
stripeInfo.customerID = subscription.customer
|
stripeInfo.customerID = customer.id
|
||||||
req.body.stripe = stripeInfo # to make sure things work for admins, who are mad with power
|
req.body.stripe = stripeInfo # to make sure things work for admins, who are mad with power
|
||||||
user.set('stripe', stripeInfo)
|
user.set('stripe', stripeInfo)
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ moment = require 'moment'
|
||||||
LevelSession = require '../levels/sessions/LevelSession'
|
LevelSession = require '../levels/sessions/LevelSession'
|
||||||
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
LevelSessionHandler = require '../levels/sessions/level_session_handler'
|
||||||
SubscriptionHandler = require '../payments/subscription_handler'
|
SubscriptionHandler = require '../payments/subscription_handler'
|
||||||
|
DiscountHandler = require '../payments/discount_handler'
|
||||||
EarnedAchievement = require '../achievements/EarnedAchievement'
|
EarnedAchievement = require '../achievements/EarnedAchievement'
|
||||||
UserRemark = require './remarks/UserRemark'
|
UserRemark = require './remarks/UserRemark'
|
||||||
{isID} = require '../lib/utils'
|
{isID} = require '../lib/utils'
|
||||||
|
@ -123,6 +124,25 @@ UserHandler = class UserHandler extends Handler
|
||||||
return callback(err) if err
|
return callback(err) if err
|
||||||
return callback(null, req, user)
|
return callback(null, req, user)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Discount setting
|
||||||
|
(req, user, callback) ->
|
||||||
|
return callback(null, req, user) unless req.user?.isAdmin()
|
||||||
|
hasCoupon = user.get('stripe')?.couponID
|
||||||
|
wantsCoupon = req.body.stripe?.couponID
|
||||||
|
|
||||||
|
return callback(null, req, user) if hasCoupon is wantsCoupon
|
||||||
|
if wantsCoupon and (hasCoupon isnt wantsCoupon)
|
||||||
|
DiscountHandler.discountUser(req, user, (err) ->
|
||||||
|
return callback(err) if err
|
||||||
|
return callback(null, req, user)
|
||||||
|
)
|
||||||
|
else if hasCoupon and not wantsCoupon
|
||||||
|
DiscountHandler.removeDiscountFromCustomer(req, user, (err) ->
|
||||||
|
return callback(err) if err
|
||||||
|
return callback(null, req, user)
|
||||||
|
)
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
getById: (req, res, id) ->
|
getById: (req, res, id) ->
|
||||||
|
|
|
@ -56,7 +56,6 @@ setupExpressMiddleware = (app) ->
|
||||||
express.logger.format('prod', productionLogging)
|
express.logger.format('prod', productionLogging)
|
||||||
app.use(express.logger('prod'))
|
app.use(express.logger('prod'))
|
||||||
app.use express.compress filter: (req, res) ->
|
app.use express.compress filter: (req, res) ->
|
||||||
return false if req.headers.host is 'codecombat.com' # CloudFlare will gzip it for us on codecombat.com # But now it's disabled.
|
|
||||||
compressible res.getHeader('Content-Type')
|
compressible res.getHeader('Content-Type')
|
||||||
else
|
else
|
||||||
express.logger.format('dev', developmentLogging)
|
express.logger.format('dev', developmentLogging)
|
||||||
|
|
114
test/server/functional/discount_handler.spec.coffee
Normal file
114
test/server/functional/discount_handler.spec.coffee
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
|
||||||
|
config = require '../../../server_config'
|
||||||
|
require '../common'
|
||||||
|
|
||||||
|
# sample data that comes in through the webhook when you subscribe
|
||||||
|
|
||||||
|
|
||||||
|
describe '/db/user, editing stripe.couponID property', ->
|
||||||
|
|
||||||
|
stripe = require('stripe')(config.stripe.secretKey)
|
||||||
|
userURL = getURL('/db/user')
|
||||||
|
webhookURL = getURL('/stripe/webhook')
|
||||||
|
|
||||||
|
it 'clears the db first', (done) ->
|
||||||
|
clearModels [User, Payment], (err) ->
|
||||||
|
throw err if err
|
||||||
|
done()
|
||||||
|
|
||||||
|
#- shared data between tests
|
||||||
|
joeData = null
|
||||||
|
firstSubscriptionID = null
|
||||||
|
|
||||||
|
it 'does not work for non-admins', (done) ->
|
||||||
|
loginJoe (joe) ->
|
||||||
|
joeData = joe.toObject()
|
||||||
|
expect(joeData.stripe).toBeUndefined()
|
||||||
|
joeData.stripe = { couponID: '20pct' }
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(200) # fails silently
|
||||||
|
expect(res.body.stripe).toBeUndefined() # but still fails
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'does not work with invalid coupons', (done) ->
|
||||||
|
loginAdmin (admin) ->
|
||||||
|
joeData.stripe = { couponID: 'DNE' }
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(404)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'sets the couponID on a user without an existing stripe object', (done) ->
|
||||||
|
joeData.stripe = { couponID: '20pct' }
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
joeData = body
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe.couponID).toBe('20pct')
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'just updates the couponID when it changes and there is no existing subscription', (done) ->
|
||||||
|
joeData.stripe.couponID = '500off'
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe.couponID).toBe('500off')
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'removes the couponID from the user when the admin makes it so', (done) ->
|
||||||
|
delete joeData.stripe.couponID
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
joeData = body
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe).toBeUndefined()
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'puts the coupon back', (done) ->
|
||||||
|
joeData.stripe = {couponID: '500off'}
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe.couponID).toBe('500off')
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'applies a discount to the newly created customer when a plan is set', (done) ->
|
||||||
|
stripe.tokens.create {
|
||||||
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||||
|
}, (err, token) ->
|
||||||
|
stripeTokenID = token.id
|
||||||
|
loginJoe (joe) ->
|
||||||
|
joeData.stripe.token = stripeTokenID
|
||||||
|
joeData.stripe.planID = 'basic'
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
joeData = body
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
||||||
|
expect(customer.discount).toBeDefined()
|
||||||
|
expect(customer.discount.coupon.id).toBe('500off')
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
it 'updates the discount on the customer when an admin changes the couponID', (done) ->
|
||||||
|
loginAdmin (admin) ->
|
||||||
|
joeData.stripe.couponID = '20pct'
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe.couponID).toBe('20pct')
|
||||||
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
||||||
|
expect(customer.discount.coupon.id).toBe('20pct')
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'removes discounts from the customer when an admin removes the couponID', (done) ->
|
||||||
|
delete joeData.stripe.couponID
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe.couponID).toBeUndefined()
|
||||||
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
||||||
|
expect(customer.discount).toBe(null)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'adds a discount to the customer when an admin adds the couponID', (done) ->
|
||||||
|
joeData.stripe.couponID = '20pct'
|
||||||
|
request.put {uri: userURL, json: joeData }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(body.stripe.couponID).toBe('20pct')
|
||||||
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
||||||
|
expect(customer.discount.coupon.id).toBe('20pct')
|
||||||
|
done()
|
||||||
|
|
|
@ -82,6 +82,7 @@ describe '/db/payment', ->
|
||||||
joeID = null
|
joeID = null
|
||||||
timestamp = new Date().getTime()
|
timestamp = new Date().getTime()
|
||||||
stripeTokenID = null
|
stripeTokenID = null
|
||||||
|
joeData = null
|
||||||
|
|
||||||
it 'clears the db first', (done) ->
|
it 'clears the db first', (done) ->
|
||||||
clearModels [User, Payment], (err) ->
|
clearModels [User, Payment], (err) ->
|
||||||
|
@ -131,6 +132,34 @@ describe '/db/payment', ->
|
||||||
done()
|
done()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it 'allows a new charge on the existing customer', (done) ->
|
||||||
|
data = { productID: 'gems_5', stripe: { timestamp: new Date().getTime() } }
|
||||||
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe 201
|
||||||
|
Payment.count {}, (err, count) ->
|
||||||
|
expect(count).toBe(2)
|
||||||
|
User.findById joeID, (err, user) ->
|
||||||
|
joeData = user.toObject()
|
||||||
|
expect(user.get('purchased').gems).toBe(10000)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "updates the customer's card when you submit a new token", (done) ->
|
||||||
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
||||||
|
originalCustomerID = customer.id
|
||||||
|
originalCardID = customer.cards.data[0].id
|
||||||
|
stripe.tokens.create {
|
||||||
|
card: { number: '4242424242424242', exp_month: 12, exp_year: 2020, cvc: '123' }
|
||||||
|
}, (err, token) ->
|
||||||
|
data = { productID: 'gems_5', stripe: { timestamp: new Date().getTime(), token: token.id } }
|
||||||
|
request.post {uri: paymentURL, json: data }, (err, res, body) ->
|
||||||
|
expect(res.statusCode).toBe(201)
|
||||||
|
User.findById joeID, (err, user) ->
|
||||||
|
joeData = user.toObject()
|
||||||
|
expect(joeData.stripe.customerID).toBe(originalCustomerID)
|
||||||
|
stripe.customers.retrieve joeData.stripe.customerID, (err, customer) ->
|
||||||
|
expect(customer.cards.data[0].id).not.toBe(originalCardID)
|
||||||
|
done()
|
||||||
|
|
||||||
it 'clears the db', (done) ->
|
it 'clears the db', (done) ->
|
||||||
clearModels [User, Payment], (err) ->
|
clearModels [User, Payment], (err) ->
|
||||||
|
|
Reference in a new issue