loleaflet/README                              |   22 -
 loleaflet/build/deps.js                       |   18 
 loleaflet/spec/loadtest/LoadTestSpec.js       |   36 -
 loleaflet/spec/loleaflet/loleafletSpec.js     |   23 -
 loleaflet/spec/tilebench/TileBenchSpec.js     |   28 -
 loleaflet/src/control/Control.Parts.js        |    6 
 loleaflet/src/control/Control.PartsPreview.js |    8 
 loleaflet/src/control/Control.Tabs.js         |    4 
 loleaflet/src/control/Parts.js                |   44 +-
 loleaflet/src/core/Socket.js                  |   33 +
 loleaflet/src/layer/tile/CalcTileLayer.js     |  127 ++++++
 loleaflet/src/layer/tile/GridLayer.js         |   34 -
 loleaflet/src/layer/tile/ImpressTileLayer.js  |  157 ++++++++
 loleaflet/src/layer/tile/TileLayer.js         |  505 +++++++++++---------------
 loleaflet/src/layer/tile/WriterTileLayer.js   |  158 ++++++++
 15 files changed, 793 insertions(+), 410 deletions(-)

New commits:
commit 91b295fca58492122e157234c67b3971fdfc40f2
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 18:09:08 2015 +0300

    loleaflet: updated the tests to reflect the new map initialization

diff --git a/loleaflet/spec/loadtest/LoadTestSpec.js 
b/loleaflet/spec/loadtest/LoadTestSpec.js
index c339926..7be5091 100644
--- a/loleaflet/spec/loadtest/LoadTestSpec.js
+++ b/loleaflet/spec/loadtest/LoadTestSpec.js
@@ -15,16 +15,9 @@ describe('LoadTest', function () {
                if (docPath === 'file:///PATH') {
                        throw new Error('Document file path not set');
                }
-
-               map = L.map('map-test', {
-                       center: [0, 0],
-                       zoom: 10,
-                       minZoom: 1,
-                       maxZoom: 20,
-                       server: 'ws://localhost:9980',
-                       doubleClickZoom: false
-               });
-               map.on('docsize', function (e) { x = e.x; y = e.y; }, this);
+               else if (docPath[docPath.length - 1] !== '/') {
+                       docPath += '/';
+               }
        });
 
        var docPath = 'file:///PATH';
