Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/codemirror/extensions/insert-link.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import * as CodeMirror from "codemirror";6import { Button, Checkbox, Form, Input, Modal } from "antd";78import { show_react_modal } from "../../misc";9import { Icon } from "../../components";10import { alert_message } from "../../alerts";1112export interface Options {13url: string;14displayed_text: string;15target: boolean; // if true, opens in a new window16title: string;17}1819function insert_link(mode: string, opts: Options): string {20for (const k in opts) {21if (typeof opts[k] == "string") {22opts[k] = opts[k].trim();23}24}25let { url, displayed_text, target, title } = opts;26let s: string = "";27if (mode === "md") {28// [Python](http://www.python.org/ "the python website")29if (title.length > 0) {30title = ` \"${title}\"`;31}32if (displayed_text.length > 0) {33s = `[${displayed_text}](${url}${title})`;34} else {35s = url;36}37} else if (mode === "rst") {38// `Python <http://www.python.org/#target>`_39if (displayed_text.length == 0) {40displayed_text = url;41}42s = `\`${displayed_text} <${url}>\`_`;43} else if (mode === "tex") {44// \url{http://www.wikibooks.org}45// \href{http://www.wikibooks.org}{Wikibooks home}46url = url.replace(/#/g, "\\#"); // should end up as \#47url = url.replace(/&/g, "\\&"); // ... \&48url = url.replace(/_/g, "\\_"); // ... \_49if (displayed_text.length > 0) {50s = `\\href{${url}}{${displayed_text}}`;51} else {52s = `\\url{${url}}`;53}54} else if (mode === "mediawiki") {55// https://www.mediawiki.org/wiki/Help:Links56// [http://mediawiki.org MediaWiki]57if (displayed_text.length > 0) {58displayed_text = ` ${displayed_text}`;59}60s = `[${url}${displayed_text}]`;61} else {62// if (mode == "html") ## HTML default fallback63const target1 = target ? " target='_blank' rel='noopener'" : "";6465if (title.length > 0) {66title = ` title='${title}'`;67}6869if (displayed_text.length == 0) {70displayed_text = url;71}72s = `<a href='${url}'${title}${target1}>${displayed_text}</a>`;73}74return s;75}7677export async function get_insert_link_opts_from_user(78default_display: string,79show_target: boolean80): Promise<undefined | Options> {81return await show_react_modal((cb) => {82return (83<Modal84title={85<h3>86<Icon name="link" /> Insert Link87</h3>88}89open90footer={<Button onClick={() => cb()}>Cancel</Button>}91onCancel={() => cb()}92>93<Form94labelCol={{ span: 8 }}95wrapperCol={{ span: 16 }}96name="options"97initialValues={{98url: "",99displayed_text: default_display,100target: false,101title: "",102}}103onFinish={(values) => {104// empty displayed text really doesn't work well (since can't see the link).105if (!values.displayed_text) values.displayed_text = values.title;106if (!values.displayed_text) values.displayed_text = values.url;107if (!values.displayed_text) values.displayed_text = "link";108cb(undefined, values);109}}110onFinishFailed={(err) => cb(err)}111>112<Form.Item113label="URL"114name="url"115rules={[116{117required: true,118message: "You must enter the URL of the link.",119},120]}121>122<Input placeholder="URL..." />123</Form.Item>124<Form.Item label="Displayed text" name="displayed_text">125<Input placeholder="Displayed text..." />126</Form.Item>127<Form.Item label="Title" name="title">128<Input placeholder="Title..." />129</Form.Item>130{show_target && (131<Form.Item label="Target" name="target" valuePropName="checked">132<Checkbox>Open in new window</Checkbox>133</Form.Item>134)}135<Form.Item wrapperCol={{ offset: 8, span: 16 }}>136<Button type="primary" htmlType="submit">137Submit138</Button>139</Form.Item>140</Form>141</Modal>142);143});144}145146CodeMirror.defineExtension("insert_link", async function () {147// @ts-ignore148const cm = this;149150const default_display = cm.getSelection();151const mode = cm.get_edit_mode();152153// HTML target option not supported for md, rst, and tex, which have154// their own notation for links (and always open externally).155const show_target = ["md", "rst", "tex"].indexOf(mode) == -1;156157let opts: Options | undefined = undefined;158try {159opts = await get_insert_link_opts_from_user(default_display, show_target);160} catch (err) {161alert_message({ type: "error", message: err.errorFields[0]?.errors });162return;163}164165if (opts == null) {166return; // user canceled167}168169const selections = cm.listSelections();170selections.reverse();171for (const sel of selections) {172const link = insert_link(cm.get_edit_mode(sel.head), opts);173if (sel.empty()) {174cm.replaceRange(link, sel.head);175} else {176cm.replaceRange(link, sel.from(), sel.to());177}178}179});180181182