-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathminimap.js
272 lines (236 loc) · 10.1 KB
/
minimap.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/**
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview JavaScript for Blockly's Minimap demo.
* @author [email protected] (Karan Purohit)
* @author [email protected] update code (Sébastien Canet)
*/
'use strict';
/**
* Creating a seperate namespace for minimap.
*/
var Minimap = {};
/**
* Initialize the workspace and minimap.
* @param {!Workspace} workspace The main workspace of the user.
* @param {!Workspace} minimap The workspace that will be used as a minimap.
*/
Minimap.init = function (workspace, minimap) {
this.workspace = workspace;
this.minimap = minimap;
//Adding scroll callback functionlity to vScroll and hScroll just for this demo.
//IMPORTANT: This should be changed when there is proper UI event handling
// api available and should be handled by workspace's event listeners.
this.workspace.scrollbar.vScroll.setHandlePosition = function (newPosition) {
// Code below is same as the original setHandlePosition function in core/scrollbar.js.
this.handlePosition_ = newPosition;
this.svgHandle_.setAttribute(this.positionAttribute_, this.handlePosition_);
// New code starts from here.
// Get the absolutePosition.
var absolutePosition = (this.handlePosition_ / this.ratio);
// Firing the scroll change listener.
Minimap.onScrollChange(absolutePosition, this.horizontal_);
};
// Adding call back for horizontal scroll.
this.workspace.scrollbar.hScroll.setHandlePosition = function (newPosition) {
// Code below is same as the original setHandlePosition function in core/scrollbar.js.
this.handlePosition_ = newPosition;
this.svgHandle_.setAttribute(this.positionAttribute_, this.handlePosition_);
// New code starts from here.
// Get the absolutePosition.
var absolutePosition = (this.handlePosition_ / this.ratio);
// Firing the scroll change listener.
Minimap.onScrollChange(absolutePosition, this.horizontal_);
};
// Required to stop a positive feedback loop when user clicks minimap
// and the scroll changes, which inturn may change minimap.
this.disableScrollChange = false;
// Listen to events on the main workspace.
this.workspace.addChangeListener(Minimap.mirrorEvent);
//Get rectangle bounding the minimap div.
this.rect = document.getElementById('mapDiv').getBoundingClientRect();
// Create a svg overlay on the top of mapDiv for the minimap.
this.svg = Blockly.utils.dom.createSvgElement('svg', {
'xmlns': Blockly.utils.dom.SVG_NS,
'xmlns:html': Blockly.utils.dom.HTML_NS,
'xmlns:xlink': Blockly.utils.dom.XLINK_NS,
'version': '1.1',
'height': this.rect.bottom - this.rect.top,
'width': this.rect.right - this.rect.left,
'class': 'minimap',
}, document.getElementById('mapDiv'));
this.svg.style.top = this.rect.top + 'px';
this.svg.style.left = this.rect.left + 'px';
// Creating a rectangle in the minimap that represents current view.
Blockly.utils.dom.createSvgElement('rect', {
'width': 50,
'height': 50,
'class': 'mapDragger'
}, this.svg);
// Rectangle in the minimap that represents current view.
this.mapDragger = this.svg.childNodes[0];
// Adding mouse events to the rectangle, to make it Draggable.
// Using Blockly.bindEvent_ to attach mouse/touch listeners.
Blockly.bindEvent_(this.mapDragger, "mousedown", null, Minimap.mousedown);
//When the window change, we need to resize the minimap window.
window.addEventListener('resize', Minimap.repositionMinimap);
// Mouse up event for the minimap.
this.svg.addEventListener('mouseup', Minimap.updateMapDragger);
//Boolen to check whether I am dragging the surface or not.
this.isDragging = false;
};
Minimap.mousedown = function (e) {
// Using Blockly.bindEvent_ to attach mouse/touch listeners.
Minimap.mouseMoveBindData = Blockly.bindEvent_(document, "mousemove", null, Minimap.mousemove);
Minimap.mouseUpBindData = Blockly.bindEvent_(document, "mouseup", null, Minimap.mouseup);
Minimap.isDragging = true;
e.stopPropagation();
};
Minimap.mouseup = function (e) {
Minimap.isDragging = false;
// Removing listeners.
Blockly.unbindEvent_(Minimap.mouseUpBindData);
Blockly.unbindEvent_(Minimap.mouseMoveBindData);
Minimap.updateMapDragger(e);
e.stopPropagation();
};
Minimap.mousemove = function (e) {
if (Minimap.isDragging) {
Minimap.updateMapDragger(e);
e.stopPropagation();
}
};
/**
* Run non-UI events from the main workspace on the minimap.
* @param {!Blockly.Events.Abstract} event Event that triggered in the main
* workspace.
*/
Minimap.mirrorEvent = function (event) {
if (event instanceof Blockly.Events.Ui) {
return; // Don't mirror UI events.
}
// Convert event to JSON. This could then be transmitted across the net.
var json = event.toJson();
// Convert JSON back into an event, then execute it.
var minimapEvent = Blockly.Events.fromJson(json, Minimap.minimap);
minimapEvent.run(true);
Minimap.scaleMinimap();
Minimap.setDraggerHeight();
Minimap.setDraggerWidth();
};
/**
* Called when window is resized. Repositions the minimap overlay.
*/
Minimap.repositionMinimap = function () {
Minimap.rect = document.getElementById('mapDiv').getBoundingClientRect();
Minimap.svg.style.top = Minimap.rect.top + 'px';
Minimap.svg.style.left = Minimap.rect.left + 'px';
};
/**
* Updates the rectangle's height.
*/
Minimap.setDraggerHeight = function () {
var workspaceMetrics = Minimap.workspace.getMetrics();
var draggerHeight = workspaceMetrics.viewHeight * Minimap.minimap.scale / Minimap.workspace.scale;
// It's zero when first block is placed.
if (draggerHeight != 0) {
Minimap.mapDragger.setAttribute("height", draggerHeight);
}
};
/**
* Updates the rectangle's width.
*/
Minimap.setDraggerWidth = function () {
var workspaceMetrics = Minimap.workspace.getMetrics();
var draggerWidth = workspaceMetrics.viewWidth * Minimap.minimap.scale / Minimap.workspace.scale;
// It's zero when first block is placed.
if (draggerWidth != 0) {
Minimap.mapDragger.setAttribute("width", draggerWidth);
}
};
/**
* Updates the overall position of the viewport of the minimap by appropriately
* using translate functions.
*/
Minimap.scaleMinimap = function () {
var minimapBoundingBox = Minimap.minimap.getBlocksBoundingBox();
var workspaceBoundingBox = Minimap.workspace.getBlocksBoundingBox();
var workspaceMetrics = Minimap.workspace.getMetrics();
var minimapMetrics = Minimap.minimap.getMetrics();
minimapBoundingBox.width = Math.abs(minimapBoundingBox.left - minimapBoundingBox.right);
minimapBoundingBox.height = Math.abs(minimapBoundingBox.top - minimapBoundingBox.bottom);
// Scaling the mimimap such that all the blocks can be seen in the viewport.
// This padding is default because this is how to scrollbar(in main workspace)
// is implemented.
var topPadding = (workspaceMetrics.viewHeight) * Minimap.minimap.scale / (2 * Minimap.workspace.scale);
var sidePadding = (workspaceMetrics.viewWidth) * Minimap.minimap.scale / (2 * Minimap.workspace.scale);
// If actual padding is more than half view ports height,
// change it to actual padding.
if ((workspaceBoundingBox.y * Minimap.workspace.scale - workspaceMetrics.contentTop)
* Minimap.minimap.scale / Minimap.workspace.scale > topPadding) {
topPadding = (workspaceBoundingBox.y * Minimap.workspace.scale - workspaceMetrics.contentTop)
* Minimap.minimap.scale / Minimap.workspace.scale;
}
// If actual padding is more than half view ports width,
// change it to actual padding.
if ((workspaceBoundingBox.x * Minimap.workspace.scale - workspaceMetrics.contentLeft)
* Minimap.minimap.scale / Minimap.workspace.scale > sidePadding) {
sidePadding = (workspaceBoundingBox.x * Minimap.workspace.scale - workspaceMetrics.contentLeft)
* Minimap.minimap.scale / Minimap.workspace.scale;
}
var scalex = (minimapMetrics.viewWidth - 2 * sidePadding) / minimapBoundingBox.width;
var scaley = (minimapMetrics.viewHeight - 2 * topPadding) / minimapBoundingBox.height;
Minimap.minimap.setScale(Math.min(scalex, scaley));
// Translating the minimap.
Minimap.minimap.translate( - minimapMetrics.contentLeft * Minimap.minimap.scale + sidePadding,
- minimapMetrics.contentTop * Minimap.minimap.scale + topPadding);
};
/**
* Handles the onclick event on the minimapBoundingBox.
* Changes mapDraggers position.
* @param {!Event} e Event from the mouse click.
*/
Minimap.updateMapDragger = function (e) {
var y = e.clientY;
var x = e.clientX;
var draggerHeight = Minimap.mapDragger.getAttribute("height");
var draggerWidth = Minimap.mapDragger.getAttribute("width");
var finalY = y - Minimap.rect.top - (draggerHeight / 2);
var finalX = x - Minimap.rect.left - (draggerWidth / 2);
var maxValidY = (Minimap.workspace.getMetrics().contentHeight - Minimap.workspace.getMetrics().viewHeight)
* Minimap.minimap.scale;
var maxValidX = (Minimap.workspace.getMetrics().contentWidth - Minimap.workspace.getMetrics().viewWidth)
* Minimap.minimap.scale;
if (y + (draggerHeight / 2) > Minimap.rect.bottom) {
finalY = Minimap.rect.bottom - Minimap.rect.top - draggerHeight;
} else if (y < Minimap.rect.top + (draggerHeight / 2)) {
finalY = 0;
}
if (x + (draggerWidth / 2) > Minimap.rect.right) {
finalX = Minimap.rect.right - Minimap.rect.left - draggerWidth;
} else if (x < Minimap.rect.left + (draggerWidth / 2)) {
finalX = 0;
}
Minimap.mapDragger.setAttribute("y", finalY);
Minimap.mapDragger.setAttribute("x", finalX);
// Required, otherwise creates a feedback loop.
Minimap.disableScrollChange = true;
Minimap.workspace.scrollbar.vScroll.set(finalY * Minimap.workspace.scale / Minimap.minimap.scale);
Minimap.workspace.scrollbar.hScroll.set(finalX * Minimap.workspace.scale / Minimap.minimap.scale);
Minimap.disableScrollChange = false;
};
/**
* Handles the onclick event on the minimapBoundingBox, paramaters are passed by
* the event handler.
* @param {Float} position This is the absolute postion of the scrollbar.
* @param {boolean} horizontal Informs if the change event if for horizontal(true)
* scrollbar or vertical(false) scrollbar.
*/
Minimap.onScrollChange = function (position, horizontal) {
if (!Minimap.disableScrollChange) {
Minimap.mapDragger.setAttribute(horizontal ? 'x' : 'y',
position * Minimap.minimap.scale / Minimap.workspace.scale);
}
};