From 863eed6c6fab2ec232c59ffb02f4486f9a4fb418 Mon Sep 17 00:00:00 2001 From: Aaron Shafovaloff Date: Tue, 30 Jan 2024 11:34:13 -0700 Subject: [PATCH] move RCE's makeAllExternalLinksExternalLinks Change-Id: I4471750caf9f13220471f3a5af7f983eaf5c3b08 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/339255 Tested-by: Service Cloud Jenkins Reviewed-by: Samuel Lee QA-Review: Aaron Shafovaloff Product-Review: Aaron Shafovaloff --- .../enhance_user_content.js | 64 +-------------- .../enhance-user-content/external_links.js | 81 +++++++++++++++++++ .../enhanced-user-content/jquery/index.js | 3 +- 3 files changed, 86 insertions(+), 62 deletions(-) create mode 100644 packages/canvas-rce/src/enhance-user-content/external_links.js diff --git a/packages/canvas-rce/src/enhance-user-content/enhance_user_content.js b/packages/canvas-rce/src/enhance-user-content/enhance_user_content.js index 782fd3f4b43..1bcfca5a699 100644 --- a/packages/canvas-rce/src/enhance-user-content/enhance_user_content.js +++ b/packages/canvas-rce/src/enhance-user-content/enhance_user_content.js @@ -16,17 +16,17 @@ * with this program. If not, see . */ -import {IconDownloadLine, IconExternalLinkLine} from '@instructure/ui-icons/es/svg' +import {IconDownloadLine} from '@instructure/ui-icons/es/svg' import formatMessage from '../format-message' import {closest, getData, hide, insertAfter, setData, show} from './jqueryish_funcs' -import {getTld, isExternalLink, showFilePreview, youTubeID} from './instructure_helper' +import {isExternalLink, showFilePreview, youTubeID} from './instructure_helper' import mediaCommentThumbnail from './media_comment_thumbnail' import {addParentFrameContextToUrl} from '../rce/plugins/instructure_rce_external_tools/util/addParentFrameContextToUrl' import {MathJaxDirective, Mathml} from './mathml' +import {makeExternalLinkIcon} from './external_links' // in jest the es directory doesn't exist so stub the undefined svg const IconDownloadSVG = IconDownloadLine?.src || '' -const IconExternalLinkSVG = IconExternalLinkLine?.src || '' function makeDownloadButton(download_url, filename) { const a = document.createElement('a') @@ -53,28 +53,6 @@ function makeDownloadButton(download_url, filename) { return a } -function makeExternalLinkIcon(forLink) { - const dir = (forLink && window.getComputedStyle(forLink).direction) || 'ltr' - const $icon = document.createElement('span') - $icon.setAttribute('class', 'external_link_icon') - const style = `margin-inline-start: 5px; display: inline-block; text-indent: initial; ${ - dir === 'rtl' ? 'transform:scale(-1, 1)' : '' - }` - $icon.setAttribute('style', style) - $icon.setAttribute('role', 'presentation') - $icon.innerHTML = IconExternalLinkSVG - $icon.firstChild.setAttribute( - 'style', - 'width:1em; height:1em; vertical-align:middle; fill:currentColor' - ) - - const srspan = document.createElement('span') - srspan.setAttribute('class', 'screenreader-only') - srspan.textContent = formatMessage('Links to an external site.') - $icon.appendChild(srspan) - return $icon -} - function handleYoutubeLink($link) { const href = $link.getAttribute('href') const id = youTubeID(href || '') @@ -400,39 +378,3 @@ export function enhanceUserContent(container = document, opts = {}) { frame.src = src }) } - -export function makeAllExternalLinksExternalLinks() { - // in 100ms (to give time for everything else to load), find all the external links and add give them - // the external link look and behavior (force them to open in a new tab) - setTimeout(function () { - const content = document.getElementById('content') - if (!content) return - const tld = getTld(window.location.hostname) - const links = content.querySelectorAll(`a[href*="//"]:not([href*="${tld}"])`) // technique for finding "external" links copied from https://davidwalsh.name/external-links-css - for (let i = 0; i < links.length; i++) { - const $link = links[i] - // don't mess with the ones that were already processed in enhanceUserContent - if ($link.classList.contains('external')) continue - if ($link.matches('.open_in_a_new_tab')) continue - if ($link.querySelectorAll('img').length > 0) continue - if ($link.matches('.not_external')) continue - if ($link.matches('.exclude_external_icon')) continue - // we have some pre-instui buttons that are styled links - if ($link.classList.contains('btn')) continue - const $linkToReplace = $link - if ($linkToReplace) { - const $linkIndicator = makeExternalLinkIcon() - $linkToReplace.classList.add('external') - $linkToReplace.querySelectorAll('span.ui-icon-extlink').forEach(c => c.remove) - $linkToReplace.setAttribute('target', '_blank') - $linkToReplace.setAttribute('rel', 'noreferrer noopener') - const $linkSpan = document.createElement('span') - const $linkText = $linkToReplace.innerHTML - $linkSpan.innerHTML = $linkText - while ($linkToReplace.firstChild) $linkToReplace.removeChild($linkToReplace.firstChild) - $linkToReplace.appendChild($linkSpan) - $linkToReplace.appendChild($linkIndicator) - } - } - }, 100) -} diff --git a/packages/canvas-rce/src/enhance-user-content/external_links.js b/packages/canvas-rce/src/enhance-user-content/external_links.js new file mode 100644 index 00000000000..01a98b212c3 --- /dev/null +++ b/packages/canvas-rce/src/enhance-user-content/external_links.js @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 - present Instructure, Inc. + * + * This file is part of Canvas. + * + * Canvas is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3 of the License. + * + * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + */ + +import {IconExternalLinkLine} from '@instructure/ui-icons/es/svg' +import {getTld} from './instructure_helper' +import formatMessage from '../format-message' + +const IconExternalLinkSVG = IconExternalLinkLine?.src || '' + +export function makeExternalLinkIcon(forLink) { + const dir = (forLink && window.getComputedStyle(forLink).direction) || 'ltr' + const $icon = document.createElement('span') + $icon.setAttribute('class', 'external_link_icon') + const style = `margin-inline-start: 5px; display: inline-block; text-indent: initial; ${ + dir === 'rtl' ? 'transform:scale(-1, 1)' : '' + }` + $icon.setAttribute('style', style) + $icon.setAttribute('role', 'presentation') + $icon.innerHTML = IconExternalLinkSVG + $icon.firstChild.setAttribute( + 'style', + 'width:1em; height:1em; vertical-align:middle; fill:currentColor' + ) + + const srspan = document.createElement('span') + srspan.setAttribute('class', 'screenreader-only') + srspan.textContent = formatMessage('Links to an external site.') + $icon.appendChild(srspan) + return $icon +} + +export function makeAllExternalLinksExternalLinks() { + // in 100ms (to give time for everything else to load), find all the external links and add give them + // the external link look and behavior (force them to open in a new tab) + setTimeout(function () { + const content = document.getElementById('content') + if (!content) return + const tld = getTld(window.location.hostname) + const links = content.querySelectorAll(`a[href*="//"]:not([href*="${tld}"])`) // technique for finding "external" links copied from https://davidwalsh.name/external-links-css + for (let i = 0; i < links.length; i++) { + const $link = links[i] + // don't mess with the ones that were already processed in enhanceUserContent + if ($link.classList.contains('external')) continue + if ($link.matches('.open_in_a_new_tab')) continue + if ($link.querySelectorAll('img').length > 0) continue + if ($link.matches('.not_external')) continue + if ($link.matches('.exclude_external_icon')) continue + // we have some pre-instui buttons that are styled links + if ($link.classList.contains('btn')) continue + const $linkToReplace = $link + if ($linkToReplace) { + const $linkIndicator = makeExternalLinkIcon() + $linkToReplace.classList.add('external') + $linkToReplace.querySelectorAll('span.ui-icon-extlink').forEach(c => c.remove) + $linkToReplace.setAttribute('target', '_blank') + $linkToReplace.setAttribute('rel', 'noreferrer noopener') + const $linkSpan = document.createElement('span') + const $linkText = $linkToReplace.innerHTML + $linkSpan.innerHTML = $linkText + while ($linkToReplace.firstChild) $linkToReplace.removeChild($linkToReplace.firstChild) + $linkToReplace.appendChild($linkSpan) + $linkToReplace.appendChild($linkIndicator) + } + } + }, 100) +} diff --git a/ui/shared/enhanced-user-content/jquery/index.js b/ui/shared/enhanced-user-content/jquery/index.js index efcd086268b..902a6492e64 100644 --- a/ui/shared/enhanced-user-content/jquery/index.js +++ b/ui/shared/enhanced-user-content/jquery/index.js @@ -22,7 +22,8 @@ import {uniqueId} from 'lodash' import htmlEscape from '@instructure/html-escape' import {showFlashAlert} from '@canvas/alerts/react/FlashAlert' import RichContentEditor from '@canvas/rce/RichContentEditor' -import {enhanceUserContent, makeAllExternalLinksExternalLinks} from '@instructure/canvas-rce' +import {enhanceUserContent} from '@instructure/canvas-rce' +import {makeAllExternalLinksExternalLinks} from '@instructure/canvas-rce/es/enhance-user-content/external_links' import './instructure_helper' import 'jqueryui/draggable' import '@canvas/jquery/jquery.ajaxJSON'