libreoffice-online/loleaflet
Weblate 9c2f6382d2 update translations
LibreOffice Online/loleaflet-help (Catalan)
Currently translated at 81.2% (338 of 416 strings)

Change-Id: I8b49030c370d554db036a0e186d6332a9263d0d0

update translations

LibreOffice Online/android-lib (Slovenian)
Currently translated at 100.0% (12 of 12 strings)

Change-Id: I70effac3f9908b12815c430931a2292bf5dbed5b

update translations

LibreOffice Online/android-app (Slovenian)
Currently translated at 100.0% (100 of 100 strings)

Change-Id: Ie1fd88e1eae85134679f9f7285ab3c1420bf15ad

update translations

LibreOffice Online/loleaflet-help (Slovenian)
Currently translated at 83.9% (349 of 416 strings)

Change-Id: I53b08543ba97e157fc0e505bd98d69e1c70237a7

update translations

LibreOffice Online/loleaflet-help (Catalan)
Currently translated at 80.0% (333 of 416 strings)

Change-Id: Ib6cedbdedcbf1c14204aae5c6a26964b99a09867

update translations

LibreOffice Online/loleaflet-ui (Slovenian)
Currently translated at 100.0% (290 of 290 strings)

Change-Id: I41024ff5c600da3f26471c00a5c06c0380b6c1ec

update translations

LibreOffice Online/android-lib (Turkish)
Currently translated at 83.3% (10 of 12 strings)

Change-Id: Ibc0f532df8170198be67b2699a8b3c265bffde23

update translations

LibreOffice Online/android-app (Turkish)
Currently translated at 100.0% (100 of 100 strings)

Change-Id: I3ea29557fe41bc46faa7313875c095e5e45185fe

update translations

LibreOffice Online/loleaflet-ui (Turkish)
Currently translated at 99.7% (289 of 290 strings)

Change-Id: Idbe17cd7b879bd1333a6fa5554309a5a437d82a8

update translations

LibreOffice Online/android-app (Italian)
Currently translated at 100.0% (100 of 100 strings)

Change-Id: I39c6d1dca5c9acfb51ad46dea4fa549b38b9a220

update translations

LibreOffice Online/android-lib (Italian)
Currently translated at 100.0% (12 of 12 strings)

Change-Id: I715122ed869741ae66f65efc8425b76fb024e0a8

update translations

LibreOffice Online/loleaflet-help (Italian)
Currently translated at 100.0% (416 of 416 strings)

Change-Id: Ide96c3d2ff684e67d1121c2d9bd3268353366f54

update translations

LibreOffice Online/loleaflet-ui (Italian)
Currently translated at 100.0% (290 of 290 strings)

Change-Id: Iab76b9dfc1a8835fe8fb226e4003d73b5057782c

update translations

LibreOffice Online/loleaflet-help (German)
Currently translated at 64.7% (269 of 416 strings)

Change-Id: I121156a5f5e9832237aa4eb7ed3bd88af3d2b14c

update translations

LibreOffice Online/loleaflet-ui (Hebrew)
Currently translated at 99.3% (288 of 290 strings)

Change-Id: I39c7a2d9bbc0dc4f3fa95e406561f9b313ec9e03

update translations

LibreOffice Online/android-app (Hebrew)
Currently translated at 100.0% (100 of 100 strings)

Change-Id: Ib11902d876161296d1694b0e456c302355523424

update translations

LibreOffice Online/android-lib (Bulgarian)
Currently translated at 100.0% (12 of 12 strings)

Change-Id: I9229d448dc8832226981faba1ccbc7a821edc396

update translations

LibreOffice Online/android-app (German)
Currently translated at 100.0% (100 of 100 strings)

Change-Id: I9596247c4d194c758759f2e9183626e4e569f548

update translations

LibreOffice Online/android-lib (German)
Currently translated at 100.0% (12 of 12 strings)

Change-Id: I76299ef7b35754ae2ca9e7975950ad5b4ecf0424

update translations

LibreOffice Online/loleaflet-ui (Hebrew)
Currently translated at 99.3% (288 of 290 strings)

Change-Id: I7500a2968f0b2d5a108058df434632a4543e476c

update translations

LibreOffice Online/loleaflet-ui (German)
Currently translated at 100.0% (290 of 290 strings)

Change-Id: I80d34bc21e0b40f4e01d4458be57064cd7ae0013

update translations

LibreOffice Online/android-app (Swedish)
Currently translated at 14.0% (14 of 100 strings)

