Sending goal states to GoalManager and GoalStatusView

This commit is contained in:
Nick Winter 2016-07-15 09:11:36 -07:00
parent c44c16e5d2
commit 739973cb47
4 changed files with 52 additions and 24 deletions

View file

@ -1,27 +1,36 @@
// TODO: don't serve this script from codecombat.com; serve it from a harmless extra domain we don't have yet.
window.addEventListener("message", receiveMessage, false);
window.addEventListener('message', receiveMessage, false);
var concreteDOM;
var virtualDOM;
var goalStates;
var allowedOrigins = [
'https://codecombat.com',
'http://localhost:3000',
'http://direct.codecombat.com',
'http://staging.codecombat.com'
];
function receiveMessage(event) {
var origin = event.origin || event.originalEvent.origin; // For Chrome, the origin property is in the event.originalEvent object.
if (origin != 'https://codecombat.com' && origin != 'http://localhost:3000') {
console.log("Bad origin:", origin);
if (allowedOrigins.indexOf(origin) == -1) {
console.log('Ignoring message from bad origin:', origin);
return;
}
//console.log(event);
switch (event.data.type) {
case 'create':
create(event.data.dom);
checkGoals(event.data.goals);
checkGoals(event.data.goals, event.source);
break;
case 'update':
if (virtualDOM)
update(event.data.dom);
else
create(event.data.dom);
checkGoals(event.data.goals);
checkGoals(event.data.goals, event.source);
break;
case 'log':
console.log(event.data.text);
@ -29,8 +38,6 @@ function receiveMessage(event) {
default:
console.log('Unknown message type:', event.data.type);
}
//event.source.postMessage("hi there yourself! the secret response is: rheeeeet!", event.origin);
}
function create(dom) {
@ -44,21 +51,29 @@ function update(dom) {
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 changes = deku.diff.diffNode(virtualDOM, event.data.dom);
changes.reduce(deku.dom.update(dispatch, context), concreteDOM) // Rerender
changes.reduce(deku.dom.update(dispatch, context), concreteDOM); // Rerender
virtualDOM = event.data.dom;
}
function checkGoals(goals) {
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)
success = success && matchesCheck($result, check);
});
console.log('HTML', goal.id, '-', goal.name, '- succeeds?', success)
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
event.source.postMessage({type: 'goals-updated', goalStates: goalStates, overallStatus: overallStatus}, event.origin);
}
}
function downTheChain(obj, keyChain) {
@ -71,7 +86,7 @@ function downTheChain(obj, keyChain) {
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?
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.

View file

@ -38,8 +38,8 @@ module.exports = class GoalManager extends CocoClass
subscriptions:
'god:new-world-created': 'onNewWorldCreated'
'god:new-html-goal-states': 'onNewHTMLGoalStates'
'level:restarted': 'onLevelRestarted'
#'tome:html-updated': 'onHTMLUpdated'
backgroundSubscriptions:
'world:thang-died': 'onThangDied'
@ -87,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]?
@ -221,11 +224,6 @@ module.exports = class GoalManager extends CocoClass
return unless linesAllowed = who[thang.id] ? who[thang.team]
@updateGoalState goalID, thang.id, 'lines', frameNumber if linesUsed > linesAllowed
#checkHTML: (goal, html) ->
# console.log 'should run selector', goal.html.selector, 'to find element(s)'
# console.log ' ... and should make sure that the value of', check.eventProps, 'is', _.omit(check, 'eventProps') for check in goal.html.valueChecks
# console.log 'should do it with cheerio', window.cheerio
wrapUpGoalStates: (finalFrame) ->
for goalID, state of @goalStates
if state.status is null
@ -320,9 +318,6 @@ module.exports = class GoalManager extends CocoClass
linesOfCode: 0
codeProblems: 0
#onHTMLUpdated: (e) ->
# @checkHTML goal, e.html for goal in @goals when goal.html
updateCodeGoalStates: ->
# TODO

View file

@ -45,6 +45,10 @@ module.exports =
'god:streaming-world-updated': worldUpdatedEventSchema
'god:new-html-goal-states': c.object {required: ['goalStates', 'overallStatus']},
goalStates: goalStatesSchema
overallStatus: {type: ['string', 'null'], enum: ['success', 'failure', 'incomplete', null]}
'god:goals-calculated': c.object {required: ['goalStates', 'god']},
god: {type: 'object'}
goalStates: goalStatesSchema

View file

@ -20,7 +20,8 @@ module.exports = class WebSurfaceView extends CocoView
super()
@iframe = @$('iframe')[0]
$(@iframe).on 'load', (e) =>
@iframe.contentWindow.postMessage {type: 'log', text: 'Player HTML iframe is ready.'}, "*"
window.addEventListener 'message', @onIframeMessage
#@iframe.contentWindow.postMessage {type: 'log', text: 'Player HTML iframe is ready.'}, "*"
@iframeLoaded = true
@onIframeLoaded?()
@onIframeLoaded = null
@ -39,9 +40,6 @@ module.exports = class WebSurfaceView extends CocoView
@iframe.contentWindow.postMessage {type: messageType, dom: virtualDOM, goals: @goals}, '*'
@virtualDOM = virtualDOM
checkGoals: (dom) ->
# TODO: uhh, figure these out
dekuify: (elem) ->
return elem.data if elem.type is 'text'
return null if elem.type is 'comment' # TODO: figure out how to make a comment in virtual dom
@ -49,3 +47,19 @@ module.exports = class WebSurfaceView extends CocoView
console.log("Failed to dekuify", elem)
return elem.type
deku.element(elem.name, elem.attribs, (@dekuify(c) for c in elem.children ? []))
onIframeMessage: (e) =>
origin = e.origin or e.originalEvent.origin
unless origin in ['https://codecombat.com', 'http://localhost:3000']
return console.log 'Ignoring message from bad origin:', origin
unless event.source is @iframe.contentWindow
return console.log 'Ignoring message from somewhere other than our iframe:', event.source
switch event.data.type
when 'goals-updated'
Backbone.Mediator.publish 'god:new-html-goal-states', goalStates: event.data.goalStates, overallStatus: event.data.overallStatus
else
console.warn 'Unknown message type', event.data.type, 'for message', e, 'from origin', origin
destroy: ->
window.removeEventListener 'message', @onIframeMessage
super()