Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 11
Línea 1... Línea -...
1
{"version":3,"file":"noautolink.min.js","sources":["../src/noautolink.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Helper for Tiny noautolink plugin.\n *\n * @module      tiny_noautolink/noautolink\n * @copyright   2023 Meirza <meirza.arson@moodle.com>\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\n\nconst noautolinkClassName = 'nolink';\nconst noautolinkTagHTML = 'span';\nconst notificationTimeout = 2000;\n\n/**\n * Handle action.\n *\n * @param {TinyMCE} editor\n * @param {object} messages\n */\nexport const handleAction = (editor, messages) => {\n    const toggleState = isInAnchor(editor, editor.selection.getNode());\n    const urlString = getSelectedContent(editor);\n    if (!toggleState && urlString !== '') {\n        setNoAutoLink(editor, messages, urlString);\n    } else if (toggleState) {\n        unsetNoAutoLink(editor, messages, urlString);\n    } else {\n        editor.notificationManager.open({text: messages.infoEmptySelection, type: 'info', timeout: notificationTimeout});\n    }\n};\n\n/**\n * Display notification feedback when applying the noautolink to the selected text.\n *\n * @param {TinyMCE} editor\n * @param {object} messages\n * @param {String} urlString\n */\nconst setNoAutoLink = (editor, messages, urlString) => {\n    // Check whether the string is a URL. Otherwise, show an error notification.\n    if (isValidUrl(urlString)) {\n        const pendingPromise = new Pending('tiny_noautolink/setNoautolink');\n        // Applying the auto-link prevention.\n        setNoautolinkOnSelection(editor, urlString)\n        .catch(error => {\n            editor.notificationManager.open({text: error, type: 'error', timeout: notificationTimeout});\n        })\n        .finally(() => {\n            editor.notificationManager.open({text: messages.infoAddSuccess, type: 'success', timeout: notificationTimeout});\n            pendingPromise.resolve();\n        });\n    } else {\n        editor.notificationManager.open({text: messages.errorInvalidURL, type: 'error', timeout: notificationTimeout});\n    }\n};\n\n/**\n * Display notification feedback when removing the noautolink to the selected text.\n *\n * @param {TinyMCE} editor\n * @param {object} messages\n */\nconst unsetNoAutoLink = (editor, messages) => {\n    const nodeString = editor.selection.getNode().outerHTML.trim();\n    // Convert HTML string to DOM element to get nolink class.\n    const wrapper = document.createElement('div');\n    wrapper.innerHTML = nodeString;\n    const tempElement = wrapper.firstChild;\n    if (tempElement.classList.contains('nolink')) {\n        const pendingPromise = new Pending('tiny_noautolink/setNoautolink');\n        // Removing the auto-link prevention.\n        unsetNoautolinkOnSelection(editor, nodeString)\n        .catch(error => {\n            editor.notificationManager.open({text: error, type: 'error', timeout: notificationTimeout});\n            pendingPromise.reject(error); // Handle the error as needed.\n        })\n        .finally(() => {\n            editor.notificationManager.open({text: messages.infoRemoveSuccess, type: 'success', timeout: notificationTimeout});\n            pendingPromise.resolve();\n        });\n    }\n};\n\n/**\n * Return the full string based on the position of the cursor within the string.\n *\n * @param {TinyMCE} editor\n * @returns {String}\n */\nconst getSelectedContent = (editor) => {\n    const selection = editor.selection; // Get the selection object.\n    let content = selection.getContent({format: 'text'}).trim();\n    if (content == '') {\n        const range = selection.getRng(); // Get the range object.\n\n        // Check if the cursor is within a text node.\n        if (range.startContainer.nodeType === Node.TEXT_NODE) {\n            const textContent = range.startContainer.textContent;\n            const cursorOffset = range.startOffset;\n\n            // Find the word boundaries around the cursor.\n            let wordStart = cursorOffset;\n            while (wordStart > 0 && /\\S/.test(textContent[wordStart - 1])) {\n                wordStart--;\n            }\n\n            let wordEnd = cursorOffset;\n            while (wordEnd < textContent.length && /\\S/.test(textContent[wordEnd])) {\n                wordEnd++;\n            }\n\n            // Set the selection range to the word.\n            selection.setRng({\n                startContainer: range.startContainer,\n                startOffset: wordStart,\n                endContainer: range.startContainer,\n                endOffset: wordEnd,\n            });\n            content = selection.getContent({format: 'text'}).trim();\n        }\n    }\n    return content;\n};\n\n/**\n * Wrap the selection with the nolink class.\n *\n * @param {TinyMCE} editor\n * @param {String} url URL the link will point to.\n */\nconst setNoautolinkOnSelection = async(editor, url) => {\n    const newContent = `<${noautolinkTagHTML} class=\"${noautolinkClassName}\">${url}</${noautolinkTagHTML}>`;\n    editor.selection.setContent(newContent);\n\n    // Select the new content.\n    const currentNode = editor.selection.getNode();\n    const currentDOM = editor.dom.select(`${noautolinkTagHTML}.${noautolinkClassName}`, currentNode);\n    currentDOM.forEach(function(value, index) {\n        if (value.outerHTML == newContent) {\n            editor.selection.select(currentDOM[index]);\n            return;\n        }\n    });\n};\n\n/**\n * Remove the nolink on the selection.\n *\n * @param {TinyMCE} editor\n * @param {String} url URL the link will point to.\n */\nconst unsetNoautolinkOnSelection = async(editor, url) => {\n    const regex = new RegExp(`</?${noautolinkTagHTML}[^>]*>`, \"g\");\n    url = url.replace(regex, \"\");\n    const currentSpan = editor.dom.getParent(editor.selection.getNode(), noautolinkTagHTML);\n    currentSpan.outerHTML = url;\n};\n\n/**\n * Check if given string is a valid URL.\n *\n * @param {String} urlString URL the link will point to.\n * @returns {boolean} True is valid, otherwise false.\n */\nconst isValidUrl = urlString => {\n    const urlPattern = new RegExp('^((http|https):\\\\/\\\\/|www\\\\.)' + // A URL must have one of these https/https/www.\n                                '((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|' + // Validate domain name.\n                                '((\\\\d{1,3}\\\\.){3}\\\\d{1,3}))' + // Validate ip (v4) address.\n                                '(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*' + // Validate port and path.\n                                '(\\\\?[;&a-z\\\\d%_.~+=-]*)?' + // Validate query string.\n                                '(\\\\#[-a-z\\\\d_]*)?$', 'i'); // Validate fragment locator.\n\n    return !!urlPattern.test(urlString);\n};\n\n/**\n * Get anchor element.\n *\n * @param {TinyMCE} editor\n * @param {Element} selectedElm\n * @returns {Element}\n */\nconst getAnchorElement = (editor, selectedElm) => {\n    selectedElm = selectedElm || editor.selection.getNode();\n    return editor.dom.getParent(selectedElm, `${noautolinkTagHTML}.${noautolinkClassName}`);\n};\n\n\n/**\n * Check the current selected element is an anchor or not.\n *\n * @param {TinyMCE} editor\n * @param {Element} selectedElm\n * @returns {boolean}\n */\nconst isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm) !== null;\n\n/**\n * Change state of button.\n *\n * @param {TinyMCE} editor\n * @param {function()} toggler\n * @returns {function()}\n */\nconst toggleState = (editor, toggler) => {\n    editor.on('NodeChange', toggler);\n    return () => editor.off('NodeChange', toggler);\n};\n\n/**\n * Change the active state of button.\n *\n * @param {TinyMCE} editor\n * @returns {function(*): function(): *}\n */\nexport const toggleActiveState = (editor) => (api) => {\n    const updateState = () => api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));\n    updateState();\n    return toggleState(editor, updateState);\n};"],"names":["editor","messages","toggleState","isInAnchor","selection","getNode","urlString","getSelectedContent","unsetNoAutoLink","notificationManager","open","text","infoEmptySelection","type","timeout","setNoAutoLink","isValidUrl","pendingPromise","Pending","setNoautolinkOnSelection","catch","error","finally","infoAddSuccess","resolve","errorInvalidURL","nodeString","outerHTML","trim","wrapper","document","createElement","innerHTML","firstChild","classList","contains","unsetNoautolinkOnSelection","reject","infoRemoveSuccess","content","getContent","format","range","getRng","startContainer","nodeType","Node","TEXT_NODE","textContent","cursorOffset","startOffset","wordStart","test","wordEnd","length","setRng","endContainer","endOffset","async","url","newContent","setContent","currentNode","currentDOM","dom","select","forEach","value","index","regex","RegExp","replace","getParent","selectedElm","getAnchorElement","api","updateState","setActive","mode","isReadOnly","toggler","on","off"],"mappings":"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"}
-
 
