summaryrefslogtreecommitdiff
path: root/threads.js
diff options
context:
space:
mode:
Diffstat (limited to 'threads.js')
-rw-r--r--threads.js190
1 files changed, 165 insertions, 25 deletions
diff --git a/threads.js b/threads.js
index 1f45162..f28704d 100644
--- a/threads.js
+++ b/threads.js
@@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff ////////////////////////////////////////////////////////
-modules.threads = '2015-February-28';
+modules.threads = '2015-March-23';
var ThreadManager;
var Process;
@@ -128,6 +128,15 @@ function snapEquals(a, b) {
return x === y;
}
+// stricter alternative to parseFloat
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat/
+function filterFloat(value) {
+ if(/^(\-|\+)?([0-9]+(\.[0-9]+)?|Infinity)$/
+ .test(value))
+ return Number(value);
+ return NaN;
+}
+
// ThreadManager ///////////////////////////////////////////////////////
function ThreadManager() {
@@ -441,11 +450,20 @@ Process.prototype.pause = function () {
if (this.context && this.context.startTime) {
this.pauseOffset = Date.now() - this.context.startTime;
}
+ if (this.context.activeNote) {
+ this.context.activeNote.stop();
+ }
};
Process.prototype.resume = function () {
this.isPaused = false;
this.pauseOffset = null;
+ if (this.context.activeNote) {
+ if (this.context.activeNote.oscillator === null) {
+ // prevents Note from resuming twice
+ this.context.activeNote.play();
+ }
+ }
};
Process.prototype.pauseStep = function () {
@@ -735,6 +753,13 @@ Process.prototype.expectReport = function () {
// Process Exception Handling
+Process.prototype.checkIfList = function (maybeList) {
+ if (!(maybeList instanceof List)) {
+ maybeList = new List();
+ }
+ return maybeList;
+};
+
Process.prototype.handleError = function (error, element) {
var m = element;
this.stop();
@@ -1159,7 +1184,7 @@ Process.prototype.doSetVar = function (varName, value) {
name = name.expression.blockSpec;
}
}
- varFrame.setVar(name, value);
+ varFrame.setVar(name, value, this.blockReceiver());
};
Process.prototype.doChangeVar = function (varName, value) {
@@ -1171,7 +1196,7 @@ Process.prototype.doChangeVar = function (varName, value) {
name = name.expression.blockSpec;
}
}
- varFrame.changeVar(name, value);
+ varFrame.changeVar(name, value, this.blockReceiver());
};
Process.prototype.reportGetVar = function () {
@@ -1216,7 +1241,7 @@ Process.prototype.doShowVar = function (varName) {
}
// if no watcher exists, create a new one
isGlobal = contains(
- this.homeContext.receiver.variables.parentFrame.names(),
+ this.homeContext.receiver.globalVariables().names(),
varName
);
if (isGlobal || target.owner) {
@@ -1295,6 +1320,77 @@ Process.prototype.doRemoveTemporaries = function () {
}
};
+// Peer to peer primitives
+
+Process.prototype.sendPeerMessage = function (message, peer) {
+ var myself = this;
+ var stage = this.homeContext.receiver.parentThatIsA(StageMorph),
+ ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
+
+ if (peer instanceof List) {
+ peer.asArray().forEach(function (singlePeer) {
+ myself.sendPeerMessage(message, singlePeer);
+ });
+ return;
+ }
+ if (peer == ide.peer.id) {
+ stage.newPeerMessage(message, peer);
+ return;
+ }
+
+ var connection = ide.peer.connect(peer, {reliable: true});
+ connection.on('open', function () {
+ var data;
+ if (typeof message == "string") {
+ data = message;
+ } else if (typeof message == "function") {
+ data = message.toString();
+ } else {
+ data = ide.serializer.serialize(message);
+ }
+ connection.send(data);
+ });
+};
+
+Process.prototype.reportPeerList = function () {
+ var myself = this;
+
+ if (!this.context.wait) {
+ this.context.wait = true;
+ var ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
+ ide.peer.listAllPeers(function (peers) {
+ myself.context.result = new List(peers);
+ });
+ } else if (this.context.result) {
+ return this.context.result;
+ }
+
+ this.pushContext('doYield');
+ this.pushContext();
+};
+
+Process.prototype.reportPeerId = function () {
+ var ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
+ return ide.peer.id;
+};
+
+// Process sprite inheritance primitives
+
+Process.prototype.doDeleteAttr = function (attrName) {
+ // currently only variables are deletable
+ var name = attrName,
+ rcvr = this.blockReceiver();
+
+ if (name instanceof Context) {
+ if (name.expression.selector === 'reportGetVar') {
+ name = name.expression.blockSpec;
+ }
+ }
+ if (contains(rcvr.inheritedVariableNames(true), name)) {
+ rcvr.deleteVariable(name);
+ }
+};
+
// Process lists primitives
Process.prototype.reportNewList = function (elements) {
@@ -1306,15 +1402,19 @@ Process.prototype.reportCONS = function (car, cdr) {
};
Process.prototype.reportCDR = function (list) {
+ list = this.checkIfList(list);
return list.cdr();
};
Process.prototype.doAddToList = function (element, list) {
+ list = this.checkIfList(list);
list.add(element);
};
Process.prototype.doDeleteFromList = function (index, list) {
var idx = index;
+ list = this.checkIfList(list);
+
if (this.inputOption(index) === 'all') {
return list.clear();
}
@@ -1329,6 +1429,8 @@ Process.prototype.doDeleteFromList = function (index, list) {
Process.prototype.doInsertInList = function (element, index, list) {
var idx = index;
+ list = this.checkIfList(list);
+
if (index === '') {
return null;
}
@@ -1343,6 +1445,8 @@ Process.prototype.doInsertInList = function (element, index, list) {
Process.prototype.doReplaceInList = function (index, list, element) {
var idx = index;
+ list = this.checkIfList(list);
+
if (index === '') {
return null;
}
@@ -1357,6 +1461,8 @@ Process.prototype.doReplaceInList = function (index, list, element) {
Process.prototype.reportListItem = function (index, list) {
var idx = index;
+ list = this.checkIfList(list);
+
if (index === '') {
return '';
}
@@ -1370,10 +1476,12 @@ Process.prototype.reportListItem = function (index, list) {
};
Process.prototype.reportListLength = function (list) {
+ list = this.checkIfList(list);
return list.length();
};
Process.prototype.reportListContainsItem = function (list, element) {
+ list = this.checkIfList(list);
return list.contains(element);
};
@@ -1429,7 +1537,7 @@ Process.prototype.doStopAll = function () {
if (this.homeContext.receiver) {
stage = this.homeContext.receiver.parentThatIsA(StageMorph);
if (stage) {
- stage.threads.resumeAll(stage);
+ //stage.threads.resumeAll(stage); // leads to a strange Note bug
stage.keysPressed = {};
stage.threads.stopAll();
stage.stopAllActiveSounds();
@@ -1632,6 +1740,8 @@ Process.prototype.reportMap = function (reporter, list) {
// documented in each of the variants' code (linked or arrayed) below
var next;
+ list = this.checkIfList(list);
+
if (list.isLinked) {
// this.context.inputs:
// [0] - reporter
@@ -1920,6 +2030,7 @@ Process.prototype.reportIsA = function (thing, typeString) {
Process.prototype.reportTypeOf = function (thing) {
// answer a string denoting the argument's type
var exp;
+
if (thing === null || (thing === undefined)) {
return 'nothing';
}
@@ -2921,18 +3032,32 @@ Process.prototype.doPlayNote = function (pitch, beats) {
Process.prototype.doPlayNoteForSecs = function (pitch, secs) {
// interpolated
+ var receiver = this.homeContext.receiver;
+ var volume = receiver.volume;
+ var muted = receiver.parentThatIsA(StageMorph).muted;
+
+ if (muted === true) {
+ volume = 0;
+ }
+
if (!this.context.startTime) {
this.context.startTime = Date.now();
- this.context.activeNote = new Note(pitch);
+ this.context.activeNote = new Note(pitch, volume);
this.context.activeNote.play();
}
+
if ((Date.now() - this.context.startTime) >= (secs * 1000)) {
if (this.context.activeNote) {
this.context.activeNote.stop();
this.context.activeNote = null;
}
return null;
+ } else if (this.context.activeNote) {
+ if (this.context.activeNote.volume !== volume) {
+ this.context.activeNote.setVolume(volume);
+ }
}
+
this.pushContext('doYield');
this.pushContext();
};
@@ -3448,35 +3573,50 @@ VariableFrame.prototype.silentFind = function (name) {
return null;
};
-VariableFrame.prototype.setVar = function (name, value) {
-/*
- change the specified variable if it exists
- else throw an error, because variables need to be
- declared explicitly (e.g. through a "script variables" block),
- before they can be accessed.
-*/
+VariableFrame.prototype.setVar = function (name, value, sender) {
+ // change the specified variable if it exists
+ // else throw an error, because variables need to be
+ // declared explicitly (e.g. through a "script variables" block),
+ // before they can be accessed.
+ // if the found frame is inherited by the sender sprite
+ // shadow it (create an explicit one for the sender)
+ // before setting the value ("create-on-write")
+
var frame = this.find(name);
if (frame) {
- frame.vars[name].value = value;
+ if (sender instanceof SpriteMorph &&
+ (frame.owner instanceof SpriteMorph) &&
+ (sender !== frame.owner)) {
+ sender.shadowVar(name, value);
+ } else {
+ frame.vars[name].value = value;
+ }
}
};
-VariableFrame.prototype.changeVar = function (name, delta) {
-/*
- change the specified variable if it exists
- else throw an error, because variables need to be
- declared explicitly (e.g. through a "script variables" block,
- before they can be accessed.
-*/
+VariableFrame.prototype.changeVar = function (name, delta, sender) {
+ // change the specified variable if it exists
+ // else throw an error, because variables need to be
+ // declared explicitly (e.g. through a "script variables" block,
+ // before they can be accessed.
+ // if the found frame is inherited by the sender sprite
+ // shadow it (create an explicit one for the sender)
+ // before changing the value ("create-on-write")
+
var frame = this.find(name),
- value;
+ value,
+ newValue;
if (frame) {
value = parseFloat(frame.vars[name].value);
- if (isNaN(value)) {
- frame.vars[name].value = delta;
+ newValue = isNaN(value) ? delta : value + parseFloat(delta);
+ if (sender instanceof SpriteMorph &&
+ (frame.owner instanceof SpriteMorph) &&
+ (sender !== frame.owner)) {
+ sender.shadowVar(name, newValue);
} else {
- frame.vars[name].value = value + parseFloat(delta);
+ frame.vars[name].value = newValue;
}
+
}
};