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>
|
||||
<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/widgets.js?version=2021-17-09"></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
|
||||
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!.
|
||||
|
||||
|
@ -317,7 +317,6 @@
|
|||
var world1, world2;
|
||||
|
||||
window.onload = function () {
|
||||
disableRetinaSupport();
|
||||
world1 = new WorldMorph(
|
||||
document.getElementById('world1'), false);
|
||||
world2 = new WorldMorph(
|
||||
|
@ -326,7 +325,7 @@
|
|||
};
|
||||
|
||||
function loop() {
|
||||
requestAnimationFrame(loop);
|
||||
requestAnimationFrame(loop);
|
||||
world1.doOneCycle();
|
||||
world2.doOneCycle();
|
||||
}
|
||||
|
@ -642,7 +641,7 @@
|
|||
|
||||
Drops of image elements from outside the world canvas are dispatched as
|
||||
|
||||
droppedImage(aCanvas, name)
|
||||
droppedImage(aCanvas, name, embeddedData)
|
||||
droppedSVG(anImage, name)
|
||||
|
||||
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
|
||||
SVG.
|
||||
|
||||
The same applies to drops of audio or text files from outside the world
|
||||
canvas.
|
||||
Note that PNG images provide for embedded text comments, which can be used
|
||||
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
|
||||
|
||||
|
@ -1276,6 +1289,8 @@
|
|||
Jason N (@cyderize) contributed native copy & paste for text editing.
|
||||
Bartosz Leper contributed retina display support.
|
||||
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.
|
||||
Michael Ball found and fixed a longstanding scrolling bug.
|
||||
Brian Harvey contributed to the design and implementation of submenus.
|
||||
|
@ -1289,9 +1304,9 @@
|
|||
|
||||
/*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 useBlurredShadows = true;
|
||||
|
||||
|
@ -1303,6 +1318,7 @@ const CLEAR = new Color(0, 0, 0, 0);
|
|||
Object.freeze(ZERO);
|
||||
Object.freeze(BLACK);
|
||||
Object.freeze(WHITE);
|
||||
Object.freeze(CLEAR);
|
||||
|
||||
var standardSettings = {
|
||||
minimumFontHeight: getMinimumFontHeight(), // browser settings
|
||||
|
@ -1318,6 +1334,7 @@ var standardSettings = {
|
|||
mouseScrollAmount: 40,
|
||||
useSliderForInput: false,
|
||||
isTouchDevice: false, // turned on by touch events, don't set
|
||||
pngPayloadMarker: 'Data\tPayload\tEmbedded',
|
||||
rasterizeSVGs: false,
|
||||
isFlat: false,
|
||||
grabThreshold: 5,
|
||||
|
@ -1338,6 +1355,7 @@ var touchScreenSettings = {
|
|||
mouseScrollAmount: 40,
|
||||
useSliderForInput: false,
|
||||
isTouchDevice: true,
|
||||
pngPayloadMarker: 'Data\tPayload\tEmbedded',
|
||||
rasterizeSVGs: false,
|
||||
isFlat: false,
|
||||
grabThreshold: 5,
|
||||
|
@ -1569,6 +1587,45 @@ function copy(target) {
|
|||
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 //////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
|
@ -1721,7 +1778,7 @@ function enableRetinaSupport() {
|
|||
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', {
|
||||
|
@ -1739,7 +1796,7 @@ function enableRetinaSupport() {
|
|||
context.restore();
|
||||
context.save();
|
||||
*/
|
||||
context.scale(pixelRatio, pixelRatio);
|
||||
context?.scale(pixelRatio, pixelRatio);
|
||||
} catch (err) {
|
||||
console.log('Retina Display Support Problem', err);
|
||||
uber.width.set.call(this, width);
|
||||
|
@ -1760,7 +1817,7 @@ function enableRetinaSupport() {
|
|||
context.restore();
|
||||
context.save();
|
||||
*/
|
||||
context.scale(pixelRatio, pixelRatio);
|
||||
context?.scale(pixelRatio, pixelRatio);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2708,7 +2765,7 @@ Rectangle.prototype.boundingBox = function () {
|
|||
|
||||
Rectangle.prototype.center = function () {
|
||||
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) {
|
||||
this.setPosition(
|
||||
aPoint.subtract(
|
||||
this.extent().floorDivideBy(2)
|
||||
this.extent().divideBy(2)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
@ -3415,7 +3472,7 @@ Morph.prototype.setCenter = function (aPoint) {
|
|||
Morph.prototype.setFullCenter = function (aPoint) {
|
||||
this.setPosition(
|
||||
aPoint.subtract(
|
||||
this.fullBounds().extent().floorDivideBy(2)
|
||||
this.fullBounds().extent().divideBy(2)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
@ -4456,7 +4513,28 @@ Morph.prototype.developersMenu = function () {
|
|||
);
|
||||
menu.addItem(
|
||||
"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'
|
||||
);
|
||||
menu.addLine();
|
||||
|
@ -8183,7 +8261,7 @@ MenuMorph.prototype.adjustWidths = function () {
|
|||
if (item === this.label) {
|
||||
item.text.setPosition(
|
||||
item.center().subtract(
|
||||
item.text.extent().floorDivideBy(2)
|
||||
item.text.extent().divideBy(2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -9289,7 +9367,25 @@ TextMorph.prototype.parse = function () {
|
|||
this.maxLineWidth,
|
||||
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 {
|
||||
oldline = newline;
|
||||
}
|
||||
|
@ -9823,7 +9919,7 @@ TriggerMorph.prototype.createLabel = function () {
|
|||
TriggerMorph.prototype.fixLayout = function () {
|
||||
this.label.setPosition(
|
||||
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.touchHoldTimeout = null;
|
||||
this.contextMenuEnabled = false;
|
||||
this.touchStartPosition = null;
|
||||
|
||||
// properties for caching dragged objects:
|
||||
this.cachedFullImage = null;
|
||||
|
@ -11284,12 +11381,12 @@ HandMorph.prototype.grab = function (aMorph) {
|
|||
if (!aMorph.noDropShadow) {
|
||||
aMorph.addShadow();
|
||||
}
|
||||
this.add(aMorph);
|
||||
|
||||
// cache the dragged object's display resources
|
||||
this.cachedFullImage = aMorph.fullImage();
|
||||
this.cachedFullBounds = aMorph.fullBounds();
|
||||
|
||||
this.add(aMorph);
|
||||
this.changed();
|
||||
if (oldParent && oldParent.reactToGrabOf) {
|
||||
oldParent.reactToGrabOf(aMorph);
|
||||
|
@ -11409,6 +11506,10 @@ HandMorph.prototype.processTouchStart = function (event) {
|
|||
MorphicPreferences.isTouchDevice = true;
|
||||
clearInterval(this.touchHoldTimeout);
|
||||
if (event.touches.length === 1) {
|
||||
this.touchStartPosition = new Point(
|
||||
event.touches[0].pageX,
|
||||
event.touches[0].pageY
|
||||
);
|
||||
this.touchHoldTimeout = setInterval( // simulate mouseRightClick
|
||||
() => {
|
||||
this.processMouseDown({button: 2});
|
||||
|
@ -11425,7 +11526,12 @@ HandMorph.prototype.processTouchStart = function (event) {
|
|||
};
|
||||
|
||||
HandMorph.prototype.processTouchMove = function (event) {
|
||||
var pos = new Point(event.touches[0].pageX, event.touches[0].pageY);
|
||||
MorphicPreferences.isTouchDevice = true;
|
||||
if (this.touchStartPosition.distanceTo(pos) <
|
||||
MorphicPreferences.grabThreshold) {
|
||||
return;
|
||||
}
|
||||
if (event.touches.length === 1) {
|
||||
var touch = event.touches[0];
|
||||
this.processMouseMove(touch);
|
||||
|
@ -11643,7 +11749,7 @@ HandMorph.prototype.processDrop = function (event) {
|
|||
onto the world canvas, turn it into an offscreen canvas or audio
|
||||
element and dispatch the
|
||||
|
||||
droppedImage(canvas, name)
|
||||
droppedImage(canvas, name, embeddedData)
|
||||
droppedSVG(image, name)
|
||||
droppedAudio(audio, name)
|
||||
droppedText(text, name, type)
|
||||
|
@ -11692,16 +11798,38 @@ HandMorph.prototype.processDrop = function (event) {
|
|||
function readImage(aFile) {
|
||||
var pic = new Image(),
|
||||
frd = new FileReader(),
|
||||
trg = target;
|
||||
trg = target,
|
||||
embedTag = MorphicPreferences.pngPayloadMarker;
|
||||
|
||||
while (!trg.droppedImage) {
|
||||
trg = trg.parent;
|
||||
}
|
||||
|
||||
pic.onload = () => {
|
||||
canvas = newCanvas(new Point(pic.width, pic.height), true);
|
||||
canvas.getContext('2d').drawImage(pic, 0, 0);
|
||||
trg.droppedImage(canvas, aFile.name);
|
||||
bulkDrop();
|
||||
(async () => {
|
||||
// extract embedded data (e.g. blocks)
|
||||
// from the image's metadata if present
|
||||
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.onloadend = (e) => pic.src = e.target.result;
|
||||
frd.readAsDataURL(aFile);
|
||||
|
@ -11953,6 +12081,10 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) {
|
|||
this.activeMenu = null;
|
||||
this.activeHandle = null;
|
||||
|
||||
if (!fillPage && aCanvas.isRetinaEnabled) {
|
||||
this.initRetina();
|
||||
}
|
||||
|
||||
this.initKeyboardHandler();
|
||||
this.resetKeyboardHandler();
|
||||
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.prototype.getGlobalPixelColor = function (point) {
|
||||
// answer the color at the given point.
|
||||
var dta = this.worldCanvas.getContext('2d').getImageData(
|
||||
point.x,
|
||||
point.y,
|
||||
1,
|
||||
1
|
||||
).data;
|
||||
// first, create a new temporary canvas representing the fullImage
|
||||
// and sample that one instead of the actual world canvas
|
||||
// this slows things down but keeps Chrome from crashing
|
||||
// in v119 in the Fall of 2023
|
||||
var dta = Morph.prototype.fullImage.call(this)
|
||||
.getContext('2d')
|
||||
.getImageData(point.x, point.y, 1, 1)
|
||||
.data;
|
||||
return new Color(dta[0], dta[1], dta[2]);
|
||||
};
|
||||
|
||||
|
@ -12113,6 +12258,8 @@ WorldMorph.prototype.initKeyboardHandler = function () {
|
|||
kbd.world = this;
|
||||
kbd.style.zIndex = -1;
|
||||
kbd.autofocus = true;
|
||||
kbd.style.width = '0px';
|
||||
kbd.style.height = '0px';
|
||||
document.body.appendChild(kbd);
|
||||
this.keyboardHandler = kbd;
|
||||
|
||||
|
@ -12305,6 +12452,7 @@ WorldMorph.prototype.initEventListeners = function () {
|
|||
window.addEventListener(
|
||||
"drop",
|
||||
event => {
|
||||
this.hand.processMouseMove(event);
|
||||
this.hand.processDrop(event);
|
||||
event.preventDefault();
|
||||
},
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
written by Jens Mönig
|
||||
jens@moenig.org
|
||||
|
||||
Copyright (C) 2010-2020 by Jens Mönig
|
||||
|
||||
This documentation last changed: June 9, 2020
|
||||
Copyright (C) 2010-2022 by Jens Mönig
|
||||
|
||||
This documentation last changed: November 22
|
||||
|
||||
This file is part of Snap!.
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
example html file:
|
||||
|
@ -318,7 +318,6 @@
|
|||
var world1, world2;
|
||||
|
||||
window.onload = function () {
|
||||
disableRetinaSupport();
|
||||
world1 = new WorldMorph(
|
||||
document.getElementById('world1'), false);
|
||||
world2 = new WorldMorph(
|
||||
|
@ -327,7 +326,7 @@
|
|||
};
|
||||
|
||||
function loop() {
|
||||
requestAnimationFrame(loop);
|
||||
requestAnimationFrame(loop);
|
||||
world1.doOneCycle();
|
||||
world2.doOneCycle();
|
||||
}
|
||||
|
@ -472,6 +471,8 @@
|
|||
mouseLeave
|
||||
mouseEnterDragging
|
||||
mouseLeaveDragging
|
||||
mouseEnterBounds
|
||||
mouseLeaveBounds
|
||||
mouseMove
|
||||
mouseScroll
|
||||
|
||||
|
@ -480,7 +481,7 @@
|
|||
|
||||
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
|
||||
coordinate system. The
|
||||
|
||||
|
@ -490,6 +491,16 @@
|
|||
currently pressed mouse button, which is either 'left' or 'right'.
|
||||
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
|
||||
|
||||
this.escalateEvent(functionName, arg)
|
||||
|
@ -631,7 +642,7 @@
|
|||
|
||||
Drops of image elements from outside the world canvas are dispatched as
|
||||
|
||||
droppedImage(aCanvas, name)
|
||||
droppedImage(aCanvas, name, embeddedData)
|
||||
droppedSVG(anImage, name)
|
||||
|
||||
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
|
||||
SVG.
|
||||
|
||||
The same applies to drops of audio or text files from outside the world
|
||||
canvas.
|
||||
Note that PNG images provide for embedded text comments, which can be used
|
||||
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
|
||||
|
||||
|
@ -667,6 +692,15 @@
|
|||
|
||||
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
|
||||
-------------------
|
||||
|
@ -1256,10 +1290,12 @@
|
|||
Jason N (@cyderize) contributed native copy & paste for text editing.
|
||||
Bartosz Leper contributed retina display support.
|
||||
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.
|
||||
Michael Ball found and fixed a longstanding scrolling bug.
|
||||
Brian Harvey contributed to the design and implementation of submenus.
|
||||
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
|
||||
|
|
Ładowanie…
Reference in New Issue