Compare commits

...
This repository has been archived on 2025-05-04. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.

513 commits

Author SHA1 Message Date
Scott Erickson
b1dec7b6dd Install coveralls 2016-09-23 10:32:44 -07:00
Matt Lott
f73f1bd196 Update inbound lead assignment ratios 2016-09-23 09:37:13 -07:00
Scott Erickson
21fcc01cca Add istanbul coverage npm script 2016-09-21 15:56:56 -07:00
Scott Erickson
831bc38573 Remove test focus 2016-09-21 15:29:43 -07:00
Rob
72d44ca5fb Change slack message to point to LogDNA instead of paper trail. 2016-09-21 14:45:51 -07:00
Rob
8d4d617069 Merge branch 'master' of github.com:codecombat/codecombat 2016-09-21 14:43:51 -07:00
Matt Lott
9c83418812 🐛Fix inbound sales automation double callback 2016-09-21 10:53:04 -07:00
Matt Lott
8fffc31fdc Change inbound lead past range from 3 to 10 days 2016-09-21 10:45:56 -07:00
Matt Lott
062990851a Contact button opens Intercom for teachers 2016-09-21 10:19:52 -07:00
Matt Lott
f1b1cdbdcc Trim mixpanel event logging 2016-09-21 06:17:37 -07:00
Robin Yang
a16deb9059 Update Jobs, Privacy, FAQ links in footer 2016-09-20 16:31:28 -07:00
Scott Erickson
6f6c3406ac Refactor smoke test, add a second one 2016-09-20 15:07:59 -07:00
Matt Lott
12baae7acd 🐛Fix sales lead assignments
Lead assignments can be incorrect when the user requests licenses
needed via the contact form before the related emails have been added
to the Close.io lead.

We’ll use a custom field for automation sales contact if there’s no
email activity.
2016-09-20 14:59:51 -07:00
Robin Yang
86d54d78c0 Remove Curriculum Specialist Role on /about 2016-09-20 14:10:52 -07:00
Nick Winter
7453dafa63 Move Dutch translations to nl.coffee and leave only nl-BE deviations 2016-09-20 12:49:06 -07:00
Nick Winter
b8296c67a1 Avoid skimmers thinking we are teaching Microsoft Excel 2016-09-20 12:17:39 -07:00
Matt Lott
3d7b055bee 🐛Email new contacts on old EU lead from correct email 2016-09-20 05:55:18 -07:00
Matt Lott
db4cc18dcd Switch inbound sales automation to be contact-based
Instead of lead-based.  I.e. process trial request emails individually,
in serial.  This should fix a number of bugs we’ve seen recently.
Find previous via email query should be more accurate.
Send new contact email should be more accurate.
Process coco contacts in series to avoid race condition bugs when two
contacts belong on the same eventual Close lead.
2016-09-19 16:58:39 -07:00
Rob
6ba11f3f14 Maybe fix webdev in BR 2016-09-19 16:39:47 -07:00
Scott Erickson
1ac40a94e7 Add initial nightwatch smoke test, package commands 2016-09-19 16:18:40 -07:00
Matt Lott
951db5a721 🐛Fix course ordering and furthest logic in dashboard
Also speeding up server APIs via lean() db calls
2016-09-19 13:17:02 -07:00
Scott Erickson
7efccfeac4 Tweak fix-patch-originals script 2016-09-19 09:51:04 -07:00
Scott Erickson
4c277a1b1a Unfocus server tests, comment out inconsistent test 2016-09-19 09:13:22 -07:00
Adam Csillag
526e6e741e Update hu.coffee ()
LogMeIn and espell's translation of all untranslated strings
2016-09-19 08:10:21 -07:00
Scott Erickson
be1c1323ac Patch fixes
* Partially revert GET /db/:collection/:handle/patches, as it returned limited results for versioned docs
* Fix POST /db/:collection/:handle/patch to:
   * normalize diffs based on latest doc version
   * handle empty deltas
   * not swallow thrown errors due to _.curry
   * Set 'target.original' correctly for versioned collections
2016-09-16 16:02:56 -07:00
Scott Erickson
1b314b7d14 Add script for calculating how many translations we have 2016-09-16 16:02:32 -07:00
Nick Winter
91cb621f96 Don't load Stripe Checkout for anonymous users (saves 30 requests and a warning message) 2016-09-16 11:04:31 -07:00
Nick Winter
6b75fb0a76 Use old China server behavior for users who have paid in the past but aren't currently subscribed 2016-09-16 10:49:05 -07:00
Nick Winter
ea7b94bba1 Working on some logging for a ladder load issue 2016-09-15 22:29:36 -07:00
Nick Winter
c67f1cddda Don't print our footer when printing curriculum guides 2016-09-15 20:28:23 -07:00
Matt Lott
ed69afc5d1 🐛Remove empty needed string from /teachers/licenses 2016-09-15 16:49:09 -07:00
Matt Lott
2fded40e99 🐛Fix sales followup email matching
Was not handling Close.io lead contacts with multiple email addresses,
and sometimes contacts reply to one email from a different one.
2016-09-15 16:33:20 -07:00
Nick Winter
5a4be88887 Named CS2 instructive activity 2016-09-15 13:20:22 -07:00
Nick Winter
77c16000e9 Add events module to CS2 curriculum plan. 2016-09-15 13:16:09 -07:00
ksuszka
fbe3733e5f Fixed wrong message for wrong password. () 2016-09-15 15:17:11 +01:00
Matt Lott
860ac8894b 🐛Fix delighted trial request test 2016-09-14 12:43:39 -07:00
Matt Lott
c92f2e152e Increase course trial request delighted delay to 10 days 2016-09-14 11:50:25 -07:00
Matt Lott
453aa1d0fb Show course description if no concepts on homepage 2016-09-14 11:11:01 -07:00
Matt Lott
7d38e9a06c 🐛Fix resource hub anonymous blurb centering 2016-09-14 10:37:38 -07:00
Matt Lott
42ed2ffde1 Remove student goals from game dev player view 2016-09-14 10:35:12 -07:00
Matt Lott
2be6a97dbd Unlock 1st part of resource hub for anonymous 2016-09-14 10:00:54 -07:00
Imperadeiro98
509df9a372 Some fixes to pt-BR 2016-09-14 15:01:22 +01:00
Matt Lott
0eae43c2e8 Add play level links to classroom levels admin page 2016-09-14 07:00:22 -07:00
Abimael Alcebíades
5b71a9bdb0 Update pt-BR.coffee () 2016-09-14 14:59:57 +01:00
Justinschut
eb77135b38 Update nl-NL.coffee ()
Some more translations for the dutch language.
2016-09-14 14:59:26 +01:00
Matt Lott
cf34d1c935 No cache IE/Edge ajax db GETs via server response
Closes 
2016-09-14 06:06:32 -07:00
Nick Winter
5c4e9ca417 Finished CS3 curriculum guide 2016-09-13 16:12:23 -07:00
Rob
2a6e186c98 Also make redirect_uri configurable. 2016-09-13 15:33:58 -07:00
Phoenix Eliot
327474e53c Fix Brunch complaining about fonts' bower.json files
Before this, Brunch would try to copy bower.json from those folders into our /fonts folder. Because there were two bower.json files, Brunch would always give this error:
`Error: ENOENT: no such file or directory, unlink '~/work/codecombat/public/fonts/bower.json'`

This fix just ignores those bower.json files instead of trying to put them in /public/fonts.
2016-09-13 15:23:54 -07:00
Rob
8359c95489 Initial draft of clever integration. 2016-09-13 15:18:19 -07:00
Rob
6e2e3d5cb7 Check if searching for an id 2016-09-13 11:49:18 -07:00
Rob
61bf5824ac Query sphinx search if configured. 2016-09-13 11:48:49 -07:00
Nick Winter
9702c91127 Add first few modules for CS3 curriculum 2016-09-12 18:26:41 -07:00
Nick Winter
d7f4ef6809 Updates to CS 2 curriculum; stub for CS 3 curriculum modules 2016-09-12 16:26:59 -07:00
Thekiddiejsandpython
9f6d7c4932 Change Ipad message, fr.coffee update () 2016-09-12 23:50:18 +01:00
Thekiddiejsandpython
0db0409aa7 Update fr.coffee () 2016-09-12 23:21:12 +01:00
Justinschut
6c4d362210 Update nl-NL.coffee () 2016-09-12 23:19:29 +01:00
Matt Lott
318ec778d5 🐛Fix IE11 Array.find combat bug
Array.find not supported yet
2016-09-12 15:02:31 -07:00
Scott Erickson
a2e8badb6f Fix tests when running them as a user who has earned something 2016-09-12 10:57:17 -07:00
Matt Lott
9ccca9a5a3 🐛Remove duplicate test utils.makeLevelSession 2016-09-12 10:54:03 -07:00
Scott Erickson
71a63709e7 Restrict server jasmine to 2.4.1 2016-09-12 10:44:07 -07:00
Matt Lott
b803080bed Only return member sessions for assigned courses
Closes 
2016-09-12 09:24:20 -07:00
Matt Lott
346c29361b Redirect students away from /teachers and vice versa
Closes 
2016-09-12 06:28:42 -07:00
Matt Lott
ed8e36f5cd Put student pages under /students
Closes 
2016-09-12 06:20:59 -07:00
Matt Lott
9f93c0ed18 Restrict resource hub access
Closes 
2016-09-12 05:39:28 -07:00
Nick Winter
c86c3c4628 Fix session loading after all other dependencies and hanging the level load 2016-09-11 09:16:50 -07:00
Robin Yang
4eed50f92a Add Curriculum Specialist role to /about 2016-09-09 17:09:45 -07:00
Scott Erickson
6ac854c40a Add update classroom buttons to AdministerUserModal 2016-09-09 16:05:03 -07:00
Robin Yang
cdda8855b5 Fix homepage well button 2016-09-09 15:26:25 -07:00
Nick Winter
5d8569480b Separate print and web styles for course guides 2016-09-09 15:12:28 -07:00
Phoenix Eliot
c24e81aebb Redirect with same protocol as request
Try using // instead of explicit protocol
2016-09-09 14:59:41 -07:00
Nick Winter
bbe4a0540d Fix solution template problems breaking course guides, and list those broken solution templates in /artisans/solution-problems 2016-09-09 14:41:32 -07:00
Scott Erickson
7bab895dee Generalize new I18N view system
Previously, when diplomats submit translations, the system
would try to figure out whether it should be a 'patch' or a 'change',
and then would either create a patch for an admin or artisan to
review and accept or reject, or would apply the changes immediately
and they would be live. This was done as a compromise between
getting translations live quickly, but also preventing already-translated
text from getting overwritten without oversight.

But having the client handle this added logical complexity. So
this makes all diplomats submit patches, no matter what. The server
is then in charge of deciding if it should auto-accept the patch or not.
Either way, a patch is created.

There was also much refactoring. This commit includes:

* Update jsondiffpatch so changes within array items are handled correctly
* Refactor posting patches to use the new auto-accepting logic, and out of Patch model
* Refactor POST /db/patch/:handle/status so that it doesn't rely on handlers
* Refactor patch stat handling to ensure auto-accepted patches are counted
* Refactor User.incrementStat to use mongodb update commands, to avoid race conditions
* Refactor Patch tests
2016-09-09 10:59:26 -07:00
Imperadeiro98
e70556e900 Change pt-BR description 2016-09-09 15:01:58 +01:00
Rob
ffb4dd6a54 Dang HTTPS 2016-09-08 16:02:39 -07:00
Imperadeiro98
baa916e37a Update i18n ()
* Update i18n
* Change pt-BR description
* Remove dead code from HomeView
2016-09-08 22:48:28 +01:00
Rob Blanckaert
af3e069828 Merge pull request from codecombat/filter-domains
Code Review: Filter domains for webdev iFrame
2016-09-08 17:46:00 -04:00
Robin Yang
e79b76818b Change Lisa's title 2016-09-08 12:43:57 -07:00
Robin Yang
1ec76050e2 Add link to /community on /about 2016-09-08 11:03:36 -07:00
Robin Yang
0219844c9d Increase legibility of Play Now
Overrides $gold hex.
2016-09-08 10:56:18 -07:00
Scott Erickson
1fb7996f6c Fix EarnedAchievement.upsertFor to handle achievements that had no rewards 2016-09-08 10:32:59 -07:00
Phoenix Eliot
00d6d588ec Update sales lead portioning 2016-09-07 16:11:12 -07:00
Robin Yang
bab9d0b944 Merge pull request from codecombat/fix-homepage-well
Modify Homepage Well Behavior
2016-09-07 15:50:22 -07:00
Phoenix Eliot
d134047293 Fix algolia inputs in teacher signup form 2016-09-07 15:26:38 -07:00
Robin Yang
ea44d87e91 Add Resource Hub to logged-in teacher homepage well 2016-09-07 14:36:03 -07:00
Robin Yang
f0eae0eb38 Rename "View Progress" button for logged in student 2016-09-07 14:13:19 -07:00
Robin Yang
547b2a2b50 Use broadName for homepage well instead of email 2016-09-07 14:05:44 -07:00
Rob Blanckaert
9619ff5a0a Try harder to find the ip address, and dont split the ipaddress if we didnt find it. 2016-09-07 13:46:45 -07:00
Phoenix Eliot
963b82eadd Fix more query variable getting 2016-09-07 13:44:26 -07:00
Phoenix Eliot
32b56eacb8 Fix getting query parameters in CourseVictoryModal 2016-09-07 13:42:27 -07:00
Phoenix Eliot
e29fe7e761 Add CourseInstance ID to back-to-ladder link 2016-09-07 11:43:09 -07:00
Scott Erickson
15038026e2 Merge pull request from Imperadeiro98/add-sounds
Add opening/closing sounds to HintsView
2016-09-07 09:41:48 -07:00
Imperadeiro98
5922f0947b Add opening/closing sounds to HintsView 2016-09-07 14:13:29 +01:00
Phoenix Eliot
6cbc6452fc Actually do filter safe paths, but allow any other domain 2016-09-06 17:10:58 -07:00
Phoenix Eliot
b08c1af038 Simplify logic now that we're not redirecting safe paths 2016-09-06 16:24:40 -07:00
Phoenix Eliot
bdabee865c Filter domains for webdev iFrame
This serves the web-dev surface iFrame from another domain, such that user-created levels can't sniff cookies from a visitor to their page. It forces a redirect if a path is accesses through the wrong domain.

Use ENV variables for hostnames

Allow messages from all relevant domains

Use the right iFrame URL for different domains

Let the load balancer check /healthcheck

Add special handling for china server

Generalize subdomain handling
2016-09-06 16:17:38 -07:00
Scott Erickson
f29f4cb82b Do not fetch sessions for course instances that do not have classrooms 2016-09-06 12:43:06 -07:00
Scott Erickson
914dd16530 Merge pull request from codecombat/achievement-updates
Fix, update and enable achievement updates
2016-09-06 10:52:55 -07:00
Scott Erickson
9088f98eae Add function to properties that trigger achievement updates 2016-09-06 10:32:54 -07:00
Scott Erickson
2fe28852b4 More achievement tweaks
* Clients check updated achievements as well as new ones
* Clients do not wait to keep checking
* Update achievement points along with everything else in EarnedAchievement.upsertFor
* Fix various bugs
2016-09-06 09:37:02 -07:00
Matt Lott
d223143557 List /teachers/classes classes most recent first 2016-09-03 11:11:09 -07:00
Matt Lott
0316cc507e 🐛Exclude practice levels from course progress counts 2016-09-03 11:11:09 -07:00
Rob
9888bb7cef Display info when connected to analytics mongo like we do for LS and main. 2016-09-02 16:00:50 -07:00
Rob
888d43fc5c Change how we connect to the analytics database. 2016-09-02 15:57:26 -07:00
Matt Lott
3dcfa2cc8a A/b test default language in home product 2016-09-02 15:33:29 -07:00
ksuszka
8dacc619a0 Fixed a few spelling erros and added a few lines to polish translation. ()
* Translated a few new lines and corrected a few spelling erros.

* Fixed spelling.

* Fixed spelling.
2016-09-02 23:03:44 +01:00
Scott Erickson
2696effec8 Add time to realtime keyboard events 2016-09-02 12:02:43 -07:00
Matt Lott
99a148a136 bug:Fix /courses using outdated level slugs
Closes 
2016-09-02 11:50:14 -07:00
Robin Yang
60f5e88280 Recrop team photos 2016-09-02 11:31:53 -07:00
JurianLock
18d23a1e4b Update nl-NL.coffee ()
Just a couple of upgrades.
2016-09-02 19:20:12 +01:00
Robin Yang
f2dbdce3b8 Merge pull request from codecombat/new-team-headshots
Add Hover behavior to Team Photos
2016-09-02 11:14:51 -07:00
Robin Yang
d7b4cadd29 Standardize avatar sizes 2016-09-02 11:06:43 -07:00
Robin Yang
af91c299a4 Update img urls 2016-09-02 10:58:00 -07:00
Robin Yang
2df6ef9f0f Add Hover behavior to Team Photos
Also reorganized image folders
2016-09-02 10:51:07 -07:00
Matt Lott
cb096b5727 🐛Fix close leads country code email routing 2016-09-02 10:47:06 -07:00
Robin Yang
7c085f73f4 Update Team Headshot Photos 2016-09-02 10:00:17 -07:00
themaka
8f20ccaed6 Add headshot thumbnails of full-time team 2016-09-01 16:39:08 -07:00
Phoenix Eliot
8780f335c7 Fix adjusting for whitespace in playercode 2016-09-01 15:57:59 -07:00
Robin Yang
6f1019878e Add Pair Programming Activity 2016-09-01 15:45:04 -07:00
Robin Yang
099781c513 Merge pull request from codecombat/course-curriculum
Update CS2 Guide
2016-09-01 15:08:23 -07:00
Robin Yang
9c31b21293 Update CS2 Guide
Changes to course progression/levels reflected in updated guide,
properties and input handling moved to CS3.
2016-09-01 14:52:59 -07:00
Scott Erickson
b02c1efa07 Re-disable CreateAccountModal checks; Travis still breaks on them 2016-09-01 14:15:38 -07:00
Scott Erickson
ed130e0c0e Fix Student and Teacher signup forms firstName, lastName inputs 2016-09-01 14:06:33 -07:00
Scott Erickson
f3254f1061 Change NewHomeView to HomeView 2016-09-01 10:41:43 -07:00
Scott Erickson
77e45072a5 Remove justPlayCourses heuristic, replace with isStudent, remove old HomeView 2016-09-01 10:41:43 -07:00
Matt Lott
e43d90ae23 🐛Fix auto level solutions numbering
Practice levels were being numbered normally.
2016-08-31 17:34:15 -07:00
Matt Lott
df69cb54a8 🐛/courses wd2 Start button goes to wrong level
We can’t cache course instance sessions by course because of web dev
primer levels and classroom versioning.
2016-08-31 17:04:43 -07:00
Matt Lott
527ba277ab Order /courses classrooms most recent first 2016-08-31 17:04:43 -07:00
Imperadeiro98
dfdb57e18f Fix {change} in en.coffee 2016-08-31 22:50:40 +01:00
Phoenix Eliot
e0dafc571a Fix making Problems from AetherProblems with no range 2016-08-31 14:35:50 -07:00
Phoenix Eliot
663c220eaf Show wev-dev iFrame error messages like Aether's
This heavily refactors SpellView and adds infrastructure for receiving and reporting Errors raised by the web-dev iFrame. The web-dev error system, the Aether error system, and the Ace html-worker avoid disturbing each others' errors/annotations (though currently Aether+web-dev errors won't coexist), and they clear/update their own asynchronously.

Show web-dev iFrame errors as Ace annotations

Add functional error banners (with poor messages)

Improve error banners, don't allow duplicate Problems

Refactor setAnnotations override

Convert all constructor calls for Problems

Add comments, clean up

Clean up

Don't clear things unnecessarily

Clean up error message sending from iFrame

Add web-dev:error schema

Clarify error message attributes

Refactor displaying AetherProblems

Refactor displaying user problem banners

Refactor onWebDevError

Set ace styles on updating @problems

Clean up, fix off-by-1 error

Add comment

Show stale web-dev errors differently
Some web-dev errors are generated by "stale" code — code that's still running in the iFrame but doesn't have the player's recent changes.
This shows those errors differently than if they weren't "stale", and suggests they re-run their code.

Hook up web-dev event schema

Destroy ignored duplicate problems

Functionalize a bit of stuff

Fix ProblemAlertView never loading
2016-08-31 10:59:06 -07:00
Scott Erickson
273845ce2e Fix POST /db/earned_achievement to be accessible to anonymous users 2016-08-31 10:07:19 -07:00
Scott Erickson
956ff3300b Fix POST /db/level/:names 2016-08-31 09:56:41 -07:00
Matt Lott
a7d51d672a Remove email activities from close lead export script 2016-08-31 08:04:58 -07:00
Matt Lott
275f708041 🐛Fix campaign editor next level arrows 2016-08-30 16:19:41 -07:00
Scott Erickson
9e24670993 Tweak README wording 2016-08-30 16:17:58 -07:00
Scott Erickson
f57ec17261 Fix play buttons not appearing sometimes on navigation
Repro steps:

* Go to a ladder page
* Click to play
* Click link back to ladder
* Before, the play buttons would not appear, now they do.
2016-08-30 16:05:31 -07:00
Matt Lott
3387e5ee34 🐛Don't overwrite close.io nces custom field data 2016-08-30 15:32:26 -07:00
Scott Erickson
652c5237aa Fix updateI18NCoverage, handles i18n objects with nothing to translate, fix 2016-08-30 14:17:40 -07:00
Scott Erickson
be11c47e17 Have campaign-view translate campaign name normally 2016-08-30 10:49:48 -07:00
Robin Yang
a168ee905c Merge pull request from codecombat/homepage-updates
Remove Java from homepage, Update languages
2016-08-30 09:48:12 -07:00
Matt Lott
ee3ce24d6c 🐛Fix email case check in followup leads script 2016-08-30 09:21:50 -07:00
Robin Yang
c4dcf0f756 Remove Java from homepage, Update languages 2016-08-29 18:13:07 -07:00
Robin Yang
79415fc99d Merge pull request from codecombat/course-curriculum
Add Getting Started guide in Markdown
2016-08-29 17:09:32 -07:00
Robin Yang
5554a733a9 Merge pull request from codecombat/update-footer
Replace Educator Wiki footer link with Resource Hub
2016-08-29 17:08:59 -07:00
Robin Yang
3cdec61fe1 Add Getting Started guide in Markdown
- Replace PDF link on /teachers/resources
- Add .pngs
2016-08-29 17:06:05 -07:00
Scott Erickson
cf9d082ffa Have client check for achievement updates 2016-08-29 16:32:42 -07:00
Scott Erickson
139efe4cf7 Implement POST /db/user/:handle/check-for-new-achievement, couple tweaks
* Enforce being logged in for POST /db/earned_achievement
* Extend timeout for race condition user tests
2016-08-29 14:53:51 -07:00
Scott Erickson
f509c95a4b Refactor POST /db/earned_achievement 2016-08-29 14:53:36 -07:00
Scott Erickson
5c8b8832b3 Refactor and better test EarnedAchievement.createForAchievement 2016-08-29 14:52:50 -07:00
Scott Erickson
2f8ad4afdf Prevent email-formatted usernames on teacher signup 2016-08-29 14:16:15 -07:00
Matt Lott
0b3c7d3189 Don't overwrite Close.io Lead Origin custom field 2016-08-29 11:58:00 -07:00
Robin Yang
0d5185dca5 Replace Educator Wiki footer link with Resource Hub 2016-08-29 11:32:40 -07:00
Robin Yang
bd87783631 Merge pull request from codecombat/about-page-language-logos
Change programming language listing on About page
2016-08-29 09:30:03 -07:00
Robin Yang
c7c3eff0bd Merge pull request from codecombat/course-curriculum
Update formatting on CS2 Guide
2016-08-29 09:29:57 -07:00
Scott Erickson
8d582082a7 Merge pull request from Imperadeiro98/remove-minicolors
Remove jQuery minicolors
2016-08-29 09:09:15 -07:00
Matt Lott
d20600b381 Update campaign next level algorithm for practice levels
Don’t show not-started unlocked levels if previous incomplete practice
level is available
Yellow arrow points at adventurer levels too now

Closes 
2016-08-29 06:26:40 -07:00
Imperadeiro98
dd6f862f70 Remove minicolors image 2016-08-28 23:14:25 +01:00
Imperadeiro98
3b29aae616 Remove jQuery minicolors 2016-08-27 23:03:25 +01:00
Yuki Ueda
8bef580909 Update ja.coffee () 2016-08-27 12:13:30 +01:00
Imperadeiro98
cc2bf8ebd9 Some fixes to pt-BR 2016-08-27 12:12:55 +01:00
Abimael Alcebíades
7a2911951c Update pt-BR.coffee ()
* Update pt-BR.coffee

translations updated

* Update pt-BR.coffee

Updated translations
2016-08-27 12:11:14 +01:00
Robin Yang
d6d8d84722 Update formatting on CS2 Guide 2016-08-26 16:53:28 -07:00
Matt Lott
a700a00c94 Autocomplete game.
And don’t create duplicate entries for hero. autocompletes

