2013-10-12 16:05:13 +00:00
/ * \
2013-11-08 08:47:00 +00:00
title : $ : / c o r e / m o d u l e s / w i d g e t s / l i n k . j s
2013-10-12 16:05:13 +00:00
type : application / javascript
2013-11-08 08:47:00 +00:00
module - type : widget
2013-10-12 16:05:13 +00:00
Link widget
\ * /
( function ( ) {
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict" ;
2013-11-08 08:47:00 +00:00
var Widget = require ( "$:/core/modules/widgets/widget.js" ) . widget ;
2013-10-12 16:05:13 +00:00
var LinkWidget = function ( parseTreeNode , options ) {
this . initialise ( parseTreeNode , options ) ;
} ;
/ *
Inherit from the base widget class
* /
LinkWidget . prototype = new Widget ( ) ;
/ *
Render this widget into the DOM
* /
LinkWidget . prototype . render = function ( parent , nextSibling ) {
// Save the parent dom node
this . parentDomNode = parent ;
// Compute our attributes
this . computeAttributes ( ) ;
// Execute our logic
this . execute ( ) ;
2014-08-28 21:28:02 +00:00
// Get the value of the tv-wikilinks configuration macro
var wikiLinksMacro = this . getVariable ( "tv-wikilinks" ) ,
2016-04-04 11:43:40 +00:00
useWikiLinks = wikiLinksMacro ? ( wikiLinksMacro . trim ( ) !== "no" ) : true ,
2016-04-20 10:57:38 +00:00
missingLinksEnabled = ! ( this . hideMissingLinks && this . isMissing && ! this . isShadow ) ;
2013-10-21 17:31:04 +00:00
// Render the link if required
2016-04-04 11:43:40 +00:00
if ( useWikiLinks && missingLinksEnabled ) {
2013-10-21 17:31:04 +00:00
this . renderLink ( parent , nextSibling ) ;
} else {
// Just insert the link text
var domNode = this . document . createElement ( "span" ) ;
parent . insertBefore ( domNode , nextSibling ) ;
this . renderChildren ( domNode , null ) ;
this . domNodes . push ( domNode ) ;
}
} ;
/ *
Render this widget into the DOM
* /
LinkWidget . prototype . renderLink = function ( parent , nextSibling ) {
2013-10-21 19:20:32 +00:00
var self = this ;
2015-02-23 10:16:44 +00:00
// Sanitise the specified tag
var tag = this . linkTag ;
if ( $tw . config . htmlUnsafeElements . indexOf ( tag ) !== - 1 ) {
tag = "a" ;
}
2013-10-12 16:05:13 +00:00
// Create our element
2020-04-19 11:57:56 +00:00
var namespace = this . getVariable ( "namespace" , { defaultValue : "http://www.w3.org/1999/xhtml" } ) ,
domNode = this . document . createElementNS ( namespace , tag ) ;
2013-10-12 16:05:13 +00:00
// Assign classes
2014-08-08 16:19:48 +00:00
var classes = [ ] ;
2017-12-12 15:56:56 +00:00
if ( this . overrideClasses === undefined ) {
2017-12-12 12:25:06 +00:00
classes . push ( "tc-tiddlylink" ) ;
if ( this . isShadow ) {
classes . push ( "tc-tiddlylink-shadow" ) ;
}
if ( this . isMissing && ! this . isShadow ) {
classes . push ( "tc-tiddlylink-missing" ) ;
} else {
if ( ! this . isMissing ) {
classes . push ( "tc-tiddlylink-resolves" ) ;
}
}
2017-12-12 18:15:33 +00:00
if ( this . linkClasses ) {
2021-05-30 18:20:17 +00:00
classes . push ( this . linkClasses ) ;
2017-12-12 18:15:33 +00:00
}
2017-12-12 17:51:08 +00:00
} else if ( this . overrideClasses !== "" ) {
2017-12-12 12:30:34 +00:00
classes . push ( this . overrideClasses )
2013-10-12 16:05:13 +00:00
}
2017-12-12 12:25:06 +00:00
if ( classes . length > 0 ) {
domNode . setAttribute ( "class" , classes . join ( " " ) ) ;
2013-10-12 16:05:13 +00:00
}
// Set an href
2017-08-28 10:06:21 +00:00
var wikilinkTransformFilter = this . getVariable ( "tv-filter-export-link" ) ,
wikiLinkText ;
if ( wikilinkTransformFilter ) {
// Use the filter to construct the href
wikiLinkText = this . wiki . filterTiddlers ( wikilinkTransformFilter , this , function ( iterator ) {
iterator ( self . wiki . getTiddler ( self . to ) , self . to )
} ) [ 0 ] ;
} else {
// Expand the tv-wikilink-template variable to construct the href
var wikiLinkTemplateMacro = this . getVariable ( "tv-wikilink-template" ) ,
wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro . trim ( ) : "#$uri_encoded$" ;
2016-08-04 14:54:33 +00:00
wikiLinkText = $tw . utils . replaceString ( wikiLinkTemplate , "$uri_encoded$" , encodeURIComponent ( this . to ) ) ;
2017-08-28 10:06:21 +00:00
wikiLinkText = $tw . utils . replaceString ( wikiLinkText , "$uri_doubleencoded$" , encodeURIComponent ( encodeURIComponent ( this . to ) ) ) ;
}
// Override with the value of tv-get-export-link if defined
2015-02-20 21:45:40 +00:00
wikiLinkText = this . getVariable ( "tv-get-export-link" , { params : [ { name : "to" , value : this . to } ] , defaultValue : wikiLinkText } ) ;
2015-02-23 10:16:44 +00:00
if ( tag === "a" ) {
2020-04-19 11:57:56 +00:00
var namespaceHref = ( namespace === "http://www.w3.org/2000/svg" ) ? "http://www.w3.org/1999/xlink" : undefined ;
domNode . setAttributeNS ( namespaceHref , "href" , wikiLinkText ) ;
2015-03-25 11:11:07 +00:00
}
2017-08-28 10:06:21 +00:00
// Set the tabindex
2015-03-25 11:11:07 +00:00
if ( this . tabIndex ) {
domNode . setAttribute ( "tabindex" , this . tabIndex ) ;
2015-02-23 10:16:44 +00:00
}
2014-03-08 16:06:57 +00:00
// Set the tooltip
2014-04-05 16:37:58 +00:00
// HACK: Performance issues with re-parsing the tooltip prevent us defaulting the tooltip to "<$transclude field='tooltip'><$transclude field='title'/></$transclude>"
2014-08-28 21:28:02 +00:00
var tooltipWikiText = this . tooltip || this . getVariable ( "tv-wikilink-tooltip" ) ;
2014-03-08 16:06:57 +00:00
if ( tooltipWikiText ) {
var tooltipText = this . wiki . renderText ( "text/plain" , "text/vnd.tiddlywiki" , tooltipWikiText , {
parseAsInline : true ,
variables : {
currentTiddler : this . to
} ,
parentWidget : this
} ) ;
domNode . setAttribute ( "title" , tooltipText ) ;
2014-03-06 12:38:47 +00:00
}
2014-06-17 06:54:10 +00:00
if ( this [ "aria-label" ] ) {
domNode . setAttribute ( "aria-label" , this [ "aria-label" ] ) ;
}
2013-10-12 16:05:13 +00:00
// Add a click event handler
2013-10-25 22:22:46 +00:00
$tw . utils . addEventListeners ( domNode , [
2013-10-26 07:36:43 +00:00
{ name : "click" , handlerObject : this , handlerMethod : "handleClickEvent" } ,
2013-10-25 22:22:46 +00:00
] ) ;
2017-03-23 14:23:33 +00:00
// Make the link draggable if required
2015-02-23 10:16:44 +00:00
if ( this . draggable === "yes" ) {
2017-03-23 14:23:33 +00:00
$tw . utils . makeDraggable ( {
domNode : domNode ,
dragTiddlerFn : function ( ) { return self . to ; } ,
widget : this
} ) ;
2015-02-23 10:16:44 +00:00
}
2013-10-12 16:05:13 +00:00
// Insert the link into the DOM and render any children
parent . insertBefore ( domNode , nextSibling ) ;
this . renderChildren ( domNode , null ) ;
this . domNodes . push ( domNode ) ;
} ;
2015-01-06 01:39:24 +00:00
LinkWidget . prototype . handleClickEvent = function ( event ) {
// Send the click on its way as a navigate event
2013-10-25 22:22:46 +00:00
var bounds = this . domNodes [ 0 ] . getBoundingClientRect ( ) ;
this . dispatchEvent ( {
2014-08-28 20:43:44 +00:00
type : "tm-navigate" ,
2013-10-25 22:22:46 +00:00
navigateTo : this . to ,
2013-12-17 15:42:53 +00:00
navigateFromTitle : this . getVariable ( "storyTiddler" ) ,
2013-10-25 22:22:46 +00:00
navigateFromNode : this ,
navigateFromClientRect : { top : bounds . top , left : bounds . left , width : bounds . width , right : bounds . right , bottom : bounds . bottom , height : bounds . height
2014-02-12 22:00:12 +00:00
} ,
2021-07-05 08:52:17 +00:00
navigateFromClientTop : bounds . top ,
navigateFromClientLeft : bounds . left ,
navigateFromClientWidth : bounds . width ,
navigateFromClientRight : bounds . right ,
navigateFromClientBottom : bounds . bottom ,
navigateFromClientHeight : bounds . height ,
2017-06-14 17:16:13 +00:00
navigateSuppressNavigation : event . metaKey || event . ctrlKey || ( event . button === 1 ) ,
metaKey : event . metaKey ,
ctrlKey : event . ctrlKey ,
altKey : event . altKey ,
2020-08-27 14:52:25 +00:00
shiftKey : event . shiftKey ,
event : event
2013-10-25 22:22:46 +00:00
} ) ;
2015-02-23 10:16:44 +00:00
if ( this . domNodes [ 0 ] . hasAttribute ( "href" ) ) {
event . preventDefault ( ) ;
}
2015-08-30 18:12:12 +00:00
event . stopPropagation ( ) ;
return false ;
2013-10-25 22:22:46 +00:00
} ;
2013-10-12 16:05:13 +00:00
/ *
Compute the internal state of the widget
* /
LinkWidget . prototype . execute = function ( ) {
2015-02-23 10:16:44 +00:00
// Pick up our attributes
2013-10-28 23:40:45 +00:00
this . to = this . getAttribute ( "to" , this . getVariable ( "currentTiddler" ) ) ;
2014-03-06 12:38:47 +00:00
this . tooltip = this . getAttribute ( "tooltip" ) ;
2014-06-17 06:54:10 +00:00
this [ "aria-label" ] = this . getAttribute ( "aria-label" ) ;
2014-08-08 16:19:48 +00:00
this . linkClasses = this . getAttribute ( "class" ) ;
2017-12-12 12:30:34 +00:00
this . overrideClasses = this . getAttribute ( "overrideClass" ) ;
2015-02-23 10:16:44 +00:00
this . tabIndex = this . getAttribute ( "tabindex" ) ;
this . draggable = this . getAttribute ( "draggable" , "yes" ) ;
this . linkTag = this . getAttribute ( "tag" , "a" ) ;
2013-10-12 16:05:13 +00:00
// Determine the link characteristics
this . isMissing = ! this . wiki . tiddlerExists ( this . to ) ;
this . isShadow = this . wiki . isShadowTiddler ( this . to ) ;
2018-11-24 15:53:39 +00:00
this . hideMissingLinks = ( this . getVariable ( "tv-show-missing-links" ) || "yes" ) === "no" ;
2013-10-12 16:05:13 +00:00
// Make the child widgets
2019-06-10 20:04:21 +00:00
var templateTree ;
if ( this . parseTreeNode . children && this . parseTreeNode . children . length > 0 ) {
templateTree = this . parseTreeNode . children ;
} else {
// Default template is a link to the title
templateTree = [ { type : "text" , text : this . to } ] ;
}
this . makeChildWidgets ( templateTree ) ;
2013-10-12 16:05:13 +00:00
} ;
/ *
Selectively refreshes the widget if needed . Returns true if the widget or any of its children needed re - rendering
* /
LinkWidget . prototype . refresh = function ( changedTiddlers ) {
var changedAttributes = this . computeAttributes ( ) ;
2018-11-24 13:36:48 +00:00
if ( changedAttributes . to || changedTiddlers [ this . to ] || changedAttributes [ "aria-label" ] || changedAttributes . tooltip ) {
2013-10-12 16:05:13 +00:00
this . refreshSelf ( ) ;
return true ;
}
return this . refreshChildren ( changedTiddlers ) ;
} ;
exports . link = LinkWidget ;
} ) ( ) ;