Permalink
musicblocks/js/protoblocks.js
Newer
100644
1786 lines (1566 sloc)
49.5 KB
3
// This program is free software; you can redistribute it and/or
4
// modify it under the terms of the The GNU Affero General Public
5
// License as published by the Free Software Foundation; either
6
// version 3 of the License, or (at your option) any later version.
8
// You should have received a copy of the GNU Affero General Public
9
// License along with this library; if not, write to the Free Software
10
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
11
12
/*
13
global
14
15
createjs, SVG, DEFAULTBLOCKSCALE, STANDARDBLOCKHEIGHT
16
*/
17
18
/*
19
exported
20
21
ProtoBlock, ValueBlock, BooleanBlock, BooleanSensorBlock,
22
LeftBlock, FlowClampBlock, StackClampBlock
23
*/
24
25
// The ProtoBlock class is defined in this file. Protoblocks are the
26
// prototypes from which Blocks are created.
27
28
// Note that protoblocks.js is largely deprecated. It is only used to
29
// generate palette blocks.
30
31
// Protoblock contain generic information about blocks and some
32
// methods common to all blocks.
33
class ProtoBlock {
34
constructor(name) {
35
// Name is used run-dictionary index, and palette label.
36
this.name = name;
37
// The palette to which this block is assigned.
38
this.palette = null;
39
// The graphic style used by the block.
40
this.style = null;
41
// The generator function used to create the artwork
42
this.generator = null;
43
// Does the block expand (or collapse) when other blocks are
44
// attached? e.g., start, repeat...
45
this.expandable = false;
46
// Is this block a parameter? Parameters have their labels
47
// overwritten with their current value.
48
this.parameter = false;
49
// How many "non-flow" arguments does a block have? (flow is
50
// vertical down a stack; args are horizontal. The pendown block
51
// has 0 args; the forward block has 1 arg; the setxy block has 2
52
// args.
53
this.args = 0;
54
// Default values for block parameters, e.g., forward 100 or right 90.
55
this.defaults = [];
56
// What is the size of the block prior to any expansion?
57
this.size = 1.0;
58
// Dock types are a list of the types associated with the docking points.
59
this.dockTypes = [];
60
// Static labels are generated as part of the inline SVG.
61
this.staticLabels = [];
62
// Default fontsize used for static labels.
63
this.fontsize = null;
64
// Extra block width for long labels
65
this.extraWidth = 0;
66
// Block scale
67
this.scale = DEFAULTBLOCKSCALE;
68
// The filepath of the image.
69
this.image = null;
70
// Hidden: don't show on any palette
71
this.hidden = false;
72
// Disabled: use inactive colors
73
this.disabled = false;
74
// Deprecated
75
this.deprecated = false;
76
//Stores the width of the text component
77
this.textWidth = 0;
78
this.labelOffset = 0;
79
this.beginnerModeBlock = false;
80
}
81
82
adjustWidthToLabel() {
86
const c = new createjs.Container();
87
const text = new createjs.Text(
88
this.staticLabels[0],
89
this.fontSize + "px Sans",
90
"#000000"
91
);
95
this.extraWidth += Math.max(b.width - 30, 0);
97
98
// What follows are the initializations for different block
99
// styles.
100
101
// The generator methods return the svg artwork, the dock
102
// positions, and the width and height, and the height used to
103
// calculate the hit area for the block. (Note that clamp blocks
104
// only extend their hit area to the top of the clamp.)
105
109
this.dockTypes.push("out");
110
this.dockTypes.push("in");
116
svg.setScale(this.scale);
117
svg.setTab(true);
118
svg.setSlot(true);
119
if (this.fontsize) {
120
svg.setFontSize(this.fontsize);
121
}
122
svg.setExpand(30 + this.extraWidth, 0, 0, 0);
125
svg.docks,
126
svg.getWidth(),
127
svg.getHeight(),
128
svg.getHeight()
129
];
131
132
// E.g., hidden (used at end of clamp)
134
this.args = 0;
135
this.size = 0;
136
this.dockTypes.push("out");
137
this.dockTypes.push("in");
138
this.generator = this.hiddenBlockFlowGenerator;
140
141
// E.g., hidden (used at end of no flow clamp)
145
this.dockTypes.push("out");
146
this.dockTypes.push("unavailable");
147
this.generator = this.hiddenBlockFlowGenerator;
152
svg.setScale(this.scale);
153
svg.setSlot(true);
154
svg.setTab(true);
155
if (this.fontsize) {
156
svg.setFontSize(this.fontsize);
157
}
158
// We need to generate the artwork in order to generate the dock.
160
// Then we replace the artwork with a single pixel.
162
'<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"><text style="font-size:10px;fill:#000000;font-family:sans-serif;text-anchor:end"><tspan x="46.333333333333336" y="13.5">block_label</tspan></text></svg>';
163
// And bring the last dock position to the top.
164
svg.docks[1][1] = svg.docks[0][1];
165
return [artwork, svg.docks, 0, 0, 0];
171
this.dockTypes.push("out");
172
this.dockTypes.push("unavailable");
178
svg.setScale(this.scale);
179
svg.setSlot(true);
180
svg.setTail(true);
189
svg.docks,
190
svg.getWidth(),
191
svg.getHeight(),
192
svg.getHeight()
193
];
199
this.dockTypes.push("unavailable");
200
this.dockTypes.push("unavailable");
206
svg.setScale(this.scale);
207
svg.setCap(true);
208
svg.setTail(true);
217
svg.docks,
218
svg.getWidth(),
219
svg.getHeight(),
220
svg.getHeight()
221
];
227
this.dockTypes.push("out");
228
this.dockTypes.push("numberin");
229
this.dockTypes.push("in");
235
svg.setScale(this.scale);
236
svg.setTab(true);
237
svg.setInnies([true]);
238
svg.setSlot(true);
247
svg.docks,
248
svg.getWidth(),
249
svg.getHeight(),
250
svg.getHeight()
251
];
258
this.dockTypes.push("out");
259
this.dockTypes.push("booleanin");
260
this.dockTypes.push("in");
266
svg.setScale(this.scale);
267
svg.setTab(true);
268
svg.setSlot(true);
269
svg.setBoolean(true);
270
svg.setClampCount(0);
279
svg.docks,
280
svg.getWidth(),
281
svg.getHeight(),
282
svg.getHeight()
283
];
292
this.dockTypes.push("out");
293
this.dockTypes.push("numberin");
294
this.dockTypes.push("numberin");
295
this.dockTypes.push("in");
301
svg.setScale(this.scale);
302
svg.setTab(true);
303
svg.setInnies([true, true]);
304
svg.setSlot(true);
310
} else {
311
svg.setExpand(30 + this.extraWidth, 0, 0, 0);
312
}
320
svg.docks,
321
svg.getWidth(),
322
svg.getHeight(),
323
svg.getHeight()
324
];
333
this.dockTypes.push("out");
334
this.dockTypes.push("numberin");
335
this.dockTypes.push("numberin");
336
this.dockTypes.push("numberin");
337
this.dockTypes.push("in");
343
svg.setScale(this.scale);
344
svg.setTab(true);
345
svg.setInnies([true, true, true]);
346
svg.setSlot(true);
362
svg.docks,
363
svg.getWidth(),
364
svg.getHeight(),
365
svg.getHeight()
366
];
374
this.dockTypes.push("out");
375
this.dockTypes.push("numberin");
376
this.dockTypes.push("numberin");
377
this.dockTypes.push("numberin");
378
this.dockTypes.push("numberin");
379
this.dockTypes.push("in");
385
svg.setScale(this.scale);
386
svg.setTab(true);
387
svg.setInnies([true, true, true, true]);
388
svg.setSlot(true);
404
svg.docks,
405
svg.getWidth(),
406
svg.getHeight(),
407
svg.getHeight()
408
];
417
this.dockTypes.push("numberout");
418
this.dockTypes.push("numberin");
424
svg.setScale(this.scale);
425
svg.setSlot(false);
426
svg.setInnies([true]);
427
svg.setOutie(true);
428
svg.setTab(false);
437
svg.docks,
438
svg.getWidth(),
439
svg.getHeight(),
440
svg.getHeight()
441
];
444
// E.g., plus, minus, multiply, divide, power,distance. These are also expandable.
451
this.dockTypes.push("numberout");
452
this.dockTypes.push("numberin");
453
this.dockTypes.push("numberin");
459
svg.setScale(this.scale);
460
svg.setSlot(false);
461
svg.setInnies([true, true]);
462
svg.setOutie(true);
463
svg.setTab(false);
469
} else {
470
svg.setExpand(30 + this.extraWidth, 0, 0, 0);
471
}
479
svg.docks,
480
svg.getWidth(),
481
svg.getHeight(),
482
svg.getHeight()
483
];
490
this.size = 3;
491
this.args = 3;
492
this.parameter = true;
493
this.dockTypes.push("numberout");
494
this.dockTypes.push("numberin");
495
this.dockTypes.push("numberin");
496
this.dockTypes.push("numberin");
497
this.generator = this.threeArgMathBlockGenerator;
502
svg.setScale(this.scale);
503
svg.setSlot(false);
504
svg.setInnies([true, true, true]);
505
svg.setOutie(true);
506
svg.setTab(false);
512
} else {
513
svg.setExpand(30 + this.extraWidth, 0, 0, 0);
514
}
516
if (this.fontsize) {
517
svg.setFontSize(this.fontsize);
518
}
522
svg.docks,
523
svg.getWidth(),
524
svg.getHeight(),
525
svg.getHeight()
526
];
528
// E.g., distance . Distance block will calculate geometrical distance between two pointa
529
// by default (cursor x ,cursor y ) and x and y
533
this.size = 4;
534
this.args = 4;
535
this.parameter = true;
536
this.dockTypes.push("numberout");
537
this.dockTypes.push("numberin");
538
this.dockTypes.push("numberin");
539
this.dockTypes.push("numberin");
540
this.dockTypes.push("numberin");
541
this.generator = this.fourArgMathBlockGenerator;
546
svg.setScale(this.scale);
547
svg.setSlot(false);
548
svg.setInnies([true, true, true, true]);
549
svg.setOutie(true);
550
svg.setTab(false);
551
552
if (expandY) {
556
} else {
557
svg.setExpand(30 + this.extraWidth, 0, 0, 0);
558
}
560
if (this.fontsize) {
561
svg.setFontSize(this.fontsize);
562
}
566
svg.docks,
567
svg.getWidth(),
568
svg.getHeight(),
569
svg.getHeight()
570
];
573
// E.g., number, string. Value blocks get DOM textareas associated
574
// with them so their values can be edited by the user.
585
svg.setScale(this.scale);
586
// Extra room for parameter label
587
svg.setExpand(60 + this.extraWidth, 0, 0, 0);
588
svg.setOutie(true);
596
svg.docks,
597
svg.getWidth(),
598
svg.getHeight(),
599
svg.getHeight()
600
];
602
603
// E.g., media. Media blocks invoke a chooser and a thumbnail
604
// image is overlayed to represent the data associated with the
605
// block.
616
svg.setScale(this.scale);
617
// Extra room for graphics
618
svg.setExpand(60 + this.extraWidth, 23, 0, 0);
619
svg.setOutie(true);
627
svg.docks,
628
svg.getWidth(),
629
svg.getHeight(),
630
svg.getHeight()
631
];
633
634
// E.g., start. A "child" flow is docked in an expandable clamp.
635
// There are no additional arguments and no flow above or below.
641
this.dockTypes.push("unavailable");
642
this.dockTypes.push("in");
643
this.dockTypes.push("unavailable");
647
stackClampZeroArgBlockGenerator(slots) {
649
svg.setScale(this.scale);
650
svg.setCap(true);
651
svg.setTail(true);
652
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
655
if (slots) {
656
svg.setClampSlots(0, slots);
657
} else {
658
svg.setClampSlots(0, 1);
659
}
667
svg.docks,
668
svg.getWidth(),
669
svg.getHeight(),
670
svg.docks[1][1]
671
];
673
674
// E.g., emptyclamp. Unlike start, there is a flow above and below.
680
this.dockTypes.push("out");
681
this.dockTypes.push("in");
682
this.dockTypes.push("in");
691
svg.setSlot(true);
692
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
694
if (slots) {
695
svg.setClampSlots(0, slots);
696
} else {
697
svg.setClampSlots(0, 1);
698
}
706
svg.docks,
707
svg.getWidth(),
708
svg.getHeight(),
709
svg.docks[1][1]
710
];
712
713
// E.g., repeat. Unlike action, there is a flow above and below.
719
this.dockTypes.push("out");
720
this.dockTypes.push("numberin");
721
this.dockTypes.push("in");
722
this.dockTypes.push("in");
728
svg.setScale(this.scale);
729
svg.setTab(true);
730
svg.setSlot(true);
731
svg.setInnies([true]);
735
if (slots) {
736
svg.setClampSlots(0, slots);
737
} else {
738
svg.setClampSlots(0, 1);
739
}
747
svg.docks,
748
svg.getWidth(),
749
svg.getHeight(),
750
svg.docks[2][1]
751
];
754
// E.g., tuplet, which takes two args plus an interior flow.
755
// There is a flow above and below.
758
this.expandable = true;
759
this.size = 3;
760
this.args = 3;
761
this.dockTypes.push("out");
762
this.dockTypes.push("numberin");
763
this.dockTypes.push("numberin");
764
this.dockTypes.push("in");
765
this.dockTypes.push("in");
766
this.generator = this.flowClampTwoArgBlockGenerator;
771
svg.setScale(this.scale);
772
svg.setTab(true);
773
svg.setSlot(true);
774
svg.setInnies([true, true]);
775
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
777
if (slots) {
778
svg.setClampSlots(0, slots);
779
} else {
780
svg.setClampSlots(0, 1);
781
}
783
if (this.fontsize) {
784
svg.setFontSize(this.fontsize);
785
}
789
svg.docks,
790
svg.getWidth(),
791
svg.getHeight(),
792
svg.docks[3][1]
793
];
798
this.expandable = true;
799
this.size = 4;
800
this.args = 4;
801
this.dockTypes.push("out");
802
this.dockTypes.push("numberin");
803
this.dockTypes.push("numberin");
804
this.dockTypes.push("textin");
805
this.dockTypes.push("in");
806
this.dockTypes.push("in");
807
this.generator = this.flowClampThreeArgBlockGenerator;
810
flowClampThreeArgBlockGenerator(slots) {
812
svg.setScale(this.scale);
813
svg.setTab(true);
814
svg.setSlot(true);
818
if (slots) {
819
svg.setClampSlots(0, slots);
820
} else {
821
svg.setClampSlots(0, 1);
822
}
824
if (this.fontsize) {
825
svg.setFontSize(this.fontsize);
826
}
830
svg.docks,
831
svg.getWidth(),
832
svg.getHeight(),
833
svg.docks[4][1]
834
];
837
// E.g., do with args: innies instead of interior slots.
840
this.expandable = true;
841
this.size = 3;
842
this.args = 2;
843
this.dockTypes.push("out");
844
this.dockTypes.push("textin");
845
this.dockTypes.push("anyin");
846
this.dockTypes.push("in");
847
this.generator = this.argClampOneArgBlockGenerator;
852
svg.setScale(this.scale);
853
svg.setTab(true);
854
svg.setSlot(true);
855
svg.setInnies([true]);
856
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
858
if (slots) {
859
svg.setClampSlots(0, slots);
860
} else {
861
svg.setClampSlots(0, [1]);
862
}
864
if (this.fontsize) {
865
svg.setFontSize(this.fontsize);
866
}
868
// The hit area extends halfway between the label dock and the
869
// first innie arg dock.
872
svg.docks,
873
svg.getWidth(),
874
svg.getHeight(),
875
(svg.docks[1][1] + svg.docks[2][1]) / 2
876
];
878
879
// E.g., calculate with args: innies instead of interior slots.
882
this.expandable = true;
883
this.size = 3;
884
this.args = 2;
885
this.dockTypes.push("anyout");
886
this.dockTypes.push("textin");
887
this.dockTypes.push("anyin");
888
this.generator = this.argClampOneArgMathBlockGenerator;
891
argClampOneArgMathBlockGenerator(slots) {
893
svg.setScale(this.scale);
894
svg.setInnies([true]);
895
svg.setOutie(true);
896
svg.setTab(false);
897
svg.setSlot(false);
898
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
900
if (slots) {
901
svg.setClampSlots(0, slots);
902
} else {
903
svg.setClampSlots(0, [1]);
904
}
906
if (this.fontsize) {
907
svg.setFontSize(this.fontsize);
908
}
910
// The hit area extends halfway between the label dock and the
911
// first innie arg dock.
914
svg.docks,
915
svg.getWidth(),
916
svg.getHeight(),
917
(svg.docks[1][1] + svg.docks[2][1]) / 2
918
];
920
921
// E.g., named do with args: innies instead of interior slots.
924
this.expandable = true;
925
this.size = 3;
926
this.args = 1;
927
this.dockTypes.push("out");
928
this.dockTypes.push("anyin");
929
this.dockTypes.push("in");
930
this.generator = this.argClampBlockGenerator;
935
svg.setScale(this.scale);
936
svg.setTab(true);
937
svg.setSlot(true);
938
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
939
if (slots) {
940
svg.setClampSlots(0, slots);
941
} else {
942
svg.setClampSlots(0, [1]);
943
}
945
if (this.fontsize) {
946
svg.setFontSize(this.fontsize);
947
}
951
svg.docks,
952
svg.getWidth(),
953
svg.getHeight(),
954
(svg.docks[1][1] * 2) / 3
955
];
957
958
// E.g., named calculate with args: innies instead of interior slots.
961
this.expandable = true;
962
this.size = 3;
963
this.args = 1;
964
this.dockTypes.push("anyout");
965
this.dockTypes.push("anyin");
966
this.generator = this.argClampMathBlockGenerator;
971
svg.setScale(this.scale);
972
svg.setOutie(true);
973
svg.setTab(false);
974
svg.setSlot(false);
975
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
977
if (slots) {
978
svg.setClampSlots(0, slots);
979
} else {
980
svg.setClampSlots(0, [1]);
981
}
983
if (this.fontsize) {
984
svg.setFontSize(this.fontsize);
985
}
989
svg.docks,
990
svg.getWidth(),
991
svg.getHeight(),
992
(svg.docks[1][1] * 2) / 3
993
];
996
// E.g., if. A "child" flow is docked in an expandable clamp. The
997
// additional argument is a boolean. There is flow above and below.
1000
this.expandable = true;
1001
this.size = 3;
1002
this.args = 2;
1003
this.dockTypes.push("out");
1004
this.dockTypes.push("booleanin");
1005
this.dockTypes.push("in");
1006
this.dockTypes.push("in");
1010
flowClampBooleanArgBlockGenerator(slots) {
1012
svg.setScale(this.scale);
1013
svg.setTab(true);
1014
svg.setBoolean(true);
1015
svg.setSlot(true);
1016
svg.setExpand(this.extraWidth, 0, 0, 0);
1018
if (slots) {
1019
svg.setClampSlots(0, slots);
1020
} else {
1021
svg.setClampSlots(0, 1);
1022
}
1024
if (this.fontsize) {
1025
svg.setFontSize(this.fontsize);
1026
}
1030
svg.docks,
1031
svg.getWidth(),
1032
svg.getHeight(),
1033
svg.docks[2][1]
1034
];
1036
1037
// E.g., if then else. Two "child" flows are docked in expandable
1038
// clamps. The additional argument is a boolean. There is flow
1039
// above and below.
1042
this.expandable = true;
1043
this.size = 4;
1044
this.args = 3;
1045
this.dockTypes.push("out");
1046
this.dockTypes.push("booleanin");
1047
this.dockTypes.push("in");
1048
this.dockTypes.push("in");
1049
this.dockTypes.push("in");
1050
this.generator = this.doubleFlowClampBooleanArgBlockGenerator;
1053
doubleFlowClampBooleanArgBlockGenerator(bottomSlots,topSlots) {
1055
svg.setScale(this.scale);
1056
svg.setTab(true);
1057
svg.setSlot(true);
1058
svg.setBoolean(true);
1059
svg.setClampCount(2);
1060
1061
if (topSlots) {
1062
svg.setClampSlots(0, topSlots);
1063
} else {
1064
svg.setClampSlots(0, 1);
1065
}
1067
if (bottomSlots) {
1068
svg.setClampSlots(1, bottomSlots);
1069
} else {
1070
svg.setClampSlots(1, 1);
1071
}
1075
if (this.fontsize) {
1076
svg.setFontSize(this.fontsize);
1077
}
1081
svg.docks,
1082
svg.getWidth(),
1083
svg.getHeight(),
1084
svg.docks[2][1]
1085
];
1087
1088
// E.g., forever. Unlike start, there is flow above and below.
1091
this.expandable = true;
1092
this.size = 2;
1093
this.args = 1;
1094
this.dockTypes.push("out");
1095
this.dockTypes.push("in");
1096
this.dockTypes.push("in");
1100
flowClampZeroArgBlockGenerator(slots) {
1102
svg.setScale(this.scale);
1103
svg.setTab(true);
1104
svg.setSlot(true);
1105
svg.setExpand(10 + this.extraWidth, 0, 0, 0);
1107
if (slots) {
1108
svg.setClampSlots(0, slots);
1109
} else {
1110
svg.setClampSlots(0, 1);
1111
}
1113
if (this.fontsize) {
1114
svg.setFontSize(this.fontsize);
1115
}
1119
svg.docks,
1120
svg.getWidth(),
1121
svg.getHeight(),
1122
svg.docks[1][1]
1123
];
1126
// E.g., count clamp: math block with interior slots
1129
this.expandable = true;
1130
this.size = 3;
1132
this.dockTypes.push("anyout");
1133
this.dockTypes.push("in");
1134
this.generator = this.argFlowClampGenerator;
1136
1139
svg.setScale(this.scale);
1140
svg.setSlot(false);
1141
svg.setOutie(true);
1142
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
1144
if (slots) {
1145
svg.setClampSlots(0, slots);
1146
} else {
1147
svg.setClampSlots(0, [1]);
1148
}
1150
if (this.fontsize) {
1151
svg.setFontSize(this.fontsize);
1152
}
1156
svg.docks,
1157
svg.getWidth(),
1158
svg.getHeight(),
1159
svg.docks[1][1]
1160
];
1162
1163
// E.g., action. A "child" flow is docked in an expandable clamp.
1164
// The additional argument is a name. Again, no flow above or below.
1170
this.dockTypes.push("unavailable");
1171
this.dockTypes.push("anyin");
1172
this.dockTypes.push("in");
1173
this.dockTypes.push("unavailable");
1177
stackClampOneArgBlockGenerator(slots) {
1179
svg.setScale(this.scale);
1180
svg.setCap(true);
1181
svg.setTail(true);
1182
svg.setInnies([true]);
1183
svg.setExpand(10 + this.extraWidth, 0, 0, 0);
1186
if (slots) {
1187
svg.setClampSlots(0, slots);
1188
} else {
1189
svg.setClampSlots(0, 1);
1190
}
1192
if (this.fontsize) {
1193
svg.setFontSize(this.fontsize);
1194
}
1198
svg.docks,
1199
svg.getWidth(),
1200
svg.getHeight(),
1201
svg.docks[2][1]
1202
];
1216
svg.setScale(this.scale);
1217
svg.setExpand(60 + this.extraWidth, 0, 0, 4);
1219
if (this.fontsize) {
1220
svg.setFontSize(this.fontsize);
1221
}
1225
svg.docks,
1226
svg.getWidth(),
1227
svg.getHeight(),
1228
svg.getHeight()
1229
];
1238
this.dockTypes.push("booleanout");
1239
this.dockTypes.push("textin");
1245
svg.setScale(this.scale);
1246
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
1248
if (this.fontsize) {
1249
svg.setFontSize(this.fontsize);
1250
}
1255
svg.docks,
1256
svg.getWidth(),
1257
svg.getHeight(),
1258
svg.getHeight()
1259
];
1268
this.dockTypes.push("booleanout");
1269
this.dockTypes.push("booleanin");
1275
svg.setScale(this.scale);
1276
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
1278
if (this.fontsize) {
1279
svg.setFontSize(this.fontsize);
1280
}
1284
svg.docks,
1285
svg.getWidth(),
1286
svg.getHeight(),
1287
svg.getHeight()
1288
];
1297
this.dockTypes.push("booleanout");
1298
this.dockTypes.push("booleanin");
1299
this.dockTypes.push("booleanin");
1305
svg.setScale(this.scale);
1306
svg.setExpand(20 + this.extraWidth, 0, 0, 0);
1308
if (this.fontsize) {
1309
svg.setFontSize(this.fontsize);
1310
}
1314
svg.docks,
1315
svg.getWidth(),
1316
svg.getHeight(),
1317
svg.getHeight()
1318
];
1324
this.size = 2;
1325
this.args = 2;
1326
this.parameter = true;
1327
this.expandable = true;
1328
this.dockTypes.push("booleanout");
1329
this.dockTypes.push("numberin");
1330
this.dockTypes.push("numberin");
1342
} else {
1343
svg.setExpand(10 + this.extraWidth, 0, 0, 0);
1344
}
1346
if (this.fontsize) {
1347
svg.setFontSize(this.fontsize);
1348
}
1352
svg.docks,
1353
svg.getWidth(),
1354
svg.getHeight(),
1355
svg.getHeight()
1356
];
1371
svg.setScale(this.scale);
1372
// Extra room for parameter label
1373
svg.setExpand(70 + this.extraWidth, 0, 0, 0);
1374
svg.setOutie(true);
1376
if (this.fontsize) {
1377
svg.setFontSize(this.fontsize);
1378
}
1382
svg.docks,
1383
svg.getWidth(),
1384
svg.getHeight(),
1385
svg.getHeight()
1386
];
1390
const isObject = (item) => {
1391
return item && typeof item === "object" && !Array.isArray(item);
1394
const mergeDeep = (target, ...sources) => {
1395
// From https://stackoverflow.com/a/34749873
1396
if (!sources.length) return target;
1397
const source = sources.shift();
1398
1399
if (isObject(target) && isObject(source)) {
1400
for (const key in source) {
1401
if (isObject(source[key])) {
1402
if (!target[key]) Object.assign(target, { [key]: {} });
1403
mergeDeep(target[key], source[key]);
1404
} else {
1405
Object.assign(target, { [key]: source[key] });
1406
}
1407
}
1408
}
1409
1410
return mergeDeep(target, ...sources);
1412
1413
class BaseBlock extends ProtoBlock {
1414
constructor(name) {
1415
super(name);
1416
1417
this.macroFunc = null;
1420
this.deprecated = false;
1421
this.extraSearchTerms = [];
1423
this.piemenuValuesC1 = [];
1424
this.piemenuValuesC2 = [];
1425
this.piemenuValuesC3 = [];
1427
1428
// Just for brevity
1429
this.lang = localStorage.languagePreference || navigator.language;
1430
}
1431
1432
setPalette(palette, activity) {
1433
this.palette = activity.palettes.dict[palette];
1434
}
1435
1439
1440
formBlock(style) {
1441
mergeDeep(this._style, style);
1442
this._style.args ||= 0;
1443
this._style.argTypes ||= [];
1444
this._style.argLabels ||= [];
1445
this._style.defaults ||= [];
1446
this._style.flows ||= {};
1447
this._style.flows.labels ||= [];
1449
if (this._style.args > 1) {
1450
this.expandable = true;
1451
}
1452
1453
if (this._style.flows.labels.length > 0) {
1454
if (this._style.flows.type === "arg") this.style = "argclamp";
1455
else if (this._style.flows.type === "value") this.style = "value";
1456
else if (this._style.flows.labels.length == 2)
1457
this.style = "doubleclamp";
1458
else this.style = "clamp";
1459
this.expandable = true;
1460
} else {
1461
if (this._style.flows.type === "value") this.style = "value";
1462
else if (this._style.flows.left) {
1464
this.parameter = true;
1465
} else if (this._style.args === 2) this.style = "twoarg";
1466
}
1467
1468
if (this._style.flows.type === "value" && this._style.args === 2)
1469
this.expandable = true;
1470
1471
this.args = this._style.flows.labels.length + this._style.args;
1473
this.size = 1 + this._style.flows.labels.length;
1474
}
1475
if (this._style.argTypes[0] === "booleanin") this.size++;
1476
else if (this._style.argTypes[1] === "booleanin") this.size++;
1477
else this.size += Math.max(0, this._style.args - 1);
1478
if (this._style.flows.type === "arg") this.size++;
1479
if (this._style.image) {
1480
this.size++;
1481
this.image = this._style.image;
1482
}
1483
1485
this.dockTypes = [];
1486
this.defaults = [];
1487
this._style.argLabels.forEach(i => this.staticLabels.push(i));
1488
this._style.flows.labels.forEach(i => this.staticLabels.push(i));
1489
1490
if (this._style.flows.left)
1491
this.dockTypes.push(this._style.outType || "numberout");
1492
if (this._style.flows.top)
1493
this.dockTypes.push(
1494
this._style.flows.top === "cap" ? "unavailable" : "out"
1495
);
1496
if (typeof this._style.args === "number")
1497
for (let i = 0; i < this._style.args; i++) {
1498
this.dockTypes.push(this._style.argTypes[i] || "numberin");
1499
if (i < this._style.defaults.length)
1500
this.defaults.push(this._style.defaults[i]);
1501
}
1503
for (let i = 0; i < this._style.flows.labels.length; i++)
1504
this.dockTypes.push(this._style.flows.types[i] || "numberin");
1505
for (let i = 0; i < this._style.flows.labels.length; i++)
1507
if (this._style.flows.bottom)
1508
this.dockTypes.push(
1509
this._style.flows.bottom === "tail" ? "unavailable" : "in"
1510
);
1514
svg.setScale(this.scale);
1515
1516
if (this._style.flows.top === "cap") svg.setCap(true);
1517
else svg.setSlot(this._style.flows.top);
1519
if (this._style.flows.bottom === "tail") svg.setTail(true);
1520
else if (this._style.flows.bottom) svg.setTab(true);
1521
if (this._style.flows.left) svg.setOutie(true);
1523
let pad = this._style.flows.type === "value" ? 60 : 20;
1524
if (!this._style.flows.type) pad += 10;
1525
if (this._style.outType === "booleanout" && this._style.args === 2)
1526
pad -= 30;
1527
else if (this._style.argTypes[0] === "booleanin") pad -= 5;
1528
if (this.size !== 0)
1529
svg.setExpand(
1530
pad + this.extraWidth,
1531
this.image ? 23 : 0,
1532
0,
1533
this._style.outType === "booleanout" && !this._style.args
1534
? 4
1535
: 0
1536
);
1537
1538
for (
1539
let i = arguments.length;
1540
i < this._style.flows.labels.length;
1541
i++
1542
)
1543
svg.setClampSlots(i, 1);
1544
svg.setClampCount(this._style.flows.labels.length);
1545
1546
for (let i = 0; i < arguments.length; i++) {
1547
if (this._style.flows.type == undefined) {
1548
svg.setExpand(
1549
30 + this.extraWidth,
1550
((arguments[arguments.length - i - 1] - 1) *
1551
STANDARDBLOCKHEIGHT) /
1552
2,
1553
0,
1554
0
1555
);
1556
} else if (this._style.flows.type == "value") {
1557
svg.setExpand(
1558
60 + this.extraWidth,
1559
((arguments[arguments.length - i - 1] - 1) *
1560
STANDARDBLOCKHEIGHT) /
1561
2,
1562
0,
1563
0
1564
);
1565
} else {
1566
svg.setClampSlots(
1567
i,
1568
arguments[arguments.length - i - 1] || 1
1569
);
1570
}
1571
}
1572
1573
if (this._style.argTypes[0] === "booleanin") {
1574
svg.setBoolean(true);
1575
} else if (typeof this._style.args === "number") {
1576
svg.setInnies(Array(this._style.args).fill(true));
1577
}
1578
1579
// Make space for the expand icon
1580
if (this._style.canCollapse) svg.setLabelOffset(15);
1582
if (this.fontsize) svg.setFontSize(this.fontsize);
1583
1584
let artwork;
1586
artwork = svg.argClamp();
1588
artwork = svg.basicClamp();
1589
} else if (this._style.outType === "booleanout") {
1590
if (this._style.args === 1 || !this._style.args) {
1591
artwork = svg.booleanNot(!this._style.args);
1592
} else if (this._style.argTypes[0] === "booleanin") {
1593
artwork = svg.booleanAndOr();
1594
} else {
1595
artwork = svg.booleanCompare();
1596
}
1598
artwork = svg.basicBox();
1599
} else {
1600
artwork = svg.basicBlock();
1601
}
1602
// If the block has 0 size, clear out the artwork
1603
if (this.size === 0) {
1604
artwork =
1605
'<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"><text style="font-size:10px;fill:#000000;font-family:sans-serif;text-anchor:end"><tspan x="46.333333333333336" y="13.5">block_label</tspan></text></svg>';
1606
svg.docks[1][1] = svg.docks[0][1];
1608
let clickHeight;
1609
this.isLeftClamp = this.style === "clamp" &&
1610
this._style.flows.left == true &&
1611
this._style.args === 0 &&
1612
this._style.flows.type == "flow" ;
1613
1614
if (this._style.flows.top || this._style.flows.bottom)
1615
clickHeight =
1616
svg.docks[
1617
svg.docks.length - this._style.flows.labels.length - 1
1618
][1];
1620
clickHeight = // special Case with no .top and .bottom .
1621
svg.docks[
1622
svg.docks.length - this._style.flows.labels.length
1623
][1];
1625
if (this.size === 0) return [artwork, svg.docks, 0, 0, 0];
1626
1627
// Special case for one argument boolean output blocks e.g found mouse
1628
1629
if(this._style.flows.left === "bool") {
1630
artwork = svg.booleanNot(true); // OneArg
1631
}
1632
1633
return [
1634
artwork,
1635
svg.docks,
1636
svg.getWidth(),
1637
svg.getHeight(),
1638
clickHeight
1639
];
1640
};
1641
}
1642
1643
makeMacro(macroFunc) {
1644
this.macroFunc = macroFunc;
1645
}
1646
1647
updateDockValue(slot, value) {
1648
this.dockTypes[slot] = value;
1649
}
1650
1651
changeName(name) {
1652
this.name = name;
1653
}
1654
1655
beginnerBlock(value) {
1656
this.beginnerModeBlock = value;
1657
}
1658
1659
setup(activity) {
1660
activity.blocks.protoBlockDict[this.name] = this;
1662
if (activity.beginnerMode && !this.beginnerModeBlock) {
1663
this.hidden = true;
1664
}
1667
if (!this.palette)
1669
console.warn("Block " + this.name + " was not added to a palette!");
1670
else this.palette.add(this);
1671
}
1672
}
1673
1674
class ValueBlock extends BaseBlock {
1675
constructor(name, displayName) {
1676
super(name);
1679
this.formBlock(
1680
{
1681
name: displayName,
1682
flows: {
1683
left: true,
1684
type: "value"
1685
}
1686
},
1687
!!displayName
1688
);
1689
}
1690
}
1691
1692
class BooleanBlock extends BaseBlock {
1693
constructor(name) {
1694
super(name);
1695
1696
this.formBlock({
1697
flows: {
1700
},
1702
});
1703
}
1704
}
1705
1706
class BooleanSensorBlock extends BaseBlock {
1707
constructor(name, displayName) {
1708
super(name);
1710
1711
this.formBlock({
1712
name: displayName,
1713
flows: {
1716
},
1718
});
1719
}
1720
}
1721
1722
class FlowBlock extends BaseBlock {
1723
constructor(name, displayName) {
1724
super(name);
1727
this.formBlock(
1728
{
1729
name: displayName,
1730
flows: {
1731
top: true,
1732
bottom: true
1733
}
1734
},
1735
!!displayName
1736
);
1737
}
1738
}
1739
1740
class LeftBlock extends BaseBlock {
1741
constructor(name, displayName) {
1742
super(name);
1745
this.formBlock(
1746
{
1747
name: displayName,
1748
flows: {
1749
left: true,
1750
type: null
1751
}
1752
},
1753
!!displayName
1754
);
1755
}
1756
}
1757
1758
class FlowClampBlock extends FlowBlock {
1759
constructor(name) {
1760
super(name);
1761
1762
this.extraWidth = 20;
1763
this.formBlock({
1764
flows: {
1767
}
1768
});
1769
}
1770
}
1771
1772
class StackClampBlock extends BaseBlock {
1773
constructor(name) {
1774
super(name);
1775
1776
this.extraWidth = 40;
1777
this.formBlock({
1778
flows: {
1779
top: "cap",
1780
bottom: "tail",
1781
type: "flow",
1782
labels: [""]
1783
}
1784
});
1785
}