Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-12-04 22:22:39 -08:00
commit 76ee449693
66 changed files with 364 additions and 85 deletions

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Bearbeite den Zauberer"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "Προσαρμογή Μάγου"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
# game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
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:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Personalizar Hechicero"
game_menu:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Personalizar Mago"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Personnaliser le magicien"
game_menu:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Persoalizar Mago"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "魔法使いの設定"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "사용자 정의 마법사"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Personalize o feiticeiro"
game_menu:

View file

@ -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_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_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Personalizar Feiticeiro"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -296,6 +296,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
tip_hofstadters_law: "Закон Хофштадтера: Любое дело всегда длится дольше, чем ожидается, даже если учесть закон Хофштадтера."
tip_premature_optimization: "Поспешная оптимизация - корень всех зол. - Donald Knuth"
tip_brute_force: "Когда сомневаешься используй грубую силу. - Кен Томпсон"
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "Настройки волшебника"
game_menu:
@ -376,13 +377,13 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
blocks: "Блокирует" # As in "this shield blocks this much damage"
skills: "Умения"
available_for_purchase: "Доступно для покупки"
level_to_unlock: "Разблокируется на уровне:" # ToDo: check in interface
level_to_unlock: "Разблокируется на уровне:"
restricted_to_certain_heroes: "Только определенные герои могут играть этот уровень."
skill_docs:
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
# read_only: "read-only"
action_name: "имя" # ToDo: check in interface
action_name: "имя"
action_cooldown: "Применяется"
action_specific_cooldown: "Восстановление"
action_damage: "Повреждения"

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "Прилагоди Чаробњака"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "Налаштування персонажа"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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"
game_menu:

View file

@ -296,6 +296,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
tip_hofstadters_law: "侯世达定律:做事所花费的时间总是比你预期的要长,即使你的预期中考虑了侯世达定律。"
tip_premature_optimization: "过早的优化是万恶之源。 - 高德纳"
tip_brute_force: "拿不准时就用穷举法。 - Ken Thompson"
# tip_extrapolation: "There are only two kinds of people: those that can extrapolate from incomplete data..."
customize_wizard: "自定义向导"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "自定義巫師"
game_menu:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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:

View file

@ -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_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# 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: "自設定獻路人"
game_menu:

View file

@ -278,6 +278,9 @@ _.extend UserSchema.properties,
planID: { enum: ['basic'] }
subscriptionID: { type: 'string' }
token: { type: 'string' }
couponID: { type: 'string' }
discountID: { type: 'string' }
free: { type: ['boolean', 'string'], format: 'date-time' }
}
c.extendBasicProperties UserSchema, 'user'

View file

@ -107,6 +107,9 @@
.executed
background-color: rgba(110, 110, 110, 0.12)
.locked-code
border: 1px dashed rgba(53, 45, 34, 0.5)
+keyframes(pulseRedBackground)
from
background-color: rgba(255, 45, 27, 0.4)

View file

@ -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_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_extrapolation') There are only two kinds of people: those that can extrapolate from incomplete data...
strong.tip.rare
span(data-i18n='play_level.tip_harry') Yer a Wizard,
span= me.get('name', true)

View file

@ -30,6 +30,7 @@ module.exports = class PaymentsView extends RootView
onClickStartSubscription: (e) ->
@openModalView new SubscribeModal()
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'payments view'
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
onSubscribed: ->
document.location.reload()

View file

@ -124,6 +124,7 @@ module.exports = class WorldMapView extends RootView
@openModalView? new modal() unless window.currentModal
if modal is SubscribeModal
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'world map loadded'
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
, 2000
onSubscribed: ->
@ -214,6 +215,7 @@ module.exports = class WorldMapView extends RootView
@openModalView new modal()
if modal is SubscribeModal
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')
Backbone.Mediator.publish 'router:navigate', route: '/contribute/adventurer'
return
@ -222,11 +224,13 @@ module.exports = class WorldMapView extends RootView
else
@startLevel levelElement
window.tracker?.trackEvent 'Clicked Level', category: 'World Map', levelID: levelID, ['Google Analytics']
window.tracker?.trackPageView "world-map/clicked-level/#{levelID}", ['Google Analytics']
onClickStartLevel: (e) ->
levelElement = $(e.target).parents('.level-info-container')
@startLevel levelElement
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) ->
@setupManager?.destroy()

