kopia lustrzana https://github.com/backface/turtlestitch
replace morphic version due to new chrome bugs
rodzic
2b6160e80b
commit
d6fe6e23f9
|
@ -5,7 +5,7 @@
|
||||||
<title>TurtleStitch</title>
|
<title>TurtleStitch</title>
|
||||||
<link rel="shortcut icon" href="stitchcode/favicon-32x32.png" type="image/png" />
|
<link rel="shortcut icon" href="stitchcode/favicon-32x32.png" type="image/png" />
|
||||||
|
|
||||||
<script src="src/morphic.js?version=2022-01-28"></script>
|
<script src="src/morphic.js?version=2023-11-24"></script>
|
||||||
<script src="src/symbols.js?version=2021-03-03"></script>
|
<script src="src/symbols.js?version=2021-03-03"></script>
|
||||||
<script src="src/widgets.js?version=2021-17-09"></script>
|
<script src="src/widgets.js?version=2021-17-09"></script>
|
||||||
<script src="src/blocks.js?version=2022-01-30"></script>
|
<script src="src/blocks.js?version=2022-01-30"></script>
|
||||||
|
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
210
src/morphic.js
210
src/morphic.js
|
@ -8,7 +8,7 @@
|
||||||
written by Jens Mönig
|
written by Jens Mönig
|
||||||
jens@moenig.org
|
jens@moenig.org
|
||||||
|
|
||||||
Copyright (C) 2010-2022 by Jens Mönig
|
Copyright (C) 2010-2023 by Jens Mönig
|
||||||
|
|
||||||
This file is part of Snap!.
|
This file is part of Snap!.
|
||||||
|
|
||||||
|
@ -317,7 +317,6 @@
|
||||||
var world1, world2;
|
var world1, world2;
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
disableRetinaSupport();
|
|
||||||
world1 = new WorldMorph(
|
world1 = new WorldMorph(
|
||||||
document.getElementById('world1'), false);
|
document.getElementById('world1'), false);
|
||||||
world2 = new WorldMorph(
|
world2 = new WorldMorph(
|
||||||
|
@ -326,7 +325,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
function loop() {
|
function loop() {
|
||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
world1.doOneCycle();
|
world1.doOneCycle();
|
||||||
world2.doOneCycle();
|
world2.doOneCycle();
|
||||||
}
|
}
|
||||||
|
@ -642,7 +641,7 @@
|
||||||
|
|
||||||
Drops of image elements from outside the world canvas are dispatched as
|
Drops of image elements from outside the world canvas are dispatched as
|
||||||
|
|
||||||
droppedImage(aCanvas, name)
|
droppedImage(aCanvas, name, embeddedData)
|
||||||
droppedSVG(anImage, name)
|
droppedSVG(anImage, name)
|
||||||
|
|
||||||
events to interested Morphs at the mouse pointer. If you want your Morph
|
events to interested Morphs at the mouse pointer. If you want your Morph
|
||||||
|
@ -663,8 +662,22 @@
|
||||||
droppedImage() event with a canvas containing a rasterized version of the
|
droppedImage() event with a canvas containing a rasterized version of the
|
||||||
SVG.
|
SVG.
|
||||||
|
|
||||||
The same applies to drops of audio or text files from outside the world
|
Note that PNG images provide for embedded text comments, which can be used
|
||||||
canvas.
|
to include code or arbitrary data such as a CSV, JSON or XML file inside
|
||||||
|
the image. Such a payload has to be identified by an agreed-upon marker.
|
||||||
|
The default tag is stored in MorphicPreferences and can be overriden by
|
||||||
|
apps wishing to make use of this feature. If such an embedded text-payload
|
||||||
|
is found inside a PNG it is passed as the optional third "embeddedData"
|
||||||
|
parameter to the "droppedImage()" event. embedded text only applies to PNGs.
|
||||||
|
You can embed a string into the PNG metadata of a PNG by calling
|
||||||
|
|
||||||
|
embedMetadataPNG(aCanvas, aString)
|
||||||
|
|
||||||
|
with a raster image represented by a canvas and a string that is to be
|
||||||
|
embedded into the PNG's metadata.
|
||||||
|
|
||||||
|
The same event mechanism applies to drops of audio or text files from
|
||||||
|
outside the world canvas.
|
||||||
|
|
||||||
Those are dispatched as
|
Those are dispatched as
|
||||||
|
|
||||||
|
@ -1276,6 +1289,8 @@
|
||||||
Jason N (@cyderize) contributed native copy & paste for text editing.
|
Jason N (@cyderize) contributed native copy & paste for text editing.
|
||||||
Bartosz Leper contributed retina display support.
|
Bartosz Leper contributed retina display support.
|
||||||
Zhenlei Jia and Dariusz Dorożalski pioneered IME text editing.
|
Zhenlei Jia and Dariusz Dorożalski pioneered IME text editing.
|
||||||
|
Dariusz Dorożalski and Jesus Villalobos contributed embedding blocks
|
||||||
|
into image metadata.
|
||||||
Bernat Romagosa contributed to text editing and to the core design.
|
Bernat Romagosa contributed to text editing and to the core design.
|
||||||
Michael Ball found and fixed a longstanding scrolling bug.
|
Michael Ball found and fixed a longstanding scrolling bug.
|
||||||
Brian Harvey contributed to the design and implementation of submenus.
|
Brian Harvey contributed to the design and implementation of submenus.
|
||||||
|
@ -1289,9 +1304,9 @@
|
||||||
|
|
||||||
/*global window, HTMLCanvasElement, FileReader, Audio, FileList, Map*/
|
/*global window, HTMLCanvasElement, FileReader, Audio, FileList, Map*/
|
||||||
|
|
||||||
/*jshint esversion: 6*/
|
/*jshint esversion: 11, bitwise: false*/
|
||||||
|
|
||||||
var morphicVersion = '2022-January-28';
|
var morphicVersion = '2023-November-07';
|
||||||
var modules = {}; // keep track of additional loaded modules
|
var modules = {}; // keep track of additional loaded modules
|
||||||
var useBlurredShadows = true;
|
var useBlurredShadows = true;
|
||||||
|
|
||||||
|
@ -1303,6 +1318,7 @@ const CLEAR = new Color(0, 0, 0, 0);
|
||||||
Object.freeze(ZERO);
|
Object.freeze(ZERO);
|
||||||
Object.freeze(BLACK);
|
Object.freeze(BLACK);
|
||||||
Object.freeze(WHITE);
|
Object.freeze(WHITE);
|
||||||
|
Object.freeze(CLEAR);
|
||||||
|
|
||||||
var standardSettings = {
|
var standardSettings = {
|
||||||
minimumFontHeight: getMinimumFontHeight(), // browser settings
|
minimumFontHeight: getMinimumFontHeight(), // browser settings
|
||||||
|
@ -1318,6 +1334,7 @@ var standardSettings = {
|
||||||
mouseScrollAmount: 40,
|
mouseScrollAmount: 40,
|
||||||
useSliderForInput: false,
|
useSliderForInput: false,
|
||||||
isTouchDevice: false, // turned on by touch events, don't set
|
isTouchDevice: false, // turned on by touch events, don't set
|
||||||
|
pngPayloadMarker: 'Data\tPayload\tEmbedded',
|
||||||
rasterizeSVGs: false,
|
rasterizeSVGs: false,
|
||||||
isFlat: false,
|
isFlat: false,
|
||||||
grabThreshold: 5,
|
grabThreshold: 5,
|
||||||
|
@ -1338,6 +1355,7 @@ var touchScreenSettings = {
|
||||||
mouseScrollAmount: 40,
|
mouseScrollAmount: 40,
|
||||||
useSliderForInput: false,
|
useSliderForInput: false,
|
||||||
isTouchDevice: true,
|
isTouchDevice: true,
|
||||||
|
pngPayloadMarker: 'Data\tPayload\tEmbedded',
|
||||||
rasterizeSVGs: false,
|
rasterizeSVGs: false,
|
||||||
isFlat: false,
|
isFlat: false,
|
||||||
grabThreshold: 5,
|
grabThreshold: 5,
|
||||||
|
@ -1569,6 +1587,45 @@ function copy(target) {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function embedMetadataPNG(aCanvas, aString) {
|
||||||
|
var embedTag = MorphicPreferences.pngPayloadMarker,
|
||||||
|
crc32 = (str, crc) => {
|
||||||
|
let table = [...Array(256).keys()].map(it =>
|
||||||
|
[...Array(8)].reduce((cc) =>
|
||||||
|
(cc & 1) ? (0xedb88320 ^ (cc >>> 1)) : (cc >>> 1), it)
|
||||||
|
);
|
||||||
|
crc = [...str].reduce(
|
||||||
|
(crc, ch) => (crc >>> 8) ^ table[(crc ^ ch.charCodeAt(0)) & 0xff],
|
||||||
|
(crc ? crc = 0 : crc) ^ (-1) // (crc ||= 0) ^ (-1)
|
||||||
|
);
|
||||||
|
return ( crc ^ (-1) ) >>> 0;
|
||||||
|
},
|
||||||
|
arr2Str = (arr) =>
|
||||||
|
arr.reduce((res, byte) => res + String.fromCharCode(byte), ''),
|
||||||
|
int2BStr = (val) =>
|
||||||
|
arr2Str(Array.from(new Uint8Array(new Uint32Array( [val] ).buffer)).reverse()),
|
||||||
|
buildChunk = (data) => {
|
||||||
|
let res = "iTXt" + data;
|
||||||
|
return int2BStr(data.length) + res + int2BStr(crc32(res));
|
||||||
|
},
|
||||||
|
parts = aCanvas.toDataURL("image/png").split(","),
|
||||||
|
bPart = atob(parts[1]).split(""),
|
||||||
|
newChunk = buildChunk(
|
||||||
|
"Snap!_SRC\0\0\0\0\0" +
|
||||||
|
embedTag +
|
||||||
|
encodeURIComponent(aString) +
|
||||||
|
embedTag
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
bPart.splice(-12, 0, ...newChunk);
|
||||||
|
parts[1] = btoa(bPart.join(""));
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
return parts.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Retina Display Support //////////////////////////////////////////////
|
// Retina Display Support //////////////////////////////////////////////
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1721,7 +1778,7 @@ function enableRetinaSupport() {
|
||||||
this.height = prevHeight;
|
this.height = prevHeight;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
configurable: true // [Jens]: allow to be deleted an reconfigured
|
configurable: true // [Jens]: allow to be deleted and reconfigured
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(canvasProto, 'width', {
|
Object.defineProperty(canvasProto, 'width', {
|
||||||
|
@ -1739,7 +1796,7 @@ function enableRetinaSupport() {
|
||||||
context.restore();
|
context.restore();
|
||||||
context.save();
|
context.save();
|
||||||
*/
|
*/
|
||||||
context.scale(pixelRatio, pixelRatio);
|
context?.scale(pixelRatio, pixelRatio);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Retina Display Support Problem', err);
|
console.log('Retina Display Support Problem', err);
|
||||||
uber.width.set.call(this, width);
|
uber.width.set.call(this, width);
|
||||||
|
@ -1760,7 +1817,7 @@ function enableRetinaSupport() {
|
||||||
context.restore();
|
context.restore();
|
||||||
context.save();
|
context.save();
|
||||||
*/
|
*/
|
||||||
context.scale(pixelRatio, pixelRatio);
|
context?.scale(pixelRatio, pixelRatio);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2708,7 +2765,7 @@ Rectangle.prototype.boundingBox = function () {
|
||||||
|
|
||||||
Rectangle.prototype.center = function () {
|
Rectangle.prototype.center = function () {
|
||||||
return this.origin.add(
|
return this.origin.add(
|
||||||
this.corner.subtract(this.origin).floorDivideBy(2)
|
this.corner.subtract(this.origin).divideBy(2)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3407,7 +3464,7 @@ Morph.prototype.setBottom = function (y) {
|
||||||
Morph.prototype.setCenter = function (aPoint) {
|
Morph.prototype.setCenter = function (aPoint) {
|
||||||
this.setPosition(
|
this.setPosition(
|
||||||
aPoint.subtract(
|
aPoint.subtract(
|
||||||
this.extent().floorDivideBy(2)
|
this.extent().divideBy(2)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3415,7 +3472,7 @@ Morph.prototype.setCenter = function (aPoint) {
|
||||||
Morph.prototype.setFullCenter = function (aPoint) {
|
Morph.prototype.setFullCenter = function (aPoint) {
|
||||||
this.setPosition(
|
this.setPosition(
|
||||||
aPoint.subtract(
|
aPoint.subtract(
|
||||||
this.fullBounds().extent().floorDivideBy(2)
|
this.fullBounds().extent().divideBy(2)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4456,7 +4513,28 @@ Morph.prototype.developersMenu = function () {
|
||||||
);
|
);
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
"pic...",
|
"pic...",
|
||||||
() => window.open(this.fullImage().toDataURL()),
|
() => {
|
||||||
|
var imgURL = this.fullImage().toDataURL(),
|
||||||
|
doc, body, tag, str;
|
||||||
|
try {
|
||||||
|
doc = window.open('', '_blank', 'popup').document;
|
||||||
|
body = doc.getElementsByTagName('body')[0];
|
||||||
|
str = '' + this;
|
||||||
|
doc.title = str;
|
||||||
|
tag = doc.createElement('h1');
|
||||||
|
tag.textContent = str;
|
||||||
|
body.appendChild(tag);
|
||||||
|
tag = doc.createElement('img');
|
||||||
|
tag.alt = str;
|
||||||
|
tag.src = imgURL;
|
||||||
|
body.appendChild(tag);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
'failed to popup pic, morph:%O, error:%O, image URL:%O',
|
||||||
|
this, error, [imgURL]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
'open a new window\nwith a picture of this morph'
|
'open a new window\nwith a picture of this morph'
|
||||||
);
|
);
|
||||||
menu.addLine();
|
menu.addLine();
|
||||||
|
@ -8183,7 +8261,7 @@ MenuMorph.prototype.adjustWidths = function () {
|
||||||
if (item === this.label) {
|
if (item === this.label) {
|
||||||
item.text.setPosition(
|
item.text.setPosition(
|
||||||
item.center().subtract(
|
item.center().subtract(
|
||||||
item.text.extent().floorDivideBy(2)
|
item.text.extent().divideBy(2)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9289,7 +9367,25 @@ TextMorph.prototype.parse = function () {
|
||||||
this.maxLineWidth,
|
this.maxLineWidth,
|
||||||
context.measureText(oldline).width
|
context.measureText(oldline).width
|
||||||
);
|
);
|
||||||
oldline = word + ' ';
|
w = context.measureText(word).width;
|
||||||
|
if (w > this.maxWidth) {
|
||||||
|
oldline = '';
|
||||||
|
word.split('').forEach((letter, idx) => {
|
||||||
|
w = context.measureText(oldline + letter).width;
|
||||||
|
if (w > this.maxWidth && oldline.length) {
|
||||||
|
this.lines.push(oldline);
|
||||||
|
this.lineSlots.push(slot + idx);
|
||||||
|
this.maxLineWidth = Math.max(
|
||||||
|
this.maxLineWidth,
|
||||||
|
context.measureText(oldline).width
|
||||||
|
);
|
||||||
|
oldline = '';
|
||||||
|
}
|
||||||
|
oldline += letter;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
oldline = word + ' ';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
oldline = newline;
|
oldline = newline;
|
||||||
}
|
}
|
||||||
|
@ -9823,7 +9919,7 @@ TriggerMorph.prototype.createLabel = function () {
|
||||||
TriggerMorph.prototype.fixLayout = function () {
|
TriggerMorph.prototype.fixLayout = function () {
|
||||||
this.label.setPosition(
|
this.label.setPosition(
|
||||||
this.center().subtract(
|
this.center().subtract(
|
||||||
this.label.extent().floorDivideBy(2)
|
this.label.extent().divideBy(2)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -11163,6 +11259,7 @@ HandMorph.prototype.init = function (aWorld) {
|
||||||
this.temporaries = [];
|
this.temporaries = [];
|
||||||
this.touchHoldTimeout = null;
|
this.touchHoldTimeout = null;
|
||||||
this.contextMenuEnabled = false;
|
this.contextMenuEnabled = false;
|
||||||
|
this.touchStartPosition = null;
|
||||||
|
|
||||||
// properties for caching dragged objects:
|
// properties for caching dragged objects:
|
||||||
this.cachedFullImage = null;
|
this.cachedFullImage = null;
|
||||||
|
@ -11284,12 +11381,12 @@ HandMorph.prototype.grab = function (aMorph) {
|
||||||
if (!aMorph.noDropShadow) {
|
if (!aMorph.noDropShadow) {
|
||||||
aMorph.addShadow();
|
aMorph.addShadow();
|
||||||
}
|
}
|
||||||
this.add(aMorph);
|
|
||||||
|
|
||||||
// cache the dragged object's display resources
|
// cache the dragged object's display resources
|
||||||
this.cachedFullImage = aMorph.fullImage();
|
this.cachedFullImage = aMorph.fullImage();
|
||||||
this.cachedFullBounds = aMorph.fullBounds();
|
this.cachedFullBounds = aMorph.fullBounds();
|
||||||
|
|
||||||
|
this.add(aMorph);
|
||||||
this.changed();
|
this.changed();
|
||||||
if (oldParent && oldParent.reactToGrabOf) {
|
if (oldParent && oldParent.reactToGrabOf) {
|
||||||
oldParent.reactToGrabOf(aMorph);
|
oldParent.reactToGrabOf(aMorph);
|
||||||
|
@ -11409,6 +11506,10 @@ HandMorph.prototype.processTouchStart = function (event) {
|
||||||
MorphicPreferences.isTouchDevice = true;
|
MorphicPreferences.isTouchDevice = true;
|
||||||
clearInterval(this.touchHoldTimeout);
|
clearInterval(this.touchHoldTimeout);
|
||||||
if (event.touches.length === 1) {
|
if (event.touches.length === 1) {
|
||||||
|
this.touchStartPosition = new Point(
|
||||||
|
event.touches[0].pageX,
|
||||||
|
event.touches[0].pageY
|
||||||
|
);
|
||||||
this.touchHoldTimeout = setInterval( // simulate mouseRightClick
|
this.touchHoldTimeout = setInterval( // simulate mouseRightClick
|
||||||
() => {
|
() => {
|
||||||
this.processMouseDown({button: 2});
|
this.processMouseDown({button: 2});
|
||||||
|
@ -11425,7 +11526,12 @@ HandMorph.prototype.processTouchStart = function (event) {
|
||||||
};
|
};
|
||||||
|
|
||||||
HandMorph.prototype.processTouchMove = function (event) {
|
HandMorph.prototype.processTouchMove = function (event) {
|
||||||
|
var pos = new Point(event.touches[0].pageX, event.touches[0].pageY);
|
||||||
MorphicPreferences.isTouchDevice = true;
|
MorphicPreferences.isTouchDevice = true;
|
||||||
|
if (this.touchStartPosition.distanceTo(pos) <
|
||||||
|
MorphicPreferences.grabThreshold) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (event.touches.length === 1) {
|
if (event.touches.length === 1) {
|
||||||
var touch = event.touches[0];
|
var touch = event.touches[0];
|
||||||
this.processMouseMove(touch);
|
this.processMouseMove(touch);
|
||||||
|
@ -11643,7 +11749,7 @@ HandMorph.prototype.processDrop = function (event) {
|
||||||
onto the world canvas, turn it into an offscreen canvas or audio
|
onto the world canvas, turn it into an offscreen canvas or audio
|
||||||
element and dispatch the
|
element and dispatch the
|
||||||
|
|
||||||
droppedImage(canvas, name)
|
droppedImage(canvas, name, embeddedData)
|
||||||
droppedSVG(image, name)
|
droppedSVG(image, name)
|
||||||
droppedAudio(audio, name)
|
droppedAudio(audio, name)
|
||||||
droppedText(text, name, type)
|
droppedText(text, name, type)
|
||||||
|
@ -11692,16 +11798,38 @@ HandMorph.prototype.processDrop = function (event) {
|
||||||
function readImage(aFile) {
|
function readImage(aFile) {
|
||||||
var pic = new Image(),
|
var pic = new Image(),
|
||||||
frd = new FileReader(),
|
frd = new FileReader(),
|
||||||
trg = target;
|
trg = target,
|
||||||
|
embedTag = MorphicPreferences.pngPayloadMarker;
|
||||||
|
|
||||||
while (!trg.droppedImage) {
|
while (!trg.droppedImage) {
|
||||||
trg = trg.parent;
|
trg = trg.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
pic.onload = () => {
|
pic.onload = () => {
|
||||||
canvas = newCanvas(new Point(pic.width, pic.height), true);
|
(async () => {
|
||||||
canvas.getContext('2d').drawImage(pic, 0, 0);
|
// extract embedded data (e.g. blocks)
|
||||||
trg.droppedImage(canvas, aFile.name);
|
// from the image's metadata if present
|
||||||
bulkDrop();
|
var buff = new Uint8Array(await aFile?.arrayBuffer()),
|
||||||
|
strBuff = buff.reduce((acc, b) =>
|
||||||
|
acc + String.fromCharCode(b), ""),
|
||||||
|
embedded;
|
||||||
|
|
||||||
|
if (strBuff.includes(embedTag)) {
|
||||||
|
try {
|
||||||
|
embedded = decodeURIComponent(
|
||||||
|
(strBuff)?.split(embedTag)[1]
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas = newCanvas(new Point(pic.width, pic.height), true);
|
||||||
|
canvas.getContext('2d').drawImage(pic, 0, 0);
|
||||||
|
trg.droppedImage(canvas, aFile.name, embedded);
|
||||||
|
bulkDrop();
|
||||||
|
})();
|
||||||
};
|
};
|
||||||
|
|
||||||
frd = new FileReader();
|
frd = new FileReader();
|
||||||
frd.onloadend = (e) => pic.src = e.target.result;
|
frd.onloadend = (e) => pic.src = e.target.result;
|
||||||
frd.readAsDataURL(aFile);
|
frd.readAsDataURL(aFile);
|
||||||
|
@ -11953,6 +12081,10 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) {
|
||||||
this.activeMenu = null;
|
this.activeMenu = null;
|
||||||
this.activeHandle = null;
|
this.activeHandle = null;
|
||||||
|
|
||||||
|
if (!fillPage && aCanvas.isRetinaEnabled) {
|
||||||
|
this.initRetina();
|
||||||
|
}
|
||||||
|
|
||||||
this.initKeyboardHandler();
|
this.initKeyboardHandler();
|
||||||
this.resetKeyboardHandler();
|
this.resetKeyboardHandler();
|
||||||
this.initEventListeners();
|
this.initEventListeners();
|
||||||
|
@ -12081,16 +12213,29 @@ WorldMorph.prototype.fillPage = function () {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WorldMorph.prototype.initRetina = function () {
|
||||||
|
var canvasHeight = this.worldCanvas.getBoundingClientRect().height,
|
||||||
|
canvasWidth = this.worldCanvas.getBoundingClientRect().width;
|
||||||
|
this.worldCanvas.style.width = canvasWidth + 'px';
|
||||||
|
this.worldCanvas.width = canvasWidth;
|
||||||
|
this.setWidth(canvasWidth);
|
||||||
|
this.worldCanvas.style.height = canvasHeight + 'px';
|
||||||
|
this.worldCanvas.height = canvasHeight;
|
||||||
|
this.setHeight(canvasHeight);
|
||||||
|
};
|
||||||
|
|
||||||
// WorldMorph global pixel access:
|
// WorldMorph global pixel access:
|
||||||
|
|
||||||
WorldMorph.prototype.getGlobalPixelColor = function (point) {
|
WorldMorph.prototype.getGlobalPixelColor = function (point) {
|
||||||
// answer the color at the given point.
|
// answer the color at the given point.
|
||||||
var dta = this.worldCanvas.getContext('2d').getImageData(
|
// first, create a new temporary canvas representing the fullImage
|
||||||
point.x,
|
// and sample that one instead of the actual world canvas
|
||||||
point.y,
|
// this slows things down but keeps Chrome from crashing
|
||||||
1,
|
// in v119 in the Fall of 2023
|
||||||
1
|
var dta = Morph.prototype.fullImage.call(this)
|
||||||
).data;
|
.getContext('2d')
|
||||||
|
.getImageData(point.x, point.y, 1, 1)
|
||||||
|
.data;
|
||||||
return new Color(dta[0], dta[1], dta[2]);
|
return new Color(dta[0], dta[1], dta[2]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12113,6 +12258,8 @@ WorldMorph.prototype.initKeyboardHandler = function () {
|
||||||
kbd.world = this;
|
kbd.world = this;
|
||||||
kbd.style.zIndex = -1;
|
kbd.style.zIndex = -1;
|
||||||
kbd.autofocus = true;
|
kbd.autofocus = true;
|
||||||
|
kbd.style.width = '0px';
|
||||||
|
kbd.style.height = '0px';
|
||||||
document.body.appendChild(kbd);
|
document.body.appendChild(kbd);
|
||||||
this.keyboardHandler = kbd;
|
this.keyboardHandler = kbd;
|
||||||
|
|
||||||
|
@ -12305,6 +12452,7 @@ WorldMorph.prototype.initEventListeners = function () {
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"drop",
|
"drop",
|
||||||
event => {
|
event => {
|
||||||
|
this.hand.processMouseMove(event);
|
||||||
this.hand.processDrop(event);
|
this.hand.processDrop(event);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
written by Jens Mönig
|
written by Jens Mönig
|
||||||
jens@moenig.org
|
jens@moenig.org
|
||||||
|
|
||||||
Copyright (C) 2010-2020 by Jens Mönig
|
Copyright (C) 2010-2022 by Jens Mönig
|
||||||
|
|
||||||
This documentation last changed: June 9, 2020
|
|
||||||
|
|
||||||
|
This documentation last changed: November 22
|
||||||
|
|
||||||
This file is part of Snap!.
|
This file is part of Snap!.
|
||||||
|
|
||||||
Snap! is free software: you can redistribute it and/or modify
|
Snap! is free software: you can redistribute it and/or modify
|
||||||
|
@ -303,7 +303,7 @@
|
||||||
-------------------
|
-------------------
|
||||||
If you wish to create a web page with more than one world, make
|
If you wish to create a web page with more than one world, make
|
||||||
sure to prevent each world from auto-filling the whole page and
|
sure to prevent each world from auto-filling the whole page and
|
||||||
include it in the main loop. It's also a good idea to give each
|
include it in the main loop. It's also a good idea to give each
|
||||||
world its own tabindex:
|
world its own tabindex:
|
||||||
|
|
||||||
example html file:
|
example html file:
|
||||||
|
@ -318,7 +318,6 @@
|
||||||
var world1, world2;
|
var world1, world2;
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
disableRetinaSupport();
|
|
||||||
world1 = new WorldMorph(
|
world1 = new WorldMorph(
|
||||||
document.getElementById('world1'), false);
|
document.getElementById('world1'), false);
|
||||||
world2 = new WorldMorph(
|
world2 = new WorldMorph(
|
||||||
|
@ -327,7 +326,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
function loop() {
|
function loop() {
|
||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
world1.doOneCycle();
|
world1.doOneCycle();
|
||||||
world2.doOneCycle();
|
world2.doOneCycle();
|
||||||
}
|
}
|
||||||
|
@ -472,6 +471,8 @@
|
||||||
mouseLeave
|
mouseLeave
|
||||||
mouseEnterDragging
|
mouseEnterDragging
|
||||||
mouseLeaveDragging
|
mouseLeaveDragging
|
||||||
|
mouseEnterBounds
|
||||||
|
mouseLeaveBounds
|
||||||
mouseMove
|
mouseMove
|
||||||
mouseScroll
|
mouseScroll
|
||||||
|
|
||||||
|
@ -480,7 +481,7 @@
|
||||||
|
|
||||||
MyMorph.prototype.mouseMove = function(pos) {};
|
MyMorph.prototype.mouseMove = function(pos) {};
|
||||||
|
|
||||||
All of these methods have as optional parameter a Point object
|
Most of these methods have as optional parameter a Point object
|
||||||
indicating the current position of the Hand inside the World's
|
indicating the current position of the Hand inside the World's
|
||||||
coordinate system. The
|
coordinate system. The
|
||||||
|
|
||||||
|
@ -490,6 +491,16 @@
|
||||||
currently pressed mouse button, which is either 'left' or 'right'.
|
currently pressed mouse button, which is either 'left' or 'right'.
|
||||||
You can use this to let users interact with 3D environments.
|
You can use this to let users interact with 3D environments.
|
||||||
|
|
||||||
|
The
|
||||||
|
|
||||||
|
mouseEnterDragging(morph)
|
||||||
|
mouseLeaveDragging(morph)
|
||||||
|
mouseEnterBounds(morph)
|
||||||
|
mouseLeaveBounds(morph)
|
||||||
|
|
||||||
|
event methods have as optional parameter the morph currently dragged by
|
||||||
|
the Hand, if any.
|
||||||
|
|
||||||
Events may be "bubbled" up a morph's owner chain by calling
|
Events may be "bubbled" up a morph's owner chain by calling
|
||||||
|
|
||||||
this.escalateEvent(functionName, arg)
|
this.escalateEvent(functionName, arg)
|
||||||
|
@ -631,7 +642,7 @@
|
||||||
|
|
||||||
Drops of image elements from outside the world canvas are dispatched as
|
Drops of image elements from outside the world canvas are dispatched as
|
||||||
|
|
||||||
droppedImage(aCanvas, name)
|
droppedImage(aCanvas, name, embeddedData)
|
||||||
droppedSVG(anImage, name)
|
droppedSVG(anImage, name)
|
||||||
|
|
||||||
events to interested Morphs at the mouse pointer. If you want your Morph
|
events to interested Morphs at the mouse pointer. If you want your Morph
|
||||||
|
@ -652,8 +663,22 @@
|
||||||
droppedImage() event with a canvas containing a rasterized version of the
|
droppedImage() event with a canvas containing a rasterized version of the
|
||||||
SVG.
|
SVG.
|
||||||
|
|
||||||
The same applies to drops of audio or text files from outside the world
|
Note that PNG images provide for embedded text comments, which can be used
|
||||||
canvas.
|
to include code or arbitrary data such as a CSV, JSON or XML file inside
|
||||||
|
the image. Such a payload has to be identified by an agreed-upon marker.
|
||||||
|
The default tag is stored in MorphicPreferences and can be overriden by
|
||||||
|
apps wishing to make use of this feature. If such an embedded text-payload
|
||||||
|
is found inside a PNG it is passed as the optional third "embeddedData"
|
||||||
|
parameter to the "droppedImage()" event. embedded text only applies to PNGs.
|
||||||
|
You can embed a string into the PNG metadata of a PNG by calling
|
||||||
|
|
||||||
|
embedMetadataPNG(aCanvas, aString)
|
||||||
|
|
||||||
|
with a raster image represented by a canvas and a string that is to be
|
||||||
|
embedded into the PNG's metadata.
|
||||||
|
|
||||||
|
The same event mechanism applies to drops of audio or text files from
|
||||||
|
outside the world canvas.
|
||||||
|
|
||||||
Those are dispatched as
|
Those are dispatched as
|
||||||
|
|
||||||
|
@ -667,6 +692,15 @@
|
||||||
|
|
||||||
droppedBinary(anArrayBuffer, name)
|
droppedBinary(anArrayBuffer, name)
|
||||||
|
|
||||||
|
In case multiple files are dropped simulateneously the events
|
||||||
|
|
||||||
|
beginBulkDrop()
|
||||||
|
endBulkDrop()
|
||||||
|
|
||||||
|
are dispatched to to Morphs interested in bracketing the bulk operation,
|
||||||
|
and the endBulkDrop() event is only signalled after the contents last file
|
||||||
|
has been asynchronously made available.
|
||||||
|
|
||||||
|
|
||||||
(e) keyboard events
|
(e) keyboard events
|
||||||
-------------------
|
-------------------
|
||||||
|
@ -1256,10 +1290,12 @@
|
||||||
Jason N (@cyderize) contributed native copy & paste for text editing.
|
Jason N (@cyderize) contributed native copy & paste for text editing.
|
||||||
Bartosz Leper contributed retina display support.
|
Bartosz Leper contributed retina display support.
|
||||||
Zhenlei Jia and Dariusz Dorożalski pioneered IME text editing.
|
Zhenlei Jia and Dariusz Dorożalski pioneered IME text editing.
|
||||||
|
Dariusz Dorożalski and Jesus Villalobos contributed embedding blocks
|
||||||
|
into image metadata.
|
||||||
Bernat Romagosa contributed to text editing and to the core design.
|
Bernat Romagosa contributed to text editing and to the core design.
|
||||||
Michael Ball found and fixed a longstanding scrolling bug.
|
Michael Ball found and fixed a longstanding scrolling bug.
|
||||||
Brian Harvey contributed to the design and implementation of submenus.
|
Brian Harvey contributed to the design and implementation of submenus.
|
||||||
Ken Kahn contributed to Chinese keboard entry and Android support.
|
Ken Kahn contributed to Chinese keboard entry and Android support.
|
||||||
Brian Broll contributed clickable URLs in text elements.
|
Brian Broll contributed clickable URLs in text elements and many bugfixes.
|
||||||
|
|
||||||
- Jens Mönig
|
- Jens Mönig
|
||||||
|
|
Ładowanie…
Reference in New Issue