diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f649883d..d267f497c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,96 @@ +# ZyzomysPedunculatus (3.25.0) + +## Goals + +This is the last major release of our 3.0.0 release cycle. We wanted to mark the occasion with some big improvements to keep everyone happy in case we need to take some more time to prepare our upcoming 4.0.0 release. + +## Update notes + +This update introduces some major database optimizations that should decrease both CPU and disk usage over time as users request resources and prime an on-disk cache for the next time. + +We've also introduce the ability to archive illegal or otherwise objectionable material from the admin panel assuming you possess the ability to load the content in question. It's also possible to restore archived content via an adjacent form field on the admin panel as long as it has not been permanently deleted. Due to a quirk in how ownership of uploaded files works, restored files will not retain their "owners" property. We hope to fix this in a future release. + +We've also made some minor changes to the example NGINX config file provided in `cryptpad/docs/example.nginx.confg`, specifically in [this commit](https://github.com/xwiki-labs/cryptpad/commit/2647acbb78643e651b71d2d4f74c2f66e264a258). CryptPad will probably work if you don't apply these changes to your nginx conf, but some functional improvements depend on the exposed headers. + +To upgrade from 3.24.0 to 3.25.0: + +1. Update your NGINX config as mentioned above. +2. Stop your nodejs server. +3. Pull the latest code using git (from the `3.25.0` tag or the `main` branch) +4. Ensure you have the latest clientside and serverside dependencies with `bower update` and `npm install`. +5. Restart the nodejs server. + +## Features + +* This release makes a lot of changes to how content is loaded over the network. + * Most notably, CryptPad now employs a client-side cache based on the the _indexedDB API_. Browsers that support this functionality will opportunistically store messages in a local cache for the next time they need them. This should make a considerable difference in how quickly you're able to load a pad, particularly if you accessing the server over a low-bandwidth network. + * Uploaded files (images, PDFs, etc.) are also cached in a similar way. Once you'd loaded an asset, your client will prefer to load its local copy instead of the server. + * We've updated the code for our _full drive backup_ functionality so that it uses the local cache to load files more quickly. In addition to this, backing up the contents of your drive will also populate the cache as though you had loaded your documents in the normal fashion. This cache will persist until it is invalidated (due to the authoritative document having been deleted or had its history trimmed) or until you have logged out. + * We've added the ability to configure the maximum size for automatically downloaded files. Any encrypted files that are above this size will instead require manual interaction to begin downloading. Files that are larger than this limit which are already loaded in your cache will still be automatically displayed. +* We've also changed a lot of the UI related to encrypted file uploads and downloads: + * Encrypted files can display buttons instead of the intended media under a variety of circumstances (if they are larger than your configured limit or if there is no applicable rendering mode). The styles for these buttons are now much more consistent with those found throughout the rest of the platform. + * The same assets should now display progress bars when downloading and decrypting encrypted media. + * When the same asset is embedded into a document in more than one location it used to be possible to trigger two (or more) concurrent decryption processes. We've modified the rendering process so that duplicates are detected and rendered simultaneously after the relevant assets have been decrypted (once). + * We noticed that some old code to filter out forbidden content from rich text pads was interfering with encrypted media. We've clarified the filtering rules to preserve such content (audio, video, iframes) when it occurs within an acceptable context. + * We've fixed some inconsistencies with media styles and functionality across different editors. Most types of media now allow you to right-click and choose to _share_ (open that asset's share menu) or open it in a different context (in the file app or in the relevant editor where this behaviour is supported). + * The _file_ app has been greatly simplified. It now uses the same methods to render encrypted media as is used elsewhere, so it also displays progress and has a more consistent UI. + * The file uploads/downloads table has also been improved somewhat: + * Download progress is displayed for groups of items when downloading a folder from your drive. + * We found and removed a hard-coded translation from the table's header. +* In keeping with the theme of network traffic and files we've also made some improvements to policies for users' storage: + * Users should now be prompted to trim the history of very large documents when viewing them, saving space for the server operator as well as freeing up some of the user's quota. + * Users will also be prompted to use similar functionality available through the settings page when the history of their drive and other account-related functionality is consuming a significant amount of their quota. + * Documents that you own used to be automatically added to your drive when viewed if they weren't already present. This was originally intended as an integrity check and a means to recover from incorrectly removed entries in your drive, however, as we now support the removal of owned elements from your drive without destroying them this only serves as an annoyance. As such, we have dropped this functionality. + * The whiteboard editor allows users to insert encrypted images into whiteboards, but only up to a certain size. Before it would just warn you that your image was too large. Now it provides the actual size limit that you've exceeded. + * The prompt to store uploads in your drive is now suppressed when uploading images via the support ticket panel. + +## Bug fixes + +* This release includes a fix for a very severe bug in Chrome and its derivatives where attempting to open a URL from within our sandboxing system would crash the browser entirely. This version works around the problem by _not doing that_. +* We've improved offline detection such that "offline" status is specific to particular resources like your drive, teams, and shared folders rather than treating your account as simply "online or offline". +* We've optimized one of our less style sheet mixins that was used in a lot of places at a more specific scope than was necessary. This resulted in more time compiling styles and higher storage space requirements for the css cache in localStorage. +* A small helper function that was intended to stop listening for `enter` and `esc` keypresses after closing a modal was overly zealous and stopped listening after _any keypress_. This made it so that any prompt with an input field did not correctly submit or cancel when pressing `enter` or `esc` after typing some text. +* Various browsers now require the request for the permission to send notifications to originate from a "click" event, so CryptPad now opens a dialog prompting you to allow (or disallow) permission if you haven't already made that decision. +* Modern browsers commonly prevent tabs from opening new windows unless you've explicitly enabled that behaviour (it's an important feature), however, in some cases the indication that a new tab was blocked can be very subtle and some of our users did not notice it. We now check whether attempts to open a new tab were successful, and prompt the user to enable this behaviour so that CryptPad can perform regular actions like opening a pad from the drive. +* After some deep investigation we identified a number of scenarios where contact requests would behave incorrectly, such as not triggering a notification. Contact requests should now be much more stable. On a related note, it's now possible to cancel a pending contact request from the concerned user's profile. + +# YunnanLakeNewt (3.24.0) + +## Goals + +We are once again working to develop some significant new features. This release is fairly small but includes some significant changes to detect and handle a variety of errors. + +## Update notes + +This release includes some minor corrections the recommended NGINX configuration supplied in `cryptpad/docs/example.nginx.conf`. + +To update from 3.23.2 to 3.24.0: + +1. Update your NGINX config to replicate the most recent changes and reload NGINX to apply them. +2. Stop the nodejs server. +3. Pull the latest code from the `3.24.0` tag or the `main` branch using `git`. +4. Ensure you have the latest clientside and serverside dependencies with `bower update` and `npm install`. +5. Restart the nodejs server. + +## Features + +* A variety of CryptPad's pages now feature a much-improved loading screen which provides a more informative account of what is being loaded. It also implements some generic error handling to detect and report when something has failed in a catastrophic way. This is intended to both inform users that the page is in a broken state as well as to improve the quality of the debugging information they can provide to us so that we can fix the underlying cause. +* It is now possible to create spreadsheets from templates. Template functionality has existed for a long time in our other editors, however, OnlyOffice's architecture differs significantly and required the implementation of a wholly different system. +* One user reported some confusion regarding the use of the Kanban app's _tag_ functionality. We've updated the UI to be a little more informative. +* The "table of contents" in rich text pads now includes "anchors" created via the editor's toolbar. + +## Bug fixes + +* Recent changes to CryptPad's recommended CSP headers enabled Firefox to export spreadsheets to XLSX format, but they also triggered some regressions due to a number of incompatible APIs. + * Our usage of the `sessionStorage` for the purpose of passing important information to editors opened in a new tab stopped working. This meant that when you created a document in a folder, the resulting new tab would not receive the argument describing where it should be stored, and would instead save it to the default location. We've addressed this by replacing our usage of sessionStorage with a new format for passing the same arguments via the hash in the new document's URL. + * The `window.print` API also failed in a variety of cases. We've updated the relevant CSP headers to only be applied on the sheet editor (to support XSLX export) but allow printing elsewhere. We've also updated some print styles to provide more appealing results. +* The table of contents available in rich text pads failed to scroll when there were a sufficient number of heading to flow beyond the length of the page. Now a scrollbar appears when necessary. +* We discovered a number of cases where the presence of an allow list prevented some valid behaviour due to the server incorrectly concluding that users were not authenticated. We've improved the client's ability to detect these cases and re-authenticate when necessary. +* We also found that when the server was under very heavy load some database queries were timing out because they were slow (but not stopped). We've addressed this to only terminate such queries if they have been entirely inactive for several minutes. +* It was possible for "safe links" to include a mode ("edit" or "view") which did not match the rights of the user opening them. For example, if a user loaded a safe link with edit rights though they only had read-only access via their "viewer" role in a team. CryptPad will now recover from such cases and open the document with the closest set of access rights that they possess. +* We found that the server query `"IS_NEW_PAD"` could return an error but that clients would incorrectly interpret such a response as a `false`. This has been corrected. +* Finally, we've modified the "trash" UI for user and team drives such that when users attempt to empty their trash of owned shared folders they are prompted to remove the items or delete them from the server entirely, as they would be with other owned assets. + # XerusDaamsi reloaded (3.23.2) A number of instance administrators reported issues following our 3.23.1 release. We suspect the issues were caused by applying the recommended update steps out of order which would result in the incorrect HTTP header values getting cached for the most recent version of a file. Since the most recently updated headers modified some security settings, this caused a catastrophic error on clients receiving the incorrect headers which caused them to fail to load under certain circumstances. diff --git a/bower.json b/bower.json index bd9cebdd6..f9a3dc5be 100644 --- a/bower.json +++ b/bower.json @@ -30,7 +30,7 @@ "secure-fabric.js": "secure-v1.7.9", "hyperjson": "~1.4.0", "chainpad-crypto": "^0.2.0", - "chainpad-listmap": "^0.9.0", + "chainpad-listmap": "^0.10.0", "chainpad": "^5.2.0", "file-saver": "1.3.1", "alertifyjs": "1.0.11", diff --git a/config/config.example.js b/config/config.example.js index 32bcb20cf..a49d66d90 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -42,7 +42,7 @@ module.exports = { * * In a production instance this should be available ONLY over HTTPS * using the default port for HTTPS (443) ie. https://cryptpad.fr - * In such a case this should be handled by NGINX, as documented in + * In such a case this should be also handled by NGINX, as documented in * cryptpad/docs/example.nginx.conf (see the $main_domain variable) * */ @@ -228,12 +228,12 @@ module.exports = { */ /* customLimits: { - "https://my.awesome.website/user/#/1/cryptpad-user1/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=": { + "[cryptpad-user1@my.awesome.website/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=]": { limit: 20 * 1024 * 1024 * 1024, plan: 'insider', note: 'storage space donated by my.awesome.website' }, - "https://my.awesome.website/user/#/1/cryptpad-user2/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=": { + "[cryptpad-user2@my.awesome.website/GdflkgdlkjeworijfkldfsdflkjeEAsdlEnkbx1vVOo=]": { limit: 10 * 1024 * 1024 * 1024, plan: 'insider', note: 'storage space donated by my.awesome.website' diff --git a/customize.dist/ckeditor-contents.css b/customize.dist/ckeditor-contents.css index 000162c00..0d5b5cff7 100644 --- a/customize.dist/ckeditor-contents.css +++ b/customize.dist/ckeditor-contents.css @@ -213,3 +213,61 @@ media-tag * { width: 100%; height: 100%; } +media-tag button.btn { + background-color: #fff; + box-sizing: border-box; + outline: 0; + display: inline-flex; + align-items: center; + padding: 0 6px; + min-height: 36px; + line-height: 22px; + white-space: nowrap; + text-align: center; + text-transform: uppercase; + font-size: 14px; + text-decoration: none; + cursor: pointer; + border-radius: 0; + transition: none; + color: #3F4141; + border: 1px solid #3F4141; + max-width: 250px; +} +media-tag button.mediatag-download-btn { + flex-flow: column; + min-height: 38px; + justify-content: center; +} +media-tag button.mediatag-download-btn > span { + display: flex; + line-height: 1.5; + align-items: center; + justify-content: center; +} +media-tag button.mediatag-download-btn * { + width: auto; +} +media-tag button.mediatag-download-btn > span.mediatag-download-name { + max-width: 100%; +} +media-tag button.mediatag-download-btn > span.mediatag-download-name b { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +media-tag button.btn:hover, media-tag button.btn:active, media-tag button.btn:focus { + background-color: #ccc; +} +media-tag button.btn b { + margin-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +media-tag button.btn .fa { + display: inline; + margin-right: 5px; + flex: 0; +} diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 29e5ac92b..af73a9b42 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -253,7 +253,7 @@ p.cp-password-info{ animation-timing-function: cubic-bezier(.6,0.15,0.4,0.85); } -button.primary{ +button:not(.btn).primary{ border: 1px solid #4591c4; padding: 8px 12px; text-transform: uppercase; @@ -262,7 +262,7 @@ button.primary{ font-weight: bold; } -button.primary:hover{ +button:not(.btn).primary:hover{ background-color: rgb(52, 118, 162); } @@ -291,7 +291,7 @@ button.primary:hover{ var built = false; var types = ['less', 'drive', 'migrate', 'sf', 'team', 'pad', 'end']; - var current; + var current, progress; var makeList = function (data) { var c = types.indexOf(data.type); current = c; @@ -307,7 +307,7 @@ button.primary:hover{ }; var list = ''; @@ -315,7 +315,7 @@ button.primary:hover{ }; var makeBar = function (data) { var c = types.indexOf(data.type); - var l = types.length; + var l = types.length - 1; // don't count "end" as a type var progress = Math.min(data.progress, 100); var p = (progress / l) + (100 * c / l); var bar = '
'+ @@ -327,20 +327,34 @@ button.primary:hover{ var hasErrored = false; var updateLoadingProgress = function (data) { if (!built || !data) { return; } + + // Make sure progress doesn't go backward var c = types.indexOf(data.type); - if (c < current) { return console.error(data); } + if (c < current) { return console.debug(data); } + if (c === current && progress > data.progress) { return console.debug(data); } + progress = data.progress; + try { - document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); - document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + var el1 = document.querySelector('.cp-loading-spinner-container'); + if (el1) { el1.style.display = 'none'; } + var el2 = document.querySelector('.cp-loading-progress-list'); + if (el2) { el2.innerHTML = makeList(data); } + var el3 = document.querySelector('.cp-loading-progress-container'); + if (el3) { el3.innerHTML = makeBar(data); } } catch (e) { - if (!hasErrored) { console.error(e); } + //if (!hasErrored) { console.error(e); } } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; window.CryptPad_loadingError = function (err) { if (!built) { return; } + + if (err === 'Error: XDR encoding failure') { + console.warn(err); + return; + } + hasErrored = true; var err2; if (err === 'Script error.') { diff --git a/customize.dist/pages.js b/customize.dist/pages.js index fe8ac73fd..4cc7c59b0 100644 --- a/customize.dist/pages.js +++ b/customize.dist/pages.js @@ -69,7 +69,7 @@ define([ Msg.footer_team = "Contributors"; // XXX existing key Msg.footer_tos = "Terms of Service"; // XXX existing key - Pages.versionString = "v3.24.0 (YunnanLakeNewt)"; + Pages.versionString = "v3.25.0 (ZyzomysPedunculatus)"; // used for the about menu Pages.imprintLink = AppConfig.imprint ? footLink(imprintUrl, 'imprint') : undefined; diff --git a/customize.dist/src/less2/include/fileupload.less b/customize.dist/src/less2/include/fileupload.less index 0d39b342a..d2f2fff18 100644 --- a/customize.dist/src/less2/include/fileupload.less +++ b/customize.dist/src/less2/include/fileupload.less @@ -14,7 +14,7 @@ right: 10vw; bottom: 10vh; box-sizing: border-box; - z-index: 100000; //Z file upload table container + z-index: 100001; //Z file upload table container: just above the file picker display: none; color: darken(@colortheme_static_apps[default], 10%); max-height: 180px; diff --git a/customize.dist/src/less2/include/forms.less b/customize.dist/src/less2/include/forms.less index cad9427de..b6d909a73 100644 --- a/customize.dist/src/less2/include/forms.less +++ b/customize.dist/src/less2/include/forms.less @@ -2,6 +2,10 @@ @import (reference) "./variables.less"; .forms_main() { + --LessLoader_require: LessLoader_currentFile(); +} + +& { @alertify-fore: @colortheme_modal-fg; @alertify-btn-fg: @alertify-fore; @alertify-light-bg: fade(@alertify-fore, 25%); @@ -114,7 +118,9 @@ margin: 0; } - &:hover, &:active, &:focus { + &:hover, &:not(:disabled):not(.disabled):active, &:focus { + color: @alertify-btn-fg; + border: 1px solid @alertify-btn-fg; background-color: lighten(@alertify-fore, 35%); } @@ -124,19 +130,32 @@ font-weight: bold; } + &.btn-default { + border-color: @cryptpad_text_col; + color: @cryptpad_text_col; + &:hover, &:not(:disabled):active, &:focus { + border-color: @cryptpad_text_col; + color: @cryptpad_text_col; + background-color: #ccc; + } + } + &.danger, &.btn-danger { background-color: @colortheme_alertify-red; border-color: @colortheme_alertify-red-border; color: @colortheme_alertify-red-color; - &:hover, &:active, &:focus { + &:hover, &:not(:disabled):active, &:focus { + border-color: @colortheme_alertify-red-border; + color: @colortheme_alertify-red-color; background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-red, 10%), lighten(@colortheme_alertify-red, 10%)); } } - &.danger-alt, &.btn-danger-alt { + &.danger-alt, &.btn-danger-alt, &.btn-danger-outline { border-color: @colortheme_alertify-red; color: @colortheme_alertify-red; - &:hover, &:active, &:focus { + &:hover, &:not(:disabled):active, &:focus { + border-color: @colortheme_alertify-red; color: @colortheme_alertify-red-color; background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-red, 10%), lighten(@colortheme_alertify-red, 10%)); } @@ -146,17 +165,21 @@ background-color: @colortheme_alertify-green; border-color: @colortheme_alertify-green-border; color: @colortheme_alertify-green-color; - &:hover, &:active, &:focus { + &:hover, &:not(:disabled):active, &:focus { + border-color: @colortheme_alertify-green-border; + color: @colortheme_alertify-green-color; background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-green, 10%), lighten(@colortheme_alertify-green, 10%)); } } - &.primary, &.btn-primary { + &.primary, &.btn-primary, &.btn-success { background-color: @colortheme_alertify-primary; color: @colortheme_alertify-primary-text; border-color: @colortheme_alertify-primary-border; font-weight: bold; - &:hover, &:active, &:focus { + &:hover, &:not(:disabled):active, &:focus { + color: @colortheme_alertify-primary-text; + border-color: @colortheme_alertify-primary-border; background-color: contrast(@colortheme_modal-bg, darken(@colortheme_alertify-primary, 10%), lighten(@colortheme_alertify-primary, 10%)); } } @@ -165,7 +188,9 @@ border-color: @cryptpad_text_col; color: @cryptpad_text_col; background-color: transparent; - &:hover, &:hover, &:focus { + &:hover, &:not(:disabled):active, &:focus { + border-color: @cryptpad_text_col; + color: @cryptpad_text_col; background-color: fade(@cryptpad_text_col, 25%); } } @@ -173,7 +198,9 @@ &.cancel, &.btn-cancel { border-color: @colortheme_alertify-cancel-border; color: @colortheme_alertify-cancel-border; - &:hover, &:hover, &:focus { + &:hover, &:not(:disabled):active, &:focus { + border-color: @colortheme_alertify-cancel-border; + color: @colortheme_alertify-cancel-border; background-color: fade(@colortheme_alertify-cancel-border, 25%); } } @@ -185,7 +212,7 @@ &:focus { //border: 1px dotted @alertify-base; - box-shadow: 0px 0px 5px @colortheme_alertify-primary; + box-shadow: 0px 0px 5px @colortheme_alertify-primary !important; outline: none; } &::-moz-focus-inner { @@ -202,5 +229,4 @@ } } } - } diff --git a/customize.dist/src/less2/include/markdown.less b/customize.dist/src/less2/include/markdown.less index 23eb056b0..37fd4fce8 100644 --- a/customize.dist/src/less2/include/markdown.less +++ b/customize.dist/src/less2/include/markdown.less @@ -64,6 +64,57 @@ } } +.mediatag_cryptpad() { + media-tag { + &:empty { + display: none !important; + } + cursor: pointer; + * { + max-width: 100%; + } + iframe[src$=".pdf"] { + width: 100%; + height: 80vh; + max-height: 90vh; + } + button.mediatag-download-btn { + flex-flow: column; + & > span { + display: flex; + line-height: 1.5; + align-items: center; + &.mediatag-download-name b { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + button.btn-default { + display: inline-flex; + max-width: 250px; + min-height: 38px; + justify-content: center; + .fa { + margin-right: 5px; + } + b { + margin-left: 5px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + } + media-tag:empty { + width: 100px; + height: 100px; + display: inline-block; + border: 1px solid #BBB; + } +} + .markdown_cryptpad() { word-wrap: break-word; @@ -84,23 +135,8 @@ margin-top: 4px; } } - media-tag { - cursor: pointer; - * { - max-width: 100%; - } - iframe[src$=".pdf"] { - width: 100%; - height: 80vh; - max-height: 90vh; - } - } - media-tag:empty { - width: 100px; - height: 100px; - display: inline-block; - border: 1px solid #BBB; - } + + .mediatag_cryptpad(); pre.markmap { border: 1px solid #ddd; diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less index 27eb233da..ff40729a6 100644 --- a/customize.dist/src/less2/include/modals-ui-elements.less +++ b/customize.dist/src/less2/include/modals-ui-elements.less @@ -1,6 +1,7 @@ @import (reference) "./colortheme-all.less"; @import (reference) "./variables.less"; @import (reference) "./browser.less"; +@import (reference) "./markdown.less"; .modals-ui-elements_main() { --LessLoader_require: LessLoader_currentFile(); @@ -214,6 +215,7 @@ flex: 1; min-width: 0; overflow: auto; + .mediatag_cryptpad(); media-tag { & > * { max-width: 100%; diff --git a/customize.dist/src/less2/include/sidebar-layout.less b/customize.dist/src/less2/include/sidebar-layout.less index ace7350df..4273b0b9a 100644 --- a/customize.dist/src/less2/include/sidebar-layout.less +++ b/customize.dist/src/less2/include/sidebar-layout.less @@ -118,7 +118,7 @@ //border-radius: 0 0.25em 0.25em 0; //border: 1px solid #adadad; border-left: 0px; - height: @variables_input-height; + height: 40px; margin: 0 !important; } } diff --git a/customize.dist/src/less2/include/support.less b/customize.dist/src/less2/include/support.less index d83746b51..105599ada 100644 --- a/customize.dist/src/less2/include/support.less +++ b/customize.dist/src/less2/include/support.less @@ -78,7 +78,7 @@ } &.cp-support-list-closed { .cp-support-list-actions { - display: block !important; + display: flex !important; .cp-support-answer, .cp-support-close { display: none; } diff --git a/customize.dist/src/less2/pages/page-login.less b/customize.dist/src/less2/pages/page-login.less index 1cc2fcffc..98eafe483 100644 --- a/customize.dist/src/less2/pages/page-login.less +++ b/customize.dist/src/less2/pages/page-login.less @@ -2,10 +2,12 @@ @import (reference) "../include/colortheme-all.less"; @import (reference) "../include/alertify.less"; @import (reference) "../include/checkmark.less"; +@import (reference) "../include/forms.less"; &.cp-page-login { .infopages_main(); .alertify_main(); + .forms_main(); .checkmark_main(20px); .form-group { diff --git a/docs/cryptpad.service b/docs/cryptpad.service index eee8b2af5..43d8652f6 100644 --- a/docs/cryptpad.service +++ b/docs/cryptpad.service @@ -17,7 +17,7 @@ SyslogIdentifier=cryptpad User=cryptpad Group=cryptpad # modify to match your working directory -Environment='PWD="/home/cryptpad/cryptpad/cryptpad"' +Environment='PWD="/home/cryptpad/cryptpad"' # systemd sets the open file limit to 4000 unless you override it # cryptpad stores its data with the filesystem, so you should increase this to match the value of `ulimit -n` diff --git a/docs/example.nginx.conf b/docs/example.nginx.conf index 8319c657b..2c677436b 100644 --- a/docs/example.nginx.conf +++ b/docs/example.nginx.conf @@ -32,6 +32,9 @@ server { server_name your-main-domain.com your-sandbox-domain.com; # You'll need to Set the path to your certificates and keys here + # IMPORTANT: this config is intended to serve assets for at least two domains + # (your main domain and your sandbox domain). As such, you'll need to generate a single SSL certificate + # that includes both domains in order for things to work as expected. ssl_certificate /home/cryptpad/.acme.sh/your-main-domain.com/fullchain.cer; ssl_certificate_key /home/cryptpad/.acme.sh/your-main-domain.com/your-main-domain.com.key; ssl_trusted_certificate /home/cryptpad/.acme.sh/your-main-domain.com/ca.cer; @@ -177,8 +180,8 @@ server { add_header Cache-Control max-age=31536000; add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; - add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range'; + add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length'; + add_header 'Access-Control-Expose-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length'; try_files $uri =404; } diff --git a/lib/commands/admin-rpc.js b/lib/commands/admin-rpc.js index d7f22825d..595b4c6dc 100644 --- a/lib/commands/admin-rpc.js +++ b/lib/commands/admin-rpc.js @@ -56,6 +56,11 @@ var getCacheStats = function (env, server, cb) { }); }; +// CryptPad_AsyncStore.rpc.send('ADMIN', ['GET_WORKER_PROFILES'], console.log) +var getWorkerProfiles = function (Env, Server, cb) { + cb(void 0, Env.commandTimers); +}; + var getActiveSessions = function (Env, Server, cb) { var stats = Server.getSessionStats(); cb(void 0, [ @@ -155,9 +160,19 @@ var archiveDocument = function (Env, Server, cb, data) { switch (id.length) { case 32: // TODO disconnect users from active sessions - return void Env.msgStore.archiveChannel(id, cb); + return void Env.msgStore.archiveChannel(id, Util.both(cb, function (err) { + Env.Log.info("ARCHIVAL_CHANNEL_BY_ADMIN_RPC", { + channelId: id, + status: err? String(err): "SUCCESS", + }); + })); case 48: - return void Env.blobStore.archive.blob(id, cb); + return void Env.blobStore.archive.blob(id, Util.both(cb, function (err) { + Env.Log.info("ARCHIVAL_BLOB_BY_ADMIN_RPC", { + id: id, + status: err? String(err): "SUCCESS", + }); + })); default: return void cb("INVALID_ID_LENGTH"); } @@ -167,12 +182,30 @@ var archiveDocument = function (Env, Server, cb, data) { // Env.blobStore.archive.proof(userSafeKey, blobId, cb) }; -var restoreArchivedDocument = function (Env, Server, cb) { - // Env.msgStore.restoreArchivedChannel(channelName, cb) - // Env.blobStore.restore.blob(blobId, cb) - // Env.blobStore.restore.proof(userSafekey, blobId, cb) +var restoreArchivedDocument = function (Env, Server, cb, data) { + var id = Array.isArray(data) && data[1]; + if (typeof(id) !== 'string' || id.length < 32) { return void cb("EINVAL"); } - cb("NOT_IMPLEMENTED"); + switch (id.length) { + case 32: + return void Env.msgStore.restoreArchivedChannel(id, Util.both(cb, function (err) { + Env.Log.info("RESTORATION_CHANNEL_BY_ADMIN_RPC", { + id: id, + status: err? String(err): 'SUCCESS', + }); + })); + case 48: + // FIXME this does not yet restore blob ownership + // Env.blobStore.restore.proof(userSafekey, id, cb) + return void Env.blobStore.restore.blob(id, Util.both(cb, function (err) { + Env.Log.info("RESTORATION_BLOB_BY_ADMIN_RPC", { + id: id, + status: err? String(err): 'SUCCESS', + }); + })); + default: + return void cb("INVALID_ID_LENGTH"); + } }; // CryptPad_AsyncStore.rpc.send('ADMIN', ['CLEAR_CACHED_CHANNEL_INDEX', documentID], console.log) @@ -315,6 +348,7 @@ var commands = { INSTANCE_STATUS: instanceStatus, GET_LIMITS: getLimits, SET_LAST_EVICTION: setLastEviction, + GET_WORKER_PROFILES: getWorkerProfiles, }; Admin.command = function (Env, safeKey, data, _cb, Server) { diff --git a/lib/commands/metadata.js b/lib/commands/metadata.js index 3d20f36e6..896c89f31 100644 --- a/lib/commands/metadata.js +++ b/lib/commands/metadata.js @@ -13,11 +13,19 @@ Data.getMetadataRaw = function (Env, channel /* channelName */, _cb) { var cached = Env.metadata_cache[channel]; if (HK.isMetadataMessage(cached)) { + Env.checkCache(channel); return void cb(void 0, cached); } Env.batchMetadata(channel, cb, function (done) { - Env.computeMetadata(channel, done); + Env.computeMetadata(channel, function (err, meta) { + if (!err && HK.isMetadataMessage(meta)) { + Env.metadata_cache[channel] = meta; + // clear metadata after a delay if nobody has joined the channel within 30s + Env.checkCache(channel); + } + done(err, meta); + }); }); }; diff --git a/lib/commands/upload.js b/lib/commands/upload.js index 7286caa93..346262716 100644 --- a/lib/commands/upload.js +++ b/lib/commands/upload.js @@ -75,21 +75,9 @@ Upload.upload = function (Env, safeKey, chunk, cb) { Env.blobStore.upload(safeKey, chunk, cb); }; -var reportStatus = function (Env, label, safeKey, err, id) { - var data = { - safeKey: safeKey, - err: err && err.message || err, - id: id, - }; - var method = err? 'error': 'info'; - Env.Log[method](label, data); -}; - Upload.complete = function (Env, safeKey, arg, cb) { - Env.blobStore.complete(safeKey, arg, function (err, id) { - reportStatus(Env, 'UPLOAD_COMPLETE', safeKey, err, id); - cb(err, id); - }); + Env.blobStore.closeBlobstage(safeKey); + Env.completeUpload(safeKey, arg, false, cb); }; Upload.cancel = function (Env, safeKey, arg, cb) { @@ -97,9 +85,9 @@ Upload.cancel = function (Env, safeKey, arg, cb) { }; Upload.complete_owned = function (Env, safeKey, arg, cb) { - Env.blobStore.completeOwned(safeKey, arg, function (err, id) { - reportStatus(Env, 'UPLOAD_COMPLETE_OWNED', safeKey, err, id); - cb(err, id); - }); + Env.blobStore.closeBlobstage(safeKey); + var user = Core.getSession(Env.Sessions, safeKey); + var size = user.pendingUploadSize; + Env.completeUpload(safeKey, arg, true, size, cb); }; diff --git a/lib/env.js b/lib/env.js index b1fc6680b..322f629c6 100644 --- a/lib/env.js +++ b/lib/env.js @@ -42,6 +42,8 @@ module.exports.create = function (config) { metadata_cache: {}, channel_cache: {}, + cache_checks: {}, + queueStorage: WriteQueue(), queueDeletes: WriteQueue(), queueValidation: WriteQueue(), @@ -94,6 +96,7 @@ module.exports.create = function (config) { disableIntegratedEviction: config.disableIntegratedEviction || false, lastEviction: +new Date(), evictionReport: {}, + commandTimers: {}, }; (function () { @@ -116,8 +119,14 @@ module.exports.create = function (config) { } }()); - - + Env.checkCache = function (channel) { + var f = Env.cache_checks[channel] || Util.throttle(function () { + delete Env.cache_checks[channel]; + if (Env.channel_cache[channel]) { return; } + delete Env.metadata_cache[channel]; + }, 30000); + f(); + }; (function () { var custom = config.customLimits; diff --git a/lib/hk-util.js b/lib/hk-util.js index 14263e481..495f4ff81 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -419,9 +419,11 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { // fall through to the next block if the offset of the hash in question is not in memory if (lastKnownHash && typeof(lkh) !== "number") { return; } + // If we have a lastKnownHash or we didn't ask for one, we don't need the next blocks + waitFor.abort(); + // Since last 2 checkpoints if (!lastKnownHash) { - waitFor.abort(); // Less than 2 checkpoints in the history: return everything if (index.cpIndex.length < 2) { return void cb(null, 0); } // Otherwise return the second last checkpoint's index @@ -436,7 +438,15 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, _cb) => { to reconcile their differences. */ } - offset = lkh; + // If our lastKnownHash is older than the 2nd to last checkpoint, send + // EUNKNOWN to tell the user to empty their cache + if (lkh && index.cpIndex.length >= 2 && lkh < index.cpIndex[0].offset) { + waitFor.abort(); + return void cb(new Error('EUNKNOWN')); + } + + // Otherwise use our lastKnownHash + cb(null, lkh); })); }).nThen((w) => { // skip past this block if the offset is anything other than -1 diff --git a/lib/storage/blob.js b/lib/storage/blob.js index dfbc802b4..044eeaeaa 100644 --- a/lib/storage/blob.js +++ b/lib/storage/blob.js @@ -139,6 +139,15 @@ var upload = function (Env, safeKey, content, cb) { } }; +var closeBlobstage = function (Env, safeKey) { + var session = Env.getSession(safeKey); + if (!(session && session.blobstage && typeof(session.blobstage.close) === 'function')) { + return; + } + session.blobstage.close(); + delete session.blobstage; +}; + // upload_cancel var upload_cancel = function (Env, safeKey, fileSize, cb) { var session = Env.getSession(safeKey); @@ -159,27 +168,22 @@ var upload_cancel = function (Env, safeKey, fileSize, cb) { // upload_complete var upload_complete = function (Env, safeKey, id, cb) { - var session = Env.getSession(safeKey); - - if (session.blobstage && session.blobstage.close) { - session.blobstage.close(); - delete session.blobstage; - } + closeBlobstage(Env, safeKey); var oldPath = makeStagePath(Env, safeKey); var newPath = makeBlobPath(Env, id); nThen(function (w) { // make sure the path to your final location exists - Fse.mkdirp(Path.dirname(newPath), function (e) { + Fse.mkdirp(Path.dirname(newPath), w(function (e) { if (e) { w.abort(); return void cb('RENAME_ERR'); } - }); + })); }).nThen(function (w) { // make sure there's not already something in that exact location - isFile(newPath, function (e, yes) { + isFile(newPath, w(function (e, yes) { if (e) { w.abort(); return void cb(e); @@ -188,8 +192,8 @@ var upload_complete = function (Env, safeKey, id, cb) { w.abort(); return void cb('RENAME_ERR'); } - cb(void 0, newPath, id); - }); + cb(void 0, id); + })); }).nThen(function () { // finally, move the old file to the new path // FIXME we could just move and handle the EEXISTS instead of the above block @@ -217,15 +221,7 @@ var tryId = function (path, cb) { // owned_upload_complete var owned_upload_complete = function (Env, safeKey, id, cb) { - var session = Env.getSession(safeKey); - - // the file has already been uploaded to the staging area - // close the pending writestream - if (session.blobstage && session.blobstage.close) { - session.blobstage.close(); - delete session.blobstage; - } - + closeBlobstage(Env, safeKey); if (!isValidId(id)) { return void cb('EINVAL_ID'); } @@ -582,6 +578,9 @@ BlobStore.create = function (config, _cb) { }, }, + closeBlobstage: function (safeKey) { + closeBlobstage(Env, safeKey); + }, complete: function (safeKey, id, _cb) { var cb = Util.once(Util.mkAsync(_cb)); if (!isValidSafeKey(safeKey)) { return void cb('INVALID_SAFEKEY'); } diff --git a/lib/storage/file.js b/lib/storage/file.js index b1ccfde0f..d890cb0b9 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -1,6 +1,6 @@ /*@flow*/ /* jshint esversion: 6 */ -/* global Buffer */ +/* globals Buffer */ var Fs = require("fs"); var Fse = require("fs-extra"); var Path = require("path"); @@ -66,6 +66,10 @@ var mkTempPath = function (env, channelId) { return mkPath(env, channelId) + '.temp'; }; +var mkOffsetPath = function (env, channelId) { + return mkPath(env, channelId) + '.offset'; +}; + // pass in the path so we can reuse the same function for archived files var channelExists = function (filepath, cb) { Fs.stat(filepath, function (err, stat) { @@ -131,7 +135,9 @@ const readMessagesBin = (env, id, start, msgHandler, cb) => { const collector = createIdleStreamCollector(stream); const handleMessageAndKeepStreamAlive = Util.both(msgHandler, collector.keepAlive); const done = Util.both(cb, collector); - return void readFileBin(stream, handleMessageAndKeepStreamAlive, done); + return void readFileBin(stream, handleMessageAndKeepStreamAlive, done, { + offset: start, + }); }; // reads classic metadata from a channel log and aborts @@ -190,6 +196,37 @@ var closeChannel = function (env, channelName, cb) { } }; +var clearOffset = function (env, channelId, cb) { + var path = mkOffsetPath(env, channelId); + // we should always be able to recover from invalid offsets, so failure to delete them + // is not catastrophic. Anything calling this function can optionally ignore errors it might report + Fs.unlink(path, cb); +}; + +var writeOffset = function (env, channelId, data, cb) { + var path = mkOffsetPath(env, channelId); + var s_data; + try { + s_data = JSON.stringify(data); + } catch (err) { + return void cb(err); + } + Fs.writeFile(path, s_data, cb); +}; + +var getOffset = function (env, channelId, cb) { + var path = mkOffsetPath(env, channelId); + Fs.readFile(path, function (err, content) { + if (err) { return void cb(err); } + try { + var json = JSON.parse(content); + cb(void 0, json); + } catch (err2) { + cb(err2); + } + }); +}; + // truncates a file to the end of its metadata line // TODO write the metadata in a dedicated file var clearChannel = function (env, channelId, _cb) { @@ -213,6 +250,7 @@ var clearChannel = function (env, channelId, _cb) { cb(); }); }); + clearOffset(env, channelId, function () {}); }); }; @@ -389,6 +427,7 @@ var removeChannel = function (env, channelName, cb) { CB(labelError("E_METADATA_REMOVAL", err)); } })); + clearOffset(env, channelName, w()); }).nThen(function () { if (errors === 2) { return void CB(labelError('E_REMOVE_CHANNEL', new Error("ENOENT"))); @@ -604,6 +643,8 @@ var archiveChannel = function (env, channelName, cb) { return void cb(err); } })); + }).nThen(function (w) { + clearOffset(env, channelName, w()); }).nThen(function (w) { // archive the dedicated metadata channel var metadataPath = mkMetadataPath(env, channelName); @@ -861,6 +902,7 @@ var trimChannel = function (env, channelName, hash, _cb) { } })); }).nThen(function (w) { + clearOffset(env, channelName, w()); cleanUp(w(function (err) { if (err) { w.abort(); @@ -1177,6 +1219,25 @@ module.exports.create = function (conf, _cb) { }); }, + // OFFSETS +// these exist strictly as an optimization +// you can always remove them without data loss + clearOffset: function (channelName, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); + if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } + clearOffset(env, channelName, cb); + }, + writeOffset: function (channelName, data, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); + if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } + writeOffset(env, channelName, data, cb); + }, + getOffset: function (channelName, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); + if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); } + getOffset(env, channelName, cb); + }, + // METADATA METHODS // fetch the metadata for a channel getChannelMetadata: function (channelName, cb) { diff --git a/lib/stream-file.js b/lib/stream-file.js index c3130365b..a164ceffc 100644 --- a/lib/stream-file.js +++ b/lib/stream-file.js @@ -44,8 +44,8 @@ const mkBufferSplit = () => { // return a streaming function which transforms buffers into objects // containing the buffer and the offset from the start of the stream -const mkOffsetCounter = () => { - let offset = 0; +const mkOffsetCounter = (offset) => { + offset = offset || 0; return Pull.map((buff) => { const out = { offset: offset, buff: buff }; // +1 for the eaten newline @@ -59,13 +59,14 @@ const mkOffsetCounter = () => { // that this function has a lower memory profile than our classic method // of reading logs line by line. // it also allows the handler to abort reading at any time -Stream.readFileBin = (stream, msgHandler, cb) => { +Stream.readFileBin = (stream, msgHandler, cb, opt) => { + opt = opt || {}; //const stream = Fs.createReadStream(path, { start: start }); let keepReading = true; Pull( ToPull.read(stream), mkBufferSplit(), - mkOffsetCounter(), + mkOffsetCounter(opt.offset), Pull.asyncMap((data, moreCb) => { msgHandler(data, moreCb, () => { try { diff --git a/lib/workers/db-worker.js b/lib/workers/db-worker.js index 42c75fdca..5750ff7ac 100644 --- a/lib/workers/db-worker.js +++ b/lib/workers/db-worker.js @@ -1,5 +1,5 @@ /* jshint esversion: 6 */ -/* global process */ +/* globals process, Buffer */ const HK = require("../hk-util"); const Store = require("../storage/file"); @@ -30,6 +30,11 @@ Logger.levels.forEach(function (level) { }; }); +var DETAIL = 1000; +var round = function (n) { + return Math.floor(n * DETAIL) / DETAIL; +}; + var ready = false; var store; var pinStore; @@ -114,14 +119,15 @@ const init = function (config, _cb) { * including the initial metadata line, if it exists */ -const computeIndex = function (data, cb) { - if (!data || !data.channel) { - return void cb('E_NO_CHANNEL'); - } - const channelName = data.channel; +const OPEN_CURLY_BRACE = Buffer.from('{'); +const CHECKPOINT_PREFIX = Buffer.from('cp|'); +const isValidOffsetNumber = function (n) { + return typeof(n) === 'number' && n >= 0; +}; - const cpIndex = []; +const computeIndexFromOffset = function (channelName, offset, cb) { + let cpIndex = []; let messageBuf = []; let i = 0; @@ -129,27 +135,42 @@ const computeIndex = function (data, cb) { const offsetByHash = {}; let offsetCount = 0; - let size = 0; + let size = offset || 0; + var start = offset || 0; + let unconventional = false; + nThen(function (w) { // iterate over all messages in the channel log // old channels can contain metadata as the first message of the log // skip over metadata as that is handled elsewhere // otherwise index important messages in the log - store.readMessagesBin(channelName, 0, (msgObj, readMore) => { + store.readMessagesBin(channelName, start, (msgObj, readMore, abort) => { let msg; // keep an eye out for the metadata line if you haven't already seen it // but only check for metadata on the first line - if (!i && msgObj.buff.indexOf('{') === 0) { - i++; // always increment the message counter + if (i) { + // fall through intentionally because the following blocks are invalid + // for all but the first message + } else if (msgObj.buff.includes(OPEN_CURLY_BRACE)) { msg = HK.tryParse(Env, msgObj.buff.toString('utf8')); - if (typeof msg === "undefined") { return readMore(); } + if (typeof msg === "undefined") { + i++; // always increment the message counter + return readMore(); + } // validate that the current line really is metadata before storing it as such // skip this, as you already have metadata... - if (HK.isMetadataMessage(msg)) { return readMore(); } + if (HK.isMetadataMessage(msg)) { + i++; // always increment the message counter + return readMore(); + } + } else if (!(msg = HK.tryParse(Env, msgObj.buff.toString('utf8')))) { + w.abort(); + abort(); + return CB("OFFSET_ERROR"); } i++; - if (msgObj.buff.indexOf('cp|') > -1) { + if (msgObj.buff.includes(CHECKPOINT_PREFIX)) { msg = msg || HK.tryParse(Env, msgObj.buff.toString('utf8')); if (typeof msg === "undefined") { return readMore(); } // cache the offsets of checkpoints if they can be parsed @@ -164,6 +185,7 @@ const computeIndex = function (data, cb) { } } else if (messageBuf.length > 100 && cpIndex.length === 0) { // take the last 50 messages + unconventional = true; messageBuf = messageBuf.slice(-50); } // if it's not metadata or a checkpoint then it should be a regular message @@ -192,11 +214,40 @@ const computeIndex = function (data, cb) { size = msgObj.offset + msgObj.buff.length + 1; }); })); + }).nThen(function (w) { + cpIndex = HK.sliceCpIndex(cpIndex, i); + + var new_start; + if (cpIndex.length) { + new_start = cpIndex[0].offset; + } else if (unconventional && messageBuf.length && isValidOffsetNumber(messageBuf[0].offset)) { + new_start = messageBuf[0].offset; + } + + if (new_start === start) { return; } + if (!isValidOffsetNumber(new_start)) { return; } + + // store the offset of the earliest relevant line so that you can start from there next time... + store.writeOffset(channelName, { + start: new_start, + created: +new Date(), + }, w(function () { + var diff = new_start - start; + Env.Log.info('WORKER_OFFSET_UPDATE', { + channel: channelName, + start: start, + startMB: round(start / 1024 / 1024), + update: new_start, + updateMB: round(new_start / 1024 / 1024), + diff: diff, + diffMB: round(diff / 1024 / 1024), + }); + })); }).nThen(function () { // return the computed index CB(null, { // Only keep the checkpoints included in the last 100 messages - cpIndex: HK.sliceCpIndex(cpIndex, i), + cpIndex: cpIndex, offsetByHash: offsetByHash, offsets: offsetCount, size: size, @@ -206,6 +257,47 @@ const computeIndex = function (data, cb) { }); }; +const computeIndex = function (data, cb) { + if (!data || !data.channel) { + return void cb('E_NO_CHANNEL'); + } + + const channelName = data.channel; + const CB = Util.once(cb); + + var start = 0; + nThen(function (w) { + store.getOffset(channelName, w(function (err, obj) { + if (err) { return; } + if (obj && typeof(obj.start) === 'number' && obj.start > 0) { + start = obj.start; + Env.Log.verbose('WORKER_OFFSET_RECOVERY', { + channel: channelName, + start: start, + startMB: round(start / 1024 / 1024), + }); + } + })); + }).nThen(function (w) { + computeIndexFromOffset(channelName, start, w(function (err, index) { + if (err === 'OFFSET_ERROR') { + return Env.Log.error("WORKER_OFFSET_ERROR", { + channel: channelName, + }); + } + w.abort(); + CB(err, index); + })); + }).nThen(function (w) { + // if you're here there was an OFFSET_ERROR.. + // first remove the offset that caused the problem to begin with + store.clearOffset(channelName, w()); + }).nThen(function () { + // now get the history as though it were the first time + computeIndexFromOffset(channelName, 0, CB); + }); +}; + const computeMetadata = function (data, cb) { const ref = {}; const lineHandler = Meta.createLineHandler(ref, Env.Log.error); @@ -457,6 +549,41 @@ const evictInactive = function (data, cb) { Eviction(Env, cb); }; +var reportStatus = function (Env, label, safeKey, err, id, size) { + var data = { + safeKey: safeKey, + err: err && err.message || err, + id: id, + size: size, + sizeMB: round((size || 0) / 1024 / 1024), + }; + var method = err? 'error': 'info'; + Env.Log[method](label, data); +}; + +const completeUpload = function (data, cb) { + if (!data) { return void cb('INVALID_ARGS'); } + var owned = data.owned; + var safeKey = data.safeKey; + var arg = data.arg; + var size = data.size; + + var method; + var label; + if (owned) { + method = 'completeOwned'; + label = 'UPLOAD_COMPLETE_OWNED'; + } else { + method = 'complete'; + label = 'UPLOAD_COMPLETE'; + } + + Env.blobStore[method](safeKey, arg, function (err, id) { + reportStatus(Env, label, safeKey, err, id, size); + cb(err, id); + }); +}; + const COMMANDS = { COMPUTE_INDEX: computeIndex, COMPUTE_METADATA: computeMetadata, @@ -471,6 +598,7 @@ const COMMANDS = { RUN_TASKS: runTasks, WRITE_TASK: writeTask, EVICT_INACTIVE: evictInactive, + COMPLETE_UPLOAD: completeUpload, }; COMMANDS.INLINE = function (data, cb) { @@ -568,7 +696,7 @@ process.on('message', function (data) { const cb = function (err, value) { process.send({ - error: err, + error: Util.serializeError(err), txid: data.txid, pid: data.pid, value: value, @@ -577,7 +705,7 @@ process.on('message', function (data) { if (!ready) { return void init(data.config, function (err) { - if (err) { return void cb(err); } + if (err) { return void cb(Util.serializeError(err)); } ready = true; cb(); }); diff --git a/lib/workers/index.js b/lib/workers/index.js index 6e9f57e88..25c18d947 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -9,10 +9,19 @@ const PID = process.pid; const DB_PATH = 'lib/workers/db-worker'; const MAX_JOBS = 16; +const DEFAULT_QUERY_TIMEOUT = 60000 * 15; // increased from three to fifteen minutes because queries for very large files were taking as long as seven minutes Workers.initialize = function (Env, config, _cb) { var cb = Util.once(Util.mkAsync(_cb)); + var incrementTime = function (command, start) { + if (!command) { return; } + var end = +new Date(); + var T = Env.commandTimers; + var diff = (end - start); + T[command] = (T[command] || 0) + (diff / 1000); + }; + const workers = []; const response = Util.response(function (errLabel, info) { @@ -111,8 +120,11 @@ Workers.initialize = function (Env, config, _cb) { } const txid = guid(); + var start = +new Date(); var cb = Util.once(Util.mkAsync(Util.both(_cb, function (err /*, value */) { + incrementTime(msg && msg.command, start); if (err !== 'TIMEOUT') { return; } + Log.debug("WORKER_TIMEOUT_CAUSE", msg); // in the event of a timeout the user will receive an error // but the state used to resend a query in the event of a worker crash // won't be cleared. This also leaks a slot that could be used to keep @@ -132,7 +144,7 @@ Workers.initialize = function (Env, config, _cb) { state.tasks[txid] = msg; // default to timing out affter 180s if no explicit timeout is passed - var timeout = typeof(opt.timeout) !== 'undefined'? opt.timeout: 180000; + var timeout = typeof(opt.timeout) !== 'undefined'? opt.timeout: DEFAULT_QUERY_TIMEOUT; response.expect(txid, cb, timeout); state.worker.send(msg); }; @@ -422,6 +434,16 @@ Workers.initialize = function (Env, config, _cb) { }, cb); }; + Env.completeUpload = function (safeKey, arg, owned, size, cb) { + sendCommand({ + command: "COMPLETE_UPLOAD", + owned: owned, // Boolean + safeKey: safeKey, // String (public key) + arg: arg, // String (file id) + size: size, // Number || undefined + }, cb); + }; + cb(void 0); }); }; diff --git a/package-lock.json b/package-lock.json index 2c31e74bf..ef1573c7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cryptpad", - "version": "3.24.0", + "version": "3.25.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -389,16 +389,16 @@ "optional": true }, "chainpad-crypto": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.4.tgz", - "integrity": "sha512-fWbVyeAv35vf/dkkQaefASlJcEfpEvfRI23Mtn+/TBBry7+LYNuJMXJiovVY35pfyw2+trKh1Py5Asg9vrmaVg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.5.tgz", + "integrity": "sha512-K9vRsAspuX+uU1goXPz0CawpLIaOHq+1JP3WfDLqaz67LbCX/MLIUt9aMcSeIJcwZ9uMpqnbMGRktyVPoz6MCA==", "requires": { - "tweetnacl": "git://github.com/dchest/tweetnacl-js.git#v0.12.2" + "tweetnacl": "git+https://github.com/dchest/tweetnacl-js.git#v0.12.2" }, "dependencies": { "tweetnacl": { - "version": "git://github.com/dchest/tweetnacl-js.git#8a21381d696acdc4e99c9f706f1ad23285795f79", - "from": "git://github.com/dchest/tweetnacl-js.git#v0.12.2" + "version": "git+https://github.com/dchest/tweetnacl-js.git#8a21381d696acdc4e99c9f706f1ad23285795f79", + "from": "git+https://github.com/dchest/tweetnacl-js.git#v0.12.2" } } }, diff --git a/package.json b/package.json index 5c452b864..24041cb7b 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "cryptpad", "description": "realtime collaborative visual editor with zero knowlege server", - "version": "3.24.0", + "version": "3.25.0", "license": "AGPL-3.0+", "repository": { "type": "git", - "url": "git://github.com/xwiki-labs/cryptpad.git" + "url": "git+https://github.com/xwiki-labs/cryptpad.git" }, "funding": { "type": "opencollective", @@ -13,7 +13,7 @@ }, "dependencies": { "@mcrowe/minibloom": "^0.2.0", - "chainpad-crypto": "^0.2.2", + "chainpad-crypto": "^0.2.5", "chainpad-server": "^4.0.9", "express": "~4.16.0", "fs-extra": "^7.0.0", diff --git a/readme.md b/readme.md index 833de540f..2213b9e27 100644 --- a/readme.md +++ b/readme.md @@ -65,7 +65,7 @@ CryptPad is actively developed by a team at [XWiki SAS](https://www.xwiki.com), # Contributing -We love Open Source and we love contribution. Learn more about [contributing](https://github.com/xwiki-labs/cryptpad/wiki/Contributor-overview). +We love Open Source and we love contribution. Learn more about [contributing](https://docs.cryptpad.fr/en/how_to_contribute.html). If you have any questions or comments, or if you're interested in contributing to Cryptpad, come say hi on IRC, `#cryptpad` on Freenode. diff --git a/server.js b/server.js index 60247f47a..3869af509 100644 --- a/server.js +++ b/server.js @@ -136,6 +136,20 @@ app.head(/^\/common\/feedback\.html/, function (req, res, next) { }); }()); +app.use('/blob', function (req, res, next) { + if (req.method === 'HEAD') { + Express.static(Path.join(__dirname, (config.blobPath || './blob')), { + setHeaders: function (res, path, stat) { + res.set('Access-Control-Allow-Origin', '*'); + res.set('Access-Control-Allow-Headers', 'Content-Length'); + res.set('Access-Control-Expose-Headers', 'Content-Length'); + } + })(req, res, next); + return; + } + next(); +}); + app.use(function (req, res, next) { if (req.method === 'OPTIONS' && /\/blob\//.test(req.url)) { res.setHeader('Access-Control-Allow-Origin', '*'); @@ -202,6 +216,7 @@ var serveConfig = (function () { adminKeys: Env.admins, inactiveTime: Env.inactiveTime, supportMailbox: Env.supportMailbox, + defaultStorageLimit: Env.defaultStorageLimit, maxUploadSize: Env.maxUploadSize, premiumUploadSize: Env.premiumUploadSize, }, null, '\t'), diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 1f90fbfff..d19e6b4d7 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -27,6 +27,9 @@ margin-top: 5px; } } + .cp-admin-setlimit-form + button { + margin-top: 5px !important; + } .cp-admin-getlimits { code { cursor: pointer; @@ -58,14 +61,62 @@ .cp-support-container { display: flex; - flex-flow: column; + flex-wrap: wrap; + .cp-support-column { + min-width: 700px; + flex: 1 0 50%; + h1 { + display: flex; + align-items: center; + button { + margin-left: 50px !important; + } + } + .cp-support-count { + margin-left: 10px; + } + &.cp-support-column-collapsed { + .cp-support-list-ticket { + display: none; + } + } + } } .cp-support-list-actions { margin: 10px 0px 10px 2px; } + .cp-support-list-ticket { + h2 { + font-size: 1.5rem; + display: flex; + justify-content: space-between; + align-items: top; + .cp-support-title-buttons { + flex-shrink: 0; + } + } + .cp-support-collapsed { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + color: #666; + .cp-support-ispremium { + padding: 0 5px; + color: @colortheme_cp-red; + background-color: lighten(@colortheme_cp-red, 25%); + } + } + } + .cp-support-list-ticket:not(.cp-support-list-closed) { + .cp-support-list-actions { + .cp-button-confirm, .cp-support-close { + order: 20; + margin-left: auto !important; + } + } .cp-support-list-message { &:last-child:not(.cp-support-fromadmin) { color: @colortheme_cp-red; @@ -85,6 +136,28 @@ } } } + .cp-support-list-ticket:not(.cp-support-open) { + span:first-child { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + & > :not(h2):not(.cp-support-collapsed) { + display: none; + } + .cp-support-collapse { + display: none; + } + cursor: pointer; + } + .cp-support-list-ticket.cp-support-open { + .cp-support-collapsed { + display: none; + } + .cp-support-expand { + display: none; + } + } .cp-support-fromadmin { color: @colortheme_logo-2; @@ -93,5 +166,15 @@ color: @colortheme_logo-2; } } + + input.cp-admin-inval { + border-color: red !important; + } + .cp-admin-nopassword { + .cp-admin-pw { + display: none !important; + } + } + } diff --git a/www/admin/inner.js b/www/admin/inner.js index db5afca54..755b60ddc 100644 --- a/www/admin/inner.js +++ b/www/admin/inner.js @@ -43,6 +43,8 @@ define([ 'general': [ 'cp-admin-flush-cache', 'cp-admin-update-limit', + 'cp-admin-archive', + 'cp-admin-unarchive', // 'cp-admin-registration', ], 'quota': [ @@ -107,6 +109,129 @@ define([ }); return $div; }; + + var archiveForm = function (archive, $div, $button) { + var label = h('label', { for: 'cp-admin-archive' }, Messages.admin_archiveInput); + var input = h('input#cp-admin-archive', { + type: 'text' + }); + + var label2 = h('label.cp-admin-pw', { + for: 'cp-admin-archive-pw' + }, Messages.admin_archiveInput2); + var input2 = UI.passwordInput({ + id: 'cp-admin-archive-pw', + placeholder: Messages.login_password + }); + var $pw = $(input2); + $pw.addClass('cp-admin-pw'); + var $pwInput = $pw.find('input'); + + + $button.before(h('div.cp-admin-setlimit-form', [ + label, + input, + label2, + input2 + ])); + + $div.addClass('cp-admin-nopassword'); + + var parsed; + var $input = $(input).on('keypress change paste', function () { + setTimeout(function () { + $input.removeClass('cp-admin-inval'); + var val = $input.val().trim(); + if (!val) { + $div.toggleClass('cp-admin-nopassword', true); + return; + } + + parsed = Hash.isValidHref(val); + $pwInput.val(''); + + if (!parsed || !parsed.hashData) { + $div.toggleClass('cp-admin-nopassword', true); + return void $input.addClass('cp-admin-inval'); + } + + var pw = parsed.hashData.version !== 3 && parsed.hashData.password; + $div.toggleClass('cp-admin-nopassword', !pw); + }); + }); + $pw.on('keypress change', function () { + setTimeout(function () { + $pw.toggleClass('cp-admin-inval', !$pwInput.val()); + }); + }); + + var clicked = false; + $button.click(function () { + if (!parsed || !parsed.hashData) { + UI.warn(Messages.admin_archiveInval); + return; + } + var pw = parsed.hashData.password ? $pwInput.val() : undefined; + var channel; + if (parsed.hashData.version === 3) { + channel = parsed.hashData.channel; + } else { + var secret = Hash.getSecrets(parsed.type, parsed.hash, pw); + channel = secret && secret.channel; + } + + if (!channel) { + UI.warn(Messages.admin_archiveInval); + return; + } + + if (clicked) { return; } + clicked = true; + + nThen(function (waitFor) { + if (!archive) { return; } + common.getFileSize(channel, waitFor(function (err, size) { + if (!err && size === 0) { + clicked = false; + waitFor.abort(); + return void UI.warn(Messages.admin_archiveInval); + } + })); + }).nThen(function () { + sFrameChan.query('Q_ADMIN_RPC', { + cmd: archive ? 'ARCHIVE_DOCUMENT' : 'RESTORE_ARCHIVED_DOCUMENT', + data: channel + }, function (err, obj) { + var e = err || (obj && obj.error); + clicked = false; + if (e) { + UI.warn(Messages.error); + console.error(e); + return; + } + UI.log(archive ? Messages.archivedFromServer : Messages.restoredFromServer); + $input.val(''); + $pwInput.val(''); + }); + }); + }); + }; + + create['archive'] = function () { + var key = 'archive'; + var $div = makeBlock(key, true); + var $button = $div.find('button'); + archiveForm(true, $div, $button); + return $div; + }; + create['unarchive'] = function () { + var key = 'unarchive'; + var $div = makeBlock(key, true); + var $button = $div.find('button'); + archiveForm(false, $div, $button); + return $div; + }; + create['registration'] = function () { var key = 'registration'; var $div = makeBlock(key, true); @@ -222,7 +347,7 @@ define([ var keyEl = h('code.cp-limit-key', key); $(keyEl).click(function () { $('.cp-admin-setlimit-form').find('.cp-setlimit-key').val(key); - $('.cp-admin-setlimit-form').find('.cp-setlimit-quota').val(Math.floor(user.limit/1024)); + $('.cp-admin-setlimit-form').find('.cp-setlimit-quota').val(Math.floor(user.limit / 1024 / 1024)); $('.cp-admin-setlimit-form').find('.cp-setlimit-note').val(user.note); }); if (compact) { @@ -427,7 +552,53 @@ define([ var $div = $(h('div.cp-support-container')).appendTo($container); var catContainer = h('div.cp-dropdown-container'); - $div.append(catContainer); + Messages.admin_support_premium = "Premium tickets:"; // XXX + Messages.admin_support_normal = "Unanswered tickets:"; + Messages.admin_support_answered = "Answered tickets:"; + Messages.admin_support_closed = "Closed tickets:"; + Messages.admin_support_open = "Show"; + Messages.admin_support_collapse = "Collapse"; + Messages.admin_support_first = "Created on: "; + Messages.admin_support_last = "Updated on: "; + var col1 = h('div.cp-support-column', h('h1', [ + h('span', Messages.admin_support_premium), + h('span.cp-support-count'), + h('button.btn.cp-support-column-button', Messages.admin_support_collapse) + ])); + var col2 = h('div.cp-support-column', h('h1', [ + h('span', Messages.admin_support_normal), + h('span.cp-support-count'), + h('button.btn.cp-support-column-button', Messages.admin_support_collapse) + ])); + var col3 = h('div.cp-support-column', h('h1', [ + h('span', Messages.admin_support_answered), + h('span.cp-support-count'), + h('button.btn.cp-support-column-button', Messages.admin_support_collapse) + ])); + var col4 = h('div.cp-support-column', h('h1', [ + h('span', Messages.admin_support_closed), + h('span.cp-support-count'), + h('button.btn.cp-support-column-button', Messages.admin_support_collapse) + ])); + var $col1 = $(col1), $col2 = $(col2), $col3 = $(col3), $col4 = $(col4); + $div.append([ + //catContainer + col1, + col2, + col3, + col4 + ]); + $div.find('.cp-support-column-button').click(function () { + var $col = $(this).closest('.cp-support-column'); + $col.toggleClass('cp-support-column-collapsed'); + if ($col.hasClass('cp-support-column-collapsed')) { + $(this).text(Messages.admin_support_open); + $(this).toggleClass('btn-primary'); + } else { + $(this).text(Messages.admin_support_collapse); + $(this).toggleClass('btn-primary'); + } + }); var category = 'all'; var $drop = APP.support.makeCategoryDropdown(catContainer, function (key) { category = key; @@ -447,37 +618,128 @@ define([ var hashesById = {}; - var reorder = function () { - var order = Object.keys(hashesById); - order.sort(function (id1, id2) { - var t1 = hashesById[id1]; - var t2 = hashesById[id2]; - if (!Array.isArray(t1)) { return 1; } - if (!Array.isArray(t2)) { return -1; } - var lastMsg1 = t1[t1.length - 1]; - var lastMsg2 = t2[t2.length - 1]; - var time1 = Util.find(lastMsg1, ['content', 'msg', 'content', 'time']); - var time2 = Util.find(lastMsg2, ['content', 'msg', 'content', 'time']); - var authorEd1 = Util.find(lastMsg1, ['content', 'msg', 'content', 'sender', 'edPublic']); - var authorEd2 = Util.find(lastMsg2, ['content', 'msg', 'content', 'sender', 'edPublic']); - var admin1 = ApiConfig.adminKeys.indexOf(authorEd1) !== -1; - var admin2 = ApiConfig.adminKeys.indexOf(authorEd2) !== -1; - // If one is answered and not the other, put the unanswered first - if (admin1 && !admin2) { return 1; } - if (!admin1 && admin2) { return -1; } - // Otherwise, sort them by time - return time2 - time1; + var getTicketData = function (id) { + var t = hashesById[id]; + if (!Array.isArray(t) || !t.length) { return; } + var ed = Util.find(t[0], ['content', 'msg', 'content', 'sender', 'edPublic']); + // If one of their ticket was sent as a premium user, mark them as premium + var premium = t.some(function (msg) { + var _ed = Util.find(msg, ['content', 'msg', 'content', 'sender', 'edPublic']); + if (ed !== _ed) { return; } + return Util.find(t[0], ['content', 'msg', 'content', 'sender', 'plan']); + }); + var lastMsg = t[t.length - 1]; + var lastMsgEd = Util.find(lastMsg, ['content', 'msg', 'content', 'sender', 'edPublic']); + return { + lastMsg: lastMsg, + time: Util.find(lastMsg, ['content', 'msg', 'content', 'time']), + lastMsgEd: lastMsgEd, + lastAdmin: lastMsgEd !== ed && ApiConfig.adminKeys.indexOf(lastMsgEd) !== -1, + premium: premium, + authorEd: ed, + closed: Util.find(lastMsg, ['content', 'msg', 'type']) === 'CLOSE' + }; + }; + + var addClickHandler = function ($ticket) { + $ticket.on('click', function () { + $ticket.toggleClass('cp-support-open', true); + $ticket.off('click'); + }); + }; + var makeOpenButton = function ($ticket) { + var button = h('button.btn.btn-primary.cp-support-expand', Messages.admin_support_open); + var collapse = h('button.btn.cp-support-collapse', Messages.admin_support_collapse); + $(button).click(function () { + $ticket.toggleClass('cp-support-open', true); + }); + addClickHandler($ticket); + $(collapse).click(function (e) { + $ticket.toggleClass('cp-support-open', false); + e.stopPropagation(); + setTimeout(function () { + addClickHandler($ticket); + }); }); - order.forEach(function (id, i) { - $div.find('[data-id="'+id+'"]').css('order', i); + $ticket.find('.cp-support-title-buttons').prepend([button, collapse]); + $ticket.append(h('div.cp-support-collapsed')); + }; + var updateTicketDetails = function ($ticket, isPremium) { + var $first = $ticket.find('.cp-support-message-from').first(); + var user = $first.find('span').first().html(); + var time = $first.find('.cp-support-message-time').text(); + var last = $ticket.find('.cp-support-message-from').last().find('.cp-support-message-time').text(); + var $c = $ticket.find('.cp-support-collapsed'); + var txtClass = isPremium ? ".cp-support-ispremium" : ""; + $c.html('').append([ + UI.setHTML(h('span'+ txtClass), user), + h('span', [ + h('b', Messages.admin_support_first), + h('span', time) + ]), + h('span', [ + h('b', Messages.admin_support_last), + h('span', last) + ]) + ]); + + }; + + var sort = function (id1, id2) { + var t1 = getTicketData(id1); + var t2 = getTicketData(id2); + if (!t1) { return 1; } + if (!t2) { return -1; } + /* + // If one is answered and not the other, put the unanswered first + if (t1.lastAdmin && !t2.lastAdmin) { return 1; } + if (!t1.lastAdmin && t2.lastAdmin) { return -1; } + */ + // Otherwise, sort them by time + return t1.time - t2.time; + }; + + var _reorder = function () { + var orderAnswered = Object.keys(hashesById).filter(function (id) { + var d = getTicketData(id); + return d && d.lastAdmin && !d.closed; + }).sort(sort); + var orderPremium = Object.keys(hashesById).filter(function (id) { + var d = getTicketData(id); + return d && d.premium && !d.lastAdmin && !d.closed; + }).sort(sort); + var orderNormal = Object.keys(hashesById).filter(function (id) { + var d = getTicketData(id); + return d && !d.premium && !d.lastAdmin && !d.closed; + }).sort(sort); + var orderClosed = Object.keys(hashesById).filter(function (id) { + var d = getTicketData(id); + return d && d.closed; + }).sort(sort); + var cols = [$col1, $col2, $col3, $col4]; + [orderPremium, orderNormal, orderAnswered, orderClosed].forEach(function (list, j) { + list.forEach(function (id, i) { + var $t = $div.find('[data-id="'+id+'"]'); + var d = getTicketData(id); + $t.css('order', i).appendTo(cols[j]); + updateTicketDetails($t, d.premium); + }); + if (!list.length) { + cols[j].hide(); + } else { + cols[j].show(); + cols[j].find('.cp-support-count').text(list.length); + } }); }; + var reorder = Util.throttle(_reorder, 150); var to = Util.throttle(function () { var $ticket = $div.find('.cp-support-list-ticket[data-id="'+linkedId+'"]'); + $ticket.addClass('cp-support-open'); $ticket[0].scrollIntoView(); linkedId = undefined; - }, 100); + }, 200); // Register to the "support" mailbox common.mailbox.subscribe(['supportadmin'], { @@ -505,6 +767,7 @@ define([ if (!$ticket.length) { return; } $ticket.addClass('cp-support-list-closed'); $ticket.append(APP.support.makeCloseMessage(content, hash)); + reorder(); return; } if (msg.type !== 'TICKET') { return; } @@ -525,13 +788,19 @@ define([ })); }); }).nThen(function () { - if (!error) { return void $ticket.remove(); } + if (!error) { + $ticket.remove(); + delete hashesById[id]; + reorder(); + return; + } // if deletion failed then reactivate the button and warn hideButton.removeAttribute('disabled'); // and show a generic error message UI.alert(Messages.error); }); }); + makeOpenButton($ticket); if (category !== 'all' && $ticket.attr('data-cat') !== category) { $ticket.hide(); } diff --git a/www/assert/main.js b/www/assert/main.js index 054a17c0f..db417c761 100644 --- a/www/assert/main.js +++ b/www/assert/main.js @@ -334,6 +334,12 @@ define([ !secret.hashData.present); }, "test support for ugly tracking query paramaters in url"); + assert(function (cb) { + var url = '//cryptpad.fr/pad/#/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/'; + var parsed = Hash.isValidHref(url); + cb(!parsed); + }, "test that protocol relative URLs are rejected"); + assert(function (cb) { var keys = Block.genkeys(Nacl.randomBytes(64)); var hash = Block.getBlockHash(keys); @@ -349,7 +355,7 @@ define([ var v3 = Hash.isValidHref('/pad'); var v4 = Hash.isValidHref('/pad/'); - var res = v1 && v2 && v3 && v4; + var res = Boolean(v1 && v2 && v3 && v4); cb(res); if (!res) { console.log(v1, v2, v3, v4); @@ -361,7 +367,7 @@ define([ var v3 = Hash.isValidHref('/pad#'); // Invalid var v4 = Hash.isValidHref('/pad/#'); - var res = v1 && v2 && v3 && v4; + var res = Boolean(v1 && v2 && v3 && v4); cb(res); if (!res) { console.log(v1, v2, v3, v4); @@ -373,7 +379,7 @@ define([ var v3 = Hash.isValidHref('https://cryptpad.fr/pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy'); var v4 = Hash.isValidHref('/pad/#/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed'); - var res = v1 && v2 && v3 && v4; + var res = Boolean(v1 && v2 && v3 && v4); cb(res); if (!res) { console.log(v1, v2, v3, v4); diff --git a/www/common/application_config_internal.js b/www/common/application_config_internal.js index 734b762ac..496e65f1f 100644 --- a/www/common/application_config_internal.js +++ b/www/common/application_config_internal.js @@ -169,7 +169,7 @@ define(function() { // make them have a very slow loading time. To avoid impacting the user experience // significantly, we're limiting the number of teams per user to 3 by default. // You can change this value here. - //config.maxTeamsSlots = 3; + //config.maxTeamsSlots = 5; // Each team is considered as a registered user by the server. Users and teams are indistinguishable // in the database so teams will offer the same storage limits as users by default. @@ -177,7 +177,7 @@ define(function() { // We're limiting the number of teams each user is able to own to 1 in order to make sure // users don't use "fake" teams (1 member) just to increase their storage limit. // You can change the value here. - // config.maxOwnedTeams = 1; + // config.maxOwnedTeams = 5; return config; }); diff --git a/www/common/common-constants.js b/www/common/common-constants.js index 17db2302c..fdae2b5de 100644 --- a/www/common/common-constants.js +++ b/www/common/common-constants.js @@ -12,8 +12,8 @@ define(['/customize/application_config.js'], function (AppConfig) { tokenKey: 'loginToken', displayPadCreationScreen: 'displayPadCreationScreen', deprecatedKey: 'deprecated', - MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 3, - MAX_TEAMS_OWNED: AppConfig.maxOwnedTeams || 1, + MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 5, + MAX_TEAMS_OWNED: AppConfig.maxOwnedTeams || 5, // Apps criticalApps: ['profile', 'settings', 'debug', 'admin', 'support', 'notifications'] }; diff --git a/www/common/common-hash.js b/www/common/common-hash.js index 199c2cb54..afdecbf57 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -464,6 +464,8 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) }; if (!/^https*:\/\//.test(href)) { + // If it doesn't start with http(s), it should be a relative href + if (!/^\/($|[^\/])/.test(href)) { return ret; } idx = href.indexOf('/#'); ret.type = href.slice(1, idx); if (idx === -1) { return ret; } @@ -661,7 +663,7 @@ Version 4: Data URL when not a realtime link yet (new pad or "static" app) if (parsed.hashData.key && !/^[a-zA-Z0-9+-/=]+$/.test(parsed.hashData.key)) { return; } } } - return true; + return parsed; }; Hash.decodeDataOptions = function (opts) { diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 66c066d3f..f7f3f082f 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -65,12 +65,13 @@ define([ switch (e.which) { case 27: // cancel if (typeof(no) === 'function') { no(e); } + $(el || window).off('keydown', handler); break; case 13: // enter if (typeof(yes) === 'function') { yes(e); } + $(el || window).off('keydown', handler); break; } - $(el || window).off('keydown', handler); }; $(el || window).keydown(handler); @@ -773,7 +774,8 @@ define([ $(originalBtn).show(); }; - $button.click(function () { + $button.click(function (e) { + e.stopPropagation(); done(true); }); @@ -791,7 +793,8 @@ define([ to = setTimeout(todo, INTERVAL); }; - $(originalBtn).addClass('cp-button-confirm-placeholder').click(function () { + $(originalBtn).addClass('cp-button-confirm-placeholder').click(function (e) { + e.stopPropagation(); // If we have a validation function, continue only if it's true if (config.validate && !config.validate()) { return; } i = 1; @@ -981,6 +984,11 @@ define([ setTimeout(cb, 750); }; UI.errorLoadingScreen = function (error, transparent, exitable) { + if (error === 'Error: XDR encoding failure') { + console.warn(error); + return; + } + var $loading = $('#' + LOADING); if (!$loading.is(':visible') || $loading.hasClass('cp-loading-hidden')) { UI.addLoadingScreen(); diff --git a/www/common/common-messaging.js b/www/common/common-messaging.js index 15d0408f7..65f05961e 100644 --- a/www/common/common-messaging.js +++ b/www/common/common-messaging.js @@ -91,7 +91,7 @@ define([ Msg.updateMyData = function (store, curve) { var myData = createData(store.proxy, false); if (store.proxy.friends) { - store.proxy.friends.me = myData; + store.proxy.friends.me = Util.clone(myData); delete store.proxy.friends.me.channel; } if (store.modules['team']) { @@ -99,6 +99,7 @@ define([ } var todo = function (friend) { if (!friend || !friend.notifications) { return; } + delete friend.user; myData.channel = friend.channel; store.mailbox.sendTo('UPDATE_DATA', myData, { channel: friend.notifications, diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index 8c78d9d6d..012c2c9b4 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -3,9 +3,9 @@ define([ '/common/common-util.js', '/common/visible.js', '/common/common-hash.js', - '/file/file-crypto.js', + '/common/media-tag.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function ($, Util, Visible, Hash, FileCrypto) { +], function ($, Util, Visible, Hash, MediaTag) { var Nacl = window.nacl; var Thumb = { dimension: 100, @@ -314,7 +314,7 @@ define([ var hexFileName = secret.channel; var src = fileHost + Hash.getBlobPathFromHex(hexFileName); var key = secret.keys && secret.keys.cryptKey; - FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { + MediaTag.fetchDecryptedMetadata(src, key, function (e, metadata) { if (e) { if (e === 'XHR_ERROR') { return; } return console.error(e); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 54c5f4921..5501ca813 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1602,9 +1602,9 @@ define([ content: h('span', Messages.profileButton), action: function () { if (padType) { - window.open(origin+'/profile/'); + Common.openURL(origin+'/profile/'); } else { - window.parent.location = origin+'/profile/'; + Common.gotoURL(origin+'/profile/'); } }, }); @@ -1613,33 +1613,36 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/drive/', 'class': 'fa fa-hdd-o' }, - content: h('span', Messages.type.drive) + content: h('span', Messages.type.drive), + action: function () { + Common.openURL(origin+'/drive/'); + }, }); } if (padType !== 'teams' && accountName) { options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/teams/', 'class': 'fa fa-users' }, - content: h('span', Messages.type.teams) + content: h('span', Messages.type.teams), + action: function () { + Common.openURL('/teams/'); + }, }); } if (padType !== 'contacts' && accountName) { options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/contacts/', 'class': 'fa fa-address-book' }, - content: h('span', Messages.type.contacts) + content: h('span', Messages.type.contacts), + action: function () { + Common.openURL('/contacts/'); + }, }); } if (padType !== 'settings') { @@ -1649,9 +1652,9 @@ define([ content: h('span', Messages.settingsButton), action: function () { if (padType) { - window.open(origin+'/settings/'); + Common.openURL(origin+'/settings/'); } else { - window.parent.location = origin+'/settings/'; + Common.gotoURL(origin+'/settings/'); } }, }); @@ -1666,9 +1669,9 @@ define([ content: h('span', Messages.adminPage || 'Admin'), action: function () { if (padType) { - window.open(origin+'/admin/'); + Common.openURL(origin+'/admin/'); } else { - window.parent.location = origin+'/admin/'; + Common.gotoURL(origin+'/admin/'); } }, }); @@ -1690,29 +1693,28 @@ define([ content: h('span', Messages.supportPage || 'Support'), action: function () { if (padType) { - window.open(origin+'/support/'); + Common.openURL(origin+'/support/'); } else { - window.parent.location = origin+'/support/'; + Common.gotoURL(origin+'/support/'); } }, }); } - // XXX Trade the survey for documentation - // if (AppConfig.surveyURL) { - // options.push({ - // tag: 'a', - // attributes: { - // 'target': '_blank', - // 'rel': 'noopener', - // 'href': AppConfig.surveyURL, - // 'class': 'cp-toolbar-survey fa fa-graduation-cap' - // }, - // content: h('span', Messages.survey), - // action: function () { - // Feedback.send('SURVEY_CLICKED'); - // }, - // }); - // } +/* + if (AppConfig.surveyURL) { + options.push({ + tag: 'a', + attributes: { + 'class': 'cp-toolbar-survey fa fa-graduation-cap' + }, + content: h('span', Messages.survey), + action: function () { + Common.openUnsafeURL(AppConfig.surveyURL); + Feedback.send('SURVEY_CLICKED'); + }, + }); + } +*/ options.push({ tag: 'a', attributes: { @@ -1727,11 +1729,12 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': origin+'/index.html', 'class': 'fa fa-home' }, - content: h('span', Messages.homePage) + content: h('span', Messages.homePage), + action: function () { + Common.openURL('/index.html'); + }, }); // Add the change display name button if not in read only mode /* @@ -1747,23 +1750,24 @@ define([ options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'href': priv.plan ? priv.accounts.upgradeURL : origin+'/features.html', 'class': 'fa fa-star-o' }, - content: h('span', priv.plan ? Messages.settings_cat_subscription : Messages.pricing) + content: h('span', priv.plan ? Messages.settings_cat_subscription : Messages.pricing), + action: function () { + Common.openURL(priv.plan ? priv.accounts.upgradeURL :'/features.html'); + }, }); } if (!priv.plan && !Config.removeDonateButton) { options.push({ tag: 'a', attributes: { - 'target': '_blank', - 'rel': 'noopener', - 'href': priv.accounts.donateURL, 'class': 'fa fa-gift' }, - content: h('span', Messages.crowdfunding_button2) + content: h('span', Messages.crowdfunding_button2), + action: function () { + Common.openUnsafeURL(priv.accounts.donateURL); + }, }); } @@ -1778,7 +1782,7 @@ define([ content: h('span', Messages.logoutEverywhere), action: function () { Common.getSframeChannel().query('Q_LOGOUT_EVERYWHERE', null, function () { - window.parent.location = origin + '/'; + Common.gotoURL(origin + '/'); }); }, }); @@ -1788,7 +1792,7 @@ define([ content: h('span', Messages.logoutButton), action: function () { Common.logout(function () { - window.parent.location = origin+'/'; + Common.gotoURL(origin+'/'); }); }, }); @@ -2603,7 +2607,9 @@ define([ var block = h('div#cp-loading-burn-after-reading', [ info, - button + h('nav', { + style: 'text-align: right' + }, button), ]); UI.errorLoadingScreen(block); }; @@ -2738,6 +2744,43 @@ define([ }; + UIElements.displayTrimHistoryPrompt = function (common, data) { + var mb = Util.bytesToMegabytes(data.size); + var text = Messages._getKey('history_trimPrompt', [ + Messages._getKey('formattedMB', [mb]) + ]); + var yes = h('button.cp-corner-primary', [ + h('span.fa.fa-trash-o'), + Messages.trimHistory_button + ]); + var no = h('button.cp-corner-cancel', Messages.crowdfunding_popup_no); // Not now + var actions = h('div', [no, yes]); + + var dontShowAgain = function () { + var until = (+new Date()) + (7 * 24 * 3600 * 1000); // 7 days from now + if (data.drive) { + common.setAttribute(['drive', 'trim'], until); + return; + } + common.setPadAttribute('trim', until); + }; + + var modal = UI.cornerPopup(text, actions, '', {}); + + $(yes).click(function () { + modal.delete(); + if (data.drive) { + common.openURL('/settings/#drive'); + return; + } + common.getSframeChannel().event('EV_PROPERTIES_OPEN'); + }); + $(no).click(function () { + dontShowAgain(); + modal.delete(); + }); + }; + UIElements.displayFriendRequestModal = function (common, data) { var msg = data.content.msg; var userData = msg.content.user; diff --git a/www/common/common-util.js b/www/common/common-util.js index 41797e7c8..603e38a30 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -30,6 +30,15 @@ return JSON.parse(JSON.stringify(o)); }; + Util.serializeError = function (err) { + if (!(err instanceof Error)) { return err; } + var ser = {}; + Object.getOwnPropertyNames(err).forEach(function (key) { + ser[key] = err[key]; + }); + return ser; + }; + Util.tryParse = function (s) { try { return JSON.parse(s); } catch (e) { return;} }; @@ -113,13 +122,13 @@ var handle = function (id, args) { var fn = pending[id]; if (typeof(fn) !== 'function') { - errorHandler("MISSING_CALLBACK", { + return void errorHandler("MISSING_CALLBACK", { id: id, args: args, }); } try { - pending[id].apply(null, Array.isArray(args)? args : [args]); + fn.apply(null, Array.isArray(args)? args : [args]); } catch (err) { errorHandler('HANDLER_ERROR', { error: err, @@ -265,28 +274,73 @@ // given a path, asynchronously return an arraybuffer - Util.fetch = function (src, cb, progress) { - var CB = Util.once(cb); + var getCacheKey = function (src) { + var _src = src.replace(/(\/)*$/, ''); // Remove trailing slashes + var idx = _src.lastIndexOf('/'); + var cacheKey = _src.slice(idx+1); + if (!/^[a-f0-9]{48}$/.test(cacheKey)) { cacheKey = undefined; } + return cacheKey; + }; + Util.fetch = function (src, cb, progress, cache) { + var CB = Util.once(Util.mkAsync(cb)); + + var cacheKey = getCacheKey(src); + var getBlobCache = function (id, cb) { + if (!cache || typeof(cache.getBlobCache) !== "function") { return void cb('EINVAL'); } + cache.getBlobCache(id, cb); + }; + var setBlobCache = function (id, u8, cb) { + if (!cache || typeof(cache.setBlobCache) !== "function") { return void cb('EINVAL'); } + cache.setBlobCache(id, u8, cb); + }; - var xhr = new XMLHttpRequest(); - xhr.open("GET", src, true); - if (progress) { - xhr.addEventListener("progress", function (evt) { - if (evt.lengthComputable) { - var percentComplete = evt.loaded / evt.total; - progress(percentComplete); + var xhr; + + var fetch = function () { + xhr = new XMLHttpRequest(); + xhr.open("GET", src, true); + if (progress) { + xhr.addEventListener("progress", function (evt) { + if (evt.lengthComputable) { + var percentComplete = evt.loaded / evt.total; + progress(percentComplete); + } + }, false); + } + xhr.responseType = "arraybuffer"; + xhr.onerror = function (err) { CB(err); }; + xhr.onload = function () { + if (/^4/.test(''+this.status)) { + return CB('XHR_ERROR'); } - }, false); - } - xhr.responseType = "arraybuffer"; - xhr.onerror = function (err) { CB(err); }; - xhr.onload = function () { - if (/^4/.test(''+this.status)) { - return CB('XHR_ERROR'); + + var arrayBuffer = xhr.response; + if (arrayBuffer) { + var u8 = new Uint8Array(arrayBuffer); + if (cacheKey) { + return void setBlobCache(cacheKey, u8, function () { + CB(null, u8); + }); + } + return void CB(void 0, u8); + } + CB('ENOENT'); + }; + xhr.send(null); + }; + + if (!cacheKey) { return void fetch(); } + + getBlobCache(cacheKey, function (err, u8) { + if (err || !u8) { return void fetch(); } + CB(void 0, u8); + }); + + return { + cancel: function () { + if (xhr && xhr.abort) { xhr.abort(); } } - return void CB(void 0, new Uint8Array(xhr.response)); }; - xhr.send(null); }; Util.dataURIToBlob = function (dataURI) { diff --git a/www/common/cryptget.js b/www/common/cryptget.js index e394788d7..1cbd5056e 100644 --- a/www/common/cryptget.js +++ b/www/common/cryptget.js @@ -6,10 +6,11 @@ define([ '/common/common-hash.js', '/common/common-realtime.js', '/common/outer/network-config.js', + '/common/outer/cache-store.js', '/common/pinpad.js', '/bower_components/nthen/index.js', '/bower_components/chainpad/chainpad.dist.js', -], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Pinpad, nThen) { +], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Cache, Pinpad, nThen) { var finish = function (S, err, doc) { if (S.done) { return; } S.cb(err, doc); @@ -92,7 +93,8 @@ define([ validateKey: secret.keys.validateKey || undefined, crypto: Crypto.createEncryptor(secret.keys), logLevel: 0, - initialState: opt.initialState + initialState: opt.initialState, + Cache: Cache }; return config; }; @@ -132,9 +134,11 @@ define([ }; config.onError = function (info) { + console.warn(info); finish(Session, info.error); }; config.onChannelError = function (info) { + console.error(info); finish(Session, info.error); }; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index ca65ce77b..006f150d7 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -3,6 +3,7 @@ define([ '/customize/messages.js', '/common/common-util.js', '/common/common-hash.js', + '/common/outer/cache-store.js', '/common/common-messaging.js', '/common/common-constants.js', '/common/common-feedback.js', @@ -14,7 +15,7 @@ define([ '/customize/application_config.js', '/bower_components/nthen/index.js', -], function (Config, Messages, Util, Hash, +], function (Config, Messages, Util, Hash, Cache, Messaging, Constants, Feedback, Visible, UserObject, LocalStore, Channel, Block, AppConfig, Nthen) { @@ -147,6 +148,22 @@ define([ }; send(); }; + common.fixRosterHash = function () { + // Push teams keys + postMessage("GET", { + key: ['teams'], + }, function (obj) { + if (obj.error) { return console.error(obj.error); } + Object.keys(obj || {}).forEach(function (id) { + postMessage("SET", { + key: ['teams', id, 'keys', 'roster', 'lastKnownHash'], + value: '' + }, function () { + console.log('done, please close all your CryptPad tabs before testing the fix'); + }); + }); + }); + }; (function () { var bypassHashChange = function (key) { @@ -701,7 +718,7 @@ define([ }); }; - common.useFile = function (Crypt, cb, optsPut) { + common.useFile = function (Crypt, cb, optsPut, onProgress) { var fileHost = Config.fileHost || window.location.origin; var data = common.fromFileData; var parsed = Hash.parsePadUrl(data.href); @@ -758,7 +775,9 @@ define([ return void cb(err); } u8 = _u8; - })); + }), function (progress) { + onProgress(progress * 50); + }, Cache); }).nThen(function (waitFor) { require(["/file/file-crypto.js"], waitFor(function (FileCrypto) { FileCrypto.decrypt(u8, key, waitFor(function (err, _res) { @@ -767,7 +786,9 @@ define([ return void cb(err); } res = _res; - })); + }), function (progress) { + onProgress(50 + progress * 50); + }); })); }).nThen(function (waitFor) { var ext = Util.parseFilename(data.title).ext; @@ -991,6 +1012,8 @@ define([ pad.onJoinEvent = Util.mkEvent(); pad.onLeaveEvent = Util.mkEvent(); pad.onDisconnectEvent = Util.mkEvent(); + pad.onCacheEvent = Util.mkEvent(); + pad.onCacheReadyEvent = Util.mkEvent(); pad.onConnectEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent(); pad.onMetadataEvent = Util.mkEvent(); @@ -1003,6 +1026,10 @@ define([ postMessage("GIVE_PAD_ACCESS", data, cb); }; + common.onCorruptedCache = function (channel) { + postMessage("CORRUPTED_CACHE", channel); + }; + common.setPadMetadata = function (data, cb) { postMessage('SET_PAD_METADATA', data, cb); }; @@ -1876,12 +1903,12 @@ define([ var requestLogin = function () { // log out so that you don't go into an endless loop... - LocalStore.logout(); - - // redirect them to log in, and come back when they're done. - var href = Hash.hashToHref('', 'login'); - var url = Hash.getNewPadURL(href, { href: currentPad.href }); - window.location.href = url; + LocalStore.logout(function () { + // redirect them to log in, and come back when they're done. + var href = Hash.hashToHref('', 'login'); + var url = Hash.getNewPadURL(href, { href: currentPad.href }); + window.location.href = url; + }); }; common.startAccountDeletion = function (data, cb) { @@ -1956,6 +1983,8 @@ define([ PAD_JOIN: common.padRpc.onJoinEvent.fire, PAD_LEAVE: common.padRpc.onLeaveEvent.fire, PAD_DISCONNECT: common.padRpc.onDisconnectEvent.fire, + PAD_CACHE: common.padRpc.onCacheEvent.fire, + PAD_CACHE_READY: common.padRpc.onCacheReadyEvent.fire, PAD_CONNECT: common.padRpc.onConnectEvent.fire, PAD_ERROR: common.padRpc.onErrorEvent.fire, PAD_METADATA: common.padRpc.onMetadataEvent.fire, @@ -2057,6 +2086,32 @@ define([ var userHash; + (function iOSFirefoxFix () { +/* + For some bizarre reason Firefox on iOS throws an error during the + loading process unless we call this function. Drawing these elements + to the DOM presumably causes the JS engine to wait just a little bit longer + until some APIs we need are ready. This occurs despite all this code being + run after the usual dom-ready events. This fix was discovered while trying + to log the error messages to the DOM because it's extremely difficult + to debug Firefox iOS in the usual ways. In summary, computers are terrible. +*/ + try { + var style = document.createElement('style'); + style.type = 'text/css'; + style.appendChild(document.createTextNode('#cp-logger { display: none; }')); + document.head.appendChild(style); + + var logger = document.createElement('div'); + logger.setAttribute('id', 'cp-logger'); + document.body.appendChild(logger); + + var pre = document.createElement('pre'); + pre.innerText = 'x'; + logger.appendChild(pre); + } catch (err) { console.error(err); } + }()); + Nthen(function (waitFor) { if (AppConfig.beforeLogin) { AppConfig.beforeLogin(LocalStore.isLoggedIn(), waitFor()); diff --git a/www/common/diffMarked.js b/www/common/diffMarked.js index 734551983..71149b59f 100644 --- a/www/common/diffMarked.js +++ b/www/common/diffMarked.js @@ -303,14 +303,22 @@ define([ return renderParagraph(p); }; + // Note: iframe, video and audio are used in mediatags and are allowed in rich text pads. var forbiddenTags = [ 'SCRIPT', - 'IFRAME', + //'IFRAME', 'OBJECT', 'APPLET', - 'VIDEO', // privacy implications of videos are the same as images - 'AUDIO', // same with audio + //'VIDEO', // privacy implications of videos are the same as images + //'AUDIO', // same with audio + 'SOURCE' + ]; + var restrictedTags = [ + 'IFRAME', + 'VIDEO', + 'AUDIO' ]; + var unsafeTag = function (info) { /*if (info.node && $(info.node).parents('media-tag').length) { // Do not remove elements inside a media-tag @@ -343,13 +351,20 @@ define([ if (!(node && node.parentElement)) { return; } var parent = node.parentElement; if (!parent) { return; } - console.log('removing %s tag', node.nodeName); + console.debug('removing %s tag', node.nodeName); parent.removeChild(node); }; + // Only allow iframe, video and audio with local source + var checkSrc = function (root) { + if (restrictedTags.indexOf(root.nodeName.toUpperCase()) === -1) { return true; } + return root.getAttribute && /^(blob\:|\/common\/pdfjs)/.test(root.getAttribute('src')); + }; + var removeForbiddenTags = function (root) { if (!root) { return; } if (forbiddenTags.indexOf(root.nodeName.toUpperCase()) !== -1) { removeNode(root); } + if (!checkSrc(root)) { removeNode(root); } slice(root.children).forEach(removeForbiddenTags); }; @@ -658,7 +673,7 @@ define([ $(contextMenu.menu).find('li').show(); contextMenu.show(e); }); - if ($mt.children().length) { + if ($mt.children().length && $mt[0]._mediaObject) { $mt.off('click dblclick preview'); $mt.on('preview', onPreview($mt)); if ($mt.find('img').length) { @@ -668,15 +683,15 @@ define([ } return; } - MediaTag(el); + var mediaObject = MediaTag(el); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { - var list_values = slice(mutation.target.children) + var list_values = slice(el.children) .map(function (el) { return el.outerHTML; }) .join(''); - mediaMap[mutation.target.getAttribute('src')] = list_values; - observer.disconnect(); + mediaMap[el.getAttribute('src')] = list_values; + if (mediaObject.complete) { observer.disconnect(); } } }); $mt.off('click dblclick preview'); @@ -689,6 +704,7 @@ define([ }); observer.observe(el, { attributes: false, + subtree: true, childList: true, characterData: false }); diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index eba793e0f..2ad6e1fa3 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -42,6 +42,7 @@ define([ var APP = window.APP = { editable: false, + online: true, mobile: function () { if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; } else { return $('body').width() <= 600; } @@ -267,13 +268,25 @@ define([ }; // Handle disconnect/reconnect - var setEditable = function (state, isHistory) { + // If isHistory and isSf are both false, update the "APP.online" flag + // If isHistory is true, update the "APP.history" flag + // isSf is used to detect offline shared folders: setEditable is called on displayDirectory + var setEditable = function (state, isHistory, isSf) { if (APP.closed || !APP.$content || !$.contains(document.documentElement, APP.$content[0])) { return; } + if (isHistory) { + APP.history = !state; + } else if (!isSf) { + APP.online = state; + } + state = APP.online && !APP.history && state; APP.editable = !APP.readOnly && state; + if (!state) { APP.$content.addClass('cp-app-drive-readonly'); - if (!isHistory) { + if (!APP.history || !APP.online) { $('#cp-app-drive-connection-state').show(); + } else { + $('#cp-app-drive-connection-state').hide(); } $('[draggable="true"]').attr('draggable', false); } @@ -3661,6 +3674,15 @@ define([ } var readOnlyFolder = false; + + // If the shared folder is offline, add the "DISCONNECTED" banner, otherwise + // use the normal "editable" behavior (based on drive offline or history mode) + if (sfId && manager.folders[sfId].offline) { + setEditable(false, false, true); + } else { + setEditable(true, false, true); + } + if (APP.readOnly) { // Read-only drive (team?) $content.prepend($readOnly.clone()); @@ -4140,6 +4162,17 @@ define([ data.name = Util.fixFileName(folderName); data.folderName = Util.fixFileName(folderName) + '.zip'; + var uo = manager.user.userObject; + if (sfId && manager.folders[sfId]) { + uo = manager.folders[sfId].userObject; + } + if (uo.getFilesRecursively) { + data.list = uo.getFilesRecursively(folderElement).map(function (el) { + var d = uo.getFileData(el); + return d.channel; + }); + } + APP.FM.downloadFolder(data, function (err, obj) { console.log(err, obj); console.log('DONE'); diff --git a/www/common/inner/cache.js b/www/common/inner/cache.js new file mode 100644 index 000000000..be5b781c2 --- /dev/null +++ b/www/common/inner/cache.js @@ -0,0 +1,33 @@ +define([ +], function () { + var S = {}; + + S.create = function (sframeChan) { + var getBlobCache = function (id, cb) { + sframeChan.query('Q_GET_BLOB_CACHE', {id:id}, function (err, data) { + var e = err || (data && data.error); + if (e) { return void cb(e); } + if (!data || typeof(data) !== "object") { return void cb('EINVAL'); } + cb(null, data); + }, { raw: true }); + }; + var setBlobCache = function (id, u8, cb) { + sframeChan.query('Q_SET_BLOB_CACHE', { + id: id, + u8: u8 + }, function (err, data) { + var e = err || (data && data.error) || undefined; + cb(e); + }, { raw: true }); + }; + + + return { + getBlobCache: getBlobCache, + setBlobCache: setBlobCache + }; + }; + + return S; +}); + diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index 3d28d126a..80195c71c 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -17,11 +17,17 @@ define([ var Nacl = window.nacl; // Configure MediaTags to use our local viewer + // This file is loaded by sframe-common so the following config is used in all the inner apps if (MediaTag) { MediaTag.setDefaultConfig('pdf', { viewer: '/common/pdfjs/web/viewer.html' }); + MediaTag.setDefaultConfig('download', { + text: Messages.mediatag_saveButton, + textDl: Messages.mediatag_loadButton, + }); } + MT.MediaTag = MediaTag; // Cache of the avatars outer html (including ) var avatars = {}; @@ -68,7 +74,7 @@ define([ childList: true, characterData: false }); - MediaTag($tag[0]).on('error', function (data) { + MediaTag($tag[0], {force: true}).on('error', function (data) { console.error(data); }); }; @@ -241,7 +247,6 @@ define([ var locked = false; var show = function (_i) { if (locked) { return; } - locked = true; if (_i < 0) { i = 0; } else if (_i > tags.length -1) { i = tags.length - 1; } else { i = _i; } @@ -285,7 +290,6 @@ define([ if (_key) { key = 'cryptpad:' + Nacl.util.encodeBase64(_key); } } if (!src || !key) { - locked = false; $spinner.hide(); return void UI.log(Messages.error); } @@ -299,13 +303,18 @@ define([ locked = false; $spinner.hide(); UI.log(Messages.error); + }).on('progress', function () { + $spinner.hide(); + locked = true; + }).on('complete', function () { + locked = false; + $spinner.hide(); }); }); } var observer = new MutationObserver(function(mutations) { mutations.forEach(function() { - locked = false; $spinner.hide(); }); }); @@ -377,6 +386,14 @@ define([ 'tabindex': '-1', 'data-icon': "fa-eye", }, Messages.pad_mediatagPreview)), + h('li.cp-svg', h('a.cp-app-code-context-openin.dropdown-item', { + 'tabindex': '-1', + 'data-icon': "fa-external-link", + }, Messages.pad_mediatagOpen)), + h('li.cp-svg', h('a.cp-app-code-context-share.dropdown-item', { + 'tabindex': '-1', + 'data-icon': "fa-shhare-alt", + }, Messages.pad_mediatagShare)), h('li', h('a.cp-app-code-context-saveindrive.dropdown-item', { 'tabindex': '-1', 'data-icon': "fa-cloud-upload", @@ -413,12 +430,29 @@ define([ } else if ($this.hasClass("cp-app-code-context-download")) { var media = Util.find($mt, [0, '_mediaObject']); + if (!media) { return void console.error('no media'); } + if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); } if (!(media && media._blob)) { return void console.error($mt); } window.saveAs(media._blob.content, media.name); } else if ($this.hasClass("cp-app-code-context-open")) { $mt.trigger('preview'); } + else if ($this.hasClass("cp-app-code-context-openin")) { + var hash = common.getHashFromMediaTag($mt); + common.openURL(Hash.hashToHref(hash, 'file')); + } + else if ($this.hasClass("cp-app-code-context-share")) { + var data = { + file: true, + pathname: '/file/', + hashes: { + fileHash: common.getHashFromMediaTag($mt) + }, + title: Util.find($mt[0], ['_mediaObject', 'name']) || '' + }; + common.getSframeChannel().event('EV_SHARE_OPEN', data); + } }); return m; diff --git a/www/common/inner/properties.js b/www/common/inner/properties.js index 58f10e738..ad8571367 100644 --- a/www/common/inner/properties.js +++ b/www/common/inner/properties.js @@ -19,6 +19,13 @@ define([ var $d = $('
'); if (!data) { return void cb(void 0, $d); } + if (data.channel) { + $('
')},template:_.template(['
'].join("")),onResetItems:function(){this.innerEl=null,Common.UI.DataView.prototype.onResetItems.call(this),this.trigger("items:reset",this)},onAddItem:function(t,e,i){var n=new Common.UI.DataViewItem({template:this.itemTemplate,model:t});if(this.innerEl||(this.innerEl=$(this.el).find(".inner")),n&&this.innerEl){if(this.dataViewItems.length<1&&this.innerEl.find(".empty-text").remove(),this.options.simpleAddMode)this.innerEl.append(n.render().el),this.dataViewItems.push(n);else{var o=_.indexOf(this.store.models,t),s=this.innerEl.find("> div");o>0?$(s.get(o-1)).after(n.render().el):s.length>0?$(s[o]).before(n.render().el):this.innerEl.append(n.render().el),this.dataViewItems=this.dataViewItems.slice(0,o).concat(n).concat(this.dataViewItems.slice(o))}if(this.listenTo(n,"change",this.onChangeItem),this.listenTo(n,"remove",this.onRemoveItem),this.listenTo(n,"click",this.onClickItem),this.listenTo(n,"dblclick",this.onDblClickItem),this.listenTo(n,"select",this.onSelectItem),t.get("tip")){var a=$(n.el);a.attr("data-toggle","tooltip"),a.tooltip({title:t.get("tip"),placement:"cursor",zIndex:this.tipZIndex})}this.isSuspendEvents||this.trigger("item:add",this,n,t)}}}}())}),define("spreadsheeteditor/main/app/view/ParagraphSettingsAdvanced",["text!spreadsheeteditor/main/app/template/ParagraphSettingsAdvanced.template","common/main/lib/view/AdvancedSettingsWindow","common/main/lib/component/MetricSpinner","common/main/lib/component/CheckBox","common/main/lib/component/RadioBox","common/main/lib/component/ListView"],function(t){"use strict";SSE.Views.ParagraphSettingsAdvanced=Common.Views.AdvancedSettingsWindow.extend(_.extend({options:{contentWidth:370,height:394,toggleGroup:"paragraph-adv-settings-group",storageName:"sse-para-settings-adv-category"},initialize:function(e){var i=this;_.extend(this.options,{title:this.textTitle,items:[{panelId:"id-adv-paragraph-indents",panelCaption:this.strParagraphIndents},{panelId:"id-adv-paragraph-font",panelCaption:this.strParagraphFont},{panelId:"id-adv-paragraph-tabs",panelCaption:this.strTabs}],contentTemplate:_.template(t)({scope:this})},e),Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this,this.options),this._changedProps=null,this.checkGroup=0,this._noApply=!0,this._tabListChanged=!1,this.spinners=[],this.FirstLine=void 0,this.Spacing=null,this.api=this.options.api,this._originalProps=new Asc.asc_CParagraphProperty(this.options.paragraphProps),this._arrLineRule=[{displayValue:this.textAuto,defaultValue:1,value:c_paragraphLinerule.LINERULE_AUTO,minValue:.5,step:.01,defaultUnit:""},{displayValue:this.textExact,defaultValue:5,value:c_paragraphLinerule.LINERULE_EXACT,minValue:.03,step:.01,defaultUnit:"cm"}];var n=this._originalProps.asc_getSpacing().asc_getLineRule(),o=_.findWhere(this._arrLineRule,{value:n});this.CurLineRuleIdx=this._arrLineRule.indexOf(o),this._arrTextAlignment=[{displayValue:this.textTabLeft,value:c_paragraphTextAlignment.LEFT},{displayValue:this.textTabCenter,value:c_paragraphTextAlignment.CENTERED},{displayValue:this.textTabRight,value:c_paragraphTextAlignment.RIGHT},{displayValue:this.textJustified,value:c_paragraphTextAlignment.JUSTIFIED}],this._arrSpecial=[{displayValue:this.textNoneSpecial,value:c_paragraphSpecial.NONE_SPECIAL,defaultValue:0},{displayValue:this.textFirstLine,value:c_paragraphSpecial.FIRST_LINE,defaultValue:12.7},{displayValue:this.textHanging,value:c_paragraphSpecial.HANGING,defaultValue:12.7}],this._arrTabAlign=[{value:1,displayValue:this.textTabLeft},{value:3,displayValue:this.textTabCenter},{value:2,displayValue:this.textTabRight}],this._arrKeyTabAlign=[],this._arrTabAlign.forEach(function(t){i._arrKeyTabAlign[t.value]=t.displayValue})},render:function(){Common.Views.AdvancedSettingsWindow.prototype.render.call(this);var t=this;this.cmbTextAlignment=new Common.UI.ComboBox({el:$("#paragraphadv-spin-text-alignment"),cls:"input-group-nr",editable:!1,data:this._arrTextAlignment,style:"width: 173px;",menuStyle:"min-width: 173px;"}),this.cmbTextAlignment.setValue(""),this.numIndentsLeft=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-indent-left"),step:.1,width:85,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:0}),this.numIndentsLeft.on("change",_.bind(function(t,e,i,n){var o=t.getNumberValue();this._changedProps&&(null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd),this._changedProps.asc_getInd().put_Left(Common.Utils.Metric.fnRecalcToMM(o)))},this)),this.spinners.push(this.numIndentsLeft),this.numIndentsRight=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-indent-right"),step:.1,width:85,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:0}),this.numIndentsRight.on("change",_.bind(function(t,e,i,n){this._changedProps&&(null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd),this._changedProps.asc_getInd().put_Right(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())))},this)),this.spinners.push(this.numIndentsRight),this.cmbSpecial=new Common.UI.ComboBox({el:$("#paragraphadv-spin-special"),cls:"input-group-nr",editable:!1,data:this._arrSpecial,style:"width: 85px;",menuStyle:"min-width: 85px;"}),this.cmbSpecial.setValue(""),this.cmbSpecial.on("selected",_.bind(this.onSpecialSelect,this)),this.numSpecialBy=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-special-by"),step:.1,width:85,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:0}),this.spinners.push(this.numSpecialBy),this.numSpecialBy.on("change",_.bind(this.onFirstLineChange,this)),this.numSpacingBefore=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-spacing-before"),step:.1,width:85,value:"",defaultUnit:"cm",maxValue:55.88,minValue:0,allowAuto:!0,autoText:this.txtAutoText}),this.numSpacingBefore.on("change",_.bind(function(t,e,i,n){if(null===this.Spacing){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=o.asc_getSpacing()}this.Spacing.Before=Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())},this)),this.spinners.push(this.numSpacingBefore),this.numSpacingAfter=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-spacing-after"),step:.1,width:85,value:"",defaultUnit:"cm",maxValue:55.88,minValue:0,allowAuto:!0,autoText:this.txtAutoText}),this.numSpacingAfter.on("change",_.bind(function(t,e,i,n){if(null===this.Spacing){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=o.asc_getSpacing()}this.Spacing.After=Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())},this)),this.spinners.push(this.numSpacingAfter),this.cmbLineRule=new Common.UI.ComboBox({el:$("#paragraphadv-spin-line-rule"),cls:"input-group-nr",editable:!1,data:this._arrLineRule,style:"width: 85px;",menuStyle:"min-width: 85px;"}),this.cmbLineRule.setValue(this.CurLineRuleIdx),this.cmbLineRule.on("selected",_.bind(this.onLineRuleSelect,this)),this.numLineHeight=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-line-height"),step:.01,width:85,value:"",defaultUnit:"",maxValue:132,minValue:.5}),this.spinners.push(this.numLineHeight),this.numLineHeight.on("change",_.bind(this.onNumLineHeightChange,this)),this.chStrike=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-strike"),labelText:this.strStrike}),this.chStrike.on("change",_.bind(this.onStrikeChange,this)),this.chDoubleStrike=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-double-strike"),labelText:this.strDoubleStrike}),this.chDoubleStrike.on("change",_.bind(this.onDoubleStrikeChange,this)),this.chSuperscript=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-superscript"),labelText:this.strSuperscript}),this.chSuperscript.on("change",_.bind(this.onSuperscriptChange,this)),this.chSubscript=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-subscript"),labelText:this.strSubscript}),this.chSubscript.on("change",_.bind(this.onSubscriptChange,this)),this.chSmallCaps=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-small-caps"),labelText:this.strSmallCaps}),this.chSmallCaps.on("change",_.bind(this.onSmallCapsChange,this)),this.chAllCaps=new Common.UI.CheckBox({el:$("#paragraphadv-checkbox-all-caps"),labelText:this.strAllCaps}),this.chAllCaps.on("change",_.bind(this.onAllCapsChange,this)),this.numSpacing=new Common.UI.MetricSpinner({el:$("#paragraphadv-spin-spacing"),step:.01,width:100,defaultUnit:"cm",defaultValue:0,value:"0 cm",maxValue:55.87,minValue:-55.87}),this.numSpacing.on("change",_.bind(function(t,e,i,n){if(this._changedProps&&this._changedProps.asc_putTextSpacing(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())),this.api&&!this._noApply){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;o.asc_putTextSpacing(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())),this.api.asc_setDrawImagePlaceParagraph("paragraphadv-font-img",o)}},this)),this.spinners.push(this.numSpacing),this.numTab=new Common.UI.MetricSpinner({el:$("#paraadv-spin-tab"),step:.1,width:108,defaultUnit:"cm",value:"1.25 cm",maxValue:55.87,minValue:0}),this.spinners.push(this.numTab),this.numDefaultTab=new Common.UI.MetricSpinner({el:$("#paraadv-spin-default-tab"),step:.1,width:108,defaultUnit:"cm",value:"1.25 cm",maxValue:55.87,minValue:0}),this.numDefaultTab.on("change",_.bind(function(t,e,i,n){this._changedProps&&this._changedProps.asc_putDefaultTab(parseFloat(Common.Utils.Metric.fnRecalcToMM(t.getNumberValue()).toFixed(1)))},this)),this.spinners.push(this.numDefaultTab),this.tabList=new Common.UI.ListView({el:$("#paraadv-list-tabs"),emptyText:this.noTabs,store:new Common.UI.DataViewStore,template:_.template(['
'].join("")),itemTemplate:_.template(['
','
<%= value %>
','
<%= displayTabAlign %>
',"
"].join(""))}),this.tabList.store.comparator=function(t){return t.get("tabPos")},this.tabList.on("item:select",_.bind(this.onSelectTab,this));var e=function(){t._noApply||(t._tabListChanged=!0)};this.listenTo(this.tabList.store,"add",e),this.listenTo(this.tabList.store,"remove",e),this.listenTo(this.tabList.store,"reset",e),this.cmbAlign=new Common.UI.ComboBox({el:$("#paraadv-cmb-align"),style:"width: 108px;",menuStyle:"min-width: 108px;",editable:!1,cls:"input-group-nr",data:this._arrTabAlign}),this.cmbAlign.setValue(1),this.btnAddTab=new Common.UI.Button({el:$("#paraadv-button-add-tab")}),this.btnAddTab.on("click",_.bind(this.addTab,this)),this.btnRemoveTab=new Common.UI.Button({el:$("#paraadv-button-remove-tab")}),this.btnRemoveTab.on("click",_.bind(this.removeTab,this)),this.btnRemoveAll=new Common.UI.Button({el:$("#paraadv-button-remove-all")}),this.btnRemoveAll.on("click",_.bind(this.removeAllTabs,this)),this.on("show",function(t){t.getChild(".footer .primary").focus()}),this.afterRender()},getSettings:function(){this._tabListChanged&&(null!==this._changedProps.asc_getTabs()&&void 0!==this._changedProps.asc_getTabs()||this._changedProps.asc_putTabs(new Asc.asc_CParagraphTabs),this.tabList.store.each(function(t,e){var i=new Asc.asc_CParagraphTab(Common.Utils.Metric.fnRecalcToMM(t.get("tabPos")),t.get("tabAlign"));this._changedProps.asc_getTabs().add_Tab(i)},this));var t=this.cmbTextAlignment.getValue();return this._changedProps.asc_putJc(void 0!==t&&null!==t?t:c_paragraphTextAlignment.LEFT),null!==this.Spacing&&this._changedProps.asc_putSpacing(this.Spacing),{paragraphProps:this._changedProps}},_setDefaults:function(t){if(t){this._originalProps=new Asc.asc_CParagraphProperty(t),this.FirstLine=null!==t.asc_getInd()?t.asc_getInd().asc_getFirstLine():null,this.numIndentsLeft.setValue(null!==t.asc_getInd()&&null!==t.asc_getInd().asc_getLeft()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getInd().asc_getLeft()):"",!0),this.numIndentsRight.setValue(null!==t.asc_getInd()&&null!==t.asc_getInd().asc_getRight()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getInd().asc_getRight()):"",!0),this.cmbTextAlignment.setValue(void 0!==t.asc_getJc()&&null!==t.asc_getJc()?t.asc_getJc():c_paragraphTextAlignment.CENTERED,!0),void 0===this.CurSpecial&&(this.CurSpecial=0===t.asc_getInd().asc_getFirstLine()?c_paragraphSpecial.NONE_SPECIAL:t.asc_getInd().asc_getFirstLine()>0?c_paragraphSpecial.FIRST_LINE:c_paragraphSpecial.HANGING),this.cmbSpecial.setValue(this.CurSpecial),this.numSpecialBy.setValue(null!==this.FirstLine?Math.abs(Common.Utils.Metric.fnRecalcFromMM(this.FirstLine)):"",!0),this.numSpacingBefore.setValue(null!==t.asc_getSpacing()&&null!==t.asc_getSpacing().asc_getBefore()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getSpacing().asc_getBefore()):"",!0),this.numSpacingAfter.setValue(null!==t.asc_getSpacing()&&null!==t.asc_getSpacing().asc_getAfter()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getSpacing().asc_getAfter()):"",!0);var e=t.asc_getSpacing().asc_getLineRule();this.cmbLineRule.setValue(null!==e?e:"",!0),null!==t.asc_getSpacing()&&null!==t.asc_getSpacing().asc_getLine()?this.numLineHeight.setValue(e==c_paragraphLinerule.LINERULE_AUTO?t.asc_getSpacing().asc_getLine():Common.Utils.Metric.fnRecalcFromMM(t.asc_getSpacing().asc_getLine()),!0):this.numLineHeight.setValue("",!0),this._noApply=!0,this.chStrike.setValue(null!==t.asc_getStrikeout()&&void 0!==t.asc_getStrikeout()?t.asc_getStrikeout():"indeterminate",!0),this.chDoubleStrike.setValue(null!==t.asc_getDStrikeout()&&void 0!==t.asc_getDStrikeout()?t.asc_getDStrikeout():"indeterminate",!0),this.chSubscript.setValue(null!==t.asc_getSubscript()&&void 0!==t.asc_getSubscript()?t.asc_getSubscript():"indeterminate",!0),this.chSuperscript.setValue(null!==t.asc_getSuperscript()&&void 0!==t.asc_getSuperscript()?t.asc_getSuperscript():"indeterminate",!0),this.chSmallCaps.setValue(null!==t.asc_getSmallCaps()&&void 0!==t.asc_getSmallCaps()?t.asc_getSmallCaps():"indeterminate",!0),this.chAllCaps.setValue(null!==t.asc_getAllCaps()&&void 0!==t.asc_getAllCaps()?t.asc_getAllCaps():"indeterminate",!0),this.numSpacing.setValue(null!==t.asc_getTextSpacing()&&void 0!==t.asc_getTextSpacing()?Common.Utils.Metric.fnRecalcFromMM(t.asc_getTextSpacing()):"",!0),this.api.asc_setDrawImagePlaceParagraph("paragraphadv-font-img",this._originalProps),this.numDefaultTab.setValue(null!==t.asc_getDefaultTab()&&void 0!==t.asc_getDefaultTab()?Common.Utils.Metric.fnRecalcFromMM(parseFloat(t.asc_getDefaultTab().toFixed(1))):"",!0);var i=this.tabList.store,n=t.asc_getTabs();if(n){for(var o=[],s=n.asc_getCount(),a=0;an.length-1&&(o=n.length-1),n.length>0&&(this.tabList.selectByIndex(o),this.tabList.scrollToRecord(n.at(o)))}},removeAllTabs:function(t,e){this.tabList.store.reset()},onSelectTab:function(t,e,i){var n={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;n=i.toJSON()}else n=i;this.numTab.setValue(n.tabPos),this.cmbAlign.setValue(n.tabAlign)},onSpecialSelect:function(t,e){if(this.CurSpecial=e.value,this.CurSpecial===c_paragraphSpecial.NONE_SPECIAL&&this.numSpecialBy.setValue(0,!0),this._changedProps){null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd);var i=Common.Utils.Metric.fnRecalcToMM(this.numSpecialBy.getNumberValue());0===i&&(this.numSpecialBy.setValue(Common.Utils.Metric.fnRecalcFromMM(this._arrSpecial[e.value].defaultValue),!0),i=this._arrSpecial[e.value].defaultValue),this.CurSpecial===c_paragraphSpecial.HANGING&&(i=-i),this._changedProps.asc_getInd().put_FirstLine(i)}},onFirstLineChange:function(t,e,i,n){if(this._changedProps){null!==this._changedProps.asc_getInd()&&void 0!==this._changedProps.asc_getInd()||this._changedProps.put_Ind(new Asc.asc_CParagraphInd);var o=Common.Utils.Metric.fnRecalcToMM(t.getNumberValue());this.CurSpecial===c_paragraphSpecial.HANGING?o=-o:this.CurSpecial===c_paragraphSpecial.NONE_SPECIAL&&o>0?(this.CurSpecial=c_paragraphSpecial.FIRST_LINE,this.cmbSpecial.setValue(c_paragraphSpecial.FIRST_LINE)):0===o&&(this.CurSpecial=c_paragraphSpecial.NONE_SPECIAL,this.cmbSpecial.setValue(c_paragraphSpecial.NONE_SPECIAL)),this._changedProps.asc_getInd().put_FirstLine(o)}},onLineRuleSelect:function(t,e){if(null===this.Spacing){var i=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=i.asc_getSpacing()}this.Spacing.LineRule=e.value;var n=_.findWhere(this._arrLineRule,{value:e.value}),o=this._arrLineRule.indexOf(n);this.CurLineRuleIdx!==o&&(this.numLineHeight.setDefaultUnit(this._arrLineRule[o].defaultUnit),this.numLineHeight.setMinValue(this._arrLineRule[o].minValue),this.numLineHeight.setStep(this._arrLineRule[o].step),this.Spacing.LineRule===c_paragraphLinerule.LINERULE_AUTO?this.numLineHeight.setValue(this._arrLineRule[o].defaultValue):this.numLineHeight.setValue(Common.Utils.Metric.fnRecalcFromMM(this._arrLineRule[o].defaultValue)),this.CurLineRuleIdx=o)},onNumLineHeightChange:function(t,e,i,n){if(""!==this.cmbLineRule.getRawValue()){if(null===this.Spacing){var o=this._originalProps?this._originalProps:new Asc.asc_CParagraphProperty;this.Spacing=o.asc_getSpacing()}this.Spacing.Line=this.cmbLineRule.getValue()==c_paragraphLinerule.LINERULE_AUTO?t.getNumberValue():Common.Utils.Metric.fnRecalcToMM(t.getNumberValue())}},textTitle:"Paragraph - Advanced Settings",strIndentsLeftText:"Left",strIndentsRightText:"Right",strParagraphIndents:"Indents & Spacing",strParagraphFont:"Font",cancelButtonText:"Cancel",okButtonText:"Ok",textEffects:"Effects",textCharacterSpacing:"Character Spacing",strDoubleStrike:"Double strikethrough",strStrike:"Strikethrough",strSuperscript:"Superscript",strSubscript:"Subscript",strSmallCaps:"Small caps",strAllCaps:"All caps",strTabs:"Tab",textSet:"Specify",textRemove:"Remove",textRemoveAll:"Remove All",textTabLeft:"Left",textTabRight:"Right",textTabCenter:"Center",textAlign:"Alignment",textTabPosition:"Tab Position",textDefault:"Default Tab",noTabs:"The specified tabs will appear in this field",textJustified:"Justified",strIndentsSpecial:"Special",textNoneSpecial:"(none)",textFirstLine:"First line",textHanging:"Hanging",strIndentsSpacingBefore:"Before",strIndentsSpacingAfter:"After",strIndentsLineSpacing:"Line Spacing",txtAutoText:"Auto",textAuto:"Multiple",textExact:"Exactly",strIndent:"Indents",strSpacing:"Spacing"},SSE.Views.ParagraphSettingsAdvanced||{}))}),define("text!spreadsheeteditor/main/app/template/ImageSettingsAdvanced.template",[],function(){ return'
\n
\n
\n \n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n
\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n
\n \n
\n
\n
'}),define("spreadsheeteditor/main/app/view/ImageSettingsAdvanced",["text!spreadsheeteditor/main/app/template/ImageSettingsAdvanced.template","common/main/lib/view/AdvancedSettingsWindow","common/main/lib/component/InputField","common/main/lib/component/MetricSpinner","common/main/lib/component/CheckBox"],function(t){"use strict";SSE.Views.ImageSettingsAdvanced=Common.Views.AdvancedSettingsWindow.extend(_.extend({options:{contentWidth:300,height:342,toggleGroup:"image-adv-settings-group",properties:null,storageName:"sse-image-settings-adv-category"},initialize:function(e){_.extend(this.options,{title:this.textTitle,items:[{panelId:"id-adv-image-rotate",panelCaption:this.textRotation},{panelId:"id-adv-image-alttext",panelCaption:this.textAlt}],contentTemplate:_.template(t)({scope:this})},e),Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this,this.options),this._originalProps=this.options.imageProps,this._changedProps=null},render:function(){Common.Views.AdvancedSettingsWindow.prototype.render.call(this);var t=this;this.spnAngle=new Common.UI.MetricSpinner({el:$("#image-advanced-spin-angle"),step:1,width:80,defaultUnit:"°",value:"0 °",maxValue:3600,minValue:-3600}),this.chFlipHor=new Common.UI.CheckBox({el:$("#image-advanced-checkbox-hor"),labelText:this.textHorizontally}),this.chFlipVert=new Common.UI.CheckBox({el:$("#image-advanced-checkbox-vert"),labelText:this.textVertically}),this.inputAltTitle=new Common.UI.InputField({el:$("#image-advanced-alt-title"),allowBlank:!0,validateOnBlur:!1,style:"width: 100%;"}).on("changed:after",function(){t.isAltTitleChanged=!0}),this.textareaAltDescription=this.$window.find("textarea"),this.textareaAltDescription.keydown(function(e){e.keyCode==Common.UI.Keys.RETURN&&e.stopPropagation(),t.isAltDescChanged=!0}),this.afterRender()},afterRender:function(){if(this._setDefaults(this._originalProps),this.storageName){var t=Common.localStorage.getItem(this.storageName);this.setActiveCategory(null!==t?parseInt(t):0)}},_setDefaults:function(t){if(t){var e=t.asc_getTitle();this.inputAltTitle.setValue(e||""),e=t.asc_getDescription(),this.textareaAltDescription.val(e||""),e=t.asc_getRot(),this.spnAngle.setValue(void 0==e||null===e?"":Math.floor(180*e/3.14159265358979+.5),!0),this.chFlipHor.setValue(t.asc_getFlipH()),this.chFlipVert.setValue(t.asc_getFlipV());var i=t.asc_getPluginGuid();this.btnsCategory[0].setVisible(null===i||void 0===i),this._changedProps=new Asc.asc_CImgProperty}},getSettings:function(){return this.isAltTitleChanged&&this._changedProps.asc_putTitle(this.inputAltTitle.getValue()),this.isAltDescChanged&&this._changedProps.asc_putDescription(this.textareaAltDescription.val()),this._changedProps.asc_putRot(3.14159265358979*this.spnAngle.getNumberValue()/180),this._changedProps.asc_putFlipH("checked"==this.chFlipHor.getValue()),this._changedProps.asc_putFlipV("checked"==this.chFlipVert.getValue()),{imageProps:this._changedProps}},textTitle:"Image - Advanced Settings",cancelButtonText:"Cancel",okButtonText:"Ok",textAlt:"Alternative Text",textAltTitle:"Title",textAltDescription:"Description",textAltTip:"The alternative text-based representation of the visual object information, which will be read to the people with vision or cognitive impairments to help them better understand what information there is in the image, autoshape, chart or table.",textRotation:"Rotation",textAngle:"Angle",textFlipped:"Flipped",textHorizontally:"Horizontally",textVertically:"Vertically"},SSE.Views.ImageSettingsAdvanced||{}))}),define("spreadsheeteditor/main/app/view/SetValueDialog",["common/main/lib/component/Window","common/main/lib/component/ComboBox"],function(){"use strict";SSE.Views.SetValueDialog=Common.UI.Window.extend(_.extend({options:{width:214,header:!0,style:"min-width: 214px;",cls:"modal-dlg"},initialize:function(t){_.extend(this.options,{title:this.textTitle},t||{}),this.template=['
','
','
',"
",'"].join(""),this.options.tpl=_.template(this.template)(this.options),this.startvalue=this.options.startvalue,this.maxvalue=this.options.maxvalue,this.defaultUnit=this.options.defaultUnit,this.step=this.options.step,Common.UI.Window.prototype.initialize.call(this,this.options)},render:function(){if(Common.UI.Window.prototype.render.call(this),this.spnSize=new Common.UI.MetricSpinner({el:$("#id-spin-set-value"),width:182,step:this.step,defaultUnit:this.defaultUnit,minValue:0,maxValue:this.maxvalue,value:null!==this.startvalue?this.startvalue+" "+this.defaultUnit:""}),null!==this.startvalue){var t=this;setTimeout(function(){var e=t.spnSize.$input[0];document.selection?t.spnSize.$input.select():(e.selectionStart=0,e.selectionEnd=t.startvalue.toString().length)},10)}this.getChild().find(".dlg-btn").on("click",_.bind(this.onBtnClick,this)),this.spnSize.on("entervalue",_.bind(this.onPrimary,this)),this.options.rounding&&this.spnSize.on("change",_.bind(this.onChange,this)),this.spnSize.$el.find("input").focus()},_handleInput:function(t){this.options.handler&&this.options.handler.call(this,this,t),this.close()},onBtnClick:function(t){this._handleInput(t.currentTarget.attributes.result.value)},onChange:function(){var t=this.spnSize.getNumberValue();t/=this.step,t=(t|t)*this.step,this.spnSize.setValue(t,!0)},getSettings:function(){return this.spnSize.getNumberValue()},onPrimary:function(){return this._handleInput("ok"),!1},cancelButtonText:"Cancel",okButtonText:"Ok",txtMinText:"The minimum value for this field is {0}",txtMaxText:"The maximum value for this field is {0}"},SSE.Views.SetValueDialog||{}))}),void 0===Common)var Common={};define("common/main/lib/component/ColorPaletteExt",["common/main/lib/component/BaseView"],function(){"use strict";Common.UI.ColorPaletteExt=Common.UI.BaseView.extend({options:{dynamiccolors:10,allowReselect:!0,cls:"",style:""},template:_.template(['
',"<% var me = this; %>","<% $(colors).each(function(num, item) { %>","<% if (me.isColor(item)) { %>",'
',' ',"
","<% } else if (me.isTransparent(item)) { %>",'
',' ',"
","<% } else if (me.isEffect(item)) { %>",'
',' ',"
","<% } %>","<% }); %>","
"].join("")),colorRe:/(?:^|\s)color-(.{6})(?:\s|$)/,selectedCls:"selected",initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t),this.id=this.options.id,this.cls=this.options.cls,this.style=this.options.style,this.colors=this.options.colors||[],this.value=this.options.value,this.options.el&&this.render(),this.options.value&&this.select(this.options.value,!0)},render:function(t){var e=this;return e.rendered?this.cmpEl=$(this.el):(this.cmpEl=$(this.template({id:this.id,cls:this.cls,style:this.style,colors:this.colors})),t?(this.setElement(t,!1),t.html(this.cmpEl)):$(this.el).html(this.cmpEl),this.cmpEl.on("click",_.bind(this.handleClick,this))),e.rendered=!0,this},isColor:function(t){return"string"==typeof t&&/[0-9A-F]{6}/.test(t)},isTransparent:function(t){return"string"==typeof t&&"transparent"==t},isEffect:function(t){return"object"==typeof t&&void 0!==t.effectId},getColor:function(){return this.value},handleClick:function(t){var e,i=this,n=$(t.target).closest("div.palette-color-item");if(0!=n.length)if(n.hasClass("color-transparent"))$(i.el).find("div."+i.selectedCls).removeClass(i.selectedCls),n.addClass(i.selectedCls),i.value="transparent",i.trigger("select",i,"transparent");else if((!/^[a-fA-F0-9]{6}$/.test(i.value)||_.indexOf(i.colors,i.value)<0)&&(i.value=!1),$(i.el).find("div."+i.selectedCls).removeClass(i.selectedCls),n.addClass(i.selectedCls),e=n[0].className.match(i.colorRe)[1],n.hasClass("palette-color-effect")){var o=parseInt(n.attr("effectid"));e&&(i.value=e.toUpperCase(),i.trigger("select",i,{color:e,effectId:o}))}else/#?[a-fA-F0-9]{6}/.test(e)&&(e=/#?([a-fA-F0-9]{6})/.exec(e)[1].toUpperCase(),i.value=e,i.trigger("select",i,e))},select:function(t,e){var i=$(this.el);if(i.find("div."+this.selectedCls).removeClass(this.selectedCls),t)if("object"==typeof t){var n;void 0!==t.effectId?(n=i.find('div[effectid="'+t.effectId+'"]').first(),n.length>0?(n.addClass(this.selectedCls),this.value=n[0].className.match(this.colorRe)[1].toUpperCase()):this.value=!1):void 0!==t.effectValue&&(n=i.find('div[effectvalue="'+t.effectValue+'"].color-'+t.color.toUpperCase()).first(),n.length>0?(n.addClass(this.selectedCls),this.value=n[0].className.match(this.colorRe)[1].toUpperCase()):this.value=!1)}else if(/#?[a-fA-F0-9]{6}/.test(t)&&(t=/#?([a-fA-F0-9]{6})/.exec(t)[1].toUpperCase(),this.value=t),/^[a-fA-F0-9]{6}|transparent$/.test(t)&&_.indexOf(this.colors,t)>=0)_.indexOf(this.colors,this.value)<0&&(this.value=!1),(t!=this.value||this.options.allowReselect)&&("transparent"==t?i.find("div.color-transparent").addClass(this.selectedCls):i.find("div.palette-color.color-"+t).first().addClass(this.selectedCls),this.value=t,!0!==e&&this.fireEvent("select",this,t));else{var o=i.find("#"+t).first();0==o.length&&(o=i.find('div[color="'+t+'"]').first()),o.length>0&&(o.addClass(this.selectedCls),this.value=t.toUpperCase())}},updateColors:function(t){void 0!==t&&(this.colors=t,this.cmpEl=$(this.template({id:this.id,cls:this.cls,style:this.style,colors:this.colors})),$(this.el).html(this.cmpEl),this.cmpEl.on("click",_.bind(this.handleClick,this)))},clearSelection:function(t){$(this.el).find("div."+this.selectedCls).removeClass(this.selectedCls),this.value=void 0}})}),define("spreadsheeteditor/main/app/view/AutoFilterDialog",["common/main/lib/component/Window","common/main/lib/component/ColorPaletteExt"],function(){"use strict";SSE.Views=SSE.Views||{},SSE.Views.DigitalFilterDialog=Common.UI.Window.extend(_.extend({initialize:function(t){var e=this,i={};_.extend(i,{width:501,height:230,contentWidth:180,header:!0,cls:"filter-dlg",contentTemplate:"",title:e.txtTitle,items:[]},t),this.template=t.template||['
','
','",'
','
','
',"
","
",'
','
',"
",'
','
','
',"
","
","
",'
','"].join(""),this.api=t.api,this.handler=t.handler,this.type=t.type||"number",i.tpl=_.template(this.template)(i),Common.UI.Window.prototype.initialize.call(this,i)},render:function(){Common.UI.Window.prototype.render.call(this),this.conditions=[{value:Asc.c_oAscCustomAutoFilter.equals,displayValue:this.capCondition1},{value:Asc.c_oAscCustomAutoFilter.doesNotEqual,displayValue:this.capCondition2},{value:Asc.c_oAscCustomAutoFilter.isGreaterThan,displayValue:this.capCondition3},{value:Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo,displayValue:this.capCondition4},{value:Asc.c_oAscCustomAutoFilter.isLessThan,displayValue:this.capCondition5},{value:Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo,displayValue:this.capCondition6}],"text"==this.type&&(this.conditions=this.conditions.concat([{value:Asc.c_oAscCustomAutoFilter.beginsWith,displayValue:this.capCondition7},{value:Asc.c_oAscCustomAutoFilter.doesNotBeginWith,displayValue:this.capCondition8},{value:Asc.c_oAscCustomAutoFilter.endsWith,displayValue:this.capCondition9},{value:Asc.c_oAscCustomAutoFilter.doesNotEndWith,displayValue:this.capCondition10},{value:Asc.c_oAscCustomAutoFilter.contains,displayValue:this.capCondition11},{value:Asc.c_oAscCustomAutoFilter.doesNotContain,displayValue:this.capCondition12}])),this.cmbCondition1=new Common.UI.ComboBox({el:$("#id-search-begin-digital-combo",this.$window),menuStyle:"min-width: 225px;max-height: 135px;",cls:"input-group-nr",data:this.conditions,scrollAlwaysVisible:!0,editable:!1}),this.cmbCondition1.setValue(Asc.c_oAscCustomAutoFilter.equals),this.conditions.splice(0,0,{value:0,displayValue:this.textNoFilter}),this.cmbCondition2=new Common.UI.ComboBox({el:$("#id-search-end-digital-combo",this.$window),menuStyle:"min-width: 225px;max-height: 135px;",cls:"input-group-nr",data:this.conditions,scrollAlwaysVisible:!0,editable:!1}),this.cmbCondition2.setValue(0),this.rbAnd=new Common.UI.RadioBox({el:$("#id-and-radio",this.$window),labelText:this.capAnd,name:"asc-radio-filter-tab",checked:!0}),this.rbOr=new Common.UI.RadioBox({el:$("#id-or-radio",this.$window),labelText:this.capOr,name:"asc-radio-filter-tab"}),this.cmbValue1=new Common.UI.ComboBox({el:$("#id-sd-cell-search-begin",this.$window),cls:"input-group-nr",menuStyle:"min-width: 225px;max-height: 135px;",scrollAlwaysVisible:!0,data:[]}),this.cmbValue2=new Common.UI.ComboBox({el:$("#id-sd-cell-search-end",this.$window),cls:"input-group-nr",menuStyle:"min-width: 225px;max-height: 135px;",scrollAlwaysVisible:!0,data:[]});var t=function(t,e){var i=t.get("intval"),n=e.get("intval"),o=void 0!==i;return o!==(void 0!==n)?o?-1:1:(!o&&(i=t.get("value").toLowerCase())&&(n=e.get("value").toLowerCase()),i==n?0:""==n||""!==i&&i1?n[1].asc_getOperator()||0:0),this.cmbValue1.setValue(null===n[0].asc_getVal()?"":n[0].asc_getVal()),this.cmbValue2.setValue(n.length>1?null===n[1].asc_getVal()?"":n[1].asc_getVal():"")}}},save:function(){if(this.api&&this.properties&&this.rbOr&&this.rbAnd&&this.cmbCondition1&&this.cmbCondition2&&this.cmbValue1&&this.cmbValue2){var t=this.properties.asc_getFilterObj();t.asc_setFilter(new Asc.CustomFilters),t.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters);var e=t.asc_getFilter();e.asc_setCustomFilters(0==this.cmbCondition2.getValue()?[new Asc.CustomFilter]:[new Asc.CustomFilter,new Asc.CustomFilter]);var i=e.asc_getCustomFilters();e.asc_setAnd(this.rbAnd.getValue()),i[0].asc_setOperator(this.cmbCondition1.getValue()),i[0].asc_setVal(this.cmbValue1.getValue()),0!==this.cmbCondition2.getValue()&&(i[1].asc_setOperator(this.cmbCondition2.getValue()||void 0),i[1].asc_setVal(this.cmbValue2.getValue())),this.api.asc_applyAutoFilter(this.properties)}},onPrimary:function(){return this.save(),this.close(),!1},cancelButtonText:"Cancel",capAnd:"And",capCondition1:"equals",capCondition10:"does not end with",capCondition11:"contains",capCondition12:"does not contain",capCondition2:"does not equal",capCondition3:"is greater than",capCondition4:"is greater than or equal to",capCondition5:"is less than",capCondition6:"is less than or equal to",capCondition7:"begins with",capCondition8:"does not begin with",capCondition9:"ends with",capOr:"Or",textNoFilter:"no filter",textShowRows:"Show rows where",textUse1:"Use ? to present any single character",textUse2:"Use * to present any series of character",txtTitle:"Custom Filter"},SSE.Views.DigitalFilterDialog||{})),SSE.Views.Top10FilterDialog=Common.UI.Window.extend(_.extend({initialize:function(t){var e=this,i={};_.extend(i,{width:318,height:160,contentWidth:180,header:!0,cls:"filter-dlg",contentTemplate:"",title:e.txtTitle,items:[]},t),this.template=t.template||['
','
','
','",'
',"
",'
','','
',"
",'
','','
',"
","
","
",'
','"].join(""),this.api=t.api,this.handler=t.handler,i.tpl=_.template(this.template)(i),Common.UI.Window.prototype.initialize.call(this,i)},render:function(){Common.UI.Window.prototype.render.call(this),this.cmbType=new Common.UI.ComboBox({el:$("#id-top10-type-combo",this.$window),style:"width: 85px;",menuStyle:"min-width: 85px;",cls:"input-group-nr",data:[{value:!0,displayValue:this.txtTop},{value:!1,displayValue:this.txtBottom}],editable:!1}),this.cmbType.setValue(!0),this.cmbItem=new Common.UI.ComboBox({el:$("#id-top10-item-combo",this.$window),style:"width: 85px;",menuStyle:"min-width: 85px;",cls:"input-group-nr",data:[{value:!1,displayValue:this.txtItems},{value:!0,displayValue:this.txtPercent}],editable:!1}),this.cmbItem.setValue(!1),this.cmbItem.on("selected",_.bind(function(t,e){this.spnCount.setDefaultUnit(e.value?"%":"")},this)),this.spnCount=new Common.UI.MetricSpinner({el:$("#id-top10-count-spin"),step:1,width:85,defaultUnit:"",value:"10",maxValue:500,minValue:1}),this.$window.find(".dlg-btn").on("click",_.bind(this.onBtnClick,this)),this.loadDefaults()},show:function(){Common.UI.Window.prototype.show.call(this);var t=this;_.defer(function(){t.spnCount&&t.spnCount.$input.focus()},500)},close:function(){this.api&&this.api.asc_enableKeyEvents(!0),Common.UI.Window.prototype.close.call(this)},onBtnClick:function(t){t.currentTarget.attributes&&t.currentTarget.attributes.result&&("ok"===t.currentTarget.attributes.result.value&&this.save(),this.close())},setSettings:function(t){this.properties=t},loadDefaults:function(){if(this.properties){var t=this.properties.asc_getFilterObj();if(t.asc_getType()==Asc.c_oAscAutoFilterTypes.Top10){var e=t.asc_getFilter(),i=e.asc_getTop(),n=e.asc_getPercent();this.cmbType.setValue(i||null===i),this.cmbItem.setValue(n||null===n),this.spnCount.setDefaultUnit(n||null===n?"%":""),this.spnCount.setValue(e.asc_getVal())}}},save:function(){if(this.api&&this.properties){var t=this.properties.asc_getFilterObj();t.asc_setFilter(new Asc.Top10),t.asc_setType(Asc.c_oAscAutoFilterTypes.Top10);var e=t.asc_getFilter();e.asc_setTop(this.cmbType.getValue()),e.asc_setPercent(this.cmbItem.getValue()),e.asc_setVal(this.spnCount.getNumberValue()),this.api.asc_applyAutoFilter(this.properties)}},onPrimary:function(){return this.save(),this.close(),!1},cancelButtonText:"Cancel",okButtonText:"OK",txtTitle:"Top 10 AutoFilter",textType:"Show",txtTop:"Top",txtBottom:"Bottom",txtItems:"Item",txtPercent:"Percent"},SSE.Views.Top10FilterDialog||{})),SSE.Views.AutoFilterDialog=Common.UI.Window.extend(_.extend({initialize:function(t){var e=this,i={},n=void 0,o=void 0;Common.Utils.InternalSettings.get("sse-settings-size-filter-window")&&(n=Common.Utils.InternalSettings.get("sse-settings-size-filter-window")[0],o=Common.Utils.InternalSettings.get("sse-settings-size-filter-window")[1]),_.extend(i,{width:n||450,height:o||265,contentWidth:n-50||400,header:!1,cls:"filter-dlg",contentTemplate:"",title:e.txtTitle,modal:!1,animate:!1,items:[],resizable:!0,minwidth:450,minheight:265},t),this.template=t.template||['
','
','
','','
','
',"
","
",'","
",'","
"].join(""),this.api=t.api,this.handler=t.handler,this.throughIndexes=[],this.filteredIndexes=[],this.curSize=null,i.tpl=_.template(this.template)(i),Common.UI.Window.prototype.initialize.call(this,i),this.on("resizing",_.bind(this.onWindowResizing,this)),this.on("resize",_.bind(this.onWindowResize,this))},render:function(){var t=this;Common.UI.Window.prototype.render.call(this);var e=this.$window.find(".resize-border");this.$window.find(".resize-border.left, .resize-border.top").css({cursor:"default"}),e.css({background:"none",border:"none"}),e.removeClass("left"),e.removeClass("top"),this.$window.find(".btn").on("click",_.bind(this.onBtnClick,this)),this.btnOk=new Common.UI.Button({cls:"btn normal dlg-btn primary",caption:this.okButtonText,style:"margin-right:10px;",enableToggle:!1,allowDepress:!1}),this.btnOk&&(this.btnOk.render($("#id-apply-filter",this.$window)),this.btnOk.on("click",_.bind(this.onApplyFilter,this))),this.miSortLow2High=new Common.UI.MenuItem({caption:this.txtSortLow2High,toggleGroup:"menufiltersort",checkable:!0,checked:!1}),this.miSortLow2High.on("click",_.bind(this.onSortType,this,Asc.c_oAscSortOptions.Ascending)),this.miSortHigh2Low=new Common.UI.MenuItem({caption:this.txtSortHigh2Low,toggleGroup:"menufiltersort",checkable:!0,checked:!1}),this.miSortHigh2Low.on("click",_.bind(this.onSortType,this,Asc.c_oAscSortOptions.Descending)),this.miSortCellColor=new Common.UI.MenuItem({caption:this.txtSortCellColor,toggleGroup:"menufiltersort",checkable:!0,checked:!1,menu:new Common.UI.Menu({style:"min-width: inherit; padding: 0px;",menuAlign:"tl-tr",items:[{template:_.template('
')}]})}),this.miSortFontColor=new Common.UI.MenuItem({caption:this.txtSortFontColor,toggleGroup:"menufiltersort",checkable:!0,checked:!1,menu:new Common.UI.Menu({style:"min-width: inherit; padding: 0px;",menuAlign:"tl-tr",items:[{template:_.template('
')}]})}),this.miNumFilter=new Common.UI.MenuItem({caption:this.txtNumFilter,toggleGroup:"menufilterfilter",checkable:!0,checked:!1,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{value:Asc.c_oAscCustomAutoFilter.equals,caption:this.txtEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.doesNotEqual,caption:this.txtNotEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isGreaterThan,caption:this.txtGreater,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo,caption:this.txtGreaterEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isLessThan,caption:this.txtLess,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo,caption:this.txtLessEquals,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:-2,caption:this.txtBetween,checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters},{value:Asc.c_oAscCustomAutoFilter.top10,caption:this.txtTop10,checkable:!0,type:Asc.c_oAscAutoFilterTypes.Top10},{value:Asc.c_oAscDynamicAutoFilter.aboveAverage,caption:this.txtAboveAve,checkable:!0,type:Asc.c_oAscAutoFilterTypes.DynamicFilter},{value:Asc.c_oAscDynamicAutoFilter.belowAverage,caption:this.txtBelowAve,checkable:!0,type:Asc.c_oAscAutoFilterTypes.DynamicFilter},{value:-1,caption:this.btnCustomFilter+"...",checkable:!0,type:Asc.c_oAscAutoFilterTypes.CustomFilters}]})});for(var i=this.miNumFilter.menu.items,n=0;n
')}]})}),this.miFilterFontColor=new Common.UI.MenuItem({caption:this.txtFilterFontColor,toggleGroup:"menufilterfilter",checkable:!0,checked:!1,menu:new Common.UI.Menu({style:"min-width: inherit; padding: 0px;",menuAlign:"tl-tr",items:[{template:_.template('
')}]})}),this.miClear=new Common.UI.MenuItem({caption:this.txtClear,checkable:!1}),this.miClear.on("click",_.bind(this.onClear,this)),this.miReapply=new Common.UI.MenuItem({caption:this.txtReapply,checkable:!1}),this.miReapply.on("click",_.bind(this.onReapply,this)),this.filtersMenu=new Common.UI.Menu({items:[this.miSortLow2High,this.miSortHigh2Low,this.miSortCellColor,this.miSortFontColor,{caption:"--"},this.miNumFilter,this.miTextFilter,this.miFilterCellColor,this.miFilterFontColor,this.miClear,{caption:"--"},this.miReapply]});var o=this.$window.find("#menu-container-filters");this.filtersMenu.render(o),this.filtersMenu.cmpEl.attr({tabindex:"-1"}),this.mnuSortColorCellsPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-sort-cells-color"),colors:[]}),this.mnuSortColorCellsPicker.on("select",_.bind(this.onSortColorSelect,this,Asc.c_oAscSortOptions.ByColorFill)),this.mnuSortColorFontPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-sort-font-color"),colors:[]}),this.mnuSortColorFontPicker.on("select",_.bind(this.onSortColorSelect,this,Asc.c_oAscSortOptions.ByColorFont)), this.mnuFilterColorCellsPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-filter-cells-color"),colors:[]}),this.mnuFilterColorCellsPicker.on("select",_.bind(this.onFilterColorSelect,this,!0)),this.mnuFilterColorFontPicker=new Common.UI.ColorPaletteExt({el:$("#filter-dlg-filter-font-color"),colors:[]}),this.mnuFilterColorFontPicker.on("select",_.bind(this.onFilterColorSelect,this,!1)),this.input=new Common.UI.InputField({el:$("#id-sd-cell-search",this.$window),allowBlank:!0,placeHolder:this.txtEmpty,validateOnChange:!0,validation:function(){return!0}}).on("changing",function(e,i){i.length?(i=i.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1"),t.filter=new RegExp(i,"ig")):t.filter=void 0,t.setupDataCells()}),this.cells=new Common.UI.DataViewStore,this.filterExcludeCells=new Common.UI.DataViewStore,this.cells&&(this.cellsList=new Common.UI.ListView({el:$("#id-dlg-filter-values",this.$window),store:this.cells,simpleAddMode:!0,template:_.template(['
'].join("")),itemTemplate:_.template(["
",'",'
','
<%= Common.Utils.String.htmlEncode(value) %>
','<% if (typeof count !=="undefined" && count) { %>','
<%= count%>
',"<% } %>","
","
"].join(""))}),this.cellsList.store.comparator=function(t,e){if("0"==t.get("groupid"))return-1;if("0"==e.get("groupid"))return 1;if("2"==t.get("groupid"))return-1;if("2"==e.get("groupid"))return 1;var i=t.get("intval"),n=e.get("intval"),o=void 0!==i;return o!==(void 0!==n)?o?-1:1:(!o&&(i=t.get("cellvalue").toLowerCase())&&(n=e.get("cellvalue").toLowerCase()),i==n?0:""==n||""!==i&&i1?r[1].asc_getOperator()||0:0,i=null===r[0].asc_getVal()?"":r[0].asc_getVal(),n=r.length>1?null===r[1].asc_getVal()?"":r[1].asc_getVal():""}if(-1!==t.value){var c=new Asc.CustomFilters;c.asc_setCustomFilters(-2==t.value?[new Asc.CustomFilter,new Asc.CustomFilter]:[new Asc.CustomFilter]);var h=c.asc_getCustomFilters();if(h[0].asc_setOperator(-2==t.value?Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo:t.value),-2==t.value){var d=o==Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo&&s==Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo;c.asc_setAnd(!d||a),h[0].asc_setVal(d?i:""),h[1].asc_setOperator(Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo),h[1].asc_setVal(d?n:"")}else c.asc_setAnd(!0),h[0].asc_setVal(t.value==o?i:"");e.asc_setFilter(c),e.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters)}var p=this,m=new SSE.Views.DigitalFilterDialog({api:this.api,type:"number"}).on({close:function(){p.close()}});this.close(),m.setSettings(this.configTo),m.show()},onTextFilterMenuClick:function(t,e){var i=this.configTo.asc_getFilterObj(),n="",o=Asc.c_oAscCustomAutoFilter.equals;if(i.asc_getType()==Asc.c_oAscAutoFilterTypes.CustomFilters){var s=i.asc_getFilter(),a=s.asc_getCustomFilters();s.asc_getAnd(),o=a[0].asc_getOperator(),a.length>1?a[1].asc_getOperator()||0:0,n=null===a[0].asc_getVal()?"":a[0].asc_getVal(),a.length>1?null===a[1].asc_getVal()?"":a[1].asc_getVal():""}if(-1!==e.value){var l=new Asc.CustomFilters;l.asc_setCustomFilters([new Asc.CustomFilter]);var r=l.asc_getCustomFilters();l.asc_setAnd(!0),r[0].asc_setOperator(e.value),r[0].asc_setVal(e.value==o?n:""),i.asc_setFilter(l),i.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters)}var c=this,h=new SSE.Views.DigitalFilterDialog({api:this.api,type:"text"}).on({close:function(){c.close()}});this.close(),h.setSettings(this.configTo),h.show()},onNumDynamicFilterItemClick:function(t){var e=this.configTo.asc_getFilterObj();e.asc_getType()!==Asc.c_oAscAutoFilterTypes.DynamicFilter&&(e.asc_setFilter(new Asc.DynamicFilter),e.asc_setType(Asc.c_oAscAutoFilterTypes.DynamicFilter)),e.asc_getFilter().asc_setType(t.value),this.api.asc_applyAutoFilter(this.configTo),this.close()},onTop10FilterItemClick:function(t,e){var i=this,n=new SSE.Views.Top10FilterDialog({api:this.api}).on({close:function(){i.close()}});this.close(),n.setSettings(this.configTo),n.show()},onFilterColorSelect:function(t,e,i){var n=this.configTo.asc_getFilterObj();n.asc_getType()!==Asc.c_oAscAutoFilterTypes.ColorFilter&&(n.asc_setFilter(new Asc.ColorFilter),n.asc_setType(Asc.c_oAscAutoFilterTypes.ColorFilter));var o=n.asc_getFilter();o.asc_setCellColor(!!t&&null),o.asc_setCColor(t&&"transparent"==i||!t&&"#000000"==i?null:Common.Utils.ThemeColor.getRgbColor(i)),this.api.asc_applyAutoFilter(this.configTo),this.close()},onSortColorSelect:function(t,e,i){if(this.api&&this.configTo){var n=t==Asc.c_oAscSortOptions.ByColorFill;this.api.asc_sortColFilter(t,this.configTo.asc_getCellId(),this.configTo.asc_getDisplayName(),n&&"transparent"==i||!n&&"#000000"==i?null:Common.Utils.ThemeColor.getRgbColor(i))}this.close()},onCellCheck:function(t,e,i){if(!this.checkCellTrigerBlock){var n="",o="",s=!1,a=null,l=window.event?window.event:window._event;if(l){if(o=l.target.type,n=$(l.currentTarget).find(".list-item"),n.length){a=n.get(0).getBoundingClientRect();var r=l.clientX*Common.Utils.zoom(),c=l.clientY*Common.Utils.zoom();a.left1&&(o[parseInt(t.get("throughIndex"))]=i))});else{e.set("check",i),o[parseInt(e.get("throughIndex"))]=i;for(var s=i,a=0;a0;if(this.miSortFontColor.setVisible(c),this.miFilterFontColor.setVisible(c),c){var h=[];a.forEach(function(t,e){t?h.push(Common.Utils.ThemeColor.getHexColor(t.get_r(),t.get_g(),t.get_b()).toLocaleUpperCase()):h.push("000000")}),this.mnuSortColorFontPicker.updateColors(h),this.mnuFilterColorFontPicker.updateColors(h),this.miFilterFontColor.setChecked(!1,!0),this.miSortFontColor.setChecked(l==Asc.c_oAscSortOptions.ByColorFont,!0),l==Asc.c_oAscSortOptions.ByColorFont&&this.mnuSortColorFontPicker.select(r||"000000",!0)}if(c=s&&s.length>0,this.miSortCellColor.setVisible(c),this.miFilterCellColor.setVisible(c),c){var h=[];s.forEach(function(t,e){t?h.push(Common.Utils.ThemeColor.getHexColor(t.get_r(),t.get_g(),t.get_b()).toLocaleUpperCase()):h.push("transparent")}),this.mnuSortColorCellsPicker.updateColors(h),this.mnuFilterColorCellsPicker.updateColors(h),this.miFilterCellColor.setChecked(!1,!0),this.miSortCellColor.setChecked(l==Asc.c_oAscSortOptions.ByColorFill,!0),l==Asc.c_oAscSortOptions.ByColorFill&&this.mnuSortColorCellsPicker.select(r||"transparent",!0)}if(e){var d=t.asc_getFilter(),p=d.asc_getCustomFilters(),m=(d.asc_getAnd(),p[0].asc_getOperator()),u=p.length>1?p[1].asc_getOperator()||0:0,g=o?this.miTextFilter.menu.items:this.miNumFilter.menu.items,b=!0;1==p.length?g.forEach(function(t){var e=t.options.type==Asc.c_oAscAutoFilterTypes.CustomFilters&&t.value==m;t.setChecked(e,!0),e&&(b=!1)}):!o&&(m==Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo&&u==Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo||m==Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo&&u==Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo)&&(g[6].setChecked(!0,!0),b=!1),b&&g[g.length-1].setChecked(!0,!0)}else if(this.initialFilterType===Asc.c_oAscAutoFilterTypes.ColorFilter){var f=t.asc_getFilter(),C=f.asc_getCColor();C&&(C=Common.Utils.ThemeColor.getHexColor(C.get_r(),C.get_g(),C.get_b()).toLocaleUpperCase()),null===f.asc_getCellColor()?(this.miFilterCellColor.setChecked(!0,!0),this.mnuFilterColorCellsPicker.select(C||"transparent",!0)):!1===f.asc_getCellColor()&&(this.miFilterFontColor.setChecked(!0,!0),this.mnuFilterColorFontPicker.select(C||"000000",!0))}else if(i||n){var v=i?t.asc_getFilter().asc_getType():null,g=this.miNumFilter.menu.items;g.forEach(function(t){t.setChecked(i&&t.options.type==Asc.c_oAscAutoFilterTypes.DynamicFilter&&t.value==v||n&&t.options.type==Asc.c_oAscAutoFilterTypes.Top10,!0)})}this.miClear.setDisabled(this.initialFilterType===Asc.c_oAscAutoFilterTypes.None),this.miReapply.setDisabled(this.initialFilterType===Asc.c_oAscAutoFilterTypes.None),this.btnOk.setDisabled(this.initialFilterType!==Asc.c_oAscAutoFilterTypes.Filters&&this.initialFilterType!==Asc.c_oAscAutoFilterTypes.None)},setupDataCells:function(){function t(t){return!isNaN(parseFloat(t))&&isFinite(t)}var e,i,n,o=this,s=0,a=2,l=!0,r=!1,c=0,h=[],d=[],p=o.filter?o.filteredIndexes:o.throughIndexes;this.configTo.asc_getValues().forEach(function(r){i=r.asc_getText(),e=t(i),l=!0,n=r.asc_getRepeats?r.asc_getRepeats():void 0,o.filter?(null===i.match(o.filter)&&(l=!1),p[a]=l):void 0==p[a]&&(p[a]=r.asc_getVisible()),l?(h.push(new Common.UI.DataViewModel({id:++s,selected:!1,allowSelected:!0,cellvalue:i,value:e?i:i.length>0?i:o.textEmptyItem,intval:e?parseFloat(i):void 0,strval:e?"":i,groupid:"1",check:p[a],throughIndex:a,count:n?n.toString():""})),p[a]&&c++):d.push(new Common.UI.DataViewModel({cellvalue:i})),++a}),c==h.length?r=!0:c>0&&(r="indeterminate"),(o.filter||void 0==p[0])&&(p[0]=!0),(!o.filter||h.length>0)&&h.unshift(new Common.UI.DataViewModel({id:++s,selected:!1,allowSelected:!0,value:o.filter?this.textSelectAllResults:this.textSelectAll,groupid:"0",check:p[0],throughIndex:0})),o.filter&&h.length>1&&(void 0==p[1]&&(p[1]=!1),h.splice(1,0,new Common.UI.DataViewModel({id:++s,selected:!1,allowSelected:!0,value:this.textAddSelection,groupid:"2",check:p[1],throughIndex:1}))),this.cells.reset(h),this.filterExcludeCells.reset(d),this.cells.length&&(this.checkCellTrigerBlock=!0,this.cells.at(0).set("check",r),this.checkCellTrigerBlock=void 0),this.btnOk.setDisabled(this.cells.length<1),this.cellsList.scroller.update({minScrollbarLength:40,alwaysVisibleY:!0,suppressScrollX:!0}),this.cellsList.cmpEl.toggleClass("scroll-padding",this.cellsList.scroller.isVisible())},testFilter:function(){var t=this,e=!1;return this.cells&&(this.filter&&this.filteredIndexes[1]?e=!0:this.cells.forEach(function(t){if("1"==t.get("groupid")&&t.get("check"))return e=!0,!0})),e||(t._skipCheckDocumentClick=!0,Common.UI.warning({title:this.textWarning,msg:this.warnNoSelected,callback:function(){t._skipCheckDocumentClick=!1,_.delay(function(){t.input.$el.find("input").focus()},100,this)}})),e},save:function(){if(this.api&&this.configTo&&this.cells&&this.filterExcludeCells){var t=this.configTo.asc_getValues(),e=!1;if(this.filter&&this.filteredIndexes[1])this.initialFilterType===Asc.c_oAscAutoFilterTypes.CustomFilters&&t.forEach(function(t,e){t.asc_setVisible(!0)}),this.cells.each(function(e){"1"==e.get("groupid")&&t[parseInt(e.get("throughIndex"))-2].asc_setVisible(e.get("check"))}),t.forEach(function(t,i){if(t.asc_getVisible())return e=!0,!0});else{var i=this.filter?this.filteredIndexes:this.throughIndexes;t.forEach(function(t,e){t.asc_setVisible(i[e+2])}),e=!0}e&&(this.configTo.asc_getFilterObj().asc_setType(Asc.c_oAscAutoFilterTypes.Filters),this.api.asc_applyAutoFilter(this.configTo))}},onPrimary:function(){return this.save(),this.close(),!1},onWindowResize:function(t){if(t&&"start"==t[1])this.curSize={resize:!1,height:this.getSize()[1]};else if(this.curSize.resize){var e=this.getSize();this.$window.find(".combo-values").css({height:e[1]-100+"px"}),this.cellsList.scroller.update({minScrollbarLength:40,alwaysVisibleY:!0,suppressScrollX:!0})}},onWindowResizing:function(){if(this.curSize){var t=this.getSize();t[1]!==this.curSize.height&&(this.curSize.resize||(this.curSize.resize=!0,this.cellsList.scroller.update({minScrollbarLength:40,alwaysVisibleY:!1,suppressScrollX:!0})),this.$window.find(".combo-values").css({height:t[1]-100+"px"}),this.curSize.height=t[1]),Common.Utils.InternalSettings.set("sse-settings-size-filter-window",t)}},okButtonText:"Ok",btnCustomFilter:"Custom Filter",textSelectAll:"Select All",txtTitle:"Filter",warnNoSelected:"You must choose at least one value",textWarning:"Warning",cancelButtonText:"Cancel",textEmptyItem:"{Blanks}",txtEmpty:"Enter cell's filter",txtSortLow2High:"Sort Lowest to Highest",txtSortHigh2Low:"Sort Highest to Lowest",txtSortCellColor:"Sort by cells color",txtSortFontColor:"Sort by font color",txtNumFilter:"Number filter",txtTextFilter:"Text filter",txtFilterCellColor:"Filter by cells color",txtFilterFontColor:"Filter by font color",txtClear:"Clear",txtReapply:"Reapply",txtEquals:"Equals...",txtNotEquals:"Does not equal...",txtGreater:"Greater than...",txtGreaterEquals:"Greater than or equal to...",txtLess:"Less than...",txtLessEquals:"Less than or equal to...",txtBetween:"Between...",txtTop10:"Top 10",txtAboveAve:"Above average",txtBelowAve:"Below average",txtBegins:"Begins with...",txtNotBegins:"Does not begin with...",txtEnds:"Ends with...",txtNotEnds:"Does not end with...",txtContains:"Contains...",txtNotContains:"Does not contain...",textSelectAllResults:"Select All Search Results",textAddSelection:"Add current selection to filter"},SSE.Views.AutoFilterDialog||{}))});var c_paragraphLinerule={LINERULE_AUTO:1,LINERULE_EXACT:2},c_paragraphTextAlignment={RIGHT:0,LEFT:1,CENTERED:2,JUSTIFIED:3},c_paragraphSpecial={NONE_SPECIAL:0,FIRST_LINE:1,HANGING:2};if(define("spreadsheeteditor/main/app/controller/DocumentHolder",["core","common/main/lib/util/utils","common/main/lib/util/Shortcuts","common/main/lib/view/CopyWarningDialog","common/main/lib/view/OpenDialog","spreadsheeteditor/main/app/view/DocumentHolder","spreadsheeteditor/main/app/view/HyperlinkSettingsDialog","spreadsheeteditor/main/app/view/ParagraphSettingsAdvanced","spreadsheeteditor/main/app/view/ImageSettingsAdvanced","spreadsheeteditor/main/app/view/SetValueDialog","spreadsheeteditor/main/app/view/AutoFilterDialog"],function(){"use strict";SSE.Controllers.DocumentHolder=Backbone.Controller.extend(_.extend({models:[],collections:[],views:["DocumentHolder"],initialize:function(){var t=this;t.tooltips={hyperlink:{},comment:{},coauth:{ttHeight:20},row_column:{ttHeight:20},filter:{ttHeight:40},func_arg:{},input_msg:{}},t.mouse={},t.popupmenu=!1,t.rangeSelectionMode=!1,t.namedrange_locked=!1,t._currentMathObj=void 0,t._currentParaObjDisabled=!1,t._isDisabled=!1,t._state={},this.wrapEvents={apiHideComment:_.bind(this.onApiHideComment,this)},this.addListeners({DocumentHolder:{createdelayedelements:this.onCreateDelayedElements}});var e={};this.hkComments="alt+h",e[this.hkComments]=function(){return t.onAddComment(),!1},Common.util.Shortcuts.delegateShortcuts({shortcuts:e})},onLaunch:function(){var t=this;t.documentHolder=this.createView("DocumentHolder"),t.documentHolder.render(),t.documentHolder.el.tabIndex=-1,$(document).on("mousewheel",_.bind(t.onDocumentWheel,t)),$(document).on("mousedown",_.bind(t.onDocumentRightDown,t)),$(document).on("mouseup",_.bind(t.onDocumentRightUp,t)),$(document).on("keydown",_.bind(t.onDocumentKeyDown,t)),$(document).on("mousemove",_.bind(t.onDocumentMouseMove,t)),$(window).on("resize",_.bind(t.onDocumentResize,t)),SSE.getController("Viewport").getView("Viewport").hlayout.on("layout:resizedrag",_.bind(t.onDocumentResize,t)),Common.NotificationCenter.on({"window:show":function(e){t.hideHyperlinkTip()},"modal:show":function(e){t.hideCoAuthTips()},"layout:changed":function(e){t.hideHyperlinkTip(),t.hideCoAuthTips(),t.onDocumentResize()},"cells:range":function(e){t.onCellsRange(e)}}),Common.Gateway.on("processmouse",_.bind(t.onProcessMouse,t))},onCreateDelayedElements:function(t){var e=this;e.permissions.isEdit?(t.pmiCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiImgCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiImgCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiImgPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiTextCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiTextCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiTextPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiCommonCut.on("click",_.bind(e.onCopyPaste,e)),t.pmiCommonCopy.on("click",_.bind(e.onCopyPaste,e)),t.pmiCommonPaste.on("click",_.bind(e.onCopyPaste,e)),t.pmiInsertEntire.on("click",_.bind(e.onInsertEntire,e)),t.pmiDeleteEntire.on("click",_.bind(e.onDeleteEntire,e)),t.pmiInsertCells.menu.on("item:click",_.bind(e.onInsertCells,e)),t.pmiDeleteCells.menu.on("item:click",_.bind(e.onDeleteCells,e)),t.pmiSparklines.menu.on("item:click",_.bind(e.onClear,e)),t.pmiSortCells.menu.on("item:click",_.bind(e.onSortCells,e)),t.pmiFilterCells.menu.on("item:click",_.bind(e.onFilterCells,e)),t.pmiReapply.on("click",_.bind(e.onReapply,e)),t.pmiClear.menu.on("item:click",_.bind(e.onClear,e)),t.pmiSelectTable.menu.on("item:click",_.bind(e.onSelectTable,e)),t.pmiInsertTable.menu.on("item:click",_.bind(e.onInsertTable,e)),t.pmiDeleteTable.menu.on("item:click",_.bind(e.onDeleteTable,e)),t.pmiInsFunction.on("click",_.bind(e.onInsFunction,e)),t.menuAddHyperlink.on("click",_.bind(e.onInsHyperlink,e)),t.menuEditHyperlink.on("click",_.bind(e.onInsHyperlink,e)),t.menuRemoveHyperlink.on("click",_.bind(e.onDelHyperlink,e)),t.pmiRowHeight.menu.on("item:click",_.bind(e.onSetSize,e)),t.pmiColumnWidth.menu.on("item:click",_.bind(e.onSetSize,e)),t.pmiEntireHide.on("click",_.bind(e.onEntireHide,e)),t.pmiEntireShow.on("click",_.bind(e.onEntireShow,e)),t.pmiFreezePanes.on("click",_.bind(e.onFreezePanes,e)),t.pmiEntriesList.on("click",_.bind(e.onEntriesList,e)),t.pmiAddComment.on("click",_.bind(e.onAddComment,e)),t.pmiAddNamedRange.on("click",_.bind(e.onAddNamedRange,e)),t.menuImageArrange.menu.on("item:click",_.bind(e.onImgMenu,e)),t.menuImgRotate.menu.on("item:click",_.bind(e.onImgMenu,e)),t.menuImgCrop.menu.on("item:click",_.bind(e.onImgCrop,e)),t.menuImageAlign.menu.on("item:click",_.bind(e.onImgMenuAlign,e)),t.menuParagraphVAlign.menu.on("item:click",_.bind(e.onParagraphVAlign,e)),t.menuParagraphDirection.menu.on("item:click",_.bind(e.onParagraphDirection,e)),t.menuParagraphBullets.menu.on("item:click",_.bind(e.onSelectNoneBullet,e)),t.menuAddHyperlinkShape.on("click",_.bind(e.onInsHyperlink,e)),t.menuEditHyperlinkShape.on("click",_.bind(e.onInsHyperlink,e)),t.menuRemoveHyperlinkShape.on("click",_.bind(e.onRemoveHyperlinkShape,e)),t.pmiTextAdvanced.on("click",_.bind(e.onTextAdvanced,e)),t.mnuShapeAdvanced.on("click",_.bind(e.onShapeAdvanced,e)),t.mnuChartEdit.on("click",_.bind(e.onChartEdit,e)),t.mnuImgAdvanced.on("click",_.bind(e.onImgAdvanced,e)),t.textInShapeMenu.on("render:after",_.bind(e.onTextInShapeAfterRender,e)),t.menuSignatureEditSign.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureEditSetup.on("click",_.bind(e.onSignatureClick,e)),t.menuImgOriginalSize.on("click",_.bind(e.onOriginalSizeClick,e)),t.menuImgReplace.menu.on("item:click",_.bind(e.onImgReplace,e)),t.pmiNumFormat.menu.on("item:click",_.bind(e.onNumberFormatSelect,e)),t.pmiNumFormat.menu.on("show:after",_.bind(e.onNumberFormatOpenAfter,e)),t.pmiAdvancedNumFormat.on("click",_.bind(e.onCustomNumberFormat,e))):(t.menuViewCopy.on("click",_.bind(e.onCopyPaste,e)),t.menuViewUndo.on("click",_.bind(e.onUndo,e)),t.menuViewAddComment.on("click",_.bind(e.onAddComment,e)),t.menuSignatureViewSign.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureDetails.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureViewSetup.on("click",_.bind(e.onSignatureClick,e)),t.menuSignatureRemove.on("click",_.bind(e.onSignatureClick,e)));var i=t.cmpEl;if(i){i.on({mousedown:function(t){"canvas"==t.target.localName&&2!=t.button&&Common.UI.Menu.Manager.hideAll()},click:function(t){e.api&&(e.api.isTextAreaBlur=!1,"canvas"!=t.target.localName||e.isEditFormula||i.focus())}});var n=/Firefox/i.test(navigator.userAgent)?"DOMMouseScroll":"mousewheel";!function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)}(t.el,n,_.bind(this.onDocumentWheel,this)),e.cellEditor=$("#ce-cell-content")}},loadConfig:function(t){this.editorConfig=t.config},setMode:function(t){this.permissions=t,this.permissions.canCoAuthoring&&this.permissions.canComments?Common.util.Shortcuts.resumeEvents(this.hkComments):Common.util.Shortcuts.suspendEvents(this.hkComments)},setApi:function(t){return this.api=t,this.api.asc_registerCallback("asc_onContextMenu",_.bind(this.onApiContextMenu,this)),this.api.asc_registerCallback("asc_onMouseMove",_.bind(this.onApiMouseMove,this)),this.api.asc_registerCallback("asc_onHideComment",this.wrapEvents.apiHideComment),this.api.asc_registerCallback("asc_onHyperlinkClick",_.bind(this.onApiHyperlinkClick,this)),this.api.asc_registerCallback("asc_onCoAuthoringDisconnect",_.bind(this.onApiCoAuthoringDisconnect,this)),Common.NotificationCenter.on("api:disconnect",_.bind(this.onApiCoAuthoringDisconnect,this)),this.api.asc_registerCallback("asc_onSelectionChanged",_.bind(this.onSelectionChanged,this)),!0===this.permissions.isEdit&&(this.api.asc_registerCallback("asc_onSetAFDialog",_.bind(this.onApiAutofilter,this)),this.api.asc_registerCallback("asc_onEditCell",_.bind(this.onApiEditCell,this)),this.api.asc_registerCallback("asc_onLockDefNameManager",_.bind(this.onLockDefNameManager,this)),this.api.asc_registerCallback("asc_onEntriesListMenu",_.bind(this.onEntriesListMenu,this)),this.api.asc_registerCallback("asc_onFormulaCompleteMenu",_.bind(this.onFormulaCompleteMenu,this)),this.api.asc_registerCallback("asc_onShowSpecialPasteOptions",_.bind(this.onShowSpecialPasteOptions,this)),this.api.asc_registerCallback("asc_onHideSpecialPasteOptions",_.bind(this.onHideSpecialPasteOptions,this)),this.api.asc_registerCallback("asc_onToggleAutoCorrectOptions",_.bind(this.onToggleAutoCorrectOptions,this)),this.api.asc_registerCallback("asc_onFormulaInfo",_.bind(this.onFormulaInfo,this)),this.api.asc_registerCallback("asc_ChangeCropState",_.bind(this.onChangeCropState,this)),this.api.asc_registerCallback("asc_onInputMessage",_.bind(this.onInputMessage,this))),this},resetApi:function(t){this.api.asc_unregisterCallback("asc_onHideComment",this.wrapEvents.apiHideComment),this.api.asc_registerCallback("asc_onHideComment",this.wrapEvents.apiHideComment)},onCopyPaste:function(t){var e=this;if(e.api){if("cut"==t.value?e.api.asc_Cut():"copy"==t.value?e.api.asc_Copy():e.api.asc_Paste())Common.component.Analytics.trackEvent("ToolBar","Copy Warning");else{var i=Common.localStorage.getItem("sse-hide-copywarning");i&&1==parseInt(i)||new Common.Views.CopyWarningDialog({handler:function(t){t&&Common.localStorage.setItem("sse-hide-copywarning",1),Common.NotificationCenter.trigger("edit:complete",e.documentHolder)}}).show()}}Common.NotificationCenter.trigger("edit:complete",e.documentHolder)},onInsertEntire:function(t){if(this.api){switch(this.api.asc_getCellInfo().asc_getFlags().asc_getSelectionType()){case Asc.c_oAscSelectionType.RangeRow:this.api.asc_insertCells(Asc.c_oAscInsertOptions.InsertRows);break;case Asc.c_oAscSelectionType.RangeCol:this.api.asc_insertCells(Asc.c_oAscInsertOptions.InsertColumns)}Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Insert Entire")}},onInsertCells:function(t,e){this.api&&(this.api.asc_insertCells(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Insert Cells"))},onDeleteEntire:function(t){if(this.api){switch(this.api.asc_getCellInfo().asc_getFlags().asc_getSelectionType()){case Asc.c_oAscSelectionType.RangeRow:this.api.asc_deleteCells(Asc.c_oAscDeleteOptions.DeleteRows);break;case Asc.c_oAscSelectionType.RangeCol:this.api.asc_deleteCells(Asc.c_oAscDeleteOptions.DeleteColumns)}Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Delete Entire")}},onDeleteCells:function(t,e){this.api&&(this.api.asc_deleteCells(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Delete Cells"))},onSortCells:function(t,e){if(this.api){var i=this.api.asc_sortCellsRangeExpand();if(i){var n={width:500,title:this.txtSorting,msg:this.txtExpandSort,buttons:[{caption:this.txtExpand,primary:!0,value:"expand"},{caption:this.txtSortSelected,primary:!0,value:"sort"},"cancel"],callback:_.bind(function(t){"expand"!=t&&"sort"!=t||this.api.asc_sortColFilter(e.value,"",void 0,e.value==Asc.c_oAscSortOptions.ByColorFill?this.documentHolder.ssMenu.cellColor:this.documentHolder.ssMenu.fontColor,"expand"==t),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Sort Cells")},this)};Common.UI.alert(n)}else this.api.asc_sortColFilter(e.value,"",void 0,e.value==Asc.c_oAscSortOptions.ByColorFill?this.documentHolder.ssMenu.cellColor:this.documentHolder.ssMenu.fontColor,null!==i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Sort Cells")}},onFilterCells:function(t,e){if(this.api){var i=new Asc.AutoFiltersOptions,n=new Asc.AutoFilterObj;if(e.value>0){n.asc_setFilter(new Asc.ColorFilter),n.asc_setType(Asc.c_oAscAutoFilterTypes.ColorFilter);var o=n.asc_getFilter();o.asc_setCellColor(1==e.value&&null),o.asc_setCColor(1==e.value?this.documentHolder.ssMenu.cellColor:this.documentHolder.ssMenu.fontColor)}else{n.asc_setFilter(new Asc.CustomFilters),n.asc_setType(Asc.c_oAscAutoFilterTypes.CustomFilters);var s=n.asc_getFilter();s.asc_setCustomFilters([new Asc.CustomFilter]),s.asc_setAnd(!0);s.asc_getCustomFilters()[0].asc_setOperator(Asc.c_oAscCustomAutoFilter.equals)}i.asc_setFilterObj(n),this.api.asc_applyAutoFilterByType(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Filter Cells")}},onReapply:function(){this.api.asc_reapplyAutoFilter(this.documentHolder.ssMenu.formatTableName)},onClear:function(t,e){this.api&&(this.api.asc_emptyCells(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Clear"))},onSelectTable:function(t,e){this.api&&this.documentHolder.ssMenu.formatTableName&&(this.api.asc_changeSelectionFormatTable(this.documentHolder.ssMenu.formatTableName,e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Select Table"))},onInsertTable:function(t,e){this.api&&this.documentHolder.ssMenu.formatTableName&&(this.api.asc_insertCellsInTable(this.documentHolder.ssMenu.formatTableName,e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Insert to Table"))},onDeleteTable:function(t,e){this.api&&this.documentHolder.ssMenu.formatTableName&&(this.api.asc_deleteCellsInTable(this.documentHolder.ssMenu.formatTableName,e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Delete from Table"))},onInsFunction:function(t){var e=this.getApplication().getController("FormulaDialog");e&&this.api&&e.showDialog()},onInsHyperlink:function(t){var e,i,n=this;if(n.api){for(var o=n.api.asc_getWorksheetsCount(),s=-1,a=[];++s-1&&e.value<6?(this.api.asc_setSelectedDrawingObjectAlign(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Objects Align")):6==e.value?(this.api.asc_DistributeSelectedDrawingObjectHor(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")):7==e.value&&(this.api.asc_DistributeSelectedDrawingObjectVer(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")))},onParagraphVAlign:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVerticalTextAlign(e.value),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Paragraph Vertical Align")}},onParagraphDirection:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVert(e.options.direction),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Text Direction")}},onSelectNoneBullet:function(t,e){this.api&&-1==e.options.value&&(this.api.asc_setListType(e.options.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type"))},onSelectBullets:function(t,e,i,n){var o={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;o=i.toJSON()}else o=i;this.api&&this.api.asc_setListType(o.type,o.subtype),"click"!==n.type&&this.documentHolder.textInShapeMenu.hide(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type")},onRemoveHyperlinkShape:function(t){this.api&&(this.api.asc_removeHyperlink(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Remove Hyperlink"))},onTextAdvanced:function(t){var e=this;new SSE.Views.ParagraphSettingsAdvanced({paragraphProps:t.textInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.paragraphProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced paragraph settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onShapeAdvanced:function(t){var e=this;new SSE.Views.ShapeSettingsAdvanced({shapeProps:t.shapeInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.shapeProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced shape settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onImgAdvanced:function(t){var e=this;new SSE.Views.ImageSettingsAdvanced({imageProps:t.imageInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.imageProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced image settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onChartEdit:function(t){var e,i=this;i.api&&(e=i.api.asc_getChartObject())&&new SSE.Views.ChartSettingsDlg({chartSettings:e,imageSettings:t.chartInfo,isChart:!0,api:i.api,handler:function(t,e){"ok"==t&&i.api&&(i.api.asc_editChartDrawingObject(e.chartSettings),e.imageSettings&&i.api.asc_setGraphicObjectProps(e.imageSettings)),Common.NotificationCenter.trigger("edit:complete",i)}}).show()},onApiCoAuthoringDisconnect:function(){this.permissions.isEdit=!1},hideCoAuthTips:function(){this.tooltips.coauth.ref&&($(this.tooltips.coauth.ref).remove(),this.tooltips.coauth.ref=void 0,this.tooltips.coauth.x_point=void 0,this.tooltips.coauth.y_point=void 0)},hideHyperlinkTip:function(){!this.tooltips.hyperlink.isHidden&&this.tooltips.hyperlink.ref&&(this.tooltips.hyperlink.ref.hide(),this.tooltips.hyperlink.ref=void 0,this.tooltips.hyperlink.text="",this.tooltips.hyperlink.isHidden=!0)},onApiMouseMove:function(t){if(!this._isFullscreenMenu&&t.length){for(var e,i,n,o,s,a,l=t.length;l>0;l--)switch(t[l-1].asc_getType()){case Asc.c_oAscMouseMoveType.Hyperlink:e=l;break;case Asc.c_oAscMouseMoveType.Comment:i=l;break;case Asc.c_oAscMouseMoveType.LockedObject:n=l;break;case Asc.c_oAscMouseMoveType.ResizeColumn:o=l;break;case Asc.c_oAscMouseMoveType.ResizeRow:s=l;break;case Asc.c_oAscMouseMoveType.Filter:a=l}var r=this,c=[0,0],h=r.tooltips.coauth,d=r.tooltips.comment,p=r.tooltips.hyperlink,m=r.tooltips.row_column,u=r.tooltips.filter,g=[r.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),r.documentHolder.cmpEl.offset().top-$(window).scrollTop()];if(e||r.hideHyperlinkTip(),void 0===o&&void 0===s&&!m.isHidden&&m.ref&&(m.ref.hide(),m.ref=void 0,m.text="",m.isHidden=!0),(r.permissions.isEdit||r.permissions.canViewComments)&&(!i||this.popupmenu)&&(d.moveCommentId=void 0,void 0!=d.viewCommentId)){d={};var b=this.getApplication().getController("Common.Controllers.Comments");b&&(this.permissions.canCoAuthoring&&this.permissions.canViewComments?setTimeout(function(){b.onApiHideComment(!0)},200):b.onApiHideComment(!0))}r.permissions.isEdit&&(n||r.hideCoAuthTips()),(void 0===a||r.dlgFilter&&r.dlgFilter.isVisible()||r.currentMenu&&r.currentMenu.isVisible())&&!u.isHidden&&u.ref&&(u.ref.hide(),u.ref=void 0,u.text="",u.isHidden=!0);if(e){p.parentEl||(p.parentEl=$('
'),r.documentHolder.cmpEl.append(p.parentEl));var f=t[e-1],C=f.asc_getHyperlink();if(C.asc_getType()==Asc.c_oAscHyperlinkType.WebLink){var v=C.asc_getTooltip();v=v?Common.Utils.String.htmlEncode(v)+"
"+r.textCtrlClick+"":C.asc_getHyperlinkUrl()+"
"+r.textCtrlClick+""}else v=C.asc_getTooltip()||C.asc_getLocation(),v+="
"+r.textCtrlClick+"";if(p.ref&&p.ref.isVisible()&&p.text!=v&&(p.ref.hide(),p.ref=void 0,p.text="",p.isHidden=!0),!p.ref||!p.ref.isVisible()){p.text=v,p.ref=new Common.UI.Tooltip({owner:p.parentEl,html:!0,title:v}),p.ref.show([-1e4,-1e4]),p.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20,c[1]-=p.ref.getBSTip().$tip.height();var _=p.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_),p.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if(void 0!==o||void 0!==s){m.parentEl||(m.parentEl=$('
'),r.documentHolder.cmpEl.append(m.parentEl));var f=t[void 0!==o?o-1:s-1],y=Common.Utils.String.format(void 0!==o?this.textChangeColumnWidth:this.textChangeRowHeight,f.asc_getSizeCCOrPt().toFixed(2),f.asc_getSizePx().toFixed());if(m.ref&&m.ref.isVisible()&&m.text!=y&&(m.text=y,m.ref.setTitle(y),m.ref.updateTitle()),!m.ref||!m.ref.isVisible()){m.text=y,m.ref=new Common.UI.Tooltip({owner:m.parentEl,html:!0,title:y}),m.ref.show([-1e4,-1e4]),m.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20-m.ttHeight;var _=m.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),m.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if((r.permissions.isEdit||r.permissions.canViewComments)&&i&&!this.popupmenu&&(f=t[i-1],!d.editCommentId&&d.moveCommentId!=f.asc_getCommentIndexes()[0])){d.moveCommentId=f.asc_getCommentIndexes()[0],d.moveCommentTimer&&clearTimeout(d.moveCommentTimer);var x=f.asc_getCommentIndexes(),w=f.asc_getX(),S=f.asc_getY(),A=f.asc_getReverseX();d.moveCommentTimer=setTimeout(function(){if(d.moveCommentId&&!d.editCommentId){d.viewCommentId=d.moveCommentId;var t=r.getApplication().getController("Common.Controllers.Comments");t&&(t.isSelectedComment||t.onApiShowComment(x,w,S,A,!1,!0))}},400)}if(r.permissions.isEdit&&n&&(f=t[n-1],h.XY||r.onDocumentResize(),h.x_point!=f.asc_getX()||h.y_point!=f.asc_getY())){r.hideCoAuthTips(),h.x_point=f.asc_getX(),h.y_point=f.asc_getY();var k=$(document.createElement("div")),T=f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.Sheet||f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.TableProperties;h.ref=k,k.addClass("username-tip"),k.css({height:h.ttHeight+"px",position:"absolute",zIndex:"900",visibility:"visible"}),$(document.body).append(k),c=[T?h.x_point+h.rightMenuWidth:h.bodyWidth-(h.x_point+h.XY[0]),h.y_point+h.XY[1]],c[1]>=h.XY[1]&&c[1]+h.ttHeight
'),r.documentHolder.cmpEl.append(u.parentEl));var f=t[a-1],y=r.makeFilterTip(f.asc_getFilter());if(u.ref&&u.ref.isVisible()&&u.text!=y&&(u.text=y,u.ref.setTitle(y),u.ref.updateTitle()),!u.ref||!u.ref.isVisible()){u.text=y,u.ref=new Common.UI.Tooltip({owner:u.parentEl,html:!0,title:y,cls:"auto-tooltip"}),u.ref.show([-1e4,-1e4]),u.isHidden=!1,c=[f.asc_getX()+g[0]-10,f.asc_getY()+g[1]+20];u.ref.getBSTip().$tip.width();c[1]+u.ttHeight>r.tooltips.coauth.bodyHeight&&(c[1]=r.tooltips.coauth.bodyHeight-u.ttHeight-5,c[0]+=20);var _=u.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),u.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}}},onApiHideComment:function(){this.tooltips.comment.viewCommentId=this.tooltips.comment.editCommentId=this.tooltips.comment.moveCommentId=void 0},onApiHyperlinkClick:function(t){if(!t)return void Common.UI.alert({msg:this.errorInvalidLink,title:this.notcriticalErrorTitle,iconCls:"warn",buttons:["ok"],callback:_.bind(function(t){Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},this)});if(this.api.asc_getUrlType(t)>0){var e=window.open(t,"_blank");e&&e.focus()}},onApiAutofilter:function(t){var e=this;if(!e.tooltips.filter.isHidden&&e.tooltips.filter.ref&&(e.tooltips.filter.ref.hide(),e.tooltips.filter.ref=void 0,e.tooltips.filter.text="",e.tooltips.filter.isHidden=!0),e.permissions.isEdit&&!e.dlgFilter){e.dlgFilter=new SSE.Views.AutoFilterDialog({api:this.api}).on({close:function(){e.api&&e.api.asc_enableKeyEvents(!0),e.dlgFilter=void 0}}),e.api&&e.api.asc_enableKeyEvents(!1),Common.UI.Menu.Manager.hideAll(),e.dlgFilter.setSettings(t);var i=e.documentHolder.cmpEl.offset(),n=t.asc_getCellCoord(),o=n.asc_getX()+n.asc_getWidth()+i.left,s=n.asc_getY()+n.asc_getHeight()+i.top,a=Common.Utils.innerWidth(),l=Common.Utils.innerHeight();o+e.dlgFilter.options.width>a&&(o=a-e.dlgFilter.options.width-5),s+e.dlgFilter.options.height>l&&(s=l-e.dlgFilter.options.height-5),e.dlgFilter.show(o,s)}},makeFilterTip:function(t){var e=t.asc_getFilterObj(),i=e.asc_getType(),n=(t.asc_getIsTextFilter(),t.asc_getColorsFill(),t.asc_getColorsFont(),"");if(i===Asc.c_oAscAutoFilterTypes.CustomFilters){var o=e.asc_getFilter(),s=o.asc_getCustomFilters();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[0].asc_getOperator())+' "'+s[0].asc_getVal()+'"',s.length>1&&(n=n+" "+(o.asc_getAnd()?this.txtAnd:this.txtOr),n=n+" "+this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[1].asc_getOperator())+' "'+s[1].asc_getVal()+'"')}else if(i===Asc.c_oAscAutoFilterTypes.ColorFilter){var a=e.asc_getFilter();null===a.asc_getCellColor()?n=this.txtEqualsToCellColor:!1===a.asc_getCellColor()&&(n=this.txtEqualsToFontColor)}else if(i===Asc.c_oAscAutoFilterTypes.DynamicFilter)n=this.getFilterName(Asc.c_oAscAutoFilterTypes.DynamicFilter,e.asc_getFilter().asc_getType());else if(i===Asc.c_oAscAutoFilterTypes.Top10){var l=e.asc_getFilter(),r=l.asc_getPercent();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.Top10,l.asc_getTop()),n+=" "+l.asc_getVal()+" "+(r||null===r?this.txtPercent:this.txtItems)}else if(i===Asc.c_oAscAutoFilterTypes.Filters){var c=0,h=0,d=void 0,p=t.asc_getValues();p.forEach(function(t){t.asc_getVisible()&&(h++,c<100&&t.asc_getText()&&(n+=t.asc_getText()+"; ",c=n.length)),t.asc_getText()||(d=t.asc_getVisible())}),h==p.length?n=this.txtAll:1==h&&d?n=this.txtEquals+' "'+this.txtBlanks+'"':h==p.length-1&&0==d?n=this.txtNotEquals+' "'+this.txtBlanks+'"':(d&&(n+=this.txtBlanks+"; "),n=this.txtEquals+' "'+n.substring(0,n.length-2)+'"')}else i===Asc.c_oAscAutoFilterTypes.None&&(n=this.txtAll);return n.length>100&&(n=n.substring(0,100)+"..."),n=""+(t.asc_getColumnName()||"("+this.txtColumn+" "+t.asc_getSheetColumnName()+")")+":
"+n},getFilterName:function(t,e){var i="";if(t==Asc.c_oAscAutoFilterTypes.CustomFilters)switch(e){case Asc.c_oAscCustomAutoFilter.equals:i=this.txtEquals;break;case Asc.c_oAscCustomAutoFilter.isGreaterThan:i=this.txtGreater;break;case Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo:i=this.txtGreaterEquals;break;case Asc.c_oAscCustomAutoFilter.isLessThan:i=this.txtLess;break;case Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo:i=this.txtLessEquals;break;case Asc.c_oAscCustomAutoFilter.doesNotEqual:i=this.txtNotEquals;break;case Asc.c_oAscCustomAutoFilter.beginsWith:i=this.txtBegins;break;case Asc.c_oAscCustomAutoFilter.doesNotBeginWith:i=this.txtNotBegins;break;case Asc.c_oAscCustomAutoFilter.endsWith:i=this.txtEnds;break;case Asc.c_oAscCustomAutoFilter.doesNotEndWith:i=this.txtNotEnds;break;case Asc.c_oAscCustomAutoFilter.contains:i=this.txtContains;break;case Asc.c_oAscCustomAutoFilter.doesNotContain:i=this.txtNotContains}else if(t==Asc.c_oAscAutoFilterTypes.DynamicFilter)switch(e){case Asc.c_oAscDynamicAutoFilter.aboveAverage:i=this.txtAboveAve;break;case Asc.c_oAscDynamicAutoFilter.belowAverage:i=this.txtBelowAve}else t==Asc.c_oAscAutoFilterTypes.Top10&&(i=e||null===e?this.txtFilterTop:this.txtFilterBottom);return i},onUndo:function(){this.api&&(this.api.asc_Undo(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder))},onApiContextMenu:function(t){var e=this;_.delay(function(){e.showObjectMenu.call(e,t)},10)},onAfterRender:function(t){},onDocumentResize:function(t){var e=this;e.documentHolder&&(e.tooltips.coauth.XY=[e.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),e.documentHolder.cmpEl.offset().top-$(window).scrollTop()],e.tooltips.coauth.apiHeight=e.documentHolder.cmpEl.height(),e.tooltips.coauth.rightMenuWidth=$("#right-menu").width(),e.tooltips.coauth.bodyWidth=$(window).width(),e.tooltips.coauth.bodyHeight=$(window).height())},onDocumentWheel:function(t){if(this.api&&!this.isEditCell){var e=_.isUndefined(t.originalEvent)?t.wheelDelta:t.originalEvent.wheelDelta;if(_.isUndefined(e)&&(e=t.deltaY),(t.ctrlKey||t.metaKey)&&!t.altKey){var i=this.api.asc_getZoom();e<0?(i=Math.ceil(10*i)/10,(i-=.1)<.5||this.api.asc_setZoom(i)):e>0&&(i=Math.floor(10*i)/10,(i+=.1)>0&&!(i>2)&&this.api.asc_setZoom(i)),t.preventDefault(),t.stopPropagation()}}},onDocumentKeyDown:function(t){if(this.api){var e=t.keyCode;if(!t.ctrlKey&&!t.metaKey||t.shiftKey||t.altKey){if(e==Common.UI.Keys.F10&&t.shiftKey)return this.showObjectMenu(t),t.preventDefault(),t.stopPropagation(),!1}else if(e===Common.UI.Keys.NUM_PLUS||e===Common.UI.Keys.EQUALITY||Common.Utils.isGecko&&e===Common.UI.Keys.EQUALITY_FF||Common.Utils.isOpera&&43==e){if(!this.api.isCellEdited){var i=Math.floor(10*this.api.asc_getZoom())/10;return i+=.1,i>0&&!(i>2)&&this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}}else if((e===Common.UI.Keys.NUM_MINUS||e===Common.UI.Keys.MINUS||Common.Utils.isGecko&&e===Common.UI.Keys.MINUS_FF||Common.Utils.isOpera&&45==e)&&!this.api.isCellEdited)return i=Math.ceil(10*this.api.asc_getZoom())/10,i-=.1,i<.5||this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}},onDocumentRightDown:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!0)},onDocumentRightUp:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!1)},onProcessMouse:function(t){"mouseup"==t.type&&(this.mouse.isLeftButtonDown=!1)},onDocumentMouseMove:function(t){"canvas"!==t.target.localName&&this.hideHyperlinkTip()},showObjectMenu:function(t){!this.api||this.mouse.isLeftButtonDown||this.rangeSelectionMode||(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(this.api.asc_getCellInfo(),!0,t):this.fillViewMenuProps(this.api.asc_getCellInfo(),!0,t))},onSelectionChanged:function(t){!this.mouse.isLeftButtonDown&&!this.rangeSelectionMode&&this.currentMenu&&this.currentMenu.isVisible()&&(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(t,!0):this.fillViewMenuProps(t,!0))},fillMenuProps:function(t,e,i){var n,o,s,a,l,r,c,h,d,p,m=this.documentHolder,u=t.asc_getFlags().asc_getSelectionType(),g=t.asc_getLocked(),b=!0===t.asc_getLockedTable(),f=!1,C=this.getApplication().getController("Common.Controllers.Comments"),v=this.permissions.isEditMailMerge||this.permissions.isEditDiagram;switch(u){case Asc.c_oAscSelectionType.RangeCells:n=!0;break;case Asc.c_oAscSelectionType.RangeRow:o=!0;break;case Asc.c_oAscSelectionType.RangeCol:s=!0;break;case Asc.c_oAscSelectionType.RangeMax:a=!0;break;case Asc.c_oAscSelectionType.RangeImage:r=!v;break;case Asc.c_oAscSelectionType.RangeShape:h=!v;break;case Asc.c_oAscSelectionType.RangeChart:l=!v;break;case Asc.c_oAscSelectionType.RangeChartText:d=!v;break;case Asc.c_oAscSelectionType.RangeShapeText:c=!v}if(this.api.asc_getHeaderFooterMode()){if(!m.copyPasteMenu||!e&&!m.copyPasteMenu.isVisible())return;e&&this.showPopupMenu(m.copyPasteMenu,{},i)}else if(r||h||l){if(!m.imgMenu||!e&&!m.imgMenu.isVisible())return;r=h=l=!1,m.mnuImgAdvanced.imageInfo=void 0;for(var y,x=!1,w=this.api.asc_getGraphicObjectProps(),S=0;S-1&&e.value<6?(this.api.asc_setSelectedDrawingObjectAlign(e.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Objects Align")):6==e.value?(this.api.asc_DistributeSelectedDrawingObjectHor(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")):7==e.value&&(this.api.asc_DistributeSelectedDrawingObjectVer(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Distribute")))},onParagraphVAlign:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVerticalTextAlign(e.value),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Paragraph Vertical Align")}},onParagraphDirection:function(t,e){if(this.api){var i=new Asc.asc_CImgProperty;i.asc_putVert(e.options.direction),this.api.asc_setGraphicObjectProps(i),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Text Direction")}},onSelectNoneBullet:function(t,e){this.api&&-1==e.options.value&&(this.api.asc_setListType(e.options.value),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type"))},onSelectBullets:function(t,e,i,n){var o={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;o=i.toJSON()}else o=i;this.api&&this.api.asc_setListType(o.type,o.subtype),"click"!==n.type&&this.documentHolder.textInShapeMenu.hide(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","List Type")},onRemoveHyperlinkShape:function(t){this.api&&(this.api.asc_removeHyperlink(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Remove Hyperlink"))},onTextAdvanced:function(t){var e=this;new SSE.Views.ParagraphSettingsAdvanced({paragraphProps:t.textInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.paragraphProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced paragraph settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onShapeAdvanced:function(t){var e=this;new SSE.Views.ShapeSettingsAdvanced({shapeProps:t.shapeInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.shapeProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced shape settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onImgAdvanced:function(t){var e=this;new SSE.Views.ImageSettingsAdvanced({imageProps:t.imageInfo,api:e.api,handler:function(t,i){"ok"==t&&e.api&&(e.api.asc_setGraphicObjectProps(i.imageProps),Common.component.Analytics.trackEvent("DocumentHolder","Apply advanced image settings")),Common.NotificationCenter.trigger("edit:complete",e)}}).show()},onChartEdit:function(t){var e,i=this;i.api&&(e=i.api.asc_getChartObject())&&new SSE.Views.ChartSettingsDlg({chartSettings:e,imageSettings:t.chartInfo,isChart:!0,api:i.api,handler:function(t,e){"ok"==t&&i.api&&(i.api.asc_editChartDrawingObject(e.chartSettings),e.imageSettings&&i.api.asc_setGraphicObjectProps(e.imageSettings)),Common.NotificationCenter.trigger("edit:complete",i)}}).show()},onApiCoAuthoringDisconnect:function(){this.permissions.isEdit=!1},hideCoAuthTips:function(){this.tooltips.coauth.ref&&($(this.tooltips.coauth.ref).remove(),this.tooltips.coauth.ref=void 0,this.tooltips.coauth.x_point=void 0,this.tooltips.coauth.y_point=void 0)},hideHyperlinkTip:function(){!this.tooltips.hyperlink.isHidden&&this.tooltips.hyperlink.ref&&(this.tooltips.hyperlink.ref.hide(),this.tooltips.hyperlink.ref=void 0,this.tooltips.hyperlink.text="",this.tooltips.hyperlink.isHidden=!0)},onApiMouseMove:function(t){if(!this._isFullscreenMenu&&t.length){for(var e,i,n,o,s,a,l=t.length;l>0;l--)switch(t[l-1].asc_getType()){case Asc.c_oAscMouseMoveType.Hyperlink:e=l;break;case Asc.c_oAscMouseMoveType.Comment:i=l;break;case Asc.c_oAscMouseMoveType.LockedObject:n=l;break;case Asc.c_oAscMouseMoveType.ResizeColumn:o=l;break;case Asc.c_oAscMouseMoveType.ResizeRow:s=l;break;case Asc.c_oAscMouseMoveType.Filter:a=l}var r=this,c=[0,0],h=r.tooltips.coauth,d=r.tooltips.comment,p=r.tooltips.hyperlink,m=r.tooltips.row_column,u=r.tooltips.filter,g=[r.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),r.documentHolder.cmpEl.offset().top-$(window).scrollTop()];if(e||r.hideHyperlinkTip(),void 0===o&&void 0===s&&!m.isHidden&&m.ref&&(m.ref.hide(),m.ref=void 0,m.text="",m.isHidden=!0),(r.permissions.isEdit||r.permissions.canViewComments)&&(!i||this.popupmenu)&&(d.moveCommentId=void 0,void 0!=d.viewCommentId)){d={};var b=this.getApplication().getController("Common.Controllers.Comments");b&&(this.permissions.canCoAuthoring&&this.permissions.canViewComments?setTimeout(function(){b.onApiHideComment(!0)},200):b.onApiHideComment(!0))}r.permissions.isEdit&&(n||r.hideCoAuthTips()),(void 0===a||r.dlgFilter&&r.dlgFilter.isVisible()||r.currentMenu&&r.currentMenu.isVisible())&&!u.isHidden&&u.ref&&(u.ref.hide(),u.ref=void 0,u.text="",u.isHidden=!0);if(e){p.parentEl||(p.parentEl=$('
'),r.documentHolder.cmpEl.append(p.parentEl));var f=t[e-1],C=f.asc_getHyperlink();if(C.asc_getType()==Asc.c_oAscHyperlinkType.WebLink){var v=C.asc_getTooltip();v=v?Common.Utils.String.htmlEncode(v)+"
"+r.textCtrlClick+"":C.asc_getHyperlinkUrl()+"
"+r.textCtrlClick+""}else v=C.asc_getTooltip()||C.asc_getLocation(),v+="
"+r.textCtrlClick+"";if(p.ref&&p.ref.isVisible()&&p.text!=v&&(p.ref.hide(),p.ref=void 0,p.text="",p.isHidden=!0),!p.ref||!p.ref.isVisible()){p.text=v,p.ref=new Common.UI.Tooltip({owner:p.parentEl,html:!0,title:v}),p.ref.show([-1e4,-1e4]),p.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20,c[1]-=p.ref.getBSTip().$tip.height();var _=p.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_),p.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if(void 0!==o||void 0!==s){m.parentEl||(m.parentEl=$('
'),r.documentHolder.cmpEl.append(m.parentEl));var f=t[void 0!==o?o-1:s-1],y=Common.Utils.String.format(void 0!==o?this.textChangeColumnWidth:this.textChangeRowHeight,f.asc_getSizeCCOrPt().toFixed(2),f.asc_getSizePx().toFixed());if(m.ref&&m.ref.isVisible()&&m.text!=y&&(m.text=y,m.ref.setTitle(y),m.ref.updateTitle()),!m.ref||!m.ref.isVisible()){m.text=y,m.ref=new Common.UI.Tooltip({owner:m.parentEl,html:!0,title:y}),m.ref.show([-1e4,-1e4]),m.isHidden=!1,c=[f.asc_getX(),f.asc_getY()],c[0]+=g[0]+6,c[1]+=g[1]-20-m.ttHeight;var _=m.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),m.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}if((r.permissions.isEdit||r.permissions.canViewComments)&&i&&!this.popupmenu&&(f=t[i-1],!d.editCommentId&&d.moveCommentId!=f.asc_getCommentIndexes()[0])){d.moveCommentId=f.asc_getCommentIndexes()[0],d.moveCommentTimer&&clearTimeout(d.moveCommentTimer);var x=f.asc_getCommentIndexes(),w=f.asc_getX(),S=f.asc_getY(),A=f.asc_getReverseX();d.moveCommentTimer=setTimeout(function(){if(d.moveCommentId&&!d.editCommentId){d.viewCommentId=d.moveCommentId;var t=r.getApplication().getController("Common.Controllers.Comments");t&&(t.isSelectedComment||t.onApiShowComment(x,w,S,A,!1,!0))}},400)}if(r.permissions.isEdit&&n&&(f=t[n-1],h.XY||r.onDocumentResize(),h.x_point!=f.asc_getX()||h.y_point!=f.asc_getY())){r.hideCoAuthTips(),h.x_point=f.asc_getX(),h.y_point=f.asc_getY();var k=$(document.createElement("div")),T=f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.Sheet||f.asc_getLockedObjectType()==Asc.c_oAscMouseMoveLockedObjectType.TableProperties;h.ref=k,k.addClass("username-tip"),k.css({height:h.ttHeight+"px",position:"absolute",zIndex:"900",visibility:"visible"}),$(document.body).append(k),c=[T?h.x_point+h.rightMenuWidth:h.bodyWidth-(h.x_point+h.XY[0]),h.y_point+h.XY[1]],c[1]>=h.XY[1]&&c[1]+h.ttHeight
'),r.documentHolder.cmpEl.append(u.parentEl));var f=t[a-1],y=r.makeFilterTip(f.asc_getFilter());if(u.ref&&u.ref.isVisible()&&u.text!=y&&(u.text=y,u.ref.setTitle(y),u.ref.updateTitle()),!u.ref||!u.ref.isVisible()){u.text=y,u.ref=new Common.UI.Tooltip({owner:u.parentEl,html:!0,title:y,cls:"auto-tooltip"}),u.ref.show([-1e4,-1e4]),u.isHidden=!1,c=[f.asc_getX()+g[0]-10,f.asc_getY()+g[1]+20];u.ref.getBSTip().$tip.width();c[1]+u.ttHeight>r.tooltips.coauth.bodyHeight&&(c[1]=r.tooltips.coauth.bodyHeight-u.ttHeight-5,c[0]+=20);var _=u.ref.getBSTip().$tip.width();c[0]+_>r.tooltips.coauth.bodyWidth&&(c[0]=r.tooltips.coauth.bodyWidth-_-20),u.ref.getBSTip().$tip.css({top:c[1]+"px",left:c[0]+"px"})}}}},onApiHideComment:function(){this.tooltips.comment.viewCommentId=this.tooltips.comment.editCommentId=this.tooltips.comment.moveCommentId=void 0},onApiHyperlinkClick:function(t){if(!t)return void Common.UI.alert({msg:this.errorInvalidLink,title:this.notcriticalErrorTitle,iconCls:"warn",buttons:["ok"],callback:_.bind(function(t){Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},this)});if(this.api.asc_getUrlType(t)>0){return void window.parent.APP.openURL(t)}},onApiAutofilter:function(t){var e=this;if(!e.tooltips.filter.isHidden&&e.tooltips.filter.ref&&(e.tooltips.filter.ref.hide(),e.tooltips.filter.ref=void 0,e.tooltips.filter.text="",e.tooltips.filter.isHidden=!0),e.permissions.isEdit&&!e.dlgFilter){e.dlgFilter=new SSE.Views.AutoFilterDialog({api:this.api}).on({close:function(){e.api&&e.api.asc_enableKeyEvents(!0),e.dlgFilter=void 0}}),e.api&&e.api.asc_enableKeyEvents(!1),Common.UI.Menu.Manager.hideAll(),e.dlgFilter.setSettings(t);var i=e.documentHolder.cmpEl.offset(),n=t.asc_getCellCoord(),o=n.asc_getX()+n.asc_getWidth()+i.left,s=n.asc_getY()+n.asc_getHeight()+i.top,a=Common.Utils.innerWidth(),l=Common.Utils.innerHeight();o+e.dlgFilter.options.width>a&&(o=a-e.dlgFilter.options.width-5),s+e.dlgFilter.options.height>l&&(s=l-e.dlgFilter.options.height-5),e.dlgFilter.show(o,s)}},makeFilterTip:function(t){var e=t.asc_getFilterObj(),i=e.asc_getType(),n=(t.asc_getIsTextFilter(),t.asc_getColorsFill(),t.asc_getColorsFont(),"");if(i===Asc.c_oAscAutoFilterTypes.CustomFilters){var o=e.asc_getFilter(),s=o.asc_getCustomFilters();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[0].asc_getOperator())+' "'+s[0].asc_getVal()+'"',s.length>1&&(n=n+" "+(o.asc_getAnd()?this.txtAnd:this.txtOr),n=n+" "+this.getFilterName(Asc.c_oAscAutoFilterTypes.CustomFilters,s[1].asc_getOperator())+' "'+s[1].asc_getVal()+'"')}else if(i===Asc.c_oAscAutoFilterTypes.ColorFilter){var a=e.asc_getFilter();null===a.asc_getCellColor()?n=this.txtEqualsToCellColor:!1===a.asc_getCellColor()&&(n=this.txtEqualsToFontColor)}else if(i===Asc.c_oAscAutoFilterTypes.DynamicFilter)n=this.getFilterName(Asc.c_oAscAutoFilterTypes.DynamicFilter,e.asc_getFilter().asc_getType());else if(i===Asc.c_oAscAutoFilterTypes.Top10){var l=e.asc_getFilter(),r=l.asc_getPercent();n=this.getFilterName(Asc.c_oAscAutoFilterTypes.Top10,l.asc_getTop()),n+=" "+l.asc_getVal()+" "+(r||null===r?this.txtPercent:this.txtItems)}else if(i===Asc.c_oAscAutoFilterTypes.Filters){var c=0,h=0,d=void 0,p=t.asc_getValues();p.forEach(function(t){t.asc_getVisible()&&(h++,c<100&&t.asc_getText()&&(n+=t.asc_getText()+"; ",c=n.length)),t.asc_getText()||(d=t.asc_getVisible())}),h==p.length?n=this.txtAll:1==h&&d?n=this.txtEquals+' "'+this.txtBlanks+'"':h==p.length-1&&0==d?n=this.txtNotEquals+' "'+this.txtBlanks+'"':(d&&(n+=this.txtBlanks+"; "),n=this.txtEquals+' "'+n.substring(0,n.length-2)+'"')}else i===Asc.c_oAscAutoFilterTypes.None&&(n=this.txtAll);return n.length>100&&(n=n.substring(0,100)+"..."),n=""+(t.asc_getColumnName()||"("+this.txtColumn+" "+t.asc_getSheetColumnName()+")")+":
"+n},getFilterName:function(t,e){var i="";if(t==Asc.c_oAscAutoFilterTypes.CustomFilters)switch(e){case Asc.c_oAscCustomAutoFilter.equals:i=this.txtEquals;break;case Asc.c_oAscCustomAutoFilter.isGreaterThan:i=this.txtGreater;break;case Asc.c_oAscCustomAutoFilter.isGreaterThanOrEqualTo:i=this.txtGreaterEquals;break;case Asc.c_oAscCustomAutoFilter.isLessThan:i=this.txtLess;break;case Asc.c_oAscCustomAutoFilter.isLessThanOrEqualTo:i=this.txtLessEquals;break;case Asc.c_oAscCustomAutoFilter.doesNotEqual:i=this.txtNotEquals;break;case Asc.c_oAscCustomAutoFilter.beginsWith:i=this.txtBegins;break;case Asc.c_oAscCustomAutoFilter.doesNotBeginWith:i=this.txtNotBegins;break;case Asc.c_oAscCustomAutoFilter.endsWith:i=this.txtEnds;break;case Asc.c_oAscCustomAutoFilter.doesNotEndWith:i=this.txtNotEnds;break;case Asc.c_oAscCustomAutoFilter.contains:i=this.txtContains;break;case Asc.c_oAscCustomAutoFilter.doesNotContain:i=this.txtNotContains}else if(t==Asc.c_oAscAutoFilterTypes.DynamicFilter)switch(e){case Asc.c_oAscDynamicAutoFilter.aboveAverage:i=this.txtAboveAve;break;case Asc.c_oAscDynamicAutoFilter.belowAverage:i=this.txtBelowAve}else t==Asc.c_oAscAutoFilterTypes.Top10&&(i=e||null===e?this.txtFilterTop:this.txtFilterBottom);return i},onUndo:function(){this.api&&(this.api.asc_Undo(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder))},onApiContextMenu:function(t){var e=this;_.delay(function(){e.showObjectMenu.call(e,t)},10)},onAfterRender:function(t){},onDocumentResize:function(t){var e=this;e.documentHolder&&(e.tooltips.coauth.XY=[e.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),e.documentHolder.cmpEl.offset().top-$(window).scrollTop()],e.tooltips.coauth.apiHeight=e.documentHolder.cmpEl.height(),e.tooltips.coauth.rightMenuWidth=$("#right-menu").width(),e.tooltips.coauth.bodyWidth=$(window).width(),e.tooltips.coauth.bodyHeight=$(window).height())},onDocumentWheel:function(t){if(this.api&&!this.isEditCell){var e=_.isUndefined(t.originalEvent)?t.wheelDelta:t.originalEvent.wheelDelta;if(_.isUndefined(e)&&(e=t.deltaY),(t.ctrlKey||t.metaKey)&&!t.altKey){var i=this.api.asc_getZoom();e<0?(i=Math.ceil(10*i)/10,(i-=.1)<.5||this.api.asc_setZoom(i)):e>0&&(i=Math.floor(10*i)/10,(i+=.1)>0&&!(i>2)&&this.api.asc_setZoom(i)),t.preventDefault(),t.stopPropagation()}}},onDocumentKeyDown:function(t){if(this.api){var e=t.keyCode;if(!t.ctrlKey&&!t.metaKey||t.shiftKey||t.altKey){if(e==Common.UI.Keys.F10&&t.shiftKey)return this.showObjectMenu(t),t.preventDefault(),t.stopPropagation(),!1}else if(e===Common.UI.Keys.NUM_PLUS||e===Common.UI.Keys.EQUALITY||Common.Utils.isGecko&&e===Common.UI.Keys.EQUALITY_FF||Common.Utils.isOpera&&43==e){if(!this.api.isCellEdited){var i=Math.floor(10*this.api.asc_getZoom())/10;return i+=.1,i>0&&!(i>2)&&this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}}else if((e===Common.UI.Keys.NUM_MINUS||e===Common.UI.Keys.MINUS||Common.Utils.isGecko&&e===Common.UI.Keys.MINUS_FF||Common.Utils.isOpera&&45==e)&&!this.api.isCellEdited)return i=Math.ceil(10*this.api.asc_getZoom())/10,i-=.1,i<.5||this.api.asc_setZoom(i),t.preventDefault(),t.stopPropagation(),!1}},onDocumentRightDown:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!0)},onDocumentRightUp:function(t){0==t.button&&(this.mouse.isLeftButtonDown=!1)},onProcessMouse:function(t){"mouseup"==t.type&&(this.mouse.isLeftButtonDown=!1)},onDocumentMouseMove:function(t){"canvas"!==t.target.localName&&this.hideHyperlinkTip()},showObjectMenu:function(t){!this.api||this.mouse.isLeftButtonDown||this.rangeSelectionMode||(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(this.api.asc_getCellInfo(),!0,t):this.fillViewMenuProps(this.api.asc_getCellInfo(),!0,t))},onSelectionChanged:function(t){!this.mouse.isLeftButtonDown&&!this.rangeSelectionMode&&this.currentMenu&&this.currentMenu.isVisible()&&(this.permissions.isEdit&&!this._isDisabled?this.fillMenuProps(t,!0):this.fillViewMenuProps(t,!0))},fillMenuProps:function(t,e,i){var n,o,s,a,l,r,c,h,d,p,m=this.documentHolder,u=t.asc_getFlags().asc_getSelectionType(),g=t.asc_getLocked(),b=!0===t.asc_getLockedTable(),f=!1,C=this.getApplication().getController("Common.Controllers.Comments"),v=this.permissions.isEditMailMerge||this.permissions.isEditDiagram;switch(u){case Asc.c_oAscSelectionType.RangeCells:n=!0;break;case Asc.c_oAscSelectionType.RangeRow:o=!0;break;case Asc.c_oAscSelectionType.RangeCol:s=!0;break;case Asc.c_oAscSelectionType.RangeMax:a=!0;break;case Asc.c_oAscSelectionType.RangeImage:r=!v;break;case Asc.c_oAscSelectionType.RangeShape:h=!v;break;case Asc.c_oAscSelectionType.RangeChart:l=!v;break;case Asc.c_oAscSelectionType.RangeChartText:d=!v;break;case Asc.c_oAscSelectionType.RangeShapeText:c=!v}if(this.api.asc_getHeaderFooterMode()){if(!m.copyPasteMenu||!e&&!m.copyPasteMenu.isVisible())return;e&&this.showPopupMenu(m.copyPasteMenu,{},i)}else if(r||h||l){if(!m.imgMenu||!e&&!m.imgMenu.isVisible())return;r=h=l=!1,m.mnuImgAdvanced.imageInfo=void 0;for(var y,x=!1,w=this.api.asc_getGraphicObjectProps(),S=0;S',t.id)),o.cmpEl.append(a)),t.render(a),t.cmpEl.attr({tabindex:"-1"})),2!==i.button){var l=n.api.asc_getActiveCellCoord(),r={left:0,top:0};s[0]=l.asc_getX()+l.asc_getWidth()+r.left,s[1]=(l.asc_getY()<0?0:l.asc_getY())+l.asc_getHeight()+r.top}a.css({left:s[0],top:s[1]}),_.isFunction(t.options.initMenu)&&(t.options.initMenu(e),t.alignPosition()),_.delay(function(){t.cmpEl.focus()},10),t.show(),n.currentMenu=t}},onEntriesListMenu:function(t){if(t&&t.length>0){for(var e=this,i=e.documentHolder,n=i.entriesMenu,o=i.cmpEl.find(Common.Utils.String.format("#menu-container-{0}",n.id)),s=0;s',n.id)),i.cmpEl.append(o)),n.render(o),n.cmpEl.attr({tabindex:"-1"}));var a=e.api.asc_getActiveCellCoord(),l={left:0,top:0},r=[a.asc_getX()+l.left,(a.asc_getY()<0?0:a.asc_getY())+a.asc_getHeight()+l.top];o.css({left:r[0],top:r[1]}),n.show(),n.alignPosition(),_.delay(function(){n.cmpEl.focus()},10)}else this.documentHolder.entriesMenu.hide(),Common.UI.warning({title:this.notcriticalErrorTitle,maxwidth:600,msg:this.txtNoChoices,callback:_.bind(function(t){Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},this)})},onFormulaCompleteMenu:function(t){if(this.documentHolder.funcMenu)if(t){for(var e=this,i=e.documentHolder,n=i.funcMenu,o=i.cmpEl.find("#menu-formula-selection"),s=e.getApplication().getController("FormulaDialog").getDescription(Common.Utils.InternalSettings.get("sse-settings-func-locale")),a=0;an?1:0}),_.each(t,function(t,i){var o=t.asc_getType(),a=t.asc_getName(!0),l=e.api.asc_getFormulaNameByLocale(a),r=new Common.UI.MenuItem({iconCls:o==Asc.c_oAscPopUpSelectorType.Func?"mnu-popup-func":o==Asc.c_oAscPopUpSelectorType.Table?"mnu-popup-table":"mnu-popup-range",caption:a,hint:s&&s[l]?s[l].d:""}).on("click",function(t,i){setTimeout(function(){e.api.asc_insertFormula(t.caption,o,!1)},10)});n.addItem(r)}),n.rendered||(o.length<1&&(o=$(Common.Utils.String.format('')),i.cmpEl.append(o)),n.onAfterKeydownMenu=function(t){if(t.keyCode!=Common.UI.Keys.RETURN||!t.ctrlKey&&!t.altKey){var e;if(arguments.length>1&&arguments[1]instanceof KeyboardEvent&&(t=arguments[1]),o.hasClass("open"))if(t.keyCode==Common.UI.Keys.TAB||t.keyCode==Common.UI.Keys.RETURN&&!t.ctrlKey&&!t.altKey)e=o.find("a.focus").closest("li");else if(t.keyCode==Common.UI.Keys.UP||t.keyCode==Common.UI.Keys.DOWN){var i=n.cmpEl,s=i.offset().top,a=o.find("a.focus").closest("li"),l=a.offset().top;(ls+i.height())&&(n.scroller?n.scroller.scrollTop(i.scrollTop()+l-s,0):i.scrollTop(i.scrollTop()+l-s))}e&&(e.length>0&&e.click(),Common.UI.Menu.Manager.hideAll())}},n.on("hide:after",function(){for(var t=0;t'),this.documentHolder.cmpEl.append(e.parentEl));var i=this.getApplication().getController("FormulaDialog").getDescription(Common.Utils.InternalSettings.get("sse-settings-func-locale")),n=(i&&i[t]?this.api.asc_getFormulaLocaleName(t)+i[t].a:"").replace(/[,;]/g,this.api.asc_getFunctionArgumentSeparator());if(e.ref&&e.ref.isVisible()&&e.text!=n&&(e.ref.hide(),e.ref=void 0,e.text="",e.isHidden=!0),!n)return;e.ref&&e.ref.isVisible()||(e.text=n,e.ref=new Common.UI.Tooltip({owner:e.parentEl,html:!0,title:n,cls:"auto-tooltip"}),e.ref.show([-1e4,-1e4]),e.isHidden=!1);var o=[this.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),this.documentHolder.cmpEl.offset().top-$(window).scrollTop()],s=this.api.asc_getActiveCellCoord(),a=[s.asc_getX()+o[0]-3,s.asc_getY()+o[1]-e.ref.getBSTip().$tip.height()-5],l=e.ref.getBSTip().$tip.width();a[0]+l>this.tooltips.coauth.bodyWidth&&(a[0]=this.tooltips.coauth.bodyWidth-l),e.ref.getBSTip().$tip.css({top:a[1]+"px",left:a[0]+"px"})}else!e.isHidden&&e.ref&&(e.ref.hide(),e.ref=void 0,e.text="",e.isHidden=!0)},onInputMessage:function(t,e){var i=this.tooltips.input_msg;if(e){i.parentEl||(i.parentEl=$('
'),this.documentHolder.cmpEl.append(i.parentEl));var n=t?""+(t||"")+"
":"";n+=e||"",i.ref&&i.ref.isVisible()&&i.text!=n&&(i.ref.hide(),i.ref=void 0,i.text="",i.isHidden=!0),i.ref&&i.ref.isVisible()||(i.text=n,i.ref=new Common.UI.Tooltip({owner:i.parentEl,html:!0,title:n}),i.ref.show([-1e4,-1e4]),i.isHidden=!1);var o=[this.documentHolder.cmpEl.offset().left-$(window).scrollLeft(),this.documentHolder.cmpEl.offset().top-$(window).scrollTop()],s=this.api.asc_getActiveCellCoord(),a=[s.asc_getX()+o[0]-3,s.asc_getY()+o[1]-i.ref.getBSTip().$tip.height()-5],l=i.ref.getBSTip().$tip.width();a[0]+l>this.tooltips.coauth.bodyWidth&&(a[0]=this.tooltips.coauth.bodyWidth-l),i.ref.getBSTip().$tip.css({top:a[1]+"px",left:a[0]+"px"})}else!i.isHidden&&i.ref&&(i.ref.hide(),i.ref=void 0,i.text="",i.isHidden=!0)},onShowSpecialPasteOptions:function(t){var e=this,i=e.documentHolder,n=t.asc_getCellCoord(),o=i.cmpEl.find("#special-paste-container"),s=t.asc_getOptions();if(s){if(o.length<1&&(e._arrSpecialPaste=[],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.paste]=[e.txtPaste,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.pasteOnlyFormula]=[e.txtPasteFormulas,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaNumberFormat]=[e.txtPasteFormulaNumFormat,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaAllFormatting]=[e.txtPasteKeepSourceFormat,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaWithoutBorders]=[e.txtPasteBorders,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.formulaColumnWidth]=[e.txtPasteColWidths,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.mergeConditionalFormating]=[e.txtPasteMerge,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.transpose]=[e.txtPasteTranspose,0],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.pasteOnlyValues]=[e.txtPasteValues,1],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.valueNumberFormat]=[e.txtPasteValNumFormat,1],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.valueAllFormating]=[e.txtPasteValFormat,1],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.pasteOnlyFormating]=[e.txtPasteFormat,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.link]=[e.txtPasteLink,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.picture]=[e.txtPastePicture,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.linkedPicture]=[e.txtPasteLinkPicture,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.sourceformatting]=[e.txtPasteSourceFormat,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.destinationFormatting]=[e.txtPasteDestFormat,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.keepTextOnly]=[e.txtKeepTextOnly,2],e._arrSpecialPaste[Asc.c_oSpecialPasteProps.useTextImport]=[e.txtUseTextImport,3],o=$('
'),i.cmpEl.append(o),e.btnSpecialPaste=new Common.UI.Button({cls:"btn-toolbar",iconCls:"btn-paste",menu:new Common.UI.Menu({items:[]})}),e.btnSpecialPaste.render($("#id-document-holder-btn-special-paste"))),s.length>0){for(var a=e.btnSpecialPaste.menu,l=0;l0&&(a.addItem(new Common.UI.MenuItem({caption:"--"})),h=!1),_.each(r[l],function(t,e){a.addItem(t),h=!0});a.items.length>0&&a.items[0].setChecked(!0,!0),e._state.lastSpecPasteChecked=a.items.length>0?a.items[0]:null,c&&(a.addItem(new Common.UI.MenuItem({caption:"--"})),a.addItem(c))}if(n[0].asc_getX()<0||n[0].asc_getY()<0)return void(o.is(":visible")&&o.hide());var d=n[0],p=n[1],m=e.tooltips.coauth.bodyWidth-e.tooltips.coauth.XY[0]-e.tooltips.coauth.rightMenuWidth-15,u=e.tooltips.coauth.apiHeight-15,g=[],b=[31,20],f=d.asc_getX()+d.asc_getWidth()+3+b[0],C=d.asc_getY()+d.asc_getHeight()+3+b[1];f>m?(g[0]=void 0!==p?p.asc_getX():m-b[0]-3,C>u&&(g[0]-=b[0]+3),g[0]<0&&(g[0]=m-3-b[0])):g[0]=f-b[0],g[1]=C>u?u-3-b[1]:C-b[1],o.css({left:g[0],top:g[1]}),o.show()}},onHideSpecialPasteOptions:function(){var t=this.documentHolder.cmpEl.find("#special-paste-container");t.is(":visible")&&t.hide()},onToggleAutoCorrectOptions:function(t){if(!t){var e=this.documentHolder.cmpEl.find("#autocorrect-paste-container");return void(e.is(":visible")&&e.hide())}var i=this,n=i.documentHolder,o=t.asc_getCellCoord(),e=n.cmpEl.find("#autocorrect-paste-container"),s=t.asc_getOptions();if(e.length<1&&(i._arrAutoCorrectPaste=[],i._arrAutoCorrectPaste[Asc.c_oAscAutoCorrectOptions.UndoTableAutoExpansion]=i.txtUndoExpansion,i._arrAutoCorrectPaste[Asc.c_oAscAutoCorrectOptions.RedoTableAutoExpansion]=i.txtRedoExpansion,e=$('
'),n.cmpEl.append(e),i.btnAutoCorrectPaste=new Common.UI.Button({cls:"btn-toolbar",iconCls:"btn-paste",menu:new Common.UI.Menu({items:[]})}),i.btnAutoCorrectPaste.render($("#id-document-holder-btn-autocorrect-paste"))),s.length>0){for(var a=i.btnAutoCorrectPaste.menu,l=0;lr||p>c||o.asc_getX()<0||o.asc_getY()<0?e.is(":visible")&&e.hide():(e.css({left:d-h[0],top:p-h[1]}),e.show())},onCellsRange:function(t){this.rangeSelectionMode=t!=Asc.c_oAscSelectionDialogType.None},onApiEditCell:function(t){this.isEditFormula=t==Asc.c_oAscCellEditorState.editFormula,this.isEditCell=t!=Asc.c_oAscCellEditorState.editEnd},onLockDefNameManager:function(t){this.namedrange_locked=t==Asc.c_oAscDefinedNameReason.LockDefNameManager},onChangeCropState:function(t){this.documentHolder.menuImgCrop.menu.items[0].setChecked(t,!0)},initEquationMenu:function(){if(this._currentMathObj){var t,e=this,i=e._currentMathObj.get_Type(),n=e._currentMathObj,o=[];switch(i){case Asc.c_oAscMathInterfaceType.Accent:t=new Common.UI.MenuItem({caption:e.txtRemoveAccentChar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_AccentCharacter"}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.BorderBox:t=new Common.UI.MenuItem({caption:e.txtBorderProps,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:n.get_HideTop()?e.txtAddTop:e.txtHideTop,equationProps:{type:i,callback:"put_HideTop",value:!n.get_HideTop()}},{caption:n.get_HideBottom()?e.txtAddBottom:e.txtHideBottom,equationProps:{type:i,callback:"put_HideBottom",value:!n.get_HideBottom()}},{caption:n.get_HideLeft()?e.txtAddLeft:e.txtHideLeft,equationProps:{type:i,callback:"put_HideLeft",value:!n.get_HideLeft()}},{caption:n.get_HideRight()?e.txtAddRight:e.txtHideRight,equationProps:{type:i,callback:"put_HideRight",value:!n.get_HideRight()}},{caption:n.get_HideHor()?e.txtAddHor:e.txtHideHor,equationProps:{type:i,callback:"put_HideHor",value:!n.get_HideHor()}},{caption:n.get_HideVer()?e.txtAddVer:e.txtHideVer,equationProps:{type:i,callback:"put_HideVer",value:!n.get_HideVer()}},{caption:n.get_HideTopLTR()?e.txtAddLT:e.txtHideLT,equationProps:{type:i,callback:"put_HideTopLTR",value:!n.get_HideTopLTR()}},{caption:n.get_HideTopRTL()?e.txtAddLB:e.txtHideLB,equationProps:{type:i,callback:"put_HideTopRTL",value:!n.get_HideTopRTL()}}]})}),o.push(t);break;case Asc.c_oAscMathInterfaceType.Bar:t=new Common.UI.MenuItem({caption:e.txtRemoveBar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_Bar"}}),o.push(t),t=new Common.UI.MenuItem({caption:n.get_Pos()==Asc.c_oAscMathInterfaceBarPos.Top?e.txtUnderbar:e.txtOverbar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:n.get_Pos()==Asc.c_oAscMathInterfaceBarPos.Top?Asc.c_oAscMathInterfaceBarPos.Bottom:Asc.c_oAscMathInterfaceBarPos.Top}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.Script:var s=n.get_ScriptType();s==Asc.c_oAscMathInterfaceScript.PreSubSup?(t=new Common.UI.MenuItem({caption:e.txtScriptsAfter,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:Asc.c_oAscMathInterfaceScript.SubSup}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtRemScripts,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:Asc.c_oAscMathInterfaceScript.None}}),o.push(t)):(s==Asc.c_oAscMathInterfaceScript.SubSup&&(t=new Common.UI.MenuItem({caption:e.txtScriptsBefore,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:Asc.c_oAscMathInterfaceScript.PreSubSup}}),o.push(t)),s!=Asc.c_oAscMathInterfaceScript.SubSup&&s!=Asc.c_oAscMathInterfaceScript.Sub||(t=new Common.UI.MenuItem({caption:e.txtRemSubscript,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:s==Asc.c_oAscMathInterfaceScript.SubSup?Asc.c_oAscMathInterfaceScript.Sup:Asc.c_oAscMathInterfaceScript.None}}),o.push(t)),s!=Asc.c_oAscMathInterfaceScript.SubSup&&s!=Asc.c_oAscMathInterfaceScript.Sup||(t=new Common.UI.MenuItem({caption:e.txtRemSuperscript,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_ScriptType",value:s==Asc.c_oAscMathInterfaceScript.SubSup?Asc.c_oAscMathInterfaceScript.Sub:Asc.c_oAscMathInterfaceScript.None}}),o.push(t)));break;case Asc.c_oAscMathInterfaceType.Fraction:var a=n.get_FractionType();a!=Asc.c_oAscMathInterfaceFraction.Skewed&&a!=Asc.c_oAscMathInterfaceFraction.Linear||(t=new Common.UI.MenuItem({caption:e.txtFractionStacked,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:Asc.c_oAscMathInterfaceFraction.Bar}}),o.push(t)),a!=Asc.c_oAscMathInterfaceFraction.Bar&&a!=Asc.c_oAscMathInterfaceFraction.Linear||(t=new Common.UI.MenuItem({caption:e.txtFractionSkewed,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:Asc.c_oAscMathInterfaceFraction.Skewed}}),o.push(t)),a!=Asc.c_oAscMathInterfaceFraction.Bar&&a!=Asc.c_oAscMathInterfaceFraction.Skewed||(t=new Common.UI.MenuItem({caption:e.txtFractionLinear,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:Asc.c_oAscMathInterfaceFraction.Linear}}),o.push(t)),a!=Asc.c_oAscMathInterfaceFraction.Bar&&a!=Asc.c_oAscMathInterfaceFraction.NoBar||(t=new Common.UI.MenuItem({caption:a==Asc.c_oAscMathInterfaceFraction.Bar?e.txtRemFractionBar:e.txtAddFractionBar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_FractionType",value:a==Asc.c_oAscMathInterfaceFraction.Bar?Asc.c_oAscMathInterfaceFraction.NoBar:Asc.c_oAscMathInterfaceFraction.Bar}}),o.push(t));break;case Asc.c_oAscMathInterfaceType.Limit:t=new Common.UI.MenuItem({caption:n.get_Pos()==Asc.c_oAscMathInterfaceLimitPos.Top?e.txtLimitUnder:e.txtLimitOver,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:n.get_Pos()==Asc.c_oAscMathInterfaceLimitPos.Top?Asc.c_oAscMathInterfaceLimitPos.Bottom:Asc.c_oAscMathInterfaceLimitPos.Top}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtRemLimit,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:Asc.c_oAscMathInterfaceLimitPos.None}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.Matrix:t=new Common.UI.MenuItem({caption:n.get_HidePlaceholder()?e.txtShowPlaceholder:e.txtHidePlaceholder,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HidePlaceholder",value:!n.get_HidePlaceholder()}}),o.push(t),t=new Common.UI.MenuItem({caption:e.insertText,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.insertRowAboveText,equationProps:{type:i,callback:"insert_MatrixRow",value:!0}},{caption:e.insertRowBelowText,equationProps:{type:i,callback:"insert_MatrixRow",value:!1}},{caption:e.insertColumnLeftText,equationProps:{type:i,callback:"insert_MatrixColumn",value:!0}},{caption:e.insertColumnRightText,equationProps:{type:i,callback:"insert_MatrixColumn",value:!1}}]})}),o.push(t),t=new Common.UI.MenuItem({caption:e.deleteText,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.deleteRowText,equationProps:{type:i,callback:"delete_MatrixRow"}},{caption:e.deleteColumnText,equationProps:{type:i,callback:"delete_MatrixColumn"}}]})}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtMatrixAlign,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.txtTop,checkable:!0,checked:n.get_MatrixAlign()==Asc.c_oAscMathInterfaceMatrixMatrixAlign.Top,equationProps:{type:i,callback:"put_MatrixAlign",value:Asc.c_oAscMathInterfaceMatrixMatrixAlign.Top}},{caption:e.centerText,checkable:!0,checked:n.get_MatrixAlign()==Asc.c_oAscMathInterfaceMatrixMatrixAlign.Center,equationProps:{type:i,callback:"put_MatrixAlign",value:Asc.c_oAscMathInterfaceMatrixMatrixAlign.Center}},{caption:e.txtBottom,checkable:!0,checked:n.get_MatrixAlign()==Asc.c_oAscMathInterfaceMatrixMatrixAlign.Bottom,equationProps:{type:i,callback:"put_MatrixAlign",value:Asc.c_oAscMathInterfaceMatrixMatrixAlign.Bottom}}]})}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtColumnAlign,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.leftText,checkable:!0,checked:n.get_ColumnAlign()==Asc.c_oAscMathInterfaceMatrixColumnAlign.Left,equationProps:{type:i,callback:"put_ColumnAlign",value:Asc.c_oAscMathInterfaceMatrixColumnAlign.Left}},{caption:e.centerText,checkable:!0,checked:n.get_ColumnAlign()==Asc.c_oAscMathInterfaceMatrixColumnAlign.Center,equationProps:{type:i,callback:"put_ColumnAlign",value:Asc.c_oAscMathInterfaceMatrixColumnAlign.Center}},{caption:e.rightText,checkable:!0,checked:n.get_ColumnAlign()==Asc.c_oAscMathInterfaceMatrixColumnAlign.Right,equationProps:{type:i,callback:"put_ColumnAlign",value:Asc.c_oAscMathInterfaceMatrixColumnAlign.Right}}]})}),o.push(t);break;case Asc.c_oAscMathInterfaceType.EqArray:t=new Common.UI.MenuItem({caption:e.txtInsertEqBefore,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_Equation",value:!0}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtInsertEqAfter,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_Equation",value:!1}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtDeleteEq,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"delete_Equation"}}),o.push(t),t=new Common.UI.MenuItem({caption:e.alignmentText,equation:!0,disabled:e._currentParaObjDisabled,menu:new Common.UI.Menu({menuAlign:"tl-tr",items:[{caption:e.txtTop,checkable:!0,checked:n.get_Align()==Asc.c_oAscMathInterfaceEqArrayAlign.Top,equationProps:{type:i,callback:"put_Align",value:Asc.c_oAscMathInterfaceEqArrayAlign.Top}},{caption:e.centerText,checkable:!0,checked:n.get_Align()==Asc.c_oAscMathInterfaceEqArrayAlign.Center,equationProps:{type:i,callback:"put_Align",value:Asc.c_oAscMathInterfaceEqArrayAlign.Center}},{caption:e.txtBottom,checkable:!0,checked:n.get_Align()==Asc.c_oAscMathInterfaceEqArrayAlign.Bottom,equationProps:{type:i,callback:"put_Align",value:Asc.c_oAscMathInterfaceEqArrayAlign.Bottom}}]})}),o.push(t);break;case Asc.c_oAscMathInterfaceType.LargeOperator:t=new Common.UI.MenuItem({caption:e.txtLimitChange,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_LimitLocation",value:n.get_LimitLocation()==Asc.c_oAscMathInterfaceNaryLimitLocation.UndOvr?Asc.c_oAscMathInterfaceNaryLimitLocation.SubSup:Asc.c_oAscMathInterfaceNaryLimitLocation.UndOvr}}),o.push(t),void 0!==n.get_HideUpper()&&(t=new Common.UI.MenuItem({caption:n.get_HideUpper()?e.txtShowTopLimit:e.txtHideTopLimit,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideUpper",value:!n.get_HideUpper()}}),o.push(t)),void 0!==n.get_HideLower()&&(t=new Common.UI.MenuItem({caption:n.get_HideLower()?e.txtShowBottomLimit:e.txtHideBottomLimit,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideLower",value:!n.get_HideLower()}}),o.push(t));break;case Asc.c_oAscMathInterfaceType.Delimiter:t=new Common.UI.MenuItem({caption:e.txtInsertArgBefore,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_DelimiterArgument",value:!0}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtInsertArgAfter,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_DelimiterArgument",value:!1}}),o.push(t),n.can_DeleteArgument()&&(t=new Common.UI.MenuItem({caption:e.txtDeleteArg,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"delete_DelimiterArgument"}}),o.push(t)),t=new Common.UI.MenuItem({caption:n.has_Separators()?e.txtDeleteCharsAndSeparators:e.txtDeleteChars,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_DelimiterCharacters"}}),o.push(t),t=new Common.UI.MenuItem({caption:n.get_HideOpeningBracket()?e.txtShowOpenBracket:e.txtHideOpenBracket,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideOpeningBracket",value:!n.get_HideOpeningBracket()}}),o.push(t),t=new Common.UI.MenuItem({caption:n.get_HideClosingBracket()?e.txtShowCloseBracket:e.txtHideCloseBracket,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideClosingBracket",value:!n.get_HideClosingBracket()}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtStretchBrackets,equation:!0,disabled:e._currentParaObjDisabled,checkable:!0,checked:n.get_StretchBrackets(),equationProps:{type:i,callback:"put_StretchBrackets",value:!n.get_StretchBrackets()}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtMatchBrackets,equation:!0,disabled:!n.get_StretchBrackets()||e._currentParaObjDisabled,checkable:!0,checked:n.get_StretchBrackets()&&n.get_MatchBrackets(),equationProps:{type:i,callback:"put_MatchBrackets",value:!n.get_MatchBrackets()}}),o.push(t);break;case Asc.c_oAscMathInterfaceType.GroupChar:n.can_ChangePos()&&(t=new Common.UI.MenuItem({caption:n.get_Pos()==Asc.c_oAscMathInterfaceGroupCharPos.Top?e.txtGroupCharUnder:e.txtGroupCharOver,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:n.get_Pos()==Asc.c_oAscMathInterfaceGroupCharPos.Top?Asc.c_oAscMathInterfaceGroupCharPos.Bottom:Asc.c_oAscMathInterfaceGroupCharPos.Top}}),o.push(t),t=new Common.UI.MenuItem({caption:e.txtDeleteGroupChar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_Pos",value:Asc.c_oAscMathInterfaceGroupCharPos.None}}),o.push(t));break;case Asc.c_oAscMathInterfaceType.Radical:void 0!==n.get_HideDegree()&&(t=new Common.UI.MenuItem({caption:n.get_HideDegree()?e.txtShowDegree:e.txtHideDegree,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"put_HideDegree",value:!n.get_HideDegree()}}),o.push(t)),t=new Common.UI.MenuItem({caption:e.txtDeleteRadical,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"remove_Radical"}}),o.push(t)}return n.can_IncreaseArgumentSize()&&(t=new Common.UI.MenuItem({caption:e.txtIncreaseArg,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"increase_ArgumentSize"}}),o.push(t)),n.can_DecreaseArgumentSize()&&(t=new Common.UI.MenuItem({caption:e.txtDecreaseArg,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"decrease_ArgumentSize"}}),o.push(t)),n.can_InsertManualBreak()&&(t=new Common.UI.MenuItem({caption:e.txtInsertBreak,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"insert_ManualBreak"}}),o.push(t)),n.can_DeleteManualBreak()&&(t=new Common.UI.MenuItem({caption:e.txtDeleteBreak,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"delete_ManualBreak"}}),o.push(t)),n.can_AlignToCharacter()&&(t=new Common.UI.MenuItem({caption:e.txtAlignToChar,equation:!0,disabled:e._currentParaObjDisabled,equationProps:{type:i,callback:"align_ToCharacter"}}),o.push(t)),o}},addEquationMenu:function(t){var e=this;e.clearEquationMenu(t);var i=e.documentHolder.textInShapeMenu,n=e.initEquationMenu();return n.length>0&&_.each(n,function(n,o){n.menu?_.each(n.menu.items,function(t){t.on("click",_.bind(e.equationCallback,e,t.options.equationProps))}):n.on("click",_.bind(e.equationCallback,e,n.options.equationProps)),i.insertItem(t,n),t++}),n.length},clearEquationMenu:function(t){for(var e=this,i=e.documentHolder.textInShapeMenu,n=t;n')}),e.paraBulletsPicker.on("item:click",_.bind(this.onSelectBullets,this)),i&&e.paraBulletsPicker.selectRecord(i.rec,!0)},onSignatureClick:function(t){var e=t.cmpEl.attr("data-value");switch(t.value){case 0:Common.NotificationCenter.trigger("protect:sign",e);break;case 1:this.api.asc_ViewCertificate(e);break;case 2:Common.NotificationCenter.trigger("protect:signature","visible",this._isDisabled,e);break;case 3:this.api.asc_RemoveSignature(e)}},onOriginalSizeClick:function(t){if(this.api){var e=this.api.asc_getOriginalImageSize(),i=e.asc_getImageWidth(),n=e.asc_getImageHeight(),o=new Asc.asc_CImgProperty;o.asc_putWidth(i),o.asc_putHeight(n),o.put_ResetCrop(!0),this.api.asc_setGraphicObjectProps(o),Common.NotificationCenter.trigger("edit:complete",this.documentHolder),Common.component.Analytics.trackEvent("DocumentHolder","Set Image Original Size")}},onImgReplace:function(t,e){var i=this;this.api&&("file"==e.value?setTimeout(function(){i.api&&i.api.asc_changeImageFromFile(),Common.NotificationCenter.trigger("edit:complete",i.documentHolder)},10):new Common.Views.ImageFromUrlDialog({handler:function(t,e){if("ok"==t&&i.api){var n=e.replace(/ /g,"");if(!_.isEmpty(n)){var o=new Asc.asc_CImgProperty;o.asc_putImageUrl(n),i.api.asc_setGraphicObjectProps(o)}}Common.NotificationCenter.trigger("edit:complete",i.documentHolder)}}).show())},onNumberFormatSelect:function(t,e){void 0!==e.value&&"advanced"!==e.value&&this.api&&this.api.asc_setCellFormat(e.options.format),Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},onCustomNumberFormat:function(t){var e=this,i=e.api.asc_getLocale();!i&&(i=e.permissions.lang?parseInt(Common.util.LanguageInfo.getLocalLanguageCode(e.permissions.lang)):1033),new SSE.Views.FormatSettingsDialog({api:e.api,handler:function(t,i){i&&e.api.asc_setCellFormat(i.format),Common.NotificationCenter.trigger("edit:complete",e.documentHolder)},props:{format:t.options.numformat,formatInfo:t.options.numformatinfo,langId:i}}).show(),Common.NotificationCenter.trigger("edit:complete",this.documentHolder)},onNumberFormatOpenAfter:function(t){if(this.api){var e=this,i=e.api.asc_getLocale();if(!i&&(i=e.permissions.lang?parseInt(Common.util.LanguageInfo.getLocalLanguageCode(e.permissions.lang)):1033),this._state.langId!==i){this._state.langId=i;var n=new Asc.asc_CFormatCellsInfo ;n.asc_setType(Asc.c_oAscNumFormatType.None),n.asc_setSymbol(this._state.langId);for(var o=this.api.asc_getFormatCells(n),s=0;sOnly text values from the column can be selected for replacement.",txtExpandSort:"The data next to the selection will not be sorted. Do you want to expand the selection to include the adjacent data or continue with sorting the currently selected cells only?",txtExpand:"Expand and sort",txtSorting:"Sorting",txtSortSelected:"Sort selected",txtPaste:"Paste",txtPasteFormulas:"Paste only formula",txtPasteFormulaNumFormat:"Formula + number format",txtPasteKeepSourceFormat:"Formula + all formatting",txtPasteBorders:"Formula without borders",txtPasteColWidths:"Formula + column width",txtPasteMerge:"Merge conditional formatting",txtPasteTranspose:"Transpose",txtPasteValues:"Paste only value",txtPasteValNumFormat:"Value + number format",txtPasteValFormat:"Value + all formatting",txtPasteFormat:"Paste only formatting",txtPasteLink:"Paste Link",txtPastePicture:"Picture",txtPasteLinkPicture:"Linked Picture",txtPasteSourceFormat:"Source formatting",txtPasteDestFormat:"Destination formatting",txtKeepTextOnly:"Keep text only",txtUseTextImport:"Use text import wizard",txtUndoExpansion:"Undo table autoexpansion",txtRedoExpansion:"Redo table autoexpansion",txtAnd:"and",txtOr:"or",txtEquals:"Equals",txtNotEquals:"Does not equal",txtGreater:"Greater than",txtGreaterEquals:"Greater than or equal to",txtLess:"Less than",txtLessEquals:"Less than or equal to",txtAboveAve:"Above average",txtBelowAve:"Below average",txtBegins:"Begins with",txtNotBegins:"Does not begin with",txtEnds:"Ends with",txtNotEnds:"Does not end with",txtContains:"Contains",txtNotContains:"Does not contain",txtFilterTop:"Top",txtFilterBottom:"Bottom",txtItems:"items",txtPercent:"percent",txtEqualsToCellColor:"Equals to cell color",txtEqualsToFontColor:"Equals to font color",txtAll:"(All)",txtBlanks:"(Blanks)",txtColumn:"Column",txtImportWizard:"Text Import Wizard"},SSE.Controllers.DocumentHolder||{}))}),define("text!spreadsheeteditor/main/app/template/CellEditor.template",[],function(){return'
\r\n \r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n'}),define("spreadsheeteditor/main/app/view/CellEditor",["text!spreadsheeteditor/main/app/template/CellEditor.template","common/main/lib/component/BaseView"],function(t){"use strict";SSE.Views.CellEditor=Common.UI.BaseView.extend(_.extend({template:_.template(t),initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t)},render:function(){$(this.el).html(this.template()),this.btnNamedRanges=new Common.UI.Button({menu:new Common.UI.Menu({style:"min-width: 70px;max-width:400px;",maxHeight:250,items:[{caption:this.textManager,value:"manager"},{caption:"--"}]})}),this.btnNamedRanges.render($("#ce-cell-name-menu")),this.btnNamedRanges.setVisible(!1),this.btnNamedRanges.menu.setOffset(-81),this.$cellname=$("#ce-cell-name",this.el),this.$btnexpand=$("#ce-btn-expand",this.el),this.$btnfunc=$("#ce-func-label",this.el);var t=this;return this.$cellname.on("focus",function(e){var i=t.$cellname[0];i.selectionStart=0,i.selectionEnd=i.value.length,i.scrollLeft=i.scrollWidth}),this.$btnfunc.addClass("disabled"),this.$btnfunc.tooltip({title:this.tipFormula,placement:"cursor"}),this},updateCellInfo:function(t){t&&this.$cellname.val("string"==typeof t?t:t.asc_getName())},cellNameDisabled:function(t){t?this.$cellname.attr("disabled","disabled"):this.$cellname.removeAttr("disabled"),this.btnNamedRanges.setDisabled(t)},tipFormula:"Insert Function",textManager:"Manager"},SSE.Views.CellEditor||{}))}),define("text!spreadsheeteditor/main/app/template/NameManagerDlg.template",[],function(){return'
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
'}),define("spreadsheeteditor/main/app/view/NameManagerDlg",["text!spreadsheeteditor/main/app/template/NameManagerDlg.template","common/main/lib/view/AdvancedSettingsWindow","common/main/lib/component/ComboBox","common/main/lib/component/ListView","common/main/lib/component/InputField"],function(t){"use strict";SSE.Views=SSE.Views||{},SSE.Views.NameManagerDlg=Common.Views.AdvancedSettingsWindow.extend(_.extend({options:{alias:"NameManagerDlg",contentWidth:510,height:353},initialize:function(e){_.extend(this.options,{title:this.txtTitle,template:['
','
'+_.template(t)({scope:this})+"
","
",'
','"].join("")},e),this.api=e.api,this.handler=e.handler,this.sheets=e.sheets||[],this.sheetNames=e.sheetNames||[],this.ranges=e.ranges||[],this.props=e.props,this.sort=e.sort||{type:"name",direction:1},this.locked=e.locked||!1,this.userTooltip=!0,this.currentNamedRange=void 0,this.rangesStore=new Common.UI.DataViewStore,this.wrapEvents={onRefreshDefNameList:_.bind(this.onRefreshDefNameList,this),onLockDefNameManager:_.bind(this.onLockDefNameManager,this)},Common.Views.AdvancedSettingsWindow.prototype.initialize.call(this,this.options)},render:function(){Common.Views.AdvancedSettingsWindow.prototype.render.call(this);var t=this;this.cmbFilter=new Common.UI.ComboBox({el:$("#name-manager-combo-filter"),menuStyle:"min-width: 100%;",editable:!1,cls:"input-group-nr",data:[{value:0,displayValue:this.textFilterAll},{value:1,displayValue:this.textFilterDefNames},{value:2,displayValue:this.textFilterTableNames},{value:3,displayValue:this.textFilterSheet},{value:4,displayValue:this.textFilterWorkbook}]}).on("selected",function(e,i){t.refreshRangeList(null,0)}),this.cmbFilter.setValue(0),this.rangeList=new Common.UI.ListView({el:$("#name-manager-range-list",this.$window),store:new Common.UI.DataViewStore,simpleAddMode:!0,emptyText:this.textEmpty,template:_.template(['
'].join("")),itemTemplate:_.template(['
','
','
<%= name %>
','
<%= scopeName %>
','
<%= range %>
',"<% if (lock) { %>",'
<%=lockuser%>
',"<% } %>","
"].join(""))}),this.rangeList.store.comparator=function(e,i){var n=e.get(t.sort.type).toLowerCase(),o=i.get(t.sort.type).toLowerCase();return n==o?0:n0?this.textnoNames:this.textEmpty)}var a=this,l=this.rangeList.store,r=this.rangesStore.models,c=this.cmbFilter.getValue(),h=c<3?2==c:-1,d=c>2?4==c:-1;if(c>0&&(r=this.rangesStore.filter(function(t){return-1!==h?h===t.get("isTable"):-1!==d&&d===(null===t.get("scope"))})),l.reset(r,{silent:!1}),c=l.length,this.btnEditRange.setDisabled(!c),this.btnDeleteRange.setDisabled(!c),c>0){if(void 0!==e&&null!==e||(e=0),_.isNumber(e))e>c-1&&(e=c-1),this.rangeList.selectByIndex(e),setTimeout(function(){a.rangeList.scrollToRecord(l.at(e))},50);else if(e){var p=l.findWhere({name:e.asc_getName(!0),scope:e.asc_getScope()});p&&(this.rangeList.selectRecord(p),setTimeout(function(){a.rangeList.scrollToRecord(p)},50))}!0===this.userTooltip&&this.rangeList.cmpEl.find(".lock-user").length>0&&this.rangeList.cmpEl.on("mouseover",_.bind(a.onMouseOverLock,a)).on("mouseout",_.bind(a.onMouseOutLock,a))}_.delay(function(){a.rangeList.cmpEl.find(".listview").focus(),a.rangeList.scroller.update({alwaysVisibleY:!0})},100,this)},onMouseOverLock:function(t,e,i){if(!0===this.userTooltip&&$(t.target).hasClass("lock-user")){var n=this,o=$(t.target).tooltip({title:this.tipIsLocked,trigger:"manual"}).data("bs.tooltip");this.userTooltip=o.tip(),this.userTooltip.css("z-index",parseInt(this.$window.css("z-index"))+10),o.show(),setTimeout(function(){n.userTipHide()},5e3)}},userTipHide:function(){"object"==typeof this.userTooltip&&(this.userTooltip.remove(),this.userTooltip=void 0,this.rangeList.cmpEl.off("mouseover").off("mouseout"))},onMouseOutLock:function(t,e,i){"object"==typeof this.userTooltip&&this.userTipHide()},onEditRange:function(t){if(this.locked)return void Common.NotificationCenter.trigger("namedrange:locked");var e=this,i=e.$window.offset(),n=this.rangeList.getSelectedRec(),o=(_.indexOf(this.rangeList.store.models,n),t&&n?new Asc.asc_CDefName(n.get("name"),n.get("range"),n.get("scope"),n.get("isTable"),void 0,void 0,void 0,!0):null),s=new SSE.Views.NamedRangeEditDlg({api:e.api,sheets:this.sheets,props:t?o:this.props,isEdit:t,handler:function(i,n){"ok"==i&&n&&(t?(e.currentNamedRange=n,e.api.asc_editDefinedNames(o,n)):(e.cmbFilter.setValue(0),e.currentNamedRange=n,e.api.asc_setDefinedNames(n)))}}).on("close",function(){e.show(),_.delay(function(){e.rangeList.cmpEl.find(".listview").focus()},100,e)});e.hide(),s.show(i.left+65,i.top+77)},onDeleteRange:function(){var t=this.rangeList.getSelectedRec();t&&(this.currentNamedRange=_.indexOf(this.rangeList.store.models,t),this.api.asc_delDefinedNames(new Asc.asc_CDefName(t.get("name"),t.get("range"),t.get("scope"),t.get("isTable"),void 0,void 0,void 0,!0)))},getSettings:function(){return this.sort},onPrimary:function(){return!0},onDlgBtnClick:function(t){this.handler&&this.handler.call(this,t.currentTarget.attributes.result.value),this.close()},onSortNames:function(t){t!==this.sort.type?(this.sort={type:t,direction:1},this.spanSortName.toggleClass("hidden"),this.spanSortScope.toggleClass("hidden")):this.sort.direction=-this.sort.direction;var e="name"==t?this.spanSortName:this.spanSortScope;this.sort.direction>0?e.removeClass("sort-desc"):e.addClass("sort-desc"),this.rangeList.store.sort(),this.rangeList.onResetItems(),this.rangeList.scroller.update({alwaysVisibleY:!0})},getUserName:function(t){var e=SSE.getCollection("Common.Collections.Users");if(e){var i=e.findUser(t);if(i)return i.get("username")}return this.guestText},onSelectRangeItem:function(t,e,i){this.userTipHide();var n={};if(_.isFunction(i.toJSON)){if(!i.get("selected"))return;n=i.toJSON(),this.currentNamedRange=_.indexOf(this.rangeList.store.models,i),this.btnEditRange.setDisabled(n.lock),this.btnDeleteRange.setDisabled(n.lock||n.isTable)}},hide:function(){this.userTipHide(),Common.UI.Window.prototype.hide.call(this)},close:function(){this.userTipHide(),this.api.asc_unregisterCallback("asc_onLockDefNameManager",this.wrapEvents.onLockDefNameManager),this.api.asc_unregisterCallback("asc_onRefreshDefNameList",this.wrapEvents.onRefreshDefNameList),Common.UI.Window.prototype.close.call(this)},onKeyDown:function(t,e,i){i.keyCode!=Common.UI.Keys.DELETE||this.btnDeleteRange.isDisabled()||this.onDeleteRange()},onDblClickItem:function(t,e,i){this.btnEditRange.isDisabled()||this.onEditRange(!0)},onLockDefNameManager:function(t){this.locked=t==Asc.c_oAscDefinedNameReason.LockDefNameManager},txtTitle:"Name Manager",closeButtonText:"Close",okButtonText:"Ok",textDataRange:"Data Range",textNew:"New",textEdit:"Edit",textDelete:"Delete",textRanges:"Named Ranges",textScope:"Scope",textFilter:"Filter",textEmpty:"No named ranges have been created yet.
Create at least one named range and it will appear in this field.",textnoNames:"No named ranges matching your filter could be found.",textFilterAll:"All",textFilterDefNames:"Defined names",textFilterTableNames:"Table names",textFilterSheet:"Names Scoped to Sheet",textFilterWorkbook:"Names Scoped to Workbook",textWorkbook:"Workbook",guestText:"Guest",tipIsLocked:"This element is being edited by another user."},SSE.Views.NameManagerDlg||{}))}),define("spreadsheeteditor/main/app/controller/CellEditor",["core","spreadsheeteditor/main/app/view/CellEditor","spreadsheeteditor/main/app/view/NameManagerDlg"],function(t){"use strict";SSE.Controllers.CellEditor=Backbone.Controller.extend({views:["CellEditor"],events:function(){return{"keyup input#ce-cell-name":_.bind(this.onCellName,this),"keyup textarea#ce-cell-content":_.bind(this.onKeyupCellEditor,this),"blur textarea#ce-cell-content":_.bind(this.onBlurCellEditor,this),"click button#ce-btn-expand":_.bind(this.expandEditorField,this),"click button#ce-func-label":_.bind(this.onInsertFunction,this)}},initialize:function(){this.addListeners({CellEditor:{},Viewport:{"layout:resizedrag":_.bind(this.onLayoutResize,this)},"Common.Views.Header":{"formulabar:hide":function(t){this.editor.setVisible(!t),Common.localStorage.setBool("sse-hidden-formula",t),Common.NotificationCenter.trigger("layout:changed","celleditor",t?"hidden":"showed")}.bind(this)}})},setApi:function(t){return this.api=t,this.api.isCEditorFocused=!1,this.api.asc_registerCallback("asc_onSelectionNameChanged",_.bind(this.onApiCellSelection,this)),this.api.asc_registerCallback("asc_onEditCell",_.bind(this.onApiEditCell,this)),this.api.asc_registerCallback("asc_onCoAuthoringDisconnect",_.bind(this.onApiDisconnect,this)),Common.NotificationCenter.on("api:disconnect",_.bind(this.onApiDisconnect,this)),Common.NotificationCenter.on("cells:range",_.bind(this.onCellsRange,this)),this.api.asc_registerCallback("asc_onLockDefNameManager",_.bind(this.onLockDefNameManager,this)),this.api.asc_registerCallback("asc_onInputKeyDown",_.bind(this.onInputKeyDown,this)),this},setMode:function(t){this.mode=t,this.editor.$btnfunc[this.mode.isEdit?"removeClass":"addClass"]("disabled"),this.editor.btnNamedRanges.setVisible(this.mode.isEdit&&!this.mode.isEditDiagram&&!this.mode.isEditMailMerge),this.mode.isEdit&&this.api.asc_registerCallback("asc_onSelectionChanged",_.bind(this.onApiSelectionChanged,this))},onInputKeyDown:function(t){if(Common.UI.Keys.UP===t.keyCode||Common.UI.Keys.DOWN===t.keyCode||Common.UI.Keys.TAB===t.keyCode||Common.UI.Keys.RETURN===t.keyCode||Common.UI.Keys.ESC===t.keyCode||Common.UI.Keys.LEFT===t.keyCode||Common.UI.Keys.RIGHT===t.keyCode){var e=$("#menu-formula-selection");e.hasClass("open")&&e.find(".dropdown-menu").trigger("keydown",t)}},onLaunch:function(){this.editor=this.createView("CellEditor",{el:"#cell-editing-box"}).render(),this.bindViewEvents(this.editor,this.events),this.editor.$el.parent().find(".after").css({zIndex:"4"}),this.editor.btnNamedRanges.menu.on("item:click",_.bind(this.onNamedRangesMenu,this)).on("show:before",_.bind(this.onNameBeforeShow,this)),this.namedrange_locked=!1},onApiEditCell:function(t){t==Asc.c_oAscCellEditorState.editStart?(this.api.isCellEdited=!0,this.editor.cellNameDisabled(!0)):t==Asc.c_oAscCellEditorState.editInCell?this.api.isCEditorFocused="clear":t==Asc.c_oAscCellEditorState.editEnd&&(this.api.isCellEdited=!1,this.api.isCEditorFocused=!1,this.editor.cellNameDisabled(!1)),this.editor.$btnfunc.toggleClass("disabled",t==Asc.c_oAscCellEditorState.editText)},onApiCellSelection:function(t){this.editor.updateCellInfo(t)},onApiSelectionChanged:function(t){var e=t.asc_getFlags().asc_getSelectionType(),i=!this.mode.isEditMailMerge&&!this.mode.isEditDiagram&&(!0===t.asc_getLocked()||!0===t.asc_getLockedTable()),n=e==Asc.c_oAscSelectionType.RangeChartText,o=e==Asc.c_oAscSelectionType.RangeChart,s=e==Asc.c_oAscSelectionType.RangeShapeText,a=e==Asc.c_oAscSelectionType.RangeShape,l=e==Asc.c_oAscSelectionType.RangeImage,r=s||a||n||o;this.editor.$btnfunc.toggleClass("disabled",l||r||i)},onApiDisconnect:function(){this.mode.isEdit=!1;var t=this.getApplication().getController("FormulaDialog");t&&t.hideDialog(),this.mode.isEdit||($("#ce-func-label",this.editor.el).addClass("disabled"),this.editor.btnNamedRanges.setVisible(!1))},onCellsRange:function(t){this.editor.cellNameDisabled(t!=Asc.c_oAscSelectionDialogType.None),this.editor.$btnfunc.toggleClass("disabled",t!=Asc.c_oAscSelectionDialogType.None)},onLayoutResize:function(t,e){"cell:edit"==e&&(this.editor.$el.height()>19?this.editor.$btnexpand.hasClass("btn-collapse")||this.editor.$btnexpand.addClass("btn-collapse"):this.editor.$btnexpand.removeClass("btn-collapse"))},onCellName:function(t){if(t.keyCode==Common.UI.Keys.RETURN){var e=this.editor.$cellname.val();e&&e.length&&this.api.asc_findCell(e),Common.NotificationCenter.trigger("edit:complete",this.editor)}},onBlurCellEditor:function(){"clear"==this.api.isCEditorFocused?this.api.isCEditorFocused=void 0:this.api.isCellEdited&&(this.api.isCEditorFocused=!0)},onKeyupCellEditor:function(t){t.keyCode!=Common.UI.Keys.RETURN||t.altKey||(this.api.isCEditorFocused="clear")},expandEditorField:function(){this.editor.$el.height()>19?(this.editor.keep_height=this.editor.$el.height(),this.editor.$el.height(19),this.editor.$btnexpand.removeClass("btn-collapse")):(this.editor.$el.height(this.editor.keep_height||74),this.editor.$btnexpand.addClass("btn-collapse")),Common.NotificationCenter.trigger("layout:changed","celleditor"),Common.NotificationCenter.trigger("edit:complete",this.editor,{restorefocus:!0})},onInsertFunction:function(){if(this.mode.isEdit&&!this.editor.$btnfunc.hasClass("disabled")){var t=this.getApplication().getController("FormulaDialog");t&&($("#ce-func-label",this.editor.el).blur(),t.showDialog())}},onNamedRangesMenu:function(t,e){var i=this;if("manager"==e.options.value){for(var n=this.api.asc_getWorksheetsCount(),o=-1,s=[],a=[];++o2)},onLockDefNameManager:function(t){this.namedrange_locked=t==Asc.c_oAscDefinedNameReason.LockDefNameManager}})}),define("common/main/lib/view/ImageFromUrlDialog",["common/main/lib/component/Window"],function(){"use strict";Common.Views.ImageFromUrlDialog=Common.UI.Window.extend(_.extend({options:{width:330,header:!1,cls:"modal-dlg"},initialize:function(t){_.extend(this.options,t||{}),this.template=['
','
',"","
",'
',"
",'"].join(""),this.options.tpl=_.template(this.template)(this.options),Common.UI.Window.prototype.initialize.call(this,this.options)},render:function(){Common.UI.Window.prototype.render.call(this);var t=this;t.inputUrl=new Common.UI.InputField({el:$("#id-dlg-url"),allowBlank:!1,blankError:t.txtEmpty,style:"width: 100%;",validateOnBlur:!1,validation:function(e){return!!/((^https?)|(^ftp)):\/\/.+/i.test(e)||t.txtNotUrl}}),this.getChild().find(".btn").on("click",_.bind(this.onBtnClick,this))},show:function(){Common.UI.Window.prototype.show.apply(this,arguments);var t=this;_.delay(function(){t.getChild("input").focus()},500)},onPrimary:function(t){return this._handleInput("ok"),!1},onBtnClick:function(t){this._handleInput(t.currentTarget.attributes.result.value)},_handleInput:function(t){if(this.options.handler){if("ok"==t&&!0!==this.inputUrl.checkValidate())return void this.inputUrl.cmpEl.find("input").focus();this.options.handler.call(this,t,this.inputUrl.getValue())}this.close()},textUrl:"Paste an image URL:",cancelButtonText:"Cancel",okButtonText:"Ok",txtEmpty:"This field is required",txtNotUrl:'This field should be a URL in the format "http://www.example.com"'},Common.Views.ImageFromUrlDialog||{}))}),void 0===Common)var Common={};if(define("common/main/lib/component/LoadMask",["common/main/lib/component/BaseView"],function(){"use strict";Common.UI.LoadMask=Common.UI.BaseView.extend(function(){var t,e,i;return{options:{cls:"",style:"",title:"Loading...",owner:document.body},template:_.template(['"].join("")),initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t),this.template=this.options.template||this.template,this.cls=this.options.cls,this.style=this.options.style,this.title=this.options.title,this.owner=this.options.owner},render:function(){return this},show:function(){if(!e&&!i){if(t=this.owner instanceof Common.UI.BaseView?$(this.owner.el):$(this.owner),t.hasClass("masked"))return this;var n=this;return e=$('
'),i=$(this.template({id:n.id,cls:n.cls,style:n.style,title:n.title})),t.addClass("masked"),t.append(e),t.append(i),i.css({top:Math.round(t.height()/2-(i.height()+parseInt(i.css("padding-top"))+parseInt(i.css("padding-bottom")))/2)+"px",left:Math.round(t.width()/2-(i.width()+parseInt(i.css("padding-left"))+parseInt(i.css("padding-right")))/2)+"px"}),(t.height()<1||t.width()<1)&&i.css({visibility:"hidden"}),Common.util.Shortcuts.suspendEvents(),this}},hide:function(){t&&t.removeClass("masked"),e&&e.remove(),i&&i.remove(),e=null,i=null,Common.util.Shortcuts.resumeEvents()},setTitle:function(e){this.title=e,t&&t.hasClass("masked")&&i&&$(".asc-loadmask-title",i).html(e)},isVisible:function(){return!_.isEmpty(i)},updatePosition:function(){t&&t.hasClass("masked")&&i&&(i.css({top:Math.round(t.height()/2-(i.height()+parseInt(i.css("padding-top"))+parseInt(i.css("padding-bottom")))/2)+"px",left:Math.round(t.width()/2-(i.width()+parseInt(i.css("padding-left"))+parseInt(i.css("padding-right")))/2)+"px"}),i.css({visibility:"visible"}))}}}())}),define("common/main/lib/view/SelectFileDlg",["common/main/lib/component/Window","common/main/lib/component/LoadMask"],function(){"use strict";Common.Views.SelectFileDlg=Common.UI.Window.extend(_.extend({initialize:function(t){var e={};_.extend(e,{title:this.textTitle,width:1024,height:621,header:!0},t),this.template=['
'].join(""),e.tpl=_.template(this.template)(e),this.fileChoiceUrl=t.fileChoiceUrl||"",Common.UI.Window.prototype.initialize.call(this,e)},render:function(){Common.UI.Window.prototype.render.call(this),this.$window.find("> .body").css({height:"auto",overflow:"hidden"});var t=document.createElement("iframe");t.width="100%",t.height=585,t.align="top",t.frameBorder=0,t.scrolling="no",t.onload=_.bind(this._onLoad,this),$("#id-select-file-placeholder").append(t),this.loadMask=new Common.UI.LoadMask({owner:$("#id-select-file-placeholder")}),this.loadMask.setTitle(this.textLoading),this.loadMask.show(), t.src=this.fileChoiceUrl;var e=this;this._eventfunc=function(t){e._onWindowMessage(t)},this._bindWindowEvents.call(this),this.on("close",function(t){e._unbindWindowEvents()})},_bindWindowEvents:function(){window.addEventListener?window.addEventListener("message",this._eventfunc,!1):window.attachEvent&&window.attachEvent("onmessage",this._eventfunc)},_unbindWindowEvents:function(){window.removeEventListener?window.removeEventListener("message",this._eventfunc):window.detachEvent&&window.detachEvent("onmessage",this._eventfunc)},_onWindowMessage:function(t){if(t&&window.JSON)try{this._onMessage.call(this,window.JSON.parse(t.data))}catch(t){}},_onMessage:function(t){if(t&&"onlyoffice"==t.Referer&&void 0!==t.file){Common.NotificationCenter.trigger("window:close",this);var e=this;setTimeout(function(){_.isEmpty(t.file)||e.trigger("selectfile",e,t.file)},50)}},_onLoad:function(){this.loadMask&&this.loadMask.hide()},textTitle:"Select Data Source",textLoading:"Loading"},Common.Views.SelectFileDlg||{}))}),void 0===Common)var Common={};if(define("common/main/lib/util/define",[],function(){"use strict";Common.define={},Common.define.c_oAscMathMainType={Symbol:0,Fraction:1,Script:2,Radical:3,Integral:4,LargeOperator:5,Bracket:6,Function:7,Accent:8,LimitLog:9,Operator:10,Matrix:11},Common.define.c_oAscMathType={Symbol_pm:0,Symbol_infinity:1,Symbol_equals:2,Symbol_neq:3,Symbol_about:4,Symbol_times:5,Symbol_div:6,Symbol_factorial:7,Symbol_propto:8,Symbol_less:9,Symbol_ll:10,Symbol_greater:11,Symbol_gg:12,Symbol_leq:13,Symbol_geq:14,Symbol_mp:15,Symbol_cong:16,Symbol_approx:17,Symbol_equiv:18,Symbol_forall:19,Symbol_additional:20,Symbol_partial:21,Symbol_sqrt:22,Symbol_cbrt:23,Symbol_qdrt:24,Symbol_cup:25,Symbol_cap:26,Symbol_emptyset:27,Symbol_percent:28,Symbol_degree:29,Symbol_fahrenheit:30,Symbol_celsius:31,Symbol_inc:32,Symbol_nabla:33,Symbol_exists:34,Symbol_notexists:35,Symbol_in:36,Symbol_ni:37,Symbol_leftarrow:38,Symbol_uparrow:39,Symbol_rightarrow:40,Symbol_downarrow:41,Symbol_leftrightarrow:42,Symbol_therefore:43,Symbol_plus:44,Symbol_minus:45,Symbol_not:46,Symbol_ast:47,Symbol_bullet:48,Symbol_vdots:49,Symbol_cdots:50,Symbol_rddots:51,Symbol_ddots:52,Symbol_aleph:53,Symbol_beth:54,Symbol_QED:55,Symbol_alpha:65536,Symbol_beta:65537,Symbol_gamma:65538,Symbol_delta:65539,Symbol_varepsilon:65540,Symbol_epsilon:65541,Symbol_zeta:65542,Symbol_eta:65543,Symbol_theta:65544,Symbol_vartheta:65545,Symbol_iota:65546,Symbol_kappa:65547,Symbol_lambda:65548,Symbol_mu:65549,Symbol_nu:65550,Symbol_xsi:65551,Symbol_o:65552,Symbol_pi:65553,Symbol_varpi:65554,Symbol_rho:65555,Symbol_varrho:65556,Symbol_sigma:65557,Symbol_varsigma:65558,Symbol_tau:65559,Symbol_upsilon:65560,Symbol_varphi:65561,Symbol_phi:65562,Symbol_chi:65563,Symbol_psi:65564,Symbol_omega:65565,Symbol_Alpha:131072,Symbol_Beta:131073,Symbol_Gamma:131074,Symbol_Delta:131075,Symbol_Epsilon:131076,Symbol_Zeta:131077,Symbol_Eta:131078,Symbol_Theta:131079,Symbol_Iota:131080,Symbol_Kappa:131081,Symbol_Lambda:131082,Symbol_Mu:131083,Symbol_Nu:131084,Symbol_Xsi:131085,Symbol_O:131086,Symbol_Pi:131087,Symbol_Rho:131088,Symbol_Sigma:131089,Symbol_Tau:131090,Symbol_Upsilon:131091,Symbol_Phi:131092,Symbol_Chi:131093,Symbol_Psi:131094,Symbol_Omega:131095,FractionVertical:16777216,FractionDiagonal:16777217,FractionHorizontal:16777218,FractionSmall:16777219,FractionDifferential_1:16842752,FractionDifferential_2:16842753,FractionDifferential_3:16842754,FractionDifferential_4:16842755,FractionPi_2:16842756,ScriptSup:33554432,ScriptSub:33554433,ScriptSubSup:33554434,ScriptSubSupLeft:33554435,ScriptCustom_1:33619968,ScriptCustom_2:33619969,ScriptCustom_3:33619970,ScriptCustom_4:33619971,RadicalSqrt:50331648,RadicalRoot_n:50331649,RadicalRoot_2:50331650,RadicalRoot_3:50331651,RadicalCustom_1:50397184,RadicalCustom_2:50397185,Integral:67108864,IntegralSubSup:67108865,IntegralCenterSubSup:67108866,IntegralDouble:67108867,IntegralDoubleSubSup:67108868,IntegralDoubleCenterSubSup:67108869,IntegralTriple:67108870,IntegralTripleSubSup:67108871,IntegralTripleCenterSubSup:67108872,IntegralOriented:67174400,IntegralOrientedSubSup:67174401,IntegralOrientedCenterSubSup:67174402,IntegralOrientedDouble:67174403,IntegralOrientedDoubleSubSup:67174404,IntegralOrientedDoubleCenterSubSup:67174405,IntegralOrientedTriple:67174406,IntegralOrientedTripleSubSup:67174407,IntegralOrientedTripleCenterSubSup:67174408,Integral_dx:67239936,Integral_dy:67239937,Integral_dtheta:67239938,LargeOperator_Sum:83886080,LargeOperator_Sum_CenterSubSup:83886081,LargeOperator_Sum_SubSup:83886082,LargeOperator_Sum_CenterSub:83886083,LargeOperator_Sum_Sub:83886084,LargeOperator_Prod:83951616,LargeOperator_Prod_CenterSubSup:83951617,LargeOperator_Prod_SubSup:83951618,LargeOperator_Prod_CenterSub:83951619,LargeOperator_Prod_Sub:83951620,LargeOperator_CoProd:83951621,LargeOperator_CoProd_CenterSubSup:83951622,LargeOperator_CoProd_SubSup:83951623,LargeOperator_CoProd_CenterSub:83951624,LargeOperator_CoProd_Sub:83951625,LargeOperator_Union:84017152,LargeOperator_Union_CenterSubSup:84017153,LargeOperator_Union_SubSup:84017154,LargeOperator_Union_CenterSub:84017155,LargeOperator_Union_Sub:84017156,LargeOperator_Intersection:84017157,LargeOperator_Intersection_CenterSubSup:84017158,LargeOperator_Intersection_SubSup:84017159,LargeOperator_Intersection_CenterSub:84017160,LargeOperator_Intersection_Sub:84017161,LargeOperator_Disjunction:84082688,LargeOperator_Disjunction_CenterSubSup:84082689,LargeOperator_Disjunction_SubSup:84082690,LargeOperator_Disjunction_CenterSub:84082691,LargeOperator_Disjunction_Sub:84082692,LargeOperator_Conjunction:84082693,LargeOperator_Conjunction_CenterSubSup:84082694,LargeOperator_Conjunction_SubSup:84082695,LargeOperator_Conjunction_CenterSub:84082696,LargeOperator_Conjunction_Sub:84082697,LargeOperator_Custom_1:84148224,LargeOperator_Custom_2:84148225,LargeOperator_Custom_3:84148226,LargeOperator_Custom_4:84148227,LargeOperator_Custom_5:84148228,Bracket_Round:100663296,Bracket_Square:100663297,Bracket_Curve:100663298,Bracket_Angle:100663299,Bracket_LowLim:100663300,Bracket_UppLim:100663301,Bracket_Line:100663302,Bracket_LineDouble:100663303,Bracket_Square_OpenOpen:100663304,Bracket_Square_CloseClose:100663305,Bracket_Square_CloseOpen:100663306,Bracket_SquareDouble:100663307,Bracket_Round_Delimiter_2:100728832,Bracket_Curve_Delimiter_2:100728833,Bracket_Angle_Delimiter_2:100728834,Bracket_Angle_Delimiter_3:100728835,Bracket_Round_OpenNone:100794368,Bracket_Round_NoneOpen:100794369,Bracket_Square_OpenNone:100794370,Bracket_Square_NoneOpen:100794371,Bracket_Curve_OpenNone:100794372,Bracket_Curve_NoneOpen:100794373,Bracket_Angle_OpenNone:100794374,Bracket_Angle_NoneOpen:100794375,Bracket_LowLim_OpenNone:100794376,Bracket_LowLim_NoneNone:100794377,Bracket_UppLim_OpenNone:100794378,Bracket_UppLim_NoneOpen:100794379,Bracket_Line_OpenNone:100794380,Bracket_Line_NoneOpen:100794381,Bracket_LineDouble_OpenNone:100794382,Bracket_LineDouble_NoneOpen:100794383,Bracket_SquareDouble_OpenNone:100794384,Bracket_SquareDouble_NoneOpen:100794385,Bracket_Custom_1:100859904,Bracket_Custom_2:100859905,Bracket_Custom_3:100859906,Bracket_Custom_4:100859907,Bracket_Custom_5:100925440,Bracket_Custom_6:100925441,Bracket_Custom_7:100925442,Function_Sin:117440512,Function_Cos:117440513,Function_Tan:117440514,Function_Csc:117440515,Function_Sec:117440516,Function_Cot:117440517,Function_1_Sin:117506048,Function_1_Cos:117506049,Function_1_Tan:117506050,Function_1_Csc:117506051,Function_1_Sec:117506052,Function_1_Cot:117506053,Function_Sinh:117571584,Function_Cosh:117571585,Function_Tanh:117571586,Function_Csch:117571587,Function_Sech:117571588,Function_Coth:117571589,Function_1_Sinh:117637120,Function_1_Cosh:117637121,Function_1_Tanh:117637122,Function_1_Csch:117637123,Function_1_Sech:117637124,Function_1_Coth:117637125,Function_Custom_1:117702656,Function_Custom_2:117702657,Function_Custom_3:117702658,Accent_Dot:134217728,Accent_DDot:134217729,Accent_DDDot:134217730,Accent_Hat:134217731,Accent_Check:134217732,Accent_Accent:134217733,Accent_Grave:134217734,Accent_Smile:134217735,Accent_Tilde:134217736,Accent_Bar:134217737,Accent_DoubleBar:134217738,Accent_CurveBracketTop:134217739,Accent_CurveBracketBot:134217740,Accent_GroupTop:134217741,Accent_GroupBot:134217742,Accent_ArrowL:134217743,Accent_ArrowR:134217744,Accent_ArrowD:134217745,Accent_HarpoonL:134217746,Accent_HarpoonR:134217747,Accent_BorderBox:134283264,Accent_BorderBoxCustom:134283265,Accent_BarTop:134348800,Accent_BarBot:134348801,Accent_Custom_1:134414336,Accent_Custom_2:134414337,Accent_Custom_3:134414338,LimitLog_LogBase:150994944,LimitLog_Log:150994945,LimitLog_Lim:150994946,LimitLog_Min:150994947,LimitLog_Max:150994948,LimitLog_Ln:150994949,LimitLog_Custom_1:151060480,LimitLog_Custom_2:151060481,Operator_ColonEquals:167772160,Operator_EqualsEquals:167772161,Operator_PlusEquals:167772162,Operator_MinusEquals:167772163,Operator_Definition:167772164,Operator_UnitOfMeasure:167772165,Operator_DeltaEquals:167772166,Operator_ArrowL_Top:167837696,Operator_ArrowR_Top:167837697,Operator_ArrowL_Bot:167837698,Operator_ArrowR_Bot:167837699,Operator_DoubleArrowL_Top:167837700,Operator_DoubleArrowR_Top:167837701,Operator_DoubleArrowL_Bot:167837702,Operator_DoubleArrowR_Bot:167837703,Operator_ArrowD_Top:167837704,Operator_ArrowD_Bot:167837705,Operator_DoubleArrowD_Top:167837706,Operator_DoubleArrowD_Bot:167837707,Operator_Custom_1:167903232,Operator_Custom_2:167903233,Matrix_1_2:184549376,Matrix_2_1:184549377,Matrix_1_3:184549378,Matrix_3_1:184549379,Matrix_2_2:184549380,Matrix_2_3:184549381,Matrix_3_2:184549382,Matrix_3_3:184549383,Matrix_Dots_Center:184614912,Matrix_Dots_Baseline:184614913,Matrix_Dots_Vertical:184614914,Matrix_Dots_Diagonal:184614915,Matrix_Identity_2:184680448,Matrix_Identity_2_NoZeros:184680449,Matrix_Identity_3:184680450,Matrix_Identity_3_NoZeros:184680451,Matrix_2_2_RoundBracket:184745984,Matrix_2_2_SquareBracket:184745985,Matrix_2_2_LineBracket:184745986,Matrix_2_2_DLineBracket:184745987,Matrix_Flat_Round:184811520,Matrix_Flat_Square:184811521}}),define("text!spreadsheeteditor/main/app/template/Toolbar.template",[],function(){return'
\n
\n
\n <%= tabsmarkup %>\n
\n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n
\n
\n
\n
\n \n \n \n \n
\n
\n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n \n \n \n
\n
\n \n \n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n
\n \n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n \n \n \n \n \n
\n
\n
\n \n \n
\n
\n
\n \n
\n
\n
\n
\n \n \n \n \n \n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n \n
\n
\n
\n \n \n \n \n \n \n \n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n
\n \n
\n
\n
\n \n \n
\n \x3c!--
--\x3e\n \x3c!--
--\x3e\n \x3c!----\x3e\n \x3c!--
--\x3e\n \x3c!--
--\x3e\n \x3c!----\x3e\n \x3c!--
--\x3e\n \x3c!--
--\x3e\n
\n
\n
\n
'}),define("text!spreadsheeteditor/main/app/template/ToolbarAnother.template",[],function(){return'
\n <% if ( isEditDiagram ) { %>\n \x3c!-----------------------\x3e\n \x3c!-- Edit diagram mode --\x3e\n \x3c!-----------------------\x3e\n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n
\n \n \n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n <% } else if ( isEditMailMerge ) { %>\n \x3c!-----------------------\x3e\n \x3c!-- Edit mail merge mode --\x3e\n \x3c!-----------------------\x3e\n
\n
\n
\n \n \n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
\n \n
\n
\n
\n
\n <% } %>\n
'}),define("text!spreadsheeteditor/main/app/template/ToolbarView.template",[],function(){return'
\n
\n
\n <%= tabsmarkup %>\n
\n
\n
\n
\n
\n
\n
\n
'}),void 0===Common)var Common={};if(Common.Models=Common.Models||{},define("common/main/lib/model/Font",["backbone"],function(t){"use strict";Common.Models.Font=t.Model.extend({defaults:function(){return{id:Common.UI.getId(),name:null,cloneid:null,imgidx:0,type:0}}})}),void 0===Common)var Common={};if(Common.Collections=Common.Collections||{},define("common/main/lib/collection/Fonts",["backbone","common/main/lib/model/Font"],function(t){"use strict";Common.Collections.Fonts=t.Collection.extend({model:Common.Models.Font,comparator:function(t,e){var i=t.get("name").toLowerCase(),n=e.get("name").toLowerCase();return i==n?0:i","<% if (iconImg) { print(''); } else { %>","<% if (iconCls != \"\") { if (/svgicon/.test(iconCls)) {print('');} else print(' '); }} %>","<% } %>","<% if ( !menu ) { %>",'","<% } else if (split == false) {%>",'
','","
","<% } else { %>",'
','",'","
","<% } %>"].join("")),initialize:function(t){Common.UI.BaseView.prototype.initialize.call(this,t);var e=this;e.id=e.options.id||Common.UI.getId(),e.hint=e.options.hint,e.enableToggle=e.options.enableToggle,e.allowDepress=e.options.allowDepress,e.cls=e.options.cls,e.iconCls=e.options.iconCls,e.menu=e.options.menu,e.split=e.options.split,e.toggleGroup=e.options.toggleGroup,e.disabled=e.options.disabled,e.visible=e.options.visible,e.pressed=e.options.pressed,e.caption=e.options.caption,e.template=e.options.template||e.template,e.style=e.options.style,e.rendered=!1,e.options.el&&e.render()},render:function(t){var e=this;if(e.trigger("render:before",e),e.cmpEl=$(e.el),t&&(e.setElement(t,!1),e.rendered||(/icon-top/.test(e.cls)&&e.caption&&/huge/.test(e.cls)&&(!0===e.split?(!!e.cls&&(e.cls=e.cls.replace(/\s?(?:x-huge|icon-top)/g,"")),this.template=_.template('
')):e.menu?this.template=_.template('
'):this.template=_.template('')),e.cmpEl=$(this.template({id:e.id,cls:e.cls,iconCls:e.iconCls,iconImg:e.options.iconImg,menu:e.menu,split:e.split,disabled:e.disabled,pressed:e.pressed,caption:e.caption,style:e.style})),e.menu&&_.isObject(e.menu)&&_.isFunction(e.menu.render)&&e.menu.render(e.cmpEl),t.html(e.cmpEl),e.$icon=e.$el.find(".icon"))),!e.rendered){var i=e.cmpEl,n=i.hasClass("btn-group"),o=i.hasClass("split");if(e.options.hint){var s=e.cmpEl.closest(".asc-window");if("object"==typeof e.options.hint&&e.options.hint.length>1&&$("button",i).length>0){var a=$("button",i);e.btnEl=$(a[0]),e.btnMenuEl=$(a[1])}else e.btnEl=e.cmpEl,e.btnEl.attr("data-toggle","tooltip");e.btnEl.tooltip({ diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 62e078050..717097b32 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -10,6 +10,7 @@ define([ '/common/common-realtime.js', '/common/common-messaging.js', '/common/pinpad.js', + '/common/outer/cache-store.js', '/common/outer/sharedfolder.js', '/common/outer/cursor.js', '/common/outer/onlyoffice.js', @@ -28,7 +29,7 @@ define([ '/bower_components/nthen/index.js', '/bower_components/saferphore/index.js', ], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, - Realtime, Messaging, Pinpad, + Realtime, Messaging, Pinpad, Cache, SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History, NetConfig, AppConfig, Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) { @@ -120,10 +121,13 @@ define([ Store.getSharedFolder = function (clientId, data, cb) { var s = getStore(data.teamId); var id = data.id; + var proxy; if (!s || !s.manager) { return void cb({ error: 'ENOTFOUND' }); } if (s.manager.folders[id]) { + proxy = Util.clone(s.manager.folders[id].proxy); + proxy.offline = Boolean(s.manager.folders[id].offline); // If it is loaded, return the shared folder proxy - return void cb(s.manager.folders[id].proxy); + return void cb(proxy); } else { // Otherwise, check if we know this shared folder var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; @@ -327,6 +331,7 @@ define([ if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); } s.rpc.removeOwnedChannel(channel, function (err) { + if (!err) { Cache.clearChannel(channel); } cb({error:err}); }); }; @@ -459,6 +464,7 @@ define([ store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) { if (e) { return void cb({error: e}); } if (response && response.length && typeof(response[0]) === 'number') { + if (response[0] === 0) { Cache.clearChannel(channelId); } return void cb({size: response[0]}); } else { cb({error: 'INVALID_RESPONSE'}); @@ -472,6 +478,7 @@ define([ store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) { if (e) { return void cb({error: e}); } if (response && response.length && typeof(response[0]) === 'boolean') { + if (response[0]) { Cache.clearChannel(channelId); } return void cb({ isNew: response[0] }); @@ -1133,7 +1140,7 @@ define([ var ownedByMe = Array.isArray(owners) && owners.indexOf(edPublic) !== -1; // Add the pad if it does not exist in our drive - if (!contains || (ownedByMe && !inMyDrive)) { + if (!contains) { // || (ownedByMe && !inMyDrive)) { var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']); if (autoStore !== 1 && !data.forceSave && !data.path && !ownedByMe) { // send event to inner to display the corner popup @@ -1326,13 +1333,16 @@ define([ store.proxy.friends_pending = store.proxy.friends_pending || {}; - var twoDaysAgo = +new Date() - (2 * 24 * 3600 * 1000); - if (store.proxy.friends_pending[data.curvePublic] && - store.proxy.friends_pending[data.curvePublic] > twoDaysAgo) { - return void cb({error: 'TIMEOUT'}); + var p = store.proxy.friends_pending[data.curvePublic]; + if (p) { + return void cb({error: 'ALREADY_SENT'}); } - store.proxy.friends_pending[data.curvePublic] = +new Date(); + store.proxy.friends_pending[data.curvePublic] = { + time: +new Date(), + channel: data.notifications, + curvePublic: data.curvePublic + }; broadcast([], "UPDATE_METADATA"); store.mailbox.sendTo('FRIEND_REQUEST', { @@ -1344,6 +1354,37 @@ define([ cb(obj); }); }; + Store.cancelFriendRequest = function (data, cb) { + if (!data.curvePublic || !data.notifications) { + return void cb({error: 'EINVAL'}); + } + + var proxy = store.proxy; + var f = Messaging.getFriend(proxy, data.curvePublic); + + if (f) { + // Already friend + console.error("You can't cancel an accepted friend request"); + return void cb({error: 'ALREADY_FRIEND'}); + } + + var pending = Util.find(store, ['proxy', 'friends_pending']) || {}; + if (!pending) { return void cb(); } + + store.mailbox.sendTo('CANCEL_FRIEND_REQUEST', { + user: Messaging.createData(store.proxy) + }, { + channel: data.notifications, + curvePublic: data.curvePublic + }, function (obj) { + if (obj && obj.error) { return void cb(obj); } + delete store.proxy.friends_pending[data.curvePublic]; + broadcast([], "UPDATE_METADATA"); + onSync(null, function () { + cb(obj); + }); + }); + }; Store.anonGetPreviewContent = function (clientId, data, cb) { Team.anonGetPreviewContent({ @@ -1590,13 +1631,20 @@ define([ Store.leavePad(null, data, function () {}); }; var conf = { + Cache: Cache, // XXX re-enable cache usage + onCacheStart: function () { + postMessage(clientId, "PAD_CACHE"); + }, + onCacheReady: function () { + postMessage(clientId, "PAD_CACHE_READY"); + }, onReady: function (pad) { var padData = pad.metadata || {}; channel.data = padData; if (padData && padData.validateKey && store.messenger) { store.messenger.storeValidateKey(data.channel, padData.validateKey); } - postMessage(clientId, "PAD_READY"); + postMessage(clientId, "PAD_READY", pad.noCache); }, onMessage: function (m, user, validateKey, isCp, hash) { channel.lastHash = hash; @@ -1727,6 +1775,14 @@ define([ channel.sendMessage(msg, clientId, cb); }; + Store.corruptedCache = function (clientId, channel) { + var chan = channels[channel]; + if (!chan || !chan.cpNf) { return; } + Cache.clearChannel(channel); + if (!chan.cpNf.resetCache) { return; } + chan.cpNf.resetCache(); + }; + // Unpin and pin the new channel in all team when changing a pad password Store.changePadPasswordPin = function (clientId, data, cb) { var oldChannel = data.oldChannel; @@ -2224,6 +2280,9 @@ define([ try { store.onlyoffice.leavePad(chanId); } catch (e) { console.error(e); } + try { + Cache.leaveChannel(chanId); + } catch (e) { console.error(e); } if (!Store.channels[chanId]) { return; } @@ -2429,18 +2488,6 @@ define([ }); }; - var cleanFriendRequests = function () { - try { - if (!store.proxy.friends_pending) { return; } - var twoDaysAgo = +new Date() - (2 * 24 * 3600 * 1000); - Object.keys(store.proxy.friends_pending).forEach(function (curve) { - if (store.proxy.friends_pending[curve] < twoDaysAgo) { - delete store.proxy.friends_pending[curve]; - } - }); - } catch (e) {} - }; - ////////////////////////////////////////////////////////////////// /////////////////////// Init ///////////////////////////////////// ////////////////////////////////////////////////////////////////// @@ -2524,7 +2571,6 @@ define([ loadUniversal(Profile, 'profile', waitFor); loadUniversal(Team, 'team', waitFor, clientId); loadUniversal(History, 'history', waitFor); - cleanFriendRequests(); }).nThen(function () { var requestLogin = function () { broadcast([], "REQUEST_LOGIN"); @@ -2640,6 +2686,7 @@ define([ readOnly: false, validateKey: secret.keys.validateKey || undefined, crypto: Crypto.createEncryptor(secret.keys), + Cache: Cache, // XXX re-enable cache usage userName: 'fs', logLevel: 1, ChainPad: ChainPad, diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js new file mode 100644 index 000000000..7a0a50209 --- /dev/null +++ b/www/common/outer/cache-store.js @@ -0,0 +1,147 @@ +define([ + '/common/common-util.js', + '/bower_components/localforage/dist/localforage.min.js', +], function (Util, localForage) { + var S = {}; + var onReady = Util.mkEvent(true); + + // Check if indexedDB is allowed + var allowed = false; + try { + var request = window.indexedDB.open('test_db', 1); + request.onsuccess = function () { + allowed = true; + onReady.fire(); + }; + request.onerror = function () { + onReady.fire(); + }; + } catch (e) { + onReady.fire(); + } + + var cache = localForage.createInstance({ + driver: localForage.INDEXEDDB, + name: "cp_cache" + }); + + S.getBlobCache = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + + onReady.reg(function () { + if (!allowed) { return void cb('NOCACHE'); } + cache.getItem(id, function (err, obj) { + if (err || !obj || !obj.c) { + return void cb(err || 'EINVAL'); + } + cb(null, obj.c); + obj.t = +new Date(); + cache.setItem(id, obj); + }); + }); + }; + S.setBlobCache = function (id, u8, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + + onReady.reg(function () { + if (!allowed) { return void cb('NOCACHE'); } + if (!u8) { return void cb('EINVAL'); } + cache.setItem(id, { + c: u8, + t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set) + }, function (err) { + cb(err); + }); + }); + }; + + // id: channel ID or blob ID + // returns array of messages + S.getChannelCache = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + + onReady.reg(function () { + if (!allowed) { return void cb('NOCACHE'); } + cache.getItem(id, function (err, obj) { + if (err || !obj || !Array.isArray(obj.c)) { + return void cb(err || 'EINVAL'); + } + cb(null, obj); + obj.t = +new Date(); + cache.setItem(id, obj); + }); + }); + }; + + // Keep the last two checkpoint + any checkpoint that may exist in the last 100 messages + // FIXME: duplicate system with sliceCpIndex from lib/hk-util.js + var checkCheckpoints = function (array) { + if (!Array.isArray(array)) { return; } + // Keep the last 100 messages + if (array.length > 100) { + array.splice(0, array.length - 100); + } + // Remove every message before the first checkpoint + var firstCpIdx; + array.some(function (el, i) { + if (!el.isCheckpoint) { return; } + firstCpIdx = i; + return true; + }); + array.splice(0, firstCpIdx); + }; + + var t = {}; + S.storeCache = function (id, validateKey, val, onError) { + onError = Util.once(Util.mkAsync(onError || function () {})); + + onReady.reg(function () { + + // Make a throttle or use the existing one to avoid calling + // storeCache with the same array multiple times + t[id] = t[id] || Util.throttle(function (validateKey, val, onError) { + if (!allowed) { return void onError('NOCACHE'); } + if (!Array.isArray(val) || !validateKey) { return void onError('EINVAL'); } + checkCheckpoints(val); + cache.setItem(id, { + k: validateKey, + c: val, + t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set) + }, function (err) { + if (err) { onError(err); } + }); + + }, 50); + t[id](validateKey, val, onError); + + }); + }; + + S.leaveChannel = function (id) { + delete t[id]; + }; + + S.clearChannel = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + + onReady.reg(function () { + if (!allowed) { return void cb('NOCACHE'); } + cache.removeItem(id, function () { + cb(); + }); + }); + }; + + S.clear = function (cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); + + onReady.reg(function () { + if (!allowed) { return void cb('NOCACHE'); } + cache.clear(cb); + }); + }; + + self.CryptPad_clearIndexedDB = S.clear; + + return S; +}); diff --git a/www/common/outer/local-store.js b/www/common/outer/local-store.js index 0aff1f9ce..95a3bd88b 100644 --- a/www/common/outer/local-store.js +++ b/www/common/outer/local-store.js @@ -1,9 +1,11 @@ define([ '/common/common-constants.js', '/common/common-hash.js', + '/common/outer/cache-store.js', '/bower_components/localforage/dist/localforage.min.js', '/customize/application_config.js', -], function (Constants, Hash, localForage, AppConfig) { + '/common/common-util.js', +], function (Constants, Hash, Cache, localForage, AppConfig, Util) { var LocalStore = {}; LocalStore.setThumbnail = function (key, value, cb) { @@ -119,7 +121,14 @@ define([ return void AppConfig.customizeLogout(cb); } - if (cb) { cb(); } + cb = Util.once(cb || function () {}); + + try { + Cache.clear(cb); + } catch (e) { + console.error(e); + cb(); + } }; var loginHandlers = []; LocalStore.loginReload = function () { diff --git a/www/common/outer/mailbox-handlers.js b/www/common/outer/mailbox-handlers.js index 326b4afc7..3a39e4ce7 100644 --- a/www/common/outer/mailbox-handlers.js +++ b/www/common/outer/mailbox-handlers.js @@ -33,11 +33,15 @@ define([ // in memory from the same user, dismiss the new one if (friendRequest[data.msg.author]) { return void cb(true); } - friendRequest[data.msg.author] = true; + friendRequest[data.msg.author] = { + type: box.type, + hash: data.hash + }; // If the user is already in our friend list, automatically accept the request if (Messaging.getFriend(ctx.store.proxy, data.msg.author) || ctx.store.proxy.friends_pending[data.msg.author]) { + delete ctx.store.proxy.friends_pending[data.msg.author]; Messaging.acceptFriendRequest(ctx.store, userData, function (obj) { @@ -49,13 +53,16 @@ define([ realtime: ctx.store.realtime, pinPads: ctx.pinPads }, userData, function (err) { - if (err) { return void console.error(err); } + if (err) { + console.error(err); + return void cb(true); + } if (ctx.store.messenger) { ctx.store.messenger.onFriendAdded(userData); } + ctx.updateMetadata(); + cb(true); }); - ctx.updateMetadata(); - cb(true); }); return; } @@ -79,6 +86,7 @@ define([ // until the message is manually dismissed. var friendRequestDeclined = {}; + var friendRequestAccepted = {}; handlers['DECLINE_FRIEND_REQUEST'] = function (ctx, box, data, cb) { // Old format: data was stored directly in "content" var userData = data.msg.content.user || data.msg.content; @@ -97,27 +105,35 @@ define([ ctx.updateMetadata(); if (friendRequestDeclined[data.msg.author]) { return; } - friendRequestDeclined[data.msg.author] = true; box.sendMessage({ type: 'FRIEND_REQUEST_DECLINED', content: { user: userData } - }, function () {}); + }, function (hash) { + friendRequestDeclined[data.msg.author] = { + type: box.type, + hash: hash + }; + }); }, getRandomTimeout(ctx)); }; // UI for declined friend request handlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data, cb) { ctx.updateMetadata(); var curve = data.msg.content.user.curvePublic || data.msg.content.user; - if (friendRequestDeclined[curve]) { return void cb(true); } - friendRequestDeclined[curve] = true; - cb(); + var toRemove = friendRequestAccepted[curve]; + delete friendRequestAccepted[curve]; + if (friendRequestDeclined[curve]) { return void cb(true, toRemove); } + friendRequestDeclined[curve] = { + type: box.type, + hash: data.hash + }; + cb(false, toRemove); }; removeHandlers['FRIEND_REQUEST_DECLINED'] = function (ctx, box, data) { var curve = data.content.user.curvePublic || data.content.user; if (friendRequestDeclined[curve]) { delete friendRequestDeclined[curve]; } }; - var friendRequestAccepted = {}; handlers['ACCEPT_FRIEND_REQUEST'] = function (ctx, box, data, cb) { // Old format: data was stored directly in "content" var userData = data.msg.content.user || data.msg.content; @@ -148,11 +164,15 @@ define([ if (ctx.store.modules['profile']) { ctx.store.modules['profile'].update(); } // Display the "accepted" state in the UI if (friendRequestAccepted[data.msg.author]) { return; } - friendRequestAccepted[data.msg.author] = true; box.sendMessage({ type: 'FRIEND_REQUEST_ACCEPTED', content: { user: userData } - }, function () {}); + }, function (hash) { + friendRequestAccepted[data.msg.author] = { + type: box.type, + hash: hash + }; + }); }); }, getRandomTimeout(ctx)); }; @@ -160,20 +180,32 @@ define([ handlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data, cb) { ctx.updateMetadata(); var curve = data.msg.content.user.curvePublic || data.msg.content.user; - if (friendRequestAccepted[curve]) { return void cb(true); } - friendRequestAccepted[curve] = true; - cb(); + var toRemove = friendRequestDeclined[curve]; + delete friendRequestDeclined[curve]; + if (friendRequestAccepted[curve]) { return void cb(true, toRemove); } + friendRequestAccepted[curve] = { + type: box.type, + hash: data.hash + }; + cb(false, toRemove); }; removeHandlers['FRIEND_REQUEST_ACCEPTED'] = function (ctx, box, data) { var curve = data.content.user.curvePublic || data.content.user; if (friendRequestAccepted[curve]) { delete friendRequestAccepted[curve]; } }; + handlers['CANCEL_FRIEND_REQUEST'] = function (ctx, box, data, cb) { + var f = friendRequest[data.msg.author]; + if (!f) { return void cb(true); } + cb(true, f); + }; + handlers['UNFRIEND'] = function (ctx, box, data, cb) { var curve = data.msg.author; var friend = Messaging.getFriend(ctx.store.proxy, curve); if (!friend) { return void cb(true); } delete ctx.store.proxy.friends[curve]; + delete ctx.store.proxy.friends_pending[curve]; if (ctx.store.messenger) { ctx.store.messenger.onFriendRemoved(curve, friend.channel); } diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index 11c912a44..d9c0dc4e2 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -271,7 +271,7 @@ proxy.mailboxes = { hash: hash }; showMessage(ctx, type, message); - cb(); + cb(hash); }, keys.curvePublic); }; box.queue.forEach(function (msg) { @@ -297,6 +297,7 @@ proxy.mailboxes = { msg: msg, hash: hash }; + var notify = box.ready; Handlers.add(ctx, box, message, function (dismissed, toDismiss) { if (toDismiss) { // List of other messages to remove dismiss(ctx, toDismiss, '', function () { @@ -314,8 +315,7 @@ proxy.mailboxes = { } box.content[hash] = msg; showMessage(ctx, type, message, null, function (obj) { - if (!box.ready) { return; } - if (!obj || !obj.msg) { return; } + if (!obj || !obj.msg || !notify) { return; } Notify.system(undefined, obj.msg); }); }); diff --git a/www/common/outer/messenger.js b/www/common/outer/messenger.js index 6587d9e4d..b5aab9ca5 100644 --- a/www/common/outer/messenger.js +++ b/www/common/outer/messenger.js @@ -459,6 +459,13 @@ define([ } }; + // Cancel pending friend requests + var cancelFriend = function (ctx, data, _cb) { + var cb = Util.once(_cb); + if (typeof(cb) !== 'function') { return void console.error('NO_CALLBACK'); } + ctx.Store.cancelFriendRequest(data, cb); + }; + var getAllClients = function (ctx) { var all = []; Array.prototype.push.apply(all, ctx.friendsClients); @@ -935,6 +942,7 @@ define([ if (AppConfig.availablePadTypes.indexOf('contacts') === -1) { return; } var ctx = { store: store, + Store: cfg.Store, updateMetadata: cfg.updateMetadata, pinPads: cfg.pinPads, emit: emit, @@ -1047,6 +1055,9 @@ define([ if (cmd === 'REMOVE_FRIEND') { return void removeFriend(ctx, data, cb); } + if (cmd === 'CANCEL_FRIEND') { + return void cancelFriend(ctx, data, cb); + } if (cmd === 'MUTE_USER') { return void muteUser(ctx, data, cb); } diff --git a/www/common/outer/roster.js b/www/common/outer/roster.js index 2c2675037..72f343477 100644 --- a/www/common/outer/roster.js +++ b/www/common/outer/roster.js @@ -1,5 +1,5 @@ (function () { -var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) { +var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback) { var Roster = {}; // this constant is somewhat arbitrary. @@ -587,6 +587,11 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) { // deleted while you are open // emit an event var onChannelError = function (info) { + if (Feedback) { Feedback.send('ROSTER_CHANNEL_ERROR='+(info && info.type)); } + if (info && info.type === "EUNKNOWN") { + // chainpad-netflux should recover by itself + return; + } if (!ready) { return void cb(info); } console.error("CHANNEL_ERROR", info); }; @@ -870,7 +875,8 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) { require("../../bower_components/chainpad-netflux/chainpad-netflux.js"), require("../../bower_components/json.sortify"), require("nthen"), - require("../../bower_components/chainpad-crypto/crypto") + require("../../bower_components/chainpad-crypto/crypto"), + null // no feedback here ); } else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) { require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); @@ -880,16 +886,18 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto) { '/bower_components/chainpad-netflux/chainpad-netflux.js', 'json.sortify', '/bower_components/nthen/index.js', - '/bower_components/chainpad-crypto/crypto.js' + '/bower_components/chainpad-crypto/crypto.js', + '/common/common-feedback.js', //'/bower_components/tweetnacl/nacl-fast.min.js', - ], function (Util, Hash, CPNF, Sortify, nThen, Crypto) { + ], function (Util, Hash, CPNF, Sortify, nThen, Crypto, Feedback) { return factory.apply(null, [ Util, Hash, CPNF, Sortify, nThen, - Crypto + Crypto, + Feedback ]); }); } else { diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index e34cd64df..2ac98cdb7 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -2,12 +2,13 @@ define([ '/common/common-hash.js', '/common/common-util.js', '/common/userObject.js', + '/common/outer/cache-store.js', '/bower_components/nthen/index.js', '/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad/chainpad.dist.js', -], function (Hash, Util, UserObject, +], function (Hash, Util, UserObject, Cache, nThen, Crypto, Listmap, ChainPad) { var SF = {}; @@ -174,6 +175,7 @@ define([ ChainPad: ChainPad, classic: true, network: network, + Cache: Cache, // XXX re-enable cache usage metadata: { validateKey: secret.keys.validateKey || undefined, owners: owners diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 6ac3e52b0..767448284 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -88,6 +88,7 @@ define([ CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin, GET_LAST_HASH: Store.getLastHash, GET_SNAPSHOT: Store.getSnapshot, + CORRUPTED_CACHE: Store.corruptedCache, // Drive DRIVE_USEROBJECT: Store.userObjectCommand, // Settings, diff --git a/www/common/outer/team.js b/www/common/outer/team.js index de9206511..26bd32f3c 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -12,6 +12,7 @@ define([ '/common/common-feedback.js', '/common/outer/invitation.js', '/common/cryptget.js', + '/common/outer/cache-store.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', '/bower_components/chainpad-crypto/crypto.js', @@ -21,7 +22,7 @@ define([ '/bower_components/saferphore/index.js', '/bower_components/tweetnacl/nacl-fast.min.js', ], function (Util, Hash, Constants, Realtime, - ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt, + ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt, Cache, Listmap, Crypto, CpNetflux, ChainPad, nThen, Saferphore) { var Team = {}; @@ -57,11 +58,11 @@ define([ }); proxy.on('disconnect', function () { team.offline = true; - team.sendEvent('NETWORK_DISCONNECT'); + team.sendEvent('NETWORK_DISCONNECT', team.id); }); proxy.on('reconnect', function () { team.offline = false; - team.sendEvent('NETWORK_RECONNECT'); + team.sendEvent('NETWORK_RECONNECT', team.id); }); } proxy.on('change', [], function (o, n, p) { @@ -426,6 +427,7 @@ define([ channel: secret.channel, crypto: crypto, ChainPad: ChainPad, + Cache: Cache, // XXX re-enable cache usage metadata: { validateKey: secret.keys.validateKey || undefined, }, @@ -573,6 +575,7 @@ define([ logLevel: 1, classic: true, ChainPad: ChainPad, + Cache: Cache, owners: [ctx.store.proxy.edPublic] }; nThen(function (waitFor) { @@ -931,7 +934,9 @@ define([ if (!team) { return void cb ({error: 'ENOENT'}); } if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } var state = team.roster.getState() || {}; - cb(state.metadata || {}); + var md = state.metadata || {}; + md.offline = team.offline; + cb(md); }; var setTeamMetadata = function (ctx, data, cId, cb) { @@ -1791,7 +1796,11 @@ define([ teams[id].keys.mailbox = deriveMailbox(teams[id]); } openChannel(ctx, teams[id], id, waitFor(function (err) { - if (err) { return void console.error(err); } + if (err) { + var txt = typeof(err) === "string" ? err : (err.type || err.message); + Feedback.send("TEAM_LOADING_ERROR="+txt); + return void console.error(err); + } console.debug('Team '+id+' ready'); })); }); @@ -1879,15 +1888,15 @@ define([ var t = Util.clone(teams); Object.keys(t).forEach(function (id) { // If failure to load the team, don't send it - if (ctx.teams[id]) { return; } + if (ctx.teams[id]) { + t[id].offline = ctx.teams[id].offline; + return; + } t[id].error = true; }); cb(t); }; team.execCommand = function (clientId, obj, cb) { - if (ctx.store.offline) { - return void cb({ error: 'OFFLINE' }); - } var cmd = obj.cmd; var data = obj.data; @@ -1911,30 +1920,36 @@ define([ return void setTeamMetadata(ctx, data, clientId, cb); } if (cmd === 'OFFER_OWNERSHIP') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void offerOwnership(ctx, data, clientId, cb); } if (cmd === 'ANSWER_OWNERSHIP') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void answerOwnership(ctx, data, clientId, cb); } if (cmd === 'DESCRIBE_USER') { return void describeUser(ctx, data, clientId, cb); } if (cmd === 'INVITE_TO_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void inviteToTeam(ctx, data, clientId, cb); } if (cmd === 'LEAVE_TEAM') { return void leaveTeam(ctx, data, clientId, cb); } if (cmd === 'JOIN_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void joinTeam(ctx, data, clientId, cb); } if (cmd === 'REMOVE_USER') { return void removeUser(ctx, data, clientId, cb); } if (cmd === 'DELETE_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void deleteTeam(ctx, data, clientId, cb); } if (cmd === 'CREATE_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void createTeam(ctx, data, clientId, cb); } if (cmd === 'GET_EDITABLE_FOLDERS') { @@ -1947,6 +1962,7 @@ define([ return void getPreviewContent(ctx, data, clientId, cb); } if (cmd === 'ACCEPT_LINK_INVITATION') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void acceptLinkInvitation(ctx, data, clientId, cb); } }; diff --git a/www/common/outer/upload.js b/www/common/outer/upload.js index 62f305612..0615ed7a7 100644 --- a/www/common/outer/upload.js +++ b/www/common/outer/upload.js @@ -1,9 +1,11 @@ define([ '/file/file-crypto.js', '/common/common-hash.js', + '/common/common-util.js', + '/common/outer/cache-store.js', '/bower_components/nthen/index.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function (FileCrypto, Hash, nThen) { +], function (FileCrypto, Hash, Util, Cache, nThen) { var Nacl = window.nacl; var module = {}; @@ -31,9 +33,11 @@ define([ }; var actual = 0; + var encryptedArr = []; var again = function (err, box) { if (err) { onError(err); } if (box) { + encryptedArr.push(box); actual += box.length; var progressValue = (actual / estimate * 100); progressValue = Math.min(progressValue, 100); @@ -55,9 +59,11 @@ define([ var uri = ['', 'blob', id.slice(0,2), id].join('/'); console.log("encrypted blob is now available as %s", uri); - - - cb(); + var box_u8 = Util.uint8ArrayJoin(encryptedArr); + Cache.setBlobCache(id, box_u8, function (err) { + if (err) { console.warn(err); } + cb(); + }); }); }; diff --git a/www/common/outer/worker-channel.js b/www/common/outer/worker-channel.js index 545ce435b..a359d8836 100644 --- a/www/common/outer/worker-channel.js +++ b/www/common/outer/worker-channel.js @@ -65,11 +65,13 @@ define([ cb(undefined, data.content, msg); }; evReady.reg(function () { - postMsg(JSON.stringify({ + var toSend = { txid: txid, content: content, - q: q - })); + q: q, + raw: opts.raw + }; + postMsg(opts.raw ? toSend : JSON.stringify(toSend)); }); }; @@ -84,12 +86,13 @@ define([ // If the type is a query, your handler will be invoked with a reply function that takes // one argument (the content to reply with). chan.on = function (queryType, handler, quiet) { - var h = function (data, msg) { + var h = function (data, msg, raw) { handler(data.content, function (replyContent) { - postMsg(JSON.stringify({ + var toSend = { txid: data.txid, content: replyContent - })); + }; + postMsg(raw ? toSend : JSON.stringify(toSend)); }, msg); }; (handlers[queryType] = handlers[queryType] || []).push(h); @@ -150,7 +153,7 @@ define([ onMsg.reg(function (msg) { if (!chanLoaded) { return; } if (!msg.data || msg.data === '_READY') { return; } - var data = JSON.parse(msg.data); + var data = typeof(msg.data) === "object" ? msg.data : JSON.parse(msg.data); if (typeof(data.ack) !== "undefined") { if (acks[data.txid]) { acks[data.txid](!data.ack); } } else if (typeof(data.q) === 'string') { @@ -163,7 +166,7 @@ define([ })); } handlers[data.q].forEach(function (f) { - f(data || JSON.parse(msg.data), msg); + f(data || JSON.parse(msg.data), msg, data && data.raw); data = undefined; }); } else { diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 88f4a052b..1c917939a 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -40,6 +40,14 @@ define([ userObject: userObject, leave: leave }; + if (proxy.on) { + proxy.on('disconnect', function () { + Env.folders[id].offline = true; + }); + proxy.on('reconnect', function () { + Env.folders[id].offline = false; + }); + } return userObject; }; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 3097cf6d2..5c0f9b511 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -221,6 +221,10 @@ define([ evStart.reg(function () { toolbar.deleted(); }); break; } + case STATE.READY: { + evStart.reg(function () { toolbar.ready(); }); + break; + } default: } var isEditable = (state === STATE.READY && !unsyncMode); @@ -467,7 +471,51 @@ define([ }); }; + var noCache = false; // Prevent reload loops + var onCorruptedCache = function () { + if (noCache) { + UI.errorLoadingScreen(Messages.unableToDisplay, false, function () { + common.gotoURL(''); + }); + } + noCache = true; + var sframeChan = common.getSframeChannel(); + sframeChan.event("EV_CORRUPTED_CACHE"); + }; + var onCacheReady = function () { + stateChange(STATE.INITIALIZING); + toolbar.offline(true); + var newContentStr = cpNfInner.chainpad.getUserDoc(); + if (toolbar) { + // Check if we have a new chainpad instance + toolbar.resetChainpad(cpNfInner.chainpad); + } + + // Invalid cache + if (newContentStr === '') { return void onCorruptedCache(); } + + var privateDat = cpNfInner.metadataMgr.getPrivateData(); + var type = privateDat.app; + + var newContent = JSON.parse(newContentStr); + var metadata = extractMetadata(newContent); + + // Make sure we're using the correct app for this cache + if (metadata && typeof(metadata.type) !== 'undefined' && metadata.type !== type) { + return void onCorruptedCache(); + } + + cpNfInner.metadataMgr.updateMetadata(metadata); + newContent = normalize(newContent); + if (!unsyncMode) { + contentUpdate(newContent, function () { return function () {}; }); + } + + UI.removeLoadingScreen(emitResize); + }; var onReady = function () { + toolbar.offline(false); + var newContentStr = cpNfInner.chainpad.getUserDoc(); if (state === STATE.DELETED) { return; } @@ -482,7 +530,6 @@ define([ var privateDat = cpNfInner.metadataMgr.getPrivateData(); var type = privateDat.app; - // contentUpdate may be async so we need an nthen here nThen(function (waitFor) { if (!newPad) { @@ -508,14 +555,19 @@ define([ console.log("Either this is an empty document which has not been touched"); console.log("Or else something is terribly wrong, reloading."); Feedback.send("NON_EMPTY_NEWDOC"); - setTimeout(function () { common.gotoURL(); }, 1000); + // The cache may be wrong, empty it and reload after. + waitFor.abort(); + onCorruptedCache(); return; } - console.log('updating title'); title.updateTitle(title.defaultTitle); evOnDefaultContentNeeded.fire(); } }).nThen(function () { + // We have a valid chainpad, reenable cache fix in case with reconnect with + // a corrupted cache + noCache = false; + stateChange(STATE.READY); firstConnection = false; @@ -551,6 +603,8 @@ define([ Thumb.initPadThumbnails(common, options.thumbnail); } } + + common.checkTrimHistory(); }); }; var onConnectionChange = function (info) { @@ -732,6 +786,7 @@ define([ onRemote: onRemote, onLocal: onLocal, onInit: onInit, + onCacheReady: function () { evStart.reg(onCacheReady); }, onReady: function () { evStart.reg(onReady); }, onConnectionChange: onConnectionChange, onError: onError, diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index abb1cebdf..1bdcc260f 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -34,6 +34,7 @@ define([ var onLocal = config.onLocal || function () { }; var setMyID = config.setMyID || function () { }; var onReady = config.onReady || function () { }; + var onCacheReady = config.onCacheReady || function () { }; var onError = config.onError || function () { }; var userName = config.userName; var initialState = config.initialState; @@ -93,6 +94,9 @@ define([ evInfiniteSpinner.fire(); }, 2000); + sframeChan.on('EV_RT_CACHE_READY', function () { + onCacheReady({realtime: chainpad}); + }); sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) { isReady = false; chainpad.abort(); diff --git a/www/common/sframe-chainpad-netflux-outer.js b/www/common/sframe-chainpad-netflux-outer.js index 72cfdef9a..f47dc812a 100644 --- a/www/common/sframe-chainpad-netflux-outer.js +++ b/www/common/sframe-chainpad-netflux-outer.js @@ -46,6 +46,7 @@ define([], function () { // shim between chainpad and netflux var msgIn = function (peer, msg) { try { + if (/^\[/.test(msg)) { return msg; } // Already decrypted var isHk = peer.length !== 32; var key = isNewHash ? validateKey : false; var decryptedMsg = Crypto.decrypt(msg, key, isHk); @@ -114,16 +115,25 @@ define([], function () { if (firstConnection) { firstConnection = false; // Add the handlers to the WebChannel - padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); }); padRpc.onJoinEvent.reg(function (m) { sframeChan.event('EV_RT_JOIN', m); }); padRpc.onLeaveEvent.reg(function (m) { sframeChan.event('EV_RT_LEAVE', m); }); } }; + padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); }); + padRpc.onDisconnectEvent.reg(function (permanent) { sframeChan.event('EV_RT_DISCONNECT', permanent); }); + padRpc.onCacheReadyEvent.reg(function () { + sframeChan.event('EV_RT_CACHE_READY'); + }); + + padRpc.onCacheEvent.reg(function () { + sframeChan.event('EV_RT_CACHE'); + }); + padRpc.onConnectEvent.reg(function (data) { onOpen(data); }); diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index a150a4acf..674a98ae8 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -1,5 +1,6 @@ define([ 'jquery', + '/api/config', '/file/file-crypto.js', '/common/make-backup.js', '/common/common-thumbnail.js', @@ -12,7 +13,7 @@ define([ '/bower_components/file-saver/FileSaver.min.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function ($, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages) { +], function ($, ApiConfig, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages) { var Nacl = window.nacl; var module = {}; @@ -48,7 +49,7 @@ define([ }; var tableHeader = h('div.cp-fileupload-header', [ - h('div.cp-fileupload-header-title', h('span', Messages.fileuploadHeader || 'Uploaded files')), + h('div.cp-fileupload-header-title', h('span', Messages.fileTableHeader)), h('div.cp-fileupload-header-close', h('span.fa.fa-times')), ]); @@ -136,13 +137,11 @@ define([ file.uid = Util.uid(); response.expect(file.uid, function (href) { - var mdMgr = common.getMetadataMgr(); - var origin = mdMgr.getPrivateData().origin; $link.prepend($('', {'class': 'fa fa-external-link'})); $link.attr('href', href) .click(function (e) { e.preventDefault(); - window.open(origin + $link.attr('href'), '_blank'); + common.openURL($link.attr('href')); }); var title = metadata.name; if (!config.noStore) { @@ -168,8 +167,12 @@ define([ if (config.onError) { config.onError(e); } if (e === 'TOO_LARGE') { - $pv.text(Messages.upload_tooLargeBrief); - return void UI.alert(Messages.upload_tooLarge); + var privateData = common.getMetadataMgr().getPrivateData(); + var l = privateData.plan ? ApiConfig.premiumUploadSize : false; + l = l || ApiConfig.maxUploadSize || '?'; + var maxSizeStr = Util.bytesToMegabytes(l); + $pv.text(Messages.error); + return void UI.alert(Messages._getKey('upload_tooLargeBrief', [maxSizeStr])); } if (e === 'NOT_ENOUGH_SPACE') { $pv.text(Messages.upload_notEnoughSpaceBrief); @@ -262,7 +265,8 @@ define([ // name $('').append($link).appendTo($tr); // size - $('').text(UIElements.prettySize(estimate)).appendTo($tr); + var size = estimate ? UIElements.prettySize(estimate) : ''; + $(h('td.cp-fileupload-size')).text(size).appendTo($tr); // progress $('', {'class': 'cp-fileupload-table-progress'}).append($progressContainer).appendTo($tr); // cancel @@ -590,12 +594,11 @@ define([ queue.next(); }; - /* var cancelled = function () { $row.find('.cp-fileupload-table-cancel').addClass('cancelled').html('').append(h('span.fa.fa-minus')); queue.inProgress = false; queue.next(); - };*/ + }; /** * Update progress in the download panel, for downloading a file @@ -629,6 +632,17 @@ define([ */ var updateProgress = function (progressValue) { var text = Math.round(progressValue*100) + '%'; + if (Array.isArray(data.list)) { + text = Messages._getKey('download_zip_file', [Math.round(progressValue * data.list.length), data.list.length]); + } + if (progressValue === 2) { + text = Messages.download_zip; + progressValue = 1; + } + if (progressValue === 3) { + text = "100%"; + progressValue = 1; + } $pv.text(text); $pb.css({ width: (progressValue * 100) + '%' @@ -640,8 +654,10 @@ define([ fileHost: privateData.fileHost, get: common.getPad, sframeChan: sframeChan, + cache: common.getCache() }; - downloadFunction(ctx, data, function (err, obj) { + + var dl = downloadFunction(ctx, data, function (err, obj) { $link.prepend($('', {'class': 'fa fa-external-link'})) .attr('href', '#') .click(function (e) { @@ -657,19 +673,17 @@ define([ folderProgress: updateProgress, }); -/* - var $cancel = $('', {'class': 'cp-fileupload-table-cancel-button fa fa-times'}).click(function () { - dl.cancel(); - $cancel.remove(); - $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); - cancelled(); - }); -*/ - - $row.find('.cp-fileupload-table-cancel') - .html('') - .append(h('span.fa.fa-minus')); - //.append($cancel); + var $cancel = $row.find('.cp-fileupload-table-cancel').html(''); + if (dl && dl.cancel) { + $('', { + 'class': 'cp-fileupload-table-cancel-button fa fa-times' + }).click(function () { + dl.cancel(); + $cancel.remove(); + $row.find('.cp-fileupload-table-progress-value').text(Messages.upload_cancelled); + cancelled(); + }).appendTo($cancel); + } }; File.downloadFile = function (fData, cb) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index ad128db33..ca760efb8 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -8,7 +8,7 @@ define([ ], function (nThen, ApiConfig, RequireConfig, Messages, $) { var common = {}; - common.initIframe = function (waitFor, isRt) { + common.initIframe = function (waitFor, isRt, pathname) { var requireConfig = RequireConfig(); var lang = Messages._languageUsed; var req = { @@ -31,7 +31,7 @@ define([ } document.getElementById('sbox-iframe').setAttribute('src', - ApiConfig.httpSafeOrigin + window.location.pathname + 'inner.html?' + + ApiConfig.httpSafeOrigin + (pathname || window.location.pathname) + 'inner.html?' + requireConfig.urlArgs + '#' + encodeURIComponent(JSON.stringify(req))); // This is a cheap trick to avoid loading sframe-channel in parallel with the @@ -97,15 +97,17 @@ define([ '/common/common-hash.js', '/common/common-util.js', '/common/common-realtime.js', + '/common/notify.js', '/common/common-constants.js', '/common/common-feedback.js', '/common/outer/local-store.js', + '/common/outer/cache-store.js', '/customize/application_config.js', '/common/test.js', '/common/userObject.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, - _SecureIframe, _Messaging, _Notifier, _Hash, _Util, _Realtime, - _Constants, _Feedback, _LocalStore, _AppConfig, _Test, _UserObject) { + _SecureIframe, _Messaging, _Notifier, _Hash, _Util, _Realtime, _Notify, + _Constants, _Feedback, _LocalStore, _Cache, _AppConfig, _Test, _UserObject) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = Utils.Crypto = _Crypto; @@ -120,7 +122,9 @@ define([ Utils.Constants = _Constants; Utils.Feedback = _Feedback; Utils.LocalStore = _LocalStore; + Utils.Cache = _Cache; Utils.UserObject = _UserObject; + Utils.Notify = _Notify; Utils.currentPad = currentPad; AppConfig = _AppConfig; Test = _Test; @@ -478,6 +482,7 @@ define([ // We've received a link without /p/ and it doesn't work without a password: abort return void todo(); } + // Wrong password or deleted file? askPassword(true, passwordCfg); })); @@ -532,6 +537,12 @@ define([ var edPublic, curvePublic, notifications, isTemplate; var settings = {}; var isSafe = ['debug', 'profile', 'drive', 'teams'].indexOf(currentPad.app) !== -1; + + var isDeleted = isNewFile && currentPad.hash.length > 0; + if (isDeleted) { + Utils.Cache.clearChannel(secret.channel); + } + var updateMeta = function () { //console.log('EV_METADATA_UPDATE'); var metaObj; @@ -555,6 +566,7 @@ define([ defaultTitle: defaultTitle, type: cfg.type || parsed.type }; + var notifs = Utils.Notify.isSupported() && Utils.Notify.hasPermission(); var additionalPriv = { app: parsed.type, loggedIn: Utils.LocalStore.isLoggedIn(), @@ -569,12 +581,13 @@ define([ isPresent: parsed.hashData && parsed.hashData.present, isEmbed: parsed.hashData && parsed.hashData.embed, isHistoryVersion: parsed.hashData && parsed.hashData.versionHash, + notifications: notifs, accounts: { donateURL: Cryptpad.donateURL, upgradeURL: Cryptpad.upgradeURL }, isNewFile: isNewFile, - isDeleted: isNewFile && currentPad.hash.length > 0, + isDeleted: isDeleted, password: password, channel: secret.channel, enableSF: localStorage.CryptPad_SF === "1", // TODO to remove when enabled by default @@ -677,6 +690,22 @@ define([ }); }); + sframeChan.on('Q_GET_BLOB_CACHE', function (data, cb) { + if (!Utils.Cache) { return void cb({error: 'NOCACHE'}); } + Utils.Cache.getBlobCache(data.id, function (err, obj) { + if (err) { return void cb({error: err}); } + cb(obj); + }); + }); + sframeChan.on('Q_SET_BLOB_CACHE', function (data, cb) { + if (!Utils.Cache) { return void cb({error: 'NOCACHE'}); } + if (!data || !data.u8 || typeof(data.u8) !== "object") { return void cb({error: 'EINVAL'}); } + Utils.Cache.setBlobCache(data.id, data.u8, function (err) { + if (err) { return void cb({error: err}); } + cb(); + }); + }); + sframeChan.on('Q_GET_ATTRIBUTE', function (data, cb) { Cryptpad.getAttribute(data.key, function (e, data) { cb({ @@ -722,7 +751,16 @@ define([ sframeChan.on('EV_OPEN_URL', function (url) { if (url) { - window.open(url); + var a = window.open(url); + if (!a) { + sframeChan.event('EV_POPUP_BLOCKED'); + } + } + }); + + sframeChan.on('EV_OPEN_UNSAFE_URL', function (url) { + if (url) { + window.open(ApiConfig.httpSafeOrigin + '/bounce/#' + encodeURIComponent(url)); } }); @@ -1569,9 +1607,14 @@ define([ }); }); - if (cfg.messaging) { - Notifier.getPermission(); + sframeChan.on('Q_ASK_NOTIFICATION', function (data, cb) { + if (!Utils.Notify.isSupported()) { return void cb(false); } + Notification.requestPermission(function (s) { + cb(s === "granted"); + }); + }); + if (cfg.messaging) { sframeChan.on('Q_CHAT_OPENPADCHAT', function (data, cb) { Cryptpad.universal.execCommand({ type: 'messenger', @@ -1683,6 +1726,10 @@ define([ }); }; + sframeChan.on('EV_CORRUPTED_CACHE', function () { + Cryptpad.onCorruptedCache(secret.channel); + }); + sframeChan.on('Q_CREATE_PAD', function (data, cb) { if (!isNewFile || rtStarted) { return; } // Create a new hash @@ -1797,7 +1844,12 @@ define([ } startRealtime(); cb(); - }, cryptputCfg); + }, cryptputCfg, function (progress) { + sframeChan.event('EV_LOADING_INFO', { + type: 'pad', + progress: progress + }); + }); return; } // Start realtime outside the iframe and callback diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 7b56bdf07..33ebd0c78 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -11,6 +11,7 @@ define([ '/common/sframe-common-codemirror.js', '/common/sframe-common-cursor.js', '/common/sframe-common-mailbox.js', + '/common/inner/cache.js', '/common/inner/common-mediatag.js', '/common/metadata-manager.js', @@ -36,6 +37,7 @@ define([ CodeMirror, Cursor, Mailbox, + Cache, MT, MetadataMgr, AppConfig, @@ -142,7 +144,7 @@ define([ } return; }; - funcs.importMediaTag = function ($mt) { + var getMtData = function ($mt) { if (!$mt || !$mt.is('media-tag')) { return; } var chanStr = $mt.attr('src'); var keyStr = $mt.attr('data-crypto-key'); @@ -154,10 +156,27 @@ define([ var channel = src.replace(/\/blob\/[0-9a-f]{2}\//i, ''); // Get key var key = keyStr.replace(/cryptpad:/i, ''); + return { + channel: channel, + key: key + }; + }; + funcs.getHashFromMediaTag = function ($mt) { + var data = getMtData($mt); + if (!data) { return; } + return Hash.getFileHashFromKeys({ + version: 1, + channel: data.channel, + keys: { fileKeyStr: data.key } + }); + }; + funcs.importMediaTag = function ($mt) { + var data = getMtData($mt); + if (!data) { return; } var metadata = $mt[0]._mediaObject._blob.metadata; ctx.sframeChan.query('Q_IMPORT_MEDIATAG', { - channel: channel, - key: key, + channel: data.channel, + key: data.key, name: metadata.name, type: metadata.type, owners: metadata.owners @@ -264,6 +283,65 @@ define([ return teamChatChannel; }; + // When opening a pad, if were an owner check the history size and prompt for trimming if + // necessary + funcs.checkTrimHistory = function (channels, isDrive) { + channels = channels || []; + var priv = ctx.metadataMgr.getPrivateData(); + + var limit = 100 * 1024 * 1024; // 100MB + + var owned; + nThen(function (w) { + if (isDrive) { + funcs.getAttribute(['drive', 'trim'], w(function (err, val) { + if (err || typeof(val) !== "number") { return; } + if (val < (+new Date())) { return; } + w.abort(); + })); + return; + } + funcs.getPadAttribute('trim', w(function (err, val) { + if (err || typeof(val) !== "number") { return; } + if (val < (+new Date())) { return; } + w.abort(); + })); + }).nThen(function (w) { + // Check ownership + // DRIVE + if (isDrive) { + if (!priv.isDriveOwned) { return void w.abort(); } + return; + } + // PAD + channels.push({ channel: priv.channel }); + funcs.getPadMetadata({ + channel: priv.channel + }, w(function (md) { + if (md && md.error) { return void w.abort(); } + var owners = md.owners; + owned = funcs.isOwned(owners); + if (!owned) { return void w.abort(); } + })); + }).nThen(function () { + // We're an owner: check the history size + var history = funcs.makeUniversal('history'); + history.execCommand('GET_HISTORY_SIZE', { + account: isDrive, + pad: !isDrive, + channels: channels, + teamId: typeof(owned) === "number" && owned + }, function (obj) { + if (obj && obj.error) { return; } // can't get history size: abort + var bytes = obj.size; + if (!bytes || typeof(bytes) !== "number") { return; } // no history: abort + if (bytes < limit) { return; } + obj.drive = isDrive; + UIElements.displayTrimHistoryPrompt(funcs, obj); + }); + }); + }; + var cursorChannel; // common-ui-elements needs to be able to get the cursor channel to put it in metadata when // importing a template @@ -528,6 +606,10 @@ define([ }); }; + funcs.getCache = function () { + return ctx.cache; + }; + /* funcs.storeLinkToClipboard = function (readOnly, cb) { ctx.sframeChan.query('Q_STORE_LINK_TO_CLIPBOARD', readOnly, function (err) { if (cb) { cb(err); } @@ -555,7 +637,11 @@ define([ return window.location.origin + '/bounce/#' + encodeURIComponent(url); }; funcs.openUnsafeURL = function (url) { - var bounceHref = funcs.getBounceURL(url); + var app = ctx.metadataMgr.getPrivateData().app; + if (app === "sheet") { + return void ctx.sframeChan.event('EV_OPEN_UNSAFE_URL', url); + } + var bounceHref = window.location.origin + '/bounce/#' + encodeURIComponent(url); window.open(bounceHref); }; @@ -685,6 +771,10 @@ define([ UI.errorLoadingScreen(Messages.password_error_seed); }); + ctx.sframeChan.on("EV_POPUP_BLOCKED", function () { + UI.alert(Messages.errorPopupBlocked); + }); + ctx.sframeChan.on("EV_EXPIRED_ERROR", function () { funcs.onServerError({ type: 'EEXPIRED' @@ -729,12 +819,24 @@ define([ modules[type].onEvent(obj.data); }); + ctx.cache = Cache.create(ctx.sframeChan); + ctx.metadataMgr.onReady(waitFor()); }).nThen(function () { var privateData = ctx.metadataMgr.getPrivateData(); funcs.addShortcuts(window, Boolean(privateData.app)); + var mt = Util.find(privateData, ['settings', 'general', 'mediatag-size']); + if (MT.MediaTag && typeof(mt) === "number") { + var maxMtSize = mt === -1 ? Infinity : mt * 1024 * 1024; + MT.MediaTag.setDefaultConfig('maxDownloadSize', maxMtSize); + } + + if (MT.MediaTag && ctx.cache) { + MT.MediaTag.setDefaultConfig('Cache', ctx.cache); + } + try { var feedback = privateData.feedbackAllowed; Feedback.init(feedback); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 7b1f02db4..f8e6d7754 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -229,7 +229,6 @@ MessengerUI, Messages) { // Editors var pendingFriends = Common.getPendingFriends(); // Friend requests sent var friendRequests = Common.getFriendRequests(); // Friend requests received - var friendTo = +new Date() - (2 * 24 * 3600 * 1000); editUsersNames.forEach(function (data) { var name = data.name || Messages.anonymous; var $span = $('', {'class': 'cp-avatar'}); @@ -297,7 +296,7 @@ MessengerUI, Messages) { } } else if (Common.isLoggedIn() && data.curvePublic && !friends[data.curvePublic] && !priv.readOnly) { - if (pendingFriends[data.curvePublic] && pendingFriends[data.curvePublic] > friendTo) { + if (pendingFriends[data.curvePublic]) { $('
- diff --git a/www/file/inner.js b/www/file/inner.js index b2aef53ed..59291c964 100644 --- a/www/file/inner.js +++ b/www/file/inner.js @@ -8,6 +8,7 @@ define([ '/common/common-util.js', '/common/common-hash.js', '/common/common-interface.js', + '/common/hyperscript.js', '/customize/messages.js', '/file/file-crypto.js', @@ -29,6 +30,7 @@ define([ Util, Hash, UI, + h, Messages, FileCrypto, MediaTag) @@ -37,18 +39,12 @@ define([ var Nacl = window.nacl; var APP = window.APP = {}; - MediaTag.setDefaultConfig('download', { - text: Messages.download_mt_button - }); var andThen = function (common) { var $appContainer = $('#cp-app-file-content'); var $form = $('#cp-app-file-upload-form'); - var $dlform = $('#cp-app-file-download-form'); var $dlview = $('#cp-app-file-download-view'); var $label = $form.find('label'); - var $dllabel = $dlform.find('label span'); - var $progress = $('#cp-app-file-dlprogress'); var $bar = $('.cp-toolbar-container'); var $body = $('body'); @@ -88,142 +84,86 @@ define([ var toolbar = APP.toolbar = Toolbar.create(configTb); if (!uploadMode) { - var hexFileName = secret.channel; - var src = fileHost + Hash.getBlobPathFromHex(hexFileName); - var key = secret.keys && secret.keys.cryptKey; - var cryptKey = Nacl.util.encodeBase64(key); - - FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) { - if (e) { - if (e === 'XHR_ERROR') { - return void UI.errorLoadingScreen(Messages.download_resourceNotAvailable, false, function () { - common.gotoURL('/file/'); - }); + (function () { + var hexFileName = secret.channel; + var src = fileHost + Hash.getBlobPathFromHex(hexFileName); + var key = secret.keys && secret.keys.cryptKey; + var cryptKey = Nacl.util.encodeBase64(key); + + var $mt = $dlview.find('media-tag'); + $mt.attr('src', src); + $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); + $mt.css('transform', 'scale(2)'); + + var rightsideDisplayed = false; + var metadataReceived = false; + UI.removeLoadingScreen(); + $dlview.show(); + + MediaTag($mt[0]).on('complete', function (decrypted) { + $mt.css('transform', ''); + if (!rightsideDisplayed) { + toolbar.$drawer + .append(common.createButton('export', true, {}, function () { + saveAs(decrypted.content, decrypted.metadata.name); + })); + rightsideDisplayed = true; } - return void console.error(e); - } - - // Add pad attributes when the file is saved in the drive - Title.onTitleChange(function () { - var owners = metadata.owners; - if (owners) { common.setPadAttribute('owners', owners); } - common.setPadAttribute('fileType', metadata.type); - }); - $(document).on('cpPadStored', function () { - var owners = metadata.owners; - if (owners) { common.setPadAttribute('owners', owners); } - common.setPadAttribute('fileType', metadata.type); - }); - - // Save to the drive or update the acces time - var title = document.title = metadata.name; - Title.updateTitle(title || Title.defaultTitle); - - var owners = metadata.owners; - if (owners) { - common.setPadAttribute('owners', owners); - } - if (metadata.type) { - common.setPadAttribute('fileType', metadata.type); - } - - toolbar.addElement(['pageTitle'], { - pageTitle: title, - title: Title.getTitleConfig(), - }); - toolbar.$drawer.append(common.createButton('forget', true)); - toolbar.$drawer.append(common.createButton('properties', true)); - if (common.isLoggedIn()) { - toolbar.$drawer.append(common.createButton('hashtag', true)); - } - toolbar.$file.show(); - - var displayFile = function (ev, sizeMb, CB) { - var called_back; - var cb = function (e) { - if (called_back) { return; } - called_back = true; - if (CB) { CB(e); } - }; - - var $mt = $dlview.find('media-tag'); - $mt.attr('src', src); - $mt.attr('data-crypto-key', 'cryptpad:'+cryptKey); - var rightsideDisplayed = false; - - MediaTag($mt[0]).on('complete', function (decrypted) { - $dlview.show(); - $dlform.hide(); - var $dlButton = $dlview.find('media-tag button'); - if (ev) { $dlButton.click(); } - - if (!rightsideDisplayed) { - toolbar.$drawer - .append(common.createButton('export', true, {}, function () { - saveAs(decrypted.content, decrypted.metadata.name); - })); - rightsideDisplayed = true; - } - - // make pdfs big - var toolbarHeight = $('#cp-toolbar').height(); - var $another_iframe = $('media-tag iframe').css({ - 'height': 'calc(100vh - ' + toolbarHeight + 'px)', - 'width': '100vw', - 'position': 'absolute', - 'bottom': 0, - 'left': 0, - 'border': 0 - }); - - if ($another_iframe.length) { - $another_iframe.load(function () { - cb(); - }); - } else { - cb(); - } - }).on('progress', function (data) { - var p = data.progress +'%'; - $progress.width(p); - }).on('error', function (err) { - console.error(err); + // make pdfs big + var toolbarHeight = $('#cp-toolbar').height(); + $('media-tag iframe').css({ + 'height': 'calc(100vh - ' + toolbarHeight + 'px)', + 'width': '100vw', + 'position': 'absolute', + 'bottom': 0, + 'left': 0, + 'border': 0 + }); + }).on('metadata', function (metadata) { + if (metadataReceived) { return; } + metadataReceived = true; + // Add pad attributes when the file is saved in the drive + Title.onTitleChange(function () { + var owners = metadata.owners; + if (owners) { common.setPadAttribute('owners', owners); } + common.setPadAttribute('fileType', metadata.type); + }); + $(document).on('cpPadStored', function () { + var owners = metadata.owners; + if (owners) { common.setPadAttribute('owners', owners); } + common.setPadAttribute('fileType', metadata.type); }); - }; - var todoBigFile = function (sizeMb) { - $dlform.show(); - UI.removeLoadingScreen(); - $dllabel.append($('
')); - $dllabel.append(Util.fixHTML(metadata.name)); + // Save to the drive or update the acces time + var title = document.title = metadata.name; + Title.updateTitle(title || Title.defaultTitle); - // don't display the size if you don't know it. - if (typeof(sizeM) === 'number') { - $dllabel.append($('
')); - $dllabel.append(Messages._getKey('formattedMB', [sizeMb])); + var owners = metadata.owners; + if (owners) { + common.setPadAttribute('owners', owners); } - var decrypting = false; - var onClick = function (ev) { - if (decrypting) { return; } - decrypting = true; - displayFile(ev, sizeMb, function (err) { - $appContainer.css('background-color', - common.getAppConfig().appBackgroundColor); - if (err) { UI.alert(err); } - }); - }; - if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); } - $dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick); - }; - common.getFileSize(hexFileName, function (e, data) { - if (e) { - return void UI.errorLoadingScreen(e); + if (metadata.type) { + common.setPadAttribute('fileType', metadata.type); } - var size = Util.bytesToMegabytes(data); - return void todoBigFile(size); + + toolbar.addElement(['pageTitle'], { + pageTitle: title, + title: Title.getTitleConfig(), + }); + toolbar.$drawer.append(common.createButton('forget', true)); + toolbar.$drawer.append(common.createButton('properties', true)); + if (common.isLoggedIn()) { + toolbar.$drawer.append(common.createButton('hashtag', true)); + } + toolbar.$file.show(); + }).on('error', function (err) { + $appContainer.css('background-color', + common.getAppConfig().appBackgroundColor); + UI.warn(Messages.error); + console.error(err); }); - }); + })(); return; } diff --git a/www/oodoc/inner.html b/www/oodoc/inner.html index 884ae5a00..1891839ff 100644 --- a/www/oodoc/inner.html +++ b/www/oodoc/inner.html @@ -9,9 +9,11 @@
-
-
- +
+
+
+ +
diff --git a/www/ooslide/inner.html b/www/ooslide/inner.html index e7c4e111f..53833e600 100644 --- a/www/ooslide/inner.html +++ b/www/ooslide/inner.html @@ -9,9 +9,11 @@
-
-
- +
+
+
+ +
diff --git a/www/pad/inner.js b/www/pad/inner.js index 235b4fafa..4968cf4cd 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -46,6 +46,7 @@ define([ '/common/test.js', '/bower_components/diff-dom/diffDOM.js', + '/bower_components/file-saver/FileSaver.min.js', 'css!/customize/src/print.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', @@ -462,7 +463,9 @@ define([ setTimeout(function() { // Just in case var tags = dom.querySelectorAll('media-tag:empty'); Array.prototype.slice.call(tags).forEach(function(el) { - MediaTag(el); + var mediaObject = MediaTag(el, { + body: dom + }); $(el).on('keydown', function(e) { if ([8, 46].indexOf(e.which) !== -1) { $(el).remove(); @@ -472,13 +475,17 @@ define([ var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList') { - var list_values = [].slice.call(el.children); + var list_values = slice(el.children) + .map(function (el) { return el.outerHTML; }) + .join(''); mediaTagMap[el.getAttribute('src')] = list_values; + if (mediaObject.complete) { observer.disconnect(); } } }); }); observer.observe(el, { attributes: false, + subtree: true, childList: true, characterData: false }); @@ -491,9 +498,10 @@ define([ Array.prototype.slice.call(tags).forEach(function(tag) { var src = tag.getAttribute('src'); if (mediaTagMap[src]) { - mediaTagMap[src].forEach(function(n) { - tag.appendChild(n.cloneNode()); - }); + tag.innerHTML = mediaTagMap[src]; + /*mediaTagMap[src].forEach(function(n) { + tag.appendChild(n.cloneNode(true)); + });*/ } }); }; @@ -1084,6 +1092,9 @@ define([ border: Messages.pad_mediatagBorder, preview: Messages.pad_mediatagPreview, 'import': Messages.pad_mediatagImport, + download: Messages.download_mt_button, + share: Messages.pad_mediatagShare, + open: Messages.pad_mediatagOpen, options: Messages.pad_mediatagOptions }; Ckeditor._commentsTranslations = { @@ -1164,6 +1175,28 @@ define([ editor.plugins.mediatag.import = function($mt) { framework._.sfCommon.importMediaTag($mt); }; + editor.plugins.mediatag.download = function($mt) { + var media = Util.find($mt, [0, '_mediaObject']); + if (!media) { return void console.error('no media'); } + if (!media.complete) { return void UI.warn(Messages.mediatag_notReady); } + if (!(media && media._blob)) { return void console.error($mt); } + window.saveAs(media._blob.content, media.name); + }; + editor.plugins.mediatag.open = function($mt) { + var hash = framework._.sfCommon.getHashFromMediaTag($mt); + framework._.sfCommon.openURL(Hash.hashToHref(hash, 'file')); + }; + editor.plugins.mediatag.share = function($mt) { + var data = { + file: true, + pathname: '/file/', + hashes: { + fileHash: framework._.sfCommon.getHashFromMediaTag($mt) + }, + title: Util.find($mt[0], ['_mediaObject', 'name']) || '' + }; + framework._.sfCommon.getSframeChannel().event('EV_SHARE_OPEN', data); + }; Links.init(Ckeditor, editor); }).nThen(function() { // Move ckeditor parts to have a structure like the other apps diff --git a/www/pad/mediatag-plugin.js b/www/pad/mediatag-plugin.js index 46747b18a..22a732748 100644 --- a/www/pad/mediatag-plugin.js +++ b/www/pad/mediatag-plugin.js @@ -53,15 +53,57 @@ editor.plugins.mediatag.import($mt); } }); + editor.addCommand('downloadMT', { + exec: function (editor) { + var w = targetWidget; + targetWidget = undefined; + var $mt = $(w.$).find('media-tag'); + editor.plugins.mediatag.download($mt); + } + }); + editor.addCommand('openMT', { + exec: function (editor) { + var w = targetWidget; + targetWidget = undefined; + var $mt = $(w.$).find('media-tag'); + editor.plugins.mediatag.open($mt); + } + }); + editor.addCommand('shareMT', { + exec: function (editor) { + var w = targetWidget; + targetWidget = undefined; + var $mt = $(w.$).find('media-tag'); + editor.plugins.mediatag.share($mt); + } + }); if (editor.addMenuItems) { editor.addMenuGroup('mediatag'); + editor.addMenuItem('open', { + label: Messages.open, + icon: 'iframe', + command: 'openMT', + group: 'mediatag' + }); + editor.addMenuItem('share', { + label: Messages.share, + icon: 'link', + command: 'shareMT', + group: 'mediatag' + }); editor.addMenuItem('importMediatag', { label: Messages.import, icon: 'save', command: 'importMediatag', group: 'mediatag' }); + editor.addMenuItem('download', { + label: Messages.download, + icon: 'save', + command: 'downloadMT', + group: 'mediatag' + }); editor.addMenuItem('mediatag', { label: Messages.options, icon: 'image', @@ -76,6 +118,9 @@ targetWidget = element; return { mediatag: CKEDITOR.TRISTATE_OFF, + open: CKEDITOR.TRISTATE_OFF, + share: CKEDITOR.TRISTATE_OFF, + download: CKEDITOR.TRISTATE_OFF, importMediatag: CKEDITOR.TRISTATE_OFF, }; } diff --git a/www/poll/inner.js b/www/poll/inner.js index b36466a13..bd40bc678 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -1087,6 +1087,8 @@ define([ common.openPadChat(function () {}); UI.removeLoadingScreen(); + + common.checkTrimHistory(); }; var onError = function (info) { diff --git a/www/profile/inner.js b/www/profile/inner.js index 7a872fdd0..375f9588c 100644 --- a/www/profile/inner.js +++ b/www/profile/inner.js @@ -101,13 +101,13 @@ define([ var url = APP.origin + '/profile/#' + hash; $('