Started implementing web-dev goals

This commit is contained in:
Nick Winter 2016-07-15 00:40:32 -07:00
parent 69f21514b9
commit c44c16e5d2
5 changed files with 93 additions and 7 deletions

View file

@ -14,18 +14,20 @@ function receiveMessage(event) {
switch (event.data.type) {
case 'create':
create(event.data.dom);
checkGoals(event.data.goals);
break;
case 'update':
if (virtualDOM)
update(event.data.dom);
else
create(event.data.dom);
create(event.data.dom);
checkGoals(event.data.goals);
break;
case 'log':
console.log(event.data.text);
break;
default:
console.log('Unknown message type:', event.data.type);
console.log('Unknown message type:', event.data.type);
}
//event.source.postMessage("hi there yourself! the secret response is: rheeeeet!", event.origin);
@ -45,3 +47,73 @@ function update(dom) {
changes.reduce(deku.dom.update(dispatch, context), concreteDOM) // Rerender
virtualDOM = event.data.dom;
}
function checkGoals(goals) {
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)
});
console.log('HTML', goal.id, '-', goal.name, '- succeeds?', success)
});
}
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

@ -39,6 +39,7 @@ module.exports = class GoalManager extends CocoClass
subscriptions:
'god:new-world-created': 'onNewWorldCreated'
'level:restarted': 'onLevelRestarted'
#'tome:html-updated': 'onHTMLUpdated'
backgroundSubscriptions:
'world:thang-died': 'onThangDied'
@ -114,7 +115,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) ->
@ -220,6 +221,11 @@ 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
@ -264,7 +270,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 +297,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)
@ -314,6 +320,9 @@ 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

@ -114,6 +114,9 @@ GoalSchema = c.object {title: 'Goal', description: 'A goal that the player can a
targets: c.array {title: 'Targets', description: 'The target items which the Thangs must not collect.', minItems: 1}, thang
codeProblems: c.array {title: 'Code Problems', description: 'A list of Thang IDs that should not have any code problems, or team names.', uniqueItems: true, minItems: 1, 'default': ['humans']}, thang
linesOfCode: {title: 'Lines of Code', description: 'A mapping of Thang IDs or teams to how many many lines of code should be allowed (well, statements).', type: 'object', default: {humans: 10}, additionalProperties: {type: 'integer', description: 'How many lines to allow for this Thang.'}}
html: c.object {title: 'HTML', description: 'A jQuery selector and what its result should be'},
selector: {type: 'string', description: 'jQuery selector to run on the user HTML, like "h1:first-child"'}
valueChecks: c.array {title: 'Value checks', description: 'Logical checks on the resulting value for this goal to pass.', format: 'event-prereqs'}, EventPrereqSchema
ResponseSchema = c.object {title: 'Dialogue Button', description: 'A button to be shown to the user with the dialogue.', required: ['text']},
text: {title: 'Title', description: 'The text that will be on the button', 'default': 'Okay', type: 'string', maxLength: 30}

View file

@ -269,7 +269,7 @@ module.exports = class PlayLevelView extends RootView
@insertSubView new DuelStatsView level: @level, session: @session, otherSession: @otherSession, supermodel: @supermodel, thangs: @world.thangs if @level.isType('hero-ladder', 'course-ladder')
@insertSubView @controlBar = new ControlBarView {worldName: utils.i18n(@level.attributes, 'name'), session: @session, level: @level, supermodel: @supermodel, courseID: @courseID, courseInstanceID: @courseInstanceID}
@insertSubView @hintsView = new HintsView({ @session, @level, @hintsState }), @$('.hints-view')
@insertSubView @webSurface = new WebSurfaceView level: @level if @level.isType('web-dev')
@insertSubView @webSurface = new WebSurfaceView {level: @level, @goalManager} if @level.isType('web-dev')
#_.delay (=> Backbone.Mediator.publish('level:set-debug', debug: true)), 5000 if @isIPadApp() # if me.displayName() is 'Nick'
initVolume: ->

View file

@ -12,6 +12,8 @@ module.exports = class WebSurfaceView extends CocoView
initialize: (options) ->
@state = new State
blah: 'blah'
@goals = (goal for goal in options.goalManager.goals when goal.html)
# Consider https://www.npmjs.com/package/css-select to do this on virtualDOM instead of in iframe on concreteDOM
super(options)
afterRender: ->
@ -34,7 +36,7 @@ module.exports = class WebSurfaceView extends CocoView
# TODO: pull out the actual scripts, styles, and body/elements they are doing so we can merge them with our initial structure on the other side
virtualDOM = @dekuify html
messageType = if e.create or not @virtualDOM then 'create' else 'update'
@iframe.contentWindow.postMessage {type: messageType, dom: virtualDOM}, '*'
@iframe.contentWindow.postMessage {type: messageType, dom: virtualDOM, goals: @goals}, '*'
@virtualDOM = virtualDOM
checkGoals: (dom) ->