scratch-blocks 1.2.5 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blockly_compressed_horizontal.js +30 -17
- package/blockly_compressed_vertical.js +42 -23
- package/blockly_uncompressed_horizontal.js +2 -2
- package/blockly_uncompressed_vertical.js +2 -2
- package/blocks_compressed.js +1 -1
- package/blocks_compressed_horizontal.js +1 -1
- package/blocks_compressed_vertical.js +1 -1
- package/core/block_render_svg_horizontal.js +8 -7
- package/core/block_render_svg_vertical.js +117 -15
- package/core/block_svg.js +256 -5
- package/core/blockly.js +34 -0
- package/core/constants.js +9 -0
- package/dist/horizontal.js +3 -3
- package/dist/vertical.js +3 -3
- package/dist/web/horizontal.js +3 -3
- package/dist/web/vertical.js +3 -3
- package/package.json +1 -1
|
@@ -29,6 +29,7 @@ goog.provide('Blockly.BlockSvg.render');
|
|
|
29
29
|
goog.require('Blockly.BlockSvg');
|
|
30
30
|
goog.require('Blockly.scratchBlocksUtils');
|
|
31
31
|
goog.require('Blockly.utils');
|
|
32
|
+
goog.require('Blockly.constants');
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
// UI constants for rendering blocks.
|
|
@@ -139,7 +140,17 @@ Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE = 2 * Blockly.BlockSvg.GRID_UNIT;
|
|
|
139
140
|
* Height of the top hat.
|
|
140
141
|
* @const
|
|
141
142
|
*/
|
|
142
|
-
Blockly.BlockSvg
|
|
143
|
+
Object.defineProperty(Blockly.BlockSvg, 'START_HAT_HEIGHT', {
|
|
144
|
+
get: function() {
|
|
145
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
146
|
+
return 31;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return 16;
|
|
150
|
+
},
|
|
151
|
+
enumerable: true,
|
|
152
|
+
configurable: true
|
|
153
|
+
});
|
|
143
154
|
|
|
144
155
|
/**
|
|
145
156
|
* Height of the vertical separator line for icons that appear at the left edge
|
|
@@ -152,7 +163,20 @@ Blockly.BlockSvg.ICON_SEPARATOR_HEIGHT = 10 * Blockly.BlockSvg.GRID_UNIT;
|
|
|
152
163
|
* Path of the top hat's curve.
|
|
153
164
|
* @const
|
|
154
165
|
*/
|
|
155
|
-
Blockly.BlockSvg
|
|
166
|
+
Object.defineProperty(Blockly.BlockSvg, 'START_HAT_PATH', {
|
|
167
|
+
get: function() {
|
|
168
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
169
|
+
return 'c2.6,-2.3 5.5,-4.3 8.5,-6.2' +
|
|
170
|
+
'c-1,-12.5 5.3,-23.3 8.4,-24.8c3.7,-1.8 16.5,13.1 18.4,15.4' +
|
|
171
|
+
'c8.4,-1.3 17,-1.3 25.4,0c1.9,-2.3 14.7,-17.2 18.4,-15.4' +
|
|
172
|
+
'c3.1,1.5 9.4,12.3 8.4,24.8c3,1.8 5.9,3.9 8.5,6.1';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return 'c 25,-22 71,-22 96,0';
|
|
176
|
+
},
|
|
177
|
+
enumerable: true,
|
|
178
|
+
configurable: true
|
|
179
|
+
});
|
|
156
180
|
|
|
157
181
|
/**
|
|
158
182
|
* SVG path for drawing next/previous notch from left to right.
|
|
@@ -476,11 +500,22 @@ Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS = 5 * Blockly.BlockSvg.GRID_UNIT;
|
|
|
476
500
|
* SVG path for drawing the rounded top-left corner.
|
|
477
501
|
* @const
|
|
478
502
|
*/
|
|
479
|
-
Blockly.BlockSvg
|
|
480
|
-
|
|
481
|
-
Blockly.
|
|
482
|
-
|
|
483
|
-
|
|
503
|
+
Object.defineProperty(Blockly.BlockSvg, 'TOP_LEFT_CORNER_DEFINE_HAT', {
|
|
504
|
+
get: function() {
|
|
505
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
506
|
+
return 'c0,-7.1 3.7,-13.3 9.3,-16.9c1.7,-7.5 5.4,-13.2 7.6,-14.2' +
|
|
507
|
+
'c2.6,-1.3 10,6 14.6,11.1h33c4.6,-5.1 11.9,-12.4 14.6,-11.1' +
|
|
508
|
+
'c1.9,0.9 4.9,5.2 6.8,11.1c2.6,0,5.2,0,7.8,0';
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return 'a ' + Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ',' +
|
|
512
|
+
Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ' 0 0,1 ' +
|
|
513
|
+
Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ',-' +
|
|
514
|
+
Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS;
|
|
515
|
+
},
|
|
516
|
+
enumerable: true,
|
|
517
|
+
configurable: true
|
|
518
|
+
});
|
|
484
519
|
|
|
485
520
|
/**
|
|
486
521
|
* SVG path for drawing the rounded top-left corner.
|
|
@@ -518,8 +553,7 @@ Blockly.BlockSvg.prototype.updateColour = function() {
|
|
|
518
553
|
}
|
|
519
554
|
}
|
|
520
555
|
|
|
521
|
-
|
|
522
|
-
this.svgPath_.setAttribute('stroke', strokeColour);
|
|
556
|
+
this.blockFrameElement_.setAttribute('stroke', strokeColour);
|
|
523
557
|
|
|
524
558
|
// Render block fill
|
|
525
559
|
if (this.isGlowingBlock_ || renderShadowed) {
|
|
@@ -532,10 +566,10 @@ Blockly.BlockSvg.prototype.updateColour = function() {
|
|
|
532
566
|
} else {
|
|
533
567
|
var fillColour = this.getColour();
|
|
534
568
|
}
|
|
535
|
-
this.
|
|
569
|
+
this.blockFrameElement_.setAttribute('fill', fillColour);
|
|
536
570
|
|
|
537
571
|
// Render opacity
|
|
538
|
-
this.
|
|
572
|
+
this.blockFrameElement_.setAttribute('fill-opacity', this.getOpacity());
|
|
539
573
|
|
|
540
574
|
// Update colours of input shapes.
|
|
541
575
|
for (var i = 0, input; input = this.inputList[i]; i++) {
|
|
@@ -567,11 +601,11 @@ Blockly.BlockSvg.prototype.highlightForReplacement = function(add) {
|
|
|
567
601
|
if (add) {
|
|
568
602
|
var replacementGlowFilterId = this.workspace.options.replacementGlowFilterId
|
|
569
603
|
|| 'blocklyReplacementGlowFilter';
|
|
570
|
-
this.
|
|
604
|
+
this.blockFrameElement_.setAttribute('filter', 'url(#' + replacementGlowFilterId + ')');
|
|
571
605
|
Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_),
|
|
572
606
|
'blocklyReplaceable');
|
|
573
607
|
} else {
|
|
574
|
-
this.
|
|
608
|
+
this.blockFrameElement_.removeAttribute('filter');
|
|
575
609
|
Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_),
|
|
576
610
|
'blocklyReplaceable');
|
|
577
611
|
}
|
|
@@ -1119,6 +1153,67 @@ Blockly.BlockSvg.prototype.computeOutputPadding_ = function(inputRows) {
|
|
|
1119
1153
|
row.paddingEnd += Blockly.BlockSvg.SHAPE_IN_SHAPE_PADDING[shape][otherShape];
|
|
1120
1154
|
};
|
|
1121
1155
|
|
|
1156
|
+
// Cat face and ear animation for CatBlocks
|
|
1157
|
+
// TODO: Do we want to move this and `initCatFace_` to a separate file?
|
|
1158
|
+
// Or would just that complicate unforking?
|
|
1159
|
+
Blockly.BlockSvg.prototype.renderCatFace_ = function() {
|
|
1160
|
+
// This only makes sense in the context of the Cat Blocks theme.
|
|
1161
|
+
if (Blockly.theme !== Blockly.Themes.CAT_BLOCKS) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
this.svgPath_.svgFace.setAttribute('fill','#000000');
|
|
1166
|
+
|
|
1167
|
+
var closedEye = Blockly.utils.createSvgElement('path', {}, this.svgFace_);
|
|
1168
|
+
closedEye.setAttribute('d','M25.2-1.1c0.1,0,0.2,0,0.2,0l8.3-2.1l-7-4.8' +
|
|
1169
|
+
'c-0.5-0.3-1.1-0.2-1.4,0.3s-0.2,1.1,0.3,1.4L29-4.1l-4,1' +
|
|
1170
|
+
'c-0.5,0.1-0.9,0.7-0.7,1.2C24.3-1.4,24.7-1.1,25.2-1.1z');
|
|
1171
|
+
closedEye.setAttribute('fill-opacity','0');
|
|
1172
|
+
this.svgPath_.svgFace.closedEye = closedEye;
|
|
1173
|
+
|
|
1174
|
+
var closedEye2 = Blockly.utils.createSvgElement('path', {}, this.svgFace_);
|
|
1175
|
+
closedEye2.setAttribute('d','M62.4-1.1c-0.1,0-0.2,0-0.2,0l-8.3-2.1l7-4.8' +
|
|
1176
|
+
'c0.5-0.3,1.1-0.2,1.4,0.3s0.2,1.1-0.3,1.4l-3.4,2.3l4,1' +
|
|
1177
|
+
'c0.5,0.1,0.9,0.7,0.7,1.2C63.2-1.4,62.8-1.1,62.4-1.1z');
|
|
1178
|
+
closedEye2.setAttribute('fill-opacity','0');
|
|
1179
|
+
this.svgPath_.svgFace.closedEye2 = closedEye2;
|
|
1180
|
+
|
|
1181
|
+
var eye = Blockly.utils.createSvgElement('circle', {}, this.svgFace_);
|
|
1182
|
+
eye.setAttribute('cx','59.2');
|
|
1183
|
+
eye.setAttribute('cy','-3.3');
|
|
1184
|
+
eye.setAttribute('r','3.4');
|
|
1185
|
+
eye.setAttribute('fill-opacity','0.6');
|
|
1186
|
+
this.svgPath_.svgFace.eye = eye;
|
|
1187
|
+
|
|
1188
|
+
var eye2 = Blockly.utils.createSvgElement('circle', {}, this.svgFace_);
|
|
1189
|
+
eye2.setAttribute('cx','29.1');
|
|
1190
|
+
eye2.setAttribute('cy','-3.3');
|
|
1191
|
+
eye2.setAttribute('r','3.4');
|
|
1192
|
+
eye2.setAttribute('fill-opacity','0.6');
|
|
1193
|
+
this.svgPath_.svgFace.eye2 = eye2;
|
|
1194
|
+
|
|
1195
|
+
var mouth = Blockly.utils.createSvgElement('path', {}, this.svgFace_);
|
|
1196
|
+
mouth.setAttribute('d','M45.6,0.1c-0.9,0-1.7-0.3-2.3-0.9' +
|
|
1197
|
+
'c-0.6,0.6-1.3,0.9-2.2,0.9c-0.9,0-1.8-0.3-2.3-0.9c-1-1.1-1.1-2.6-1.1-2.8' +
|
|
1198
|
+
'c0-0.5,0.5-1,1-1l0,0c0.6,0,1,0.5,1,1c0,0.4,0.1,1.7,1.4,1.7' +
|
|
1199
|
+
'c0.5,0,0.7-0.2,0.8-0.3c0.3-0.3,0.4-1,0.4-1.3c0-0.1,0-0.1,0-0.2' +
|
|
1200
|
+
'c0-0.5,0.5-1,1-1l0,0c0.5,0,1,0.4,1,1c0,0,0,0.1,0,0.2' +
|
|
1201
|
+
'c0,0.3,0.1,0.9,0.4,1.2C44.8-2.2,45-2,45.5-2s0.7-0.2,0.8-0.3' +
|
|
1202
|
+
'c0.3-0.4,0.4-1.1,0.3-1.3c0-0.5,0.4-1,0.9-1.1c0.5,0,1,0.4,1.1,0.9' +
|
|
1203
|
+
'c0,0.2,0.1,1.8-0.8,2.8C47.5-0.4,46.8,0.1,45.6,0.1z');
|
|
1204
|
+
mouth.setAttribute('fill-opacity','0.6');
|
|
1205
|
+
|
|
1206
|
+
this.svgPath_.ear.setAttribute('d','M73.1-15.6c1.7-4.2,4.5-9.1,5.8-8.5' +
|
|
1207
|
+
'c1.6,0.8,5.4,7.9,5,15.4c0,0.6-0.7,0.7-1.1,0.5c-3-1.6-6.4-2.8-8.6-3.6' +
|
|
1208
|
+
'C72.8-12.3,72.4-13.7,73.1-15.6z');
|
|
1209
|
+
this.svgPath_.ear.setAttribute('fill','#FFD5E6');
|
|
1210
|
+
|
|
1211
|
+
this.svgPath_.ear2.setAttribute('d','M22.4-15.6c-1.7-4.2-4.5-9.1-5.8-8.5' +
|
|
1212
|
+
'c-1.6,0.8-5.4,7.9-5,15.4c0,0.6,0.7,0.7,1.1,0.5c3-1.6,6.4-2.8,8.6-3.6' +
|
|
1213
|
+
'C22.8-12.3,23.2-13.7,22.4-15.6z');
|
|
1214
|
+
this.svgPath_.ear2.setAttribute('fill','#FFD5E6');
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1122
1217
|
/**
|
|
1123
1218
|
* Draw the path of the block.
|
|
1124
1219
|
* Move the fields to the correct locations.
|
|
@@ -1136,6 +1231,9 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) {
|
|
|
1136
1231
|
// No output or previous connection.
|
|
1137
1232
|
this.squareTopLeftCorner_ = true;
|
|
1138
1233
|
this.startHat_ = true;
|
|
1234
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
1235
|
+
this.initCatStuff();
|
|
1236
|
+
}
|
|
1139
1237
|
inputRows.rightEdge = Math.max(inputRows.rightEdge, 100);
|
|
1140
1238
|
}
|
|
1141
1239
|
|
|
@@ -1162,12 +1260,16 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) {
|
|
|
1162
1260
|
this.renderDrawLeft_(steps);
|
|
1163
1261
|
|
|
1164
1262
|
var pathString = steps.join(' ');
|
|
1165
|
-
this.
|
|
1263
|
+
this.blockFrameElement_.setAttribute('d', pathString);
|
|
1264
|
+
|
|
1265
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS && this.startHat_ && !this.svgFace_.firstChild) {
|
|
1266
|
+
this.renderCatFace_();
|
|
1267
|
+
}
|
|
1166
1268
|
|
|
1167
1269
|
if (this.RTL) {
|
|
1168
1270
|
// Mirror the block's path.
|
|
1169
1271
|
// This is awesome.
|
|
1170
|
-
this.
|
|
1272
|
+
this.blockFrameElement_.setAttribute('transform', 'scale(-1 1)');
|
|
1171
1273
|
}
|
|
1172
1274
|
};
|
|
1173
1275
|
|
package/core/block_svg.js
CHANGED
|
@@ -37,6 +37,7 @@ goog.require('Blockly.scratchBlocksUtils');
|
|
|
37
37
|
goog.require('Blockly.Tooltip');
|
|
38
38
|
goog.require('Blockly.Touch');
|
|
39
39
|
goog.require('Blockly.utils');
|
|
40
|
+
goog.require('Blockly.constants');
|
|
40
41
|
|
|
41
42
|
goog.require('goog.Timer');
|
|
42
43
|
goog.require('goog.asserts');
|
|
@@ -64,10 +65,25 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
|
|
64
65
|
*/
|
|
65
66
|
this.svgGroup_ = Blockly.utils.createSvgElement('g', {}, null);
|
|
66
67
|
/** @type {SVGElement} */
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
69
|
+
this.svgPath_ = Blockly.utils.createSvgElement('g', {}, this.svgGroup_);
|
|
70
|
+
this.svgPathBody_ = Blockly.utils.createSvgElement('path',
|
|
71
|
+
{'class': 'blocklyPath blocklyBlockBackground'}, this.svgPath_);
|
|
72
|
+
|
|
73
|
+
this.svgFace_ = Blockly.utils.createSvgElement('g', {},
|
|
74
|
+
this.svgPath_);
|
|
75
|
+
this.svgGroup_.svgPath = this.svgPath_;
|
|
76
|
+
this.svgPath_.svgFace = this.svgFace_;
|
|
77
|
+
this.svgPath_.svgBody = this.svgPathBody_;
|
|
78
|
+
this.lastCallTime = 0;
|
|
79
|
+
this.CALL_FREQUENCY_MS = 60;
|
|
80
|
+
|
|
81
|
+
this.svgPathBody_.tooltip = this;
|
|
82
|
+
} else {
|
|
83
|
+
this.svgPath_ = Blockly.utils.createSvgElement('path', {'class': 'blocklyPath blocklyBlockBackground'},
|
|
84
|
+
this.svgGroup_);
|
|
85
|
+
this.svgPath_.tooltip = this;
|
|
86
|
+
}
|
|
71
87
|
|
|
72
88
|
/** @type {boolean} */
|
|
73
89
|
this.rendered = false;
|
|
@@ -80,7 +96,7 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
|
|
80
96
|
*/
|
|
81
97
|
this.useDragSurface_ = Blockly.utils.is3dSupported() && !!workspace.blockDragSurface_;
|
|
82
98
|
|
|
83
|
-
Blockly.Tooltip.bindMouseEvents(this.
|
|
99
|
+
Blockly.Tooltip.bindMouseEvents(this.blockFrameElement_);
|
|
84
100
|
Blockly.BlockSvg.superClass_.constructor.call(this,
|
|
85
101
|
workspace, prototypeName, opt_id);
|
|
86
102
|
|
|
@@ -160,6 +176,8 @@ Blockly.BlockSvg.prototype.initSvg = function() {
|
|
|
160
176
|
for (i = 0; i < icons.length; i++) {
|
|
161
177
|
icons[i].createIcon();
|
|
162
178
|
}
|
|
179
|
+
} else if (this.svgPathBody_) {
|
|
180
|
+
this.svgPathBody_.setAttribute('stroke-opacity', '0');
|
|
163
181
|
}
|
|
164
182
|
this.updateColour();
|
|
165
183
|
this.updateMovable();
|
|
@@ -174,6 +192,23 @@ Blockly.BlockSvg.prototype.initSvg = function() {
|
|
|
174
192
|
}
|
|
175
193
|
};
|
|
176
194
|
|
|
195
|
+
Object.defineProperty(Blockly.BlockSvg.prototype, 'blockFrameElement_', {
|
|
196
|
+
/**
|
|
197
|
+
* The svg element (e.g. svgPath_ or svgPathBody_) that is
|
|
198
|
+
* responsible for the outline of the block, based on the current theme.
|
|
199
|
+
* @return {!SVGElement} The SVG element forming the outline.
|
|
200
|
+
* @this {Blockly.BlockSvg}
|
|
201
|
+
*/
|
|
202
|
+
get: function() {
|
|
203
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
204
|
+
return this.svgPathBody_;
|
|
205
|
+
}
|
|
206
|
+
return this.svgPath_;
|
|
207
|
+
},
|
|
208
|
+
enumerable: true,
|
|
209
|
+
configurable: true
|
|
210
|
+
});
|
|
211
|
+
|
|
177
212
|
/**
|
|
178
213
|
* Select this block. Highlight it visually.
|
|
179
214
|
*/
|
|
@@ -218,6 +253,191 @@ Blockly.BlockSvg.prototype.unselect = function() {
|
|
|
218
253
|
this.removeSelect();
|
|
219
254
|
};
|
|
220
255
|
|
|
256
|
+
Blockly.BlockSvg.prototype.initCatStuff = function() {
|
|
257
|
+
if (Blockly.theme !== Blockly.Themes.CAT_BLOCKS || this.hasInitCatStuff) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// TODO: Test what happens if we turn on and off Cat Blocks several times
|
|
261
|
+
this.hasInitCatStuff = true;
|
|
262
|
+
|
|
263
|
+
// Ear part of the SVG path for hat blocks
|
|
264
|
+
var LEFT_EAR_UP = 'c-1,-12.5 5.3,-23.3 8.4,-24.8c3.7,-1.8 16.5,13.1 18.4,15.4';
|
|
265
|
+
var LEFT_EAR_DOWN = 'c-5.8,-4.8 -8,-18 -4.9,-19.5c3.7,-1.8 24.5,11.1 31.7,10.1';
|
|
266
|
+
var RIGHT_EAR_UP = 'c1.9,-2.3 14.7,-17.2 18.4,-15.4c3.1,1.5 9.4,12.3 8.4,24.8';
|
|
267
|
+
var RIGHT_EAR_DOWN = 'c7.2,1 28,-11.9 31.7,-10.1c3.1,1.5 0.9,14.7 -4.9,19.5';
|
|
268
|
+
// Ears look slightly different for define hat blocks
|
|
269
|
+
var DEFINE_HAT_LEFT_EAR_UP = 'c0,-7.1 3.7,-13.3 9.3,-16.9c1.7,-7.5 5.4,-13.2 7.6,-14.2c2.6,-1.3 10,6 14.6,11.1';
|
|
270
|
+
var DEFINE_HAT_RIGHT_EAR_UP = 'h33c4.6,-5.1 11.9,-12.4 14.6,-11.1c1.9,0.9 4.9,5.2 6.8,11.1c2.6,0,5.2,0,7.8,0';
|
|
271
|
+
var DEFINE_HAT_LEFT_EAR_DOWN = 'c0,-4.6 1.6,-8.9 4.3,-12.3c-2.4,-5.6 -2.9,-12.4 -0.7,-13.4c2.1,-1 9.6,2.6 17,5.8' +
|
|
272
|
+
'c2.6,0 6.2,0 10.9,0';
|
|
273
|
+
var DEFINE_HAT_RIGHT_EAR_DOWN = 'c0,0 25.6,0 44,0c7.4,-3.2 14.8,-6.8 16.9,-5.8c1.2,0.6 1.6,2.9 1.3,5.8';
|
|
274
|
+
|
|
275
|
+
var that = this;
|
|
276
|
+
this.svgPath_.ear = Blockly.utils.createSvgElement('path', {}, this.svgPath_);
|
|
277
|
+
this.svgPath_.ear2 = Blockly.utils.createSvgElement('path', {}, this.svgPath_);
|
|
278
|
+
if (this.RTL) {
|
|
279
|
+
// Mirror the ears.
|
|
280
|
+
this.svgPath_.ear.setAttribute('transform', 'scale(-1 1)');
|
|
281
|
+
this.svgPath_.ear2.setAttribute('transform', 'scale(-1 1)');
|
|
282
|
+
}
|
|
283
|
+
this.svgPath_.addEventListener("mouseenter", function(event) {
|
|
284
|
+
clearTimeout(that.blinkFn);
|
|
285
|
+
// blink
|
|
286
|
+
if (event.target.svgFace.eye) {
|
|
287
|
+
event.target.svgFace.eye.setAttribute('fill-opacity','0');
|
|
288
|
+
event.target.svgFace.eye2.setAttribute('fill-opacity','0');
|
|
289
|
+
event.target.svgFace.closedEye.setAttribute('fill-opacity','0.6');
|
|
290
|
+
event.target.svgFace.closedEye2.setAttribute('fill-opacity','0.6');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// reset after a short delay
|
|
294
|
+
that.blinkFn = setTimeout(function() {
|
|
295
|
+
if (event.target.svgFace.eye) {
|
|
296
|
+
event.target.svgFace.eye.setAttribute('fill-opacity','0.6');
|
|
297
|
+
event.target.svgFace.eye2.setAttribute('fill-opacity','0.6');
|
|
298
|
+
event.target.svgFace.closedEye.setAttribute('fill-opacity','0');
|
|
299
|
+
event.target.svgFace.closedEye2.setAttribute('fill-opacity','0');
|
|
300
|
+
}
|
|
301
|
+
}, 100);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
this.svgPath_.ear.addEventListener("mouseenter", function() {
|
|
305
|
+
clearTimeout(that.earFn);
|
|
306
|
+
clearTimeout(that.ear2Fn);
|
|
307
|
+
// ear flick
|
|
308
|
+
that.svgPath_.ear.setAttribute('fill-opacity','0');
|
|
309
|
+
that.svgPath_.ear2.setAttribute('fill-opacity','');
|
|
310
|
+
var bodyPath = that.svgPath_.svgBody.getAttribute('d');
|
|
311
|
+
bodyPath = bodyPath.replace(RIGHT_EAR_UP, RIGHT_EAR_DOWN);
|
|
312
|
+
bodyPath = bodyPath.replace(DEFINE_HAT_RIGHT_EAR_UP, DEFINE_HAT_RIGHT_EAR_DOWN);
|
|
313
|
+
bodyPath = bodyPath.replace(LEFT_EAR_DOWN, LEFT_EAR_UP);
|
|
314
|
+
bodyPath = bodyPath.replace(DEFINE_HAT_LEFT_EAR_DOWN, DEFINE_HAT_LEFT_EAR_UP);
|
|
315
|
+
that.svgPath_.svgBody.setAttribute('d', bodyPath);
|
|
316
|
+
|
|
317
|
+
// reset after a short delay
|
|
318
|
+
that.earFn = setTimeout(function() {
|
|
319
|
+
that.svgPath_.ear.setAttribute('fill-opacity','');
|
|
320
|
+
var bodyPath = that.svgPath_.svgBody.getAttribute('d');
|
|
321
|
+
bodyPath = bodyPath.replace(RIGHT_EAR_DOWN, RIGHT_EAR_UP);
|
|
322
|
+
bodyPath = bodyPath.replace(DEFINE_HAT_RIGHT_EAR_DOWN, DEFINE_HAT_RIGHT_EAR_UP);
|
|
323
|
+
that.svgPath_.svgBody.setAttribute('d', bodyPath);
|
|
324
|
+
}, 50);
|
|
325
|
+
});
|
|
326
|
+
this.svgPath_.ear2.addEventListener("mouseenter", function() {
|
|
327
|
+
clearTimeout(that.earFn);
|
|
328
|
+
clearTimeout(that.ear2Fn);
|
|
329
|
+
// ear flick
|
|
330
|
+
that.svgPath_.ear2.setAttribute('fill-opacity','0');
|
|
331
|
+
that.svgPath_.ear.setAttribute('fill-opacity','');
|
|
332
|
+
var bodyPath = that.svgPath_.svgBody.getAttribute('d');
|
|
333
|
+
bodyPath = bodyPath.replace(LEFT_EAR_UP, LEFT_EAR_DOWN);
|
|
334
|
+
bodyPath = bodyPath.replace(DEFINE_HAT_LEFT_EAR_UP, DEFINE_HAT_LEFT_EAR_DOWN);
|
|
335
|
+
bodyPath = bodyPath.replace(RIGHT_EAR_DOWN, RIGHT_EAR_UP);
|
|
336
|
+
bodyPath = bodyPath.replace(DEFINE_HAT_RIGHT_EAR_DOWN, DEFINE_HAT_RIGHT_EAR_UP);
|
|
337
|
+
that.svgPath_.svgBody.setAttribute('d', bodyPath);
|
|
338
|
+
|
|
339
|
+
// reset after a short delay
|
|
340
|
+
that.ear2Fn = setTimeout(function() {
|
|
341
|
+
that.svgPath_.ear2.setAttribute('fill-opacity','');
|
|
342
|
+
var bodyPath = that.svgPath_.svgBody.getAttribute('d');
|
|
343
|
+
bodyPath = bodyPath.replace(LEFT_EAR_DOWN, LEFT_EAR_UP);
|
|
344
|
+
bodyPath = bodyPath.replace(DEFINE_HAT_LEFT_EAR_DOWN, DEFINE_HAT_LEFT_EAR_UP);
|
|
345
|
+
that.svgPath_.svgBody.setAttribute('d', bodyPath);
|
|
346
|
+
}, 50);
|
|
347
|
+
});
|
|
348
|
+
this.windowListener = function(event) {
|
|
349
|
+
var time = Date.now();
|
|
350
|
+
if (time < that.lastCallTime + that.CALL_FREQUENCY_MS) return;
|
|
351
|
+
that.lastCallTime = time;
|
|
352
|
+
if (!that.shouldWatchMouse()) return;
|
|
353
|
+
|
|
354
|
+
// mouse watching
|
|
355
|
+
if (that.workspace) { // not disposed
|
|
356
|
+
var xy = that.getCatFacePosition();
|
|
357
|
+
var mouseLocation = {
|
|
358
|
+
x: event.x / that.workspace.scale,
|
|
359
|
+
y: event.y / that.workspace.scale
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
var dx = mouseLocation.x - xy.x;
|
|
363
|
+
var dy = mouseLocation.y - xy.y;
|
|
364
|
+
var theta = Math.atan2(dx, dy);
|
|
365
|
+
|
|
366
|
+
// Map the vector from the cat face to the mouse location to a much shorter
|
|
367
|
+
// vector in the same direction, which will be the translation vector for
|
|
368
|
+
// the cat face
|
|
369
|
+
var delta = Math.sqrt(dx * dx + dy * dy);
|
|
370
|
+
var scaleFactor = delta / (delta + 1);
|
|
371
|
+
|
|
372
|
+
// Equation for radius of ellipse at theta for axes with length a and b
|
|
373
|
+
var a = 2;
|
|
374
|
+
var b = 5;
|
|
375
|
+
var r = a * b / Math.sqrt(Math.pow(b * Math.cos(theta), 2) + Math.pow(a * Math.sin(theta), 2));
|
|
376
|
+
|
|
377
|
+
// Convert polar coordinate back to x, y coordinate
|
|
378
|
+
dx = (r * scaleFactor) * Math.sin(theta);
|
|
379
|
+
dy = (r * scaleFactor) * Math.cos(theta);
|
|
380
|
+
|
|
381
|
+
if (that.RTL) dx -= 87; // Translate face over
|
|
382
|
+
that.svgFace_.style.transform = 'translate(' + dx + 'px, ' + dy + 'px)';
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
if (this.RTL) {
|
|
386
|
+
// Set to the correct initial position
|
|
387
|
+
this.svgFace_.style.transform = 'translate(-87px, 0px)';
|
|
388
|
+
}
|
|
389
|
+
if (this.shouldWatchMouse()) {
|
|
390
|
+
document.addEventListener('mousemove', this.windowListener);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Get cat face position
|
|
396
|
+
* @return {Object} coordinates of center of cat face
|
|
397
|
+
*/
|
|
398
|
+
Blockly.BlockSvg.prototype.getCatFacePosition = function() {
|
|
399
|
+
// getBoundingClientRect is not performant
|
|
400
|
+
//var offset = that.workspace.getParentSvg().getBoundingClientRect();
|
|
401
|
+
var offset = {x:0, y:92};
|
|
402
|
+
|
|
403
|
+
offset.x += 120; // scratchCategoryMenu width
|
|
404
|
+
|
|
405
|
+
if (!this.isInFlyout && this.workspace.getFlyout()) {
|
|
406
|
+
offset.x += this.workspace.getFlyout().getWidth();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
offset.x += this.workspace.scrollX;
|
|
410
|
+
offset.y += this.workspace.scrollY;
|
|
411
|
+
|
|
412
|
+
var xy = this.getRelativeToSurfaceXY(this.svgGroup_);
|
|
413
|
+
if (this.RTL) {
|
|
414
|
+
xy.x = this.workspace.getWidth() - xy.x - this.width;
|
|
415
|
+
}
|
|
416
|
+
// convert to workspace units
|
|
417
|
+
xy.x += offset.x / this.workspace.scale;
|
|
418
|
+
xy.y += offset.y / this.workspace.scale;
|
|
419
|
+
// distance to center of face
|
|
420
|
+
xy.x -= 43.5;
|
|
421
|
+
xy.y -= 4;
|
|
422
|
+
if (this.RTL) {
|
|
423
|
+
// We've been calculating from the right edge. Convert x to from left edge.
|
|
424
|
+
xy.x = screen.width - xy.x;
|
|
425
|
+
}
|
|
426
|
+
return xy;
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* True if cat should watch mouse
|
|
431
|
+
* @return {boolean} true if the block should be watching the mouse
|
|
432
|
+
*/
|
|
433
|
+
Blockly.BlockSvg.prototype.shouldWatchMouse = function() {
|
|
434
|
+
if (window.vmLoadHigh || !window.CAT_CHASE_MOUSE) return false;
|
|
435
|
+
var xy = this.getCatFacePosition();
|
|
436
|
+
var blockXOnScreen = xy.x > 0 && xy.x < screen.width / this.workspace.scale;
|
|
437
|
+
var blockYOnScreen = xy.y > 0 && xy.y < screen.height / this.workspace.scale;
|
|
438
|
+
return this.startHat_ && !this.isGlowingStack_ && blockXOnScreen && blockYOnScreen;
|
|
439
|
+
};
|
|
440
|
+
|
|
221
441
|
/**
|
|
222
442
|
* Glow only this particular block, to highlight it visually as if it's running.
|
|
223
443
|
* @param {boolean} isGlowingBlock Whether the block should glow.
|
|
@@ -232,6 +452,23 @@ Blockly.BlockSvg.prototype.setGlowBlock = function(isGlowingBlock) {
|
|
|
232
452
|
* @param {boolean} isGlowingStack Whether the stack starting with this block should glow.
|
|
233
453
|
*/
|
|
234
454
|
Blockly.BlockSvg.prototype.setGlowStack = function(isGlowingStack) {
|
|
455
|
+
if (Blockly.theme === Blockly.Themes.CAT_BLOCKS) {
|
|
456
|
+
if (isGlowingStack) {
|
|
457
|
+
// For performance, don't follow the mouse when the stack is glowing
|
|
458
|
+
document.removeEventListener('mousemove', this.windowListener);
|
|
459
|
+
if (this.workspace && this.svgFace_.style) {
|
|
460
|
+
// reset face direction
|
|
461
|
+
if (this.RTL) {
|
|
462
|
+
this.svgFace_.style.transform = 'translate(-87px, 0px)';
|
|
463
|
+
} else {
|
|
464
|
+
this.svgFace_.style.transform = '';
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
document.addEventListener('mousemove', this.windowListener);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
235
472
|
this.isGlowingStack_ = isGlowingStack;
|
|
236
473
|
// Update the applied SVG filter if the property has changed
|
|
237
474
|
var svg = this.getSvgRoot();
|
|
@@ -822,6 +1059,18 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
|
|
822
1059
|
// The block has already been deleted.
|
|
823
1060
|
return;
|
|
824
1061
|
}
|
|
1062
|
+
if (this.blinkFn) {
|
|
1063
|
+
clearTimeout(this.blinkFn);
|
|
1064
|
+
}
|
|
1065
|
+
if (this.earFn) {
|
|
1066
|
+
clearTimeout(this.earFn);
|
|
1067
|
+
}
|
|
1068
|
+
if (this.ear2Fn) {
|
|
1069
|
+
clearTimeout(this.ear2Fn);
|
|
1070
|
+
}
|
|
1071
|
+
if (this.windowListener) {
|
|
1072
|
+
document.removeEventListener('mousemove', this.windowListener);
|
|
1073
|
+
}
|
|
825
1074
|
Blockly.Tooltip.hide();
|
|
826
1075
|
Blockly.Field.startCache();
|
|
827
1076
|
// Save the block's workspace temporarily so we can resize the
|
|
@@ -860,6 +1109,8 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
|
|
860
1109
|
// Sever JavaScript to DOM connections.
|
|
861
1110
|
this.svgGroup_ = null;
|
|
862
1111
|
this.svgPath_ = null;
|
|
1112
|
+
this.svgPathBody_ = null;
|
|
1113
|
+
this.svgFace_ = null;
|
|
863
1114
|
Blockly.Field.stopCache();
|
|
864
1115
|
};
|
|
865
1116
|
|
package/core/blockly.js
CHANGED
|
@@ -112,6 +112,13 @@ Blockly.clipboardSource_ = null;
|
|
|
112
112
|
*/
|
|
113
113
|
Blockly.cache3dSupported_ = null;
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Active theme.
|
|
117
|
+
* @type {!Blockly.Themes}
|
|
118
|
+
* @private
|
|
119
|
+
*/
|
|
120
|
+
Blockly.theme_ = Blockly.Themes.CLASSIC;
|
|
121
|
+
|
|
115
122
|
/**
|
|
116
123
|
* Convert a hue (HSV model) into an RGB hex triplet.
|
|
117
124
|
* @param {number} hue Hue on a colour wheel (0-360).
|
|
@@ -175,6 +182,33 @@ Blockly.svgResize = function(workspace) {
|
|
|
175
182
|
mainWorkspace.resize();
|
|
176
183
|
};
|
|
177
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Apply a global theme to Blockly. This will then be used in all workspaces -
|
|
187
|
+
* both newly created and already existing.
|
|
188
|
+
*/
|
|
189
|
+
Object.defineProperty(Blockly, 'theme', {
|
|
190
|
+
/**
|
|
191
|
+
* Get the current theme.
|
|
192
|
+
* @return {!Blockly.Themes} The current global theme.
|
|
193
|
+
*/
|
|
194
|
+
get: function() {
|
|
195
|
+
return Blockly.theme_;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Sets the global theme, which is applied to all workspaces.
|
|
201
|
+
* If the passed theme depends on initialization logic, that will only be applied to newly created workspaces.
|
|
202
|
+
* @param {!Blockly.Themes} theme the theme to set as the global theme.
|
|
203
|
+
*/
|
|
204
|
+
Blockly.setTheme = function(theme) {
|
|
205
|
+
if (theme === Blockly.Themes.CAT_BLOCKS) {
|
|
206
|
+
Blockly.theme_ = theme;
|
|
207
|
+
} else {
|
|
208
|
+
Blockly.theme_ = Blockly.Themes.CLASSIC;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
178
212
|
/**
|
|
179
213
|
* Handle a key-down on SVG drawing surface. Does nothing if the main workspace is not visible.
|
|
180
214
|
* @param {!Event} e Key down event.
|
package/core/constants.js
CHANGED