2
1
{"version":3,"file":"noautolink.min.js","sources":["../src/noautolink.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Helper for Tiny noautolink plugin.\n *\n * @module      tiny_noautolink/noautolink\n * @copyright   2023 Meirza <meirza.arson@moodle.com>\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Pending from 'core/pending';\n\nconst noautolinkClassName = 'nolink';\nconst noautolinkTagHTML = 'span';\nconst notificationTimeout = 2000;\n\n/**\n * Handle action.\n *\n * @param {TinyMCE} editor\n * @param {object} messages\n */\nexport const handleAction = (editor, messages) => {\n    const toggleState = isInAnchor(editor, editor.selection.getNode());\n    const urlString = getSelectedContent(editor);\n    if (!toggleState && urlString !== '') {\n        setNoAutoLink(editor, messages, urlString);\n    } else if (toggleState) {\n        unsetNoAutoLink(editor, messages, urlString);\n    } else {\n        editor.notificationManager.open({text: messages.infoEmptySelection, type: 'info', timeout: notificationTimeout});\n    }\n};\n\n/**\n * Display notification feedback when applying the noautolink to the selected text.\n *\n * @param {TinyMCE} editor\n * @param {object} messages\n * @param {String} urlString\n */\nconst setNoAutoLink = (editor, messages, urlString) => {\n    const pendingPromise = new Pending('tiny_noautolink/setNoautolink');\n    // Applying the auto-link prevention.\n    setNoautolinkOnSelection(editor, urlString)\n    .catch(error => {\n        editor.notificationManager.open({text: error, type: 'error', timeout: notificationTimeout});\n    })\n    .finally(() => {\n        editor.notificationManager.open({text: messages.infoAddSuccess, type: 'success', timeout: notificationTimeout});\n        pendingPromise.resolve();\n    });\n};\n\n/**\n * Display notification feedback when removing the noautolink to the selected text.\n *\n * @param {TinyMCE} editor\n * @param {object} messages\n */\nconst unsetNoAutoLink = (editor, messages) => {\n    const nodeString = editor.selection.getNode().outerHTML.trim();\n    // Convert HTML string to DOM element to get nolink class.\n    const wrapper = document.createElement('div');\n    wrapper.innerHTML = nodeString;\n    const tempElement = wrapper.firstChild;\n    if (tempElement.classList.contains('nolink')) {\n        const pendingPromise = new Pending('tiny_noautolink/setNoautolink');\n        // Removing the auto-link prevention.\n        unsetNoautolinkOnSelection(editor, nodeString)\n        .catch(error => {\n            editor.notificationManager.open({text: error, type: 'error', timeout: notificationTimeout});\n            pendingPromise.reject(error); // Handle the error as needed.\n        })\n        .finally(() => {\n            editor.notificationManager.open({text: messages.infoRemoveSuccess, type: 'success', timeout: notificationTimeout});\n            pendingPromise.resolve();\n        });\n    }\n};\n\n/**\n * Return the full string based on the position of the cursor within the string.\n *\n * @param {TinyMCE} editor\n * @returns {String}\n */\nconst getSelectedContent = (editor) => {\n    const selection = editor.selection; // Get the selection object.\n    let content = selection.getContent({format: 'text'}).trim();\n    if (content == '') {\n        const range = selection.getRng(); // Get the range object.\n\n        // Check if the cursor is within a text node.\n        if (range.startContainer.nodeType === Node.TEXT_NODE) {\n            const textContent = range.startContainer.textContent;\n            const cursorOffset = range.startOffset;\n\n            // Find the word boundaries around the cursor.\n            let wordStart = cursorOffset;\n            while (wordStart > 0 && /\\S/.test(textContent[wordStart - 1])) {\n                wordStart--;\n            }\n\n            let wordEnd = cursorOffset;\n            while (wordEnd < textContent.length && /\\S/.test(textContent[wordEnd])) {\n                wordEnd++;\n            }\n\n            // Set the selection range to the word.\n            selection.setRng({\n                startContainer: range.startContainer,\n                startOffset: wordStart,\n                endContainer: range.startContainer,\n                endOffset: wordEnd,\n            });\n            content = selection.getContent({format: 'text'}).trim();\n        }\n    }\n    return content;\n};\n\n/**\n * Wrap the selection with the nolink class.\n *\n * @param {TinyMCE} editor\n * @param {String} url URL the link will point to.\n */\nconst setNoautolinkOnSelection = async(editor, url) => {\n    const newContent = `<${noautolinkTagHTML} class=\"${noautolinkClassName}\">${url}</${noautolinkTagHTML}>`;\n    editor.selection.setContent(newContent);\n\n    // Select the new content.\n    const currentNode = editor.selection.getNode();\n    const currentDOM = editor.dom.select(`${noautolinkTagHTML}.${noautolinkClassName}`, currentNode);\n    currentDOM.forEach(function(value, index) {\n        if (value.outerHTML == newContent) {\n            editor.selection.select(currentDOM[index]);\n            return;\n        }\n    });\n};\n\n/**\n * Remove the nolink on the selection.\n *\n * @param {TinyMCE} editor\n * @param {String} url URL the link will point to.\n */\nconst unsetNoautolinkOnSelection = async(editor, url) => {\n    const regex = new RegExp(`</?${noautolinkTagHTML}[^>]*>`, \"g\");\n    url = url.replace(regex, \"\");\n    const currentSpan = editor.dom.getParent(editor.selection.getNode(), noautolinkTagHTML);\n    currentSpan.outerHTML = url;\n};\n\n/**\n * Get anchor element.\n *\n * @param {TinyMCE} editor\n * @param {Element} selectedElm\n * @returns {Element}\n */\nconst getAnchorElement = (editor, selectedElm) => {\n    selectedElm = selectedElm || editor.selection.getNode();\n    return editor.dom.getParent(selectedElm, `${noautolinkTagHTML}.${noautolinkClassName}`);\n};\n\n\n/**\n * Check the current selected element is an anchor or not.\n *\n * @param {TinyMCE} editor\n * @param {Element} selectedElm\n * @returns {boolean}\n */\nconst isInAnchor = (editor, selectedElm) => getAnchorElement(editor, selectedElm) !== null;\n\n/**\n * Change state of button.\n *\n * @param {TinyMCE} editor\n * @param {function()} toggler\n * @returns {function()}\n */\nconst toggleState = (editor, toggler) => {\n    editor.on('NodeChange', toggler);\n    return () => editor.off('NodeChange', toggler);\n};\n\n/**\n * Change the active state of button.\n *\n * @param {TinyMCE} editor\n * @returns {function(*): function(): *}\n */\nexport const toggleActiveState = (editor) => (api) => {\n    const updateState = () => api.setActive(!editor.mode.isReadOnly() && isInAnchor(editor, editor.selection.getNode()));\n    updateState();\n    return toggleState(editor, updateState);\n};\n"],"names":["editor","messages","toggleState","isInAnchor","selection","getNode","urlString","getSelectedContent","unsetNoAutoLink","notificationManager","open","text","infoEmptySelection","type","timeout","setNoAutoLink","pendingPromise","Pending","setNoautolinkOnSelection","catch","error","finally","infoAddSuccess","resolve","nodeString","outerHTML","trim","wrapper","document","createElement","innerHTML","firstChild","classList","contains","unsetNoautolinkOnSelection","reject","infoRemoveSuccess","content","getContent","format","range","getRng","startContainer","nodeType","Node","TEXT_NODE","textContent","cursorOffset","startOffset","wordStart","test","wordEnd","length","setRng","endContainer","endOffset","async","url","newContent","setContent","currentNode","currentDOM","dom","select","forEach","value","index","regex","RegExp","replace","getParent","selectedElm","getAnchorElement","api","updateState","setActive","mode","isReadOnly","toggler","on","off"],"mappings":"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"}
-
 
2
3
3