Change-Id: I4705a3cd09a3de1b7ee4d183980b6f1fe85f4d3f

update translations

LibreOffice Online/android-lib (English (United Kingdom))
Currently translated at 100.0% (12 of 12 strings)

Change-Id: Ice3d527ec2672769988ba26f55d20e29d5ace51f

update translations

LibreOffice Online/android-lib (Hebrew)
Currently translated at 100.0% (12 of 12 strings)

Change-Id: Id9f4b645ce296ec177d5653397b8135f532b44e2

update translations

LibreOffice Online/loleaflet-help (Spanish)
Currently translated at 100.0% (416 of 416 strings)

Change-Id: I954a911766bc78bd2501bcdf1dde405ed5ea20c7

update translations

LibreOffice Online/loleaflet-help (Catalan)
Currently translated at 76.7% (319 of 416 strings)

Change-Id: I71e722ad3cd5d6602b069ef71329860c4cd8d6b9
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90866
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Andras Timar <andras.timar@collabora.com>
2020-03-26 12:00:37 +01:00
..
admin loleaflet: makefile: do not execute javascript to get admin files 2020-03-16 16:12:00 +01:00
archived-packages
css Get rid of the iOS app specific "context toolbar" 2020-03-25 16:29:25 +01:00
docs
html help: Apply suggestions from Mihail Balabanov and Steen Rønnow from Weblate 2020-03-15 22:16:47 +01:00
images Mobile: Calc: Chart: add axis contextual icons 2020-03-25 10:46:40 +01:00
js Move the window.mode.* functions to global.js for clarity 2020-03-26 10:13:02 +01:00
l10n Update UNO command translations 2020-03-18 19:47:51 +01:00
po update translations 2020-03-26 12:00:37 +01:00
spec Bin pointless global constant variables L.INCOMING and L.OUTGOING 2020-03-25 11:26:38 +01:00
src loleaflet: Reordering slide always moved it to the beginning 2020-03-26 11:59:34 +01:00
test
util android: Make the localization work for the JS pieces. 2019-12-11 11:49:37 +01:00
.eslintignore loleaflet: import Autolinker.js library 2020-02-21 00:49:04 +01:00
.eslintrc cypress: new impress editing tests 2020-03-17 22:53:11 +01:00
.gitattributes
.gitignore
.npmignore
.travis.yml
bundle.js.m4 loleaflet: makefile: use m4 to bundle all js files 2020-03-03 18:21:10 +01:00
LICENSE
loleaflet-src.js.m4 loleaflet: makefile: use m4 to bundle src js files 2020-03-02 16:51:32 +01:00
Makefile.am Get rid of the iOS app specific "context toolbar" 2020-03-25 16:29:25 +01:00
npm-shrinkwrap.json.in
package.json loleaflet: import sanitize-url.js library 2020-02-20 13:20:44 +01:00
README loleaflet: import sanitize-url.js library 2020-02-20 13:20:44 +01:00
reference.html tdf#129979: loleaflet send postMessage on cancel for password protected files 2020-01-16 17:27:08 +01:00

Leaflet platform for LibreOffice Online
========================================

This is the client part of LibreOffice Online.  For the server part, see the
../wsd/README, and install it first.

Build dependencies
------------------

npm is provided by the nodejs package.

npm should be at least 2.14, if not 3.x. Use `npm -v` to find the current version.
Upgrade npm (as root):

    npm install -g npm

Another way is to use npm as a user, but set its prefix to a directory where
you have write access. If you want that, you need to have an ~/.npmrc with the
line e.g.

    prefix=/opt/npm

All of the dependency tree is locked in the repository, so there is no need to fetch
data from npm registry at all. An npm install will use the tarballs in node_shrinkwrap/
directory, so build process can move forward even without an internet connection.

To update any of the dependency, process often goes like this:

1.) Change version of the dependency in package.json
2.) Do an `npm update <package_name>', which fetches the new module from npm registry. Make sure that
    only the module that you want to update is updated.
3.) `npm shrinkwrap --dev` to update npm-shrinkwrap.json with newer information. You might see
    that this tool updates path convention of tarballs inside node_shrinkwrap/ also, but this
    should get corrected in next step.
4.) Do `shrinkpack', which should remove the old tarball, add new one, and make appropriate
    changes to npm-shrinkwrap.json (removing path convention changes by step 3).

If you have not installed `shrinkpack' globally using `npm install -g shrinkpack@next', it should be
in node_modules/.bin/shrinkpack, so you can use the binary from there.