Closes 
2016-08-26 15:10:22 -07:00
Matt Lott
b8d4e51331 Add line endings and newlines for fuzzy autocompletes
Closes 
2016-08-26 10:32:56 -07:00
Josh Callebaut
e54cd7c05f Merge pull request from Zerrien/intro-view
View for tracking Intro/Overview guide issues
2016-08-25 16:53:38 -07:00
Josh Callebaut
ff00091408 Tweaks based on CR from Rob 2016-08-25 15:52:49 -07:00
Josh Callebaut
cb41100d2e Style-fix quotes and EOD newlines 2016-08-25 15:37:49 -07:00
Josh Callebaut
53e68a79a6 Add a page for tracking intro/guide issues 2016-08-25 15:31:24 -07:00
Matt Lott
9553238a64 🐛Fix adjacent campaign map arrows 2016-08-25 10:40:07 -07:00
Josh Callebaut
f5770c3a2a Merge pull request from Zerrien/adv-color-2
Adventurer levels lose star and become purple
2016-08-25 10:38:27 -07:00
Scott Erickson
ae82875c57 Refactor post new level version handler, add failed save handling
When a new version is created, the latest version is updated, then
the new one is made. If making a new one fails (most commonly due to
a name conflict), the latest version is left in a broken state. Set up
the new middleware to revert changes to latest version in this case,
and update the level handler to use the middleware. Also added
warning logs if models do not have editableProperties or postEditableProperties
set.
2016-08-25 10:28:46 -07:00
Matt Lott
76b1efdefb Update Close lead creation when have NCES data 2016-08-25 10:21:39 -07:00
Nick Winter
878dcbdc49 Better handling of non-user-code problems in game-dev mode. Don't simulate whole level first time a game-dev level is loaded. 2016-08-24 15:21:09 -07:00
Matt Lott
80f8fd60d8 Update close automated emails 2016-08-24 14:46:15 -07:00
Josh Callebaut
18e985a845 No longer display star on adventurer levels and make all adventurer levels purple 2016-08-24 14:35:26 -07:00
Matt Lott
d85cfcaddf Update zp -> close import script to be more forgiving 2016-08-24 14:33:47 -07:00
Robin Yang
8435789691 Change programming language listing on About page 2016-08-24 14:02:03 -07:00
Matt Lott
f391030b44 Add hints test group distinction comments 2016-08-24 12:05:40 -07:00
Matt Lott
22e3f445c0 Add achievement hidden property 2016-08-24 07:37:11 -07:00
Rob Blanckaert
d9b688c5b8 Merge pull request from codecombat/course-curriculum
Create Markdown for CS1, CS2 Curriculum
2016-08-23 18:16:19 -07:00
Rob
5c6fb20d7f Add back to top buttons. 2016-08-23 18:12:44 -07:00
Scott Erickson
b175c02714 Fix article test to handle patches endpoint change 2016-08-23 16:19:25 -07:00
Robin Yang
b550aae76a Remove target grades from Curriculum 2016-08-23 16:07:10 -07:00
Robin Yang
c3b3c6ea2a Update Student License requirement copy 2016-08-23 16:05:16 -07:00
Nick Winter
bfa49cbbc6 Fix sending too much and not enough data on arena sessions 2016-08-23 16:02:03 -07:00
Matt Lott
e05536e1e4 Send exported Close.io leads script 2016-08-23 14:48:42 -07:00
Robin Yang
52c1f3d625 Add CS1, CS2 to Resource Hub
Also adding additional worksheets/PDFs.
2016-08-23 14:46:02 -07:00
Robin Yang
5d31496ceb Update CS1, CS2 curriculum content 2016-08-23 14:45:32 -07:00
Scott Erickson
09a9358b9e Modify courses i18n edit view to be less error-prone
* Do not use 'backup' system
* Warn when about to lose changes
* Show list of patches and their statuses
2016-08-23 14:36:45 -07:00
Scott Erickson
ae28223b96 Add course edits stats properties to user schema 2016-08-23 13:34:19 -07:00
Scott Erickson
7d9d42a23f Fix i18nCoverage bug
Sometimes, translations is undefined, so check that it exists
2016-08-23 13:33:51 -07:00
Scott Erickson
24848da7bf Fix scrolling on PlayGameDevLevelView 2016-08-23 12:50:50 -07:00
Phoenix Eliot
e929686502 Enable session spectating for WebDev levels 2016-08-23 11:48:02 -07:00
Phoenix Eliot
209b7d724e Fix webdev for china server 2016-08-23 11:08:57 -07:00
Scott Erickson
ef0547f72a Simplify applying licenses
In TeacherClassView, when a teacher assigns a paid course to any unenrolled
student, the view automatically enrolls those students, rather than requiring
the teacher to enroll those students manually first. Update copy throughout.

Also add back (smaller) padding to progress dots in TeacherClassView.
2016-08-23 10:43:31 -07:00
Matt Lott
c2ae4d3748 Add gd2 and wd2 to individual game
Closes 
2016-08-23 10:15:47 -07:00
Robin Yang
f8caa86579 Move resource templating from coffee to jade files 2016-08-23 09:38:11 -07:00
Scott Erickson
382304a256 Merge pull request from kdparkinson/master
Thang name contributions
2016-08-23 09:10:49 -07:00
Matt Lott
2584124017 Add array option to adjacentCampaigns.showIfUnlocked 2016-08-23 05:42:23 -07:00
Kenny Parkinson
2642dc896f Removed duplicate Anya 2016-08-22 19:14:55 -06:00
Robin Yang
fe3b9e01e0 Create Markdown for both CS1, CS2 Curriculum 2016-08-22 17:33:49 -07:00
Phoenix Eliot
3d8d0b96d3 Make popover pins stickier 2016-08-22 13:29:35 -07:00
Phoenix Eliot
b4fe0a3bee Fix SpellPaletteView's popover Ace 2016-08-22 13:23:52 -07:00
Phoenix Eliot
7f2d3d8b57 Fix iFrame being truncated 2016-08-22 12:01:48 -07:00
Scott Erickson
136d274d54 Render from partially-completed prerendered spritesheet correctly, fix 2016-08-22 11:45:43 -07:00
Matt Lott
0cef562d9b Update subscription dialog blurbs 2016-08-22 11:29:30 -07:00
Matt Lott
61be34a0f4 Sort courses in teacher ux
Closes 
2016-08-22 11:18:26 -07:00
Scott Erickson
cdba1788e7 Tweak username-should-not-be-email error message 2016-08-22 11:02:40 -07:00
Matt Lott
fe49867043 Don't key off slugs in updateCourses.js 2016-08-22 06:27:02 -07:00
Matt Lott
7c9f7cbce7 Show correct programming language in level intro 2016-08-22 06:11:01 -07:00
Matt Lott
f2597229b5 No autocomplete x y, rename Zatanna to Autocomplete
Don’t autocomplete common variables ’x’ or ‘y’:
return if /^x$|^y$/i.test(prefix)
Renaming zatanna to autocomplete

Closes 
2016-08-22 05:46:07 -07:00
Kenny Parkinson
c1e9730499 added klondike as name for Polar Bear Pet 2016-08-20 09:39:05 -06:00
Kenny Parkinson
023a0e9ca8 Added 'Anya' as a female pixie. 2016-08-20 09:17:21 -06:00
Rob Blanckaert
47e115a7eb Merge pull request from codecombat/static-teacher-pages
Add Resource Hub to teacher dashboard
2016-08-19 17:44:19 -07:00
Robin Yang
2b586420f3 Add i18n for Resource Hub 2016-08-19 17:42:02 -07:00
Robin Yang
611368f742 Add Resource Hub to teacher dashboard 2016-08-19 17:41:06 -07:00
Matt Lott
0cae331e2f Update gd2 and wd2 to released 2016-08-19 16:48:06 -07:00
Scott Erickson
c595183177 Remove teacher class view progress dot padding 2016-08-19 16:47:25 -07:00
Matt Lott
05159ff7c2 Support course slugs that match course names
Must be backwards compatible until we update the course slugs.

Closes 
2016-08-19 16:05:37 -07:00
Scott Erickson
acfe62a786 Merge pull request from codecombat/course-progress-projects
Add project/arena pills to course progress tab to TeacherClassView
2016-08-19 16:05:09 -07:00
Scott Erickson
aeddfe77d6 Add project/arena pills to course progress tab to TeacherClassView
Also remove templating for deprecated progress tab
2016-08-19 15:48:45 -07:00
Matt Lott
cfcda2bd00 🐛Update no students text in class view 2016-08-19 15:06:25 -07:00
Matt Lott
d392f7780e Force level primer language usage for students 2016-08-19 14:26:39 -07:00
Matt Lott
726ad525fa Remove add students from /teacher/classes
No longer make sense after add students dialog UI update.
2016-08-19 13:32:17 -07:00
Imperadeiro98
b21cfe55cb Fix i18n of cast-button-view.jade 2016-08-19 17:56:05 +01:00
Scott Erickson
dbf63e250c Add realtime keyboard events for game dev 2016-08-19 09:44:36 -07:00
Lucas P
de739bb6db Translation PT-BR ()
Just  an small update.
Will do more!
2016-08-19 17:06:00 +01:00
Matt Lott
b280e6ff79 Find invalid courses script 2016-08-18 18:36:31 -07:00
Matt Lott
d6d208803c Undefined check on homepage, update updateCourses.js 2016-08-18 18:36:31 -07:00
Phoenix Eliot
dc7c220b57 Add goal checking on user clicks 2016-08-18 16:40:48 -07:00
Matt Lott
fb0357e0f9 Remove duplicate course entries from updateCourses.js 2016-08-18 15:27:06 -07:00
Nick Winter
2f220eb007 Fix 2016-08-18 14:39:14 -07:00
Phoenix Eliot
c33f86768a Translate level wrapper code 2016-08-18 14:09:34 -07:00
Imperadeiro98
605169e792 Update pt-PT.coffee 2016-08-18 22:06:39 +01:00
Nick Winter
0364463f99 Propagate i18n 2016-08-18 13:50:34 -07:00
Nick Winter
37d94b8a1c Merge branch 'lazydogP-master' 2016-08-18 13:47:59 -07:00
Nick Winter
8fbf9983d5 Fix zh-HANS.coffee merge 2016-08-18 13:47:51 -07:00
Nick Winter
a5be5feb85 Merge branch 'master' of github.com:codecombat/codecombat 2016-08-18 13:41:15 -07:00
Nick Winter
1f9ecfc7a8 Merge branch 'dungkunit-patch-1' 2016-08-18 13:41:06 -07:00
Nick Winter
f749938800 Fix vi.coffee merge 2016-08-18 13:40:49 -07:00
Imperadeiro98
5dd746af4a Small change to select hero wording 2016-08-18 21:39:54 +01:00
Nick Winter
ee168e9313 Merge branch 'VilkkuV-patch-1' 2016-08-18 13:39:13 -07:00
Nick Winter
54d45cbfbf Fix fi.coffee merge 2016-08-18 13:39:03 -07:00
Scott Erickson
300c81e72b Course translations fixes
* Restrict patch handling properly
* Fix , CS 2 description
* i18nCoverage is updated when new translations are auto-accepted
* Course patches are listed on PendingPatchesView properly
* 'Artisan' permission allows editing course translations
2016-08-18 13:29:52 -07:00
Scott Erickson
d4af931e05 Add course (name, description) translations 2016-08-18 10:10:18 -07:00
Matt Lott
f61f14571f Remove CS: prefixes, add game-dev-2 course 2016-08-18 09:57:09 -07:00
Yuki Ueda
bd5a453b7f Update ja.coffee () 2016-08-18 17:32:46 +01:00
Matt Lott
720e7ea897 Update beta campaign world selector images 2016-08-17 21:14:11 -07:00
Matt Lott
f44821bfe1 🐛Fix admin student licenses range coloring 2016-08-17 21:08:23 -07:00
Phoenix Eliot
6b1adb9b31 Turn off emails to Josh about timeouts 2016-08-17 16:54:37 -07:00
Imperadeiro98
15b795b855 Update pt-PT.coffee 2016-08-17 22:32:41 +01:00
Matt Lott
46e05e893b Redirect students and teachers on sign in
Closes 
2016-08-17 14:17:16 -07:00
Matt Lott
b647252956 Update /courses UI 2016-08-17 14:15:52 -07:00
Nick Winter
01affc2054 Fix bug in premium level selection code 2016-08-17 12:05:31 -07:00
Matt Lott
b057168e61 Update add students dialog
Updating copy, removing everything except email inviting
2016-08-17 11:10:48 -07:00
Nick Winter
6e4cd57d44 Propagate i18n 2016-08-17 10:02:36 -07:00
Nick Winter
8ec17265dc Give China server all free levels but hide paid levels ()
* Give China server all free levels but hide paid levels

* Don't prompt to buy gmes on free-only server on insufficient gems for item/hero purchases
2016-08-17 10:00:56 -07:00
Matt Lott
2c2c052d5a 🐛Fix course slug check for /teachers/courses tests 2016-08-16 17:08:47 -07:00
Matt Lott
84e3ee270a Add primer level support to classroom Ux
Exclude levels if classroom.aceConfig.language == level.primerLanguage

Closes 
2016-08-16 16:52:17 -07:00
Matt Lott
efa4b2b158 🐛Fix teacher dashboard course # progress 2016-08-16 16:30:12 -07:00
Matt Lott
8054cd6cf0 Update gd1 course to released 2016-08-16 15:27:34 -07:00
Phoenix Eliot
a1b012c7f5 Add support for optional arguments, and web components
Refactor

Don't use globals in template
2016-08-16 13:43:08 -07:00
Rob
07f6f98c24 Refactor remote server readpref and connection strings. 2016-08-16 13:35:15 -07:00
Phoenix Eliot
fd45c9d473 Implement HeroSelectModal for demo flow
Add Campaign factory

First basic tests for HeroSelectModal in demo flow

Implement HeroSelectModal for demo flow

Improve tests

Disable empty test

Fix text inconsistency around 'me'

Just listen once

Add HeroSelectModal events test

Don't reuse destroyed modal

Fix inconsistent modal close behavior

Fix tests
2016-08-16 11:19:40 -07:00
Matt Lott
7cd49b438b Update ZP -> Close lead importing to only query for last 30 days 2016-08-16 05:28:50 -07:00
lazydogP
2e0a7769ac Update zh-HANS.coffee 2016-08-16 07:33:15 +08:00
lazydogP
931103e4a4 Merge remote-tracking branch 'refs/remotes/codecombat/master' 2016-08-16 07:32:33 +08:00
Scott Erickson
b471b909c5 Disable projects tab 2016-08-15 15:04:05 -07:00
Matt Lott
f84897eaa7 Update home course tiles 2016-08-15 14:41:09 -07:00
Matt Lott
f49305bd9d Update wd1 to released 2016-08-15 13:47:24 -07:00
Matt Lott
e62dbed362 Shift user code yellow arrow tips inward based on indentation
Closes 
2016-08-15 13:36:43 -07:00
Rob
b308da0473 Add analytics even for LevelLoad errors. 2016-08-15 13:34:06 -07:00
Scott Erickson
e9c7edb6be Fix populate i18n to also populate thang component configs 2016-08-15 11:54:04 -07:00
iammehmetguler
803fc39998 Update tr.coffee ()
* Update tr.coffee

calendar

* Update tr.coffee

login, signup
2016-08-13 11:26:39 +01:00
Phoenix Eliot
c2ce804839 Fix worker-based syntax annotations 2016-08-12 16:28:29 -07:00
Scott Erickson
2dadc8458c Remove hardcoded student ui game dev play instructions 2016-08-12 14:38:32 -07:00
Scott Erickson
a5f18f88be Add script that migrates users with email-formatted usernames 2016-08-12 14:34:08 -07:00
Phoenix Eliot
76e461398e Clean up i18n TODOs 2016-08-12 13:27:30 -07:00
Phoenix Eliot
31a3f9dff8 Clean up console.log in server tests 2016-08-12 13:25:11 -07:00
Phoenix Eliot
73bbe598da Improve WebDev level image gallery
Improve image gallery

Add How to Copy/Paste section

Fix modal close button

Add specs for image gallery

Fix up i18n

Fix render resetting scroll

Address code review feedback

Ensure afterRender is called
2016-08-12 13:19:26 -07:00
Matt Lott
f96c3ab00e 🐛Fix course instance server test day range bugs 2016-08-11 22:46:15 -07:00
Matt Lott
5a6bed96b2 🐛Fix course solutions level ordering 2016-08-11 22:26:47 -07:00
Phoenix Eliot
e91a15338e Add View Project buttons to /courses 2016-08-11 16:38:14 -07:00
Matt Lott
341154ab31 🐛Fix course description typo 2016-08-11 16:03:56 -07:00
Matt Lott
0f00ae1718 Disable supermodel timeout handling tests 2016-08-11 15:36:52 -07:00
Matt Lott
2905563729 🐛Remove semi-colons from course solutions view 2016-08-11 15:34:39 -07:00
Matt Lott
a7e290fffe Replace course guide PDFs with solutions pages 2016-08-11 15:29:56 -07:00
Zeldaretter
5fc184da67 Update de-De.coffee ()
some translations
2016-08-11 22:13:03 +01:00
iammehmetguler
e21217dad3 Update tr.coffee ()
calendar
2016-08-11 21:29:23 +01:00
Scott Erickson
d679700966 Disable SuperModel retrying
Seems to be causing issues when requests retry with different parameters,
for example:

https://github.com/codecombat/codecombat/blob/master/app/views/NewHomeView.coffee#L42-L45

This retries without the original GET parameters and returns a 403 as a result.
2016-08-11 09:44:36 -07:00
Scott Erickson
37d0b23103 Merge pull request from Imperadeiro98/master
Remove obsolete ErrorView from templates
2016-08-11 08:30:42 -07:00
Matt Lott
eaddf2fa8d Add level schema primerLanguage
For web-dev 2 course in the short term
2016-08-11 06:08:58 -07:00
Abimael Alcebíades
1436b6e2a3 Update pt-Br.coffee ()
added new translations
2016-08-11 11:12:09 +01:00
Phoenix Eliot
a4f48bbc17 Add tests for SuperModel load retrying
Clear timeouts after each test
2016-08-10 15:28:33 -07:00
Phoenix Eliot
d3db19dee3 Report timeout problems, and fix retries
Fix getting model URLs
2016-08-10 15:28:25 -07:00
Matt Lott
c87f505759 Add gd1 and wd1 to campaign world Ux
Add 1st game-dev and web-dev campaigns to world selector
Take player back out to world selector after completing campaigns up through forest

Closes 
2016-08-10 15:06:14 -07:00
Matt Lott
2e5f9abd97 🐛Fix autocomplete suggestion limit comment 2016-08-10 14:45:59 -07:00
Imperadeiro98
4b4c19928f Remove obsolete ErrorView from templates 2016-08-10 21:15:38 +01:00
Matt Lott
e65dc8e2a5 Update homepage anonymous CTA buttons
I’m a Teacher -> /teachers/signup
I’m a Student -> /home#create-account-student
2016-08-10 13:14:41 -07:00
Scott Erickson
2e5c15cc6d Internationalize student UI for game dev levels 2016-08-10 12:47:34 -07:00
Scott Erickson
ff2d505720 Internationalize PlayGameDevLevelView 2016-08-10 12:47:34 -07:00
Scott Erickson
ffa840910d Do not automatically show victory modal for shareable game dev levels
Students are likely to want to keep working on their level, even if
they beat it.
2016-08-10 12:47:34 -07:00
Matt Lott
d14bea3110 Add hero. autocomplete entries
Adding up to 10 highest importance hero. duplicate autocomplete entries
that include ‘hero.’
Increasing max suggestions allowed from 10 to 20
2016-08-10 11:17:56 -07:00
Scott Erickson
954780d19f Finesse RequestQuoteView load waiting
Trying to get those tests to pass consistently on Travis.
2016-08-10 10:13:28 -07:00
Matt Lott
e968afc8ec Add school and district ids to zp lead importing 2016-08-10 10:13:00 -07:00
Scott Erickson
9b992a8679 Merge pull request from Imperadeiro98/master
Fix 
2016-08-10 09:46:20 -07:00
Abimael Alcebíades
54ec028962 added new translations () 2016-08-10 09:17:52 +01:00
Nguyễn Việt Dũng
65e7aa358b Update vi.coffee 2016-08-10 10:16:27 +07:00
Abimael Alcebíades
40aa30d161 Update pt-BR.coffee () 2016-08-09 21:17:11 +01:00
Bram Dekker
c620d29755 Update nl-NL.coffee ()
translated some lines in the signup section.
2016-08-09 21:16:25 +01:00
Glubu
d3b56b3e97 typo and punctuation fixed () 2016-08-09 21:15:43 +01:00
Matt Lott
1439016f9b Remove schoolName from school-counts user query 2016-08-09 06:33:31 -07:00
Matt Lott
6a8776c31b 🐛Add parent blurb to signup birthdate check 2016-08-09 06:33:15 -07:00
Matt Lott
a00f2600f6 🐛Show API docs popovers higher over surface 2016-08-09 06:03:58 -07:00
Matt Lott
5fc88ebeeb 🐛Fix extra space in he.coffee 2016-08-09 05:38:35 -07:00
VilkkuV
7188822552 Update fi.coffee 2016-08-09 10:26:01 +03:00
themaka
f111c16482 Add Sean to /about ()
* Add Sean to /about

* Remove empty line
2016-08-08 17:46:22 -07:00
phoenixeliot
5b8d66cd1c Add hero selector to create account modal
Don't show grey border around Anya in signup modal

Refactor reload handling

Fix courses page updating to chosen hero
2016-08-08 15:19:44 -07:00
Nick Winter
6b49d4ccf6 Propagate he.coffee 2016-08-08 15:18:57 -07:00
Nick Winter
3ff34b376f Manually merge : add Hawaiian 2016-08-08 13:10:28 -07:00
wyyi
f3ffaf5e34 Update uk.coffee () 2016-08-08 20:48:24 +01:00
borispinatel
5a077e628d Update fr.coffee () 2016-08-08 20:47:34 +01:00
Scott Erickson
51b292cc3d Add static messages to game dev levels for playtesting 2016-08-08 11:49:38 -07:00
VilkkuV
2e8a395349 Update fi.coffee 2016-08-08 21:44:39 +03:00
VilkkuV
a32d32674a Update fi.coffee 2016-08-08 20:48:30 +03:00
Scott Erickson
c89ee139ed Have GET param 'project' work with [] notation
Otherwise queries like $.ajax('/db/campaign', {data: {project:['name','slug']}})
cause 500 errors.
2016-08-08 09:35:42 -07:00
Imperadeiro98
d76a81438d Remove dead line from ArticleEditView template 2016-08-07 19:13:31 +01:00
Imperadeiro98
9df2c97923 Fix 2016-08-07 19:02:51 +01:00
Nora Kenez
57b7791286 Update hu.coffee ()
* Update hu.coffee

* Update hu.coffee
2016-08-07 15:40:25 +01:00
Imperadeiro98
cc24d962c5 Fix systems documentation tab background
Same as 
2016-08-07 15:35:42 +01:00
Phoenix Eliot
6697d33681 Fix level editor child window size
Makes it slightly larger at 1280x640, which is our target screen size
for slightly smaller than chromebook screens.
2016-08-05 16:16:09 -07:00
Matt Lott
6f08d5e422 Add required district field to teacher trial request forms
School now optional
N/A placeholder on district field and don't save it if it's n/a
Updating required field error UI a bit, and there is some larger
refactoring needed here later.
NCES phone number for district only entries will currently be a child
school, will fix later.

Closes 
2016-08-05 13:25:19 -07:00
Scott Erickson
f4c0e4144e Add some comments and a TODO for the supermodel retrying system 2016-08-05 13:23:44 -07:00
Scott Erickson
2287c4f1e5 Merge pull request from themaka/master
Adding Croatian and Māori to languages
2016-08-05 13:04:47 -07:00
themaka
175d8a9983 Comment out the "common"
Which I've learned is not used until someone's actually translated
something in that section ...
2016-08-05 15:11:15 -04:00
Matt Lott
8b8086a95d 🐛Fix admin classrooms levels page campaign lookup 2016-08-05 12:09:36 -07:00
Scott Erickson
960d296dd1 Merge pull request from matt-sanders/background_fixes
fixes   
2016-08-05 10:33:33 -07:00
Catsync
adda9747e9 Add a value 'hidden' for campaign's type property ()
Nick would have to manually remove the defaulted ‘hero’ type from new
campaigns to make sure it wasn’t included in various campaign related
queries until we were ready. Now we can set this to hidden in the
campaign editor.
2016-08-05 10:31:46 -07:00
Matt Lott
96cbcfada2 🐛Fix prepaids middleware missing this.logError 2016-08-05 09:21:37 -07:00
Matt Sanders
ef7b9aa3aa fixes by not making everything 100% height 2016-08-05 17:12:18 +12:00
Matt Sanders
95146e7820 fixed the background issue on the tasks tab 2016-08-05 17:02:22 +12:00
Matt Sanders
d5e3be62a4 fixed the background issue on the achievements view 2016-08-05 16:52:02 +12:00
Nick Winter
246469bdde A couple fixes for demo flow 2016-08-04 18:37:13 -07:00
Josh Callebaut
33134eb6c9 Merge pull request from Zerrien/campaign-arrow-fix
Tweak campaign connecton arrows
2016-08-04 10:38:45 -07:00
Josh Callebaut
41cf864dc7 Add divide by zero check' 2016-08-04 10:20:50 -07:00
Bryukhanov Valentin
c4ee32f3e1 Merge pull request from Bryukh/master
Add bunch of Russian translation
2016-08-04 08:16:31 +03:00
Josh Callebaut
796c35f66d Tweak campaign connecton arrows so they point more accurately to the next level 2016-08-03 14:53:03 -07:00
Nick Winter
6917843018 Add a couple animations to defaultActions while game-dev lazy rendering isn't working 2016-08-03 13:32:49 -07:00
Nick Winter
35966b24c1 ES5ify web-dev-listener.js 2016-08-03 13:02:55 -07:00
Matt Lott
ba6d51659c Show in-game programming language
Closes 
2016-08-03 11:08:20 -07:00
Matt Lott
079ad6c0fa Replace /about Matt and Nick pics with real photos 2016-08-03 10:17:57 -07:00
phoenixeliot
5dc3ebe9b1 Include other timeout error codes for good measure 2016-08-02 14:25:14 -07:00
phoenixeliot
36449bb6b6 Account for both types of timeouts in retry logic 2016-08-02 14:18:26 -07:00
phoenixeliot
2e9ff61436 Fix broken ladder in course 2016-08-02 13:43:13 -07:00
phoenixeliot
eaaea6f08b Retry models when they time out 2016-08-02 13:42:56 -07:00
Nick Winter
fad53cf43f Don't wait for simulation load progress on game-dev levels 2016-08-02 12:31:36 -07:00
Matt Lott
e51186ec46 Add JS primer to update courses script 2016-08-02 11:08:46 -07:00
phoenixeliot
5f560f3e7d Don't allow conversion to student without a ClassCode
Fix i18n and tests
2016-08-01 16:44:57 -07:00
phoenixeliot
bd45f75bc0 Add test for Export Student Progress (CSV) button
Add number checking to CSV test
2016-08-01 15:31:55 -07:00
phoenixeliot
94e3c5a2b5 Add first/last names to Account Settings 2016-08-01 14:12:35 -07:00
Valentin Bryukhanov
2e3d1531bf Add bunch of Russian translation 2016-08-01 08:48:00 +03:00
Imperadeiro98
e34abb0d27 Update pt-PT.coffee 2016-07-30 13:59:55 +01:00
Nick Winter
24d58a1971 Handle old programming languages during simulation 2016-07-29 16:36:12 -07:00
phoenixeliot
d388975217 Fix unescaped HTML in goal descriptions 2016-07-29 16:21:13 -07:00
phoenixeliot
51706a52be Fix indefiniteLength error on first frame 2016-07-29 16:16:50 -07:00
phoenixeliot
f3ffcc2885 Make hamburger more visible 2016-07-29 14:55:42 -07:00
phoenixeliot
83557910b2 Fix CSV export and projects tab 2016-07-29 14:37:45 -07:00
Nick Winter
e7c1d3d5d3 Remove stale educator wiki link 2016-07-29 14:36:31 -07:00
themaka
8135bd73d0 Added Māori and Croatian to list of languages to display on Diplomat page. 2016-07-29 15:53:01 -04:00
themaka
2d87a51f30 Forgot to update language names in first line of hr/mi .coffee 2016-07-29 15:39:37 -04:00
themaka
66817657f0 Added "te reo Māori" (Māori) to locale 2016-07-29 15:34:34 -04:00
themaka
715939dfb1 Adding Croatian language to locale 2016-07-29 15:20:24 -04:00
themaka
499c5f28b6 Merge remote-tracking branch 'refs/remotes/codecombat/master' 2016-07-29 15:05:24 -04:00
phoenixeliot
4af822d13a Revert "Add game UI programming language label"
This reverts commit b7f916116d.