View file

@ -114,6 +114,7 @@ module.exports = class LevelLoadingView extends CocoView
@openModalView new modal()
if modal is SubscribeModal
window.tracker?.trackEvent 'Show subscription modal', category: 'Subscription', label: 'level loading'
window.tracker?.trackPageView "subscription/show-modal", ['Google Analytics']
onSubscribed: ->
document.location.reload()

View file

@ -273,6 +273,15 @@ module.exports = class SpellView extends CocoView
return true for range in @readOnlyRanges when leftRange.intersects(range)
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) ->
return true if intersects()
next?()
@ -284,27 +293,16 @@ module.exports = class SpellView extends CocoView
wrapper => orig.apply obj, args
obj[method]
finishRange = (row, startRow, startColumn) =>
range = new Range startRow, startColumn, row, @aceSession.getLine(row).length - 1
range.start = @aceDoc.createAnchor range.start
range.end = @aceDoc.createAnchor range.end
range.end.$insertRight = true
@readOnlyRanges.push range
if @lockedCodeMarkerID?
@aceSession.removeMarker @lockedCodeMarkerID
@lockedCodeMarkerID = null
# Create a read-only range for each chunk of text not separated by an empty line
@readOnlyRanges = []
startRow = startColumn = null
for row in [0...@aceSession.getLength()]
unless /^\s*$/.test @aceSession.getLine(row)
unless startRow? and startColumn?
startRow = row
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
lines = @aceDoc.getAllLines()
lastRow = row for line, row in lines when not /^\s*$/.test(line)
if lastRow?
@readOnlyRanges.push new Range 0, 0, lastRow, lines[lastRow].length - 1
@lockedCodeMarkerID = @aceSession.addMarker @readOnlyRanges[0], 'locked-code', 'fullLine'
# Override write operations that intersect with default code
interceptCommand @ace, 'onPaste', preventReadonly
@ -319,6 +317,9 @@ module.exports = class SpellView extends CocoView
if e.command.name in ['Backspace', 'throttle-backspaces'] and intersectsLeft()
@zatanna?.off?()
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 intersects()
e.editor.navigateDown 1

View file

