@nextcloud/files 3.3.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/files/node.d.ts +3 -2
- package/dist/index.cjs +182 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.mjs +183 -85
- package/dist/index.mjs.map +1 -1
- package/dist/utils/fileSorting.d.ts +34 -0
- package/dist/utils/sorting.d.ts +12 -0
- package/dist/vendor.LICENSE.txt +1 -1
- package/package.json +3 -3
- /package/dist/{humanfilesize.d.ts → utils/fileSize.d.ts} +0 -0
- /package/dist/{filename.d.ts → utils/filename.d.ts} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Entry } from './newFileMenu';
|
|
2
2
|
import { Folder } from './files/folder';
|
|
3
3
|
|
|
4
|
-
export { formatFileSize, parseFileSize } from './humanfilesize';
|
|
5
4
|
export { FileAction, getFileActions, registerFileAction, DefaultType } from './fileAction';
|
|
6
5
|
export { Header, getFileListHeaders, registerFileListHeaders } from './fileListHeaders';
|
|
7
6
|
export { type Entry, NewMenuEntryCategory } from './newFileMenu';
|
|
@@ -13,7 +12,10 @@ export { FileType } from './files/fileType';
|
|
|
13
12
|
export { File } from './files/file';
|
|
14
13
|
export { Folder } from './files/folder';
|
|
15
14
|
export { Node, NodeStatus } from './files/node';
|
|
16
|
-
export { isFilenameValid } from './filename';
|
|
15
|
+
export { isFilenameValid } from './utils/filename';
|
|
16
|
+
export { formatFileSize, parseFileSize } from './utils/fileSize';
|
|
17
|
+
export { orderBy } from './utils/sorting';
|
|
18
|
+
export { sortNodes, FilesSortingMode, type FilesSortingOptions } from './utils/fileSorting';
|
|
17
19
|
export * from './navigation/navigation';
|
|
18
20
|
export * from './navigation/column';
|
|
19
21
|
export * from './navigation/view';
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getCurrentUser, onRequestTokenUpdate, getRequestToken } from "@nextcloud/auth";
|
|
2
2
|
import { getLoggerBuilder } from "@nextcloud/logger";
|
|
3
|
-
import { getCanonicalLocale } from "@nextcloud/l10n";
|
|
4
3
|
import { join, basename, extname, dirname } from "path";
|
|
5
4
|
import { encodePath } from "@nextcloud/paths";
|
|
6
5
|
import { generateRemoteUrl } from "@nextcloud/router";
|
|
7
6
|
import { createClient, getPatcher } from "webdav";
|
|
8
7
|
import { CancelablePromise } from "cancelable-promise";
|
|
8
|
+
import { getCanonicalLocale, getLanguage } from "@nextcloud/l10n";
|
|
9
9
|
/**
|
|
10
10
|
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
|
11
11
|
*
|
|
@@ -121,72 +121,6 @@ const getNewFileMenu = function() {
|
|
|
121
121
|
}
|
|
122
122
|
return window._nc_newfilemenu;
|
|
123
123
|
};
|
|
124
|
-
/**
|
|
125
|
-
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
|
126
|
-
*
|
|
127
|
-
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
|
128
|
-
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
|
129
|
-
*
|
|
130
|
-
* @license AGPL-3.0-or-later
|
|
131
|
-
*
|
|
132
|
-
* This program is free software: you can redistribute it and/or modify
|
|
133
|
-
* it under the terms of the GNU Affero General Public License as
|
|
134
|
-
* published by the Free Software Foundation, either version 3 of the
|
|
135
|
-
* License, or (at your option) any later version.
|
|
136
|
-
*
|
|
137
|
-
* This program is distributed in the hope that it will be useful,
|
|
138
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
139
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
140
|
-
* GNU Affero General Public License for more details.
|
|
141
|
-
*
|
|
142
|
-
* You should have received a copy of the GNU Affero General Public License
|
|
143
|
-
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
144
|
-
*
|
|
145
|
-
*/
|
|
146
|
-
const humanList = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
147
|
-
const humanListBinary = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
|
|
148
|
-
function formatFileSize(size, skipSmallSizes = false, binaryPrefixes = false, base1000 = false) {
|
|
149
|
-
binaryPrefixes = binaryPrefixes && !base1000;
|
|
150
|
-
if (typeof size === "string") {
|
|
151
|
-
size = Number(size);
|
|
152
|
-
}
|
|
153
|
-
let order = size > 0 ? Math.floor(Math.log(size) / Math.log(base1000 ? 1e3 : 1024)) : 0;
|
|
154
|
-
order = Math.min((binaryPrefixes ? humanListBinary.length : humanList.length) - 1, order);
|
|
155
|
-
const readableFormat = binaryPrefixes ? humanListBinary[order] : humanList[order];
|
|
156
|
-
let relativeSize = (size / Math.pow(base1000 ? 1e3 : 1024, order)).toFixed(1);
|
|
157
|
-
if (skipSmallSizes === true && order === 0) {
|
|
158
|
-
return (relativeSize !== "0.0" ? "< 1 " : "0 ") + (binaryPrefixes ? humanListBinary[1] : humanList[1]);
|
|
159
|
-
}
|
|
160
|
-
if (order < 2) {
|
|
161
|
-
relativeSize = parseFloat(relativeSize).toFixed(0);
|
|
162
|
-
} else {
|
|
163
|
-
relativeSize = parseFloat(relativeSize).toLocaleString(getCanonicalLocale());
|
|
164
|
-
}
|
|
165
|
-
return relativeSize + " " + readableFormat;
|
|
166
|
-
}
|
|
167
|
-
function parseFileSize(value, forceBinary = false) {
|
|
168
|
-
try {
|
|
169
|
-
value = `${value}`.toLocaleLowerCase().replaceAll(/\s+/g, "").replaceAll(",", ".");
|
|
170
|
-
} catch (e) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
const match = value.match(/^([0-9]*(\.[0-9]*)?)([kmgtp]?)(i?)b?$/);
|
|
174
|
-
if (match === null || match[1] === "." || match[1] === "") {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
const bytesArray = {
|
|
178
|
-
"": 0,
|
|
179
|
-
k: 1,
|
|
180
|
-
m: 2,
|
|
181
|
-
g: 3,
|
|
182
|
-
t: 4,
|
|
183
|
-
p: 5,
|
|
184
|
-
e: 6
|
|
185
|
-
};
|
|
186
|
-
const decimalString = `${match[1]}`;
|
|
187
|
-
const base = match[4] === "i" || forceBinary ? 1024 : 1e3;
|
|
188
|
-
return Math.round(Number.parseFloat(decimalString) * base ** bytesArray[match[3]]);
|
|
189
|
-
}
|
|
190
124
|
/**
|
|
191
125
|
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
|
|
192
126
|
*
|
|
@@ -759,23 +693,37 @@ class Node {
|
|
|
759
693
|
_data;
|
|
760
694
|
_attributes;
|
|
761
695
|
_knownDavService = /(remote|public)\.php\/(web)?dav/i;
|
|
696
|
+
readonlyAttributes = Object.entries(Object.getOwnPropertyDescriptors(Node.prototype)).filter((e) => typeof e[1].get === "function" && e[0] !== "__proto__").map((e) => e[0]);
|
|
762
697
|
handler = {
|
|
763
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
764
698
|
set: (target, prop, value) => {
|
|
699
|
+
if (this.readonlyAttributes.includes(prop)) {
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
765
702
|
this.updateMtime();
|
|
766
703
|
return Reflect.set(target, prop, value);
|
|
767
704
|
},
|
|
768
705
|
deleteProperty: (target, prop) => {
|
|
706
|
+
if (this.readonlyAttributes.includes(prop)) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
769
709
|
this.updateMtime();
|
|
770
710
|
return Reflect.deleteProperty(target, prop);
|
|
711
|
+
},
|
|
712
|
+
// TODO: This is deprecated and only needed for files v3
|
|
713
|
+
get: (target, prop, receiver) => {
|
|
714
|
+
if (this.readonlyAttributes.includes(prop)) {
|
|
715
|
+
logger.warn(`Accessing "Node.attributes.${prop}" is deprecated, access it directly on the Node instance.`);
|
|
716
|
+
return Reflect.get(this, prop);
|
|
717
|
+
}
|
|
718
|
+
return Reflect.get(target, prop, receiver);
|
|
771
719
|
}
|
|
772
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
773
720
|
};
|
|
774
721
|
constructor(data, davService) {
|
|
775
722
|
validateData(data, davService || this._knownDavService);
|
|
776
|
-
this._data = data;
|
|
777
|
-
this._attributes = new Proxy(this.
|
|
778
|
-
|
|
723
|
+
this._data = { ...data, attributes: {} };
|
|
724
|
+
this._attributes = new Proxy(this._data.attributes, this.handler);
|
|
725
|
+
this.update(data.attributes ?? {});
|
|
726
|
+
this._data.mtime = data.mtime;
|
|
779
727
|
if (davService) {
|
|
780
728
|
this._knownDavService = davService;
|
|
781
729
|
}
|
|
@@ -868,6 +816,7 @@ class Node {
|
|
|
868
816
|
}
|
|
869
817
|
/**
|
|
870
818
|
* Get the file attribute
|
|
819
|
+
* This contains all additional attributes not provided by the Node class
|
|
871
820
|
*/
|
|
872
821
|
get attributes() {
|
|
873
822
|
return this._attributes;
|
|
@@ -986,22 +935,23 @@ class Node {
|
|
|
986
935
|
/**
|
|
987
936
|
* Update the attributes of the node
|
|
988
937
|
*
|
|
989
|
-
* @param attributes The new attributes to update
|
|
938
|
+
* @param attributes The new attributes to update on the Node attributes
|
|
990
939
|
*/
|
|
991
940
|
update(attributes) {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
941
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
942
|
+
try {
|
|
943
|
+
if (value === void 0) {
|
|
944
|
+
delete this.attributes[name];
|
|
945
|
+
} else {
|
|
946
|
+
this.attributes[name] = value;
|
|
947
|
+
}
|
|
948
|
+
} catch (e) {
|
|
949
|
+
if (e instanceof TypeError) {
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
throw e;
|
|
1001
953
|
}
|
|
1002
|
-
clean[key] = attributes[key];
|
|
1003
954
|
}
|
|
1004
|
-
return clean;
|
|
1005
955
|
}
|
|
1006
956
|
}
|
|
1007
957
|
/**
|
|
@@ -1179,6 +1129,142 @@ function isFilenameValid(filename) {
|
|
|
1179
1129
|
}
|
|
1180
1130
|
return true;
|
|
1181
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
|
|
1134
|
+
*
|
|
1135
|
+
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
|
1136
|
+
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
|
1137
|
+
*
|
|
1138
|
+
* @license AGPL-3.0-or-later
|
|
1139
|
+
*
|
|
1140
|
+
* This program is free software: you can redistribute it and/or modify
|
|
1141
|
+
* it under the terms of the GNU Affero General Public License as
|
|
1142
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
1143
|
+
* License, or (at your option) any later version.
|
|
1144
|
+
*
|
|
1145
|
+
* This program is distributed in the hope that it will be useful,
|
|
1146
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
1147
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
1148
|
+
* GNU Affero General Public License for more details.
|
|
1149
|
+
*
|
|
1150
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
1151
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
1152
|
+
*
|
|
1153
|
+
*/
|
|
1154
|
+
const humanList = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
1155
|
+
const humanListBinary = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
|
|
1156
|
+
function formatFileSize(size, skipSmallSizes = false, binaryPrefixes = false, base1000 = false) {
|
|
1157
|
+
binaryPrefixes = binaryPrefixes && !base1000;
|
|
1158
|
+
if (typeof size === "string") {
|
|
1159
|
+
size = Number(size);
|
|
1160
|
+
}
|
|
1161
|
+
let order = size > 0 ? Math.floor(Math.log(size) / Math.log(base1000 ? 1e3 : 1024)) : 0;
|
|
1162
|
+
order = Math.min((binaryPrefixes ? humanListBinary.length : humanList.length) - 1, order);
|
|
1163
|
+
const readableFormat = binaryPrefixes ? humanListBinary[order] : humanList[order];
|
|
1164
|
+
let relativeSize = (size / Math.pow(base1000 ? 1e3 : 1024, order)).toFixed(1);
|
|
1165
|
+
if (skipSmallSizes === true && order === 0) {
|
|
1166
|
+
return (relativeSize !== "0.0" ? "< 1 " : "0 ") + (binaryPrefixes ? humanListBinary[1] : humanList[1]);
|
|
1167
|
+
}
|
|
1168
|
+
if (order < 2) {
|
|
1169
|
+
relativeSize = parseFloat(relativeSize).toFixed(0);
|
|
1170
|
+
} else {
|
|
1171
|
+
relativeSize = parseFloat(relativeSize).toLocaleString(getCanonicalLocale());
|
|
1172
|
+
}
|
|
1173
|
+
return relativeSize + " " + readableFormat;
|
|
1174
|
+
}
|
|
1175
|
+
function parseFileSize(value, forceBinary = false) {
|
|
1176
|
+
try {
|
|
1177
|
+
value = `${value}`.toLocaleLowerCase().replaceAll(/\s+/g, "").replaceAll(",", ".");
|
|
1178
|
+
} catch (e) {
|
|
1179
|
+
return null;
|
|
1180
|
+
}
|
|
1181
|
+
const match = value.match(/^([0-9]*(\.[0-9]*)?)([kmgtp]?)(i?)b?$/);
|
|
1182
|
+
if (match === null || match[1] === "." || match[1] === "") {
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
const bytesArray = {
|
|
1186
|
+
"": 0,
|
|
1187
|
+
k: 1,
|
|
1188
|
+
m: 2,
|
|
1189
|
+
g: 3,
|
|
1190
|
+
t: 4,
|
|
1191
|
+
p: 5,
|
|
1192
|
+
e: 6
|
|
1193
|
+
};
|
|
1194
|
+
const decimalString = `${match[1]}`;
|
|
1195
|
+
const base = match[4] === "i" || forceBinary ? 1024 : 1e3;
|
|
1196
|
+
return Math.round(Number.parseFloat(decimalString) * base ** bytesArray[match[3]]);
|
|
1197
|
+
}
|
|
1198
|
+
function stringify(value) {
|
|
1199
|
+
if (value instanceof Date) {
|
|
1200
|
+
return value.toISOString();
|
|
1201
|
+
}
|
|
1202
|
+
return String(value);
|
|
1203
|
+
}
|
|
1204
|
+
function orderBy(collection, identifiers, orders) {
|
|
1205
|
+
identifiers = identifiers ?? [(value) => value];
|
|
1206
|
+
orders = orders ?? [];
|
|
1207
|
+
const sorting = identifiers.map((_, index) => (orders[index] ?? "asc") === "asc" ? 1 : -1);
|
|
1208
|
+
const collator = Intl.Collator(
|
|
1209
|
+
[getLanguage(), getCanonicalLocale()],
|
|
1210
|
+
{
|
|
1211
|
+
// handle 10 as ten and not as one-zero
|
|
1212
|
+
numeric: true,
|
|
1213
|
+
usage: "sort"
|
|
1214
|
+
}
|
|
1215
|
+
);
|
|
1216
|
+
return [...collection].sort((a, b) => {
|
|
1217
|
+
for (const [index, identifier] of identifiers.entries()) {
|
|
1218
|
+
const value = collator.compare(stringify(identifier(a)), stringify(identifier(b)));
|
|
1219
|
+
if (value !== 0) {
|
|
1220
|
+
return value * sorting[index];
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return 0;
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
var FilesSortingMode = /* @__PURE__ */ ((FilesSortingMode2) => {
|
|
1227
|
+
FilesSortingMode2["Name"] = "basename";
|
|
1228
|
+
FilesSortingMode2["Modified"] = "mtime";
|
|
1229
|
+
FilesSortingMode2["Size"] = "size";
|
|
1230
|
+
return FilesSortingMode2;
|
|
1231
|
+
})(FilesSortingMode || {});
|
|
1232
|
+
function sortNodes(nodes, options = {}) {
|
|
1233
|
+
const sortingOptions = {
|
|
1234
|
+
// Default to sort by name
|
|
1235
|
+
sortingMode: "basename",
|
|
1236
|
+
// Default to sort ascending
|
|
1237
|
+
sortingOrder: "asc",
|
|
1238
|
+
...options
|
|
1239
|
+
};
|
|
1240
|
+
const identifiers = [
|
|
1241
|
+
// 1: Sort favorites first if enabled
|
|
1242
|
+
...sortingOptions.sortFavoritesFirst ? [(v) => v.attributes?.favorite !== 1] : [],
|
|
1243
|
+
// 2: Sort folders first if sorting by name
|
|
1244
|
+
...sortingOptions.sortFoldersFirst ? [(v) => v.type !== "folder"] : [],
|
|
1245
|
+
// 3: Use sorting mode if NOT basename (to be able to use displayName too)
|
|
1246
|
+
...sortingOptions.sortingMode !== "basename" ? [(v) => v[sortingOptions.sortingMode]] : [],
|
|
1247
|
+
// 4: Use displayName if available, fallback to name
|
|
1248
|
+
(v) => v.attributes?.displayName || v.basename,
|
|
1249
|
+
// 5: Finally, use basename if all previous sorting methods failed
|
|
1250
|
+
(v) => v.basename
|
|
1251
|
+
];
|
|
1252
|
+
const orders = [
|
|
1253
|
+
// (for 1): always sort favorites before normal files
|
|
1254
|
+
...sortingOptions.sortFavoritesFirst ? ["asc"] : [],
|
|
1255
|
+
// (for 2): always sort folders before files
|
|
1256
|
+
...sortingOptions.sortFoldersFirst ? ["asc"] : [],
|
|
1257
|
+
// (for 3): Reverse if sorting by mtime as mtime higher means edited more recent -> lower
|
|
1258
|
+
...sortingOptions.sortingMode === "mtime" ? [sortingOptions.sortingOrder === "asc" ? "desc" : "asc"] : [],
|
|
1259
|
+
// (also for 3 so make sure not to conflict with 2 and 3)
|
|
1260
|
+
...sortingOptions.sortingMode !== "mtime" && sortingOptions.sortingMode !== "basename" ? [sortingOptions.sortingOrder] : [],
|
|
1261
|
+
// for 4: use configured sorting direction
|
|
1262
|
+
sortingOptions.sortingOrder,
|
|
1263
|
+
// for 5: use configured sorting direction
|
|
1264
|
+
sortingOptions.sortingOrder
|
|
1265
|
+
];
|
|
1266
|
+
return orderBy(nodes, identifiers, orders);
|
|
1267
|
+
}
|
|
1182
1268
|
/**
|
|
1183
1269
|
* @copyright Copyright (c) 2022 John Molakvoæ <skjnldsv@protonmail.com>
|
|
1184
1270
|
*
|
|
@@ -1419,6 +1505,8 @@ validator$2.validate = function(xmlData, options) {
|
|
|
1419
1505
|
return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
|
|
1420
1506
|
} else if (attrStr.trim().length > 0) {
|
|
1421
1507
|
return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
1508
|
+
} else if (tags.length === 0) {
|
|
1509
|
+
return getErrorObject("InvalidTag", "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
|
|
1422
1510
|
} else {
|
|
1423
1511
|
const otg = tags.pop();
|
|
1424
1512
|
if (tagName !== otg.tagName) {
|
|
@@ -2172,6 +2260,13 @@ const parseXml = function(xmlData) {
|
|
|
2172
2260
|
if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) {
|
|
2173
2261
|
let tagContent = "";
|
|
2174
2262
|
if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
|
|
2263
|
+
if (tagName[tagName.length - 1] === "/") {
|
|
2264
|
+
tagName = tagName.substr(0, tagName.length - 1);
|
|
2265
|
+
jPath = jPath.substr(0, jPath.length - 1);
|
|
2266
|
+
tagExp = tagName;
|
|
2267
|
+
} else {
|
|
2268
|
+
tagExp = tagExp.substr(0, tagExp.length - 1);
|
|
2269
|
+
}
|
|
2175
2270
|
i = result.closeIndex;
|
|
2176
2271
|
} else if (this.options.unpairedTags.indexOf(tagName) !== -1) {
|
|
2177
2272
|
i = result.closeIndex;
|
|
@@ -3123,6 +3218,7 @@ export {
|
|
|
3123
3218
|
File,
|
|
3124
3219
|
FileAction,
|
|
3125
3220
|
FileType,
|
|
3221
|
+
FilesSortingMode,
|
|
3126
3222
|
Folder,
|
|
3127
3223
|
Header,
|
|
3128
3224
|
Navigation,
|
|
@@ -3151,9 +3247,11 @@ export {
|
|
|
3151
3247
|
getNavigation,
|
|
3152
3248
|
getNewFileMenuEntries,
|
|
3153
3249
|
isFilenameValid,
|
|
3250
|
+
orderBy,
|
|
3154
3251
|
parseFileSize,
|
|
3155
3252
|
registerDavProperty,
|
|
3156
3253
|
registerFileAction,
|
|
3157
3254
|
registerFileListHeaders,
|
|
3158
|
-
removeNewFileMenuEntry
|
|
3255
|
+
removeNewFileMenuEntry,
|
|
3256
|
+
sortNodes
|
|
3159
3257
|
};
|