Some heights in gameplay were made worse by this; needs some more work.
2016-07-29 11:23:42 -07:00
Nick Winter
488e533e4e Remove old bower dependency on tablesorter 2016-07-28 19:03:25 -07:00
Matt Lott
1c777ee7f5 Update site desert image 2016-07-28 16:10:28 -07:00
phoenixeliot
4abc26a8d4 Fix progress resetting 2016-07-28 15:47:17 -07:00
phoenixeliot
a47bdc084a Allow admins to become teachers 2016-07-28 15:28:31 -07:00
Matt Lott
00221e6c91 🐛Fix license inquiry contact lookup boolean 2016-07-28 15:14:38 -07:00
phoenixeliot
dd66a8c252 Add detection of HTML/CSS/JS comments for entrypoints 2016-07-28 15:10:01 -07:00
Nick Winter
592e57351c Propagate i18n 2016-07-28 13:49:12 -07:00
Nick Winter
82c4367308 Avoid \n escape character in i18n strings for tooling 2016-07-28 13:47:23 -07:00
Nick Winter
d77625bc77 Game dev levels ()
* Tweak API doc behavior and styling

* Instead of moving to the left during active dialogues, just move to the top
* Allow pointer events
* Adjust close button
* Re-enable pinning API docs for game-dev and web-dev levels

* Make sidebar in PlayGameDevLevelView stretch, better layout columns

* Set up content of PlayGameDevLevelView sidebar to scroll

* Add rest of PlayGameDevLevelView sidebar content, rework what loading looks like

* Finish PlayGameDevLevelView

* Add share area below
* Cover the brown background, paint it gray

* Tweak PlayGameDevLevelView

* Have progress bar show everything
* Fix Surface resize handling

* Fix PlayGameDevLevelView resizing incorrectly when playing

* Add GameDevVictoryModal to PlayGameDevLevelView

* Don't show missing-doctype annotation in Ace

* Hook up GameDevVictoryModal copy button

* Fix onChangeAnnotation runtime error

* Fix onLevelLoaded runtime error

* Have CourseVictoryModal link to /courses when course is done

* Trim, update CourseDetailsView

* Remove last vestiges of teacherMode
* Remove giant navigation buttons at top
* Quick switch to flat style

* Add analytics for game-dev

* Update Analytics events for gamedev

* Prefix event names with context
* Send to Mixpanel
* Include more properties

* Mostly set up indefinite play and autocast for game-dev levels

* Set up cast buttons and shortcut for game-dev

* Add rudimentary instructions when students play game-dev levels

* Couple tweaks

* fix a bit of code that expects frames to always stick around
* have PlayGameDevLevelView render a couple frames on load

* API Docs use 'game' instead of 'hero'

* Move tags to head without combining

* Add HTML comment-start string

Fixes missing entry point arrows

* Fix some whitespace
2016-07-28 13:39:58 -07:00
Matt Lott
b7f916116d Add game UI programming language label 2016-07-28 12:00:52 -07:00
Matt Lott
2f7fa4e6e7 Always show level and course name in-game 2016-07-28 06:15:17 -07:00
Rob
4d8d4ff268 Defer breakage of Concept Map if we don't have esper. 2016-07-27 16:55:10 -07:00
Rob
b0f653db19 Remove white pixels from logo via. Robin. 2016-07-27 16:45:14 -07:00
Rob
b7fcaa50a6 Change keyword autocomplete logic. 2016-07-27 16:37:57 -07:00
Nick Winter
e3453cb0f3 Update Simulator to new, simple way of making UserCodeMap 2016-07-27 15:57:35 -07:00
Rob
6c9f351f01 Group concept map by campaign. 2016-07-27 14:17:37 -07:00
Rob
75e3c54d54 Add concept map view to artisans. 2016-07-27 13:44:55 -07:00
Rob
fd17793819 Add user completion time analysis script for students. 2016-07-27 13:43:22 -07:00
Scott Erickson
ac4df997c1 Display treema errors 2016-07-26 16:48:04 -07:00
Robin Yang
2cfe7d0b43 Add Elliot Blurb on About Page 2016-07-26 15:32:37 -07:00
phoenixeliot
642847c9c5 Improve progress dot layout for extra courses 2016-07-26 13:11:51 -07:00
phoenixeliot
bd8a284ce6 Improve layout of help blocks in signup form 2016-07-26 11:54:02 -07:00
Scott Erickson
8117a000c1 Fix PlayLevelView race condition typo 2016-07-26 10:38:15 -07:00
Rob
ccb1d3cd94 Plural 2016-07-25 16:36:49 -07:00
Scott Erickson
053ce34faf Prevent users from signing up with or changing to an email-like username 2016-07-25 16:33:48 -07:00
Rob
782aacf35e Patch Earned Achievements more generally. 2016-07-25 16:33:11 -07:00
Rob
d292e180d2 Fix earned acheivements 2016-07-25 16:12:14 -07:00
Scott Erickson
34310642c7 Add destudent and deteacher buttons to AdministerUserModal 2016-07-25 16:09:24 -07:00
Scott Erickson
1064d64172 Update FB API to v2.7
We were on v2.0, which is being deprecated on August 8th
2016-07-25 13:56:05 -07:00
phoenixeliot
f1f020a50d Use course.releasePhase instead of .adminOnly
Remove old comment
2016-07-25 11:29:30 -07:00
phoenixeliot
5fa2e9b865 Stop spec from using your godmode setting 2016-07-25 10:51:52 -07:00
Nick Winter
9ae5109e40 Show projects in TeacherClassView, proper font in first web-dev project level for now for demo 2016-07-24 14:42:07 -07:00
Matt Lott
9ad7376edf Add shareable to admin classroom levels page 2016-07-24 08:09:12 -07:00
Matt Lott
9962ff83d4 Level editor play classroom options 2016-07-23 21:03:19 -07:00
rudmanmrrod
7b76180902 Added more translations () 2016-07-23 22:44:01 +01:00
Matt Lott
e4867e3700 Fix close.io opportunities script
License inquiries initially assigned to wrong owner, and this fixes
them up.
2016-07-23 10:30:05 -07:00
Nick Winter
ea61499a10 Add Done button hotkey 2016-07-22 17:59:32 -07:00
Matt Lott
12f46a6f31 Add followup call tasks for CA/UK/AU/NZ 2016-07-22 17:23:38 -07:00
Matt Lott
2722d501be Add followup emails for CA/UK/AU/NZ 2016-07-22 16:59:55 -07:00
phoenixeliot
cac87559c6 Remove frequent console logs in web levels 2016-07-22 10:43:08 -07:00
phoenixeliot
f9d0adaca6 Fix levelType refactor 2016-07-22 10:43:08 -07:00
Scott Erickson
c3381c1274 Fix updatePrepaid.js user updates 2016-07-22 09:33:50 -07:00
Matt Lott
1e0aa1a375 Import lead using NCES school name if we have it 2016-07-21 16:57:08 -07:00
Scott Erickson
e19d31b9b2 updatePrepaid function can modify creator property 2016-07-21 16:42:32 -07:00
Scott Erickson
2b09a24436 Have createLicenses.js script include redeemers array 2016-07-21 16:41:39 -07:00
Scott Erickson
b1c69b686c Add admin button to update course content for classrooms 2016-07-21 15:16:17 -07:00
Matt Lott
e834b93a0a Use ZP custom school name field if available 2016-07-21 15:09:06 -07:00
Matt Lott
fd0fdfe83e Update inbound sales lead split 2016-07-21 15:09:06 -07:00
Scott Erickson
57f8aedaa5 Hide student tab on TeacherClassView until it's ready 2016-07-21 13:54:37 -07:00
Scott Erickson
b52cbd6b62 Tweak output 2016-07-21 11:33:24 -07:00
Scott Erickson
4bc9ea77c6 Add updatePrepaid.js stored mongodb script 2016-07-21 11:31:08 -07:00
Scott Erickson
6b78ab3fe8 Add createLicenses.js mongodb script 2016-07-21 11:23:14 -07:00
Matt Lott
01679982d5 Remove intro/overview from guide game menu
Unless picoCTF
Will show guide option if non-course and help videos.
2016-07-21 11:11:11 -07:00
phoenixeliot
b1277dc95f Merge branch 'game-dev-levels' 2016-07-21 10:14:22 -07:00
Scott Erickson
908354d6cb Merge pull request from UltCombo/campaign-view-level-titles-tooltip
Campaign view: fix level titles getting cut off
2016-07-21 09:18:43 -07:00
Nick Winter
436896de4a Update /about jobs 2016-07-21 09:06:57 -07:00
Matt Lott
4226cd098d Shrink campaign map flags, hovers, click areas 2016-07-20 21:03:48 -07:00
UltCombo
11ba5333d4 Campaign view: fix level titles getting cut off
E.g.:

![Forest - level titles cut off](http://i.imgur.com/cfIbyXv.png)
2016-07-21 00:02:39 -03:00
phoenixeliot
2ebf94c3db Combine extracted script/style tags 2016-07-20 17:20:21 -07:00
Matt Lott
15bcae8a17 Show API popup entirely over surface
Covering user code causes problems for players.
2016-07-20 17:06:51 -07:00
phoenixeliot
ce137f00cb Fix level preview CSS 2016-07-20 17:04:01 -07:00
phoenixeliot
f28c17c9bf Fix spacing to be consistent 2016-07-20 15:47:20 -07:00
phoenixeliot
62813e41a3 Extract styles and scripts 2016-07-20 15:44:01 -07:00
Matt Lott
72905c7ff2 Reduce Mixpanel event tracking
Turning off homepage events and play pageviews.
2016-07-20 14:45:13 -07:00
phoenixeliot
6fdda491bd Fix for when database courses/campaigns don't match 2016-07-19 14:13:24 -07:00
Rob Blanckaert
5bb7f243f5 Update web-dev-listener.js 2016-07-19 13:44:25 -07:00
phoenixeliot
58284dff33 Turn on Ace HTML worker for syntax errors 2016-07-19 11:30:02 -07:00
Matt Lott
01a8312617 🐛Classroom last played string null level check 2016-07-19 10:10:48 -07:00
Matt Lott
7bf3fc6a78 🐛Don't route level load error mails to sales 2016-07-19 06:36:11 -07:00
Nick Winter
cb1021e013 Fix typo in game-dev-2 ordering 2016-07-18 22:27:20 -07:00
Matt Lott
1f1132ecf8 Prioritize displaying first/last names over username 2016-07-18 12:38:29 -07:00
phoenixeliot
4e449fea3b Fix gplus/fb signin and tests 2016-07-18 11:41:18 -07:00
phoenixeliot
d7209d784e Add option to print current spec name to jasmine 2016-07-18 10:56:25 -07:00
phoenixeliot
1d616cd92a Fix indentation 2016-07-18 10:46:51 -07:00
phoenixeliot
bb6262483f Allow username-only signup for classroom users
Address some code review feedback

Correct error code in test

Don't try to send emails to empty addresses

Add tests for subscriptions

Add tests for Next Steps email

Fix specs

Add reason for disabled test
2016-07-18 10:41:17 -07:00
Bryukhanov Valentin
ff523c2431 Merge pull request from codecombat/Bryukh-ru-hints
Update ru.coffee - hints ui
2016-07-18 20:23:51 +03:00
Matt Lott
68ebfa0e39 Update license inquiry call task name 2016-07-18 10:08:23 -07:00
Bryukhanov Valentin
2852f5014c Update ru.coffee - hints ui
Add "hints" translations in the editor UI
2016-07-18 19:54:20 +03:00
Matt Lott
607c129c7f School active licenses admin page 2016-07-18 09:41:42 -07:00
Nick Winter
f94cc2ec1f Fix CS1, CS2, GD1, WD2, CS3, etc. labeling in TeacherClassesView, too 2016-07-17 01:12:58 -07:00
Nick Winter
6e65171a83 i18n, comments, misc cleanup 2016-07-17 00:53:17 -07:00
Nick Winter
320aa0f3d9 Add first guess for other web-dev concepts 2016-07-16 23:30:10 -07:00
Nick Winter
b04e968da5 Add support for CSS docs 2016-07-16 23:17:05 -07:00
Nick Winter
5d0b9c875a Fix some typos that made it not work in Firefox (not sure how it worked in Chrome) 2016-07-16 13:32:54 -07:00
Nick Winter
0cd3278b8f Add simple ImageGalleryView for some sample images in web-dev levels 2016-07-16 13:11:43 -07:00
Nick Winter
d37527d21b Ordering/labeling courses: CS1, CS2, GD1, WD1, CS3, etc 2016-07-16 00:35:52 -07:00
Nick Winter
dc6a1de9fa Ordering/labeling courses: CS1, CS2, GD1, WD1, CS3, etc 2016-07-16 00:33:10 -07:00
Nick Winter
b64bcd9f02 Use shareable false/true/'project' for different levels of shareability 2016-07-15 23:26:43 -07:00
Nick Winter
6ae89e31f1 Add direct link to play game/web-dev creations while coding them 2016-07-15 22:14:25 -07:00
Nick Winter
5d26b03918 Add buttons to view game/web-dev levels to courses views 2016-07-15 21:57:04 -07:00
Nick Winter
5f95a4d158 Play game-dev levels without API restrictions. Show game button in CourseDetailsView only when appropriate. 2016-07-15 20:47:09 -07:00
Nick Winter
7e4733f07e Hack: check HTML goals now and in a second to account for built-in CSS transition 2016-07-15 20:25:37 -07:00
Nick Winter
224ad54bdd View web dev levels. Add proper victory modal game/webpage share links. Fix playing game dev levels. Add generic change transition to all web-dev pages. 2016-07-15 20:03:12 -07:00
Scott Erickson
0570644943 Set up a bunch of game dev, web dev playing logic 2016-07-15 16:57:39 -07:00
Nick Winter
788a14398a Fix starting web dev levels 2016-07-15 16:22:33 -07:00
Scott Erickson
7b38181241 Merge branch 'master' into game-dev-levels 2016-07-15 16:02:21 -07:00
Scott Erickson
ab704a1cab Merge remote-tracking branch 'origin/web-dev-levels' into game-dev-levels
# Conflicts:
#	app/views/play/level/PlayLevelView.coffee
2016-07-15 16:01:57 -07:00
Scott Erickson
10ca59d10f Have CourseVictoryModal used for course-ladder levels 2016-07-15 15:54:22 -07:00
Nick Winter
0922eec2cc Add stubs for game-dev-1, web-dev-1, and web-dev-2 to updateCourses script 2016-07-15 14:47:42 -07:00
Scott Erickson
0a8514b688 Merge branch 'master' of https://github.com/codecombat/codecombat 2016-07-15 13:44:19 -07:00
Scott Erickson
df028b32bb Add compiler requirements to fix npm update 2016-07-15 13:43:08 -07:00
Scott Erickson
486949d07c Admin only courses ()
* adminOnly course field

* Properly check not-admin-only when fetching courses
2016-07-15 13:28:35 -07:00
Nick Winter
9be8151959 Don't create God for web-dev levels 2016-07-15 13:24:54 -07:00
Nick Winter
cb085d019d Update Aether version 2016-07-15 11:27:58 -07:00
Nick Winter
5a688e42c7 Slightly more flexible iframe origin checking 2016-07-15 11:19:22 -07:00
Nick Winter
1e640fb74c Fix CampaignView styles to cover the whole screen with background again 2016-07-15 10:14:00 -07:00
Nick Winter
bc5375770e Fix Mongoose at 4.5.3 while 4.5.4 has bug creating new clans and trial requests 2016-07-15 10:13:45 -07:00
Nick Winter
1e89775486 Basic campaign mode victory modal hookup for web-dev levels 2016-07-15 09:53:16 -07:00
Nick Winter
739973cb47 Sending goal states to GoalManager and GoalStatusView 2016-07-15 09:11:36 -07:00
Nick Winter
c44c16e5d2 Started implementing web-dev goals 2016-07-15 00:40:32 -07:00
Nick Winter
69f21514b9 Add Lodash to iframe content 2016-07-14 22:43:49 -07:00
Nick Winter
e3670165e7 Remove code for multiple spells; rename SpellListTabEntryView to SpellTopBarView; remove hero avatar from SpellTopBarView 2016-07-14 22:43:25 -07:00
Nick Winter
220db3106c Run button now recreates web-dev DOM; no submit button 2016-07-14 19:48:27 -07:00
Nick Winter
33ba3f6033 Enable docs for web-dev levels 2016-07-14 19:14:18 -07:00
Nick Winter
ed320a8d9e WebSurfaceView now parsing player code through virtual DOM into iframe 2016-07-14 18:07:36 -07:00
Scott Erickson
9d0ad7af44 Start work on having course arenas use the CourseVictoryModal 2016-07-14 16:50:17 -07:00
Scott Erickson
9a79cae09d Fix PlayGameDevLevelView to run in course mode 2016-07-14 16:49:48 -07:00
Scott Erickson
c0bc10ffb6 Add projects tab stub to TeacherClassView 2016-07-14 15:53:54 -07:00
Scott Erickson
61caf3dcd3 Merge branch 'master' into game-dev-levels 2016-07-14 15:50:04 -07:00
Scott Erickson
0cb92582f4 Add destroy method 2016-07-14 15:13:02 -07:00
Nick Winter
5b16da099a Hack LevelEditor to load web-dev levels 2016-07-14 12:47:25 -07:00
Nick Winter
be50657530 Remove Firebase for now. 2016-07-14 12:47:00 -07:00
Nick Winter
16b10612b6 Stub WebSurface showing for web-dev levels 2016-07-14 12:34:22 -07:00
Nick Winter
c5c831c211 Remove real-time multiplayer prototype code 2016-07-14 10:26:09 -07:00
Nick Winter
349ab24da7 First pass at adding 'web-dev' level type 2016-07-14 09:38:45 -07:00
Nick Winter
c0a70cb2ab Refactor level type checks for easy greppability (level.isType) 2016-07-14 08:58:43 -07:00
Nick Winter
87ed53b24b Merge branch 'master' into web-dev-levels 2016-07-14 08:26:27 -07:00
Scott Erickson
fb9998b15e Add temp buttons to CourseDetailsView for testing PlayGameDevLevelView 2016-07-13 16:05:41 -07:00
Scott Erickson
b982f3fd52 Fix Camera bounds by adding ScriptManager 2016-07-13 16:05:41 -07:00
Scott Erickson
4a51045a41 Fix PlayGameDevLevelView when playing the first time, get frames streaming
For whatever reason, the Angel does not normally allow streaming on the first world.
I hacked around it, but would be good to figure out why that restriction is there
in the first place.
2016-07-13 16:05:41 -07:00
Scott Erickson
c9986ee05a Tweak Promises in PlayGameDevLevelView 2016-07-13 16:05:41 -07:00
Scott Erickson
45c8c2006d Quick fix LevelSessions require error
In areas of the site that do not have lib/aether_utils, the require broke because it's
fetched only sometimes through the ModuleLoader.
2016-07-13 16:05:41 -07:00
Nick Winter
f88223b994 Fix spawning Hero kind ThangTypes in game-dev levels 2016-07-13 14:20:22 -07:00
Scott Erickson
d7a2219b16 Refactor PlayGameDevLevelView to use promises 2016-07-13 13:28:54 -07:00
Scott Erickson
1b7ac76b9f Add loading and playing to PlayGameDevLevelView 2016-07-13 11:43:25 -07:00
Scott Erickson
3a0695f59c Add some basic info to PlayGameDevLevelView 2016-07-12 15:12:11 -07:00
Scott Erickson
b674277e14 Add PlayGameDevLevelView styling 2016-07-12 14:44:31 -07:00
Scott Erickson
25e348c5ad Initial, basically working PlayGameDevLevelView 2016-07-12 14:07:10 -07:00
554 changed files with 42641 additions and 15971 deletions
.gitignore.istanbul.yml.travis.ymlREADME.md
app
assets
collections
core
lib

3
.gitignore vendored
View file

@ -105,3 +105,6 @@ Dockerfile
# coffeelint for editors (might be standardized eventually)
coffeelint.json
gitSpy/*
# nightwatch reports
spec/smoke/reports/

2
.istanbul.yml Normal file
View file

@ -0,0 +1,2 @@
instrumentation:
root: ./server

View file

@ -4,13 +4,18 @@ language: node_js
node_js:
- 5.1.1
env:
- CXX=g++-4.8
addons:
apt:
sources:
- mongodb-upstart
- ubuntu-toolchain-r-test
packages:
- mongodb-org-server
- g++-4.8
cache:
directories:
@ -31,6 +36,10 @@ before_script:
script:
- "./node_modules/karma/bin/karma start --browsers Firefox --single-run --reporters dots"
- "npm run jasmine"
- "npm run coverage"
after_script:
- "npm install coveralls && cat ./coverage/lcov.info | coveralls"
notifications:
slack:

View file

@ -9,8 +9,7 @@
CodeCombat is a multiplayer programming game for learning how to code.
**See the [Archmage (coder) developer wiki](../../wiki/Archmage-Home) for a dev
setup guide, extensive documentation, and much more. Every new person that wants
to start contributing the project coding should start there.**
setup guide, extensive documentation, and much more to get started hacking!**
It's both a startup and a community project, completely open source under the
[MIT and Creative Commons licenses](http://codecombat.com/legal). It's the
@ -46,11 +45,11 @@ so we can accept your pull requests. It is easy.
### [Join Us!](http://blog.codecombat.com/why-you-should-open-source-your-startup)
![Nick Winter](http://codecombat.com/images/pages/about/nick_small.png "Nick Winter")
![Nick Winter](http://codecombat.com/images/pages/about/team-avatars/nick-avatar.png "Nick Winter")
![George Saines](http://codecombat.com/images/pages/about/george_small.png "George Saines")
![Scott Erickson](http://codecombat.com/images/pages/about/scott_small.png "Scott Erickson")
![Matt Lott](http://codecombat.com/images/pages/about/matt_small.png "Matt Lott")
![Catherine Weresow](http://codecombat.com/images/pages/about/cat_small.png "Catherine Weresow")
![Scott Erickson](http://codecombat.com/images/pages/about/team-avatars/scott-avatar.png "Scott Erickson")
![Matt Lott](http://codecombat.com/images/pages/about/team-avatars/matt-avatar.png "Matt Lott")
![Catherine Weresow](http://codecombat.com/images/pages/about/team-avatars/cat-avatar.png "Catherine Weresow")
![Maka Gradin](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Maka%20Gradin/maka_gradin_100.png "Maka Gradin")
![Rob Blanckaert](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Rob%20Blanckaert/rob_blanckaert_100.png "Rob Blanckaert")
![Josh Callebaut](https://dl.dropboxusercontent.com/u/138899/GitHub%20Wikis/avatars/Josh%20Callebaut/josh_callebaut_100.png "Josh Callebaut")

Binary file not shown.

Before

(image error) Size: 68 KiB

BIN
app/assets/images/pages/about/desert.png Executable file → Normal file

Binary file not shown.

Before

(image error) Size: 344 KiB

After

(image error) Size: 486 KiB

Before After
Before After

Binary file not shown.

Before

(image error) Size: 23 KiB

Binary file not shown.

Before

(image error) Size: 20 KiB

Binary file not shown.

Before

(image error) Size: 21 KiB

Binary file not shown.

After

(image error) Size: 55 KiB

Binary file not shown.

After

(image error) Size: 54 KiB

Binary file not shown.

Before

(image error) Size: 8.1 KiB

Binary file not shown.

Before

(image error) Size: 15 KiB

BIN
app/assets/images/pages/about/screenshot_desert.png Executable file → Normal file

Binary file not shown.

Before

(image error) Size: 90 KiB

After

(image error) Size: 112 KiB

Before After
Before After

View file

Before

(image error) Size: 6.7 KiB

After

(image error) Size: 6.7 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 18 KiB

View file

Before

(image error) Size: 8.2 KiB

After

(image error) Size: 8.2 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 20 KiB

Binary file not shown.

After

(image error) Size: 11 KiB

View file

Before

(image error) Size: 18 KiB

After

(image error) Size: 18 KiB

Before After
Before After

View file

Before

(image error) Size: 6.8 KiB

After

(image error) Size: 6.8 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 17 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 21 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 20 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

Binary file not shown.

After

(image error) Size: 24 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

Binary file not shown.

After

(image error) Size: 22 KiB

Binary file not shown.

After

(image error) Size: 21 KiB

Binary file not shown.

After

(image error) Size: 25 KiB

Binary file not shown.

After

(image error) Size: 23 KiB

Binary file not shown.

Before

(image error) Size: 59 KiB

After

(image error) Size: 43 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 24 KiB

View file

Before

(image error) Size: 33 KiB

After

(image error) Size: 33 KiB

Before After
Before After

View file

Before

(image error) Size: 24 KiB

After

(image error) Size: 24 KiB

Before After
Before After

View file

Before

(image error) Size: 27 KiB

After

(image error) Size: 27 KiB

Before After
Before After

View file

Before

(image error) Size: 38 KiB

After

(image error) Size: 38 KiB

Before After
Before After

Binary file not shown.

Before

(image error) Size: 108 KiB

After

(image error) Size: 486 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 34 KiB

Binary file not shown.

After

(image error) Size: 20 KiB

Binary file not shown.

After

(image error) Size: 20 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 11 KiB

Binary file not shown.

After

(image error) Size: 317 KiB

View file

@ -0,0 +1,201 @@
// TODO: don't serve this script from codecombat.com; serve it from a harmless extra domain we don't have yet.
var lastSource = null;
var lastOrigin = null;
window.onerror = function(message, url, line, column, error){
console.log("User script error on line " + line + ", column " + column + ": ", error);
lastSource.postMessage({ type: 'error', message: message, url: url, line: line, column: column }, lastOrigin);
}
window.addEventListener('message', receiveMessage, false);
var concreteDom;
var concreteStyles;
var concreteScripts;
var virtualDom;
var virtualStyles;
var virtualScripts;
var goalStates;
var allowedOrigins = [
/^https?:\/\/(.*\.)?codecombat\.com$/,
/^https?:\/\/localhost:3000$/,
/^https?:\/\/.*codecombat-staging-codecombat\.runnableapp\.com$/,
];
function receiveMessage(event) {
var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
var allowed = false;
allowedOrigins.forEach(function(pattern) {
allowed = allowed || pattern.test(origin);
});
if (!allowed) {
console.log('Ignoring message from bad origin:', origin);
return;
}
lastOrigin = origin;
var data = event.data;
var source = lastSource = event.source;
switch (data.type) {
case 'create':
create(_.pick(data, 'dom', 'styles', 'scripts'));
checkGoals(data.goals, source, origin);
$('body').first().off('click', checkRememberedGoals);
$('body').first().on('click', checkRememberedGoals);
break;
case 'update':
if (virtualDom)
update(_.pick(data, 'dom', 'styles', 'scripts'));
else
create(_.pick(data, 'dom', 'styles', 'scripts'));
checkGoals(data.goals, source, origin);
break;
case 'log':
console.log(data.text);
break;
default:
console.log('Unknown message type:', data.type);
}
}
function create(options) {
virtualDom = options.dom;
virtualStyles = options.styles;
virtualScripts = options.scripts;
concreteDom = deku.dom.create(virtualDom);
concreteStyles = deku.dom.create(virtualStyles);
concreteScripts = deku.dom.create(virtualScripts);
// TODO: :after elements don't seem to work? (:before do)
$('body').first().empty().append(concreteDom);
replaceNodes('[for="player-styles"]', unwrapConcreteNodes(concreteStyles));
replaceNodes('[for="player-scripts"]', unwrapConcreteNodes(concreteScripts));
}
function unwrapConcreteNodes(wrappedNodes) {
return wrappedNodes.children;
}
function replaceNodes(selector, newNodes){
$newNodes = $(newNodes).clone();
$(selector + ':not(:first)').remove();
firstNode = $(selector).first();
$newNodes.attr('for', firstNode.attr('for'));
newFirstNode = $newNodes[0];
firstNode.replaceWith(newFirstNode); // Removes newFirstNode from its array (!!)
$(newFirstNode).after($newNodes);
}
function update(options) {
var dom = options.dom;
var styles = options.styles;
var scripts = options.scripts;
function dispatch() {} // Might want to do something here in the future
var context = {}; // Might want to use this to send shared state to every component
var domChanges = deku.diff.diffNode(virtualDom, dom);
domChanges.reduce(deku.dom.update(dispatch, context), concreteDom); // Rerender
// var scriptChanges = deku.diff.diffNode(virtualScripts, scripts);
// scriptChanges.reduce(deku.dom.update(dispatch, context), concreteScripts); // Rerender
// replaceNodes('[for="player-scripts"]', unwrapConcreteNodes(concreteScripts));
var styleChanges = deku.diff.diffNode(virtualStyles, styles);
styleChanges.reduce(deku.dom.update(dispatch, context), concreteStyles); // Rerender
replaceNodes('[for="player-styles"]', unwrapConcreteNodes(concreteStyles));
virtualDom = dom;
virtualStyles = styles;
virtualScripts = scripts;
}
var lastGoalArgs = [];
function checkRememberedGoals() {
checkGoals.apply(this, lastGoalArgs);
}
function checkGoals(goals, source, origin) {
lastGoalArgs = [goals, source, origin]; // Memoize for checkRememberedGoals
// Check right now and also in one second, since our 1-second CSS transition might be affecting things until it is done.
doCheckGoals(goals, source, origin);
_.delay(function() { doCheckGoals(goals, source, origin); }, 1001);
}
function doCheckGoals(goals, source, origin) {
var newGoalStates = {};
var overallSuccess = true;
goals.forEach(function(goal) {
var $result = $(goal.html.selector);
//console.log('ran selector', goal.html.selector, 'to find element(s)', $result);
var success = true;
goal.html.valueChecks.forEach(function(check) {
//console.log(' ... and should make sure that the value of', check.eventProps, 'is', _.omit(check, 'eventProps'), '?', matchesCheck($result, check))
success = success && matchesCheck($result, check);
});
overallSuccess = overallSuccess && success;
newGoalStates[goal.id] = {status: success ? 'success' : 'incomplete'}; // No 'failure' state
});
if (!_.isEqual(newGoalStates, goalStates)) {
goalStates = newGoalStates;
var overallStatus = overallSuccess ? 'success' : null; // Can't really get to 'failure', just 'incomplete', which is represented by null here
source.postMessage({type: 'goals-updated', goalStates: goalStates, overallStatus: overallStatus}, origin);
}
}
function downTheChain(obj, keyChain) {
if (!obj)
return null;
if (!_.isArray(keyChain))
return obj[keyChain];
var value = obj;
while (keyChain.length && value) {
if (keyChain[0].match(/\(.*\)$/)) {
var args, argsString = keyChain[0].match(/\((.*)\)$/)[1];
if (argsString)
args = eval(argsString).split(/, ?/g).filter(function(x) { return x !== ''; }); // TODO: can/should we avoid eval here?
else
args = [];
value = value[keyChain[0].split('(')[0]].apply(value, args); // value.text(), value.css('background-color'), etc.
}
else
value = value[keyChain[0]];
keyChain = keyChain.slice(1);
}
return value;
}
function matchesCheck(value, check) {
var v = downTheChain(value, check.eventProps);
if ((check.equalTo != null) && v !== check.equalTo) {
return false;
}
if ((check.notEqualTo != null) && v === check.notEqualTo) {
return false;
}
if ((check.greaterThan != null) && !(v > check.greaterThan)) {
return false;
}
if ((check.greaterThanOrEqualTo != null) && !(v >= check.greaterThanOrEqualTo)) {
return false;
}
if ((check.lessThan != null) && !(v < check.lessThan)) {
return false;
}
if ((check.lessThanOrEqualTo != null) && !(v <= check.lessThanOrEqualTo)) {
return false;
}
if ((check.containingString != null) && (!v || v.search(check.containingString) === -1)) {
return false;
}
if ((check.notContainingString != null) && (v != null ? v.search(check.notContainingString) : void 0) !== -1) {
return false;
}
if ((check.containingRegexp != null) && (!v || v.search(new RegExp(check.containingRegexp)) === -1)) {
return false;
}
if ((check.notContainingRegexp != null) && (v != null ? v.search(new RegExp(check.notContainingRegexp)) : void 0) !== -1) {
return false;
}
return true;
}

View file

@ -19,6 +19,7 @@ var languagesImported = {};
var ensureLanguageImported = function(language) {
if (languagesImported[language]) return;
if (language === 'html') return;
importScripts("/javascripts/app/vendor/aether-" + language + ".js");
languagesImported[language] = true;
};

View file

@ -80,7 +80,7 @@ var myImportScripts = importScripts;
var languagesImported = {};
var ensureLanguageImported = function(language) {
if (languagesImported[language]) return;
if (language === 'javascript') return; // Only has JSHint, but we don't need to lint here.
if (language === 'javascript' || language === 'html') return; // Only has JSHint, but we don't need to lint here.
myImportScripts("/javascripts/app/vendor/aether-" + language + ".js");
languagesImported[language] = true;
};
@ -389,6 +389,8 @@ self.runWorld = function runWorld(args) {
self.world.preloading = args.preload;
self.world.headless = args.headless;
self.world.realTime = args.realTime;
self.world.indefiniteLength = args.indefiniteLength;
self.world.justBegin = args.justBegin;
self.goalManager = new GoalManager(self.world);
self.goalManager.setGoals(args.goals);
self.goalManager.setCode(args.userCodeMap);
@ -434,6 +436,9 @@ self.onWorldLoaded = function onWorldLoaded() {
var diff = t1 - self.t0;
var goalStates = self.goalManager.getGoalStates();
var totalFrames = self.world.totalFrames;
if(self.world.indefiniteLength) {
totalFrames = self.world.frames.length;
}
if(self.world.ended) {
var overallStatus = self.goalManager.checkOverallStatus();
var lastFrameHash = self.world.frames[totalFrames - 2].hash
@ -502,6 +507,11 @@ self.onWorldError = function onWorldError(error) {
}
else {
console.log("Non-UserCodeError:", error.toString() + "\n" + error.stack || error.stackTrace);
if(self.world.indefiniteLength) {
// We don't abort completely, since the player can always click to end the game.
// TODO: some better error to the user would be nice, though.
return true;
}
self.postMessage({type: 'non-user-code-problem', problem: {message: error.toString()}});
return false;
}

411
app/assets/markdown/cs1.md Normal file
View file

@ -0,0 +1,411 @@
###### Last updated: 08/23/2016
##### Lesson Plans
# Introduction to Computer Science
### Curriculum Summary
- Level: Beginner
- 4 x 45-60 minute coding sessions
#### Overview
With the right environment, learning the basics of formal syntax and typing code can be fun and intuitive for students as early as 3rd grade. Instead of block-based visual programming languages that hinder a students proper understanding of code, CodeCombat introduces real coding from the very first level. By strengthening their typing, syntax and debugging skills, we empower students to feel capable of building real programs successfully.
_This guide is written with Python-language classrooms in mind, but can easily be adapted for JavaScript._
### Scope and Sequence
| Module | Levels | Transfer Goals |
| ---------------------------------------------------------- | :----------------- | :--------------------------- |
| [1. Basic Syntax](#basic-syntax) | 1-6 | Call functions in order |
| [2. Loops](#loops) | 7-14 | Repeat code sequences |
| [3. Variables](#variables) | 15-20 | Save and access data |
| [4. Review - Multiplayer Arena](#review-multiplayer-arena) | 21 | Master syntax and sequencing |
### Core Vocabulary
**Basic Syntax** - the basic spelling and grammar of a language, and must be carefully paid attention to in order for code to properly execute. For example, while Python and JavaScript are used to do similar things in Course 1, the syntax for them is noticeably different, because they are different programming languages.
**Object** - a character or thing that can perform actions.
**String** - a type of programming data that represents text. In both Python and JavaScript, strings are represented by text inside quotes. In Course 1, strings are used to identify objects for the hero to attack.
**Function** - an action performed by an object.
**Argument** - extra information passed into a method in order to modify what the method does. In both Python and JavaScript, arguments are represented by code that is inside the parentheses after a method. In Course 1, arguments must be used to define enemies before the hero can attack them, and can also be used to move multiple times without writing new lines of code.
**Property** - data about or belonging to an object.
**While Loop** - used to repeat actions without the player needing to write the same lines of code over and over. In Python, the code that is looped must be indented underneath the while true statement. In JavaScript, the code that is looped must be enclosed by curly brackets {}. In Course 1, while loops repeat forever, and are used to navigate mazes made up of identical paths, as well as attack objects that take a lot of hits to defeat (strong Doors, for example).
**Variable** - a symbol that represents data, and the value of the variable can change as you store new data in it. In Course 1, variables are used to first define an enemy, and then passed along as an argument to the attack method so that the hero can attack the right enemy.
##### Module 1
## Basic Syntax 
### Summary
The puzzles in these levels are framed as mazes for students to solve using Computational Thinking and computer programming. They are designed to be a gentle introduction to Python syntax through a relatable medium. 
The hero starts at a particular place and has to navigate to the goal without running into spikes or being spotted by ogres. 
Some students may want to delete their code every time and only type the next step. Explain to them that the code must contain all the instructions from start to finish, like a story: it has a beginning, a middle, and an end. Every time you click Start, the hero returns to the beginning. 
### Transfer goals
- Use Python syntax
- Call functions
- Understand that order matters
### Standards 
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP6** Attend to precision.
### Instructive Activity: Basic Syntax (10 mins)
#### Explain (3 mins)
**Syntax** is how we write code. Just like spelling and grammar are important in writing prose, syntax is important when writing code. Humans are good at figuring out what something means, even if it isnt exactly correct, but computers arent that smart, and they need you to write with no mistakes. 
code example: `hero.moveRight()`
vocabulary:   (object) (function)
read aloud: “hero dot move right”
**Objects** are the building blocks of Python. They are things or characters that can perform actions. Your hero is an object. It can perform the moving actions.
**Functions** are actions an object can do. `moveRight()` is a function. Function names are always followed by parentheses. The order of functions matters!
#### Interact (5 mins): Recycling Robot
Practice giving written instructions using Python functions in order.
**Materials:** Desk or table, recycling bin, balls of paper to recycle
You (the teacher) are going to be the robot that the class controls using functions. The goal of this exercise is for the class to collectively write a program like this: 
``` python
teacher.pickUpBall()
teacher.turnRight()
teacher.moveForward()
teacher.moveForward()
teacher.turnLeft()
teacher.moveForward()
teacher.dropBall()
```
The experience should introduce them to Python syntax (including the dot between the object and function, and the parentheses at the end) and the importance of order in a sequence of instructions. 
At the front of the class, set some scrunched up paper balls on a flat surface. Place the recycling bin a few steps away. Explain that you are a recycling robot, and the classs job is to program you. 
The robot is a Python object. What is your name in Python? Whatever you choose, make sure it starts with a lower-case letter. Write it on the board. 
`teacher`
To make the robot perform an action, you have to call a function. Write a dot after your object name, then decide as a class what the first action should be. After the dot, write the function name followed by empty parentheses. Off to one side, draw a “Run” button.  
`teacher.pickUpBall()`
Have a volunteer press the “Run” button to run the program and test that it works. 
_It is important that you reset yourself and the paper balls every time the code is changed, and run the whole program from the beginning._
Invite students to add code to the program one at a time. If there is an error in the syntax, make a funny beeping sound and stop. Have the class work together to write and rewrite the program until you successfully get a ball in the recycling bin. 
#### Reflect (2 mins)
**Why is syntax important?** (It lets you be specific about exactly what you want to happen.)
**Does order matter?** (yes)
**Can a human understand the directions even if theres a mistake in the syntax?** (sometimes)
**Can a computer?** (no)
### Coding Time (30-45 mins)
**First time students will need to create accounts**
For additional information on helping students create account, see our [Teacher Getting Started Guide](http://files.codecombat.com/docs/resources/TeacherGettingStartedGuide.pdf).
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips.
If student have trouble breaking the problem down, refer to the [Engineering Cycle Worksheet [PDF]](http://files.codecombat.com/docs/resources/EngineeringCycleWorksheet.pdf) to reinforce the steps to solving each puzzle.
### Written reflection (5 mins)
Select appropriate prompt(s) for the students respond to, referring to their notes.
**Tell me how to play CodeCombat.**
>You have to move to the gem without hitting the spikes. I learned that you have to type “hero.” then the moving code. You have to spell it right and put () at the end. But it shows you the things you can type and you can click on them instead. You click RUN to make it go. You can try as many times as you need.
**Whats the difference between an object and a function?**
>The object is the hero and she has functions that are things she can do. The object has a dot after it and the function has (). 
**How can you tell when youve made a mistake in your code? How do you fix it?**
>Sometimes the code doesnt wont run because there is a mistake in it. They put a red ! next to the mistake and try to help you. You have to read the code to figure out whats wrong.
**Why is your hero in the Kithgard Dungeon? What is your quest? Are you a good guy or a bad guy?**
_(write your own backstory)_
>I went into the Kithgard Dungeon to steal gems from the ogres. I need to get a lot of gems to pay the ransom for my village, otherwise a big bully monster will destroy it and my family will be homeless. I think Im a good guy but the ogres probably think Im bad because Im stealing from them. 
##### Module 2
## Loops
### Summary
Up to now, students have had to write long sequences of actions with no shortcuts. These levels introduce loops, which allow them to achieve more with fewer lines of code. 
The puzzles in this section are harder to solve than in the first module. Encourage collaboration among your students, as they first must understand what their goal is, then devise a strategy for solving the level, then put that plan into action. 
### Transfer Goals
- Write an infinite loop
- Break a problem into smaller pieces
- Decide which parts of an action repeat
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP8** Look for and express regularity in repeated reasoning.
### Instructive Activity: Loops (10 mins)
#### Explain (3 mins)
A **loop** is a way of repeating code. One way of writing loops uses the keyword _while,_ followed by an **expression** that can be evaluated as True or False. _while_ is a special word that tells the computer to evaluate (or solve) what comes after it, and then do the actions indented underneath until the expression becomes False.
These levels in CodeCombat require an **infinite loop**, or a loop that repeats forever.  For that, we need an expression that is always true. Luckily, _True_ is a Python shortcut that always evaluates as True!
Below, `while` is the keyword, and `True` is the expression
``` python
while True: 
    hero.moveRight()  # action
    hero.moveUp()     # another action
```
You can put as many lines of code as you want inside the loop. They all have to be indented with four spaces. Thats how Python knows theyre part of the loop. Indentation is an important part of Python! Whenever you have a problem with your code, check the indentation first. 
#### Interact (5 mins)
As a class, think of as many ways as possible of writing a repeating action in English. (Use the following examples if students have a hard time thinking of their own.) 
Circle the English words that tell you its a loop. Rewrite these instructions using `while`. Check indentation. Label each part as keyword, expression, or action. Here are some examples to get you started:
Keep walking **until** you get to the door. _While you are not at the door, keep walking._
``` python
while door = 0: 
    walk()
```
Bounce the ball five **times**. _While bounces are less than 5, bounce the ball._
``` python
while bounces < 5 : 
    ball.bounce()
```
Put away **every** toy. _While there are still toys out, put a toy away._
``` python
while toys > 0: 
    putAway(toy)
```
Have students take turns writing, checking, and labelling the code until it becomes easy. 
#### Reflect (2 mins)
**What is a loop?** (a way of repeating actions)
**What is an expression?** (something that is True or False, usually using =, <, or >)
**How do you write a loop that never ends?** (Use `while True`)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips.
Focus on debugging, employing appropriate strategies for getting yourself unstuck. Use your class/schools growth mindset guidelines, use the [Engineering Cycle Worksheet [PDF]](http://files.codecombat.com/docs/resources/EngineeringCycleWorksheet.pdf) as an unblocking tool, or ask them to follow this list:
1. Read the comments line by line
2. Read your code line by line
3. Read the hints
4. Explain the problem youre having to a neighbor
5. Press the reload button and try again
6. Ask the teacher
### Written Reflection (5 mins)
**Tell me how you used a shortcut to save time and effort.**
>I used while True to make my code repeat forever. I had to remember to put four spaces on each line. Its good because you dont have to type all the code. 
**What are the things you have to remember to write an infinite loop?**
>You have to type while True, and remember to put a : after it. On the next line, put four spaces before your code. If you want more than one line to repeat, they all have to have four spaces. 
**Can you give me tips about solving these kinds of levels? Give an example.**
>You have to see what are the things that repeat. Sometimes it is just one thing, and sometimes it is lots of things. For example, in the Haunted Kithmaze you go to a dead end if you just put moveRight() in the loop because it just goes right, right, right forever. You also have to do moveUp() so it goes right, up, right, up. 
##### Module 3
## Variables
### Summary
These levels introduce the game mechanic of attacking. Attacks will not work unless you specify whom to attack (`hero.attack()` is wrong; `hero.attack(jeremy)` is correct.) 
Some of these puzzles can be hard for some students to wrap their heads around. Make sure they read the instructions thoroughly and understand the goal of each level. The challenge depends on not knowing the names of the objects you want to manipulate. Think of variables like nicknames for referring to objects when you dont know what else to call them.  
### Transfer Goals
- Create a variable
- Use a variable as an argument
- Choose appropriate variable names
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
### Instructive Activity: Variables (10 mins)
#### Explain (3 mins)
A **variable** holds your data for later. You make a variable by giving it a name, then saying what **value** it should hold. 
`enemy = “Kratt”`
The variable `enemy` holds (`=`) the value `"Kratt"`
Now you can use your variable instead of the value itself!
`hero.attack(“Kratt”)` is the same as `hero.attack(enemy)`
So a variable can stand in for a value. 
Variables can also be changed and checked. You could say `score = 0`, and then later `score = 1`. Or you could use your variable is in the expression for loop, i.e. `while score < 10`: 
#### Interact (5 mins)
As a class, discuss your preconceptions of the word “variable.” 
In math, it is a symbol that stands in for a number, which you are usually solving for.
In science, its a part of an experiment that can change and be observed. 
Which aspects of coding variables are like the math kind, and which are like science? 
#### Reflect (2 mins)
**How do you create a variable?** (variable = something)
**What can you use a variable for?** (Standing in for a value, checking it in a loop)
**Can you use a variable before you create it?** (No, it wont exist yet!)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips.
Focus on clearly communicating the goal of the level, and describing the problem they are currently facing. Remind students to read their code from start to end before asking you for help. Most problems can be solved by inserting missing quotation marks or fixing indentation. 
### Written Reflection (5 mins)
**What was the hardest puzzle you solved today? How did you solve it?**
>15 was a hard level. There were lots of enemies and I died. So I did a loop for attack, but I didnt know the name of who to attack. So I clicked on the glasses and it said I could use `findNearestEnemy`, but it didnt work without saying `enemy =`. Then I could `attack(enemy)` and it worked. 
**Write a user manual for findNearestEnemy.**
>The hero can see which enemy is closest by writing `hero.findNearestEnemy()`. But you have to remember which one it is in a variable. You can say `enemy = hero.findNearestEnemy()`. Then you can attack the enemy on the next line by saying `hero.attack(enemy)`. 
##### Module 4
## Review - Multiplayer Arena
### Summary 
The arena level is a reward for completing the required work. Students who have fallen behind in the levels or who have not completed their written reflections should use this time to finish. As students turn in their work, they can enter the Wakka Maul arena and attempt multiple solutions until time is called. 
### Transfer Goals
- Write accurate Python syntax
- Debug Python programs
- Refine solutions based on observations
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
**CCSS.Math.Practice.MP6** Attend to precision.
### Coding Time (40-55 mins)
Have students navigate to the last level, **Wakka Maul**, and complete it at their own pace. 
#### Rankings**
Once students beat the default computer they will be put in for the class ranking. Red teams only fight against blue teams and there will be top rankings for each. Students will only compete against the computer and other students in your CodeCombat class (not strangers).
Note that the class rankings are plainly visible. If some students are intimidated by competition or being publicly ranked, give them the option of a writing exercise instead: 
- Write a walkthrough or guide to your favorite level
- Write a review of the game
- Design a new level
#### Dividing the Class
Students must choose a team to join: Red or Blue.  It is important to divide the class as most students will choose red. It doesnt matter if the sides are even, but it is important that there ARE players for both sides. 
- Divide the class into two randomly by drawing from a deck of cards.
- Students who turn in their work early join the blue team, and latecomers play red.
#### Refining the Code
Code for Wakka Maul can be submitted more than once. Encourage your students to submit code, observe how it fares against their classmates, and then make improvements and resubmit. In addition, students who have finished the code for one team can go on to create code for the other team.
### Reflect (5 mins)
**Class discussion: How is coding a solution different from controlling a hero in real time?**
You have been playing a game that requires you to think about a whole plan in advance, then let the hero carry out your instructions without intervention. This differs dramatically from the traditional way of playing video games by directly controlling the hero and making decisions while the game is running. Talk about how these differences feel. Which is more fun? Which is harder? How does your strategy change? How do you deal with mistakes? 

597
app/assets/markdown/cs2.md Normal file
View file

@ -0,0 +1,597 @@
###### Last updated: 09/14/2016
##### Lesson Plans
# Computer Science 2
### Curriculum Summary
- Recommended Prerequisite: Introduction to Computer Science
- 6 x 45-60 minute coding sessions
#### Overview
Armed with basic knowledge of the structure and syntax of simple programs, students are ready to tackle more advanced topics. Conditionals, functions, and events, oh my! Computer Science 2 is where students move past the programming-toy stage into writing code similar to that they would use in the next major software or killer app!
In Computer Science 2, students will continue to learn the fundamentals, (basic syntax, arguments, strings, variables, and loops) as well as being introduced to a second level of concepts for them to master. If statements allow the student to perform different actions depending on the state of the battlefield. Functions let students organize their code into reusable pieces of logic, and once students can write basic functions, they can start writing code to handle events--which is the basis for lots of coding patterns in game development, web development, and app development.
_This guide is written with Python-language classrooms in mind, but can easily be adapted for JavaScript._
### Scope and Sequence
| Module | First Level | Transfer Goals |
| ----------------------------------------------------------- | :-------------------- | :--------------------------------- |
| [5. Conditionals (if)](#conditionals-if-) | Defense of Plainswood | Check expression before executing |
| [6. Conditionals (else)](#conditionals-else-) | Back to Back | Execute default code |
| [7. Nested Conditionals](#nested-conditionals) | Forest Fire Dancing | Put one conditional inside another |
| [8. Functions](#functions) | Village Rover | Save code for later |
| [9. Events](#events) | Backwoods Buddy | Listen for events and execute code |
| [10. Review - Multiplayer Arena](#review-multiplayer-arena) | Power Peak | Design and implement algorithms |
### Core Vocabulary
**Object** - a character or thing that can perform actions. Objects are the building blocks of Python. They are things or characters that can perform actions. Your `hero` is an object. It can perform the moving actions. In `hero.moveRight()`, the object is `hero`. In Course 2, students will also be using the `pet` object to perform actions.
**Function** - an action performed by an object. Functions are actions an object can do. `moveRight()` is a function. Function names are always followed by parentheses.
**Argument** - additional information for a function. Arguments are what we put inside the parentheses of a function. They tell the function more information about what it should do. In `hero.attack(enemy)`, `enemy` is the argument.
**Loop** - code that repeats. A loop is a way of repeating code. One way of writing loops uses the keyword `while`, followed by an expression that can be evaluated as `True` or `False`.
**Variable** - a holder for data. A variable holds your data for later. You create a variable by giving it a name, then saying what value it should hold.
**Conditional** - the building block of modern programming, the conditional. Its named as such because of its ability to check the conditions at the moment and perform different actions depending on the expression. The player is no longer able to assume there will be an enemy to attack, or if there is a gem to grab. Now, they need to check whether it exists, check if their abilities are ready, and check if an enemy is close enough to attack.
**Event** - an object representing something that happened. Students can write code to respond to events: when this type of event happens, run this function. This is called event handling, and it's a very useful programming pattern and an alternative to an infinite while-loop.
#### Extra activities for students who finish Course 2 early:
- Help someone else
- Refine a multiplayer arena strategy in Power Peak
- Write a walkthrough
- Write a review of the game
- Write a guide to their favorite level
- Design a new level
##### Module 5
## Conditionals (If)
### Summary
Course 2 introduces more advanced programming concepts, so the progress through the levels should be slower. Pay careful attention to the directions, so you know what the goal of the level is, and to the in-line comments (denoted with a `#`) so you know what code is missing.
### Transfer Goals
- Construct a conditional
- Choose appropriate expressions
- Evaluate expressions
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
**CCSS.Math.Practice.MP4** Model with mathematics.
**CCSS.Math.Practice.MP7** Look for and make use of structure.
### Instructive Activity: Conditionals (10 mins)
#### Explain (2 mins)
Conditionals carry out code depending on the state of the game. They start by evaluating a statement as `True` or `False`, then they carry out the code only if the statement is `True`. Notice that the syntax is similar to a loop, since it needs a colon and a four-space indent.
`if` is the keyword, and `==` is the expression
``` python
if enemy == “Kratt”:
attack(enemy) # This is the action
```
The keyword for a conditional is `if`. A conditional on its own will only happen once, but if you want it to keep checking, you have to put it inside a loop. Notice how the indentation works.
``` python
while true:
if enemy == “Kratt”:
attack(enemy)
```
#### Interact (5 mins)
Rewrite your classroom rules as conditionals using Python syntax.
Identify some school or classroom rules, and write them on the board, e.g.
- Raise your hand to ask a question.
- You get a detention if youre late.
- Stop talking when the teacher claps twice.
Reformulate them in English to start with the word “If”, e.g.
- **If** you have a question, then raise your hand.
- **If** youre late, then you get a detention.
- **If** the teacher claps twice, then stop talking.
Now reformulate again using Python syntax, e.g.
``` python
if student.hasQuestion():
student.raise(hand)
```
``` python
if student.arrivalTime > class.startTime:
teacher.giveDetention(student)
```
``` python
if teacher.claps == 2:
class.volume = 0
```
Label each of the parts of the conditionals: *keyword*, *expression*, *action*.
#### Explain (1 min)
Code is called code because were encoding our ideas into a language the computer can understand. You can use this three-step process of reformulating your ideas any time youre writing code. As long as you know the syntax of the programming language, you know what the encoded idea should look like!
#### Reflect (2 mins)
**Why do we need conditionals?** (Not all actions happen all the time)
**What is the part that comes between the if and the colon?** (an expression)
**Whats important about expressions?** (They have to be True or False)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips. Students will need to use (x,y) coordinates to specify locations. Exact coordinates can be found by placing the mouse pointer over the target position. Students will also have to use a conditional to check if a condition is met before taking an action.
### Written Reflection (5 mins)
**What does if mean? What kinds of things did you write after if?**
>If does the code only if its true. You can see if all kinds of things are true, like if your weapon is ready or if the enemy is close. Sometimes you need == or >, but sometimes you only need the ().
**If you could design a CodeCombat level, what would it look like?**
>There would be lots of ogres and you have to attack them, but not the humans. And you would protect the village by building walls and fires.
##### Module 6
## Conditionals (Else)
### Summary
These levels have two things going on at once. Students have to decide under which condition to do each action. This is a good point to have a tips & tricks discussion, where any student who wants to share a discovery or shortcut with the class may present their advice.
### Transfer Goals
- Construct an if-else conditional.
- Identify different actions taking place in different circumstances.
- Define `else` as the opposite of `if`.
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
**CCSS.Math.Practice.MP7** Look for and make use of structure.
**CCSS.Math.Practice.MP8** Look for and express regularity in repeated reasoning.
### Instructive Activity: Conditionals (Else) (10 mins)
#### Explain (2 mins)
Were used to using conditionals to do something if the expression is `True`, but what if its `False`? Thats where `else` comes in. `else` means “if not” or “otherwise” or “the opposite”.
Notice that `else` must be indented the same number of spaces as the if it goes with. And it also needs a colon `:` just like `if`.
Below, `if` and `else` are keywords, and `==` is the expression
``` python
if today == weekday:
goToSchool() # action
else: # keyword
watchCartoons() # action
```
#### Interact (6 mins)
Revisit the classroom rules from the previous lesson and see if any need else statements, e.g.
``` python
if student.hasQuestion():
student.raise(hand)
else:
student.payAttention()
```
``` python
if student.arrivalTime > class.startTime:
teacher.giveDetention(student)
else:
teacher.markPresent(student)
```
``` python
if teacher.claps == 2:
class.volume = 0
# this doesnt need an else because no action is taken if the teacher doesn't clap
```
Label the parts of these conditionals: _keywords_ (`if` and `else`), _expression_, _actions_
#### Reflect (2 mins)
**What does else mean?** (if not)
**Why doesnt else come with another expression?** (the expression is implied-- its the opposite of the if, or when the if is False)
**Do you always need an else?** (no, it depends on the situation)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips. The crux of these levels is in deciding what should happen and when it should happen. Students have to entertain multiple possibilities to figure out what the best course of action is under every condition.
### Written Reflection (5 mins)
**Do you know more code now than in the beginning? What powers do you have now that you couldnt do before?**
>In the beginning I could just walk around. Now I can attack enemies and see who is closest. I can also put my mouse on the screen and see the coordinates of where to go. I can use if and else to do two different things. And I can use a loop to make my code repeat.
**What advice can you give to someone just starting out the game?**
>Read the directions. First figure out what you want to do and then worry about the code. You have to put : after while True and if. And you have to use four spaces every time. Most of the time the level will tell you what to do in blue writing and you just have to do it. You can use your gems to buy stuff.
**What do you do when youre stuck?**
>I ask the person next to me if they have done this level. If I am ahead, I look at it some more until they catch up. Then we work on it together. Or I ask the teacher. Sometimes the answer is in the help or in the blue text.
##### Module 7
## Nested Conditionals
### Summary
Serious coding starts now. Students will have to remember how to construct conditionals and expressions, or refer to the tips below the code editor. These levels have three or more actions to control, so they require complex thinking and planning. Up to three levels of indentation are used, so checking spaces is vital to writing code that runs.
### Transfer Goals
- Construct a nested conditional
- Read and understand a nested conditional
- Attend to indentation
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
**CCSS.Math.Practice.MP6** Attend to precision.
**CCSS.Math.Practice.MP7** Look for and make use of structure.
### Instructive Activity: Nested Conditionals (10 mins)
#### Explain (3 mins)
We started out with conditionals checking one thing before we took an action. Then we had two actions to do and had to decide which one to do when. But sometimes you have more than two things you want to do. Thats when you want to put a conditional inside another conditional.
The first conditional below is `if it's a weekend`, the second (nested) conditional is `if I have a soccer game`.
``` python
if its a weekend:
if I have a soccer game:
Wake up at 6
else:
Sleep in
else:
Wake up at 7
```
Indentation starts to matter a lot now. We indent four spaces to put code inside a loop or conditional, and that includes other conditionals. The code inside the second conditional is indented a total of eight spaces.
#### Activity (5 mins)
Have students write the rules of their wake-up times, bedtimes, or recess times as nested conditionals. Do at least three different actions, so you have to use a nested conditional.
When they have finished, trade papers with a partner. Read each others schedules and discuss them. Check for syntax and indentation.
Invite volunteers to share their finished schedules with the class.
#### Reflect (2 mins)
**Why do we need nested conditionals?** (Because sometimes more than two different actions are possible)
**Why do we indent the second conditional by 4 spaces?** (To show that it is inside the first conditional.)
**What does it mean when an action is indented by 8 spaces?** (It depends on two expressions being True or False)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Make sure students are reading all the comments in the starter code before they start making changes. The goals are complex, so understanding each sub-goal is important. Encourage collaboration and allow students to help each other.
### Written Reflection (5 mins)
**Tell me about cleave.**
>Cleave smashes a bunch of enemies all around you. You do it by saying hero.cleave(). You have to put (), but you dont need to say cleave(enemy). It just does it to everyone. Cleave takes a while to warm up, so you can check if its ready with the watch, and if its not ready yet just do a normal attack.
**Debate: Is your hero a good guy or a bad guy?**
>My hero is sort of a good guy and sort of a bad guy. He is a good guy because he protects the villagers from getting hurt. But he is a bad guy because he stole the gems from the ogres in the dungeon. And he kills people. Maybe he should protect the people without killing and not steal.
### Writing Checkpoint: Conditionals
**What is a conditional? How many different ways can you write a conditional? Give an example.**
>A conditional asks “if.” You can say if something is true, then do something. You can use else if you want to do something if that first thing was not true. Elif is for if you want to do three things, like if its raining wear a jacket elif its snowing wear a hat else wear a t-shirt. You can put ifs inside other ifs but you have to remember the right number of spaces.
**What is elif? Is it an elf?**
>Elif means else if. You use it to do three things instead of two with if. Its like an elf because its tricky.
**Tell me about spaces.**
>You use four spaces to make code go inside a while True, if, else, or elif. If an if is inside another if, you have to use eight spaces. Its important to count the spaces and get them exactly right, or else the computer thinks you mean something different. You have to be really careful.
##### Module 8
## Functions
### Summary
These levels give students the chance to take some shortcuts. Just like loops gave them the power to write more code quickly, functions enable reuse of code. Syntax remains vital; so check that colons and indentation are in the right place, and remember to read and understand the directions for each level before starting to code a solution.
### Transfer Goals
- Identify functions.
- Construct a function definition.
- Call a function.
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
**CCSS.Math.Practice.MP7** Look for and make use of structure.
### Instructive Activity: Functions (10 mins)
#### Explain
Youve been using functions already! When you type `hero.cleave()`, `cleave()` is a function. So far youve only been using built-in functions, but you can also write your own. First, you need to define the function using `def`.
``` python
def getReady():
hero.wash(face)
hero.brush(teeth)
hero.putOn(armor)
```
Then you need to call the function.
``` python
getReady()
```
**What is the difference between defining and calling?** (Defining needs def before, and a colon after. Then it has some code indented under it. They both have parentheses.)
Programmers use functions to make their code easy to read and quick to write. Its sort of like a set play in basketball: you know how to shoot, dribble, and pass, so you can make up a function that combines those parts and give it a name.
``` python
def out-over-up():
p1.dribble()
p1.pass(p2)
p2.shoot()
```
Then when the coach wants this sequence of actions to happen, she just calls out the name of the play: “Out-over-up!”
### Interact (5 mins)
**Simon Says.**
As a class, write your own functions for complicated Simon Says moves on the board using Python syntax. Here are some examples to get you started:
``` python
def pogo():
student.handsOn(hips)
student.jump()
```
``` python
def popcorn():
if student.sittingDown():
student.standUp()
else:
student.sitDown()
```
Then, play Simon Says by calling the functions, e.g.
- Simon says raise your hand!
- Simon says popcorn!
- Pogo! (Simon didnt say)
### Reflect (2 mins)
**Why do functions make coding easier?** (Because you dont have to say the complicated steps every time; you can just use the function name.)
**Why is it important to give your functions good names?** (So you can remember what theyre for later.)
**What does the keyword def stand for?** (define, or make)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips.
These levels are all about writing good code. The helper code that is given to you may have the word `pass` in it. This is just so the sample code doesnt show any errors. Once the students fill in their code, they should delete `pass`. When you help debug their code, look for `pass` first.
### Written Reflection (5 mins)
**Why are functions useful? When would they not be useful?**
>They make it so you dont have to write the same code over and over and they make your code easier to read. I dont think its useful if youre just going to put one line of code in your function. It would be easier just to write that one line every time.
##### Module 9
## Events
### Summary
An **event** is an object representing something that happened. Students can write code to respond to events: when this type of event happens, run this function. This is called event handling, and it's a very useful programming pattern and an alternative to an infinite while-loop.
### Transfer Goals
- Listen for events and execute code
- Use event handling to control a pet
- Write concurrent code mixing direct execution and event handling
### Instructive Activity: President Teacher (12 mins)
#### Explain (2 mins)
Up until now, you have been writing code that executes once, from top to bottom: *first do this, then do this, then do that*. You also learned how to write while loops, where you can say, *then do this forever*. Using event handling, you now have a way to say, * **when** this happens, **then** do that*. It's kind of like an if-statement, except events can happen at any time, not just when you are checking for them.
#### Interact (8 mins)
Explain to the class that you're waiting for an important call from the White House about whether you've been elected the next President. You're going to write a program to answer the phone when it rings using a while loop and an if statement, but with no events yet:
``` python
while True:
if phone.isRinging:
teacher.answer(phone)
```
But that's boring, since you're not doing anything else. So you're going to grade their homework while you wait:
``` python
while True:
paper = teacher.findNextPaper()
teacher.grade(paper)
if phone.isRinging:
teacher.answer(phone)
```
Say that each paper takes five minutes to grade. Ask the class what will likely happen if you are running this program and you get a phone call from the White House. (You will probably be in the middle of grading the paper and will only check if the phone is ringing every five minutes, thus you'll probably miss the call and won't get to be President.)
Now rewrite the program to use event handling, explaining how you **listen** for events so that when they happen, you can **handle** them by running a function:
``` python
def answerPhone():
teacher.answer(phone)
phone.on("ring", answerPhone)
```
Explain that you pronounce this as, "On the `phone`'s `"ring"` event, run the `answerPhone` function." Now say you want to grade papers while you wait, you just add a while loop, and when the event happens, it will interrupt your grading so you can answer the phone and become President:
``` python
def answerPhone():
teacher.answer(phone)
phone.on("ring", answerPhone)
while True:
paper = teacher.findNextPaper()
teacher.grade(paper)
```
Explain that the `phone.on("ring", answerPhone)` makes your code start listening for the `"ring"` event, and note that you **don't use parentheses** on the function you are listening with: `answerPhone`, not `answerPhone()`. This is because you are telling the code the name of the function to run, but you are **not running it yet**. (The parentheses would run the function immediately.)
Ask the class for more examples of events and functions that could respond to them, and write them on the board, something like this:
``` python
student.on("wake", goBackToSleep)
dog.on("hear", obeyMaster)
goal.on("touchBall", increaseScore)
bigRedButton.on("press", initiateSelfDestruct)
```
#### Reflect (2 mins)
**What do you use event handling for?** (To run a function when something happens.)
**What kind of data is an event name?** (The event name you listen to is a string.)
**Why don't you use function parentheses when you start listening to an event?** (The parentheses would make the function run now, and you want to run it later when the event happens.)
### Coding Time (30-45 mins)
Allow the students to go through the game at their own pace, keeping notes about every level on paper or digital document. We recommend using following format, which you can also print out as templates: [Progress Journal [PDF]](http://files.codecombat.com/docs/resources/ProgressJournal.pdf)
```
Level #: _____ Level Name: ____________________________________
Goal: __________________________________________________________
What I did:
What I learned:
What was challenging:
```
Circulate to assist. Draw students attention to the instructions and tips. Make sure students are writing their functions above where they use them to start listening to events. It can be trick to keep track of what code is executing when events happen (your default program or the event handler function), so have students look at the white code execution highlights to see what line of code is being run at each time.
### Written Reflection (5 mins)
Select appropriate prompt(s) for the students respond to, referring to their notes.
**Tell me about the cat.**
>I got a pet cat and its a cougar or a lioness. There was a function that said meow, and the cat waited until you talked to it and then it said meow. I think the cat should help protect you from enemies. You should be able to make it do other stuff by commands, like pouncing and biting.
**Events are really useful when developing games. Guess the names of at least three kinds of events you think might happen in code for games you like to play.**p
>In Minecraft there might be an "explosion" event when a creeper blows up. In chess there might be a "checkmate" event. In Bejeweled there could be a "combo" event.
##### Module 10
## Review - Multiplayer Arena
### Summary
The arena level is a reward for completing the required work. Students who have fallen behind in the levels or who have not completed their written reflections should use this time to finish. As students turn in their work, they can enter the Power Peak arena and attempt multiple solutions until time is called.
### Transfer Goals
- Design an algorithm to solve a problem.
- Implement an algorithm in Python.
- Debug a Python program.
### Standards
**CCSS.Math.Practice.MP1** Make sense of problems and persevere in solving them.
**CCSS.Math.Practice.MP2** Reason abstractly and quantitatively.
**CCSS.Math.Practice.MP3** Construct viable arguments and critique the reasoning of others.
**CCSS.Math.Practice.MP5** Use appropriate tools strategically.
**CCSS.Math.Practice.MP6** Attend to precision.
### Instructive Activity: Engineering Cycle (10 mins)
#### Explain (3 mins)
Engineering is all about solving problems, but the first rule of engineering is that no one gets it right the first time. Thats where the Engineering Cycle comes in:
First, we DESIGN a solution to our problem. This includes figuring out what the problem is, and breaking it down into smaller parts. Then we IMPLEMENT this design, which putting our ideas into action with code. Third, we TEST our implementation. Does it work? Does it solve the problem? If our test fails, we have to decide if it was because of the DESIGN or the IMPLEMENTATION.
Then we keep designing, implementing, and testing until it the problem is solved!
#### Reflect (2 mins)
**What are the steps of the Engineering Cycle?** (Design, implement, test)
**When does the Engineering Cycle stop?** (When the problem is solved, or you run out of time)
#### Interact (5 mins)
As a class, make a list of all the things your hero can do (functions). Use appropriate vocabulary. Annotate with any tips or code snippets the students deem useful.
`moveUp()`, `moveDown()`, `moveLeft()`, `moveRight()`
`moveToXY(x,y)`
`attack(something)`
### Coding Time (30-45 mins)
Have students navigate to the last level, **Power Peak**, and complete it at their own pace.
#### Rankings
Once students beat the default computer they will be put in for the class ranking. Red teams only fight against blue teams and there will be top rankings for each. Students will only compete against the computer and other students in your CodeCombat class (not strangers).
Note that the class rankings are plainly visible. If some students are intimidated by competition or being publicly ranked, give them the option of a writing exercise instead:
- Write a walkthrough or guide to your favorite level
- Write a review of the game
- Design a new level
#### Dividing the Class
Students must choose a team to join: Red or Blue. It is important to divide the class as most students will choose red. It doesnt matter if the sides are even, but it is important that there ARE players for both sides.
- Divide the class into two randomly by drawing from a deck of cards.
- Students who turn in their work early join the blue team, and latecomers play red.
#### Refining the Code
Code for Power Peak can be submitted more than once. Encourage your students to submit code, observe how it fares against their classmates, and then make improvements and resubmit. In addition, students who have finished the code for one team can go on to create code for the other team.
Remind students to use the Engineering Cycle when working on their algorithms:
**DESIGN**: Make observations about the level. Make a list of requirements. Decide what part of the problem you will start with.
**IMPLEMENT**: Write the solution to that part of your problem in code. Tip: Use a different function to solve each part of the problem!
**TEST**: Does your code work? If not, fix your code. If it does, does it solve the right part of the problem? If not, redesign. If so, move on to the next part!
### Written Reflection (5 mins)
**Writing Checkpoint: What is code?**
>Code is when you type instructions to make the computer do things. Sometimes it gives you hints and completes the words for you. You have to spell everything right and indent the right number of spaces. Sometimes the puzzles are easy and sometimes they are hard. You have to make a plan for how to solve it, and then write the code exactly to make it work. The language we used is called Python. It has while True: to make your code repeat and if, else, and elif to make different things happen at different times.

1157
app/assets/markdown/cs3.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
##### Getting Started
# Start Using CodeCombat in 10 Minutes!
#### Get your class up and running with these steps.
##### STEP 1
## Create a Teacher Account
After youve created your Teacher account, you will be able to create classes, invite students, monitor students progress, enroll students, and assign course content once students have been enrolled.
Select the **Sign up as a Teacher** option during account creation in order to sign up as a teacher.
<img src="http://files.codecombat.com/docs/getting-started/teacher-account.png" title="Create a Teacher Account" alt="create-teacher-account-modal"/>
Once your teacher account is setup, youll be able to access your [Teacher Dashboard](/teachers/classes).
### What if I already have an account?
If you already have a CodeCombat account as a Student or Individual but want to convert it to a Teacher account, visit the [Update to Teacher Account](/teachers/update-account) page. Once youve converted, your student account will be removed from any classrooms you may have previously joined.
### What are the technical requirements for CodeCombat?
CodeCombat runs best on computers with at least 4GB of RAM, on a modern browser such as Chrome, Safari, Firefox, or Edge. Chromebooks with less RAM may have minor graphics issues in later courses.
*We do not currently support iPads or Android Tablets at this time.*
##### STEP 2
## Create a New Class
Once logged in, or if you click the Teacher link in the navigation bar, youll see your new [Teacher Dashboard](/teachers/classes). From here, youll be able to create classes and monitor your students progress.
Click the blue “Create a New Class” button, then choose a class name that will help you and your students identify the class, such as “Mr. Smith 3rd period.”
<img src="http://files.codecombat.com/docs/getting-started/create-new-class-modal.png" title="Create a New Class" alt="create-new-class-modal" width="400px" />
### Should I choose Python or JavaScript?
We recommend Python, because its both beginner-friendly and currently used by major corporations (such as Google). If you have younger or first-time learners, we strongly recommend Python.
JavaScript will work great too. Its the language of the web; used across every website, and still beginner-friendly. If you are planning to also study web development, you may prefer to choose JavaScript to avoid the confusion some students may have switching from one languages syntax to another. However, JavaScripts syntax is a little more difficult for beginners than Python.
##### STEP 3
## Add Students
Once youve created your class, youll see it under the list of Current Classes. Navigate the class where you want to add students, then choose one of three ways to add students.
<img src="http://files.codecombat.com/docs/getting-started/add-students.png" title="Add Students" alt="add-students" width="400px"/>
_Use your unique class code, class URL or invite students via email._
### Option 1: Invite Students via Email
*Easiest option if your students have email addresses*
Click the blue "Invite Students by Email" button, then enter your students email addresses (you can copy and paste this from your class list or student information system) and click "Invite Students". Students will receive an email instructing them to follow a link, which will allow them to create an account and join your class.
Make sure they are creating a **Student Account** and that the correct class name is displayed when they create their account.
<img src="http://files.codecombat.com/docs/getting-started/test-class.png" title="Create a Student Account" alt="create-student-account" width="400px"/>
_Students should see your class name in place of "Test Class 1"._
Students will need to enter the following to create a Student Account:
- First name
- Last initial
- Username (help them choose an appropriate unique username)
- Password
Email addresses are _not required_ for students creating an account when they have a valid Class Code from you. That said, using an email address is recommended if they have one, for easier password recovery.
*If your school uses Google Apps for Education*, students can connect using the red "Google" button at the top of the screen instead of manually entering their information. They can then sign in to CodeCombat using the G+ Connect button in the future.
After students have created their account, they are shown their username and instructed to write down this information.
Thats it! Students can now use their login credentials to start playing CodeCombat!
### Option 2: Students Join via a Class Code
*Easiest option if you students dont have email addresses*
Direct your students to [CodeCombat](https://codecombat.com) and have them click “Create Account” on the top navigation bar. Students should select the green "Sign up as a Student" button.
<img src="http://files.codecombat.com/docs/getting-started/student-account.png" title="Create a Student Account" alt="create-student-account" width="400px"/>
_Make sure your students click “Sign up as a Student” when prompted._
The website will request the “Class Code” for your class, which can be found if you click “View Class” on your Teacher Dashboard. Your classroom will have its own unique three-word code.
<img src="http://files.codecombat.com/docs/getting-started/class-code-example.png" title="Class Code Example" alt="class-code-example" width="400px"/>
_How to find your Class Code as a Teacher: Click on one of your classes, and look for the three-word Class Code next under “Adding Students”._
Once students enter your Class Code, they should see the correct class name and instructor on the screen.
Students will need to enter the following to create a Student Account:
- First name
- Last initial
- Username (help them choose an appropriate unique username)
- Password
Email addresses are _not required_ for students creating an account when they have a valid Class Code from you. That said, using an email address is recommended if they have one, for easier password recovery.
*If your school uses Google Apps for Education*, students can connect using the red "Google" button at the top of the screen instead of manually entering their information. They can then sign in to CodeCombat using the G+ Connect button in the future.
After students have created their account, they are shown their username and instructed to write down this information.
Thats it! Students can now use their login credentials to start playing CodeCombat!
##### STEP 4
## Introduction to Computer Science
All students are automatically granted access to the first course in CodeCombat, Introduction to Computer Science. This is a course that introduces students to concepts such as basic syntax, variables, and while loops. Generally this course takes about 1-3 hours for a middle school class.
<img src="http://files.codecombat.com/docs/getting-started/course-guides.png" title="Course Guides" alt="course-guides" width="400px"/>
_Course Guides allow you to preview course levels and view solutions._
As a teacher, you can access solutions for each course by going to [Course Guides](/teachers/courses/) (located in the blue teacher navigation bar). You can also preview every level using the dropdown selectors.
<img src="http://files.codecombat.com/docs/getting-started/resource-hub.png" title="Resource Hub" alt="resource-hub" width="400px"/>
_Course Guides allow you to preview course levels and view solutions._
If you're new to teaching computer science, we also recommend taking a look at the [Resource Hub](/teachers/resources), where you can find lesson plans, worksheets and supplemental guides to help you kickstart your classroom.
##### STEP 5
## Tracking Progress
<img src="http://files.codecombat.com/docs/getting-started/student-overview.png" title="Student Overview" alt="student-overview" width="400px"/>
_A high-level view of student progress in each course is displayed in the main class view._
After students join the class, youll see their progress appear in the individual classroom pages. Any assigned courses and each students progress in each course (starting with CS1, Introduction to Computer Science and onwards) is represented by a colored circle. A grey circle means a student has not begun any levels in that course, yellow circle means they have started working on the courses levels, and a green circle means that theyve completed all of the levels in the course.
<img src="http://files.codecombat.com/docs/getting-started/student-progress.png" title="Student Progress" alt="student-progress" width="400px"/>
_Navigate to the "Course Progress" tab to view more detailed information of student progress within each course._
If you want to see how your students are doing within a course, click on the “Course Progress” tab. Youll be able to view how much progress a student has made in a specific course. A gray circle means a level has not been started, a yellow circle indicates a level has been started but not completed, and a green circle means a level has been completed. By moving your mouse pointer over the circle for a level, you can see information about when they completed the level, as well as a rough estimate of how long the level took to complete.
##### STEP 6
## Licensing Students
Students are required to have a license to access any content after the first course. When you assign a new course, a license will automatically be applied to the student. By default, all licenses expire one year from when they are granted. A single license allows a single student access to all of the courses available.
To manually assign a license to a student, click on the *License Status* tab while viewing a class and use the "Assign License" button.
### How do I get Licenses for my students?
If you would like to purchase more licenses, click on *Student Licenses* in the Teacher Dashboard navigation bar, and follow the instructions under "Get More Licenses". One of CodeCombats specialists will be in contact with you shortly to discuss your needs.
##### STEP 7
## Assigning Courses
<img src="http://files.codecombat.com/docs/getting-started/bulk-assign.png" title="Bulk Assign Courses" alt="bulk-assign" width="400px"/>
Once a student is enrolled, youll be able to assign additional courses to them. We recommend not assigning students to more than one course ahead of where they currently are. You can bulk-assign a course to multiple students at a time by selecting students using checkboxes on the left-hand side (or the “Select All” checkbox), then choosing the appropriate course from the dropdown menu, and then clicking “Assign to Selected Students.”
##### STEP 8
## Start Teaching!
There are great supplemental materials for teachers available on our [Course Guides](/teachers/courses/) and [Resource Hub](/teachers/resources). If you're new to teaching computer science, we highly recommend checking these out -- we've built these with first-time teachers in mind. You can also browse our [Teacher Forums](https://discourse.codecombat.com/c/teachers), where you can discuss curriculum planning with other educators, share ideas, or ask questions.
You can also email us at [schools@codecombat.com](mailto:schools@codecombat.com) with any support questions or concerns!

View file

@ -0,0 +1,32 @@
##### Activity
# Pair Programming
### What is Pair Programming?
Pair programming is an exercise that real-life engineers use to collaborate on code together. One person is the **driver**, who controls the keyboard/mouse, and the other person is the **navigator**, who observes and plans. Pair programming is great because it teaches the value of communication while allowing two brains to work on the same problem at the same time. It also reduces bugs, encourages communication and listening skills, and reinforces existing concept mastery by allowing students to fill in each others' knowledge gaps.
*A great time to introduce Pair Programming is during an early challenging level like Haunted Kithmaze in Course 1. You can also suggest Pair Programming during later levels where there may be a larger mastery gap -- allow a student who has a firm grasp of the concept to be the Navigator.*
### What you need
- One workstation (computer, keyboard, mouse)
- One student to be the Driver
- One student to be the Navigator
### How to set up Pair Programming
1. Pair students up and assign one of them to be the **driver**, the other to be the **navigator**.
2. Remind students of each roles instructions: the **navigator** should not touch the keyboard/mouse, and the **driver** should communicate what they are doing.
3. Every 15 minutes, switch roles. If one player is much more experienced (or, say, has done all these levels before and is helping out), you can keep them as the navigator instead of switching them into the driver role.
4. Students should practice clear communication, patience and the spirit of collaboration.
### Discussion Questions
**Was it easier to be a Driver or a Navigator? Why?**
**What was challenging about pair programming?**
**What was useful about pair programming?**
**When would it be beneficial to pair program with a partner?**
### Additional Resources
[NCWIT: Pair Programming Activities](https://www.ncwit.org/resources/pair-programming-box-power-collaborative-learning)

View file

@ -0,0 +1,50 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>My CodeCombat Website</title>
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js" integrity="sha256-8SeyqJ7ZAZx8WnIgP/bgK6LGIjKjhojNPHSMV/fo29Y=" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script src="/javascripts/web-dev-listener.js"></script>
<script src="/javascripts/app/vendor/aether-html.js"></script>
<style>
@import 'https://fonts.googleapis.com/css?family=Holtwood+One+SC';
/* Import that font for demoing web-dev levels until @import ordering bug is fixed */
* {
transition: 1s ease-in-out;
}
</style>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
<!-- Extracted player/level styles and scripts -->
<style for="level-styles">
</style>
<script for="level-scripts">
</script>
<style for="player-styles">
</style>
<script for="player-scripts">
</script>
</head>
<body>
<h1>Loading...</h1>
</body>
</html>

View file

@ -15,3 +15,8 @@ module.exports = class Classrooms extends CocoCollection
options.data ?= {}
options.data.ownerID = me.id
@fetch(options)
fetchByOwner: (ownerID, options={}) ->
options.data ?= {}
options.data.ownerID = ownerID
@fetch(options)

View file

@ -33,4 +33,6 @@ module.exports = class CocoCollection extends Backbone.Collection
setProjection: (@project) ->
stringify: -> return JSON.stringify(@toJSON())
stringify: -> return JSON.stringify(@toJSON())
wait: (event) -> new Promise((resolve) => @once(event, resolve))

View file

@ -4,3 +4,8 @@ CocoCollection = require 'collections/CocoCollection'
module.exports = class Courses extends CocoCollection
model: Course
url: '/db/course'
fetchReleased: (options = {}) ->
options.data ?= {}
options.data.releasePhase = 'released'
@fetch(options)

View file

@ -3,3 +3,9 @@ CocoCollection = require 'collections/CocoCollection'
module.exports = class Patches extends CocoCollection
model: PatchModel
fetchMineFor: (targetModel, options={}) ->
options.url = "#{_.result(targetModel, 'url')}/patches"
options.data ?= {}
options.data.creator = me.id
@fetch(options)

View file

@ -1,6 +0,0 @@
module.exports = class RealTimeCollection extends Backbone.Firebase.Collection
constructor: (savePath) ->
# TODO: Don't hard code this here
# TODO: Use prod path in prod
@firebase = 'https://codecombat.firebaseio.com/test/db/' + savePath
super()

View file

@ -84,3 +84,5 @@ module.exports = class CocoClass
playSound: (trigger, volume=1) ->
Backbone.Mediator.publish 'audio-player:play-sound', trigger: trigger, volume: volume
wait: (event) -> new Promise((resolve) => @once(event, resolve))

View file

@ -18,6 +18,9 @@ class NameLoader extends CocoClass
loadedNames: (newNames) =>
_.extend namesCache, newNames
getName: (id) -> namesCache[id]?.name or id
getName: (id) ->
if namesCache[id]?.firstName and namesCache[id]?.lastName
return "#{namesCache[id]?.firstName} #{namesCache[id]?.lastName}"
namesCache[id]?.firstName or namesCache[id]?.name or id
module.exports = new NameLoader()

View file

@ -84,7 +84,6 @@ module.exports = ParticleMan = class ParticleMan extends CocoClass
addEmitter: (x, y, kind="level-dungeon-premium") ->
return if @unsupported
kind = kind.replace 'intro', 'dungeon'
options = $.extend true, {}, particleKinds[kind]
return console.error "Couldn't find particle configuration for", kind unless options.group
options.group.texture = THREE.ImageUtils.loadTexture "/images/common/particles/#{options.group.texture}.png"
@ -245,6 +244,12 @@ particleKinds['level-dungeon-game-dev'] = particleKinds['level-dungeon-game-dev-
colorMiddle: hsl 0.7, 0.75, 0.5
colorEnd: hsl 0.7, 0.75, 0.3
particleKinds['level-dungeon-web-dev'] = particleKinds['level-dungeon-web-dev-premium'] = ext particleKinds['level-dungeon-hero-ladder'],
emitter:
colorStart: hsl 0.7, 0.25, 0.7
colorMiddle: hsl 0.7, 0.25, 0.5
colorEnd: hsl 0.7, 0.25, 0.3
particleKinds['level-dungeon-premium-item'] = ext particleKinds['level-dungeon-gate'],
emitter:
particleCount: 2000
@ -300,6 +305,12 @@ particleKinds['level-forest-game-dev'] = particleKinds['level-forest-game-dev-pr
colorMiddle: hsl 0.7, 0.75, 0.5
colorEnd: hsl 0.7, 0.75, 0.3
particleKinds['level-forest-web-dev'] = particleKinds['level-forest-web-dev-premium'] = ext particleKinds['level-forest-hero-ladder'],
emitter:
colorStart: hsl 0.7, 0.25, 0.7
colorMiddle: hsl 0.7, 0.25, 0.5
colorEnd: hsl 0.7, 0.25, 0.3
particleKinds['level-forest-premium-item'] = ext particleKinds['level-forest-gate'],
emitter:
particleCount: 2000
@ -355,6 +366,12 @@ particleKinds['level-desert-game-dev'] = particleKinds['level-desert-game-dev-pr
colorMiddle: hsl 0.7, 0.75, 0.5
colorEnd: hsl 0.7, 0.75, 0.3
particleKinds['level-desert-web-dev'] = particleKinds['level-desert-web-dev-premium'] = ext particleKinds['level-desert-hero-ladder'],
emitter:
colorStart: hsl 0.7, 0.25, 0.7
colorMiddle: hsl 0.7, 0.25, 0.5
colorEnd: hsl 0.7, 0.25, 0.3
particleKinds['level-mountain-premium-hero'] = ext particleKinds['level-mountain-premium'],
emitter:
particleCount: 200
@ -395,6 +412,12 @@ particleKinds['level-mountain-game-dev'] = particleKinds['level-mountain-game-de
colorMiddle: hsl 0.7, 0.75, 0.5
colorEnd: hsl 0.7, 0.75, 0.3
particleKinds['level-mountain-web-dev'] = particleKinds['level-mountain-web-dev-premium'] = ext particleKinds['level-mountain-hero-ladder'],
emitter:
colorStart: hsl 0.7, 0.25, 0.7
colorMiddle: hsl 0.7, 0.25, 0.5
colorEnd: hsl 0.7, 0.25, 0.3
particleKinds['level-glacier-premium-hero'] = ext particleKinds['level-glacier-premium'],
emitter:
particleCount: 200
@ -435,6 +458,12 @@ particleKinds['level-glacier-game-dev'] = particleKinds['level-glacier-game-dev-
colorMiddle: hsl 0.7, 0.75, 0.5
colorEnd: hsl 0.7, 0.75, 0.3
particleKinds['level-glacier-web-dev'] = particleKinds['level-glacier-web-dev-premium'] = ext particleKinds['level-glacier-hero-ladder'],
emitter:
colorStart: hsl 0.7, 0.25, 0.7
colorMiddle: hsl 0.7, 0.25, 0.5
colorEnd: hsl 0.7, 0.25, 0.3
particleKinds['level-volcano-premium-hero'] = ext particleKinds['level-volcano-premium'],
emitter:
particleCount: 200
@ -474,3 +503,9 @@ particleKinds['level-volcano-game-dev'] = particleKinds['level-volcano-game-dev-
colorStart: hsl 0.7, 0.75, 0.7
colorMiddle: hsl 0.7, 0.75, 0.5
colorEnd: hsl 0.7, 0.75, 0.3
particleKinds['level-volcano-web-dev'] = particleKinds['level-volcano-web-dev-premium'] = ext particleKinds['level-volcano-hero-ladder'],
emitter:
colorStart: hsl 0.7, 0.25, 0.7
colorMiddle: hsl 0.7, 0.25, 0.5
colorEnd: hsl 0.7, 0.25, 0.3

View file

@ -1,5 +1,5 @@
go = (path, options) -> -> @routeDirectly path, arguments, options
redirect = (path) -> -> @navigate(path, { trigger: true, replace: true })
redirect = (path) -> -> @navigate(path + document.location.search, { trigger: true, replace: true })
utils = require './utils'
module.exports = class CocoRouter extends Backbone.Router
@ -16,7 +16,7 @@ module.exports = class CocoRouter extends Backbone.Router
return @routeDirectly 'play/CampaignView', ['picoctf'], {}
if utils.getQueryVariable 'hour_of_code'
return @navigate "/play", {trigger: true, replace: true}
return @routeDirectly('NewHomeView', [])
return @routeDirectly('HomeView', [])
'about': go('AboutView')
@ -34,9 +34,10 @@ module.exports = class CocoRouter extends Backbone.Router
'admin/design-elements': go('admin/DesignElementsView')
'admin/files': go('admin/FilesView')
'admin/analytics': go('admin/AnalyticsView')
'admin/school-counts': go('admin/SchoolCountsView')
'admin/analytics/subscriptions': go('admin/AnalyticsSubscriptionsView')
'admin/level-sessions': go('admin/LevelSessionsView')
'admin/school-counts': go('admin/SchoolCountsView')
'admin/school-licenses': go('admin/SchoolLicensesView')
'admin/users': go('admin/UsersView')
'admin/base': go('admin/BaseView')
'admin/demo-requests': go('admin/DemoRequestsView')
@ -50,8 +51,8 @@ module.exports = class CocoRouter extends Backbone.Router
'artisans/level-tasks': go('artisans/LevelTasksView')
'artisans/solution-problems': go('artisans/SolutionProblemsView')
'artisans/thang-tasks': go('artisans/ThangTasksView')
'beta': go('HomeView')
'artisans/level-concepts': go('artisans/LevelConceptMap')
'artisans/level-guides': go('artisans/LevelGuidesView')
'careers': => window.location.href = 'https://jobs.lever.co/codecombat'
'Careers': => window.location.href = 'https://jobs.lever.co/codecombat'
@ -71,15 +72,15 @@ module.exports = class CocoRouter extends Backbone.Router
'contribute/diplomat': go('contribute/DiplomatView')
'contribute/scribe': go('contribute/ScribeView')
'courses': go('courses/CoursesView')
'Courses': go('courses/CoursesView')
'courses/students': redirect('/courses')
'courses': redirect('/students') # Redirected 9/3/16
'Courses': redirect('/students') # Redirected 9/3/16
'courses/students': redirect('/students') # Redirected 9/3/16
'courses/teachers': redirect('/teachers/classes')
'courses/purchase': redirect('/teachers/licenses')
'courses/enroll(/:courseID)': redirect('/teachers/licenses')
'courses/update-account': go('courses/CoursesUpdateAccountView')
'courses/:classroomID': go('courses/ClassroomView', { studentsOnly: true })
'courses/:courseID/:courseInstanceID': go('courses/CourseDetailsView', { studentsOnly: true })
'courses/update-account': redirect('students/update-account') # Redirected 9/3/16
'courses/:classroomID': -> @navigate("/students/#{arguments[0]}", {trigger: true, replace: true}) # Redirected 9/3/16
'courses/:courseID/:courseInstanceID': -> @navigate("/students/#{arguments[0]}/#{arguments[1]}", {trigger: true, replace: true}) # Redirected 9/3/16
'db/*path': 'routeToServer'
'demo(/*subpath)': go('DemoView')
@ -103,6 +104,8 @@ module.exports = class CocoRouter extends Backbone.Router
'editor/thang-tasks': go('editor/ThangTasksView')
'editor/verifier': go('editor/verifier/VerifierView')
'editor/verifier/:levelID': go('editor/verifier/VerifierView')
'editor/course': go('editor/course/CourseSearchView')
'editor/course/:courseID': go('editor/course/CourseEditView')
'file/*path': 'routeToServer'
@ -111,7 +114,7 @@ module.exports = class CocoRouter extends Backbone.Router
'hoc': ->
# Matching /?hour_of_code=true behavior
@navigate "/play", {trigger: true, replace: true}
'home': go('NewHomeView')
'home': go('HomeView')
'i18n': go('i18n/I18NHomeView')
'i18n/thang/:handle': go('i18n/I18NEditThangTypeView')
@ -120,18 +123,19 @@ module.exports = class CocoRouter extends Backbone.Router
'i18n/achievement/:handle': go('i18n/I18NEditAchievementView')
'i18n/campaign/:handle': go('i18n/I18NEditCampaignView')
'i18n/poll/:handle': go('i18n/I18NEditPollView')
'i18n/course/:handle': go('i18n/I18NEditCourseView')
'identify': go('user/IdentifyView')
'legal': go('LegalView')
'multiplayer': go('MultiplayerView')
'play(/)': go('play/CampaignView') # extra slash is to get Facebook app to work
'play/ladder/:levelID/:leagueType/:leagueID': go('ladder/LadderView')
'play/ladder/:levelID': go('ladder/LadderView')
'play/ladder': go('ladder/MainLadderView')
'play/level/:levelID': go('play/level/PlayLevelView')
'play/game-dev-level/:levelID/:sessionID': go('play/level/PlayGameDevLevelView')
'play/web-dev-level/:levelID/:sessionID': go('play/level/PlayWebDevLevelView')
'play/spectate/:levelID': go('play/SpectateView')
'play/:map': go('play/CampaignView')
@ -139,24 +143,34 @@ module.exports = class CocoRouter extends Backbone.Router
'privacy': go('PrivacyView')
'schools': go('NewHomeView')
'seen': go('NewHomeView')
'SEEN': go('NewHomeView')
'schools': go('HomeView')
'seen': go('HomeView')
'SEEN': go('HomeView')
'students': go('courses/CoursesView', { redirectTeachers: true })
'students/update-account': go('courses/CoursesUpdateAccountView', { redirectTeachers: true })
'students/:classroomID': go('courses/ClassroomView', { redirectTeachers: true, studentsOnly: true })
'students/:courseID/:courseInstanceID': go('courses/CourseDetailsView', { redirectTeachers: true, studentsOnly: true })
'teachers': redirect('/teachers/classes')
'teachers/classes': go('courses/TeacherClassesView', { teachersOnly: true })
'teachers/classes/:classroomID': go('courses/TeacherClassView', { teachersOnly: true })
'teachers/courses': go('courses/TeacherCoursesView')
'teachers/demo': go('teachers/RequestQuoteView')
'teachers/classes': go('courses/TeacherClassesView', { redirectStudents: true, teachersOnly: true })
'teachers/classes/:classroomID': go('courses/TeacherClassView', { redirectStudents: true, teachersOnly: true })
'teachers/courses': go('courses/TeacherCoursesView', { redirectStudents: true })
'teachers/course-solution/:courseID/:language': go('teachers/TeacherCourseSolutionView', { redirectStudents: true })
'teachers/demo': go('teachers/RequestQuoteView', { redirectStudents: true })
'teachers/enrollments': redirect('/teachers/licenses')
'teachers/licenses': go('courses/EnrollmentsView', { teachersOnly: true })
'teachers/freetrial': go('teachers/RequestQuoteView')
'teachers/licenses': go('courses/EnrollmentsView', { redirectStudents: true, teachersOnly: true })
'teachers/freetrial': go('teachers/RequestQuoteView', { redirectStudents: true })
'teachers/quote': redirect('/teachers/demo')
'teachers/resources': go('teachers/ResourceHubView', { redirectStudents: true })
'teachers/resources/:name': go('teachers/MarkdownResourceView', { redirectStudents: true })
'teachers/signup': ->
return @routeDirectly('teachers/CreateTeacherAccountView', []) if me.isAnonymous()
return @navigate('/students', {trigger: true, replace: true}) if me.isStudent() and not me.isAdmin()
@navigate('/teachers/update-account', {trigger: true, replace: true})
'teachers/update-account': ->
return @navigate('/teachers/signup', {trigger: true, replace: true}) if me.isAnonymous()
return @navigate('/students', {trigger: true, replace: true}) if me.isStudent() and not me.isAdmin()
@routeDirectly('teachers/ConvertToTeacherAccountView', [])
'test(/*subpath)': go('TestView')
@ -174,6 +188,10 @@ module.exports = class CocoRouter extends Backbone.Router
@navigate e, {trigger: true}
routeDirectly: (path, args=[], options={}) ->
if options.redirectStudents and me.isStudent() and not me.isAdmin()
return @navigate('/students', {trigger: true, replace: true})
if options.redirectTeachers and me.isTeacher() and not me.isAdmin()
return @navigate('/teachers', {trigger: true, replace: true})
if options.teachersOnly and not (me.isTeacher() or me.isAdmin())
return @routeDirectly('teachers/RestrictedToTeachersView')
if options.studentsOnly and not (me.isStudent() or me.isAdmin())
@ -192,7 +210,7 @@ module.exports = class CocoRouter extends Backbone.Router
@listenToOnce application.moduleLoader, 'load-complete', ->
@routeDirectly(path, args, options)
return
return @openView @notFoundView() if not ViewClass
return go('NotFoundView') if not ViewClass
view = new ViewClass(options, args...) # options, then any path fragment args
view.render()
@openView(view)

View file

@ -96,7 +96,7 @@ module.exports = class Tracker extends CocoClass
trackPageView: (includeIntegrations=[]) ->
includeMixpanel = (name) ->
mixpanelIncludes = ['', 'schools', 'play', 'play/level/dungeons-of-kithgard']
mixpanelIncludes = []
name in mixpanelIncludes or /courses|students|teachers/ig.test(name)
name = Backbone.history.getFragment()
@ -181,6 +181,7 @@ module.exports = class Tracker extends CocoClass
ga? 'send', 'timing', category, variable, duration, label
updateRole: ->
return if me.isAdmin()
return unless me.isTeacher()
return require('core/services/segment')() unless @segmentLoaded
@identify()

View file

@ -44,42 +44,73 @@ window.console ?=
debug: ->
console.debug ?= console.log # Needed for IE10 and earlier
Application = initialize: ->
Router = require('core/Router')
@isProduction = -> document.location.href.search('https?://localhost') is -1
@isIPadApp = webkit?.messageHandlers? and navigator.userAgent?.indexOf('CodeCombat-iPad') isnt -1
$('body').addClass 'ipad' if @isIPadApp
$('body').addClass 'picoctf' if window.serverConfig.picoCTF
if $.browser.msie and parseInt($.browser.version) is 10
$("html").addClass("ie10")
@tracker = new Tracker()
@facebookHandler = new FacebookHandler()
@gplusHandler = new GPlusHandler()
@githubHandler = new GitHubHandler()
@moduleLoader = new ModuleLoader()
@moduleLoader.loadLanguage(me.get('preferredLanguage', true))
$(document).bind 'keydown', preventBackspace
preload(COMMON_FILES)
CocoModel.pollAchievements()
$.i18n.init {
lng: me.get('preferredLanguage', true)
fallbackLng: 'en'
resStore: locale
useDataAttrOptions: true
#debug: true
#sendMissing: true
#sendMissingTo: 'current'
#resPostPath: '/languages/add/__lng__/__ns__'
}, (t) =>
@router = new Router()
onIdleChanged = (to) => => Backbone.Mediator.publish 'application:idle-changed', idle: @userIsIdle = to
@idleTracker = new Idle
onAway: onIdleChanged true
onAwayBack: onIdleChanged false
onHidden: onIdleChanged true
onVisible: onIdleChanged false
awayTimeout: 5 * 60 * 1000
@idleTracker.start()
Application = {
initialize: ->
Router = require('core/Router')
@isProduction = -> document.location.href.search('https?://localhost') is -1
@isIPadApp = webkit?.messageHandlers? and navigator.userAgent?.indexOf('CodeCombat-iPad') isnt -1
$('body').addClass 'ipad' if @isIPadApp
$('body').addClass 'picoctf' if window.serverConfig.picoCTF
if $.browser.msie and parseInt($.browser.version) is 10
$("html").addClass("ie10")
@tracker = new Tracker()
@facebookHandler = new FacebookHandler()
@gplusHandler = new GPlusHandler()
@githubHandler = new GitHubHandler()
@moduleLoader = new ModuleLoader()
@moduleLoader.loadLanguage(me.get('preferredLanguage', true))
$(document).bind 'keydown', preventBackspace
preload(COMMON_FILES)
CocoModel.pollAchievements()
unless me.get('anonymous')
# TODO: Remove logging later, once this system has proved stable
me.on 'change:earned', (user, newEarned) ->
newEarned ?= {}
oldEarned = user.previous('earned') ? {}
if oldEarned.gems isnt newEarned.gems
console.log 'Gems changed', oldEarned.gems, '->', newEarned.gems
newLevels = _.difference(newEarned.levels, oldEarned.levels)
if newLevels.length
console.log 'Levels added', newLevels
newItems = _.difference(newEarned.items, oldEarned.items)
if newItems.length
console.log 'Items added', newItems
newHeroes = _.difference(newEarned.heroes, oldEarned.heroes)
if newHeroes.length
console.log 'Heroes added', newHeroes
me.on 'change:points', (user, newPoints) ->
console.log 'Points changed', user.previous('points'), '->', newPoints
@checkForNewAchievement()
$.i18n.init {
lng: me.get('preferredLanguage', true)
fallbackLng: 'en'
resStore: locale
useDataAttrOptions: true
#debug: true
#sendMissing: true
#sendMissingTo: 'current'
#resPostPath: '/languages/add/__lng__/__ns__'
}, (t) =>
@router = new Router()
onIdleChanged = (to) => => Backbone.Mediator.publish 'application:idle-changed', idle: @userIsIdle = to
@idleTracker = new Idle
onAway: onIdleChanged true
onAwayBack: onIdleChanged false
onHidden: onIdleChanged true
onVisible: onIdleChanged false
awayTimeout: 5 * 60 * 1000
@idleTracker.start()
checkForNewAchievement: ->
if me.get('lastAchievementChecked')
startFrom = new Date(me.get('lastAchievementChecked'))
else
startFrom = me.created()
daysSince = moment.duration(new Date() - startFrom).asDays()
if daysSince > 1
me.checkForNewAchievement().then => @checkForNewAchievement()
}
module.exports = Application
window.application = Application

View file

@ -89,8 +89,8 @@ expandFlattenedDelta = (delta, left, schema) ->
delta
module.exports.makeJSONDiffer = ->
hasher = (obj) -> if obj? then obj.name or obj.id or obj._id or JSON.stringify(_.keys(obj)) else 'null'
jsondiffpatch.create({objectHash: hasher})
objectHash = (obj) -> if obj? then (obj.name or obj.id or obj._id or JSON.stringify(_.keys(obj))) else 'null'
jsondiffpatch.create({objectHash})
module.exports.getConflicts = (headDeltas, pendingDeltas) ->
# headDeltas and pendingDeltas should be lists of deltas returned by expandDelta
@ -179,4 +179,4 @@ prunePath = (delta, path) ->
module.exports.DOC_SKIP_PATHS = [
'_id','version', 'commitMessage', 'parent', 'created',
'slug', 'index', '__v', 'patches', 'creator', 'js', 'watchers', 'levelsUpdated'
]
]

View file

@ -45,7 +45,7 @@ module.exports.applyErrorsToForm = (el, errors, warning=false) ->
for error in errors
if error.code is tv4.errorCodes.OBJECT_REQUIRED
prop = _.last(_.string.words(error.message)) # hack
message = 'Required field'
message = $.i18n.t('common.required_field')
else if error.dataPath
prop = error.dataPath[1..]

View file

@ -8,12 +8,12 @@ channelSchemas =
'errors': require 'schemas/subscriptions/errors'
'ipad': require 'schemas/subscriptions/ipad'
'misc': require 'schemas/subscriptions/misc'
'multiplayer': require 'schemas/subscriptions/multiplayer'
'play': require 'schemas/subscriptions/play'
'surface': require 'schemas/subscriptions/surface'
'tome': require 'schemas/subscriptions/tome'
'god': require 'schemas/subscriptions/god'
'scripts': require 'schemas/subscriptions/scripts'
'web-dev': require 'schemas/subscriptions/web-dev'
'world': require 'schemas/subscriptions/world'
definitionSchemas =
@ -165,5 +165,5 @@ window.onbeforeunload = (e) ->
return leavingMessage
else
return
$ -> init()

View file

@ -1,6 +1,11 @@
publishableKey = if application.isProduction() then 'pk_live_27jQZozjDGN1HSUTnSuM578g' else 'pk_test_zG5UwVu6Ww8YhtE9ZYh0JO6a'
if StripeCheckout?
if me.isAnonymous()
module.exports = {}
else if not StripeCheckout?
module.exports = {}
console.error "Failure loading StripeCheckout API, returning empty object."
else
module.exports = handler = StripeCheckout.configure({
key: publishableKey
name: 'CodeCombat'
@ -11,7 +16,4 @@ if StripeCheckout?
Backbone.Mediator.publish 'stripe:received-token', { token: token }
locale: 'auto'
})
else
module.exports = {}
console.error "Failure loading StripeCheckout API, returning empty object."
_.extend(handler, Backbone.Events)
_.extend(handler, Backbone.Events)

View file

@ -55,7 +55,7 @@ module.exports = FacebookHandler = class FacebookHandler extends CocoClass
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/sdk.js'
#js.src = '//connect.facebook.net/en_US/all/debug.js'
ref.parentNode.insertBefore js, ref
@ -63,12 +63,13 @@ module.exports = FacebookHandler = class FacebookHandler extends CocoClass
)(document)
window.fbAsyncInit = =>
FB.init
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
cookie: true # enable cookies to allow the server to access the session
xfbml: true # parse XFBML
version: 'v2.7'
})
FB.getLoginStatus (response) =>
if response.status is 'connected'
@connected = true

View file

@ -2,6 +2,7 @@ CocoModel = require 'models/CocoModel'
CocoCollection = require 'collections/CocoCollection'
{me} = require('core/auth')
locale = require 'locale/locale'
utils = require 'core/utils'
initializeFilePicker = ->
require('core/services/filepicker')() unless window.application.isIPadApp
@ -234,21 +235,14 @@ class ImageFileTreema extends TreemaNode.nodeMap.string
@refreshDisplay()
codeLanguages =
javascript: 'ace/mode/javascript'
coffeescript: 'ace/mode/coffee'
python: 'ace/mode/python'
lua: 'ace/mode/lua'
java: 'ace/mode/java'
class CodeLanguagesObjectTreema extends TreemaNode.nodeMap.object
childPropertiesAvailable: ->
(key for key in _.keys(codeLanguages) when not @data[key]? and not (key is 'javascript' and @workingSchema.skipJavaScript))
(key for key in _.keys(utils.aceEditModes) when not @data[key]? and not (key is 'javascript' and @workingSchema.skipJavaScript))
class CodeLanguageTreema extends TreemaNode.nodeMap.string
buildValueForEditing: (valEl, data) ->
super(valEl, data)
valEl.find('input').autocomplete(source: _.keys(codeLanguages), minLength: 0, delay: 0, autoFocus: true)
valEl.find('input').autocomplete(source: _.keys(utils.aceEditModes), minLength: 0, delay: 0, autoFocus: true)
valEl
class CodeTreema extends TreemaNode.nodeMap.ace
@ -256,8 +250,8 @@ class CodeTreema extends TreemaNode.nodeMap.ace
super(arguments...)
@workingSchema.aceTabSize = 4
# TODO: Find a less hacky solution for this
@workingSchema.aceMode = mode if mode = codeLanguages[@keyForParent]
@workingSchema.aceMode = mode if mode = codeLanguages[@parent?.data?.language]
@workingSchema.aceMode = mode if mode = utils.aceEditModes[@keyForParent]
@workingSchema.aceMode = mode if mode = utils.aceEditModes[@parent?.data?.language]
class CoffeeTreema extends CodeTreema
constructor: ->

13
app/core/urls.coffee Normal file
View file

@ -0,0 +1,13 @@
module.exports =
playDevLevel: ({level, session, course}) ->
shareURL = "#{window.location.origin}/play/#{level.get('type')}-level/#{level.get('slug')}/#{session.id}"
shareURL += "?course=#{course.id}" if course
return shareURL
courseArenaLadder: ({level, courseInstance}) ->
"/play/ladder/#{level.get('slug')}/course/#{courseInstance.id}"
courseLevel: ({level, courseInstance}) ->
url = "/play/level/#{level.get('slug')}?course=#{courseInstance.get('courseID')}&course-instance=#{courseInstance.id}"
url += "&codeLanguage=#{level.get('primerLanguage')}" if level.get('primerLanguage')
url

View file

@ -18,6 +18,17 @@ module.exports.combineAncestralObject = (obj, propertyName) ->
obj = Object.getPrototypeOf(obj)
combined
module.exports.courseIDs = courseIDs =
INTRODUCTION_TO_COMPUTER_SCIENCE: '560f1a9f22961295f9427742'
COMPUTER_SCIENCE_2: '5632661322961295f9428638'
GAME_DEVELOPMENT_1: '5789587aad86a6efb573701e'
WEB_DEVELOPMENT_1: '5789587aad86a6efb573701f'
COMPUTER_SCIENCE_3: '56462f935afde0c6fd30fc8c'
GAME_DEVELOPMENT_2: '57b621e7ad86a6efb5737e64'
WEB_DEVELOPMENT_2: '5789587aad86a6efb5737020'
COMPUTER_SCIENCE_4: '56462f935afde0c6fd30fc8d'
COMPUTER_SCIENCE_5: '569ed916efa72b0ced971447'
module.exports.normalizeFunc = (func_thing, object) ->
# func could be a string to a function in this class
# or a function in its own right
@ -49,6 +60,10 @@ toHex = (n) ->
h = '0'+h if h.length is 1
h
module.exports.pathToUrl = (path) ->
base = location.protocol + '//' + location.hostname + (location.port && ":" + location.port)
base + path
module.exports.i18n = (say, target, language=me.get('preferredLanguage', true), fallback='en') ->
generalResult = null
fallBackResult = null
@ -184,6 +199,10 @@ if document?.createElement
return
)(document)
# So that we can stub out userAgent in tests
module.exports.userAgent = ->
window.navigator.userAgent
module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) ->
query = document.location.search.substring 1
pairs = (pair.split('=') for pair in query.split '&')
@ -259,7 +278,7 @@ startsWithVowel = (s) -> s[0] in 'aeiouAEIOU'
module.exports.filterMarkdownCodeLanguages = (text, language) ->
return '' unless text
currentLanguage = language or me.get('aceConfig')?.language or 'python'
excludedLanguages = _.without ['javascript', 'python', 'coffeescript', 'clojure', 'lua', 'java', 'io'], currentLanguage
excludedLanguages = _.without ['javascript', 'python', 'coffeescript', 'clojure', 'lua', 'java', 'io', 'html'], currentLanguage
# Exclude language-specific code blocks like ```python (... code ...)``` for each non-target language.
codeBlockExclusionRegex = new RegExp "```(#{excludedLanguages.join('|')})\n[^`]+```\n?", 'gm'
# Exclude language-specific images like ![python - image description](image url) for each non-target language.
@ -290,13 +309,15 @@ module.exports.filterMarkdownCodeLanguages = (text, language) ->
return text
module.exports.aceEditModes = aceEditModes =
'javascript': 'ace/mode/javascript'
'coffeescript': 'ace/mode/coffee'
'python': 'ace/mode/python'
'java': 'ace/mode/java'
'lua': 'ace/mode/lua'
'java': 'ace/mode/java'
javascript: 'ace/mode/javascript'
coffeescript: 'ace/mode/coffee'
python: 'ace/mode/python'
lua: 'ace/mode/lua'
java: 'ace/mode/java'
html: 'ace/mode/html'
# These ACEs are used for displaying code snippets statically, like in SpellPaletteEntryView popovers
# and have short lifespans
module.exports.initializeACE = (el, codeLanguage) ->
contents = $(el).text().trim()
editor = ace.edit el
@ -325,6 +346,7 @@ module.exports.capitalLanguages = capitalLanguages =
'python': 'Python'
'java': 'Java'
'lua': 'Lua'
'html': 'HTML'
module.exports.createLevelNumberMap = (levels) ->
levelNumberMap = {}
@ -371,6 +393,24 @@ module.exports.findNextLevel = (levels, currentIndex, needsPractice) ->
module.exports.needsPractice = (playtime=0, threshold=2) ->
playtime / 60 > threshold
module.exports.sortCourses = (courses) ->
orderedIDs = [
courseIDs.INTRODUCTION_TO_COMPUTER_SCIENCE
courseIDs.COMPUTER_SCIENCE_2
courseIDs.GAME_DEVELOPMENT_1
courseIDs.WEB_DEVELOPMENT_1
courseIDs.COMPUTER_SCIENCE_3
courseIDs.GAME_DEVELOPMENT_2
courseIDs.WEB_DEVELOPMENT_2
courseIDs.COMPUTER_SCIENCE_4
courseIDs.COMPUTER_SCIENCE_5
]
_.sortBy courses, (course) ->
# ._id can be from classroom.courses, otherwise it's probably .id
index = orderedIDs.indexOf(course.id ? course._id)
index = 9001 if index is -1
index
module.exports.usStateCodes =
# https://github.com/mdzhang/us-state-codes
# generated by js2coffee 2.2.0
@ -470,3 +510,4 @@ module.exports.usStateCodes =
getStateCodeByStateName: getStateCodeByStateName
}
)()

