mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-26 14:03:28 -04:00
Merge remote-tracking branch 'codecombat/master'
This commit is contained in:
commit
70fe21f645
12 changed files with 148 additions and 34 deletions
app
assets/javascripts/workers
lib/world
templates/courses
views
editor
play/level/tome
teachers
server
|
@ -1,7 +1,18 @@
|
|||
var window = self;
|
||||
var Global = self;
|
||||
|
||||
importScripts("/javascripts/lodash.js", "/javascripts/aether.js", "/javascripts/esper.js");
|
||||
importScripts("/javascripts/lodash.js", "/javascripts/aether.js");
|
||||
|
||||
try {
|
||||
//Detect very modern javascript support.
|
||||
(0,eval("'use strict'; let test = (class Test { *gen(a=7) { yield yield * () => WeakMap; } });"));
|
||||
console.log("Modern javascript detected, aw yeah!");
|
||||
self.importScripts('/javascripts/esper.modern.js');
|
||||
} catch (e) {
|
||||
console.log("Legacy javascript detected, falling back...", e.message);
|
||||
self.importScripts('/javascripts/esper.js');
|
||||
}
|
||||
|
||||
//console.log("Aether Tome worker has finished importing scripts.");
|
||||
var aethers = {};
|
||||
var languagesImported = {};
|
||||
|
|
|
@ -63,7 +63,18 @@ var console = {
|
|||
console.error = console.warn = console.info = console.debug = console.log;
|
||||
self.console = console;
|
||||
|
||||
self.importScripts('/javascripts/lodash.js', '/javascripts/world.js', '/javascripts/aether.js', '/javascripts/esper.js');
|
||||
self.importScripts('/javascripts/lodash.js', '/javascripts/world.js', '/javascripts/aether.js');
|
||||
try {
|
||||
//Detect very modern javascript support.
|
||||
(0,eval("'use strict'; let test = (class Test { *gen(a=7) { yield yield * () => WeakMap; } });"));
|
||||
console.log("Modern javascript detected, aw yeah!");
|
||||
self.importScripts('/javascripts/esper.modern.js');
|
||||
} catch (e) {
|
||||
console.log("Legacy javascript detected, falling back...", e.message);
|
||||
self.importScripts('/javascripts/esper.js');
|
||||
}
|
||||
|
||||
|
||||
var myImportScripts = importScripts;
|
||||
|
||||
var languagesImported = {};
|
||||
|
@ -402,6 +413,17 @@ self.serializeFramesSoFar = function serializeFramesSoFar() {
|
|||
self.world.framesSerializedSoFar = self.world.frames.length;
|
||||
};
|
||||
|
||||
function trySerialize() {
|
||||
try {
|
||||
var serialized = self.world.serialize();
|
||||
}
|
||||
catch(error) {
|
||||
console.log("World serialization error:", error.toString() + "\n" + error.stack || error.stackTrace);
|
||||
return false;
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
self.onWorldLoaded = function onWorldLoaded() {
|
||||
if(self.world.framesSerializedSoFar == self.world.frames.length) return;
|
||||
if(self.world.ended)
|
||||
|
@ -419,12 +441,9 @@ self.onWorldLoaded = function onWorldLoaded() {
|
|||
return console.log('Headless simulation completed in ' + diff + 'ms.');
|
||||
|
||||
var worldEnded = self.world.ended;
|
||||
var serialized;
|
||||
var transferableSupported = self.transferableSupported();
|
||||
try {
|
||||
var serialized = self.world.serialize();
|
||||
}
|
||||
catch(error) {
|
||||
console.log("World serialization error:", error.toString() + "\n" + error.stack || error.stackTrace);
|
||||
if ( !( serialized = trySerialize()) ) {
|
||||
self.destroyWorld();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -162,11 +162,25 @@ module.exports = class World
|
|||
|
||||
shouldContinueLoading: (t1, loadProgressCallback, skipDeferredLoading, continueLaterFn) ->
|
||||
t2 = now()
|
||||
chunkSize = @frames.length - @framesSerializedSoFar
|
||||
simedTime = @frames.length / @frameRate
|
||||
|
||||
chunkTime = switch
|
||||
when simedTime > 15 then 7
|
||||
when simedTime > 10 then 5
|
||||
when simedTime > 5 then 3
|
||||
when simedTime > 2 then 1
|
||||
else 0.5
|
||||
|
||||
bailoutTime = Math.max(2000*chunkTime, 10000)
|
||||
|
||||
dt = t2 - t1
|
||||
|
||||
if @realTime
|
||||
shouldUpdateProgress = @shouldUpdateRealTimePlayback t2
|
||||
shouldDelayRealTimeSimulation = not shouldUpdateProgress and @shouldDelayRealTimeSimulation t2
|
||||
else
|
||||
shouldUpdateProgress = t2 - t1 > PROGRESS_UPDATE_INTERVAL# and (@frames.length - @framesSerializedSoFar >= @frameRate or t2 - t1 > 1000)
|
||||
shouldUpdateProgress = (dt > PROGRESS_UPDATE_INTERVAL and (chunkSize / @frameRate >= chunkTime) or dt > bailoutTime)
|
||||
shouldDelayRealTimeSimulation = false
|
||||
return true unless shouldUpdateProgress or shouldDelayRealTimeSimulation
|
||||
# Stop loading frames for now; continue in a moment.
|
||||
|
|
|
@ -57,6 +57,8 @@ block content
|
|||
select.level-select.form-control
|
||||
if view.campaigns.loaded
|
||||
each level, levelIndex in view.campaigns.get(course.get('campaignID')).getLevels().models
|
||||
if level.get('type') === 'hero-practice'
|
||||
- continue;
|
||||
option(value=level.get('slug'))
|
||||
span
|
||||
= levelIndex + 1
|
||||
|
|
|
@ -239,6 +239,14 @@ module.exports = class CampaignEditorView extends RootView
|
|||
@campaign.set key, value for key, value of @treema.data
|
||||
@campaignView.setCampaign(@campaign)
|
||||
|
||||
onTreemaSelectionChanged: (e, node) =>
|
||||
return unless node[0]?.data?.original?
|
||||
elem = @$("div[data-level-original='#{node[0].data.original}']")
|
||||
elem.toggle('pulsate')
|
||||
setTimeout ()->
|
||||
elem.toggle('pulsate')
|
||||
, 1000
|
||||
|
||||
onTreemaDoubleClicked: (e, node) =>
|
||||
path = node.getPath()
|
||||
return unless _.string.startsWith path, '/levels/'
|
||||
|
|
|
@ -15,7 +15,7 @@ module.exports = class SettingsTabView extends CocoView
|
|||
editableSettings: [
|
||||
'name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals',
|
||||
'type', 'terrain', 'showsGuide', 'banner', 'employerDescription', 'loadingTip', 'requiresSubscription',
|
||||
'helpVideos', 'replayable', 'scoreTypes', 'concepts', 'picoCTFProblem'
|
||||
'helpVideos', 'replayable', 'scoreTypes', 'concepts', 'picoCTFProblem', 'practiceThresholdMinutes'
|
||||
]
|
||||
|
||||
subscriptions:
|
||||
|
|
|
@ -316,7 +316,7 @@ module.exports = class SpellView extends CocoView
|
|||
|
||||
xstart = startOfRow(row)
|
||||
if language is 'python'
|
||||
requiredIndent = new RegExp '^' + new Array(xstart / 4 + 1).join('( |\t)') + '( |\t)+(\\S|\\s*$)'
|
||||
requiredIndent = new RegExp '^' + new Array(Math.floor(xstart / 4 + 1)).join('( |\t)') + '( |\t)+(\\S|\\s*$)'
|
||||
for crow in [docRange.start.row+1..docRange.end.row]
|
||||
unless requiredIndent.test lines[crow]
|
||||
docRange.end.row = crow - 1
|
||||
|
|
|
@ -62,7 +62,7 @@ module.exports = class TeachersContactModal extends ModalView
|
|||
return unless _.isEmpty(formErrors)
|
||||
|
||||
@state.set('sendingState', 'sending')
|
||||
data = _.extend({ country: me.get('country'), recipientID: 'schools@codecombat.com' }, formValues)
|
||||
data = _.extend({ country: me.get('country') }, formValues)
|
||||
contact.send({
|
||||
data
|
||||
context: @
|
||||
|
|
|
@ -207,6 +207,7 @@ exports.config =
|
|||
copyTo:
|
||||
'lib/ace': ['node_modules/ace-builds/src-min-noconflict/*']
|
||||
'fonts': ['bower_components/openSansCondensed/*', 'bower_components/openSans/*']
|
||||
'javascripts': ['bower_components/esper.js/esper.modern.js']
|
||||
autoReload:
|
||||
delay: 1000
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
'scoreTypes'
|
||||
'concepts'
|
||||
'picoCTFProblem'
|
||||
'practiceThresholdMinutes'
|
||||
]
|
||||
|
||||
postEditableProperties: ['name']
|
||||
|
|
|
@ -66,33 +66,81 @@ module.exports =
|
|||
return done("Unexpected activities format: " + body) unless activities.data?
|
||||
for activity in activities.data when activity._type is 'Email'
|
||||
if /@codecombat\.(?:com)|(?:nl)/ig.test(activity.sender) and not activity.sender?.indexOf(config.mail.username) >= 0
|
||||
return done(null, activity.sender, lead.id)
|
||||
return done(null, activity.sender, activity.user_id, lead.id)
|
||||
return done(null, config.mail.supportSchools, lead.id)
|
||||
catch error
|
||||
log.error("closeIO.getSalesContactEmail Error for #{email}: #{JSON.stringify(error)}")
|
||||
return done(error)
|
||||
|
||||
sendMail: (fromAddress, subject, content, done) ->
|
||||
sendMail: (fromAddress, subject, content, salesContactEmail, leadID, done) ->
|
||||
# log.info("DEBUG: closeIO.sendMail #{fromAddress} #{subject} #{content}")
|
||||
@getSalesContactEmail fromAddress, (err, salesContactEmail, leadID) ->
|
||||
return done("Error getting sales contact for #{fromAddress}: #{err}") if err
|
||||
matches = salesContactEmail.match(/^[a-zA-Z_]+ <(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})>$|(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})/i)
|
||||
salesContactEmail = matches?[1] ? matches?[2] ? config.mail.supportSchools
|
||||
salesContactEmail = config.mail.supportSchools if salesContactEmail?.indexOf('brian@codecombat.com') >= 0
|
||||
matches = salesContactEmail.match(/^[a-zA-Z_]+ <(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})>$|(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3})/i)
|
||||
salesContactEmail = matches?[1] ? matches?[2] ? config.mail.supportSchools
|
||||
salesContactEmail = config.mail.supportSchools if salesContactEmail?.indexOf('brian@codecombat.com') >= 0
|
||||
|
||||
postData =
|
||||
to: [salesContactEmail]
|
||||
sender: config.mail.username
|
||||
subject: subject
|
||||
body_text: content
|
||||
lead_id: leadID
|
||||
status: 'outbox'
|
||||
options =
|
||||
uri: "https://#{apiKey}:X@app.close.io/api/v1/activity/email/"
|
||||
body: JSON.stringify(postData)
|
||||
request.post options, (error, response, body) =>
|
||||
return done(error) if error
|
||||
result = JSON.parse(body)
|
||||
if result.errors or result['field-errors']
|
||||
errorMessage = "Close.io Send email POST error for #{fromAddress} #{JSON.stringify(result.errors)} #{JSON.stringify(result['field-errors'])}"
|
||||
return done(errorMessage)
|
||||
return done()
|
||||
|
||||
processLicenseRequest: (teacherEmail, userID, leadID, licensesRequested, amount, done) ->
|
||||
# Update lead with licenses requested
|
||||
putData = 'custom.licensesRequested': licensesRequested
|
||||
options =
|
||||
uri: "https://#{apiKey}:X@app.close.io/api/v1/lead/#{leadID}/"
|
||||
body: JSON.stringify(putData)
|
||||
request.put options, (error, response, body) =>
|
||||
return done(error) if error
|
||||
result = JSON.parse(body)
|
||||
if result.errors or result['field-errors']
|
||||
errorMessage = "Update Close.io lead PUT error for #{teacherEmail} #{leadID}"
|
||||
return done(errorMessage)
|
||||
|
||||
# Create call task
|
||||
postData =
|
||||
to: [salesContactEmail]
|
||||
sender: config.mail.username
|
||||
subject: subject
|
||||
body_text: content
|
||||
_type: "lead"
|
||||
lead_id: leadID
|
||||
status: 'outbox'
|
||||
assigned_to: userID
|
||||
text: "Call #{teacherEmail}"
|
||||
is_complete: false
|
||||
options =
|
||||
uri: "https://#{apiKey}:X@app.close.io/api/v1/activity/email/"
|
||||
uri: "https://#{apiKey}:X@app.close.io/api/v1/task/"
|
||||
body: JSON.stringify(postData)
|
||||
request.post options, (error, response, body) =>
|
||||
return done(error) if error
|
||||
result = JSON.parse(body);
|
||||
return done(error) if error
|
||||
result = JSON.parse(body)
|
||||
if result.errors or result['field-errors']
|
||||
errorMessage = "Close.io Send email POST error for #{fromAddress} #{JSON.stringify(result.errors)} #{JSON.stringify(result['field-errors'])}";
|
||||
errorMessage = "Create Close.io call task POST error for #{teacherEmail} #{leadID}"
|
||||
return done(errorMessage)
|
||||
return done()
|
||||
|
||||
# Create opportunity
|
||||
postData =
|
||||
note: "#{licensesRequested} licenses requested"
|
||||
confidence: 5
|
||||
lead_id: leadID
|
||||
status: 'Active'
|
||||
value: parseInt(licensesRequested) * amount
|
||||
value_period: "annual"
|
||||
options =
|
||||
uri: "https://#{apiKey}:X@app.close.io/api/v1/opportunity/"
|
||||
body: JSON.stringify(postData)
|
||||
request.post options, (error, response, body) =>
|
||||
return done(error) if error
|
||||
result = JSON.parse(body)
|
||||
if result.errors or result['field-errors']
|
||||
errorMessage = "Create Close.io opportunity POST error for #{teacherEmail} #{leadID}"
|
||||
return done(errorMessage)
|
||||
return done()
|
||||
|
|
|
@ -3,8 +3,9 @@ log = require 'winston'
|
|||
User = require '../models/User'
|
||||
sendwithus = require '../sendwithus'
|
||||
async = require 'async'
|
||||
LevelSession = require '../models/LevelSession'
|
||||
moment = require 'moment'
|
||||
LevelSession = require '../models/LevelSession'
|
||||
Product = require '../models/Product'
|
||||
closeIO = require '../lib/closeIO'
|
||||
|
||||
module.exports.setup = (app) ->
|
||||
|
@ -13,10 +14,19 @@ module.exports.setup = (app) ->
|
|||
# log.info "Sending mail from #{req.body.email} saying #{req.body.message}"
|
||||
fromAddress = req.body.sender or req.body.email or req.user.get('email')
|
||||
createMailContent req, fromAddress, (subject, content) ->
|
||||
if req.body.recipientID is 'schools@codecombat.com' or req.user.isTeacher()
|
||||
req.user.update({$set: { enrollmentRequestSent: true }}).exec(_.noop) if req.body.recipientID is 'schools@codecombat.com'
|
||||
closeIO.sendMail fromAddress, subject, content, (err) ->
|
||||
log.error "Error sending contact form email via Close.io: #{err.message or err}" if err
|
||||
if req.body.licensesNeeded or req.user.isTeacher()
|
||||
closeIO.getSalesContactEmail fromAddress, (err, salesContactEmail, userID, leadID) ->
|
||||
return log.error("Error getting sales contact for #{fromAddress}: #{err.message or err}") if err
|
||||
closeIO.sendMail fromAddress, subject, content, salesContactEmail, leadID, (err) ->
|
||||
return log.error("Error sending contact form email via Close.io: #{err.message or err}") if err
|
||||
if licensesNeeded = req.body.licensesNeeded
|
||||
Product.findOne({name: 'course'}).exec (err, product) =>
|
||||
return log.error(err) if err
|
||||
return log.error('course product not found') if not product
|
||||
amount = product.get('amount')
|
||||
closeIO.processLicenseRequest fromAddress, userID, leadID, licensesNeeded, amount, (err) ->
|
||||
return log.error("Error processing license request via Close.io: #{err.message or err}") if err
|
||||
req.user.update({$set: { enrollmentRequestSent: true }}).exec(_.noop)
|
||||
else
|
||||
createSendWithUsContext req, fromAddress, subject, content, (context) ->
|
||||
sendwithus.api.send context, (err, result) ->
|
||||
|
@ -53,7 +63,7 @@ createSendWithUsContext = (req, fromAddress, subject, content, done) ->
|
|||
premium = user?.isPremium()
|
||||
teacher = user?.isTeacher()
|
||||
|
||||
if recipientID is 'schools@codecombat.com' or teacher
|
||||
if teacher or req.body.licensesNeeded
|
||||
return done("Tried to send a teacher contact us email via sendwithus #{fromAddress} #{subject}")
|
||||
|
||||
toAddress = switch
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue