Merge branch 'staging' into history

pull/1/head
yflory 4 years ago
commit 6e4e932cc0

@ -16,6 +16,7 @@ server.js
www/common/old-media-tag.js
www/scratch
www/lib
www/accounts
www/common/toolbar.js
www/common/hyperscript.js
@ -37,3 +38,4 @@ www/debug/chainpad.dist.js
www/pad/mathjax/
www/code/mermaid*.js
www/code/orgmode.js

@ -1,3 +1,97 @@
# WoollyMammoth (3.22.0)
## Goals
We've been working on some long-term projects that we hope to deliver over the course of the next few releases. In the meantime, this release includes a number of minor improvements.
## Update notes
To upgrade from 3.21.0 to 3.22.0:
1. Stop your server
2. Get the latest platform code with git
3. Install client-side dependencies with `bower update`
4. Restart the CryptPad API server
## Features
* Contributors have helped by translating more of CryptPad into Finnish and traditional Chinese via [our weblate instance](https://weblate.cryptpad.fr/projects/cryptpad/app/)
## Bug fixes
* Some of the special behaviour implemented for Org-mode in our code editor sometimes failed when the document was first changed into Org-mode.
* We now clear some minor personal preferences like whether certain tooltips had been dismissed when you log out.
* We identified and addressed a number of issues with teams that caused valid teams to not be displayed and team member rights to fail to upgrade until a full session reload.
* We now display the number of days before an unregistered user's documents are considered inactive in their drive instead of hardcoding "3 months".
# VietnameseRhinoceros (3.21.0)
## Goals
This release was developed over a longer period than usual due to holidays, our yearly company seminar, and generally working on some important software-adjacent projects. As such, we opted not to aim for any major features and instead introduce some minor improvements and address some users' complaints.
## Update notes
We've had a few disgruntled administrators contact us about our apparent _failure to provide a docker image_ or to otherwise support their preferred configuration. With that in mind, this is a periodic reminder that CryptPad is provided to the public under the terms of the AGPL (found within this repository in the [LICENSE file](./LICENSE)) which implies on our part no warranty, liability, or responsibility to configure your server for you. We do our best to provide the necessary information to correctly launch your own instance of the software given our limited budget, however, all such files are provided **AS IS** and are only intended to function under the narrow circumstances of usage which we recommend within the comments of the provided example configuration files.
With that said, the vast majority of our community acts kindly and courteously towards us and each other. We really do appreciate it, and we'll continue to help you to the best of our ability. With that in mind, we're happy to announce that we've written and deployed a first version of our user guide, available at https://docs.cryptpad.fr. The work that went into this was funded by NLnet foundation as an NGI Zero PET (Privacy-Enhancing Technology) grant. We are currently working on two more guides intended for developers and administrators, and will deploy them to the same domain as they are completed. In the meantime we have begun to update our README, GitHub wiki, and other resources to reflect the current recommended practices and remove references to unsupported configurations.
If you're only reading this for instructions on how to update your instance from 3.20.1 to 3.21.0:
1. Stop your server
2. Get the latest platform code with git
3. Install client-side dependencies with `bower update`
4. Install server-side dependencies with `npm install`
4. Restart the CryptPad API server
## Features
* We spent a little bit of time during our company seminar and implemented a first version of an automatically generated _table of contents_ in our rich text editor. It is populated using header styles applied with the editor's dropdown menus, and can be hidden by clicking the "Outline" button in the app toolbar.
* We also made it possible to change the default behaviour of the Kanban tag filter via the settings page. You may choose to compound the selection of multiple tags as AND, resulting in the display of cards that have all the selected tags rather than the default OR behaviour which displays any card including any one of the selected tags.
* We've integrated a third-party Org-mode library into our code editor which features some fancy click-handlers that toggle the state of certain org-mode classifications.
* The search results interface which is present in individual and team drives has been improved such that it displays a spinner while a search is pending and that it indicates when there are no results for a given term.
* We've added a Japanese font (Komorebi-gothic) for use within the spreadsheet editor and have received and integrated Japanese translations from a contributor via our weblate instance (https://weblate.cryptpad.fr).
* Finally, we've modified some behaviour in individual and team drives, making it possible to move a shared folder to the trash where it was previously only possible to directly remove it from your drive.
## Bug fixes
* We've corrected a minor server issue in which it would respond to requests to destroy non-existent files with an E_NO_OWNERS error, rather than an ENOENT (doesn't exist) error. The client code interpreted this as the file existing without them having the rights to delete it, rather than realizing that it no longer existed. This made it more difficult to remove files from your drive since destruction would fail rather than be interpreted as unnecessary.
* We now guard against race conditions in our internal _write-queue_ library, preventing a rare occurrence of a type error triggered by unknown circumstances.
* We discovered that Firefox had enabled (by default) half of the functionality required to export sheets to an XLSX format. We interpreted the presence of this feature as sufficient cause to display XLSX as an export option, even though the export would fail if you tried to use it. The second half of the required functionality is available in Firefox, but requires specific HTTP headers to be sent by our server. We're currently testing the configuration parameters and expect to make XLSX export available on CryptPad.fr very soon, along with an update to our recommended configuration which would enable it on other instances.
* Lastly, we discovered an incompatibility betweeen our "safe links" behaviour and the process of redirecting users to log in or register to access specific functionality. Users that were redirected from pads accessed with safe links were redirected to that safe link whether or not they had imported the pad's keys into their newly created drive. This could result in a temporary loss of access to the pad, even though its credentials were still stored within their browser. We've corrected the redirect process to preserve the full document credentials for after you have logged in.
# UplandMoa's revenge (3.20.1)
Once again we've decided to follow up our last major release with a minor "revenge" release that we wanted to make available as soon as possible.
We expect to deploy and release version 3.21.0 on Tuesday, July 28th, 2020.
Features
* The _markmap_ rendering mode which was recently added to markdown preview pane implements some click event handlers which overlap with our existing handlers which open the embedded mindmap in our full screen "lightbox". You can now use _ctrl-click_ to trigger its built-in events (collapsing subtrees of the mindmap) without opening the lightbox.
* We've made a few improvement to user and team drives:
* The _list mode_ now features a "ghost icon" which you can use to create a new pad in the current folder, matching behaviour that already existed in grid mode.
* We've also updated the search mode to display a spinner while your search is in progress. We also display some text when no results are found.
* Team drives now open with the sidebar collapsed.
* Our rich text, code, slide, and poll apps now intercept pasted images and prompt the user to upload them, matching the existing experience of dragging an image into the same editable area.
* We've received new contributions to our Romanian translation via [our weblate instance](https://weblate.cryptpad.fr/projects/cryptpad/app/).
Bug fixes
* We identified some race conditions in our spreadsheet app that were responsible for some corrupted data during the period leading up to our 3.20.0 release, however, we wanted to take a little more time to test before releasing the fixes. As of this release we're moving to a third version of our internal data format. This requires a client-side migration for each older sheet which will be performed by the first registered user to open a sheet in edit mode, after which a page reload will be required. Unregistered users with edit rights will only be able to view older sheets until they have been migrated by a registered user.
* We now guard against empty _mathjax_ and _markmap_ code blocks in their respective markdown preview rendering extensions, as we discovered that empty inputs resulted in the display of "undefined" in the rendered element.
* We noticed and fixed two regressions in user and team drives:
1. drive history had stopped working since the introduction of the "restricted mode" for shared folders which were made inaccessible due to the enforcement of their access lists.
2. users with shared folders which had been deleted or had their passwords changed were prompted to delete the folder from their drive or enter its new password. The "submit" button was affected by a style regression which we've addressed.
* We've updated to a new version of `lodash` as a dependency of the linters that we use to validate our code. Unless you were actively using those linters while developing CryptPad this should have no effect for you.
* Finally, when users open a link to a "self-destructing pad" we now check to make sure that the deletion key they possess has not been revoked before displaying a warning indicating that the pad in question will be deleted once they open it.
To update from 3.20.0 to 3.20.1:
1. Stop your server
2. Get the latest code with `git checkout 3.20.1`
3. Install the latest dependencies with `bower update` and `npm i`
3. Restart your server
# UplandMoa (3.20.0)
## Goals

@ -205,3 +205,11 @@ a > img {
.cp-link-clicked a {
cursor: pointer;
}
media-tag {
display: inline-block;
}
media-tag * {
width: 100%;
height: 100%;
}

@ -62,7 +62,7 @@ define([
var imprintUrl = AppConfig.imprint && (typeof(AppConfig.imprint) === "boolean" ?
'/imprint.html' : AppConfig.imprint);
Pages.versionString = "CryptPad v3.20.0 (UplandMoa)";
Pages.versionString = "CryptPad v3.22.0 (WoollyMammoth)";
// used for the about menu
Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined;

@ -227,16 +227,7 @@
}
}
::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
color: @cryptpad_color_grey;
opacity: 1; /* Firefox */
}
:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: @cryptpad_color_grey;
}
::-ms-input-placeholder { /* Microsoft Edge */
color: @cryptpad_color_grey;
}
.tools_placeholder-color(@cryptpad_color_grey);
span.cp-password-container {
display: flex;

@ -222,7 +222,7 @@
#cp-app-drive-search {
display: flex;
display: inline-flex;
align-items: center;
max-width: 400px;
font-size: 30px;
@ -230,7 +230,7 @@
input {
background: transparent;
color: @colortheme_drive-color;
.tools_placeholder-color(@colortheme_drive-color);
.tools_placeholder-color(@cryptpad_color_grey);
outline-width: 0px;
border-radius: 0;
width: 100%;
@ -256,7 +256,19 @@
color: @colortheme_drive-color;
}
}
.cp-app-drive-search-spinner {
display: inline-flex;
color: @colortheme_drive-color;
font-size: 40px;
align-items: center;
justify-content: center;
}
.cp-app-drive-search-noresult {
font-size: 30px;
padding: 15px;
font-style: italic;
color: @cryptpad_color_grey;
}
/* TREE */
@ -553,6 +565,21 @@
.cp-app-drive-element {
.cp-app-drive-element-truncated { display: none; }
}
.cp-app-drive-new-ghost {
cursor: pointer;
opacity: 0.5;
padding: 0;
align-items: center;
justify-content: center;
display: inline-flex;
&:hover {
opacity: 0.7;
}
.fa, .cptools {
cursor: pointer;
}
}
div.cp-app-drive-content-grid {
padding: 1em;
ul {
@ -600,27 +627,17 @@
}
}
}
.cp-app-drive-element-list {
display: none;
}
.cp-app-drive-new-ghost {
cursor: pointer;
opacity: 0.5;
padding: 0;
flex-flow: column;
align-items: center;
justify-content: center;
display: inline-flex;
&:hover {
opacity: 0.7;
}
.fa, .cptools {
cursor: pointer;
font-size: 90px;
margin-top: 5px;
margin-bottom: 0;
}
}
.cp-app-drive-element-list {
display: none;
}
}
.cp-app-drive-content-list {
@ -628,6 +645,10 @@
display: none;
}
// Make it act as a table!
.cp-app-drive-new-ghost {
padding: 0 5px;
margin-top: 20px;
}
padding-left: 10px;
ul {
width: 100%;

@ -996,6 +996,9 @@
.cp-toolbar-tools {
order: 7;
}
.cp-toolbar-icon-pad_toc {
order: 8;
}
.cp-toolbar-file {
button {
&.fa-plus { order: 0; }

@ -2,13 +2,9 @@
&::-webkit-input-placeholder { /* WebKit, Blink, Edge */
color: @color;;
}
&:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
&::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
color: @color;
opacity: 1;
}
&::-moz-placeholder { /* Mozilla Firefox 19+ */
color: @color;
opacity: 1;
opacity: 1; /* Firefox */
}
&:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: @color;

@ -54,14 +54,29 @@ Channel.clearOwnedChannel = function (Env, safeKey, channelId, cb, Server) {
});
};
var archiveOwnedChannel = function (Env, safeKey, channelId, cb, Server) {
var archiveOwnedChannel = function (Env, safeKey, channelId, _cb, Server) {
var unsafeKey = Util.unescapeKeyCharacters(safeKey);
Metadata.getMetadata(Env, channelId, function (err, metadata) {
if (err) { return void cb(err); }
if (!Core.hasOwners(metadata)) { return void cb('E_NO_OWNERS'); }
if (!Core.isOwner(metadata, unsafeKey)) {
return void cb('INSUFFICIENT_PERMISSIONS');
}
nThen(function (w) {
// confirm that the channel exists before worrying about whether
// we have permission to delete it.
var cb = _cb;
Env.msgStore.getChannelSize(channelId, w(function (err, bytes) {
if (!bytes) {
w.abort();
return cb(err || "ENOENT");
}
}));
}).nThen(function (w) {
var cb = Util.both(w.abort, _cb);
Metadata.getMetadata(Env, channelId, function (err, metadata) {
if (err) { return void cb(err); }
if (!Core.hasOwners(metadata)) { return void cb('E_NO_OWNERS'); }
if (!Core.isOwner(metadata, unsafeKey)) {
return void cb('INSUFFICIENT_PERMISSIONS');
}
});
}).nThen(function () {
var cb = _cb;
// temporarily archive the file
return void Env.msgStore.archiveChannel(channelId, function (e) {
Env.Log.info('ARCHIVAL_CHANNEL_BY_OWNER_RPC', {

@ -671,29 +671,6 @@ var unarchiveChannel = function (env, channelName, cb) {
}));
});
};
/*
var flushUnusedChannels = function (env, cb, frame) {
var currentTime = +new Date();
var expiration = typeof(frame) === 'undefined'? env.channelExpirationMs: frame;
Object.keys(env.channels).forEach(function (chanId) {
var chan = env.channels[chanId];
if (typeof(chan.atime) !== 'number') { return; }
if (currentTime >= expiration + chan.atime) {
closeChannel(env, chanId, function (err) {
if (err) {
console.error(err);
return;
}
if (env.verbose) {
console.log("Closed channel [%s]", chanId);
}
});
}
});
cb();
};
*/
/* channelBytes
calls back with an error or the size (in bytes) of a channel and its metadata
@ -1235,11 +1212,6 @@ module.exports.create = function (conf, _cb) {
closeChannel(env, channelName, Util.both(cb, next));
});
},
// iterate over open channels and close any that are not active
flushUnusedChannels: function (cb) {
cb("DEPRECATED");
//flushUnusedChannels(env, cb);
},
// write to a log file
log: function (channelName, content, cb) {
// you probably want the events in your log to be in the correct order.
@ -1253,8 +1225,4 @@ module.exports.create = function (conf, _cb) {
}
});
});
/*
it = setInterval(function () {
flushUnusedChannels(env, function () { });
}, 5000);*/
};

@ -17,7 +17,7 @@ module.exports = function () {
var next = function (id) {
setTimeout(function () {
if (map[id] && map[id].length === 0) { return void delete map[id]; }
if (!map[id] || map[id].length === 0) { return void delete map[id]; }
var task = map[id].shift();
task(fix1(next, id));
});

30
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "cryptpad",
"version": "3.20.0",
"version": "3.22.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -393,12 +393,12 @@
}
},
"dot-prop": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
"integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
"integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"dev": true,
"requires": {
"is-obj": "^1.0.0"
"is-obj": "^2.0.0"
}
},
"ecc-jsbn": {
@ -770,9 +770,9 @@
"integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
},
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"dev": true
},
"is-typedarray": {
@ -977,9 +977,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"dev": true
},
"lodash.clonedeep": {
@ -1243,12 +1243,12 @@
}
},
"postcss-selector-parser": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz",
"integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
"integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
"dev": true,
"requires": {
"dot-prop": "^4.1.1",
"dot-prop": "^5.2.0",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}

@ -1,7 +1,7 @@
{
"name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server",
"version": "3.20.0",
"version": "3.22.0",
"license": "AGPL-3.0+",
"repository": {
"type": "git",

@ -1,6 +1,6 @@
[![An XWiki Labs Project](https://raw.githubusercontent.com/xwiki-labs/xwiki-labs-logo/master/projects/xwikilabs/xlabs-project.png "XWiki labs")](https://labs.xwiki.com/xwiki/bin/view/Main/WebHome)
![CryptPad screenshot](https://github.com/xwiki-labs/cryptpad/raw/master/screenshot.png "Pads are an easy way to collaborate")
![CryptPad screenshot](screenshot.png "Private real-time collaboration on a Rich Text document.")
CryptPad is the **Zero Knowledge** realtime collaborative editor.
@ -24,9 +24,6 @@ The most recent version and all past release notes can be found [here](https://g
See [Cryptpad-Docker](https://github.com/xwiki-labs/cryptpad-docker) repository for details on how to get up-and-running with Cryptpad in Docker. This repository is maintained by the community and not officially supported.
## Setup using Ansible
See [Ansible Role for Cryptpad](https://github.com/systemli/ansible-role-cryptpad).
# Security

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 116 KiB

@ -10,7 +10,6 @@ define([
'/common/common-util.js',
'/common/common-hash.js',
'/code/markers.js',
'/common/modes.js',
'/common/visible.js',
'/common/TypingTests.js',
'/customize/messages.js',
@ -57,7 +56,6 @@ define([
Util,
Hash,
Markers,
Modes,
Visible,
TypingTest,
Messages,

@ -4,10 +4,40 @@ define([
], function (CodeMirror) {
CodeMirror.__mode = 'orgmode';
var isEmpty = function (el, idx) {
if (idx < 2) { return true; }
return !Boolean(el);
};
var onLevelOne = function (matches) {
// If all the elements starting from index 2 are empty, remove them
// because it means it's an empty header for now and it may break codemirror
if (matches && matches.length > 2 && matches.every(isEmpty)) {
matches.splice(2, (matches.length-2));
}
return ["header level1 org-level-star","header level1 org-todo","header level1 org-done", "header level1 org-priority", "header level1", "header level1 void", "header level1 comment"];
};
// Dirty hack to make the function also work as an array
onLevelOne().forEach(function (str, i) { onLevelOne[i] = str; });
var onLevelStar = function (matches) {
// If all the elements starting from index 2 are empty, remove them
// because it means it's an empty header for now and it may break codemirror
if (matches && matches.length > 2 && matches.every(isEmpty)) {
matches.splice(2, (matches.length-2));
}
return ["header org-level-star","header org-todo","header org-done", "header org-priority", "header", "header void", "header comment"];
};
// Dirty hack to make the function also work as an array
onLevelStar().forEach(function (str, i) { onLevelStar[i] = str; });
CodeMirror.defineSimpleMode("orgmode", {
start: [
{regex: /(\*\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: onLevelOne},
{regex: /(\*{1,}\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/g, sol: true, token: onLevelStar},
/*
{regex: /(\*\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header level1 org-level-star","header level1 org-todo","header level1 org-done", "header level1 org-priority", "header level1", "header level1 void", "header level1 comment"]},
{regex: /(\*{1,}\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/, sol: true, token: ["header org-level-star","header org-todo","header org-done", "header org-priority", "header", "header void", "header comment"]},
{regex: /(\*{1,}\s)(TODO|DOING|WAITING|NEXT|PENDING|)(CANCELLED|CANCELED|CANCEL|DEFERRED|DONE|REJECTED|STOP|STOPPED|)(\s+\[\#[A-C]\]\s+|)(.*?)(?:(\s{10,}|))(\:[\S]+\:|)$/g, sol: true, token: ["header org-level-star","header org-todo","header org-done", "header org-priority", "header", "header void", "header comment"]},
*/
{regex: /(\+[^\+]+\+)/, token: ["strikethrough"]},
{regex: /(\*[^\*]+\*)/, token: ["strong"]},
{regex: /(\/[^\/]+\/)/, token: ["em"]},
@ -93,6 +123,210 @@ define([
};
});
var init = false;
CodeMirror.registerHelper("orgmode", "init", function (editor) {
if (init) { return; }
editor.setOption("extraKeys", {
"Tab": function(cm) { org_cycle(cm); },
"Shift-Tab": function(cm){ org_shifttab(cm); },
"Alt-Left": function(cm){ org_metaleft(cm); },
"Alt-Right": function(cm){ org_metaright(cm); },
"Alt-Enter": function(cm){ org_meta_return(cm); },
"Alt-Up": function(cm){ org_metaup(cm); },
"Alt-Down": function(cm){ org_metadown(cm); },
"Shift-Alt-Left": function(cm){ org_shiftmetaleft(cm); },
"Shift-Alt-Right": function(cm){ org_shiftmetaright(cm); },
"Shift-Alt-Enter": function(cm){ org_insert_todo_heading(cm); },
"Shift-Left": function(cm){ org_shiftleft(cm); },
"Shift-Right": function(cm){ org_shiftright(cm); }
});
init = true;
editor.on('mousedown', toggleHandler);
editor.on('touchstart', toggleHandler);
editor.on('gutterClick', foldLine);
// fold everything except headers by default
editor.operation(function() {
for (var i = 0; i < editor.lineCount() ; i++) {
if(/header/.test(editor.getTokenTypeAt(CodeMirror.Pos(i, 0))) === false){
fold(editor, CodeMirror.Pos(i, 0));
}
}
});
return CodeMirror.orgmode.destroy.bind(this, editor);
});
CodeMirror.registerHelper("orgmode", "destroy", function (editor) {
if (!init) { return; }
init = false;
editor.off('mousedown', toggleHandler);
editor.off('touchstart', toggleHandler);
editor.off('gutterClick', foldLine);
// Restore CryptPad shortcuts
if (typeof (editor.updateSettings) === "function") { editor.updateSettings(); }
});
function foldLine (cm, line){
var cursor = {line: line, ch: 0};
isFold(cm, cursor) ? unfold(cm, cursor) : fold(cm, cursor);
}
var widgets = [];
function toggleHandler (cm, e){
var position = cm.coordsChar({
left: e.clientX || (e.targetTouches && e.targetTouches[0].clientX),
top: e.clientY || (e.targetTouches && e.targetTouches[0].clientY)
}, "page"),
token = cm.getTokenAt(position);
_disableSelection();
if(/org-level-star/.test(token.type)){
_preventIfShould();
_foldHeadline();
_disableSelection();
}else if(/org-toggle/.test(token.type)){
_preventIfShould();
_toggleCheckbox();
_disableSelection();
}else if(/org-todo/.test(token.type)){
_preventIfShould();
_toggleTodo();
_disableSelection();
}else if(/org-done/.test(token.type)){
_preventIfShould();
_toggleDone();
_disableSelection();
}else if(/org-priority/.test(token.type)){
_preventIfShould();
_togglePriority();
_disableSelection();
}else if(/org-url/.test(token.type)){
_disableSelection();
_navigateLink();
}else if(/org-image/.test(token.type)){
_disableSelection();
_toggleImageWidget();
}
function _preventIfShould(){
if('ontouchstart' in window) e.preventDefault();
}
function _disableSelection(){
cm.on('beforeSelectionChange', _onSelectionChangeHandler);
function _onSelectionChangeHandler(cm, obj){
obj.update([{
anchor: position,
head: position
}]);
cm.off('beforeSelectionChange', _onSelectionChangeHandler);
}
}
function _foldHeadline(){
var line = position.line;
if(line >= 0){
var cursor = {line: line, ch: 0};
isFold(cm, cursor) ? unfold(cm, cursor) : fold(cm, cursor);
}
}
function _toggleCheckbox(){
var line = position.line;
var content = cm.getRange({line: line, ch: token.start}, {line: line, ch: token.end});
var new_content = content === "[X]" || content === "[x]" ? "[ ]" : "[X]";
cm.replaceRange(new_content, {line: line, ch: token.start}, {line: line, ch: token.end});
}
function _toggleTodo(){
var line = position.line;
cm.replaceRange("DONE", {line: line, ch: token.start}, {line: line, ch: token.end});
}
function _toggleDone(){
var line = position.line;
cm.replaceRange("TODO", {line: line, ch: token.start}, {line: line, ch: token.end});
}
function _togglePriority(){
var PRIORITIES = [" [#A] ", " [#B] ", " [#C] ", " [#A] "];
var line = position.line;
var content = cm.getRange({line: line, ch: token.start}, {line: line, ch: token.end});
var new_content = PRIORITIES[PRIORITIES.indexOf(content) + 1];
cm.replaceRange(new_content, {line: line, ch: token.start}, {line: line, ch: token.end});
}
function _toggleImageWidget(){
var exist = !!widgets
.filter(function (line) { return line === position.line; })[0];
if(exist === false){
if(!token.string.match(/\[\[(.*)\]\]/)) return null;
var $node = _buildImage(RegExp.$1);
var widget = cm.addLineWidget(position.line, $node, {coverGutter: false});
widgets.push(position.line);
$node.addEventListener('click', closeWidget);
function closeWidget(){
widget.clear();
$node.removeEventListener('click', closeWidget);
widgets = widgets.filter(function (line) { return line !== position.line; });
}
}
function _buildImage(src){
var $el = document.createElement("div");
var $img = document.createElement("img");
if(/^https?\:\/\//.test(src)){
$img.src = src;
}else{
var root_path = dirname(window.location.pathname.replace(/^\/view/, ''));
var img_path = src;
$img.src = "/api/files/cat?path="+encodeURIComponent(pathBuilder(root_path, img_path));
}
$el.appendChild($img);
return $el;
}
return null;
}
function _navigateLink(){
token.string.match(/\[\[(.*?)\]\[/);
var link = RegExp.$1;
if(!link) return;
if(/^https?\:\/\//.test(link)){
window.open(link);
}else{
var root_path = dirname(window.location.pathname.replace(/^\/view/, ''));
var link_path = link;
window.open("/view"+pathBuilder(root_path, link_path));
}
}
}
CodeMirror.defineMIME("text/org", "org");
function fold(cm, start){
cm.foldCode(start, null, "fold");
}
function unfold(cm, start){
cm.foldCode(start, null, "unfold");
}
function isFold(cm, start){
var line = start.line;
var marks = cm.findMarks(CodeMirror.Pos(line, 0), CodeMirror.Pos(line + 1, 0));
for (var i = 0; i < marks.length; ++i) {
if (marks[i].__isFold && marks[i].find().from.line === line) { return marks[i]; }
}
return false;
}
/*
CodeMirror.afterInit = function(editor){
function fold(cm, start){
cm.foldCode(start, null, "fold");
@ -154,5 +388,662 @@ define([
}
});
};
*/
var org_cycle = function (cm) {
var pos = cm.getCursor();
isFold(cm, pos) ? unfold(cm, pos) : fold(cm, pos);
};
var state = {
stab: 'CONTENT'
};
var org_set_fold = function (cm) {
var cursor = cm.getCursor();
set_folding_mode(cm, state.stab);
cm.setCursor(cursor);
return state.stab;
};
/*
* DONE: Global visibility cycling
* TODO: or move to previous table field.
*/
var org_shifttab = function (cm) {
if(state.stab === "SHOW_ALL"){
state.stab = 'OVERVIEW';
}else if(state.stab === "OVERVIEW"){
state.stab = 'CONTENT';
}else if(state.stab === "CONTENT"){
state.stab = 'SHOW_ALL';
}
set_folding_mode(cm, state.stab);
return state.stab;
};
function set_folding_mode(cm, mode){
if(mode === "OVERVIEW"){
folding_mode_overview(cm);
}else if(mode === "SHOW_ALL"){
folding_mode_all(cm);
}else if(mode === "CONTENT"){
folding_mode_content(cm);
}
cm.refresh();
function folding_mode_overview(cm){
cm.operation(function() {
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){
fold(cm, CodeMirror.Pos(i, 0));
}
});
}
function folding_mode_content(cm){
cm.operation(function() {
var previous_header = null;
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){
fold(cm, CodeMirror.Pos(i, 0));
if(/header/.test(cm.getTokenTypeAt(CodeMirror.Pos(i, 0))) === true){
var level = cm.getLine(i).replace(/^(\*+).*/, "$1").length;
if(previous_header && level > previous_header.level){
unfold(cm, CodeMirror.Pos(previous_header.line, 0));
}
previous_header = {
line: i,
level: level
};
}
}
});
}
function folding_mode_all(cm){
cm.operation(function() {
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++){
if(/header/.test(cm.getTokenTypeAt(CodeMirror.Pos(i, 0))) === true){
unfold(cm, CodeMirror.Pos(i, 0));
}
}
});
}
}
/*
* Promote heading or move table column to left.
*/
var org_metaleft = function (cm) {
var line = cm.getCursor().line;
_metaleft(cm, line);
};
function _metaleft(cm, line){
var p = null;
if(p = isTitle(cm, line)){
if(p['level'] > 1) cm.replaceRange('', {line: p.start, ch: 0}, {line: p.start, ch: 1});
}else if(p = isItemList(cm, line)){
for(var i=p.start; i<=p.end; i++){
if(p['level'] > 0) cm.replaceRange('', {line: i, ch: 0}, {line: i, ch: 2});
}
}else if(p = isNumberedList(cm, line)){
for(var i=p.start; i<=p.end; i++){
if(p['level'] > 0) cm.replaceRange('', {line: i, ch: 0}, {line: i, ch: 3});
}
rearrange_list(cm, line);
}
}
/*
* Demote a subtree, a list item or move table column to right.
* In front of a drawer or a block keyword, indent it correctly.
*/
var org_metaright = function (cm){
var line = cm.getCursor().line;
_metaright(cm, line);
};
function _metaright(cm, line) {
var p = null, tmp = null;
if(p = isTitle(cm, line)){
cm.replaceRange('*', {line: p.start, ch: 0});
}else if(p = isItemList(cm, line)){
if(tmp = isItemList(cm, p.start - 1)){
if(p.level < tmp.level + 1){
for(var i=p.start; i<=p.end; i++){
cm.replaceRange(' ', {line: i, ch: 0});
}
}
}
}else if(p = isNumberedList(cm, line)){
if(tmp = isNumberedList(cm, p.start - 1)){
if(p.level < tmp.level + 1){
for(var i=p.start; i<=p.end; i++){
cm.replaceRange(' ', {line: i, ch: 0});
}
rearrange_list(cm, p.start);
}
}
}
}
/*
* Insert a new heading or wrap a region in a table
*/
var org_meta_return = function (cm) {
var line = cm.getCursor().line,
content = cm.getLine(line);
var p = null;
if(p = isItemList(cm, line)){
var level = p.level;
cm.replaceRange('\n'+" ".repeat(level*2)+'- ', {line: p.end, ch: cm.getLine(p.end).length});
cm.setCursor({line: p.end+1, ch: level*2+2});
}else if(p = isNumberedList(cm, line)){
var level = p.level;
cm.replaceRange('\n'+" ".repeat(level*3)+(p.n+1)+'. ', {line: p.end, ch: cm.getLine(p.end).length});
cm.setCursor({line: p.end+1, ch: level*3+3});
rearrange_list(cm, line);
}else if(p = isTitle(cm, line)){
var tmp = previousOfType(cm, 'title', line);
var level = tmp && tmp.level || 1;
cm.replaceRange('\n'+'*'.repeat(level)+' ', {line: line, ch: content.length});
cm.setCursor({line: line+1, ch: level+1});
}else if(content.trim() === ""){
cm.replaceRange('* ', {line: line, ch: 0});
cm.setCursor({line: line, ch: 2});
}else{
cm.replaceRange('\n\n* ', {line: line, ch: content.length});
cm.setCursor({line: line + 2, ch: 2});
}
};
var TODO_CYCLES = ["TODO", "DONE", ""];
/*
* Cycle the thing at point or in the current line, depending on context.
* Depending on context, this does one of the following:
* - TODO: switch a timestamp at point one day into the past
* - DONE: on a headline, switch to the previous TODO keyword.
* - TODO: on an item, switch entire list to the previous bulvar type
* - TODO: on a property line, switch to the previous allowed value
* - TODO: on a clocktable definition line, move time block into the past
*/
var org_shiftleft = function (cm) {
var cycles = [].concat(TODO_CYCLES.slice(0).reverse(), TODO_CYCLES.slice(-1)),
line = cm.getCursor().line,
content = cm.getLine(line),
params = isTitle(cm, line);
if(params === null) return;
params['status'] = cycles[cycles.indexOf(params['status']) + 1];
cm.replaceRange(makeTitle(params), {line: line, ch: 0}, {line: line, ch: content.length});
};
/*
* Cycle the thing at point or in the current line, depending on context.
* Depending on context, this does one of the following:
* - TODO: switch a timestamp at point one day into the future
* - DONE: on a headline, switch to the next TODO keyword.
* - TODO: on an item, switch entire list to the next bulvar type
* - TODO: on a property line, switch to the next allowed value
* - TODO: on a clocktable definition line, move time block into the future
*/
var org_shiftright = function (cm) {
cm.operation(function () {
var cycles = [].concat(TODO_CYCLES, [TODO_CYCLES[0]]),
line = cm.getCursor().line,
content = cm.getLine(line),
params = isTitle(cm, line);
if(params === null) return;
params['status'] = cycles[cycles.indexOf(params['status']) + 1];
cm.replaceRange(makeTitle(params), {line: line, ch: 0}, {line: line, ch: content.length});
});
};
var org_insert_todo_heading = function (cm) {
cm.operation(function () {
var line = cm.getCursor().line,
content = cm.getLine(line);
var p = null;
if(p = isItemList(cm, line)){
var level = p.level;
cm.replaceRange('\n'+" ".repeat(level*2)+'- [ ] ', {line: p.end, ch: cm.getLine(p.end).length});
cm.setCursor({line: line+1, ch: 6+level*2});
}else if(p = isNumberedList(cm, line)){
var level = p.level;
cm.replaceRange('\n'+" ".repeat(level*3)+(p.n+1)+'. [ ] ', {line: p.end, ch: cm.getLine(p.end).length});
cm.setCursor({line: p.end+1, ch: level*3+7});
rearrange_list(cm, line);
}else if(p = isTitle(cm, line)){
var level = p && p.level || 1;
cm.replaceRange('\n'+"*".repeat(level)+' TODO ', {line: line, ch: content.length});
cm.setCursor({line: line+1, ch: level+6});
}else if(content.trim() === ""){
cm.replaceRange('* TODO ', {line: line, ch: 0});
cm.setCursor({line: line, ch: 7});
}else{
cm.replaceRange('\n\n* TODO ', {line: line, ch: content.length});
cm.setCursor({line: line + 2, ch: 7});
}
});
}
/*
* Move subtree up or move table row up.
* Calls org-move-subtree-up or org-table-move-row or
* org-move-item-up, depending on context
*/
var org_metaup = function (cm) {
cm.operation(function () {
var line = cm.getCursor().line;
var p = null;
if(p = isItemList(cm, line)){
var a = isItemList(cm, p.start - 1);
if(a){
swap(cm, [p.start, p.end], [a.start, a.end]);
rearrange_list(cm, line);
}
}else if(p = isNumberedList(cm, line)){
var a = isNumberedList(cm, p.start - 1);
if(a){
swap(cm, [p.start, p.end], [a.start, a.end]);
rearrange_list(cm, line);
}
}else if(p = isTitle(cm, line)){
var _line = line,
a;
do{
_line -= 1;
if(a = isTitle(cm, _line, p.level)){
break;
}
}while(_line > 0);
if(a){
swap(cm, [p.start, p.end], [a.start, a.end]);
org_set_fold(cm);
}
}
});
}
/*
* Move subtree down or move table row down.
* Calls org-move-subtree-down or org-table-move-row or
* org-move-item-down, depending on context
*/
var org_metadown = function (cm) {
cm.operation(function () {
var line = cm.getCursor().line;
var p = null;
if(p = isItemList(cm, line)){
var a = isItemList(cm, p.end + 1);
if(a){
swap(cm, [p.start, p.end], [a.start, a.end]);
}
}else if(p = isNumberedList(cm, line)){
var a = isNumberedList(cm, p.end + 1);
if(a){
swap(cm, [p.start, p.end], [a.start, a.end]);
}
rearrange_list(cm, line);
}else if(p = isTitle(cm, line)){
var a = isTitle(cm, p.end + 1, p.level);
if(a){
swap(cm, [p.start, p.end], [a.start, a.end]);
org_set_fold(cm);
}
}
});
}
var org_shiftmetaright = function(cm) {
cm.operation(function () {
var line = cm.getCursor().line;
var p = null;
if(p = isTitle(cm, line)){
_metaright(cm, line);
for(var i=p.start + 1; i<=p.end; i++){
if(isTitle(cm, i)){
_metaright(cm, i);
}
}
}
});
};
var org_shiftmetaleft = function(cm){
cm.operation(function () {
var line = cm.getCursor().line;
var p = null;
if(p = isTitle(cm, line)){
if(p.level === 1) return;
_metaleft(cm, line);
for(var i=p.start + 1; i<=p.end; i++){
if(isTitle(cm, i)){
_metaleft(cm, i);
}
}
}
});
};
function makeTitle(p){
var content = "*".repeat(p['level'])+" ";
if(p['status']){
content += p['status']+" ";
}
content += p['content'];
return content;
}
function previousOfType(cm, type, line){
var content, tmp, i;
for(i=line - 1; i>0; i--){
if(type === 'list' || type === null){
tmp = isItemList(cm, line);
}else if(type === 'numbered' || type === null){
tmp = isNumberedList(cm, line);
}else if(type === 'title' || type === null){
tmp = isTitle(cm, line);
}
if(tmp !== null){
return tmp;
}
}
return null;
}
function isItemList(cm, line){
var rootLineItem = findRootLine(cm, line);
if(rootLineItem === null) return null;
line = rootLineItem;
var content = cm.getLine(line);
if(content && (content.trimLeft()[0] !== "-" || content.trimLeft()[1] !== " ")) return null;
var padding = content.replace(/^(\s*).*$/, "$1").length;
if(padding % 2 !== 0) return null;
return {
type: 'list',
level: padding / 2,
content: content.trimLeft().replace(/^\s*\-\s(.*)$/, '$1'),
start: line,
end: function(_cm, _line){
var line_candidate = _line,
content = null;
do{
_line += 1;
content = _cm.getLine(_line);
if(content === undefined || content.trimLeft()[0] === "-"){
break;
}else if(/^\s+/.test(content)){
line_candidate = _line;
continue;
}else{
break;
}
}while(_line <= _cm.lineCount())
return line_candidate;
}(cm, line)
};
function findRootLine(_cm, _line){
var content;
do{
content = _cm.getLine(_line);
if(/^\s*\-/.test(content)) return _line;
else if(/^\s+/.test(content) === false){
break;
}
_line -= 1;
}while(_line >= 0);
return null;
}
}
function isNumberedList(cm, line){
var rootLineItem = findRootLine(cm, line);
if(rootLineItem === null) return null;
line = rootLineItem;
var content = cm.getLine(line);
if(/^[0-9]+[\.\)]\s.*$/.test(content && content.trimLeft()) === false) return null;
var padding = content.replace(/^(\s*)[0-9]+.*$/, "$1").length;
if(padding % 3 !== 0) return null;
return {
type: 'numbered',
level: padding / 3,
content: content.trimLeft().replace(/^[0-9]+[\.\)]\s(.*)$/, '$1'),
start: line,
end: function(_cm, _line){
var line_candidate = _line,
content = null;
do{
_line += 1;
content = _cm.getLine(_line);
if(content === undefined || /^[0-9]+[\.\)]/.test(content.trimLeft())){
break;
}else if(/^\s+/.test(content)){
line_candidate = _line;
continue;
}else{
break;
}
}while(_line <= _cm.lineCount())
return line_candidate;
}(cm, line),
// specific
n: parseInt(content.trimLeft().replace(/^([0-9]+).*$/, "$1")),
separator: content.trimLeft().replace(/^[0-9]+([\.\)]).*$/, '$1')
};
function findRootLine(_cm, _line){
var content;
do{
content = _cm.getLine(_line);
if(/^\s*[0-9]+[\.\)]\s/.test(content)) return _line;
else if(/^\s+/.test(content) === false){
break;
}
_line -= 1;
}while(_line >= 0);
return null;
}
}
function isTitle(cm, line, level){
var content = cm.getLine(line);
if(/^\*+\s/.test(content) === false) return null;
var match = content.match(/^(\*+)([\sA-Z]*)\s(.*)$/);
var reference_level = match[1].length;
if(level !== undefined && level !== reference_level){ return null; }
if(match === null) return null;
return {
type: 'title',
level: reference_level,
content: match[3],
start: line,
end: function(_cm, _line){
var line_candidate = _line,
content = null;
do{
_line += 1;
content = _cm.getLine(_line);
if(content === undefined) break;
var match = content.match(/^(\*+)\s.*/);
if(match && match[1] && ( match[1].length === reference_level || match[1].length < reference_level)){
break;
}else{
line_candidate = _line;
continue;
}
}while(_line <= _cm.lineCount())
return line_candidate;
}(cm, line),
// specific
status: match[2].trim()
};
}
function rearrange_list(cm, line){
var line_inferior = find_limit_inferior(cm, line);
var line_superior = find_limit_superior(cm, line);
var last_p = null, p;
for(var i=line_inferior; i<=line_superior; i++){
if(p = isNumberedList(cm, i)){
// rearrange numbers on the numbered list
if(last_p){
if(p.level === last_p.level){
var tmp = findLastAtLevel(cm, p.start, line_inferior, p.level);
if(tmp && p.n !== tmp.n + 1) setNumber(cm, p.start, tmp.n + 1);
}else if(p.level > last_p.level){
if(p.n !== 1){
setNumber(cm, p.start, 1);
}
}else if(p.level < last_p.level){
var tmp = findLastAtLevel(cm, p.start, line_inferior, p.level);
if(tmp && p.n !== tmp.n + 1) setNumber(cm, p.start, tmp.n + 1);
}
}else{
if(p.n !== 1){ setNumber(cm, p.start, 1); }
}
}
if(p = (isNumberedList(cm, i) || isItemList(cm, i))){
// rearrange spacing levels in list
if(last_p){
if(p.level > last_p.level){
if(p.level !== last_p.level + 1){
setLevel(cm, [p.start, p.end], last_p.level + 1, p.type);
}
}
}else{
if(p.level !== 0){
setLevel(cm, [p.start, p.end], 0, p.type);
}
}
}
last_p = p;
// we can process content block instead of line
if(p){
i += (p.end - p.start);
}
}
function findLastAtLevel(_cm, line, line_limit_inf, level){
var p;
do{
line -= 1;
if((p = isNumberedList(_cm, line)) && p.level === level)
return p;
}while(line > line_limit_inf);
return null;
}
function setLevel(_cm, range, level, type){
var content, i;
for(i=range[0]; i<=range[1]; i++){
content = cm.getLine(i).trimLeft();
var n_spaces = function(_level, _line, _type){
var spaces = _level * 3;
if(_line > 0){
spaces += _type === 'numbered' ? 3 : 2;
}
return spaces;
}(level, i - range[0], type)
content = " ".repeat(n_spaces) + content;
cm.replaceRange(content, {line: i, ch: 0}, {line: i, ch: _cm.getLine(i).length});
}
}
function setNumber(_cm, line, level){
var content = _cm.getLine(line);
var new_content = content.replace(/[0-9]+\./, level+".");
cm.replaceRange(new_content, {line: line, ch: 0}, {line: line, ch: content.length});
}
function find_limit_inferior(_cm, _line){
var content, p, match, line_candidate = _line;
do{
content = _cm.getLine(_line);
p = isNumberedList(_cm, _line);
match = /(\s+).*$/.exec(content);
if(p){ line_candidate = _line;}
if(!p || !match) break;
_line -= 1;
}while(_line >= 0);
return line_candidate;
}
function find_limit_superior(_cm, _line){
var content, p, match, line_candidate = _line;
do{
content = _cm.getLine(_line);
p = isNumberedList(_cm, _line);
match = /(\s+).*$/.exec(content);
if(p){ line_candidate = _line;}
if(!p || !match) break;
_line += 1;
}while(_line < _cm.lineCount());
return line_candidate;
}
}
function swap(cm, from, to){
var from_content = cm.getRange({line: from[0], ch: 0}, {line: from[1], ch: cm.getLine(from[1]).length}),
to_content = cm.getRange({line: to[0], ch: 0}, {line: to[1], ch: cm.getLine(to[1]).length}),
cursor = cm.getCursor();
if(to[0] > from[0]){
// moving down
cm.replaceRange(
from_content,
{line: to[0], ch:0},
{line: to[1], ch: cm.getLine(to[1]).length}
);
cm.replaceRange(
to_content,
{line: from[0], ch:0},
{line: from[1], ch: cm.getLine(from[1]).length}
);
cm.setCursor({
line: cursor.line + (to[1] - to[0] + 1),
ch: cursor.ch
});
}else{
// moving up
cm.replaceRange(
to_content,
{line: from[0], ch:0},
{line: from[1], ch: cm.getLine(from[1]).length}
);
cm.replaceRange(
from_content,
{line: to[0], ch:0},
{line: to[1], ch: cm.getLine(to[1]).length}
);
cm.setCursor({
line: cursor.line - (to[1] - to[0] + 1),
ch: cursor.ch
});
}
}
});

@ -113,7 +113,7 @@ define(function() {
poll: 'cptools-poll',
whiteboard: 'cptools-whiteboard',
todo: 'cptools-todo',
contacts: 'cptools-contacts',
contacts: 'fa-address-book',
kanban: 'cptools-kanban',
oodoc: 'fa-file-word-o',
ooslide: 'fa-file-powerpoint-o',

@ -28,6 +28,13 @@ var factory = function (Util, Crypto, Keys, Nacl) {
};
};
Hash.getSignPublicFromPrivate = function (edPrivateSafeStr) {
var edPrivateStr = Crypto.b64AddSlashes(edPrivateSafeStr);
var privateKey = Nacl.util.decodeBase64(edPrivateStr);
var keyPair = Nacl.sign.keyPair.fromSecretKey(privateKey);
return Nacl.util.encodeBase64(keyPair.publicKey);
};
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;

@ -418,7 +418,7 @@ define([
var localStore = window.cryptpadStore;
localStore.get('hide-alert-teamInvite', function (val) {
if (val === '1') { return; }
$(linkWarning).show();
$(linkWarning).css('display', 'flex');
$(dismissButton).on('click', function () {
localStore.put('hide-alert-teamInvite', '1');
@ -1431,7 +1431,12 @@ define([
});
$container.keydown(function (e) {
var $value = $innerblock.find('[data-value].cp-dropdown-element-active:visible');
if (!$value.length) {
$value = $innerblock.find('[data-value]').first();
}
if (e.which === 38) { // Up
e.preventDefault();
e.stopPropagation();
if ($value.length) {
$value.mouseleave();
var $prev = $value.prev();
@ -1440,6 +1445,8 @@ define([
}
}
if (e.which === 40) { // Down
e.preventDefault();
e.stopPropagation();
if ($value.length) {
$value.mouseleave();
var $next = $value.next();
@ -1448,12 +1455,16 @@ define([
}
}
if (e.which === 13) { //Enter
e.preventDefault();
e.stopPropagation();
if ($value.length) {
$value.click();
hide();
}
}
if (e.which === 27) { // Esc
e.preventDefault();
e.stopPropagation();
$value.mouseleave();
hide();
}
@ -1615,7 +1626,7 @@ define([
attributes: {
'target': '_blank',
'href': origin+'/contacts/',
'class': 'cptools cptools-contacts'
'class': 'fa fa-address-book'
},
content: h('span', Messages.type.contacts)
});
@ -3281,5 +3292,11 @@ define([
};
};
UIElements.isVisible = function (el, $container) {
var size = $container.outerHeight();
var pos = el.getBoundingClientRect();
return (pos.bottom < size) && (pos.y > 0);
};
return UIElements;
});

@ -310,6 +310,10 @@
clearTimeout(to);
to = setTimeout(Util.bake(f, Util.slice(arguments)), ms);
};
g.clear = function () {
clearTimeout(to);
to = undefined;
};
return g;
};
@ -347,10 +351,6 @@
};
};
Util.slice = function (A) {
return Array.prototype.slice.call(A);
};
Util.blobToImage = function (blob, cb) {
var reader = new FileReader();
reader.onloadend = function() {

@ -2273,7 +2273,7 @@ define([
var o = e.oldValue;
var n = e.newValue;
if (!o && n) {
document.location.reload();
LocalStore.loginReload();
} else if (o && !n) {
LocalStore.logout();
}

@ -191,6 +191,7 @@ define([
var defaultCode = renderer.code;
renderer.code = function (code, language) {
if (!code || typeof(code) !== 'string' || !code.trim()) { return defaultCode.apply(renderer, arguments); }
if (language === 'mermaid' && code.match(/^(graph|pie|gantt|sequenceDiagram|classDiagram|gitGraph)/)) {
return '<pre class="mermaid" data-plugin="mermaid">'+Util.fixHTML(code)+'</pre>';
} else if (language === 'markmap') {
@ -570,6 +571,7 @@ define([
var MutationObserver = window.MutationObserver;
var onPreview = function ($mt) {
return function () {
if (window.event.ctrlKey) { return; }
var mts = [];
// Get all previewable elements from the doc
$content.find('media-tag, pre[data-plugin]').each(function (i, el) {

@ -1,5 +1,6 @@
define([
'jquery',
'/api/config',
'/common/toolbar.js',
'json.sortify',
'/common/common-util.js',
@ -20,6 +21,7 @@ define([
'/customize/messages.js',
], function (
$,
ApiConfig,
Toolbar,
JSONSortify,
Util,
@ -236,9 +238,9 @@ define([
return LS;
};
var getViewModeClass = function () {
var getViewModeClass = function (forceList) {
var mode = APP.store[LS_VIEWMODE];
if (mode === 'list') { return 'cp-app-drive-content-list'; }
if (mode === 'list' || forceList) { return 'cp-app-drive-content-list'; }
return 'cp-app-drive-content-grid';
};
var getViewMode = function () {
@ -1204,7 +1206,7 @@ define([
hide.push('collapseall');
}
if (path.length === 1) {
// Can't rename, share, delete, or change the color of root elements
// Can't rename, share, delete, or change the color of categories
hide.push('delete');
hide.push('rename');
hide.push('share');
@ -1259,7 +1261,7 @@ define([
hide.push('openro');
hide.push('openincode');
hide.push('hashtag');
hide.push('delete');
//hide.push('delete');
hide.push('makeacopy');
//hide.push('deleteowned');
} else { // it's a folder
@ -1661,7 +1663,6 @@ define([
if (pathsList.length === 1) {
msg = Messages.fm_removePermanentlyDialog;
}
// XXX update key to tell the user that these pads will still be avialble to other users
UI.confirm(msg, function(res) {
$(window).focus();
if (!res) { return; }
@ -1732,9 +1733,6 @@ define([
&& $target.parents('#cp-app-drive-content')) {
newPath = currentPath;
}
// XXX Why did we add the following line?
// https://github.com/xwiki-labs/cryptpad/commit/f103a0fb08d137901174ef7e15dbc2c9a2ec3ca1
//if (newPath[0] !== ROOT) { newPath = [ROOT]; }
return newPath;
};
var onFileDrop = APP.onFileDrop = function (file, e) {
@ -1774,25 +1772,15 @@ define([
});
if (sharedF && manager.isPathIn(newPath, [TRASH])) {
return void deletePaths(null, movedPaths);
// XXX create a key here?
// You can't move to YOUR trash documents stored in a shared folder
// XXX or keep deletePaths: trigger the "Remove from cryptdrive" modal
return void UI.warn(Messages.error);
//return void deletePaths(null, movedPaths);
}
var copy = false;
if (manager.isPathIn(newPath, [TRASH])) {
// Filter the selection to remove shared folders.
// Shared folders can't be moved to the trash!
var filteredPaths = movedPaths.filter(function (p) {
var el = manager.find(p);
return !manager.isSharedFolder(el);
});
if (!filteredPaths.length) {
// We only have shared folder, delete them
return void deletePaths(null, movedPaths);
}
movedPaths = filteredPaths;
} else if (ev.ctrlKey || (ev.metaKey && APP.isMac)) {
if (ev.ctrlKey || (ev.metaKey && APP.isMac)) {
copy = true;
}
@ -1860,7 +1848,7 @@ define([
// In list mode, display metadata from the filesData object
var _addOwnership = function ($span, $state, data) {
if (data.owners && data.owners.indexOf(edPublic) !== -1) {
if (data && Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1) {
var $owned = $ownedIcon.clone().appendTo($state);
$owned.attr('title', Messages.fm_padIsOwned);
$span.addClass('cp-app-drive-element-owned');
@ -2259,15 +2247,25 @@ define([
var skipNext = false; // When encountering a shared folder, skip a key in the path
path.forEach(function (p, idx) {
if (skipNext) { skipNext = false; return; }
if (isTrash && [2,3].indexOf(idx) !== -1) { return; }
if (skipNext) { skipNext = false; return; }
var name = p;
if (manager.isFile(el) && isInTrashRoot && idx === 1) {
idx = 3;
}
// Check if the current element is a shared folder. If it is, get its
// name and skip the next itme in the path (it will be "root")
var currentEl = isVirtual ? undefined : manager.find(path.slice(0, idx+1));
// If we're in trash root, check if the "element" is a shared folder
if (isTrash && idx === 1) {
currentEl = manager.find(path.slice(0, idx+3));
}
// Name and skip next...
// "p === SHARED_FOLDER" for anonymous shared folders
if (p === SHARED_FOLDER || (currentEl && manager.isSharedFolder(currentEl))) {
name = manager.getSharedFolderData(currentEl || APP.newSharedFolder).title;
skipNext = true;
@ -2345,7 +2343,7 @@ define([
return $(common.fixLinks($box.html(msg)));
}
if (!APP.loggedIn) {
msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages.fm_info_anonymous;
msg = APP.newSharedFolder ? Messages.fm_info_sharedFolder : Messages._getKey('fm_info_anonymous', [ApiConfig.inactiveTime || 90]);
return $(common.fixLinks($box.html(msg)));
}
if (!msg || APP.store['hide-info-' + path[0]] === '1') {
@ -2909,7 +2907,6 @@ define([
var title = sfData.title || sfData.lastTitle || el;
return String(title).toLowerCase();
} else if (folder) {
console.log(el);
return String((el && el.key) || _el).toLowerCase();
}
var data = manager.getFileData(el);
@ -2931,7 +2928,6 @@ define([
}
props[uid] = getProp(k);
});
if (folder) { console.error(useId, props); }
keys.sort(function(a, b) {
var _a = props[(a && a.uid) || a];
var _b = props[(b && b.uid) || b];
@ -3035,7 +3031,7 @@ define([
if (APP.$content.data('readOnlyFolder') || !APP.editable) { return; }
var isInRoot = currentPath[0] === ROOT;
var $element = $('<li>', {
'class': 'cp-app-drive-element-row cp-app-drive-element-grid cp-app-drive-new-ghost'
'class': 'cp-app-drive-element-row cp-app-drive-new-ghost'
}).prepend($addIcon.clone()).appendTo($list);
$element.append($('<span>', {'class': 'cp-app-drive-element-name'})
.text(Messages.fm_newFile));
@ -3152,7 +3148,10 @@ define([
return;
}
var allfiles = files[FILES_DATA];
if (allfiles.length === 0) { return; }
if (Object.keys(allfiles || {}).length === 0) {
createGhostIcon($container);
return;
}
var $fileHeader = getFileListHeader(true);
$container.append($fileHeader);
var keys = manager.getFiles([FILES_DATA]);
@ -3232,18 +3231,26 @@ define([
$searchIcon.clone().appendTo($div);
var $spinnerContainer = $(h('div.cp-app-drive-search-spinner'));
var spinner = UI.makeSpinner($spinnerContainer);
var $input = APP.Search.$input = $('<input>', {
id: 'cp-app-drive-search-input',
placeholder: Messages.fm_searchName,
type: 'text',
draggable: false,
tabindex: 1,
}).keyup(function (e) {
var lastValue = search.value;
search.value = $input.val().trim();
if (lastValue === search.value) { return; }
if (search.to) { window.clearTimeout(search.to); }
if ($input.val().trim() === "") {
if (search.value === "") {
search.cursor = 0;
APP.displayDirectory([SEARCH]);
return;
}
spinner.spin();
if (e.which === 13) {
var newLocation = [SEARCH, $input.val()];
search.cursor = $input[0].selectionStart;
@ -3288,73 +3295,88 @@ define([
$div.append(cancel);
$list.append($div);
$spinnerContainer.appendTo($list);
setTimeout(function () {
$input.focus();
});
$list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list');
var filesList = manager.search(value);
var sortable = {};
var sortableFolders = [];
filesList.forEach(function (r) {
// if r.id === null, then it's a folder, not a file
r.paths.forEach(function (path) {
if (!r.inSharedFolder &&
APP.hideDuplicateOwned && manager.isDuplicateOwned(path)) { return; }
var _path = path.slice();
var key = path.pop();
var root = manager.find(path);
var obj = {
path: path,
_path: _path,
key: key,
root: root,
data: r.data
};
if (manager.isFolder(root[key])) {
sortableFolders.push(obj);
return;
}
sortable[root[key]] = obj;
});
});
var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true);
var sortableKeys = Object.keys(sortable).map(Number);
var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
var addEl = function (obj, folder) {
var $element = createElement(obj.path, obj.key, obj.root, folder);
$element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result');
$element.off('contextmenu');
$element.contextmenu(openContextMenu('default'));
$element.data('context', 'default');
if (folder) {
$element.find('.cp-app-drive-element-list').css({
visibility: 'hidden'
}).text('');
}
if (manager.isPathIn(obj._path, ['hrefArray'])) {
obj._path.pop();
obj._path.push(obj.data.title);
if (typeof(value) === "string" && value.trim()) {
spinner.spin();
} else {
return;
}
setTimeout(function () {
//$list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list');
var filesList = manager.search(value);
if (!filesList.length) {
$list.append(h('div.cp-app-drive-search-noresult', Messages.fm_noResult));
spinner.hide();
return;
}
var $path = $('<span>', {
'class': 'cp-app-drive-search-path'
}).appendTo($element.find('.cp-app-drive-element-name'));
createTitle($path, obj._path);
var sortable = {};
var sortableFolders = [];
filesList.forEach(function (r) {
// if r.id === null, then it's a folder, not a file
r.paths.forEach(function (path) {
if (!r.inSharedFolder &&
APP.hideDuplicateOwned && manager.isDuplicateOwned(path)) { return; }
var _path = path.slice();
var key = path.pop();
var root = manager.find(path);
var obj = {
path: path,
_path: _path,
key: key,
root: root,
data: r.data
};
if (manager.isFolder(root[key])) {
sortableFolders.push(obj);
return;
}
sortable[root[key]] = obj;
});
});
var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true);
var sortableKeys = Object.keys(sortable).map(Number);
var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
var addEl = function (obj, folder) {
var $element = createElement(obj.path, obj.key, obj.root, folder);
$element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result');
$element.off('contextmenu');
$element.contextmenu(openContextMenu('default'));
$element.data('context', 'default');
if (folder) {
$element.find('.cp-app-drive-element-list').css({
visibility: 'hidden'
}).text('');
}
if (manager.isPathIn(obj._path, ['hrefArray'])) {
obj._path.pop();
obj._path.push(obj.data.title);
}
var $path = $('<span>', {
'class': 'cp-app-drive-search-path'
}).appendTo($element.find('.cp-app-drive-element-name'));
createTitle($path, obj._path);
$list.append($element);
};
if (_folders.length) { getFolderListHeader(true, true).appendTo($list); }
_folders.forEach(function (el) {
var obj = el;
addEl(obj, true);
});
if (_files.length) { getFileListHeader(true).appendTo($list); }
_files.forEach(function (el) {
var obj = sortable[el];
addEl(obj, false);
$list.append($element);
};
if (_folders.length) { getFolderListHeader(true, true).appendTo($list); }
_folders.forEach(function (el) {
var obj = el;
addEl(obj, true);
});
if (_files.length) { getFileListHeader(true).appendTo($list); }
_files.forEach(function (el) {
var obj = sortable[el];
addEl(obj, false);
});
setTimeout(collapseDrivePath);
spinner.hide();
});
setTimeout(collapseDrivePath);
};
var displayRecent = function ($list) {
@ -3610,12 +3632,11 @@ define([
var $dirContent = $('<div>', {id: FOLDER_CONTENT_ID});
$dirContent.data('path', path);
if (!isSearch && !isTags) {
var mode = getViewMode();
if (mode) {
$dirContent.addClass(getViewModeClass());
if (!isTags) {
$dirContent.addClass(getViewModeClass(isSearch));
if (!isSearch) {
createViewModeButton(APP.toolbar.$bottomR);
}
createViewModeButton(APP.toolbar.$bottomR);
}
var $list = $('<ul>').appendTo($dirContent);
@ -4557,23 +4578,18 @@ define([
paths.push($(elmt).data('path'));
});
if (!paths.length) { return; }
// Remove shared folders from the selection (they can't be moved to the trash)
// unless the selection is only shared folders
var paths2 = paths.filter(function (p) {
var el = manager.find(p);
return !manager.isSharedFolder(el);
});
// If we are in the trash or anon pad or if we are holding the "shift" key,
// If we are in the trash or anon USER or if we are holding the "shift" key,
// delete permanently
// Or if we are in a shared folder
// Or if the selection is only shared folders
if (!APP.loggedIn || isTrash || manager.isInSharedFolder(currentPath)
|| e.shiftKey || !paths2.length) {
|| e.shiftKey) {
deletePaths(null, paths);
return;
}
// else move to trash
moveElements(paths2, [TRASH], false, refresh);
moveElements(paths, [TRASH], false, refresh);
return;
}
});
@ -4708,7 +4724,10 @@ define([
var ok = manager.isValidDrive(obj.drive);
if (!ok) { return; }
var restricted = files.restrictedFolders;
copyObjectValue(files, obj.drive);
files.restrictedFolders = restricted;
appStatus.isReady = true;
refresh();
};

@ -350,7 +350,6 @@ define([
localStore.put('hide-alert-shareLinkWarning', '1');
$(shareLinkWarning).remove();
});
});
}
@ -767,7 +766,7 @@ define([
localStore.get('hide-alert-shareLinkWarning', function (val) {
if (val === '1') { return; }
$(shareLinkWarning).show();
$(shareLinkWarning).css('display', 'flex');
$(dismissButton).on('click', function () {
localStore.put('hide-alert-shareLinkWarning', '1');

@ -312,7 +312,7 @@ define([
}
};
handlers['INVITE_TO_TEAM_ANSWER'] = function(common, data) {
handlers['INVITE_TO_TEAM_ANSWERED'] = function(common, data) {
var content = data.content;
var msg = content.msg;

@ -54,9 +54,9 @@ define([
var CHECKPOINT_INTERVAL = 100;
var DISPLAY_RESTORE_BUTTON = false;
var NEW_VERSION = 2;
var NEW_VERSION = 3;
var PENDING_TIMEOUT = 30000;
var READ_ONLY_REFRESH_DATA_DELAY = 15000;
var READONLY_REFRESH_TO = 15000;
var debug = function (x) {
if (!window.CP_DEV_MODE) { return; }
@ -112,7 +112,7 @@ define([
return window.frames[0].editor || window.frames[0].editorCell;
};
var setEditable = function (state) {
var setEditable = function (state, force) {
$('#cp-app-oo-editor').find('#cp-app-oo-offline').remove();
/*
try {
@ -120,7 +120,7 @@ define([
//window.frames[0].editor.setViewModeDisconnect(true);
} catch (e) {}
*/
if (!state && !readOnly) {
if (!state && (!readOnly || force)) {
$('#cp-app-oo-editor').append(h('div#cp-app-oo-offline'));
}
};
@ -272,6 +272,7 @@ define([
}
};
/*
var checkDrawings = function () {
var editor = getEditor();
if (!editor || !editor.GetSheets) { return false; }
@ -280,12 +281,18 @@ define([
return obj.worksheet.Drawings.length;
});
};
*/
// DEPRECATED from version 3
// Loading a checkpoint reorder the sheet starting from ID "5".
// We have to reorder it manually when a checkpoint is created
// so that the messages we send to the realtime channel are
// loadable by users joining after the checkpoint
var fixSheets = function () {
// Starting from version 3, we don't need to fix the sheet IDs anymore
// because we reload onlyoffice whenever we receive a checkpoint
if (!APP.migrate || (content && content.version > 2)) { return; }
try {
var editor = getEditor();
// if we are not in the sheet app
@ -323,11 +330,8 @@ define([
index: ev.index
};
oldHashes = JSON.parse(JSON.stringify(content.hashes));
var hasDrawings = checkDrawings();
if (hasDrawings) {
content.locks = {};
content.ids = {};
}
content.locks = {};
content.ids = {};
// If this is a migration, set the new version
if (APP.migrate) {
delete content.migration;
@ -383,6 +387,10 @@ define([
};
var resetData = function (blob, type) {
// If a read-only refresh popup was planned, abort it
delete APP.refreshPopup;
clearTimeout(APP.refreshRoTo);
if (!isLockedModal.modal) {
isLockedModal.modal = UI.openCustomModal(isLockedModal.content);
}
@ -410,14 +418,11 @@ define([
};
fixSheets();
var hasDrawings = checkDrawings();
if (hasDrawings) {
ooChannel.ready = false;
ooChannel.queue = [];
data.callback = function () {
resetData(blob, file);
};
}
ooChannel.ready = false;
ooChannel.queue = [];
data.callback = function () {
resetData(blob, file);
};
APP.FM.handleFile(blob, data);
};
@ -528,6 +533,7 @@ define([
};
var loadLastDocument = function (lastCp, onCpError, cb) {
ooChannel.cpIndex = lastCp.index || 0;
ooChannel.lastHash = lastCp.hash;
var parsed = Hash.parsePadUrl(lastCp.file);
var secret = Hash.getSecrets('file', parsed.hash);
if (!secret || !secret.channel) { return; }
@ -540,7 +546,7 @@ define([
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
if (/^4/.test('' + this.status)) {
onCpError();
onCpError(this.status);
return void console.error('XHR error', this.status);
}
var arrayBuffer = xhr.response;
@ -556,8 +562,8 @@ define([
});
}
};
xhr.onerror = function () {
onCpError();
xhr.onerror = function (err) {
onCpError(err);
};
xhr.send(null);
};
@ -607,6 +613,7 @@ define([
sframeChan.on('EV_OO_EVENT', function (obj) {
switch (obj.ev) {
case 'READY':
cb();
break;
case 'LEAVE':
removeClient(obj.data);
@ -622,7 +629,7 @@ define([
// Don't "spam" the user instantly and no more than
// 1 popup every 15s
setTimeout(refreshReadOnly, READ_ONLY_REFRESH_DATA_DELAY);
APP.refreshRoTo = setTimeout(refreshReadOnly, READONLY_REFRESH_TO);
return;
}
ooChannel.send(obj.data.msg);
@ -634,7 +641,6 @@ define([
break;
}
});
cb();
};
var getParticipants = function () {
@ -745,12 +751,26 @@ define([
};
var handleAuth = function (obj, send) {
setEditable(false);
ooChannel.lastHash = getLastCp().hash;
//setEditable(false);
var changes = [];
if (content.version > 2) {
ooChannel.queue.forEach(function (data) {
Array.prototype.push.apply(changes, data.msg.changes);
});
ooChannel.ready = true;
ooChannel.cpIndex += ooChannel.queue.length;
var last = ooChannel.queue.pop();
if (last) { ooChannel.lastHash = last.hash; }
} else {
setEditable(false, true);
}
send({
type: "authChanges",
changes: []
changes: changes
});
// Answer to the auth command
var p = getParticipants();
send({
@ -884,6 +904,10 @@ define([
return;
}
// XXX
// If save lock, tell onlyoffice that it can't save now...
// if (content && content.saveLock] {}
// Send the changes
content.locks = content.locks || {};
rtChannel.sendMsg({
@ -896,6 +920,12 @@ define([
if (err) {
return void console.error(err);
}
// XXX
// If save lock, it means the sheet was locked for a checkpoint before
// our message was received!
// Add our message to our own queue to load it after the checkpoint reload
if (pendingChanges[uid]) {
clearTimeout(pendingChanges[uid]);
delete pendingChanges[uid];
@ -998,6 +1028,10 @@ define([
index: -1
});
}
if (APP.onDocumentUnlock) {
APP.onDocumentUnlock();
APP.onDocumentUnlock = undefined;
}
break;
}
});
@ -1009,6 +1043,10 @@ define([
var url = URL.createObjectURL(blob);
var lock = readOnly || APP.migrate;
// Starting from version 3, we can use the view mode again
// defined but never used
//var mode = (content && content.version > 2 && lock) ? "view" : "edit";
// Config
APP.ooconfig = {
"document": {
@ -1075,29 +1113,63 @@ define([
}
},
"onDocumentReady": function () {
// The doc is ready, fix the worksheets IDs and push the queue
fixSheets();
// Push changes since last cp
ooChannel.ready = true;
ooChannel.queue.forEach(function (data) {
ooChannel.send(data.msg);
var onMigrateRdy = Util.mkEvent();
onMigrateRdy.reg(function () {
var div = h('div.cp-oo-x2tXls', [
h('span.fa.fa-spin.fa-spinner'),
h('span', Messages.oo_sheetMigration_loading) // XXX tell them that it will take ~ 1min)
]);
UI.openCustomModal(UI.dialog.customModal(div, {buttons: []}));
makeCheckpoint(true);
});
if (!readOnly) {
// DEPRECATED: from version 3, the queue is sent again during init
if (APP.migrate && ((content.version || 1) <= 2)) {
// The doc is ready, fix the worksheets IDs and push the queue
fixSheets();
// Push changes since last cp
ooChannel.ready = true;
var changes = [];
var changesIndex;
ooChannel.queue.forEach(function (data) {
Array.prototype.push.apply(changes, data.msg.changes);
changesIndex = data.msg.changesIndex;
//ooChannel.send(data.msg);
});
ooChannel.cpIndex += ooChannel.queue.length;
var last = ooChannel.queue.pop();
if (last) { ooChannel.lastHash = last.hash; }
var onDocUnlock = function () {
// Migration required but read-only: continue...
if (readOnly) {
setEditable(true);
getEditor().setViewModeDisconnect();
} else {
// No changes after the cp: migrate now
onMigrateRdy.fire();
}
};
// Send the changes all at once
if (changes.length) {
setTimeout(function () {
ooChannel.send({
type: 'saveChanges',
changesIndex: changesIndex,
changes: changes,
locks: []
});
APP.onDocumentUnlock = onDocUnlock;
}, 5000);
return;
}
onDocUnlock();
return;
}
ooChannel.cpIndex += ooChannel.queue.length;
// Apply existing locks
deleteOfflineLocks();
APP.onLocal();
handleNewLocks(oldLocks, content.locks || {});
// Allow edition
if (lock) {
setTimeout(function () {
setEditable(true);
getEditor().setViewModeDisconnect();
}, 5000);
getEditor().setViewModeDisconnect();
} else {
setEditable(true);
}
@ -1109,14 +1181,7 @@ define([
}
if (APP.migrate && !readOnly) {
var div = h('div.cp-oo-x2tXls', [
h('span.fa.fa-spin.fa-spinner'),
h('span', Messages.oo_sheetMigration_loading)
]);
UI.openCustomModal(UI.dialog.customModal(div, {buttons: []}));
setTimeout(function () {
makeCheckpoint(true);
}, 5000);
onMigrateRdy.fire();
}
}
}
@ -1326,6 +1391,9 @@ define([
});
};
var supportsXLSX = function () {
return !(typeof(Atomics) === "undefined" || typeof (SharedArrayBuffer) === "undefined");
};
var exportXLSXFile = function() {
var text = getContent();
@ -1339,7 +1407,7 @@ define([
ext = ['.docx', /*'.odt',*/ '.bin'];
}
if (typeof(Atomics) === "undefined") {
if (!supportsXLSX()) {
ext = ['.bin'];
warning = '<div class="alert alert-info cp-alert-top">'+Messages.oo_exportChrome+'</div>';
}
@ -1519,7 +1587,7 @@ define([
if (ext === "bin") {
return void importFile(content);
}
if (typeof(Atomics) === "undefined") {
if (!supportsXLSX()) {
return void UI.alert(Messages.oo_invalidFormat);
}
var div = h('div.cp-oo-x2tXls', [
@ -1694,7 +1762,7 @@ define([
} else if (type === "oodoc") {
accept = ['.bin', '.odt', '.docx'];
}
if (typeof(Atomics) === "undefined") {
if (!supportsXLSX()) {
accept = ['.bin'];
}
@ -1760,6 +1828,7 @@ define([
}
var version = 'v2a/';
var msg;
// Old version detected: use the old OO and start the migration if we can
if (privateData.ooForceVersion) {
if (privateData.ooForceVersion === "1") {
@ -1768,12 +1837,23 @@ define([
} else if (content && (!content.version || content.version === 1)) {
version = 'v1/';
APP.migrate = true;
// Registedred users can start the migration
if (common.isLoggedIn()) {
// Registedred ~~users~~ editors can start the migration
if (common.isLoggedIn() && !readOnly) {
content.migration = true;
APP.onLocal();
} else {
var msg = h('div.alert.alert-warning.cp-burn-after-reading', Messages.oo_sheetMigration_anonymousEditor);
msg = h('div.alert.alert-warning.cp-burn-after-reading', Messages.oo_sheetMigration_anonymousEditor); // XXX update: "anonymous users or viewers"
$(APP.helpMenu.menu).after(msg);
readOnly = true;
}
} else if (content && content.version === 2) {
APP.migrate = true;
// Registedred ~~users~~ editors can start the migration
if (common.isLoggedIn() && !readOnly) {
content.migration = true;
APP.onLocal();
} else {
msg = h('div.alert.alert-warning.cp-burn-after-reading', Messages.oo_sheetMigration_anonymousEditor);
$(APP.helpMenu.menu).after(msg);
readOnly = true;
}
@ -1820,15 +1900,14 @@ define([
var reloadPopup = false;
var checkNewCheckpoint = function () {
var hasDrawings = checkDrawings();
if (hasDrawings) {
var lastCp = getLastCp();
loadLastDocument(lastCp, function () {
// On error, do nothing
}, function (blob, type) {
resetData(blob, type);
});
}
var lastCp = getLastCp();
loadLastDocument(lastCp, function (err) {
console.error(err);
// On error, do nothing
// XXX lock the document or ask for a page reload?
}, function (blob, type) {
resetData(blob, type);
});
};
config.onRemote = function () {
@ -1857,10 +1936,7 @@ define([
var newLatest = getLastCp();
if (newLatest.index > latest.index) {
ooChannel.queue = [];
var hasDrawings = checkDrawings();
if (hasDrawings) {
ooChannel.ready = false;
}
ooChannel.ready = false;
// New checkpoint
sframeChan.query('Q_OO_SAVE', {
hash: newLatest.hash,

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

@ -1,322 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>ONLYOFFICE Document Editor</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=IE8"/>
<meta name="description" content="" />
<meta name="keywords" content="" />
<link rel="icon" href="resources/img/favicon.ico" type="image/x-icon" />
<!-- splash -->
<style type="text/css">
.loadmask {
left: 0;
top: 0;
position: absolute;
height: 100%;
width: 100%;
overflow: hidden;
border: none;
background-color: #f4f4f4;
z-index: 1001;
}
.loader-page {
width: 100%;
height: 170px;
bottom: 42%;
position: absolute;
text-align: center;
line-height: 10px;
}
.loader-logo {
max-height: 160px;
margin-bottom: 10px;
}
.loader-page-romb {
width: 40px;
display: inline-block;
}
.loader-page-text {
width: 100%;
bottom: 42%;
position: absolute;
text-align: center;
color: #888;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 20px;
}
.loader-page-text-loading {
font-size: 14px;
}
.loader-page-text-customer {
font-size: 16px;
margin-bottom: 5px;
}
.romb {
width: 40px;
height: 40px;
-webkit-transform: rotate(135deg) skew(20deg, 20deg);
-moz-transform: rotate(135deg) skew(20deg, 20deg);
-ms-transform: rotate(135deg) skew(20deg, 20deg);
-o-transform: rotate(135deg) skew(20deg, 20deg);
position: absolute;
background: red;
border-radius: 6px;
-webkit-animation: movedown 3s infinite ease;
-moz-animation: movedown 3s infinite ease;
-ms-animation: movedown 3s infinite ease;
-o-animation: movedown 3s infinite ease;
animation: movedown 3s infinite ease;
}
#blue {
z-index: 3;
background: #55bce6;
-webkit-animation-name: blue;
-moz-animation-name: blue;
-ms-animation-name: blue;
-o-animation-name: blue;
animation-name: blue;
}
#red {
z-index:1;
background: #de7a59;
-webkit-animation-name: red;
-moz-animation-name: red;
-ms-animation-name: red;
-o-animation-name: red;
animation-name: red;
}
#green {
z-index: 2;
background: #a1cb5c;
-webkit-animation-name: green;
-moz-animation-name: green;
-ms-animation-name: green;
-o-animation-name: green;
animation-name: green;
}
@-webkit-keyframes red {
0% { top:120px; background: #de7a59; }
10% { top:120px; background: #F2CBBF; }
14% { background: #f4f4f4; top:120px; }
15% { background: #f4f4f4; top:0;}
20% { background: #E6E4E4; }
30% { background: #D2D2D2; }
40% { top:120px; }
100% { top:120px; background: #de7a59; }
}
@keyframes red {
0% { top:120px; background: #de7a59; }
10% { top:120px; background: #F2CBBF; }
14% { background: #f4f4f4; top:120px; }
15% { background: #f4f4f4; top:0; }
20% { background: #E6E4E4; }
30% { background: #D2D2D2; }
40% { top:120px; }
100% { top:120px; background: #de7a59; }
}
@-webkit-keyframes green {
0% { top:110px; background: #a1cb5c; opacity:1; }
10% { top:110px; background: #CBE0AC; opacity:1; }
14% { background: #f4f4f4; top:110px; opacity:1; }
15% { background: #f4f4f4; top:0; opacity:1; }
20% { background: #f4f4f4; top:0; opacity:0; }
25% { background: #EFEFEF; top:0; opacity:1; }
30% { background:#E6E4E4; }
70% { top:110px; }
100% { top:110px; background: #a1cb5c; }
}
@keyframes green {
0% { top:110px; background: #a1cb5c; opacity:1; }
10% { top:110px; background: #CBE0AC; opacity:1; }
14% { background: #f4f4f4; top:110px; opacity:1; }
15% { background: #f4f4f4; top:0; opacity:1; }
20% { background: #f4f4f4; top:0; opacity:0; }
25% { background: #EFEFEF; top:0; opacity:1; }
30% { background:#E6E4E4; }
70% { top:110px; }
100% { top:110px; background: #a1cb5c; }
}
@-webkit-keyframes blue {
0% { top:100px; background: #55bce6; opacity:1; }
10% { top:100px; background: #BFE8F8; opacity:1; }
14% { background: #f4f4f4; top:100px; opacity:1; }
15% { background: #f4f4f4; top:0; opacity:1; }
20% { background: #f4f4f4; top:0; opacity:0; }
25% { background: #f4f4f4; top:0; opacity:0; }
45% { background: #EFEFEF; top:0; opacity:0.2; }
100% { top:100px; background: #55bce6; }
}
@keyframes blue {
0% { top:100px; background: #55bce6; opacity:1; }
10% { top:100px; background: #BFE8F8; opacity:1; }
14% { background: #f4f4f4; top:100px; opacity:1; }
15% { background: #f4f4f4; top:0; opacity:1; }
20% { background: #f4f4f4; top:0; opacity:0; }
25% { background: #f4f4f4; top:0; opacity:0; }
45% { background: #EFEFEF; top:0; opacity:0.2; }
100% { top:100px; background: #55bce6; }
}
</style>
<script>
var userAgent = navigator.userAgent.toLowerCase(),
check = function(regex){ return regex.test(userAgent); },
stopLoading = false;
if (!check(/opera/) && (check(/msie/) || check(/trident/))) {
var m = /msie (\d+\.\d+)/.exec(userAgent);
if (m && parseFloat(m[1]) < 10.0) {
document.write('<div class="app-error-panel">' +
'<div class="message-block">' +
'<div class="message-inner">' +
'<div class="title">Your browser is not supported.</div>' +
'<div class="text">Sorry, Document Editor is currently only supported in the latest versions of the Chrome, Firefox, Safari or Internet Explorer web browsers.</div>' +
'</div>' +
'</div></div>');
stopLoading = true;
}
} else
if (check(/windows\snt/i)) {
var re = /chrome\/(\d+)/i.exec(userAgent);
if (!!re && !!re[1] && !(re[1] > 49)) {
setTimeout(function () {
document.getElementsByTagName('body')[0].className += "winxp";
},0);
}
}
function getUrlParams() {
var e,
a = /\+/g, // Regex for replacing addition symbol with a space
r = /([^&=]+)=?([^&]*)/g,
d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
q = window.location.search.substring(1),
urlParams = {};
while (e = r.exec(q))
urlParams[d(e[1])] = d(e[2]);
return urlParams;
}
function encodeUrlParam(str) {
return str.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
var params = getUrlParams(),
lang = (params["lang"] || 'en').split(/[\-\_]/)[0],
customer = params["customer"] ? ('<div class="loader-page-text-customer">' + encodeUrlParam(params["customer"]) + '</div>') : '',
margin = (customer !== '') ? 50 : 20,
loading = 'Loading...',
logo = params["logo"] ? ((params["logo"] !== 'none') ? ('<img src="' + encodeUrlParam(params["logo"]) + '" class="loader-logo" />') : '') : null;
window.frameEditorId = params["frameEditorId"];
if ( lang == 'de') loading = 'Ladevorgang...';
else if ( lang == 'es') loading = 'Cargando...';
else if ( lang == 'fr') loading = 'Chargement en cours...';
else if ( lang == 'it') loading = 'Caricamento in corso...';
else if ( lang == 'pt') loading = 'Carregando...';
else if ( lang == 'ru') loading = 'Загрузка...';
else if ( lang == 'sl') loading = 'Nalaganje...';
else if ( lang == 'tr') loading = 'Yükleniyor...';
else if ( lang == 'bg') loading = 'Зареждане...';
else if ( lang == 'cs') loading = 'Nahrávám...';
else if ( lang == 'hu') loading = 'Betöltés...';
else if ( lang == 'ja') loading = '読み込み中...';
else if ( lang == 'ko') loading = '로드 중...';
else if ( lang == 'lv') loading = 'Ieladēšana ...';
else if ( lang == 'nl') loading = 'Laden...';
else if ( lang == 'pl') loading = 'Ładowanie...';
else if ( lang == 'sk') loading = 'Nahrávam...';
else if ( lang == 'uk') loading = 'Завантаження...';
else if ( lang == 'vi') loading = 'Đang tải...';
else if ( lang == 'zh') loading = '加载中...';
if ( !stopLoading )
document.write(
'<div id="loading-mask" class="loadmask">' +
'<div class="loader-page" style="margin-bottom: ' + margin + 'px;' + ((logo!==null) ? 'height: auto;' : '') + '">' +
((logo!==null) ? logo :
'<div class="loader-page-romb">' +
'<div class="romb" id="blue"></div>' +
'<div class="romb" id="green"></div>' +
'<div class="romb" id="red"></div>' +
'</div>') +
'</div>' +
'<div class="loader-page-text">' + customer +
'<div class="loader-page-text-loading">' + loading + '</div>' +
'</div>' +
'</div>');
</script>
<link rel="stylesheet" type="text/css" href="../../../apps/documenteditor/main/resources/css/app.css">
</head>
<body>
<script>
window.requireTimeourError = function(){
var reqerr;
if ( lang == 'de') reqerr = 'Die Verbindung ist zu langsam, einige Komponenten konnten nicht geladen werden. Aktualisieren Sie bitte die Seite.';
else if ( lang == 'es') reqerr = 'La conexión es muy lenta, algunos de los componentes no han podido cargar. Por favor recargue la página.';
else if ( lang == 'fr') reqerr = 'La connexion est trop lente, certains des composants n\'ons pas pu être chargé. Veuillez recharger la page.';
else if ( lang == 'ru') reqerr = 'Слишком медленное соединение, не удается загрузить некоторые компоненты. Пожалуйста, обновите страницу.';
else reqerr = 'The connection is too slow, some of the components could not be loaded. Please reload the page.';
return reqerr;
};
var requireTimeoutID = setTimeout(function(){
window.alert(window.requireTimeourError());
window.location.reload();
}, 30000);
var require = {
waitSeconds: 30,
callback: function(){
clearTimeout(requireTimeoutID);
}
};
</script>
<inline src="resources/img/header/buttons.svg" />
<inline src="resources/img/doc-formats/docx.svg" />
<inline src="resources/img/doc-formats/dotx.svg" />
<inline src="resources/img/doc-formats/pdf.svg" />
<inline src="resources/img/doc-formats/pdfa.svg" />
<inline src="resources/img/doc-formats/txt.svg" />
<inline src="resources/img/doc-formats/odt.svg" />
<inline src="resources/img/doc-formats/ott.svg" />
<inline src="resources/img/doc-formats/rtf.svg" />
<inline src="resources/img/doc-formats/html.svg" />
<inline src="resources/img/doc-formats/blank.svg" />
<inline src="resources/img/toolbar/shapetypes.svg" />
<div id="viewport"></div>
<script data-main="app" src="../../../vendor/requirejs/require.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,94 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Compare documents</title>
<meta charset="utf-8" />
<meta name="description" content="Instructions on how to compare and merge two documents" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
<script type="text/javascript" src="../search/js/page-search.js"></script>
</head>
<body>
<div class="mainpart">
<div class="search-field">
<input id="search" class="searchBar" placeholder="Search" type="text" onkeypress="doSearch(event)">
</div>
<h1>Compare documents</h1>
<p class="note"><b>Note</b>: this option is available in the <b>paid</b> <em>online version</em> only starting from <b>Document Server</b> v. <b>5.5</b>.</p>
<p>If you need to compare and merge two documents, you can use the document <b>Compare</b> feature. It allows to display the differences between two documents and merge the documents by accepting the changes one by one or all at once.</p>
<p>After comparing and merging two documents, the result will be stored on the portal as a new version of the original file<!-- (in the <em>online version</em> of editors)-->. <!--In the desktop version, when you click the <b>Save</b> button, the dialog window will appear where you will be suggested to save a new file.--></p>
<p>If you do not need to merge documents which are being compared, you can reject all the changes so that the original document remains unchanged.</p>
<h3 id="choosedocument">Choose a document for comparison</h3>
<p>To compare two documents, open the original document that you need to compare and select the second document for comparison:</p>
<ol>
<li>switch to the <b>Collaboration</b> tab at the top toolbar and press the <img alt="Compare button" src="../images/comparebutton.png" /> <b>Compare</b> button,</li>
<li>
select one of the following options to load the document:
<ul>
<li>the <b>Document from File</b> option will open the standard dialog window for file selection. Browse your computer hard disk drive for the necessary <em>.docx</em> file and click the <b>Open</b> button.</li>
<li>
the <b>Document from URL</b> option will open the window where you can enter a link to the file stored in a third-party web storage (for example, Nextcloud) if you have corresponding access rights to it. The link must be a <b>direct link for downloading the file</b>. When the link is specified, click the <b>OK</b> button.
<p class="note"><b>Note</b>: The direct link allows to download the file directly without opening it in a web browser. For example, to get a direct link in Nextcloud, find the necessary document in the file list, select the <b>Details</b> option from the file menu. Click the <b>Copy direct link (only works for users who have access to this file/folder)</b> icon to the right of the file name at the details panel. To find out how to get a direct link for downloading the file in a different third-party web storage, please refer to the corresponding third-party service documentation.</p>
</li>
<li class="onlineDocumentFeatures"> the <b>Document from Storage</b> option <!--(available in the <em>online version</em> only)--> will open the <b>Select Data Source</b> window. It displays the list of all the <em>.docx</em> documents stored on your portal you have corresponding access rights to. To navigate between the <b>Documents</b> module sections use the menu in the left part of the window. Select the necessary <em>.docx</em> document and click the <b>OK</b> button.</li>
</ul>
</li>
</ol>
<p>When the second document for comparison is selected, the comparison process will start and the document will look as if it was opened in the <b>Review</b> mode. All the changes are highlighted with a color, and you can view the changes, navigate between them, accept or reject them one by one or all the changes at once. It's also possible to change the display mode and see how the document looks before comparison, in the process of comparison, or how it will look after comparison if you accept all changes.</p>
<h3 id="displaymode">Choose the changes display mode</h3>
<p>Click the <img alt="Display Mode button" src="../images/review_displaymode.png" /> <b>Display Mode</b> button at the top toolbar and select one of the available modes from the list:</p>
<ul>
<li>
<b>Markup</b> - this option is selected by default. It is used to display the document <b>in the process of comparison</b>. This mode allows both to view the changes and edit the document.
<p><img alt="Compare documents - Markup" src="../images/compare_markup.png" /></p>
</li>
<li>
<b>Final</b> - this mode is used to display the document <b>after comparison</b> as if all the changes were accepted. This option does not actually accept all changes, it only allows you to see how the document will look like after you accept all the changes. In this mode, you cannot edit the document.
<p><img alt="Compare documents - Final" src="../images/compare_final.png" /></p>
</li>
<li>
<b>Original</b> - this mode is used to display the document <b>before comparison</b> as if all the changes were rejected. This option does not actually reject all changes, it only allows you to view the document without changes. In this mode, you cannot edit the document.
<p><img alt="Compare documents - Original" src="../images/compare_original.png" /></p>
</li>
</ul>
<h3 id="managechanges">Accept or reject changes</h3>
<p>Use the <img alt="To Previous Change button" src="../images/review_previous.png" /> <b>Previous</b> and the <img alt="To Next Change button" src="../images/review_next.png" /> <b>Next</b> buttons at the top toolbar to navigate among the changes.</p>
<p>To accept the currently selected change you can:</p>
<ul>
<li>click the <img alt="Accept button" src="../images/review_accepttoptoolbar.png" /> <b>Accept</b> button at the top toolbar, or</li>
<li>click the downward arrow below the <b>Accept</b> button and select the <b>Accept Current Change</b> option (in this case, the change will be accepted and you will proceed to the next change), or</li>
<li>click the <b>Accept</b> <img alt="Accept button" src="../images/review_accept.png" /> button of the change notification.</li>
</ul>
<p>To quickly accept all the changes, click the downward arrow below the <img alt="Accept button" src="../images/review_accepttoptoolbar.png" /> <b>Accept</b> button and select the <b>Accept All Changes</b> option.</p>
<p>To reject the current change you can:</p>
<ul>
<li>click the <img alt="Reject button" src="../images/review_rejecttoptoolbar.png" /> <b>Reject</b> button at the top toolbar, or</li>
<li>click the downward arrow below the <b>Reject</b> button and select the <b>Reject Current Change</b> option (in this case, the change will be rejected and you will move on to the next available change), or</li>
<li>click the <b>Reject</b> <img alt="Reject button" src="../images/review_reject.png" /> button of the change notification.</li>
</ul>
<p>To quickly reject all the changes, click the downward arrow below the <img alt="Reject button" src="../images/review_rejecttoptoolbar.png" /> <b>Reject</b> button and select the <b>Reject All Changes</b> option.</p>
<h3 id="comparisonnotes">Additional info on the comparison feature</h3>
<h5>Method of the comparison</h5>
<p>Documents are compared <b>by words</b>. If a word contains a change of at least one character (e.g. if a character was removed or replaced), in the result, the difference will be displayed as the change of the entire word, not the character.</p>
<p>The image below illustrates the case when the original file contains the word 'Characters' and the document for comparison contains the word 'Character'.</p>
<p><img alt="Compare documents - method" src="../images/compare_method.png" /></p>
<h5>Authorship of the document</h5>
<p>When the comparison process is launched, the second document for comparison is being loaded and compared to the current one.</p>
<ul>
<li>If the loaded document contains some data which is not represented in the original document, the data will be marked as added by a reviewer.</li>
<li>If the original document contains some data which is not represented in the loaded document, the data will be marked as deleted by a reviewer.</li>
</ul>
<p>If the authors of the original and loaded documents are the same person, the reviewer is the same user. His/her name is displayed in the change balloon.</p>
<p>If the authors of two files are different users, then the author of the second file loaded for comparison is the author of the added/removed changes.</p>
<h5>Presence of the tracked changes in the compared document</h5>
<p>If the original document contains some changes made in the review mode, they will be accepted in the comparison process. When you choose the second file for comparison, you'll see the corresponding warning message.</p>
<p>In this case, when you choose the <b>Original</b> display mode, the document will not contain any changes.</p>
</div>
</body>
</html>

@ -1,61 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Add caption</title>
<meta charset="utf-8" />
<meta name="description" content=">The Caption is a numbered label that you can apply to objects, such as equations, tables, figures and images within your documents" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
<script type="text/javascript" src="../search/js/page-search.js"></script>
</head>
<body>
<div class="mainpart">
<div class="search-field">
<input id="search" class="searchBar" placeholder="Search" type="text" onkeypress="doSearch(event)">
</div>
<h1>Add caption</h1>
<p>The <b>Caption</b> is a numbered label that you can apply to objects, such as equations, tables, figures and images within your documents.</p>
<p>This makes it easy to reference within your text as there is an easily recognizable label on your object.</p>
<p>To add the caption to an object:</p>
<ul>
<li>select the object which one to apply a caption;</li>
<li>switch to the <b>References</b> tab of the top toolbar;</li>
<li>
click the <img alt="Rich text content control" src="../images/caption_icon.png" /> <b>Caption</b> icon at the top toolbar or right lick o nthe object and select the <b>Insert Caption</b> option to open the <b>Insert Caption</b> dialogue box
<ul>
<li>choose the label to use for your caption by clicking the label drop-down and choosing the object. or</li>
<li>create a new label by clicking the <b>Add label</b> button to open the <b>Add label</b> dialogue box. Enter a name for the label into the label text box. Then click the <b>OK</b> button to add a new label into the label list;</li>
</ul>
<li>check the <b>Include chapter number</b> checkbox to change the numbering for your caption;</li>
<li>in <b>Insert</b> drop-down menu choose <b>Before</b> to place the label above the object or <b>After</b> to place it below the object;</li>
<li>check the <b>Exclude label from caption</b> checkbox to leave only a number for this particular caption in accordance with a sequence number;</li>
<li>you can then choose how to number your caption by assigning a specific style to the caption and adding a separator;</li>
<li>to apply the caption click the <b>OK</b> button.</li>
</ul>
<p><img alt="Content Control settings window" src="../images/insertcaptionbox.png" /></p>
<h2>Deleting a label</h2>
<p>To <b>delete</b> a label you have created, choose the label from the label list within the caption dialogue box then click the <b>Delete label</b> button. The label you created will be immediately deleted.</p>
<p class="note"><b>Note:</b>You may delete labels you have created but you cannot delete the default labels.</p>
<h2>Formatting captions</h2>
<p>As soon as you add a caption, a new style for captions is automatically added to the styles section. In order to change the style for all captions throughout the document, you should follow these steps:</p>
<ul>
<li>select the text a new <b>Caption</b> style will be copied from;</li>
<li>search for the <b>Caption</b> style (highlighted in blue by default) in the styles gallery which you may find on <b>Home</b> tab of the top toolbar;</li>
<li>right click on it and choose the <b>Update from selection</b> option.</li>
</ul>
<p><img alt="Content Control settings window" src="../images/updatefromseleciton.png" /></p>
<h2>Grouping captions up</h2>
<p>If you want to be able to move the object and the caption as one unit, you need <a href="../UsageInstructions/AlignArrangeObjects.htm" onclick="onhyperlinkclick(this)">to group</a> the object and the caption together</p>
<ul>
<li>select the object;</li>
<li>select one of the <b>Wrapping styles</b> using the right sidebar;</li>
<li>add the caption as it is mentioned above;</li>
<li>hold down Shift and select the items you want to group up;</li>
<li><b>right click</b> on either item and choose <b>Arrange</b> > <b>Group</b>.</li>
</ul>
<p><img alt="Content Control settings window" src="../images/Groupup.png" /></p>
<p>Now both items will move simultaneously if you drag them somewhere else in the document.</p>
<p>To unbind the objects click on <b>Arrange</b> > <b>Ungroup</b> respectively.</p>
</div>
</body>
</html>

@ -1,50 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Add watermark</title>
<meta charset="utf-8" />
<meta name="description" content="Text watermarks allow to indicate your document status (for example, confidential, draft etc.), image watermarks allow to add an image, for example your company logo." />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
<script type="text/javascript" src="../search/js/page-search.js"></script>
</head>
<body>
<div class="mainpart">
<div class="search-field">
<input id="search" class="searchBar" placeholder="Search" type="text" onkeypress="doSearch(event)">
</div>
<h1>Add watermark</h1>
<p>Watermark is a text or image placed below the main text layer. Text watermarks allow to indicate your document status (for example, confidential, draft etc.), image watermarks allow to add an image, for example your company logo.</p>
<p>To add a watermark within a document:</p>
<ol>
<li>Switch to the <b>Layout</b> tab of the top toolbar.</li>
<li>Click the <img alt="Watermark icon" src="../images/watermark.png" /> <b>Watermark</b> icon at the top toolbar and choose the <b>Custom Watermark</b> option from the menu. After that the <b>Watermark Settings</b> window will appear.</li>
<li>Select a watermark type you wish to insert:
<ul>
<li>Use the <b>Text watermark</b> option and adjust the available parameters:
<p><img alt="Watermark Settings window" src="../images/watermark_settings.png" /></p>
<ul>
<li><b>Language</b> - select one of the available languages from the list,</li>
<li><b>Text</b> - select one of the available text examples on the selected language. For English, the following watermark texts are available: <em>ASAP</em>, <em>CONFIDENTIAL</em>, <em>COPY</em>, <em>DO NOT COPY</em>, <em>DRAFT</em>, <em>ORIGINAL</em>, <em>PERSONAL</em>, <em>SAMPLE</em>, <em>TOP SECRET</em>, <em>URGENT</em>.</li>
<li><b>Font</b> - select the font name and size from the corresponding drop-down lists. Use the icons on the right to set the font color or apply one of the font decoration styles: <em>Bold</em>, <em>Italic</em>, <em>Underline</em>, <em>Strikeout</em>,</li>
<li><b>Semitransparent</b> - check this box if you want to apply transparency,</li>
<li><b>Layout</b> - select the <b>Diagonal</b> or <b>Horizontal</b> option.</li>
</ul>
</li>
<li>Use the <b>Image watermark</b> option and adjust the available parameters:
<p><img alt="Watermark Settings window" src="../images/watermark_settings2.png" /></p>
<ul>
<li>Choose the image file source using one of the buttons: <b>From File</b> or <b>From URL</b> - the image will be displayed in the preview window on the right,</li>
<li><b>Scale</b> - select the necessary scale value from the available ones: <em>Auto</em>, <em>500%</em>, <em>200%</em>, <em>150%</em>, <em>100%</em>, <em>50%</em>.</li>
</ul>
</li>
</ul>
</li>
<li>Click the <b>OK</b> button.</li>
</ol>
<p>To edit the added watermark, open the <b>Watermark Settings</b> window as described above, change the necessary parameters and click <b>OK</b>.</p>
<p>To delete the added watermark click the <img alt="Watermark icon" src="../images/watermark.png" /> <b>Watermark</b> icon at the <b>Layout</b> tab of the top toolbar and choose the <b>Remove Watermark</b> option from the menu. It's also possible to use the <b>None</b> option in the <b>Watermark Settings</b> window.</p>
</div>
</body>
</html>

@ -1,53 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Insert symbols and characters</title>
<meta charset="utf-8" />
<meta name="description" content="Insert symbols and characters" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
<script type="text/javascript" src="../search/js/page-search.js"></script>
</head>
<body>
<div class="mainpart">
<div class="search-field">
<input id="search" class="searchBar" placeholder="Search" type="text" onkeypress="doSearch(event)">
</div>
<h1>Insert symbols and characters</h1>
<p>During working process you may need to insert a symbol which is not on your keyboard. To insert such symbols into your document, use the <img alt="Symbol table icon" src="../images/vector.png" /><b>Insert symbol</b> option and follow these simple steps:</p>
<ul>
<li>place the cursor at the location where a special symbol has to be inserted,</li>
<li>switch to the <b>Insert</b> tab of the top toolbar,</li>
<li>
click the <img alt="Symbol table icon" src="../images/vector.png" /><b>Symbol</b>,
<p><img alt="Insert symbol sidebar " src="../images/insert_symbol_window.png" /></p>
</li>
<li>The <b>Symbol</b> dialog box appears from which you can select the appropriate symbol,</li>
<li>
<p>use the <b>Range</b> section to quickly find the nesessary symbol. All symbols are divided into specific groups, for example, select 'Currency Symbols' if you want to insert a currency character.</p>
<p>If this character is not in the set, select a different font. Many of them also have characters other than the standard set.</p>
<p>Or, enter the Unicode hex value of the symbol you want into the <b>Unicode hex value field</b>. This code can be found in the <b>Character map</b>.</p>
<p>Previously used symbols are also displayed in the <b>Recently used symbols</b> field,</p>
</li>
<li>click <b>Insert</b>. The selected character will be added to the document.</li>
</ul>
<h2>Insert ASCII symbols</h2>
<p>ASCII table is also used to add characters.</p>
<p>To do this, hold down ALT key and use the numeric keypad to enter the character code.</p>
<p class="note"><b>Note</b>: be sure to use the numeric keypad, not the numbers on the main keyboard. To enable the numeric keypad, press the Num Lock key.</p>
<p>For example, to add a paragraph character (§), press and hold down ALT while typing 789, and then release ALT key.</p>
<h2>Insert symbols using Unicode table</h2>
<p>Additional charachters and symbols might also be found via Windows symbol table. To open this table, do one of the following:</p>
<ul>
<li>in the Search field write 'Character table' and open it,</li>
<li>
simultaneously presss Win + R, and then in the following window type <code>charmap.exe</code> and click OK.
<p><img alt="Insert symbol windpow" src="../images/insert_symbols_windows.png" /></p>
</li>
</ul>
<p>In the opened <b>Character Map</b>, select one of the <b>Character sets</b>, <b>Groups</b> and <b>Fonts</b>. Next, click on the nesessary characters, copy them to clipboard and paste in the right place of the document.</p>
</div>
</body>
</html>

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Set up paragraph outline level</title>
<meta charset="utf-8" />
<meta name="description" content="Set up paragraph outline level" />
<link type="text/css" rel="stylesheet" href="../editor.css" />
<script type="text/javascript" src="../callback.js"></script>
<script type="text/javascript" src="../search/js/page-search.js"></script>
</head>
<body>
<div class="mainpart">
<div class="search-field">
<input id="search" class="searchBar" placeholder="Search" type="text" onkeypress="doSearch(event)">
</div>
<h1>Set up paragraph outline level</h1>
<p>Outline level means the paragraph level in the document structure. The following levels are available: <em>Basic Text</em>, <em>Level 1</em> - <em>Level 9</em>. Outline level can be specified in different ways, for example, by using <a href="../UsageInstructions/FormattingPresets.htm" onclick="onhyperlinkclick(this)">heading styles</a>: once you assign a heading style (<em>Heading 1</em> - <em>Heading 9</em>) to a paragraph, it acquires a corresponding outline level. If you assign a level to a paragraph using the paragraph advanced settings, the paragraph acquires the structure level only while its style remains unchanged. The outline level can also be changed at the <a href="../UsageInstructions/CreateTableOfContents.htm#navigation" onclick="onhyperlinkclick(this)"><b>Navigation</b></a> panel on the left using the contextual menu options.</p>
<p>To change a paragraph outline level using the paragraph advanced settings,</p>
<ol>
<li>right-click the text and choose the <b>Paragraph Advanced Settings</b> option from the contextual menu or use the <b>Show advanced settings</b> option at the right sidebar,</li>
<li>open the <b>Paragraph - Advanced Settings</b> window, switch to the <b>Indents &amp; Spacing</b> tab,</li>
<li>select the necessary outline level from the <b>Outline level</b> list.</li>
<li>click the <b>OK</b> button to apply the changes.</li>
</ol>
<p><img alt="Paragraph Advanced Settings - Indents &amp; Spacing" src="../images/paradvsettings_indents.png" /></p>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save