View file

@ -111,7 +111,9 @@ module.exports = class Angel extends CocoClass
when 'user-code-problem'
@publishGodEvent 'user-code-problem', problem: event.data.problem
when 'world-load-progress-changed'
@publishGodEvent 'world-load-progress-changed', progress: event.data.progress
progress = event.data.progress
progress = Math.min(progress, 0.9) if @work.indefiniteLength
@publishGodEvent 'world-load-progress-changed', { progress }
unless event.data.progress is 1 or @work.preload or @work.headless or @work.synchronous or @deserializationQueue.length or (@shared.firstWorld and not @shared.spectate)
@worker.postMessage func: 'serializeFramesSoFar' # Stream it!
@ -138,6 +140,7 @@ module.exports = class Angel extends CocoClass
return if @aborting
# Toggle BOX2D_ENABLED during deserialization so that if we have box2d in the namespace, the Collides Components still don't try to create bodies for deserialized Thangs upon attachment.
window.BOX2D_ENABLED = false
streamingWorld?.indefiniteLength = @work.indefiniteLength
@streamingWorld = World.deserialize serialized, @shared.worldClassMap, @shared.lastSerializedWorldFrames, @finishBeholdingWorld(goalStates), startFrame, endFrame, @work.level, streamingWorld
window.BOX2D_ENABLED = true
@shared.lastSerializedWorldFrames = serialized.frames
@ -145,7 +148,10 @@ module.exports = class Angel extends CocoClass
finishBeholdingWorld: (goalStates) -> (world) =>
return if @aborting or @destroyed
finished = world.frames.length is world.totalFrames
firstChangedFrame = world.findFirstChangedFrame @shared.world
if @work?.indefiniteLength and world.victory?
finished = true
world.totalFrames = world.frames.length
firstChangedFrame = if @work?.indefiniteLength then 0 else world.findFirstChangedFrame @shared.world
eventType = if finished then 'new-world-created' else 'streaming-world-updated'
if finished
@shared.world = world
@ -228,7 +234,7 @@ module.exports = class Angel extends CocoClass
@running = false
@work = null
@streamingWorld = null
@deserializationQueue = null
@deserializationQueue = []
_.remove @shared.busyAngels, @
@abortTimeout = _.delay @fireWorker, @abortTimeoutDuration
@aborting = true
@ -248,7 +254,7 @@ module.exports = class Angel extends CocoClass
@initialized = false
@work = null
@streamingWorld = null
@deserializationQueue = null
@deserializationQueue = []
@hireWorker() if rehire
hireWorker: ->

