blockly 7.20211209.2 → 8.0.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.
Files changed (262) hide show
  1. package/blockly.d.ts +18963 -18432
  2. package/blockly.min.js +852 -844
  3. package/blockly_compressed.js +669 -664
  4. package/blockly_compressed.js.map +1 -1
  5. package/blocks/blocks.js +47 -0
  6. package/blocks/colour.js +13 -3
  7. package/blocks/lists.js +22 -13
  8. package/blocks/logic.js +13 -3
  9. package/blocks/loops.js +24 -11
  10. package/blocks/math.js +12 -3
  11. package/blocks/procedures.js +41 -27
  12. package/blocks/text.js +22 -13
  13. package/blocks/variables.js +14 -3
  14. package/blocks/variables_dynamic.js +13 -3
  15. package/blocks_compressed.js +146 -141
  16. package/blocks_compressed.js.map +1 -1
  17. package/core/block.js +1869 -1814
  18. package/core/block_drag_surface.js +201 -200
  19. package/core/block_dragger.js +377 -373
  20. package/core/block_svg.js +1593 -1479
  21. package/core/blockly.js +8 -22
  22. package/core/blocks.js +9 -2
  23. package/core/browser_events.js +22 -5
  24. package/core/bubble.js +841 -797
  25. package/core/bubble_dragger.js +213 -206
  26. package/core/bump_objects.js +2 -2
  27. package/core/clipboard.js +9 -9
  28. package/core/comment.js +353 -332
  29. package/core/common.js +46 -17
  30. package/core/component_manager.js +181 -174
  31. package/core/config.js +87 -0
  32. package/core/connection.js +595 -584
  33. package/core/connection_checker.js +242 -244
  34. package/core/connection_db.js +235 -230
  35. package/core/contextmenu.js +9 -6
  36. package/core/contextmenu_items.js +1 -2
  37. package/core/contextmenu_registry.js +93 -89
  38. package/core/css.js +474 -474
  39. package/core/delete_area.js +45 -42
  40. package/core/drag_target.js +57 -56
  41. package/core/dropdowndiv.js +153 -163
  42. package/core/events/events.js +2 -2
  43. package/core/events/events_abstract.js +89 -77
  44. package/core/events/events_block_base.js +37 -36
  45. package/core/events/events_block_change.js +130 -124
  46. package/core/events/events_block_create.js +73 -71
  47. package/core/events/events_block_delete.js +84 -82
  48. package/core/events/events_block_drag.js +50 -49
  49. package/core/events/events_block_move.js +147 -140
  50. package/core/events/events_bubble_open.js +51 -50
  51. package/core/events/events_click.js +48 -44
  52. package/core/events/events_comment_base.js +72 -69
  53. package/core/events/events_comment_change.js +63 -61
  54. package/core/events/events_comment_create.js +44 -42
  55. package/core/events/events_comment_delete.js +42 -40
  56. package/core/events/events_comment_move.js +106 -104
  57. package/core/events/events_marker_move.js +65 -64
  58. package/core/events/events_selected.js +46 -45
  59. package/core/events/events_theme_change.js +36 -35
  60. package/core/events/events_toolbox_item_select.js +46 -45
  61. package/core/events/events_trashcan_open.js +37 -36
  62. package/core/events/events_ui.js +47 -46
  63. package/core/events/events_ui_base.js +30 -29
  64. package/core/events/events_var_base.js +37 -36
  65. package/core/events/events_var_create.js +50 -48
  66. package/core/events/events_var_delete.js +50 -48
  67. package/core/events/events_var_rename.js +51 -49
  68. package/core/events/events_viewport.js +66 -65
  69. package/core/events/utils.js +29 -14
  70. package/core/events/workspace_events.js +49 -55
  71. package/core/extensions.js +4 -3
  72. package/core/field.js +1061 -997
  73. package/core/field_angle.js +462 -442
  74. package/core/field_checkbox.js +194 -182
  75. package/core/field_colour.js +519 -505
  76. package/core/field_dropdown.js +617 -598
  77. package/core/field_image.js +229 -220
  78. package/core/field_label.js +102 -91
  79. package/core/field_label_serializable.js +42 -41
  80. package/core/field_multilineinput.js +372 -358
  81. package/core/field_number.js +272 -253
  82. package/core/field_textinput.js +499 -467
  83. package/core/field_variable.js +458 -420
  84. package/core/flyout_base.js +1005 -952
  85. package/core/flyout_button.js +277 -260
  86. package/core/flyout_horizontal.js +304 -302
  87. package/core/flyout_metrics_manager.js +64 -64
  88. package/core/flyout_vertical.js +306 -300
  89. package/core/generator.js +459 -446
  90. package/core/gesture.js +829 -813
  91. package/core/grid.js +166 -163
  92. package/core/icon.js +168 -159
  93. package/core/inject.js +7 -5
  94. package/core/input.js +257 -248
  95. package/core/insertion_marker_manager.js +655 -624
  96. package/core/internal_constants.js +0 -129
  97. package/core/keyboard_nav/ast_node.js +605 -596
  98. package/core/keyboard_nav/basic_cursor.js +166 -165
  99. package/core/keyboard_nav/cursor.js +99 -97
  100. package/core/keyboard_nav/marker.js +83 -79
  101. package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
  102. package/core/marker_manager.js +153 -141
  103. package/core/menu.js +377 -372
  104. package/core/menuitem.js +223 -217
  105. package/core/metrics_manager.js +403 -390
  106. package/core/mutator.js +468 -437
  107. package/core/names.js +229 -188
  108. package/core/options.js +290 -284
  109. package/core/procedures.js +29 -17
  110. package/core/registry.js +19 -16
  111. package/core/rendered_connection.js +482 -463
  112. package/core/renderers/common/block_rendering.js +9 -3
  113. package/core/renderers/common/constants.js +1119 -1112
  114. package/core/renderers/common/debug.js +14 -0
  115. package/core/renderers/common/debugger.js +338 -316
  116. package/core/renderers/common/drawer.js +380 -370
  117. package/core/renderers/common/i_path_object.js +2 -2
  118. package/core/renderers/common/info.js +626 -618
  119. package/core/renderers/common/marker_svg.js +579 -541
  120. package/core/renderers/common/path_object.js +203 -200
  121. package/core/renderers/common/renderer.js +220 -218
  122. package/core/renderers/geras/constants.js +36 -36
  123. package/core/renderers/geras/drawer.js +155 -147
  124. package/core/renderers/geras/highlight_constants.js +244 -238
  125. package/core/renderers/geras/highlighter.js +231 -179
  126. package/core/renderers/geras/info.js +392 -369
  127. package/core/renderers/geras/measurables/inline_input.js +25 -19
  128. package/core/renderers/geras/measurables/statement_input.js +23 -17
  129. package/core/renderers/geras/path_object.js +106 -121
  130. package/core/renderers/geras/renderer.js +96 -98
  131. package/core/renderers/measurables/base.js +30 -18
  132. package/core/renderers/measurables/bottom_row.js +83 -80
  133. package/core/renderers/measurables/connection.js +22 -15
  134. package/core/renderers/measurables/external_value_input.js +35 -22
  135. package/core/renderers/measurables/field.js +35 -20
  136. package/core/renderers/measurables/hat.js +18 -13
  137. package/core/renderers/measurables/icon.js +24 -17
  138. package/core/renderers/measurables/in_row_spacer.js +15 -13
  139. package/core/renderers/measurables/inline_input.js +43 -33
  140. package/core/renderers/measurables/input_connection.js +41 -28
  141. package/core/renderers/measurables/input_row.js +50 -44
  142. package/core/renderers/measurables/jagged_edge.js +14 -12
  143. package/core/renderers/measurables/next_connection.js +16 -14
  144. package/core/renderers/measurables/output_connection.js +26 -20
  145. package/core/renderers/measurables/previous_connection.js +16 -15
  146. package/core/renderers/measurables/round_corner.js +20 -18
  147. package/core/renderers/measurables/row.js +184 -168
  148. package/core/renderers/measurables/spacer_row.js +38 -23
  149. package/core/renderers/measurables/square_corner.js +18 -16
  150. package/core/renderers/measurables/statement_input.js +23 -20
  151. package/core/renderers/measurables/top_row.js +88 -85
  152. package/core/renderers/minimalist/constants.js +8 -7
  153. package/core/renderers/minimalist/drawer.js +11 -10
  154. package/core/renderers/minimalist/info.js +18 -18
  155. package/core/renderers/minimalist/renderer.js +40 -39
  156. package/core/renderers/thrasos/info.js +258 -248
  157. package/core/renderers/thrasos/renderer.js +20 -20
  158. package/core/renderers/zelos/constants.js +898 -873
  159. package/core/renderers/zelos/drawer.js +186 -169
  160. package/core/renderers/zelos/info.js +502 -479
  161. package/core/renderers/zelos/marker_svg.js +129 -115
  162. package/core/renderers/zelos/measurables/bottom_row.js +31 -30
  163. package/core/renderers/zelos/measurables/inputs.js +22 -21
  164. package/core/renderers/zelos/measurables/row_elements.js +14 -13
  165. package/core/renderers/zelos/measurables/top_row.js +34 -33
  166. package/core/renderers/zelos/path_object.js +181 -180
  167. package/core/renderers/zelos/renderer.js +91 -92
  168. package/core/scrollbar.js +759 -713
  169. package/core/scrollbar_pair.js +250 -245
  170. package/core/serialization/blocks.js +19 -9
  171. package/core/serialization/workspaces.js +3 -2
  172. package/core/shortcut_registry.js +286 -277
  173. package/core/sprites.js +31 -0
  174. package/core/theme.js +135 -141
  175. package/core/theme_manager.js +147 -143
  176. package/core/toolbox/category.js +602 -576
  177. package/core/toolbox/collapsible_category.js +226 -227
  178. package/core/toolbox/separator.js +70 -61
  179. package/core/toolbox/toolbox.js +934 -927
  180. package/core/toolbox/toolbox_item.js +115 -99
  181. package/core/tooltip.js +108 -35
  182. package/core/touch.js +8 -3
  183. package/core/touch_gesture.js +254 -251
  184. package/core/trashcan.js +606 -595
  185. package/core/utils/coordinate.js +97 -95
  186. package/core/utils/dom.js +2 -2
  187. package/core/utils/global.js +2 -0
  188. package/core/utils/rect.js +41 -37
  189. package/core/utils/sentinel.js +25 -0
  190. package/core/utils/size.js +30 -27
  191. package/core/utils/svg.js +18 -16
  192. package/core/variable_map.js +325 -341
  193. package/core/variable_model.js +55 -54
  194. package/core/variables.js +9 -2
  195. package/core/variables_dynamic.js +3 -1
  196. package/core/warning.js +126 -120
  197. package/core/widgetdiv.js +4 -4
  198. package/core/workspace.js +685 -664
  199. package/core/workspace_audio.js +124 -118
  200. package/core/workspace_comment.js +308 -298
  201. package/core/workspace_comment_svg.js +1029 -951
  202. package/core/workspace_drag_surface_svg.js +147 -140
  203. package/core/workspace_dragger.js +70 -71
  204. package/core/workspace_svg.js +2322 -2297
  205. package/core/xml.js +30 -20
  206. package/core/zoom_controls.js +431 -439
  207. package/dart_compressed.js +40 -43
  208. package/dart_compressed.js.map +1 -1
  209. package/generators/dart/colour.js +56 -64
  210. package/generators/dart/lists.js +61 -50
  211. package/generators/dart/math.js +160 -148
  212. package/generators/dart/text.js +83 -61
  213. package/generators/javascript/colour.js +37 -34
  214. package/generators/javascript/lists.js +50 -43
  215. package/generators/javascript/math.js +123 -139
  216. package/generators/javascript/text.js +67 -81
  217. package/generators/lua/colour.js +25 -23
  218. package/generators/lua/lists.js +97 -69
  219. package/generators/lua/logic.js +1 -2
  220. package/generators/lua/math.js +182 -144
  221. package/generators/lua/text.js +116 -99
  222. package/generators/php/colour.js +38 -32
  223. package/generators/php/lists.js +109 -89
  224. package/generators/php/math.js +90 -81
  225. package/generators/php/text.js +63 -61
  226. package/generators/python/colour.js +18 -18
  227. package/generators/python/lists.js +38 -30
  228. package/generators/python/loops.js +12 -8
  229. package/generators/python/math.js +104 -106
  230. package/generators/python/text.js +34 -30
  231. package/javascript_compressed.js +37 -39
  232. package/javascript_compressed.js.map +1 -1
  233. package/lua_compressed.js +39 -42
  234. package/lua_compressed.js.map +1 -1
  235. package/msg/az.js +2 -2
  236. package/msg/be.js +4 -4
  237. package/msg/cs.js +15 -15
  238. package/msg/de.js +1 -1
  239. package/msg/diq.js +1 -1
  240. package/msg/eo.js +1 -1
  241. package/msg/es.js +1 -1
  242. package/msg/fa.js +1 -1
  243. package/msg/fr.js +4 -4
  244. package/msg/he.js +1 -1
  245. package/msg/hr.js +2 -2
  246. package/msg/hy.js +2 -2
  247. package/msg/id.js +12 -12
  248. package/msg/inh.js +14 -14
  249. package/msg/ja.js +7 -7
  250. package/msg/lv.js +29 -29
  251. package/msg/pa.js +3 -3
  252. package/msg/smn.js +436 -0
  253. package/msg/te.js +1 -1
  254. package/msg/yue.js +1 -1
  255. package/msg/zh-hans.js +3 -3
  256. package/msg/zh-hant.js +3 -3
  257. package/package.json +7 -6
  258. package/php_compressed.js +38 -42
  259. package/php_compressed.js.map +1 -1
  260. package/python_compressed.js +26 -25
  261. package/python_compressed.js.map +1 -1
  262. package/blocks/all.js +0 -23