@@ -39,28 +32,23 @@ describe('LoadTest', function () {
                        });
 
                        after(function () {
-                               map.removeLayer(docLayer);
+                               map.remove();
                        });
 
                        it('Load the document', function (done) {
-                               map._initSocket();
-                               map.on('statusindicator', function (e) {
-                                       if (e.statusType === 'alltilesloaded') {
-                                               done();
-                                       }
-                               });
-
-                               docLayer = new L.TileLayer('', {
+                               map = L.map('map-test', {
+                                       server: 'ws://localhost:9980',
                                        doc: docPath + testDoc,
-                                       useSocket : true,
                                        edit: false,
                                        readOnly: false
                                });
 
-                               // don't pre-fetch tiles
-                               docLayer._preFetchTiles = L.Util.falseFn;
-
-                               map.addLayer(docLayer);
+                               map.on('statusindicator', function (e) {
+                                       if (e.statusType === 'alltilesloaded') {
+                                               y = map.getDocSize().y;
+                                               done();
+                                       }
+                               });
                        });
 
                        it('Scroll to the middle', function (done) {
diff --git a/loleaflet/spec/loleaflet/loleafletSpec.js 
b/loleaflet/spec/loleaflet/loleafletSpec.js
index 2d0edf5..fd16985 100644
--- a/loleaflet/spec/loleaflet/loleafletSpec.js
+++ b/loleaflet/spec/loleaflet/loleafletSpec.js
@@ -15,34 +15,11 @@ describe('TileBench', function () {
        before(function () {
                // initialize the map and load the document
                map = L.map('map', {
-                       center: [0, 0],
-                       zoom: 10,
-                       minZoom: 1,
-                       maxZoom: 20,
                        server: 'ws://localhost:9980',
-                       doubleClickZoom: false
-               });
-
-               var docLayer = new L.TileLayer('', {
                        doc: 'file:///home/mihai/Desktop/test_docs/eval.odt',
-                       useSocket : true,
                        edit: false,
                        readOnly: false
                });
-
-               docLayer.sendMessage = L.bind(function (msg, coords) {
-                       var now = Date.now();
-                       if (msg.startsWith('tile')) {
-                               msg += ' timestamp=' + now;
-                       }
-                       L.Log.log(msg, L.OUTGOING, coords, now);
-                       this._map.socket.send(msg);
-               }, docLayer);
-
-               // don't pre-fetch tiles
-               docLayer._preFetchTiles = L.Util.falseFn;
-
-               map.addLayer(docLayer);
        });
 
        afterEach(function () {
diff --git a/loleaflet/spec/tilebench/TileBenchSpec.js 
b/loleaflet/spec/tilebench/TileBenchSpec.js
index a15cdb1..463f2d2 100644
--- a/loleaflet/spec/tilebench/TileBenchSpec.js
+++ b/loleaflet/spec/tilebench/TileBenchSpec.js
@@ -16,36 +16,23 @@ describe('TileBench', function () {
        before(function () {
                // initialize the map and load the document
                map = L.map('map', {
-                       center: [0, 0],
-                       zoom: 10,
-                       minZoom: 1,
-                       maxZoom: 20,
                        server: 'ws://localhost:9980',
-                       doubleClickZoom: false
-               });
-
-               var docLayer = new L.TileLayer('', {
                        doc: 'file:///home/mihai/Desktop/test_docs/eval.odt',
-                       useSocket : true,
                        edit: false,
                        readOnly: false
                });
 
                // add a timestamp to tile messages so we can identify
                // the response
-               docLayer.sendMessage = L.bind(function (msg, coords) {
+               L.Socket.sendMessage = L.bind(function (msg, coords) {
                        var now = Date.now();
                        if (msg.startsWith('tile')) {
                                msg += ' timestamp=' + now;
                        }
                        L.Log.log(msg, L.OUTGOING, coords, now);
-                       this._map.socket.send(msg);
-               }, docLayer);
-
-               // don't pre-fetch tiles
-               docLayer._preFetchTiles = L.Util.falseFn;
+                       this.socket.send(msg);
+               }, L.Socket);
 
-               map.addLayer(docLayer);
                map.addControl(L.control.scroll());
        });
 
@@ -54,15 +41,14 @@ describe('TileBench', function () {
        });
 
        after(function () {
-               map.socket.onclose = undefined;
-               map.socket.onerror = undefined;
-               map.socket.close();
+               map.remove();
        });
 
        describe('Benchmarking', function () {
                it('Load all new tiles', function (done) {
                        map.on('statusindicator', function (e) {
                                if (e.statusType === 'alltilesloaded') {
+                                       map.fire('requestloksession');
                                        done();
                                }
                        });
@@ -90,7 +76,7 @@ describe('TileBench', function () {
                                var y = Math.floor(docLayer._docHeightTwips / 
docLayer._tileHeightTwips);
                                var coords = new L.Point(x, y);
                                coords.z = map.getZoom();
-                               coords.part = docLayer._currentPart;
+                               coords.part = docLayer._selectedPart;
                                var key = docLayer._tileCoordsToKey(coords);
                                if (docLayer._tiles[key]) {
                                        // the tile is already here, the whole 
document is loaded
@@ -102,7 +88,7 @@ describe('TileBench', function () {
 
                        for (var i = 0; i < keyInput.length; i++) {
                                setTimeout(L.bind(function () {
-                                       
map._docLayer.sendMessage(keyInput[this][1]);
+                                       L.Socket.sendMessage(keyInput[this][1]);
                                }, i), keyInput[i][0]);
                        }
                });
commit c7672438f032b8b30b2101ca3833e31586dbf65c
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 17:23:33 2015 +0300

    loleaflet: autoupdate previews from the preview control

diff --git a/loleaflet/src/control/Control.PartsPreview.js 
b/loleaflet/src/control/Control.PartsPreview.js
index 1bec8a4..4112d01 100644
--- a/loleaflet/src/control/Control.PartsPreview.js
+++ b/loleaflet/src/control/Control.PartsPreview.js
@@ -3,6 +3,10 @@
  */
 
 L.Control.PartsPreview = L.Control.extend({
+       options: {
+               autoUpdate: true
+       },
+
        onAdd: function (map) {
                this._previewInitialized = false;
                this._previewTiles = {};
@@ -42,7 +46,7 @@ L.Control.PartsPreview = L.Control.extend({
                                        .on(img, 'click', L.DomEvent.stop)
                                        .on(img, 'click', this._setPart, this)
                                        .on(img, 'click', this._refocusOnMap, 
this);
-                               this._map.getPartPreview(i, i, 180, 180);
+                               this._map.getPartPreview(i, i, 180, 180, 
{autoUpdate: this.options.autoUpdate});
                        }
                        this._previewInitialized = true;
                }
@@ -57,7 +61,7 @@ L.Control.PartsPreview = L.Control.extend({
 
        _updatePart: function (e) {
                if (e.docType === 'presentation') {
-                       this._map.getPartPreview(e.part, e.part, 180, 180);
+                       this._map.getPartPreview(e.part, e.part, 180, 180, 
{autoUpdate: this.options.autoUpdate});
                }
        },
 
commit dc617eb7009ef1e5ae0a27495fa0287a57a9e125
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 17:21:35 2015 +0300

    loleaflet: impress preview invalidation

diff --git a/loleaflet/src/layer/tile/ImpressTileLayer.js 
b/loleaflet/src/layer/tile/ImpressTileLayer.js
index 2a437ef..2acc5c3 100644
--- a/loleaflet/src/layer/tile/ImpressTileLayer.js
+++ b/loleaflet/src/layer/tile/ImpressTileLayer.js
@@ -86,6 +86,10 @@ L.ImpressTileLayer = L.TileLayer.extend({
                        this._lastValidPart = command.part;
                        this._map.fire('updatepart', {part: command.part, 
docType: this._docType});
                }
+
+               // 1s after the last invalidation, update the preview
+               clearTimeout(this._previewInvalidator);
+               this._previewInvalidator = 
setTimeout(L.bind(this._invalidatePreview, this), 1000);
        },
 
        _onSetPartMsg: function (textMsg) {
@@ -124,6 +128,30 @@ L.ImpressTileLayer = L.TileLayer.extend({
                                this._preFetchPart = this._selectedPart;
                                this._preFetchBorder = null;
                        }
-        }
+               }
+       },
+
+       _invalidatePreview: function () {
+               if (this._map._docPreviews) {
+                       // invalidate part previews
+                       for (var key in this._map._docPreviews) {
+                               var preview = this._map._docPreviews[key];
+                               if (preview.part === this._selectedPart ||
+                                       (preview.part === 
this._prevSelectedPart && this._prevSelectedPartNeedsUpdate)) {
+                                       // if the current part needs its 
preview updated OR
+                                       // the part has been changed and we 
need to update the previous part preview
+                                       if (preview.part === 
this._prevSelectedPart) {
+                                               
this._prevSelectedPartNeedsUpdate = false;
+                                       }
+                                       if (preview.autoUpdate) {
+                                               
this._map.getPartPreview(preview.id, preview.part, preview.maxWidth, 
preview.maxHeight,
+                                                               {autoUpdate: 
true});
+                                       }
+                                       else {
+                                               
this._map.fire('invalidatepreview', {id: preview.id});
+                                       }
+                               }
+                       }
+               }
        }
 });
commit 5e843d23814c055a4da3b94625ecaf0d63ba23b4
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 17:21:19 2015 +0300

    loleaflet: writer preview invalidation

diff --git a/loleaflet/src/layer/tile/WriterTileLayer.js 
b/loleaflet/src/layer/tile/WriterTileLayer.js
index 68260a1..fe85e9d 100644
--- a/loleaflet/src/layer/tile/WriterTileLayer.js
+++ b/loleaflet/src/layer/tile/WriterTileLayer.js
@@ -79,6 +79,13 @@ L.WriterTileLayer = L.TileLayer.extend({
                                delete this._tileCache[key];
                        }
                }
+               if (!this._previewInvalidations) {
+                       this._previewInvalidations = [];
+               }
+               this._previewInvalidations.push(invalidBounds);
+               // 1s after the last invalidation, update the preview
+               clearTimeout(this._previewInvalidator);
+               this._previewInvalidator = 
setTimeout(L.bind(this._invalidatePreview, this), 1000);
        },
 
        _onSetPartMsg: function (textMsg) {
@@ -111,5 +118,41 @@ L.WriterTileLayer = L.TileLayer.extend({
                        this._resetPreFetching(true);
                        this._update();
                }
+       },
+
+       _invalidatePreview: function () {
+               // invalidate writer page previews
+               if (this._map._docPreviews && this._previewInvalidations) {
+                       var toInvalidate = {};
+                       for (var i = 0; i < this._previewInvalidations.length; 
i++) {
+                               var invalidBounds = 
this._previewInvalidations[i];
+                               var invalidPixBounds = new L.Bounds(
+                                               
invalidBounds.min.divideBy(this.options.tileWidthTwips).multiplyBy(this._tileSize),
+                                               
invalidBounds.max.divideBy(this.options.tileWidthTwips).multiplyBy(this._tileSize));
+
+                               for (var key in this._map._docPreviews) {
+                                       // find preview tiles that need to be 
updated and add them in a set
+                                       var preview = 
this._map._docPreviews[key];
+                                       var bounds = new L.Bounds(new 
L.Point(preview.x, preview.y),
+                                                                               
          new L.Point(preview.x + preview.width, preview.y + preview.height));
+                                       if 
(invalidPixBounds.intersects(bounds)) {
+                                               toInvalidate[key] = true;
+                                       }
+                               }
+
+                               for (key in toInvalidate) {
+                                       // update invalid preview tiles
+                                       preview = this._map._docPreviews[key];
+                                       if (preview.autoUpdate) {
+                                               
this._map.getDocPreview(preview.id, preview.maxWidth, preview.maxHeight,
+                                                               preview.x, 
preview.y, preview.width, preview.height, {autoUpdate: true});
+                                       }
+                                       else {
+                                               
this._map.fire('invalidatepreview', {id: preview.id});
+                                       }
+                               }
+                       }
+               }
+               this._previewInvalidations = [];
        }
 });
commit 2a2eb14532b914ebcde09e5ccf9063105170d36f
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 17:12:02 2015 +0300

    loleaflet: option for automatically updating the previews

diff --git a/loleaflet/README b/loleaflet/README
index f5b3488..dbf59a7 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -90,10 +90,11 @@ Buttons like Bold, Italic, Strike through etc.
 Parts (like slides in presentation, or sheets in spreadsheets):
     - API:
         map.setPart('next' | 'prev' | partNumber)
-        map.getPartPreview(id, part, maxWidth, maxHeight) where:
+        map.getPartPreview(id, part, maxWidth, maxHeight, [options]) 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
+            + options = {autoUpdate: true} - automatically updates the previews
         map.getNumberOfParts()
         map.getCurrentPartNumber()
     - events:
@@ -102,6 +103,8 @@ Parts (like slides in presentation, or sheets in 
spreadsheets):
             + 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)
+        map.on('invalidatepreview', function (e) {})
+            + e.id = the preview's id
 
 Statusindicator (when the document is loading):
     - events
@@ -161,17 +164,20 @@ Writer pages:
         map.goToPage(page)
         map.getNumberOfPages()
         map.getCurrentPageNumber()
-        map.getDocPreview(id, maxWidth, maxHeight, x, y, width, height)
+        map.getDocPreview(id, maxWidth, maxHeight, x, y, width, height, 
[options])
             + 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
             + x/y = starting position, where to get the preview from
+            + options = {autoUpdate: true} - automatically updates the previews
 
     - events
         map.on('pagenumberchanged', function (e) {}) where:
             + e.currentPage = the page on which the cursor lies
             + e.pages = number of pages
             + e.docType = document type, should be 'text'
+        map.on('invalidatepreview', function (e) {})
+            + e.id = the preview's id
 
 Styles:
     - API:
diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index 08e1c89..040d909 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -4,6 +4,7 @@
 L.Map.include({
        setPart: function (part) {
                var docLayer = this._docLayer;
+               docLayer._prevSelectedPart = docLayer._selectedPart;
                if (part === 'prev') {
                        if (docLayer._selectedPart > 0) {
                                docLayer._selectedPart -= 1;
@@ -33,9 +34,19 @@ L.Map.include({
                docLayer._update();
                docLayer._pruneTiles();
                docLayer._clearSelections();
+               docLayer._prevSelectedPartNeedsUpdate = true;
+               if (docLayer._invalidatePreview) {
+                       docLayer._invalidatePreview();
+               }
        },
 
-       getPartPreview: function (id, part, maxWidth, maxHeight) {
+       getPartPreview: function (id, part, maxWidth, maxHeight, options) {
+               if (!this._docPreviews) {
+                       this._docPreviews = {};
+               }
+               var autoUpdate = options ? options.autoUpdate : false;
+               this._docPreviews[id] = {id: id, part: part, maxWidth: 
maxWidth, maxHeight: maxHeight, autoUpdate: autoUpdate};
+
                var docLayer = this._docLayer;
                var docRatio = docLayer._docWidthTwips / 
docLayer._docHeightTwips;
                var imgRatio = maxWidth / maxHeight;
@@ -56,7 +67,14 @@ L.Map.include({
                                                        'id=' + id);
        },
 
-       getDocPreview: function (id, maxWidth, maxHeight, x, y, width, height) {
+       getDocPreview: function (id, maxWidth, maxHeight, x, y, width, height, 
options) {
+               if (!this._docPreviews) {
+                       this._docPreviews = {};
+               }
+               var autoUpdate = options ? options.autoUpdate : false;
+               this._docPreviews[id] = {id: id, maxWidth: maxWidth, maxHeight: 
maxHeight, x: x, y: y,
+                       width: width, height: height, autoUpdate: autoUpdate};
+
                var docLayer = this._docLayer;
                var docRatio = width / height;
                var imgRatio = maxWidth / maxHeight;
@@ -83,6 +101,12 @@ L.Map.include({
                                                        'id=' + id);
        },
 
+       removePreviewUpdate: function (id) {
+               if (this._docPreviews && this._docPreviews[id]) {
+                       this._docPreviews[id].autoUpdate = false;
+               }
+       },
+
        goToPage: function (page) {
                var docLayer = this._docLayer;
                if (page === 'prev') {
commit f8aab94e49a5dbcfa565107e4af4f1aacf9939c4
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 15:14:00 2015 +0300

    loleaflet: setPart msg is now handled separately

diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js 
b/loleaflet/src/layer/tile/CalcTileLayer.js
index 4a0d1fb..7550ddb 100644
--- a/loleaflet/src/layer/tile/CalcTileLayer.js
+++ b/loleaflet/src/layer/tile/CalcTileLayer.js
@@ -86,6 +86,16 @@ L.CalcTileLayer = L.TileLayer.extend({
                }
        },
 
+       _onSetPartMsg: function (textMsg) {
+               var part = parseInt(textMsg.match(/\d+/g)[0]);
+               if (part !== this._selectedPart) {
+                       this._selectedPart = part;
+                       this._update();
+                       this._clearSelections();
+                       this._map.fire('setpart', {selectedPart: 
this._selectedPart});
+               }
+       },
+
        _onStatusMsg: function (textMsg) {
                var command = L.Socket.parseServerCmd(textMsg);
                if (command.width && command.height && this._documentInfo !== 
textMsg) {
@@ -106,6 +116,7 @@ L.CalcTileLayer = L.TileLayer.extend({
                                docType: this._docType,
                                partNames: partNames
                        });
+                       this._resetPreFetching(true);
                        this._update();
                        if (this._preFetchPart !== this._selectedPart) {
                                this._preFetchPart = this._selectedPart;
diff --git a/loleaflet/src/layer/tile/ImpressTileLayer.js 
b/loleaflet/src/layer/tile/ImpressTileLayer.js
index d9eddf1..2a437ef 100644
--- a/loleaflet/src/layer/tile/ImpressTileLayer.js
+++ b/loleaflet/src/layer/tile/ImpressTileLayer.js
@@ -88,31 +88,42 @@ L.ImpressTileLayer = L.TileLayer.extend({
                }
        },
 
+       _onSetPartMsg: function (textMsg) {
+               var part = parseInt(textMsg.match(/\d+/g)[0]);
+               if (part !== this._selectedPart) {
+                       this._selectedPart = part;
+                       this._update();
+                       this._clearSelections();
+                       this._map.fire('setpart', {selectedPart: 
this._selectedPart});
+               }
+       },
+
        _onStatusMsg: function (textMsg) {
                var command = L.Socket.parseServerCmd(textMsg);
                if (command.width && command.height && this._documentInfo !== 
textMsg) {
                        this._docWidthTwips = command.width;
-            this._docHeightTwips = command.height;
-            this._docType = command.type;
-            this._updateMaxBounds(true);
-            this._documentInfo = textMsg;
-            this._parts = command.parts;
-            this._selectedPart = command.selectedPart;
-            L.Socket.sendMessage('setclientpart part=' + this._selectedPart);
-            var partNames = textMsg.match(/[^\r\n]+/g);
-            // only get the last matches
-            partNames = partNames.slice(partNames.length - this._parts);
-            this._map.fire('updateparts', {
-                selectedPart: this._selectedPart,
-                parts: this._parts,
-                docType: this._docType,
-                partNames: partNames
-            });
-            this._update();
-            if (this._preFetchPart !== this._selectedPart) {
-                this._preFetchPart = this._selectedPart;
-                this._preFetchBorder = null;
-            }
+                       this._docHeightTwips = command.height;
+                       this._docType = command.type;
+                       this._updateMaxBounds(true);
+                       this._documentInfo = textMsg;
+                       this._parts = command.parts;
+                       this._selectedPart = command.selectedPart;
+                       L.Socket.sendMessage('setclientpart part=' + 
this._selectedPart);
+                       var partNames = textMsg.match(/[^\r\n]+/g);
+                       // only get the last matches
+                       partNames = partNames.slice(partNames.length - 
this._parts);
+                       this._map.fire('updateparts', {
+                               selectedPart: this._selectedPart,
+                               parts: this._parts,
+                               docType: this._docType,
+                               partNames: partNames
+                       });
+                       this._resetPreFetching(true);
+                       this._update();
+                       if (this._preFetchPart !== this._selectedPart) {
+                               this._preFetchPart = this._selectedPart;
+                               this._preFetchBorder = null;
+                       }
         }
        }
 });
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 2de696d..97ced2f 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -271,24 +271,6 @@ L.TileLayer = L.GridLayer.extend({
                this._map.fire('search', {originalPhrase: originalPhrase, 
count: 0});
        },
 
-       _onSetPartMsg: function (textMsg) {
-               var part = parseInt(textMsg.match(/\d+/g)[0]);
-               if (part !== this._selectedPart && this._docType !== 'text') {
-                       this._selectedPart = part;
-                       this._update();
-                       this._clearSelections();
-                       this._map.fire('setpart', {selectedPart: 
this._selectedPart});
-               }
-               else if (this._docType === 'text') {
-                       this._currentPage = part;
-                       this._map.fire('pagenumberchanged', {
-                               currentPage: part,
-                               pages: this._pages,
-                               docType: this._docType
-                       });
-               }
-       },
-
        _onStateChangedMsg: function (textMsg) {
                var unoMsg = textMsg.substr(14);
                var unoCmd = unoMsg.match('.uno:(.*)=')[1];
diff --git a/loleaflet/src/layer/tile/WriterTileLayer.js 
b/loleaflet/src/layer/tile/WriterTileLayer.js
index 178c8e6..68260a1 100644
--- a/loleaflet/src/layer/tile/WriterTileLayer.js
+++ b/loleaflet/src/layer/tile/WriterTileLayer.js
@@ -81,9 +81,18 @@ L.WriterTileLayer = L.TileLayer.extend({
                }
        },
 
+       _onSetPartMsg: function (textMsg) {
+               var part = parseInt(textMsg.match(/\d+/g)[0]);
+               this._currentPage = part;
+               this._map.fire('pagenumberchanged', {
+                       currentPage: part,
+                       pages: this._pages,
+                       docType: this._docType
+               });
+       },
+
        _onStatusMsg: function (textMsg) {
                var command = L.Socket.parseServerCmd(textMsg);
-               console.log(textMsg);
                if (command.width && command.height && this._documentInfo !== 
textMsg) {
                        this._docWidthTwips = command.width;
                        this._docHeightTwips = command.height;
@@ -99,6 +108,7 @@ L.WriterTileLayer = L.TileLayer.extend({
                                pages: this._pages,
                                docType: this._docType
                        });
+                       this._resetPreFetching(true);
                        this._update();
                }
        }
commit 58862970c38496304292b0dfabffaadc43cc5a10
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 14:28:02 2015 +0300

    loleaflet: the status msg is handled now separately

diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js 
b/loleaflet/src/layer/tile/CalcTileLayer.js
index 687d03a..4a0d1fb 100644
--- a/loleaflet/src/layer/tile/CalcTileLayer.js
+++ b/loleaflet/src/layer/tile/CalcTileLayer.js
@@ -84,5 +84,33 @@ L.CalcTileLayer = L.TileLayer.extend({
                                delete this._tileCache[key];
                        }
                }
+       },
+
+       _onStatusMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.width && command.height && this._documentInfo !== 
textMsg) {
+                       this._docWidthTwips = command.width;
+                       this._docHeightTwips = command.height;
+                       this._docType = command.type;
+                       this._updateMaxBounds(true);
+                       this._documentInfo = textMsg;
+                       this._parts = command.parts;
+                       this._selectedPart = command.selectedPart;
+                       L.Socket.sendMessage('setclientpart part=' + 
this._selectedPart);
+                       var partNames = textMsg.match(/[^\r\n]+/g);
+                       // only get the last matches
+                       partNames = partNames.slice(partNames.length - 
this._parts);
+                       this._map.fire('updateparts', {
+                               selectedPart: this._selectedPart,
+                               parts: this._parts,
+                               docType: this._docType,
+                               partNames: partNames
+                       });
+                       this._update();
+                       if (this._preFetchPart !== this._selectedPart) {
+                               this._preFetchPart = this._selectedPart;
+                               this._preFetchBorder = null;
+                       }
+               }
        }
 });
diff --git a/loleaflet/src/layer/tile/ImpressTileLayer.js 
b/loleaflet/src/layer/tile/ImpressTileLayer.js
index ba2f765..d9eddf1 100644
--- a/loleaflet/src/layer/tile/ImpressTileLayer.js
+++ b/loleaflet/src/layer/tile/ImpressTileLayer.js
@@ -86,5 +86,33 @@ L.ImpressTileLayer = L.TileLayer.extend({
                        this._lastValidPart = command.part;
                        this._map.fire('updatepart', {part: command.part, 
docType: this._docType});
                }
+       },
+
+       _onStatusMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.width && command.height && this._documentInfo !== 
textMsg) {
+                       this._docWidthTwips = command.width;
+            this._docHeightTwips = command.height;
+            this._docType = command.type;
+            this._updateMaxBounds(true);
+            this._documentInfo = textMsg;
+            this._parts = command.parts;
+            this._selectedPart = command.selectedPart;
+            L.Socket.sendMessage('setclientpart part=' + this._selectedPart);
+            var partNames = textMsg.match(/[^\r\n]+/g);
+            // only get the last matches
+            partNames = partNames.slice(partNames.length - this._parts);
+            this._map.fire('updateparts', {
+                selectedPart: this._selectedPart,
+                parts: this._parts,
+                docType: this._docType,
+                partNames: partNames
+            });
+            this._update();
+            if (this._preFetchPart !== this._selectedPart) {
+                this._preFetchPart = this._selectedPart;
+                this._preFetchBorder = null;
+            }
+        }
        }
 });
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 205163f..2de696d 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -298,47 +298,6 @@ L.TileLayer = L.GridLayer.extend({
                }
        },
 
-       _onStatusMsg: function (textMsg) {
-               var command = L.Socket.parseServerCmd(textMsg);
-               if (command.width && command.height && this._documentInfo !== 
textMsg) {
-                       this._docWidthTwips = command.width;
-                       this._docHeightTwips = command.height;
-                       this._docType = command.type;
-                       this._updateMaxBounds(true);
-                       this._documentInfo = textMsg;
-                       this._parts = command.parts;
-                       this._selectedPart = command.selectedPart;
-                       if (this._docType === 'text') {
-                               this._selectedPart = 0;
-                               this._parts = 1;
-                               this._currentPage = command.selectedPart;
-                               this._pages = command.parts;
-                               this._map.fire('pagenumberchanged', {
-                                       currentPage: this._currentPage,
-                                       pages: this._pages,
-                                       docType: this._docType
-                               });
-                       }
-                       else {
-                               L.Socket.sendMessage('setclientpart part=' + 
this._selectedPart);
-                               var partNames = textMsg.match(/[^\r\n]+/g);
-                               // only get the last matches
-                               partNames = partNames.slice(partNames.length - 
this._parts);
-                               this._map.fire('updateparts', {
-                                       selectedPart: this._selectedPart,
-                                       parts: this._parts,
-                                       docType: this._docType,
-                                       partNames: partNames
-                               });
-                       }
-                       this._update();
-                       if (this._preFetchPart !== this._selectedPart) {
-                               this._preFetchPart = this._selectedPart;
-                               this._preFetchBorder = null;
-                       }
-               }
-       },
-
        _onStatusIndicatorMsg: function (textMsg) {
                if (textMsg.startsWith('statusindicatorstart:')) {
                        this._map.fire('statusindicator', {statusType : 
'start'});
diff --git a/loleaflet/src/layer/tile/WriterTileLayer.js 
b/loleaflet/src/layer/tile/WriterTileLayer.js
index e2dde74..178c8e6 100644
--- a/loleaflet/src/layer/tile/WriterTileLayer.js
+++ b/loleaflet/src/layer/tile/WriterTileLayer.js
@@ -79,5 +79,27 @@ L.WriterTileLayer = L.TileLayer.extend({
                                delete this._tileCache[key];
                        }
                }
+       },
+
+       _onStatusMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               console.log(textMsg);
+               if (command.width && command.height && this._documentInfo !== 
textMsg) {
+                       this._docWidthTwips = command.width;
+                       this._docHeightTwips = command.height;
+                       this._docType = command.type;
+                       this._updateMaxBounds(true);
+                       this._documentInfo = textMsg;
+                       this._selectedPart = 0;
+                       this._parts = 1;
+                       this._currentPage = command.selectedPart;
+                       this._pages = command.parts;
+                       this._map.fire('pagenumberchanged', {
+                               currentPage: this._currentPage,
+                               pages: this._pages,
+                               docType: this._docType
+                       });
+                       this._update();
+               }
        }
 });
commit bbc65ebeb0f58d98fe847660c675be3867df0697
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 14:27:29 2015 +0300

    loleaflet: fixed typo

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index fc4a18d..c35d667 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -63,7 +63,7 @@ var deps = {
                deps: ['TileLayer']
        },
 
-       WriterTileLayer: {
+       CalcTileLayer: {
                src: ['layer/tile/CalcTileLayer.js'],
                desc: 'Calc tile layer.',
                deps: ['TileLayer']
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index b25cf1c..b9bf086 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -90,13 +90,13 @@ L.Socket = {
                        // first status message, we need to create the document 
layer
                        var command = this.parseServerCmd(textMsg);
                        var docLayer = null;
-                       if (command.style === 'text') {
+                       if (command.type === 'text') {
                                docLayer = new L.WriterTileLayer('', {
                                        edit: this._map.options.edit,
                                        readOnly: this._map.options.readOnly
                                });
                        }
-                       else if (command.style === 'spreadsheet') {
+                       else if (command.type === 'spreadsheet') {
                                docLayer = new L.CalcTileLayer('', {
                                        edit: this._map.options.edit,
                                        readOnly: this._map.options.readOnly
commit 7d1625e93813f73ccff2ff61bbfef26409e5d260
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 13:57:34 2015 +0300

    loleaflet: tile invalidation is now handled in the 3 components

diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js 
b/loleaflet/src/layer/tile/CalcTileLayer.js
index 1a1795a..687d03a 100644
--- a/loleaflet/src/layer/tile/CalcTileLayer.js
+++ b/loleaflet/src/layer/tile/CalcTileLayer.js
@@ -3,4 +3,86 @@
  */
 
 L.CalcTileLayer = L.TileLayer.extend({
+
+       _onInvalidateTilesMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.x === undefined || command.y === undefined || 
command.part === undefined) {
+                       var strTwips = textMsg.match(/\d+/g);
+                       command.x = parseInt(strTwips[0]);
+                       command.y = parseInt(strTwips[1]);
+                       command.width = parseInt(strTwips[2]);
+                       command.height = parseInt(strTwips[3]);
+                       command.part = this._currentPart;
+               }
+               if (this._docType === 'text') {
+                       command.part = 0;
+               }
+               var topLeftTwips = new L.Point(command.x, command.y);
+               var offset = new L.Point(command.width, command.height);
+               var bottomRightTwips = topLeftTwips.add(offset);
+               var invalidBounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
+               var visibleTopLeft = 
this._latLngToTwips(this._map.getBounds().getNorthWest());
+               var visibleBottomRight = 
this._latLngToTwips(this._map.getBounds().getSouthEast());
+               var visibleArea = new L.Bounds(visibleTopLeft, 
visibleBottomRight);
+               var toRequest = [];
+
+               for (var key in this._tiles) {
+                       var coords = this._tiles[key].coords;
+                       var tileTopLeft = this._coordsToTwips(coords);
+                       var tileBottomRight = new L.Point(this._tileWidthTwips, 
this._tileHeightTwips);
+                       var bounds = new L.Bounds(tileTopLeft, 
tileTopLeft.add(tileBottomRight));
+                       if (invalidBounds.intersects(bounds) && coords.part === 
command.part) {
+                               if (this._tiles[key]._invalidCount) {
+                                       this._tiles[key]._invalidCount += 1;
+                               }
+                               else {
+                                       this._tiles[key]._invalidCount = 1;
+                               }
+                               if (visibleArea.intersects(bounds)) {
+                                       var msg = 'tile ' +
+                                                       'part=' + coords.part + 
' ' +
+                                                       'width=' + 
this._tileSize + ' ' +
+                                                       'height=' + 
this._tileSize + ' ' +
+                                                       'tileposx=' + 
tileTopLeft.x + ' '    +
+                                                       'tileposy=' + 
tileTopLeft.y + ' ' +
+                                                       'tilewidth=' + 
this._tileWidthTwips + ' ' +
+                                                       'tileheight=' + 
this._tileHeightTwips;
+                                       toRequest.push({msg: msg, key: key, 
coords: coords});
+                               }
+                               else {
+                                       // tile outside of the visible area, 
just remove it
+                                       this._preFetchBorder = null;
+                                       this._removeTile(key);
+                               }
+                       }
+               }
+
+               // Sort tiles so that we request those closer to the cursor 
first
+               var cursorPos = 
this._map.project(this._visibleCursor.getNorthWest());
+               cursorPos = cursorPos.divideBy(this._tileSize);
+               toRequest.sort(function(x, y) {return 
x.coords.distanceTo(cursorPos) - y.coords.distanceTo(cursorPos);});
+               for (var i = 0; i < toRequest.length; i++) {
+                       L.Socket.sendMessage(toRequest[i].msg, 
toRequest[i].key);
+               }
+
+               for (key in this._tileCache) {
+                       // compute the rectangle that each tile covers in the 
document based
+                       // on the zoom level
+                       coords = this._keyToTileCoords(key);
+                       if (coords.part !== command.part) {
+                               continue;
+                       }
+                       var scale = this._map.getZoomScale(coords.z);
+                       topLeftTwips = new L.Point(
+                                       this.options.tileWidthTwips / scale * 
coords.x,
+                                       this.options.tileHeightTwips / scale * 
coords.y);
+                       bottomRightTwips = topLeftTwips.add(new L.Point(
+                                       this.options.tileWidthTwips / scale,
+                                       this.options.tileHeightTwips / scale));
+                       bounds = new L.Bounds(topLeftTwips, bottomRightTwips);
+                       if (invalidBounds.intersects(bounds)) {
+                               delete this._tileCache[key];
+                       }
+               }
+       }
 });
diff --git a/loleaflet/src/layer/tile/ImpressTileLayer.js 
b/loleaflet/src/layer/tile/ImpressTileLayer.js
index a2d1fbf..ba2f765 100644
--- a/loleaflet/src/layer/tile/ImpressTileLayer.js
+++ b/loleaflet/src/layer/tile/ImpressTileLayer.js
@@ -2,6 +2,89 @@
  * Impress tile layer is used to display a presentation document
  */
 
-
 L.ImpressTileLayer = L.TileLayer.extend({
+
+       _onInvalidateTilesMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.x === undefined || command.y === undefined || 
command.part === undefined) {
+                       var strTwips = textMsg.match(/\d+/g);
+                       command.x = parseInt(strTwips[0]);
+                       command.y = parseInt(strTwips[1]);
+                       command.width = parseInt(strTwips[2]);
+                       command.height = parseInt(strTwips[3]);
+                       command.part = this._currentPart;
+               }
+               var topLeftTwips = new L.Point(command.x, command.y);
+               var offset = new L.Point(command.width, command.height);
+               var bottomRightTwips = topLeftTwips.add(offset);
+               var invalidBounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
+               var visibleTopLeft = 
this._latLngToTwips(this._map.getBounds().getNorthWest());
+               var visibleBottomRight = 
this._latLngToTwips(this._map.getBounds().getSouthEast());
+               var visibleArea = new L.Bounds(visibleTopLeft, 
visibleBottomRight);
+               var toRequest = [];
+
+               for (var key in this._tiles) {
+                       var coords = this._tiles[key].coords;
+                       var tileTopLeft = this._coordsToTwips(coords);
+                       var tileBottomRight = new L.Point(this._tileWidthTwips, 
this._tileHeightTwips);
+                       var bounds = new L.Bounds(tileTopLeft, 
tileTopLeft.add(tileBottomRight));
+                       if (invalidBounds.intersects(bounds) && coords.part === 
command.part) {
+                               if (this._tiles[key]._invalidCount) {
+                                       this._tiles[key]._invalidCount += 1;
+                               }
+                               else {
+                                       this._tiles[key]._invalidCount = 1;
+                               }
+                               if (visibleArea.intersects(bounds)) {
+                                       var msg = 'tile ' +
+                                                       'part=' + coords.part + 
' ' +
+                                                       'width=' + 
this._tileSize + ' ' +
+                                                       'height=' + 
this._tileSize + ' ' +
+                                                       'tileposx=' + 
tileTopLeft.x + ' '    +
+                                                       'tileposy=' + 
tileTopLeft.y + ' ' +
+                                                       'tilewidth=' + 
this._tileWidthTwips + ' ' +
+                                                       'tileheight=' + 
this._tileHeightTwips;
+                                       toRequest.push({msg: msg, key: key, 
coords: coords});
+                               }
+                               else {
+                                       // tile outside of the visible area, 
just remove it
+                                       this._preFetchBorder = null;
+                                       this._removeTile(key);
+                               }
+                       }
+               }
+
+               // Sort tiles so that we request those closer to the cursor 
first
+               var cursorPos = 
this._map.project(this._visibleCursor.getNorthWest());
+               cursorPos = cursorPos.divideBy(this._tileSize);
+               toRequest.sort(function(x, y) {return 
x.coords.distanceTo(cursorPos) - y.coords.distanceTo(cursorPos);});
+               for (var i = 0; i < toRequest.length; i++) {
+                       L.Socket.sendMessage(toRequest[i].msg, 
toRequest[i].key);
+               }
+
+               for (key in this._tileCache) {
+                       // compute the rectangle that each tile covers in the 
document based
+                       // on the zoom level
+                       coords = this._keyToTileCoords(key);
+                       if (coords.part !== command.part) {
+                               continue;
+                       }
+                       var scale = this._map.getZoomScale(coords.z);
+                       topLeftTwips = new L.Point(
+                                       this.options.tileWidthTwips / scale * 
coords.x,
+                                       this.options.tileHeightTwips / scale * 
coords.y);
+                       bottomRightTwips = topLeftTwips.add(new L.Point(
+                                       this.options.tileWidthTwips / scale,
+                                       this.options.tileHeightTwips / scale));
+                       bounds = new L.Bounds(topLeftTwips, bottomRightTwips);
+                       if (invalidBounds.intersects(bounds)) {
+                               delete this._tileCache[key];
+                       }
+               }
+               if (command.part === this._currentPart &&
+                       command.part !== this._lastValidPart) {
+                       this._lastValidPart = command.part;
+                       this._map.fire('updatepart', {part: command.part, 
docType: this._docType});
+               }
+       }
 });
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 6029884..205163f 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -266,94 +266,6 @@ L.TileLayer = L.GridLayer.extend({
                this._onUpdateCursor();
        },
 
-       _onInvalidateTilesMsg: function (textMsg) {
-               var command = L.Socket.parseServerCmd(textMsg);
-               if (command.x === undefined || command.y === undefined || 
command.part === undefined) {
-                       var strTwips = textMsg.match(/\d+/g);
-                       command.x = parseInt(strTwips[0]);
-                       command.y = parseInt(strTwips[1]);
-                       command.width = parseInt(strTwips[2]);
-                       command.height = parseInt(strTwips[3]);
-                       command.part = this._selectedPart;
-               }
-               if (this._docType === 'text') {
-                       command.part = 0;
-               }
-               var topLeftTwips = new L.Point(command.x, command.y);
-               var offset = new L.Point(command.width, command.height);
-               var bottomRightTwips = topLeftTwips.add(offset);
-               var invalidBounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
-               var visibleTopLeft = 
this._latLngToTwips(this._map.getBounds().getNorthWest());
-               var visibleBottomRight = 
this._latLngToTwips(this._map.getBounds().getSouthEast());
-               var visibleArea = new L.Bounds(visibleTopLeft, 
visibleBottomRight);
-               var toRequest = [];
-
-               for (var key in this._tiles) {
-                       var coords = this._tiles[key].coords;
-                       var tileTopLeft = this._coordsToTwips(coords);
-                       var tileBottomRight = new L.Point(this._tileWidthTwips, 
this._tileHeightTwips);
-                       var bounds = new L.Bounds(tileTopLeft, 
tileTopLeft.add(tileBottomRight));
-                       if (invalidBounds.intersects(bounds) && coords.part === 
command.part) {
-                               if (this._tiles[key]._invalidCount) {
-                                       this._tiles[key]._invalidCount += 1;
-                               }
-                               else {
-                                       this._tiles[key]._invalidCount = 1;
-                               }
-                               if (visibleArea.intersects(bounds)) {
-                                       var msg = 'tile ' +
-                                                       'part=' + coords.part + 
' ' +
-                                                       'width=' + 
this._tileSize + ' ' +
-                                                       'height=' + 
this._tileSize + ' ' +
-                                                       'tileposx=' + 
tileTopLeft.x + ' '    +
-                                                       'tileposy=' + 
tileTopLeft.y + ' ' +
-                                                       'tilewidth=' + 
this._tileWidthTwips + ' ' +
-                                                       'tileheight=' + 
this._tileHeightTwips;
-                                       toRequest.push({msg: msg, key: key, 
coords: coords});
-                               }
-                               else {
-                                       // tile outside of the visible area, 
just remove it
-                                       this._preFetchBorder = null;
-                                       this._removeTile(key);
-                               }
-                       }
-               }
-
-               // Sort tiles so that we request those closer to the cursor 
first
-               var cursorPos = 
this._map.project(this._visibleCursor.getNorthWest());
-               cursorPos = cursorPos.divideBy(this._tileSize);
-               toRequest.sort(function(x, y) {return 
x.coords.distanceTo(cursorPos) - y.coords.distanceTo(cursorPos);});
-               for (var i = 0; i < toRequest.length; i++) {
-                       L.Socket.sendMessage(toRequest[i].msg, 
toRequest[i].key);
-               }
-
-               for (key in this._tileCache) {
-                       // compute the rectangle that each tile covers in the 
document based
-                       // on the zoom level
-                       coords = this._keyToTileCoords(key);
-                       if (coords.part !== command.part) {
-                               continue;
-                       }
-                       var scale = this._map.getZoomScale(coords.z);
-                       topLeftTwips = new L.Point(
-                                       this.options.tileWidthTwips / scale * 
coords.x,
-                                       this.options.tileHeightTwips / scale * 
coords.y);
-                       bottomRightTwips = topLeftTwips.add(new L.Point(
-                                       this.options.tileWidthTwips / scale,
-                                       this.options.tileHeightTwips / scale));
-                       bounds = new L.Bounds(topLeftTwips, bottomRightTwips);
-                       if (invalidBounds.intersects(bounds)) {
-                               delete this._tileCache[key];
-                       }
-               }
-               if (command.part === this._selectedPart &&
-                       command.part !== this._lastValidPart) {
-                       this._lastValidPart = command.part;
-                       this._map.fire('updatepart', {part: command.part, 
docType: this._docType});
-               }
-
-       },
-
        _onSearchNotFoundMsg: function (textMsg) {
                var originalPhrase = textMsg.substring(16);
                this._map.fire('search', {originalPhrase: originalPhrase, 
count: 0});
diff --git a/loleaflet/src/layer/tile/WriterTileLayer.js 
b/loleaflet/src/layer/tile/WriterTileLayer.js
index 6eb0258..e2dde74 100644
--- a/loleaflet/src/layer/tile/WriterTileLayer.js
+++ b/loleaflet/src/layer/tile/WriterTileLayer.js
@@ -3,4 +3,81 @@
  */
 
 L.WriterTileLayer = L.TileLayer.extend({
+
+       _onInvalidateTilesMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.x === undefined || command.y === undefined || 
command.part === undefined) {
+                       var strTwips = textMsg.match(/\d+/g);
+                       command.x = parseInt(strTwips[0]);
+                       command.y = parseInt(strTwips[1]);
+                       command.width = parseInt(strTwips[2]);
+                       command.height = parseInt(strTwips[3]);
+                       command.part = this._currentPart;
+               }
+               command.part = 0;
+               var topLeftTwips = new L.Point(command.x, command.y);
+               var offset = new L.Point(command.width, command.height);
+               var bottomRightTwips = topLeftTwips.add(offset);
+               var invalidBounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
+               var visibleTopLeft = 
this._latLngToTwips(this._map.getBounds().getNorthWest());
+               var visibleBottomRight = 
this._latLngToTwips(this._map.getBounds().getSouthEast());
+               var visibleArea = new L.Bounds(visibleTopLeft, 
visibleBottomRight);
+               var toRequest = [];
+
+               for (var key in this._tiles) {
+                       var coords = this._tiles[key].coords;
+                       var tileTopLeft = this._coordsToTwips(coords);
+                       var tileBottomRight = new L.Point(this._tileWidthTwips, 
this._tileHeightTwips);
+                       var bounds = new L.Bounds(tileTopLeft, 
tileTopLeft.add(tileBottomRight));
+                       if (invalidBounds.intersects(bounds) && coords.part === 
command.part) {
+                               if (this._tiles[key]._invalidCount) {
+                                       this._tiles[key]._invalidCount += 1;
+                               }
+                               else {
+                                       this._tiles[key]._invalidCount = 1;
+                               }
+                               if (visibleArea.intersects(bounds)) {
+                                       var msg = 'tile ' +
+                                                       'part=' + coords.part + 
' ' +
+                                                       'width=' + 
this._tileSize + ' ' +
+                                                       'height=' + 
this._tileSize + ' ' +
+                                                       'tileposx=' + 
tileTopLeft.x + ' '    +
+                                                       'tileposy=' + 
tileTopLeft.y + ' ' +
+                                                       'tilewidth=' + 
this._tileWidthTwips + ' ' +
+                                                       'tileheight=' + 
this._tileHeightTwips;
+                                       toRequest.push({msg: msg, key: key, 
coords: coords});
+                               }
+                               else {
+                                       // tile outside of the visible area, 
just remove it
+                                       this._preFetchBorder = null;
+                                       this._removeTile(key);
+                               }
+                       }
+               }
+
+               // Sort tiles so that we request those closer to the cursor 
first
+               var cursorPos = 
this._map.project(this._visibleCursor.getNorthWest());
+               cursorPos = cursorPos.divideBy(this._tileSize);
+               toRequest.sort(function(x, y) {return 
x.coords.distanceTo(cursorPos) - y.coords.distanceTo(cursorPos);});
+               for (var i = 0; i < toRequest.length; i++) {
+                       L.Socket.sendMessage(toRequest[i].msg, 
toRequest[i].key);
+               }
+
+               for (key in this._tileCache) {
+                       // compute the rectangle that each tile covers in the 
document based
+                       // on the zoom level
+                       coords = this._keyToTileCoords(key);
+                       var scale = this._map.getZoomScale(coords.z);
+                       topLeftTwips = new L.Point(
+                                       this.options.tileWidthTwips / scale * 
coords.x,
+                                       this.options.tileHeightTwips / scale * 
coords.y);
+                       bottomRightTwips = topLeftTwips.add(new L.Point(
+                                       this.options.tileWidthTwips / scale,
+                                       this.options.tileHeightTwips / scale));
+                       bounds = new L.Bounds(topLeftTwips, bottomRightTwips);
+                       if (invalidBounds.intersects(bounds)) {
+                               delete this._tileCache[key];
+                       }
+               }
+       }
 });
commit 6bf8a7dee0689e349360e158941eb18e82da9c7c
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 12:28:37 2015 +0300

    loleaflet: renamed 'currentPart' to 'selectedPart'

diff --git a/loleaflet/src/control/Control.Parts.js 
b/loleaflet/src/control/Control.Parts.js
index 9fc0b16..4a24e78 100644
--- a/loleaflet/src/control/Control.Parts.js
+++ b/loleaflet/src/control/Control.Parts.js
@@ -62,17 +62,17 @@ L.Control.Parts = L.Control.extend({
        _updateDisabled: function (e) {
                var className = 'leaflet-disabled';
                var parts = e.parts;
-               var currentPart = e.currentPart;
+               var selectedPart = e.selectedPart;
                var docType = e.docType;
                if (docType === 'text') {
                        return;
                }
-               if (currentPart === 0) {
+               if (selectedPart === 0) {
                        L.DomUtil.addClass(this._prevPartButton, className);
                } else {
                        L.DomUtil.removeClass(this._prevPartButton, className);
                }
-               if (currentPart === parts - 1) {
+               if (selectedPart === parts - 1) {
                        L.DomUtil.addClass(this._nextPartButton, className);
                } else {
                        L.DomUtil.removeClass(this._nextPartButton, className);
diff --git a/loleaflet/src/control/Control.Tabs.js 
b/loleaflet/src/control/Control.Tabs.js
index cce0de9..b5ce57d 100644
--- a/loleaflet/src/control/Control.Tabs.js
+++ b/loleaflet/src/control/Control.Tabs.js
@@ -15,7 +15,7 @@ L.Control.Tabs = L.Control.extend({
 
        _updateDisabled: function (e) {
                var parts = e.parts;
-               var currentPart = e.currentPart;
+               var selectedPart = e.selectedPart;
                var docType = e.docType;
                var partNames = e.partNames;
                if (docType === 'text') {
@@ -47,7 +47,7 @@ L.Control.Tabs = L.Control.extend({
                        for (var key in this._spreadsheetTabs) {
                                var part =  parseInt(key.match(/\d+/g)[0]);
                                
L.DomUtil.removeClass(this._spreadsheetTabs[key], 'selected');
-                               if (part === currentPart) {
+                               if (part === selectedPart) {
                                        
L.DomUtil.addClass(this._spreadsheetTabs[key], 'selected');
                                }
                        }
diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js
index 7089cfb..08e1c89 100644
--- a/loleaflet/src/control/Parts.js
+++ b/loleaflet/src/control/Parts.js
@@ -5,17 +5,17 @@ L.Map.include({
        setPart: function (part) {
                var docLayer = this._docLayer;
                if (part === 'prev') {
-                       if (docLayer._currentPart > 0) {
-                               docLayer._currentPart -= 1;
+                       if (docLayer._selectedPart > 0) {
+                               docLayer._selectedPart -= 1;
                        }
                }
                else if (part === 'next') {
-                       if (docLayer._currentPart < docLayer._parts - 1) {
-                               docLayer._currentPart += 1;
+                       if (docLayer._selectedPart < docLayer._parts - 1) {
+                               docLayer._selectedPart += 1;
                        }
                }
                else if (typeof (part) === 'number' && part >= 0 && part < 
docLayer._parts) {
-                       docLayer._currentPart = part;
+                       docLayer._selectedPart = part;
                }
                else {
                        return;
@@ -25,11 +25,11 @@ L.Map.include({
                        L.Socket.sendMessage('resetselection');
                }
                this.fire('updateparts', {
-                       currentPart: docLayer._currentPart,
+                       selectedPart: docLayer._selectedPart,
                        parts: docLayer._parts,
                        docType: docLayer._docType
                });
-               L.Socket.sendMessage('setclientpart part=' + 
docLayer._currentPart);
+               L.Socket.sendMessage('setclientpart part=' + 
docLayer._selectedPart);
                docLayer._update();
                docLayer._pruneTiles();
                docLayer._clearSelections();
@@ -114,7 +114,7 @@ L.Map.include({
        },
 
        getCurrentPartNumber: function () {
-               return this._docLayer._currentPart;
+               return this._docLayer._selectedPart;
        },
 
        getDocSize: function () {
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 5e49874..b25cf1c 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -160,7 +160,7 @@ L.Socket = {
                                command.parts = 
parseInt(tokens[i].substring(6));
                        }
                        else if (tokens[i].substring(0, 8) === 'current=') {
-                               command.currentPart = 
parseInt(tokens[i].substring(8));
+                               command.selectedPart = 
parseInt(tokens[i].substring(8));
                        }
                        else if (tokens[i].substring(0, 3) === 'id=') {
                                // remove newline characters
diff --git a/loleaflet/src/layer/tile/GridLayer.js 
b/loleaflet/src/layer/tile/GridLayer.js
index 58ea647..7df84b5 100644
--- a/loleaflet/src/layer/tile/GridLayer.js
+++ b/loleaflet/src/layer/tile/GridLayer.js
@@ -470,7 +470,7 @@ L.GridLayer = L.Layer.extend({
 
                for (var key in this._tiles) {
                        if (this._keyToTileCoords(key).z !== zoom ||
-                                       this._keyToTileCoords(key).part !== 
this._currentPart) {
+                                       this._keyToTileCoords(key).part !== 
this._selectedPart) {
                                this._tiles[key].current = false;
                        }
                }
@@ -482,7 +482,7 @@ L.GridLayer = L.Layer.extend({
                        for (var i = tileRange.min.x; i <= tileRange.max.x; 
i++) {
                                var coords = new L.Point(i, j);
                                coords.z = zoom;
-                               coords.part = this._currentPart;
+                               coords.part = this._selectedPart;
 
                                if (!this._isValidTile(coords)) { continue; }
 
@@ -619,7 +619,7 @@ L.GridLayer = L.Layer.extend({
                var tilePos = this._getTilePos(coords),
                        key = this._tileCoordsToKey(coords);
 
-               if (coords.part === this._currentPart) {
+               if (coords.part === this._selectedPart) {
                        var tile = this.createTile(this._wrapCoords(coords), 
L.bind(this._tileReady, this, coords));
 
                        this._initTile(tile);
@@ -660,7 +660,7 @@ L.GridLayer = L.Layer.extend({
                                        'tileposy=' + twips.y + ' ' +
                                        'tilewidth=' + this._tileWidthTwips + ' 
' +
                                        'tileheight=' + this._tileHeightTwips;
-                       if (coords.part !== this._currentPart) {
+                       if (coords.part !== this._selectedPart) {
                                msg += ' prefetch=true';
                        }
                        L.Socket.sendMessage(msg, key);
@@ -770,7 +770,7 @@ L.GridLayer = L.Layer.extend({
                }
 
                if (!this._preFetchBorder) {
-                       if (this._currentPart !== this._preFetchPart) {
+                       if (this._selectedPart !== this._preFetchPart) {
                                // all tiles from the new part have to be 
pre-fetched
                                var tileBorder = this._preFetchBorder = new 
L.Bounds(new L.Point(0, 0), new L.Point(0, 0));
                        }
@@ -855,36 +855,36 @@ L.GridLayer = L.Layer.extend({
                                        tileBorder.max.x * this._tileWidthTwips 
< this._docWidthTwips ||
                                         tileBorder.max.y * 
this._tileHeightTwips < this._docHeightTwips) &&
                                        this.options.preFetchOtherParts) {
-                               var diff = this._preFetchPart - 
this._currentPart;
-                               if (diff === 0 && this._currentPart < 
this._parts - 1) {
+                               var diff = this._preFetchPart - 
this._selectedPart;
+                               if (diff === 0 && this._selectedPart < 
this._parts - 1) {
                                        this._preFetchPart += 1;
                                        this._preFetchBorder = null;
                                }
-                               else if (diff === 0 && this._currentPart > 0) {
+                               else if (diff === 0 && this._selectedPart > 0) {
                                        this._preFetchPart -= 1;
                                        this._preFetchBorder = null;
                                }
                                else if (diff > 0) {
-                                       if (this._currentPart - diff >= 0) {
+                                       if (this._selectedPart - diff >= 0) {
                                                // lower part number
-                                               this._preFetchPart = 
this._currentPart - diff;
+                                               this._preFetchPart = 
this._selectedPart - diff;
                                                this._preFetchBorder = null;
                                        }
-                                       else if (this._currentPart + diff + 1 < 
this._parts) {
+                                       else if (this._selectedPart + diff + 1 
< this._parts) {
                                                // higher part number
-                                               this._preFetchPart = 
this._currentPart + diff + 1;
+                                               this._preFetchPart = 
this._selectedPart + diff + 1;
                                                this._preFetchBorder = null;
                                        }
                                }
                                else if (diff < 0) {
-                                       if (this._currentPart - diff + 1 < 
this._parts) {
+                                       if (this._selectedPart - diff + 1 < 
this._parts) {
                                                // higher part number
-                                               this._preFetchPart = 
this._currentPart - diff + 1;
+                                               this._preFetchPart = 
this._selectedPart - diff + 1;
                                                this._preFetchBorder = null;
                                        }
-                                       else if (this._currentPart + diff - 1 
>= 0) {
+                                       else if (this._selectedPart + diff - 1 
>= 0) {
                                                // lower part number
-                                               this._preFetchPart = 
this._currentPart + diff - 1;
+                                               this._preFetchPart = 
this._selectedPart + diff - 1;
                                                this._preFetchBorder = null;
                                        }
                                }
@@ -908,7 +908,7 @@ L.GridLayer = L.Layer.extend({
                }
                var interval = 750;
                var idleTime = 5000;
-               this._preFetchPart = this._currentPart;
+               this._preFetchPart = this._selectedPart;
                this._preFetchIdle = setTimeout(L.bind(function () {
                        this._tilesPreFetcher = 
setInterval(L.bind(this._preFetchTiles, this), interval);
                }, this), idleTime);
diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index a4e4ef0..6029884 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -274,7 +274,7 @@ L.TileLayer = L.GridLayer.extend({
                        command.y = parseInt(strTwips[1]);
                        command.width = parseInt(strTwips[2]);
                        command.height = parseInt(strTwips[3]);
-                       command.part = this._currentPart;
+                       command.part = this._selectedPart;
                }
                if (this._docType === 'text') {
                        command.part = 0;
@@ -346,7 +346,7 @@ L.TileLayer = L.GridLayer.extend({
                                delete this._tileCache[key];
                        }
                }
-               if (command.part === this._currentPart &&
+               if (command.part === this._selectedPart &&
                        command.part !== this._lastValidPart) {
                        this._lastValidPart = command.part;
                        this._map.fire('updatepart', {part: command.part, 
docType: this._docType});
@@ -361,11 +361,11 @@ L.TileLayer = L.GridLayer.extend({
 
        _onSetPartMsg: function (textMsg) {
                var part = parseInt(textMsg.match(/\d+/g)[0]);
-               if (part !== this._currentPart && this._docType !== 'text') {
-                       this._currentPart = part;
+               if (part !== this._selectedPart && this._docType !== 'text') {
+                       this._selectedPart = part;
                        this._update();
                        this._clearSelections();
-                       this._map.fire('setpart', {currentPart: 
this._currentPart});
+                       this._map.fire('setpart', {selectedPart: 
this._selectedPart});
                }
                else if (this._docType === 'text') {
                        this._currentPage = part;
@@ -395,11 +395,11 @@ L.TileLayer = L.GridLayer.extend({
                        this._updateMaxBounds(true);
                        this._documentInfo = textMsg;
                        this._parts = command.parts;
-                       this._currentPart = command.currentPart;
+                       this._selectedPart = command.selectedPart;
                        if (this._docType === 'text') {
-                               this._currentPart = 0;
+                               this._selectedPart = 0;
                                this._parts = 1;
-                               this._currentPage = command.currentPart;
+                               this._currentPage = command.selectedPart;
                                this._pages = command.parts;
                                this._map.fire('pagenumberchanged', {
                                        currentPage: this._currentPage,
@@ -408,20 +408,20 @@ L.TileLayer = L.GridLayer.extend({
                                });
                        }
                        else {
-                               L.Socket.sendMessage('setclientpart part=' + 
this._currentPart);
+                               L.Socket.sendMessage('setclientpart part=' + 
this._selectedPart);
                                var partNames = textMsg.match(/[^\r\n]+/g);
                                // only get the last matches
                                partNames = partNames.slice(partNames.length - 
this._parts);
                                this._map.fire('updateparts', {
-                                       currentPart: this._currentPart,
+                                       selectedPart: this._selectedPart,
                                        parts: this._parts,
                                        docType: this._docType,
                                        partNames: partNames
                                });
                        }
                        this._update();
-                       if (this._preFetchPart !== this._currentPart) {
-                               this._preFetchPart = this._currentPart;
+                       if (this._preFetchPart !== this._selectedPart) {
+                               this._preFetchPart = this._selectedPart;
                                this._preFetchBorder = null;
                        }
                }
commit e28da7e12ed464d5cf1122c107ab7551850a9271
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 12:04:13 2015 +0300

    loleaflet: alphabetically ordered the server commands

diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 111b610..a4e4ef0 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -178,21 +178,24 @@ L.TileLayer = L.GridLayer.extend({
                if (textMsg.startsWith('cursorvisible:')) {
                        this._onCursorVisibleMsg(textMsg);
                }
-               else if (textMsg.startsWith('invalidatecursor:')) {
-                       this._onInvalidateCursorMsg(textMsg);
-               }
-               else if (textMsg.startsWith('textselectionstart:')) {
-                       this._onTextSelectionStartMsg(textMsg);
-               }
-               else if (textMsg.startsWith('textselectionend:')) {
-                       this._onTextSelectionEndMsg(textMsg);
+               else if (textMsg.startsWith('error:')) {
+                       this._onErrorMsg(textMsg);
                }
                else if (textMsg.startsWith('graphicselection:')) {
                        this._onGraphicSelectionMsg(textMsg);
                }
+               else if (textMsg.startsWith('invalidatecursor:')) {
+                       this._onInvalidateCursorMsg(textMsg);
+               }
                else if (textMsg.startsWith('invalidatetiles:') && 
!textMsg.match('EMPTY')) {
                        this._onInvalidateTilesMsg(textMsg);
                }
+               else if (textMsg.startsWith('searchnotfound:')) {
+                       this._onSearchNotFoundMsg(textMsg);
+               }
+               else if (textMsg.startsWith('setpart:')) {
+                       this._onSetPartMsg(textMsg);
+               }
                else if (textMsg.startsWith('statechanged:')) {
                        this._onStateChangedMsg(textMsg);
                }
@@ -202,8 +205,8 @@ L.TileLayer = L.GridLayer.extend({
                else if (textMsg.startsWith('statusindicator')) {
                        this._onStatusIndicatorMsg(textMsg);
                }
-               else if (textMsg.startsWith('tile:')) {
-                       this._onTileMsg(textMsg, imgBytes, index);
+               else if (textMsg.startsWith('styles:')) {
+                       this._onStylesMsg(textMsg);
                }
                else if (textMsg.startsWith('textselection:')) {
                        this._onTextSelectionMsg(textMsg);
@@ -211,17 +214,14 @@ L.TileLayer = L.GridLayer.extend({
                else if (textMsg.startsWith('textselectioncontent:')) {
                        this._onTextSelectionContentMsg(textMsg);
                }
-               else if (textMsg.startsWith('setpart:')) {
-                       this._onSetPartMsg(textMsg);
-               }
-               else if (textMsg.startsWith('searchnotfound:')) {
-                       this._onSearchNotFoundMsg(textMsg);
+               else if (textMsg.startsWith('textselectionend:')) {
+                       this._onTextSelectionEndMsg(textMsg);
                }
-               else if (textMsg.startsWith('styles:')) {
-                       this._onStylesMsg(textMsg);
+               else if (textMsg.startsWith('textselectionstart:')) {
+                       this._onTextSelectionStartMsg(textMsg);
                }
-               else if (textMsg.startsWith('error:')) {
-                       this._onErrorMsg(textMsg);
+               else if (textMsg.startsWith('tile:')) {
+                       this._onTileMsg(textMsg, imgBytes, index);
                }
        },
 
commit c927aec30637f94c43de7cf84bb8d2dbc046f2fb
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 11:59:38 2015 +0300

    loleaflet: modularized the onMessage method

diff --git a/loleaflet/src/layer/tile/TileLayer.js 
b/loleaflet/src/layer/tile/TileLayer.js
index 7812936..111b610 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -176,198 +176,259 @@ L.TileLayer = L.GridLayer.extend({
 
        _onMessage: function (textMsg, imgBytes, index) {
                if (textMsg.startsWith('cursorvisible:')) {
-                       var command = textMsg.match('cursorvisible: true');
-                       this._isCursorVisible = command ? true : false;
-                       this._isCursorOverlayVisible = true;
-                       this._onUpdateCursor();
+                       this._onCursorVisibleMsg(textMsg);
                }
                else if (textMsg.startsWith('invalidatecursor:')) {
-                       var strTwips = textMsg.match(/\d+/g);
-                       var topLeftTwips = new L.Point(parseInt(strTwips[0]), 
parseInt(strTwips[1]));
-                       var offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
-                       var bottomRightTwips = topLeftTwips.add(offset);
-                       this._visibleCursor = new L.LatLngBounds(
-                                                       
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
-                                                       
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
-                       this._isCursorOverlayVisible = true;
-                       this._onUpdateCursor();
+                       this._onInvalidateCursorMsg(textMsg);
                }
                else if (textMsg.startsWith('textselectionstart:')) {
-                       strTwips = textMsg.match(/\d+/g);
-                       if (strTwips != null) {
-                               topLeftTwips = new 
L.Point(parseInt(strTwips[0]), parseInt(strTwips[1]));
-                               offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
-                               bottomRightTwips = topLeftTwips.add(offset);
-                               this._textSelectionStart = new L.LatLngBounds(
-                                                       
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
-                                                       
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
-                       }
-                       else {
-                               this._textSelectionStart = new 
L.LatLngBounds(new L.LatLng(0, 0), new L.LatLng(0, 0));
-                       }
+                       this._onTextSelectionStartMsg(textMsg);
                }
                else if (textMsg.startsWith('textselectionend:')) {
-                       strTwips = textMsg.match(/\d+/g);
-                       if (strTwips != null) {
-                               topLeftTwips = new 
L.Point(parseInt(strTwips[0]), parseInt(strTwips[1]));
-                               offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
-                               bottomRightTwips = topLeftTwips.add(offset);
-                               this._textSelectionEnd = new L.LatLngBounds(
-                                                       
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
-                                                       
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
-                       }
-                       else {
-                               this._textSelectionEnd = new L.LatLngBounds(new 
L.LatLng(0, 0), new L.LatLng(0, 0));
-                       }
+                       this._onTextSelectionEndMsg(textMsg);
                }
                else if (textMsg.startsWith('graphicselection:')) {
-                       if (textMsg.match('EMPTY')) {
-                               this._graphicSelection = new L.LatLngBounds(new 
L.LatLng(0, 0), new L.LatLng(0, 0));
-                       }
-                       else {
-                               strTwips = textMsg.match(/\d+/g);
-                               topLeftTwips = new 
L.Point(parseInt(strTwips[0]), parseInt(strTwips[1]));
-                               offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
-                               bottomRightTwips = topLeftTwips.add(offset);
-                               this._graphicSelection = new L.LatLngBounds(
-                                                               
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
-                                                               
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
-                       }
-
-                       this._onUpdateGraphicSelection();
+                       this._onGraphicSelectionMsg(textMsg);
                }
                else if (textMsg.startsWith('invalidatetiles:') && 
!textMsg.match('EMPTY')) {
-                       command = L.Socket.parseServerCmd(textMsg);
-                       if (command.x === undefined || command.y === undefined 
|| command.part === undefined) {
-                               strTwips = textMsg.match(/\d+/g);
-                               command.x = parseInt(strTwips[0]);
-                               command.y = parseInt(strTwips[1]);
-                               command.width = parseInt(strTwips[2]);
-                               command.height = parseInt(strTwips[3]);
-                               command.part = this._currentPart;
-                       }
-                       if (this._docType === 'text') {
-                               command.part = 0;
-                       }
-                       topLeftTwips = new L.Point(command.x, command.y);
-                       offset = new L.Point(command.width, command.height);
-                       bottomRightTwips = topLeftTwips.add(offset);
-                       var invalidBounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
-                       var visibleTopLeft = 
this._latLngToTwips(this._map.getBounds().getNorthWest());
-                       var visibleBottomRight = 
this._latLngToTwips(this._map.getBounds().getSouthEast());
-                       var visibleArea = new L.Bounds(visibleTopLeft, 
visibleBottomRight);
-                       var toRequest = [];
-
-                       for (var key in this._tiles) {
-                               var coords = this._tiles[key].coords;
-                               var tileTopLeft = this._coordsToTwips(coords);
-                               var tileBottomRight = new 
L.Point(this._tileWidthTwips, this._tileHeightTwips);
-                               var bounds = new L.Bounds(tileTopLeft, 
tileTopLeft.add(tileBottomRight));
-                               if (invalidBounds.intersects(bounds) && 
coords.part === command.part) {
-                                       if (this._tiles[key]._invalidCount) {
-                                               this._tiles[key]._invalidCount 
+= 1;
-                                       }
-                                       else {
-                                               this._tiles[key]._invalidCount 
= 1;
-                                       }
-                                       if (visibleArea.intersects(bounds)) {
-                                               var msg = 'tile ' +
-                                                               'part=' + 
coords.part + ' ' +
-                                                               'width=' + 
this._tileSize + ' ' +
-                                                               'height=' + 
this._tileSize + ' ' +
-                                                               'tileposx=' + 
tileTopLeft.x + ' '    +
-                                                               'tileposy=' + 
tileTopLeft.y + ' ' +
-                                                               'tilewidth=' + 
this._tileWidthTwips + ' ' +
-                                                               'tileheight=' + 
this._tileHeightTwips;
-                                               toRequest.push({msg: msg, key: 
key, coords: coords});
-                                       }
-                                       else {
-                                               // tile outside of the visible 
area, just remove it
-                                               this._preFetchBorder = null;
-                                               this._removeTile(key);
-                                       }
-                               }
-                       }
-                       var cursorPos = 
this._map.project(this._visibleCursor.getNorthWest());
-                       cursorPos = cursorPos.divideBy(this._tileSize);
-                       toRequest.sort(function(x, y) {return 
x.coords.distanceTo(cursorPos) - y.coords.distanceTo(cursorPos);});
-                       for (var i = 0; i < toRequest.length; i++) {
-                               L.Socket.sendMessage(toRequest[i].msg, 
toRequest[i].key);
-                       }
-                       for (key in this._tileCache) {
-                               // compute the rectangle that each tile covers 
in the document based
-                               // on the zoom level
-                               coords = this._keyToTileCoords(key);
-                               if (coords.part !== command.part) {
-                                       continue;
-                               }
-                               var scale = this._map.getZoomScale(coords.z);
-                               topLeftTwips = new L.Point(
-                                               this.options.tileWidthTwips / 
scale * coords.x,
-                                               this.options.tileHeightTwips / 
scale * coords.y);
-                               bottomRightTwips = topLeftTwips.add(new L.Point(
-                                               this.options.tileWidthTwips / 
scale,
-                                               this.options.tileHeightTwips / 
scale));
-                               bounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
-                               if (invalidBounds.intersects(bounds)) {
-                                       delete this._tileCache[key];
-                               }
-                       }
-                       if (command.part === this._currentPart &&
-                               command.part !== this._lastValidPart) {
-                               this._lastValidPart = command.part;
-                               this._map.fire('updatepart', {part: 
command.part, docType: this._docType});
-                       }
+                       this._onInvalidateTilesMsg(textMsg);
                }
                else if (textMsg.startsWith('statechanged:')) {
-                       var unoMsg = textMsg.substr(14);
-                       var unoCmd = unoMsg.match('.uno:(.*)=')[1];
-                       var state = unoMsg.match('.*=(.*)')[1];
-                       if (unoCmd && state) {
-                               this._map.fire('commandstatechanged', {unoCmd : 
unoCmd, state : state});
-                       }
+                       this._onStateChangedMsg(textMsg);
                }
                else if (textMsg.startsWith('status:')) {
-                       command = L.Socket.parseServerCmd(textMsg);
-                       if (command.width && command.height && 
this._documentInfo !== textMsg) {
-                               this._docWidthTwips = command.width;
-                               this._docHeightTwips = command.height;
-                               this._docType = command.type;
-                               this._updateMaxBounds(true);
-                               this._documentInfo = textMsg;
-                               this._parts = command.parts;
-                               this._currentPart = command.currentPart;
-                               if (this._docType === 'text') {
-                                       this._currentPart = 0;
-                                       this._parts = 1;
-                                       this._currentPage = command.currentPart;
-                                       this._pages = command.parts;
-                                       this._map.fire('pagenumberchanged', {
-                                               currentPage: this._currentPage,
-                                               pages: this._pages,
-                                               docType: this._docType
-                                       });
+                       this._onStatusMsg(textMsg);
+               }
+               else if (textMsg.startsWith('statusindicator')) {
+                       this._onStatusIndicatorMsg(textMsg);
+               }
+               else if (textMsg.startsWith('tile:')) {
+                       this._onTileMsg(textMsg, imgBytes, index);
+               }
+               else if (textMsg.startsWith('textselection:')) {
+                       this._onTextSelectionMsg(textMsg);
+               }
+               else if (textMsg.startsWith('textselectioncontent:')) {
+                       this._onTextSelectionContentMsg(textMsg);
+               }
+               else if (textMsg.startsWith('setpart:')) {
+                       this._onSetPartMsg(textMsg);
+               }
+               else if (textMsg.startsWith('searchnotfound:')) {
+                       this._onSearchNotFoundMsg(textMsg);
+               }
+               else if (textMsg.startsWith('styles:')) {
+                       this._onStylesMsg(textMsg);
+               }
+               else if (textMsg.startsWith('error:')) {
+                       this._onErrorMsg(textMsg);
+               }
+       },
+
+       _onCursorVisibleMsg: function(textMsg) {
+               var command = textMsg.match('cursorvisible: true');
+               this._isCursorVisible = command ? true : false;
+               this._isCursorOverlayVisible = true;
+               this._onUpdateCursor();
+       },
+
+       _onErrorMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               this._map.fire('error', {cmd: command.errorCmd, kind: 
command.errorKind});
+       },
+
+       _onGraphicSelectionMsg: function (textMsg) {
+               if (textMsg.match('EMPTY')) {
+                       this._graphicSelection = new L.LatLngBounds(new 
L.LatLng(0, 0), new L.LatLng(0, 0));
+               }
+               else {
+                       var strTwips = textMsg.match(/\d+/g);
+                       var topLeftTwips = new L.Point(parseInt(strTwips[0]), 
parseInt(strTwips[1]));
+                       var offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
+                       var bottomRightTwips = topLeftTwips.add(offset);
+                       this._graphicSelection = new L.LatLngBounds(
+                                                       
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
+                                                       
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
+               }
+
+               this._onUpdateGraphicSelection();
+       },
+
+       _onInvalidateCursorMsg: function (textMsg) {
+               var strTwips = textMsg.match(/\d+/g);
+               var topLeftTwips = new L.Point(parseInt(strTwips[0]), 
parseInt(strTwips[1]));
+               var offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
+               var bottomRightTwips = topLeftTwips.add(offset);
+               this._visibleCursor = new L.LatLngBounds(
+                                               
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
+                                               
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
+               this._isCursorOverlayVisible = true;
+               this._onUpdateCursor();
+       },
+
+       _onInvalidateTilesMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.x === undefined || command.y === undefined || 
command.part === undefined) {
+                       var strTwips = textMsg.match(/\d+/g);
+                       command.x = parseInt(strTwips[0]);
+                       command.y = parseInt(strTwips[1]);
+                       command.width = parseInt(strTwips[2]);
+                       command.height = parseInt(strTwips[3]);
+                       command.part = this._currentPart;
+               }
+               if (this._docType === 'text') {
+                       command.part = 0;
+               }
+               var topLeftTwips = new L.Point(command.x, command.y);
+               var offset = new L.Point(command.width, command.height);
+               var bottomRightTwips = topLeftTwips.add(offset);
+               var invalidBounds = new L.Bounds(topLeftTwips, 
bottomRightTwips);
+               var visibleTopLeft = 
this._latLngToTwips(this._map.getBounds().getNorthWest());
+               var visibleBottomRight = 
this._latLngToTwips(this._map.getBounds().getSouthEast());
+               var visibleArea = new L.Bounds(visibleTopLeft, 
visibleBottomRight);
+               var toRequest = [];
+
+               for (var key in this._tiles) {
+                       var coords = this._tiles[key].coords;
+                       var tileTopLeft = this._coordsToTwips(coords);
+                       var tileBottomRight = new L.Point(this._tileWidthTwips, 
this._tileHeightTwips);
+                       var bounds = new L.Bounds(tileTopLeft, 
tileTopLeft.add(tileBottomRight));
+                       if (invalidBounds.intersects(bounds) && coords.part === 
command.part) {
+                               if (this._tiles[key]._invalidCount) {
+                                       this._tiles[key]._invalidCount += 1;
                                }
                                else {
-                                       L.Socket.sendMessage('setclientpart 
part=' + this._currentPart);
-                                       var partNames = 
textMsg.match(/[^\r\n]+/g);
-                                       // only get the last matches
-                                       partNames = 
partNames.slice(partNames.length - this._parts);
-                                       this._map.fire('updateparts', {
-                                               currentPart: this._currentPart,
-                                               parts: this._parts,
-                                               docType: this._docType,
-                                               partNames: partNames
-                                       });
+                                       this._tiles[key]._invalidCount = 1;
+                               }
+                               if (visibleArea.intersects(bounds)) {
+                                       var msg = 'tile ' +
+                                                       'part=' + coords.part + 
' ' +
+                                                       'width=' + 
this._tileSize + ' ' +
+                                                       'height=' + 
this._tileSize + ' ' +
+                                                       'tileposx=' + 
tileTopLeft.x + ' '    +
+                                                       'tileposy=' + 
tileTopLeft.y + ' ' +
+                                                       'tilewidth=' + 
this._tileWidthTwips + ' ' +
+                                                       'tileheight=' + 
this._tileHeightTwips;
+                                       toRequest.push({msg: msg, key: key, 
coords: coords});
                                }
-                               this._update();
-                               if (this._preFetchPart !== this._currentPart) {
-                                       this._preFetchPart = this._currentPart;
+                               else {
+                                       // tile outside of the visible area, 
just remove it
                                        this._preFetchBorder = null;
+                                       this._removeTile(key);
                                }
                        }
                }
-               else if (textMsg.startsWith('statusindicatorstart:')) {
+
+               // Sort tiles so that we request those closer to the cursor 
first
+               var cursorPos = 
this._map.project(this._visibleCursor.getNorthWest());
+               cursorPos = cursorPos.divideBy(this._tileSize);
+               toRequest.sort(function(x, y) {return 
x.coords.distanceTo(cursorPos) - y.coords.distanceTo(cursorPos);});
+               for (var i = 0; i < toRequest.length; i++) {
+                       L.Socket.sendMessage(toRequest[i].msg, 
toRequest[i].key);
+               }
+
+               for (key in this._tileCache) {
+                       // compute the rectangle that each tile covers in the 
document based
+                       // on the zoom level
+                       coords = this._keyToTileCoords(key);
+                       if (coords.part !== command.part) {
+                               continue;
+                       }
+                       var scale = this._map.getZoomScale(coords.z);
+                       topLeftTwips = new L.Point(
+                                       this.options.tileWidthTwips / scale * 
coords.x,
+                                       this.options.tileHeightTwips / scale * 
coords.y);
+                       bottomRightTwips = topLeftTwips.add(new L.Point(
+                                       this.options.tileWidthTwips / scale,
+                                       this.options.tileHeightTwips / scale));
+                       bounds = new L.Bounds(topLeftTwips, bottomRightTwips);
+                       if (invalidBounds.intersects(bounds)) {
+                               delete this._tileCache[key];
+                       }
+               }
+               if (command.part === this._currentPart &&
+                       command.part !== this._lastValidPart) {
+                       this._lastValidPart = command.part;
+                       this._map.fire('updatepart', {part: command.part, 
docType: this._docType});
+               }
+
+       },
+
+       _onSearchNotFoundMsg: function (textMsg) {
+               var originalPhrase = textMsg.substring(16);
+               this._map.fire('search', {originalPhrase: originalPhrase, 
count: 0});
+       },
+
+       _onSetPartMsg: function (textMsg) {
+               var part = parseInt(textMsg.match(/\d+/g)[0]);
+               if (part !== this._currentPart && this._docType !== 'text') {
+                       this._currentPart = part;
+                       this._update();
+                       this._clearSelections();
+                       this._map.fire('setpart', {currentPart: 
this._currentPart});
+               }
+               else if (this._docType === 'text') {
+                       this._currentPage = part;
+                       this._map.fire('pagenumberchanged', {
+                               currentPage: part,
+                               pages: this._pages,
+                               docType: this._docType
+                       });
+               }
+       },
+
+       _onStateChangedMsg: function (textMsg) {
+               var unoMsg = textMsg.substr(14);
+               var unoCmd = unoMsg.match('.uno:(.*)=')[1];
+               var state = unoMsg.match('.*=(.*)')[1];
+               if (unoCmd && state) {
+                       this._map.fire('commandstatechanged', {unoCmd : unoCmd, 
state : state});
+               }
+       },
+
+       _onStatusMsg: function (textMsg) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               if (command.width && command.height && this._documentInfo !== 
textMsg) {
+                       this._docWidthTwips = command.width;
+                       this._docHeightTwips = command.height;
+                       this._docType = command.type;
+                       this._updateMaxBounds(true);
+                       this._documentInfo = textMsg;
+                       this._parts = command.parts;
+                       this._currentPart = command.currentPart;
+                       if (this._docType === 'text') {
+                               this._currentPart = 0;
+                               this._parts = 1;
+                               this._currentPage = command.currentPart;
+                               this._pages = command.parts;
+                               this._map.fire('pagenumberchanged', {
+                                       currentPage: this._currentPage,
+                                       pages: this._pages,
+                                       docType: this._docType
+                               });
+                       }
+                       else {
+                               L.Socket.sendMessage('setclientpart part=' + 
this._currentPart);
+                               var partNames = textMsg.match(/[^\r\n]+/g);
+                               // only get the last matches
+                               partNames = partNames.slice(partNames.length - 
this._parts);
+                               this._map.fire('updateparts', {
+                                       currentPart: this._currentPart,
+                                       parts: this._parts,
+                                       docType: this._docType,
+                                       partNames: partNames
+                               });
+                       }
+                       this._update();
+                       if (this._preFetchPart !== this._currentPart) {
+                               this._preFetchPart = this._currentPart;
+                               this._preFetchBorder = null;
+                       }
+               }
+       },
+
+       _onStatusIndicatorMsg: function (textMsg) {
+               if (textMsg.startsWith('statusindicatorstart:')) {
                        this._map.fire('statusindicator', {statusType : 
'start'});
                }
                else if (textMsg.startsWith('statusindicatorsetvalue:')) {
@@ -377,126 +438,137 @@ L.TileLayer = L.GridLayer.extend({
                else if (textMsg.startsWith('statusindicatorfinish:')) {
                        this._map.fire('statusindicator', {statusType : 
'finish'});
                }
-               else if (textMsg.startsWith('tile:')) {
-                       command = L.Socket.parseServerCmd(textMsg);
-                       coords = this._twipsToCoords(command);
-                       coords.z = command.zoom;
-                       coords.part = command.part;
-                       var data = imgBytes.subarray(index + 1);
-
-                       // read the tile data
-                       var strBytes = '';
-                       for (i = 0; i < data.length; i++) {
-                               strBytes += String.fromCharCode(data[i]);
-                       }
+       },
 
-                       key = this._tileCoordsToKey(coords);
-                       var tile = this._tiles[key];
-                       var img = 'data:image/png;base64,' + 
window.btoa(strBytes);
-                       if (command.id !== undefined) {
-                               this._map.fire('tilepreview', {
-                                       tile: img,
-                                       id: command.id,
-                                       width: command.width,
-                                       height: command.height,
-                                       part: command.part,
-                                       docType: this._docType
-                               });
+       _onStylesMsg: function (textMsg) {
+               this._docStyles = JSON.parse(textMsg.substring(8));
+               this._map.fire('updatestyles', {styles: this._docStyles});
+       },
+
+       _onTextSelectionMsg: function (textMsg) {
+               var strTwips = textMsg.match(/\d+/g);
+               this._clearSelections();
+               if (strTwips != null) {
+                       var rectangles = [];
+                       var selectionCenter = new L.Point(0, 0);
+                       for (var i = 0; i < strTwips.length; i += 4) {
+                               var topLeftTwips = new 
L.Point(parseInt(strTwips[i]), parseInt(strTwips[i + 1]));
+                               var offset = new L.Point(parseInt(strTwips[i + 
2]), parseInt(strTwips[i + 3]));
+                               var topRightTwips = topLeftTwips.add(new 
L.Point(offset.x, 0));
+                               var bottomLeftTwips = topLeftTwips.add(new 
L.Point(0, offset.y));
+                               var bottomRightTwips = topLeftTwips.add(offset);
+                               rectangles.push([bottomLeftTwips, 
bottomRightTwips, topLeftTwips, topRightTwips]);
+                               selectionCenter = 
selectionCenter.add(topLeftTwips);
+                               selectionCenter = 
selectionCenter.add(offset.divideBy(2));
                        }
-                       else if (tile) {
-                               if (this._tiles[key]._invalidCount > 0) {
-                                       this._tiles[key]._invalidCount -= 1;
-                               }
-                               if (!tile.loaded) {
-                                       this._emptyTilesCount -= 1;
-                                       if (this._emptyTilesCount === 0) {
-                                               
this._map.fire('statusindicator', {statusType: 'alltilesloaded'});
-                                       }
-                               }
-                               tile.el.src = img;
+                       // average of all rectangles' centers
+                       selectionCenter = 
selectionCenter.divideBy(strTwips.length / 4);
+                       selectionCenter = this._twipsToLatLng(selectionCenter);
+                       if (!this._map.getBounds().contains(selectionCenter)) {
+                               var center = this._map.project(selectionCenter);
+                               center = 
center.subtract(this._map.getSize().divideBy(2));
+                               center.x = Math.round(center.x < 0 ? 0 : 
center.x);
+                               center.y = Math.round(center.y < 0 ? 0 : 
center.y);
+                               this._map.fire('scrollto', {x: center.x, y: 
center.y});
                        }
-                       else if (command.preFetch === 'true') {
-                               this._tileCache[key] = img;
+
+                       var polygons = 
L.PolyUtil.rectanglesToPolygons(rectangles, this);
+                       for (i = 0; i < polygons.length; i++) {
+                               var selection = new L.Polygon(polygons[i], {
+                                       pointerEvents: 'none',
+                                       fillColor: '#43ACE8',
+                                       fillOpacity: 0.25,
+                                       weight: 2,
+                                       opacity: 0.25});
+                               this._selections.addLayer(selection);
                        }
-                       L.Log.log(textMsg, L.INCOMING, key);
+                       if (this._selectionContentRequest) {
+                               clearTimeout(this._selectionContentRequest);
+                       }
+                       this._selectionContentRequest = 
setTimeout(L.bind(function () {
+                               L.Socket.sendMessage('gettextselection 
mimetype=text/plain;charset=utf-8');}, this), 100);
                }
-               else if (textMsg.startsWith('textselection:')) {
-                       strTwips = textMsg.match(/\d+/g);
-                       this._clearSelections();
-                       if (strTwips != null) {
-                               var rectangles = [];
-                               var selectionCenter = new L.Point(0, 0);
-                               for (i = 0; i < strTwips.length; i += 4) {
-                                       topLeftTwips = new 
L.Point(parseInt(strTwips[i]), parseInt(strTwips[i + 1]));
-                                       offset = new 
L.Point(parseInt(strTwips[i + 2]), parseInt(strTwips[i + 3]));
-                                       var topRightTwips = 
topLeftTwips.add(new L.Point(offset.x, 0));
-                                       var bottomLeftTwips = 
topLeftTwips.add(new L.Point(0, offset.y));
-                                       bottomRightTwips = 
topLeftTwips.add(offset);
-                                       rectangles.push([bottomLeftTwips, 
bottomRightTwips, topLeftTwips, topRightTwips]);
-                                       selectionCenter = 
selectionCenter.add(topLeftTwips);
-                                       selectionCenter = 
selectionCenter.add(offset.divideBy(2));
-                               }
-                               // average of all rectangles' centers
-                               selectionCenter = 
selectionCenter.divideBy(strTwips.length / 4);
-                               selectionCenter = 
this._twipsToLatLng(selectionCenter);
-                               if 
(!this._map.getBounds().contains(selectionCenter)) {
-                                       var center = 
this._map.project(selectionCenter);
-                                       center = 
center.subtract(this._map.getSize().divideBy(2));
-                                       center.x = Math.round(center.x < 0 ? 0 
: center.x);
-                                       center.y = Math.round(center.y < 0 ? 0 
: center.y);
-                                       this._map.fire('scrollto', {x: 
center.x, y: center.y});
-                               }
+               this._onUpdateTextSelection();
+       },
 
-                               var polygons = 
L.PolyUtil.rectanglesToPolygons(rectangles, this);
-                               for (i = 0; i < polygons.length; i++) {
-                                       var selection = new 
L.Polygon(polygons[i], {
-                                               pointerEvents: 'none',
-                                               fillColor: '#43ACE8',
-                                               fillOpacity: 0.25,
-                                               weight: 2,
-                                               opacity: 0.25});
-                                       this._selections.addLayer(selection);
-                               }
-                               if (this._selectionContentRequest) {
-                                       
clearTimeout(this._selectionContentRequest);
-                               }
-                               this._selectionContentRequest = 
setTimeout(L.bind(function () {
-                                       L.Socket.sendMessage('gettextselection 
mimetype=text/plain;charset=utf-8');}, this), 100);
-                       }
-                       this._onUpdateTextSelection();
+       _onTextSelectionContentMsg: function (textMsg) {
+               this._selectionTextContent = textMsg.substr(22);
+       },
+
+       _onTextSelectionEndMsg: function (textMsg) {
+               var strTwips = textMsg.match(/\d+/g);
+               if (strTwips != null) {
+                       var topLeftTwips = new L.Point(parseInt(strTwips[0]), 
parseInt(strTwips[1]));
+                       var offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
+                       var bottomRightTwips = topLeftTwips.add(offset);
+                       this._textSelectionEnd = new L.LatLngBounds(
+                                               
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
+                                               
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
                }
-               else if (textMsg.startsWith('textselectioncontent:')) {
-                       this._selectionTextContent = textMsg.substr(22);
+               else {
+                       this._textSelectionEnd = new L.LatLngBounds(new 
L.LatLng(0, 0), new L.LatLng(0, 0));
                }
-               else if (textMsg.startsWith('setpart:')) {
-                       var part = parseInt(textMsg.match(/\d+/g)[0]);
-                       if (part !== this._currentPart && this._docType !== 
'text') {
-                               this._currentPart = part;
-                               this._update();
-                               this._clearSelections();
-                               this._map.fire('setpart', {currentPart: 
this._currentPart});
-                       }
-                       else if (this._docType === 'text') {
-                               this._currentPage = part;
-                               this._map.fire('pagenumberchanged', {
-                                       currentPage: part,
-                                       pages: this._pages,
-                                       docType: this._docType
-                               });
-                       }
+       },
+
+       _onTextSelectionStartMsg: function (textMsg) {
+               var strTwips = textMsg.match(/\d+/g);
+               if (strTwips != null) {
+                       var topLeftTwips = new L.Point(parseInt(strTwips[0]), 
parseInt(strTwips[1]));
+                       var offset = new L.Point(parseInt(strTwips[2]), 
parseInt(strTwips[3]));
+                       var bottomRightTwips = topLeftTwips.add(offset);
+                       this._textSelectionStart = new L.LatLngBounds(
+                                               
this._twipsToLatLng(topLeftTwips, this._map.getZoom()),
+                                               
this._twipsToLatLng(bottomRightTwips, this._map.getZoom()));
                }
-               else if (textMsg.startsWith('searchnotfound:')) {
-                       var originalPhrase = textMsg.substring(16);
-                       this._map.fire('search', {originalPhrase: 
originalPhrase, count: 0});
+               else {
+                       this._textSelectionStart = new L.LatLngBounds(new 
L.LatLng(0, 0), new L.LatLng(0, 0));
                }
-               else if (textMsg.startsWith('styles:')) {
-                       this._docStyles = JSON.parse(textMsg.substring(8));
-                       this._map.fire('updatestyles', {styles: 
this._docStyles});
+
+       },
+
+       _onTileMsg: function (textMsg, imgBytes, index) {
+               var command = L.Socket.parseServerCmd(textMsg);
+               var coords = this._twipsToCoords(command);
+               coords.z = command.zoom;
+               coords.part = command.part;
+               var data = imgBytes.subarray(index + 1);
+
+               // read the tile data
+               var strBytes = '';
+               for (var i = 0; i < data.length; i++) {
+                       strBytes += String.fromCharCode(data[i]);
+               }
+
+               var key = this._tileCoordsToKey(coords);
+               var tile = this._tiles[key];
+               var img = 'data:image/png;base64,' + window.btoa(strBytes);
+               if (command.id !== undefined) {
+                       this._map.fire('tilepreview', {
+                               tile: img,
+                               id: command.id,
+                               width: command.width,
+                               height: command.height,
+                               part: command.part,
+                               docType: this._docType
+                       });
+               }
+               else if (tile) {
+                       if (this._tiles[key]._invalidCount > 0) {
+                               this._tiles[key]._invalidCount -= 1;
+                       }
+                       if (!tile.loaded) {
+                               this._emptyTilesCount -= 1;
+                               if (this._emptyTilesCount === 0) {
+                                       this._map.fire('statusindicator', 
{statusType: 'alltilesloaded'});
+                               }
+                       }
+                       tile.el.src = img;
                }
-               else if (textMsg.startsWith('error:')) {
-                       command = L.Socket.parseServerCmd(textMsg);
-                       this._map.fire('error', {cmd: command.errorCmd, kind: 
command.errorKind});
+               else if (command.preFetch === 'true') {
+                       this._tileCache[key] = img;
                }
+               L.Log.log(textMsg, L.INCOMING, key);
+
        },
 
        _tileOnLoad: function (done, tile) {
@@ -545,8 +617,6 @@ L.TileLayer = L.GridLayer.extend({
                                ' x=' + x + ' y=' + y);
        },
 
-
-
        // Is rRectangle empty?
        _isEmptyRectangle: function (aBounds) {
                return aBounds.getSouthWest().equals(new L.LatLng(0, 0)) && 
aBounds.getNorthEast().equals(new L.LatLng(0, 0));
commit ad756ec2576d1b30edcdecb121b56bd5581a0d40
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Wed Sep 2 10:47:19 2015 +0300

    loleaflet: first splitting of TileLayer into 3 components

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index d806268..fc4a18d 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -51,6 +51,24 @@ var deps = {
                deps: ['TileLayer']
        },
 
+       WriterTileLayer: {
+               src: ['layer/tile/WriterTileLayer.js'],
+               desc: 'Writer tile layer.',
+               deps: ['TileLayer']
+       },
+
+       ImpressTileLayer: {
+               src: ['layer/tile/ImpressTileLayer.js'],
+               desc: 'Impress tile layer.',
+               deps: ['TileLayer']
+       },
+
+       WriterTileLayer: {
+               src: ['layer/tile/CalcTileLayer.js'],
+               desc: 'Calc tile layer.',
+               deps: ['TileLayer']
+       },
+
        ImageOverlay: {
                src: ['layer/ImageOverlay.js'],
                desc: 'Used to display an image over a particular rectangular 
area of the map.'
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 78cb922..5e49874 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -89,13 +89,32 @@ L.Socket = {
                if (textMsg.startsWith('status:') && !this._map._docLayer) {
                        // first status message, we need to create the document 
layer
                        var command = this.parseServerCmd(textMsg);
-                       this._map._docLayer = new L.TileLayer('', {
-                               edit: this._map.options.edit,
-                               readOnly: this._map.options.readOnly
-                       });
-                       this._map.addLayer(this._map._docLayer);
+                       var docLayer = null;
+                       if (command.style === 'text') {
+                               docLayer = new L.WriterTileLayer('', {
+                                       edit: this._map.options.edit,
+                                       readOnly: this._map.options.readOnly
+                               });
+                       }
+                       else if (command.style === 'spreadsheet') {
+                               docLayer = new L.CalcTileLayer('', {
+                                       edit: this._map.options.edit,
+                                       readOnly: this._map.options.readOnly
+                               });
+                       }
+                       else {
+                               docLayer = new L.ImpressTileLayer('', {
+                                       edit: this._map.options.edit,
+                                       readOnly: this._map.options.readOnly
+                               });
+                       }
+
+                       this._map._docLayer = docLayer;
+                       this._map.addLayer(docLayer);
+               }
+               if (this._map._docLayer) {
+                       this._map._docLayer._onMessage(textMsg, imgBytes, 
index);
                }
-               this._map._docLayer._onMessage(textMsg, imgBytes, index);
        },
 
        _onSocketError: function () {
diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js 
b/loleaflet/src/layer/tile/CalcTileLayer.js
new file mode 100644
index 0000000..1a1795a
--- /dev/null
+++ b/loleaflet/src/layer/tile/CalcTileLayer.js
@@ -0,0 +1,6 @@
+/*
+ * Calc tile layer is used to display a spreadsheet document
+ */
+
+L.CalcTileLayer = L.TileLayer.extend({
+});
diff --git a/loleaflet/src/layer/tile/ImpressTileLayer.js 
b/loleaflet/src/layer/tile/ImpressTileLayer.js
new file mode 100644
index 0000000..a2d1fbf
--- /dev/null
+++ b/loleaflet/src/layer/tile/ImpressTileLayer.js
@@ -0,0 +1,7 @@
+/*
+ * Impress tile layer is used to display a presentation document
+ */
+
+
+L.ImpressTileLayer = L.TileLayer.extend({
+});
diff --git a/loleaflet/src/layer/tile/WriterTileLayer.js 
b/loleaflet/src/layer/tile/WriterTileLayer.js
new file mode 100644
index 0000000..6eb0258
--- /dev/null
+++ b/loleaflet/src/layer/tile/WriterTileLayer.js
@@ -0,0 +1,6 @@
+/*
+ * Writer tile layer is used to display a text document
+ */
+
+L.WriterTileLayer = L.TileLayer.extend({
+});
commit ccffd33a3b7fcb8bcce0992f632b03ecb86eff3b
Author: Mihai Varga <mihai.va...@collabora.com>
Date:   Tue Sep 1 18:15:43 2015 +0300

    loleaflet: README update

diff --git a/loleaflet/README b/loleaflet/README
index fcf1b09..f5b3488 100644
--- a/loleaflet/README
+++ b/loleaflet/README
@@ -213,15 +213,11 @@ Implementation details
 
 Loading a document:
     The map should have the following options:
-        - center at [0, 0] this will try to place the [0, 0] point in the 
middle of the
-          map, but it will be moved in the top left corner when the maxBounds 
are set
-        - zoom = defautl zoom value, zooming in and out will refer to this 
value
         - server address
-
-    The layer (actual document) should have the following options:
-        - doc = path to the document that will be loaded
-        - useSocket = tells the map the tiles will be received from a websocket
-          connection. If this parameter is false, an image will be loaded in 
each tile
+        - doc - path to the document that will be loaded
+        - edit = the initial permission
+        - readOnly - whether the document is read only
+        - [timestamp] - optionally provided for remote documents
 
 How zooming works:
     The zoom level goes from 1 to 20 (those limits can be changed) and the 
initial
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to