View file

@ -22,6 +22,7 @@ module.exports = Bus = class Bus extends CocoClass
'auth:me-synced': 'onMeSynced'
connect: ->
# Put Firebase back in bower if you want to use this
Backbone.Mediator.publish 'bus:connecting', {bus: @}
Firebase.goOnline()
@fireRef = new Firebase(Bus.fireHost + '/' + @docName)

View file

@ -20,6 +20,7 @@ module.exports = class God extends CocoClass
options ?= {}
@retrieveValueFromFrame = _.throttle @retrieveValueFromFrame, 1000
@gameUIState ?= options.gameUIState or new GameUIState()
@indefiniteLength = options.indefiniteLength or false
super()
# Angels are all given access to this.
@ -71,9 +72,9 @@ module.exports = class God extends CocoClass
@lastFixedSeed = e.fixedSeed
@lastFlagHistory = (flag for flag in e.flagHistory when flag.source isnt 'code')
@lastDifficulty = e.difficulty
@createWorld e.spells, e.preload, e.realTime
@createWorld e.spells, e.preload, e.realTime, e.justBegin
createWorld: (spells, preload, realTime) ->
createWorld: (spells, preload, realTime, justBegin) ->
console.log "#{@nick}: Let there be light upon #{@level.name}! (preload: #{preload})"
userCodeMap = @getUserCodeMap spells
@ -94,9 +95,9 @@ module.exports = class God extends CocoClass
return if hadPreloader
@angelsShare.workQueue = []
work =
work = {
userCodeMap: userCodeMap
level: @level
@level
levelSessionIDs: @levelSessionIDs
submissionCount: @lastSubmissionCount
fixedSeed: @lastFixedSeed
@ -104,9 +105,12 @@ module.exports = class God extends CocoClass
difficulty: @lastDifficulty
goals: @angelsShare.goalManager?.getGoals()
headless: @angelsShare.headless
preload: preload
preload
synchronous: not Worker? # Profiling world simulation is easier on main thread, or we are IE9.
realTime: realTime
realTime
justBegin
indefiniteLength: @indefiniteLength and realTime
}
@angelsShare.workQueue.push work
angel.workIfIdle() for angel in @angelsShare.angels
work
@ -114,9 +118,7 @@ module.exports = class God extends CocoClass
getUserCodeMap: (spells) ->
userCodeMap = {}
for spellKey, spell of spells
for thangID, spellThang of spell.thangs
continue if spellThang.thang?.programmableMethods[spell.name].cloneOf
(userCodeMap[thangID] ?= {})[spell.name] = spellThang.aether.serialize()
(userCodeMap[spell.thang.thang.id] ?= {})[spell.name] = spell.thang.aether.serialize()
userCodeMap

