[Libreoffice-commits] online.git: Branch 'distro/collabora/milestone-3' - 12 commits - loleaflet/build loleaflet/debug loleaflet/dist loleaflet/README loleaflet/src

Mihai Varga mihai.varga at collabora.com
Wed Jul 29 09:10:40 PDT 2015


 loleaflet/README                                      |   84 +++++
 loleaflet/build/deps.js                               |   29 +-
 loleaflet/debug/document/document_simple_example.html |   28 -
 loleaflet/dist/leaflet.css                            |    8 
 loleaflet/src/control/Control.Scroll.js               |   93 ++++++
 loleaflet/src/control/Scroll.js                       |   19 +
 loleaflet/src/layer/tile/GridLayer.js                 |   50 ---
 loleaflet/src/layer/tile/TileLayer.js                 |  254 ------------------
 loleaflet/src/map/Map.js                              |   23 -
 loleaflet/src/map/handler/Map.Keyboard.js             |  214 ++++++++++-----
 loleaflet/src/map/handler/Map.Mouse.js                |  131 +++++++++
 loleaflet/src/map/handler/Map.Scroll.js               |   59 ++++
 loleaflet/src/map/handler/Map.ScrollWheelZoom.js      |   68 ----
 13 files changed, 582 insertions(+), 478 deletions(-)

