From d361ab5809a81bb2280a44b28844e6b592eb76a9 Mon Sep 17 00:00:00 2001 From: barbuddha Date: Wed, 11 Jun 2014 09:56:15 +0400 Subject: [PATCH 01/25] Update ru.coffee --- app/locale/ru.coffee | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index a50b1df39..31132122e 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -161,7 +161,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi autosave: "Настройки сохраняются автоматически" me_tab: "Я" picture_tab: "Аватар" -# upload_picture: "Upload a picture" + upload_picture: "Загрузить изображение" wizard_tab: "Волшебник" password_tab: "Пароль" emails_tab: "Email-адреса" @@ -175,9 +175,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi email_notifications: "Уведомления" # email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." # email_any_notes: "Any Notifications" -# email_any_notes_description: "Disable to stop all activity notification emails." -# email_recruit_notes: "Job Opportunities" -# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." + email_any_notes_description: "Отключите, чтобы больше не получать извещения." + email_recruit_notes: "Возможности для работы" + email_recruit_notes_description: "Если вы действительно хорошо играете, то мы можем связаться с вами для предложения (лучшей) работы." contributor_emails: "Рассылки по классам участников" contribute_prefix: "Нам нужны люди, которые присоединятся к нашей команде! Зайдите на " contribute_page: "страницу участников," @@ -194,7 +194,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi account_profile: edit_settings: "Изменить настройки" -# done_editing_settings: "Done Editing" + done_editing_settings: "Завершить редактирование" profile_for_prefix: "Профиль для " profile_for_suffix: "" approved: "Одобрено" @@ -205,9 +205,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi # active: "Looking for interview offers now" # inactive: "Not looking for offers right now" # complete: "complete" -# next: "Next" -# next_city: "city?" -# next_country: "pick your country." + next: "Далее" + next_city: "Город?" + next_country: "Выберите вашу страну." # next_name: "name?" # next_short_description: "summarize yourself at a glance." # next_long_description: "describe the work you're looking for." @@ -320,7 +320,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi multiplayer: "Мультиплеер" restart: "Перезапустить" goals: "Цели" -# success: "Success!" + success: "Успешно!" # incomplete: "Incomplete" # timed_out: "Ran out of time" # failing: "Failing" @@ -345,7 +345,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi multiplayer_hint_label: "Подсказка: " multiplayer_hint: "кликните на ссылку, чтобы выделить её, затем нажмите ⌘-С или Ctrl-C, чтобы скопировать." multiplayer_coming_soon: "Больше возможностей мультиплеера на подходе!" -# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." + multiplayer_sign_in_leaderboard: "Войдите или создайте аккаунт, чтобы ваше решение оказалось в таблице лидеров." guide_title: "Руководство" tome_minion_spells: "Заклинания ваших миньонов" tome_read_only_spells: "Заклинания только для чтения" @@ -362,9 +362,9 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi skip_tutorial: "Пропуск (Esc)" editor_config: "Настройки редактора" editor_config_title: "Настройки редактора" -# editor_config_level_language_label: "Language for This Level" + editor_config_level_language_label: "Язык для этого уровня" # editor_config_level_language_description: "Define the programming language for this particular level." -# editor_config_default_language_label: "Default Programming Language" + editor_config_default_language_label: "Язык по умолчанию" # editor_config_default_language_description: "Define the programming language you want to code in when starting new levels." editor_config_keybindings_label: "Сочетания клавиш" editor_config_keybindings_default: "По умолчанию (Ace)" @@ -375,7 +375,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi editor_config_indentguides_description: "Отображение вертикальных линий для лучшего обзора отступов." editor_config_behaviors_label: "Умное поведение" editor_config_behaviors_description: "Автозавершать квадратные, фигурные скобки и кавычки." -# keyboard_shortcuts: "Key Shortcuts" + keyboard_shortcuts: "Горячие клавиши" loading_ready: "Готово!" tip_insert_positions: "Shift+Клик по карте вставит координаты в редактор заклинаний." tip_toggle_play: "Переключайте воспроизведение/паузу комбинацией Ctrl+P." @@ -413,11 +413,11 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi infinite_loop_comment_out: "Закомментировать мой код" # keyboard_shortcuts: -# keyboard_shortcuts: "Keyboard Shortcuts" -# space: "Space" -# enter: "Enter" -# escape: "Escape" -# cast_spell: "Cast current spell." + keyboard_shortcuts: "Горячие клавиши" + space: "Пробел" + enter: "Enter" + escape: "Escape" + cast_spell: ".произнести текущее заклинание." # continue_script: "Continue past current script." # skip_scripts: "Skip past all skippable scripts." # toggle_playback: "Toggle play/pause." @@ -457,7 +457,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi thang_description: "Создавайте юнитов, определяйте их логику по умолчанию, графику и звук. В настоящий момент поддерживается импорт только векторной графики Flash." level_title: "Редактор уровней" level_description: "Включает в себя инструменты для написания сценариев, загрузки аудио и построения собственной логики для создания всевозможных уровней. Всё, что мы используем сами!" -# achievement_title: "Achievement Editor" + achievement_title: "Редактор достижений" got_questions: "Вопросы по использованию редакторов CodeCombat?" contact_us: "свяжитесь с нами!" hipchat_prefix: "Также вы можете найти нас в нашей" @@ -508,7 +508,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi article_search_title: "Искать статьи" thang_search_title: "Искать типы объектов" level_search_title: "Искать уровни" -# achievement_search_title: "Search Achievements" + achievement_search_title: "Искать достижения" read_only_warning2: "Примечание: вы не можете сохранять любые правки здесь, потому что вы не авторизованы." article: @@ -639,7 +639,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi introduction_desc_ending: "Мы надеемся, что вы присоединитесь к нашей команде!" introduction_desc_signature: "- Ник, Джордж, Скотт, Михаэль, Джереми и Глен" alert_account_message_intro: "Привет!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." + alert_account_message: "Чтобы подписаться за классовые сообщения, необходимо войти в аккаунт" archmage_summary: "Интересует работа над игровой графикой, дизайном пользовательского интерфейса, базой данных и организацией сервера, сетевым мультиплеером, физикой, звуком или производительностью игрового движка? Хотите помочь создать игру для помощи другим людям в изучении того, в чём вы хорошо разбираетесь? У нас много работы, и если вы опытный программист и хотите разрабатывать для CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов." archmage_introduction: "Одна из лучших черт в создании игр - то, что они синтезируют так много различных вещей. Графика, звук, сетевое взаимодействие в режиме реального времени, социальное сетевое взаимодействие, и, конечно, большинство из более распространённых аспектов программирования, от низкоуровневого управления базами данных и администрирования сервера до построения дизайна и интерфейсов, видимых пользователю. У нас много работы, и если вы опытный программист со страстным желанием погрузиться в действительно мельчайшие детали CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов." class_attributes: "Атрибуты класса" From 140541925e7694911083576680b38fcc8005eaa6 Mon Sep 17 00:00:00 2001 From: Michael Arnold Date: Sat, 14 Jun 2014 15:43:59 -0400 Subject: [PATCH 02/25] Changed final_score to match coding style. Extremely insignificant change, I just noticed that final_score was set as: final_score = final_score / fuzzies; compared to: "final_score /= fuzzies;" which is how the rest of your syntax is written. (Ex: "+=" and "-=") --- vendor/scripts/string_score.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/scripts/string_score.js b/vendor/scripts/string_score.js index d282858bf..497b4b619 100644 --- a/vendor/scripts/string_score.js +++ b/vendor/scripts/string_score.js @@ -124,7 +124,7 @@ //final_score = (word_score + abbreviation_score) / 2; final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2; - final_score = final_score / fuzzies; + final_score /= fuzzies; if (start_of_string_bonus && (final_score + 0.15 < 1)) { final_score += 0.15; From 479c2a0cc14e0e4cb62ff07367c17efcd9b9b74a Mon Sep 17 00:00:00 2001 From: Fergus Leen Date: Sun, 15 Jun 2014 08:32:06 +0100 Subject: [PATCH 03/25] Added str around sys.maxsize --- scripts/devSetup/systemConfiguration.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/devSetup/systemConfiguration.py b/scripts/devSetup/systemConfiguration.py index 9d49e95ce..c8b2b99de 100644 --- a/scripts/devSetup/systemConfiguration.py +++ b/scripts/devSetup/systemConfiguration.py @@ -33,7 +33,6 @@ class SystemConfiguration(object): return 64 else: if self.operating_system == u"mac": - raise NotSupportedError(u"Your processor is determined to have a maxSize of" + sys.maxsize + + raise NotSupportedError(u"Your processor is determined to have a maxSize of" + str(sys.maxsize) + u",\n which doesn't correspond with a 64-bit architecture.") return 32 - From ff5f46e5fbe95a0af591018a88dfa8382d9e49f5 Mon Sep 17 00:00:00 2001 From: barbuddha Date: Mon, 16 Jun 2014 08:43:27 +0400 Subject: [PATCH 04/25] Update ru.coffee --- app/locale/ru.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index 31132122e..d46cd12a1 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -417,7 +417,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi space: "Пробел" enter: "Enter" escape: "Escape" - cast_spell: ".произнести текущее заклинание." + cast_spell: "Произнести текущее заклинание." # continue_script: "Continue past current script." # skip_scripts: "Skip past all skippable scripts." # toggle_playback: "Toggle play/pause." @@ -639,7 +639,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi introduction_desc_ending: "Мы надеемся, что вы присоединитесь к нашей команде!" introduction_desc_signature: "- Ник, Джордж, Скотт, Михаэль, Джереми и Глен" alert_account_message_intro: "Привет!" - alert_account_message: "Чтобы подписаться за классовые сообщения, необходимо войти в аккаунт" + alert_account_message: "Чтобы подписаться на классовые сообщения, необходимо войти в аккаунт" archmage_summary: "Интересует работа над игровой графикой, дизайном пользовательского интерфейса, базой данных и организацией сервера, сетевым мультиплеером, физикой, звуком или производительностью игрового движка? Хотите помочь создать игру для помощи другим людям в изучении того, в чём вы хорошо разбираетесь? У нас много работы, и если вы опытный программист и хотите разрабатывать для CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов." archmage_introduction: "Одна из лучших черт в создании игр - то, что они синтезируют так много различных вещей. Графика, звук, сетевое взаимодействие в режиме реального времени, социальное сетевое взаимодействие, и, конечно, большинство из более распространённых аспектов программирования, от низкоуровневого управления базами данных и администрирования сервера до построения дизайна и интерфейсов, видимых пользователю. У нас много работы, и если вы опытный программист со страстным желанием погрузиться в действительно мельчайшие детали CodeCombat, этот класс для вас. Мы будем рады вашей помощи в создании самой лучшей игры для программистов." class_attributes: "Атрибуты класса" From 931f3ea33035dee515af977f6f369bf9dfa0c408 Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 16 Jun 2014 11:38:25 +0200 Subject: [PATCH 05/25] Upsate /// Translate Enjoy --- app/locale/fr.coffee | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/locale/fr.coffee b/app/locale/fr.coffee index dae1e4266..dce3be2d7 100644 --- a/app/locale/fr.coffee +++ b/app/locale/fr.coffee @@ -193,13 +193,13 @@ module.exports = nativeDescription: "français", englishDescription: "French", t view_profile: "Voir votre profil" account_profile: -# settings: "Settings" -# edit_profile: "Edit Profile" -# done_editing: "Done Editing" + settings: "Paramètres" + edit_profile: "Editer Profil" + done_editing: "Modifications effectué" profile_for_prefix: "Profil pour " profile_for_suffix: "" -# featured: "Featured" -# not_featured: "Not Featured" + featured: "Complet" + not_featured: "Incomplet" looking_for: "à la recherche de:" last_updated: "Dernière Mise à jour:" contact: "Contact" @@ -220,7 +220,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t next_photo: "ajouter une photo professionelle (optionnel)." next_active: "déclarez vous ouvert aux offres pour apparaitre dans les recherches." example_blog: "Votre blog" -# example_personal_site: "Personal Site" + example_personal_site: "Site Web" links_header: "Liens personnels" links_blurb: "Lien vers d'autres sites ou profils que vous souhaitez mettre en avant, comme votre GitHub, LinkedIn ou votre blog." links_name: "Nom du lien" @@ -448,7 +448,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t av_entities_sub_title: "Entités" av_entities_users_url: "Utilisateurs" av_entities_active_instances_url: "Instances actives" -# av_entities_employer_list_url: "Employer List" + av_entities_employer_list_url: "Liste des employés" av_other_sub_title: "Autre" av_other_debug_base_url: "Base (pour debugger base.jade)" u_title: "Liste des utilisateurs" @@ -653,7 +653,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t introduction_desc_ending: "Nous espérons que vous allez joindre notre aventure!" introduction_desc_signature: "- Nick, George, Scott, Michael, Jeremy et Glen" alert_account_message_intro: "Et tiens!" -# alert_account_message: "To subscribe for class emails, you'll need to be logged in first." + alert_account_message: "Pour souscrire aux e-mails, vous devez être connecté" # archmage_summary: "Interested in working on game graphics, user interface design, database and server organization, multiplayer networking, physics, sound, or game engine performance? Want to help build a game to help other people learn what you are good at? We have a lot to do and if you are an experienced programmer and want to develop for CodeCombat, this class is for you. We would love your help building the best programming game ever." archmage_introduction: "L'une des meilleures parties de la création d'un jeu est qu'il regroupe tant de choses différentes. Graphismes, sons, réseau en temps réel, réseaux sociaux, et bien sur bien d'autres aspects de la programmation, de la gestion bas niveau de base de données, et de l'administration de serveur à l'élaboration d'interfaces utilisateur. Il y a tant à faire, et si vous êtes un programmeur expérimenté avec une aspiration à vraiment plonger dans le fond de CodeCombat, cette classe est faite pour vous. Nous aimerions avoir votre aide pour le meilleur jeu de développement de tous les temps." class_attributes: "Attributs de classe" @@ -860,7 +860,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t # facebook_friend_sessions: "Facebook Friend Sessions" # gplus_friends: "G+ Friends" # gplus_friend_sessions: "G+ Friend Sessions" -# leaderboard: "Leaderboard" + leaderboard: "Classement" # user_schema: "User Schema" # user_profile: "User Profile" patches: "Patchs" From c611cafa259757c2a5106f103cd6372ae7c9a3c2 Mon Sep 17 00:00:00 2001 From: Inez KEMENES Date: Mon, 16 Jun 2014 16:39:02 +0200 Subject: [PATCH 06/25] Update hu.coffee --- app/locale/hu.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/locale/hu.coffee b/app/locale/hu.coffee index ceb464809..debe8466e 100644 --- a/app/locale/hu.coffee +++ b/app/locale/hu.coffee @@ -26,14 +26,14 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t minutes: "percek" hour: "óra" hours: "órák" -# day: "day" -# days: "days" -# week: "week" -# weeks: "weeks" -# month: "month" -# months: "months" -# year: "year" -# years: "years" + day: "nap" + days: "napok" + week: "hét" + weeks: "hetek" + month: "hónap" + months: "hónapok" + year: "év" + years: "évek" modal: close: "Mégse" From 9a64ca5973297761ce6aafb009bf82d05b676ef5 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Mon, 16 Jun 2014 10:49:20 -0700 Subject: [PATCH 07/25] A few misc fixes. --- app/locale/ru.coffee | 2 +- app/schemas/definitions/bus.coffee | 4 ++-- app/schemas/subscriptions/bus.coffee | 2 +- app/views/play/level/tome/spell.coffee | 6 +++++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/locale/ru.coffee b/app/locale/ru.coffee index d4a5accb7..b6abadad7 100644 --- a/app/locale/ru.coffee +++ b/app/locale/ru.coffee @@ -425,7 +425,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi infinite_loop_reset_level: "Сбросить уровень" infinite_loop_comment_out: "Закомментировать мой код" -# keyboard_shortcuts: + keyboard_shortcuts: keyboard_shortcuts: "Горячие клавиши" space: "Пробел" enter: "Enter" diff --git a/app/schemas/definitions/bus.coffee b/app/schemas/definitions/bus.coffee index b5625025e..bdec6248f 100644 --- a/app/schemas/definitions/bus.coffee +++ b/app/schemas/definitions/bus.coffee @@ -7,8 +7,8 @@ module.exports = type: "object" properties: # TODO joined: - type: "boolean" + type: ["boolean", "null"] players: type: "object" required: ["joined", "players"] - additionalProperties: false \ No newline at end of file + additionalProperties: true diff --git a/app/schemas/subscriptions/bus.coffee b/app/schemas/subscriptions/bus.coffee index 91569ae7f..3e4702cfd 100644 --- a/app/schemas/subscriptions/bus.coffee +++ b/app/schemas/subscriptions/bus.coffee @@ -33,7 +33,7 @@ module.exports = type: "object" properties: message: - type: "string" + type: "object" bus: $ref: "bus" diff --git a/app/views/play/level/tome/spell.coffee b/app/views/play/level/tome/spell.coffee index a3f0de4ce..7c796482d 100644 --- a/app/views/play/level/tome/spell.coffee +++ b/app/views/play/level/tome/spell.coffee @@ -25,7 +25,11 @@ module.exports = class Spell teamSpells = @session.get('teamSpells') team = @session.get('team') ? 'humans' @useTranspiledCode = @permissions.readwrite.length and ((teamSpells and not _.contains(teamSpells[team], @spellKey)) or (@session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions'))) or @spectateView) - #console.log @spellKey, "using transpiled code?", @useTranspiledCode + if @useTranspiledCode + console.log "#{@spellKey} is using transpiled code because permissions.readwrite is #{@permissions.readwrite} + #{if @spectateView then ', we are spectating' else ''} + #{if teamSpells and not _.contains(teamSpells[team], @spellKey) then ', teamSpells[' + team + '] does not have ' + @spellKey + ' (just ' + teamSpells[team] + ')' else ''} + #{if @session.get('creator') isnt me.id then ', and the session was created by ' + @session.get('creator') + ' but I am ' + me.id else ''}" @source = @originalSource = p.source @parameters = p.parameters if @permissions.readwrite.length and sessionSource = @session.getSourceFor(@spellKey) From 1786eab09ddfb7d0c2687805d8f8ee82ce22cf5c Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 14 Jun 2014 07:37:31 -0700 Subject: [PATCH 08/25] Initial work on a demo view. But brunch won't build demo-app.js for some reason. --- app/lib/Router.coffee | 5 +- app/styles/demo.sass | 14 ++++ app/templates/demo.jade | 22 ++++++ app/views/{test.coffee => TestView.coffee} | 59 ++------------- config.coffee | 3 +- demo/views/editor/PatchesView.demo.coffee | 21 ++++++ test/app/lib/requireUtils.coffee | 48 ++++++++++++ test/app/views/DemoView.coffee | 85 ++++++++++++++++++++++ 8 files changed, 201 insertions(+), 56 deletions(-) create mode 100644 app/styles/demo.sass create mode 100644 app/templates/demo.jade rename app/views/{test.coffee => TestView.coffee} (60%) create mode 100644 demo/views/editor/PatchesView.demo.coffee create mode 100644 test/app/lib/requireUtils.coffee create mode 100644 test/app/views/DemoView.coffee diff --git a/app/lib/Router.coffee b/app/lib/Router.coffee index 775a2dae5..d7ac1a1cc 100644 --- a/app/lib/Router.coffee +++ b/app/lib/Router.coffee @@ -16,8 +16,9 @@ module.exports = class CocoRouter extends Backbone.Router # editor views tend to have the same general structure 'editor/:model(/:slug_or_id)(/:subview)': 'editorModelView' - # Experimenting with direct links - 'test/*subpath': go('test') + # Direct links + 'test/*subpath': go('TestView') + 'demo/*subpath': go('DemoView') 'play/ladder/:levelID': go('play/ladder/ladder_view') 'play/ladder': go('play/ladder_home') diff --git a/app/styles/demo.sass b/app/styles/demo.sass new file mode 100644 index 000000000..dec0fcba3 --- /dev/null +++ b/app/styles/demo.sass @@ -0,0 +1,14 @@ +#demo-view + margin: 0 20px + + h2 + background: #add8e6 + font-family: Arial, Geneva, sans-serif + padding: 20px + font-weight: bold + + #demo-wrapper + width: 78% + + #demo-nav + width: 20% diff --git a/app/templates/demo.jade b/app/templates/demo.jade new file mode 100644 index 000000000..4c093d0fd --- /dev/null +++ b/app/templates/demo.jade @@ -0,0 +1,22 @@ +h2 Demo Page + +ol.breadcrumb + for path in parentFolders + li + a(href=path.url)= path.name + li.active= currentFolder + +.well.pull-left#demo-wrapper + #demo-area + +.nav.nav-pills.nav-stacked.pull-right.well#demo-nav + for child in children + li(class=child.type) + a(href=child.url).small + if child.type == 'folder' + span.glyphicon.glyphicon-folder-close + else + span.glyphicon.glyphicon-file + span.spl= child.name + if child.type == 'folder' + strong (#{child.size}) diff --git a/app/views/test.coffee b/app/views/TestView.coffee similarity index 60% rename from app/views/test.coffee rename to app/views/TestView.coffee index f4667fc44..e999b3b0f 100644 --- a/app/views/test.coffee +++ b/app/views/TestView.coffee @@ -1,7 +1,9 @@ CocoView = require 'views/kinds/CocoView' template = require 'templates/test' +requireUtils = require 'lib/requireUtils' -TEST_BASE_PATH = 'test/app/' +TEST_REQUIRE_PREFIX = 'test/app/' +TEST_URL_PREFIX = '/test/' module.exports = TestView = class TestView extends CocoView id: "test-view" @@ -33,67 +35,18 @@ module.exports = TestView = class TestView extends CocoView getRenderData: -> c = super(arguments...) - c.parentFolders = @getParentFolders() - c.children = @getChildren() + c.parentFolders = requireUtils.getParentFolders(@subPath, TEST_URL_PREFIX) + c.children = requireUtils.parseImmediateChildren(@specFiles, @subPath, TEST_REQUIRE_PREFIX, TEST_URL_PREFIX) parts = @subPath.split('/') c.currentFolder = parts[parts.length-1] or parts[parts.length-2] or 'All' c - - getParentFolders: -> - return [] unless @subPath - paths = [] - parts = @subPath.split('/') - while parts.length - parts.pop() - paths.unshift { - name: parts[parts.length-1] or 'All' - url: '/test/' + parts.join('/') - } - paths - - getChildren: -> - return [] unless @specFiles - folders = {} - files = {} - - requirePrefix = TEST_BASE_PATH + @subPath - if requirePrefix[requirePrefix.length-1] isnt '/' - requirePrefix += '/' - - for f in @specFiles - f = f[requirePrefix.length..] - continue unless f - parts = f.split('/') - name = parts[0] - group = if parts.length is 1 then files else folders - group[name] ?= 0 - group[name] += 1 - - children = [] - urlPrefix = '/test/'+@subPath - urlPrefix += '/' if urlPrefix[urlPrefix.length-1] isnt '/' - - for name in _.keys(folders) - children.push { - type:'folder', - url: urlPrefix+name - name: name+'/' - size: folders[name] - } - for name in _.keys(files) - children.push { - type:'file', - url: urlPrefix+name - name: name - } - children # RUNNING TESTS initSpecFiles: -> @specFiles = TestView.getAllSpecFiles() if @subPath - prefix = TEST_BASE_PATH + @subPath + prefix = TEST_REQUIRE_PREFIX + @subPath @specFiles = (f for f in @specFiles when f.startsWith prefix) @runTests: (specFiles) -> diff --git a/config.coffee b/config.coffee index 56adbd46d..eabcfcfc3 100644 --- a/config.coffee +++ b/config.coffee @@ -35,7 +35,8 @@ exports.config = (bower_components[\/\\]aether[\/\\]build[\/\\]aether.js) )/// 'javascripts/test-app.js': /^test[\/\\]app/ -# 'test/javascripts/test-vendor.js': /^test[\/\\](?=vendor)/ + 'javascripts/demo-app.js': /^demo/ + order: before: [ 'bower_components/jquery/dist/jquery.js' diff --git a/demo/views/editor/PatchesView.demo.coffee b/demo/views/editor/PatchesView.demo.coffee new file mode 100644 index 000000000..9004924ae --- /dev/null +++ b/demo/views/editor/PatchesView.demo.coffee @@ -0,0 +1,21 @@ +PatchesView = require 'views/editor/patches_view' +CocoModel = require 'models/CocoModel' + +class BlandModel extends CocoModel + @className: 'Bland' + @schema: { + type: 'object' + additionalProperties: false + properties: + number: {type: 'number'} + object: {type: 'object'} + string: {type: 'string'} + _id: {type: 'string'} + } + urlRoot: '/db/bland' + + +module.exports = -> + model = new BlandModel({_id:'12345'}) + new PatchesView(model) + diff --git a/test/app/lib/requireUtils.coffee b/test/app/lib/requireUtils.coffee new file mode 100644 index 000000000..a79fe790c --- /dev/null +++ b/test/app/lib/requireUtils.coffee @@ -0,0 +1,48 @@ +module.exports.getParentFolders = (subPath, urlPrefix='/test/') -> + return [] unless subPath + paths = [] + parts = subPath.split('/') + while parts.length + parts.pop() + paths.unshift { + name: parts[parts.length-1] or 'All' + url: urlPrefix + parts.join('/') + } + paths + +module.exports.parseImmediateChildren = (allChildren, subPath, baseRequirePath='test/app/', urlPrefix='/test/') -> + return [] unless allChildren + folders = {} + files = {} + + requirePrefix = baseRequirePath + subPath + if requirePrefix[requirePrefix.length-1] isnt '/' + requirePrefix += '/' + + for f in allChildren + f = f[requirePrefix.length..] + continue unless f + parts = f.split('/') + name = parts[0] + group = if parts.length is 1 then files else folders + group[name] ?= 0 + group[name] += 1 + + children = [] + urlPrefix += subPath + urlPrefix += '/' if urlPrefix[urlPrefix.length-1] isnt '/' + + for name in _.keys(folders) + children.push { + type:'folder', + url: urlPrefix+name + name: name+'/' + size: folders[name] + } + for name in _.keys(files) + children.push { + type:'file', + url: urlPrefix+name + name: name + } + children diff --git a/test/app/views/DemoView.coffee b/test/app/views/DemoView.coffee new file mode 100644 index 000000000..b3470ab9d --- /dev/null +++ b/test/app/views/DemoView.coffee @@ -0,0 +1,85 @@ +CocoView = require 'views/kinds/CocoView' +template = require 'templates/demo' +requireUtils = require 'lib/requireUtils' + +DEMO_REQUIRE_PREFIX = 'demo/app/' +DEMO_URL_PREFIX = '/demo/' + +### + What are demo files? + + They could be a function which returns an element to insert into the demo page. + But what about demoing achievements? They'll get put into the main html. Or modals. + Well, I was thinking that a single folder would show all demos at the same time, line them up. + But it'd be confusing to have a whole bunch of achievement demos show up all at the same time? + Maybe there could be a button to show all the demos. Hmm, that'd be cool. + It could work like Jasmine, where it modifies the path and so when you select to run them, they all run with page reloads. + I think for now, I'll just say: have it be a function which we can run anytime. + It may or may not return an element to be inserted into the main area. + + Another idea. Do we want root views to just take over the full view? + Or should they just go into the central part? + Probably should take over the full view, and if you want to get out of the demo, you navigate back. + +### + +module.exports = DemoView = class DemoView extends CocoView + id: "demo-view" + template: template + + # INITIALIZE + + constructor: (options, @subPath='') -> + super(options) + @subPath = @subPath[1..] if @subPath[0] is '/' + @loadDemoingLibs() unless DemoView.loaded + + loadDemoingLibs: -> + @queue = new createjs.LoadQueue() + @queue.on('complete', @scriptsLoaded, @) + for f in ['jasmine', 'jasmine-html', 'boot', 'mock-ajax', 'demo-app'] + @queue.loadFile({ + src: "/javascripts/#{f}.js" + type: createjs.LoadQueue.JAVASCRIPT + }) + + scriptsLoaded: -> + @initDemoFiles() + @children = requireUtils.parseImmediateChildren(@demoFiles, @subPath, DEMO_REQUIRE_PREFIX, DEMO_URL_PREFIX) + @render() + @runDemo() + + # RENDER DATA + + getRenderData: -> + c = super(arguments...) + c.parentFolders = requireUtils.getParentFolders(@subPath, DEMO_URL_PREFIX) + c.children = @children or [] + parts = @subPath.split('/') + c.currentFolder = parts[parts.length-1] or parts[parts.length-2] or 'All' + c + + # RUNNING DEMOS + + initDemoFiles: -> + @demoFiles = @getAllDemoFiles() + if @subPath + prefix = TEST_REQUIRE_PREFIX + @subPath + @demoFiles = (f for f in @demoFiles when f.startsWith prefix) + + runDemo: -> + # TODO: Maybe have an option to run all demos in this folder at the same time? + return unless @subPath and _.last(@subPath.split('/')).indexOf('.demo') > -1 + requirePath = DEMO_REQUIRE_PREFIX + @subPath + demoFunc = require requirePath + if not _.isFunction(demoFunc) + console.error "Demo files must export a function. #{requirePath} does not." + return + view = demoFunc() + return unless view + @$el.find('#demo-area').empty().append(view.el) + # TODO, maybe handle root views differently than modal views differently than everything else? + + getAllDemoFiles = -> + allFiles = window.require.list() + (f for f in allFiles when f.indexOf('.demo') > -1) From 1013f52c14b52789c91ff43694e971d90be15ad5 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 14 Jun 2014 08:20:01 -0700 Subject: [PATCH 09/25] The demo folder is now being compiled correctly, but still having brunch issues. vendor.js isn't happening. --- config.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/config.coffee b/config.coffee index eabcfcfc3..15b0b34ed 100644 --- a/config.coffee +++ b/config.coffee @@ -5,6 +5,7 @@ startsWith = (string, substring) -> exports.config = paths: 'public': 'public' + 'watched': ['app', 'test', 'vendor', 'demo', 'bower_components'] conventions: ignored: (path) -> startsWith(sysPath.basename(path), '_') sourceMaps: true From 493231798235f7a777abf4c4e2b2a8f5af4bb0c6 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Sat, 14 Jun 2014 15:02:14 -0700 Subject: [PATCH 10/25] Finally got the demo view working (mostly). Need to turn off third party ajax requests. --- {test/app => app}/lib/requireUtils.coffee | 0 {test/app => app}/views/DemoView.coffee | 13 ++++++++----- config.coffee | 3 +-- .../demo}/views/editor/PatchesView.demo.coffee | 9 ++++++++- 4 files changed, 17 insertions(+), 8 deletions(-) rename {test/app => app}/lib/requireUtils.coffee (100%) rename {test/app => app}/views/DemoView.coffee (90%) rename {demo => test/demo}/views/editor/PatchesView.demo.coffee (67%) diff --git a/test/app/lib/requireUtils.coffee b/app/lib/requireUtils.coffee similarity index 100% rename from test/app/lib/requireUtils.coffee rename to app/lib/requireUtils.coffee diff --git a/test/app/views/DemoView.coffee b/app/views/DemoView.coffee similarity index 90% rename from test/app/views/DemoView.coffee rename to app/views/DemoView.coffee index b3470ab9d..00def8100 100644 --- a/test/app/views/DemoView.coffee +++ b/app/views/DemoView.coffee @@ -2,7 +2,7 @@ CocoView = require 'views/kinds/CocoView' template = require 'templates/demo' requireUtils = require 'lib/requireUtils' -DEMO_REQUIRE_PREFIX = 'demo/app/' +DEMO_REQUIRE_PREFIX = 'test/demo/' DEMO_URL_PREFIX = '/demo/' ### @@ -37,7 +37,8 @@ module.exports = DemoView = class DemoView extends CocoView loadDemoingLibs: -> @queue = new createjs.LoadQueue() @queue.on('complete', @scriptsLoaded, @) - for f in ['jasmine', 'jasmine-html', 'boot', 'mock-ajax', 'demo-app'] + window.jasmine = {} # so that mock-ajax properly loads. It expects jasmine to be loaded + for f in ['mock-ajax', 'demo-app'] @queue.loadFile({ src: "/javascripts/#{f}.js" type: createjs.LoadQueue.JAVASCRIPT @@ -64,7 +65,7 @@ module.exports = DemoView = class DemoView extends CocoView initDemoFiles: -> @demoFiles = @getAllDemoFiles() if @subPath - prefix = TEST_REQUIRE_PREFIX + @subPath + prefix = DEMO_REQUIRE_PREFIX + @subPath @demoFiles = (f for f in @demoFiles when f.startsWith prefix) runDemo: -> @@ -75,11 +76,13 @@ module.exports = DemoView = class DemoView extends CocoView if not _.isFunction(demoFunc) console.error "Demo files must export a function. #{requirePath} does not." return + + jasmine.Ajax.install() view = demoFunc() return unless view - @$el.find('#demo-area').empty().append(view.el) + @$el.find('#demo-area').empty().append(view.$el) # TODO, maybe handle root views differently than modal views differently than everything else? - getAllDemoFiles = -> + getAllDemoFiles: -> allFiles = window.require.list() (f for f in allFiles when f.indexOf('.demo') > -1) diff --git a/config.coffee b/config.coffee index 15b0b34ed..9bbd3abff 100644 --- a/config.coffee +++ b/config.coffee @@ -5,7 +5,6 @@ startsWith = (string, substring) -> exports.config = paths: 'public': 'public' - 'watched': ['app', 'test', 'vendor', 'demo', 'bower_components'] conventions: ignored: (path) -> startsWith(sysPath.basename(path), '_') sourceMaps: true @@ -36,7 +35,7 @@ exports.config = (bower_components[\/\\]aether[\/\\]build[\/\\]aether.js) )/// 'javascripts/test-app.js': /^test[\/\\]app/ - 'javascripts/demo-app.js': /^demo/ + 'javascripts/demo-app.js': /^test[\/\\]demo/ order: before: [ diff --git a/demo/views/editor/PatchesView.demo.coffee b/test/demo/views/editor/PatchesView.demo.coffee similarity index 67% rename from demo/views/editor/PatchesView.demo.coffee rename to test/demo/views/editor/PatchesView.demo.coffee index 9004924ae..dd1c347b6 100644 --- a/demo/views/editor/PatchesView.demo.coffee +++ b/test/demo/views/editor/PatchesView.demo.coffee @@ -17,5 +17,12 @@ class BlandModel extends CocoModel module.exports = -> model = new BlandModel({_id:'12345'}) - new PatchesView(model) + v = new PatchesView(model) + v.load() + + # doesn't quite work yet. Intercepts a mixpanel request instead + r = jasmine.Ajax.requests.mostRecent() + r.send({statusCode:200, responseText:"[]"}) + v.render() + v From 2e40a536740b811d69a346d54a96bc80088e73b3 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken Date: Sun, 15 Jun 2014 23:19:37 +0200 Subject: [PATCH 11/25] Extracting third party scripts out of main --- app/assets/main.html | 108 ----------------------------------- app/initialize.coffee | 13 +++++ app/lib/facebook.coffee | 42 ++++++++++++++ app/lib/filepicker.coffee | 28 +++++++++ app/lib/google.coffee | 16 ++++++ app/lib/olark.coffee | 116 ++++++++++++++++++++++++++++++++++++++ app/lib/segmentio.coffee | 41 ++++++++++++++ app/lib/temp.txt | 18 ++++++ app/lib/twitter.coffee | 12 ++++ 9 files changed, 286 insertions(+), 108 deletions(-) create mode 100644 app/lib/facebook.coffee create mode 100644 app/lib/filepicker.coffee create mode 100644 app/lib/google.coffee create mode 100644 app/lib/olark.coffee create mode 100644 app/lib/segmentio.coffee create mode 100644 app/lib/temp.txt create mode 100644 app/lib/twitter.coffee diff --git a/app/assets/main.html b/app/assets/main.html index b0fac676c..9f583173b 100644 --- a/app/assets/main.html +++ b/app/assets/main.html @@ -55,123 +55,15 @@ authorize: true - - - - - - - -
- - - - -
- - - - - - - - - - - - diff --git a/app/initialize.coffee b/app/initialize.coffee index 08e0a4b39..938a6188b 100644 --- a/app/initialize.coffee +++ b/app/initialize.coffee @@ -19,6 +19,7 @@ definitionSchemas = init = -> # Set up Backbone.Mediator schemas + initializeVendors() setUpDefinitions() setUpChannels() Backbone.Mediator.setValidationEnabled document.location.href.search(/codecombat.com/) is -1 @@ -60,3 +61,15 @@ setUpChannels = -> setUpDefinitions = -> for definition of definitionSchemas Backbone.Mediator.addDefSchemas definitionSchemas[definition] + +initializeVendors = -> + initializers = + filepicker: require './lib/filepicker' + #olark: require './lib/olark' + facebook: require './lib/facebook' + google: require './lib/google' + twitter: require './lib/twitter' + #segmentio: require './lib/segmentio' + + for name, initializer of initializers + initializer() diff --git a/app/lib/facebook.coffee b/app/lib/facebook.coffee new file mode 100644 index 000000000..bc4834c0b --- /dev/null +++ b/app/lib/facebook.coffee @@ -0,0 +1,42 @@ +module.exports = initializeFacebook = -> + # Additional JS functions here + window.fbAsyncInit = -> + Backbone.Mediator.publish "fbapi-loaded" + FB.init + appId: (if document.location.origin is "http://localhost:3000" then "607435142676437" else "148832601965463") # App ID + channelUrl: document.location.origin + "/channel.html" # Channel File + status: true # check login status + cookie: true # enable cookies to allow the server to access the session + xfbml: true # parse XFBML + + + # This is fired for any auth related change, such as login, logout or session refresh. + FB.Event.subscribe "auth.authResponseChange", (response) -> + + # Here we specify what we do with the response anytime this event occurs. + if response.status is "connected" + + # They have logged in to the app. + Backbone.Mediator.publish "facebook-logged-in", + response: response + + else if response.status is "not_authorized" + # + else + # + + # Load the SDK asynchronously + ((d) -> + js = undefined + id = "facebook-jssdk" + ref = d.getElementsByTagName("script")[0] + return if d.getElementById(id) + js = d.createElement("script") + js.id = id + js.async = true + js.src = "//connect.facebook.net/en_US/all.js" + + #js.src = "//connect.facebook.net/en_US/all/debug.js"; + ref.parentNode.insertBefore js, ref + return + ) document diff --git a/app/lib/filepicker.coffee b/app/lib/filepicker.coffee new file mode 100644 index 000000000..5b4ee7f82 --- /dev/null +++ b/app/lib/filepicker.coffee @@ -0,0 +1,28 @@ +module.exports = initializeFilepicker = -> + ((a) -> + return if window.filepicker + b = a.createElement("script") + b.type = "text/javascript" + b.async = not 0 + b.src = ((if "https:" is a.location.protocol then "https:" else "http:")) + "//api.filepicker.io/v1/filepicker.js" + c = a.getElementsByTagName("script")[0] + c.parentNode.insertBefore b, c + d = {} + d._queue = [] + e = "pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",") + f = (a, b) -> + -> + b.push [ + a + arguments_ + ] + return + + g = 0 + + while g < e.length + d[e[g]] = f(e[g], d._queue) + g++ + window.filepicker = d + return + ) document diff --git a/app/lib/google.coffee b/app/lib/google.coffee new file mode 100644 index 000000000..c664efb86 --- /dev/null +++ b/app/lib/google.coffee @@ -0,0 +1,16 @@ +module.exports = initializeGoogle = -> + onGPlusLoaded = -> + Backbone.Mediator.publish "gapi-loaded" + return + signinCallback = (authResult) -> + Backbone.Mediator.publish "gplus-logged-in", authResult if authResult["access_token"] + return + (-> + po = document.createElement("script") + po.type = "text/javascript" + po.async = true + po.src = "https://apis.google.com/js/client:plusone.js?onload=onGPlusLoaded" + s = document.getElementsByTagName("script")[0] + s.parentNode.insertBefore po, s + return + )() diff --git a/app/lib/olark.coffee b/app/lib/olark.coffee new file mode 100644 index 000000000..09187223d --- /dev/null +++ b/app/lib/olark.coffee @@ -0,0 +1,116 @@ +module.exports = initializeOlark = -> + window.olark or ((c) -> # + s = -> + a.P r + f[z] r + return + f[z] = -> + (a.s = a.s or []).push arguments_ + return + + a = f[z]._ = {} + q = c.methods.length + while q-- + ((n) -> + f[z][n] = -> + f[z] "call", n, arguments_ + return + + return + ) c.methods[q] + a.l = c.loader + a.i = nt + a.p = 0: +new Date + a.P = (u) -> + a.p[u] = new Date - a.p[0] + return + + (if f.addEventListener then f.addEventListener(r, s, false) else f.attachEvent("on" + r, s)) + ld = -> + p = (hd) -> + hd = "head" + [ + "<" + hd + "><" + i + " onl" + "oad=\"var d=" + g + ";d.getElementsByTagName('head')[0]." + j + "(d." + h + "('script'))." + k + "='" + l + "//" + a.l + "'" + "\"" + ">" + ].join "" + i = "body" + m = d[i] + return setTimeout(ld, 100) unless m + a.P 1 + j = "appendChild" + h = "createElement" + k = "src" + n = d[h]("div") + v = n[j](d[h](z)) + b = d[h]("iframe") + g = "document" + e = "domain" + o = undefined + n.style.display = "none" + m.insertBefore(n, m.firstChild).id = z + b.frameBorder = "0" + b.id = z + "-loader" + b.src = "javascript:false" if /MSIE[ ]+6/.test(navigator.userAgent) + b.allowTransparency = "true" + v[j] b + try + b.contentWindow[g].open() + catch w + c[e] = d[e] + o = "javascript:var d=" + g + ".open();d.domain='" + d.domain + "';" + b[k] = o + "void(0);" + try + t = b.contentWindow[g] + t.write p() + t.close() + catch x + b[k] = o + "d.write(\"" + p().replace(/"/g, String.fromCharCode(92) + "\"") + "\");d.close();" + a.P 2 + return + + ld() + return + + nt() + return + )( + loader: "static.olark.com/jsclient/loader0.js" + name: "olark" + methods: [ + "configure" + "extend" + "declare" + "identify" + ] + ) + + # custom configuration goes here (www.olark.com/documentation) + olark.identify "1451-787-10-5544" #]]> + diff --git a/app/lib/segmentio.coffee b/app/lib/segmentio.coffee new file mode 100644 index 000000000..eae7dd588 --- /dev/null +++ b/app/lib/segmentio.coffee @@ -0,0 +1,41 @@ +module.exports = initializeSegmentio = -> + analytics = analytics or [] + (-> + e = [ + "identify" + "track" + "trackLink" + "trackForm" + "trackClick" + "trackSubmit" + "page" + "pageview" + "ab" + "alias" + "ready" + "group" + ] + t = (e) -> + -> + analytics.push [e].concat(Array::slice.call(arguments_, 0)) + return + + n = 0 + + while n < e.length + analytics[e[n]] = t(e[n]) + n++ + return + )() + analytics.load = (e) -> + t = document.createElement("script") + t.type = "text/javascript" + t.async = not 0 + t.src = ((if "https:" is document.location.protocol then "https://" else "http://")) + "d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/" + e + "/analytics.min.js" + + n = document.getElementsByTagName("script")[0] + n.parentNode.insertBefore t, n + return + + + analytics.load "jsjzx9n4d2" diff --git a/app/lib/temp.txt b/app/lib/temp.txt new file mode 100644 index 000000000..345d300f6 --- /dev/null +++ b/app/lib/temp.txt @@ -0,0 +1,18 @@ +/*<",i,' onl' + 'oad="var d=',g,";d.getElementsByTagName('head')[0].",j,"(d.",h,"('script')).",k,"='",l,"//",a.l,"'",'"',">"].join("")}var i="body",m=d[i];if(!m){ + return setTimeout(ld,100)}a.P(1);var j="appendChild",h="createElement",k="src",n=d[h]("div"),v=n[j](d[h](z)),b=d[h]("iframe"),g="document",e="domain",o;n.style.display="none";m.insertBefore(n,m.firstChild).id=z;b.frameBorder="0";b.id=z+"-loader";if(/MSIE[ ]+6/.test(navigator.userAgent)){ + b.src="javascript:false"}b.allowTransparency="true";v[j](b);try{ + b.contentWindow[g].open()}catch(w){ + c[e]=d[e];o="javascript:var d="+g+".open();d.domain='"+d.domain+"';";b[k]=o+"void(0);"}try{ + var t=b.contentWindow[g];t.write(p());t.close()}catch(x){ + b[k]=o+'d.write("'+p().replace(/"/g,String.fromCharCode(92)+'"')+'");d.close();'}a.P(2)};ld()};nt()})({ + loader: "static.olark.com/jsclient/loader0.js",name:"olark",methods:["configure","extend","declare","identify"]}); + /* custom configuration goes here (www.olark.com/documentation) */ + olark.identify('1451-787-10-5544');/*]]>*/ diff --git a/app/lib/twitter.coffee b/app/lib/twitter.coffee new file mode 100644 index 000000000..eaba1f36f --- /dev/null +++ b/app/lib/twitter.coffee @@ -0,0 +1,12 @@ +module.exports = initializeTwitter = -> + ((d, s, id) -> + js = undefined + fjs = d.getElementsByTagName(s)[0] + p = (if /^http:/.test(d.location) then "http" else "https") + unless d.getElementById(id) + js = d.createElement(s) + js.id = id + js.src = p + "://platform.twitter.com/widgets.js" + fjs.parentNode.insertBefore js, fjs + return + ) document, "script", "twitter-wjs" From 1c671a3c25281d595b101a1e2fa8911a631e3e72 Mon Sep 17 00:00:00 2001 From: Ruben Vereecken Date: Mon, 16 Jun 2014 12:55:58 +0200 Subject: [PATCH 12/25] Refactored vendor loading --- app/assets/main.html | 13 ------------- app/initialize.coffee | 18 +++++++++++++++--- app/lib/filepicker.coffee | 2 +- app/lib/linkedin.coffee | 12 ++++++++++++ app/lib/olark.coffee | 4 ++-- app/lib/segmentio.coffee | 2 +- app/lib/temp.txt | 18 ------------------ 7 files changed, 31 insertions(+), 38 deletions(-) create mode 100644 app/lib/linkedin.coffee delete mode 100644 app/lib/temp.txt diff --git a/app/assets/main.html b/app/assets/main.html index 9f583173b..1aede0254 100644 --- a/app/assets/main.html +++ b/app/assets/main.html @@ -43,19 +43,6 @@ - - - - -
diff --git a/app/initialize.coffee b/app/initialize.coffee index 938a6188b..9f0283488 100644 --- a/app/initialize.coffee +++ b/app/initialize.coffee @@ -18,8 +18,12 @@ definitionSchemas = 'misc': require './schemas/definitions/misc' init = -> - # Set up Backbone.Mediator schemas + # Don't initialize all the social scripts when visiting demo pages + if not (/.*\/demo\/.*/).exec window.location.href + initializeSocial() + initializeVendors() + # Set up Backbone.Mediator schemas setUpDefinitions() setUpChannels() Backbone.Mediator.setValidationEnabled document.location.href.search(/codecombat.com/) is -1 @@ -65,11 +69,19 @@ setUpDefinitions = -> initializeVendors = -> initializers = filepicker: require './lib/filepicker' - #olark: require './lib/olark' + segmentio: require './lib/segmentio' + + for name, initializer of initializers + initializer() + +initializeSocial = -> + initializers = + + olark: require './lib/olark' facebook: require './lib/facebook' google: require './lib/google' twitter: require './lib/twitter' - #segmentio: require './lib/segmentio' + linkedin: require './lib/linkedin' for name, initializer of initializers initializer() diff --git a/app/lib/filepicker.coffee b/app/lib/filepicker.coffee index 5b4ee7f82..fde8ccd50 100644 --- a/app/lib/filepicker.coffee +++ b/app/lib/filepicker.coffee @@ -14,7 +14,7 @@ module.exports = initializeFilepicker = -> -> b.push [ a - arguments_ + arguments ] return diff --git a/app/lib/linkedin.coffee b/app/lib/linkedin.coffee new file mode 100644 index 000000000..aca441148 --- /dev/null +++ b/app/lib/linkedin.coffee @@ -0,0 +1,12 @@ +module.exports = initializeLinkedIn = -> + window.linkedInAsyncInit = -> + Backbone.Mediator.publish 'linkedin-loaded' + + linkedInSnippet = + '' + + $('head').append(linkedInSnippet) diff --git a/app/lib/olark.coffee b/app/lib/olark.coffee index 09187223d..e0189da1c 100644 --- a/app/lib/olark.coffee +++ b/app/lib/olark.coffee @@ -11,7 +11,7 @@ module.exports = initializeOlark = -> f[z] r return f[z] = -> - (a.s = a.s or []).push arguments_ + (a.s = a.s or []).push arguments return a = f[z]._ = {} @@ -19,7 +19,7 @@ module.exports = initializeOlark = -> while q-- ((n) -> f[z][n] = -> - f[z] "call", n, arguments_ + f[z] "call", n, arguments return return diff --git a/app/lib/segmentio.coffee b/app/lib/segmentio.coffee index eae7dd588..afb156245 100644 --- a/app/lib/segmentio.coffee +++ b/app/lib/segmentio.coffee @@ -17,7 +17,7 @@ module.exports = initializeSegmentio = -> ] t = (e) -> -> - analytics.push [e].concat(Array::slice.call(arguments_, 0)) + analytics.push [e].concat(Array::slice.call(arguments, 0)) return n = 0 diff --git a/app/lib/temp.txt b/app/lib/temp.txt deleted file mode 100644 index 345d300f6..000000000 --- a/app/lib/temp.txt +++ /dev/null @@ -1,18 +0,0 @@ -/*<",i,' onl' + 'oad="var d=',g,";d.getElementsByTagName('head')[0].",j,"(d.",h,"('script')).",k,"='",l,"//",a.l,"'",'"',">"].join("")}var i="body",m=d[i];if(!m){ - return setTimeout(ld,100)}a.P(1);var j="appendChild",h="createElement",k="src",n=d[h]("div"),v=n[j](d[h](z)),b=d[h]("iframe"),g="document",e="domain",o;n.style.display="none";m.insertBefore(n,m.firstChild).id=z;b.frameBorder="0";b.id=z+"-loader";if(/MSIE[ ]+6/.test(navigator.userAgent)){ - b.src="javascript:false"}b.allowTransparency="true";v[j](b);try{ - b.contentWindow[g].open()}catch(w){ - c[e]=d[e];o="javascript:var d="+g+".open();d.domain='"+d.domain+"';";b[k]=o+"void(0);"}try{ - var t=b.contentWindow[g];t.write(p());t.close()}catch(x){ - b[k]=o+'d.write("'+p().replace(/"/g,String.fromCharCode(92)+'"')+'");d.close();'}a.P(2)};ld()};nt()})({ - loader: "static.olark.com/jsclient/loader0.js",name:"olark",methods:["configure","extend","declare","identify"]}); - /* custom configuration goes here (www.olark.com/documentation) */ - olark.identify('1451-787-10-5544');/*]]>*/ From ee791da34c5713ed48bfeb55a0282040a4bb0c54 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Mon, 16 Jun 2014 10:42:13 -0700 Subject: [PATCH 13/25] Moved services into their own folder, and made no services run for both testing and demo pages. --- app/initialize.coffee | 42 ++++++++++-------------- app/lib/{ => services}/facebook.coffee | 0 app/lib/{ => services}/filepicker.coffee | 1 + app/lib/{ => services}/google.coffee | 0 app/lib/{ => services}/linkedin.coffee | 0 app/lib/{ => services}/olark.coffee | 0 app/lib/{ => services}/segmentio.coffee | 0 app/lib/{ => services}/twitter.coffee | 0 8 files changed, 19 insertions(+), 24 deletions(-) rename app/lib/{ => services}/facebook.coffee (100%) rename app/lib/{ => services}/filepicker.coffee (95%) rename app/lib/{ => services}/google.coffee (100%) rename app/lib/{ => services}/linkedin.coffee (100%) rename app/lib/{ => services}/olark.coffee (100%) rename app/lib/{ => services}/segmentio.coffee (100%) rename app/lib/{ => services}/twitter.coffee (100%) diff --git a/app/initialize.coffee b/app/initialize.coffee index 9f0283488..2e76c6de4 100644 --- a/app/initialize.coffee +++ b/app/initialize.coffee @@ -18,11 +18,11 @@ definitionSchemas = 'misc': require './schemas/definitions/misc' init = -> - # Don't initialize all the social scripts when visiting demo pages - if not (/.*\/demo\/.*/).exec window.location.href - initializeSocial() - - initializeVendors() + path = document.location.pathname + testing = path.startsWith '/test' + demoing = path.startsWith '/demo' + initializeServices() unless testing or demoing + # Set up Backbone.Mediator schemas setUpDefinitions() setUpChannels() @@ -33,7 +33,6 @@ init = -> treemaExt = require 'treema-ext' treemaExt.setup() - filepicker.setKey('AvlkNoldcTOU4PvKi2Xm7z') $ -> init() @@ -66,22 +65,17 @@ setUpDefinitions = -> for definition of definitionSchemas Backbone.Mediator.addDefSchemas definitionSchemas[definition] -initializeVendors = -> - initializers = - filepicker: require './lib/filepicker' - segmentio: require './lib/segmentio' +initializeServices = -> + services = [ + './lib/services/filepicker' + './lib/services/segmentio' + './lib/services/olark' + './lib/services/facebook' + './lib/services/google' + './lib/services/twitter' + './lib/services/linkedin' + ] - for name, initializer of initializers - initializer() - -initializeSocial = -> - initializers = - - olark: require './lib/olark' - facebook: require './lib/facebook' - google: require './lib/google' - twitter: require './lib/twitter' - linkedin: require './lib/linkedin' - - for name, initializer of initializers - initializer() + for service in services + service = require service + service() diff --git a/app/lib/facebook.coffee b/app/lib/services/facebook.coffee similarity index 100% rename from app/lib/facebook.coffee rename to app/lib/services/facebook.coffee diff --git a/app/lib/filepicker.coffee b/app/lib/services/filepicker.coffee similarity index 95% rename from app/lib/filepicker.coffee rename to app/lib/services/filepicker.coffee index fde8ccd50..2c16b48bd 100644 --- a/app/lib/filepicker.coffee +++ b/app/lib/services/filepicker.coffee @@ -23,6 +23,7 @@ module.exports = initializeFilepicker = -> while g < e.length d[e[g]] = f(e[g], d._queue) g++ + d.setKey('AvlkNoldcTOU4PvKi2Xm7z') window.filepicker = d return ) document diff --git a/app/lib/google.coffee b/app/lib/services/google.coffee similarity index 100% rename from app/lib/google.coffee rename to app/lib/services/google.coffee diff --git a/app/lib/linkedin.coffee b/app/lib/services/linkedin.coffee similarity index 100% rename from app/lib/linkedin.coffee rename to app/lib/services/linkedin.coffee diff --git a/app/lib/olark.coffee b/app/lib/services/olark.coffee similarity index 100% rename from app/lib/olark.coffee rename to app/lib/services/olark.coffee diff --git a/app/lib/segmentio.coffee b/app/lib/services/segmentio.coffee similarity index 100% rename from app/lib/segmentio.coffee rename to app/lib/services/segmentio.coffee diff --git a/app/lib/twitter.coffee b/app/lib/services/twitter.coffee similarity index 100% rename from app/lib/twitter.coffee rename to app/lib/services/twitter.coffee From a3af0d2ea5f301c46b490a4331f2b3a716cc4cec Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Mon, 16 Jun 2014 10:42:28 -0700 Subject: [PATCH 14/25] Got the PatchView demo working. --- .../demo/views/editor/PatchesView.demo.coffee | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/demo/views/editor/PatchesView.demo.coffee b/test/demo/views/editor/PatchesView.demo.coffee index dd1c347b6..a06da6631 100644 --- a/test/demo/views/editor/PatchesView.demo.coffee +++ b/test/demo/views/editor/PatchesView.demo.coffee @@ -16,13 +16,28 @@ class BlandModel extends CocoModel module.exports = -> - model = new BlandModel({_id:'12345'}) + model = new BlandModel({_id:'12345', name:'name', original:'original'}) v = new PatchesView(model) v.load() - # doesn't quite work yet. Intercepts a mixpanel request instead + # Respond to request for pending patches. r = jasmine.Ajax.requests.mostRecent() - r.send({statusCode:200, responseText:"[]"}) + patches = [ + { + delta: null + commitMessage: 'Demo message' + creator: '12345' + created: "2014-01-01T12:00:00.000Z" + status: 'pending' + } + ] + r.response({ status:200, responseText: JSON.stringify patches }) + + # Respond to request for user ids -> names + r = jasmine.Ajax.requests.mostRecent() + names = { '12345': { name: 'Patchman' } } + r.response({ status:200, responseText: JSON.stringify names }) + v.render() v From d6bb7ffe81b36da6a4844d7a746bd03288de6746 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Mon, 16 Jun 2014 11:17:48 -0700 Subject: [PATCH 15/25] Fixing tests. Somehow segmentio loading was making the jasmine test runner load properly in WebKit browsers. No idea why. --- app/assets/javascripts/boot.js | 7 +------ app/views/TestView.coffee | 3 ++- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/boot.js b/app/assets/javascripts/boot.js index e9a9123a3..47b125211 100755 --- a/app/assets/javascripts/boot.js +++ b/app/assets/javascripts/boot.js @@ -160,12 +160,7 @@ * * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. */ - var currentWindowOnload = window.onload; - - window.onload = function() { - if (currentWindowOnload) { - currentWindowOnload(); - } + window.runJasmine = function() { htmlReporter.initialize(); env.execute(); }; diff --git a/app/views/TestView.coffee b/app/views/TestView.coffee index e999b3b0f..ef1d02fbf 100644 --- a/app/views/TestView.coffee +++ b/app/views/TestView.coffee @@ -15,7 +15,7 @@ module.exports = TestView = class TestView extends CocoView constructor: (options, @subPath='') -> super(options) @subPath = @subPath[1..] if @subPath[0] is '/' - @loadTestingLibs() unless TestView.loaded + @loadTestingLibs() loadTestingLibs: -> @queue = new createjs.LoadQueue() @@ -30,6 +30,7 @@ module.exports = TestView = class TestView extends CocoView @initSpecFiles() @render() TestView.runTests(@specFiles) + window.runJasmine() # RENDER DATA From 3e24b2f86e856a74a25db763d5873b3bfed9d023 Mon Sep 17 00:00:00 2001 From: Scott Erickson Date: Mon, 16 Jun 2014 11:38:03 -0700 Subject: [PATCH 16/25] Fixed tests running in karma. --- app/assets/javascripts/run-tests.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/run-tests.js b/app/assets/javascripts/run-tests.js index d2afed5cd..ad1de1989 100644 --- a/app/assets/javascripts/run-tests.js +++ b/app/assets/javascripts/run-tests.js @@ -1,5 +1,6 @@ // Helper for running tests through Karma. // Hooks into the test view logic for running tests. -TestView = require('views/test'); +require('initialize'); +TestView = require('views/TestView'); TestView.runTests(); \ No newline at end of file From 15d89789f22069e78709b6627a2a02ac31d44968 Mon Sep 17 00:00:00 2001 From: Fergus Leen Date: Mon, 16 Jun 2014 21:48:27 +0100 Subject: [PATCH 17/25] Checks for false positive 64 bit --- scripts/devSetup/systemConfiguration.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/devSetup/systemConfiguration.py b/scripts/devSetup/systemConfiguration.py index c8b2b99de..78df92fdf 100644 --- a/scripts/devSetup/systemConfiguration.py +++ b/scripts/devSetup/systemConfiguration.py @@ -33,6 +33,8 @@ class SystemConfiguration(object): return 64 else: if self.operating_system == u"mac": + if os.uname()[4] == u"x86_64": + return 64 raise NotSupportedError(u"Your processor is determined to have a maxSize of" + str(sys.maxsize) + u",\n which doesn't correspond with a 64-bit architecture.") return 32 From b6c8a170d1bbe2b1b69751b5b6cf10dc6d927226 Mon Sep 17 00:00:00 2001 From: Tery Lim Date: Tue, 17 Jun 2014 10:50:56 +0800 Subject: [PATCH 18/25] Fix typo for receive --- server/commons/errors.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/commons/errors.coffee b/server/commons/errors.coffee index 3f60ef852..74c50a1f8 100644 --- a/server/commons/errors.coffee +++ b/server/commons/errors.coffee @@ -39,6 +39,6 @@ module.exports.gatewayTimeoutError = (res, message="Gateway timeout") -> res.send 504, message res.end() -module.exports.clientTimeout = (res, message="The server did not recieve the client response in a timely manner") -> +module.exports.clientTimeout = (res, message="The server did not receive the client response in a timely manner") -> res.send 408, message res.end() From c4d741294b0ccb65c7c41ff77315fd20203caa7d Mon Sep 17 00:00:00 2001 From: Michael Schmatz Date: Tue, 17 Jun 2014 10:31:16 -0700 Subject: [PATCH 19/25] Created multicore.coffee --- multicore.coffee | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 multicore.coffee diff --git a/multicore.coffee b/multicore.coffee new file mode 100644 index 000000000..194b5940e --- /dev/null +++ b/multicore.coffee @@ -0,0 +1,14 @@ +cluster = require 'cluster' +numCPUs = require('os').cpus().length + +if cluster.isMaster + for i in [0...numCPUs] + cluster.fork() + cluster.on 'exit', (worker, code, signal) -> + console.log 'worker' + worker.process.id + 'died' + cluster.fork() +else + require('coffee-script') + require('coffee-script/register') + server = require('./server') + server.startServer() From 42304d8fb44984fd0fd05f91b3120798e846e74f Mon Sep 17 00:00:00 2001 From: Fergus Leen Date: Tue, 17 Jun 2014 19:30:32 +0100 Subject: [PATCH 20/25] Check for git repository before cloning --- scripts/devSetup/bootstrap.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/devSetup/bootstrap.sh b/scripts/devSetup/bootstrap.sh index 9d1f34b8e..46099635c 100644 --- a/scripts/devSetup/bootstrap.sh +++ b/scripts/devSetup/bootstrap.sh @@ -58,7 +58,13 @@ checkDependencies deps[@] basicDependenciesErrorHandling if command -v node >/dev/null 2>&1; then checkNodeVersion fi -#install git repository -git clone $repositoryUrl coco -#python ./coco/scripts/devSetup/setup.py -echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!" + +#check if a git repository already exists here +if [ -d .git ]; then + echo "A git repository already exists here!" +else + #install git repository + git clone $repositoryUrl coco + #python ./coco/scripts/devSetup/setup.py + echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!" +fi From de1f6ab50ae39c81dbab8c37bf9636c6aaa3ab9d Mon Sep 17 00:00:00 2001 From: Fergus Leen Date: Tue, 17 Jun 2014 19:44:32 +0100 Subject: [PATCH 21/25] Check for git repository before cloning --- scripts/devSetup/bootstrap.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/devSetup/bootstrap.sh b/scripts/devSetup/bootstrap.sh index 9d1f34b8e..46099635c 100644 --- a/scripts/devSetup/bootstrap.sh +++ b/scripts/devSetup/bootstrap.sh @@ -58,7 +58,13 @@ checkDependencies deps[@] basicDependenciesErrorHandling if command -v node >/dev/null 2>&1; then checkNodeVersion fi -#install git repository -git clone $repositoryUrl coco -#python ./coco/scripts/devSetup/setup.py -echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!" + +#check if a git repository already exists here +if [ -d .git ]; then + echo "A git repository already exists here!" +else + #install git repository + git clone $repositoryUrl coco + #python ./coco/scripts/devSetup/setup.py + echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!" +fi From ecf24c28754baf17c73d494f42ec72b2c4fcabbf Mon Sep 17 00:00:00 2001 From: Fergus Leen Date: Tue, 17 Jun 2014 19:56:13 +0100 Subject: [PATCH 22/25] Commit on wrong branch - reverted --- scripts/devSetup/bootstrap.sh | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/scripts/devSetup/bootstrap.sh b/scripts/devSetup/bootstrap.sh index 46099635c..9d1f34b8e 100644 --- a/scripts/devSetup/bootstrap.sh +++ b/scripts/devSetup/bootstrap.sh @@ -58,13 +58,7 @@ checkDependencies deps[@] basicDependenciesErrorHandling if command -v node >/dev/null 2>&1; then checkNodeVersion fi - -#check if a git repository already exists here -if [ -d .git ]; then - echo "A git repository already exists here!" -else - #install git repository - git clone $repositoryUrl coco - #python ./coco/scripts/devSetup/setup.py - echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!" -fi +#install git repository +git clone $repositoryUrl coco +#python ./coco/scripts/devSetup/setup.py +echo "Now copy and paste 'sudo python ./coco/scripts/devSetup/setup.py' into the terminal!" From 0b8a0c8f6fe55b24e81c61e1d6ab245b7e401da0 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 17 Jun 2014 13:03:08 -0700 Subject: [PATCH 23/25] Added UserRemarks. --- app/locale/en.coffee | 3 + app/models/UserRemark.coffee | 6 ++ app/schemas/models/user_remark.coffee | 24 ++++++ app/styles/account/profile.sass | 5 ++ app/templates/account/profile.jade | 15 +++- app/templates/employers.jade | 9 ++- app/treema-ext.coffee | 2 + app/views/account/job_profile_view.coffee | 4 +- app/views/account/profile_view.coffee | 76 ++++++++++++++++++- app/views/employers_view.coffee | 13 +++- server/commons/mapping.coffee | 1 + server/users/remarks/UserRemark.coffee | 11 +++ .../users/remarks/user_remark_handler.coffee | 12 +++ server/users/user_handler.coffee | 19 ++++- 14 files changed, 190 insertions(+), 10 deletions(-) create mode 100644 app/models/UserRemark.coffee create mode 100644 app/schemas/models/user_remark.coffee create mode 100644 server/users/remarks/UserRemark.coffee create mode 100644 server/users/remarks/user_remark_handler.coffee diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 4c734e41a..3ea886e85 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -282,6 +282,7 @@ education_description: "Description" education_description_help: "Highlight anything about this educational experience. (140 chars; optional)" our_notes: "Our Notes" + remarks: "Remarks" projects: "Projects" projects_header: "Add 3 projects" projects_header_2: "Projects (Top 3)" @@ -320,6 +321,7 @@ candidate_top_skills: "Top Skills" candidate_years_experience: "Yrs Exp" candidate_last_updated: "Last Updated" + candidate_who: "Who" featured_developers: "Featured Developers" other_developers: "Other Developers" inactive_developers: "Inactive Developers" @@ -882,6 +884,7 @@ document: "Document" sprite_sheet: "Sprite Sheet" candidate_sessions: "Candidate Sessions" + user_remark: "User Remark" delta: added: "Added" diff --git a/app/models/UserRemark.coffee b/app/models/UserRemark.coffee new file mode 100644 index 000000000..2bb37f42d --- /dev/null +++ b/app/models/UserRemark.coffee @@ -0,0 +1,6 @@ +CocoModel = require('./CocoModel') + +module.exports = class UserRemark extends CocoModel + @className: "UserRemark" + @schema: require 'schemas/models/user_remark' + urlRoot: "/db/user.remark" diff --git a/app/schemas/models/user_remark.coffee b/app/schemas/models/user_remark.coffee new file mode 100644 index 000000000..cd351c4a4 --- /dev/null +++ b/app/schemas/models/user_remark.coffee @@ -0,0 +1,24 @@ +c = require './../schemas' + +UserRemarkSchema = c.object { + title: "Remark" + description: "Remarks on a user, point of contact, tasks." +} + +_.extend UserRemarkSchema.properties, + user: c.objectId links: [{rel: 'extra', href: "/db/user/{($)}"}] + contact: c.objectId links: [{rel: 'extra', href: "/db/user/{($)}"}] + created: c.date title: 'Created', readOnly: true + history: c.array {title: 'History', description: 'Records of our interactions with the user.'}, + c.object {title: 'Record'}, {date: c.date(title: 'Date'), content: {title: 'Content', type: 'string', format: 'markdown'}} + tasks: c.array {title: 'Tasks', description: 'Task entries: when to email the contact about something.'}, + c.object {title: 'Task'}, {date: c.date(title: 'Date'), action: {title: 'Action', type: 'string'}} + + # denormalization + userName: { title: "Player Name", type: 'string' } + contactName: { title: "Contact Name", type: 'string' } # Not actually our usernames + + +c.extendBasicProperties UserRemarkSchema, 'user.remark' + +module.exports = UserRemarkSchema diff --git a/app/styles/account/profile.sass b/app/styles/account/profile.sass index 797bcad4d..05a06f6ee 100644 --- a/app/styles/account/profile.sass +++ b/app/styles/account/profile.sass @@ -193,6 +193,11 @@ width: 100% height: 100px + #remark-treema + background-color: white + border: 0 + padding-top: 0 + .right-column width: $side-width background-color: $sideBackground diff --git a/app/templates/account/profile.jade b/app/templates/account/profile.jade index 0b16838a3..639d14d84 100644 --- a/app/templates/account/profile.jade +++ b/app/templates/account/profile.jade @@ -169,6 +169,10 @@ block content button#contact-candidate.btn.btn-large.btn-inverse.flat-button span(data-i18n="account_profile.contact") Contact | #{profile.name.split(' ')[0]} + if me.isAdmin() + select#admin-contact.form-control + for contact in adminContacts + option(value=contact.id, selected=remark && remark.get('contact') == contact.id)= contact.name if !editing && sessions.length h3(data-i18n="account_profile.player_code") Player Code @@ -191,9 +195,12 @@ block content if editing && !profile.name h3.edit-label(data-i18n="account_profile.name_header") Fill in your name else if profile.name - h3= profile.name + h3= profile.name + (me.isAdmin() ? ' (' + user.get('name') + ')' : '') else - h3(data-i18n="account_profile.name_anonymous") Anonymous Developer + h3 + span(data-i18n="account_profile.name_anonymous") Anonymous Developer + if me.isAdmin() + span (#{user.get('name')}) form.editable-form .editable-icon.glyphicon.glyphicon-remove @@ -396,6 +403,10 @@ block content else div!= marked(notes) + if me.isAdmin() + h3(data-i18n="account_profile.remarks") Remarks + #remark-treema + .right-column.full-height-column .sub-column #projects-container.editable-section diff --git a/app/templates/employers.jade b/app/templates/employers.jade index 3125d7ea6..208c2bdf7 100644 --- a/app/templates/employers.jade +++ b/app/templates/employers.jade @@ -84,6 +84,8 @@ block content th(data-i18n="employers.candidate_top_skills") Top Skills th(data-i18n="employers.candidate_years_experience") Yrs Exp th(data-i18n="employers.candidate_last_updated") Last Updated + if me.isAdmin() + th(data-i18n="employers.candidate_who") Who if me.isAdmin() && area.id == 'inactive-candidates' th ✓? @@ -95,7 +97,10 @@ block content td if authorized img(src=candidate.getPhotoURL(50), alt=profile.name, title=profile.name, height=50) - p= profile.name + if profile.name + p= profile.name + else if me.isAdmin() + p (#{candidate.get('name')}) else img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50) p Developer ##{index + 1 + (area.id == 'featured-candidates' ? 0 : featuredCandidates.length)} @@ -111,6 +116,8 @@ block content span td= profile.experience td(data-profile-age=(new Date() - new Date(profile.updated)) / 86400 / 1000)= moment(profile.updated).fromNow() + if me.isAdmin() + td= remarks[candidate.id] ? remarks[candidate.id].get('contactName') : '' if me.isAdmin() && area.id == 'inactive-candidates' if candidate.get('jobProfileApproved') td ✓ diff --git a/app/treema-ext.coffee b/app/treema-ext.coffee index 0a4b8d036..dcfd4dbed 100644 --- a/app/treema-ext.coffee +++ b/app/treema-ext.coffee @@ -6,6 +6,8 @@ locale = require 'locale/locale' class DateTimeTreema extends TreemaNode.nodeMap.string valueClass: 'treema-date-time' buildValueForDisplay: (el) -> el.text(moment(@data).format('llll')) + buildValueForEditing: (valEl) -> + @buildValueForEditingSimply valEl, null, 'date' class VersionTreema extends TreemaNode valueClass: 'treema-version' diff --git a/app/views/account/job_profile_view.coffee b/app/views/account/job_profile_view.coffee index c38bc2d5c..97b2371e4 100644 --- a/app/views/account/job_profile_view.coffee +++ b/app/views/account/job_profile_view.coffee @@ -19,10 +19,10 @@ module.exports = class JobProfileView extends CocoView buildJobProfileTreema: -> visibleSettings = @editableSettings.concat @readOnlySettings - data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings + data = _.pick (me.get('jobProfile') ? {}), (value, key) -> key in visibleSettings data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName') schema = _.cloneDeep me.schema().properties.jobProfile - schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings + schema.properties = _.pick schema.properties, (value, key) -> key in visibleSettings schema.required = _.intersection schema.required, visibleSettings for prop in @readOnlySettings schema.properties[prop].readOnly = true diff --git a/app/views/account/profile_view.coffee b/app/views/account/profile_view.coffee index 295269552..a4100808c 100644 --- a/app/views/account/profile_view.coffee +++ b/app/views/account/profile_view.coffee @@ -6,6 +6,7 @@ CocoCollection = require 'collections/CocoCollection' {me} = require 'lib/auth' JobProfileContactView = require 'views/modal/job_profile_contact_modal' JobProfileView = require 'views/account/job_profile_view' +UserRemark = require 'models/UserRemark' forms = require 'lib/forms' class LevelSessionsCollection extends CocoCollection @@ -14,6 +15,14 @@ class LevelSessionsCollection extends CocoCollection constructor: (@userID) -> super() +adminContacts = [ + {id: "", name: "Assign a Contact"} + {id: "512ef4805a67a8c507000001", name: "Nick"} + {id: "5162fab9c92b4c751e000274", name: "Scott"} + {id: "51eb2714fa058cb20d0006ef", name: "Michael"} + {id: "51538fdb812dd9af02000001", name: "George"} +] + module.exports = class ProfileView extends View id: "profile-view" template: template @@ -36,12 +45,14 @@ module.exports = class ProfileView extends View 'change .editable-profile .editable-array input': 'onEditArray' 'keyup .editable-profile .editable-array input': 'onEditArray' 'click .editable-profile a': 'onClickLinkWhileEditing' + 'change #admin-contact': 'onAdminContactChanged' constructor: (options, @userID) -> @userID ?= me.id @onJobProfileNotesChanged = _.debounce @onJobProfileNotesChanged, 1000 + @onRemarkChanged = _.debounce @onRemarkChanged, 1000 @authorizedWithLinkedIn = IN?.User?.isAuthorized() - @linkedInLoaded = Boolean(IN.parse) + @linkedInLoaded = Boolean(IN?.parse) @waitingForLinkedIn = false window.contractCallback = => @authorizedWithLinkedIn = IN?.User?.isAuthorized() @@ -70,6 +81,22 @@ module.exports = class ProfileView extends View else @user = User.getByID(@userID) @sessions = @supermodel.loadCollection(new LevelSessionsCollection(@userID), 'candidate_sessions').model + if me.isAdmin() + # Mimicking how the VictoryModal fetches LevelFeedback + @remark = new UserRemark() + @remark.setURL "/db/user/#{@userID}/remark" + @remark.fetch() + @listenToOnce @remark, 'sync', @onRemarkLoaded + @listenToOnce @remark, 'error', @onRemarkNotFound + + onRemarkLoaded: -> + @remark.setURL "/db/user.remark/#{@remark.id}" + @render() + + onRemarkNotFound: -> + @remark = new UserRemark() # hmm, why do we create a new one here? + @remark.set 'user', @userID + @remark.set 'userName', name if name = @user.get('name') onLinkedInLoaded: => @linkedinLoaded = true @@ -229,6 +256,8 @@ module.exports = class ProfileView extends View context.sessions.sort (a, b) -> (b.playtime ? 0) - (a.playtime ? 0) else context.sessions = [] + context.adminContacts = adminContacts + context.remark = @remark context afterRender: -> @@ -249,6 +278,31 @@ module.exports = class ProfileView extends View _.delay -> justSavedSection.removeClass "just-saved", duration: 1500, easing: 'easeOutQuad' , 500 + if me.isAdmin() + visibleSettings = ['history', 'tasks'] + data = _.pick (@remark.attributes), (value, key) -> key in visibleSettings + data.history ?= [] + data.tasks ?= [] + schema = _.cloneDeep @remark.schema() + schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings + schema.required = _.intersection (schema.required ? []), visibleSettings + treemaOptions = + filePath: "db/user/#{@userID}" + schema: schema + data: data + aceUseWrapMode: true + callbacks: {change: @onRemarkChanged} + @remarkTreema = @$el.find('#remark-treema').treema treemaOptions + @remarkTreema.build() + @remarkTreema.open(3) + + onRemarkChanged: (e) => + return unless @remarkTreema.isValid() + for key in ['history', 'tasks'] + val = _.filter(@remarkTreema.get(key), (entry) -> entry?.content or entry?.action) + entry.date ?= (new Date()).toISOString() for entry in val if key is 'history' + @remark.set key, val + @saveRemark() initializeAutocomplete: (container) -> (container ? @$el).find('input[data-autocomplete]').each -> @@ -455,6 +509,26 @@ module.exports = class ProfileView extends View onClickLinkWhileEditing: (e) -> e.preventDefault() + onAdminContactChanged: (e) -> + newContact = @$el.find('#admin-contact').val() + newContactName = if newContact then _.find(adminContacts, id: newContact).name else '' + @remark.set 'contact', newContact + @remark.set 'contactName', newContactName + @saveRemark() + + saveRemark: -> + @remark.set 'user', @user.id + @remark.set 'userName', @user.get('name') + if errors = @remark.validate() + return console.error "UserRemark", @remark, "failed validation with errors:", errors + res = @remark.save() + res.error => + return if @destroyed + console.error "UserRemark", @remark, "failed to save with error:", res.responseText + res.success (model, response, options) => + return if @destroyed + console.log "Saved UserRemark", @remark, "with response", response + updateProgress: (highlightNext) -> return unless @user completed = 0 diff --git a/app/views/employers_view.coffee b/app/views/employers_view.coffee index 8a2c4e4ff..2b1186422 100644 --- a/app/views/employers_view.coffee +++ b/app/views/employers_view.coffee @@ -2,6 +2,7 @@ View = require 'views/kinds/RootView' template = require 'templates/employers' app = require 'application' User = require 'models/User' +UserRemark = require 'models/UserRemark' {me} = require 'lib/auth' CocoCollection = require 'collections/CocoCollection' EmployerSignupView = require 'views/modal/employer_signup_modal' @@ -10,6 +11,10 @@ class CandidatesCollection extends CocoCollection url: '/db/user/x/candidates' model: User +class UserRemarksCollection extends CocoCollection + url: '/db/user.remark?project=contact,contactName,user' + model: UserRemark + module.exports = class EmployersView extends View id: "employers-view" template: template @@ -37,6 +42,8 @@ module.exports = class EmployersView extends View ctx.inactiveCandidates = _.reject ctx.candidates, (c) -> c.get('jobProfile').active ctx.featuredCandidates = _.filter ctx.activeCandidates, (c) -> c.get('jobProfileApproved') ctx.otherCandidates = _.reject ctx.activeCandidates, (c) -> c.get('jobProfileApproved') + ctx.remarks = {} + ctx.remarks[remark.get('user')] = remark for remark in @remarks.models ctx.moment = moment ctx._ = _ ctx @@ -48,11 +55,13 @@ module.exports = class EmployersView extends View getCandidates: -> @candidates = new CandidatesCollection() @candidates.fetch() + @remarks = new UserRemarksCollection() + @remarks.fetch() # Re-render when we have fetched them, but don't wait and show a progress bar while loading. @listenToOnce @candidates, 'all', @renderCandidatesAndSetupScrolling + @listenToOnce @remarks, 'all', @renderCandidatesAndSetupScrolling renderCandidatesAndSetupScrolling: => - @render() $(".nano").nanoScroller() if window.history?.state?.lastViewedCandidateID @@ -179,7 +188,7 @@ module.exports = class EmployersView extends View "Last 4 weeks": (e, n, f, i, $r) -> days = parseFloat $($r.find('td')[i]).data('profile-age') days <= 28 - 7: + 8: "✓": filterSelectExactMatch "✗": filterSelectExactMatch diff --git a/server/commons/mapping.coffee b/server/commons/mapping.coffee index 8e3188fa2..802b33790 100644 --- a/server/commons/mapping.coffee +++ b/server/commons/mapping.coffee @@ -8,6 +8,7 @@ module.exports.handlers = 'patch': 'patches/patch_handler' 'thang_type': 'levels/thangs/thang_type_handler' 'user': 'users/user_handler' + 'user_remark': 'users/remarks/user_remark_handler' 'achievement': 'achievements/achievement_handler' 'earned_achievement': 'achievements/earned_achievement_handler' diff --git a/server/users/remarks/UserRemark.coffee b/server/users/remarks/UserRemark.coffee new file mode 100644 index 000000000..974bc05f3 --- /dev/null +++ b/server/users/remarks/UserRemark.coffee @@ -0,0 +1,11 @@ +mongoose = require('mongoose') +plugins = require('../../plugins/plugins') +jsonschema = require('../../../app/schemas/models/user_remark') + +UserRemarkSchema = new mongoose.Schema({ + created: + type: Date + 'default': Date.now +}, {strict: false}) + +module.exports = UserRemark = mongoose.model('user.remark', UserRemarkSchema) diff --git a/server/users/remarks/user_remark_handler.coffee b/server/users/remarks/user_remark_handler.coffee new file mode 100644 index 000000000..8d89b7873 --- /dev/null +++ b/server/users/remarks/user_remark_handler.coffee @@ -0,0 +1,12 @@ +UserRemark = require('./UserRemark') +Handler = require('../../commons/Handler') + +class UserRemarkHandler extends Handler + modelClass: UserRemark + editableProperties: ['user', 'contact', 'history', 'tasks', 'userName', 'contactName'] + jsonSchema: require '../../../app/schemas/models/user_remark' + + hasAccess: (req) -> + req.user?.isAdmin() + +module.exports = new UserRemarkHandler() diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index 5ffa68c06..a9e0dc106 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -11,6 +11,7 @@ log = require 'winston' LevelSession = require('../levels/sessions/LevelSession') LevelSessionHandler = require '../levels/sessions/level_session_handler' EarnedAchievement = require '../achievements/EarnedAchievement' +UserRemark = require './remarks/UserRemark' serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset'] privateProperties = [ @@ -197,6 +198,7 @@ UserHandler = class UserHandler extends Handler return @getMySimulatorLeaderboardRank(req, res, args[0]) if args[1] is 'simulator_leaderboard_rank' return @getEarnedAchievements(req, res, args[0]) if args[1] is 'achievements' return @trackActivity(req, res, args[0], args[2], args[3]) if args[1] is 'track' and args[2] + return @getRemark(req, res, args[0]) if args[1] is 'remark' return @sendNotFoundError(res) super(arguments...) @@ -313,7 +315,7 @@ UserHandler = class UserHandler extends Handler #query.jobProfileApproved = true unless req.user.isAdmin() # We split into featured and other now. query['jobProfile.active'] = true unless req.user.isAdmin() selection = 'jobProfile jobProfileApproved photoURL' - selection += ' email' if authorized + selection += ' email name' if authorized User.find(query).select(selection).exec (err, documents) => return @sendDatabaseError(res, err) if err candidates = (candidate for candidate in documents when @employerCanViewCandidate req.user, candidate.toObject()) @@ -321,7 +323,7 @@ UserHandler = class UserHandler extends Handler @sendSuccess(res, candidates) formatCandidate: (authorized, document) -> - fields = if authorized then ['jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile', 'jobProfileApproved'] + fields = if authorized then ['name', 'jobProfile', 'jobProfileApproved', 'photoURL', '_id'] else ['jobProfile', 'jobProfileApproved'] obj = _.pick document.toObject(), fields obj.photoURL ||= obj.jobProfile.photoURL if authorized subfields = ['country', 'city', 'lookingFor', 'jobTitle', 'skills', 'experience', 'updated', 'active'] @@ -363,4 +365,17 @@ UserHandler = class UserHandler extends Handler hash.update(user.get('_id') + '') hash.digest('hex') + getRemark: (req, res, userID) -> + return @sendUnauthorizedError(res) unless req.user.isAdmin() + query = user: userID + projection = null + if req.query.project + projection = {} + projection[field] = 1 for field in req.query.project.split(',') + UserRemark.findOne(query).select(projection).exec (err, remark) => + return @sendDatabaseError res, err if err + return @sendNotFoundError res unless remark? + @sendSuccess res, remark + + module.exports = new UserHandler() From 4bd02ba6dc47358fc8fc86162228d8682cec5d6d Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 17 Jun 2014 15:17:19 -0700 Subject: [PATCH 24/25] ModelModal now grants real ultimate power. --- app/templates/account/profile.jade | 4 +++- app/templates/modal/model.jade | 6 ++++-- app/views/account/profile_view.coffee | 5 +++++ app/views/modal/model_modal.coffee | 27 +++++++++++++++++++++++++++ server/users/user_handler.coffee | 14 +++++--------- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/app/templates/account/profile.jade b/app/templates/account/profile.jade index 639d14d84..de9dcca11 100644 --- a/app/templates/account/profile.jade +++ b/app/templates/account/profile.jade @@ -43,7 +43,9 @@ block content i.icon-eye-close span(data-i18n='account_profile.not_featured') Not Featured if me.isAdmin() && !myProfile - button.btn.edit-settings-button#enter-espionage-mode 007 + button.btn#enter-espionage-mode 007 + if me.isAdmin() + button.btn#open-model-modal Raw if profile && allowedToViewJobProfile div(class="job-profile-container" + (editing ? " editable-profile" : "")) diff --git a/app/templates/modal/model.jade b/app/templates/modal/model.jade index 4fae4c294..404dfb571 100644 --- a/app/templates/modal/model.jade +++ b/app/templates/modal/model.jade @@ -4,6 +4,8 @@ block modal-header block modal-body-content for model in models - h3= model.type() + ': ' + model.id - .model-treema(data-model-id=model.id) + .model-container(data-model-id=model.id) + h3= model.type() + ': ' + model.id + .model-treema(data-model-id=model.id) + btn.btn.btn-success.save-model(data-i18n="common.save") Save hr diff --git a/app/views/account/profile_view.coffee b/app/views/account/profile_view.coffee index a4100808c..e67b7a8fb 100644 --- a/app/views/account/profile_view.coffee +++ b/app/views/account/profile_view.coffee @@ -8,6 +8,7 @@ JobProfileContactView = require 'views/modal/job_profile_contact_modal' JobProfileView = require 'views/account/job_profile_view' UserRemark = require 'models/UserRemark' forms = require 'lib/forms' +ModelModal = require 'views/modal/model_modal' class LevelSessionsCollection extends CocoCollection url: -> "/db/user/#{@userID}/level.sessions/employer" @@ -37,6 +38,7 @@ module.exports = class ProfileView extends View 'click #save-notes-button': 'onJobProfileNotesChanged' 'click #contact-candidate': 'onContactCandidate' 'click #enter-espionage-mode': 'enterEspionageMode' + 'click #open-model-modal': 'openModelModal' 'click .editable-profile .profile-photo': 'onEditProfilePhoto' 'click .editable-profile .project-image': 'onEditProjectImage' 'click .editable-profile .editable-display': 'onEditSection' @@ -340,6 +342,9 @@ module.exports = class ProfileView extends View espionageSuccess: (model) -> window.location.reload() + openModelModal: (e) -> + @openModalView new ModelModal models: [@user] + onJobProfileNotesChanged: (e) => notes = @$el.find("#job-profile-notes").val() @user.set 'jobProfileNotes', notes diff --git a/app/views/modal/model_modal.coffee b/app/views/modal/model_modal.coffee index e1f983247..7424d6822 100644 --- a/app/views/modal/model_modal.coffee +++ b/app/views/modal/model_modal.coffee @@ -6,6 +6,8 @@ module.exports = class ModelModal extends View template: template plain: true + events: 'click .save-model': 'onSaveModel' + constructor: (options) -> super options @models = options.models @@ -20,6 +22,7 @@ module.exports = class ModelModal extends View afterRender: -> return unless @supermodel.finished() + @modelTreemas = {} for model in @models data = $.extend true, {}, model.attributes schema = $.extend true, {}, model.schema() @@ -31,6 +34,7 @@ module.exports = class ModelModal extends View modelTreema?.build() modelTreema?.open() @openTastyTreemas modelTreema, model + @modelTreemas[model.id] = modelTreema openTastyTreemas: (modelTreema, model) -> # To save on quick inspection, let's auto-open the properties we're most likely to want to see. @@ -45,3 +49,26 @@ module.exports = class ModelModal extends View }[team] for dessert in desserts child.childrenTreemas[dessert]?.open() + + onSaveModel: (e) -> + container = $(e.target).closest('.model-container') + model = _.find @models, id: container.data('model-id') + treema = @modelTreemas[model.id] + changes = {} + for key, val of treema.data when not _.isEqual val, model.get key + console.log "Updating", key, "from", model.get(key), "to", val + model.set key, val + changes[key] = val + for key, val of model.attributes when treema.get(key) is undefined and not _.string.startsWith key, '_' + console.log "Deleting", key, "which was", val, "but man, that ain't going to work, now is it?" + #changes[key] = undefined + model.unset key + if errors = model.validate() + return console.warn model, "failed validation with errors:", errors + res = model.save changes, {patch: true} + res.error => + return if @destroyed + console.error model, "failed to save with error:", res.responseText + res.success (model, response, options) => + return if @destroyed + @hide() diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee index a9e0dc106..af797f614 100644 --- a/server/users/user_handler.coffee +++ b/server/users/user_handler.coffee @@ -24,7 +24,7 @@ candidateProperties = [ UserHandler = class UserHandler extends Handler modelClass: User - + jsonSchema: schema editableProperties: [ 'name', 'photoURL', 'password', 'anonymous', 'wizardColor1', 'volume', 'firstName', 'lastName', 'gender', 'facebookID', 'gplusID', 'emails', @@ -32,15 +32,11 @@ UserHandler = class UserHandler extends Handler 'wizard', 'aceConfig', 'autocastDelay', 'lastLevel', 'jobProfile' ] - jsonSchema: schema - - constructor: -> - super(arguments...) - @editableProperties.push('permissions') unless config.isProduction - getEditableProperties: (req, document) -> props = super req, document - props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin() + props.push 'permissions' unless config.isProduction + props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin() # Admins naturally edit these + props.push privateProperties... if req.user.isAdmin() # Admins are mad with power props formatEntity: (req, document) -> @@ -344,7 +340,7 @@ UserHandler = class UserHandler extends Handler getEmployers: (req, res) -> return @sendUnauthorizedError(res) unless req.user.isAdmin() - query = {employerAt: {$exists: true}} + query = {employerAt: {$exists: true, $ne: ''}} selection = 'name firstName lastName email activity signedEmployerAgreement photoURL employerAt' User.find(query).select(selection).lean().exec (err, documents) => return @sendDatabaseError res, err if err From 59654da24bbce81cdafa0d6232bc184fb0179cff Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 17 Jun 2014 22:17:44 -0700 Subject: [PATCH 25/25] Starting more work on integrating additional languages. Improved Component code editing performance with a trusty debounce. --- app/views/editor/level/component/edit.coffee | 2 ++ .../editor/level/components_tab_view.coffee | 4 +-- app/views/play/level/tome/spell.coffee | 32 ++++++++++++------- app/views/play/level/tome/spell_view.coffee | 7 ++-- 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/app/views/editor/level/component/edit.coffee b/app/views/editor/level/component/edit.coffee index 3bc526813..856f79a4d 100644 --- a/app/views/editor/level/component/edit.coffee +++ b/app/views/editor/level/component/edit.coffee @@ -25,6 +25,7 @@ module.exports = class LevelComponentEditView extends View super options @levelComponent = @supermodel.getModelByOriginalAndMajorVersion LevelComponent, options.original, options.majorVersion or 0 console.log "Couldn't get levelComponent for", options, "from", @supermodel.models unless @levelComponent + @onEditorChange = _.debounce @onEditorChange, 1500 getRenderData: (context={}) -> context = super(context) @@ -95,6 +96,7 @@ module.exports = class LevelComponentEditView extends View @editor.on('change', @onEditorChange) onEditorChange: => + return if @destroyed @levelComponent.set 'code', @editor.getValue() Backbone.Mediator.publish 'level-component-edited', levelComponent: @levelComponent null diff --git a/app/views/editor/level/components_tab_view.coffee b/app/views/editor/level/components_tab_view.coffee index a0fe32f94..0ae4a3873 100644 --- a/app/views/editor/level/components_tab_view.coffee +++ b/app/views/editor/level/components_tab_view.coffee @@ -35,13 +35,13 @@ module.exports = class ComponentsTabView extends View componentModels = @supermodel.getModels LevelComponent componentModelMap = {} - componentModelMap[comp.get('original')] = comp for comp in componentModels + componentModelMap[comp.get('original')] = comp for comp in componentModels components = ({original: key.split('.')[0], majorVersion: parseInt(key.split('.')[1], 10), thangs: value, count: value.length} for key, value of @presentComponents) treemaData = _.sortBy components, (comp) -> comp = componentModelMap[comp.original] res = [comp.get('system'), comp.get('name')] return res - + treemaOptions = supermodel: @supermodel schema: {type: 'array', items: {type: 'object', format: 'level-component'}} diff --git a/app/views/play/level/tome/spell.coffee b/app/views/play/level/tome/spell.coffee index 7c796482d..5e3501014 100644 --- a/app/views/play/level/tome/spell.coffee +++ b/app/views/play/level/tome/spell.coffee @@ -18,23 +18,19 @@ module.exports = class Spell @supermodel = options.supermodel @skipProtectAPI = options.skipProtectAPI @worker = options.worker - p = options.programmableMethod + p = options.programmableMethod + @languages = p.languages ? {} + @languages.javascript ?= p.source @name = p.name @permissions = read: p.permissions?.read ? [], readwrite: p.permissions?.readwrite ? [] # teams - teamSpells = @session.get('teamSpells') - team = @session.get('team') ? 'humans' - @useTranspiledCode = @permissions.readwrite.length and ((teamSpells and not _.contains(teamSpells[team], @spellKey)) or (@session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions'))) or @spectateView) - if @useTranspiledCode - console.log "#{@spellKey} is using transpiled code because permissions.readwrite is #{@permissions.readwrite} - #{if @spectateView then ', we are spectating' else ''} - #{if teamSpells and not _.contains(teamSpells[team], @spellKey) then ', teamSpells[' + team + '] does not have ' + @spellKey + ' (just ' + teamSpells[team] + ')' else ''} - #{if @session.get('creator') isnt me.id then ', and the session was created by ' + @session.get('creator') + ' but I am ' + me.id else ''}" - @source = @originalSource = p.source + @setLanguage if @canWrite() then options.language else 'javascript' + @useTranspiledCode = @shouldUseTranspiledCode() + + @source = @originalSource @parameters = p.parameters if @permissions.readwrite.length and sessionSource = @session.getSourceFor(@spellKey) @source = sessionSource - @language = if @canWrite() then options.language else 'javascript' @thangs = {} @view = new SpellView {spell: @, session: @session, worker: @worker} @view.render() # Get it ready and code loaded in advance @@ -49,6 +45,9 @@ module.exports = class Spell @thangs = null @worker = null + setLanguage: (@language) -> + @originalSource = @languages[language] ? @languages.javascript + addThang: (thang) -> if @thangs[thang.id] @thangs[thang.id].thang = thang @@ -155,3 +154,14 @@ module.exports = class Spell toString: -> "" + + shouldUseTranspiledCode: -> + # Determine whether this code has already been transpiled, or whether it's raw source needing transpilation. + return false unless @permissions.readwrite.length # Only player-writable code will be stored transpiled. + return true if @spectateView # Use transpiled code for both teams if we're just spectating. + teamSpells = @session.get('teamSpells') + team = @session.get('team') ? 'humans' + return true if teamSpells and not _.contains(teamSpells[team], @spellKey) # Use transpiled for enemy spells. + # Players without permissions can't view the raw code. + return true if @session.get('creator') isnt me.id and not (me.isAdmin() or 'employer' in me.get('permissions')) + false diff --git a/app/views/play/level/tome/spell_view.coffee b/app/views/play/level/tome/spell_view.coffee index 9933033d0..3c69d79c2 100644 --- a/app/views/play/level/tome/spell_view.coffee +++ b/app/views/play/level/tome/spell_view.coffee @@ -619,8 +619,11 @@ module.exports = class SpellView extends View @ace.setKeyboardHandler @keyBindings[aceConfig.keyBindings ? 'default'] onChangeLanguage: (e) -> - if @spell.canWrite() - @aceSession.setMode @editModes[e.language] + return unless @spell.canWrite() + @aceSession.setMode @editModes[e.language] + wasDefault = @getSource() is @spell.originalSource + @spell.setLanguage e.language + @reloadCode true if wasDefault dismiss: -> @spell.hasChangedSignificantly @getSource(), null, (hasChanged) =>