mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Merge branch 'staging' into worker-loading
This commit is contained in:
commit
93f8ea2d07
@ -117,7 +117,6 @@ define(req, function(AppConfig, Default, Language) {
|
||||
|
||||
Messages._languages = map;
|
||||
Messages._languageUsed = language;
|
||||
|
||||
// Get keys with parameters
|
||||
Messages._getKey = function (key, argArray) {
|
||||
if (!Messages[key]) { return '?'; }
|
||||
@ -135,6 +134,14 @@ define(req, function(AppConfig, Default, Language) {
|
||||
}
|
||||
};
|
||||
|
||||
Messages.addItemBottom = 'Add item to bottom of the board'; // XXX or 'Add card..'
|
||||
Messages.addItemTop = 'Add item to top of the board'; // XXX or 'Add card..'
|
||||
|
||||
Messages.admin_logoSize_error = "The logo size is too large"; // XXX
|
||||
Messages.limit_error = "Please enter a valid number"; // XXX
|
||||
Messages.positiveNumber_error = "Please enter a positive number"; // XXX
|
||||
Messages.answerType_error = "Please select how to answer the form"; // XXX
|
||||
|
||||
// XXX
|
||||
Messages.admin_cat_admins = "Administrators";
|
||||
Messages.admin_admin = "Admin";
|
||||
@ -144,6 +151,8 @@ define(req, function(AppConfig, Default, Language) {
|
||||
Messages.admin_addAdminsHint = "Add administrators from their public key or from your contacts list";
|
||||
Messages.admin_addAdminsAdd = "Promote a contact to admin";
|
||||
Messages.admin_addKeyLabel = "Add an admin using their public key";
|
||||
Messages.admin_errorAddKeyLabel = "Add a valid public key";
|
||||
Messages.admin_errorAddAdmins = "Pick a contact to promote to admin";
|
||||
|
||||
Messages.admin_listName = "Admin name";
|
||||
Messages.admin_listKey = "Admin key";
|
||||
@ -152,6 +161,9 @@ define(req, function(AppConfig, Default, Language) {
|
||||
Messages.admin_listHardcoded = "Admin added into config.js. Can only be removed by editing the config file.";
|
||||
Messages.admin_listConfirm = "Are you sure you want to remove the admin rights of this user?";
|
||||
|
||||
Messages.fm_link_invalid = "Please provide a valid URL"; // XXX
|
||||
Messages.skipLink = "Skip to main content"; // XXX
|
||||
|
||||
return Messages;
|
||||
|
||||
});
|
||||
|
||||
@ -445,6 +445,23 @@
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cp-toolbar-skip-link {
|
||||
position: absolute;
|
||||
top: -100px;
|
||||
left: 45%;
|
||||
background-color: @cp_buttons-primary;
|
||||
color: @cp_buttons-primary-text;
|
||||
padding: 0.3rem 0.5rem;
|
||||
font-size: 1rem;
|
||||
text-decoration: none;
|
||||
border-radius: @variables_radius;
|
||||
z-index: 1000;
|
||||
transition: top 0.3s ease;
|
||||
}
|
||||
.cp-toolbar-skip-link:focus {
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: @browser_media-medium-screen),
|
||||
screen and (max-height: 500px) {
|
||||
flex-wrap: wrap;
|
||||
|
||||
@ -70,6 +70,7 @@ module.exports = [{
|
||||
"linebreak-style": ["off", "unix"],
|
||||
quotes: ["off", "single"],
|
||||
semi: ["error", "always"],
|
||||
eqeqeq: ["error", "always"],
|
||||
"no-irregular-whitespace": ["off"],
|
||||
"no-self-assign": ["off"],
|
||||
"no-empty": ["off"],
|
||||
|
||||
@ -37,7 +37,17 @@ main() {
|
||||
install_x2t v7.3+1 ab0c05b0e4c81071acea83f0c6a8e75f5870c360ec4abc4af09105dd9b52264af9711ec0b7020e87095193ac9b6e20305e446f2321a541f743626a598e5318c1
|
||||
|
||||
rm -rf "$BUILDS_DIR"
|
||||
if command -v rdfind &>/dev/null; then
|
||||
|
||||
if [ "${RDFIND+x}" != "x" ]; then
|
||||
if command -v rdfind &>/dev/null; then
|
||||
RDFIND="1"
|
||||
else
|
||||
RDFIND="0"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$RDFIND" = "1" ]; then
|
||||
ensure_command_available rdfind
|
||||
rdfind -makehardlinks true -makeresultsfile false $OO_DIR/v*
|
||||
fi
|
||||
}
|
||||
@ -73,6 +83,18 @@ parse_arguments() {
|
||||
TRUST_REPOSITORY="1"
|
||||
shift
|
||||
;;
|
||||
--check)
|
||||
CHECK="1"
|
||||
shift
|
||||
;;
|
||||
--rdfind)
|
||||
RDFIND="1"
|
||||
shift
|
||||
;;
|
||||
--no-rdfind)
|
||||
RDFIND="0"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
shift
|
||||
@ -103,9 +125,6 @@ show_help() {
|
||||
cat <<EOF
|
||||
install-onlyoffice installs or upgrades OnlyOffice.
|
||||
|
||||
NOTE: When you have rdfind installed, it will be used to save ~650MB of disk
|
||||
space.
|
||||
|
||||
OPTIONS:
|
||||
-h, --help
|
||||
Show this help.
|
||||
@ -120,6 +139,18 @@ OPTIONS:
|
||||
as a safe.directory.
|
||||
https://git-scm.com/docs/git-config/#Documentation/git-config.txt-safedirectory
|
||||
|
||||
--check
|
||||
Do not install OnlyOffice, only check if the existing installation
|
||||
is up to date. Exits 0 if it is up to date, nonzero otherwise.
|
||||
|
||||
--rdfind
|
||||
Run rdfind to save ~650MB of disk space.
|
||||
If neither '--rdfind' nor '--no-rdfind' is specified, then rdfind
|
||||
will only run if rdfind is installed.
|
||||
|
||||
--no-rdfind
|
||||
Do not run rdfind, even if it is installed.
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
@ -143,7 +174,17 @@ install_version() {
|
||||
local LAST_DIR
|
||||
LAST_DIR=$(pwd)
|
||||
|
||||
if [ ! -e "$FULL_DIR"/.commit ] || [ "$(cat "$FULL_DIR"/.commit)" != "$COMMIT" ]; then
|
||||
local ACTUAL_COMMIT="not installed"
|
||||
if [ -e "$FULL_DIR"/.commit ]; then
|
||||
ACTUAL_COMMIT="$(cat "$FULL_DIR"/.commit)"
|
||||
fi
|
||||
|
||||
if [ "$ACTUAL_COMMIT" != "$COMMIT" ]; then
|
||||
if [ ${CHECK+x} ]; then
|
||||
echo "Wrong commit of $FULL_DIR found. Expected: $COMMIT. Actual: $ACTUAL_COMMIT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ensure_oo_is_downloaded
|
||||
|
||||
rm -rf "$FULL_DIR"
|
||||
@ -166,22 +207,31 @@ install_version() {
|
||||
}
|
||||
|
||||
install_x2t() {
|
||||
ensure_command_available curl
|
||||
ensure_command_available sha512sum
|
||||
ensure_command_available unzip
|
||||
|
||||
local VERSION=$1
|
||||
local HASH=$2
|
||||
local LAST_DIR
|
||||
LAST_DIR=$(pwd)
|
||||
local X2T_DIR=$OO_DIR/x2t
|
||||
|
||||
local ACTUAL_VERSION="not installed"
|
||||
if [ -e "$X2T_DIR"/.version ]; then
|
||||
ACTUAL_VERSION="$(cat "$X2T_DIR"/.version)"
|
||||
fi
|
||||
|
||||
if [ ! -e "$X2T_DIR"/.version ] || [ "$(cat "$X2T_DIR"/.version)" != "$VERSION" ]; then
|
||||
if [ ${CHECK+x} ]; then
|
||||
echo "Wrong version of x2t found. Expected: $VERSION. Actual: $ACTUAL_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$X2T_DIR"
|
||||
mkdir -p "$X2T_DIR"
|
||||
|
||||
cd "$X2T_DIR"
|
||||
|
||||
ensure_command_available curl
|
||||
ensure_command_available sha512sum
|
||||
ensure_command_available unzip
|
||||
curl "https://github.com/cryptpad/onlyoffice-x2t-wasm/releases/download/$VERSION/x2t.zip" --location --output x2t.zip
|
||||
# curl "https://github.com/cryptpad/onlyoffice-x2t-wasm/releases/download/v7.3%2B1/x2t.zip" --location --output x2t.zip
|
||||
echo "$HASH x2t.zip" >x2t.zip.sha512
|
||||
|
||||
11
lib/env.js
11
lib/env.js
@ -242,9 +242,14 @@ module.exports.create = function (config) {
|
||||
sso: plugins?.SSO?.config || {},
|
||||
enforceMFA: config.enforceMFA,
|
||||
|
||||
onlyOffice: {
|
||||
availableVersions: getInstalledOOVersions()
|
||||
},
|
||||
...(getInstalledOOVersions().length > 0
|
||||
? {
|
||||
onlyOffice: {
|
||||
availableVersions: getInstalledOOVersions(),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
|
||||
|
||||
// initialized as undefined
|
||||
bearerSecret: void 0,
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@ -6404,9 +6404,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/xml-crypto": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz",
|
||||
"integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==",
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.1.tgz",
|
||||
"integrity": "sha512-0GUNbPtQt+PLMsC5HoZRONX+K6NBJEqpXe/lsvrFj0EqfpGPpVfJKGE7a5jCg8s2+Wkrf/2U1G41kIH+zC9eyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.8",
|
||||
"xpath": "0.0.32"
|
||||
|
||||
@ -513,6 +513,7 @@ const factory = (ApiConfig = {}, Sortify, UserObject, ProxyManager,
|
||||
if (e) { return void cb({error: e}); }
|
||||
|
||||
store.rpc = call;
|
||||
|
||||
store.onRpcReadyEvt.fire();
|
||||
|
||||
Store.getPinLimit(null, null, function (obj) {
|
||||
|
||||
@ -342,27 +342,6 @@ define([
|
||||
}
|
||||
}
|
||||
});
|
||||
const $keyBtn = $(keyButton);
|
||||
Util.onClickEnter($keyBtn, () => {
|
||||
let val = $keyInput.val().trim();
|
||||
let key = Keys.canonicalize(val);
|
||||
if (!key) { return; }
|
||||
// We have a valid key
|
||||
let name = Messages.admin_admin;
|
||||
try {
|
||||
let parsed = Keys.parseUser(val);
|
||||
name = parsed.user;
|
||||
} catch (e) {}
|
||||
$keyBtn.prop('disabled', 'disabled');
|
||||
addAdmin({ ed:key, name }, (err) => {
|
||||
$keyBtn.prop('disabled', false);
|
||||
if (!err) { $keyInput.val(''); }
|
||||
// refresh
|
||||
APP.updateStatus(function () {
|
||||
evRefreshAdmins.fire();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const drawContacts = () => {
|
||||
$div.empty();
|
||||
@ -388,6 +367,9 @@ define([
|
||||
let addBtn = blocks.button('primary', 'fa-plus', Messages.tag_add);
|
||||
Util.onClickEnter($(addBtn), () => {
|
||||
var $sel = $(contactsGrid.div).find('.cp-usergrid-user.cp-selected');
|
||||
if (!$sel.length) {
|
||||
return UI.warn(Messages.admin_errorAddAdmins);
|
||||
}
|
||||
nThen((waitFor) => {
|
||||
$sel.each((i, el) => {
|
||||
const $el = $(el);
|
||||
@ -402,7 +384,6 @@ define([
|
||||
}).nThen(() => {
|
||||
APP.updateStatus(function () {
|
||||
evRefreshAdmins.fire();
|
||||
drawContacts();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -410,6 +391,30 @@ define([
|
||||
drawContacts();
|
||||
});
|
||||
|
||||
const $keyBtn = $(keyButton);
|
||||
Util.onClickEnter($keyBtn, () => {
|
||||
let val = $keyInput.val().trim();
|
||||
let key = Keys.canonicalize(val);
|
||||
if (!key) {
|
||||
return UI.warn(Messages.admin_errorAddKeyLabel);
|
||||
}
|
||||
// We have a valid key
|
||||
let name = Messages.admin_admin;
|
||||
try {
|
||||
let parsed = Keys.parseUser(val);
|
||||
name = parsed.user;
|
||||
} catch (e) {}
|
||||
$keyBtn.prop('disabled', 'disabled');
|
||||
addAdmin({ ed:key, name }, (err) => {
|
||||
$keyBtn.prop('disabled', false);
|
||||
if (!err) { $keyInput.val(''); }
|
||||
// refresh
|
||||
APP.updateStatus(function () {
|
||||
evRefreshAdmins.fire();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const list = blocks.form([
|
||||
//currentList.div,
|
||||
contactsGrid.div,
|
||||
@ -1086,7 +1091,12 @@ define([
|
||||
sframeCommand('UPLOAD_LOGO', {dataURL}, (err, response) => {
|
||||
$button.removeAttr('disabled');
|
||||
if (err) {
|
||||
UI.warn(Messages.error);
|
||||
if(err === 'E_TOO_LARGE') {
|
||||
UI.warn(Messages.admin_logoSize_error);
|
||||
}
|
||||
else{
|
||||
UI.warn(Messages.error);
|
||||
}
|
||||
$(input).val('');
|
||||
console.error(err, response);
|
||||
spinner.hide();
|
||||
@ -1828,7 +1838,9 @@ define([
|
||||
multiple: true,
|
||||
validate: function () {
|
||||
var l = parseInt($(newLimit).val());
|
||||
if (isNaN(l)) { return false; }
|
||||
if (isNaN(l)) {
|
||||
return UI.warn(Messages.limit_error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, function () {
|
||||
@ -1845,6 +1857,7 @@ define([
|
||||
}
|
||||
var limit = getPrettySize(l);
|
||||
$(text).text(Messages._getKey('admin_limit', [limit]));
|
||||
UI.log(Messages.saved);
|
||||
});
|
||||
});
|
||||
|
||||
@ -3902,12 +3915,16 @@ define([
|
||||
multiple: true,
|
||||
validate: function () {
|
||||
var l = parseInt($(newDuration).val());
|
||||
if (isNaN(l)) { return false; }
|
||||
if (isNaN(l)) {
|
||||
return void UI.warn(Messages.limit_error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, function () {
|
||||
var d = parseInt($(newDuration).val());
|
||||
if (!isPositiveInteger(d)) { return void UI.warn(Messages.error); }
|
||||
if (!isPositiveInteger(d)) {
|
||||
return void UI.warn(Messages.positiveNumber_error);
|
||||
}
|
||||
|
||||
var data = [d];
|
||||
sFrameChan.query('Q_ADMIN_RPC', {
|
||||
@ -3919,6 +3936,7 @@ define([
|
||||
return void console.error(e, response);
|
||||
}
|
||||
$(form).find('.cp-admin-bytes-written-duration').text(Messages._getKey('admin_bytesWrittenDuration', [d]));
|
||||
UI.log(Messages.saved);
|
||||
});
|
||||
});
|
||||
cb(form);
|
||||
@ -4171,6 +4189,7 @@ define([
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.adminPage || 'Admin',
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
skipLink: '#cp-sidebarlayout-container'
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
|
||||
@ -176,6 +176,7 @@
|
||||
}
|
||||
.tui-full-calendar-icon {
|
||||
text-align:center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tui-full-calendar-popup-detail-item {
|
||||
a {
|
||||
@ -193,7 +194,7 @@
|
||||
}
|
||||
li.tui-full-calendar-popup-section-item {
|
||||
padding: 0 6px;
|
||||
height: 32px;
|
||||
min-height: 32px;
|
||||
}
|
||||
.tui-full-calendar-popup-section-item {
|
||||
height: auto;
|
||||
@ -215,6 +216,8 @@
|
||||
}
|
||||
.tui-full-calendar-content {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font: @colortheme_app-font;
|
||||
padding: 0 10px;
|
||||
&:focus{
|
||||
|
||||
@ -670,7 +670,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': '.ics',
|
||||
'href': '#'
|
||||
},
|
||||
content: '.ics'
|
||||
});
|
||||
@ -1303,7 +1302,6 @@ ICS ==> create a new event with the same UID and a RECURRENCE-ID field (with a v
|
||||
attributes: {
|
||||
'class': 'cp-calendar-view',
|
||||
'data-value': k,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages['calendar_'+k]
|
||||
// Messages.calendar_day
|
||||
@ -1564,7 +1562,6 @@ APP.recurrenceRule = {
|
||||
attributes: {
|
||||
'class': 'cp-calendar-recurrence',
|
||||
'data-value': '',
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages.calendar_rec_no
|
||||
}];
|
||||
@ -1577,7 +1574,6 @@ APP.recurrenceRule = {
|
||||
attributes: {
|
||||
'class': 'cp-calendar-recurrence',
|
||||
'data-value': basicStr[rec],
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages._getKey('calendar_rec_' + rec, [
|
||||
getWeekDays(true)[date.getDay()],
|
||||
@ -1598,7 +1594,6 @@ APP.recurrenceRule = {
|
||||
attributes: {
|
||||
'class': 'cp-calendar-recurrence',
|
||||
'data-value': basicStr.days,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages['calendar_rec_' + (isWeekend ? 'weekend' : 'weekdays')]
|
||||
});
|
||||
@ -1608,7 +1603,6 @@ APP.recurrenceRule = {
|
||||
attributes: {
|
||||
'class': 'cp-calendar-recurrence',
|
||||
'data-value': 'custom',
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages.calendar_rec_custom
|
||||
});
|
||||
@ -1690,7 +1684,6 @@ APP.recurrenceRule = {
|
||||
attributes: {
|
||||
'class': 'cp-calendar-recurrence-freq',
|
||||
'data-value': rec,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages['calendar_rec_freq_' + rec]
|
||||
});
|
||||
@ -2000,7 +1993,6 @@ APP.recurrenceRule = {
|
||||
attributes: {
|
||||
'class': 'cp-calendar-reminder',
|
||||
'data-value': k,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages['calendar_'+k]
|
||||
// Messages.calendar_minutes
|
||||
@ -2145,6 +2137,7 @@ APP.recurrenceRule = {
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.calendar,
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
skipLink: '#cp-sidebarlayout-container'
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
@ -2228,6 +2221,7 @@ APP.recurrenceRule = {
|
||||
$el.find('.tui-full-calendar-dropdown-menu li').each(function (i, li) {
|
||||
var $li = $(li);
|
||||
var id = $li.attr('data-calendar-id');
|
||||
$li.attr('tabindex', 0);
|
||||
var c = calendars[id];
|
||||
if (!c || c.readOnly) {
|
||||
return void $li.remove();
|
||||
@ -2235,6 +2229,86 @@ APP.recurrenceRule = {
|
||||
// If at least one calendar is editable, show the popup
|
||||
show = true;
|
||||
});
|
||||
let calendarDropdown = function ($el) {
|
||||
let $dropdownButton = $el.find('.tui-full-calendar-dropdown-button');
|
||||
let $dropdownMenu = $el.find('.tui-full-calendar-dropdown-menu');
|
||||
|
||||
let toggleAriaExpanded = function (isOpen) {
|
||||
$dropdownButton.attr('aria-expanded', isOpen ? 'true' : 'false');
|
||||
isOpen ? $dropdownMenu.show() : $dropdownMenu.hide();
|
||||
};
|
||||
|
||||
let calendarDropdownNavigation = function (event) {
|
||||
let $focusedItem = $dropdownMenu.find('li:focus');
|
||||
switch (event.key) {
|
||||
case ' ':
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
$focusedItem.click();
|
||||
toggleAriaExpanded(false);
|
||||
$el.find('#tui-full-calendar-schedule-title').focus();
|
||||
break;
|
||||
case 'Tab':
|
||||
event.preventDefault();
|
||||
toggleAriaExpanded(false);
|
||||
$el.find('.tui-full-calendar-popup-section').removeClass('tui-full-calendar-open');
|
||||
if (event.shiftKey) {
|
||||
$el.find('.tui-full-calendar-popup-save').focus();
|
||||
} else {
|
||||
$el.find('#tui-full-calendar-schedule-title').focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
var $next = $focusedItem.next('li');
|
||||
if ($next.length) {
|
||||
$next.focus();
|
||||
} else {
|
||||
$dropdownMenu.find('li').first().focus();
|
||||
}
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
var $prev = $focusedItem.prev('li');
|
||||
if ($prev.length) {
|
||||
$prev.focus();
|
||||
} else {
|
||||
$dropdownMenu.find('li').last().focus();
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleAriaExpanded(false);
|
||||
$dropdownButton.focus();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
$dropdownButton.on('click keydown', function (event) {
|
||||
if (event.type !== 'click' && event.key !== 'Enter' && event.key !== ' ' && event.key !== 'ArrowDown' && event.key !== 'ArrowUp') {
|
||||
return;
|
||||
}
|
||||
let isOpen = $el.find('.tui-full-calendar-open').length > 0;
|
||||
toggleAriaExpanded(!isOpen);
|
||||
if (!isOpen && event.key !== 'ArrowUp') {
|
||||
$dropdownMenu.find('li').first().focus();
|
||||
}
|
||||
else if(!isOpen){
|
||||
$dropdownMenu.find('li').last().focus();
|
||||
}
|
||||
});
|
||||
// click outside the dropdown button => closes the dropdown => aria-expanded is false
|
||||
$(document).on('click', function (event) {
|
||||
if (!$(event.target).closest($dropdownButton).length) {
|
||||
toggleAriaExpanded(false);
|
||||
}
|
||||
});
|
||||
|
||||
$dropdownMenu.on('keydown', calendarDropdownNavigation);
|
||||
};
|
||||
calendarDropdown($el);
|
||||
|
||||
if ($el.find('.tui-full-calendar-hide.tui-full-calendar-dropdown').length || !show) {
|
||||
$el.hide();
|
||||
UI.warn(Messages.calendar_errorNoCalendar);
|
||||
|
||||
@ -423,34 +423,39 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
const ooEnabled = ApiConfig.onlyOffice && ApiConfig.onlyOffice.availableVersions.includes(
|
||||
OOCurrentVersion.currentVersion,
|
||||
);
|
||||
var sheetURL = `/common/onlyoffice/dist/${OOCurrentVersion.currentVersion}/web-apps/apps/spreadsheeteditor/main/index.html`;
|
||||
|
||||
assert(function (cb, msg) {
|
||||
msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. ";
|
||||
var expect = {
|
||||
'cross-origin-resource-policy': 'cross-origin',
|
||||
'cross-origin-embedder-policy': 'require-corp',
|
||||
};
|
||||
|
||||
Tools.common_xhr(sheetURL, function (xhr) {
|
||||
var result = !Object.keys(expect).some(function (k) {
|
||||
var response = xhr.getResponseHeader(k);
|
||||
if (response !== expect[k]) {
|
||||
msg.appendChild(h('span', [
|
||||
'A value of ',
|
||||
code(expect[k]),
|
||||
' was expected for the ',
|
||||
code(k),
|
||||
' HTTP header, but instead a value of "',
|
||||
code(response),
|
||||
'" was received.',
|
||||
]));
|
||||
return true; // returning true indicates that a value is incorrect
|
||||
}
|
||||
if (ooEnabled) {
|
||||
assert(function (cb, msg) {
|
||||
msg.innerText = "Missing HTTP headers required for .xlsx export from sheets. ";
|
||||
var expect = {
|
||||
'cross-origin-resource-policy': 'cross-origin',
|
||||
'cross-origin-embedder-policy': 'require-corp',
|
||||
};
|
||||
|
||||
Tools.common_xhr(sheetURL, function (xhr) {
|
||||
var result = !Object.keys(expect).some(function (k) {
|
||||
var response = xhr.getResponseHeader(k);
|
||||
if (response !== expect[k]) {
|
||||
msg.appendChild(h('span', [
|
||||
'A value of ',
|
||||
code(expect[k]),
|
||||
' was expected for the ',
|
||||
code(k),
|
||||
' HTTP header, but instead a value of "',
|
||||
code(response),
|
||||
'" was received.',
|
||||
]));
|
||||
return true; // returning true indicates that a value is incorrect
|
||||
}
|
||||
});
|
||||
cb(result || xhr.getAllResponseHeaders());
|
||||
});
|
||||
cb(result || xhr.getAllResponseHeaders());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
assert(function (cb, msg) {
|
||||
setWarningClass(msg);
|
||||
@ -719,20 +724,21 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
assert(function (cb, msg) { // FIXME possibly superseded by more advanced CSP tests?
|
||||
var url = `/common/onlyoffice/dist/${OOCurrentVersion.currentVersion}/web-apps/apps/spreadsheeteditor/main/index.html`;
|
||||
msg.appendChild(CSP_WARNING(url));
|
||||
deferredPostMessage({
|
||||
command: 'GET_HEADER',
|
||||
content: {
|
||||
url: url,
|
||||
header: 'content-security-policy',
|
||||
},
|
||||
}, function (content) {
|
||||
var CSP_headers = parseCSP(content);
|
||||
cb(hasOnlyOfficeHeaders(CSP_headers) || CSP_headers);
|
||||
if (ooEnabled) {
|
||||
assert(function (cb, msg) { // FIXME possibly superseded by more advanced CSP tests?
|
||||
msg.appendChild(CSP_WARNING(sheetURL));
|
||||
deferredPostMessage({
|
||||
command: 'GET_HEADER',
|
||||
content: {
|
||||
url: sheetURL,
|
||||
header: 'content-security-policy',
|
||||
},
|
||||
}, function (content) {
|
||||
var CSP_headers = parseCSP(content);
|
||||
cb(hasOnlyOfficeHeaders(CSP_headers) || CSP_headers);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
assert(function (cb, msg) {
|
||||
|
||||
@ -610,6 +610,7 @@ define([
|
||||
Framework.create({
|
||||
toolbarContainer: '#cme_toolbox',
|
||||
contentContainer: '#cp-app-code-editor',
|
||||
skipLink: '.CodeMirror',
|
||||
thumbnail: {
|
||||
getContainer: getThumbnailContainer,
|
||||
filter: function (el, before) {
|
||||
|
||||
@ -32,7 +32,7 @@ const factory = function (AppConfig = {}) {
|
||||
MAX_PREMIUM_TEAMS_OWNED: Math.max(AppConfig.maxOwnedTeams || 0, AppConfig.maxPremiumTeamsOwned || 0) || 5,
|
||||
// Apps
|
||||
criticalApps: ['profile', 'settings', 'debug', 'admin', 'support', 'notifications', 'calendar', 'moderation', 'oldadmin'], // XXX oldadmin
|
||||
earlyAccessApps: ['doc', 'presentation']
|
||||
earlyAccessApps: []
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1517,6 +1517,9 @@ define([
|
||||
});
|
||||
|
||||
Util.onClickEnter(entry, function(e) {
|
||||
if ($(e.target).attr('href') === '#') {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (config.isSelect) { return; }
|
||||
e.stopPropagation();
|
||||
if (typeof(config.action) === "function") {
|
||||
@ -2407,7 +2410,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-language-value',
|
||||
'data-value': l,
|
||||
'href': '#',
|
||||
},
|
||||
content: [ // supplying content as an array ensures it's a text node, not parsed HTML
|
||||
languages[l] // Pretty name of the language value
|
||||
@ -3548,14 +3550,15 @@ define([
|
||||
|
||||
var text = Messages._getKey('owner_add', [name, title]);
|
||||
|
||||
var obj = { pw: msg.content.password || '', f: 1 };
|
||||
let newHref = Hash.getNewPadURL(msg.content.href, obj);
|
||||
var link = h('a', {
|
||||
href: '#'
|
||||
href: newHref
|
||||
}, Messages.requestEdit_viewPad);
|
||||
$(link).click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var obj = { pw: msg.content.password || '', f: 1 };
|
||||
common.openURL(Hash.getNewPadURL(msg.content.href, obj));
|
||||
common.openURL(newHref);
|
||||
});
|
||||
|
||||
var div = h('div', [
|
||||
|
||||
@ -86,6 +86,12 @@
|
||||
Util.mkEvent = function (once) {
|
||||
var handlers = [];
|
||||
var fired = false;
|
||||
let promiseResolve;
|
||||
|
||||
const promise = new Promise(resolve => {
|
||||
promiseResolve = resolve;
|
||||
});
|
||||
|
||||
return {
|
||||
reg: function (cb) {
|
||||
if (once && fired) { return void setTimeout(cb); }
|
||||
@ -99,10 +105,13 @@
|
||||
},
|
||||
fire: function () {
|
||||
if (once && fired) { return; }
|
||||
fired = true;
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
if (!fired) { promiseResolve.apply(null, args); }
|
||||
fired = true;
|
||||
handlers.forEach(function (h) { h.apply(null, args); });
|
||||
}
|
||||
},
|
||||
// Since a promise can only resolve once only the 1st call to fire() is reflected here. Even is `once` is `false`.
|
||||
promise
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -2919,9 +2919,9 @@ define([
|
||||
]);
|
||||
var content = h('p', [
|
||||
h('label', {for: 'cp-app-drive-link-name'}, Messages.fm_link_name),
|
||||
name = h('input#cp-app-drive-link-name', { autocomplete: 'off', placeholder: Messages.fm_link_name_placeholder, tabindex:'1'}),
|
||||
name = h('input#cp-app-drive-link-name', { autocomplete: 'off', placeholder: Messages.fm_link_name_placeholder}),
|
||||
h('label', {for: 'cp-app-drive-link-url'}, Messages.fm_link_url),
|
||||
url = h('input#cp-app-drive-link-url', { type: 'url', autocomplete: 'off', placeholder: Messages.form_input_ph_url,tabindex:'1'}),
|
||||
url = h('input#cp-app-drive-link-url', { type: 'url', autocomplete: 'off', placeholder: Messages.form_input_ph_url}),
|
||||
warning,
|
||||
]);
|
||||
|
||||
@ -2936,9 +2936,13 @@ define([
|
||||
};
|
||||
|
||||
var $warning = $(warning).hide();
|
||||
var $url = $(url).on('change keypress keyup keydown', function () {
|
||||
var $url = $(url).on('change keypress keydown', function () {
|
||||
var v = $url.val().trim();
|
||||
$url.toggleClass('cp-input-invalid', !Util.isValidURL(v));
|
||||
if (Util.isValidURL(v)) {
|
||||
$url.removeClass('cp-input-invalid');
|
||||
} else {
|
||||
$url.addClass('cp-input-invalid');
|
||||
}
|
||||
if (v.length > 200) {
|
||||
$warning.show();
|
||||
return;
|
||||
@ -2961,8 +2965,7 @@ define([
|
||||
var $name = $(name);
|
||||
var n = $name.val().trim() || $name.attr('placeholder');
|
||||
var u = $url.val().trim();
|
||||
if (!n || !u) { return true; }
|
||||
if (!Util.isValidURL(u)) {
|
||||
if (!n || !u || !Util.isValidURL(u)) {
|
||||
UI.warn(Messages.fm_link_invalid);
|
||||
return true;
|
||||
}
|
||||
@ -3137,7 +3140,7 @@ define([
|
||||
|
||||
var newObj = {
|
||||
tag: 'a',
|
||||
attributes: { 'class': obj.class, href: '#' },
|
||||
attributes: { 'class': obj.class },
|
||||
content: [obj.icon, obj.name]
|
||||
};
|
||||
|
||||
@ -3179,7 +3182,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'class': 'cp-app-drive-rm-filter',
|
||||
'href': '#'
|
||||
},
|
||||
content: [
|
||||
h('i.fa.fa-times'),
|
||||
@ -3192,7 +3194,6 @@ define([
|
||||
var attributes = {
|
||||
'class': 'cp-app-drive-filter-doc',
|
||||
'data-type': type,
|
||||
'href': '#'
|
||||
};
|
||||
|
||||
var premium = common.checkRestrictedApp(type);
|
||||
@ -3218,7 +3219,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-app-drive-filter-doc',
|
||||
'data-type': 'link',
|
||||
'href': '#'
|
||||
},
|
||||
content: [
|
||||
getIcon('link')[0],
|
||||
@ -3230,7 +3230,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-app-drive-filter-doc',
|
||||
'data-type': 'file',
|
||||
'href': '#'
|
||||
},
|
||||
content: [
|
||||
getIcon('file')[0],
|
||||
@ -3453,6 +3452,58 @@ define([
|
||||
return $fihElement;
|
||||
};
|
||||
|
||||
var lexicographicCompare = function(a, b) {
|
||||
if (!Array.isArray(a)) {
|
||||
a = [a];
|
||||
}
|
||||
if (!Array.isArray(b)) {
|
||||
b = [b];
|
||||
}
|
||||
|
||||
if(a.length === 0 && b.length === 0) {
|
||||
return 0;
|
||||
} else if (a.length === 0) {
|
||||
return -1;
|
||||
} else if (b.length === 0) {
|
||||
return 1;
|
||||
} else if(a[0] < b[0]) {
|
||||
return -1;
|
||||
} else if(a[0] > b[0]) {
|
||||
return 1;
|
||||
} else {
|
||||
// This means `a[0] == b[0]`. Chop off the first elements and compare the rest.
|
||||
return lexicographicCompare(a.slice(1), b.slice(1));
|
||||
}
|
||||
};
|
||||
|
||||
var splitStringToTextAndNumbers = function(s) {
|
||||
var textOrDigitsRe = /(?<text>\D+)?(?<digits>\d+)?/g;
|
||||
var split = [];
|
||||
|
||||
for (var match of s.matchAll(textOrDigitsRe)) {
|
||||
if (match.groups.text !== undefined) {
|
||||
split.push(match.groups.text);
|
||||
}
|
||||
if (match.groups.digits !== undefined) {
|
||||
split.push(parseInt(match.groups.digits));
|
||||
}
|
||||
}
|
||||
|
||||
return split;
|
||||
};
|
||||
|
||||
var naturalSort = function(a, b) {
|
||||
if (typeof(a) === "string") {
|
||||
a = splitStringToTextAndNumbers(a);
|
||||
}
|
||||
if (typeof(b) === "string") {
|
||||
b = splitStringToTextAndNumbers(b);
|
||||
}
|
||||
|
||||
var comp = lexicographicCompare(a, b);
|
||||
return comp;
|
||||
};
|
||||
|
||||
var sortElements = function (folder, path, oldkeys, prop, asc, useId) {
|
||||
var root = path && manager.find(path);
|
||||
if (path[0] === SHARED_FOLDER) {
|
||||
@ -3497,9 +3548,8 @@ define([
|
||||
keys.sort(function(a, b) {
|
||||
var _a = props[(a && a.uid) || a];
|
||||
var _b = props[(b && b.uid) || b];
|
||||
if (_a < _b) { return mult * -1; }
|
||||
if (_b < _a) { return mult; }
|
||||
return 0;
|
||||
|
||||
return mult * naturalSort(_a, _b);
|
||||
});
|
||||
return keys;
|
||||
};
|
||||
@ -4091,7 +4141,8 @@ define([
|
||||
];
|
||||
sortedTags.forEach(function (tag) {
|
||||
var tagLink = h('a', { href: '#' }, '#' + tag);
|
||||
$(tagLink).click(function () {
|
||||
$(tagLink).click(function (e) {
|
||||
e.preventDefault();
|
||||
if (displayedCategories.indexOf(SEARCH) !== -1) {
|
||||
APP.displayDirectory([SEARCH, '#' + tag]);
|
||||
}
|
||||
@ -4529,8 +4580,7 @@ define([
|
||||
manager.getSharedFolderData(root[a]).title : a;
|
||||
var newB = manager.isSharedFolder(root[b]) ?
|
||||
manager.getSharedFolderData(root[b]).title : b;
|
||||
return newA < newB ? -1 :
|
||||
(newA === newB ? 0 : 1);
|
||||
return naturalSort(newA, newB);
|
||||
});
|
||||
keys.forEach(function (key) {
|
||||
// Do not display files in the menu
|
||||
@ -4758,6 +4808,7 @@ define([
|
||||
data.sharedFolderId = sfId;
|
||||
data.name = Util.fixFileName(folderName);
|
||||
data.folderName = Util.fixFileName(folderName) + '.zip';
|
||||
data.common = common;
|
||||
|
||||
var uo = manager.user.userObject;
|
||||
if (sfId && manager.folders[sfId]) {
|
||||
@ -4939,7 +4990,7 @@ define([
|
||||
else if ($this.hasClass('cp-app-drive-context-download')) {
|
||||
if (paths.length !== 1) { return; }
|
||||
var path = paths[0];
|
||||
el = manager.find(path.path);
|
||||
el = $(path.element).data('element') || manager.find(path.path);
|
||||
// folder
|
||||
if (manager.isFolder(el)) {
|
||||
// folder
|
||||
|
||||
@ -64,7 +64,7 @@ define([
|
||||
let prefix = icon.slice(0, icon.indexOf('-'));
|
||||
cls = `.${prefix}.${icon}`;
|
||||
}
|
||||
return h(`i${cls}`);
|
||||
return h(`i${cls}`, { 'aria-hidden': 'true' });
|
||||
};
|
||||
blocks.button = (type, icon, text) => {
|
||||
type = type || 'primary';
|
||||
|
||||
@ -357,11 +357,22 @@ define([
|
||||
max: 0,
|
||||
done: 0,
|
||||
cache: cache,
|
||||
sframeChan: sframeChan
|
||||
sframeChan: sframeChan,
|
||||
common: data.common,
|
||||
};
|
||||
var filesData = data.sharedFolderId && ctx.sf[data.sharedFolderId] ? ctx.sf[data.sharedFolderId].filesData : ctx.data.filesData;
|
||||
var links = ctx.sf[data.sharedFolderId] && ctx.sf[data.sharedFolderId].static ? ctx.data.static && ctx.sf[data.sharedFolderId].static : ctx.data.static;
|
||||
|
||||
if (ctx.common && !ctx.common.isLoggedIn()) {
|
||||
// Anonymous Drive
|
||||
ctx.data.root = {};
|
||||
let index = 0;
|
||||
Object.keys(ctx.data.filesData).forEach(file => {
|
||||
ctx.data.root[index] = file;
|
||||
index += 1;
|
||||
});
|
||||
}
|
||||
|
||||
progress('reading', -1); // Msg.settings_export_reading
|
||||
nThen(function (waitFor) {
|
||||
ctx.waitFor = waitFor;
|
||||
|
||||
@ -2271,7 +2271,6 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': val,
|
||||
href: '#'
|
||||
},
|
||||
content: val
|
||||
};
|
||||
@ -2690,7 +2689,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
(content.version <= 3 ? 'v2b/' : CURRENT_VERSION+'/');
|
||||
var s = h('script', {
|
||||
type:'text/javascript',
|
||||
src: '/common/onlyoffice/dist/'+version+'web-apps/apps/api/documents/api.js'
|
||||
src: ApiConfig.httpSafeOrigin + '/common/onlyoffice/dist/'+version+'web-apps/apps/api/documents/api.js'
|
||||
});
|
||||
$('#cp-app-oo-editor').empty().append(h('div#cp-app-oo-placeholder-a')).append(s);
|
||||
|
||||
@ -2726,7 +2725,8 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
},
|
||||
sfCommon: common,
|
||||
$container: $bar,
|
||||
$contentContainer: $('#cp-app-oo-container')
|
||||
$contentContainer: $('#cp-app-oo-container'),
|
||||
skipLink: 'iframe[name="frameEditor"]|#editor_sdk'
|
||||
};
|
||||
toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||
toolbar.showColors();
|
||||
@ -3132,7 +3132,7 @@ Uncaught TypeError: Cannot read property 'calculatedType' of null
|
||||
|
||||
var s = h('script', {
|
||||
type:'text/javascript',
|
||||
src: '/common/onlyoffice/dist/'+version+'web-apps/apps/api/documents/api.js'
|
||||
src: ApiConfig.httpSafeOrigin + '/common/onlyoffice/dist/'+version+'web-apps/apps/api/documents/api.js'
|
||||
});
|
||||
$('#cp-app-oo-editor').append(s);
|
||||
|
||||
|
||||
@ -723,7 +723,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': _ext,
|
||||
'href': '#'
|
||||
},
|
||||
content: _ext
|
||||
});
|
||||
@ -734,7 +733,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': ext,
|
||||
'href': '#'
|
||||
},
|
||||
content: ext
|
||||
});
|
||||
@ -743,7 +741,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': '',
|
||||
'href': '#'
|
||||
},
|
||||
content: ' ',
|
||||
});
|
||||
@ -978,7 +975,8 @@ define([
|
||||
realtime: cpNfInner.chainpad,
|
||||
sfCommon: common,
|
||||
$container: $(toolbarContainer),
|
||||
$contentContainer: $(contentContainer)
|
||||
$contentContainer: $(contentContainer),
|
||||
skipLink: options.skipLink,
|
||||
};
|
||||
toolbar = Toolbar.create(configTb);
|
||||
title.setToolbar(toolbar);
|
||||
|
||||
@ -365,7 +365,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': l.mode,
|
||||
'href': '#',
|
||||
},
|
||||
content: [l.language] // Pretty name of the language value
|
||||
});
|
||||
@ -431,7 +430,6 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': l.name,
|
||||
'href': '#',
|
||||
},
|
||||
content: [l.name] // Pretty name of the language value
|
||||
});
|
||||
|
||||
@ -2463,4 +2463,3 @@ define([
|
||||
|
||||
return common;
|
||||
});
|
||||
|
||||
|
||||
@ -1057,6 +1057,11 @@ define([
|
||||
|
||||
Mailbox.create(funcs);
|
||||
|
||||
// automatically configure all relative links in the inner iframe
|
||||
// to point to the outer domain by adding a 'base' element to iframe's <head>
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
document.head.appendChild(h('base', { href: ApiConfig.httpUnsafeOrigin }));
|
||||
|
||||
cb(funcs);
|
||||
});
|
||||
} };
|
||||
|
||||
@ -863,6 +863,38 @@ MessengerUI, Messages, Pages, PadTypes) {
|
||||
};
|
||||
};
|
||||
|
||||
Bar.createSkipLink = function (toolbar, config) {
|
||||
if (config.readOnly === 1) {return;}
|
||||
const targetId = config.skipLink;
|
||||
const $skipLink = $('<a>', {
|
||||
'class': 'cp-toolbar-skip-link',
|
||||
'href': targetId,
|
||||
'tabindex': 0,
|
||||
'text': Messages.skipLink
|
||||
});
|
||||
toolbar.$top.append($skipLink);
|
||||
$skipLink.on('click', function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
let split = targetId.split('|'); // split for iframes
|
||||
let $container = $('body');
|
||||
split.some(selector => {
|
||||
let $targetElement = $container.find(selector);
|
||||
if ($targetElement.is('iframe')) {
|
||||
$container = $targetElement.contents();
|
||||
return;
|
||||
}
|
||||
const $firstFocusable = $targetElement.find('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]').first();
|
||||
if ($firstFocusable.length) {
|
||||
$firstFocusable.trigger('focus');
|
||||
} else {
|
||||
$skipLink.hide();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
return $skipLink;
|
||||
};
|
||||
var createLinkToMain = function (toolbar, config) {
|
||||
var $linkContainer = $('<span>', {
|
||||
'class': LINK_CLS
|
||||
@ -1457,6 +1489,7 @@ MessengerUI, Messages, Pages, PadTypes) {
|
||||
|
||||
|
||||
toolbar['linkToMain'] = createLinkToMain(toolbar, config);
|
||||
toolbar['skipLink'] = Bar.createSkipLink(toolbar, config);
|
||||
|
||||
if (!config.realtime) { toolbar.connected = true; }
|
||||
|
||||
|
||||
@ -248,7 +248,7 @@ const factory = (Util, Hash,
|
||||
|
||||
var isFolder = exp.isFolder = function (element) {
|
||||
if (isFolderData(element)) { return false; }
|
||||
return typeof(element) === "object" || isSharedFolder(element);
|
||||
return (typeof(element) === "object" && !element.channel) || isSharedFolder(element);
|
||||
};
|
||||
exp.isFolderEmpty = function (element) {
|
||||
if (!isFolder(element)) { return false; }
|
||||
|
||||
@ -7,28 +7,6 @@ define([
|
||||
], function (
|
||||
DiagramUtil
|
||||
) {
|
||||
const parseDrawioStyle = (styleAttrValue) => {
|
||||
if (!styleAttrValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = {};
|
||||
for (const part of styleAttrValue.split(';')) {
|
||||
const s = part.split(/=(.*)/);
|
||||
result[s[0]] = s[1];
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const stringifyDrawioStyle = (styleAttrValue) => {
|
||||
const parts = [];
|
||||
for (const [key, value] of Object.entries(styleAttrValue)) {
|
||||
parts.push(`${key}=${value}`);
|
||||
}
|
||||
return parts.join(';');
|
||||
};
|
||||
|
||||
const blobToImage = (blob) => {
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
@ -44,28 +22,18 @@ define([
|
||||
};
|
||||
|
||||
const loadCryptPadImages = (doc) => {
|
||||
return Array.from(doc .querySelectorAll('mxCell'))
|
||||
.map((element) => [element, parseDrawioStyle(element.getAttribute('style'))])
|
||||
.filter(([, style]) => style && style.image && style.image.startsWith('cryptpad://'))
|
||||
return Array.from(doc.querySelectorAll('mxCell'))
|
||||
.map((element) => [element, DiagramUtil.parseDrawioStyle(element.getAttribute('style'))])
|
||||
.filter(([, style]) => style.image && style.image.startsWith('cryptpad://'))
|
||||
.map(([element, style]) => {
|
||||
return loadImage(style.image)
|
||||
.then((dataUrl) => {
|
||||
style.image = dataUrl.replace(';base64', ''); // ';' breaks draw.ios style format
|
||||
element.setAttribute('style', stringifyDrawioStyle(style));
|
||||
element.setAttribute('style', DiagramUtil.stringifyDrawioStyle(style));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const parseXML = (xmlStr) => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xmlStr, "application/xml");
|
||||
const errorNode = doc.querySelector("parsererror");
|
||||
if (errorNode) {
|
||||
throw Error("error while parsing " + errorNode);
|
||||
}
|
||||
return doc;
|
||||
};
|
||||
|
||||
return {
|
||||
main: function(userDoc, cb) {
|
||||
delete userDoc.metadata;
|
||||
@ -74,7 +42,7 @@ define([
|
||||
|
||||
let doc;
|
||||
try {
|
||||
doc = parseXML(xml);
|
||||
doc = DiagramUtil.parseXML(xml);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
return;
|
||||
|
||||
65
www/diagram/import.js
Normal file
65
www/diagram/import.js
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
define([
|
||||
'/diagram/util.js',
|
||||
], function (
|
||||
DiagramUtil,
|
||||
) {
|
||||
const Nacl = window.nacl;
|
||||
|
||||
const splitAt = function (str, char) {
|
||||
const pos = str.indexOf(char);
|
||||
if (pos <= 0) {
|
||||
return [str, ''];
|
||||
}
|
||||
return [str.substring(0, pos), str.substring(pos + 1)];
|
||||
};
|
||||
|
||||
const parseDataUrl = function (url) {
|
||||
const [prefix, data] = splitAt(url, ',');
|
||||
const [, metadata] = splitAt(prefix, ':');
|
||||
const [mimeType, ] = splitAt(metadata, ';');
|
||||
|
||||
const u8 = Nacl.util.decodeBase64(data);
|
||||
return new Blob([u8], { type: mimeType });
|
||||
};
|
||||
|
||||
const saveImagesToCryptPad = async (fileManager, doc) => {
|
||||
const images = Array.from(doc.querySelectorAll('mxCell'))
|
||||
.map((element) => ({
|
||||
element,
|
||||
style: DiagramUtil.parseDrawioStyle(element.getAttribute('style')),
|
||||
}))
|
||||
.filter(({ style }) => style.image && style.image.startsWith('data:'));
|
||||
|
||||
for(const image of images) {
|
||||
const blob = parseDataUrl(image.style.image);
|
||||
|
||||
const cryptPadUrl = await DiagramUtil.uploadFile(fileManager, blob);
|
||||
image.style.image = cryptPadUrl;
|
||||
image.element.setAttribute('style', DiagramUtil.stringifyDrawioStyle(image.style));
|
||||
}
|
||||
};
|
||||
|
||||
const importDiagram = async (common, content) => {
|
||||
let doc;
|
||||
try {
|
||||
doc = DiagramUtil.parseXML(content);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileManager = DiagramUtil.createSimpleFileManager(common);
|
||||
|
||||
await saveImagesToCryptPad(fileManager, doc);
|
||||
return DiagramUtil.xmlAsJsonContent(new XMLSerializer().serializeToString(doc));
|
||||
};
|
||||
|
||||
return {
|
||||
importDiagram
|
||||
};
|
||||
});
|
||||
@ -4,10 +4,10 @@
|
||||
|
||||
// This is the initialization loading the CryptPad libraries
|
||||
define([
|
||||
'/api/config',
|
||||
'jquery',
|
||||
'/common/sframe-app-framework.js',
|
||||
'/customize/messages.js', // translation keys
|
||||
'/components/pako/dist/pako.min.js',
|
||||
'/components/x2js/x2js.js',
|
||||
'/diagram/util.js',
|
||||
'/common/common-ui-elements.js',
|
||||
@ -15,51 +15,16 @@ define([
|
||||
'less!/diagram/app-diagram.less',
|
||||
'css!/diagram/drawio.css',
|
||||
], function (
|
||||
ApiConfig,
|
||||
$,
|
||||
Framework,
|
||||
Messages,
|
||||
pako,
|
||||
X2JS,
|
||||
DiagramUtil,
|
||||
UIElements
|
||||
) {
|
||||
const Nacl = window.nacl;
|
||||
const APP = window.APP = {};
|
||||
|
||||
// As described here: https://drawio-app.com/extracting-the-xml-from-mxfiles/
|
||||
const decompressDrawioXml = function(xmlDocStr) {
|
||||
var TEXT_NODE = 3;
|
||||
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(xmlDocStr, "application/xml");
|
||||
|
||||
var errorNode = doc.querySelector("parsererror");
|
||||
if (errorNode) {
|
||||
console.error("error while parsing", errorNode);
|
||||
return xmlDocStr;
|
||||
}
|
||||
|
||||
doc.firstChild.removeAttribute('modified');
|
||||
doc.firstChild.removeAttribute('agent');
|
||||
doc.firstChild.removeAttribute('etag');
|
||||
|
||||
var diagrams = doc.querySelectorAll('diagram');
|
||||
|
||||
diagrams.forEach(function(diagram) {
|
||||
if (diagram.childNodes.length === 1 && diagram.firstChild && diagram.firstChild.nodeType === TEXT_NODE) {
|
||||
const innerText = diagram.firstChild.nodeValue;
|
||||
const bin = Nacl.util.decodeBase64(innerText);
|
||||
const xmlUrlStr = pako.inflateRaw(bin, {to: 'string'});
|
||||
const xmlStr = decodeURIComponent(xmlUrlStr);
|
||||
const diagramDoc = parser.parseFromString(xmlStr, "application/xml");
|
||||
diagram.replaceChild(diagramDoc.firstChild, diagram.firstChild);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var result = new XMLSerializer().serializeToString(doc);
|
||||
return result;
|
||||
};
|
||||
|
||||
const deepEqual = function(o1, o2) {
|
||||
return JSON.stringify(o1) === JSON.stringify(o2);
|
||||
@ -105,28 +70,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
const numbersToNumbers = function(o) {
|
||||
const type = typeof o;
|
||||
|
||||
if (type === "object") {
|
||||
for (const key in o) {
|
||||
o[key] = numbersToNumbers(o[key]);
|
||||
}
|
||||
return o;
|
||||
} else if (type === 'string' && o.match(/^[+-]?(0|(([1-9]\d*)(\.\d+)?))$/)) {
|
||||
return parseFloat(o, 10);
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
const xmlAsJsonContent = (xml) => {
|
||||
var decompressedXml = decompressDrawioXml(xml);
|
||||
return numbersToNumbers(x2js.xml2js(decompressedXml));
|
||||
};
|
||||
|
||||
var onDrawioChange = function(newXml) {
|
||||
var newJson = xmlAsJsonContent(newXml);
|
||||
var newJson = DiagramUtil.xmlAsJsonContent(newXml);
|
||||
if (!deepEqual(lastContent, newJson)) {
|
||||
lastContent = newJson;
|
||||
framework.localChange();
|
||||
@ -150,7 +95,10 @@ define([
|
||||
return new Promise((resolve) => {
|
||||
framework.insertImage({}, (imageData) => {
|
||||
if (imageData.blob) {
|
||||
resolve(imageData.blob);
|
||||
const fileManager = DiagramUtil.createSimpleFileManager(framework._.sfCommon);
|
||||
DiagramUtil.uploadFile(fileManager, imageData.blob)
|
||||
.then(url => resolve(url))
|
||||
.catch(e => console.error(e));
|
||||
} else if (imageData.url) {
|
||||
resolve(imageData.url);
|
||||
} else {
|
||||
@ -179,9 +127,12 @@ define([
|
||||
|
||||
framework.setFileImporter(
|
||||
{accept: ['.drawio', 'application/x-drawio']},
|
||||
(content) => {
|
||||
return xmlAsJsonContent(content);
|
||||
}
|
||||
(content, file, cb) => {
|
||||
require(['/diagram/import.js'], (importer) => {
|
||||
importer.importDiagram(framework._.sfCommon, content, file).then(cb);
|
||||
});
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
framework.setFileExporter(
|
||||
@ -207,7 +158,7 @@ define([
|
||||
// starting the CryptPad framework
|
||||
framework.start();
|
||||
|
||||
drawioFrame.src = '/components/drawio/src/main/webapp/index.html?'
|
||||
drawioFrame.src = ApiConfig.httpSafeOrigin + '/components/drawio/src/main/webapp/index.html?'
|
||||
+ new URLSearchParams({
|
||||
test: 1,
|
||||
stealth: 1,
|
||||
@ -250,6 +201,7 @@ define([
|
||||
Framework.create({
|
||||
toolbarContainer: '#cme_toolbox',
|
||||
contentContainer: '#cp-app-diagram-editor',
|
||||
skipLink: '#cp-app-diagram-content|body .geSearchSidebar',
|
||||
// validateContent: validateXml,
|
||||
}, function (framework) {
|
||||
onFrameworkReady(framework);
|
||||
|
||||
@ -7,11 +7,19 @@ define([
|
||||
'/file/file-crypto.js',
|
||||
'/common/outer/cache-store.js',
|
||||
'/components/x2js/x2js.js',
|
||||
'/components/pako/dist/pako.min.js',
|
||||
'/common/common-hash.js',
|
||||
'/api/config',
|
||||
'jquery',
|
||||
], function (
|
||||
Util,
|
||||
FileCrypto,
|
||||
Cache,
|
||||
X2JS,
|
||||
pako,
|
||||
Hash,
|
||||
ApiConfig,
|
||||
$,
|
||||
) {
|
||||
const Nacl = window.nacl;
|
||||
const x2js = new X2JS();
|
||||
@ -49,10 +57,139 @@ define([
|
||||
return x2js.js2xml(cleaned);
|
||||
};
|
||||
|
||||
const parseXML = (xmlStr) => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(xmlStr, "application/xml");
|
||||
const errorNode = doc.querySelector("parsererror");
|
||||
if (errorNode) {
|
||||
throw Error("error while parsing " + errorNode);
|
||||
}
|
||||
return doc;
|
||||
};
|
||||
|
||||
const numbersToNumbers = function(o) {
|
||||
const type = typeof o;
|
||||
|
||||
if (type === "object") {
|
||||
for (const key in o) {
|
||||
o[key] = numbersToNumbers(o[key]);
|
||||
}
|
||||
return o;
|
||||
} else if (type === 'string' && o.match(/^[+-]?(0|(([1-9]\d*)(\.\d+)?))$/)) {
|
||||
return parseFloat(o, 10);
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
const xmlAsJsonContent = (xml) => {
|
||||
var decompressedXml = decompressDrawioXml(xml);
|
||||
return numbersToNumbers(x2js.xml2js(decompressedXml));
|
||||
};
|
||||
|
||||
// As described here: https://drawio-app.com/extracting-the-xml-from-mxfiles/
|
||||
const decompressDrawioXml = function(xmlDocStr) {
|
||||
var TEXT_NODE = 3;
|
||||
|
||||
var parser = new DOMParser();
|
||||
var doc = parser.parseFromString(xmlDocStr, "application/xml");
|
||||
|
||||
var errorNode = doc.querySelector("parsererror");
|
||||
if (errorNode) {
|
||||
console.error("error while parsing", errorNode);
|
||||
return xmlDocStr;
|
||||
}
|
||||
|
||||
doc.firstChild.removeAttribute('modified');
|
||||
doc.firstChild.removeAttribute('agent');
|
||||
doc.firstChild.removeAttribute('etag');
|
||||
|
||||
var diagrams = doc.querySelectorAll('diagram');
|
||||
|
||||
diagrams.forEach(function(diagram) {
|
||||
if (diagram.childNodes.length === 1 && diagram.firstChild && diagram.firstChild.nodeType === TEXT_NODE) {
|
||||
const innerText = diagram.firstChild.nodeValue;
|
||||
const bin = Nacl.util.decodeBase64(innerText);
|
||||
const xmlUrlStr = pako.inflateRaw(bin, {to: 'string'});
|
||||
const xmlStr = decodeURIComponent(xmlUrlStr);
|
||||
const diagramDoc = parser.parseFromString(xmlStr, "application/xml");
|
||||
diagram.replaceChild(diagramDoc.firstChild, diagram.firstChild);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var result = new XMLSerializer().serializeToString(doc);
|
||||
return result;
|
||||
};
|
||||
|
||||
const parseDrawioStyle = (styleAttrValue) => {
|
||||
if (!styleAttrValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const result = {};
|
||||
for (const part of styleAttrValue.split(';')) {
|
||||
const s = part.split(/=(.*)/);
|
||||
result[s[0]] = s[1];
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const stringifyDrawioStyle = (styleAttrValue) => {
|
||||
const parts = [];
|
||||
for (const [key, value] of Object.entries(styleAttrValue)) {
|
||||
parts.push(`${key}=${value}`);
|
||||
}
|
||||
return parts.join(';');
|
||||
};
|
||||
|
||||
const getCryptPadUrlForUploadData = (data) => {
|
||||
const urlHash = data.url.split('#')[1];
|
||||
const secret = Hash.getSecrets('file', urlHash);
|
||||
|
||||
const fileHost = ApiConfig.fileHost || window.location.origin;
|
||||
const hexFileName = secret.channel;
|
||||
const src = fileHost + Hash.getBlobPathFromHex(hexFileName);
|
||||
const key = secret.keys && secret.keys.cryptKey;
|
||||
const cryptKey = Nacl.util.encodeBase64(key);
|
||||
return getCryptPadUrl(src, cryptKey, data.fileType);
|
||||
};
|
||||
|
||||
const uploadFile = async (fileManager, blob) => {
|
||||
return new Promise((resolve) => {
|
||||
fileManager.handleFile(blob, {
|
||||
callback: (data) => {
|
||||
const cryptPadUrl = getCryptPadUrlForUploadData(data);
|
||||
resolve(cryptPadUrl);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const createSimpleFileManager = (common) => {
|
||||
const fmConfigImages = {
|
||||
noHandlers: true,
|
||||
noStore: true,
|
||||
onUploaded: function (ev, data) {
|
||||
if (!ev.callback) { return; }
|
||||
ev.callback(data);
|
||||
}
|
||||
};
|
||||
return common.createFileManager(fmConfigImages);
|
||||
};
|
||||
|
||||
return {
|
||||
parseCryptPadUrl,
|
||||
getCryptPadUrl,
|
||||
jsonContentAsXML,
|
||||
parseXML,
|
||||
xmlAsJsonContent,
|
||||
decompressDrawioXml,
|
||||
parseDrawioStyle,
|
||||
stringifyDrawioStyle,
|
||||
uploadFile,
|
||||
createSimpleFileManager,
|
||||
|
||||
loadImage: function(href) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@ -220,7 +220,8 @@ define([
|
||||
metadataMgr: metadataMgr,
|
||||
readOnly: privateData.readOnly,
|
||||
sfCommon: common,
|
||||
$container: APP.$bar
|
||||
$container: APP.$bar,
|
||||
skipLink: '#cp-app-drive-tree'
|
||||
};
|
||||
var toolbar = Toolbar.create(configTb);
|
||||
|
||||
|
||||
@ -159,7 +159,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-type-value',
|
||||
'data-value': t,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages['form_text_'+t]
|
||||
};
|
||||
@ -262,7 +261,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-type-value',
|
||||
'data-value': t,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages['form_poll_'+t]
|
||||
};
|
||||
@ -1295,7 +1293,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-condition-question',
|
||||
'data-value': obj.uid,
|
||||
'href': '#',
|
||||
},
|
||||
content: obj.q
|
||||
};
|
||||
@ -1316,14 +1313,12 @@ define([
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': 1,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages.form_condition_is
|
||||
}, {
|
||||
tag: 'a',
|
||||
attributes: {
|
||||
'data-value': 0,
|
||||
'href': '#',
|
||||
},
|
||||
content: Messages.form_condition_isnot
|
||||
}];
|
||||
@ -1402,7 +1397,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-condition-value',
|
||||
'data-value': str,
|
||||
'href': '#',
|
||||
},
|
||||
content: str
|
||||
};
|
||||
@ -1576,7 +1570,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-condition-question',
|
||||
'data-value': obj.uid,
|
||||
'href': '#',
|
||||
},
|
||||
content: obj.q
|
||||
};
|
||||
@ -1604,7 +1597,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-condition-value',
|
||||
'data-value': str,
|
||||
'href': '#',
|
||||
},
|
||||
content: str
|
||||
};
|
||||
@ -2988,7 +2980,6 @@ define([
|
||||
attributes: {
|
||||
'class': 'cp-form-type-value',
|
||||
'data-value': t.key,
|
||||
'href': '#',
|
||||
},
|
||||
content: t.str
|
||||
};
|
||||
@ -3574,7 +3565,7 @@ define([
|
||||
});
|
||||
var $send = $(send).click(function () {
|
||||
if (!$radio.find('input[type="radio"]:checked').length) {
|
||||
return UI.warn(Messages.error);
|
||||
return UI.warn(Messages.answerType_error);
|
||||
}
|
||||
|
||||
var results = getFormResults();
|
||||
@ -5676,5 +5667,6 @@ define([
|
||||
Framework.create({
|
||||
toolbarContainer: '#cp-toolbar',
|
||||
contentContainer: '#cp-app-form-editor',
|
||||
skipLink: '#cp-app-form-editor'
|
||||
}, andThen);
|
||||
});
|
||||
|
||||
@ -1401,6 +1401,7 @@ define([
|
||||
Framework.create({
|
||||
toolbarContainer: '#cme_toolbox',
|
||||
contentContainer: '#cp-app-kanban-editor',
|
||||
skipLink: '#cp-app-kanban-content'
|
||||
}, waitFor(function (framework) {
|
||||
andThen2(framework);
|
||||
}));
|
||||
|
||||
@ -160,12 +160,20 @@ define([
|
||||
}
|
||||
|
||||
function __onAddItemClickHandler(nodeItem) {
|
||||
nodeItem.addEventListener('click', function (e) {
|
||||
function handleAddItem(e, item) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
self.options.addItemClick(this);
|
||||
if (typeof (this.clickfn) === 'function') {
|
||||
this.clickfn(this);
|
||||
self.options.addItemClick(item);
|
||||
if (typeof (item.clickfn) === 'function') {
|
||||
item.clickfn(item);
|
||||
}
|
||||
}
|
||||
nodeItem.addEventListener('click', function (e) {
|
||||
handleAddItem(e,this);
|
||||
});
|
||||
nodeItem.addEventListener('keydown', function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
handleAddItem(e,this);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -719,6 +727,7 @@ define([
|
||||
var addTopBoardItem = document.createElement('span');
|
||||
addTopBoardItem.classList.add('kanban-title-button');
|
||||
$(addTopBoardItem).attr('tabindex', '0');
|
||||
$(addTopBoardItem).attr('aria-label', Messages.addItemTop);
|
||||
addTopBoardItem.setAttribute('data-top', "1");
|
||||
addTopBoardItem.innerHTML = '<i class="cptools cptools-add-top">';
|
||||
footerBoard.appendChild(addTopBoardItem);
|
||||
@ -726,6 +735,7 @@ define([
|
||||
var addBoardItem = document.createElement('span');
|
||||
addBoardItem.classList.add('kanban-title-button');
|
||||
$(addBoardItem).attr('tabindex', '0');
|
||||
$(addBoardItem).attr('aria-label', Messages.addItemBottom);
|
||||
addBoardItem.innerHTML = '<i class="cptools cptools-add-bottom">';
|
||||
footerBoard.appendChild(addBoardItem);
|
||||
__onAddItemClickHandler(addBoardItem);
|
||||
|
||||
@ -205,7 +205,7 @@ define([
|
||||
var active = privateData.category || 'all';
|
||||
common.setHash(active);
|
||||
Object.keys(categories).forEach(function (key) {
|
||||
var $category = $('<div>', {'class': 'cp-sidebarlayout-category'}).appendTo($categories);
|
||||
var $category = $('<div>', {'class': 'cp-sidebarlayout-category', 'tabindex': 0}).appendTo($categories);
|
||||
if (key === 'all') { $category.append($('<span>', {'class': 'fa fa-bars'})); }
|
||||
if (key === 'friends') { $category.append($('<span>', {'class': 'fa fa-user'})); }
|
||||
if (key === 'pads') { $category.append($('<span>', {'class': 'cptools cptools-richtext'})); }
|
||||
@ -215,6 +215,11 @@ define([
|
||||
$category.addClass('cp-leftside-active');
|
||||
}
|
||||
|
||||
$category.keydown(function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
$category.click();
|
||||
}
|
||||
});
|
||||
$category.click(function () {
|
||||
if (!Array.isArray(categories[key]) && categories[key].onClick) {
|
||||
categories[key].onClick();
|
||||
@ -240,6 +245,7 @@ define([
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.notificationsPage || 'Notifications',
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
skipLink: '#cp-sidebarlayout-container',
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
|
||||
@ -1335,6 +1335,7 @@ define([
|
||||
Framework.create({
|
||||
toolbarContainer: '#cp-app-pad-toolbar',
|
||||
contentContainer: '#cp-app-pad-editor',
|
||||
skipLink: '#cke_1_contents .cke_wysiwyg_frame|html',
|
||||
patchTransformer: ChainPad.NaiveJSONTransformer,
|
||||
/*thumbnail: {
|
||||
getContainer: function () { return $('iframe').contents().find('html')[0]; },
|
||||
|
||||
@ -603,6 +603,7 @@ define([
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.profileButton,
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
skipLink: '#cp-sidebarlayout-container'
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
|
||||
@ -902,7 +902,7 @@ define([
|
||||
|
||||
var todo = function () {
|
||||
var val = parseInt($input.val());
|
||||
if (typeof(val) !== 'number' || isNaN(val)) { return UI.warn(Messages.error); }
|
||||
if (typeof(val) !== 'number' || isNaN(val)) { return UI.warn(Messages.limit_error); }
|
||||
if (val === oldVal) { return; }
|
||||
spinner.spin();
|
||||
common.setAttribute(['general', 'mediatag-size'], val, function (err) {
|
||||
@ -1196,6 +1196,7 @@ define([
|
||||
Feedback.send('FULL_DRIVE_EXPORT_START');
|
||||
var todo = function(data, filename) {
|
||||
var ui = Backup.createExportUI(privateData.origin);
|
||||
data.common = common;
|
||||
|
||||
var bu = Backup.create(data, common.getPad, privateData.fileHost, function(blob, errors) {
|
||||
saveAs(blob, filename);
|
||||
@ -1978,6 +1979,7 @@ define([
|
||||
$container: APP.$toolbar,
|
||||
pageTitle: Messages.settings_title,
|
||||
metadataMgr: common.getMetadataMgr(),
|
||||
skipLink: '#cp-sidebarlayout-leftside'
|
||||
};
|
||||
APP.toolbar = Toolbar.create(configTb);
|
||||
APP.toolbar.$rightside.hide();
|
||||
|
||||
@ -619,7 +619,8 @@ define([
|
||||
}
|
||||
$(el).css('background-color', '');
|
||||
}
|
||||
}
|
||||
},
|
||||
skipLink: '.CodeMirror',
|
||||
}, waitFor(function (fw) { framework = fw; }));
|
||||
|
||||
nThen(function (waitFor) {
|
||||
|
||||
@ -1564,7 +1564,8 @@ define([
|
||||
metadataMgr: metadataMgr,
|
||||
readOnly: privateData.readOnly,
|
||||
sfCommon: common,
|
||||
$container: $bar
|
||||
$container: $bar,
|
||||
skipLink: '#cp-sidebarlayout-leftside'
|
||||
};
|
||||
var toolbar = APP.toolbar = Toolbar.create(configTb);
|
||||
// Update the name in the user menu
|
||||
|
||||
@ -636,6 +636,7 @@ define([
|
||||
patchTransformer: ChainPad.NaiveJSONTransformer,
|
||||
toolbarContainer: '#cp-toolbar',
|
||||
contentContainer: '#cp-app-whiteboard-canvas-area',
|
||||
skipLink: '#cp-app-whiteboard-controls'
|
||||
}, waitFor(function (framework) {
|
||||
andThen2(framework);
|
||||
}));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user