mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-30 12:21:05 +00:00
Tighten Project Page Description Rendering and Keep More Formatting
Link and image URLs in descriptions must now start with an http or https scheme as written and parse as such with the URL parser. Preview images are built as DOM nodes like the file list, and accessory names show their full text as a tooltip. Descriptions keep more plain formatting: del, ins, figure, figcaption, dl, dt, dd, caption, q, abbr, kbd and wbr, plus alt, title, width and height on images, colspan and rowspan on table cells and start on ordered lists. Numeric attributes must be plain integers. Embedded YouTube players become a link to the video.
This commit is contained in:
@@ -281,16 +281,15 @@ function ShowModelInfo( pModel )
|
|||||||
|
|
||||||
if(TotalPreview>0)
|
if(TotalPreview>0)
|
||||||
{
|
{
|
||||||
let htmlPreview='';
|
$('#ModelPreviewList').empty();
|
||||||
for(let pn=0;pn<TotalPreview;pn++)
|
for(let pn=0;pn<TotalPreview;pn++)
|
||||||
{
|
{
|
||||||
//let FTmpPath=decodeURIComponent(ModelPreviewList[pn]);
|
//let FTmpPath=decodeURIComponent(ModelPreviewList[pn]);
|
||||||
let FTmpPath=ModelPreviewList[pn]['filepath'];
|
let FTmpPath=ModelPreviewList[pn]['filepath'];
|
||||||
|
|
||||||
htmlPreview+='<div class="swiper-slide"><img class="Model_PrevImg" src="'+FTmpPath+'" /></div>';
|
$('#ModelPreviewList').append( $('<div class="swiper-slide"></div>').append( $('<img class="Model_PrevImg" />').attr('src',FTmpPath) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#ModelPreviewList').html(htmlPreview);
|
|
||||||
$('#Model_Preview_Image').viewer({
|
$('#Model_Preview_Image').viewer({
|
||||||
title: false,
|
title: false,
|
||||||
fullsreen: false,
|
fullsreen: false,
|
||||||
@@ -462,7 +461,7 @@ function ConstructFileHtml( ID, pItem )
|
|||||||
|
|
||||||
let pFileItem=$('<div class="FileItem"></div>');
|
let pFileItem=$('<div class="FileItem"></div>');
|
||||||
pFileItem.append( $('<div></div>').addClass(strClass).append(pIconImg) );
|
pFileItem.append( $('<div></div>').addClass(strClass).append(pIconImg) );
|
||||||
pFileItem.append( $('<div class="FileText"></div>').append( $('<div class="FileName"></div>').text(tName) ) );
|
pFileItem.append( $('<div class="FileText"></div>').append( $('<div class="FileName"></div>').text(tName).attr('title',tName) ) );
|
||||||
pFileItem.append( pMenu );
|
pFileItem.append( pMenu );
|
||||||
pBoard.append( pFileItem );
|
pBoard.append( pFileItem );
|
||||||
}
|
}
|
||||||
@@ -474,15 +473,39 @@ function ConstructFileHtml( ID, pItem )
|
|||||||
|
|
||||||
// Descriptions are untrusted 3MF metadata that may carry rich-text HTML (e.g. from MakerWorld).
|
// Descriptions are untrusted 3MF metadata that may carry rich-text HTML (e.g. from MakerWorld).
|
||||||
// Rebuild them from an inert parse, keeping only plain formatting tags and http(s) links and images.
|
// Rebuild them from an inert parse, keeping only plain formatting tags and http(s) links and images.
|
||||||
var DescAllowedTags=['P','BR','B','STRONG','I','EM','U','S','STRIKE','SUB','SUP','SMALL','MARK',
|
var DescAllowedTags=['P','BR','B','STRONG','I','EM','U','S','STRIKE','DEL','INS','SUB','SUP','SMALL','MARK',
|
||||||
'H1','H2','H3','H4','H5','H6','UL','OL','LI','BLOCKQUOTE','PRE','CODE','HR','SPAN','DIV',
|
'Q','ABBR','KBD','WBR','H1','H2','H3','H4','H5','H6','UL','OL','LI','DL','DT','DD','BLOCKQUOTE','PRE','CODE',
|
||||||
'TABLE','THEAD','TBODY','TFOOT','TR','TH','TD','A','IMG'];
|
'HR','SPAN','DIV','FIGURE','FIGCAPTION','TABLE','CAPTION','THEAD','TBODY','TFOOT','TR','TH','TD','A','IMG'];
|
||||||
|
// Plain attributes kept per tag; the numeric ones must be plain non-negative integers.
|
||||||
|
var DescAllowedAttrs={'IMG':['alt','title','width','height'],'TD':['colspan','rowspan'],'TH':['colspan','rowspan'],'OL':['start']};
|
||||||
|
var DescNumericAttrs=['width','height','colspan','rowspan','start'];
|
||||||
// Dropped together with their content; any other unknown tag is unwrapped to its children.
|
// Dropped together with their content; any other unknown tag is unwrapped to its children.
|
||||||
var DescDroppedTags=['SCRIPT','STYLE','TEMPLATE','NOSCRIPT','TEXTAREA','TITLE','IFRAME','FRAME','OBJECT','EMBED','SVG','MATH'];
|
var DescDroppedTags=['SCRIPT','STYLE','TEMPLATE','NOSCRIPT','TEXTAREA','TITLE','IFRAME','FRAME','OBJECT','EMBED','SVG','MATH'];
|
||||||
|
|
||||||
function IsHttpUrl( strUrl )
|
function IsHttpUrl( strUrl )
|
||||||
{
|
{
|
||||||
return /^\s*https?:\/\//i.test(strUrl||'');
|
// The scheme must be written as is, so nothing the URL parser would strip can precede or split it.
|
||||||
|
if( typeof strUrl!='string' || !/^https?:/i.test(strUrl) )
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
let sProtocol=new URL(strUrl).protocol;
|
||||||
|
return sProtocol=='http:' || sProtocol=='https:';
|
||||||
|
}
|
||||||
|
catch(e)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embedded YouTube players become a plain link to the video.
|
||||||
|
function GetYouTubeEmbedUrl( pNode )
|
||||||
|
{
|
||||||
|
let sSrc=pNode.getAttribute('src');
|
||||||
|
if( !IsHttpUrl(sSrc) )
|
||||||
|
return null;
|
||||||
|
let pUrl=new URL(sSrc);
|
||||||
|
return ( pUrl.origin=='https://www.youtube.com' && pUrl.pathname.indexOf('/embed/')==0 ) ? pUrl.href : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CopyDescNodes( pSrc, pDst )
|
function CopyDescNodes( pSrc, pDst )
|
||||||
@@ -498,6 +521,18 @@ function CopyDescNodes( pSrc, pDst )
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
let sTag=pNode.nodeName.toUpperCase();
|
let sTag=pNode.nodeName.toUpperCase();
|
||||||
|
if( sTag=='IFRAME' )
|
||||||
|
{
|
||||||
|
let sVideoUrl=GetYouTubeEmbedUrl(pNode);
|
||||||
|
if( sVideoUrl!=null )
|
||||||
|
{
|
||||||
|
let pLink=document.createElement('A');
|
||||||
|
pLink.setAttribute('href',sVideoUrl);
|
||||||
|
pLink.textContent=sVideoUrl;
|
||||||
|
pDst.appendChild(pLink);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if( $.inArray(sTag,DescDroppedTags)>=0 )
|
if( $.inArray(sTag,DescDroppedTags)>=0 )
|
||||||
continue;
|
continue;
|
||||||
if( $.inArray(sTag,DescAllowedTags)<0 )
|
if( $.inArray(sTag,DescAllowedTags)<0 )
|
||||||
@@ -515,6 +550,11 @@ function CopyDescNodes( pSrc, pDst )
|
|||||||
continue;
|
continue;
|
||||||
pElem.setAttribute('src',pNode.getAttribute('src'));
|
pElem.setAttribute('src',pNode.getAttribute('src'));
|
||||||
}
|
}
|
||||||
|
$.each( DescAllowedAttrs[sTag]||[], function(i,sAttr){
|
||||||
|
let sValue=pNode.getAttribute(sAttr);
|
||||||
|
if( sValue!=null && ( $.inArray(sAttr,DescNumericAttrs)<0 || /^\d+$/.test(sValue) ) )
|
||||||
|
pElem.setAttribute(sAttr,sValue);
|
||||||
|
});
|
||||||
CopyDescNodes(pNode,pElem);
|
CopyDescNodes(pNode,pElem);
|
||||||
pDst.appendChild(pElem);
|
pDst.appendChild(pElem);
|
||||||
}
|
}
|
||||||
@@ -553,15 +593,14 @@ function ShowProfilelInfo( pProfile )
|
|||||||
|
|
||||||
if(TotalPreview>0)
|
if(TotalPreview>0)
|
||||||
{
|
{
|
||||||
let htmlPreview='';
|
$('#ProfilePreviewList').empty();
|
||||||
for(let pn=0;pn<TotalPreview;pn++)
|
for(let pn=0;pn<TotalPreview;pn++)
|
||||||
{
|
{
|
||||||
let FTmpPath=ProfilePreviewList[pn]['filepath'];
|
let FTmpPath=ProfilePreviewList[pn]['filepath'];
|
||||||
|
|
||||||
htmlPreview+='<div class="swiper-slide"><img class="Model_PrevImg" src="'+FTmpPath+'" /></div>';
|
$('#ProfilePreviewList').append( $('<div class="swiper-slide"></div>').append( $('<img class="Model_PrevImg" />').attr('src',FTmpPath) ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#ProfilePreviewList').html(htmlPreview);
|
|
||||||
$('#Profile_Preview_Image').viewer({
|
$('#Profile_Preview_Image').viewer({
|
||||||
title: false,
|
title: false,
|
||||||
fullsreen: false,
|
fullsreen: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user