If you need to get the sources:
	make libs

To bundle the file sanitize-url.js
	browserify braintree-sanitize-url/index.js --standalone sanitizeUrl > sanitize-url.js

Building
--------

Just do:

    make

Above step would create a non-minified bundle.js and admin-bundle.js without source-maps in dist/ for main loleaflet and admin console
respectively.

To build with debug-info, i.e with sourcemaps:

    make DEBUG=true

Above will produce source-map files, bundle.js.map and admin-bundle.js.map, for bundle.js and admin-bundle.js respectively.
It will also link existing bundle.js and admin-bundle.js to their map files by adding a sourceMappingURL to them. While debugging,
these .map files will be fetched from the server if present in dist/, otherwise there is no way to debug while browsing without these
source-map files.

To minify our bundle.js and admin-bundle.js passing a MINIFY=true flag to 'make' will minify it. This can be helpful in production
environments.

    make MINIFY=true

'make dist' forces minifying.

Running
-------

To see an example:

* run loolwsd, like:

    ./loolwsd --o:sys_template_path=${SYSTEMPLATE} --o:child_root_path=${ROOTFORJAILS}

Note that this will, by default, set the loolwsd's file server's root to the parent directory of loolwsd,
which means you can access all the files in loleaflet using /loleaflet/ path. It is advised to set
file_server_root_path manually for more control. See wsd/README for more information.

* open dist/loleaflet.html through loolwsd's fileserver

    https://localhost:9980/loleaflet/dist/loleaflet.html?file_path=file:///PATH/TO_DOC&host=wss://localhost:9980

and you should see the document in the browser.
Note that accessing local storage is disabled by default for security reasons. You need to explicitly enable it
with the --o:storage.filesystem[@allow]=true option of loolwsd. In case anything goes wrong, check the loolwsd console for
the debugging output. You might be asked to confirm the certificate if you are using self-signed certificate
for loolwsd.

Development
-----------

For faster development you might want to install browser-sync which can reload app in your browsers automatically
after you edit and save the source code (so for .css only):

npm install -g browser-sync

Remember to add '--enable-browsersync' argument for './configure' script and delete `dist` directory.

Run server and start browser-sync:

LOOL_SERVE_FROM_FS=1 make run

and in the second terminal:

make sync-[writer|calc|impress]

Your browser will open example document and now you can edit .css files and see the result without server restart.

To run another document use:
browser-sync start --config browsersync-config.js --startPath "loleaflet/96c23f663/loleaflet.html?file_path=file:///path/to/the/file.ods"

Admin Panel
-----------

You can do live monitoring of all the user sessions running on loolwsd instance. To access the admin
console you need to ask for admin.html file from loolwsd which resides in dist/admin/admin.html.

For example:

    https://localhost:9980/loleaflet/dist/admin/admin.html

It will ask for username and password which is set by the admin_console options of loolwsd. For example you can
pass --o:admin_console.username=admin --o:admin_console.password=admin in command line, or set these values in
loolwsd.xml. After entering the correct password you should be able to monitor the live documents opened, total
users, memory consumption, document URLs with number of users viewing that document etc. You can also kill the
documents directly from the panel which would result in closing the socket connection to the respective document.

Testing
-------
    - to run the unit tests
        + open spec/loleaflet.html in the browser
    - to simulate an editing session and to get the tile loading times
        + open spec/tilebench.html in the browser
    - to simulate a client opening several documents in the browser
        + open spec/loadtest.html in the browser
    - to simulate a client opening several documents in the console
        + run: node_modules/.bin/mocha spec/headlessLoadTest.js
    - to simulate multiple clients opening several documents in the console
        + run: make load-test

The structure of the unit tests:
Loleaflet unit tests are located under spec/loleaflet. Following Leaflet's testing style, each test's path
should mirror the source's path, so spec/loleaflet/control/ToolbarSpec.js tests the features from
src/control/Toolbar.js . Any new test file needs also to be added in spec/leaflet.html


API & events
------------

#######################################################################
# See /loleaflet/reference.html for a better formatted documentation. #
# See /wsd/reference.md for the HTTP API documentation.               #
#######################################################################

Search:
    - API:
        map.search(text, [backward])
        map.higlightAll(text)
    - events:
        map.on('search', function (e) {}) (currently only fired when no search result is found) where:
            + e.originalPhrase = the phrase that has been searched for
            + e.count = number of results
            + e.results = [SearchResult], where SearchResult = {part: part, rectangles: [Bounds]}

