1013 lines
30 KiB
JavaScript
1013 lines
30 KiB
JavaScript
/* -*- js-indent-level: 8; fill-column: 100 -*- */
|
|
|
|
/*
|
|
* Copyright the Collabora Online contributors.
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
/*
|
|
* L.DebugManager contains debugging tools and support for toggling them.
|
|
* Open the Debug Menu with
|
|
* - Ctrl+Shift+Alt+D (Map.Keyboard.js _handleCtrlCommand)
|
|
* - Help > About > D (Toolbar.js aboutDialogKeyHandler)
|
|
* - Help > About > Triple Click (Toolbar.js aboutDialogClickHandler)
|
|
* - &debug=true URL parameter (Map.js initialize)
|
|
* - &randomUser=true URL parameter (global.js)
|
|
*/
|
|
|
|
/* global app L _ */
|
|
|
|
L.DebugManager = L.Class.extend({
|
|
initialize: function(map) {
|
|
this._map = map;
|
|
this.debugOn = false;
|
|
this.debugNeverStarted = true;
|
|
},
|
|
|
|
toggle: function() {
|
|
if (!this.debugOn) {
|
|
this._start();
|
|
} else {
|
|
this._stop();
|
|
}
|
|
|
|
// redraw canvas with changed debug overlays
|
|
this._painter.update();
|
|
},
|
|
|
|
_start: function() {
|
|
this._docLayer = this._map._docLayer;
|
|
this._painter = this._map._docLayer._painter;
|
|
|
|
this.debugOn = true;
|
|
this.debugNeverStarted = false;
|
|
|
|
this._controls = {};
|
|
// Add header
|
|
this._controls['header'] = L.control.layers({}, {}, {collapsed: false}).addTo(this._map);
|
|
var b = document.createElement('b');
|
|
b.append(_('Debug Tools'));
|
|
this._controls['header']._container.prepend(b);
|
|
this._controls['header']._container.append(_('Ctrl+Shift+Alt+D to exit'));
|
|
|
|
this._toolLayers = [];
|
|
this._addDebugTools();
|
|
|
|
// Initialize here because tasks can add themselves to the queue even
|
|
// if the user is not active
|
|
this._automatedUserQueue = [];
|
|
this._automatedUserTasks = {};
|
|
},
|
|
|
|
_stop: function() {
|
|
this.debugOn = false;
|
|
|
|
// Remove layers
|
|
for (var i in this._toolLayers) {
|
|
this._map.removeLayer(this._toolLayers[i]);
|
|
}
|
|
|
|
// Remove controls
|
|
for (var category in this._controls) {
|
|
this._controls[category].remove();
|
|
}
|
|
this._controls = {};
|
|
},
|
|
|
|
_addDebugTool: function (tool) {
|
|
// Create control if it doesn't exist
|
|
if (!(tool.category in this._controls)) {
|
|
this._controls[tool.category] = L.control.layers({}, {}, {collapsed: false}).addTo(this._map);
|
|
// Add a title
|
|
var b = document.createElement('b');
|
|
b.append(tool.category);
|
|
this._controls[tool.category]._container.prepend(b);
|
|
}
|
|
|
|
// Create layer
|
|
var layer = new L.LayerGroup();
|
|
this._toolLayers.push(layer);
|
|
this._controls[tool.category]._addLayer(layer, tool.name, true);
|
|
this._controls[tool.category]._update();
|
|
|
|
this._map.on('layeradd', function(e) {
|
|
if (e.layer === layer) {
|
|
tool.onAdd();
|
|
}
|
|
}, this);
|
|
this._map.on('layerremove', function(e) {
|
|
if (e.layer === layer) {
|
|
tool.onRemove();
|
|
}
|
|
}, this);
|
|
if (tool.startsOn) {
|
|
this._map.addLayer(layer);
|
|
}
|
|
},
|
|
|
|
_addDebugTools: function () {
|
|
var self = this; // easier than using (function (){}).bind(this) each time
|
|
|
|
this._addDebugTool({
|
|
name: 'Data Overlay',
|
|
category: 'Display',
|
|
startsOn: true,
|
|
onAdd: function () {
|
|
self.overlayOn = true;
|
|
self._overlayData = {};
|
|
},
|
|
onRemove: function () {
|
|
self.overlayOn = false;
|
|
for (var i in self._overlayData) {
|
|
self._overlayData[i].remove();
|
|
}
|
|
delete self._overlayData;
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Tile Overlays',
|
|
category: 'Display',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self.tileOverlaysOn = true;
|
|
self._painter.update();
|
|
},
|
|
onRemove: function () {
|
|
self.tileOverlaysOn = false;
|
|
self._painter.update();
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Tile Invalidations',
|
|
category: 'Display',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self.tileInvalidationsOn = true;
|
|
self._tileInvalidationRectangles = {};
|
|
self._tileInvalidationMessages = {};
|
|
self._tileInvalidationId = 0;
|
|
self._tileInvalidationKeypressQueue = [];
|
|
self._tileInvalidationKeypressTimes = self.getTimeArray();
|
|
self._tileInvalidationLayer = new L.LayerGroup();
|
|
self._map.addLayer(self._tileInvalidationLayer);
|
|
self._tileInvalidationTimeout();
|
|
},
|
|
onRemove: function () {
|
|
self.tileInvalidationsOn = false;
|
|
self.clearOverlayMessage('tileInvalidationMessages');
|
|
self.clearOverlayMessage('tileInvalidationTime');
|
|
clearTimeout(self._tileInvalidationTimeoutId);
|
|
self._map.removeLayer(self._tileInvalidationLayer);
|
|
self._painter.update();
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Tile data',
|
|
category: 'Display',
|
|
startsOn: true,
|
|
onAdd: function () {
|
|
self.tileDataOn = true;
|
|
self._tileDataTotalMessages = 0;
|
|
self._tileDataTotalLoads = 0;
|
|
self._tileDataTotalUpdates = 0;
|
|
self._tileDataTotalDeltas = 0;
|
|
self._tileDataTotalInvalidates = 0;
|
|
self._tileDataShowOverlay();
|
|
},
|
|
onRemove: function () {
|
|
self.tileDataOn = false;
|
|
self.clearOverlayMessage('tileData');
|
|
},
|
|
});
|
|
|
|
/*
|
|
* Doesn't seem to do anything
|
|
* TODO: Reenable
|
|
this._addDebugTool({
|
|
name: 'Always Active',
|
|
category: 'Functionality',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._map._debugAlwaysActive = true;
|
|
},
|
|
onRemove: function () {
|
|
self._map._debugAlwaysActive = false;
|
|
},
|
|
});
|
|
*/
|
|
|
|
this._addDebugTool({
|
|
name: 'Show Clipboard',
|
|
category: 'Display',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._map._textInput.debug(true);
|
|
},
|
|
onRemove: function () {
|
|
self._map._textInput.debug(false);
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Tile pixel grid section',
|
|
category: 'Display',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._painter._addTilePixelGridSection();
|
|
},
|
|
onRemove: function () {
|
|
self._painter._removeTilePixelGridSection();
|
|
},
|
|
});
|
|
|
|
if (this._docLayer.isCalc()) {
|
|
this._addDebugTool({
|
|
name: 'Splits section',
|
|
category: 'Display',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._painter._addSplitsSection();
|
|
},
|
|
onRemove: function () {
|
|
self._painter._removeSplitsSection();
|
|
},
|
|
});
|
|
}
|
|
|
|
this._addDebugTool({
|
|
name: 'Ping',
|
|
category: 'Display',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self.pingOn = true;
|
|
self._pingQueue = [];
|
|
self._pingTimes = self.getTimeArray();
|
|
self._pingTimeout();
|
|
},
|
|
onRemove: function () {
|
|
self.pingOn = false;
|
|
self.clearOverlayMessage('rendercount');
|
|
self.clearOverlayMessage('ping');
|
|
clearTimeout(self._pingTimeoutId);
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Performance Tracing',
|
|
category: 'Logging',
|
|
startsOn: app.socket.traceEventRecordingToggle,
|
|
onAdd: function () {
|
|
app.socket.setTraceEventLogging(true);
|
|
},
|
|
onRemove: function () {
|
|
app.socket.setTraceEventLogging(false);
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Protocol Logging',
|
|
category: 'Logging',
|
|
startsOn: true,
|
|
onAdd: function () {
|
|
window.setLogging(true);
|
|
L.Log.print();
|
|
},
|
|
onRemove: function () {
|
|
window.setLogging(false);
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Log incoming messages',
|
|
category: 'Logging',
|
|
startsOn: true,
|
|
onAdd: function () {
|
|
self.logIncomingMessages = true;
|
|
},
|
|
onRemove: function () {
|
|
self.logIncomingMessages = false;
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Log outgoing messages',
|
|
category: 'Logging',
|
|
startsOn: true,
|
|
onAdd: function () {
|
|
self.logOutgoingMessages = true;
|
|
},
|
|
onRemove: function () {
|
|
self.logOutgoingMessages = false;
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Log keyboard events',
|
|
category: 'Logging',
|
|
startsOn: true,
|
|
onAdd: function () {
|
|
self.logKeyboardEvents = true;
|
|
},
|
|
onRemove: function () {
|
|
self.logKeyboardEvents = false;
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Tile Dumping',
|
|
category: 'Logging',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
app.socket.sendMessage('toggletiledumping true');
|
|
},
|
|
onRemove: function () {
|
|
app.socket.sendMessage('toggletiledumping false');
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Debug Deltas',
|
|
category: 'Logging',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._docLayer._debugDeltas = true;
|
|
self._docLayer._debugDeltasDetail = true;
|
|
},
|
|
onRemove: function () {
|
|
self._docLayer._debugDeltas = false;
|
|
self._docLayer._debugDeltasDetail = false;
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Typer',
|
|
category: 'Functionality',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._typerLorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n';
|
|
self._typerLoremPos = 0;
|
|
self._typerTimeout();
|
|
},
|
|
onRemove: function () {
|
|
clearTimeout(self._typerTimeoutId);
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Randomize user settings',
|
|
category: 'Functionality',
|
|
startsOn: !!window.coolParams.get('randomUser'),
|
|
onAdd: function () {
|
|
self._randomizeSettings();
|
|
},
|
|
onRemove: function () {
|
|
// do nothing
|
|
},
|
|
});
|
|
|
|
this._addDebugTool({
|
|
name: 'Enable automated user',
|
|
category: 'Automated User',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._automatedUserTimeout();
|
|
},
|
|
onRemove: function () {
|
|
clearTimeout(self._automatedUserTimeoutId);
|
|
self._automatedUserTask = undefined;
|
|
self._automatedUserPhase = 0;
|
|
},
|
|
});
|
|
|
|
if (this._docLayer.isCalc()) {
|
|
this._addDebugTool({
|
|
name: 'Click, type, delete, cells & formulas',
|
|
category: 'Automated User',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._automatedUserAddTask(this.name, L.bind(self._automatedUserTypeCellFormula, self));
|
|
},
|
|
onRemove: function () {
|
|
self._automatedUserRemoveTask(this.name);
|
|
},
|
|
});
|
|
}
|
|
|
|
this._addDebugTool({
|
|
name: 'Insert and delete shape',
|
|
category: 'Automated User',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._automatedUserAddTask(this.name, L.bind(self._automatedUserInsertTypeShape, self));
|
|
},
|
|
onRemove: function () {
|
|
self._automatedUserRemoveTask(this.name);
|
|
},
|
|
});
|
|
|
|
if (this._docLayer.isCalc()) {
|
|
this._addDebugTool({
|
|
name: 'Resize rows and columns',
|
|
category: 'Automated User',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._automatedUserAddTask(this.name, L.bind(self._automatedUserResizeRowsColumns, self));
|
|
},
|
|
onRemove: function () {
|
|
self._automatedUserRemoveTask(this.name);
|
|
},
|
|
});
|
|
}
|
|
|
|
if (this._docLayer.isCalc()) {
|
|
this._addDebugTool({
|
|
name: 'Insert rows and columns',
|
|
category: 'Automated User',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._automatedUserAddTask(this.name, L.bind(self._automatedUserInsertRowsColumns, self));
|
|
},
|
|
onRemove: function () {
|
|
self._automatedUserRemoveTask(this.name);
|
|
},
|
|
});
|
|
}
|
|
|
|
if (this._docLayer.isCalc()) {
|
|
this._addDebugTool({
|
|
name: 'Delete rows and columns',
|
|
category: 'Automated User',
|
|
startsOn: false,
|
|
onAdd: function () {
|
|
self._automatedUserAddTask(this.name, L.bind(self._automatedUserDeleteRowsColumns, self));
|
|
},
|
|
onRemove: function () {
|
|
self._automatedUserRemoveTask(this.name);
|
|
},
|
|
});
|
|
}
|
|
},
|
|
|
|
_randomizeSettings: function() {
|
|
// Toggle dark mode
|
|
var isDark = this._map.uiManager.getDarkModeState();
|
|
if (Math.random() < 0.5) {
|
|
window.app.console.log('Randomize Settings: Toggle dark mode to ' + (isDark?'Light':'Dark'));
|
|
this._map.uiManager.toggleDarkMode();
|
|
} else {
|
|
window.app.console.log('Randomize Settings: Leave dark mode as ' + (isDark?'Dark':'Light'));
|
|
}
|
|
|
|
// Set zoom
|
|
var targetZoom = Math.floor(Math.random() * 9) + 6; // 6 to 14, 50% to 200%
|
|
window.app.console.log('Randomize Settings: Set zoom to '+targetZoom);
|
|
this._map.setZoom(targetZoom, null, false);
|
|
|
|
// Toggle spell check
|
|
var isSpellCheck = this._map['stateChangeHandler'].getItemValue('.uno:SpellOnline');
|
|
if (Math.random() < 0.5) {
|
|
window.app.console.log('Randomize Settings: Toggle spell check to ' + (isSpellCheck=='true'?'off':'on'));
|
|
this._map.sendUnoCommand('.uno:SpellOnline');
|
|
} else {
|
|
window.app.console.log('Randomize Settings: Leave spell check as ' + (isSpellCheck=='true'?'on':'off'));
|
|
}
|
|
|
|
// Toggle formatting marks
|
|
if (this._docLayer.isWriter()) {
|
|
if (Math.random() < 0.5) {
|
|
window.app.console.log('Randomize Settings: Toggle formatting marks');
|
|
this._map.sendUnoCommand('.uno:ControlCodes');
|
|
} else {
|
|
window.app.console.log('Randomize Settings: Leave formatting marks');
|
|
}
|
|
}
|
|
|
|
// Move to different part of sheet
|
|
if (this._docLayer.isCalc()) {
|
|
// Select random position
|
|
var docSize = this._map.getDocSize();
|
|
var maxX = docSize.x; //Math.min(docSize.x, 10000);
|
|
var maxY = docSize.y; //Math.min(docSize.y, 10000);
|
|
var positions = [
|
|
{x: maxX, y: 0}, // top right
|
|
{x: 0, y: maxY}, // bottom left
|
|
{x: maxX, y: maxY}, // bottom right
|
|
{x: maxX/2, y: maxY/2}, // center
|
|
];
|
|
var pos = positions[Math.floor(Math.random()*positions.length)];
|
|
|
|
// Calculate mouse click position
|
|
var viewSize = this._map.getSize();
|
|
var centerPos = {x: pos.x + viewSize.x/2, y: pos.y + viewSize.y/2};
|
|
var centerTwips = this._docLayer._pixelsToTwips(centerPos);
|
|
|
|
// Perform action
|
|
window.app.console.log('Randomize Settings: Move to ',pos,' click at ', centerPos, centerTwips);
|
|
this._map.fire('scrollto', pos);
|
|
this._docLayer._postMouseEvent('buttondown', centerTwips.x, centerTwips.y, 1, 1, 0);
|
|
this._docLayer._postMouseEvent('buttonup', centerTwips.x, centerTwips.y, 1, 1, 0);
|
|
}
|
|
|
|
// Toggle sidebar
|
|
if (!this._map._docLoadedOnce) {
|
|
// When first opening the document, initializeSidebar is called
|
|
// 200ms after setup, which would overwrite our randomization.
|
|
// So in this case, wait for sidebar initialization and the
|
|
// response to complete so that we know the current state
|
|
setTimeout(this._randomizeSidebar.bind(this), 1000);
|
|
} else {
|
|
this._randomizeSidebar();
|
|
}
|
|
|
|
this._painter.update();
|
|
},
|
|
|
|
_randomizeSidebar: function() {
|
|
var sidebars = ['none','.uno:SidebarDeck.PropertyDeck','.uno:Navigator'];
|
|
if (this._docLayer.isImpress()) {
|
|
sidebars = sidebars.concat(['.uno:SlideChangeWindow','.uno:CustomAnimation','.uno:MasterSlidesPanel','.uno:ModifyPage']);
|
|
}
|
|
var sidebar = sidebars[Math.floor(Math.random()*sidebars.length)];
|
|
window.app.console.log('Randomize Settings: Target sidebar: ' + sidebar);
|
|
if (this._map.sidebar && this._map.sidebar.isVisible()) {
|
|
// There is currently a sidebar
|
|
var currentSidebar = this._map.sidebar.getTargetDeck();
|
|
if (sidebar == 'none') {
|
|
window.app.console.log('Randomize Settings: Remove sidebar');
|
|
// Send message for existing sidebar to remove it
|
|
this._map.sendUnoCommand(currentSidebar);
|
|
} else if (sidebar == currentSidebar) {
|
|
window.app.console.log('Randomize Settings: Leave sidebar as ' + sidebar);
|
|
} else {
|
|
window.app.console.log('Randomize Settings: Switch sidebar to ' + sidebar);
|
|
this._map.sendUnoCommand(sidebar);
|
|
}
|
|
} else {
|
|
// Sidebar currently hidden
|
|
// eslint-disable-next-line no-lonely-if
|
|
if (sidebar == 'none') {
|
|
window.app.console.log('Randomize Settings: Leave sidebar off');
|
|
} else {
|
|
window.app.console.log('Randomize Settings: Open sidebar ' + sidebar);
|
|
this._map.sendUnoCommand(sidebar);
|
|
}
|
|
}
|
|
},
|
|
|
|
_automatedUserAddTask: function(name, taskFn) {
|
|
// task function takes phase number, returns waitTime
|
|
// When waitTime == 0, task is done.
|
|
|
|
// Save taskFn
|
|
this._automatedUserTasks[name] = taskFn;
|
|
// Add to queue
|
|
if (!this._automatedUserQueue.includes(name)) {
|
|
this._automatedUserQueue.push(name);
|
|
}
|
|
},
|
|
|
|
_automatedUserRemoveTask: function(name) {
|
|
// Don't bother deleting function from _debugAutomatedUserTasks
|
|
// Remove from queue
|
|
if (this._automatedUserQueue.includes(name)) {
|
|
this._automatedUserQueue.splice(
|
|
this._automatedUserQueue.indexOf(name),
|
|
1);
|
|
}
|
|
},
|
|
|
|
_automatedUserTimeout: function () {
|
|
if (!this._automatedUserTask) {
|
|
// Not in the middle of a task, pick a new one
|
|
window.app.console.log('Automated User: Pick a new task. Current queue: ',this._automatedUserQueue);
|
|
this._automatedUserTask = this._automatedUserQueue.shift();
|
|
this._automatedUserPhase = 0;
|
|
}
|
|
|
|
if (this._automatedUserTask && this._automatedUserPhase == 0) {
|
|
window.app.console.log('Automated User: Starting task ' + this._automatedUserTask);
|
|
// Re-enqueue task
|
|
this._automatedUserQueue.push(this._automatedUserTask);
|
|
}
|
|
|
|
if (this._automatedUserTask) {
|
|
window.app.console.log('Automated User: Current task: ' + this._automatedUserTask + ' Current phase: ' + this._automatedUserPhase);
|
|
var taskFn = this._automatedUserTasks[this._automatedUserTask];
|
|
var waitTime = taskFn(this._automatedUserPhase);
|
|
this._automatedUserPhase++;
|
|
if (waitTime == 0) {
|
|
window.app.console.log('Automated User: Task complete: ' + this._automatedUserTask);
|
|
this._automatedUserTask = undefined;
|
|
this._automatedUserPhase = 0;
|
|
}
|
|
this._automatedUserTimeoutId = setTimeout(L.bind(this._automatedUserTimeout, this), waitTime);
|
|
} else {
|
|
window.app.console.log('Automated User: Waiting for tasks');
|
|
// Nothing in queue, check again in 1s
|
|
this._automatedUserTimeoutId = setTimeout(L.bind(this._automatedUserTimeout, this), 1000);
|
|
}
|
|
},
|
|
|
|
_automatedUserTypeCellFormula: function (phase) {
|
|
var waitTime = 0;
|
|
switch (phase) {
|
|
case 0:
|
|
window.app.console.log('Automated User: Click in center');
|
|
var pos = this._docLayer._latLngToTwips(this._map.getCenter());
|
|
this._docLayer._postMouseEvent('buttondown',pos.x,pos.y,1,1,0);
|
|
this._docLayer._postMouseEvent('buttonup',pos.x,pos.y,1,1,0);
|
|
waitTime = 500;
|
|
break;
|
|
case 1:
|
|
window.app.console.log('Automated User: Type text');
|
|
this._typeText('asdf\nqwer\n', 100);
|
|
waitTime = 1000;
|
|
break;
|
|
case 2:
|
|
window.app.console.log('Automated User: Click formula bar');
|
|
this._map.sendUnoCommand('.uno:StartFormula');
|
|
waitTime = 500;
|
|
break;
|
|
case 3:
|
|
window.app.console.log('Automated User: Type formula');
|
|
this._typeText('A1\n', 100);
|
|
waitTime = 1000;
|
|
break;
|
|
case 4:
|
|
window.app.console.log('Automated User: Delete row');
|
|
this._docLayer.postKeyboardEvent('input', 0, 1025); //up
|
|
this._docLayer.postKeyboardEvent('input', 0, 1025); //up
|
|
this._map.sendUnoCommand('.uno:DeleteRows');
|
|
waitTime = 1000;
|
|
break;
|
|
case 5:
|
|
window.app.console.log('Automated User: Delete cells');
|
|
app.socket.sendMessage('removetextcontext id=0 before=0 after=1'); //delete
|
|
this._docLayer.postKeyboardEvent('input', 0, 1025); //up
|
|
app.socket.sendMessage('removetextcontext id=0 before=0 after=1'); //delete
|
|
waitTime = 500;
|
|
break;
|
|
}
|
|
return waitTime;
|
|
},
|
|
|
|
_automatedUserInsertTypeShape: function (phase) {
|
|
var waitTime = 0;
|
|
switch (phase) {
|
|
case 0:
|
|
window.app.console.log('Automated User: Insert Shape');
|
|
var shapes = ['rectangle','circle','diamond','pentagon'];
|
|
var shape = shapes[Math.floor(Math.random() * shapes.length)];
|
|
this._map.sendUnoCommand('.uno:BasicShapes.'+shape);
|
|
waitTime = 1000;
|
|
break;
|
|
case 1:
|
|
window.app.console.log('Automated User: Type in Shape');
|
|
this._docLayer.postKeyboardEvent('input',0, 1280); // enter to select text
|
|
this._typeText('textinshape', 100);
|
|
waitTime = 1500;
|
|
break;
|
|
case 2:
|
|
window.app.console.log('Automated User: Type Escape');
|
|
this._docLayer.postKeyboardEvent('input',0, 1281); // esc
|
|
waitTime = 500;
|
|
break;
|
|
case 3:
|
|
window.app.console.log('Automated User: Select Shape');
|
|
var pos = this._docLayer._latLngToTwips(this._map.getCenter());
|
|
this._docLayer._postMouseEvent('buttondown',pos.x,pos.y,1,1,0);
|
|
this._docLayer._postMouseEvent('buttonup',pos.x,pos.y,1,1,0);
|
|
waitTime = 1000;
|
|
break;
|
|
case 4:
|
|
window.app.console.log('Automated User: Delete Shape');
|
|
app.socket.sendMessage('removetextcontext id=0 before=0 after=1');
|
|
waitTime = 1000;
|
|
break;
|
|
}
|
|
return waitTime;
|
|
},
|
|
|
|
_automatedUserResizeRowsColumns: function (phase) {
|
|
var waitTime = 0;
|
|
switch (phase) {
|
|
case 0:
|
|
window.app.console.log('Automated User: Resize row smaller');
|
|
// Not necessary here, but nice to highlight the row being changed
|
|
app.sectionContainer.getSectionWithName('row header')._selectRow(1,0);
|
|
this._map.sendUnoCommand('.uno:RowHeight {"RowHeight":{"type":"unsigned short","value":200},"Row":{"type":"long","value":2}}');
|
|
waitTime = 2000;
|
|
break;
|
|
case 1:
|
|
window.app.console.log('Automated User: Resize row larger');
|
|
// Not necessary here, but nice to highlight the row being changed
|
|
app.sectionContainer.getSectionWithName('row header')._selectRow(1,0);
|
|
this._map.sendUnoCommand('.uno:RowHeight {"RowHeight":{"type":"unsigned short","value":2000},"Row":{"type":"long","value":2}}');
|
|
waitTime = 2000;
|
|
break;
|
|
case 2:
|
|
window.app.console.log('Automated User: Resize row auto');
|
|
// Selecting row is necessary here
|
|
app.sectionContainer.getSectionWithName('row header')._selectRow(1,0);
|
|
this._map.sendUnoCommand('.uno:SetOptimalRowHeight {"aExtraHeight":{"type":"unsigned short","value":0}}');
|
|
waitTime = 2000;
|
|
break;
|
|
case 3:
|
|
window.app.console.log('Automated User: Resize column smaller');
|
|
// Not necessary here, but nice to highlight the column being changed
|
|
app.sectionContainer.getSectionWithName('column header')._selectColumn(1,0);
|
|
this._map.sendUnoCommand('.uno:ColumnWidth {"ColumnWidth":{"type":"unsigned short","value":400},"Column":{"type":"long","value":2}}');
|
|
waitTime = 2000;
|
|
break;
|
|
case 4:
|
|
window.app.console.log('Automated User: Resize column larger');
|
|
// Not necessary here, but nice to highlight the column being changed
|
|
app.sectionContainer.getSectionWithName('column header')._selectColumn(1,0);
|
|
this._map.sendUnoCommand('.uno:ColumnWidth {"ColumnWidth":{"type":"unsigned short","value":8000},"Column":{"type":"long","value":2}}');
|
|
waitTime = 2000;
|
|
break;
|
|
case 5:
|
|
window.app.console.log('Automated User: Resize column auto');
|
|
// Selecting column is necessary here
|
|
app.sectionContainer.getSectionWithName('column header')._selectColumn(1,0);
|
|
this._map.sendUnoCommand('.uno:SetOptimalColumnWidthDirect {"aExtraHeight":{"type":"unsigned short","value":0}}');
|
|
waitTime = 2000;
|
|
break;
|
|
}
|
|
return waitTime;
|
|
},
|
|
|
|
_automatedUserInsertRowsColumns: function (phase) {
|
|
var waitTime = 0;
|
|
switch (phase) {
|
|
case 0:
|
|
window.app.console.log('Automated User: Insert row');
|
|
// Select just this row first, doesn't work if multiple rows are selected
|
|
app.sectionContainer.getSectionWithName('row header')._selectRow(1,0);
|
|
app.sectionContainer.getSectionWithName('row header').insertRowAbove(1);
|
|
waitTime = 2000;
|
|
break;
|
|
case 1:
|
|
window.app.console.log('Automated User: Delete column');
|
|
// Select just this column first, doesn't work if multiple columns are selected
|
|
app.sectionContainer.getSectionWithName('column header')._selectColumn(1,0);
|
|
app.sectionContainer.getSectionWithName('column header').insertColumnBefore(1);
|
|
waitTime = 2000;
|
|
break;
|
|
}
|
|
return waitTime;
|
|
},
|
|
|
|
_automatedUserDeleteRowsColumns: function (phase) {
|
|
var waitTime = 0;
|
|
switch (phase) {
|
|
case 0:
|
|
window.app.console.log('Automated User: Delete row');
|
|
// Select just this row first, otherwise multiple rows could get deleted
|
|
app.sectionContainer.getSectionWithName('row header')._selectRow(1,0);
|
|
app.sectionContainer.getSectionWithName('row header').deleteRow(1);
|
|
waitTime = 2000;
|
|
break;
|
|
case 1:
|
|
window.app.console.log('Automated User: Delete column');
|
|
// Select just this column first, otherwise multiple columns could get deleted
|
|
app.sectionContainer.getSectionWithName('column header')._selectColumn(1,0);
|
|
app.sectionContainer.getSectionWithName('column header').deleteColumn(1);
|
|
waitTime = 2000;
|
|
break;
|
|
}
|
|
return waitTime;
|
|
},
|
|
|
|
_typerTimeout: function() {
|
|
var letter = this._typerLorem.charCodeAt(this._typerLoremPos % this._typerLorem.length);
|
|
this._typeChar(letter);
|
|
this._typerLoremPos++;
|
|
this._typerTimeoutId = setTimeout(L.bind(this._typerTimeout, this), 50);
|
|
},
|
|
|
|
_typeText: function(text, delayMs) {
|
|
for (var i=0; i<text.length; i++) {
|
|
if (delayMs) {
|
|
setTimeout(L.bind(this._typeChar, this, text.charCodeAt(i)), i*delayMs);
|
|
} else {
|
|
this._typeChar(text.charCodeAt(i));
|
|
}
|
|
}
|
|
},
|
|
|
|
_typeChar: function(charCode) {
|
|
if (this.tileInvalidationsOn) {
|
|
this.addTileInvalidationKeypress();
|
|
}
|
|
if (charCode === '\n'.charCodeAt(0)) {
|
|
this._docLayer.postKeyboardEvent('input', 0, 1280);
|
|
} else {
|
|
this._docLayer.postKeyboardEvent('input', charCode, 0);
|
|
}
|
|
},
|
|
|
|
setOverlayMessage: function(id, message) {
|
|
if (this.overlayOn) {
|
|
if (!this._overlayData[id]) {
|
|
var topLeftNames = ['tileData'];
|
|
var position = topLeftNames.includes(id) ? 'topleft' : 'bottomleft';
|
|
this._overlayData[id] = L.control.attribution({prefix: '', position: position});
|
|
this._overlayData[id].addTo(this._map);
|
|
}
|
|
this._overlayData[id].setPrefix(message);
|
|
}
|
|
},
|
|
|
|
clearOverlayMessage: function(id) {
|
|
if (this.overlayOn) {
|
|
if (this._overlayData[id]) {
|
|
this._overlayData[id].remove();
|
|
delete this._overlayData[id];
|
|
}
|
|
}
|
|
},
|
|
|
|
getTimeArray: function() {
|
|
return {count: 0, ms: 0, best: Number.MAX_SAFE_INTEGER, worst: 0, date: 0};
|
|
},
|
|
|
|
updateTimeArray: function(times, value) {
|
|
if (value < times.best) {
|
|
times.best = value;
|
|
}
|
|
if (value > times.worst) {
|
|
times.worst = value;
|
|
}
|
|
times.ms += value;
|
|
times.count++;
|
|
return 'best: ' + times.best + ' ms, worst: ' + times.worst + ' ms, avg: ' + Math.round(times.ms/times.count) + ' ms, last: ' + value + ' ms';
|
|
},
|
|
|
|
_tileDataShowOverlay: function() {
|
|
var messages = this._tileDataTotalMessages;
|
|
var loads = this._tileDataTotalLoads;
|
|
var deltas = this._tileDataTotalDeltas;
|
|
var updates = this._tileDataTotalUpdates;
|
|
var invalidates = this._tileDataTotalInvalidates;
|
|
this.setOverlayMessage('tileData',
|
|
'Total tile messages: ' + messages + '<br>' +
|
|
'loads: ' + loads + ' ' +
|
|
'deltas: ' + deltas + ' ' +
|
|
'updates: ' + updates + '<br>' +
|
|
'invalidates: ' + invalidates + '<br>' +
|
|
'<b>Tile update waste: ' + Math.round(100.0 * updates / (updates + deltas)) + '%</b>' + '<br>' +
|
|
'<b>New Tile ratio: ' + Math.round(100.0 * loads / (loads + updates + deltas)) + '%</b>'
|
|
);
|
|
},
|
|
|
|
tileDataAddMessage() {
|
|
if (!this.tileDataOn) {
|
|
return;
|
|
}
|
|
this._tileDataTotalMessages++;
|
|
this._tileDataShowOverlay();
|
|
},
|
|
|
|
tileDataAddLoad() {
|
|
if (!this.tileDataOn) {
|
|
return;
|
|
}
|
|
this._tileDataTotalLoads++;
|
|
this._tileDataShowOverlay();
|
|
},
|
|
|
|
tileDataAddUpdate() {
|
|
if (!this.tileDataOn) {
|
|
return;
|
|
}
|
|
this._tileDataTotalUpdates++;
|
|
this._tileDataShowOverlay();
|
|
},
|
|
|
|
tileDataAddDelta() {
|
|
if (!this.tileDataOn) {
|
|
return;
|
|
}
|
|
this._tileDataTotalDeltas++;
|
|
this._tileDataShowOverlay();
|
|
},
|
|
|
|
tileDataAddInvalidate() {
|
|
if (!this.tileDataOn) {
|
|
return;
|
|
}
|
|
this._tileDataTotalInvalidates++;
|
|
this._tileDataShowOverlay();
|
|
},
|
|
|
|
_tileInvalidationTimeout: function() {
|
|
for (var key in this._tileInvalidationRectangles) {
|
|
var rect = this._tileInvalidationRectangles[key];
|
|
var opac = rect.options.fillOpacity;
|
|
if (opac <= 0.04) {
|
|
if (key < this._tileInvalidationId - 5) {
|
|
this._tileInvalidationLayer.removeLayer(rect);
|
|
delete this._tileInvalidationRectangles[key];
|
|
delete this._tileInvalidationMessages[key];
|
|
} else {
|
|
rect.setStyle({fillOpacity: 0, opacity: 1 - (this._tileInvalidationId - key) / 7});
|
|
}
|
|
} else {
|
|
rect.setStyle({fillOpacity: opac - 0.04});
|
|
}
|
|
}
|
|
this._tileInvalidationTimeoutId = setTimeout(L.bind(this._tileInvalidationTimeout, this), 50);
|
|
},
|
|
|
|
// key press times will be paired with the invalidation messages
|
|
addTileInvalidationKeypress: function() {
|
|
if (!this.tileInvalidationsOn) {
|
|
return;
|
|
}
|
|
this._tileInvalidationKeypressQueue.push(+new Date());
|
|
},
|
|
|
|
addTileInvalidationMessage: function(message) {
|
|
if (!this.tileInvalidationsOn) {
|
|
return;
|
|
}
|
|
|
|
this._tileInvalidationMessages[this._tileInvalidationId - 1] = message;
|
|
|
|
var messages = '';
|
|
for (var i = this._tileInvalidationId - 1; i > this._tileInvalidationId - 6; i--) {
|
|
if (i >= 0 && this._tileInvalidationMessages[i]) {
|
|
messages += '' + i + ': ' + this._tileInvalidationMessages[i] + ' <br>';
|
|
}
|
|
}
|
|
this.setOverlayMessage('tileInvalidationMessages',messages);
|
|
},
|
|
|
|
addTileInvalidationRectangle: function(topLeftTwips, bottomRightTwips, command) {
|
|
if (!this.tileInvalidationsOn) {
|
|
return;
|
|
}
|
|
|
|
var signX = this._docLayer.isCalcRTL() ? -1 : 1;
|
|
|
|
var absTopLeftTwips = L.point(topLeftTwips.x * signX, topLeftTwips.y);
|
|
var absBottomRightTwips = L.point(bottomRightTwips.x * signX, bottomRightTwips.y);
|
|
|
|
var invalidBoundCoords = new L.LatLngBounds(
|
|
this._docLayer._twipsToLatLng(absTopLeftTwips, this._docLayer._tileZoom),
|
|
this._docLayer._twipsToLatLng(absBottomRightTwips, this._docLayer._tileZoom)
|
|
);
|
|
var rect = L.rectangle(invalidBoundCoords, {color: 'red', weight: 1, opacity: 1, fillOpacity: 0.4, pointerEvents: 'none'});
|
|
this._tileInvalidationRectangles[this._tileInvalidationId] = rect;
|
|
this._tileInvalidationMessages[this._tileInvalidationId] = command;
|
|
this._tileInvalidationId++;
|
|
this._tileInvalidationLayer.addLayer(rect);
|
|
|
|
|
|
// There is not always an invalidation for every keypress.
|
|
// Keypresses at the front of the queue that are older than 1s
|
|
// are probably stale and should be ignored.
|
|
var now = +new Date();
|
|
do {
|
|
var oldestKeypress = this._tileInvalidationKeypressQueue.shift();
|
|
} while (oldestKeypress && now - oldestKeypress > 1000);
|
|
if (oldestKeypress) {
|
|
var timeText = this.updateTimeArray(this._tileInvalidationKeypressTimes, now - oldestKeypress);
|
|
this.setOverlayMessage('tileInvalidationTime', 'Tile invalidation time: ' + timeText);
|
|
}
|
|
},
|
|
|
|
_pingTimeout: function() {
|
|
// pings will be paired with the pong messages
|
|
this._pingQueue.push(+new Date());
|
|
app.socket.sendMessage('ping');
|
|
this._pingTimeoutId = setTimeout(L.bind(this._pingTimeout, this), 2000);
|
|
},
|
|
|
|
reportPong: function(rendercount) {
|
|
if (!this.pingOn) {
|
|
return;
|
|
}
|
|
|
|
// TODO: move rendercount from pong to tile data tool
|
|
this.setOverlayMessage('rendercount', 'Server rendered tiles: ' + rendercount);
|
|
|
|
var oldestPing = this._pingQueue.shift();
|
|
if (oldestPing) {
|
|
var now = +new Date();
|
|
var timeText = this._map._debug.updateTimeArray(this._pingTimes, now - oldestPing);
|
|
this.setOverlayMessage('ping', 'Server ping time: ' + timeText);
|
|
}
|
|
},
|
|
|
|
});
|