Files
rappaurio-sae501_502/app/node_modules/cejs/application/storage.js
2023-09-25 09:41:55 +02:00

459 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @name CeL function for storage.
* @fileoverview 載入在不同執行環境與平台皆可使用的檔案操作功能公用API以統一使用介面。
* @since 2017/1/27
*/
'use strict';
// 'use asm';
// --------------------------------------------------------------------------------------------
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
typeof CeL === 'function' && CeL.run({
// module name
name : 'application.storage',
// 依照不同執行環境與平台載入可用的操作功能。
require : detect_require,
// 設定不匯出的子函式。
// no_extend : '*',
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
code : module_code
});
function detect_require(library_namespace) {
if (library_namespace.platform.nodejs) {
return 'application.platform.nodejs.';
}
// 理想作法應該偵測JScript與COM環境。
// @see CeL.application.OS.Windows.file
this.has_ActiveX = typeof WScript === 'object'
|| typeof ActiveXObject === 'function'
|| typeof Server === 'object' && Server.CreateObject;
if (this.has_ActiveX) {
// TODO: application.OS.Windows.archive.
return 'application.OS.Windows.file.';
}
library_namespace.error('It seems I am running on an unknown OS.');
}
function module_code(library_namespace) {
/**
* null module constructor
*
* @class storage 的 functions
*/
var _// JSDT:_module_
= function() {
// null module constructor
};
/**
* for JSDT: 有 prototype 才會將之當作 Class
*/
_// JSDT:_module_
.prototype = {};
// -------------------------------------------------------------------------
// 維護公用API。
/**
* 公用API: 有些尚未完備,需要先確認。<code>
// set/get current working directory. 設定/取得目前工作目錄。
// current_directory() ends with path_separator
CeL.storage.working_directory([change_to_directory])
CeL.storage.fso_status(fso_path)
CeL.storage.chmod(fso_path, options)
CeL.storage.fso_exists(file_path)
CeL.storage.file_exists(file_path)
// get the contents of file
CeL.storage.read_file(file_path, character_encoding = 'UTF-8').toString()
CeL.storage.write_file(file_path, contents, character_encoding = 'UTF-8')
CeL.storage.append_file(file_path, contents, character_encoding = 'UTF-8')
// alias: delete
CeL.storage.remove_file(file_path / directory_path_list)
// alias: rename
CeL.storage.move_file(move_from_path, move_to_path)
CeL.storage.copy_file(copy_from_path, copy_to_path)
// .folder_exists()
CeL.storage.directory_exists(directory_path)
// get the fso list (file and sub-directory list) of the directory.
CeL.storage.read_directory(directory_path, options)
CeL.storage.directory_is_empty(directory_path, options)
// alias: mkdir
CeL.storage.create_directory(directory_path / directory_path_list)
// alias: delete. recursive: clean directory
CeL.storage.remove_directory(directory_path / directory_path_list, recursive)
// alias: rename
CeL.storage.move_directory(move_from_path, move_to_path)
CeL.storage.copy_directory(copy_from_path, copy_to_path)
// 遍歷檔案系統,對每個 FSO 執行指定的動作。
// TODO: 以 data.file.file_system_structure 代替 traverse_file_system()
CeL.storage.traverse_file_system(directory_path, handler, filter or options)
</code>
*/
// main module of OS adapted functions
var storage_module;
if (library_namespace.platform.nodejs) {
library_namespace.debug('application.storage: use node.js functions.');
storage_module = library_namespace.application.platform.nodejs;
/** node.js file system module */
var node_fs = require('fs');
// _.current_directory()
_.working_directory = storage_module.working_directory;
// 警告: 此函數之API尚未規範。
// .file_stats()
// Not exist: will return false.
_.fso_status = storage_module.fs_status;
_.chmod = storage_module.chmod;
_.file_exists = storage_module.file_exists;
_.directory_exists = storage_module.directory_exists;
_.read_file = storage_module.fs_read;
_.write_file = storage_module.fs_write;
_.copy_file = storage_module.fs_copySync;
_.remove_file = _.remove_directory = function(path, recursive) {
return storage_module.fs_remove(path, recursive);
};
_.move_directory = _.move_file =
//
_.move_fso = storage_module.fs_move;
_.read_directory = function(directory_path, options) {
try {
// fso_name_list
return node_fs.readdirSync(directory_path, options);
} catch (e) {
library_namespace.debug('Error to read directory: '
+ directory_path);
}
};
// directory exists and is empty
_.directory_is_empty = function(directory_path, options) {
var fso_name_list = _.read_directory(directory_path, options);
return Array.isArray(fso_name_list) && fso_name_list.length === 0;
};
_.create_directory = storage_module.fs_mkdir;
_.traverse_file_system = storage_module.traverse_file_system;
} else if (this.has_ActiveX) {
storage_module = library_namespace.application.OS.Windows.file;
_.working_directory = storage_module.working_directory;
_.read_file = storage_module.read_file;
_.write_file = storage_module.write_file;
// TODO: many
// others done @ CeL.application.OS.Windows.file
}
// ----------------------------------------------------
function write_file(file_path, data, options) {
// options = library_namespace.new_options(options);
if (library_namespace.is_Object(data)
// JSON.stringify() 放在 try 外面。這樣出現 circular structure 的時候才知道要處理。
&& /.json$/i.test(file_path)) {
// 自動將資料轉成 string。
try {
data = JSON.stringify(data);
} catch (e) {
// data 可能很長,擺在首位。
library_namespace.debug(data);
library_namespace.error('write_file: Failed to write to '
+ file_path + ': ' + e);
library_namespace.error(e);
throw e;
}
}
if (options && options.changed_only) {
var original_data = _.read_file(file_path, options);
if (String(data) === String(original_data))
return new Error('Nothing changed');
// delete options.changed_only;
}
if (options && options.backup && _.file_exists(file_path)) {
var backup_options = typeof options.backup === 'string' ? /[\\\/]/
.test(options.backup) ? {
directory : options.backup
} : {
directory_name : options.backup
} : library_namespace.new_options(options.backup);
if (!backup_options.directory && backup_options.directory_name) {
// 設定備份目錄於與檔案相同的目錄下。
backup_options.directory = file_path.replace(/[^\\\/]+$/, '')
+ backup_options.directory_name;
}
var backup_file_path = backup_options.directory
//
? append_path_separator(backup_options.directory,
// get file name only
file_path.match(/[^\\\/]+$/)[0])
// append file name extension
: backup_options.extension ? file_path + backup_options.extension
// preserve original file name extension
: file_path.replace(/(\.\w+)$/,
(backup_options.file_name_mark || '.bak') + '$1');
// Create backup
if (backup_options.directory)
_.create_directory(backup_options.directory);
_.remove_file(backup_file_path);
_.move_file(file_path, backup_file_path);
}
return _.write_file__OS_adapted(file_path, data, options);
}
if (_.write_file) {
_.write_file__OS_adapted = _.write_file;
_.write_file = write_file;
}
// ----------------------------------------------------
get_not_exist_filename.PATTERN = /( )?(?:\((\d{1,3})\))?(\.[^.]*)?$/;
get_not_exist_filename.max_index = 999;
// 找到下一個可用的檔案名稱。若是有重複的檔案存在,則會找到下一個沒有使用的編號為止。
// recheck: 從頭檢查起。否則接續之前的序號檢查。
// CeL.next_fso_NO_unused("n (2).txt") 先檢查 "n (2).txt", "n (3).txt"
// CeL.next_fso_NO_unused("n (2).txt", true) 先檢查 "n.txt", "n (1).txt"
function get_not_exist_filename(move_to_path, recheck) {
if (recheck) {
move_to_path = move_to_path.replace(get_not_exist_filename.PATTERN,
//
function(all, prefix_space, index, extension) {
return extension || '';
});
}
while (_.fso_status(move_to_path)) {
move_to_path = move_to_path.replace(
// Get next index that can use.
get_not_exist_filename.PATTERN, function(all, prefix_space, index,
extension) {
if (index > get_not_exist_filename.max_index) {
throw new Error('get_not_exist_filename: The index '
+ index + ' is too big! ' + move_to_path);
}
return (prefix_space || !index ? ' ' : '') + '('
+ ((index | 0) + 1) + ')' + (extension || '');
});
}
return move_to_path;
}
_.next_fso_NO_unused = get_not_exist_filename;
_.move_fso_with_NO = function(from_path, to_path) {
to_path = get_not_exist_filename(to_path);
if (false)
library_namespace.info(library_namespace.display_align([
[ 'Move: ', from_path ], [ '→ ', move_to_path ] ]));
_.move_fso(from_path, to_path);
};
// 從一個目錄或檔案列表中,找出第一個存在的。
_.first_exist_fso = function(fso_list) {
var first_exist;
if (!Array.isArray(fso_list)) {
fso_list = [ fso_list ];
}
if (fso_list.some(function(fso) {
if (_.fso_status(fso)) {
first_exist = fso;
return true;
}
})) {
return first_exist;
}
};
// -------------------------------------------------------------------------
// 一些與平台無關,且常用的檔案操作函數。或者簡單並且依賴於上面所列出操作的函數。
// platform-independent model (PIM)
// https://en.wikipedia.org/wiki/Platform-independent_model
var path_separator = library_namespace.env.path_separator;
// join path 自動加上最後的路徑分隔符號/目錄分隔符號。
function append_path_separator(directory_path, file_name) {
if (!directory_path) {
return directory_path;
}
// assert: typeof directory_path === 'string'
// 正規化成當前作業系統使用的路徑分隔符號。
if (false) {
directory_path = directory_path.replace(/[\\\/]/g, path_separator);
}
if (!/[\\\/]$/.test(directory_path)) {
directory_path +=
// 所添加的路徑分隔符號,以路徑本身的路徑分隔符號為主。
directory_path.includes('/') ? '/'
//
: directory_path.includes('\\')
// e.g., 'C:'
|| directory_path.endsWith(':') ? '\\'
//
: path_separator;
} else {
// 去除末尾太多個、不需要也不正規的路徑分隔符號。
directory_path = directory_path.replace(/[\\\/]{2,}$/, function(
path_separators) {
return path_separators[0];
});
}
// library_namespace.simplify_path()
if (file_name || file_name === 0)
file_name = String(file_name).replace(/^(\.{0,2}[\\\/])+/, '');
return file_name ? directory_path + file_name : directory_path;
}
_.append_path_separator = append_path_separator;
function extract_wildcard(pattern, options) {
var matched = library_namespace.simplify_path(pattern).match(
/^([\s\S]*[\\\/])?([^\\\/]+)$/);
if (!matched)
return [ pattern ];
var directory = matched[1] || '.' + path_separator;
matched = matched[2];
pattern = library_namespace.wildcard_to_RegExp(matched);
// console.trace(pattern);
var fso_list = library_namespace.read_directory(directory);
// console.trace(fso_list);
fso_list = fso_list.filter(function(fso_name) {
// console.trace([ fso_name, pattern, pattern.test(fso_name) ]);
return pattern.test(fso_name)
// e.g., 包含 "[", "]" 時。
|| fso_name === matched;
});
// console.trace(fso_list);
if (!options || !options.get_name) {
fso_list = fso_list.map(function(fso_name) {
return directory + fso_name;
});
}
return fso_list;
}
_.extract_wildcard = extract_wildcard;
// 決定預設的主要下載目錄。
// macOS dmg APP 中無法將檔案儲存在APP目錄下。
// 另外安裝包也比較適合放在 home directory 之下。
// test_current_directory: 先嘗試下載於當前目錄下。
function determin_download_directory(test_current_directory) {
var download_directory = test_current_directory
&& library_namespace.platform.nodejs && require.main
&& require.main.filename;
if (download_directory
// macOS dmg electron APP 中: require.main.filename 例如為
// /Applications/work_crawler.app/Contents/Resources/app.asar/gui_electron/gui_electron.html
//
// Linux Mint 中 AppImage: require.main.filename 例如為
// /tmp/.mount_work_cWtD4AY/resources/app.asar/gui_electron/gui_electron.html
//
// Linux Mint 中 electron: require.main.filename 例如為
// .../work_crawler-master/gui_electron/gui_electron.html
//
// Windows 10 中 electron: require.main.filename 例如為
// ...\work_crawler\gui_electron\gui_electron.html
//
// 2021/4/20 11:36:5 require.main===undefined @ new electron-builder
// package
// may use `module.filename`
// in electron-builder package: e.g.,
// "C:\Users\user_name\AppData\Local\Programs\work_crawler\resources\app.asar\gui_electron\gui_electron.html"
// NOT in electron-builder package: e.g.,
// "/program/work_crawler/gui_electron/gui_electron.html"
&& !/\.html?$/i.test(download_directory)) {
download_directory = download_directory.match(/[^\\\/]+$/)[0]
.replace(/\.js$/i, '');
} else if (test_current_directory && (download_directory =
// 避免 "/". e.g., macOS dmg APP 中 process.cwd() === '/'
_.working_directory().replace(/[\\\/]+$/, ''))) {
;
} else if (download_directory = library_namespace.env.home) {
if ([ 'Downloads', '下載' ]
// '下載': Linux Mint
.some(function(user_download_directory) {
user_download_directory = append_path_separator(
download_directory, user_download_directory);
if (_.directory_exists(user_download_directory)) {
download_directory = user_download_directory;
return true;
}
})) {
library_namespace.debug('預設的主要下載目錄設置於用戶預設之下載目錄下: '
+ download_directory, 1, 'determin_download_directory');
} else {
library_namespace.debug(
// 家目錄 @see os.userInfo().homedir , os.homedir()
'預設的主要下載目錄設置於用戶個人文件夾 home directory 下: ' + download_directory,
1, 'determin_download_directory');
}
} else {
// 應該不會到這邊來。
library_namespace
.warn('determin_download_directory: Cannot determin main download directory!');
download_directory = '.';
}
// main_directory 必須以 path separator 作結。
download_directory = append_path_separator(download_directory);
library_namespace.debug('預設的主要下載目錄: ' + download_directory, 1,
'determin_download_directory');
return download_directory;
}
_.determin_download_directory = determin_download_directory;
return (_// JSDT:_module_
);
}