View file

@ -41,7 +41,6 @@ module.exports = class LevelBus extends Bus
@fireScriptsRef = @fireRef?.child('scripts')
setSession: (@session) ->
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
@timerIntervalID = setInterval(@incrementSessionPlaytime, 1000)
onIdleChanged: (e) ->
@ -53,8 +52,7 @@ module.exports = class LevelBus extends Bus
@session.set('playtime', (@session.get('playtime') ? 0) + 1)
onPoint: ->
return true unless @session?.get('multiplayer')
super()
return true
onMeSynced: =>
super()
@ -236,17 +234,11 @@ module.exports = class LevelBus extends Bus
@changedSessionProperties.chat = true
@saveSession()
onMultiplayerChanged: ->
@changedSessionProperties.multiplayer = true
@session.updatePermissions()
@changedSessionProperties.permissions = true
@saveSession()
# Debounced as saveSession
reallySaveSession: ->
return if _.isEmpty @changedSessionProperties
# don't let peeking admins mess with the session accidentally
return unless @session.get('multiplayer') or @session.get('creator') is me.id
return unless @session.get('creator') is me.id
return if @session.fake
Backbone.Mediator.publish 'level:session-will-save', session: @session
patch = {}

View file

@ -12,7 +12,7 @@ app = require 'core/application'
World = require 'lib/world/world'
utils = require 'core/utils'
LOG = false
LOG = me.get('name') is 'Shanakin' # Debugging a hanging load issue in production
# This is an initial stab at unifying loading and setup into a single place which can
# monitor everything and keep a LoadingScreen visible overall progress.
@ -22,6 +22,11 @@ LOG = false
# * Sprite map generation
# * Connecting to Firebase
# LevelLoader depends on SuperModel retrying timed out requests, as these occasionally happen during play.
# If LevelLoader ever moves away from SuperModel, it will have to manage its own retries.
reportedLoadErrorAlready = false
module.exports = class LevelLoader extends CocoClass
constructor: (options) ->
@ -49,10 +54,21 @@ module.exports = class LevelLoader extends CocoClass
if @supermodel.finished()
@onSupermodelLoaded()
else
@loadTimeoutID = setTimeout @reportLoadError.bind(@), 30000
@listenToOnce @supermodel, 'loaded-all', @onSupermodelLoaded
# Supermodel (Level) Loading
loadWorldNecessities: ->
# TODO: Actually trigger loading, instead of in the constructor
new Promise((resolve, reject) =>
return resolve(@) if @world
@once 'world-necessities-loaded', => resolve(@)
@once 'world-necessity-load-failed', ({resource}) ->
{ jqxhr } = resource
reject({message: jqxhr.responseJSON?.message or jqxhr.responseText or 'Unknown Error'})
)
loadLevel: ->
@level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID
if @level.loaded
@ -61,10 +77,25 @@ module.exports = class LevelLoader extends CocoClass
@level = @supermodel.loadModel(@level, 'level').model
@listenToOnce @level, 'sync', @onLevelLoaded
reportLoadError: ->
window.tracker?.trackEvent 'LevelLoadError',
category: 'Error',
levelSlug: @work?.level?.slug,
unloaded: JSON.stringify(@supermodel.report().map (m) -> _.result(m.model, 'url'))
onLevelLoaded: ->
if not @sessionless and @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop', 'course']
if not @sessionless and @level.isType('hero', 'hero-ladder', 'hero-coop', 'course')
@sessionDependenciesRegistered = {}
if (@courseID and @level.get('type', true) not in ['course', 'course-ladder']) or window.serverConfig.picoCTF
if @level.isType('web-dev')
@headless = true
if @sessionless
# When loading a web-dev level in the level editor, pretend it's a normal hero level so we can put down our placeholder Thang.
# TODO: avoid this whole roundabout Thang-based way of doing web-dev levels
originalGet = @level.get
@level.get = ->
return 'hero' if arguments[0] is 'type'
originalGet.apply @, arguments
if (@courseID and not @level.isType('course', 'course-ladder', 'game-dev', 'web-dev')) or window.serverConfig.picoCTF
# Because we now use original hero levels for both hero and course levels, we fake being a course level in this context.
originalGet = @level.get
@level.get = ->
@ -122,7 +153,8 @@ module.exports = class LevelLoader extends CocoClass
url += "?course=#{@courseID}" if @courseID
session = new LevelSession().setURL url
session.project = ['creator', 'team', 'heroConfig', 'codeLanguage', 'submittedCodeLanguage', 'state', 'submittedCode'] if @headless
if @headless and not @level.isType('web-dev')
session.project = ['creator', 'team', 'heroConfig', 'codeLanguage', 'submittedCodeLanguage', 'state', 'submittedCode', 'submitted']
@sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false})
@session = @sessionResource.model
if @opponentSessionID
@ -169,7 +201,7 @@ module.exports = class LevelLoader extends CocoClass
@consolidateFlagHistory() if @opponentSession?.loaded
else if session is @opponentSession
@consolidateFlagHistory() if @session.loaded
if @level.get('type', true) in ['course'] # course-ladder is hard to handle because there's 2 sessions
if @level.isType('course') # course-ladder is hard to handle because there's 2 sessions
heroThangType = me.get('heroConfig')?.thangType or ThangType.heroes.captain
console.log "Course mode, loading custom hero: ", heroThangType if LOG
url = "/db/thang.type/#{heroThangType}/version"
@ -177,8 +209,12 @@ module.exports = class LevelLoader extends CocoClass
console.log "Pushing resource: ", heroResource if LOG
@worldNecessities.push heroResource
@sessionDependenciesRegistered[session.id] = true
unless @level.isType('hero', 'hero-ladder', 'hero-coop')
# Return before loading heroConfig ThangTypes. Finish if all world necessities were completed by the time the session loaded.
if @checkAllWorldNecessitiesRegisteredAndLoaded()
@onWorldNecessitiesLoaded()
return
return unless @level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']
# Load the ThangTypes needed for the session's heroConfig for these types of levels
heroConfig = session.get('heroConfig')
heroConfig ?= me.get('heroConfig') if session is @session and not @headless
heroConfig ?= {}
@ -332,8 +368,9 @@ module.exports = class LevelLoader extends CocoClass
@worldNecessities = (r for r in @worldNecessities when r?)
@onWorldNecessitiesLoaded() if @checkAllWorldNecessitiesRegisteredAndLoaded()
onWorldNecessityLoadFailed: (resource) ->
@trigger('world-necessity-load-failed', resource: resource)
onWorldNecessityLoadFailed: (event) ->
@reportLoadError()
@trigger('world-necessity-load-failed', event)
checkAllWorldNecessitiesRegisteredAndLoaded: ->
return false unless _.filter(@worldNecessities).length is 0
@ -344,6 +381,8 @@ module.exports = class LevelLoader extends CocoClass
onWorldNecessitiesLoaded: ->
console.log "World necessities loaded." if LOG
return if @initialized
@initialized = true
@initWorld()
@supermodel.clearMaxProgress()
@trigger 'world-necessities-loaded'
@ -371,6 +410,7 @@ module.exports = class LevelLoader extends CocoClass
@supermodel.loadModel(model, resourceName)
onSupermodelLoaded: ->
clearTimeout @loadTimeoutID
return if @destroyed
console.log 'SuperModel for Level loaded in', new Date().getTime() - @t0, 'ms' if LOG
@loadLevelSounds()
@ -401,7 +441,8 @@ module.exports = class LevelLoader extends CocoClass
resource.markLoaded() if resource.spriteSheetKeys.length is 0
denormalizeSession: ->
return if @headless or @sessionDenormalized or @spectateMode or @sessionless or me.isSessionless()
return if @sessionDenormalized or @spectateMode or @sessionless or me.isSessionless()
return if @headless and not @level.isType('web-dev')
# This is a way (the way?) PUT /db/level.sessions/undefined was happening
# See commit c242317d9
return if not @session.id
@ -443,7 +484,7 @@ module.exports = class LevelLoader extends CocoClass
@grabTeamConfigs()
@thangTypeTeams = {}
for thang in @level.get('thangs')
if @level.get('type', true) in ['hero', 'course'] and thang.id is 'Hero Placeholder'
if @level.isType('hero', 'course') and thang.id is 'Hero Placeholder'
continue # No team colors for heroes on single-player levels
for component in thang.components
if team = component.config?.team
@ -469,8 +510,7 @@ module.exports = class LevelLoader extends CocoClass
# World init
initWorld: ->
return if @initialized
@initialized = true
return if @level.isType('web-dev')
@world = new World()
@world.levelSessionIDs = if @opponentSessionID then [@sessionID, @opponentSessionID] else [@sessionID]
@world.submissionCount = @session?.get('state')?.submissionCount ? 0

