@instructure/canvas-rce
Version:
A component wrapping Canvas's usage of Tinymce
102 lines (100 loc) • 2.91 kB
JavaScript
/*
* Copyright (C) 2023 - 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 <http://www.gnu.org/licenses/>.
*/
// based on https://github.com/thlorenz/parse-link-header/blob/master/index.js (MIT)
function parseQueryParams(linkUrl) {
const queryParams = {};
const urlParts = linkUrl.split('?');
if (urlParts.length > 1) {
urlParts[1].split('&').forEach(param => {
const [key, value] = param.split('=');
queryParams[key] = decodeURIComponent(value);
});
}
return queryParams;
}
function parseLink(link) {
try {
const linkMatch = link.match(/<([^>]*)>\s*(.*)/);
if (!linkMatch) {
return null;
}
const [, linkUrl, partsString] = linkMatch;
const parts = partsString.split(';').map(part => part.trim());
const info = {
url: linkUrl
};
parts.forEach(part => {
const partMatch = part.match(/(.+)\s*=\s*"?([^"]+)"?/);
if (partMatch) {
const [, key, value] = partMatch;
info[key.trim()] = value.trim();
}
});
return {
...parseQueryParams(linkUrl),
...info
};
} catch (e) {
return null;
}
}
function hasRel(x) {
return x !== null && 'rel' in x;
}
function intoRels(acc, x) {
x.rel.split(/\s+/).forEach(rel => {
const {
...rest
} = x;
switch (rel) {
case 'first':
acc.first = rest;
break;
case 'prev':
acc.prev = rest;
break;
case 'current':
acc.current = rest;
break;
case 'next':
acc.next = rest;
break;
case 'last':
acc.last = rest;
break;
}
});
return acc;
}
const PARSE_LINK_HEADER_MAXLEN = 4000;
const PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED = process.env.PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED != null;
function checkHeader(linkHeader) {
if (!linkHeader) return false;
if (linkHeader.length > PARSE_LINK_HEADER_MAXLEN) {
if (PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED) {
throw new Error(`Input string too long, it should be under ${PARSE_LINK_HEADER_MAXLEN} characters.`);
} else {
return false;
}
}
return true;
}
export default function parseLinkHeader(linkHeader) {
if (!checkHeader(linkHeader)) return null;
return linkHeader.split(/,\s*(?=<)/).map(parseLink).filter(hasRel).reduce(intoRels, {});
}