@ -45,10 +45,12 @@ module.exports = class SubscribeModal extends ModalView
container: @$el
).on 'shown.bs.popover', =>
application.tracker?.trackEvent 'Subscription parent hover', {}
application.tracker?.trackPageView "subscription/parent-hover", ['Google Analytics']
onClickPurchaseButton: (e) ->
@playSound 'menu-button-click'
application.tracker?.trackEvent 'Started subscription purchase', {}
application.tracker?.trackPageView "subscription/start-purchase", ['Google Analytics']
stripeHandler.open({
description: $.i18n.t('subscribe.stripe_description')
amount: @product.amount
@ -69,6 +71,7 @@ module.exports = class SubscribeModal extends ModalView
onSubscriptionSuccess: ->
application.tracker?.trackEvent 'Finished subscription purchase', {}
application.tracker?.trackPageView "subscription/finish-purchase", ['Google Analytics']
Backbone.Mediator.publish 'subscribe-modal:subscribed', {}
@playSound 'victory'
@hide()

View 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()

View file

@ -166,29 +166,34 @@ PaymentHandler = class PaymentHandler extends Handler
handleStripePaymentPost: (req, res, timestamp, productID, token) ->
# First, make sure we save the payment info as a Customer object, if we haven't already.
if not req.user.get('stripe')?.customerID
stripe.customers.create({
card: token
email: req.user.get('email')
metadata: {
id: req.user._id + ''
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)
if token
customerID = req.user.get('stripe')?.customerID
if customerID
# old customer, new token. Save it.
stripe.customers.update customerID, { card: token }, (err, customer) =>
@beginStripePayment(req, res, timestamp, productID)
)
),
(err) =>
@logPaymentError(req, 'Stripe customer creation error. '+err)
return @sendDatabaseError(res, err)
)
else
newCustomer = {
card: token
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
@beginStripePayment(req, res, timestamp, productID)

View file

@ -4,6 +4,7 @@
Handler = require '../commons/Handler'
config = require '../../server_config'
stripe = require('stripe')(config.stripe.secretKey)
discountHandler = require './discount_handler'
subscriptions = {
basic: {
@ -19,61 +20,52 @@ class SubscriptionHandler extends Handler
if (not req.user) or req.user.isAnonymous()
return done({res: 'You must be signed in to subscribe.', code: 403})
stripeToken = req.body.stripe?.token
extantCustomerID = user.get('stripe')?.customerID
if not (stripeToken or extantCustomerID)
token = req.body.stripe?.token
customerID = user.get('stripe')?.customerID
if not (token or customerID)
@logSubscriptionError(req, 'Missing stripe token or customer ID.')
return done({res: 'Missing stripe token or customer ID.', code: 422})
if stripeToken
stripe.customers.create({
card: stripeToken
email: req.user.get('email')
metadata: {
id: req.user._id + ''
slug: req.user.get('slug')
if token
if customerID
stripe.customers.update customerID, { card: token }, (err, customer) =>
@checkForExistingSubscription(req, user, customer, done)
else
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.customerID = customer.id
req.user.set('stripe', stripeInfo)
req.user.save((err) =>
req.user.save (err) =>
if err
@logSubscriptionError(req, 'Stripe customer id save db error. '+err)
return done({res: 'Database error.', code: 500})
@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
stripe.customers.retrieve(extantCustomerID, (err, customer) =>
stripe.customers.retrieve(customerID, (err, customer) =>
if err
@logSubscriptionError(req, 'Stripe customer creation error. '+err)
return done({res: 'Database error.', code: 500})
else if not customer
# 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.cancel_at_period_end
@ -87,30 +79,34 @@ class SubscriptionHandler extends Handler
return done({res: 'Database error.', code: 500})
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) =>
if err
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
return done({res: 'Database error.', code: 500})
@updateUser(req, user, customer.subscriptions.data[0], false, done)
@updateUser(req, user, customer, false, done)
else
# can skip creating the subscription
return @updateUser(req, user, customer.subscriptions.data[0], false, done)
return @updateUser(req, user, customer, false, done)
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
@logSubscriptionError(req, 'Stripe customer plan setting error. '+err)
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.planID = 'basic'
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
user.set('stripe', stripeInfo)

View file

@ -12,6 +12,7 @@ moment = require 'moment'
LevelSession = require '../levels/sessions/LevelSession'
LevelSessionHandler = require '../levels/sessions/level_session_handler'
SubscriptionHandler = require '../payments/subscription_handler'
DiscountHandler = require '../payments/discount_handler'
EarnedAchievement = require '../achievements/EarnedAchievement'
UserRemark = require './remarks/UserRemark'
{isID} = require '../lib/utils'
@ -123,6 +124,25 @@ UserHandler = class UserHandler extends Handler
return callback(err) if err
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) ->

View file

@ -56,7 +56,6 @@ setupExpressMiddleware = (app) ->
express.logger.format('prod', productionLogging)
app.use(express.logger('prod'))
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')
else
express.logger.format('dev', developmentLogging)

View 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()

View file

@ -82,6 +82,7 @@ describe '/db/payment', ->
joeID = null
timestamp = new Date().getTime()
stripeTokenID = null
joeData = null
it 'clears the db first', (done) ->
clearModels [User, Payment], (err) ->
@ -131,6 +132,34 @@ describe '/db/payment', ->
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) ->
clearModels [User, Payment], (err) ->