Zoom:
    - API:
        map.zoomIn(amount)
        map.zoomOut(amount)
        map.getMinZoom()
        map.getMaxZoom()
    - events:
        map.on('zoomend zoomlevelschange', function)

Edit, view, readOnly:
    - API:
        map.setPermission('edit' | 'view' | 'readonly')
    - events:
        map.on('updatepermission', function (e) {}) where:
            + e.perm == 'edit' | 'view' | 'readonly'

Buttons like Bold, Italic, Strike through etc.
    - API:
        map.toggleCommandState('.uno:' + 'Bold' | 'Italic' | 'Underline' | 'Strikeout' |
            'LeftPara' | 'CenterPara' | 'RightPara' | 'JustifyPara' |
            'IncrementIndent' | 'DecrementIndent'
    - events:
        map.on('commandstatechanged', function (e) {}) where:
            + e.commandName == '.uno:' + 'Bold' | 'Italic' | 'StyleApply' | 'CharFontName' | 'FontHeight' etc.
            + e.state = 'true' | 'false'
            + e.state = fontName | fontSize | styleName
        map.on('commandresult', function (e) {}) where:
            + e.commandName == '.uno:' + 'Bold' | 'Italic' | 'StyleApply' | 'CharFontName' | 'FontHeight' etc.
            + e.success = true | false | undefined

Parts (like slides in presentation, or sheets in spreadsheets):
    - API:
        map.setPart('next' | 'prev' | partNumber)
        map.getNumberOfParts()
        map.getCurrentPartNumber()
        map.getPreview(id, index, maxWidth, maxHeight, [options], forAllClients)
            + id = the ID of the request so that the response can be identified
            + index = the part / page 's number
            + maxWidth / maxHeight = max dimensions so that the ratio is preserved
            + options = {autoUpdate: <boolean>, broadcast: <boolean>} -
	      + autoUpdate - boolean, automatically updates the previews
	      + broadcast - boolean, whether the response (a preview of a slide) should be sent to all clients
                viewing the same presentation
        map.getCustomPreview(id, part, width, height, tilePosX, tilePosY, tileWidth, tileHeight, [options])
            + id = the ID of the request so that the response can be identified
            + part = the part containing the desired preview
            + width / height = the preview's size in pixels
            + tilePosX / tilePosY = the rectangles's starting position in twips
            + tileWidth / tileHeight = the rectangle's dimension in twips
            + options = {autoUpdate: true} - automatically updates the previews
        map.removePreviewUpdate(id)
            + id = the preview's id

    - events:
        map.on('updateparts', function (e) {}) where:
            + e.selectedPart is the current part
            + e.parts == the number of parts that the document has
            + e.docType == 'text' | 'spreadsheet' | 'presentation' | 'drawing' | 'other'
            + [e.partNames] if present, part names (e.g. sheet names)
        map.on('tilepreview', function (e) {}) where:
            + e.tile - the preview image
            + e.id - the preview id
            + e.width - width of the image
            + e.height - height of the image
            + [e.part] - if the preview is for a part
            + e.docType

Statusindicator (when the document is loading):
    - events
        map.on('statusindicator', function (e) {}) where:
            + e.statusType = 'start' | 'setvalue' | 'finish' | 'loleafletloaded' | 'alltilesloaded'
            + e.value == a value from 0 to 100 indicating the status
              if the statusType is 'setvalue
            + 'loleafletloaded' is fired when the JS code is initialized and the document
                load request is sent and we're waiting for the tiles
            + 'alltilesloaded' is fired when all newly requested (empty tiles) have been loaded
                it is not fired during pre-fetching and during editing

Save:
    - API:
        map.saveAs(url, [format, options])
        map.downloadAs(name, [format, options])

Scroll (the following are measured in pixels):
    - API:
            + options = An object with members: update (type: Boolean, default: false)
                like {update: true}
        map.scroll(x,y, options)
            + scroll right by 'x' and down by 'y' (or left and up if negative)
        map.scrollDown(y, options)
            + scroll down by 'y' (or up if negative)
        map.scrollRight(x, options)
            + scroll right by 'x' (or left if negative)
        map.scrollTop(y, options)
            + scroll to 'y' offset relative to the beginning of the document
        map.scrollLeft(x, options)
            + scroll to 'x' offset relative to the beginning of the document
        map.scrollOffset()
            + returns the scroll offset relative to the beginning of the document
        map.getDocSize()
            + returns the document's size in pixels
        map.getDocType()
            + returns 'text' | 'spreadsheet' | 'presentation' | 'drawing' | 'other'
    - events
        map.on('docsize', function (e) {}) where:
            + e.x = document width
            + e.y = document height
        map.on('updatescrolloffset', function (e) {}) where:
            + e.x = difference between document's left and current view's left
                (how much has the document been scrolled right)
            + e.y = difference between document's top and current view's top
                (how much has the document been scrolled down)
            - this event is fired when zooming and the current view is maintained but the
                document shrinks or grow OR when the document is panned OR when the container is resized
        map.on('scrollto', function (e) {}) where:
            + e.x = view's left position (so that the cursor/search result is in the center)
            + e.y = view's top position (so that the cursor/search result is in the center)
        map.on('scrollby', function (e) {}) where:
            + e.x = the amount scrolled to the right (or left if negative)
            + e.y = the amount scrolled to the bottom (or top if negative)

Writer pages:
    - API:
        map.goToPage(page)
        map.getNumberOfPages()
        map.getCurrentPageNumber()
        map.getPreview(id, index, maxWidth, maxHeight, [options])
            + id = the ID of the request so that the response can be identified
            + index = the part / page 's number
            + maxWidth / maxHeight = max dimensions so that the ratio is preserved
            + options = {autoUpdate: true} - automatically updates the previews
        map.getCustomPreview(id, part, width, height, tilePosX, tilePosY, tileWidth, tileHeight, [options])
            + id = the ID of the request so that the response can be identified
            + part = the part containing the desired preview
            + width / height = the preview's size in pixels
            + tilePosX / tilePosY = the rectangles's starting position in twips
            + tileWidth / tileHeight = the rectangle's dimension in twips
            + options = {autoUpdate: true} - automatically updates the previews
        map.removePreviewUpdate(id)
            + id = the preview's id
        map.getPageSizes()
            + returns {twips: [Bounds], pixels: [Bounds]}

    - 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'

Error:
    - events
        map.on('error', function (e) {}) where
            + [e.msg] = a message describing the error
            + [e.cmd] = the command that caused the error
            + [e.kind] = the kind of error

Infobars:
    - events
        map.on('infobar', function (e) {}) where
            + [e.msg] = a message
            + [e.actionlabel] = Label for the action button
            + [e.action] = A link (starting with http).
                           Please extend to allow other actions when needed.

CommandValues:
    - api:
        map.getToolbarCommandValues(command)
            + returns a JSON mapping of all possible values for the command
        map.applyFont(fontName)
        map.applyFontSize(fontSize)
        map.applyStyle(style, styleFamily)
    - events
        map.on('updatetoolbarcommandvalues', function (e) {}) where
            + e.commandName = '.uno:StyleApply', etc
            + e.commandValues = a JSON mapping of all possible values for the command

Print:
    - events
        map.on('print', function (e) {}) where
            + e.url = file download url

Contributing
------------

Code conventions:

    * files should have unix line terminators (LF)
    * tools to convert files: dos2unix or fromdos

Implementation details
----------------------

Loading a document:
    The map should have the following options:
        - server address
        - 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
    level is 10, which represents the 100% zoom level. The zoom factor is 1.2

Controls are added above the map in a div called "controls" is intended to be used as a toolbar.
There is no leaflet method of adding them in a separate div, so for now this is done in the html
document after the map initialization.

To enable scrollbars the map is placed above a div that contains a bigger div of
the document's size (a mock document). So the div under the map gets scrollbars which
are independent of the map's div, thus enabling us to link them to the map as needed.
When the user scrolls, the map is panned by the same amount as it would've been scrolled.
Also, some custom jquery scrollbars are used, to trigger the same scroll events across
browsers.

Z-index values:
-------------------------------------------
		leaflet
-------------------------------------------
10			map
11			ruler
-------------------------------------------
		under menu
-------------------------------------------
990			sidebar
999			toolbar-up
-------------------------------------------
		menu items
-------------------------------------------
1000			main-menu(desktop-only), toolbar-down
1050			closebuttonwrapper (not being used currently)
-------------------------------------------
		on the top
-------------------------------------------
1105			dialogs
1001			mobile-edit-button
1500			mobile-wizard (with class=menuwizard)
1501			toolbar-hamburger (with class=menuwizard-opened)
2000			vex-overlay
2001			vex dialog (vex-content)
-------------------------------------------