2021-01-29 20:45:11 +02:00
import utils , { ItemIdToUrlHandler } from '../utils' ;
2020-11-20 18:04:47 +02:00
const Entities = require ( 'html-entities' ) . AllHtmlEntities ;
const htmlentities = new Entities ( ) . encode ;
const urlUtils = require ( '../urlUtils.js' ) ;
const { getClassNameForMimeType } = require ( 'font-awesome-filetypes' ) ;
export interface Options {
title? : string ;
resources? : any ;
ResourceModel? : any ;
linkRenderingType? : number ;
plainResourceRendering? : boolean ;
postMessageSyntax? : string ;
enableLongPress? : boolean ;
2021-01-29 20:45:11 +02:00
itemIdToUrl? : ItemIdToUrlHandler ;
2020-11-20 18:04:47 +02:00
}
2020-12-09 23:30:51 +02:00
export interface LinkReplacementResult {
html : string ;
resource : any ;
resourceReady : boolean ;
resourceFullPath : string ;
}
export default function ( href : string , options : Options = null ) : LinkReplacementResult {
2020-11-20 18:04:47 +02:00
options = {
title : '' ,
resources : { } ,
ResourceModel : null ,
linkRenderingType : 1 ,
plainResourceRendering : false ,
postMessageSyntax : 'postMessage' ,
enableLongPress : false ,
. . . options ,
} ;
const resourceHrefInfo = urlUtils . parseResourceUrl ( href ) ;
const isResourceUrl = options . resources && ! ! resourceHrefInfo ;
let title = options . title ;
let resourceIdAttr = '' ;
let icon = '' ;
let hrefAttr = '#' ;
let mime = '' ;
let resourceId = '' ;
2020-12-09 23:30:51 +02:00
let resource = null ;
2020-11-20 18:04:47 +02:00
if ( isResourceUrl ) {
resourceId = resourceHrefInfo . itemId ;
const result = options . resources [ resourceId ] ;
const resourceStatus = utils . resourceStatus ( options . ResourceModel , result ) ;
if ( result && result . item ) {
if ( ! title ) title = result . item . title ;
mime = result . item . mime ;
2020-12-09 23:30:51 +02:00
resource = result . item ;
2020-11-20 18:04:47 +02:00
}
if ( result && resourceStatus !== 'ready' && ! options . plainResourceRendering ) {
const icon = utils . resourceStatusFile ( resourceStatus ) ;
2020-12-09 23:30:51 +02:00
return {
resourceReady : false ,
html : ` <a class="not-loaded-resource resource-status- ${ resourceStatus } " data-resource-id=" ${ resourceId } "> ` + ` <img src="data:image/svg+xml;utf8, ${ htmlentities ( icon ) } "/> ` ,
resource ,
resourceFullPath : null ,
} ;
2020-11-20 18:04:47 +02:00
} else {
2021-01-29 20:45:11 +02:00
// If we are rendering a note link, we'll get here too, so in that
// case "resourceId" would actually be the note ID.
2020-11-20 18:04:47 +02:00
href = ` joplin:// ${ resourceId } ` ;
if ( resourceHrefInfo . hash ) href += ` # ${ resourceHrefInfo . hash } ` ;
resourceIdAttr = ` data-resource-id=' ${ resourceId } ' ` ;
const iconType = mime ? getClassNameForMimeType ( mime ) : 'fa-joplin' ;
// Icons are defined in lib/renderers/noteStyle using inline svg
// The icons are taken from fork-awesome but use the font-awesome naming scheme in order
// to be more compatible with the getClass library
icon = ` <span class="resource-icon ${ iconType } "></span> ` ;
}
} else {
// If the link is a plain URL (as opposed to a resource link), set the href to the actual
// link. This allows the link to be exported too when exporting to PDF.
hrefAttr = href ;
}
// A single quote is valid in a URL but we don't want any because the
// href is already enclosed in single quotes.
// https://github.com/laurent22/joplin/issues/2030
href = href . replace ( /'/g , '%27' ) ;
let js = ` ${ options . postMessageSyntax } ( ${ JSON . stringify ( href ) } , { resourceId: ${ JSON . stringify ( resourceId ) } }); return false; ` ;
if ( options . enableLongPress && ! ! resourceId ) {
const onClick = ` ${ options . postMessageSyntax } ( ${ JSON . stringify ( href ) } ) ` ;
const onLongClick = ` ${ options . postMessageSyntax } ("longclick: ${ resourceId } ") ` ;
2021-01-03 13:23:17 +02:00
// if t is set when ontouchstart is called it means the user has already touched the screen once and this is the 2nd touch
// in this case we assume the user is trying to zoom and we don't want to show the menu
const touchStart = ` if (typeof(t) !== "undefined" && !!t) { clearTimeout(t); t = null; } else { t = setTimeout(() => { t = null; ${ onLongClick } ; }, ${ utils . longPressDelay } ); } ` ;
2020-11-20 18:04:47 +02:00
const cancel = 'if (!!t) {clearTimeout(t); t=null;' ;
const touchEnd = ` ${ cancel } ${ onClick } ;} ` ;
js = ` ontouchstart=' ${ touchStart } ' ontouchend=' ${ touchEnd } ' ontouchcancel=' ${ cancel } ontouchmove=" ${ cancel } ' ` ;
} else {
js = ` onclick=' ${ js } ' ` ;
}
if ( hrefAttr . indexOf ( '#' ) === 0 && href . indexOf ( '#' ) === 0 ) js = '' ; // If it's an internal anchor, don't add any JS since the webview is going to handle navigating to the right place
const attrHtml = [ ] ;
attrHtml . push ( 'data-from-md' ) ;
if ( resourceIdAttr ) attrHtml . push ( resourceIdAttr ) ;
if ( title ) attrHtml . push ( ` title=' ${ htmlentities ( title ) } ' ` ) ;
if ( mime ) attrHtml . push ( ` type=' ${ htmlentities ( mime ) } ' ` ) ;
2021-01-29 20:45:11 +02:00
let resourceFullPath = resource && options ? . ResourceModel ? . fullPath ? options . ResourceModel . fullPath ( resource ) : null ;
if ( resourceId && options . itemIdToUrl ) {
const url = options . itemIdToUrl ( resourceId ) ;
attrHtml . push ( ` href=' ${ htmlentities ( url ) } ' ` ) ;
resourceFullPath = url ;
} else if ( options . plainResourceRendering || options . linkRenderingType === 2 ) {
2020-11-20 18:04:47 +02:00
icon = '' ;
attrHtml . push ( ` href=' ${ htmlentities ( href ) } ' ` ) ;
} else {
2021-11-08 17:38:44 +02:00
attrHtml . push ( ` href=' ${ htmlentities ( hrefAttr ) } ' ` ) ;
2020-11-20 18:04:47 +02:00
if ( js ) attrHtml . push ( js ) ;
}
2020-12-09 23:30:51 +02:00
return {
html : ` <a ${ attrHtml . join ( ' ' ) } > ${ icon } ` ,
resourceReady : true ,
resource ,
2021-01-29 20:45:11 +02:00
resourceFullPath : resourceFullPath ,
2020-12-09 23:30:51 +02:00
} ;
2020-11-20 18:04:47 +02:00
}