New commits:
commit fd00c9b133fc709fb6bea739ac15a93bb2c81b4d
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Wed Jul 29 19:08:59 2015 +0300

    loleaflet: cherry-pick fix

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index dcaef5e..2c58f86 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -120,7 +120,6 @@ L.TileLayer = L.GridLayer.extend({
 	getEvents: function () {
 		var events = {
 			viewreset: this._viewReset,
-			movestart: this._moveStart,
 			moveend: this._move
 		};
 
commit 71b015657626b9dc356ed2a528aa689336f806f4
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 17:17:49 2015 +0300

    loleaflet: keyboard navigation now also fires 'scrollby' event
    
    Conflicts:
    	loleaflet/src/map/handler/Map.Keyboard.js

diff --git a/loleaflet/src/map/handler/Map.Keyboard.js b/loleaflet/src/map/handler/Map.Keyboard.js
index 084fc03..f1454b5 100644
--- a/loleaflet/src/map/handler/Map.Keyboard.js
+++ b/loleaflet/src/map/handler/Map.Keyboard.js
@@ -4,6 +4,8 @@
 
 L.Map.mergeOptions({
 	keyboard: true,
+	keyboardPanOffset: 20,
+	keyboardZoomOffset: 1
 });
 
 L.Map.Keyboard = L.Handler.extend({
@@ -93,8 +95,19 @@ L.Map.Keyboard = L.Handler.extend({
 		46  : true // delete
 	},
 
+	navigationKeyCodes: {
+		left:    [37],
+		right:   [39],
+		down:    [40],
+		up:      [38],
+		zoomIn:  [187, 107, 61, 171],
+		zoomOut: [189, 109, 173]
+	},
+
 	initialize: function (map) {
 		this._map = map;
+		this._setPanOffset(map.options.keyboardPanOffset);
+		this._setZoomOffset(map.options.keyboardZoomOffset);
 	},
 
 	addHooks: function () {
@@ -109,6 +122,38 @@ L.Map.Keyboard = L.Handler.extend({
 		this._map.on('keydown keyup keypress', this._onKeyDown, this);
 	},
 
+	_setPanOffset: function (pan) {
+		var keys = this._panKeys = {},
+		    codes = this.navigationKeyCodes,
+		    i, len;
+
+		for (i = 0, len = codes.left.length; i < len; i++) {
+			keys[codes.left[i]] = [-1 * pan, 0];
+		}
+		for (i = 0, len = codes.right.length; i < len; i++) {
+			keys[codes.right[i]] = [pan, 0];
+		}
+		for (i = 0, len = codes.down.length; i < len; i++) {
+			keys[codes.down[i]] = [0, pan];
+		}
+		for (i = 0, len = codes.up.length; i < len; i++) {
+			keys[codes.up[i]] = [0, -1 * pan];
+		}
+	},
+
+	_setZoomOffset: function (zoom) {
+		var keys = this._zoomKeys = {},
+		    codes = this.navigationKeyCodes,
+		    i, len;
+
+		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
+			keys[codes.zoomIn[i]] = zoom;
+		}
+		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
+			keys[codes.zoomOut[i]] = -zoom;
+		}
+	},
+
 	_onMouseDown: function () {
 		if (this._map._docLayer._permission === 'edit') {
 			return;
@@ -131,30 +176,41 @@ L.Map.Keyboard = L.Handler.extend({
 			return;
 		}
 
-		if (docLayer._permission !== 'edit') {
-			return;
-		}
-
-		var charCode = e.originalEvent.charCode;
-		var keyCode = e.originalEvent.keyCode;
-		if (e.type === 'keydown' && this.handleOnKeyDown[keyCode] && charCode === 0) {
-			docLayer._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
-		}
-		else if (e.type === 'keypress' &&
-				(!this.handleOnKeyDown[keyCode] || charCode !== 0)) {
-			if (charCode === keyCode && charCode !== 13) {
-				// Chrome sets keyCode = charCode for printable keys
-				// while LO requires it to be 0
-				keyCode = 0;
+		if (docLayer._permission === 'edit') {
+			var charCode = e.originalEvent.charCode;
+			var keyCode = e.originalEvent.keyCode;
+			if (e.type === 'keydown' && this.handleOnKeyDown[keyCode] && charCode === 0) {
+				docLayer._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
+			}
+			else if (e.type === 'keypress' &&
+					(!this.handleOnKeyDown[keyCode] || charCode !== 0)) {
+				if (charCode === keyCode && charCode !== 13) {
+					// Chrome sets keyCode = charCode for printable keys
+					// while LO requires it to be 0
+					keyCode = 0;
+				}
+				docLayer._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
+			}
+			else if (e.type === 'keyup') {
+				docLayer._postKeyboardEvent('up', charCode, this._toUNOKeyCode(keyCode));
+			}
+			if (keyCode === 9) {
+				// tab would change focus to other DOM elements
+				e.originalEvent.preventDefault();
 			}
-			docLayer._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
-		}
-		else if (e.type === 'keyup') {
-			docLayer._postKeyboardEvent('up', charCode, this._toUNOKeyCode(keyCode));
 		}
-		if (keyCode === 9) {
-			// tab would change focus to other DOM elements
-			e.originalEvent.preventDefault();
+		else if (e.type === 'keydown') {
+			var key = e.originalEvent.keyCode;
+			var map = this._map;
+			if (key in this._panKeys) {
+				if (map._panAnim && map._panAnim._inProgress) {
+					return;
+				}
+				map.fire('scrollby', {x: this._panKeys[key][0], y: this._panKeys[key][1]});
+			}
+			else if (key in this._zoomKeys) {
+				map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
+			}
 		}
 		L.DomEvent.stopPropagation(e.originalEvent);
 	}
commit db382f2435b653e3d57c1fbea50726c12c92a3c3
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 16:04:28 2015 +0300

    loleaflet: scroll API and event description in README
    
    Conflicts:
    	loleaflet/README

diff --git a/loleaflet/README b/loleaflet/README
index 48359c8..5ea8421 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -51,6 +51,90 @@ To see an example:
 and you should see the document in the browser.  In case anything goes wrong,
 check the loolwsd console for the debugging output.
 
+API & events
+------------
+
+Search:
+    - API:
+        map.search(text, [backward])
+    - events:
+        map.on('searchnotfound', function)
+
+Zoom:
+    - API:
+        map.zoomIn(amount)
+        map.zoomOut(amout)
+        map.getMinZoom()
+        map.getMaxZoom()
+    - events:
+        map.on('zoomend zoomlevelschange', function)
+
+Edit, view, readOnly:
+    - API:
+        map.setPermission('edit' | 'view' | 'readonly')
+    - events:
+        map.on('updatepermission', function (e) {}) where:
+            + e.perm == 'edit' | 'view' | 'readonly'
+
+Buttons like Bold, Italic, Strike through etc.
+    - API:
+        map.toggleCommandState('Bold' | 'Italic' | 'Underline' | 'Strikeout' | 'AlighLeft', etc)
+    - events:
+        map.on('commandstatechanged', function (e) {}) where:
+            + e.unoCmd == 'Bold' | 'Italic' etc.
+            + e.state = 'true' | 'false'
+
+Parts (like slides in presentation, or sheets in spreadsheets):
+    - API:
+        map.setPart('next' | 'prev' | partNumber)
+        map.getPartPreview(id, part, maxWidth, maxHeight) where:
+            + id = the ID of the request so that the response can be identified
+            + maxWidth / maxHeight are the desired dimensions of the preview, a smaller
+              image might be returned in order to keep the original ratio of the document
+    - events:
+        map.on('updateparts', function (e) {}) where:
+            + e.currentPart is the current part
+            + e.parts == the number of parts that the document has
+            + e.docType == 'text' | 'spreadsheet' | 'presentation' | 'drawing' | 'other'
+            + [e.partNames] if present, part names (e.g. sheet names)
+
+Statusindicator (when the document is loading):
+    - events
+        map.on('statusindicator', function (e) {}) where:
+            + e.statusType = 'start' | 'setvalue' | 'finish'
+            + e.value == a value from 0 to 100 indicating the status
+              if the statusType is 'setvalue
+
+Save:
+    - API:
+        map.saveAs(url, [format, options])
+
+Scroll (the following are measured in pixels):
+    - API:
+        map.scroll(x,y)
+            + scroll right by 'x' and down by 'y' (or left and up if negative)
+        map.scrollDown(y)
+            + scroll down by 'y' (or up if negative)
+        map.scrollRight(x)
+            + scroll right by 'x' (or left if nevative)
+    - events
+        map.on('docsize', function (e) {}) where:
+            + e.x = document width
+            + e.y = document height
+        map.on('updatescrolloffset', function (e) {}) where:
+            + e.x = difference between document's left and current view's left
+                (how much has the document been scrolled right)
+            + e.y = difference between document's top and current view's top
+                (how much has the document been scrolled down)
+            - this event is fired when zooming and the current view is maintained but the
+                document shrinks or grow OR when the document is panned OR when the container is resized
+        map.on('scrollto', function (e) {}) where:
+            + e.x = view's left position (so that the cursor/search result is in the center)
+            + e.y = view's top position (so that the cursor/search result is in the center)
+        map.on('scrollby', function (e) {}) where:
+            + e.x = the amount scrolled to the right (or left if negative)
+            + e.y = the amount scrolled to the bottom (or top if negative)
+
 Contributing
 ------------
 
diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 4bffd2e..f98f480 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -293,6 +293,11 @@ var deps = {
 		desc: 'Parts change handler.'
 	},
 
+	Scroll: {
+		src: ['control/Scroll.js'],
+		desc: 'Scroll handler.'
+	},
+
 	AnimationPan: {
 		src: [
 			'dom/DomEvent.js',
diff --git a/loleaflet/src/control/Control.Scroll.js b/loleaflet/src/control/Control.Scroll.js
index 0755de6..5cb1a81 100644
--- a/loleaflet/src/control/Control.Scroll.js
+++ b/loleaflet/src/control/Control.Scroll.js
@@ -49,7 +49,7 @@ L.Control.Scroll = L.Control.extend({
 		if (!offset.equals(new L.Point(0, 0))) {
 			this._prevScrollY = -e.mcs.top;
 			this._prevScrollX = -e.mcs.left;
-			this._map.panBy(offset, {animate:false});
+			this._map.scroll(offset.x, offset.y);
 		}
 	},
 
diff --git a/loleaflet/src/control/Scroll.js b/loleaflet/src/control/Scroll.js
new file mode 100644
index 0000000..ba0b2de
--- /dev/null
+++ b/loleaflet/src/control/Scroll.js
@@ -0,0 +1,19 @@
+/*
+ * Scroll methods
+ */
+L.Map.include({
+	scroll: function (x, y) {
+		if (typeof (x) !== 'number' || typeof (y) !== 'number') {
+			return;
+		}
+		this.panBy(new L.Point(x, y), {animate: false});
+	},
+
+	scrollDown: function (y) {
+		this.scroll(0, y);
+	},
+
+	scrollRight: function (x) {
+		this.scroll(x, 0);
+	}
+});
commit d40d0aad9e6dab8117a4592587905f283a609153
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 15:44:02 2015 +0300

    loleaflet: scroll by 1/4 of the view size

diff --git a/loleaflet/src/map/handler/Map.Scroll.js b/loleaflet/src/map/handler/Map.Scroll.js
index 9814ffb..fac8cbf 100644
--- a/loleaflet/src/map/handler/Map.Scroll.js
+++ b/loleaflet/src/map/handler/Map.Scroll.js
@@ -4,9 +4,7 @@
 
 L.Map.mergeOptions({
 	scroll: true,
-	wheelDebounceTime: 40,
-	// scroll by 150px
-	scrollAmount: 150
+	wheelDebounceTime: 40
 });
 
 L.Map.Scroll = L.Handler.extend({
@@ -47,13 +45,14 @@ L.Map.Scroll = L.Handler.extend({
 
 	_performScroll: function () {
 		var map = this._map,
-		    delta = -this._delta;
+			delta = -this._delta,
+			scrollAmount = Math.round(map.getSize().y / 4);
 
 		this._delta = 0;
 		this._startTime = null;
 
 		if (!delta) { return; }
-		map.fire('scrollby', {x: 0, y: delta * this._map.options.scrollAmount});
+		map.fire('scrollby', {x: 0, y: delta * scrollAmount});
 	}
 });
 
commit a1331edb49dcb0b4c7c58b8605cd29d5e8d169f7
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 15:40:58 2015 +0300

    loleaflet: reverse mouse wheel delta sign
    
    So that a positive value would scroll down

diff --git a/loleaflet/src/control/Control.Scroll.js b/loleaflet/src/control/Control.Scroll.js
index f086109..0755de6 100644
--- a/loleaflet/src/control/Control.Scroll.js
+++ b/loleaflet/src/control/Control.Scroll.js
@@ -64,6 +64,7 @@ L.Control.Scroll = L.Control.extend({
 	},
 
 	_onScrollBy: function (e) {
+		e.y *= (-1);
 		var y = '+=' + e.y;
 		if (e.y < 0) {
 			y = '-=' + Math.abs(e.y);
diff --git a/loleaflet/src/map/handler/Map.Scroll.js b/loleaflet/src/map/handler/Map.Scroll.js
index 141bf61..9814ffb 100644
--- a/loleaflet/src/map/handler/Map.Scroll.js
+++ b/loleaflet/src/map/handler/Map.Scroll.js
@@ -47,7 +47,7 @@ L.Map.Scroll = L.Handler.extend({
 
 	_performScroll: function () {
 		var map = this._map,
-		    delta = this._delta;
+		    delta = -this._delta;
 
 		this._delta = 0;
 		this._startTime = null;
commit 4f32f7aafcdf4ebc99ca957fe54c9ce7b7128b28
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 15:10:08 2015 +0300

    loleaflet: renamed handler so we can later have a 'scroll' method

diff --git a/loleaflet/src/map/handler/Map.Scroll.js b/loleaflet/src/map/handler/Map.Scroll.js
index 46edf12..141bf61 100644
--- a/loleaflet/src/map/handler/Map.Scroll.js
+++ b/loleaflet/src/map/handler/Map.Scroll.js
@@ -57,4 +57,4 @@ L.Map.Scroll = L.Handler.extend({
 	}
 });
 
-L.Map.addInitHook('addHandler', 'scroll', L.Map.Scroll);
+L.Map.addInitHook('addHandler', 'scrollHandler', L.Map.Scroll);
commit 4b3976fe778731b5ad4dc93e3510e68b881e5565
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 12:52:05 2015 +0300

    loleaflet: mouse scroll is now triggered on the map div
    
    Not on the document scrollbar container as before

diff --git a/loleaflet/src/control/Control.Scroll.js b/loleaflet/src/control/Control.Scroll.js
index 2e72a75..f086109 100644
--- a/loleaflet/src/control/Control.Scroll.js
+++ b/loleaflet/src/control/Control.Scroll.js
@@ -10,6 +10,7 @@ L.Control.Scroll = L.Control.extend({
 		this._mockDoc.id = 'mock-doc';
 
 		map.on('scrollto', this._onScrollTo, this);
+		map.on('scrollby', this._onScrollBy, this);
 		map.on('docsize', this._onUpdateSize, this);
 		map.on('updatescrolloffset', this._onUpdateScrollOffset, this);
 
@@ -59,7 +60,15 @@ L.Control.Scroll = L.Control.extend({
 
 	_onScrollTo: function (e) {
 		// triggered by the document (e.g. search result out of the viewing area)
-		$('#scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x]);
+		$('.scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x]);
+	},
+
+	_onScrollBy: function (e) {
+		var y = '+=' + e.y;
+		if (e.y < 0) {
+			y = '-=' + Math.abs(e.y);
+		}
+		$('.scroll-container').mCustomScrollbar('scrollTo', [y, '+=0']);
 	},
 
 	_onUpdateSize: function (e) {
@@ -71,10 +80,10 @@ L.Control.Scroll = L.Control.extend({
 
 	_onUpdateScrollOffset: function (e) {
 		this._ignoreScroll = null;
-		$('#scroll-container').mCustomScrollbar('stop');
+		$('.scroll-container').mCustomScrollbar('stop');
 		this._prevScrollY = e.y;
 		this._prevScrollX = e.x;
-		$('#scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x], {callbacks: false, timeout:0});
+		$('.scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x], {callbacks: false, timeout:0});
 	}
 });
 
diff --git a/loleaflet/src/map/handler/Map.Scroll.js b/loleaflet/src/map/handler/Map.Scroll.js
index 24fd4df..46edf12 100644
--- a/loleaflet/src/map/handler/Map.Scroll.js
+++ b/loleaflet/src/map/handler/Map.Scroll.js
@@ -1,13 +1,15 @@
 /*
- * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
+ * L.Handler.Scroll is used by L.Map to enable mouse scroll wheel zoom on the map.
  */
 
 L.Map.mergeOptions({
-	scrollWheelZoom: true,
-	wheelDebounceTime: 40
+	scroll: true,
+	wheelDebounceTime: 40,
+	// scroll by 150px
+	scrollAmount: 150
 });
 
-L.Map.ScrollWheelZoom = L.Handler.extend({
+L.Map.Scroll = L.Handler.extend({
 	addHooks: function () {
 		L.DomEvent.on(this._map._container, {
 			mousewheel: this._onWheelScroll,
@@ -38,31 +40,21 @@ L.Map.ScrollWheelZoom = L.Handler.extend({
 		var left = Math.max(debounce - (+new Date() - this._startTime), 0);
 
 		clearTimeout(this._timer);
-		this._timer = setTimeout(L.bind(this._performZoom, this), left);
+		this._timer = setTimeout(L.bind(this._performScroll, this), left);
 
 		L.DomEvent.stop(e);
 	},
 
-	_performZoom: function () {
+	_performScroll: function () {
 		var map = this._map,
-		    delta = this._delta,
-		    zoom = map.getZoom();
-
-		map.stop(); // stop panning and fly animations if any
-
-		delta = delta > 0 ? Math.ceil(delta) : Math.floor(delta);
-		delta = Math.max(Math.min(delta, 4), -4);
-		delta = map._limitZoom(zoom + delta) - zoom;
+		    delta = this._delta;
 
 		this._delta = 0;
 		this._startTime = null;
 
 		if (!delta) { return; }
-
-		if (map.options.scrollWheelZoom === 'center') {
-			map.setZoom(zoom + delta);
-		} else {
-			map.setZoomAround(this._lastMousePos, zoom + delta);
-		}
+		map.fire('scrollby', {x: 0, y: delta * this._map.options.scrollAmount});
 	}
 });
+
+L.Map.addInitHook('addHandler', 'scroll', L.Map.Scroll);
commit bec925995c2fa618da83e6085313f88e9645b5ba
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 12:04:18 2015 +0300

    loleaflet: renamed Map.ScrollWheelZoom to Map.Scroll

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 1b896b0..4bffd2e 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -165,11 +165,17 @@ var deps = {
 		heading: 'Interaction'
 	},
 
+	MapScroll: {
+		src: ['dom/DomEvent.js',
+		      'core/Handler.js',
+		      'map/handler/Map.Scroll.js'],
+		desc: 'Handles the mouse wheel scroll',
+	},
+
 	MouseZoom: {
 		src: ['dom/DomEvent.js',
 		      'core/Handler.js',
-		      'map/handler/Map.DoubleClickZoom.js',
-		      'map/handler/Map.ScrollWheelZoom.js'],
+		      'map/handler/Map.DoubleClickZoom.js'],
 		desc: 'Scroll wheel zoom and double click zoom on the map.'
 	},
 
diff --git a/loleaflet/src/map/handler/Map.ScrollWheelZoom.js b/loleaflet/src/map/handler/Map.Scroll.js
similarity index 100%
rename from loleaflet/src/map/handler/Map.ScrollWheelZoom.js
rename to loleaflet/src/map/handler/Map.Scroll.js
commit 68b1d35b4c0b55d03b2adeaca5e21c643e64e4d1
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Fri Jul 24 11:30:32 2015 +0300

    loleaflet: add the scrollbars as a control to be easily removed
    
    Conflicts:
    	loleaflet/src/layer/tile/GridLayer.js

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index a6df481..1b896b0 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -247,6 +247,13 @@ var deps = {
 		desc: 'Display document loading status'
 	},
 
+	ControlScroll: {
+		src: ['control/Control.js',
+		      'control/Control.Scroll.js'],
+		heading: 'Controls',
+		desc: 'Creates and handles the scrollbar'
+	},
+
 	ControlAttrib: {
 		src: ['control/Control.js',
 		      'control/Control.Attribution.js'],
diff --git a/loleaflet/debug/document/document_simple_example.html b/loleaflet/debug/document/document_simple_example.html
index 2d71bd2..3ea29f3 100644
--- a/loleaflet/debug/document/document_simple_example.html
+++ b/loleaflet/debug/document/document_simple_example.html
@@ -32,10 +32,6 @@
     </div>
     <div id="document-container" style="top:100px">
         <div id="map"></div>
-        <div id="scroll-container">
-            <div id="mock-document">
-            </div>
-        </div>
     </div>
 
 	<script>
@@ -57,7 +53,7 @@
         alert('Wrong host, usage: host=ws://localhost:9980');
     }
 
-    var map = L.map('map', 'scroll-container', 'mock-document', {
+    var map = L.map('map', {
             center: [0, 0],
             zoom: 10,
             minZoom: 1,
@@ -73,6 +69,7 @@
     map.addControl(L.control.zoom());
     map.addControl(L.control.parts());
     map.addControl(L.control.statusIndicator());
+    map.addControl(L.control.scroll());
 
     ////// Document layer ////
     var docLayer = new L.TileLayer('', {
@@ -82,25 +79,5 @@
         readOnly: false
     });
     map.addLayer(docLayer);
-
-    ////// Scrollbar /////
-    (function($){
-        $(window).load(function(){
-            $("#scroll-container").mCustomScrollbar({
-                axis: 'yx',
-                theme: 'dark-thick',
-                scrollInertia: 0,
-                callbacks:{
-                    onScroll: function(){
-                        docLayer._onScrollEnd(this);
-                    },
-                    whileScrolling: function(){
-                        docLayer._onScroll(this);
-                    },
-                    alwaysTriggerOffsets:false
-                }
-            });
-        });
-    })(jQuery);
     </script>
 </body></html>
diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index 7ee709b..4b5db03 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -525,7 +525,7 @@
 }
 
 #map
-#scroll-container {
+.scroll-container {
 	position: absolute;
 	left: 0;
 	top: 0;
@@ -533,7 +533,7 @@
 	bottom: 0;
 }
 
-#scroll-container {
+.scroll-container {
 	height: 100%;
 	width: 100%;
 	overflow: auto;
diff --git a/loleaflet/src/control/Control.Scroll.js b/loleaflet/src/control/Control.Scroll.js
new file mode 100644
index 0000000..2e72a75
--- /dev/null
+++ b/loleaflet/src/control/Control.Scroll.js
@@ -0,0 +1,83 @@
+/*
+ * L.Control.Scroll handles scrollbars
+ */
+
+L.Control.Scroll = L.Control.extend({
+
+	onAdd: function (map) {
+		this._scrollContainer = L.DomUtil.create('div', 'scroll-container', map._container.parentElement);
+		this._mockDoc = L.DomUtil.create('div', '', this._scrollContainer);
+		this._mockDoc.id = 'mock-doc';
+
+		map.on('scrollto', this._onScrollTo, this);
+		map.on('docsize', this._onUpdateSize, this);
+		map.on('updatescrolloffset', this._onUpdateScrollOffset, this);
+
+		var control = this;
+		$(".scroll-container").mCustomScrollbar({
+			axis: 'yx',
+			theme: 'dark-thick',
+			scrollInertia: 0,
+			callbacks:{
+				onScroll: function() {
+					control._onScrollEnd(this);
+				},
+				whileScrolling: function() {
+					control._onScroll(this);
+				},
+				alwaysTriggerOffsets: false
+			}
+		});
+		return document.createElement('div');
+	},
+
+	_onScroll: function (e) {
+		if (this._ignoreScroll) {
+			return;
+		}
+		if (this._prevScrollY === undefined) {
+			this._prevScrollY = 0;
+		}
+		if (this._prevScrollX === undefined) {
+			this._prevScrollX = 0;
+		}
+		var offset = new L.Point(
+				-e.mcs.left - this._prevScrollX,
+				-e.mcs.top - this._prevScrollY);
+
+		if (!offset.equals(new L.Point(0, 0))) {
+			this._prevScrollY = -e.mcs.top;
+			this._prevScrollX = -e.mcs.left;
+			this._map.panBy(offset, {animate:false});
+		}
+	},
+
+	_onScrollEnd: function (e) {
+		this._prevScrollY = -e.mcs.top;
+		this._prevScrollX = -e.mcs.left;
+	},
+
+	_onScrollTo: function (e) {
+		// triggered by the document (e.g. search result out of the viewing area)
+		$('#scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x]);
+	},
+
+	_onUpdateSize: function (e) {
+		this._ignoreScroll = true;
+		setTimeout(L.bind(function() {this._ignoreScroll = null;}, this), 200);
+		L.DomUtil.setStyle(this._mockDoc, 'width', e.x + 'px');
+		L.DomUtil.setStyle(this._mockDoc, 'height', e.y + 'px');
+	},
+
+	_onUpdateScrollOffset: function (e) {
+		this._ignoreScroll = null;
+		$('#scroll-container').mCustomScrollbar('stop');
+		this._prevScrollY = e.y;
+		this._prevScrollX = e.x;
+		$('#scroll-container').mCustomScrollbar('scrollTo', [e.y, e.x], {callbacks: false, timeout:0});
+	}
+});
+
+L.control.scroll = function (options) {
+	return new L.Control.Scroll(options);
+};
diff --git a/loleaflet/src/layer/tile/GridLayer.js b/loleaflet/src/layer/tile/GridLayer.js
index b658b28..d0e37c2 100644
--- a/loleaflet/src/layer/tile/GridLayer.js
+++ b/loleaflet/src/layer/tile/GridLayer.js
@@ -385,27 +385,15 @@ L.GridLayer = L.Layer.extend({
 		var scrollPixelLimits = new L.Point(this._docWidthTwips / this._tileWidthTwips,
 										 this._docHeightTwips / this._tileHeightTwips);
 		scrollPixelLimits = scrollPixelLimits.multiplyBy(this._tileSize);
-
-		if (!sizeChanged) {
-			this._ignoreScroll = true;
-			setTimeout(L.bind(function() {this._ignoreScroll = null;}, this), 200);
-		}
-		L.DomUtil.setStyle(this._map._mockDoc, 'width', scrollPixelLimits.x + 'px');
-		L.DomUtil.setStyle(this._map._mockDoc, 'height', scrollPixelLimits.y + 'px');
+		this._map.fire('docsize', {x: scrollPixelLimits.x, y: scrollPixelLimits.y});
 	},
 
 	_updateScrollOffset: function () {
-		this._ignoreScroll = null;
-		if (this._map._scrollContainer.mcs) {
-			$('#scroll-container').mCustomScrollbar('stop');
-			var centerPixel = this._map.project(this._map.getCenter());
-			var newScrollPos = centerPixel.subtract(this._map.getSize().divideBy(2));
-			var x = newScrollPos.x < 0 ? 0 : newScrollPos.x;
-			var y = newScrollPos.y < 0 ? 0 : newScrollPos.y;
-			this._prevScrollY = y;
-			this._prevScrollX = x;
-			$('#scroll-container').mCustomScrollbar('scrollTo', [y, x], {callbacks: false, timeout:0});
-		}
+		var centerPixel = this._map.project(this._map.getCenter());
+		var newScrollPos = centerPixel.subtract(this._map.getSize().divideBy(2));
+		var x = newScrollPos.x < 0 ? 0 : newScrollPos.x;
+		var y = newScrollPos.y < 0 ? 0 : newScrollPos.y;
+		this._map.fire('updatescrolloffset', {x: x, y: y});
 	},
 
 	_setZoomTransforms: function (center, zoom) {
@@ -763,32 +751,6 @@ L.GridLayer = L.Layer.extend({
 			if (!this._tiles[key].loaded) { return false; }
 		}
 		return true;
-	},
-
-	_onScroll: function (evt) {
-		if (this._ignoreScroll) {
-			return;
-		}
-		if (this._prevScrollY === undefined) {
-			this._prevScrollY = 0;
-		}
-		if (this._prevScrollX === undefined) {
-			this._prevScrollX = 0;
-		}
-		var offset = new L.Point(
-				-evt.mcs.left - this._prevScrollX,
-				-evt.mcs.top - this._prevScrollY);
-
-		if (!offset.equals(new L.Point(0, 0))) {
-			this._prevScrollY = -evt.mcs.top;
-			this._prevScrollX = -evt.mcs.left;
-			this._map.panBy(offset, {animate:false});
-		}
-	},
-
-	_onScrollEnd: function (evt) {
-		this._prevScrollY = -evt.mcs.top;
-		this._prevScrollX = -evt.mcs.left;
 	}
 });
 
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index f5d1bc2..dcaef5e 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -101,11 +101,9 @@ L.TileLayer = L.GridLayer.extend({
 			this._map.socket.send('load url=' + this.options.doc);
 			this._map.socket.send('status');
 		}
-		this._map._scrollContainer.onscroll = L.bind(this._onScroll, this);
-		this._map.on('zoomend resize', this._updateScrollOffset, this);
+		this._map.on('drag resize zoomend', this._updateScrollOffset, this);
 		this._map.on('zoomstart zoomend', this._onZoom, this);
 		this._map.on('clearselection', this._clearSelections, this);
-		this._map.on('drag', this._updateScrollOffset, this);
 		this._map.on('copy', this._onCopy, this);
 		this._startMarker.on('drag dragend', this._onSelectionHandleDrag, this);
 		this._endMarker.on('drag dragend', this._onSelectionHandleDrag, this);
@@ -361,7 +359,7 @@ L.TileLayer = L.GridLayer.extend({
 					center = center.subtract(this._map.getSize().divideBy(2));
 					center.x = center.x < 0 ? 0 : center.x;
 					center.y = center.y < 0 ? 0 : center.y;
-					$('#scroll-container').mCustomScrollbar('scrollTo', [center.y, center.x]);
+					this._map.fire('scrollto', {x: center.x, y: center.y});
 				}
 
 				var polygons = this._rectanglesToPolygons(rectangles);
@@ -654,7 +652,7 @@ L.TileLayer = L.GridLayer.extend({
 				center = center.subtract(this._map.getSize().divideBy(2));
 				center.x = center.x < 0 ? 0 : center.x;
 				center.y = center.y < 0 ? 0 : center.y;
-				$('#scroll-container').mCustomScrollbar('scrollTo', [center.y, center.x]);
+				this._map.fire('scrollto', {x: center.x, y: center.y});
 			}
 		}
 		else if (this._cursorMarker) {
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 3de0f7c..d259ff5 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -19,13 +19,13 @@ L.Map = L.Evented.extend({
 		markerZoomAnimation: true
 	},
 
-	initialize: function (id, scrollContId, mockDocId, options) { // (HTMLElement or String, Object)
+	initialize: function (id, options) { // (HTMLElement or String, Object)
 		options = L.setOptions(this, options);
 
 		if (options.server) {
 			this._initSocket();
 		}
-		this._initContainer(id, scrollContId, mockDocId);
+		this._initContainer(id);
 		this._initLayout();
 
 		// hack for https://github.com/Leaflet/Leaflet/issues/1980
@@ -448,10 +448,8 @@ L.Map = L.Evented.extend({
 	},
 
 
-	_initContainer: function (id, scrollContId, mockDocId) {
+	_initContainer: function (id) {
 		var container = this._container = L.DomUtil.get(id);
-		var scrollContainer = this._scrollContainer = L.DomUtil.get(scrollContId);
-		var mockDoc = this._mockDoc = L.DomUtil.get(mockDocId);
 
 		if (!container) {
 			throw new Error('Map container not found.');
@@ -459,14 +457,6 @@ L.Map = L.Evented.extend({
 			throw new Error('Map container is already initialized.');
 		}
 
-		if (!scrollContainer) {
-			throw new Error('Scroll container not found.');
-		}
-
-		if (!mockDoc) {
-			throw new Error('Mock document div not found.');
-		}
-
 		var textAreaContainer = L.DomUtil.create('div', 'clipboard-container', container.parentElement);
 		this._textArea = L.DomUtil.create('textarea', 'clipboard', textAreaContainer);
 
@@ -765,6 +755,6 @@ L.Map = L.Evented.extend({
 	}
 });
 
-L.map = function (id, scrollContId, mockDocId, options) {
-	return new L.Map(id, scrollContId, mockDocId, options);
+L.map = function (id, options) {
+	return new L.Map(id, options);
 };
commit 0563e24c50e4a9abd30020d6806fda81dde8da2e
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Thu Jul 23 17:17:56 2015 +0300

    loleaflet: dynamically create the clipboard div
    
    Conflicts:
    	loleaflet/spec/tilebench.html
    	loleaflet/spec/tilebench/TileBenchSpec.js
    	loleaflet/src/layer/tile/TileLayer.js

diff --git a/loleaflet/debug/document/document_simple_example.html b/loleaflet/debug/document/document_simple_example.html
index d259531..2d71bd2 100644
--- a/loleaflet/debug/document/document_simple_example.html
+++ b/loleaflet/debug/document/document_simple_example.html
@@ -31,7 +31,6 @@
         <p style="margin:0px">This space is a placeholder for the future toolbar</p>
     </div>
     <div id="document-container" style="top:100px">
-        <div id="clipboard-container"><textarea id="clipboard"></textarea></div>
         <div id="map"></div>
         <div id="scroll-container">
             <div id="mock-document">
diff --git a/loleaflet/dist/leaflet.css b/loleaflet/dist/leaflet.css
index c192339..7ee709b 100644
--- a/loleaflet/dist/leaflet.css
+++ b/loleaflet/dist/leaflet.css
@@ -616,7 +616,7 @@
   }
 }
 
-#clipboard-container {
+.clipboard-container {
 	position: fixed;
 	left: 0px;
 	top: 0px;
@@ -625,7 +625,7 @@
 	z-index: 100;
 	opacity: 0;
 }
-#clipboard {
+.clipboard {
 	width: 1px;
 	height: 1px;
 	padding: 0px;
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index d7e9ba4..f5d1bc2 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -89,9 +89,7 @@ L.TileLayer = L.GridLayer.extend({
 			}),
 			draggable: true
 		});
-		this._pendingTilesCount = 0;
-		this._textArea = L.DomUtil.get('clipboard');
-		this._textArea.focus();
+		this._emptyTilesCount = 0;
 	},
 
 	_initDocument: function () {
@@ -111,6 +109,8 @@ L.TileLayer = L.GridLayer.extend({
 		this._map.on('copy', this._onCopy, this);
 		this._startMarker.on('drag dragend', this._onSelectionHandleDrag, this);
 		this._endMarker.on('drag dragend', this._onSelectionHandleDrag, this);
+		this._textArea = this._map._textArea;
+		this._textArea.focus();
 		if (this.options.edit && !this.options.readOnly) {
 			this._map.setPermission('edit');
 		}
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 4f25698..3de0f7c 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -467,6 +467,9 @@ L.Map = L.Evented.extend({
 			throw new Error('Mock document div not found.');
 		}
 
+		var textAreaContainer = L.DomUtil.create('div', 'clipboard-container', container.parentElement);
+		this._textArea = L.DomUtil.create('textarea', 'clipboard', textAreaContainer);
+
 		container._leaflet = true;
 	},
 
@@ -584,7 +587,7 @@ L.Map = L.Evented.extend({
 
 		L.DomEvent[onOff](this._container, 'click dblclick mousedown mouseup ' +
 			'mouseover mouseout mousemove contextmenu keydown keypress keyup', this._handleDOMEvent, this);
-		L.DomEvent[onOff](L.DomUtil.get('clipboard'), 'copy keydown keypress keyup', this._handleDOMEvent, this);
+		L.DomEvent[onOff](this._textArea, 'copy keydown keypress keyup', this._handleDOMEvent, this);
 
 		if (this.options.trackResize) {
 			L.DomEvent[onOff](window, 'resize', this._onResize, this);
commit d64eb77938b5deeb3919dd379a56c6eafbd564ab
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Thu Jul 16 16:15:30 2015 +0300

    loleaflet: moved mouse handler from TileLayer.js -> Map.Mouse.js
    
    Conflicts:
    	loleaflet/src/layer/tile/TileLayer.js

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index b3ce61c..a6df481 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -191,7 +191,12 @@ var deps = {
 
 	Keyboard: {
 		src: ['map/handler/Map.Keyboard.js'],
-		desc: 'Enables keyboard pan/zoom when the map is focused.'
+		desc: 'Handles keyboard interaction with the document.'
+	},
+
+	Mouse: {
+		src: ['map/handler/Map.Mouse.js'],
+		desc: 'Handles mouse interaction with the document.'
 	},
 
 	MarkerDrag: {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 1321d1c..d7e9ba4 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -89,7 +89,7 @@ L.TileLayer = L.GridLayer.extend({
 			}),
 			draggable: true
 		});
-		this._mouseEventsQueue = [];
+		this._pendingTilesCount = 0;
 		this._textArea = L.DomUtil.get('clipboard');
 		this._textArea.focus();
 	},
@@ -109,8 +109,6 @@ L.TileLayer = L.GridLayer.extend({
 		this._map.on('clearselection', this._clearSelections, this);
 		this._map.on('drag', this._updateScrollOffset, this);
 		this._map.on('copy', this._onCopy, this);
-		this._map.on('mousedown mouseup mouseover mouseout mousemove dblclick',
-				this._onMouseEvent, this);
 		this._startMarker.on('drag dragend', this._onSelectionHandleDrag, this);
 		this._endMarker.on('drag dragend', this._onSelectionHandleDrag, this);
 		if (this.options.edit && !this.options.readOnly) {
@@ -627,113 +625,7 @@ L.TileLayer = L.GridLayer.extend({
 				' x=' + x + ' y=' + y);
 	},
 
-	_onMouseEvent: function (e) {
-		if (this._graphicMarker && this._graphicMarker.isDragged) {
-			return;
-		}
-
-		if (this._startMarker.isDragged === true || this._endMarker.isDragged === true) {
-			return;
-		}
-
-		if (e.type === 'mousedown') {
-			this._mouseDown = true;
-			if (this._holdMouseEvent) {
-				clearTimeout(this._holdMouseEvent);
-			}
-			var mousePos = this._latLngToTwips(e.latlng);
-			this._mouseEventsQueue.push(L.bind(function() {
-				this._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1);}, this));
-			this._holdMouseEvent = setTimeout(L.bind(this._executeMouseEvents, this), 500);
-		}
-		else if (e.type === 'mouseup') {
-			this._mouseDown = false;
-			if (this._map.dragging.enabled()) {
-				if (this._mouseEventsQueue.length === 0) {
-					// mouse up after panning
-					return;
-				}
-			}
-			clearTimeout(this._holdMouseEvent);
-			this._holdMouseEvent = null;
-			if (this._clickTime && Date.now() - this._clickTime <= 250) {
-				// double click, a click was sent already
-				this._mouseEventsQueue = [];
-				return;
-			}
-			else {
-				this._clickTime = Date.now();
-				mousePos = this._latLngToTwips(e.latlng);
-				var timeOut = 250;
-				if (this._permission === 'edit') {
-					timeOut = 0;
-				}
-				this._mouseEventsQueue.push(L.bind(function() {
-					// if it's a click or mouseup after selecting
-					if (this._mouseEventsQueue.length > 1) {
-						// it's a click, fire mousedown
-						this._mouseEventsQueue[0]();
-						if (this._permission === 'view') {
-							this._map.setPermission('edit');
-						}
-					}
-					this._mouseEventsQueue = [];
-					this._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1);
-					this._textArea.focus();
-				}, this));
-				this._holdMouseEvent = setTimeout(L.bind(this._executeMouseEvents, this), timeOut);
-
-				if (this._startMarker._icon) {
-					L.DomUtil.removeClass(this._startMarker._icon, 'leaflet-not-clickable');
-				}
-				if (this._endMarker._icon) {
-					L.DomUtil.removeClass(this._endMarker._icon, 'leaflet-not-clickable');
-				}
-			}
-		}
-		else if (e.type === 'mousemove' && this._mouseDown) {
-			if (this._holdMouseEvent) {
-				clearTimeout(this._holdMouseEvent);
-				this._holdMouseEvent = null;
-				if (this._map.dragging.enabled()) {
-					// The user just panned the document
-					this._mouseEventsQueue = [];
-					return;
-				}
-				for (var i = 0; i < this._mouseEventsQueue.length; i++) {
-					// synchronously execute old mouse events so we know that
-					// they arrive to the server before the move command
-					this._mouseEventsQueue[i]();
-				}
-				this._mouseEventsQueue = [];
-			}
-			if (!this._map.dragging.enabled()) {
-				mousePos = this._latLngToTwips(e.latlng);
-				this._postMouseEvent('move', mousePos.x, mousePos.y, 1);
-				if (this._startMarker._icon) {
-					L.DomUtil.addClass(this._startMarker._icon, 'leaflet-not-clickable');
-				}
-				if (this._endMarker._icon) {
-					L.DomUtil.addClass(this._endMarker._icon, 'leaflet-not-clickable');
-				}
-			}
-		}
-		else if (e.type === 'dblclick') {
-			mousePos = this._latLngToTwips(e.latlng);
-			this._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1);
-			this._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2);
-			this._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2);
-			this._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1);
-		}
-	},
 
-	_executeMouseEvents: function () {
-		this._holdMouseEvent = null;
-		for (var i = 0; i < this._mouseEventsQueue.length; i++) {
-			this._mouseEventsQueue[i]();
-		}
-		this._mouseEventsQueue = [];
-	},
 
 	// Is rRectangle empty?
 	_isEmptyRectangle: function (aBounds) {
diff --git a/loleaflet/src/map/handler/Map.Mouse.js b/loleaflet/src/map/handler/Map.Mouse.js
new file mode 100644
index 0000000..270bdb2
--- /dev/null
+++ b/loleaflet/src/map/handler/Map.Mouse.js
@@ -0,0 +1,131 @@
+/*
+ * L.Map.Mouse is handling mouse interaction with the document
+ */
+
+L.Map.mergeOptions({
+	mouse: true,
+});
+
+L.Map.Mouse = L.Handler.extend({
+
+	initialize: function (map) {
+		this._map = map;
+		this._mouseEventsQueue = [];
+	},
+
+	addHooks: function () {
+		this._map.on('mousedown mouseup mouseover mouseout mousemove dblclick',
+			this._onMouseEvent, this);
+	},
+
+	_onMouseEvent: function (e) {
+		var docLayer = this._map._docLayer;
+		if (docLayer._graphicMarker && docLayer._graphicMarker.isDragged) {
+			return;
+		}
+
+		if (docLayer._startMarker.isDragged === true || docLayer._endMarker.isDragged === true) {
+			return;
+		}
+
+		if (e.type === 'mousedown') {
+			this._mouseDown = true;
+			if (this._holdMouseEvent) {
+				clearTimeout(this._holdMouseEvent);
+			}
+			var mousePos = docLayer._latLngToTwips(e.latlng);
+			this._mouseEventsQueue.push(L.bind(function() {
+				docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1);}, docLayer));
+			this._holdMouseEvent = setTimeout(L.bind(this._executeMouseEvents, this), 500);
+		}
+		else if (e.type === 'mouseup') {
+			this._mouseDown = false;
+			if (this._map.dragging.enabled()) {
+				if (this._mouseEventsQueue.length === 0) {
+					// mouse up after panning
+					return;
+				}
+			}
+			clearTimeout(this._holdMouseEvent);
+			this._holdMouseEvent = null;
+			if (this._clickTime && Date.now() - this._clickTime <= 250) {
+				// double click, a click was sent already
+				this._mouseEventsQueue = [];
+				return;
+			}
+			else {
+				this._clickTime = Date.now();
+				mousePos = docLayer._latLngToTwips(e.latlng);
+				var timeOut = 250;
+				if (docLayer._permission === 'edit') {
+					timeOut = 0;
+				}
+				this._mouseEventsQueue.push(L.bind(function() {
+					// if it's a click or mouseup after selecting
+					if (this._mouseEventsQueue.length > 1) {
+						// it's a click, fire mousedown
+						this._mouseEventsQueue[0]();
+						if (docLayer._permission === 'view') {
+							docLayer._map.setPermission('edit');
+						}
+					}
+					this._mouseEventsQueue = [];
+					docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1);
+					docLayer._textArea.focus();
+				}, this, docLayer));
+				this._holdMouseEvent = setTimeout(L.bind(this._executeMouseEvents, this), timeOut);
+
+				if (docLayer._startMarker._icon) {
+					L.DomUtil.removeClass(docLayer._startMarker._icon, 'leaflet-not-clickable');
+				}
+				if (docLayer._endMarker._icon) {
+					L.DomUtil.removeClass(docLayer._endMarker._icon, 'leaflet-not-clickable');
+				}
+			}
+		}
+		else if (e.type === 'mousemove' && this._mouseDown) {
+			if (this._holdMouseEvent) {
+				clearTimeout(this._holdMouseEvent);
+				this._holdMouseEvent = null;
+				if (this._map.dragging.enabled()) {
+					// The user just panned the document
+					this._mouseEventsQueue = [];
+					return;
+				}
+				for (var i = 0; i < this._mouseEventsQueue.length; i++) {
+					// synchronously execute old mouse events so we know that
+					// they arrive to the server before the move command
+					this._mouseEventsQueue[i]();
+				}
+				this._mouseEventsQueue = [];
+			}
+			if (!this._map.dragging.enabled()) {
+				mousePos = docLayer._latLngToTwips(e.latlng);
+				docLayer._postMouseEvent('move', mousePos.x, mousePos.y, 1);
+				if (docLayer._startMarker._icon) {
+					L.DomUtil.addClass(docLayer._startMarker._icon, 'leaflet-not-clickable');
+				}
+				if (docLayer._endMarker._icon) {
+					L.DomUtil.addClass(docLayer._endMarker._icon, 'leaflet-not-clickable');
+				}
+			}
+		}
+		else if (e.type === 'dblclick') {
+			mousePos = docLayer._latLngToTwips(e.latlng);
+			docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1);
+			docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2);
+			docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2);
+			docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1);
+		}
+	},
+
+	_executeMouseEvents: function () {
+		this._holdMouseEvent = null;
+		for (var i = 0; i < this._mouseEventsQueue.length; i++) {
+			this._mouseEventsQueue[i]();
+		}
+		this._mouseEventsQueue = [];
+	}
+});
+
+L.Map.addInitHook('addHandler', 'mouse', L.Map.Mouse);
commit aa42255e6afb784e77379a954cfc636beabb8c68
Author: Mihai Varga <mihai.varga at collabora.com>
Date:   Thu Jul 16 15:23:42 2015 +0300

    loleaflet: moved keyboard handler from TileLayer.js -> Map.Keyboard.js
    
    Conflicts:
    	loleaflet/src/layer/tile/TileLayer.js

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index f240ca8..1321d1c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -27,91 +27,6 @@ L.TileLayer = L.GridLayer.extend({
 		crossOrigin: false
 	},
 
-	keymap: {
-		8   : 1283, // backspace	: BACKSPACE
-		9   : 1282, // tab 		: TAB
-		13  : 1280, // enter 		: RETURN
-		16  : null, // shift		: UNKOWN
-		17  : null, // ctrl		: UNKOWN
-		18  : null, // alt		: UNKOWN
-		19  : null, // pause/break	: UNKOWN
-		20  : null, // caps lock	: UNKOWN
-		27  : 1281, // escape		: ESCAPE
-		32  : 1284, // space		: SPACE
-		33  : 1030, // page up		: PAGEUP
-		34  : 1031, // page down	: PAGEDOWN
-		35  : 1029, // end		: END
-		36  : 1028, // home		: HOME
-		37  : 1026, // left arrow	: LEFT
-		38  : 1025, // up arrow		: UP
-		39  : 1027, // right arrow	: RIGHT
-		40  : 1024, // down arrow	: DOWN
-		45  : 1285, // insert		: INSERT
-		46  : 1286, // delete		: DELETE
-		91  : null, // left window key	: UNKOWN
-		92  : null, // right window key	: UNKOWN
-		93  : null, // select key	: UNKOWN
-		96  : 256,  // numpad 0		: NUM0
-		97  : 257,  // numpad 1		: NUM1
-		98  : 258,  // numpad 2		: NUM2
-		99  : 259,  // numpad 3		: NUM3
-		100 : 260,  // numpad 4		: NUM4
-		101 : 261,  // numpad 5		: NUM5
-		102 : 262,  // numpad 6		: NUM6
-		103 : 263,  // numpad 7		: NUM7
-		104 : 264,  // numpad 8		: NUM8
-		105 : 265,  // numpad 9		: NUM9
-		106 : 1289, // multiply		: MULTIPLY
-		107 : 1287, // add		: ADD
-		109 : 1288, // subtract		: SUBTRACT
-		110 : 1309, // decimal point	: DECIMAL
-		111 : 1290, // divide		: DIVIDE
-		112 : 768,  // f1		: F1
-		113 : 769,  // f2		: F2
-		114 : 770,  // f3		: F3
-		115 : 771,  // f4		: F4
-		116 : 772,  // f5		: F5
-		117 : 773,  // f6		: F6
-		118 : 774,  // f7		: F7
-		119 : 775,  // f8		: F8
-		120 : 776,  // f9		: F9
-		121 : 777,  // f10		: F10
-		122 : 778,  // f11		: F11
-		144 : 1313, // num lock		: NUMLOCK
-		145 : 1314, // scroll lock	: SCROLLLOCK
-		186 : 1317, // semi-colon	: SEMICOLON
-		187 : 1295, // equal sign	: EQUAL
-		188 : 1292, // comma		: COMMA
-		189 : 5,    // dash		: DASH
-		190 : null, // period		: UNKOWN
-		191 : null, // forward slash	: UNKOWN
-		192 : null, // grave accent	: UNKOWN
-		219 : null, // open bracket	: UNKOWN
-		220 : null, // back slash	: UNKOWN
-		221 : null, // close bracket	: UNKOWN
-		222 : null  // single quote	: UNKOWN
-	},
-
-	handleOnKeyDown: {
-		// these keys need to be handled on keydown in order for them
-		// to work on chrome
-		8   : true, // backspace
-		9   : true, // tab
-		19  : true, // pause/break
-		20  : true, // caps lock
-		27  : true, // escape
-		33  : true, // page up
-		34  : true, // page down
-		35  : true, // end
-		36  : true, // home
-		37  : true, // left arrow
-		38  : true, // up arrow
-		39  : true, // right arrow
-		40  : true, // down arrow
-		45  : true, // insert
-		46  : true // delete
-	},
-
 	initialize: function (url, options) {
 
 		this._url = url;
@@ -209,10 +124,8 @@ L.TileLayer = L.GridLayer.extend({
 	getEvents: function () {
 		var events = {
 			viewreset: this._viewReset,
-			moveend: this._move,
-			keydown: this._signalKey,
-			keypress: this._signalKey,
-			keyup: this._signalKey
+			movestart: this._moveStart,
+			moveend: this._move
 		};
 
 		if (!this.options.updateWhenIdle) {
@@ -822,48 +735,6 @@ L.TileLayer = L.GridLayer.extend({
 		this._mouseEventsQueue = [];
 	},
 
-	// Convert javascript key codes to UNO key codes.
-	_toUNOKeyCode: function (keyCode) {
-		return this.keymap[keyCode] || keyCode;
-	},
-
-	// Receives a key press or release event.
-	_signalKey: function (e) {
-		if (e.originalEvent.ctrlKey) {
-			// we prepare for a copy event
-			this._textArea.value = 'dummy text';
-			this._textArea.focus();
-			this._textArea.select();
-			return;
-		}
-
-		if (this._permission !== 'edit') {
-			return;
-		}
-
-		var charCode = e.originalEvent.charCode;
-		var keyCode = e.originalEvent.keyCode;
-		if (e.type === 'keydown' && this.handleOnKeyDown[keyCode] && charCode === 0) {
-			this._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
-		}
-		else if (e.type === 'keypress' &&
-				(!this.handleOnKeyDown[keyCode] || charCode !== 0)) {
-			if (charCode === keyCode && charCode !== 13) {
-				// Chrome sets keyCode = charCode for printable keys
-				// while LO requires it to be 0
-				keyCode = 0;
-			}
-			this._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
-		}
-		else if (e.type === 'keyup') {
-			this._postKeyboardEvent('up', charCode, this._toUNOKeyCode(keyCode));
-		}
-		if (keyCode === 9) {
-			// tab would change focus to other DOM elements
-			e.originalEvent.preventDefault();
-		}
-	},
-
 	// Is rRectangle empty?
 	_isEmptyRectangle: function (aBounds) {
 		return aBounds.getSouthWest().equals(new L.LatLng(0, 0)) && aBounds.getNorthEast().equals(new L.LatLng(0, 0));
diff --git a/loleaflet/src/map/handler/Map.Keyboard.js b/loleaflet/src/map/handler/Map.Keyboard.js
index e524ce8..084fc03 100644
--- a/loleaflet/src/map/handler/Map.Keyboard.js
+++ b/loleaflet/src/map/handler/Map.Keyboard.js
@@ -4,26 +4,97 @@
 
 L.Map.mergeOptions({
 	keyboard: true,
-	keyboardPanOffset: 80,
-	keyboardZoomOffset: 1
 });
 
 L.Map.Keyboard = L.Handler.extend({
 
-	keyCodes: {
-		left:    [37],
-		right:   [39],
-		down:    [40],
-		up:      [38],
-		zoomIn:  [187, 107, 61, 171],
-		zoomOut: [189, 109, 173]
+	keymap: {
+		8   : 1283, // backspace	: BACKSPACE
+		9   : 1282, // tab 		: TAB
+		13  : 1280, // enter 		: RETURN
+		16  : null, // shift		: UNKOWN
+		17  : null, // ctrl		: UNKOWN
+		18  : null, // alt		: UNKOWN
+		19  : null, // pause/break	: UNKOWN
+		20  : null, // caps lock	: UNKOWN
+		27  : 1281, // escape		: ESCAPE
+		32  : 1284, // space		: SPACE
+		33  : 1030, // page up		: PAGEUP
+		34  : 1031, // page down	: PAGEDOWN
+		35  : 1029, // end		: END
+		36  : 1028, // home		: HOME
+		37  : 1026, // left arrow	: LEFT
+		38  : 1025, // up arrow		: UP
+		39  : 1027, // right arrow	: RIGHT
+		40  : 1024, // down arrow	: DOWN
+		45  : 1285, // insert		: INSERT
+		46  : 1286, // delete		: DELETE
+		91  : null, // left window key	: UNKOWN
+		92  : null, // right window key	: UNKOWN
+		93  : null, // select key	: UNKOWN
+		96  : 256,  // numpad 0		: NUM0
+		97  : 257,  // numpad 1		: NUM1
+		98  : 258,  // numpad 2		: NUM2
+		99  : 259,  // numpad 3		: NUM3
+		100 : 260,  // numpad 4		: NUM4
+		101 : 261,  // numpad 5		: NUM5
+		102 : 262,  // numpad 6		: NUM6
+		103 : 263,  // numpad 7		: NUM7
+		104 : 264,  // numpad 8		: NUM8
+		105 : 265,  // numpad 9		: NUM9
+		106 : 1289, // multiply		: MULTIPLY
+		107 : 1287, // add		: ADD
+		109 : 1288, // subtract		: SUBTRACT
+		110 : 1309, // decimal point	: DECIMAL
+		111 : 1290, // divide		: DIVIDE
+		112 : 768,  // f1		: F1
+		113 : 769,  // f2		: F2
+		114 : 770,  // f3		: F3
+		115 : 771,  // f4		: F4
+		116 : 772,  // f5		: F5
+		117 : 773,  // f6		: F6
+		118 : 774,  // f7		: F7
+		119 : 775,  // f8		: F8
+		120 : 776,  // f9		: F9
+		121 : 777,  // f10		: F10
+		122 : 778,  // f11		: F11
+		144 : 1313, // num lock		: NUMLOCK
+		145 : 1314, // scroll lock	: SCROLLLOCK
+		186 : 1317, // semi-colon	: SEMICOLON
+		187 : 1295, // equal sign	: EQUAL
+		188 : 1292, // comma		: COMMA
+		189 : 5,    // dash		: DASH
+		190 : null, // period		: UNKOWN
+		191 : null, // forward slash	: UNKOWN
+		192 : null, // grave accent	: UNKOWN
+		219 : null, // open bracket	: UNKOWN
+		220 : null, // back slash	: UNKOWN
+		221 : null, // close bracket	: UNKOWN
+		222 : null  // single quote	: UNKOWN
+	},
+
+	handleOnKeyDown: {
+		// these keys need to be handled on keydown in order for them
+		// to work on chrome
+		8   : true, // backspace
+		9   : true, // tab
+		19  : true, // pause/break
+		20  : true, // caps lock
+		27  : true, // escape
+		33  : true, // page up
+		34  : true, // page down
+		35  : true, // end
+		36  : true, // home
+		37  : true, // left arrow
+		38  : true, // up arrow
+		39  : true, // right arrow
+		40  : true, // down arrow
+		45  : true, // insert
+		46  : true // delete
 	},
 
 	initialize: function (map) {
 		this._map = map;
-
-		this._setPanOffset(map.options.keyboardPanOffset);
-		this._setZoomOffset(map.options.keyboardZoomOffset);
 	},
 
 	addHooks: function () {
@@ -34,113 +105,58 @@ L.Map.Keyboard = L.Handler.extend({
 			container.tabIndex = '0';
 		}
 
-		L.DomEvent.on(container, {
-			focus: this._onFocus,
-			blur: this._onBlur,
-			mousedown: this._onMouseDown
-		}, this);
-
-		this._map.on({
-			focus: this._addHooks,
-			blur: this._removeHooks
-		}, this);
-	},
-
-	removeHooks: function () {
-		this._removeHooks();
-
-		L.DomEvent.off(this._map._container, {
-			focus: this._onFocus,
-			blur: this._onBlur,
-			mousedown: this._onMouseDown
-		}, this);
-
-		this._map.off({
-			focus: this._addHooks,
-			blur: this._removeHooks
-		}, this);
+		this._map.on('mousedown', this._onMouseDown, this);
+		this._map.on('keydown keyup keypress', this._onKeyDown, this);
 	},
 
 	_onMouseDown: function () {
-		if (this._focused) { return; }
-
-		var body = document.body,
-		    docEl = document.documentElement,
-		    top = body.scrollTop || docEl.scrollTop,
-		    left = body.scrollLeft || docEl.scrollLeft;
-
+		if (this._map._docLayer._permission === 'edit') {
+			return;
+		}
 		this._map._container.focus();
-
-		window.scrollTo(left, top);
-	},
-
-	_onFocus: function () {
-		this._focused = true;
-		this._map.fire('focus');
 	},
 
-	_onBlur: function () {
-		this._focused = false;
-		this._map.fire('blur');
+	// Convert javascript key codes to UNO key codes.
+	_toUNOKeyCode: function (keyCode) {
+		return this.keymap[keyCode] || keyCode;
 	},
 
-	_setPanOffset: function (pan) {
-		var keys = {},
-		    codes = this.keyCodes,
-		    i, len;
-
-		for (i = 0, len = codes.left.length; i < len; i++) {
-			keys[codes.left[i]] = [-1 * pan, 0];
-		}
-		for (i = 0, len = codes.right.length; i < len; i++) {
-			keys[codes.right[i]] = [pan, 0];
-		}
-		for (i = 0, len = codes.down.length; i < len; i++) {
-			keys[codes.down[i]] = [0, pan];
-		}
-		for (i = 0, len = codes.up.length; i < len; i++) {
-			keys[codes.up[i]] = [0, -1 * pan];
+	_onKeyDown: function (e) {
+		var docLayer = this._map._docLayer;
+		if (e.originalEvent.ctrlKey) {
+			// we prepare for a copy event
+			docLayer._textArea.value = 'dummy text';
+			docLayer._textArea.focus();
+			docLayer._textArea.select();
+			return;
 		}
-	},
 
-	_setZoomOffset: function (zoom) {
-		var keys = this._zoomKeys = {},
-		    codes = this.keyCodes,
-		    i, len;
+		if (docLayer._permission !== 'edit') {
+			return;
+		}
 
-		for (i = 0, len = codes.zoomIn.length; i < len; i++) {
-			keys[codes.zoomIn[i]] = zoom;
+		var charCode = e.originalEvent.charCode;
+		var keyCode = e.originalEvent.keyCode;
+		if (e.type === 'keydown' && this.handleOnKeyDown[keyCode] && charCode === 0) {
+			docLayer._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
 		}
-		for (i = 0, len = codes.zoomOut.length; i < len; i++) {
-			keys[codes.zoomOut[i]] = -zoom;
+		else if (e.type === 'keypress' &&
+				(!this.handleOnKeyDown[keyCode] || charCode !== 0)) {
+			if (charCode === keyCode && charCode !== 13) {
+				// Chrome sets keyCode = charCode for printable keys
+				// while LO requires it to be 0
+				keyCode = 0;
+			}
+			docLayer._postKeyboardEvent('input', charCode, this._toUNOKeyCode(keyCode));
 		}
-	},
-
-	_addHooks: function () {
-		L.DomEvent.on(document, 'keydown', this._onKeyDown, this);
-	},
-
-	_removeHooks: function () {
-		L.DomEvent.off(document, 'keydown', this._onKeyDown, this);
-	},
-
-	_onKeyDown: function (e) {
-		if (this._map._bDisableKeyboard || e.altKey || e.ctrlKey || e.metaKey) { return; }
-
-		var key = e.keyCode,
-		    map = this._map;
-
-		if (key in this._zoomKeys) {
-			map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
-
-		} else if (key === 27) {
-			map.closePopup();
-
-		} else {
-			return;
+		else if (e.type === 'keyup') {
+			docLayer._postKeyboardEvent('up', charCode, this._toUNOKeyCode(keyCode));
 		}
-
-		L.DomEvent.stop(e);
+		if (keyCode === 9) {
+			// tab would change focus to other DOM elements
+			e.originalEvent.preventDefault();
+		}
+		L.DomEvent.stopPropagation(e.originalEvent);
 	}
 });
 


More information about the Libreoffice-commits mailing list