obsidian#normalizePath TypeScript Examples
The following examples show how to use
obsidian#normalizePath.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From nldates-obsidian with MIT License | 7 votes |
export function generateMarkdownLink(app: App, subpath: string, alias?: string) {
const useMarkdownLinks = (app.vault as any).getConfig("useMarkdownLinks");
const path = normalizePath(subpath);
if (useMarkdownLinks) {
if (alias) {
return `[${alias}](${path.replace(/ /g, "%20")})`;
} else {
return `[${subpath}](${path})`;
}
} else {
if (alias) {
return `[[${path}|${alias}]]`;
} else {
return `[[${path}]]`;
}
}
}
Example #2
Source File: fileutils.ts From obsidian-ReadItLater with MIT License | 6 votes |
export function pathJoin(dir: string, subpath: string): string {
const result = path.join(dir, subpath);
// it seems that obsidian do not understand paths with backslashes in Windows, so turn them into forward slashes
return normalizePath(result.replace(/\\/g, '/'));
}
Example #3
Source File: main.ts From obsidian-ReadItLater with MIT License | 6 votes |
async writeFile(fileName: string, content: string): Promise<void> {
let filePath;
fileName = normalizeFilename(fileName);
await checkAndCreateFolder(this.app.vault, this.settings.inboxDir);
if (this.settings.inboxDir) {
filePath = normalizePath(`${this.settings.inboxDir}/${fileName}`);
} else {
filePath = normalizePath(`/${fileName}`);
}
if (await this.app.vault.adapter.exists(filePath)) {
new Notice(`${fileName} already exists!`);
} else {
const newFile = await this.app.vault.create(filePath, content);
if (this.settings.openNewNote) {
this.app.workspace.getLeaf(false).openFile(newFile);
}
new Notice(`${fileName} created successful`);
}
}
Example #4
Source File: main.ts From obsidian-custom-attachment-location with GNU General Public License v3.0 | 6 votes |
getAttachmentFolderFullPath(mdFolderPath: string, mdFileName: string)
{
let attachmentFolder = '';
if(this.useRelativePath)
attachmentFolder = Path.join(mdFolderPath, this.getAttachmentFolderPath(mdFileName));
else
{
attachmentFolder = this.getAttachmentFolderPath(mdFileName);
}
return normalizePath(attachmentFolder);
}
Example #5
Source File: offlineDic.ts From obsidian-dictionary with GNU Affero General Public License v3.0 | 6 votes |
async getOfflineDictionary(): Promise<Record<string, OfflineDic>> {
const { plugin } = this.manager;
const { adapter } = plugin.app.vault;
const path = normalizePath(`${plugin.manifest.dir}/offlineDictionary.json`);
if (!this.offlineDic) {
if (!await adapter.exists(path)) {
const data = await request({ url: `https://github.com/phibr0/obsidian-dictionary/releases/download/${plugin.manifest.version}/dictionary.json` });
await adapter.write(path, data);
}
this.offlineDic = JSON.parse(await adapter.read(path));
}
return this.offlineDic;
}
Example #6
Source File: functions.ts From obsidian-rss with GNU General Public License v3.0 | 6 votes |
export async function createNewNote(plugin: RssReaderPlugin, item: RssFeedItem): Promise<void> {
const activeFile = plugin.app.workspace.getActiveFile();
let dir = plugin.app.fileManager.getNewFileParent(activeFile ? activeFile.path : "").path;
if (plugin.settings.saveLocation === "custom") {
dir = plugin.settings.saveLocationFolder;
}
let filename = applyTemplate(plugin, item, plugin.settings.defaultFilename);
//make sure there are no slashes in the title.
filename = filename.replace(/[\/\\:]/g, ' ');
if (plugin.settings.askForFilename) {
const inputPrompt = new TextInputPrompt(plugin.app, t("specify_name"), t("cannot_contain") + " * \" \\ / < > : | ?", filename, filename);
await inputPrompt
.openAndGetValue(async (text: TextComponent) => {
const value = text.getValue();
if (value.match(FILE_NAME_REGEX)) {
inputPrompt.setValidationError(text, t("invalid_filename"));
return;
}
const filePath = normalizePath([dir, `${value}.md`].join('/'));
if (isInVault(plugin.app, filePath, '')) {
inputPrompt.setValidationError(text, t("note_exists"));
return;
}
inputPrompt.close();
await createNewFile(plugin, item, filePath, value);
});
} else {
const replacedTitle = filename.replace(FILE_NAME_REGEX, '');
const filePath = normalizePath([dir, `${replacedTitle}.md`].join('/'));
await createNewFile(plugin, item, filePath, item.title);
}
}
Example #7
Source File: InternalModuleFile.ts From Templater with GNU Affero General Public License v3.0 | 6 votes |
generate_move(): (path: string) => Promise<string> {
return async (path: string) => {
const new_path = normalizePath(
`${path}.${this.config.target_file.extension}`
);
await this.app.fileManager.renameFile(
this.config.target_file,
new_path
);
return "";
};
}
Example #8
Source File: InternalModuleFile.ts From Templater with GNU Affero General Public License v3.0 | 6 votes |
generate_rename(): (new_title: string) => Promise<string> {
return async (new_title: string) => {
if (new_title.match(/[\\/:]+/g)) {
throw new TemplaterError(
"File name cannot contain any of these characters: \\ / :"
);
}
const new_path = normalizePath(
`${this.config.target_file.parent.path}/${new_title}.${this.config.target_file.extension}`
);
await this.app.fileManager.renameFile(
this.config.target_file,
new_path
);
return "";
};
}
Example #9
Source File: Utils.ts From Templater with GNU Affero General Public License v3.0 | 6 votes |
export function resolve_tfolder(app: App, folder_str: string): TFolder {
folder_str = normalizePath(folder_str);
const folder = app.vault.getAbstractFileByPath(folder_str);
if (!folder) {
throw new TemplaterError(`Folder "${folder_str}" doesn't exist`);
}
if (!(folder instanceof TFolder)) {
throw new TemplaterError(`${folder_str} is a file, not a folder`);
}
return folder;
}
Example #10
Source File: Utils.ts From Templater with GNU Affero General Public License v3.0 | 6 votes |
export function resolve_tfile(app: App, file_str: string): TFile {
file_str = normalizePath(file_str);
const file = app.vault.getAbstractFileByPath(file_str);
if (!file) {
throw new TemplaterError(`File "${file_str}" doesn't exist`);
}
if (!(file instanceof TFile)) {
throw new TemplaterError(`${file_str} is a folder, not a file`);
}
return file;
}
Example #11
Source File: checkAndCreateFolder.ts From obsidian-ReadItLater with MIT License | 6 votes |
/**
* Open or create a folderpath if it does not exist
* @param vault
* @param folderpath
*/
export async function checkAndCreateFolder(vault: Vault, folderpath: string) {
folderpath = normalizePath(folderpath);
const folder = vault.getAbstractFileByPath(folderpath);
if (folder && folder instanceof TFolder) {
return;
}
await vault.createFolder(folderpath);
}
Example #12
Source File: defineGenericAnnotation.tsx From obsidian-annotator with GNU Affero General Public License v3.0 | 6 votes |
getProxiedUrl = (url: URL | string, props, vault): string => {
const proxiedUrl = proxy(url, props);
if (proxiedUrl.protocol == 'vault:') {
return getVaultPathResourceUrl(normalizePath(proxiedUrl.pathname), vault);
}
if (proxiedUrl.protocol == 'zip:') {
const pathName = normalizePath(proxiedUrl.pathname);
const res = resourceUrls.get(pathName) || resourceUrls.get(`${pathName}.html`);
if (res) return res;
console.error('file not found', { url });
debugger;
}
return proxiedUrl.toString();
}
Example #13
Source File: main.ts From obsidian-citation-plugin with MIT License | 6 votes |
/**
* Run a case-insensitive search for the literature note file corresponding to
* the given citekey. If no corresponding file is found, create one.
*/
async getOrCreateLiteratureNoteFile(citekey: string): Promise<TFile> {
const path = this.getPathForCitekey(citekey);
const normalizedPath = normalizePath(path);
let file = this.app.vault.getAbstractFileByPath(normalizedPath);
if (file == null) {
// First try a case-insensitive lookup.
const matches = this.app.vault
.getMarkdownFiles()
.filter((f) => f.path.toLowerCase() == normalizedPath.toLowerCase());
if (matches.length > 0) {
file = matches[0];
} else {
try {
file = await this.app.vault.create(
path,
this.getInitialContentForCitekey(citekey),
);
} catch (exc) {
this.literatureNoteErrorNotifier.show();
throw exc;
}
}
}
return file as TFile;
}
Example #14
Source File: settings.ts From obsidian-consistent-attachments-and-links with MIT License | 5 votes |
getNormalizedPath(path: string): string {
return path.length == 0 ? path : normalizePath(path);
}
Example #15
Source File: main.ts From obsidian-dictionary with GNU Affero General Public License v3.0 | 5 votes |
async saveCache(): Promise<void> {
await this.app.vault.adapter.write(normalizePath(`${this.manifest.dir}/cache.json`), JSON.stringify(this.cache));
}
Example #16
Source File: main.ts From obsidian-dictionary with GNU Affero General Public License v3.0 | 5 votes |
async loadCacheFromDisk(): Promise<DictionaryCache> {
const path = normalizePath(`${this.manifest.dir}/cache.json`);
if (!(await this.app.vault.adapter.exists(path))) {
await this.app.vault.adapter.write(path, "{}");
}
return JSON.parse(await this.app.vault.adapter.read(path)) as DictionaryCache;
}
Example #17
Source File: InternalModuleFile.ts From Templater with GNU Affero General Public License v3.0 | 5 votes |
generate_find_tfile(): (filename: string) => TFile {
return (filename: string) => {
const path = normalizePath(filename);
return this.app.metadataCache.getFirstLinkpathDest(path, "");
};
}
Example #18
Source File: Templater.ts From Templater with GNU Affero General Public License v3.0 | 5 votes |
static async on_file_creation(
templater: Templater,
file: TAbstractFile
): Promise<void> {
if (!(file instanceof TFile) || file.extension !== "md") {
return;
}
// Avoids template replacement when syncing template files
const template_folder = normalizePath(
templater.plugin.settings.templates_folder
);
if (file.path.includes(template_folder) && template_folder !== "/") {
return;
}
// TODO: find a better way to do this
// Currently, I have to wait for the daily note plugin to add the file content before replacing
// Not a problem with Calendar however since it creates the file with the existing content
await delay(300);
if (
file.stat.size == 0 &&
templater.plugin.settings.enable_folder_templates
) {
const folder_template_match =
templater.get_new_file_template_for_folder(file.parent);
if (!folder_template_match) {
return;
}
const template_file: TFile = await errorWrapper(
async (): Promise<TFile> => {
return resolve_tfile(templater.app, folder_template_match);
},
`Couldn't find template ${folder_template_match}`
);
// errorWrapper failed
if (template_file == null) {
return;
}
await templater.write_template_to_file(template_file, file);
} else {
await templater.overwrite_file_commands(file);
}
}
Example #19
Source File: main.ts From obsidian-custom-attachment-location with GNU General Public License v3.0 | 5 votes |
display(): void {
let {containerEl} = this;
containerEl.empty();
containerEl.createEl('h2', {text: 'Custom Attachment Location'});
let el = new Setting(containerEl)
.setName('Location for New Attachments')
.setDesc('Start with "./" to use relative path. Available variables: ${filename}.(NOTE: DO NOT start with "/" or end with "/". )')
.addText(text => text
.setPlaceholder('./assets/${filename}')
.setValue(this.plugin.settings.attachmentFolderPath)
.onChange(async (value: string) => {
console.log('attachmentFolder: ' + value);
value = normalizePath(value);
console.log('normalized attachmentFolder: ' + value);
this.plugin.settings.attachmentFolderPath = value;
if(value.startsWith('./'))
this.plugin.useRelativePath = true;
else
this.plugin.useRelativePath = false;
await this.plugin.saveSettings();
}));
el.controlEl.addEventListener('change', (()=>{this.display();}));
new Setting(containerEl)
.setName('Pasted Image Name')
.setDesc('Available variables: ${filename}, ${date}.')
.addText(text => text
.setPlaceholder('image-${date}')
.setValue(this.plugin.settings.pastedImageFileName)
.onChange(async (value: string) => {
console.log('pastedImageFileName: ' + value);
this.plugin.settings.pastedImageFileName = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Date Format')
.setDesc('YYYYMMDDHHmmssSSS')
.addMomentFormat(text => text
.setDefaultFormat('YYYYMMDDHHmmssSSS')
.setValue(this.plugin.settings.dateTimeFormat)
.onChange(async (value: string) => {
console.log('dateTimeFormat: ' + value);
this.plugin.settings.dateTimeFormat = value || 'YYYYMMDDHHmmssSSS';
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Automatically rename attachment folder')
.setDesc('When renaming md files, automatically rename attachment folder if folder name contains "${filename}".')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoRenameFolder)
.onChange(async (value: boolean) => {
this.plugin.settings.autoRenameFolder = value;
this.display();
await this.plugin.saveSettings();
}));
if(this.plugin.settings.autoRenameFolder)
new Setting(containerEl)
.setName('Automatically rename attachment files [Experimental]')
.setDesc('When renaming md files, automatically rename attachment files if file name contains "${filename}".')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoRenameFiles)
.onChange(async (value: boolean) => {
this.plugin.settings.autoRenameFiles = value;
await this.plugin.saveSettings();
}));
}
Example #20
Source File: main.ts From obsidian-custom-attachment-location with GNU General Public License v3.0 | 5 votes |
async handleRename(newFile: TFile, oldFilePath: string){
console.log('Handle Rename');
//if autoRename is off or not a markdown file
if(!this.settings.autoRenameFolder || newFile.extension !== 'md')
return;
let newName = newFile.basename;
let oldName = Path.basename(oldFilePath, '.md');
let mdFolderPath: string = Path.dirname(newFile.path);
let oldMdFolderPath: string = Path.dirname(oldFilePath);
let oldAttachmentFolderPath: string = this.getAttachmentFolderFullPath(oldMdFolderPath, oldName);
let newAttachmentFolderPath: string = this.getAttachmentFolderFullPath(mdFolderPath, newName);
//check if old attachment folder exists and is necessary to rename Folder
if(await this.adapter.exists(oldAttachmentFolderPath) && (oldAttachmentFolderPath !== newAttachmentFolderPath))
{
let tfolder: TAbstractFile = this.app.vault.getAbstractFileByPath(oldAttachmentFolderPath);
if(tfolder == null)
return;
let newAttachmentParentFolderPath: string = Path.dirname(newAttachmentFolderPath)
if (!(await this.adapter.exists(newAttachmentParentFolderPath))) {
await this.app.vault.createFolder(newAttachmentParentFolderPath);
}
await this.app.fileManager.renameFile(tfolder, newAttachmentFolderPath);
let oldAttachmentParentFolderPath: string = Path.dirname(oldAttachmentFolderPath)
let oldAttachmentParentFolderList: ListedFiles = await this.adapter.list(oldAttachmentParentFolderPath);
if (oldAttachmentParentFolderList.folders.length === 0 && oldAttachmentParentFolderList.files.length === 0) {
await this.adapter.rmdir(oldAttachmentParentFolderPath, true);
}
this.updateAttachmentFolderConfig(this.getAttachmentFolderPath(newName));
}
//if autoRenameFiles is off
if(!this.settings.autoRenameFiles)
return;
let embeds = this.app.metadataCache.getCache(newFile.path)?.embeds;
if(!embeds)
return;
let files: string[] = [];
for(let embed of embeds)
{
let link = embed.link;
if(link.endsWith('.png') || link.endsWith('jpeg'))
files.push(Path.basename(link));
else
continue;
}
let attachmentFiles: ListedFiles= await this.adapter.list(newAttachmentFolderPath);
for(let file of attachmentFiles.files)
{
console.log(file);
let filePath = file;
let fileName = Path.basename(filePath);
if((files.indexOf(fileName) > -1) && fileName.contains(oldName))
{
fileName = fileName.replace(oldName, newName);
let newFilePath = normalizePath(Path.join(newAttachmentFolderPath, fileName));
let tfile = this.app.vault.getAbstractFileByPath(filePath);
await this.app.fileManager.renameFile(tfile, newFilePath);
}
else
continue;
}
}
Example #21
Source File: view.ts From obsidian-fantasy-calendar with MIT License | 4 votes |
build() {
this.contentEl.empty();
this._app = new CalendarUI({
target: this.contentEl,
props: {
calendar: this.helper,
fullView: this.full,
yearView: this.yearView,
moons: this.moons,
displayWeeks: this.helper.displayWeeks,
displayDayNumber: this.dayNumber
}
});
this._app.$on("day-click", (event: CustomEvent<DayHelper>) => {
const day = event.detail;
if (day.events.length) return;
this.createEventForDay(day.date);
});
this._app.$on("day-doubleclick", (event: CustomEvent<DayHelper>) => {
const day = event.detail;
if (!day.events.length) return;
this.helper.viewing.day = day.number;
this.helper.viewing.month = day.month.number;
this.helper.viewing.year = day.month.year;
this.yearView = false;
this._app.$set({ yearView: false });
this._app.$set({ dayView: true });
this.triggerHelperEvent("day-update", false);
});
this._app.$on(
"day-context-menu",
(event: CustomEvent<{ day: DayHelper; evt: MouseEvent }>) => {
const { day, evt } = event.detail;
const menu = new Menu(this.app);
menu.setNoIcon();
if (!this.full) {
menu.addItem((item) => {
item.setTitle("Open Day").onClick(() => {
this.openDay({
day: day.number,
month: this.helper.displayed.month,
year: this.helper.displayed.year
});
});
});
}
menu.addItem((item) => {
item.setTitle("Set as Today").onClick(() => {
this.calendar.current = day.date;
this.helper.current.day = day.number;
this.triggerHelperEvent("day-update");
this.saveCalendars();
});
});
menu.addItem((item) =>
item.setTitle("New Event").onClick(() => {
this.createEventForDay(day.date);
})
);
menu.showAtMouseEvent(evt);
}
);
this._app.$on("settings", (event: CustomEvent<MouseEvent>) => {
const evt = event.detail;
const menu = new Menu(this.app);
menu.setNoIcon();
menu.addItem((item) => {
item.setTitle(
`${this.calendar.displayWeeks ? "Hide" : "Show"} Weeks`
).onClick(() => {
this.calendar.displayWeeks = !this.calendar.displayWeeks;
this.helper.update(this.calendar);
this._app.$set({
displayWeeks: this.calendar.displayWeeks
});
this.saveCalendars();
});
});
menu.addItem((item) => {
item.setTitle(
`Open ${this.yearView ? "Month" : "Year"}`
).onClick(() => {
this.yearView = !this.yearView;
this._app.$set({ yearView: this.yearView });
});
});
menu.addItem((item) => {
item.setTitle(
this.moons ? "Hide Moons" : "Display Moons"
).onClick(() => {
this.toggleMoons();
});
});
menu.addItem((item) => {
item.setTitle(
this.dayNumber ? "Hide Day Number" : "Display Day Number"
).onClick(() => {
this.dayNumber = !this.dayNumber;
this.calendar.static.displayDayNumber = this.dayNumber;
this._app.$set({ displayDayNumber: this.dayNumber });
this.saveCalendars();
});
});
menu.addItem((item) => {
item.setTitle("View Day");
item.onClick(() => {
this.openDate();
});
});
menu.addItem((item) => {
item.setTitle("Switch Calendars");
item.setDisabled(this.plugin.data.calendars.length <= 1);
item.onClick(() => {
const modal = new SwitchModal(this.plugin, this.calendar);
modal.onClose = () => {
if (!modal.confirmed) return;
this.setCurrentCalendar(modal.calendar);
};
modal.open();
});
});
menu.showAtMouseEvent(evt);
});
this._app.$on(
"event-click",
(evt: CustomEvent<{ event: Event; modifier: boolean }>) => {
const { event, modifier } = evt.detail;
if (event.note) {
let leaves: WorkspaceLeaf[] = [];
this.app.workspace.iterateAllLeaves((leaf) => {
if (!(leaf.view instanceof MarkdownView)) return;
if (leaf.view.file.basename === event.note) {
leaves.push(leaf);
}
});
if (leaves.length) {
this.app.workspace.setActiveLeaf(leaves[0]);
} else {
this.app.workspace.openLinkText(
event.note,
"",
this.full || modifier
);
}
} else {
const modal = new ViewEventModal(event, this.plugin);
modal.open();
}
}
);
this._app.$on(
"event-mouseover",
(evt: CustomEvent<{ target: HTMLElement; event: Event }>) => {
if (!this.plugin.data.eventPreview) return;
const { target, event } = evt.detail;
if (event.note) {
this.app.workspace.trigger(
"link-hover",
this, //hover popover, but don't need
target, //targetEl
event.note, //linkText
"" //source
);
}
}
);
this._app.$on(
"event-context",
(custom: CustomEvent<{ evt: MouseEvent; event: Event }>) => {
const { evt, event } = custom.detail;
const menu = new Menu(this.app);
menu.setNoIcon();
if (!event.note) {
menu.addItem((item) => {
item.setTitle("Create Note").onClick(async () => {
const path =
this.app.workspace.getActiveFile()?.path;
const newFilePath = path
? this.app.fileManager.getNewFileParent(path)
?.parent ?? "/"
: "/";
const date = `${event.date.year}-${
event.date.month + 1
}-${event.date.day}`;
let end: string;
if (event.end) {
end = `${event.end.year}-${
event.end.month + 1
}-${event.end.day}`;
}
const content = {
"fc-calendar": this.calendar.name,
"fc-date": date,
...(event.end ? { "fc-end": end } : {}),
...(event.category
? {
"fc-category":
this.calendar.categories.find(
(cat) =>
cat.id == event.category
)?.name
}
: {}),
"fc-display-name": event.name
};
event.note = normalizePath(
`${newFilePath}/${event.name}.md`
);
let file = this.app.vault.getAbstractFileByPath(
event.note
);
if (!file) {
file = await this.app.vault.create(
event.note,
`---\n${stringifyYaml(content)}\n---`
);
}
this.saveCalendars();
if (file instanceof TFile) {
const fileViews =
this.app.workspace.getLeavesOfType(
"markdown"
);
const existing = fileViews.find((l) => {
l.view instanceof FileView &&
l.view.file.path == event.note;
});
if (existing) {
this.app.workspace.setActiveLeaf(existing);
} else {
await this.app.workspace
.getUnpinnedLeaf()
.openFile(file, {
active: true
});
}
}
});
});
}
menu.addItem((item) => {
item.setTitle("Edit Event").onClick(() => {
const modal = new CreateEventModal(
this.plugin,
this.calendar,
event
);
modal.onClose = () => {
if (!modal.saved) return;
const existing = this.calendar.events.find(
(e) => e.id == event.id
);
this.calendar.events.splice(
this.calendar.events.indexOf(existing),
1,
modal.event
);
this.helper.refreshMonth(
modal.event.date.month,
modal.event.date.year
);
if (
modal.event.date.month != existing.date.month ||
modal.event.date.year != existing.date.year
) {
this.helper.refreshMonth(
existing.date.month,
existing.date.year
);
}
this.saveCalendars();
this._app.$set({
calendar: this.helper
});
this.triggerHelperEvent("day-update");
};
modal.open();
});
});
menu.addItem((item) => {
item.setTitle("Delete Event").onClick(async () => {
if (
!this.plugin.data.exit.event &&
!(await confirmEventDeletion(this.plugin))
)
return;
const existing = this.calendar.events.find(
(e) => e.id == event.id
);
this.calendar.events.splice(
this.calendar.events.indexOf(existing),
1
);
this.helper.refreshMonth(
existing.date.month,
existing.date.year
);
this.saveCalendars();
this._app.$set({
calendar: this.helper
});
this.triggerHelperEvent("day-update");
});
});
menu.showAtMouseEvent(evt);
}
);
this._app.$on("event", (e: CustomEvent<CurrentCalendarData>) => {
const date = e.detail;
this.createEventForDay(date);
});
this._app.$on("reset", () => {
this.helper.reset();
this.yearView = false;
this._app.$set({ yearView: false });
this._app.$set({ dayView: true });
this.triggerHelperEvent("day-update", false);
});
}
Example #22
Source File: localDictionaryBuilder.ts From obsidian-dictionary with GNU Affero General Public License v3.0 | 4 votes |
async newNote(content: DictionaryWord, openNote = true): Promise<void> {
const { plugin, settings } = this;
let audioLinks = '';
content.phonetics.forEach((value, i, a) => {
if (value.audio) {
audioLinks += '- ' + (value.audio.startsWith("http") ? value.audio : "https:" + value.audio);
if (i != a.length - 1) {
audioLinks += '\n';
}
}
});
let phonetics = '';
content.phonetics.forEach((value, i, a) => {
if (value.text) {
phonetics += '- ' + (value.audio ? `<details><summary>${value.text}</summary><audio controls><source src="${value.audio.startsWith("http") ? value.audio : "https:" + value.audio}"></audio></details>` : value.text);
if (i != a.length - 1) {
phonetics += '\n';
}
}
});
let meanings = '';
content.meanings.forEach((value, i) => {
meanings += '### ' + this.cap(value.partOfSpeech ?? t("Meaning {{i}}").replace(/{{i}}/g, (i + 1).toString())) + '\n\n';
value.definitions.forEach((def, j, b) => {
meanings += def.definition + '\n\n';
if (def.example) {
meanings += '> ' + def.example + '\n\n';
}
if (def.synonyms && def.synonyms.length != 0) {
def.synonyms.forEach((syn, i, a) => {
meanings += syn;
if (i != a.length - 1) {
meanings += ', ';
}
})
meanings += '\n\n'
}
if (j != b.length - 1) {
meanings += '---\n\n';
}
});
});
let file: TFile;
const langString = RFC[settings.defaultLanguage];
const path = `${settings.folder ? settings.folder + '/' : ''}${settings.languageSpecificSubFolders ? langString + '/' : ''}${settings.prefix.replace(/{{lang}}/ig, langString)}${settings.capitalizedFileName ? this.cap(content.word) : content.word}${settings.suffix.replace(/{{lang}}/ig, langString)}.md`;
let contents = settings.template
.replace(/{{notice}}/ig, t('Autogenerated by Obsidian Dictionary Plugin'))
.replace(/{{word}}/ig, settings.capitalizedFileName ? this.cap(content.word) : content.word)
.replace(/{{pronunciationheader}}/ig, t('Pronunciation'))
.replace(/{{phoneticlist}}/ig, phonetics)
.replace(/{{meaningheader}}/ig, t('Meanings'))
.replace(/{{meanings}}/ig, meanings)
.replace(/{{lang}}/ig, langString)
.replace(/{{audioLinks}}/ig, audioLinks);
if (content.origin) {
contents = contents
.replace(/{{originHeader}}/ig, t('Origin'))
.replace(/{{origin}}/ig, content.origin);
} else {
contents = contents
.replace(/{{originHeader}}/ig, '')
.replace(/{{origin}}/ig, '');
}
try {
if (!(await plugin.app.vault.adapter.exists(normalizePath(`${settings.folder ? settings.folder + '/' : ''}${settings.languageSpecificSubFolders ? langString + '/' : ''}`)))) {
await plugin.app.vault.createFolder(normalizePath(`${settings.folder ? settings.folder + '/' : ''}${settings.languageSpecificSubFolders ? langString + '/' : ''}`));
}
file = await plugin.app.vault.create(normalizePath(path), contents);
if (openNote) {
const leaf = plugin.app.workspace.splitActiveLeaf();
await leaf.openFile(file);
plugin.app.workspace.setActiveLeaf(leaf);
}
} catch (error) {
new OverwriteModal(this.plugin, normalizePath(path), contents, openNote).open();
}
}
Example #23
Source File: main.ts From obsidian-tracker with MIT License | 4 votes |
async getFiles(
files: TFile[],
renderInfo: RenderInfo,
includeSubFolders: boolean = true
) {
if (!files) return;
let folderToSearch = renderInfo.folder;
let useSpecifiedFilesOnly = renderInfo.specifiedFilesOnly;
let specifiedFiles = renderInfo.file;
let filesContainsLinkedFiles = renderInfo.fileContainsLinkedFiles;
let fileMultiplierAfterLink = renderInfo.fileMultiplierAfterLink;
// Include files in folder
// console.log(useSpecifiedFilesOnly);
if (!useSpecifiedFilesOnly) {
let folder = this.app.vault.getAbstractFileByPath(
normalizePath(folderToSearch)
);
if (folder && folder instanceof TFolder) {
let folderFiles = this.getFilesInFolder(folder);
for (let file of folderFiles) {
files.push(file);
}
}
}
// Include specified file
// console.log(specifiedFiles);
for (let filePath of specifiedFiles) {
let path = filePath;
if (!path.endsWith(".md")) {
path += ".md";
}
path = normalizePath(path);
// console.log(path);
let file = this.app.vault.getAbstractFileByPath(path);
// console.log(file);
if (file && file instanceof TFile) {
files.push(file);
}
}
// console.log(files);
// Include files in pointed by links in file
// console.log(filesContainsLinkedFiles);
// console.log(fileMultiplierAfterLink);
let linkedFileMultiplier = 1;
let searchFileMultifpierAfterLink = true;
if (fileMultiplierAfterLink === "") {
searchFileMultifpierAfterLink = false;
} else if (/^[0-9]+$/.test(fileMultiplierAfterLink)) {
// integer
linkedFileMultiplier = parseFloat(fileMultiplierAfterLink);
searchFileMultifpierAfterLink = false;
} else if (!/\?<value>/.test(fileMultiplierAfterLink)) {
// no 'value' named group
searchFileMultifpierAfterLink = false;
}
for (let filePath of filesContainsLinkedFiles) {
if (!filePath.endsWith(".md")) {
filePath += ".md";
}
let file = this.app.vault.getAbstractFileByPath(
normalizePath(filePath)
);
if (file && file instanceof TFile) {
// Get linked files
let fileCache = this.app.metadataCache.getFileCache(file);
let fileContent = await this.app.vault.adapter.read(file.path);
let lines = fileContent.split(
/\r\n|[\n\v\f\r\x85\u2028\u2029]/
);
// console.log(lines);
if (!fileCache?.links) continue;
for (let link of fileCache.links) {
if (!link) continue;
let linkedFile =
this.app.metadataCache.getFirstLinkpathDest(
link.link,
filePath
);
if (linkedFile && linkedFile instanceof TFile) {
if (searchFileMultifpierAfterLink) {
// Get the line of link in file
let lineNumber = link.position.end.line;
// console.log(lineNumber);
if (lineNumber >= 0 && lineNumber < lines.length) {
let line = lines[lineNumber];
// console.log(line);
// Try extract multiplier
// if (link.position)
let splitted = line.split(link.original);
// console.log(splitted);
if (splitted.length === 2) {
let toParse = splitted[1].trim();
let strRegex = fileMultiplierAfterLink;
let regex = new RegExp(strRegex, "gm");
let match;
while ((match = regex.exec(toParse))) {
// console.log(match);
if (
typeof match.groups !==
"undefined" &&
typeof match.groups.value !==
"undefined"
) {
// must have group name 'value'
let retParse =
helper.parseFloatFromAny(
match.groups.value.trim(),
renderInfo.textValueMap
);
if (retParse.value !== null) {
linkedFileMultiplier =
retParse.value;
break;
}
}
}
}
}
}
for (let i = 0; i < linkedFileMultiplier; i++) {
files.push(linkedFile);
}
}
}
}
}
// console.log(files);
}
Example #24
Source File: main.ts From obsidian-tracker with MIT License | 4 votes |
// TODO: remove this.app and move to collecting.ts
async collectDataFromTable(
dataMap: DataMap,
renderInfo: RenderInfo,
processInfo: CollectingProcessInfo
) {
// console.log("collectDataFromTable");
let tableQueries = renderInfo.queries.filter(
(q) => q.getType() === SearchType.Table
);
// console.log(tableQueries);
// Separate queries by tables and xDatasets/yDatasets
let tables: Array<TableData> = [];
let tableFileNotFound = false;
for (let query of tableQueries) {
let filePath = query.getParentTarget();
let file = this.app.vault.getAbstractFileByPath(
normalizePath(filePath + ".md")
);
if (!file || !(file instanceof TFile)) {
tableFileNotFound = true;
break;
}
let tableIndex = query.getAccessor();
let isX = query.usedAsXDataset;
let table = tables.find(
(t) => t.filePath === filePath && t.tableIndex === tableIndex
);
if (table) {
if (isX) {
table.xDataset = query;
} else {
table.yDatasets.push(query);
}
} else {
let tableData = new TableData(filePath, tableIndex);
if (isX) {
tableData.xDataset = query;
} else {
tableData.yDatasets.push(query);
}
tables.push(tableData);
}
}
// console.log(tables);
if (tableFileNotFound) {
processInfo.errorMessage = "File containing tables not found";
return;
}
for (let tableData of tables) {
//extract xDataset from query
let xDatasetQuery = tableData.xDataset;
if (!xDatasetQuery) {
// missing xDataset
continue;
}
let yDatasetQueries = tableData.yDatasets;
let filePath = xDatasetQuery.getParentTarget();
let tableIndex = xDatasetQuery.getAccessor();
// Get table text
let textTable = "";
filePath = filePath + ".md";
let file = this.app.vault.getAbstractFileByPath(
normalizePath(filePath)
);
if (file && file instanceof TFile) {
processInfo.fileAvailable++;
let content = await this.app.vault.adapter.read(file.path);
// console.log(content);
// Test this in Regex101
// This is a not-so-strict table selector
// ((\r?\n){2}|^)([^\r\n]*\|[^\r\n]*(\r?\n)?)+(?=(\r?\n){2}|$)
let strMDTableRegex =
"((\\r?\\n){2}|^)([^\\r\\n]*\\|[^\\r\\n]*(\\r?\\n)?)+(?=(\\r?\\n){2}|$)";
// console.log(strMDTableRegex);
let mdTableRegex = new RegExp(strMDTableRegex, "gm");
let match;
let indTable = 0;
while ((match = mdTableRegex.exec(content))) {
// console.log(match);
if (indTable === tableIndex) {
textTable = match[0];
break;
}
indTable++;
}
} else {
// file not exists
continue;
}
// console.log(textTable);
let tableLines = textTable.split(/\r?\n/);
tableLines = tableLines.filter((line) => {
return line !== "";
});
let numColumns = 0;
let numDataRows = 0;
// console.log(tableLines);
// Make sure it is a valid table first
if (tableLines.length >= 2) {
// Must have header and separator line
let headerLine = tableLines.shift().trim();
headerLine = helper.trimByChar(headerLine, "|");
let headerSplitted = headerLine.split("|");
numColumns = headerSplitted.length;
let sepLine = tableLines.shift().trim();
sepLine = helper.trimByChar(sepLine, "|");
let spepLineSplitted = sepLine.split("|");
for (let col of spepLineSplitted) {
if (!col.includes("-")) {
break; // Not a valid sep
}
}
numDataRows = tableLines.length;
}
if (numDataRows == 0) continue;
// get x data
let columnXDataset = xDatasetQuery.getAccessor(1);
if (columnXDataset >= numColumns) continue;
let xValues = [];
let indLine = 0;
for (let tableLine of tableLines) {
let dataRow = helper.trimByChar(tableLine.trim(), "|");
let dataRowSplitted = dataRow.split("|");
if (columnXDataset < dataRowSplitted.length) {
let data = dataRowSplitted[columnXDataset].trim();
let date = helper.strToDate(data, renderInfo.dateFormat);
if (date.isValid()) {
xValues.push(date);
if (
!processInfo.minDate.isValid() &&
!processInfo.maxDate.isValid()
) {
processInfo.minDate = date.clone();
processInfo.maxDate = date.clone();
} else {
if (date < processInfo.minDate) {
processInfo.minDate = date.clone();
}
if (date > processInfo.maxDate) {
processInfo.maxDate = date.clone();
}
}
} else {
xValues.push(null);
}
} else {
xValues.push(null);
}
indLine++;
}
// console.log(xValues);
if (
xValues.every((v) => {
return v === null;
})
) {
processInfo.errorMessage =
"No valid date as X value found in table";
return;
} else {
processInfo.gotAnyValidXValue ||= true;
}
// get y data
for (let yDatasetQuery of yDatasetQueries) {
let columnOfInterest = yDatasetQuery.getAccessor(1);
// console.log(`columnOfInterest: ${columnOfInterest}, numColumns: ${numColumns}`);
if (columnOfInterest >= numColumns) continue;
let indLine = 0;
for (let tableLine of tableLines) {
let dataRow = helper.trimByChar(tableLine.trim(), "|");
let dataRowSplitted = dataRow.split("|");
if (columnOfInterest < dataRowSplitted.length) {
let data = dataRowSplitted[columnOfInterest].trim();
let splitted = data.split(yDatasetQuery.getSeparator());
// console.log(splitted);
if (!splitted) continue;
if (splitted.length === 1) {
let retParse = helper.parseFloatFromAny(
splitted[0],
renderInfo.textValueMap
);
// console.log(retParse);
if (retParse.value !== null) {
if (retParse.type === ValueType.Time) {
yDatasetQuery.valueType = ValueType.Time;
}
let value = retParse.value;
if (
indLine < xValues.length &&
xValues[indLine]
) {
processInfo.gotAnyValidYValue ||= true;
collecting.addToDataMap(
dataMap,
helper.dateToStr(
xValues[indLine],
renderInfo.dateFormat
),
yDatasetQuery,
value
);
}
}
} else if (
splitted.length > yDatasetQuery.getAccessor(2) &&
yDatasetQuery.getAccessor(2) >= 0
) {
let value = null;
let splittedPart =
splitted[yDatasetQuery.getAccessor(2)].trim();
// console.log(splittedPart);
let retParse = helper.parseFloatFromAny(
splittedPart,
renderInfo.textValueMap
);
// console.log(retParse);
if (retParse.value !== null) {
if (retParse.type === ValueType.Time) {
yDatasetQuery.valueType = ValueType.Time;
}
value = retParse.value;
if (
indLine < xValues.length &&
xValues[indLine]
) {
processInfo.gotAnyValidYValue ||= true;
collecting.addToDataMap(
dataMap,
helper.dateToStr(
xValues[indLine],
renderInfo.dateFormat
),
yDatasetQuery,
value
);
}
}
}
}
indLine++;
} // Loop over tableLines
}
}
}
Example #25
Source File: parsing.ts From obsidian-tracker with MIT License | 4 votes |
export function getRenderInfoFromYaml(
yamlText: string,
plugin: Tracker
): RenderInfo | string {
let yaml;
try {
// console.log(yamlText);
yaml = parseYaml(yamlText);
} catch (err) {
let errorMessage = "Error parsing YAML";
console.log(err);
return errorMessage;
}
if (!yaml) {
let errorMessage = "Error parsing YAML";
return errorMessage;
}
// console.log(yaml);
let keysFoundInYAML = getAvailableKeysOfClass(yaml);
// console.log(keysFoundInYAML);
let errorMessage = "";
// Search target
if (!keysFoundInYAML.includes("searchTarget")) {
let errorMessage = "Parameter 'searchTarget' not found in YAML";
return errorMessage;
}
let searchTarget: Array<string> = [];
if (typeof yaml.searchTarget === "object" && yaml.searchTarget !== null) {
if (Array.isArray(yaml.searchTarget)) {
for (let target of yaml.searchTarget) {
if (typeof target === "string") {
if (target !== "") {
searchTarget.push(target);
} else {
errorMessage = "Empty search target is not allowed.";
break;
}
}
}
}
} else if (typeof yaml.searchTarget === "string") {
let splitted = splitInputByComma(yaml.searchTarget);
// console.log(splitted);
if (splitted.length > 1) {
for (let piece of splitted) {
piece = piece.trim();
if (piece !== "") {
searchTarget.push(piece);
} else {
errorMessage = "Empty search target is not allowed.";
break;
}
}
} else if (yaml.searchTarget === "") {
errorMessage = "Empty search target is not allowed.";
} else {
searchTarget.push(yaml.searchTarget);
}
} else {
errorMessage = "Invalid search target (searchTarget)";
}
for (let ind = 0; ind < searchTarget.length; ind++) {
searchTarget[ind] = helper.replaceImgTagByAlt(searchTarget[ind]);
}
// console.log(searchTarget);
if (errorMessage !== "") {
return errorMessage;
}
let numDatasets = searchTarget.length;
// Search type
if (!keysFoundInYAML.includes("searchType")) {
let errorMessage = "Parameter 'searchType' not found in YAML";
return errorMessage;
}
let searchType: Array<SearchType> = [];
let retSearchType = getStringArrayFromInput(
"searchType",
yaml.searchType,
numDatasets,
"",
validateSearchType,
false
);
if (typeof retSearchType === "string") {
return retSearchType; // errorMessage
}
for (let strType of retSearchType) {
switch (strType.toLowerCase()) {
case "tag":
searchType.push(SearchType.Tag);
break;
case "frontmatter":
searchType.push(SearchType.Frontmatter);
break;
case "wiki":
searchType.push(SearchType.Wiki);
break;
case "wiki.link":
searchType.push(SearchType.WikiLink);
break;
case "wiki.display":
searchType.push(SearchType.WikiDisplay);
break;
case "text":
searchType.push(SearchType.Text);
break;
case "dvfield":
searchType.push(SearchType.dvField);
break;
case "table":
searchType.push(SearchType.Table);
break;
case "filemeta":
searchType.push(SearchType.FileMeta);
break;
case "task":
searchType.push(SearchType.Task);
break;
case "task.all":
searchType.push(SearchType.Task);
break;
case "task.done":
searchType.push(SearchType.TaskDone);
break;
case "task.notdone":
searchType.push(SearchType.TaskNotDone);
break;
}
}
// Currently, we don't allow type 'table' used with other types
if (
searchType.includes(SearchType.Table) &&
searchType.filter((t) => t !== SearchType.Table).length > 0
) {
let errorMessage =
"searchType 'table' doestn't work with other types for now";
return errorMessage;
}
// console.log(searchType);
// separator
let multipleValueSparator: Array<string> = [];
let retMultipleValueSparator = getStringArrayFromInput(
"separator",
yaml.separator,
numDatasets,
"", // set the default value later
null,
true
);
if (typeof retMultipleValueSparator === "string") {
return retMultipleValueSparator; // errorMessage
}
multipleValueSparator = retMultipleValueSparator.map((sep) => {
if (sep === "comma" || sep === "\\,") {
return ",";
}
return sep;
});
// console.log(multipleValueSparator);
// xDataset
let retXDataset = getNumberArrayFromInput(
"xDataset",
yaml.xDataset,
numDatasets,
-1,
true
);
if (typeof retXDataset === "string") {
return retXDataset; // errorMessage
}
let xDataset = retXDataset.map((d: number) => {
if (d < 0 || d >= numDatasets) {
return -1;
}
return d;
});
// assign this to renderInfo later
// Create queries
let queries: Array<Query> = [];
for (let ind = 0; ind < searchTarget.length; ind++) {
let query = new Query(
queries.length,
searchType[ind],
searchTarget[ind]
);
query.setSeparator(multipleValueSparator[ind]);
if (xDataset.includes(ind)) query.usedAsXDataset = true;
queries.push(query);
}
// console.log(queries);
// Create grarph info
let renderInfo = new RenderInfo(queries);
let keysOfRenderInfo = getAvailableKeysOfClass(renderInfo);
let additionalAllowedKeys = ["searchType", "searchTarget", "separator"];
// console.log(keysOfRenderInfo);
let yamlLineKeys = [];
let yamlBarKeys = [];
let yamlPieKeys = [];
let yamlSummaryKeys = [];
let yamlMonthKeys = [];
let yamlHeatmapKeys = [];
let yamlBulletKeys = [];
for (let key of keysFoundInYAML) {
if (/^line[0-9]*$/.test(key)) {
yamlLineKeys.push(key);
additionalAllowedKeys.push(key);
}
if (/^bar[0-9]*$/.test(key)) {
yamlBarKeys.push(key);
additionalAllowedKeys.push(key);
}
if (/^pie[0-9]*$/.test(key)) {
yamlPieKeys.push(key);
additionalAllowedKeys.push(key);
}
if (/^summary[0-9]*$/.test(key)) {
yamlSummaryKeys.push(key);
additionalAllowedKeys.push(key);
}
if (/^bullet[0-9]*$/.test(key)) {
yamlBulletKeys.push(key);
additionalAllowedKeys.push(key);
}
if (/^month[0-9]*$/.test(key)) {
yamlMonthKeys.push(key);
additionalAllowedKeys.push(key);
}
if (/^heatmap[0-9]*$/.test(key)) {
yamlHeatmapKeys.push(key);
additionalAllowedKeys.push(key);
}
}
// Custom dataset
let yamlCustomDatasetKeys = [];
for (let key of keysFoundInYAML) {
if (/^dataset[0-9]*$/.test(key)) {
// Check the id of custom dataset is not duplicated
let customDatasetId = -1;
let strCustomDatasetId = key.replace("dataset", "");
if (strCustomDatasetId === "") {
customDatasetId = 0;
} else {
customDatasetId = parseFloat(strCustomDatasetId);
}
if (
queries.some((q) => {
return q.getId() === customDatasetId;
})
) {
errorMessage = "Duplicated dataset id for key '" + key + "'";
return errorMessage;
}
yamlCustomDatasetKeys.push(key);
additionalAllowedKeys.push(key);
}
}
// console.log(additionalAllowedKeys);
for (let key of keysFoundInYAML) {
if (
!keysOfRenderInfo.includes(key) &&
!additionalAllowedKeys.includes(key)
) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
let totalNumOutputs =
yamlLineKeys.length +
yamlBarKeys.length +
yamlPieKeys.length +
yamlSummaryKeys.length +
yamlBulletKeys.length +
yamlMonthKeys.length +
yamlHeatmapKeys.length;
if (totalNumOutputs === 0) {
return "No output parameter provided, please place line, bar, pie, month, bullet, or summary.";
}
// Root folder to search
renderInfo.folder = getStringFromInput(
yaml?.folder,
plugin.settings.folder
);
if (renderInfo.folder.trim() === "") {
renderInfo.folder = plugin.settings.folder;
}
// console.log("renderInfo folder: " + renderInfo.folder);
let abstractFolder = plugin.app.vault.getAbstractFileByPath(
normalizePath(renderInfo.folder)
);
if (!abstractFolder || !(abstractFolder instanceof TFolder)) {
let errorMessage = "Folder '" + renderInfo.folder + "' doesn't exist";
return errorMessage;
}
// file
if (typeof yaml.file === "string") {
let retFiles = getStringArray("file", yaml.file);
if (typeof retFiles === "string") {
return retFiles; // error message
}
renderInfo.file = retFiles;
}
// console.log(renderInfo.file);
// specifiedFilesOnly
if (typeof yaml.specifiedFilesOnly === "boolean") {
renderInfo.specifiedFilesOnly = yaml.specifiedFilesOnly;
}
// console.log(renderInfo.specifiedFilesOnly);
// fileContainsLinkedFiles
if (typeof yaml.fileContainsLinkedFiles === "string") {
let retFiles = getStringArray(
"fileContainsLinkedFiles",
yaml.fileContainsLinkedFiles
);
if (typeof retFiles === "string") {
return retFiles;
}
renderInfo.fileContainsLinkedFiles = retFiles;
}
// console.log(renderInfo.fileContainsLinkedFiles);
// fileMultiplierAfterLink
renderInfo.fileMultiplierAfterLink = getStringFromInput(
yaml?.fileMultiplierAfterLink,
renderInfo.fileMultiplierAfterLink
);
// console.log(renderInfo.fileMultiplierAfterLink);
// Date format
const dateFormat = yaml.dateFormat;
//?? not sure why I need this to make it works,
// without that, the assigned the renderInfo.dateFormat will become undefined
if (typeof yaml.dateFormat === "string") {
if (yaml.dateFormat === "") {
renderInfo.dateFormat = plugin.settings.dateFormat;
} else {
renderInfo.dateFormat = dateFormat;
}
} else {
renderInfo.dateFormat = plugin.settings.dateFormat;
}
// console.log("renderInfo dateFormat: " + renderInfo.dateFormat);
// Date format prefix
renderInfo.dateFormatPrefix = getStringFromInput(
yaml?.dateFormatPrefix,
renderInfo.dateFormatPrefix
);
// Date fromat suffix
renderInfo.dateFormatSuffix = getStringFromInput(
yaml?.dateFormatSuffix,
renderInfo.dateFormatSuffix
);
// startDate, endDate
// console.log("Parsing startDate");
if (typeof yaml.startDate === "string") {
if (/^([\-]?[0-9]+[\.][0-9]+|[\-]?[0-9]+)m$/.test(yaml.startDate)) {
let errorMessage =
"'m' for 'minute' is too small for parameter startDate, please use 'd' for 'day' or 'M' for month";
return errorMessage;
}
let strStartDate = helper.getDateStringFromInputString(
yaml.startDate,
renderInfo.dateFormatPrefix,
renderInfo.dateFormatSuffix
);
// console.log(strStartDate);
// relative date
let startDate = null;
let isStartDateValid = false;
startDate = helper.getDateByDurationToToday(
strStartDate,
renderInfo.dateFormat
);
// console.log(startDate);
if (startDate) {
isStartDateValid = true;
} else {
startDate = helper.strToDate(strStartDate, renderInfo.dateFormat);
if (startDate.isValid()) {
isStartDateValid = true;
}
}
// console.log(startDate);
if (!isStartDateValid || startDate === null) {
let errorMessage =
"Invalid startDate, the format of startDate may not match your dateFormat " +
renderInfo.dateFormat;
return errorMessage;
}
renderInfo.startDate = startDate;
}
// console.log("Parsing endDate");
if (typeof yaml.endDate === "string") {
if (/^([\-]?[0-9]+[\.][0-9]+|[\-]?[0-9]+)m$/.test(yaml.endDate)) {
let errorMessage =
"'m' for 'minute' is too small for parameter endDate, please use 'd' for 'day' or 'M' for month";
return errorMessage;
}
let strEndDate = helper.getDateStringFromInputString(
yaml.endDate,
renderInfo.dateFormatPrefix,
renderInfo.dateFormatSuffix
);
let endDate = null;
let isEndDateValid = false;
endDate = helper.getDateByDurationToToday(
strEndDate,
renderInfo.dateFormat
);
if (endDate) {
isEndDateValid = true;
} else {
endDate = helper.strToDate(strEndDate, renderInfo.dateFormat);
if (endDate.isValid()) {
isEndDateValid = true;
}
}
// console.log(endDate);
if (!isEndDateValid || endDate === null) {
let errorMessage =
"Invalid endDate, the format of endDate may not match your dateFormat " +
renderInfo.dateFormat;
return errorMessage;
}
renderInfo.endDate = endDate;
}
if (
renderInfo.startDate !== null &&
renderInfo.startDate.isValid() &&
renderInfo.endDate !== null &&
renderInfo.endDate.isValid()
) {
// Make sure endDate > startDate
if (renderInfo.endDate < renderInfo.startDate) {
let errorMessage =
"Invalid date range (startDate larger than endDate)";
return errorMessage;
}
}
// console.log(renderInfo.startDate);
// console.log(renderInfo.endDate);
// xDataset
renderInfo.xDataset = xDataset;
// console.log(renderInfo.xDataset);
// Dataset name (need xDataset to set default name)
let retDatasetName = getStringArrayFromInput(
"datasetName",
yaml.datasetName,
numDatasets,
"untitled",
null,
true
);
if (typeof retDatasetName === "string") {
return retDatasetName; // errorMessage
}
// rename untitled
let indUntitled = 0;
for (let ind = 0; ind < retDatasetName.length; ind++) {
if (renderInfo.xDataset.includes(ind)) continue;
if (retDatasetName[ind] === "untitled") {
retDatasetName[ind] = "untitled" + indUntitled.toString();
indUntitled++;
}
}
// Check duplicated names
if (new Set(retDatasetName).size === retDatasetName.length) {
renderInfo.datasetName = retDatasetName;
} else {
let errorMessage = "Not enough dataset names or duplicated names";
return errorMessage;
}
// console.log(renderInfo.datasetName);
// constValue
let retConstValue = getNumberArrayFromInput(
"constValue",
yaml.constValue,
numDatasets,
1.0,
true
);
if (typeof retConstValue === "string") {
return retConstValue; // errorMessage
}
renderInfo.constValue = retConstValue;
// console.log(renderInfo.constValue);
// ignoreAttachedValue
let retIgnoreAttachedValue = getBoolArrayFromInput(
"ignoreAttachedValue",
yaml.ignoreAttachedValue,
numDatasets,
false,
true
);
if (typeof retIgnoreAttachedValue === "string") {
return retIgnoreAttachedValue;
}
renderInfo.ignoreAttachedValue = retIgnoreAttachedValue;
// console.log(renderInfo.ignoreAttachedValue);
// ignoreZeroValue
let retIgnoreZeroValue = getBoolArrayFromInput(
"ignoreZeroValue",
yaml.ignoreZeroValue,
numDatasets,
false,
true
);
if (typeof retIgnoreZeroValue === "string") {
return retIgnoreZeroValue;
}
renderInfo.ignoreZeroValue = retIgnoreZeroValue;
// console.log(renderInfo.ignoreAttachedValue);
// accum
let retAccum = getBoolArrayFromInput(
"accum",
yaml.accum,
numDatasets,
false,
true
);
if (typeof retAccum === "string") {
return retAccum;
}
renderInfo.accum = retAccum;
// console.log(renderInfo.accum);
// penalty
let retPenalty = getNumberArrayFromInput(
"penalty",
yaml.penalty,
numDatasets,
null,
true
);
if (typeof retPenalty === "string") {
return retPenalty;
}
renderInfo.penalty = retPenalty;
// console.log(renderInfo.penalty);
// valueShift
let retValueShift = getNumberArrayFromInput(
"valueShift",
yaml.valueShift,
numDatasets,
0,
true
);
if (typeof retValueShift === "string") {
return retValueShift;
}
renderInfo.valueShift = retValueShift;
// console.log(renderInfo.valueShift);
// shiftOnlyValueLargerThan
let retShiftOnlyValueLargerThan = getNumberArrayFromInput(
"shiftOnlyValueLargerThan",
yaml.shiftOnlyValueLargerThan,
numDatasets,
null,
true
);
if (typeof retShiftOnlyValueLargerThan === "string") {
return retShiftOnlyValueLargerThan;
}
renderInfo.shiftOnlyValueLargerThan = retShiftOnlyValueLargerThan;
// console.log(renderInfo.shiftOnlyValueLargerThan);
// textValueMap
if (typeof yaml.textValueMap !== "undefined") {
let keys = getAvailableKeysOfClass(yaml.textValueMap);
// console.log(texts);
for (let key of keys) {
let text = key.trim();
renderInfo.textValueMap[text] = yaml.textValueMap[text];
}
}
// console.log(renderInfo.textValueMap);
// fixedScale
if (typeof yaml.fixedScale === "number") {
renderInfo.fixedScale = yaml.fixedScale;
}
// fitPanelWidth
if (typeof yaml.fitPanelWidth === "boolean") {
renderInfo.fitPanelWidth = yaml.fitPanelWidth;
}
// margin
let retMargin = getNumberArrayFromInput("margin", yaml.margin, 4, 10, true);
if (typeof retMargin === "string") {
return retMargin; // errorMessage
}
if (retMargin.length > 4) {
return "margin accepts not more than four values for top, right, bottom, and left margins.";
}
renderInfo.margin = new Margin(
retMargin[0],
retMargin[1],
retMargin[2],
retMargin[3]
);
// console.log(renderInfo.margin);
// customDataset related parameters
for (let datasetKey of yamlCustomDatasetKeys) {
let customDataset = new CustomDatasetInfo();
let yamlCustomDataset = yaml[datasetKey];
let keysOfCustomDatasetInfo = getAvailableKeysOfClass(customDataset);
let keysFoundInYAML = getAvailableKeysOfClass(yamlCustomDataset);
// console.log(keysOfCustomDatasetInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfCustomDatasetInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
// id
let customDatasetId = -1;
let strCustomDatasetId = datasetKey.replace("dataset", "");
if (strCustomDatasetId === "") {
customDatasetId = 0;
} else {
customDatasetId = parseFloat(strCustomDatasetId);
}
customDataset.id = customDatasetId;
// name
customDataset.name = getStringFromInput(
yamlCustomDataset?.name,
customDataset.name
);
// xData
let retXData = getStringArray("xData", yamlCustomDataset?.xData);
if (typeof retXData === "string") {
return retXData;
}
customDataset.xData = retXData;
// console.log(customDataset.xData);
let numXData = customDataset.xData.length;
// yData
let retYData = getStringArray("yData", yamlCustomDataset?.yData);
if (typeof retYData === "string") {
return retYData;
}
customDataset.yData = retYData;
// console.log(customDataset.yData);
if (customDataset.yData.length !== numXData) {
let errorMessage =
"Number of elements in xData and yData not matched";
return errorMessage;
}
renderInfo.customDataset.push(customDataset);
} // customDataset related parameters
// console.log(renderInfo.customDataset);
// line related parameters
for (let lineKey of yamlLineKeys) {
let line = new LineInfo();
let yamlLine = yaml[lineKey];
let keysOfLineInfo = getAvailableKeysOfClass(line);
let keysFoundInYAML = getAvailableKeysOfClass(yamlLine);
// console.log(keysOfLineInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfLineInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
let retParseCommonChartInfo = parseCommonChartInfo(yamlLine, line);
if (typeof retParseCommonChartInfo === "string") {
return retParseCommonChartInfo;
}
// lineColor
let retLineColor = getStringArrayFromInput(
"lineColor",
yamlLine?.lineColor,
numDatasets,
"",
validateColor,
true
);
if (typeof retLineColor === "string") {
return retLineColor; // errorMessage
}
line.lineColor = retLineColor;
// console.log(line.lineColor);
// lineWidth
let retLineWidth = getNumberArrayFromInput(
"lineWidth",
yamlLine?.lineWidth,
numDatasets,
1.5,
true
);
if (typeof retLineWidth === "string") {
return retLineWidth; // errorMessage
}
line.lineWidth = retLineWidth;
// console.log(line.lineWidth);
// showLine
let retShowLine = getBoolArrayFromInput(
"showLine",
yamlLine?.showLine,
numDatasets,
true,
true
);
if (typeof retShowLine === "string") {
return retShowLine;
}
line.showLine = retShowLine;
// console.log(line.showLine);
// showPoint
let retShowPoint = getBoolArrayFromInput(
"showPoint",
yamlLine?.showPoint,
numDatasets,
true,
true
);
if (typeof retShowPoint === "string") {
return retShowPoint;
}
line.showPoint = retShowPoint;
// console.log(line.showPoint);
// pointColor
let retPointColor = getStringArrayFromInput(
"pointColor",
yamlLine?.pointColor,
numDatasets,
"#69b3a2",
validateColor,
true
);
if (typeof retPointColor === "string") {
return retPointColor;
}
line.pointColor = retPointColor;
// console.log(line.pointColor);
// pointBorderColor
let retPointBorderColor = getStringArrayFromInput(
"pointBorderColor",
yamlLine?.pointBorderColor,
numDatasets,
"#69b3a2",
validateColor,
true
);
if (typeof retPointBorderColor === "string") {
return retPointBorderColor;
}
line.pointBorderColor = retPointBorderColor;
// console.log(line.pointBorderColor);
// pointBorderWidth
let retPointBorderWidth = getNumberArrayFromInput(
"pointBorderWidth",
yamlLine?.pointBorderWidth,
numDatasets,
0.0,
true
);
if (typeof retPointBorderWidth === "string") {
return retPointBorderWidth; // errorMessage
}
line.pointBorderWidth = retPointBorderWidth;
// console.log(line.pointBorderWidth);
// pointSize
let retPointSize = getNumberArrayFromInput(
"pointSize",
yamlLine?.pointSize,
numDatasets,
3.0,
true
);
if (typeof retPointSize === "string") {
return retPointSize; // errorMessage
}
line.pointSize = retPointSize;
// console.log(line.pointSize);
// fillGap
let retFillGap = getBoolArrayFromInput(
"fillGap",
yamlLine?.fillGap,
numDatasets,
false,
true
);
if (typeof retFillGap === "string") {
return retFillGap;
}
line.fillGap = retFillGap;
// console.log(line.fillGap);
// yAxisLocation
let retYAxisLocation = getStringArrayFromInput(
"yAxisLocation",
yamlLine?.yAxisLocation,
numDatasets,
"left",
validateYAxisLocation,
true
);
if (typeof retYAxisLocation === "string") {
return retYAxisLocation; // errorMessage
}
line.yAxisLocation = retYAxisLocation;
// console.log(line.yAxisLocation);
renderInfo.line.push(line);
} // line related parameters
// console.log(renderInfo.line);
// bar related parameters
for (let barKey of yamlBarKeys) {
let bar = new BarInfo();
let yamlBar = yaml[barKey];
let keysOfBarInfo = getAvailableKeysOfClass(bar);
let keysFoundInYAML = getAvailableKeysOfClass(yamlBar);
// console.log(keysOfBarInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfBarInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
let retParseCommonChartInfo = parseCommonChartInfo(yamlBar, bar);
if (typeof retParseCommonChartInfo === "string") {
return retParseCommonChartInfo;
}
// barColor
let retBarColor = getStringArrayFromInput(
"barColor",
yamlBar?.barColor,
numDatasets,
"",
validateColor,
true
);
if (typeof retBarColor === "string") {
return retBarColor; // errorMessage
}
bar.barColor = retBarColor;
// console.log(bar.barColor);
// yAxisLocation
let retYAxisLocation = getStringArrayFromInput(
"yAxisLocation",
yamlBar?.yAxisLocation,
numDatasets,
"left",
validateYAxisLocation,
true
);
if (typeof retYAxisLocation === "string") {
return retYAxisLocation; // errorMessage
}
bar.yAxisLocation = retYAxisLocation;
// console.log(bar.yAxisLocation);
renderInfo.bar.push(bar);
} // bar related parameters
// console.log(renderInfo.bar);
// pie related parameters
for (let pieKey of yamlPieKeys) {
let pie = new PieInfo();
let yamlPie = yaml[pieKey];
let keysOfPieInfo = getAvailableKeysOfClass(pie);
let keysFoundInYAML = getAvailableKeysOfClass(yamlPie);
// console.log(keysOfPieInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfPieInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
// title
pie.title = getStringFromInput(yamlPie?.title, pie.title);
// console.log(pie.title);
// data
let retData = getStringArray("data", yamlPie?.data);
if (typeof retData === "string") {
return retData;
}
pie.data = retData;
// console.log(pie.data);
let numData = pie.data.length;
// dataColor
let retDataColor = getStringArrayFromInput(
"dataColor",
yamlPie?.dataColor,
numData,
null,
validateColor,
true
);
if (typeof retDataColor === "string") {
return retDataColor; // errorMessage
}
pie.dataColor = retDataColor;
// console.log(pie.dataColor);
// dataName
let retDataName = getStringArrayFromInput(
"dataName",
yamlPie?.dataName,
numData,
"",
null,
true
);
if (typeof retDataName === "string") {
return retDataName; // errorMessage
}
pie.dataName = retDataName;
// console.log(pie.dataName);
// label
let retLabel = getStringArrayFromInput(
"label",
yamlPie?.label,
numData,
"",
null,
true
);
if (typeof retLabel === "string") {
return retLabel; // errorMessage
}
pie.label = retLabel;
// console.log(pie.label);
// hideLabelLessThan
if (typeof yamlPie?.hideLabelLessThan === "number") {
pie.hideLabelLessThan = yamlPie.hideLabelLessThan;
}
// console.log(pie.hideLabelLessThan);
// extLabel
let retExtLabel = getStringArrayFromInput(
"extLabel",
yamlPie?.extLabel,
numData,
"",
null,
true
);
if (typeof retExtLabel === "string") {
return retExtLabel; // errorMessage
}
pie.extLabel = retExtLabel;
// console.log(pie.extLabel);
// showExtLabelOnlyIfNoLabel
if (typeof yamlPie?.showExtLabelOnlyIfNoLabel === "boolean") {
pie.showExtLabelOnlyIfNoLabel = yamlPie.showExtLabelOnlyIfNoLabel;
}
// console.log(pie.showExtLabelOnlyIfNoLabel);
// ratioInnerRadius
if (typeof yamlPie?.ratioInnerRadius === "number") {
pie.ratioInnerRadius = yamlPie.ratioInnerRadius;
}
// console.log(pie.ratioInnerRadius);
// showLegend
if (typeof yamlPie?.showLegend === "boolean") {
pie.showLegend = yamlPie.showLegend;
}
// legendPosition
pie.legendPosition = getStringFromInput(
yamlPie?.legendPosition,
"right"
);
// legendOrient
let defaultLegendOrientation = "horizontal";
if (pie.legendPosition === "top" || pie.legendPosition === "bottom") {
defaultLegendOrientation = "horizontal";
} else if (
pie.legendPosition === "left" ||
pie.legendPosition === "right"
) {
defaultLegendOrientation = "vertical";
} else {
defaultLegendOrientation = "horizontal";
}
pie.legendOrientation = getStringFromInput(
yamlPie?.legendOrientation,
defaultLegendOrientation
);
// console.log(pie.legendPosition);
// console.log(pie.legendOrientation);
// legendBgColor
pie.legendBgColor = getStringFromInput(
yamlPie?.legendBgColor,
pie.legendBgColor
);
// legendBorderColor
pie.legendBorderColor = getStringFromInput(
yamlPie?.legendBorderColor,
pie.legendBorderColor
);
renderInfo.pie.push(pie);
} // pie related parameters
// console.log(renderInfo.pie);
// summary related parameters
for (let summaryKey of yamlSummaryKeys) {
let summary = new SummaryInfo();
let yamlSummary = yaml[summaryKey];
let keysOfSummaryInfo = getAvailableKeysOfClass(summary);
let keysFoundInYAML = getAvailableKeysOfClass(yamlSummary);
// console.log(keysOfSummaryInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfSummaryInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
// template
summary.template = getStringFromInput(
yamlSummary?.template,
summary.template
);
// style
summary.style = getStringFromInput(yamlSummary?.style, summary.style);
renderInfo.summary.push(summary);
} // summary related parameters
// Month related parameters
for (let monthKey of yamlMonthKeys) {
let month = new MonthInfo();
let yamlMonth = yaml[monthKey];
let keysOfMonthInfo = getAvailableKeysOfClass(month);
let keysFoundInYAML = getAvailableKeysOfClass(yamlMonth);
// console.log(keysOfSummaryInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfMonthInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
// mode
month.mode = getStringFromInput(yamlMonth?.mode, month.mode);
// console.log(month.mode);
// dataset
let retDataset = getNumberArray("dataset", yamlMonth?.dataset);
if (typeof retDataset === "string") {
return retDataset;
}
if (retDataset.length === 0) {
// insert y dataset given
for (let q of queries) {
retDataset.push(q.getId());
}
}
month.dataset = retDataset;
// console.log(month.dataset);
let numDataset = month.dataset.length;
// startWeekOn
month.startWeekOn = getStringFromInput(
yamlMonth?.startWeekOn,
month.startWeekOn
);
// console.log(month.startWeekOn);
// showCircle
if (typeof yamlMonth?.showCircle === "boolean") {
month.showCircle = yamlMonth.showCircle;
}
// console.log(month.showCircle);
// threshold
let retThreshold = getNumberArray("threshold", yamlMonth?.threshold);
if (typeof retThreshold === "string") {
return retThreshold;
}
month.threshold = retThreshold;
if (month.threshold.length === 0) {
for (let indDataset = 0; indDataset < numDataset; indDataset++) {
month.threshold.push(0);
}
}
if (month.threshold.length !== month.dataset.length) {
// console.log(month.threshold);
// console.log(month.dataset);
const errorMessage =
"The number of inputs of threshold and dataset not matched";
return errorMessage;
}
// console.log(month.threshold);
// yMin
let retYMin = getNumberArray("yMin", yamlMonth?.yMin);
if (typeof retYMin === "string") {
return retYMin;
}
month.yMin = retYMin;
if (month.yMin.length === 0) {
for (let indDataset = 0; indDataset < numDataset; indDataset++) {
month.yMin.push(null);
}
}
if (month.yMin.length !== month.dataset.length) {
const errorMessage =
"The number of inputs of yMin and dataset not matched";
return errorMessage;
}
// console.log(month.yMin);
// yMax
let retYMax = getNumberArray("yMax", yamlMonth?.yMax);
if (typeof retYMax === "string") {
return retYMax;
}
month.yMax = retYMax;
if (month.yMax.length === 0) {
for (let indDataset = 0; indDataset < numDataset; indDataset++) {
month.yMax.push(null);
}
}
if (month.yMax.length !== month.dataset.length) {
const errorMessage =
"The number of inputs of yMin and dataset not matched";
return errorMessage;
}
// console.log(month.yMax);
// color
month.color = getStringFromInput(yamlMonth?.color, month.color);
// console.log(month.color);
// dimNotInMonth
if (typeof yamlMonth?.dimNotInMonth === "boolean") {
month.dimNotInMonth = yamlMonth.dimNotInMonth;
}
// console.log(month.dimNotInMonth);
// showStreak
if (typeof yamlMonth?.showStreak === "boolean") {
month.showStreak = yamlMonth.showStreak;
}
// console.log(month.showStreak);
// showTodayRing
if (typeof yamlMonth?.showTodayRing === "boolean") {
month.showTodayRing = yamlMonth.showTodayRing;
}
// console.log(month.showTodayRing);
// showSelectedValue
if (typeof yamlMonth?.showSelectedValue === "boolean") {
month.showSelectedValue = yamlMonth.showSelectedValue;
}
// console.log(month.showSelectedValue);
// showSelectedRing
if (typeof yamlMonth?.showSelectedRing === "boolean") {
month.showSelectedRing = yamlMonth.showSelectedRing;
}
// console.log(month.showSelectedRing);
// circleColor
month.circleColor = getStringFromInput(
yamlMonth?.circleColor,
month.circleColor
);
// console.log(month.circleColor);
// circleColorByValue
if (typeof yamlMonth?.circleColorByValue === "boolean") {
month.circleColorByValue = yamlMonth.circleColorByValue;
}
// console.log(month.circleColorByValue);
// headerYearColor
month.headerYearColor = getStringFromInput(
yamlMonth?.headerYearColor,
month.headerYearColor
);
// console.log(month.headerYearColor);
// headerMonthColor
month.headerMonthColor = getStringFromInput(
yamlMonth?.headerMonthColor,
month.headerMonthColor
);
// console.log(month.headerMonthColor);
// dividingLineColor
month.dividingLineColor = getStringFromInput(
yamlMonth?.dividingLineColor,
month.dividingLineColor
);
// console.log(month.dividingLineColor);
// todayRingColor
month.todayRingColor = getStringFromInput(
yamlMonth?.todayRingColor,
month.todayRingColor
);
// console.log(month.todayRingColor);
// selectedRingColor
month.selectedRingColor = getStringFromInput(
yamlMonth?.selectedRingColor,
month.selectedRingColor
);
// console.log(month.selectedRingColor);
// initMonth
month.initMonth = getStringFromInput(
yamlMonth?.initMonth,
month.initMonth
);
// console.log(month.initMonth);
// showAnnotation
if (typeof yamlMonth?.showAnnotation === "boolean") {
month.showAnnotation = yamlMonth.showAnnotation;
}
// console.log(month.showAnnotation);
// annotation
let retAnnotation = getStringArray("annotation", yamlMonth?.annotation);
if (typeof retAnnotation === "string") {
return retAnnotation;
}
month.annotation = retAnnotation;
if (month.annotation.length === 0) {
for (let indDataset = 0; indDataset < numDataset; indDataset++) {
month.annotation.push(null);
}
}
if (month.annotation.length !== month.dataset.length) {
const errorMessage =
"The number of inputs of annotation and dataset not matched";
return errorMessage;
}
// console.log(month.annotation);
// showAnnotationOfAllTargets
if (typeof yamlMonth?.showAnnotationOfAllTargets === "boolean") {
month.showAnnotationOfAllTargets =
yamlMonth.showAnnotationOfAllTargets;
}
// console.log(month.showAnnotationOfAllTargets);
renderInfo.month.push(month);
} // Month related parameters
// console.log(renderInfo.month);
// Heatmap related parameters
for (let heatmapKey of yamlHeatmapKeys) {
let heatmap = new HeatmapInfo();
let yamlHeatmap = yaml[heatmapKey];
let keysOfHeatmapInfo = getAvailableKeysOfClass(heatmap);
let keysFoundInYAML = getAvailableKeysOfClass(yamlHeatmap);
// console.log(keysOfHeatmapInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfHeatmapInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
renderInfo.heatmap.push(heatmap);
}
// console.log(renderInfo.heatmap);
// Bullet related parameters
for (let bulletKey of yamlBulletKeys) {
let bullet = new BulletInfo();
let yamlBullet = yaml[bulletKey];
let keysOfBulletInfo = getAvailableKeysOfClass(bullet);
let keysFoundInYAML = getAvailableKeysOfClass(yamlBullet);
// console.log(keysOfSummaryInfo);
// console.log(keysFoundInYAML);
for (let key of keysFoundInYAML) {
if (!keysOfBulletInfo.includes(key)) {
errorMessage = "'" + key + "' is not an available key";
return errorMessage;
}
}
// title
bullet.title = getStringFromInput(yamlBullet?.title, bullet.title);
// console.log(bullet.title);
// dataset
bullet.dataset = getStringFromInput(
yamlBullet?.dataset,
bullet.dataset
);
// console.log(bullet.dataset);
// orientation
bullet.orientation = getStringFromInput(
yamlBullet?.orientation,
bullet.orientation
);
// console.log(bullet.orientation);
// range
let retRange = getNumberArray("range", yamlBullet?.range);
if (typeof retRange === "string") {
return retRange;
}
let range = retRange as Array<number>;
// Check the value is monotonically increasing
// Check the value is not negative
if (range.length === 1) {
if (range[0] < 0) {
errorMessage = "Negative range value is not allowed";
return errorMessage;
}
} else if (range.length > 1) {
let lastBound = range[0];
if (lastBound < 0) {
errorMessage = "Negative range value is not allowed";
return errorMessage;
} else {
for (let ind = 1; ind < range.length; ind++) {
if (range[ind] <= lastBound) {
errorMessage =
"Values in parameter 'range' should be monotonically increasing";
return errorMessage;
}
}
}
} else {
errorMessage = "Empty range is not allowed";
return errorMessage;
}
bullet.range = range;
let numRange = range.length;
// console.log(renderInfo.bullet.range);
// range color
let retRangeColor = getStringArrayFromInput(
"rangeColor",
yamlBullet?.rangeColor,
numRange,
"",
validateColor,
true
);
if (typeof retRangeColor === "string") {
return retRangeColor; // errorMessage
}
bullet.rangeColor = retRangeColor;
// console.log(bullet.rangeColor);
// actual value, can possess template variable
bullet.value = getStringFromInput(yamlBullet?.value, bullet.value);
// console.log(bullet.value);
// value unit
bullet.valueUnit = getStringFromInput(
yamlBullet?.valueUnit,
bullet.valueUnit
);
// console.log(bullet.valueUnit);
// value color
bullet.valueColor = getStringFromInput(
yamlBullet?.valueColor,
bullet.valueColor
);
// console.log(bullet.valueColor);
// show mark
if (typeof yamlBullet?.showMarker === "boolean") {
bullet.showMarker = yamlBullet.showMarker;
}
// console.log(bullet.showMark);
// mark value
if (typeof yamlBullet?.markerValue === "number") {
bullet.markerValue = yamlBullet.markerValue;
}
// console.log(bullet.markValue);
// mark color
bullet.markerColor = getStringFromInput(
yamlBullet?.markerColor,
bullet.markerColor
);
// console.log(bullet.markValue);
renderInfo.bullet.push(bullet);
} // Bullet related parameters
// console.log(renderInfo.bullet);
return renderInfo;
}
Example #26
Source File: main.ts From obsidian-readwise with GNU General Public License v3.0 | 4 votes |
async downloadArchive(exportID: number, buttonContext: ButtonComponent): Promise<void> {
let artifactURL = `${baseURL}/api/download_artifact/${exportID}`;
if (exportID <= this.settings.lastSavedStatusID) {
console.log(`Readwise Official plugin: Already saved data from export ${exportID}`);
this.handleSyncSuccess(buttonContext);
this.notice("Readwise data is already up to date", false, 4);
return;
}
let response, blob;
try {
response = await fetch(
artifactURL, {headers: this.getAuthHeaders()}
);
} catch (e) {
console.log("Readwise Official plugin: fetch failed in downloadArchive: ", e);
}
if (response && response.ok) {
blob = await response.blob();
} else {
console.log("Readwise Official plugin: bad response in downloadArchive: ", response);
this.handleSyncError(buttonContext, this.getErrorMessageFromResponse(response));
return;
}
this.fs = this.app.vault.adapter;
const blobReader = new zip.BlobReader(blob);
const zipReader = new zip.ZipReader(blobReader);
const entries = await zipReader.getEntries();
this.notice("Saving files...", false, 30);
if (entries.length) {
for (const entry of entries) {
let bookID: string;
const processedFileName = normalizePath(entry.filename.replace(/^Readwise/, this.settings.readwiseDir));
try {
// ensure the directory exists
let dirPath = processedFileName.replace(/\/*$/, '').replace(/^(.+)\/[^\/]*?$/, '$1');
const exists = await this.fs.exists(dirPath);
if (!exists) {
await this.fs.mkdir(dirPath);
}
// write the actual files
const contents = await entry.getData(new zip.TextWriter());
let contentToSave = contents;
let originalName = processedFileName;
// extracting book ID from file name
let split = processedFileName.split("--");
if (split.length > 1) {
originalName = split.slice(0, -1).join("--") + ".md";
bookID = split.last().match(/\d+/g)[0];
this.settings.booksIDsMap[originalName] = bookID;
}
if (await this.fs.exists(originalName)) {
// if the file already exists we need to append content to existing one
const existingContent = await this.fs.read(originalName);
contentToSave = existingContent + contents;
}
await this.fs.write(originalName, contentToSave);
await this.saveSettings();
} catch (e) {
console.log(`Readwise Official plugin: error writing ${processedFileName}:`, e);
this.notice(`Readwise: error while writing ${processedFileName}: ${e}`, true, 4, true);
if (bookID) {
this.settings.booksToRefresh.push(bookID);
await this.saveSettings();
}
// communicate with readwise?
}
}
}
// close the ZipReader
await zipReader.close();
await this.acknowledgeSyncCompleted(buttonContext);
this.handleSyncSuccess(buttonContext, "Synced!", exportID);
this.notice("Readwise sync completed", true, 1, true);
// @ts-ignore
if (this.app.isMobile) {
this.notice("If you don't see all of your readwise files reload obsidian app", true,);
}
}
Example #27
Source File: main.ts From obsidian-readwise with GNU General Public License v3.0 | 4 votes |
display(): void {
let {containerEl} = this;
containerEl.empty();
containerEl.createEl('h1', {text: 'Readwise Official'});
containerEl.createEl('p', {text: 'Created by '}).createEl('a', {text: 'Readwise', href: 'https://readwise.io'});
containerEl.getElementsByTagName('p')[0].appendText(' ?');
containerEl.createEl('h2', {text: 'Settings'});
if (this.plugin.settings.token) {
new Setting(containerEl)
.setName("Sync your Readwise data with Obsidian")
.setDesc("On first sync, the Readwise plugin will create a new folder containing all your highlights")
.setClass('rw-setting-sync')
.addButton((button) => {
button.setCta().setTooltip("Once the sync begins, you can close this plugin page")
.setButtonText('Initiate Sync')
.onClick(async () => {
if (this.plugin.settings.isSyncing) {
// NOTE: This is used to prevent multiple syncs at the same time. However, if a previous sync fails,
// it can stop new syncs from happening. Make sure to set isSyncing to false
// if there's ever errors/failures in previous sync attempts, so that
// we don't block syncing subsequent times.
new Notice("Readwise sync already in progress");
} else {
this.plugin.clearInfoStatus(containerEl);
this.plugin.settings.isSyncing = true;
await this.plugin.saveData(this.plugin.settings);
button.setButtonText("Syncing...");
await this.plugin.requestArchive(button);
}
});
});
let el = containerEl.createEl("div", {cls: "rw-info-container"});
containerEl.find(".rw-setting-sync > .setting-item-control ").prepend(el);
new Setting(containerEl)
.setName("Customize formatting options")
.setDesc("You can customize which items export to Obsidian and how they appear from the Readwise website")
.addButton((button) => {
button.setButtonText("Customize").onClick(() => {
window.open(`${baseURL}/export/obsidian/preferences`);
});
});
new Setting(containerEl)
.setName('Customize base folder')
.setDesc("By default, the plugin will save all your highlights into a folder named Readwise")
// TODO: change this to search filed when the API is exposed (https://github.com/obsidianmd/obsidian-api/issues/22)
.addText(text => text
.setPlaceholder('Defaults to: Readwise')
.setValue(this.plugin.settings.readwiseDir)
.onChange(async (value) => {
this.plugin.settings.readwiseDir = normalizePath(value || "Readwise");
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Configure resync frequency')
.setDesc("If not set to Manual, Readwise will automatically resync with Obsidian when the app is open at the specified interval")
.addDropdown(dropdown => {
dropdown.addOption("0", "Manual");
dropdown.addOption("60", "Every 1 hour");
dropdown.addOption((12 * 60).toString(), "Every 12 hours");
dropdown.addOption((24 * 60).toString(), "Every 24 hours");
// select the currently-saved option
dropdown.setValue(this.plugin.settings.frequency);
dropdown.onChange((newValue) => {
// update the plugin settings
this.plugin.settings.frequency = newValue;
this.plugin.saveSettings();
// destroy & re-create the scheduled task
this.plugin.configureSchedule();
});
});
new Setting(containerEl)
.setName("Sync automatically when Obsidian opens")
.setDesc("If enabled, Readwise will automatically resync with Obsidian each time you open the app")
.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.triggerOnLoad);
toggle.onChange((val) => {
this.plugin.settings.triggerOnLoad = val;
this.plugin.saveSettings();
});
}
);
new Setting(containerEl)
.setName("Resync deleted files")
.setDesc("If enabled, you can refresh individual items by deleting the file in Obsidian and initiating a resync")
.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.refreshBooks);
toggle.onChange(async (val) => {
this.plugin.settings.refreshBooks = val;
await this.plugin.saveSettings();
if (val) {
this.plugin.refreshBookExport();
}
});
}
);
if (this.plugin.settings.lastSyncFailed) {
this.plugin.showInfoStatus(containerEl.find(".rw-setting-sync .rw-info-container").parentElement, "Last sync failed", "rw-error");
}
}
if (!this.plugin.settings.token) {
new Setting(containerEl)
.setName("Connect Obsidian to Readwise")
.setClass("rw-setting-connect")
.setDesc("The Readwise plugin enables automatic syncing of all your highlights from Kindle, Instapaper, Pocket, and more. Note: Requires Readwise account.")
.addButton((button) => {
button.setButtonText("Connect").setCta().onClick(async (evt) => {
const success = await this.plugin.getUserAuthToken(evt.target as HTMLElement);
if (success) {
this.display();
}
});
});
let el = containerEl.createEl("div", {cls: "rw-info-container"});
containerEl.find(".rw-setting-connect > .setting-item-control ").prepend(el);
}
const help = containerEl.createEl('p',);
help.innerHTML = "Question? Please see our <a href='https://help.readwise.io/article/125-how-does-the-readwise-to-obsidian-export-integration-work'>Documentation</a> or email us at <a href='mailto:[email protected]'>[email protected]</a> ?";
}