obsidian#htmlToMarkdown TypeScript Examples

The following examples show how to use obsidian#htmlToMarkdown. 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: Action.ts    From obsidian-rss with GNU General Public License v3.0 5 votes vote down vote up
static COPY = new Action(t("copy_to_clipboard"), "documents", ((_, item) : Promise<void> => {
        return copy(htmlToMarkdown(item.content));
    }));
Example #2
Source File: functions.ts    From obsidian-rss with GNU General Public License v3.0 5 votes vote down vote up
export function rssToMd(plugin: RssReaderPlugin, content: string): string {

    let markdown = htmlToMarkdown(content);

    //If dataview is installed
    if ((plugin.app as any).plugins.plugins["dataview"]) {
        //wrap dataview inline code
        const {
            inlineQueryPrefix,
            inlineJsQueryPrefix
        } = (plugin.app as any).plugins.plugins.dataview.api.settings as { [key: string]: string };
            markdown = markdown.replace(RegExp(`\`${escapeRegExp(inlineQueryPrefix)}.*\``, 'g'), "<pre>$&</pre>");
            markdown = markdown.replace(RegExp(`\`${escapeRegExp(inlineJsQueryPrefix)}.*\``, 'g'), "<pre>$&</pre>");
    }

    //If templater is installed
    if ((plugin.app as any).plugins.plugins["templater-obsidian"]) {
        //wrap templater commands
        markdown = markdown.replace(/<%([\s\S]*?)%>/g, "```javascript\n$&\n```");
    }

    //wrap wallabag.xml codeblocks where there is a processor registered.
    //as codeblockProcessors is not exposed publicly(and seems to be only existent after v.13) do a check first
    //@ts-ignore
    if(MarkdownPreviewRenderer.codeBlockPostProcessors) {
        //@ts-ignore
        const codeblockProcessors: string[] = Object.keys(MarkdownPreviewRenderer.codeBlockPostProcessors);
        for (const codeblockProcessor of codeblockProcessors) {
            const regex = RegExp("^```" + codeblockProcessor +"\[\\s\\S\]*?```$", "gm");
            markdown = markdown.replace(regex, "<pre>$&</pre>");
        }
    }else {
        //just remove wallabag.xml codeblocks instead
        markdown = markdown.replace(/^```.*\n([\s\S]*?)```$/gm, "<pre>$&</pre>");
    }

    if(!plugin.settings.displayMedia) {
        //remove any embeds, but keep alias
        markdown = markdown.replace(/!?\[(.*)\]\(.+\)/gm, "$1");
    }
    return markdown;
}
Example #3
Source File: functions.ts    From obsidian-rss with GNU General Public License v3.0 4 votes vote down vote up
function applyTemplate(plugin: RssReaderPlugin, item: RssFeedItem, template: string, filename?: string): string {
    let result = template.replace(/{{title}}/g, item.title);
    result = result.replace(/{{link}}/g, item.link);
    result = result.replace(/{{author}}/g, item.creator);
    result = result.replace(/{{published}}/g, moment(item.pubDate).format(plugin.settings.dateFormat));
    result = result.replace(/{{created}}/g, moment().format(plugin.settings.dateFormat));
    result = result.replace(/{{date}}/g, moment().format(plugin.settings.dateFormat));
    result = result.replace(/{{feed}}/g, item.feed);
    result = result.replace(/{{folder}}/g, item.folder);
    result = result.replace(/{{description}}/g, item.description);
    result = result.replace(/{{media}}/g, item.enclosure);

    result = result.replace(/({{published:).*(}})/g, function (k) {
        const value = k.split(":")[1];
        const format = value.substring(0, value.indexOf("}"));
        return moment(item.pubDate).format(format);
    });

    result = result.replace(/({{created:).*(}})/g, function (k) {
        const value = k.split(":")[1];
        const format = value.substring(0, value.indexOf("}"));
        return moment().format(format);
    });

    result = result.replace(/({{tags:).*(}})/g, function (k) {
        const value = k.split(":")[1];
        const separator = value.substring(0, value.indexOf("}"));
        return item.tags.join(separator);
    });

    result = result.replace(/({{#tags:).*(}})/g, function (k) {
        const value = k.split(":")[1];
        const separator = value.substring(0, value.indexOf("}"));
        return item.tags.map(i => '#' + i).join(separator);
    });

    result = result.replace(/{{tags}}/g, item.tags.join(", "));
    result = result.replace(/{{#tags}}/g, item.tags.map(i => '#' + i).join(", "));



    result = result.replace(/{{highlights}}/g, item.highlights.map(value => {
        //remove wallabag.xml - from the start of a highlight
        return "- " + rssToMd(plugin, removeFormatting(value).replace(/^(-+)/, ""))
    }).join("\n"));

    result = result.replace(/({{highlights:)[\s\S][^}]*(}})/g, function (k) {
        const value = k.split(/(:[\s\S]?)/);
        const tmp = value.slice(1).join("");
        const template = tmp.substring(1, tmp.indexOf("}"));
        return item.highlights.map(i => {
            return template.replace(/%%highlight%%/g, rssToMd(plugin, removeFormatting(i)).replace(/^(-+)/, ""));
        }).join("");
    });

    if (filename) {
        result = result.replace(/{{filename}}/g, filename);
    }

    let content = rssToMd(plugin, item.content);


    item.highlights.forEach(highlight => {
        const mdHighlight = htmlToMarkdown(highlight);
        content = content.replace(mdHighlight, "==" + mdHighlight + "==");
    });


    /*
    fixes #48
    replacing $ with $$$, because that is a special regex character:
    https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/string/replace#specifying_a_string_as_a_parameter
    solution taken from: https://stackoverflow.com/a/22612228/5589264
    */
    content = content.replace(/\$/g, "$$$");


    result = result.replace(/{{content}}/g, content);

    return result;
}
Example #4
Source File: ItemModal.ts    From obsidian-rss with GNU General Public License v3.0 4 votes vote down vote up
async display(): Promise<void> {
        this.modalEl.addClass("rss-modal");
        const {contentEl} = this;
        contentEl.empty();

        //don't add any scrolling to modal content
        contentEl.style.height = "100%";
        contentEl.style.overflowY = "hidden";

        const topButtons = contentEl.createDiv('topButtons');

        let actions = Array.of(Action.CREATE_NOTE, Action.PASTE, Action.COPY, Action.OPEN);

        if (this.save) {
            this.readButton = new ButtonComponent(topButtons)
                .setIcon(this.item.read ? 'eye-off' : 'eye')
                .setTooltip(this.item.read ? t("mark_as_unread") : t("mark_as_read"))
                .onClick(async () => {
                    await this.markAsRead();
                });
            this.readButton.buttonEl.setAttribute("tabindex", "-1");
            this.readButton.buttonEl.addClass("rss-button");

            this.favoriteButton = new ButtonComponent(topButtons)
                .setIcon(this.item.favorite ? 'star-glyph' : 'star')
                .setTooltip(this.item.favorite ? t("remove_from_favorites") : t("mark_as_favorite"))
                .onClick(async () => {
                    await this.markAsFavorite();
                });
            this.favoriteButton.buttonEl.setAttribute("tabindex", "-1");
            this.favoriteButton.buttonEl.addClass("rss-button");

            actions = Array.of(Action.TAGS, ...actions);
        }


        actions.forEach((action) => {
            const button = new ButtonComponent(topButtons)
                .setIcon(action.icon)
                .setTooltip(action.name)
                .onClick(async () => {
                    await action.processor(this.plugin, this.item);
                });
            button.buttonEl.setAttribute("tabindex", "-1");
            button.buttonEl.addClass("rss-button");
        });
        //@ts-ignore
        if (this.app.plugins.plugins["obsidian-tts"]) {
            const ttsButton = new ButtonComponent(topButtons)
                .setIcon("headphones")
                .setTooltip(t("read_article_tts"))
                .onClick(async () => {
                    const content = htmlToMarkdown(this.item.content);
                    //@ts-ignore
                    await this.app.plugins.plugins["obsidian-tts"].ttsService.say(this.item.title, content, this.item.language);
                });
            ttsButton.buttonEl.addClass("rss-button");
        }

        const prevButton = new ButtonComponent(topButtons)
            .setIcon("left-arrow-with-tail")
            .setTooltip(t("previous"))
            .onClick(() => {
                this.previous();
            });
        prevButton.buttonEl.addClass("rss-button");

        const nextButton = new ButtonComponent(topButtons)
            .setIcon("right-arrow-with-tail")
            .setTooltip(t("next"))
            .onClick(() => {
                this.next();
            });
        nextButton.buttonEl.addClass("rss-button");

        const title = contentEl.createEl('h1', 'rss-title');
        title.addClass("rss-selectable");
        title.setText(this.item.title);

        const subtitle = contentEl.createEl("h3", "rss-subtitle");
        subtitle.addClass("rss-selectable");
        if (this.item.creator) {
            subtitle.appendText(this.item.creator);
        }
        if (this.item.pubDate) {
            subtitle.appendText(" - " + window.moment(this.item.pubDate).format(this.plugin.settings.dateFormat));
        }
        const tagEl = contentEl.createSpan("tags");
        this.item.tags.forEach((tag) => {
            const tagA = tagEl.createEl("a");
            tagA.setText(tag);
            tagA.addClass("tag", "rss-tag");
        });

        const content = contentEl.createDiv('rss-content');
        content.addClass("rss-scrollable-content", "rss-selectable");

        if (this.item.enclosure && this.plugin.settings.displayMedia) {
            if (this.item.enclosureType.toLowerCase().contains("audio")) {
                const audio = content.createEl("audio", {attr: {controls: "controls"}});
                audio.createEl("source", {attr: {src: this.item.enclosure, type: this.item.enclosureType}});
            }
            if (this.item.enclosureType.toLowerCase().contains("video")) {
                const video = content.createEl("video", {attr: {controls: "controls", width: "100%", height: "100%"}});
                video.createEl("source", {attr: {src: this.item.enclosure, type: this.item.enclosureType}});
            }

            //embedded yt player
            if (this.item.enclosure && this.item.id.startsWith("yt:")) {
                content.createEl("iframe", {
                    attr: {
                        type: "text/html",
                        src: "https://www.youtube.com/embed/" + this.item.enclosure,
                        width: "100%",
                        height: "100%",
                        allowFullscreen: "true"
                    }
                });
            }
        }

        if (this.item.content) {
            //prepend empty yaml to fix rendering errors
            const markdown = "---\n---" + rssToMd(this.plugin, this.item.content);

            await MarkdownRenderer.renderMarkdown(markdown, content, "", this.plugin);

            this.item.highlights.forEach(highlight => {
                if (content.innerHTML.includes(highlight)) {
                    const newNode = contentEl.createEl("mark");
                    newNode.innerHTML = highlight;
                    content.innerHTML = content.innerHTML.replace(highlight, newNode.outerHTML);
                    newNode.remove();
                } else {
                    console.log("Highlight not included");
                    console.log(highlight);
                }
            });

            content.addEventListener('contextmenu', (event) => {
                event.preventDefault();

                const selection = document.getSelection();
                const range = selection.getRangeAt(0);

                const div = contentEl.createDiv();
                const htmlContent = range.cloneContents();
                const html = htmlContent.cloneNode(true);
                div.appendChild(html);
                const selected = div.innerHTML;
                div.remove();

                const menu = new Menu(this.app);

                let previousHighlight: HTMLElement;
                if (this.item.highlights.includes(range.startContainer.parentElement.innerHTML)) {
                    previousHighlight = range.startContainer.parentElement;
                }
                if (this.item.highlights.includes(range.startContainer.parentElement.parentElement.innerHTML)) {
                    previousHighlight = range.startContainer.parentElement.parentElement;
                }

                if(previousHighlight) {
                    menu.addItem(item => {
                        item
                            .setIcon("highlight-glyph")
                            .setTitle(t("highlight_remove"))
                            .onClick(async () => {
                                const replacement = contentEl.createSpan();
                                replacement.innerHTML = previousHighlight.innerHTML;
                                previousHighlight.replaceWith(replacement);
                                this.item.highlights.remove(previousHighlight.innerHTML);

                                const feedContents = this.plugin.settings.items;
                                await this.plugin.writeFeedContent(() => {
                                    return feedContents;
                                });
                            });
                    });
                }else if(!this.item.highlights.includes(selected) && selected.length > 0) {
                    menu.addItem(item => {
                        item
                            .setIcon("highlight-glyph")
                            .setTitle(t("highlight"))
                            .onClick(async () => {
                                const newNode = contentEl.createEl("mark");
                                newNode.innerHTML = selected;
                                range.deleteContents();
                                range.insertNode(newNode);
                                this.item.highlights.push(selected);

                                const feedContents = this.plugin.settings.items;
                                await this.plugin.writeFeedContent(() => {
                                    return feedContents;
                                });

                                //cleaning up twice to remove nested elements
                                this.removeDanglingElements(contentEl);
                                this.removeDanglingElements(contentEl);
                            });
                    });
                }

                if(selected.length > 0) {
                    menu
                        .addItem(item => {
                            item
                                .setIcon("documents")
                                .setTitle(t("copy_to_clipboard"))
                                .onClick(async () => {
                                    await copy(selection.toString());
                                });
                        });
                    //@ts-ignore
                    if (this.app.plugins.plugins["obsidian-tts"]) {
                        menu.addItem(item => {
                            item
                                .setIcon("headphones")
                                .setTitle(t("read_article_tts"))
                                .onClick(() => {
                                    //@ts-ignore
                                    const tts = this.app.plugins.plugins["obsidian-tts"].ttsService;
                                    tts.say("", selection.toString());
                                });
                        });
                    }
                }

                menu.showAtMouseEvent(event);
            });
        }
    }
Example #5
Source File: ItemModal.ts    From obsidian-rss with GNU General Public License v3.0 4 votes vote down vote up
constructor(plugin: RssReaderPlugin, item: RssFeedItem, items: RssFeedItem[], save = true) {
        super(plugin.app);
        this.plugin = plugin;
        this.items = items;
        this.item = item;
        this.save = save;


        if (this.save) {
            this.item.read = true;

            const feedContents = this.plugin.settings.items;
            this.plugin.writeFeedContent(() => {
                return feedContents;
            });

            if (!this.plugin.settings) {
                return;
            }

            if (this.plugin.settings.hotkeys.read) {
                this.scope.register([], this.plugin.settings.hotkeys.read, () => {
                    this.markAsRead();
                });
            }

            if (this.plugin.settings.hotkeys.favorite) {
                this.scope.register([], this.plugin.settings.hotkeys.favorite, () => {
                    this.markAsFavorite();
                });
            }

            if (this.plugin.settings.hotkeys.tags) {
                this.scope.register([], this.plugin.settings.hotkeys.tags, () => {
                    Action.TAGS.processor(this.plugin, this.item);
                });
            }
        }

        if (this.plugin.settings.hotkeys.create) {
            this.scope.register([], this.plugin.settings.hotkeys.create, () => {
                Action.CREATE_NOTE.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.paste) {
            this.scope.register([], this.plugin.settings.hotkeys.paste, () => {
                Action.PASTE.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.copy) {
            this.scope.register([], this.plugin.settings.hotkeys.copy, () => {
                Action.COPY.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.open) {
            this.scope.register([], this.plugin.settings.hotkeys.open, () => {
                Action.OPEN.processor(this.plugin, this.item);
            });
        }

        if (this.plugin.settings.hotkeys.next) {
            this.scope.register([], this.plugin.settings.hotkeys.next, () => {
                this.next();
            });
        }
        if (this.plugin.settings.hotkeys.previous) {
            this.scope.register([], this.plugin.settings.hotkeys.previous, () => {
                this.previous();
            });
        }


        //@ts-ignore
        if (this.app.plugins.plugins["obsidian-tts"] && this.plugin.settings.hotkeys.tts) {
            this.scope.register([], this.plugin.settings.hotkeys.tts, () => {
                //@ts-ignore
                const tts = this.app.plugins.plugins["obsidian-tts"].ttsService;
                if (tts.isSpeaking()) {
                    if (tts.isPaused()) {
                        tts.resume();
                    } else {
                        tts.pause();
                    }
                    return;
                }
                const content = htmlToMarkdown(this.item.content);
                tts.say(this.item.title, content, this.item.language);
            });
        }
    }