diff --git a/HTML/Assets/Detectors/Lumilo/critical_struggle.js b/HTML/Assets/Detectors/Lumilo/critical_struggle.js
new file mode 100644
index 0000000..590a8a7
--- /dev/null
+++ b/HTML/Assets/Detectors/Lumilo/critical_struggle.js
@@ -0,0 +1,104 @@
+//detector template
+
+//add output variable name below
+var variableName = "critical_struggle"
+
+//initializations (do not touch)
+var detector_output = {name: variableName,
+ category: "Dashboard",
+ value: "0, none",
+ history: "",
+ skill_names: "",
+ step_id: "",
+ transaction_id: "",
+ time: ""
+ };
+var mailer;
+
+//initialize any custom global variables for this detector here
+var prevStep = ""
+
+function receive_transaction( e ){
+ //e is the data of the transaction from mailer from transaction assembler
+
+ //set conditions under which transaction should be processed
+ //(i.e., to update internal state and history, without
+ //necessarily updating external state and history)
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ //do not touch
+ rawSkills = e.data.tutor_data.skills
+ var currSkills = []
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ currSkills.push(rawSkills[property].name + "/" + rawSkills[property].category)
+ }
+ }
+ detector_output.skill_names = currSkills;
+ detector_output.step_id = e.data.tutor_data.step_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ var booleanValues = [0, 1];
+ var timeValues = ["> 25 s" "> 45 s", "> 1 min", "> 2 min", "> 5 min"];
+ detector_output.value = str(booleanValues[Math.floor(Math.random() * booleanValues.length)]) + "," + str(timeValues[Math.floor(Math.random() * timeValues.length)]) ;
+
+ }
+
+ //set conditions under which detector should update
+ //external state and history
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ detector_output.time = new Date();
+ detector_output.transaction_id = e.data.transaction_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output);
+ }
+}
+
+
+self.onmessage = function ( e ) {
+ console.log(variableName, " self.onmessage:", e, e.data, (e.data?e.data.commmand:null), (e.data?e.data.transaction:null), e.ports);
+ switch( e.data.command )
+ {
+ case "connectMailer":
+ mailer = e.ports[0];
+ mailer.onmessage = receive_transaction;
+ break;
+ case "initialize":
+ for (initItem in e.data.initializer){
+ if (e.data.initializer[initItem].name == variableName){
+ detector_output.history = e.data.initializer[initItem].history;
+ detector_output.value = e.data.initializer[initItem].value;
+ }
+ }
+
+ //optional: Below, specify conditions under which a detector
+ //should NOT remember their most recent value and history (using the variable "detectorForget").
+ //(e.g., setting the condition to "true" will mean that the detector
+ // will always be reset between problems... and setting the condition to "false"
+ // means that the detector will never be reset between problems)
+ //
+ //
+ //
+ detectorForget = true;
+
+ if (detectorForget){
+ detector_output.history = "";
+ detector_output.value = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+}
\ No newline at end of file
diff --git a/HTML/Assets/Detectors/Lumilo/idle.js b/HTML/Assets/Detectors/Lumilo/idle.js
new file mode 100644
index 0000000..4a65bd5
--- /dev/null
+++ b/HTML/Assets/Detectors/Lumilo/idle.js
@@ -0,0 +1,107 @@
+//detector template
+
+//add output variable name below
+var variableName = "idle"
+
+//initializations (do not touch)
+var detector_output = {name: variableName,
+ category: "Dashboard",
+ value: "0, none",
+ history: "",
+ skill_names: "",
+ step_id: "",
+ transaction_id: "",
+ time: ""
+ };
+var mailer;
+
+//initialize any custom global variables for this detector here
+var prevStep = ""
+
+function receive_transaction( e ){
+ //e is the data of the transaction from mailer from transaction assembler
+
+ //set conditions under which transaction should be processed
+ //(i.e., to update internal state and history, without
+ //necessarily updating external state and history)
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ //do not touch
+ rawSkills = e.data.tutor_data.skills
+ var currSkills = []
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ currSkills.push(rawSkills[property].name + "/" + rawSkills[property].category)
+ }
+ }
+ detector_output.skill_names = currSkills;
+ detector_output.step_id = e.data.tutor_data.step_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+ var booleanValues = [0, 1];
+ var timeValues = ["> 25 s" "> 45 s", "> 1 min", "> 2 min", "> 5 min"];
+ detector_output.value = str(booleanValues[Math.floor(Math.random() * booleanValues.length)]) + "," + str(timeValues[Math.floor(Math.random() * timeValues.length)]) ;
+
+ }
+
+ //set conditions under which detector should update
+ //external state and history
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ detector_output.time = new Date();
+ detector_output.transaction_id = e.data.transaction_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output);
+ }
+}
+
+
+self.onmessage = function ( e ) {
+ console.log(variableName, " self.onmessage:", e, e.data, (e.data?e.data.commmand:null), (e.data?e.data.transaction:null), e.ports);
+ switch( e.data.command )
+ {
+ case "connectMailer":
+ mailer = e.ports[0];
+ mailer.onmessage = receive_transaction;
+ break;
+ case "initialize":
+ for (initItem in e.data.initializer){
+ if (e.data.initializer[initItem].name == variableName){
+ detector_output.history = e.data.initializer[initItem].history;
+ detector_output.value = e.data.initializer[initItem].value;
+ }
+ }
+
+ //optional: Below, specify conditions under which a detector
+ //should NOT remember their most recent value and history (using the variable "detectorForget").
+ //(e.g., setting the condition to "true" will mean that the detector
+ // will always be reset between problems... and setting the condition to "false"
+ // means that the detector will never be reset between problems)
+ //
+ //
+ //
+ detectorForget = true;
+
+ if (detectorForget){
+ detector_output.history = "";
+ detector_output.value = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+}
\ No newline at end of file
diff --git a/HTML/Assets/Detectors/Lumilo/invisible_hand_raise.js b/HTML/Assets/Detectors/Lumilo/invisible_hand_raise.js
new file mode 100644
index 0000000..021a48f
--- /dev/null
+++ b/HTML/Assets/Detectors/Lumilo/invisible_hand_raise.js
@@ -0,0 +1,108 @@
+//detector template
+
+//add output variable name below
+var variableName = "invisible_hand_raise"
+
+//initializations (do not touch)
+var detector_output = {name: variableName,
+ category: "Dashboard",
+ value: "0, none",
+ history: "",
+ skill_names: "",
+ step_id: "",
+ transaction_id: "",
+ time: ""
+ };
+var mailer;
+
+//initialize any custom global variables for this detector here
+var prevStep = ""
+
+function receive_transaction( e ){
+ //e is the data of the transaction from mailer from transaction assembler
+
+ //set conditions under which transaction should be processed
+ //(i.e., to update internal state and history, without
+ //necessarily updating external state and history)
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ //do not touch
+ rawSkills = e.data.tutor_data.skills
+ var currSkills = []
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ currSkills.push(rawSkills[property].name + "/" + rawSkills[property].category)
+ }
+ }
+ detector_output.skill_names = currSkills;
+ detector_output.step_id = e.data.tutor_data.step_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+ var booleanValues = [0, 1];
+ var timeValues = ["> 25 s" "> 45 s", "> 1 min", "> 2 min", "> 5 min"];
+ detector_output.value = str(booleanValues[Math.floor(Math.random() * booleanValues.length)]) + "," + str(timeValues[Math.floor(Math.random() * timeValues.length)]) ;
+
+
+ }
+
+ //set conditions under which detector should update
+ //external state and history
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ detector_output.time = new Date();
+ detector_output.transaction_id = e.data.transaction_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output);
+ }
+}
+
+
+self.onmessage = function ( e ) {
+ console.log(variableName, " self.onmessage:", e, e.data, (e.data?e.data.commmand:null), (e.data?e.data.transaction:null), e.ports);
+ switch( e.data.command )
+ {
+ case "connectMailer":
+ mailer = e.ports[0];
+ mailer.onmessage = receive_transaction;
+ break;
+ case "initialize":
+ for (initItem in e.data.initializer){
+ if (e.data.initializer[initItem].name == variableName){
+ detector_output.history = e.data.initializer[initItem].history;
+ detector_output.value = e.data.initializer[initItem].value;
+ }
+ }
+
+ //optional: Below, specify conditions under which a detector
+ //should NOT remember their most recent value and history (using the variable "detectorForget").
+ //(e.g., setting the condition to "true" will mean that the detector
+ // will always be reset between problems... and setting the condition to "false"
+ // means that the detector will never be reset between problems)
+ //
+ //
+ //
+ detectorForget = true;
+
+ if (detectorForget){
+ detector_output.history = "";
+ detector_output.value = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+}
\ No newline at end of file
diff --git a/HTML/Assets/Detectors/Lumilo/struggle.js b/HTML/Assets/Detectors/Lumilo/struggle.js
new file mode 100644
index 0000000..08f506c
--- /dev/null
+++ b/HTML/Assets/Detectors/Lumilo/struggle.js
@@ -0,0 +1,107 @@
+//detector template
+
+//add output variable name below
+var variableName = "struggle"
+
+//initializations (do not touch)
+var detector_output = {name: variableName,
+ category: "Dashboard",
+ value: "0, none",
+ history: "",
+ skill_names: "",
+ step_id: "",
+ transaction_id: "",
+ time: ""
+ };
+var mailer;
+
+//initialize any custom global variables for this detector here
+var prevStep = ""
+
+function receive_transaction( e ){
+ //e is the data of the transaction from mailer from transaction assembler
+
+ //set conditions under which transaction should be processed
+ //(i.e., to update internal state and history, without
+ //necessarily updating external state and history)
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ //do not touch
+ rawSkills = e.data.tutor_data.skills
+ var currSkills = []
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ currSkills.push(rawSkills[property].name + "/" + rawSkills[property].category)
+ }
+ }
+ detector_output.skill_names = currSkills;
+ detector_output.step_id = e.data.tutor_data.step_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+ var booleanValues = [0, 1];
+ var timeValues = ["> 25 s" "> 45 s", "> 1 min", "> 2 min", "> 5 min"];
+ detector_output.value = str(booleanValues[Math.floor(Math.random() * booleanValues.length)]) + "," + str(timeValues[Math.floor(Math.random() * timeValues.length)]) ;
+
+ }
+
+ //set conditions under which detector should update
+ //external state and history
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ detector_output.time = new Date();
+ detector_output.transaction_id = e.data.transaction_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output);
+ }
+}
+
+
+self.onmessage = function ( e ) {
+ console.log(variableName, " self.onmessage:", e, e.data, (e.data?e.data.commmand:null), (e.data?e.data.transaction:null), e.ports);
+ switch( e.data.command )
+ {
+ case "connectMailer":
+ mailer = e.ports[0];
+ mailer.onmessage = receive_transaction;
+ break;
+ case "initialize":
+ for (initItem in e.data.initializer){
+ if (e.data.initializer[initItem].name == variableName){
+ detector_output.history = e.data.initializer[initItem].history;
+ detector_output.value = e.data.initializer[initItem].value;
+ }
+ }
+
+ //optional: Below, specify conditions under which a detector
+ //should NOT remember their most recent value and history (using the variable "detectorForget").
+ //(e.g., setting the condition to "true" will mean that the detector
+ // will always be reset between problems... and setting the condition to "false"
+ // means that the detector will never be reset between problems)
+ //
+ //
+ //
+ detectorForget = true;
+
+ if (detectorForget){
+ detector_output.history = "";
+ detector_output.value = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+}
\ No newline at end of file
diff --git a/HTML/Assets/Detectors/Lumilo/student_doing_well.js b/HTML/Assets/Detectors/Lumilo/student_doing_well.js
new file mode 100644
index 0000000..3cd8997
--- /dev/null
+++ b/HTML/Assets/Detectors/Lumilo/student_doing_well.js
@@ -0,0 +1,107 @@
+//detector template
+
+//add output variable name below
+var variableName = "student_doing_well"
+
+//initializations (do not touch)
+var detector_output = {name: variableName,
+ category: "Dashboard",
+ value: "0, none",
+ history: "",
+ skill_names: "",
+ step_id: "",
+ transaction_id: "",
+ time: ""
+ };
+var mailer;
+
+//initialize any custom global variables for this detector here
+var prevStep = ""
+
+function receive_transaction( e ){
+ //e is the data of the transaction from mailer from transaction assembler
+
+ //set conditions under which transaction should be processed
+ //(i.e., to update internal state and history, without
+ //necessarily updating external state and history)
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ //do not touch
+ rawSkills = e.data.tutor_data.skills
+ var currSkills = []
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ currSkills.push(rawSkills[property].name + "/" + rawSkills[property].category)
+ }
+ }
+ detector_output.skill_names = currSkills;
+ detector_output.step_id = e.data.tutor_data.step_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+ var booleanValues = [0, 1];
+ detector_output.value = str(booleanValues[Math.floor(Math.random() * booleanValues.length)]) ;
+
+
+ }
+
+ //set conditions under which detector should update
+ //external state and history
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ detector_output.time = new Date();
+ detector_output.transaction_id = e.data.transaction_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output);
+ }
+}
+
+
+self.onmessage = function ( e ) {
+ console.log(variableName, " self.onmessage:", e, e.data, (e.data?e.data.commmand:null), (e.data?e.data.transaction:null), e.ports);
+ switch( e.data.command )
+ {
+ case "connectMailer":
+ mailer = e.ports[0];
+ mailer.onmessage = receive_transaction;
+ break;
+ case "initialize":
+ for (initItem in e.data.initializer){
+ if (e.data.initializer[initItem].name == variableName){
+ detector_output.history = e.data.initializer[initItem].history;
+ detector_output.value = e.data.initializer[initItem].value;
+ }
+ }
+
+ //optional: Below, specify conditions under which a detector
+ //should NOT remember their most recent value and history (using the variable "detectorForget").
+ //(e.g., setting the condition to "true" will mean that the detector
+ // will always be reset between problems... and setting the condition to "false"
+ // means that the detector will never be reset between problems)
+ //
+ //
+ //
+ detectorForget = true;
+
+ if (detectorForget){
+ detector_output.history = "";
+ detector_output.value = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+}
\ No newline at end of file
diff --git a/HTML/Assets/Detectors/Lumilo/system_misuse.js b/HTML/Assets/Detectors/Lumilo/system_misuse.js
new file mode 100644
index 0000000..7300dd4
--- /dev/null
+++ b/HTML/Assets/Detectors/Lumilo/system_misuse.js
@@ -0,0 +1,106 @@
+//detector template
+
+//add output variable name below
+var variableName = "system_misuse"
+
+//initializations (do not touch)
+var detector_output = {name: variableName,
+ category: "Dashboard",
+ value: "0, none",
+ history: "",
+ skill_names: "",
+ step_id: "",
+ transaction_id: "",
+ time: ""
+ };
+var mailer;
+
+//initialize any custom global variables for this detector here
+var prevStep = ""
+
+function receive_transaction( e ){
+ //e is the data of the transaction from mailer from transaction assembler
+
+ //set conditions under which transaction should be processed
+ //(i.e., to update internal state and history, without
+ //necessarily updating external state and history)
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ //do not touch
+ rawSkills = e.data.tutor_data.skills
+ var currSkills = []
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ currSkills.push(rawSkills[property].name + "/" + rawSkills[property].category)
+ }
+ }
+ detector_output.skill_names = currSkills;
+ detector_output.step_id = e.data.tutor_data.step_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+ var booleanValues = [0, 1];
+ detector_output.value = str(booleanValues[Math.floor(Math.random() * booleanValues.length)]) ;
+
+ }
+
+ //set conditions under which detector should update
+ //external state and history
+ if(e.data.actor == 'student' && e.data.tool_data.action != "UpdateVariable"){
+ detector_output.time = new Date();
+ detector_output.transaction_id = e.data.transaction_id;
+
+ //custom processing (insert code here)
+ //
+ //
+ //
+ //
+ //
+
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output);
+ }
+}
+
+
+self.onmessage = function ( e ) {
+ console.log(variableName, " self.onmessage:", e, e.data, (e.data?e.data.commmand:null), (e.data?e.data.transaction:null), e.ports);
+ switch( e.data.command )
+ {
+ case "connectMailer":
+ mailer = e.ports[0];
+ mailer.onmessage = receive_transaction;
+ break;
+ case "initialize":
+ for (initItem in e.data.initializer){
+ if (e.data.initializer[initItem].name == variableName){
+ detector_output.history = e.data.initializer[initItem].history;
+ detector_output.value = e.data.initializer[initItem].value;
+ }
+ }
+
+ //optional: Below, specify conditions under which a detector
+ //should NOT remember their most recent value and history (using the variable "detectorForget").
+ //(e.g., setting the condition to "true" will mean that the detector
+ // will always be reset between problems... and setting the condition to "false"
+ // means that the detector will never be reset between problems)
+ //
+ //
+ //
+ detectorForget = true;
+
+ if (detectorForget){
+ detector_output.history = "";
+ detector_output.value = 0;
+ }
+ break;
+ default:
+ break;
+
+ }
+
+}
\ No newline at end of file