@@ -20,7 +20,7 @@ const Touch = goog.require('Blockly.Touch');
20
20
  const browserEvents = goog.require('Blockly.browserEvents');
21
21
  const dom = goog.require('Blockly.utils.dom');
22
22
  const eventUtils = goog.require('Blockly.Events.utils');
23
- const internalConstants = goog.require('Blockly.internalConstants');
23
+ const {SPRITE} = goog.require('Blockly.sprite');
24
24
  const uiPosition = goog.require('Blockly.uiPosition');
25
25
  const {ComponentManager} = goog.require('Blockly.ComponentManager');
26
26
  /* eslint-disable-next-line no-unused-vars */
@@ -38,478 +38,470 @@ goog.require('Blockly.Events.Click');
38
38
 
39
39
  /**
40
40
  * Class for a zoom controls.
41
- * @param {!WorkspaceSvg} workspace The workspace to sit in.
42
- * @constructor
43
41
  * @implements {IPositionable}
44
42
  * @alias Blockly.ZoomControls
45
43
  */
46
- const ZoomControls = function(workspace) {
44
+ class ZoomControls {
47
45
  /**
48
- * @type {!WorkspaceSvg}
49
- * @private
46
+ * @param {!WorkspaceSvg} workspace The workspace to sit in.
50
47
  */
51
- this.workspace_ = workspace;
52
-
48
+ constructor(workspace) {
49
+ /**
50
+ * @type {!WorkspaceSvg}
51
+ * @private
52
+ */
53
+ this.workspace_ = workspace;
54
+
55
+ /**
56
+ * The unique id for this component that is used to register with the
57
+ * ComponentManager.
58
+ * @type {string}
59
+ */
60
+ this.id = 'zoomControls';
61
+
62
+ /**
63
+ * A handle to use to unbind the mouse down event handler for zoom reset
64
+ * button. Opaque data returned from browserEvents.conditionalBind.
65
+ * @type {?browserEvents.Data}
66
+ * @private
67
+ */
68
+ this.onZoomResetWrapper_ = null;
69
+
70
+ /**
71
+ * A handle to use to unbind the mouse down event handler for zoom in
72
+ * button. Opaque data returned from browserEvents.conditionalBind.
73
+ * @type {?browserEvents.Data}
74
+ * @private
75
+ */
76
+ this.onZoomInWrapper_ = null;
77
+
78
+ /**
79
+ * A handle to use to unbind the mouse down event handler for zoom out
80
+ * button. Opaque data returned from browserEvents.conditionalBind.
81
+ * @type {?browserEvents.Data}
82
+ * @private
83
+ */
84
+ this.onZoomOutWrapper_ = null;
85
+
86
+ /**
87
+ * The zoom in svg <g> element.
88
+ * @type {SVGGElement}
89
+ * @private
90
+ */
91
+ this.zoomInGroup_ = null;
92
+
93
+ /**
94
+ * The zoom out svg <g> element.
95
+ * @type {SVGGElement}
96
+ * @private
97
+ */
98
+ this.zoomOutGroup_ = null;
99
+
100
+ /**
101
+ * The zoom reset svg <g> element.
102
+ * @type {SVGGElement}
103
+ * @private
104
+ */
105
+ this.zoomResetGroup_ = null;
106
+
107
+ /**
108
+ * Width of the zoom controls.
109
+ * @type {number}
110
+ * @const
111
+ * @private
112
+ */
113
+ this.WIDTH_ = 32;
114
+
115
+ /**
116
+ * Height of each zoom control.
117
+ * @type {number}
118
+ * @const
119
+ * @private
120
+ */
121
+ this.HEIGHT_ = 32;
122
+
123
+ /**
124
+ * Small spacing used between the zoom in and out control, in pixels.
125
+ * @type {number}
126
+ * @const
127
+ * @private
128
+ */
129
+ this.SMALL_SPACING_ = 2;
130
+
131
+ /**
132
+ * Large spacing used between the zoom in and reset control, in pixels.
133
+ * @type {number}
134
+ * @const
135
+ * @private
136
+ */
137
+ this.LARGE_SPACING_ = 11;
138
+
139
+ /**
140
+ * Distance between zoom controls and bottom or top edge of workspace.
141
+ * @type {number}
142
+ * @const
143
+ * @private
144
+ */
145
+ this.MARGIN_VERTICAL_ = 20;
146
+
147
+ /**
148
+ * Distance between zoom controls and right or left edge of workspace.
149
+ * @type {number}
150
+ * @private
151
+ */
152
+ this.MARGIN_HORIZONTAL_ = 20;
153
+
154
+ /**
155
+ * The SVG group containing the zoom controls.
156
+ * @type {SVGElement}
157
+ * @private
158
+ */
159
+ this.svgGroup_ = null;
160
+
161
+ /**
162
+ * Left coordinate of the zoom controls.
163
+ * @type {number}
164
+ * @private
165
+ */
166
+ this.left_ = 0;
167
+
168
+ /**
169
+ * Top coordinate of the zoom controls.
170
+ * @type {number}
171
+ * @private
172
+ */
173
+ this.top_ = 0;
174
+
175
+ /**
176
+ * Whether this has been initialized.
177
+ * @type {boolean}
178
+ * @private
179
+ */
180
+ this.initialized_ = false;
181
+ }
53
182
  /**
54
- * The unique id for this component that is used to register with the
55
- * ComponentManager.
56
- * @type {string}
183
+ * Create the zoom controls.
184
+ * @return {!SVGElement} The zoom controls SVG group.
57
185
  */
58
- this.id = 'zoomControls';
59
-
186
+ createDom() {
187
+ this.svgGroup_ = dom.createSvgElement(Svg.G, {}, null);
188
+
189
+ // Each filter/pattern needs a unique ID for the case of multiple Blockly
190
+ // instances on a page. Browser behaviour becomes undefined otherwise.
191
+ // https://neil.fraser.name/news/2015/11/01/
192
+ const rnd = String(Math.random()).substring(2);
193
+ this.createZoomOutSvg_(rnd);
194
+ this.createZoomInSvg_(rnd);
195
+ if (this.workspace_.isMovable()) {
196
+ // If we zoom to the center and the workspace isn't movable we could
197
+ // loose blocks at the edges of the workspace.
198
+ this.createZoomResetSvg_(rnd);
199
+ }
200
+ return this.svgGroup_;
201
+ }
60
202
  /**
61
- * A handle to use to unbind the mouse down event handler for zoom reset
62
- * button. Opaque data returned from browserEvents.conditionalBind.
63
- * @type {?browserEvents.Data}
64
- * @private
203
+ * Initializes the zoom controls.
65
204
  */
66
- this.onZoomResetWrapper_ = null;
67
-
205
+ init() {
206
+ this.workspace_.getComponentManager().addComponent({
207
+ component: this,
208
+ weight: 2,
209
+ capabilities: [ComponentManager.Capability.POSITIONABLE],
210
+ });
211
+ this.initialized_ = true;
212
+ }
68
213
  /**
69
- * A handle to use to unbind the mouse down event handler for zoom in button.
70
- * Opaque data returned from browserEvents.conditionalBind.
71
- * @type {?browserEvents.Data}
72
- * @private
214
+ * Disposes of this zoom controls.
215
+ * Unlink from all DOM elements to prevent memory leaks.
73
216
  */
74
- this.onZoomInWrapper_ = null;
75
-
217
+ dispose() {
218
+ this.workspace_.getComponentManager().removeComponent('zoomControls');
219
+ if (this.svgGroup_) {
220
+ dom.removeNode(this.svgGroup_);
221
+ }
222
+ if (this.onZoomResetWrapper_) {
223
+ browserEvents.unbind(this.onZoomResetWrapper_);
224
+ }
225
+ if (this.onZoomInWrapper_) {
226
+ browserEvents.unbind(this.onZoomInWrapper_);
227
+ }
228
+ if (this.onZoomOutWrapper_) {
229
+ browserEvents.unbind(this.onZoomOutWrapper_);
230
+ }
231
+ }
76
232
  /**
77
- * A handle to use to unbind the mouse down event handler for zoom out button.
78
- * Opaque data returned from browserEvents.conditionalBind.
79
- * @type {?browserEvents.Data}
80
- * @private
233
+ * Returns the bounding rectangle of the UI element in pixel units relative to
234
+ * the Blockly injection div.
235
+ * @return {?Rect} The UI elements's bounding box. Null if
236
+ * bounding box should be ignored by other UI elements.
81
237
  */
82
- this.onZoomOutWrapper_ = null;
83
-
238
+ getBoundingRectangle() {
239
+ let height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
240
+ if (this.zoomResetGroup_) {
241
+ height += this.LARGE_SPACING_ + this.HEIGHT_;
242
+ }
243
+ const bottom = this.top_ + height;
244
+ const right = this.left_ + this.WIDTH_;
245
+ return new Rect(this.top_, bottom, this.left_, right);
246
+ }
84
247
  /**
85
- * The zoom in svg <g> element.
86
- * @type {SVGGElement}
87
- * @private
248
+ * Positions the zoom controls.
249
+ * It is positioned in the opposite corner to the corner the
250
+ * categories/toolbox starts at.
251
+ * @param {!MetricsManager.UiMetrics} metrics The workspace metrics.
252
+ * @param {!Array<!Rect>} savedPositions List of rectangles that
253
+ * are already on the workspace.
88
254
  */
89
- this.zoomInGroup_ = null;
255
+ position(metrics, savedPositions) {
256
+ // Not yet initialized.
257
+ if (!this.initialized_) {
258
+ return;
259
+ }
90
260
 
261
+ const cornerPosition =
262
+ uiPosition.getCornerOppositeToolbox(this.workspace_, metrics);
263
+ let height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
264
+ if (this.zoomResetGroup_) {
265
+ height += this.LARGE_SPACING_ + this.HEIGHT_;
266
+ }
267
+ const startRect = uiPosition.getStartPositionRect(
268
+ cornerPosition, new Size(this.WIDTH_, height), this.MARGIN_HORIZONTAL_,
269
+ this.MARGIN_VERTICAL_, metrics, this.workspace_);
270
+
271
+ const verticalPosition = cornerPosition.vertical;
272
+ const bumpDirection = verticalPosition === uiPosition.verticalPosition.TOP ?
273
+ uiPosition.bumpDirection.DOWN :
274
+ uiPosition.bumpDirection.UP;
275
+ const positionRect = uiPosition.bumpPositionRect(
276
+ startRect, this.MARGIN_VERTICAL_, bumpDirection, savedPositions);
277
+
278
+ if (verticalPosition === uiPosition.verticalPosition.TOP) {
279
+ const zoomInTranslateY = this.SMALL_SPACING_ + this.HEIGHT_;
280
+ this.zoomInGroup_.setAttribute(
281
+ 'transform', 'translate(0, ' + zoomInTranslateY + ')');
282
+ if (this.zoomResetGroup_) {
283
+ const zoomResetTranslateY =
284
+ zoomInTranslateY + this.LARGE_SPACING_ + this.HEIGHT_;
285
+ this.zoomResetGroup_.setAttribute(
286
+ 'transform', 'translate(0, ' + zoomResetTranslateY + ')');
287
+ }
288
+ } else {
289
+ const zoomInTranslateY =
290
+ this.zoomResetGroup_ ? this.LARGE_SPACING_ + this.HEIGHT_ : 0;
291
+ this.zoomInGroup_.setAttribute(
292
+ 'transform', 'translate(0, ' + zoomInTranslateY + ')');
293
+ const zoomOutTranslateY =
294
+ zoomInTranslateY + this.SMALL_SPACING_ + this.HEIGHT_;
295
+ this.zoomOutGroup_.setAttribute(
296
+ 'transform', 'translate(0, ' + zoomOutTranslateY + ')');
297
+ }
298
+
299
+ this.top_ = positionRect.top;
300
+ this.left_ = positionRect.left;
301
+ this.svgGroup_.setAttribute(
302
+ 'transform', 'translate(' + this.left_ + ',' + this.top_ + ')');
303
+ }
91
304
  /**
92
- * The zoom out svg <g> element.
93
- * @type {SVGGElement}
305
+ * Create the zoom in icon and its event handler.
306
+ * @param {string} rnd The random string to use as a suffix in the clip path's
307
+ * ID. These IDs must be unique in case there are multiple Blockly
308
+ * instances on the same page.
94
309
  * @private
95
310
  */
96
- this.zoomOutGroup_ = null;
97
-
311
+ createZoomOutSvg_(rnd) {
312
+ /* This markup will be generated and added to the .svgGroup_:
313
+ <g class="blocklyZoom">
314
+ <clipPath id="blocklyZoomoutClipPath837493">
315
+ <rect width="32" height="32></rect>
316
+ </clipPath>
317
+ <image width="96" height="124" x="-64" y="-92"
318
+ xlink:href="media/sprites.png"
319
+ clip-path="url(#blocklyZoomoutClipPath837493)"></image>
320
+ </g>
321
+ */
322
+ this.zoomOutGroup_ =
323
+ dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
324
+ const clip = dom.createSvgElement(
325
+ Svg.CLIPPATH, {'id': 'blocklyZoomoutClipPath' + rnd},
326
+ this.zoomOutGroup_);
327
+ dom.createSvgElement(
328
+ Svg.RECT, {
329
+ 'width': 32,
330
+ 'height': 32,
331
+ },
332
+ clip);
333
+ const zoomoutSvg = dom.createSvgElement(
334
+ Svg.IMAGE, {
335
+ 'width': SPRITE.width,
336
+ 'height': SPRITE.height,
337
+ 'x': -64,
338
+ 'y': -92,
339
+ 'clip-path': 'url(#blocklyZoomoutClipPath' + rnd + ')',
340
+ },
341
+ this.zoomOutGroup_);
342
+ zoomoutSvg.setAttributeNS(
343
+ dom.XLINK_NS, 'xlink:href',
344
+ this.workspace_.options.pathToMedia + SPRITE.url);
345
+
346
+ // Attach listener.
347
+ this.onZoomOutWrapper_ = browserEvents.conditionalBind(
348
+ this.zoomOutGroup_, 'mousedown', null, this.zoom_.bind(this, -1));
349
+ }
98
350
  /**
99
- * The zoom reset svg <g> element.
100
- * @type {SVGGElement}
351
+ * Create the zoom out icon and its event handler.
352
+ * @param {string} rnd The random string to use as a suffix in the clip path's
353
+ * ID. These IDs must be unique in case there are multiple Blockly
354
+ * instances on the same page.
101
355
  * @private
102
356
  */
103
- this.zoomResetGroup_ = null;
104
- };
105
-
106
- /**
107
- * Width of the zoom controls.
108
- * @type {number}
109
- * @const
110
- * @private
111
- */
112
- ZoomControls.prototype.WIDTH_ = 32;
113
-
114
- /**
115
- * Height of each zoom control.
116
- * @type {number}
117
- * @const
118
- * @private
119
- */
120
- ZoomControls.prototype.HEIGHT_ = 32;
121
-
122
- /**
123
- * Small spacing used between the zoom in and out control, in pixels.
124
- * @type {number}
125
- * @const
126
- * @private
127
- */
128
- ZoomControls.prototype.SMALL_SPACING_ = 2;
129
-
130
- /**
131
- * Large spacing used between the zoom in and reset control, in pixels.
132
- * @type {number}
133
- * @const
134
- * @private
135
- */
136
- ZoomControls.prototype.LARGE_SPACING_ = 11;
137
-
138
- /**
139
- * Distance between zoom controls and bottom or top edge of workspace.
140
- * @type {number}
141
- * @const
142
- * @private
143
- */
144
- ZoomControls.prototype.MARGIN_VERTICAL_ = 20;
145
-
146
- /**
147
- * Distance between zoom controls and right or left edge of workspace.
148
- * @type {number}
149
- * @private
150
- */
151
- ZoomControls.prototype.MARGIN_HORIZONTAL_ = 20;
152
-
153
- /**
154
- * The SVG group containing the zoom controls.
155
- * @type {SVGElement}
156
- * @private
157
- */
158
- ZoomControls.prototype.svgGroup_ = null;
159
-
160
- /**
161
- * Left coordinate of the zoom controls.
162
- * @type {number}
163
- * @private
164
- */
165
- ZoomControls.prototype.left_ = 0;
166
-
167
- /**
168
- * Top coordinate of the zoom controls.
169
- * @type {number}
170
- * @private
171
- */
172
- ZoomControls.prototype.top_ = 0;
173
-
174
- /**
175
- * Whether this has been initialized.
176
- * @type {boolean}
177
- * @private
178
- */
179
- ZoomControls.prototype.initialized_ = false;
180
-
181
- /**
182
- * Create the zoom controls.
183
- * @return {!SVGElement} The zoom controls SVG group.
184
- */
185
- ZoomControls.prototype.createDom = function() {
186
- this.svgGroup_ = dom.createSvgElement(Svg.G, {}, null);
187
-
188
- // Each filter/pattern needs a unique ID for the case of multiple Blockly
189
- // instances on a page. Browser behaviour becomes undefined otherwise.
190
- // https://neil.fraser.name/news/2015/11/01/
191
- const rnd = String(Math.random()).substring(2);
192
- this.createZoomOutSvg_(rnd);
193
- this.createZoomInSvg_(rnd);
194
- if (this.workspace_.isMovable()) {
195
- // If we zoom to the center and the workspace isn't movable we could
196
- // loose blocks at the edges of the workspace.
197
- this.createZoomResetSvg_(rnd);
357
+ createZoomInSvg_(rnd) {
358
+ /* This markup will be generated and added to the .svgGroup_:
359
+ <g class="blocklyZoom">
360
+ <clipPath id="blocklyZoominClipPath837493">
361
+ <rect width="32" height="32"></rect>
362
+ </clipPath>
363
+ <image width="96" height="124" x="-32" y="-92"
364
+ xlink:href="media/sprites.png"
365
+ clip-path="url(#blocklyZoominClipPath837493)"></image>
366
+ </g>
367
+ */
368
+ this.zoomInGroup_ =
369
+ dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
370
+ const clip = dom.createSvgElement(
371
+ Svg.CLIPPATH, {'id': 'blocklyZoominClipPath' + rnd}, this.zoomInGroup_);
372
+ dom.createSvgElement(
373
+ Svg.RECT, {
374
+ 'width': 32,
375
+ 'height': 32,
376
+ },
377
+ clip);
378
+ const zoominSvg = dom.createSvgElement(
379
+ Svg.IMAGE, {
380
+ 'width': SPRITE.width,
381
+ 'height': SPRITE.height,
382
+ 'x': -32,
383
+ 'y': -92,
384
+ 'clip-path': 'url(#blocklyZoominClipPath' + rnd + ')',
385
+ },
386
+ this.zoomInGroup_);
387
+ zoominSvg.setAttributeNS(
388
+ dom.XLINK_NS, 'xlink:href',
389
+ this.workspace_.options.pathToMedia + SPRITE.url);
390
+
391
+ // Attach listener.
392
+ this.onZoomInWrapper_ = browserEvents.conditionalBind(
393
+ this.zoomInGroup_, 'mousedown', null, this.zoom_.bind(this, 1));
198
394
  }
199
- return this.svgGroup_;
200
- };
201
-
202
- /**
203
- * Initializes the zoom controls.
204
- */
205
- ZoomControls.prototype.init = function() {
206
- this.workspace_.getComponentManager().addComponent({
207
- component: this,
208
- weight: 2,
209
- capabilities: [ComponentManager.Capability.POSITIONABLE],
210
- });
211
- this.initialized_ = true;
212
- };
213
-
214
- /**
215
- * Disposes of this zoom controls.
216
- * Unlink from all DOM elements to prevent memory leaks.
217
- */
218
- ZoomControls.prototype.dispose = function() {
219
- this.workspace_.getComponentManager().removeComponent('zoomControls');
220
- if (this.svgGroup_) {
221
- dom.removeNode(this.svgGroup_);
222
- }
223
- if (this.onZoomResetWrapper_) {
224
- browserEvents.unbind(this.onZoomResetWrapper_);
225
- }
226
- if (this.onZoomInWrapper_) {
227
- browserEvents.unbind(this.onZoomInWrapper_);
228
- }
229
- if (this.onZoomOutWrapper_) {
230
- browserEvents.unbind(this.onZoomOutWrapper_);
231
- }
232
- };
233
-
234
- /**
235
- * Returns the bounding rectangle of the UI element in pixel units relative to
236
- * the Blockly injection div.
237
- * @return {?Rect} The UI elements's bounding box. Null if
238
- * bounding box should be ignored by other UI elements.
239
- */
240
- ZoomControls.prototype.getBoundingRectangle = function() {
241
- let height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
242
- if (this.zoomResetGroup_) {
243
- height += this.LARGE_SPACING_ + this.HEIGHT_;
395
+ /**
396
+ * Handles a mouse down event on the zoom in or zoom out buttons on the
397
+ * workspace.
398
+ * @param {number} amount Amount of zooming. Negative amount values zoom out,
399
+ * and positive amount values zoom in.
400
+ * @param {!Event} e A mouse down event.
401
+ * @private
402
+ */
403
+ zoom_(amount, e) {
404
+ this.workspace_.markFocused();
405
+ this.workspace_.zoomCenter(amount);
406
+ this.fireZoomEvent_();
407
+ Touch.clearTouchIdentifier(); // Don't block future drags.
408
+ e.stopPropagation(); // Don't start a workspace scroll.
409
+ e.preventDefault(); // Stop double-clicking from selecting text.
244
410
  }
245
- const bottom = this.top_ + height;
246
- const right = this.left_ + this.WIDTH_;
247
- return new Rect(this.top_, bottom, this.left_, right);
248
- };
249
-
250
-
251
- /**
252
- * Positions the zoom controls.
253
- * It is positioned in the opposite corner to the corner the
254
- * categories/toolbox starts at.
255
- * @param {!MetricsManager.UiMetrics} metrics The workspace metrics.
256
- * @param {!Array<!Rect>} savedPositions List of rectangles that
257
- * are already on the workspace.
258
- */
259
- ZoomControls.prototype.position = function(metrics, savedPositions) {
260
- // Not yet initialized.
261
- if (!this.initialized_) {
262
- return;
411
+ /**
412
+ * Create the zoom reset icon and its event handler.
413
+ * @param {string} rnd The random string to use as a suffix in the clip path's
414
+ * ID. These IDs must be unique in case there are multiple Blockly
415
+ * instances on the same page.
416
+ * @private
417
+ */
418
+ createZoomResetSvg_(rnd) {
419
+ /* This markup will be generated and added to the .svgGroup_:
420
+ <g class="blocklyZoom">
421
+ <clipPath id="blocklyZoomresetClipPath837493">
422
+ <rect width="32" height="32"></rect>
423
+ </clipPath>
424
+ <image width="96" height="124" x="-32" y="-92"
425
+ xlink:href="media/sprites.png"
426
+ clip-path="url(#blocklyZoomresetClipPath837493)"></image>
427
+ </g>
428
+ */
429
+ this.zoomResetGroup_ =
430
+ dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
431
+ const clip = dom.createSvgElement(
432
+ Svg.CLIPPATH, {'id': 'blocklyZoomresetClipPath' + rnd},
433
+ this.zoomResetGroup_);
434
+ dom.createSvgElement(Svg.RECT, {'width': 32, 'height': 32}, clip);
435
+ const zoomresetSvg = dom.createSvgElement(
436
+ Svg.IMAGE, {
437
+ 'width': SPRITE.width,
438
+ 'height': SPRITE.height,
439
+ 'y': -92,
440
+ 'clip-path': 'url(#blocklyZoomresetClipPath' + rnd + ')',
441
+ },
442
+ this.zoomResetGroup_);
443
+ zoomresetSvg.setAttributeNS(
444
+ dom.XLINK_NS, 'xlink:href',
445
+ this.workspace_.options.pathToMedia + SPRITE.url);
446
+
447
+ // Attach event listeners.
448
+ this.onZoomResetWrapper_ = browserEvents.conditionalBind(
449
+ this.zoomResetGroup_, 'mousedown', null, this.resetZoom_.bind(this));
263
450
  }
264
-
265
- const cornerPosition =
266
- uiPosition.getCornerOppositeToolbox(this.workspace_, metrics);
267
- let height = this.SMALL_SPACING_ + 2 * this.HEIGHT_;
268
- if (this.zoomResetGroup_) {
269
- height += this.LARGE_SPACING_ + this.HEIGHT_;
451
+ /**
452
+ * Handles a mouse down event on the reset zoom button on the workspace.
453
+ * @param {!Event} e A mouse down event.
454
+ * @private
455
+ */
456
+ resetZoom_(e) {
457
+ this.workspace_.markFocused();
458
+
459
+ // zoom is passed amount and computes the new scale using the formula:
460
+ // targetScale = currentScale * Math.pow(speed, amount)
461
+ const targetScale = this.workspace_.options.zoomOptions.startScale;
462
+ const currentScale = this.workspace_.scale;
463
+ const speed = this.workspace_.options.zoomOptions.scaleSpeed;
464
+ // To compute amount:
465
+ // amount = log(speed, (targetScale / currentScale))
466
+ // Math.log computes natural logarithm (ln), to change the base, use
467
+ // formula: log(base, value) = ln(value) / ln(base)
468
+ const amount = Math.log(targetScale / currentScale) / Math.log(speed);
469
+ this.workspace_.beginCanvasTransition();
470
+ this.workspace_.zoomCenter(amount);
471
+ this.workspace_.scrollCenter();
472
+
473
+ setTimeout(this.workspace_.endCanvasTransition.bind(this.workspace_), 500);
474
+ this.fireZoomEvent_();
475
+ Touch.clearTouchIdentifier(); // Don't block future drags.
476
+ e.stopPropagation(); // Don't start a workspace scroll.
477
+ e.preventDefault(); // Stop double-clicking from selecting text.
270
478
  }
271
- const startRect = uiPosition.getStartPositionRect(
272
- cornerPosition, new Size(this.WIDTH_, height), this.MARGIN_HORIZONTAL_,
273
- this.MARGIN_VERTICAL_, metrics, this.workspace_);
274
-
275
- const verticalPosition = cornerPosition.vertical;
276
- const bumpDirection = verticalPosition === uiPosition.verticalPosition.TOP ?
277
- uiPosition.bumpDirection.DOWN :
278
- uiPosition.bumpDirection.UP;
279
- const positionRect = uiPosition.bumpPositionRect(
280
- startRect, this.MARGIN_VERTICAL_, bumpDirection, savedPositions);
281
-
282
- if (verticalPosition === uiPosition.verticalPosition.TOP) {
283
- const zoomInTranslateY = this.SMALL_SPACING_ + this.HEIGHT_;
284
- this.zoomInGroup_.setAttribute(
285
- 'transform', 'translate(0, ' + zoomInTranslateY + ')');
286
- if (this.zoomResetGroup_) {
287
- const zoomResetTranslateY =
288
- zoomInTranslateY + this.LARGE_SPACING_ + this.HEIGHT_;
289
- this.zoomResetGroup_.setAttribute(
290
- 'transform', 'translate(0, ' + zoomResetTranslateY + ')');
291
- }
292
- } else {
293
- const zoomInTranslateY =
294
- this.zoomResetGroup_ ? this.LARGE_SPACING_ + this.HEIGHT_ : 0;
295
- this.zoomInGroup_.setAttribute(
296
- 'transform', 'translate(0, ' + zoomInTranslateY + ')');
297
- const zoomOutTranslateY =
298
- zoomInTranslateY + this.SMALL_SPACING_ + this.HEIGHT_;
299
- this.zoomOutGroup_.setAttribute(
300
- 'transform', 'translate(0, ' + zoomOutTranslateY + ')');
479
+ /**
480
+ * Fires a zoom control UI event.
481
+ * @private
482
+ */
483
+ fireZoomEvent_() {
484
+ const uiEvent = new (eventUtils.get(eventUtils.CLICK))(
485
+ null, this.workspace_.id, 'zoom_controls');
486
+ eventUtils.fire(uiEvent);
301
487
  }
302
-
303
- this.top_ = positionRect.top;
304
- this.left_ = positionRect.left;
305
- this.svgGroup_.setAttribute(
306
- 'transform', 'translate(' + this.left_ + ',' + this.top_ + ')');
307
- };
308
-
309
- /**
310
- * Create the zoom in icon and its event handler.
311
- * @param {string} rnd The random string to use as a suffix in the clip path's
312
- * ID. These IDs must be unique in case there are multiple Blockly
313
- * instances on the same page.
314
- * @private
315
- */
316
- ZoomControls.prototype.createZoomOutSvg_ = function(rnd) {
317
- /* This markup will be generated and added to the .svgGroup_:
318
- <g class="blocklyZoom">
319
- <clipPath id="blocklyZoomoutClipPath837493">
320
- <rect width="32" height="32></rect>
321
- </clipPath>
322
- <image width="96" height="124" x="-64" y="-92"
323
- xlink:href="media/sprites.png"
324
- clip-path="url(#blocklyZoomoutClipPath837493)"></image>
325
- </g>
326
- */
327
- this.zoomOutGroup_ =
328
- dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
329
- const clip = dom.createSvgElement(
330
- Svg.CLIPPATH, {'id': 'blocklyZoomoutClipPath' + rnd}, this.zoomOutGroup_);
331
- dom.createSvgElement(
332
- Svg.RECT, {
333
- 'width': 32,
334
- 'height': 32,
335
- },
336
- clip);
337
- const zoomoutSvg = dom.createSvgElement(
338
- Svg.IMAGE, {
339
- 'width': internalConstants.SPRITE.width,
340
- 'height': internalConstants.SPRITE.height,
341
- 'x': -64,
342
- 'y': -92,
343
- 'clip-path': 'url(#blocklyZoomoutClipPath' + rnd + ')',
344
- },
345
- this.zoomOutGroup_);
346
- zoomoutSvg.setAttributeNS(
347
- dom.XLINK_NS, 'xlink:href',
348
- this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
349
-
350
- // Attach listener.
351
- this.onZoomOutWrapper_ = browserEvents.conditionalBind(
352
- this.zoomOutGroup_, 'mousedown', null, this.zoom_.bind(this, -1));
353
- };
354
-
355
- /**
356
- * Create the zoom out icon and its event handler.
357
- * @param {string} rnd The random string to use as a suffix in the clip path's
358
- * ID. These IDs must be unique in case there are multiple Blockly
359
- * instances on the same page.
360
- * @private
361
- */
362
- ZoomControls.prototype.createZoomInSvg_ = function(rnd) {
363
- /* This markup will be generated and added to the .svgGroup_:
364
- <g class="blocklyZoom">
365
- <clipPath id="blocklyZoominClipPath837493">
366
- <rect width="32" height="32"></rect>
367
- </clipPath>
368
- <image width="96" height="124" x="-32" y="-92"
369
- xlink:href="media/sprites.png"
370
- clip-path="url(#blocklyZoominClipPath837493)"></image>
371
- </g>
372
- */
373
- this.zoomInGroup_ =
374
- dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
375
- const clip = dom.createSvgElement(
376
- Svg.CLIPPATH, {'id': 'blocklyZoominClipPath' + rnd}, this.zoomInGroup_);
377
- dom.createSvgElement(
378
- Svg.RECT, {
379
- 'width': 32,
380
- 'height': 32,
381
- },
382
- clip);
383
- const zoominSvg = dom.createSvgElement(
384
- Svg.IMAGE, {
385
- 'width': internalConstants.SPRITE.width,
386
- 'height': internalConstants.SPRITE.height,
387
- 'x': -32,
388
- 'y': -92,
389
- 'clip-path': 'url(#blocklyZoominClipPath' + rnd + ')',
390
- },
391
- this.zoomInGroup_);
392
- zoominSvg.setAttributeNS(
393
- dom.XLINK_NS, 'xlink:href',
394
- this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
395
-
396
- // Attach listener.
397
- this.onZoomInWrapper_ = browserEvents.conditionalBind(
398
- this.zoomInGroup_, 'mousedown', null, this.zoom_.bind(this, 1));
399
- };
400
-
401
- /**
402
- * Handles a mouse down event on the zoom in or zoom out buttons on the
403
- * workspace.
404
- * @param {number} amount Amount of zooming. Negative amount values zoom out,
405
- * and positive amount values zoom in.
406
- * @param {!Event} e A mouse down event.
407
- * @private
408
- */
409
- ZoomControls.prototype.zoom_ = function(amount, e) {
410
- this.workspace_.markFocused();
411
- this.workspace_.zoomCenter(amount);
412
- this.fireZoomEvent_();
413
- Touch.clearTouchIdentifier(); // Don't block future drags.
414
- e.stopPropagation(); // Don't start a workspace scroll.
415
- e.preventDefault(); // Stop double-clicking from selecting text.
416
- };
417
-
418
- /**
419
- * Create the zoom reset icon and its event handler.
420
- * @param {string} rnd The random string to use as a suffix in the clip path's
421
- * ID. These IDs must be unique in case there are multiple Blockly
422
- * instances on the same page.
423
- * @private
424
- */
425
- ZoomControls.prototype.createZoomResetSvg_ = function(rnd) {
426
- /* This markup will be generated and added to the .svgGroup_:
427
- <g class="blocklyZoom">
428
- <clipPath id="blocklyZoomresetClipPath837493">
429
- <rect width="32" height="32"></rect>
430
- </clipPath>
431
- <image width="96" height="124" x="-32" y="-92"
432
- xlink:href="media/sprites.png"
433
- clip-path="url(#blocklyZoomresetClipPath837493)"></image>
434
- </g>
435
- */
436
- this.zoomResetGroup_ =
437
- dom.createSvgElement(Svg.G, {'class': 'blocklyZoom'}, this.svgGroup_);
438
- const clip = dom.createSvgElement(
439
- Svg.CLIPPATH, {'id': 'blocklyZoomresetClipPath' + rnd},
440
- this.zoomResetGroup_);
441
- dom.createSvgElement(Svg.RECT, {'width': 32, 'height': 32}, clip);
442
- const zoomresetSvg = dom.createSvgElement(
443
- Svg.IMAGE, {
444
- 'width': internalConstants.SPRITE.width,
445
- 'height': internalConstants.SPRITE.height,
446
- 'y': -92,
447
- 'clip-path': 'url(#blocklyZoomresetClipPath' + rnd + ')',
448
- },
449
- this.zoomResetGroup_);
450
- zoomresetSvg.setAttributeNS(
451
- dom.XLINK_NS, 'xlink:href',
452
- this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
453
-
454
- // Attach event listeners.
455
- this.onZoomResetWrapper_ = browserEvents.conditionalBind(
456
- this.zoomResetGroup_, 'mousedown', null, this.resetZoom_.bind(this));
457
- };
458
-
459
- /**
460
- * Handles a mouse down event on the reset zoom button on the workspace.
461
- * @param {!Event} e A mouse down event.
462
- * @private
463
- */
464
- ZoomControls.prototype.resetZoom_ = function(e) {
465
- this.workspace_.markFocused();
466
-
467
- // zoom is passed amount and computes the new scale using the formula:
468
- // targetScale = currentScale * Math.pow(speed, amount)
469
- const targetScale = this.workspace_.options.zoomOptions.startScale;
470
- const currentScale = this.workspace_.scale;
471
- const speed = this.workspace_.options.zoomOptions.scaleSpeed;
472
- // To compute amount:
473
- // amount = log(speed, (targetScale / currentScale))
474
- // Math.log computes natural logarithm (ln), to change the base, use formula:
475
- // log(base, value) = ln(value) / ln(base)
476
- const amount = Math.log(targetScale / currentScale) / Math.log(speed);
477
- this.workspace_.beginCanvasTransition();
478
- this.workspace_.zoomCenter(amount);
479
- this.workspace_.scrollCenter();
480
-
481
- setTimeout(this.workspace_.endCanvasTransition.bind(this.workspace_), 500);
482
- this.fireZoomEvent_();
483
- Touch.clearTouchIdentifier(); // Don't block future drags.
484
- e.stopPropagation(); // Don't start a workspace scroll.
485
- e.preventDefault(); // Stop double-clicking from selecting text.
486
- };
487
-
488
- /**
489
- * Fires a zoom control UI event.
490
- * @private
491
- */
492
- ZoomControls.prototype.fireZoomEvent_ = function() {
493
- const uiEvent = new (eventUtils.get(eventUtils.CLICK))(
494
- null, this.workspace_.id, 'zoom_controls');
495
- eventUtils.fire(uiEvent);
496
- };
488
+ }
497
489
 
498
490
  /**
499
491
  * CSS for zoom controls. See css.js for use.
500
492
  */
501
493
  Css.register(`
502
- .blocklyZoom>image, .blocklyZoom>svg>image {
503
- opacity: .4;
504
- }
494
+ .blocklyZoom>image, .blocklyZoom>svg>image {
495
+ opacity: .4;
496
+ }
505
497
 
506
- .blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {
507
- opacity: .6;
508
- }
498
+ .blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {
499
+ opacity: .6;
500
+ }
509
501
 
510
- .blocklyZoom>image:active, .blocklyZoom>svg>image:active {
511
- opacity: .8;
512
- }
502
+ .blocklyZoom>image:active, .blocklyZoom>svg>image:active {
503
+ opacity: .8;
504
+ }
513
505
  `);
514
506
 
515
507
  exports.ZoomControls = ZoomControls;