diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less
index 1ea26cd98..4255972b3 100644
--- a/customize.dist/src/less2/include/drive.less
+++ b/customize.dist/src/less2/include/drive.less
@@ -233,6 +233,7 @@
font-size: 30px;
margin: 15px;
height: 2.5rem;
+ position: relative;
input {
background: transparent;
color: @cp_drive-fg;
@@ -262,6 +263,13 @@
.cp-app-drive-tree-search-icon, .cp-app-drive-search-cancel {
color: @cp_drive-fg;
}
+ .cp-app-drive-search-timing {
+ font-size: 0.5em;
+ position: absolute;
+ right: 60px;
+ top: 100%;
+ padding-top: 0.25rem;
+ }
}
.cp-app-drive-search-spinner {
display: inline-flex;
@@ -271,7 +279,8 @@
svg {
height: 2rem;
width: 2rem;
- margin-bottom: 1.5rem;
+ margin-bottom: 0.5rem;
+ margin-left: -1rem;
}
.cp-spinner svg {
animation: spin 10s linear infinite;
@@ -297,7 +306,8 @@
}
.cp-app-drive-search-spinner svg {
width: 1.5rem;
- margin-bottom: 1rem;
+ margin-bottom: 0.3rem;
+ margin-left: -0.75rem;
}
.cp-app-drive-search-noresult {
font-size: 22px;
@@ -1146,4 +1156,3 @@
}
}
-
diff --git a/src/common/proxy-manager.js b/src/common/proxy-manager.js
index 4439db254..92b087d2c 100644
--- a/src/common/proxy-manager.js
+++ b/src/common/proxy-manager.js
@@ -1656,6 +1656,35 @@ const factory = (UserObject, Util, Hash,
});
return ret;
};
+ var searchAsync = function (Env, value, cb, opts) {
+ cb = Util.once(cb || function () {});
+ var ret = [];
+ var userObjects = _getUserObjects(Env);
+ var index = 0;
+ var next = function () {
+ if (index >= userObjects.length) { return void cb(ret); }
+ var uo = userObjects[index++];
+ var fPath = _getUserObjectPath(Env, uo);
+ var handleResults = function (results) {
+ if (!results || !results.length) { return void next(); }
+ if (fPath) {
+ results.forEach(function (r) {
+ r.inSharedFolder = true;
+ r.paths.forEach(function (p) {
+ Array.prototype.unshift.apply(p, fPath);
+ });
+ });
+ }
+ Array.prototype.push.apply(ret, results);
+ next();
+ };
+ if (uo.searchAsync) {
+ return void uo.searchAsync(value, handleResults, opts);
+ }
+ handleResults(uo.search(value));
+ };
+ next();
+ };
var getRecentPads = function (Env) {
var files = [];
@@ -1799,6 +1828,7 @@ const factory = (UserObject, Util, Hash,
isStaticFile: callWithEnv(isStaticFile),
getFiles: callWithEnv(getFiles),
search: callWithEnv(search),
+ searchAsync: callWithEnv(searchAsync),
getRecentPads: callWithEnv(getRecentPads),
getOwnedPads: callWithEnv(getOwnedPads),
getTagsList: callWithEnv(getTagsList),
diff --git a/src/common/user-object.js b/src/common/user-object.js
index 5a6ec3c73..8f431a431 100644
--- a/src/common/user-object.js
+++ b/src/common/user-object.js
@@ -782,6 +782,109 @@ const factory = (Util, Hash,
return ret;
};
+ exp.searchAsync = function (value, cb, opts) {
+ cb = Util.once(cb || function () {});
+ if (typeof(value) !== "string") { return void cb([]); }
+ value = value.trim();
+ var res = [];
+ var allFilesList = files[FILES_DATA];
+ var allSFList = files[SHARED_FOLDERS];
+ var lValue = value.toLowerCase();
+ var tags;
+ if (/^#/.test(lValue)) {
+ tags = [lValue.slice(1).trim()];
+ }
+
+ var containsSearchedTag = function (T) {
+ if (!tags) { return false; }
+ if (!T.length) { return false; }
+ T = T.map(function (t) { return t.toLowerCase(); });
+ return tags.some(function (tag) {
+ return T.some(function (t) {
+ return t === tag;
+ });
+ });
+ };
+
+ var ids = getFiles([FILES_DATA, SHARED_FOLDERS]);
+ var index = 0;
+ var batchSize = opts && opts.batchSize ? opts.batchSize : 250;
+
+ var processFilesBatch = function () {
+ console.log("SEARCH: " + new Date().toISOString() + " searchAsync files batch start");
+ var end = Math.min(index + batchSize, ids.length);
+ for (; index < end; index++) {
+ var id = ids[index];
+ var data = allFilesList[id] || allSFList[id];
+ if (!data) { continue; }
+ if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) {
+ res.push(id);
+ continue;
+ }
+ var title = data.title || data.lastTitle;
+ if ((title && title.toLowerCase().indexOf(lValue) !== -1) ||
+ (data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1)) {
+ res.push(id);
+ }
+ }
+ if (index < ids.length) {
+ return void setTimeout(processFilesBatch, 0);
+ }
+ var href = Hash.getRelativeHref(value);
+ if (href) {
+ var hrefId = getIdFromHref(href);
+ if (hrefId) { res.push(hrefId); }
+ }
+ res = Util.deduplicateString(res);
+
+ var ret = [];
+ res.forEach(function (l) {
+ ret.push({
+ id: l,
+ paths: findFile(l),
+ data: exp.getFileData(l)
+ });
+ });
+
+ var resFolders = [];
+ var stack = [{ folder: files[ROOT], path: [ROOT] }];
+ var folderBatchSize = opts && opts.folderBatchSize ? opts.folderBatchSize : 50;
+ var processFoldersBatch = function () {
+ console.log("SEARCH: " + new Date().toISOString() + " searchAsync folders batch start");
+ var processed = 0;
+ while (stack.length && processed < folderBatchSize) {
+ var current = stack.pop();
+ var folder = current.folder;
+ var path = current.path;
+ for (var key in folder) {
+ if (isFolder(folder[key]) && !isSharedFolder(folder[key])) {
+ if (key.toLowerCase().indexOf(lValue) !== -1) {
+ resFolders.push({
+ id: null,
+ paths: [path.concat(key)],
+ data: {
+ title: key
+ }
+ });
+ }
+ stack.push({ folder: folder[key], path: path.concat(key) });
+ }
+ }
+ processed++;
+ }
+ if (stack.length) {
+ return void setTimeout(processFoldersBatch, 0);
+ }
+ resFolders = resFolders.sort(function (a, b) {
+ return a.data.title.toLowerCase() > b.data.title.toLowerCase();
+ });
+ ret = resFolders.concat(ret);
+ cb(ret);
+ };
+ processFoldersBatch();
+ };
+ processFilesBatch();
+ };
exp.getRecentPads = function () {
var allFiles = files[FILES_DATA];
var sorted = Object.keys(allFiles).filter(function (a) { return allFiles[a]; })
diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js
index 0466f4e3b..9cc94a944 100644
--- a/www/common/drive-ui.js
+++ b/www/common/drive-ui.js
@@ -3931,6 +3931,9 @@ define([
APP.Search = {};
var displaySearch = function ($list, value) {
var search = APP.Search;
+ var logSearch = function (message) {
+ console.log("SEARCH: " + new Date().toISOString() + " " + message);
+ };
var $div = $('
', {'id': 'cp-app-drive-search', 'class': 'cp-unselectable'});
$searchIcon.clone().appendTo($div);
@@ -3945,56 +3948,56 @@ define([
draggable: false,
tabindex: 1,
}).keyup(function (e) {
- if (searching) {
- e.preventDefault();
- e.stopPropagation();
- return;
- }
+ logSearch("new text detected");
var currentValue = $input.val().trim();
if (search.to) { window.clearTimeout(search.to); }
if (e.which === 13) {
spinner.spin();
- var newLocation = [SEARCH, $input.val()];
search.cursor = $input[0].selectionStart;
- if (!manager.comparePath(newLocation, currentPath.slice())) {
- searching = true;
- APP.displayDirectory(newLocation);
- }
+ logSearch("search launched via enter");
+ renderSearchResults($input.val());
return;
}
if (e.which === 27) {
$input.val('');
+ logSearch("text field updated (escape clear)");
search.cursor = 0;
- searching = true;
- APP.displayDirectory([SEARCH]);
+ renderSearchResults('');
return;
}
if (currentValue === "") {
search.cursor = 0;
- APP.displayDirectory([SEARCH]);
+ renderSearchResults('');
return;
}
if (currentValue.length < 2) { return; } // Don't autosearch 1 character
+ search.inputVersion = (search.inputVersion || 0) + 1;
+ var inputVersion = search.inputVersion;
search.to = window.setTimeout(function () {
- var newLocation = [SEARCH, $input.val()];
+ if (inputVersion !== search.inputVersion) { return; }
search.cursor = $input[0].selectionStart;
if (currentValue === search.value) { return; }
- if (!manager.comparePath(newLocation, currentPath.slice())) {
- searching = true;
- APP.displayDirectory(newLocation);
- }
+ logSearch("search launched via debounce");
+ renderSearchResults($input.val());
}, 500);
}).on('click mousedown mouseup', function (e) {
e.stopPropagation();
+ }).on('focus', function () {
+ logSearch("input focus");
+ }).on('blur', function () {
+ logSearch("input blur");
}).val(value || '').appendTo($div);
+ logSearch("text field updated (initial value)");
+ search.value = typeof(value) === "string" ? value : '';
$input[0].selectionStart = search.cursor || 0;
$input[0].selectionEnd = search.cursor || 0;
var cancel = Icons.get('close', {class: 'cp-app-drive-search-cancel',title:Messages.cancel}); // not visible
cancel.addEventListener('click', function () {
$input.val('');
+ logSearch("text field updated (cancel click)");
search.cursor = 0;
APP.displayDirectory([SEARCH]);
});
@@ -4002,90 +4005,226 @@ define([
$div.append($spinnerContainer);
$list.append($div);
+ var $timing = $('
', {
+ 'class': 'cp-app-drive-search-timing'
+ }).appendTo($div);
setTimeout(function () {
$input.focus();
});
- if (typeof(value) === "string" && value.trim()) {
- spinner.spin();
- } else {
- searching = false;
- return;
- }
+ var clearSearchResults = function () {
+ $list.children().not('#cp-app-drive-search').remove();
+ };
- setTimeout(function () {
- //$list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list');
- var filesList = manager.search(value);
- if (!filesList.length) {
- $list.append(h('div.cp-app-drive-search-noresult', Messages.fm_noResult));
+ var renderSearchResults = function (nextValue) {
+ logSearch("launching search for value " + nextValue);
+ if (searching==true) {
+ logSearch("search already in progress");
+ setTimeout(function () {
+ logSearch("retrying search for value " + nextValue);
+ renderSearchResults(nextValue);
+ }, 1000);
+ return;
+ }
+
+ var searchValue = typeof(nextValue) === "string" ? nextValue : '';
+ search.value = searchValue;
+ clearSearchResults();
+ $timing.text('');
+ if (!searchValue.trim()) {
spinner.hide();
searching = false;
+ logSearch("finished search (empty query)");
return;
}
- var sortable = {};
- var sortableFolders = [];
- filesList.forEach(function (r) {
- // if r.id === null, then it's a folder, not a file
- r.paths.forEach(function (path) {
- if (!r.inSharedFolder &&
- APP.hideDuplicateOwned && manager.isDuplicateOwned(path)) { return; }
- var _path = path.slice();
- var key = path.pop();
- var root = manager.find(path);
- var obj = {
- path: path,
- _path: _path,
- key: key,
- root: root,
- data: r.data
- };
- if (manager.isFolder(root[key])) {
- sortableFolders.push(obj);
+ var searchStartedAt = Date.now();
+ spinner.spin();
+ searching = true;
+ search.displayVersion = (search.displayVersion || 0) + 1;
+ var displayVersion = search.displayVersion;
+ setTimeout(function () {
+ if (displayVersion !== search.displayVersion) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ var latestValue = $input.val();
+ if (latestValue !== searchValue) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ //$list.closest('#cp-app-drive-content-folder').addClass('cp-app-drive-content-list');
+ logSearch("search execution scheduled for value " + searchValue);
+ setTimeout(function () {
+ if (displayVersion !== search.displayVersion) {
+ spinner.hide();
+ searching = false;
return;
}
- sortable[root[key]] = obj;
- });
- });
- var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true);
- var sortableKeys = Object.keys(sortable).map(Number);
- var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
+ if ($input.val() !== searchValue) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ logSearch("search execution started for value " + searchValue);
+ var searchExecStartedAt = Date.now();
+ var handleSearchResults = function (filesList) {
+ if (displayVersion !== search.displayVersion) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ if ($input.val() !== searchValue) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ var searchExecElapsed = Date.now() - searchExecStartedAt;
+ logSearch("search execution done in " + searchExecElapsed + "ms");
+ logSearch("starting render search results");
+ var sortStartedAt = Date.now();
+ if (!filesList.length) {
+ $list.append(h('div.cp-app-drive-search-noresult', Messages.fm_noResult));
+ spinner.hide();
+ searching = false;
+ var totalElapsed = Date.now() - searchStartedAt;
+ logSearch("sort time 0ms (no results)");
+ logSearch("render time 0ms (no results)");
+ logSearch("total time " + totalElapsed + "ms");
+ $timing.text(totalElapsed + 'ms');
+ logSearch("finished render (no results)");
+ return;
+ }
+ var sortable = {};
+ var sortableFolders = [];
+ filesList.forEach(function (r) {
+ // if r.id === null, then it's a folder, not a file
+ r.paths.forEach(function (path) {
+ if (!r.inSharedFolder &&
+ APP.hideDuplicateOwned && manager.isDuplicateOwned(path)) { return; }
+ var _path = path.slice();
+ var key = path.pop();
+ var root = manager.find(path);
+ var obj = {
+ path: path,
+ _path: _path,
+ key: key,
+ root: root,
+ data: r.data
+ };
+ if (manager.isFolder(root[key])) {
+ sortableFolders.push(obj);
+ return;
+ }
+ sortable[root[key]] = obj;
+ });
+ });
+ var _folders = sortElements(true, [ROOT], sortableFolders, null, !getSortFolderDesc(), true);
+ var sortableKeys = Object.keys(sortable).map(Number);
+ var _files = sortElements(false, [ROOT], sortableKeys, APP.store[SORT_FILE_BY], !getSortFileDesc(), true);
+ var sortElapsed = Date.now() - sortStartedAt;
- var addEl = function (obj, folder) {
- var $element = createElement(obj.path, obj.key, obj.root, folder);
- $element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result');
- $element.off('contextmenu');
- $element.contextmenu(openContextMenu('default'));
- $element.data('context', 'default');
- if (folder) {
- $element.find('.cp-app-drive-element-list').css({
- visibility: 'hidden'
- }).text('');
- }
- if (manager.isPathIn(obj._path, ['hrefArray'])) {
- obj._path.pop();
- obj._path.push(obj.data.title);
- }
- var $path = $('', {
- 'class': 'cp-app-drive-search-path'
- }).appendTo($element.find('.cp-app-drive-element-name'));
- createTitle($path, obj._path);
+ var addEl = function (obj, folder) {
+ var $element = createElement(obj.path, obj.key, obj.root, folder);
+ $element.addClass('cp-app-drive-element-notrash cp-app-drive-search-result');
+ $element.off('contextmenu');
+ $element.contextmenu(openContextMenu('default'));
+ $element.data('context', 'default');
+ if (folder) {
+ $element.find('.cp-app-drive-element-list').css({
+ visibility: 'hidden'
+ }).text('');
+ }
+ if (manager.isPathIn(obj._path, ['hrefArray'])) {
+ obj._path.pop();
+ obj._path.push(obj.data.title);
+ }
+ var $path = $('', {
+ 'class': 'cp-app-drive-search-path'
+ }).appendTo($element.find('.cp-app-drive-element-name'));
+ createTitle($path, obj._path);
- $list.append($element);
- };
- if (_folders.length) { getFolderListHeader(true, true).appendTo($list); }
- _folders.forEach(function (el) {
- var obj = el;
- addEl(obj, true);
+ $list.append($element);
+ };
+
+ var renderStartedAt = Date.now();
+ var appendQueue = [];
+ if (_folders.length) {
+ appendQueue.push(function () {
+ getFolderListHeader(true, true).appendTo($list);
+ });
+ _folders.forEach(function (el) {
+ var obj = el;
+ appendQueue.push(function () {
+ addEl(obj, true);
+ });
+ });
+ }
+ if (_files.length) {
+ appendQueue.push(function () {
+ getFileListHeader(true).appendTo($list);
+ });
+ _files.forEach(function (el) {
+ var obj = sortable[el];
+ appendQueue.push(function () {
+ addEl(obj, false);
+ });
+ });
+ }
+
+ var appendIndex = 0;
+ var appendBatch = function () {
+ var batchStartedAt = Date.now();
+ if (displayVersion !== search.displayVersion) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ if ($input.val() !== searchValue) {
+ spinner.hide();
+ searching = false;
+ return;
+ }
+ var end = Math.min(appendIndex + 30, appendQueue.length);
+ for (; appendIndex < end; appendIndex++) {
+ appendQueue[appendIndex]();
+ }
+ var batchElapsed = Date.now() - batchStartedAt;
+ logSearch("render batch time " + batchElapsed + "ms (" + appendIndex + "/" + appendQueue.length + ")");
+ if (appendIndex < appendQueue.length) {
+ window.requestAnimationFrame(appendBatch);
+ return;
+ }
+ var renderElapsed = Date.now() - renderStartedAt;
+ setTimeout(collapseDrivePath);
+ spinner.hide();
+ searching = false;
+ var totalElapsed = Date.now() - searchStartedAt;
+ logSearch("sort time " + sortElapsed + "ms");
+ logSearch("render time " + renderElapsed + "ms");
+ logSearch("total time " + totalElapsed + "ms");
+ $timing.text(totalElapsed + 'ms');
+ logSearch("finished render (results displayed)");
+ };
+
+ appendBatch();
+ };
+
+ if (manager.searchAsync) {
+ manager.searchAsync(searchValue, handleSearchResults);
+ } else {
+ handleSearchResults(manager.search(searchValue));
+ }
+ }, 0);
});
- if (_files.length) { getFileListHeader(true).appendTo($list); }
- _files.forEach(function (el) {
- var obj = sortable[el];
- addEl(obj, false);
- });
- setTimeout(collapseDrivePath);
- spinner.hide();
+ };
+
+ if (typeof(value) === "string") {
+ renderSearchResults(value);
+ } else {
searching = false;
- });
+ }
};
var displayRecent = function ($list, typeFilter) {