View file

@ -74,7 +74,7 @@ module.exports = class LevelSetupManager extends CocoClass
@session.set 'heroConfig', {"thangType":raider,"inventory":{}}
@onInventoryModalPlayClicked()
return
if @level.get('type', true) in ['course', 'course-ladder'] or window.serverConfig.picoCTF
if @level.isType('course', 'course-ladder', 'game-dev', 'web-dev') or window.serverConfig.picoCTF
@onInventoryModalPlayClicked()
return
@heroesModal = new PlayHeroesModal({supermodel: @supermodel, session: @session, confirmButtonI18N: 'play.next', level: @level, hadEverChosenHero: @options.hadEverChosenHero})

View file

@ -6,10 +6,10 @@ mapred = (left, right, func) ->
result or (_.reduce (_.map right, (singleRight) -> func(singleLeft, singleRight)),
((intermediate, value) -> intermediate or value), false)), false)
doQuerySelector = (value, operatorObj) ->
value = [value] unless _.isArray value # left hand can be an array too
for operator, body of operatorObj
body = [body] unless _.isArray body # right hand can be an array too
doQuerySelector = (originalValue, operatorObj) ->
value = if _.isArray originalValue then originalValue else [originalValue] # left hand can be an array too
for operator, originalBody of operatorObj
body = if _.isArray originalBody then originalBody else [originalBody] # right hand can be an array too
switch operator
when '$gt' then return false unless mapred value, body, (l, r) -> l > r
when '$gte' then return false unless mapred value, body, (l, r) -> l >= r
@ -19,7 +19,9 @@ doQuerySelector = (value, operatorObj) ->
when '$in' then return false unless _.reduce value, ((result, val) -> result or val in body), false
when '$nin' then return false if _.reduce value, ((result, val) -> result or val in body), false
when '$exists' then return false if value[0]? isnt body[0]
else return false
else
trimmedOperator = _.pick(operatorObj, operator)
return false unless _.isObject(originalValue) and matchesQuery(originalValue, trimmedOperator)
true
matchesQuery = (target, queryObj) ->

View file

@ -4,6 +4,7 @@ module.exports =
# Result: Each course instance gains a property, numCompleted, that is the
# number of students in that course instance who have completed ALL of
# the levels in thate course
# TODO: simplify, classroom.sessions only includes sessions for assigned courses now
calculateDots: (classrooms, courses, courseInstances) ->
for classroom in classrooms.models
# map [user, level] => session so we don't have to do find TODO
@ -13,6 +14,7 @@ module.exports =
instance.numCompleted = 0
instance.started = false
levels = classroom.getLevels({courseID: course.id})
levels.remove(levels.filter((level) => level.get('practice')))
for userID in instance.get('members')
instance.started ||= _.any levels.models, (level) ->
session = _.find classroom.sessions.models, (session) ->
@ -45,6 +47,7 @@ module.exports =
users = _.map userIDs, (id) ->
students.get(id)
return {
courseName: course.get('name')
courseNumber: courseIndex + 1
levelNumber: levelIndex + 1
levelName: level.get('name')
@ -74,6 +77,7 @@ module.exports =
users = _.map userIDs, (id) ->
students.get(id)
return {
courseName: course.get('name')
courseNumber: courseIndex + 1
levelNumber: levelIndex + 1
levelName: level.get('name')
@ -178,7 +182,7 @@ module.exports =
if _.find(sessions, (s) -> s.completed()) # have finished this level
courseProgress.completed &&= true #no-op
courseProgress[userID].completed &&= true #no-op
courseProgress[userID].levelsCompleted += 1
courseProgress[userID].levelsCompleted += 1 unless level.get('practice')
courseProgress[levelID].completed &&= true #no-op
# courseProgress[levelID].numCompleted += 1
courseProgress[levelID][userID].completed = true
@ -195,6 +199,17 @@ module.exports =
_.assign(progressData, progressMixin)
return progressData
courseLabelsArray: (courses) ->
labels = []
courseLabelIndexes = CS: 0, GD: 0, WD: 0
for course in courses
acronym = switch
when /game-dev/.test(course.get('slug')) then 'GD'
when /web-dev/.test(course.get('slug')) then 'WD'
else 'CS'
labels.push acronym + ++courseLabelIndexes[acronym]
labels
progressMixin =
get: (options={}) ->
{ classroom, course, level, user } = options

View file

@ -114,7 +114,6 @@ module.exports = class Simulator extends CocoClass
catch error
console.log "Failed to form task results:", error
return @cleanupAndSimulateAnotherTask()
console.log 'Processing results:', taskResults
humanSessionRank = taskResults.sessions[0].metrics.rank
ogreSessionRank = taskResults.sessions[1].metrics.rank
if @options.headlessClient and @options.simulateOnlyOneGame
@ -377,78 +376,25 @@ module.exports = class Simulator extends CocoClass
return 1
generateSpellsObject: ->
@currentUserCodeMap = @task.generateSpellKeyToSourceMap()
@spells = {}
for thang in @level.attributes.thangs
continue if @thangIsATemplate thang
@generateSpellKeyToSourceMapPropertiesFromThang thang
@spells
spells = {}
for {hero, team} in [{hero: 'Hero Placeholder', team: 'humans'}, {hero: 'Hero Placeholder 1', team: 'ogres'}]
sessionInfo = _.filter(@task.getSessions(), {team: team})[0]
fullSpellName = _.string.slugify(hero) + '/plan'
submittedCodeLanguage = sessionInfo?.submittedCodeLanguage ? 'javascript'
submittedCodeLanguage = 'javascript' if submittedCodeLanguage in ['clojure', 'io'] # No longer supported
submittedCode = LZString.decompressFromUTF16 sessionInfo?.submittedCode?[_.string.slugify(hero)]?.plan ? ''
aether = new Aether createAetherOptions functionName: 'plan', codeLanguage: submittedCodeLanguage, skipProtectAPI: false
try
aether.transpile submittedCode
catch e
console.log "Couldn't transpile #{fullSpellName}:\n#{submittedCode}\n", e
aether.transpile ''
spells[fullSpellName] = name: 'plan', team: team, thang: {thang: {id: hero}, aether: aether}
spells
thangIsATemplate: (thang) ->
for component in thang.components
continue unless @componentHasProgrammableMethods component
for methodName, method of component.config.programmableMethods
return true if @methodBelongsToTemplateThang method
return false
componentHasProgrammableMethods: (component) -> component.config? and _.has component.config, 'programmableMethods'
methodBelongsToTemplateThang: (method) -> typeof method is 'string'
generateSpellKeyToSourceMapPropertiesFromThang: (thang) =>
for component in thang.components
continue unless @componentHasProgrammableMethods component
for methodName, method of component.config.programmableMethods
spellKey = @generateSpellKeyFromThangIDAndMethodName thang.id, methodName
@createSpellAndAssignName spellKey, methodName
@createSpellThang thang, method, spellKey
@transpileSpell thang, spellKey, methodName
generateSpellKeyFromThangIDAndMethodName: (thang, methodName) ->
spellKeyComponents = [thang, methodName]
spellKeyComponents[0] = _.string.slugify spellKeyComponents[0]
spellKey = spellKeyComponents.join '/'
spellKey
createSpellAndAssignName: (spellKey, spellName) ->
@spells[spellKey] ?= {}
@spells[spellKey].name = spellName
createSpellThang: (thang, method, spellKey) ->
@spells[spellKey].thangs ?= {}
@spells[spellKey].thangs[thang.id] ?= {}
spellTeam = @task.getSpellKeyToTeamMap()[spellKey]
playerTeams = @task.getPlayerTeams()
useProtectAPI = true
if spellTeam not in playerTeams
useProtectAPI = false
else
spellSession = _.filter(@task.getSessions(), {team: spellTeam})[0]
unless codeLanguage = spellSession?.submittedCodeLanguage
console.warn 'Session', spellSession.creatorName, spellSession.team, 'didn\'t have submittedCodeLanguage, just:', spellSession
@spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method, useProtectAPI, codeLanguage ? 'javascript'
transpileSpell: (thang, spellKey, methodName) ->
slugifiedThangID = _.string.slugify thang.id
generatedSpellKey = [slugifiedThangID,methodName].join '/'
source = @currentUserCodeMap[generatedSpellKey] ? ''
aether = @spells[spellKey].thangs[thang.id].aether
#unless _.contains(@task.spellKeysToTranspile, generatedSpellKey)
try
aether.transpile source
catch e
console.log "Couldn't transpile #{spellKey}:\n#{source}\n", e
aether.transpile ''
createAether: (methodName, method, useProtectAPI, codeLanguage) ->
aetherOptions = createAetherOptions functionName: methodName, codeLanguage: codeLanguage, skipProtectAPI: not useProtectAPI
return new Aether aetherOptions
class SimulationTask
constructor: (@rawData) ->
@spellKeyToTeamMap = {}
getLevelName: ->
levelName = @rawData.sessions?[0]?.levelID
@ -476,30 +422,4 @@ class SimulationTask
getSessions: -> @rawData.sessions
getSpellKeyToTeamMap: -> @spellKeyToTeamMap
getPlayerTeams: -> _.pluck @rawData.sessions, 'team'
setWorld: (@world) ->
generateSpellKeyToSourceMap: ->
# TODO: we always now only have hero-placeholder/plan vs. hero-placeholder-1/plan on humans vs. ogres, always just have to retranspile for Esper, and never need to transpile for NPCs or other methods, so we can get rid of almost all of this stuff.
playerTeams = _.pluck @rawData.sessions, 'team'
spellKeyToSourceMap = {}
for session in @rawData.sessions
teamSpells = session.teamSpells[session.team]
allTeams = _.keys session.teamSpells
for team in allTeams
for spell in session.teamSpells[team]
@spellKeyToTeamMap[spell] = team
teamCode = {}
for thangName, thangSpells of session.submittedCode
for spellName, spell of thangSpells
fullSpellName = [thangName, spellName].join '/'
if _.contains(teamSpells, fullSpellName)
teamCode[fullSpellName] = LZString.decompressFromUTF16 spell
_.merge spellKeyToSourceMap, teamCode
spellKeyToSourceMap

View file

@ -358,6 +358,9 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
prerenderedSpriteSheet = thangType.getPrerenderedSpriteSheet(colorConfig, 'segmented')
if prerenderedSpriteSheet and not prerenderedSpriteSheet.loadedImage
return
else if prerenderedSpriteSheet
animations = prerenderedSpriteSheet.spriteSheet._animations
renderedActions = _.zipObject(animations, _.times(animations.length, -> true))
containersToRender = thangType.getContainersForActions(actionNames)
#console.log 'render segmented', thangType.get('name'), actionNames, colorConfig, 'because we do not have prerendered sprite sheet?', prerenderedSpriteSheet
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
@ -367,7 +370,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
container = new createjs.Sprite(@spriteSheet)
container.gotoAndStop(containerKey)
frame = spriteSheetBuilder.addFrame(container)
else if prerenderedSpriteSheet
else if prerenderedSpriteSheet and renderedActions[containerGlobalName]
container = new createjs.Sprite(prerenderedSpriteSheet.spriteSheet)
container.gotoAndStop(containerGlobalName)
scale = @resolutionFactor / (prerenderedSpriteSheet.get('resolutionFactor') or 1)

View file

@ -10,7 +10,6 @@ Letterbox = require './Letterbox'
Dimmer = require './Dimmer'
CountdownScreen = require './CountdownScreen'
PlaybackOverScreen = require './PlaybackOverScreen'
WaitingScreen = require './WaitingScreen'
DebugDisplay = require './DebugDisplay'
CoordinateDisplay = require './CoordinateDisplay'
CoordinateGrid = require './CoordinateGrid'
@ -70,7 +69,6 @@ module.exports = Surface = class Surface extends CocoClass
'level:set-letterbox': 'onSetLetterbox'
'application:idle-changed': 'onIdleChanged'
'camera:zoom-updated': 'onZoomUpdated'
'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting'
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
'level:flag-color-selected': 'onFlagColorSelected'
@ -85,6 +83,8 @@ module.exports = Surface = class Surface extends CocoClass
constructor: (@world, @normalCanvas, @webGLCanvas, givenOptions) ->
super()
$(window).on('keydown', @onKeyEvent)
$(window).on('keyup', @onKeyEvent)
@normalLayers = []
@options = _.clone(@defaults)
@options = _.extend(@options, givenOptions) if givenOptions
@ -94,9 +94,9 @@ module.exports = Surface = class Surface extends CocoClass
})
@realTimeInputEvents = @gameUIState.get('realTimeInputEvents')
@listenTo(@gameUIState, 'sprite:mouse-down', @onSpriteMouseDown)
@onResize = _.debounce @onResize, resizeDelay
@initEasel()
@initAudio()
@onResize = _.debounce @onResize, resizeDelay
$(window).on 'resize', @onResize
if @world.ended
_.defer => @setWorld @world
@ -133,9 +133,9 @@ module.exports = Surface = class Surface extends CocoClass
@handleEvents
})
@countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown
@playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer, playerNames: @options.playerNames
@normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen.
@waitingScreen = new WaitingScreen camera: @camera, layer: @screenLayer
unless @options.levelType is 'game-dev'
@playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer, playerNames: @options.playerNames
@normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen.
@initCoordinates()
@webGLStage.enableMouseOver(10)
@webGLStage.addEventListener 'stagemousemove', @onMouseMove
@ -230,6 +230,7 @@ module.exports = Surface = class Surface extends CocoClass
restoreWorldState: ->
frame = @world.getFrame(@getCurrentFrame())
return unless frame
frame.restoreState()
current = Math.max(0, Math.min(@currentFrame, @world.frames.length - 1))
if current - Math.floor(current) > 0.01 and Math.ceil(current) < @world.frames.length - 1
@ -328,12 +329,12 @@ module.exports = Surface = class Surface extends CocoClass
@surfacePauseTimeout = _.delay performToggle, 2000
@lankBoss.stop()
@trailmaster?.stop()
@playbackOverScreen.show()
@playbackOverScreen?.show()
else
performToggle()
@lankBoss.play()
@trailmaster?.play()
@playbackOverScreen.hide()
@playbackOverScreen?.hide()
@ -351,7 +352,7 @@ module.exports = Surface = class Surface extends CocoClass
world: @world
)
if @lastFrame < @world.frames.length and @currentFrame >= @world.totalFrames - 1
if (not @world.indefiniteLength) and @lastFrame < @world.frames.length and @currentFrame >= @world.totalFrames - 1
@ended = true
@setPaused true
Backbone.Mediator.publish 'surface:playback-ended', {}
@ -541,7 +542,15 @@ module.exports = Surface = class Surface extends CocoClass
event.screenPos = @mouseScreenPos if @mouseScreenPos
Backbone.Mediator.publish 'surface:mouse-scrolled', event unless @disabled
@gameUIState.trigger('surface:mouse-scrolled', event)
#- Keyboard callbacks
onKeyEvent: (e) =>
return unless @realTime
event = _.pick(e, 'type', 'keyCode', 'ctrlKey', 'metaKey', 'shiftKey')
event.time = @world.dt * @world.frames.length
@realTimeInputEvents.add(event)
#- Canvas callbacks
@ -554,6 +563,9 @@ module.exports = Surface = class Surface extends CocoClass
if application.isIPadApp
newWidth = 1024
newHeight = newWidth / aspectRatio
else if @options.resizeStrategy is 'wrapper-size'
newWidth = $('#canvas-wrapper').width()
newHeight = newWidth / aspectRatio
else if @realTime or @options.spectateGame
pageHeight = $('#page-container').height() - $('#control-bar-view').outerHeight() - $('#playback-view').outerHeight()
newWidth = Math.min pageWidth, pageHeight * aspectRatio
@ -570,7 +582,7 @@ module.exports = Surface = class Surface extends CocoClass
scaleFactor = 1
if @options.stayVisible
availableHeight = window.innerHeight
availableHeight -= $('.ad-container').outerHeight()
availableHeight -= $('.ad-container').outerHeight()
availableHeight -= $('#game-area').outerHeight() - $('#canvas-wrapper').outerHeight()
scaleFactor = availableHeight / newHeight if availableHeight < newHeight
newWidth *= scaleFactor
@ -579,6 +591,7 @@ module.exports = Surface = class Surface extends CocoClass
return if newWidth is oldWidth and newHeight is oldHeight and not @options.spectateGame
return if newWidth < 200 or newHeight < 200
@normalCanvas.add(@webGLCanvas).attr width: newWidth, height: newHeight
@trigger 'resize', { width: newWidth, height: newHeight }
# Cannot do this to the webGLStage because it does not use scaleX/Y.
# Instead the LayerAdapter scales webGL-enabled layers.
@ -602,9 +615,6 @@ module.exports = Surface = class Surface extends CocoClass
#- Real-time playback
onRealTimePlaybackWaiting: (e) ->
@onRealTimePlaybackStarted e
onRealTimePlaybackStarted: (e) ->
return if @realTime
@realTimeInputEvents.reset()
@ -741,7 +751,6 @@ module.exports = Surface = class Surface extends CocoClass
@dimmer?.destroy()
@countdownScreen?.destroy()
@playbackOverScreen?.destroy()
@waitingScreen?.destroy()
@coordinateDisplay?.destroy()
@coordinateGrid?.destroy()
@normalStage.clear()
@ -759,6 +768,8 @@ module.exports = Surface = class Surface extends CocoClass
@webGLStage.enableMouseOver 0
@webGLCanvas.off 'mousewheel', @onMouseWheel
$(window).off 'resize', @onResize
$(window).off('keydown', @onKeyEvent)
$(window).off('keyup', @onKeyEvent)
clearTimeout @surfacePauseTimeout if @surfacePauseTimeout
clearTimeout @surfaceZoomPauseTimeout if @surfaceZoomPauseTimeout
super()

View file

@ -1,65 +0,0 @@
CocoClass = require 'core/CocoClass'
RealTimeCollection = require 'collections/RealTimeCollection'
module.exports = class WaitingScreen extends CocoClass
subscriptions:
'playback:real-time-playback-waiting': 'onRealTimePlaybackWaiting'
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
'real-time-multiplayer:player-status': 'onRealTimeMultiplayerPlayerStatus'
constructor: (options) ->
super()
options ?= {}
@camera = options.camera
@layer = options.layer
@waitingText = options.text or 'Waiting...'
console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), 'needs a layer.' unless @layer
@build()
onCastingBegins: (e) -> @show() unless e.preload
onCastingEnds: (e) -> @hide()
toString: -> '<WaitingScreen>'
build: ->
@dimLayer = new createjs.Container()
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
@dimLayer.addChild @dimScreen = new createjs.Shape()
@dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
@dimLayer.alpha = 0
@dimLayer.addChild @makeWaitingText()
makeWaitingText: ->
size = Math.ceil @camera.canvasHeight / 8
text = new createjs.Text @waitingText, "#{size}px Open Sans Condensed", '#F7B42C'
text.shadow = new createjs.Shadow '#000', Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 300), Math.ceil(@camera.canvasHeight / 120)
text.textAlign = 'center'
text.textBaseline = 'middle'
text.x = @camera.canvasWidth / 2
text.y = @camera.canvasHeight / 2
@text = text
return text
show: ->
return if @showing
@showing = true
@dimLayer.alpha = 0
createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
@layer.addChild @dimLayer
hide: ->
return unless @showing
@showing = false
createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha: 0}, 500).call => @layer.removeChild @dimLayer unless @destroyed
onRealTimeMultiplayerPlayerStatus: (e) -> @text.text = e.status
onRealTimePlaybackWaiting: (e) -> @show()
onRealTimePlaybackStarted: (e) -> @hide()
onRealTimePlaybackEnded: (e) -> @hide()

View file

@ -38,6 +38,7 @@ module.exports = class GoalManager extends CocoClass
subscriptions:
'god:new-world-created': 'onNewWorldCreated'
'god:new-html-goal-states': 'onNewHTMLGoalStates'
'level:restarted': 'onLevelRestarted'
backgroundSubscriptions:
@ -86,6 +87,9 @@ module.exports = class GoalManager extends CocoClass
@world = e.world
@updateGoalStates(e.goalStates) if e.goalStates?
onNewHTMLGoalStates: (e) ->
@updateGoalStates(e.goalStates) if e.goalStates?
updateGoalStates: (newGoalStates) ->
for goalID, goalState of newGoalStates
continue unless @goalStates[goalID]?
@ -114,7 +118,7 @@ module.exports = class GoalManager extends CocoClass
goalStates: @goalStates
goals: @goals
overallStatus: overallStatus
timedOut: @world.totalFrames is @world.maxTotalFrames and overallStatus not in ['success', 'failure']
timedOut: @world? and (@world.totalFrames is @world.maxTotalFrames and overallStatus not in ['success', 'failure'])
Backbone.Mediator.publish('goal-manager:new-goal-states', event)
checkOverallStatus: (ignoreIncomplete=false) ->
@ -264,7 +268,7 @@ module.exports = class GoalManager extends CocoClass
mostEagerGoal = _.min matchedGoals, 'worldEndsAfter'
victory = overallStatus is 'success'
tentative = overallStatus is 'success'
@world.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity
@world?.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity
updateGoalState: (goalID, thangID, progressObjectName, frameNumber) ->
# A thang has done something related to the goal!
@ -291,7 +295,7 @@ module.exports = class GoalManager extends CocoClass
mostEagerGoal = _.min matchedGoals, 'worldEndsAfter'
victory = overallStatus is 'success'
tentative = overallStatus is 'success'
@world.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity
@world?.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity
goalIsPositive: (goalID) ->
# Positive goals are completed when all conditions are true (kill all these thangs)

View file

@ -155,7 +155,7 @@ module.exports.thangNames = thangNames =
'Frog Pet': ['Hypnotoad']
'Griffin Pet': ['']
'Pugicorn Pet': ['']
'Polar Bear Pet': ['']
'Polar Bear Pet': ['Klondike']
'Wolf Pet': ['']
'Horse': [
# Animal

View file

@ -28,6 +28,7 @@ module.exports = class World
debugging: false # Whether we are just rerunning to debug a world we've already cast
headless: false # Whether we are just simulating for goal states instead of all serialized results
framesSerializedSoFar: 0
framesClearedSoFar: 0
apiProperties: ['age', 'dt']
realTimeBufferMax: REAL_TIME_BUFFER_MAX / 1000
constructor: (@userCodeMap, classMap) ->
@ -96,6 +97,7 @@ module.exports = class World
loadFrames: (loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) ->
return if @aborted
@totalFrames = 2 if @justBegin
console.log 'Warning: loadFrames called on empty World (no thangs).' unless @thangs.length
continueLaterFn = =>
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
@ -116,7 +118,13 @@ module.exports = class World
@lastRealTimeUpdate ?= 0
frameToLoadUntil = if loadUntilFrame then loadUntilFrame + 1 else @totalFrames # Might stop early if debugging.
i = @frames.length
while i < frameToLoadUntil and i < @totalFrames
while true
if @indefiniteLength
break if not @realTime # realtime has been stopped
break if @victory? # game won or lost # TODO: give a couple seconds of buffer after victory is set instead of ending instantly
else
break if i >= frameToLoadUntil
break if i >= @totalFrames
return unless @shouldContinueLoading t1, loadProgressCallback, skipDeferredLoading, continueLaterFn
@adjustFlowSettings loadUntilFrame if @debugging
try
@ -379,6 +387,11 @@ module.exports = class World
@freeMemoryBeforeFinalSerialization() if @ended
startFrame = @framesSerializedSoFar
endFrame = @frames.length
if @indefiniteLength
toClear = Math.max(@framesSerializedSoFar-10, 0)
for i in _.range(@framesClearedSoFar, toClear)
@frames[i] = null
@framesClearedSoFar = @framesSerializedSoFar
#console.log "... world serializing frames from", startFrame, "to", endFrame, "of", @totalFrames
[transferableObjects, nontransferableObjects] = [0, 0]
serializedFlagHistory = (_.omit(_.clone(flag), 'processed') for flag in @flagHistory)
@ -525,6 +538,14 @@ module.exports = class World
perf.framesCPUTime = 0
w.frames = [] unless streamingWorld
clearTimeout @deserializationTimeout if @deserializationTimeout
if w.indefiniteLength
clearTo = Math.max(w.frames.length - 100, 0)
if clearTo > w.framesClearedSoFar
for i in _.range(w.framesClearedSoFar, clearTo)
w.frames[i] = null
w.framesClearedSoFar = clearTo
@deserializationTimeout = _.delay @deserializeSomeFrames, 1, o, w, finishedWorldCallback, perf, startFrame, endFrame
w # Return in-progress deserializing world
@ -588,6 +609,7 @@ module.exports = class World
lastPos = x: null, y: null
for frameIndex in [lastFrameIndex .. 0] by -1
frame = @frames[frameIndex]
continue unless frame # may have been evicted for game dev levels
if pos = frame.thangStateMap[thangID]?.getStateForProp 'pos'
pos = camera.worldToSurface {x: pos.x, y: pos.y} if camera # without z
if not lastPos.x? or (Math.abs(lastPos.x - pos.x) + Math.abs(lastPos.y - pos.y)) > 1

View file

@ -10,7 +10,7 @@ module.exports = class WorldFrame
getNextFrame: ->
# Optimized. Must be called while thangs are current at this frame.
nextTime = @time + @world.dt
return null if nextTime > @world.lifespan
return null if nextTime > @world.lifespan and not @world.indefiniteLength
@hash = @world.rand.seed
@hash += system.update() for system in @world.systems
nextFrame = new WorldFrame(@world, nextTime)

Some files were not shown because too many files have changed in this diff Show more