'use strict' ;
var obsidian _module = require ( 'obsidian' ) ;
var child _process = require ( 'child_process' ) ;
var util = require ( 'util' ) ;
var fs = require ( 'fs' ) ;
var path = require ( 'path' ) ;
function _interopNamespace ( e ) {
if ( e && e . _ _esModule ) return e ;
var n = Object . create ( null ) ;
if ( e ) {
Object . keys ( e ) . forEach ( function ( k ) {
if ( k !== 'default' ) {
var d = Object . getOwnPropertyDescriptor ( e , k ) ;
Object . defineProperty ( n , k , d . get ? d : {
enumerable : true ,
get : function ( ) {
return e [ k ] ;
}
} ) ;
}
} ) ;
}
n [ 'default' ] = e ;
return Object . freeze ( n ) ;
}
var obsidian _module _ _namespace = /*#__PURE__*/ _interopNamespace ( obsidian _module ) ;
/ * ! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Copyright ( c ) Microsoft Corporation .
Permission to use , copy , modify , and / or distribute this software for any
purpose with or without fee is hereby granted .
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT ,
INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE , DATA OR PROFITS , WHETHER IN AN ACTION OF CONTRACT , NEGLIGENCE OR
OTHER TORTIOUS ACTION , ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
function _ _awaiter ( thisArg , _arguments , P , generator ) {
function adopt ( value ) { return value instanceof P ? value : new P ( function ( resolve ) { resolve ( value ) ; } ) ; }
return new ( P || ( P = Promise ) ) ( function ( resolve , reject ) {
function fulfilled ( value ) { try { step ( generator . next ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function rejected ( value ) { try { step ( generator [ "throw" ] ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function step ( result ) { result . done ? resolve ( result . value ) : adopt ( result . value ) . then ( fulfilled , rejected ) ; }
step ( ( generator = generator . apply ( thisArg , _arguments || [ ] ) ) . next ( ) ) ;
} ) ;
}
function log _error ( e ) {
const notice = new obsidian _module . Notice ( "" , 8000 ) ;
if ( e instanceof TemplaterError && e . console _msg ) {
// TODO: Find a better way for this
// @ts-ignore
notice . noticeEl . innerHTML = ` <b>Templater Error</b>:<br/> ${ e . message } <br/>Check console for more informations ` ;
console . error ( ` Templater Error: ` , e . message , "\n" , e . console _msg ) ;
}
else {
// @ts-ignore
notice . noticeEl . innerHTML = ` <b>Templater Error</b>:<br/> ${ e . message } ` ;
}
}
class TemplaterError extends Error {
constructor ( msg , console _msg ) {
super ( msg ) ;
this . console _msg = console _msg ;
this . name = this . constructor . name ;
Error . captureStackTrace ( this , this . constructor ) ;
}
}
function errorWrapper ( fn , msg ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
try {
return yield fn ( ) ;
}
catch ( e ) {
if ( ! ( e instanceof TemplaterError ) ) {
log _error ( new TemplaterError ( msg , e . message ) ) ;
}
else {
log _error ( e ) ;
}
return null ;
}
} ) ;
}
function errorWrapperSync ( fn , msg ) {
try {
return fn ( ) ;
}
catch ( e ) {
log _error ( new TemplaterError ( msg , e . message ) ) ;
return null ;
}
}
var top = 'top' ;
var bottom = 'bottom' ;
var right = 'right' ;
var left = 'left' ;
var auto = 'auto' ;
var basePlacements = [ top , bottom , right , left ] ;
var start = 'start' ;
var end = 'end' ;
var clippingParents = 'clippingParents' ;
var viewport = 'viewport' ;
var popper = 'popper' ;
var reference = 'reference' ;
var variationPlacements = /*#__PURE__*/ basePlacements . reduce ( function ( acc , placement ) {
return acc . concat ( [ placement + "-" + start , placement + "-" + end ] ) ;
} , [ ] ) ;
var placements = /*#__PURE__*/ [ ] . concat ( basePlacements , [ auto ] ) . reduce ( function ( acc , placement ) {
return acc . concat ( [ placement , placement + "-" + start , placement + "-" + end ] ) ;
} , [ ] ) ; // modifiers that need to read the DOM
var beforeRead = 'beforeRead' ;
var read = 'read' ;
var afterRead = 'afterRead' ; // pure-logic modifiers
var beforeMain = 'beforeMain' ;
var main = 'main' ;
var afterMain = 'afterMain' ; // modifier with the purpose to write to the DOM (or write into a framework state)
var beforeWrite = 'beforeWrite' ;
var write = 'write' ;
var afterWrite = 'afterWrite' ;
var modifierPhases = [ beforeRead , read , afterRead , beforeMain , main , afterMain , beforeWrite , write , afterWrite ] ;
function getNodeName ( element ) {
return element ? ( element . nodeName || '' ) . toLowerCase ( ) : null ;
}
function getWindow ( node ) {
if ( node == null ) {
return window ;
}
if ( node . toString ( ) !== '[object Window]' ) {
var ownerDocument = node . ownerDocument ;
return ownerDocument ? ownerDocument . defaultView || window : window ;
}
return node ;
}
function isElement ( node ) {
var OwnElement = getWindow ( node ) . Element ;
return node instanceof OwnElement || node instanceof Element ;
}
function isHTMLElement ( node ) {
var OwnElement = getWindow ( node ) . HTMLElement ;
return node instanceof OwnElement || node instanceof HTMLElement ;
}
function isShadowRoot ( node ) {
// IE 11 has no ShadowRoot
if ( typeof ShadowRoot === 'undefined' ) {
return false ;
}
var OwnElement = getWindow ( node ) . ShadowRoot ;
return node instanceof OwnElement || node instanceof ShadowRoot ;
}
// and applies them to the HTMLElements such as popper and arrow
function applyStyles ( _ref ) {
var state = _ref . state ;
Object . keys ( state . elements ) . forEach ( function ( name ) {
var style = state . styles [ name ] || { } ;
var attributes = state . attributes [ name ] || { } ;
var element = state . elements [ name ] ; // arrow is optional + virtual elements
if ( ! isHTMLElement ( element ) || ! getNodeName ( element ) ) {
return ;
} // Flow doesn't support to extend this property, but it's the most
// effective way to apply styles to an HTMLElement
// $FlowFixMe[cannot-write]
Object . assign ( element . style , style ) ;
Object . keys ( attributes ) . forEach ( function ( name ) {
var value = attributes [ name ] ;
if ( value === false ) {
element . removeAttribute ( name ) ;
} else {
element . setAttribute ( name , value === true ? '' : value ) ;
}
} ) ;
} ) ;
}
function effect$2 ( _ref2 ) {
var state = _ref2 . state ;
var initialStyles = {
popper : {
position : state . options . strategy ,
left : '0' ,
top : '0' ,
margin : '0'
} ,
arrow : {
position : 'absolute'
} ,
reference : { }
} ;
Object . assign ( state . elements . popper . style , initialStyles . popper ) ;
state . styles = initialStyles ;
if ( state . elements . arrow ) {
Object . assign ( state . elements . arrow . style , initialStyles . arrow ) ;
}
return function ( ) {
Object . keys ( state . elements ) . forEach ( function ( name ) {
var element = state . elements [ name ] ;
var attributes = state . attributes [ name ] || { } ;
var styleProperties = Object . keys ( state . styles . hasOwnProperty ( name ) ? state . styles [ name ] : initialStyles [ name ] ) ; // Set all values to an empty string to unset them
var style = styleProperties . reduce ( function ( style , property ) {
style [ property ] = '' ;
return style ;
} , { } ) ; // arrow is optional + virtual elements
if ( ! isHTMLElement ( element ) || ! getNodeName ( element ) ) {
return ;
}
Object . assign ( element . style , style ) ;
Object . keys ( attributes ) . forEach ( function ( attribute ) {
element . removeAttribute ( attribute ) ;
} ) ;
} ) ;
} ;
} // eslint-disable-next-line import/no-unused-modules
var applyStyles$1 = {
name : 'applyStyles' ,
enabled : true ,
phase : 'write' ,
fn : applyStyles ,
effect : effect$2 ,
requires : [ 'computeStyles' ]
} ;
function getBasePlacement ( placement ) {
return placement . split ( '-' ) [ 0 ] ;
}
var max = Math . max ;
var min = Math . min ;
var round = Math . round ;
function getBoundingClientRect ( element , includeScale ) {
if ( includeScale === void 0 ) {
includeScale = false ;
}
var rect = element . getBoundingClientRect ( ) ;
var scaleX = 1 ;
var scaleY = 1 ;
if ( isHTMLElement ( element ) && includeScale ) {
var offsetHeight = element . offsetHeight ;
var offsetWidth = element . offsetWidth ; // Do not attempt to divide by 0, otherwise we get `Infinity` as scale
// Fallback to 1 in case both values are `0`
if ( offsetWidth > 0 ) {
scaleX = round ( rect . width ) / offsetWidth || 1 ;
}
if ( offsetHeight > 0 ) {
scaleY = round ( rect . height ) / offsetHeight || 1 ;
}
}
return {
width : rect . width / scaleX ,
height : rect . height / scaleY ,
top : rect . top / scaleY ,
right : rect . right / scaleX ,
bottom : rect . bottom / scaleY ,
left : rect . left / scaleX ,
x : rect . left / scaleX ,
y : rect . top / scaleY
} ;
}
// means it doesn't take into account transforms.
function getLayoutRect ( element ) {
var clientRect = getBoundingClientRect ( element ) ; // Use the clientRect sizes if it's not been transformed.
// Fixes https://github.com/popperjs/popper-core/issues/1223
var width = element . offsetWidth ;
var height = element . offsetHeight ;
if ( Math . abs ( clientRect . width - width ) <= 1 ) {
width = clientRect . width ;
}
if ( Math . abs ( clientRect . height - height ) <= 1 ) {
height = clientRect . height ;
}
return {
x : element . offsetLeft ,
y : element . offsetTop ,
width : width ,
height : height
} ;
}
function contains ( parent , child ) {
var rootNode = child . getRootNode && child . getRootNode ( ) ; // First, attempt with faster native method
if ( parent . contains ( child ) ) {
return true ;
} // then fallback to custom implementation with Shadow DOM support
else if ( rootNode && isShadowRoot ( rootNode ) ) {
var next = child ;
do {
if ( next && parent . isSameNode ( next ) ) {
return true ;
} // $FlowFixMe[prop-missing]: need a better way to handle this...
next = next . parentNode || next . host ;
} while ( next ) ;
} // Give up, the result is false
return false ;
}
function getComputedStyle ( element ) {
return getWindow ( element ) . getComputedStyle ( element ) ;
}
function isTableElement ( element ) {
return [ 'table' , 'td' , 'th' ] . indexOf ( getNodeName ( element ) ) >= 0 ;
}
function getDocumentElement ( element ) {
// $FlowFixMe[incompatible-return]: assume body is always available
return ( ( isElement ( element ) ? element . ownerDocument : // $FlowFixMe[prop-missing]
element . document ) || window . document ) . documentElement ;
}
function getParentNode ( element ) {
if ( getNodeName ( element ) === 'html' ) {
return element ;
}
return ( // this is a quicker (but less type safe) way to save quite some bytes from the bundle
// $FlowFixMe[incompatible-return]
// $FlowFixMe[prop-missing]
element . assignedSlot || // step into the shadow DOM of the parent of a slotted node
element . parentNode || ( // DOM Element detected
isShadowRoot ( element ) ? element . host : null ) || // ShadowRoot detected
// $FlowFixMe[incompatible-call]: HTMLElement is a Node
getDocumentElement ( element ) // fallback
) ;
}
function getTrueOffsetParent ( element ) {
if ( ! isHTMLElement ( element ) || // https://github.com/popperjs/popper-core/issues/837
getComputedStyle ( element ) . position === 'fixed' ) {
return null ;
}
return element . offsetParent ;
} // `.offsetParent` reports `null` for fixed elements, while absolute elements
// return the containing block
function getContainingBlock ( element ) {
var isFirefox = navigator . userAgent . toLowerCase ( ) . indexOf ( 'firefox' ) !== - 1 ;
var isIE = navigator . userAgent . indexOf ( 'Trident' ) !== - 1 ;
if ( isIE && isHTMLElement ( element ) ) {
// In IE 9, 10 and 11 fixed elements containing block is always established by the viewport
var elementCss = getComputedStyle ( element ) ;
if ( elementCss . position === 'fixed' ) {
return null ;
}
}
var currentNode = getParentNode ( element ) ;
while ( isHTMLElement ( currentNode ) && [ 'html' , 'body' ] . indexOf ( getNodeName ( currentNode ) ) < 0 ) {
var css = getComputedStyle ( currentNode ) ; // This is non-exhaustive but covers the most common CSS properties that
// create a containing block.
// https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
if ( css . transform !== 'none' || css . perspective !== 'none' || css . contain === 'paint' || [ 'transform' , 'perspective' ] . indexOf ( css . willChange ) !== - 1 || isFirefox && css . willChange === 'filter' || isFirefox && css . filter && css . filter !== 'none' ) {
return currentNode ;
} else {
currentNode = currentNode . parentNode ;
}
}
return null ;
} // Gets the closest ancestor positioned element. Handles some edge cases,
// such as table ancestors and cross browser bugs.
function getOffsetParent ( element ) {
var window = getWindow ( element ) ;
var offsetParent = getTrueOffsetParent ( element ) ;
while ( offsetParent && isTableElement ( offsetParent ) && getComputedStyle ( offsetParent ) . position === 'static' ) {
offsetParent = getTrueOffsetParent ( offsetParent ) ;
}
if ( offsetParent && ( getNodeName ( offsetParent ) === 'html' || getNodeName ( offsetParent ) === 'body' && getComputedStyle ( offsetParent ) . position === 'static' ) ) {
return window ;
}
return offsetParent || getContainingBlock ( element ) || window ;
}
function getMainAxisFromPlacement ( placement ) {
return [ 'top' , 'bottom' ] . indexOf ( placement ) >= 0 ? 'x' : 'y' ;
}
function within ( min$1 , value , max$1 ) {
return max ( min$1 , min ( value , max$1 ) ) ;
}
function withinMaxClamp ( min , value , max ) {
var v = within ( min , value , max ) ;
return v > max ? max : v ;
}
function getFreshSideObject ( ) {
return {
top : 0 ,
right : 0 ,
bottom : 0 ,
left : 0
} ;
}
function mergePaddingObject ( paddingObject ) {
return Object . assign ( { } , getFreshSideObject ( ) , paddingObject ) ;
}
function expandToHashMap ( value , keys ) {
return keys . reduce ( function ( hashMap , key ) {
hashMap [ key ] = value ;
return hashMap ;
} , { } ) ;
}
var toPaddingObject = function toPaddingObject ( padding , state ) {
padding = typeof padding === 'function' ? padding ( Object . assign ( { } , state . rects , {
placement : state . placement
} ) ) : padding ;
return mergePaddingObject ( typeof padding !== 'number' ? padding : expandToHashMap ( padding , basePlacements ) ) ;
} ;
function arrow ( _ref ) {
var _state$modifiersData$ ;
var state = _ref . state ,
name = _ref . name ,
options = _ref . options ;
var arrowElement = state . elements . arrow ;
var popperOffsets = state . modifiersData . popperOffsets ;
var basePlacement = getBasePlacement ( state . placement ) ;
var axis = getMainAxisFromPlacement ( basePlacement ) ;
var isVertical = [ left , right ] . indexOf ( basePlacement ) >= 0 ;
var len = isVertical ? 'height' : 'width' ;
if ( ! arrowElement || ! popperOffsets ) {
return ;
}
var paddingObject = toPaddingObject ( options . padding , state ) ;
var arrowRect = getLayoutRect ( arrowElement ) ;
var minProp = axis === 'y' ? top : left ;
var maxProp = axis === 'y' ? bottom : right ;
var endDiff = state . rects . reference [ len ] + state . rects . reference [ axis ] - popperOffsets [ axis ] - state . rects . popper [ len ] ;
var startDiff = popperOffsets [ axis ] - state . rects . reference [ axis ] ;
var arrowOffsetParent = getOffsetParent ( arrowElement ) ;
var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent . clientHeight || 0 : arrowOffsetParent . clientWidth || 0 : 0 ;
var centerToReference = endDiff / 2 - startDiff / 2 ; // Make sure the arrow doesn't overflow the popper if the center point is
// outside of the popper bounds
var min = paddingObject [ minProp ] ;
var max = clientSize - arrowRect [ len ] - paddingObject [ maxProp ] ;
var center = clientSize / 2 - arrowRect [ len ] / 2 + centerToReference ;
var offset = within ( min , center , max ) ; // Prevents breaking syntax highlighting...
var axisProp = axis ;
state . modifiersData [ name ] = ( _state$modifiersData$ = { } , _state$modifiersData$ [ axisProp ] = offset , _state$modifiersData$ . centerOffset = offset - center , _state$modifiersData$ ) ;
}
function effect$1 ( _ref2 ) {
var state = _ref2 . state ,
options = _ref2 . options ;
var _options$element = options . element ,
arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element ;
if ( arrowElement == null ) {
return ;
} // CSS selector
if ( typeof arrowElement === 'string' ) {
arrowElement = state . elements . popper . querySelector ( arrowElement ) ;
if ( ! arrowElement ) {
return ;
}
}
if ( process . env . NODE _ENV !== "production" ) {
if ( ! isHTMLElement ( arrowElement ) ) {
console . error ( [ 'Popper: "arrow" element must be an HTMLElement (not an SVGElement).' , 'To use an SVG arrow, wrap it in an HTMLElement that will be used as' , 'the arrow.' ] . join ( ' ' ) ) ;
}
}
if ( ! contains ( state . elements . popper , arrowElement ) ) {
if ( process . env . NODE _ENV !== "production" ) {
console . error ( [ 'Popper: "arrow" modifier\'s `element` must be a child of the popper' , 'element.' ] . join ( ' ' ) ) ;
}
return ;
}
state . elements . arrow = arrowElement ;
} // eslint-disable-next-line import/no-unused-modules
var arrow$1 = {
name : 'arrow' ,
enabled : true ,
phase : 'main' ,
fn : arrow ,
effect : effect$1 ,
requires : [ 'popperOffsets' ] ,
requiresIfExists : [ 'preventOverflow' ]
} ;
function getVariation ( placement ) {
return placement . split ( '-' ) [ 1 ] ;
}
var unsetSides = {
top : 'auto' ,
right : 'auto' ,
bottom : 'auto' ,
left : 'auto'
} ; // Round the offsets to the nearest suitable subpixel based on the DPR.
// Zooming can change the DPR, but it seems to report a value that will
// cleanly divide the values into the appropriate subpixels.
function roundOffsetsByDPR ( _ref ) {
var x = _ref . x ,
y = _ref . y ;
var win = window ;
var dpr = win . devicePixelRatio || 1 ;
return {
x : round ( x * dpr ) / dpr || 0 ,
y : round ( y * dpr ) / dpr || 0
} ;
}
function mapToStyles ( _ref2 ) {
var _Object$assign2 ;
var popper = _ref2 . popper ,
popperRect = _ref2 . popperRect ,
placement = _ref2 . placement ,
variation = _ref2 . variation ,
offsets = _ref2 . offsets ,
position = _ref2 . position ,
gpuAcceleration = _ref2 . gpuAcceleration ,
adaptive = _ref2 . adaptive ,
roundOffsets = _ref2 . roundOffsets ,
isFixed = _ref2 . isFixed ;
var _offsets$x = offsets . x ,
x = _offsets$x === void 0 ? 0 : _offsets$x ,
_offsets$y = offsets . y ,
y = _offsets$y === void 0 ? 0 : _offsets$y ;
var _ref3 = typeof roundOffsets === 'function' ? roundOffsets ( {
x : x ,
y : y
} ) : {
x : x ,
y : y
} ;
x = _ref3 . x ;
y = _ref3 . y ;
var hasX = offsets . hasOwnProperty ( 'x' ) ;
var hasY = offsets . hasOwnProperty ( 'y' ) ;
var sideX = left ;
var sideY = top ;
var win = window ;
if ( adaptive ) {
var offsetParent = getOffsetParent ( popper ) ;
var heightProp = 'clientHeight' ;
var widthProp = 'clientWidth' ;
if ( offsetParent === getWindow ( popper ) ) {
offsetParent = getDocumentElement ( popper ) ;
if ( getComputedStyle ( offsetParent ) . position !== 'static' && position === 'absolute' ) {
heightProp = 'scrollHeight' ;
widthProp = 'scrollWidth' ;
}
} // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it
offsetParent = offsetParent ;
if ( placement === top || ( placement === left || placement === right ) && variation === end ) {
sideY = bottom ;
var offsetY = isFixed && win . visualViewport ? win . visualViewport . height : // $FlowFixMe[prop-missing]
offsetParent [ heightProp ] ;
y -= offsetY - popperRect . height ;
y *= gpuAcceleration ? 1 : - 1 ;
}
if ( placement === left || ( placement === top || placement === bottom ) && variation === end ) {
sideX = right ;
var offsetX = isFixed && win . visualViewport ? win . visualViewport . width : // $FlowFixMe[prop-missing]
offsetParent [ widthProp ] ;
x -= offsetX - popperRect . width ;
x *= gpuAcceleration ? 1 : - 1 ;
}
}
var commonStyles = Object . assign ( {
position : position
} , adaptive && unsetSides ) ;
var _ref4 = roundOffsets === true ? roundOffsetsByDPR ( {
x : x ,
y : y
} ) : {
x : x ,
y : y
} ;
x = _ref4 . x ;
y = _ref4 . y ;
if ( gpuAcceleration ) {
var _Object$assign ;
return Object . assign ( { } , commonStyles , ( _Object$assign = { } , _Object$assign [ sideY ] = hasY ? '0' : '' , _Object$assign [ sideX ] = hasX ? '0' : '' , _Object$assign . transform = ( win . devicePixelRatio || 1 ) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)" , _Object$assign ) ) ;
}
return Object . assign ( { } , commonStyles , ( _Object$assign2 = { } , _Object$assign2 [ sideY ] = hasY ? y + "px" : '' , _Object$assign2 [ sideX ] = hasX ? x + "px" : '' , _Object$assign2 . transform = '' , _Object$assign2 ) ) ;
}
function computeStyles ( _ref5 ) {
var state = _ref5 . state ,
options = _ref5 . options ;
var _options$gpuAccelerat = options . gpuAcceleration ,
gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat ,
_options$adaptive = options . adaptive ,
adaptive = _options$adaptive === void 0 ? true : _options$adaptive ,
_options$roundOffsets = options . roundOffsets ,
roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets ;
if ( process . env . NODE _ENV !== "production" ) {
var transitionProperty = getComputedStyle ( state . elements . popper ) . transitionProperty || '' ;
if ( adaptive && [ 'transform' , 'top' , 'right' , 'bottom' , 'left' ] . some ( function ( property ) {
return transitionProperty . indexOf ( property ) >= 0 ;
} ) ) {
console . warn ( [ 'Popper: Detected CSS transitions on at least one of the following' , 'CSS properties: "transform", "top", "right", "bottom", "left".' , '\n\n' , 'Disable the "computeStyles" modifier\'s `adaptive` option to allow' , 'for smooth transitions, or remove these properties from the CSS' , 'transition declaration on the popper element if only transitioning' , 'opacity or background-color for example.' , '\n\n' , 'We recommend using the popper element as a wrapper around an inner' , 'element that can have any CSS property transitioned for animations.' ] . join ( ' ' ) ) ;
}
}
var commonStyles = {
placement : getBasePlacement ( state . placement ) ,
variation : getVariation ( state . placement ) ,
popper : state . elements . popper ,
popperRect : state . rects . popper ,
gpuAcceleration : gpuAcceleration ,
isFixed : state . options . strategy === 'fixed'
} ;
if ( state . modifiersData . popperOffsets != null ) {
state . styles . popper = Object . assign ( { } , state . styles . popper , mapToStyles ( Object . assign ( { } , commonStyles , {
offsets : state . modifiersData . popperOffsets ,
position : state . options . strategy ,
adaptive : adaptive ,
roundOffsets : roundOffsets
} ) ) ) ;
}
if ( state . modifiersData . arrow != null ) {
state . styles . arrow = Object . assign ( { } , state . styles . arrow , mapToStyles ( Object . assign ( { } , commonStyles , {
offsets : state . modifiersData . arrow ,
position : 'absolute' ,
adaptive : false ,
roundOffsets : roundOffsets
} ) ) ) ;
}
state . attributes . popper = Object . assign ( { } , state . attributes . popper , {
'data-popper-placement' : state . placement
} ) ;
} // eslint-disable-next-line import/no-unused-modules
var computeStyles$1 = {
name : 'computeStyles' ,
enabled : true ,
phase : 'beforeWrite' ,
fn : computeStyles ,
data : { }
} ;
var passive = {
passive : true
} ;
function effect ( _ref ) {
var state = _ref . state ,
instance = _ref . instance ,
options = _ref . options ;
var _options$scroll = options . scroll ,
scroll = _options$scroll === void 0 ? true : _options$scroll ,
_options$resize = options . resize ,
resize = _options$resize === void 0 ? true : _options$resize ;
var window = getWindow ( state . elements . popper ) ;
var scrollParents = [ ] . concat ( state . scrollParents . reference , state . scrollParents . popper ) ;
if ( scroll ) {
scrollParents . forEach ( function ( scrollParent ) {
scrollParent . addEventListener ( 'scroll' , instance . update , passive ) ;
} ) ;
}
if ( resize ) {
window . addEventListener ( 'resize' , instance . update , passive ) ;
}
return function ( ) {
if ( scroll ) {
scrollParents . forEach ( function ( scrollParent ) {
scrollParent . removeEventListener ( 'scroll' , instance . update , passive ) ;
} ) ;
}
if ( resize ) {
window . removeEventListener ( 'resize' , instance . update , passive ) ;
}
} ;
} // eslint-disable-next-line import/no-unused-modules
var eventListeners = {
name : 'eventListeners' ,
enabled : true ,
phase : 'write' ,
fn : function fn ( ) { } ,
effect : effect ,
data : { }
} ;
var hash$1 = {
left : 'right' ,
right : 'left' ,
bottom : 'top' ,
top : 'bottom'
} ;
function getOppositePlacement ( placement ) {
return placement . replace ( /left|right|bottom|top/g , function ( matched ) {
return hash$1 [ matched ] ;
} ) ;
}
var hash = {
start : 'end' ,
end : 'start'
} ;
function getOppositeVariationPlacement ( placement ) {
return placement . replace ( /start|end/g , function ( matched ) {
return hash [ matched ] ;
} ) ;
}
function getWindowScroll ( node ) {
var win = getWindow ( node ) ;
var scrollLeft = win . pageXOffset ;
var scrollTop = win . pageYOffset ;
return {
scrollLeft : scrollLeft ,
scrollTop : scrollTop
} ;
}
function getWindowScrollBarX ( element ) {
// If <html> has a CSS width greater than the viewport, then this will be
// incorrect for RTL.
// Popper 1 is broken in this case and never had a bug report so let's assume
// it's not an issue. I don't think anyone ever specifies width on <html>
// anyway.
// Browsers where the left scrollbar doesn't cause an issue report `0` for
// this (e.g. Edge 2019, IE11, Safari)
return getBoundingClientRect ( getDocumentElement ( element ) ) . left + getWindowScroll ( element ) . scrollLeft ;
}
function getViewportRect ( element ) {
var win = getWindow ( element ) ;
var html = getDocumentElement ( element ) ;
var visualViewport = win . visualViewport ;
var width = html . clientWidth ;
var height = html . clientHeight ;
var x = 0 ;
var y = 0 ; // NB: This isn't supported on iOS <= 12. If the keyboard is open, the popper
// can be obscured underneath it.
// Also, `html.clientHeight` adds the bottom bar height in Safari iOS, even
// if it isn't open, so if this isn't available, the popper will be detected
// to overflow the bottom of the screen too early.
if ( visualViewport ) {
width = visualViewport . width ;
height = visualViewport . height ; // Uses Layout Viewport (like Chrome; Safari does not currently)
// In Chrome, it returns a value very close to 0 (+/-) but contains rounding
// errors due to floating point numbers, so we need to check precision.
// Safari returns a number <= 0, usually < -1 when pinch-zoomed
// Feature detection fails in mobile emulation mode in Chrome.
// Math.abs(win.innerWidth / visualViewport.scale - visualViewport.width) <
// 0.001
// Fallback here: "Not Safari" userAgent
if ( ! /^((?!chrome|android).)*safari/i . test ( navigator . userAgent ) ) {
x = visualViewport . offsetLeft ;
y = visualViewport . offsetTop ;
}
}
return {
width : width ,
height : height ,
x : x + getWindowScrollBarX ( element ) ,
y : y
} ;
}
// of the `<html>` and `<body>` rect bounds if horizontally scrollable
function getDocumentRect ( element ) {
var _element$ownerDocumen ;
var html = getDocumentElement ( element ) ;
var winScroll = getWindowScroll ( element ) ;
var body = ( _element$ownerDocumen = element . ownerDocument ) == null ? void 0 : _element$ownerDocumen . body ;
var width = max ( html . scrollWidth , html . clientWidth , body ? body . scrollWidth : 0 , body ? body . clientWidth : 0 ) ;
var height = max ( html . scrollHeight , html . clientHeight , body ? body . scrollHeight : 0 , body ? body . clientHeight : 0 ) ;
var x = - winScroll . scrollLeft + getWindowScrollBarX ( element ) ;
var y = - winScroll . scrollTop ;
if ( getComputedStyle ( body || html ) . direction === 'rtl' ) {
x += max ( html . clientWidth , body ? body . clientWidth : 0 ) - width ;
}
return {
width : width ,
height : height ,
x : x ,
y : y
} ;
}
function isScrollParent ( element ) {
// Firefox wants us to check `-x` and `-y` variations as well
var _getComputedStyle = getComputedStyle ( element ) ,
overflow = _getComputedStyle . overflow ,
overflowX = _getComputedStyle . overflowX ,
overflowY = _getComputedStyle . overflowY ;
return /auto|scroll|overlay|hidden/ . test ( overflow + overflowY + overflowX ) ;
}
function getScrollParent ( node ) {
if ( [ 'html' , 'body' , '#document' ] . indexOf ( getNodeName ( node ) ) >= 0 ) {
// $FlowFixMe[incompatible-return]: assume body is always available
return node . ownerDocument . body ;
}
if ( isHTMLElement ( node ) && isScrollParent ( node ) ) {
return node ;
}
return getScrollParent ( getParentNode ( node ) ) ;
}
/ *
given a DOM element , return the list of all scroll parents , up the list of ancesors
until we get to the top window object . This list is what we attach scroll listeners
to , because if any of these parent elements scroll , we ' ll need to re - calculate the
reference element ' s position .
* /
function listScrollParents ( element , list ) {
var _element$ownerDocumen ;
if ( list === void 0 ) {
list = [ ] ;
}
var scrollParent = getScrollParent ( element ) ;
var isBody = scrollParent === ( ( _element$ownerDocumen = element . ownerDocument ) == null ? void 0 : _element$ownerDocumen . body ) ;
var win = getWindow ( scrollParent ) ;
var target = isBody ? [ win ] . concat ( win . visualViewport || [ ] , isScrollParent ( scrollParent ) ? scrollParent : [ ] ) : scrollParent ;
var updatedList = list . concat ( target ) ;
return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here
updatedList . concat ( listScrollParents ( getParentNode ( target ) ) ) ;
}
function rectToClientRect ( rect ) {
return Object . assign ( { } , rect , {
left : rect . x ,
top : rect . y ,
right : rect . x + rect . width ,
bottom : rect . y + rect . height
} ) ;
}
function getInnerBoundingClientRect ( element ) {
var rect = getBoundingClientRect ( element ) ;
rect . top = rect . top + element . clientTop ;
rect . left = rect . left + element . clientLeft ;
rect . bottom = rect . top + element . clientHeight ;
rect . right = rect . left + element . clientWidth ;
rect . width = element . clientWidth ;
rect . height = element . clientHeight ;
rect . x = rect . left ;
rect . y = rect . top ;
return rect ;
}
function getClientRectFromMixedType ( element , clippingParent ) {
return clippingParent === viewport ? rectToClientRect ( getViewportRect ( element ) ) : isElement ( clippingParent ) ? getInnerBoundingClientRect ( clippingParent ) : rectToClientRect ( getDocumentRect ( getDocumentElement ( element ) ) ) ;
} // A "clipping parent" is an overflowable container with the characteristic of
// clipping (or hiding) overflowing elements with a position different from
// `initial`
function getClippingParents ( element ) {
var clippingParents = listScrollParents ( getParentNode ( element ) ) ;
var canEscapeClipping = [ 'absolute' , 'fixed' ] . indexOf ( getComputedStyle ( element ) . position ) >= 0 ;
var clipperElement = canEscapeClipping && isHTMLElement ( element ) ? getOffsetParent ( element ) : element ;
if ( ! isElement ( clipperElement ) ) {
return [ ] ;
} // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414
return clippingParents . filter ( function ( clippingParent ) {
return isElement ( clippingParent ) && contains ( clippingParent , clipperElement ) && getNodeName ( clippingParent ) !== 'body' ;
} ) ;
} // Gets the maximum area that the element is visible in due to any number of
// clipping parents
function getClippingRect ( element , boundary , rootBoundary ) {
var mainClippingParents = boundary === 'clippingParents' ? getClippingParents ( element ) : [ ] . concat ( boundary ) ;
var clippingParents = [ ] . concat ( mainClippingParents , [ rootBoundary ] ) ;
var firstClippingParent = clippingParents [ 0 ] ;
var clippingRect = clippingParents . reduce ( function ( accRect , clippingParent ) {
var rect = getClientRectFromMixedType ( element , clippingParent ) ;
accRect . top = max ( rect . top , accRect . top ) ;
accRect . right = min ( rect . right , accRect . right ) ;
accRect . bottom = min ( rect . bottom , accRect . bottom ) ;
accRect . left = max ( rect . left , accRect . left ) ;
return accRect ;
} , getClientRectFromMixedType ( element , firstClippingParent ) ) ;
clippingRect . width = clippingRect . right - clippingRect . left ;
clippingRect . height = clippingRect . bottom - clippingRect . top ;
clippingRect . x = clippingRect . left ;
clippingRect . y = clippingRect . top ;
return clippingRect ;
}
function computeOffsets ( _ref ) {
var reference = _ref . reference ,
element = _ref . element ,
placement = _ref . placement ;
var basePlacement = placement ? getBasePlacement ( placement ) : null ;
var variation = placement ? getVariation ( placement ) : null ;
var commonX = reference . x + reference . width / 2 - element . width / 2 ;
var commonY = reference . y + reference . height / 2 - element . height / 2 ;
var offsets ;
switch ( basePlacement ) {
case top :
offsets = {
x : commonX ,
y : reference . y - element . height
} ;
break ;
case bottom :
offsets = {
x : commonX ,
y : reference . y + reference . height
} ;
break ;
case right :
offsets = {
x : reference . x + reference . width ,
y : commonY
} ;
break ;
case left :
offsets = {
x : reference . x - element . width ,
y : commonY
} ;
break ;
default :
offsets = {
x : reference . x ,
y : reference . y
} ;
}
var mainAxis = basePlacement ? getMainAxisFromPlacement ( basePlacement ) : null ;
if ( mainAxis != null ) {
var len = mainAxis === 'y' ? 'height' : 'width' ;
switch ( variation ) {
case start :
offsets [ mainAxis ] = offsets [ mainAxis ] - ( reference [ len ] / 2 - element [ len ] / 2 ) ;
break ;
case end :
offsets [ mainAxis ] = offsets [ mainAxis ] + ( reference [ len ] / 2 - element [ len ] / 2 ) ;
break ;
}
}
return offsets ;
}
function detectOverflow ( state , options ) {
if ( options === void 0 ) {
options = { } ;
}
var _options = options ,
_options$placement = _options . placement ,
placement = _options$placement === void 0 ? state . placement : _options$placement ,
_options$boundary = _options . boundary ,
boundary = _options$boundary === void 0 ? clippingParents : _options$boundary ,
_options$rootBoundary = _options . rootBoundary ,
rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary ,
_options$elementConte = _options . elementContext ,
elementContext = _options$elementConte === void 0 ? popper : _options$elementConte ,
_options$altBoundary = _options . altBoundary ,
altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary ,
_options$padding = _options . padding ,
padding = _options$padding === void 0 ? 0 : _options$padding ;
var paddingObject = mergePaddingObject ( typeof padding !== 'number' ? padding : expandToHashMap ( padding , basePlacements ) ) ;
var altContext = elementContext === popper ? reference : popper ;
var popperRect = state . rects . popper ;
var element = state . elements [ altBoundary ? altContext : elementContext ] ;
var clippingClientRect = getClippingRect ( isElement ( element ) ? element : element . contextElement || getDocumentElement ( state . elements . popper ) , boundary , rootBoundary ) ;
var referenceClientRect = getBoundingClientRect ( state . elements . reference ) ;
var popperOffsets = computeOffsets ( {
reference : referenceClientRect ,
element : popperRect ,
strategy : 'absolute' ,
placement : placement
} ) ;
var popperClientRect = rectToClientRect ( Object . assign ( { } , popperRect , popperOffsets ) ) ;
var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect ; // positive = overflowing the clipping rect
// 0 or negative = within the clipping rect
var overflowOffsets = {
top : clippingClientRect . top - elementClientRect . top + paddingObject . top ,
bottom : elementClientRect . bottom - clippingClientRect . bottom + paddingObject . bottom ,
left : clippingClientRect . left - elementClientRect . left + paddingObject . left ,
right : elementClientRect . right - clippingClientRect . right + paddingObject . right
} ;
var offsetData = state . modifiersData . offset ; // Offsets can be applied only to the popper element
if ( elementContext === popper && offsetData ) {
var offset = offsetData [ placement ] ;
Object . keys ( overflowOffsets ) . forEach ( function ( key ) {
var multiply = [ right , bottom ] . indexOf ( key ) >= 0 ? 1 : - 1 ;
var axis = [ top , bottom ] . indexOf ( key ) >= 0 ? 'y' : 'x' ;
overflowOffsets [ key ] += offset [ axis ] * multiply ;
} ) ;
}
return overflowOffsets ;
}
function computeAutoPlacement ( state , options ) {
if ( options === void 0 ) {
options = { } ;
}
var _options = options ,
placement = _options . placement ,
boundary = _options . boundary ,
rootBoundary = _options . rootBoundary ,
padding = _options . padding ,
flipVariations = _options . flipVariations ,
_options$allowedAutoP = _options . allowedAutoPlacements ,
allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP ;
var variation = getVariation ( placement ) ;
var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements . filter ( function ( placement ) {
return getVariation ( placement ) === variation ;
} ) : basePlacements ;
var allowedPlacements = placements$1 . filter ( function ( placement ) {
return allowedAutoPlacements . indexOf ( placement ) >= 0 ;
} ) ;
if ( allowedPlacements . length === 0 ) {
allowedPlacements = placements$1 ;
if ( process . env . NODE _ENV !== "production" ) {
console . error ( [ 'Popper: The `allowedAutoPlacements` option did not allow any' , 'placements. Ensure the `placement` option matches the variation' , 'of the allowed placements.' , 'For example, "auto" cannot be used to allow "bottom-start".' , 'Use "auto-start" instead.' ] . join ( ' ' ) ) ;
}
} // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...
var overflows = allowedPlacements . reduce ( function ( acc , placement ) {
acc [ placement ] = detectOverflow ( state , {
placement : placement ,
boundary : boundary ,
rootBoundary : rootBoundary ,
padding : padding
} ) [ getBasePlacement ( placement ) ] ;
return acc ;
} , { } ) ;
return Object . keys ( overflows ) . sort ( function ( a , b ) {
return overflows [ a ] - overflows [ b ] ;
} ) ;
}
function getExpandedFallbackPlacements ( placement ) {
if ( getBasePlacement ( placement ) === auto ) {
return [ ] ;
}
var oppositePlacement = getOppositePlacement ( placement ) ;
return [ getOppositeVariationPlacement ( placement ) , oppositePlacement , getOppositeVariationPlacement ( oppositePlacement ) ] ;
}
function flip ( _ref ) {
var state = _ref . state ,
options = _ref . options ,
name = _ref . name ;
if ( state . modifiersData [ name ] . _skip ) {
return ;
}
var _options$mainAxis = options . mainAxis ,
checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis ,
_options$altAxis = options . altAxis ,
checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis ,
specifiedFallbackPlacements = options . fallbackPlacements ,
padding = options . padding ,
boundary = options . boundary ,
rootBoundary = options . rootBoundary ,
altBoundary = options . altBoundary ,
_options$flipVariatio = options . flipVariations ,
flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio ,
allowedAutoPlacements = options . allowedAutoPlacements ;
var preferredPlacement = state . options . placement ;
var basePlacement = getBasePlacement ( preferredPlacement ) ;
var isBasePlacement = basePlacement === preferredPlacement ;
var fallbackPlacements = specifiedFallbackPlacements || ( isBasePlacement || ! flipVariations ? [ getOppositePlacement ( preferredPlacement ) ] : getExpandedFallbackPlacements ( preferredPlacement ) ) ;
var placements = [ preferredPlacement ] . concat ( fallbackPlacements ) . reduce ( function ( acc , placement ) {
return acc . concat ( getBasePlacement ( placement ) === auto ? computeAutoPlacement ( state , {
placement : placement ,
boundary : boundary ,
rootBoundary : rootBoundary ,
padding : padding ,
flipVariations : flipVariations ,
allowedAutoPlacements : allowedAutoPlacements
} ) : placement ) ;
} , [ ] ) ;
var referenceRect = state . rects . reference ;
var popperRect = state . rects . popper ;
var checksMap = new Map ( ) ;
var makeFallbackChecks = true ;
var firstFittingPlacement = placements [ 0 ] ;
for ( var i = 0 ; i < placements . length ; i ++ ) {
var placement = placements [ i ] ;
var _basePlacement = getBasePlacement ( placement ) ;
var isStartVariation = getVariation ( placement ) === start ;
var isVertical = [ top , bottom ] . indexOf ( _basePlacement ) >= 0 ;
var len = isVertical ? 'width' : 'height' ;
var overflow = detectOverflow ( state , {
placement : placement ,
boundary : boundary ,
rootBoundary : rootBoundary ,
altBoundary : altBoundary ,
padding : padding
} ) ;
var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top ;
if ( referenceRect [ len ] > popperRect [ len ] ) {
mainVariationSide = getOppositePlacement ( mainVariationSide ) ;
}
var altVariationSide = getOppositePlacement ( mainVariationSide ) ;
var checks = [ ] ;
if ( checkMainAxis ) {
checks . push ( overflow [ _basePlacement ] <= 0 ) ;
}
if ( checkAltAxis ) {
checks . push ( overflow [ mainVariationSide ] <= 0 , overflow [ altVariationSide ] <= 0 ) ;
}
if ( checks . every ( function ( check ) {
return check ;
} ) ) {
firstFittingPlacement = placement ;
makeFallbackChecks = false ;
break ;
}
checksMap . set ( placement , checks ) ;
}
if ( makeFallbackChecks ) {
// `2` may be desired in some cases – research later
var numberOfChecks = flipVariations ? 3 : 1 ;
var _loop = function _loop ( _i ) {
var fittingPlacement = placements . find ( function ( placement ) {
var checks = checksMap . get ( placement ) ;
if ( checks ) {
return checks . slice ( 0 , _i ) . every ( function ( check ) {
return check ;
} ) ;
}
} ) ;
if ( fittingPlacement ) {
firstFittingPlacement = fittingPlacement ;
return "break" ;
}
} ;
for ( var _i = numberOfChecks ; _i > 0 ; _i -- ) {
var _ret = _loop ( _i ) ;
if ( _ret === "break" ) break ;
}
}
if ( state . placement !== firstFittingPlacement ) {
state . modifiersData [ name ] . _skip = true ;
state . placement = firstFittingPlacement ;
state . reset = true ;
}
} // eslint-disable-next-line import/no-unused-modules
var flip$1 = {
name : 'flip' ,
enabled : true ,
phase : 'main' ,
fn : flip ,
requiresIfExists : [ 'offset' ] ,
data : {
_skip : false
}
} ;
function getSideOffsets ( overflow , rect , preventedOffsets ) {
if ( preventedOffsets === void 0 ) {
preventedOffsets = {
x : 0 ,
y : 0
} ;
}
return {
top : overflow . top - rect . height - preventedOffsets . y ,
right : overflow . right - rect . width + preventedOffsets . x ,
bottom : overflow . bottom - rect . height + preventedOffsets . y ,
left : overflow . left - rect . width - preventedOffsets . x
} ;
}
function isAnySideFullyClipped ( overflow ) {
return [ top , right , bottom , left ] . some ( function ( side ) {
return overflow [ side ] >= 0 ;
} ) ;
}
function hide ( _ref ) {
var state = _ref . state ,
name = _ref . name ;
var referenceRect = state . rects . reference ;
var popperRect = state . rects . popper ;
var preventedOffsets = state . modifiersData . preventOverflow ;
var referenceOverflow = detectOverflow ( state , {
elementContext : 'reference'
} ) ;
var popperAltOverflow = detectOverflow ( state , {
altBoundary : true
} ) ;
var referenceClippingOffsets = getSideOffsets ( referenceOverflow , referenceRect ) ;
var popperEscapeOffsets = getSideOffsets ( popperAltOverflow , popperRect , preventedOffsets ) ;
var isReferenceHidden = isAnySideFullyClipped ( referenceClippingOffsets ) ;
var hasPopperEscaped = isAnySideFullyClipped ( popperEscapeOffsets ) ;
state . modifiersData [ name ] = {
referenceClippingOffsets : referenceClippingOffsets ,
popperEscapeOffsets : popperEscapeOffsets ,
isReferenceHidden : isReferenceHidden ,
hasPopperEscaped : hasPopperEscaped
} ;
state . attributes . popper = Object . assign ( { } , state . attributes . popper , {
'data-popper-reference-hidden' : isReferenceHidden ,
'data-popper-escaped' : hasPopperEscaped
} ) ;
} // eslint-disable-next-line import/no-unused-modules
var hide$1 = {
name : 'hide' ,
enabled : true ,
phase : 'main' ,
requiresIfExists : [ 'preventOverflow' ] ,
fn : hide
} ;
function distanceAndSkiddingToXY ( placement , rects , offset ) {
var basePlacement = getBasePlacement ( placement ) ;
var invertDistance = [ left , top ] . indexOf ( basePlacement ) >= 0 ? - 1 : 1 ;
var _ref = typeof offset === 'function' ? offset ( Object . assign ( { } , rects , {
placement : placement
} ) ) : offset ,
skidding = _ref [ 0 ] ,
distance = _ref [ 1 ] ;
skidding = skidding || 0 ;
distance = ( distance || 0 ) * invertDistance ;
return [ left , right ] . indexOf ( basePlacement ) >= 0 ? {
x : distance ,
y : skidding
} : {
x : skidding ,
y : distance
} ;
}
function offset ( _ref2 ) {
var state = _ref2 . state ,
options = _ref2 . options ,
name = _ref2 . name ;
var _options$offset = options . offset ,
offset = _options$offset === void 0 ? [ 0 , 0 ] : _options$offset ;
var data = placements . reduce ( function ( acc , placement ) {
acc [ placement ] = distanceAndSkiddingToXY ( placement , state . rects , offset ) ;
return acc ;
} , { } ) ;
var _data$state$placement = data [ state . placement ] ,
x = _data$state$placement . x ,
y = _data$state$placement . y ;
if ( state . modifiersData . popperOffsets != null ) {
state . modifiersData . popperOffsets . x += x ;
state . modifiersData . popperOffsets . y += y ;
}
state . modifiersData [ name ] = data ;
} // eslint-disable-next-line import/no-unused-modules
var offset$1 = {
name : 'offset' ,
enabled : true ,
phase : 'main' ,
requires : [ 'popperOffsets' ] ,
fn : offset
} ;
function popperOffsets ( _ref ) {
var state = _ref . state ,
name = _ref . name ;
// Offsets are the actual position the popper needs to have to be
// properly positioned near its reference element
// This is the most basic placement, and will be adjusted by
// the modifiers in the next step
state . modifiersData [ name ] = computeOffsets ( {
reference : state . rects . reference ,
element : state . rects . popper ,
strategy : 'absolute' ,
placement : state . placement
} ) ;
} // eslint-disable-next-line import/no-unused-modules
var popperOffsets$1 = {
name : 'popperOffsets' ,
enabled : true ,
phase : 'read' ,
fn : popperOffsets ,
data : { }
} ;
function getAltAxis ( axis ) {
return axis === 'x' ? 'y' : 'x' ;
}
function preventOverflow ( _ref ) {
var state = _ref . state ,
options = _ref . options ,
name = _ref . name ;
var _options$mainAxis = options . mainAxis ,
checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis ,
_options$altAxis = options . altAxis ,
checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis ,
boundary = options . boundary ,
rootBoundary = options . rootBoundary ,
altBoundary = options . altBoundary ,
padding = options . padding ,
_options$tether = options . tether ,
tether = _options$tether === void 0 ? true : _options$tether ,
_options$tetherOffset = options . tetherOffset ,
tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset ;
var overflow = detectOverflow ( state , {
boundary : boundary ,
rootBoundary : rootBoundary ,
padding : padding ,
altBoundary : altBoundary
} ) ;
var basePlacement = getBasePlacement ( state . placement ) ;
var variation = getVariation ( state . placement ) ;
var isBasePlacement = ! variation ;
var mainAxis = getMainAxisFromPlacement ( basePlacement ) ;
var altAxis = getAltAxis ( mainAxis ) ;
var popperOffsets = state . modifiersData . popperOffsets ;
var referenceRect = state . rects . reference ;
var popperRect = state . rects . popper ;
var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset ( Object . assign ( { } , state . rects , {
placement : state . placement
} ) ) : tetherOffset ;
var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {
mainAxis : tetherOffsetValue ,
altAxis : tetherOffsetValue
} : Object . assign ( {
mainAxis : 0 ,
altAxis : 0
} , tetherOffsetValue ) ;
var offsetModifierState = state . modifiersData . offset ? state . modifiersData . offset [ state . placement ] : null ;
var data = {
x : 0 ,
y : 0
} ;
if ( ! popperOffsets ) {
return ;
}
if ( checkMainAxis ) {
var _offsetModifierState$ ;
var mainSide = mainAxis === 'y' ? top : left ;
var altSide = mainAxis === 'y' ? bottom : right ;
var len = mainAxis === 'y' ? 'height' : 'width' ;
var offset = popperOffsets [ mainAxis ] ;
var min$1 = offset + overflow [ mainSide ] ;
var max$1 = offset - overflow [ altSide ] ;
var additive = tether ? - popperRect [ len ] / 2 : 0 ;
var minLen = variation === start ? referenceRect [ len ] : popperRect [ len ] ;
var maxLen = variation === start ? - popperRect [ len ] : - referenceRect [ len ] ; // We need to include the arrow in the calculation so the arrow doesn't go
// outside the reference bounds
var arrowElement = state . elements . arrow ;
var arrowRect = tether && arrowElement ? getLayoutRect ( arrowElement ) : {
width : 0 ,
height : 0
} ;
var arrowPaddingObject = state . modifiersData [ 'arrow#persistent' ] ? state . modifiersData [ 'arrow#persistent' ] . padding : getFreshSideObject ( ) ;
var arrowPaddingMin = arrowPaddingObject [ mainSide ] ;
var arrowPaddingMax = arrowPaddingObject [ altSide ] ; // If the reference length is smaller than the arrow length, we don't want
// to include its full size in the calculation. If the reference is small
// and near the edge of a boundary, the popper can overflow even if the
// reference is not overflowing as well (e.g. virtual elements with no
// width or height)
var arrowLen = within ( 0 , referenceRect [ len ] , arrowRect [ len ] ) ;
var minOffset = isBasePlacement ? referenceRect [ len ] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue . mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue . mainAxis ;
var maxOffset = isBasePlacement ? - referenceRect [ len ] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue . mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue . mainAxis ;
var arrowOffsetParent = state . elements . arrow && getOffsetParent ( state . elements . arrow ) ;
var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent . clientTop || 0 : arrowOffsetParent . clientLeft || 0 : 0 ;
var offsetModifierValue = ( _offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState [ mainAxis ] ) != null ? _offsetModifierState$ : 0 ;
var tetherMin = offset + minOffset - offsetModifierValue - clientOffset ;
var tetherMax = offset + maxOffset - offsetModifierValue ;
var preventedOffset = within ( tether ? min ( min$1 , tetherMin ) : min$1 , offset , tether ? max ( max$1 , tetherMax ) : max$1 ) ;
popperOffsets [ mainAxis ] = preventedOffset ;
data [ mainAxis ] = preventedOffset - offset ;
}
if ( checkAltAxis ) {
var _offsetModifierState$2 ;
var _mainSide = mainAxis === 'x' ? top : left ;
var _altSide = mainAxis === 'x' ? bottom : right ;
var _offset = popperOffsets [ altAxis ] ;
var _len = altAxis === 'y' ? 'height' : 'width' ;
var _min = _offset + overflow [ _mainSide ] ;
var _max = _offset - overflow [ _altSide ] ;
var isOriginSide = [ top , left ] . indexOf ( basePlacement ) !== - 1 ;
var _offsetModifierValue = ( _offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState [ altAxis ] ) != null ? _offsetModifierState$2 : 0 ;
var _tetherMin = isOriginSide ? _min : _offset - referenceRect [ _len ] - popperRect [ _len ] - _offsetModifierValue + normalizedTetherOffsetValue . altAxis ;
var _tetherMax = isOriginSide ? _offset + referenceRect [ _len ] + popperRect [ _len ] - _offsetModifierValue - normalizedTetherOffsetValue . altAxis : _max ;
var _preventedOffset = tether && isOriginSide ? withinMaxClamp ( _tetherMin , _offset , _tetherMax ) : within ( tether ? _tetherMin : _min , _offset , tether ? _tetherMax : _max ) ;
popperOffsets [ altAxis ] = _preventedOffset ;
data [ altAxis ] = _preventedOffset - _offset ;
}
state . modifiersData [ name ] = data ;
} // eslint-disable-next-line import/no-unused-modules
var preventOverflow$1 = {
name : 'preventOverflow' ,
enabled : true ,
phase : 'main' ,
fn : preventOverflow ,
requiresIfExists : [ 'offset' ]
} ;
function getHTMLElementScroll ( element ) {
return {
scrollLeft : element . scrollLeft ,
scrollTop : element . scrollTop
} ;
}
function getNodeScroll ( node ) {
if ( node === getWindow ( node ) || ! isHTMLElement ( node ) ) {
return getWindowScroll ( node ) ;
} else {
return getHTMLElementScroll ( node ) ;
}
}
function isElementScaled ( element ) {
var rect = element . getBoundingClientRect ( ) ;
var scaleX = round ( rect . width ) / element . offsetWidth || 1 ;
var scaleY = round ( rect . height ) / element . offsetHeight || 1 ;
return scaleX !== 1 || scaleY !== 1 ;
} // Returns the composite rect of an element relative to its offsetParent.
// Composite means it takes into account transforms as well as layout.
function getCompositeRect ( elementOrVirtualElement , offsetParent , isFixed ) {
if ( isFixed === void 0 ) {
isFixed = false ;
}
var isOffsetParentAnElement = isHTMLElement ( offsetParent ) ;
var offsetParentIsScaled = isHTMLElement ( offsetParent ) && isElementScaled ( offsetParent ) ;
var documentElement = getDocumentElement ( offsetParent ) ;
var rect = getBoundingClientRect ( elementOrVirtualElement , offsetParentIsScaled ) ;
var scroll = {
scrollLeft : 0 ,
scrollTop : 0
} ;
var offsets = {
x : 0 ,
y : 0
} ;
if ( isOffsetParentAnElement || ! isOffsetParentAnElement && ! isFixed ) {
if ( getNodeName ( offsetParent ) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078
isScrollParent ( documentElement ) ) {
scroll = getNodeScroll ( offsetParent ) ;
}
if ( isHTMLElement ( offsetParent ) ) {
offsets = getBoundingClientRect ( offsetParent , true ) ;
offsets . x += offsetParent . clientLeft ;
offsets . y += offsetParent . clientTop ;
} else if ( documentElement ) {
offsets . x = getWindowScrollBarX ( documentElement ) ;
}
}
return {
x : rect . left + scroll . scrollLeft - offsets . x ,
y : rect . top + scroll . scrollTop - offsets . y ,
width : rect . width ,
height : rect . height
} ;
}
function order ( modifiers ) {
var map = new Map ( ) ;
var visited = new Set ( ) ;
var result = [ ] ;
modifiers . forEach ( function ( modifier ) {
map . set ( modifier . name , modifier ) ;
} ) ; // On visiting object, check for its dependencies and visit them recursively
function sort ( modifier ) {
visited . add ( modifier . name ) ;
var requires = [ ] . concat ( modifier . requires || [ ] , modifier . requiresIfExists || [ ] ) ;
requires . forEach ( function ( dep ) {
if ( ! visited . has ( dep ) ) {
var depModifier = map . get ( dep ) ;
if ( depModifier ) {
sort ( depModifier ) ;
}
}
} ) ;
result . push ( modifier ) ;
}
modifiers . forEach ( function ( modifier ) {
if ( ! visited . has ( modifier . name ) ) {
// check for visited object
sort ( modifier ) ;
}
} ) ;
return result ;
}
function orderModifiers ( modifiers ) {
// order based on dependencies
var orderedModifiers = order ( modifiers ) ; // order based on phase
return modifierPhases . reduce ( function ( acc , phase ) {
return acc . concat ( orderedModifiers . filter ( function ( modifier ) {
return modifier . phase === phase ;
} ) ) ;
} , [ ] ) ;
}
function debounce ( fn ) {
var pending ;
return function ( ) {
if ( ! pending ) {
pending = new Promise ( function ( resolve ) {
Promise . resolve ( ) . then ( function ( ) {
pending = undefined ;
resolve ( fn ( ) ) ;
} ) ;
} ) ;
}
return pending ;
} ;
}
function format ( str ) {
for ( var _len = arguments . length , args = new Array ( _len > 1 ? _len - 1 : 0 ) , _key = 1 ; _key < _len ; _key ++ ) {
args [ _key - 1 ] = arguments [ _key ] ;
}
return [ ] . concat ( args ) . reduce ( function ( p , c ) {
return p . replace ( /%s/ , c ) ;
} , str ) ;
}
var INVALID _MODIFIER _ERROR = 'Popper: modifier "%s" provided an invalid %s property, expected %s but got %s' ;
var MISSING _DEPENDENCY _ERROR = 'Popper: modifier "%s" requires "%s", but "%s" modifier is not available' ;
var VALID _PROPERTIES = [ 'name' , 'enabled' , 'phase' , 'fn' , 'effect' , 'requires' , 'options' ] ;
function validateModifiers ( modifiers ) {
modifiers . forEach ( function ( modifier ) {
[ ] . concat ( Object . keys ( modifier ) , VALID _PROPERTIES ) // IE11-compatible replacement for `new Set(iterable)`
. filter ( function ( value , index , self ) {
return self . indexOf ( value ) === index ;
} ) . forEach ( function ( key ) {
switch ( key ) {
case 'name' :
if ( typeof modifier . name !== 'string' ) {
console . error ( format ( INVALID _MODIFIER _ERROR , String ( modifier . name ) , '"name"' , '"string"' , "\"" + String ( modifier . name ) + "\"" ) ) ;
}
break ;
case 'enabled' :
if ( typeof modifier . enabled !== 'boolean' ) {
console . error ( format ( INVALID _MODIFIER _ERROR , modifier . name , '"enabled"' , '"boolean"' , "\"" + String ( modifier . enabled ) + "\"" ) ) ;
}
break ;
case 'phase' :
if ( modifierPhases . indexOf ( modifier . phase ) < 0 ) {
console . error ( format ( INVALID _MODIFIER _ERROR , modifier . name , '"phase"' , "either " + modifierPhases . join ( ', ' ) , "\"" + String ( modifier . phase ) + "\"" ) ) ;
}
break ;
case 'fn' :
if ( typeof modifier . fn !== 'function' ) {
console . error ( format ( INVALID _MODIFIER _ERROR , modifier . name , '"fn"' , '"function"' , "\"" + String ( modifier . fn ) + "\"" ) ) ;
}
break ;
case 'effect' :
if ( modifier . effect != null && typeof modifier . effect !== 'function' ) {
console . error ( format ( INVALID _MODIFIER _ERROR , modifier . name , '"effect"' , '"function"' , "\"" + String ( modifier . fn ) + "\"" ) ) ;
}
break ;
case 'requires' :
if ( modifier . requires != null && ! Array . isArray ( modifier . requires ) ) {
console . error ( format ( INVALID _MODIFIER _ERROR , modifier . name , '"requires"' , '"array"' , "\"" + String ( modifier . requires ) + "\"" ) ) ;
}
break ;
case 'requiresIfExists' :
if ( ! Array . isArray ( modifier . requiresIfExists ) ) {
console . error ( format ( INVALID _MODIFIER _ERROR , modifier . name , '"requiresIfExists"' , '"array"' , "\"" + String ( modifier . requiresIfExists ) + "\"" ) ) ;
}
break ;
case 'options' :
case 'data' :
break ;
default :
console . error ( "PopperJS: an invalid property has been provided to the \"" + modifier . name + "\" modifier, valid properties are " + VALID _PROPERTIES . map ( function ( s ) {
return "\"" + s + "\"" ;
} ) . join ( ', ' ) + "; but \"" + key + "\" was provided." ) ;
}
modifier . requires && modifier . requires . forEach ( function ( requirement ) {
if ( modifiers . find ( function ( mod ) {
return mod . name === requirement ;
} ) == null ) {
console . error ( format ( MISSING _DEPENDENCY _ERROR , String ( modifier . name ) , requirement , requirement ) ) ;
}
} ) ;
} ) ;
} ) ;
}
function uniqueBy ( arr , fn ) {
var identifiers = new Set ( ) ;
return arr . filter ( function ( item ) {
var identifier = fn ( item ) ;
if ( ! identifiers . has ( identifier ) ) {
identifiers . add ( identifier ) ;
return true ;
}
} ) ;
}
function mergeByName ( modifiers ) {
var merged = modifiers . reduce ( function ( merged , current ) {
var existing = merged [ current . name ] ;
merged [ current . name ] = existing ? Object . assign ( { } , existing , current , {
options : Object . assign ( { } , existing . options , current . options ) ,
data : Object . assign ( { } , existing . data , current . data )
} ) : current ;
return merged ;
} , { } ) ; // IE11 does not support Object.values
return Object . keys ( merged ) . map ( function ( key ) {
return merged [ key ] ;
} ) ;
}
var INVALID _ELEMENT _ERROR = 'Popper: Invalid reference or popper argument provided. They must be either a DOM element or virtual element.' ;
var INFINITE _LOOP _ERROR = 'Popper: An infinite loop in the modifiers cycle has been detected! The cycle has been interrupted to prevent a browser crash.' ;
var DEFAULT _OPTIONS = {
placement : 'bottom' ,
modifiers : [ ] ,
strategy : 'absolute'
} ;
function areValidElements ( ) {
for ( var _len = arguments . length , args = new Array ( _len ) , _key = 0 ; _key < _len ; _key ++ ) {
args [ _key ] = arguments [ _key ] ;
}
return ! args . some ( function ( element ) {
return ! ( element && typeof element . getBoundingClientRect === 'function' ) ;
} ) ;
}
function popperGenerator ( generatorOptions ) {
if ( generatorOptions === void 0 ) {
generatorOptions = { } ;
}
var _generatorOptions = generatorOptions ,
_generatorOptions$def = _generatorOptions . defaultModifiers ,
defaultModifiers = _generatorOptions$def === void 0 ? [ ] : _generatorOptions$def ,
_generatorOptions$def2 = _generatorOptions . defaultOptions ,
defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT _OPTIONS : _generatorOptions$def2 ;
return function createPopper ( reference , popper , options ) {
if ( options === void 0 ) {
options = defaultOptions ;
}
var state = {
placement : 'bottom' ,
orderedModifiers : [ ] ,
options : Object . assign ( { } , DEFAULT _OPTIONS , defaultOptions ) ,
modifiersData : { } ,
elements : {
reference : reference ,
popper : popper
} ,
attributes : { } ,
styles : { }
} ;
var effectCleanupFns = [ ] ;
var isDestroyed = false ;
var instance = {
state : state ,
setOptions : function setOptions ( setOptionsAction ) {
var options = typeof setOptionsAction === 'function' ? setOptionsAction ( state . options ) : setOptionsAction ;
cleanupModifierEffects ( ) ;
state . options = Object . assign ( { } , defaultOptions , state . options , options ) ;
state . scrollParents = {
reference : isElement ( reference ) ? listScrollParents ( reference ) : reference . contextElement ? listScrollParents ( reference . contextElement ) : [ ] ,
popper : listScrollParents ( popper )
} ; // Orders the modifiers based on their dependencies and `phase`
// properties
var orderedModifiers = orderModifiers ( mergeByName ( [ ] . concat ( defaultModifiers , state . options . modifiers ) ) ) ; // Strip out disabled modifiers
state . orderedModifiers = orderedModifiers . filter ( function ( m ) {
return m . enabled ;
} ) ; // Validate the provided modifiers so that the consumer will get warned
// if one of the modifiers is invalid for any reason
if ( process . env . NODE _ENV !== "production" ) {
var modifiers = uniqueBy ( [ ] . concat ( orderedModifiers , state . options . modifiers ) , function ( _ref ) {
var name = _ref . name ;
return name ;
} ) ;
validateModifiers ( modifiers ) ;
if ( getBasePlacement ( state . options . placement ) === auto ) {
var flipModifier = state . orderedModifiers . find ( function ( _ref2 ) {
var name = _ref2 . name ;
return name === 'flip' ;
} ) ;
if ( ! flipModifier ) {
console . error ( [ 'Popper: "auto" placements require the "flip" modifier be' , 'present and enabled to work.' ] . join ( ' ' ) ) ;
}
}
var _getComputedStyle = getComputedStyle ( popper ) ,
marginTop = _getComputedStyle . marginTop ,
marginRight = _getComputedStyle . marginRight ,
marginBottom = _getComputedStyle . marginBottom ,
marginLeft = _getComputedStyle . marginLeft ; // We no longer take into account `margins` on the popper, and it can
// cause bugs with positioning, so we'll warn the consumer
if ( [ marginTop , marginRight , marginBottom , marginLeft ] . some ( function ( margin ) {
return parseFloat ( margin ) ;
} ) ) {
console . warn ( [ 'Popper: CSS "margin" styles cannot be used to apply padding' , 'between the popper and its reference element or boundary.' , 'To replicate margin, use the `offset` modifier, as well as' , 'the `padding` option in the `preventOverflow` and `flip`' , 'modifiers.' ] . join ( ' ' ) ) ;
}
}
runModifierEffects ( ) ;
return instance . update ( ) ;
} ,
// Sync update – it will always be executed, even if not necessary. This
// is useful for low frequency updates where sync behavior simplifies the
// logic.
// For high frequency updates (e.g. `resize` and `scroll` events), always
// prefer the async Popper#update method
forceUpdate : function forceUpdate ( ) {
if ( isDestroyed ) {
return ;
}
var _state$elements = state . elements ,
reference = _state$elements . reference ,
popper = _state$elements . popper ; // Don't proceed if `reference` or `popper` are not valid elements
// anymore
if ( ! areValidElements ( reference , popper ) ) {
if ( process . env . NODE _ENV !== "production" ) {
console . error ( INVALID _ELEMENT _ERROR ) ;
}
return ;
} // Store the reference and popper rects to be read by modifiers
state . rects = {
reference : getCompositeRect ( reference , getOffsetParent ( popper ) , state . options . strategy === 'fixed' ) ,
popper : getLayoutRect ( popper )
} ; // Modifiers have the ability to reset the current update cycle. The
// most common use case for this is the `flip` modifier changing the
// placement, which then needs to re-run all the modifiers, because the
// logic was previously ran for the previous placement and is therefore
// stale/incorrect
state . reset = false ;
state . placement = state . options . placement ; // On each update cycle, the `modifiersData` property for each modifier
// is filled with the initial data specified by the modifier. This means
// it doesn't persist and is fresh on each update.
// To ensure persistent data, use `${name}#persistent`
state . orderedModifiers . forEach ( function ( modifier ) {
return state . modifiersData [ modifier . name ] = Object . assign ( { } , modifier . data ) ;
} ) ;
var _ _debug _loops _ _ = 0 ;
for ( var index = 0 ; index < state . orderedModifiers . length ; index ++ ) {
if ( process . env . NODE _ENV !== "production" ) {
_ _debug _loops _ _ += 1 ;
if ( _ _debug _loops _ _ > 100 ) {
console . error ( INFINITE _LOOP _ERROR ) ;
break ;
}
}
if ( state . reset === true ) {
state . reset = false ;
index = - 1 ;
continue ;
}
var _state$orderedModifie = state . orderedModifiers [ index ] ,
fn = _state$orderedModifie . fn ,
_state$orderedModifie2 = _state$orderedModifie . options ,
_options = _state$orderedModifie2 === void 0 ? { } : _state$orderedModifie2 ,
name = _state$orderedModifie . name ;
if ( typeof fn === 'function' ) {
state = fn ( {
state : state ,
options : _options ,
name : name ,
instance : instance
} ) || state ;
}
}
} ,
// Async and optimistically optimized update – it will not be executed if
// not necessary (debounced to run at most once-per-tick)
update : debounce ( function ( ) {
return new Promise ( function ( resolve ) {
instance . forceUpdate ( ) ;
resolve ( state ) ;
} ) ;
} ) ,
destroy : function destroy ( ) {
cleanupModifierEffects ( ) ;
isDestroyed = true ;
}
} ;
if ( ! areValidElements ( reference , popper ) ) {
if ( process . env . NODE _ENV !== "production" ) {
console . error ( INVALID _ELEMENT _ERROR ) ;
}
return instance ;
}
instance . setOptions ( options ) . then ( function ( state ) {
if ( ! isDestroyed && options . onFirstUpdate ) {
options . onFirstUpdate ( state ) ;
}
} ) ; // Modifiers have the ability to execute arbitrary code before the first
// update cycle runs. They will be executed in the same order as the update
// cycle. This is useful when a modifier adds some persistent data that
// other modifiers need to use, but the modifier is run after the dependent
// one.
function runModifierEffects ( ) {
state . orderedModifiers . forEach ( function ( _ref3 ) {
var name = _ref3 . name ,
_ref3$options = _ref3 . options ,
options = _ref3$options === void 0 ? { } : _ref3$options ,
effect = _ref3 . effect ;
if ( typeof effect === 'function' ) {
var cleanupFn = effect ( {
state : state ,
name : name ,
instance : instance ,
options : options
} ) ;
var noopFn = function noopFn ( ) { } ;
effectCleanupFns . push ( cleanupFn || noopFn ) ;
}
} ) ;
}
function cleanupModifierEffects ( ) {
effectCleanupFns . forEach ( function ( fn ) {
return fn ( ) ;
} ) ;
effectCleanupFns = [ ] ;
}
return instance ;
} ;
}
var defaultModifiers = [ eventListeners , popperOffsets$1 , computeStyles$1 , applyStyles$1 , offset$1 , flip$1 , preventOverflow$1 , arrow$1 , hide$1 ] ;
var createPopper = /*#__PURE__*/ popperGenerator ( {
defaultModifiers : defaultModifiers
} ) ; // eslint-disable-next-line import/no-unused-modules
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
const wrapAround = ( value , size ) => {
return ( ( value % size ) + size ) % size ;
} ;
class Suggest {
constructor ( owner , containerEl , scope ) {
this . owner = owner ;
this . containerEl = containerEl ;
containerEl . on ( "click" , ".suggestion-item" , this . onSuggestionClick . bind ( this ) ) ;
containerEl . on ( "mousemove" , ".suggestion-item" , this . onSuggestionMouseover . bind ( this ) ) ;
scope . register ( [ ] , "ArrowUp" , ( event ) => {
if ( ! event . isComposing ) {
this . setSelectedItem ( this . selectedItem - 1 , true ) ;
return false ;
}
} ) ;
scope . register ( [ ] , "ArrowDown" , ( event ) => {
if ( ! event . isComposing ) {
this . setSelectedItem ( this . selectedItem + 1 , true ) ;
return false ;
}
} ) ;
scope . register ( [ ] , "Enter" , ( event ) => {
if ( ! event . isComposing ) {
this . useSelectedItem ( event ) ;
return false ;
}
} ) ;
}
onSuggestionClick ( event , el ) {
event . preventDefault ( ) ;
const item = this . suggestions . indexOf ( el ) ;
this . setSelectedItem ( item , false ) ;
this . useSelectedItem ( event ) ;
}
onSuggestionMouseover ( _event , el ) {
const item = this . suggestions . indexOf ( el ) ;
this . setSelectedItem ( item , false ) ;
}
setSuggestions ( values ) {
this . containerEl . empty ( ) ;
const suggestionEls = [ ] ;
values . forEach ( ( value ) => {
const suggestionEl = this . containerEl . createDiv ( "suggestion-item" ) ;
this . owner . renderSuggestion ( value , suggestionEl ) ;
suggestionEls . push ( suggestionEl ) ;
} ) ;
this . values = values ;
this . suggestions = suggestionEls ;
this . setSelectedItem ( 0 , false ) ;
}
useSelectedItem ( event ) {
const currentValue = this . values [ this . selectedItem ] ;
if ( currentValue ) {
this . owner . selectSuggestion ( currentValue , event ) ;
}
}
setSelectedItem ( selectedIndex , scrollIntoView ) {
const normalizedIndex = wrapAround ( selectedIndex , this . suggestions . length ) ;
const prevSelectedSuggestion = this . suggestions [ this . selectedItem ] ;
const selectedSuggestion = this . suggestions [ normalizedIndex ] ;
prevSelectedSuggestion === null || prevSelectedSuggestion === void 0 ? void 0 : prevSelectedSuggestion . removeClass ( "is-selected" ) ;
selectedSuggestion === null || selectedSuggestion === void 0 ? void 0 : selectedSuggestion . addClass ( "is-selected" ) ;
this . selectedItem = normalizedIndex ;
if ( scrollIntoView ) {
selectedSuggestion . scrollIntoView ( false ) ;
}
}
}
class TextInputSuggest {
constructor ( app , inputEl ) {
this . app = app ;
this . inputEl = inputEl ;
this . scope = new obsidian _module . Scope ( ) ;
this . suggestEl = createDiv ( "suggestion-container" ) ;
const suggestion = this . suggestEl . createDiv ( "suggestion" ) ;
this . suggest = new Suggest ( this , suggestion , this . scope ) ;
this . scope . register ( [ ] , "Escape" , this . close . bind ( this ) ) ;
this . inputEl . addEventListener ( "input" , this . onInputChanged . bind ( this ) ) ;
this . inputEl . addEventListener ( "focus" , this . onInputChanged . bind ( this ) ) ;
this . inputEl . addEventListener ( "blur" , this . close . bind ( this ) ) ;
this . suggestEl . on ( "mousedown" , ".suggestion-container" , ( event ) => {
event . preventDefault ( ) ;
} ) ;
}
onInputChanged ( ) {
const inputStr = this . inputEl . value ;
const suggestions = this . getSuggestions ( inputStr ) ;
if ( ! suggestions ) {
this . close ( ) ;
return ;
}
if ( suggestions . length > 0 ) {
this . suggest . setSuggestions ( suggestions ) ;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this . open ( this . app . dom . appContainerEl , this . inputEl ) ;
}
else {
this . close ( ) ;
}
}
open ( container , inputEl ) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this . app . keymap . pushScope ( this . scope ) ;
container . appendChild ( this . suggestEl ) ;
this . popper = createPopper ( inputEl , this . suggestEl , {
placement : "bottom-start" ,
modifiers : [
{
name : "sameWidth" ,
enabled : true ,
fn : ( { state , instance } ) => {
// Note: positioning needs to be calculated twice -
// first pass - positioning it according to the width of the popper
// second pass - position it with the width bound to the reference element
// we need to early exit to avoid an infinite loop
const targetWidth = ` ${ state . rects . reference . width } px ` ;
if ( state . styles . popper . width === targetWidth ) {
return ;
}
state . styles . popper . width = targetWidth ;
instance . update ( ) ;
} ,
phase : "beforeWrite" ,
requires : [ "computeStyles" ] ,
} ,
] ,
} ) ;
}
close ( ) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this . app . keymap . popScope ( this . scope ) ;
this . suggest . setSuggestions ( [ ] ) ;
if ( this . popper )
this . popper . destroy ( ) ;
this . suggestEl . detach ( ) ;
}
}
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
class FolderSuggest extends TextInputSuggest {
getSuggestions ( inputStr ) {
const abstractFiles = this . app . vault . getAllLoadedFiles ( ) ;
const folders = [ ] ;
const lowerCaseInputStr = inputStr . toLowerCase ( ) ;
abstractFiles . forEach ( ( folder ) => {
if ( folder instanceof obsidian _module . TFolder &&
folder . path . toLowerCase ( ) . contains ( lowerCaseInputStr ) ) {
folders . push ( folder ) ;
}
} ) ;
return folders ;
}
renderSuggestion ( file , el ) {
el . setText ( file . path ) ;
}
selectSuggestion ( file ) {
this . inputEl . value = file . path ;
this . inputEl . trigger ( "input" ) ;
this . close ( ) ;
}
}
function delay ( ms ) {
return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
}
function escape _RegExp ( str ) {
return str . replace ( /[.*+?^${}()|[\]\\]/g , "\\$&" ) ; // $& means the whole matched string
}
function generate _dynamic _command _regex ( ) {
return /(<%(?:-|_)?\s*[*~]{0,1})\+((?:.|\s)*?%>)/g ;
}
function resolve _tfolder ( app , folder _str ) {
folder _str = obsidian _module . normalizePath ( folder _str ) ;
const folder = app . vault . getAbstractFileByPath ( folder _str ) ;
if ( ! folder ) {
throw new TemplaterError ( ` Folder " ${ folder _str } " doesn't exist ` ) ;
}
if ( ! ( folder instanceof obsidian _module . TFolder ) ) {
throw new TemplaterError ( ` ${ folder _str } is a file, not a folder ` ) ;
}
return folder ;
}
function resolve _tfile ( app , file _str ) {
file _str = obsidian _module . normalizePath ( file _str ) ;
const file = app . vault . getAbstractFileByPath ( file _str ) ;
if ( ! file ) {
throw new TemplaterError ( ` File " ${ file _str } " doesn't exist ` ) ;
}
if ( ! ( file instanceof obsidian _module . TFile ) ) {
throw new TemplaterError ( ` ${ file _str } is a folder, not a file ` ) ;
}
return file ;
}
function get _tfiles _from _folder ( app , folder _str ) {
const folder = resolve _tfolder ( app , folder _str ) ;
const files = [ ] ;
obsidian _module . Vault . recurseChildren ( folder , ( file ) => {
if ( file instanceof obsidian _module . TFile ) {
files . push ( file ) ;
}
} ) ;
files . sort ( ( a , b ) => {
return a . basename . localeCompare ( b . basename ) ;
} ) ;
return files ;
}
function arraymove ( arr , fromIndex , toIndex ) {
if ( toIndex < 0 || toIndex === arr . length ) {
return ;
}
const element = arr [ fromIndex ] ;
arr [ fromIndex ] = arr [ toIndex ] ;
arr [ toIndex ] = element ;
}
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes
var FileSuggestMode ;
( function ( FileSuggestMode ) {
FileSuggestMode [ FileSuggestMode [ "TemplateFiles" ] = 0 ] = "TemplateFiles" ;
FileSuggestMode [ FileSuggestMode [ "ScriptFiles" ] = 1 ] = "ScriptFiles" ;
} ) ( FileSuggestMode || ( FileSuggestMode = { } ) ) ;
class FileSuggest extends TextInputSuggest {
constructor ( app , inputEl , plugin , mode ) {
super ( app , inputEl ) ;
this . app = app ;
this . inputEl = inputEl ;
this . plugin = plugin ;
this . mode = mode ;
}
get _folder ( mode ) {
switch ( mode ) {
case FileSuggestMode . TemplateFiles :
return this . plugin . settings . templates _folder ;
case FileSuggestMode . ScriptFiles :
return this . plugin . settings . user _scripts _folder ;
}
}
get _error _msg ( mode ) {
switch ( mode ) {
case FileSuggestMode . TemplateFiles :
return ` Templates folder doesn't exist ` ;
case FileSuggestMode . ScriptFiles :
return ` User Scripts folder doesn't exist ` ;
}
}
getSuggestions ( input _str ) {
const all _files = errorWrapperSync ( ( ) => get _tfiles _from _folder ( this . app , this . get _folder ( this . mode ) ) , this . get _error _msg ( this . mode ) ) ;
if ( ! all _files ) {
return [ ] ;
}
const files = [ ] ;
const lower _input _str = input _str . toLowerCase ( ) ;
all _files . forEach ( ( file ) => {
if ( file instanceof obsidian _module . TFile &&
file . extension === "md" &&
file . path . toLowerCase ( ) . contains ( lower _input _str ) ) {
files . push ( file ) ;
}
} ) ;
return files ;
}
renderSuggestion ( file , el ) {
el . setText ( file . path ) ;
}
selectSuggestion ( file ) {
this . inputEl . value = file . path ;
this . inputEl . trigger ( "input" ) ;
this . close ( ) ;
}
}
const DEFAULT _SETTINGS = {
command _timeout : 5 ,
templates _folder : "" ,
templates _pairs : [ [ "" , "" ] ] ,
trigger _on _file _creation : false ,
auto _jump _to _cursor : false ,
enable _system _commands : false ,
shell _path : "" ,
user _scripts _folder : "" ,
enable _folder _templates : true ,
folder _templates : [ { folder : "" , template : "" } ] ,
syntax _highlighting : true ,
enabled _templates _hotkeys : [ "" ] ,
startup _templates : [ "" ] ,
} ;
class TemplaterSettingTab extends obsidian _module . PluginSettingTab {
constructor ( app , plugin ) {
super ( app , plugin ) ;
this . app = app ;
this . plugin = plugin ;
}
display ( ) {
this . containerEl . empty ( ) ;
this . add _general _setting _header ( ) ;
this . add _template _folder _setting ( ) ;
this . add _internal _functions _setting ( ) ;
this . add _syntax _highlighting _setting ( ) ;
this . add _auto _jump _to _cursor ( ) ;
this . add _trigger _on _new _file _creation _setting ( ) ;
this . add _templates _hotkeys _setting ( ) ;
if ( this . plugin . settings . trigger _on _file _creation ) {
this . add _folder _templates _setting ( ) ;
}
this . add _startup _templates _setting ( ) ;
this . add _user _script _functions _setting ( ) ;
this . add _user _system _command _functions _setting ( ) ;
}
add _general _setting _header ( ) {
this . containerEl . createEl ( "h2" , { text : "General Settings" } ) ;
}
add _template _folder _setting ( ) {
new obsidian _module . Setting ( this . containerEl )
. setName ( "Template folder location" )
. setDesc ( "Files in this folder will be available as templates." )
. addSearch ( ( cb ) => {
new FolderSuggest ( this . app , cb . inputEl ) ;
cb . setPlaceholder ( "Example: folder1/folder2" )
. setValue ( this . plugin . settings . templates _folder )
. onChange ( ( new _folder ) => {
this . plugin . settings . templates _folder = new _folder ;
this . plugin . save _settings ( ) ;
} ) ;
// @ts-ignore
cb . containerEl . addClass ( "templater_search" ) ;
} ) ;
}
add _internal _functions _setting ( ) {
const desc = document . createDocumentFragment ( ) ;
desc . append ( "Templater provides multiples predefined variables / functions that you can use." , desc . createEl ( "br" ) , "Check the " , desc . createEl ( "a" , {
href : "https://silentvoid13.github.io/Templater/" ,
text : "documentation" ,
} ) , " to get a list of all the available internal variables / functions." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Internal Variables and Functions" )
. setDesc ( desc ) ;
}
add _syntax _highlighting _setting ( ) {
const desc = document . createDocumentFragment ( ) ;
desc . append ( "Adds syntax highlighting for Templater commands in edit mode." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Syntax Highlighting" )
. setDesc ( desc )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . settings . syntax _highlighting )
. onChange ( ( syntax _highlighting ) => {
this . plugin . settings . syntax _highlighting =
syntax _highlighting ;
this . plugin . save _settings ( ) ;
this . plugin . event _handler . update _syntax _highlighting ( ) ;
} ) ;
} ) ;
}
add _auto _jump _to _cursor ( ) {
const desc = document . createDocumentFragment ( ) ;
desc . append ( "Automatically triggers " , desc . createEl ( "code" , { text : "tp.file.cursor" } ) , " after inserting a template." , desc . createEl ( "br" ) , "You can also set a hotkey to manually trigger " , desc . createEl ( "code" , { text : "tp.file.cursor" } ) , "." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Automatic jump to cursor" )
. setDesc ( desc )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . settings . auto _jump _to _cursor )
. onChange ( ( auto _jump _to _cursor ) => {
this . plugin . settings . auto _jump _to _cursor =
auto _jump _to _cursor ;
this . plugin . save _settings ( ) ;
} ) ;
} ) ;
}
add _trigger _on _new _file _creation _setting ( ) {
const desc = document . createDocumentFragment ( ) ;
desc . append ( "Templater will listen for the new file creation event, and replace every command it finds in the new file's content." , desc . createEl ( "br" ) , "This makes Templater compatible with other plugins like the Daily note core plugin, Calendar plugin, Review plugin, Note refactor plugin, ..." , desc . createEl ( "br" ) , desc . createEl ( "b" , {
text : "Warning: " ,
} ) , "This can be dangerous if you create new files with unknown / unsafe content on creation. Make sure that every new file's content is safe on creation." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Trigger Templater on new file creation" )
. setDesc ( desc )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . settings . trigger _on _file _creation )
. onChange ( ( trigger _on _file _creation ) => {
this . plugin . settings . trigger _on _file _creation =
trigger _on _file _creation ;
this . plugin . save _settings ( ) ;
this . plugin . event _handler . update _trigger _file _on _creation ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
}
add _templates _hotkeys _setting ( ) {
this . containerEl . createEl ( "h2" , { text : "Template Hotkeys" } ) ;
const desc = document . createDocumentFragment ( ) ;
desc . append ( "Template Hotkeys allows you to bind a template to a hotkey." ) ;
new obsidian _module . Setting ( this . containerEl ) . setDesc ( desc ) ;
this . plugin . settings . enabled _templates _hotkeys . forEach ( ( template , index ) => {
const s = new obsidian _module . Setting ( this . containerEl )
. addSearch ( ( cb ) => {
new FileSuggest ( this . app , cb . inputEl , this . plugin , FileSuggestMode . TemplateFiles ) ;
cb . setPlaceholder ( "Example: folder1/template_file" )
. setValue ( template )
. onChange ( ( new _template ) => {
if ( new _template &&
this . plugin . settings . enabled _templates _hotkeys . contains ( new _template ) ) {
log _error ( new TemplaterError ( "This template is already bound to a hotkey" ) ) ;
return ;
}
this . plugin . command _handler . add _template _hotkey ( this . plugin . settings
. enabled _templates _hotkeys [ index ] , new _template ) ;
this . plugin . settings . enabled _templates _hotkeys [ index ] = new _template ;
this . plugin . save _settings ( ) ;
} ) ;
// @ts-ignore
cb . containerEl . addClass ( "templater_search" ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "any-key" )
. setTooltip ( "Configure Hotkey" )
. onClick ( ( ) => {
// TODO: Replace with future "official" way to do this
// @ts-ignore
this . app . setting . openTabById ( "hotkeys" ) ;
// @ts-ignore
const tab = this . app . setting . activeTab ;
tab . searchInputEl . value = "Templater: Insert" ;
tab . updateHotkeyVisibility ( ) ;
} ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "up-chevron-glyph" )
. setTooltip ( "Move up" )
. onClick ( ( ) => {
arraymove ( this . plugin . settings
. enabled _templates _hotkeys , index , index - 1 ) ;
this . plugin . save _settings ( ) ;
this . display ( ) ;
} ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "down-chevron-glyph" )
. setTooltip ( "Move down" )
. onClick ( ( ) => {
arraymove ( this . plugin . settings
. enabled _templates _hotkeys , index , index + 1 ) ;
this . plugin . save _settings ( ) ;
this . display ( ) ;
} ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "cross" )
. setTooltip ( "Delete" )
. onClick ( ( ) => {
this . plugin . command _handler . remove _template _hotkey ( this . plugin . settings
. enabled _templates _hotkeys [ index ] ) ;
this . plugin . settings . enabled _templates _hotkeys . splice ( index , 1 ) ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
s . infoEl . remove ( ) ;
} ) ;
new obsidian _module . Setting ( this . containerEl ) . addButton ( ( cb ) => {
cb . setButtonText ( "Add new hotkey for template" )
. setCta ( )
. onClick ( ( ) => {
this . plugin . settings . enabled _templates _hotkeys . push ( "" ) ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
}
add _folder _templates _setting ( ) {
this . containerEl . createEl ( "h2" , { text : "Folder Templates" } ) ;
const descHeading = document . createDocumentFragment ( ) ;
descHeading . append ( "Folder Templates are triggered when a new " , descHeading . createEl ( "strong" , { text : "empty " } ) , "file is created in a given folder." , descHeading . createEl ( "br" ) , "Templater will fill the empty file with the specified template." , descHeading . createEl ( "br" ) , "The deepest match is used. A global default template would be defined on the root " , descHeading . createEl ( "code" , { text : "/" } ) , "." ) ;
new obsidian _module . Setting ( this . containerEl ) . setDesc ( descHeading ) ;
const descUseNewFileTemplate = document . createDocumentFragment ( ) ;
descUseNewFileTemplate . append ( "When enabled Templater will make use of the folder templates defined below." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Enable Folder Templates" )
. setDesc ( descUseNewFileTemplate )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . settings . enable _folder _templates )
. onChange ( ( use _new _file _templates ) => {
this . plugin . settings . enable _folder _templates =
use _new _file _templates ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
if ( ! this . plugin . settings . enable _folder _templates ) {
return ;
}
new obsidian _module . Setting ( this . containerEl )
. setName ( "Add New" )
. setDesc ( "Add new folder template" )
. addButton ( ( button ) => {
button
. setTooltip ( "Add additional folder template" )
. setButtonText ( "+" )
. setCta ( )
. onClick ( ( ) => {
this . plugin . settings . folder _templates . push ( {
folder : "" ,
template : "" ,
} ) ;
this . plugin . save _settings ( ) ;
this . display ( ) ;
} ) ;
} ) ;
this . plugin . settings . folder _templates . forEach ( ( folder _template , index ) => {
const s = new obsidian _module . Setting ( this . containerEl )
. addSearch ( ( cb ) => {
new FolderSuggest ( this . app , cb . inputEl ) ;
cb . setPlaceholder ( "Folder" )
. setValue ( folder _template . folder )
. onChange ( ( new _folder ) => {
if ( new _folder &&
this . plugin . settings . folder _templates . some ( ( e ) => e . folder == new _folder ) ) {
log _error ( new TemplaterError ( "This folder already has a template associated with it" ) ) ;
return ;
}
this . plugin . settings . folder _templates [ index ] . folder = new _folder ;
this . plugin . save _settings ( ) ;
} ) ;
// @ts-ignore
cb . containerEl . addClass ( "templater_search" ) ;
} )
. addSearch ( ( cb ) => {
new FileSuggest ( this . app , cb . inputEl , this . plugin , FileSuggestMode . TemplateFiles ) ;
cb . setPlaceholder ( "Template" )
. setValue ( folder _template . template )
. onChange ( ( new _template ) => {
this . plugin . settings . folder _templates [ index ] . template = new _template ;
this . plugin . save _settings ( ) ;
} ) ;
// @ts-ignore
cb . containerEl . addClass ( "templater_search" ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "up-chevron-glyph" )
. setTooltip ( "Move up" )
. onClick ( ( ) => {
arraymove ( this . plugin . settings . folder _templates , index , index - 1 ) ;
this . plugin . save _settings ( ) ;
this . display ( ) ;
} ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "down-chevron-glyph" )
. setTooltip ( "Move down" )
. onClick ( ( ) => {
arraymove ( this . plugin . settings . folder _templates , index , index + 1 ) ;
this . plugin . save _settings ( ) ;
this . display ( ) ;
} ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "cross" )
. setTooltip ( "Delete" )
. onClick ( ( ) => {
this . plugin . settings . folder _templates . splice ( index , 1 ) ;
this . plugin . save _settings ( ) ;
this . display ( ) ;
} ) ;
} ) ;
s . infoEl . remove ( ) ;
} ) ;
}
add _startup _templates _setting ( ) {
this . containerEl . createEl ( "h2" , { text : "Startup Templates" } ) ;
const desc = document . createDocumentFragment ( ) ;
desc . append ( "Startup Templates are templates that will get executed once when Templater starts." , desc . createEl ( "br" ) , "These templates won't output anything." , desc . createEl ( "br" ) , "This can be useful to set up templates adding hooks to obsidian events for example." ) ;
new obsidian _module . Setting ( this . containerEl ) . setDesc ( desc ) ;
this . plugin . settings . startup _templates . forEach ( ( template , index ) => {
const s = new obsidian _module . Setting ( this . containerEl )
. addSearch ( ( cb ) => {
new FileSuggest ( this . app , cb . inputEl , this . plugin , FileSuggestMode . TemplateFiles ) ;
cb . setPlaceholder ( "Example: folder1/template_file" )
. setValue ( template )
. onChange ( ( new _template ) => {
if ( new _template &&
this . plugin . settings . startup _templates . contains ( new _template ) ) {
log _error ( new TemplaterError ( "This startup template already exist" ) ) ;
return ;
}
this . plugin . settings . startup _templates [ index ] =
new _template ;
this . plugin . save _settings ( ) ;
} ) ;
// @ts-ignore
cb . containerEl . addClass ( "templater_search" ) ;
} )
. addExtraButton ( ( cb ) => {
cb . setIcon ( "cross" )
. setTooltip ( "Delete" )
. onClick ( ( ) => {
this . plugin . settings . startup _templates . splice ( index , 1 ) ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
s . infoEl . remove ( ) ;
} ) ;
new obsidian _module . Setting ( this . containerEl ) . addButton ( ( cb ) => {
cb . setButtonText ( "Add new startup template" )
. setCta ( )
. onClick ( ( ) => {
this . plugin . settings . startup _templates . push ( "" ) ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
}
add _user _script _functions _setting ( ) {
this . containerEl . createEl ( "h2" , { text : "User Script Functions" } ) ;
let desc = document . createDocumentFragment ( ) ;
desc . append ( "All JavaScript files in this folder will be loaded as CommonJS modules, to import custom user functions." , desc . createEl ( "br" ) , "The folder needs to be accessible from the vault." , desc . createEl ( "br" ) , "Check the " , desc . createEl ( "a" , {
href : "https://silentvoid13.github.io/Templater/" ,
text : "documentation" ,
} ) , " for more informations." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Script files folder location" )
. setDesc ( desc )
. addSearch ( ( cb ) => {
new FolderSuggest ( this . app , cb . inputEl ) ;
cb . setPlaceholder ( "Example: folder1/folder2" )
. setValue ( this . plugin . settings . user _scripts _folder )
. onChange ( ( new _folder ) => {
this . plugin . settings . user _scripts _folder = new _folder ;
this . plugin . save _settings ( ) ;
} ) ;
// @ts-ignore
cb . containerEl . addClass ( "templater_search" ) ;
} ) ;
desc = document . createDocumentFragment ( ) ;
let name ;
if ( ! this . plugin . settings . user _scripts _folder ) {
name = "No User Scripts folder set" ;
}
else {
const files = errorWrapperSync ( ( ) => get _tfiles _from _folder ( this . app , this . plugin . settings . user _scripts _folder ) , ` User Scripts folder doesn't exist ` ) ;
if ( ! files || files . length === 0 ) {
name = "No User Scripts detected" ;
}
else {
let count = 0 ;
for ( const file of files ) {
if ( file . extension === "js" ) {
count ++ ;
desc . append ( desc . createEl ( "li" , {
text : ` tp.user. ${ file . basename } ` ,
} ) ) ;
}
}
name = ` Detected ${ count } User Script(s) ` ;
}
}
new obsidian _module . Setting ( this . containerEl )
. setName ( name )
. setDesc ( desc )
. addExtraButton ( ( extra ) => {
extra
. setIcon ( "sync" )
. setTooltip ( "Refresh" )
. onClick ( ( ) => {
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
}
add _user _system _command _functions _setting ( ) {
let desc = document . createDocumentFragment ( ) ;
desc . append ( "Allows you to create user functions linked to system commands." , desc . createEl ( "br" ) , desc . createEl ( "b" , {
text : "Warning: " ,
} ) , "It can be dangerous to execute arbitrary system commands from untrusted sources. Only run system commands that you understand, from trusted sources." ) ;
this . containerEl . createEl ( "h2" , {
text : "User System Command Functions" ,
} ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Enable User System Command Functions" )
. setDesc ( desc )
. addToggle ( ( toggle ) => {
toggle
. setValue ( this . plugin . settings . enable _system _commands )
. onChange ( ( enable _system _commands ) => {
this . plugin . settings . enable _system _commands =
enable _system _commands ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
if ( this . plugin . settings . enable _system _commands ) {
new obsidian _module . Setting ( this . containerEl )
. setName ( "Timeout" )
. setDesc ( "Maximum timeout in seconds for a system command." )
. addText ( ( text ) => {
text . setPlaceholder ( "Timeout" )
. setValue ( this . plugin . settings . command _timeout . toString ( ) )
. onChange ( ( new _value ) => {
const new _timeout = Number ( new _value ) ;
if ( isNaN ( new _timeout ) ) {
log _error ( new TemplaterError ( "Timeout must be a number" ) ) ;
return ;
}
this . plugin . settings . command _timeout = new _timeout ;
this . plugin . save _settings ( ) ;
} ) ;
} ) ;
desc = document . createDocumentFragment ( ) ;
desc . append ( "Full path to the shell binary to execute the command with." , desc . createEl ( "br" ) , "This setting is optional and will default to the system's default shell if not specified." , desc . createEl ( "br" ) , "You can use forward slashes ('/') as path separators on all platforms if in doubt." ) ;
new obsidian _module . Setting ( this . containerEl )
. setName ( "Shell binary location" )
. setDesc ( desc )
. addText ( ( text ) => {
text . setPlaceholder ( "Example: /bin/bash, ..." )
. setValue ( this . plugin . settings . shell _path )
. onChange ( ( shell _path ) => {
this . plugin . settings . shell _path = shell _path ;
this . plugin . save _settings ( ) ;
} ) ;
} ) ;
let i = 1 ;
this . plugin . settings . templates _pairs . forEach ( ( template _pair ) => {
const div = this . containerEl . createEl ( "div" ) ;
div . addClass ( "templater_div" ) ;
const title = this . containerEl . createEl ( "h4" , {
text : "User Function n°" + i ,
} ) ;
title . addClass ( "templater_title" ) ;
const setting = new obsidian _module . Setting ( this . containerEl )
. addExtraButton ( ( extra ) => {
extra
. setIcon ( "cross" )
. setTooltip ( "Delete" )
. onClick ( ( ) => {
const index = this . plugin . settings . templates _pairs . indexOf ( template _pair ) ;
if ( index > - 1 ) {
this . plugin . settings . templates _pairs . splice ( index , 1 ) ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
}
} ) ;
} )
. addText ( ( text ) => {
const t = text
. setPlaceholder ( "Function name" )
. setValue ( template _pair [ 0 ] )
. onChange ( ( new _value ) => {
const index = this . plugin . settings . templates _pairs . indexOf ( template _pair ) ;
if ( index > - 1 ) {
this . plugin . settings . templates _pairs [ index ] [ 0 ] = new _value ;
this . plugin . save _settings ( ) ;
}
} ) ;
t . inputEl . addClass ( "templater_template" ) ;
return t ;
} )
. addTextArea ( ( text ) => {
const t = text
. setPlaceholder ( "System Command" )
. setValue ( template _pair [ 1 ] )
. onChange ( ( new _cmd ) => {
const index = this . plugin . settings . templates _pairs . indexOf ( template _pair ) ;
if ( index > - 1 ) {
this . plugin . settings . templates _pairs [ index ] [ 1 ] = new _cmd ;
this . plugin . save _settings ( ) ;
}
} ) ;
t . inputEl . setAttr ( "rows" , 2 ) ;
t . inputEl . addClass ( "templater_cmd" ) ;
return t ;
} ) ;
setting . infoEl . remove ( ) ;
div . appendChild ( title ) ;
div . appendChild ( this . containerEl . lastChild ) ;
i += 1 ;
} ) ;
const div = this . containerEl . createEl ( "div" ) ;
div . addClass ( "templater_div2" ) ;
const setting = new obsidian _module . Setting ( this . containerEl ) . addButton ( ( button ) => {
button
. setButtonText ( "Add New User Function" )
. setCta ( )
. onClick ( ( ) => {
this . plugin . settings . templates _pairs . push ( [ "" , "" ] ) ;
this . plugin . save _settings ( ) ;
// Force refresh
this . display ( ) ;
} ) ;
} ) ;
setting . infoEl . remove ( ) ;
div . appendChild ( this . containerEl . lastChild ) ;
}
}
}
var OpenMode ;
( function ( OpenMode ) {
OpenMode [ OpenMode [ "InsertTemplate" ] = 0 ] = "InsertTemplate" ;
OpenMode [ OpenMode [ "CreateNoteTemplate" ] = 1 ] = "CreateNoteTemplate" ;
} ) ( OpenMode || ( OpenMode = { } ) ) ;
class FuzzySuggester extends obsidian _module . FuzzySuggestModal {
constructor ( app , plugin ) {
super ( app ) ;
this . app = app ;
this . plugin = plugin ;
this . setPlaceholder ( "Type name of a template..." ) ;
}
getItems ( ) {
if ( ! this . plugin . settings . templates _folder ) {
return this . app . vault . getMarkdownFiles ( ) ;
}
const files = errorWrapperSync ( ( ) => get _tfiles _from _folder ( this . app , this . plugin . settings . templates _folder ) , ` Couldn't retrieve template files from templates folder ${ this . plugin . settings . templates _folder } ` ) ;
if ( ! files ) {
return [ ] ;
}
return files ;
}
getItemText ( item ) {
return item . basename ;
}
onChooseItem ( item ) {
switch ( this . open _mode ) {
case OpenMode . InsertTemplate :
this . plugin . templater . append _template _to _active _file ( item ) ;
break ;
case OpenMode . CreateNoteTemplate :
this . plugin . templater . create _new _note _from _template ( item , this . creation _folder ) ;
break ;
}
}
start ( ) {
try {
this . open ( ) ;
}
catch ( e ) {
log _error ( e ) ;
}
}
insert _template ( ) {
this . open _mode = OpenMode . InsertTemplate ;
this . start ( ) ;
}
create _new _note _from _template ( folder ) {
this . creation _folder = folder ;
this . open _mode = OpenMode . CreateNoteTemplate ;
this . start ( ) ;
}
}
const UNSUPPORTED _MOBILE _TEMPLATE = "Error_MobileUnsupportedTemplate" ;
const ICON _DATA = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 51.1328 28.7"><path d="M0 15.14 0 10.15 18.67 1.51 18.67 6.03 4.72 12.33 4.72 12.76 18.67 19.22 18.67 23.74 0 15.14ZM33.6928 1.84C33.6928 1.84 33.9761 2.1467 34.5428 2.76C35.1094 3.38 35.3928 4.56 35.3928 6.3C35.3928 8.0466 34.8195 9.54 33.6728 10.78C32.5261 12.02 31.0995 12.64 29.3928 12.64C27.6862 12.64 26.2661 12.0267 25.1328 10.8C23.9928 9.5733 23.4228 8.0867 23.4228 6.34C23.4228 4.6 23.9995 3.1066 25.1528 1.86C26.2994.62 27.7261 0 29.4328 0C31.1395 0 32.5594.6133 33.6928 1.84M49.8228.67 29.5328 28.38 24.4128 28.38 44.7128.67 49.8228.67M31.0328 8.38C31.0328 8.38 31.1395 8.2467 31.3528 7.98C31.5662 7.7067 31.6728 7.1733 31.6728 6.38C31.6728 5.5867 31.4461 4.92 30.9928 4.38C30.5461 3.84 29.9995 3.57 29.3528 3.57C28.7061 3.57 28.1695 3.84 27.7428 4.38C27.3228 4.92 27.1128 5.5867 27.1128 6.38C27.1128 7.1733 27.3361 7.84 27.7828 8.38C28.2361 8.9267 28.7861 9.2 29.4328 9.2C30.0795 9.2 30.6128 8.9267 31.0328 8.38M49.4328 17.9C49.4328 17.9 49.7161 18.2067 50.2828 18.82C50.8495 19.4333 51.1328 20.6133 51.1328 22.36C51.1328 24.1 50.5594 25.59 49.4128 26.83C48.2595 28.0766 46.8295 28.7 45.1228 28.7C43.4228 28.7 42.0028 28.0833 40.8628 26.85C39.7295 25.6233 39.1628 24.1366 39.1628 22.39C39.1628 20.65 39.7361 19.16 40.8828 17.92C42.0361 16.6733 43.4628 16.05 45.1628 16.05C46.8694 16.05 48.2928 16.6667 49.4328 17.9M46.8528 24.52C46.8528 24.52 46.9595 24.3833 47.1728 24.11C47.3795 23.8367 47.4828 23.3033 47.4828 22.51C47.4828 21.7167 47.2595 21.05 46.8128 20.51C46.3661 19.97 45.8162 19.7 45.1628 19.7C44.5161 19.7 43.9828 19.97 43.5628 20.51C43.1428 21.05 42.9328 21.7167 42.9328 22.51C42.9328 23.3033 43.1561 23.9733 43.6028 24.52C44.0494 25.06 44.5961 25.33 45.2428 25.33C45.8895 25.33 46.4261 25.06 46.8528 24.52Z" fill="currentColor"/></svg> ` ;
class InternalModule {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
this . static _functions = new Map ( ) ;
this . dynamic _functions = new Map ( ) ;
}
getName ( ) {
return this . name ;
}
init ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . create _static _templates ( ) ;
this . static _object = Object . fromEntries ( this . static _functions ) ;
} ) ;
}
generate _object ( new _config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . config = new _config ;
yield this . create _dynamic _templates ( ) ;
return Object . assign ( Object . assign ( { } , this . static _object ) , Object . fromEntries ( this . dynamic _functions ) ) ;
} ) ;
}
}
class InternalModuleDate extends InternalModule {
constructor ( ) {
super ( ... arguments ) ;
this . name = "date" ;
}
create _static _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . static _functions . set ( "now" , this . generate _now ( ) ) ;
this . static _functions . set ( "tomorrow" , this . generate _tomorrow ( ) ) ;
this . static _functions . set ( "weekday" , this . generate _weekday ( ) ) ;
this . static _functions . set ( "yesterday" , this . generate _yesterday ( ) ) ;
} ) ;
}
create _dynamic _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) { } ) ;
}
generate _now ( ) {
return ( format = "YYYY-MM-DD" , offset , reference , reference _format ) => {
if ( reference &&
! window . moment ( reference , reference _format ) . isValid ( ) ) {
throw new TemplaterError ( "Invalid reference date format, try specifying one with the argument 'reference_format'" ) ;
}
let duration ;
if ( typeof offset === "string" ) {
duration = window . moment . duration ( offset ) ;
}
else if ( typeof offset === "number" ) {
duration = window . moment . duration ( offset , "days" ) ;
}
return window
. moment ( reference , reference _format )
. add ( duration )
. format ( format ) ;
} ;
}
generate _tomorrow ( ) {
return ( format = "YYYY-MM-DD" ) => {
return window . moment ( ) . add ( 1 , "days" ) . format ( format ) ;
} ;
}
generate _weekday ( ) {
return ( format = "YYYY-MM-DD" , weekday , reference , reference _format ) => {
if ( reference &&
! window . moment ( reference , reference _format ) . isValid ( ) ) {
throw new TemplaterError ( "Invalid reference date format, try specifying one with the argument 'reference_format'" ) ;
}
return window
. moment ( reference , reference _format )
. weekday ( weekday )
. format ( format ) ;
} ;
}
generate _yesterday ( ) {
return ( format = "YYYY-MM-DD" ) => {
return window . moment ( ) . add ( - 1 , "days" ) . format ( format ) ;
} ;
}
}
const DEPTH _LIMIT = 10 ;
class InternalModuleFile extends InternalModule {
constructor ( ) {
super ( ... arguments ) ;
this . name = "file" ;
this . include _depth = 0 ;
this . create _new _depth = 0 ;
this . linkpath _regex = new RegExp ( "^\\[\\[(.*)\\]\\]$" ) ;
}
create _static _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . static _functions . set ( "creation_date" , this . generate _creation _date ( ) ) ;
this . static _functions . set ( "create_new" , this . generate _create _new ( ) ) ;
this . static _functions . set ( "cursor" , this . generate _cursor ( ) ) ;
this . static _functions . set ( "cursor_append" , this . generate _cursor _append ( ) ) ;
this . static _functions . set ( "exists" , this . generate _exists ( ) ) ;
this . static _functions . set ( "find_tfile" , this . generate _find _tfile ( ) ) ;
this . static _functions . set ( "folder" , this . generate _folder ( ) ) ;
this . static _functions . set ( "include" , this . generate _include ( ) ) ;
this . static _functions . set ( "last_modified_date" , this . generate _last _modified _date ( ) ) ;
this . static _functions . set ( "move" , this . generate _move ( ) ) ;
this . static _functions . set ( "path" , this . generate _path ( ) ) ;
this . static _functions . set ( "rename" , this . generate _rename ( ) ) ;
this . static _functions . set ( "selection" , this . generate _selection ( ) ) ;
} ) ;
}
create _dynamic _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . dynamic _functions . set ( "content" , yield this . generate _content ( ) ) ;
this . dynamic _functions . set ( "tags" , this . generate _tags ( ) ) ;
this . dynamic _functions . set ( "title" , this . generate _title ( ) ) ;
} ) ;
}
generate _content ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
return yield this . app . vault . read ( this . config . target _file ) ;
} ) ;
}
generate _create _new ( ) {
return ( template , filename , open _new = false , folder ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . create _new _depth += 1 ;
if ( this . create _new _depth > DEPTH _LIMIT ) {
this . create _new _depth = 0 ;
throw new TemplaterError ( "Reached create_new depth limit (max = 10)" ) ;
}
const new _file = yield this . plugin . templater . create _new _note _from _template ( template , folder , filename , open _new ) ;
this . create _new _depth -= 1 ;
return new _file ;
} ) ;
}
generate _creation _date ( ) {
return ( format = "YYYY-MM-DD HH:mm" ) => {
return window
. moment ( this . config . target _file . stat . ctime )
. format ( format ) ;
} ;
}
generate _cursor ( ) {
return ( order ) => {
// Hack to prevent empty output
return ` <% tp.file.cursor( ${ order !== null && order !== void 0 ? order : "" } ) %> ` ;
} ;
}
generate _cursor _append ( ) {
return ( content ) => {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( active _view === null ) {
log _error ( new TemplaterError ( "No active view, can't append to cursor." ) ) ;
return ;
}
const editor = active _view . editor ;
const doc = editor . getDoc ( ) ;
doc . replaceSelection ( content ) ;
return "" ;
} ;
}
generate _exists ( ) {
return ( filename ) => {
// TODO: Remove this, only here to support the old way
let match ;
if ( ( match = this . linkpath _regex . exec ( filename ) ) !== null ) {
filename = match [ 1 ] ;
}
const file = this . app . metadataCache . getFirstLinkpathDest ( filename , "" ) ;
return file != null ;
} ;
}
generate _find _tfile ( ) {
return ( filename ) => {
const path = obsidian _module . normalizePath ( filename ) ;
return this . app . metadataCache . getFirstLinkpathDest ( path , "" ) ;
} ;
}
generate _folder ( ) {
return ( relative = false ) => {
const parent = this . config . target _file . parent ;
let folder ;
if ( relative ) {
folder = parent . path ;
}
else {
folder = parent . name ;
}
return folder ;
} ;
}
generate _include ( ) {
return ( include _link ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
var _a ;
// TODO: Add mutex for this, this may currently lead to a race condition.
// While not very impactful, that could still be annoying.
this . include _depth += 1 ;
if ( this . include _depth > DEPTH _LIMIT ) {
this . include _depth -= 1 ;
throw new TemplaterError ( "Reached inclusion depth limit (max = 10)" ) ;
}
let inc _file _content ;
if ( include _link instanceof obsidian _module . TFile ) {
inc _file _content = yield this . app . vault . read ( include _link ) ;
}
else {
let match ;
if ( ( match = this . linkpath _regex . exec ( include _link ) ) === null ) {
this . include _depth -= 1 ;
throw new TemplaterError ( "Invalid file format, provide an obsidian link between quotes." ) ;
}
const { path , subpath } = obsidian _module . parseLinktext ( match [ 1 ] ) ;
const inc _file = this . app . metadataCache . getFirstLinkpathDest ( path , "" ) ;
if ( ! inc _file ) {
this . include _depth -= 1 ;
throw new TemplaterError ( ` File ${ include _link } doesn't exist ` ) ;
}
inc _file _content = yield this . app . vault . read ( inc _file ) ;
if ( subpath ) {
const cache = this . app . metadataCache . getFileCache ( inc _file ) ;
if ( cache ) {
const result = obsidian _module . resolveSubpath ( cache , subpath ) ;
if ( result ) {
inc _file _content = inc _file _content . slice ( result . start . offset , ( _a = result . end ) === null || _a === void 0 ? void 0 : _a . offset ) ;
}
}
}
}
try {
const parsed _content = yield this . plugin . templater . parser . parse _commands ( inc _file _content , this . plugin . templater . current _functions _object ) ;
this . include _depth -= 1 ;
return parsed _content ;
}
catch ( e ) {
this . include _depth -= 1 ;
throw e ;
}
} ) ;
}
generate _last _modified _date ( ) {
return ( format = "YYYY-MM-DD HH:mm" ) => {
return window
. moment ( this . config . target _file . stat . mtime )
. format ( format ) ;
} ;
}
generate _move ( ) {
return ( path ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const new _path = obsidian _module . normalizePath ( ` ${ path } . ${ this . config . target _file . extension } ` ) ;
yield this . app . fileManager . renameFile ( this . config . target _file , new _path ) ;
return "" ;
} ) ;
}
generate _path ( ) {
return ( relative = false ) => {
// TODO: Add mobile support
if ( obsidian _module . Platform . isMobileApp ) {
return UNSUPPORTED _MOBILE _TEMPLATE ;
}
if ( ! ( this . app . vault . adapter instanceof obsidian _module . FileSystemAdapter ) ) {
throw new TemplaterError ( "app.vault is not a FileSystemAdapter instance" ) ;
}
const vault _path = this . app . vault . adapter . getBasePath ( ) ;
if ( relative ) {
return this . config . target _file . path ;
}
else {
return ` ${ vault _path } / ${ this . config . target _file . path } ` ;
}
} ;
}
generate _rename ( ) {
return ( new _title ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( new _title . match ( /[\\/:]+/g ) ) {
throw new TemplaterError ( "File name cannot contain any of these characters: \\ / :" ) ;
}
const new _path = obsidian _module . normalizePath ( ` ${ this . config . target _file . parent . path } / ${ new _title } . ${ this . config . target _file . extension } ` ) ;
yield this . app . fileManager . renameFile ( this . config . target _file , new _path ) ;
return "" ;
} ) ;
}
generate _selection ( ) {
return ( ) => {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( active _view == null ) {
throw new TemplaterError ( "Active view is null, can't read selection." ) ;
}
const editor = active _view . editor ;
return editor . getSelection ( ) ;
} ;
}
// TODO: Turn this into a function
generate _tags ( ) {
const cache = this . app . metadataCache . getFileCache ( this . config . target _file ) ;
return obsidian _module . getAllTags ( cache ) ;
}
// TODO: Turn this into a function
generate _title ( ) {
return this . config . target _file . basename ;
}
}
class InternalModuleWeb extends InternalModule {
constructor ( ) {
super ( ... arguments ) ;
this . name = "web" ;
}
create _static _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . static _functions . set ( "daily_quote" , this . generate _daily _quote ( ) ) ;
this . static _functions . set ( "random_picture" , this . generate _random _picture ( ) ) ;
} ) ;
}
create _dynamic _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) { } ) ;
}
getRequest ( url ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const response = yield fetch ( url ) ;
if ( ! response . ok ) {
throw new TemplaterError ( "Error performing GET request" ) ;
}
return response ;
} ) ;
}
generate _daily _quote ( ) {
return ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const response = yield this . getRequest ( "https://api.quotable.io/random" ) ;
const json = yield response . json ( ) ;
const author = json . author ;
const quote = json . content ;
const new _content = ` > ${ quote } \n > — <cite> ${ author } </cite> ` ;
return new _content ;
} ) ;
}
generate _random _picture ( ) {
return ( size , query ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const response = yield this . getRequest ( ` https://source.unsplash.com/random/ ${ size !== null && size !== void 0 ? size : "" } ? ${ query !== null && query !== void 0 ? query : "" } ` ) ;
const url = response . url ;
return ` ![tp.web.random_picture]( ${ url } ) ` ;
} ) ;
}
}
class InternalModuleFrontmatter extends InternalModule {
constructor ( ) {
super ( ... arguments ) ;
this . name = "frontmatter" ;
}
create _static _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) { } ) ;
}
create _dynamic _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const cache = this . app . metadataCache . getFileCache ( this . config . target _file ) ;
this . dynamic _functions = new Map ( Object . entries ( ( cache === null || cache === void 0 ? void 0 : cache . frontmatter ) || { } ) ) ;
} ) ;
}
}
class PromptModal extends obsidian _module . Modal {
constructor ( app , prompt _text , default _value ) {
super ( app ) ;
this . prompt _text = prompt _text ;
this . default _value = default _value ;
this . submitted = false ;
}
onOpen ( ) {
this . titleEl . setText ( this . prompt _text ) ;
this . createForm ( ) ;
}
onClose ( ) {
this . contentEl . empty ( ) ;
if ( ! this . submitted ) {
this . reject ( new TemplaterError ( "Cancelled prompt" ) ) ;
}
}
createForm ( ) {
var _a ;
const div = this . contentEl . createDiv ( ) ;
div . addClass ( "templater-prompt-div" ) ;
const form = div . createEl ( "form" ) ;
form . addClass ( "templater-prompt-form" ) ;
form . type = "submit" ;
form . onsubmit = ( e ) => {
this . submitted = true ;
e . preventDefault ( ) ;
this . resolve ( this . promptEl . value ) ;
this . close ( ) ;
} ;
this . promptEl = form . createEl ( "input" ) ;
this . promptEl . type = "text" ;
this . promptEl . placeholder = "Type text here..." ;
this . promptEl . value = ( _a = this . default _value ) !== null && _a !== void 0 ? _a : "" ;
this . promptEl . addClass ( "templater-prompt-input" ) ;
this . promptEl . select ( ) ;
}
openAndGetValue ( resolve , reject ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . resolve = resolve ;
this . reject = reject ;
this . open ( ) ;
} ) ;
}
}
class SuggesterModal extends obsidian _module . FuzzySuggestModal {
constructor ( app , text _items , items , placeholder ) {
super ( app ) ;
this . text _items = text _items ;
this . items = items ;
this . submitted = false ;
this . setPlaceholder ( placeholder ) ;
}
getItems ( ) {
return this . items ;
}
onClose ( ) {
if ( ! this . submitted ) {
this . reject ( new TemplaterError ( "Cancelled prompt" ) ) ;
}
}
selectSuggestion ( value , evt ) {
this . submitted = true ;
this . close ( ) ;
this . onChooseSuggestion ( value , evt ) ;
}
getItemText ( item ) {
if ( this . text _items instanceof Function ) {
return this . text _items ( item ) ;
}
return ( this . text _items [ this . items . indexOf ( item ) ] || "Undefined Text Item" ) ;
}
onChooseItem ( item ) {
this . resolve ( item ) ;
}
openAndGetValue ( resolve , reject ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . resolve = resolve ;
this . reject = reject ;
this . open ( ) ;
} ) ;
}
}
class InternalModuleSystem extends InternalModule {
constructor ( ) {
super ( ... arguments ) ;
this . name = "system" ;
}
create _static _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . static _functions . set ( "clipboard" , this . generate _clipboard ( ) ) ;
this . static _functions . set ( "prompt" , this . generate _prompt ( ) ) ;
this . static _functions . set ( "suggester" , this . generate _suggester ( ) ) ;
} ) ;
}
create _dynamic _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) { } ) ;
}
generate _clipboard ( ) {
return ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// TODO: Add mobile support
if ( obsidian _module . Platform . isMobileApp ) {
return UNSUPPORTED _MOBILE _TEMPLATE ;
}
return yield navigator . clipboard . readText ( ) ;
} ) ;
}
generate _prompt ( ) {
return ( prompt _text , default _value , throw _on _cancel = false ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const prompt = new PromptModal ( this . app , prompt _text , default _value ) ;
const promise = new Promise ( ( resolve , reject ) => prompt . openAndGetValue ( resolve , reject ) ) ;
try {
return yield promise ;
}
catch ( error ) {
if ( throw _on _cancel ) {
throw error ;
}
return null ;
}
} ) ;
}
generate _suggester ( ) {
return ( text _items , items , throw _on _cancel = false , placeholder = "" ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const suggester = new SuggesterModal ( this . app , text _items , items , placeholder ) ;
const promise = new Promise ( ( resolve , reject ) => suggester . openAndGetValue ( resolve , reject ) ) ;
try {
return yield promise ;
}
catch ( error ) {
if ( throw _on _cancel ) {
throw error ;
}
return null ;
}
} ) ;
}
}
class InternalModuleConfig extends InternalModule {
constructor ( ) {
super ( ... arguments ) ;
this . name = "config" ;
}
create _static _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) { } ) ;
}
create _dynamic _templates ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) { } ) ;
}
generate _object ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
return config ;
} ) ;
}
}
class InternalFunctions {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
this . modules _array = [ ] ;
this . modules _array . push ( new InternalModuleDate ( this . app , this . plugin ) ) ;
this . modules _array . push ( new InternalModuleFile ( this . app , this . plugin ) ) ;
this . modules _array . push ( new InternalModuleWeb ( this . app , this . plugin ) ) ;
this . modules _array . push ( new InternalModuleFrontmatter ( this . app , this . plugin ) ) ;
this . modules _array . push ( new InternalModuleSystem ( this . app , this . plugin ) ) ;
this . modules _array . push ( new InternalModuleConfig ( this . app , this . plugin ) ) ;
}
init ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
for ( const mod of this . modules _array ) {
yield mod . init ( ) ;
}
} ) ;
}
generate _object ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const internal _functions _object = { } ;
for ( const mod of this . modules _array ) {
internal _functions _object [ mod . getName ( ) ] =
yield mod . generate _object ( config ) ;
}
return internal _functions _object ;
} ) ;
}
}
class UserSystemFunctions {
constructor ( app , plugin ) {
this . plugin = plugin ;
if ( obsidian _module . Platform . isMobileApp ||
! ( app . vault . adapter instanceof obsidian _module . FileSystemAdapter ) ) {
this . cwd = "" ;
}
else {
this . cwd = app . vault . adapter . getBasePath ( ) ;
this . exec _promise = util . promisify ( child _process . exec ) ;
}
}
// TODO: Add mobile support
generate _system _functions ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const user _system _functions = new Map ( ) ;
const internal _functions _object = yield this . plugin . templater . functions _generator . generate _object ( config , FunctionsMode . INTERNAL ) ;
for ( const template _pair of this . plugin . settings . templates _pairs ) {
const template = template _pair [ 0 ] ;
let cmd = template _pair [ 1 ] ;
if ( ! template || ! cmd ) {
continue ;
}
if ( obsidian _module . Platform . isMobileApp ) {
user _system _functions . set ( template , ( ) => {
return new Promise ( ( resolve ) => resolve ( UNSUPPORTED _MOBILE _TEMPLATE ) ) ;
} ) ;
}
else {
cmd = yield this . plugin . templater . parser . parse _commands ( cmd , internal _functions _object ) ;
user _system _functions . set ( template , ( user _args ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const process _env = Object . assign ( Object . assign ( { } , process . env ) , user _args ) ;
const cmd _options = Object . assign ( { timeout : this . plugin . settings . command _timeout * 1000 , cwd : this . cwd , env : process _env } , ( this . plugin . settings . shell _path && {
shell : this . plugin . settings . shell _path ,
} ) ) ;
try {
const { stdout } = yield this . exec _promise ( cmd , cmd _options ) ;
return stdout . trimRight ( ) ;
}
catch ( error ) {
throw new TemplaterError ( ` Error with User Template ${ template } ` , error ) ;
}
} ) ) ;
}
}
return user _system _functions ;
} ) ;
}
generate _object ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const user _system _functions = yield this . generate _system _functions ( config ) ;
return Object . fromEntries ( user _system _functions ) ;
} ) ;
}
}
class UserScriptFunctions {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
}
generate _user _script _functions ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const user _script _functions = new Map ( ) ;
const files = errorWrapperSync ( ( ) => get _tfiles _from _folder ( this . app , this . plugin . settings . user _scripts _folder ) , ` Couldn't find user script folder " ${ this . plugin . settings . user _scripts _folder } " ` ) ;
if ( ! files ) {
return new Map ( ) ;
}
for ( const file of files ) {
if ( file . extension . toLowerCase ( ) === "js" ) {
yield this . load _user _script _function ( config , file , user _script _functions ) ;
}
}
return user _script _functions ;
} ) ;
}
load _user _script _function ( config , file , user _script _functions ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( ! ( this . app . vault . adapter instanceof obsidian _module . FileSystemAdapter ) ) {
throw new TemplaterError ( "app.vault is not a FileSystemAdapter instance" ) ;
}
const vault _path = this . app . vault . adapter . getBasePath ( ) ;
const file _path = ` ${ vault _path } / ${ file . path } ` ;
// https://stackoverflow.com/questions/26633901/reload-module-at-runtime
// https://stackoverflow.com/questions/1972242/how-to-auto-reload-files-in-node-js
if ( Object . keys ( window . require . cache ) . contains ( file _path ) ) {
delete window . require . cache [ window . require . resolve ( file _path ) ] ;
}
const user _function = yield Promise . resolve ( ) . then ( function ( ) { return /*#__PURE__*/ _interopNamespace ( require ( file _path ) ) ; } ) ;
if ( ! user _function . default ) {
throw new TemplaterError ( ` Failed to load user script ${ file _path } . No exports detected. ` ) ;
}
if ( ! ( user _function . default instanceof Function ) ) {
throw new TemplaterError ( ` Failed to load user script ${ file _path } . Default export is not a function. ` ) ;
}
user _script _functions . set ( ` ${ file . basename } ` , user _function . default ) ;
} ) ;
}
generate _object ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const user _script _functions = yield this . generate _user _script _functions ( config ) ;
return Object . fromEntries ( user _script _functions ) ;
} ) ;
}
}
class UserFunctions {
constructor ( app , plugin ) {
this . plugin = plugin ;
this . user _system _functions = new UserSystemFunctions ( app , plugin ) ;
this . user _script _functions = new UserScriptFunctions ( app , plugin ) ;
}
generate _object ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
let user _system _functions = { } ;
let user _script _functions = { } ;
if ( this . plugin . settings . enable _system _commands ) {
user _system _functions =
yield this . user _system _functions . generate _object ( config ) ;
}
// TODO: Add mobile support
// user_scripts_folder needs to be explicitly set to '/' to query from root
if ( obsidian _module . Platform . isDesktopApp && this . plugin . settings . user _scripts _folder ) {
user _script _functions =
yield this . user _script _functions . generate _object ( config ) ;
}
return Object . assign ( Object . assign ( { } , user _system _functions ) , user _script _functions ) ;
} ) ;
}
}
var FunctionsMode ;
( function ( FunctionsMode ) {
FunctionsMode [ FunctionsMode [ "INTERNAL" ] = 0 ] = "INTERNAL" ;
FunctionsMode [ FunctionsMode [ "USER_INTERNAL" ] = 1 ] = "USER_INTERNAL" ;
} ) ( FunctionsMode || ( FunctionsMode = { } ) ) ;
class FunctionsGenerator {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
this . internal _functions = new InternalFunctions ( this . app , this . plugin ) ;
this . user _functions = new UserFunctions ( this . app , this . plugin ) ;
}
init ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . internal _functions . init ( ) ;
} ) ;
}
additional _functions ( ) {
return {
obsidian : obsidian _module _ _namespace ,
} ;
}
generate _object ( config , functions _mode = FunctionsMode . USER _INTERNAL ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const final _object = { } ;
const additional _functions _object = this . additional _functions ( ) ;
const internal _functions _object = yield this . internal _functions . generate _object ( config ) ;
let user _functions _object = { } ;
Object . assign ( final _object , additional _functions _object ) ;
switch ( functions _mode ) {
case FunctionsMode . INTERNAL :
Object . assign ( final _object , internal _functions _object ) ;
break ;
case FunctionsMode . USER _INTERNAL :
user _functions _object =
yield this . user _functions . generate _object ( config ) ;
Object . assign ( final _object , Object . assign ( Object . assign ( { } , internal _functions _object ) , { user : user _functions _object } ) ) ;
break ;
}
return final _object ;
} ) ;
}
}
class CursorJumper {
constructor ( app ) {
this . app = app ;
}
jump _to _next _cursor _location ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( ! active _view ) {
return ;
}
const active _file = active _view . file ;
yield active _view . save ( ) ;
const content = yield this . app . vault . read ( active _file ) ;
const { new _content , positions } = this . replace _and _get _cursor _positions ( content ) ;
if ( positions ) {
yield this . app . vault . modify ( active _file , new _content ) ;
this . set _cursor _location ( positions ) ;
}
} ) ;
}
get _editor _position _from _index ( content , index ) {
const substr = content . substr ( 0 , index ) ;
let l = 0 ;
let offset = - 1 ;
let r = - 1 ;
for ( ; ( r = substr . indexOf ( "\n" , r + 1 ) ) !== - 1 ; l ++ , offset = r )
;
offset += 1 ;
const ch = content . substr ( offset , index - offset ) . length ;
return { line : l , ch : ch } ;
}
replace _and _get _cursor _positions ( content ) {
let cursor _matches = [ ] ;
let match ;
const cursor _regex = new RegExp ( "<%\\s*tp.file.cursor\\((?<order>[0-9]{0,2})\\)\\s*%>" , "g" ) ;
while ( ( match = cursor _regex . exec ( content ) ) != null ) {
cursor _matches . push ( match ) ;
}
if ( cursor _matches . length === 0 ) {
return { } ;
}
cursor _matches . sort ( ( m1 , m2 ) => {
return Number ( m1 . groups [ "order" ] ) - Number ( m2 . groups [ "order" ] ) ;
} ) ;
const match _str = cursor _matches [ 0 ] [ 0 ] ;
cursor _matches = cursor _matches . filter ( ( m ) => {
return m [ 0 ] === match _str ;
} ) ;
const positions = [ ] ;
let index _offset = 0 ;
for ( const match of cursor _matches ) {
const index = match . index - index _offset ;
positions . push ( this . get _editor _position _from _index ( content , index ) ) ;
content = content . replace ( new RegExp ( escape _RegExp ( match [ 0 ] ) ) , "" ) ;
index _offset += match [ 0 ] . length ;
// For tp.file.cursor(), we keep the default top to bottom
if ( match [ 1 ] === "" ) {
break ;
}
}
return { new _content : content , positions : positions } ;
}
set _cursor _location ( positions ) {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( ! active _view ) {
return ;
}
const editor = active _view . editor ;
editor . focus ( ) ;
const selections = [ ] ;
for ( const pos of positions ) {
selections . push ( { from : pos } ) ;
}
const transaction = {
selections : selections ,
} ;
editor . transaction ( transaction ) ;
}
}
const tp = {
config : {
name : "config" ,
description : "This module exposes Templater's running configuration.\n\nThis is mostly useful when writing scripts requiring some context informations.\n" ,
functions : {
template _file : {
name : "template_file" ,
description : "The `TFile` object representing the template file." ,
definition : "tp.file.template_file"
} ,
target _file : {
name : "target_file" ,
description : "The `TFile` object representing the target file where the template will be inserted." ,
definition : "tp.config.target_file"
} ,
run _mode : {
name : "run_mode" ,
description : "The `RunMode`, representing the way Templater was launched (Create new from template, Append to active file, ...)" ,
definition : "tp.config.run_mode"
} ,
active _file : {
name : "active_file" ,
description : "The active file (if existing) when launching Templater." ,
definition : "tp.config.active_file?"
}
}
} ,
date : {
name : "date" ,
description : "This module contains every internal function related to dates." ,
functions : {
now : {
name : "now" ,
description : "Retrieves the date." ,
definition : "tp.date.now(format: string = \"YYYY-MM-DD\", offset?: number⎮string, reference?: string, reference_format?: string)" ,
args : {
format : {
name : "format" ,
description : "Format for the date, refer to [format reference](https://momentjs.com/docs/#/displaying/format/)"
} ,
offset : {
name : "offset" ,
description : "Offset for the day, e.g. set this to `-7` to get last week's date. You can also specify the offset as a string using the ISO 8601 format"
} ,
reference : {
name : "reference" ,
description : "The date referential, e.g. set this to the note's title"
} ,
reference _format : {
name : "reference_format" ,
description : "The date reference format."
}
}
} ,
tomorrow : {
name : "tomorrow" ,
description : "Retrieves tomorrow's date." ,
definition : "tp.date.tomorrow(format: string = \"YYYY-MM-DD\")" ,
args : {
format : {
name : "format" ,
description : "Format for the date, refer to [format reference](https://momentjs.com/docs/#/displaying/format/)"
}
}
} ,
yesterday : {
name : "yesterday" ,
description : "Retrieves yesterday's date." ,
definition : "tp.date.yesterday(format: string = \"YYYY-MM-DD\")" ,
args : {
format : {
name : "format" ,
description : "Format for the date, refer to [format reference](https://momentjs.com/docs/#/displaying/format/)"
}
}
} ,
weekday : {
name : "weekday" ,
description : "" ,
definition : "tp.date.weekday(format: string = \"YYYY-MM-DD\", weekday: number, reference?: string, reference_format?: string)" ,
args : {
format : {
name : "format" ,
description : "Format for the date, refer to [format reference](https://momentjs.com/docs/#/displaying/format/)"
} ,
weekday : {
name : "weekday" ,
description : "Week day number. If the locale assigns Monday as the first day of the week, `0` will be Monday, `-7` will be last week's day."
} ,
reference : {
name : "reference" ,
description : "The date referential, e.g. set this to the note's title"
} ,
reference _format : {
name : "reference_format" ,
description : "The date reference format."
}
}
}
}
} ,
file : {
name : "file" ,
description : "This module contains every internal function related to files." ,
functions : {
content : {
name : "content" ,
description : "Retrieves the file's content" ,
definition : "tp.file.content"
} ,
create _new : {
name : "create_new" ,
description : "Creates a new file using a specified template or with a specified content." ,
definition : "tp.file.create_new(template: TFile ⎮ string, filename?: string, open_new: boolean = false, folder?: TFolder)" ,
args : {
template : {
name : "template" ,
description : "Either the template used for the new file content, or the file content as a string."
} ,
filename : {
name : "filename" ,
description : "The filename of the new file, defaults to \"Untitled\"."
} ,
open _new : {
name : "open_new" ,
description : "Whether to open or not the newly created file. Warning: if you use this option, since commands are executed asynchronously, the file can be opened first and then other commands are appended to that new file and not the previous file."
} ,
folder : {
name : "folder" ,
description : "The folder to put the new file in, defaults to obsidian's default location."
}
}
} ,
creation _date : {
name : "creation_date" ,
description : "Retrieves the file's creation date." ,
definition : "tp.file.creation_date(format: string = \"YYYY-MM-DD HH:mm\")" ,
args : {
format : {
name : "format" ,
description : "Format for the date, refer to format reference"
}
}
} ,
cursor : {
name : "cursor" ,
description : "Sets the cursor to this location after the template has been inserted. \n\nYou can navigate between the different tp.file.cursor using the configured hotkey in obsidian settings.\n" ,
definition : "tp.file.cursor(order?: number)" ,
args : {
order : {
name : "order" ,
description : "The order of the different cursors jump, e.g. it will jump from 1 to 2 to 3, and so on.\nIf you specify multiple tp.file.cursor with the same order, the editor will switch to multi-cursor.\n"
}
}
} ,
cursor _append : {
name : "cursor_append" ,
description : "Appends some content after the active cursor in the file." ,
definition : "tp.file.cursor_append(content: string)" ,
args : {
content : {
name : "content" ,
description : "The content to append after the active cursor"
}
}
} ,
exists : {
name : "exists" ,
description : "Checks if a file exists or not. Returns a true / false boolean." ,
definition : "tp.file.exists(filename: string)" ,
args : {
filename : {
name : "filename" ,
description : "The filename of the file we want to check existence, e.g. MyFile."
}
}
} ,
find _tfile : {
name : "find_tfile" ,
description : "Retrieves the file's folder name." ,
definition : "tp.file.folder(relative: boolean = false)" ,
args : {
relative : {
name : "relative" ,
description : "If set to true, appends the vault relative path to the folder name."
}
}
} ,
include : {
name : "include" ,
description : "Includes the file's link content. Templates in the included content will be resolved." ,
definition : "tp.file.include(include_link: string ⎮ TFile)" ,
args : {
include _link : {
name : "include_link" ,
description : "The link to the file to include, e.g. [[MyFile]], or a TFile object. Also supports sections or blocks inclusions, e.g. [[MyFile#Section1]]"
}
}
} ,
last _modified _date : {
name : "last_modified_date" ,
description : "Retrieves the file's last modification date." ,
definition : "tp.file.last_modified_date(format: string = \"YYYY-MM-DD HH:mm\")" ,
args : {
format : {
name : "format" ,
description : "Format for the date, refer to format reference."
}
}
} ,
move : {
name : "functions.move" ,
description : "Moves the file to the desired vault location." ,
definition : "tp.file.move(new_path: string)" ,
args : {
new _path : {
name : "new_path" ,
description : "The new vault relative path of the file, without the file extension. Note: the new path needs to include the folder and the filename, e.g. /Notes/MyNote"
}
}
} ,
path : {
name : "path" ,
description : "Retrieves the file's absolute path on the system." ,
definition : "tp.file.path(relative: boolean = false)" ,
args : {
relative : {
name : "relative" ,
description : "If set to true, only retrieves the vault's relative path."
}
}
} ,
rename : {
name : "rename" ,
description : "Renames the file (keeps the same file extension)." ,
definition : "tp.file.rename(new_title: string)" ,
args : {
new _title : {
name : "new_title" ,
description : "The new file title."
}
}
} ,
selection : {
name : "selection" ,
description : "Retrieves the active file's text selection." ,
definition : "tp.file.selection()"
} ,
tags : {
name : "tags" ,
description : "Retrieves the file's tags (array of string)" ,
definition : "tp.file.tags"
} ,
title : {
name : "title" ,
definition : "tp.file.title" ,
description : "Retrieves the file's title."
}
}
} ,
frontmatter : {
name : "frontmatter" ,
description : "This modules exposes all the frontmatter variables of a file as variables."
} ,
obsidian : {
name : "obsidian" ,
description : "This module exposes all the functions and classes from the obsidian API."
} ,
system : {
name : "system" ,
description : "This module contains system related functions." ,
functions : {
clipboard : {
name : "clipboard" ,
description : "Retrieves the clipboard's content" ,
definition : "tp.system.clipboard()"
} ,
prompt : {
name : "prompt" ,
description : "Spawns a prompt modal and returns the user's input." ,
definition : "tp.system.prompt(prompt_text?: string, default_value?: string, throw_on_cancel: boolean = false)" ,
args : {
prompt _text : {
name : "prompt_text" ,
description : "Text placed above the input field"
} ,
default _value : {
name : "default_value" ,
description : "A default value for the input field"
} ,
throw _on _cancel : {
name : "throw_on_cancel" ,
description : "Throws an error if the prompt is canceled, instead of returning a `null` value"
}
}
} ,
suggester : {
name : "suggester" ,
description : "Spawns a suggester prompt and returns the user's chosen item." ,
definition : "tp.system.suggester(text_items: string[] ⎮ ((item: T) => string), items: T[], throw_on_cancel: boolean = false, placeholder: string = \"\")" ,
args : {
text _items : {
name : "text_items" ,
description : "Array of strings representing the text that will be displayed for each item in the suggester prompt. This can also be a function that maps an item to its text representation."
} ,
items : {
name : "items" ,
description : "Array containing the values of each item in the correct order."
} ,
throw _on _cancel : {
name : "throw_on_cancel" ,
description : "Throws an error if the prompt is canceled, instead of returning a `null` value"
} ,
placeholder : {
name : "placeholder" ,
description : "Placeholder string of the prompt"
}
}
}
}
} ,
web : {
name : "web" ,
description : "This modules contains every internal function related to the web (making web requests)." ,
functions : {
daily _quote : {
name : "daily_quote" ,
description : "Retrieves and parses the daily quote from the API https://api.quotable.io" ,
definition : "tp.web.daily_quote()"
} ,
random _picture : {
name : "random_picture" ,
description : "Gets a random image from https://unsplash.com/" ,
definition : "tp.web.random_picture(size?: string, query?: string)" ,
args : {
size : {
name : "size" ,
description : "Image size in the format `<width>x<height>`"
} ,
query : {
name : "query" ,
description : "Limits selection to photos matching a search term. Multiple search terms can be passed separated by a comma `,`"
}
}
}
}
}
} ;
var documentation = {
tp
} ;
const module _names = [ "config" , "date" , "file" , "frontmatter" , "obsidian" , "system" , "user" , "web" ] ;
const module _names _checker = new Set ( module _names ) ;
function is _module _name ( x ) {
return typeof x === "string" && module _names _checker . has ( x ) ;
}
function is _function _documentation ( x ) {
if ( x . definition ) {
return true ;
}
return false ;
}
class Documentation {
constructor ( app ) {
this . app = app ;
this . documentation = documentation ;
}
get _all _modules _documentation ( ) {
return Object . values ( this . documentation . tp ) ;
}
get _all _functions _documentation ( module _name ) {
if ( ! this . documentation . tp [ module _name ] . functions ) {
return null ;
}
return Object . values ( this . documentation . tp [ module _name ] . functions ) ;
}
get _module _documentation ( module _name ) {
return this . documentation . tp [ module _name ] ;
}
get _function _documentation ( module _name , function _name ) {
return this . documentation . tp [ module _name ] . functions [ function _name ] ;
}
get _argument _documentation ( module _name , function _name , argument _name ) {
const function _doc = this . get _function _documentation ( module _name , function _name ) ;
if ( ! function _doc || ! function _doc . args ) {
return null ;
}
return function _doc . args [ argument _name ] ;
}
}
class Autocomplete extends obsidian _module . EditorSuggest {
constructor ( app , plugin ) {
super ( app ) ;
this . app = app ;
this . plugin = plugin ;
//private in_command = false;
// https://regex101.com/r/ocmHzR/1
this . tp _keyword _regex = /tp\.(?<module>[a-z]*)?(?<fn_trigger>\.(?<fn>[a-z_]*)?)?$/ ;
}
onTrigger ( cursor , editor , file ) {
const range = editor . getRange ( { line : cursor . line , ch : 0 } , { line : cursor . line , ch : cursor . ch } ) ;
const match = this . tp _keyword _regex . exec ( range ) ;
if ( ! match ) {
return null ;
}
let query ;
const module _name = match . groups [ "module" ] || "" ;
this . module _name = module _name ;
if ( match . groups [ "fn_trigger" ] ) {
if ( module _name == "" || ! is _module _name ( module _name ) ) {
return ;
}
this . function _trigger = true ;
this . function _name = match . groups [ "fn" ] || "" ;
query = this . function _name ;
}
else {
this . function _trigger = false ;
query = this . module _name ;
}
const trigger _info = {
start : { line : cursor . line , ch : cursor . ch - query . length } ,
end : { line : cursor . line , ch : cursor . ch } ,
query : query ,
} ;
this . latest _trigger _info = trigger _info ;
return trigger _info ;
}
getSuggestions ( context ) {
let suggestions ;
if ( this . module _name && this . function _trigger ) {
suggestions =
this . plugin . templater . documentation . get _all _functions _documentation ( this . module _name ) ;
}
else {
suggestions =
this . plugin . templater . documentation . get _all _modules _documentation ( ) ;
}
if ( ! suggestions ) {
return [ ] ;
}
return suggestions . filter ( s => s . name . startsWith ( context . query ) ) ;
}
renderSuggestion ( value , el ) {
el . createEl ( "b" , { text : value . name } ) ;
el . createEl ( "br" ) ;
if ( this . function _trigger && is _function _documentation ( value ) ) {
el . createEl ( "code" , { text : value . definition } ) ;
}
if ( value . description ) {
el . createEl ( "div" , { text : value . description } ) ;
}
}
selectSuggestion ( value , evt ) {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( ! active _view ) {
// TODO: Error msg
return ;
}
active _view . editor . replaceRange ( value . name , this . latest _trigger _info . start , this . latest _trigger _info . end ) ;
if ( this . latest _trigger _info . start . ch == this . latest _trigger _info . end . ch ) {
// Dirty hack to prevent the cursor being at the
// beginning of the word after completion,
// Not sure what's the cause of this bug.
const cursor _pos = this . latest _trigger _info . end ;
cursor _pos . ch += value . name . length ;
active _view . editor . setCursor ( cursor _pos ) ;
}
}
}
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
/* eslint-disable */
( function ( mod ) {
mod ( window . CodeMirror ) ;
} ) ( function ( CodeMirror ) {
CodeMirror . defineMode ( "javascript" , function ( config , parserConfig ) {
var indentUnit = config . indentUnit ;
var statementIndent = parserConfig . statementIndent ;
var jsonldMode = parserConfig . jsonld ;
var jsonMode = parserConfig . json || jsonldMode ;
var trackScope = parserConfig . trackScope !== false ;
var isTS = parserConfig . typescript ;
var wordRE = parserConfig . wordCharacters || /[\w$\xa1-\uffff]/ ;
// Tokenizer
var keywords = ( function ( ) {
function kw ( type ) {
return { type : type , style : "keyword" } ;
}
var A = kw ( "keyword a" ) ,
B = kw ( "keyword b" ) ,
C = kw ( "keyword c" ) ,
D = kw ( "keyword d" ) ;
var operator = kw ( "operator" ) ,
atom = { type : "atom" , style : "atom" } ;
return {
if : kw ( "if" ) ,
while : A ,
with : A ,
else : B ,
do : B ,
try : B ,
finally : B ,
return : D ,
break : D ,
continue : D ,
new : kw ( "new" ) ,
delete : C ,
void : C ,
throw : C ,
debugger : kw ( "debugger" ) ,
var : kw ( "var" ) ,
const : kw ( "var" ) ,
let : kw ( "var" ) ,
function : kw ( "function" ) ,
catch : kw ( "catch" ) ,
for : kw ( "for" ) ,
switch : kw ( "switch" ) ,
case : kw ( "case" ) ,
default : kw ( "default" ) ,
in : operator ,
typeof : operator ,
instanceof : operator ,
true : atom ,
false : atom ,
null : atom ,
undefined : atom ,
NaN : atom ,
Infinity : atom ,
this : kw ( "this" ) ,
class : kw ( "class" ) ,
super : kw ( "atom" ) ,
yield : C ,
export : kw ( "export" ) ,
import : kw ( "import" ) ,
extends : C ,
await : C ,
} ;
} ) ( ) ;
var isOperatorChar = /[+\-*&%=<>!?|~^@]/ ;
var isJsonldKeyword =
/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/ ;
function readRegexp ( stream ) {
var escaped = false ,
next ,
inSet = false ;
while ( ( next = stream . next ( ) ) != null ) {
if ( ! escaped ) {
if ( next == "/" && ! inSet ) return ;
if ( next == "[" ) inSet = true ;
else if ( inSet && next == "]" ) inSet = false ;
}
escaped = ! escaped && next == "\\" ;
}
}
// Used as scratch variables to communicate multiple values without
// consing up tons of objects.
var type , content ;
function ret ( tp , style , cont ) {
type = tp ;
content = cont ;
return style ;
}
function tokenBase ( stream , state ) {
var ch = stream . next ( ) ;
if ( ch == '"' || ch == "'" ) {
state . tokenize = tokenString ( ch ) ;
return state . tokenize ( stream , state ) ;
} else if (
ch == "." &&
stream . match ( /^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/ )
) {
return ret ( "number" , "number" ) ;
} else if ( ch == "." && stream . match ( ".." ) ) {
return ret ( "spread" , "meta" ) ;
} else if ( /[\[\]{}\(\),;\:\.]/ . test ( ch ) ) {
return ret ( ch ) ;
} else if ( ch == "=" && stream . eat ( ">" ) ) {
return ret ( "=>" , "operator" ) ;
} else if (
ch == "0" &&
stream . match ( /^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/ )
) {
return ret ( "number" , "number" ) ;
} else if ( /\d/ . test ( ch ) ) {
stream . match (
/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/
) ;
return ret ( "number" , "number" ) ;
} else if ( ch == "/" ) {
if ( stream . eat ( "*" ) ) {
state . tokenize = tokenComment ;
return tokenComment ( stream , state ) ;
} else if ( stream . eat ( "/" ) ) {
stream . skipToEnd ( ) ;
return ret ( "comment" , "comment" ) ;
} else if ( expressionAllowed ( stream , state , 1 ) ) {
readRegexp ( stream ) ;
stream . match ( /^\b(([gimyus])(?![gimyus]*\2))+\b/ ) ;
return ret ( "regexp" , "string-2" ) ;
} else {
stream . eat ( "=" ) ;
return ret ( "operator" , "operator" , stream . current ( ) ) ;
}
} else if ( ch == "`" ) {
state . tokenize = tokenQuasi ;
return tokenQuasi ( stream , state ) ;
} else if ( ch == "#" && stream . peek ( ) == "!" ) {
stream . skipToEnd ( ) ;
return ret ( "meta" , "meta" ) ;
} else if ( ch == "#" && stream . eatWhile ( wordRE ) ) {
return ret ( "variable" , "property" ) ;
} else if (
( ch == "<" && stream . match ( "!--" ) ) ||
( ch == "-" &&
stream . match ( "->" ) &&
! /\S/ . test ( stream . string . slice ( 0 , stream . start ) ) )
) {
stream . skipToEnd ( ) ;
return ret ( "comment" , "comment" ) ;
} else if ( isOperatorChar . test ( ch ) ) {
if ( ch != ">" || ! state . lexical || state . lexical . type != ">" ) {
if ( stream . eat ( "=" ) ) {
if ( ch == "!" || ch == "=" ) stream . eat ( "=" ) ;
} else if ( /[<>*+\-|&?]/ . test ( ch ) ) {
stream . eat ( ch ) ;
if ( ch == ">" ) stream . eat ( ch ) ;
}
}
if ( ch == "?" && stream . eat ( "." ) ) return ret ( "." ) ;
return ret ( "operator" , "operator" , stream . current ( ) ) ;
} else if ( wordRE . test ( ch ) ) {
stream . eatWhile ( wordRE ) ;
var word = stream . current ( ) ;
if ( state . lastType != "." ) {
if ( keywords . propertyIsEnumerable ( word ) ) {
var kw = keywords [ word ] ;
return ret ( kw . type , kw . style , word ) ;
}
if (
word == "async" &&
stream . match (
/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/ ,
false
)
)
return ret ( "async" , "keyword" , word ) ;
}
return ret ( "variable" , "variable" , word ) ;
}
}
function tokenString ( quote ) {
return function ( stream , state ) {
var escaped = false ,
next ;
if (
jsonldMode &&
stream . peek ( ) == "@" &&
stream . match ( isJsonldKeyword )
) {
state . tokenize = tokenBase ;
return ret ( "jsonld-keyword" , "meta" ) ;
}
while ( ( next = stream . next ( ) ) != null ) {
if ( next == quote && ! escaped ) break ;
escaped = ! escaped && next == "\\" ;
}
if ( ! escaped ) state . tokenize = tokenBase ;
return ret ( "string" , "string" ) ;
} ;
}
function tokenComment ( stream , state ) {
var maybeEnd = false ,
ch ;
while ( ( ch = stream . next ( ) ) ) {
if ( ch == "/" && maybeEnd ) {
state . tokenize = tokenBase ;
break ;
}
maybeEnd = ch == "*" ;
}
return ret ( "comment" , "comment" ) ;
}
function tokenQuasi ( stream , state ) {
var escaped = false ,
next ;
while ( ( next = stream . next ( ) ) != null ) {
if (
! escaped &&
( next == "`" || ( next == "$" && stream . eat ( "{" ) ) )
) {
state . tokenize = tokenBase ;
break ;
}
escaped = ! escaped && next == "\\" ;
}
return ret ( "quasi" , "string-2" , stream . current ( ) ) ;
}
var brackets = "([{}])" ;
// This is a crude lookahead trick to try and notice that we're
// parsing the argument patterns for a fat-arrow function before we
// actually hit the arrow token. It only works if the arrow is on
// the same line as the arguments and there's no strange noise
// (comments) in between. Fallback is to only notice when we hit the
// arrow, and not declare the arguments as locals for the arrow
// body.
function findFatArrow ( stream , state ) {
if ( state . fatArrowAt ) state . fatArrowAt = null ;
var arrow = stream . string . indexOf ( "=>" , stream . start ) ;
if ( arrow < 0 ) return ;
if ( isTS ) {
// Try to skip TypeScript return type declarations after the arguments
var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/ . exec (
stream . string . slice ( stream . start , arrow )
) ;
if ( m ) arrow = m . index ;
}
var depth = 0 ,
sawSomething = false ;
for ( var pos = arrow - 1 ; pos >= 0 ; -- pos ) {
var ch = stream . string . charAt ( pos ) ;
var bracket = brackets . indexOf ( ch ) ;
if ( bracket >= 0 && bracket < 3 ) {
if ( ! depth ) {
++ pos ;
break ;
}
if ( -- depth == 0 ) {
if ( ch == "(" ) sawSomething = true ;
break ;
}
} else if ( bracket >= 3 && bracket < 6 ) {
++ depth ;
} else if ( wordRE . test ( ch ) ) {
sawSomething = true ;
} else if ( /["'\/`]/ . test ( ch ) ) {
for ( ; ; -- pos ) {
if ( pos == 0 ) return ;
var next = stream . string . charAt ( pos - 1 ) ;
if (
next == ch &&
stream . string . charAt ( pos - 2 ) != "\\"
) {
pos -- ;
break ;
}
}
} else if ( sawSomething && ! depth ) {
++ pos ;
break ;
}
}
if ( sawSomething && ! depth ) state . fatArrowAt = pos ;
}
// Parser
var atomicTypes = {
atom : true ,
number : true ,
variable : true ,
string : true ,
regexp : true ,
this : true ,
import : true ,
"jsonld-keyword" : true ,
} ;
function JSLexical ( indented , column , type , align , prev , info ) {
this . indented = indented ;
this . column = column ;
this . type = type ;
this . prev = prev ;
this . info = info ;
if ( align != null ) this . align = align ;
}
function inScope ( state , varname ) {
if ( ! trackScope ) return false ;
for ( var v = state . localVars ; v ; v = v . next )
if ( v . name == varname ) return true ;
for ( var cx = state . context ; cx ; cx = cx . prev ) {
for ( var v = cx . vars ; v ; v = v . next )
if ( v . name == varname ) return true ;
}
}
function parseJS ( state , style , type , content , stream ) {
var cc = state . cc ;
// Communicate our context to the combinators.
// (Less wasteful than consing up a hundred closures on every call.)
cx . state = state ;
cx . stream = stream ;
( cx . marked = null ) , ( cx . cc = cc ) ;
cx . style = style ;
if ( ! state . lexical . hasOwnProperty ( "align" ) )
state . lexical . align = true ;
while ( true ) {
var combinator = cc . length
? cc . pop ( )
: jsonMode
? expression
: statement ;
if ( combinator ( type , content ) ) {
while ( cc . length && cc [ cc . length - 1 ] . lex ) cc . pop ( ) ( ) ;
if ( cx . marked ) return cx . marked ;
if ( type == "variable" && inScope ( state , content ) )
return "variable-2" ;
return style ;
}
}
}
// Combinator utils
var cx = { state : null , column : null , marked : null , cc : null } ;
function pass ( ) {
for ( var i = arguments . length - 1 ; i >= 0 ; i -- )
cx . cc . push ( arguments [ i ] ) ;
}
function cont ( ) {
pass . apply ( null , arguments ) ;
return true ;
}
function inList ( name , list ) {
for ( var v = list ; v ; v = v . next ) if ( v . name == name ) return true ;
return false ;
}
function register ( varname ) {
var state = cx . state ;
cx . marked = "def" ;
if ( ! trackScope ) return ;
if ( state . context ) {
if (
state . lexical . info == "var" &&
state . context &&
state . context . block
) {
// FIXME function decls are also not block scoped
var newContext = registerVarScoped ( varname , state . context ) ;
if ( newContext != null ) {
state . context = newContext ;
return ;
}
} else if ( ! inList ( varname , state . localVars ) ) {
state . localVars = new Var ( varname , state . localVars ) ;
return ;
}
}
// Fall through means this is global
if ( parserConfig . globalVars && ! inList ( varname , state . globalVars ) )
state . globalVars = new Var ( varname , state . globalVars ) ;
}
function registerVarScoped ( varname , context ) {
if ( ! context ) {
return null ;
} else if ( context . block ) {
var inner = registerVarScoped ( varname , context . prev ) ;
if ( ! inner ) return null ;
if ( inner == context . prev ) return context ;
return new Context ( inner , context . vars , true ) ;
} else if ( inList ( varname , context . vars ) ) {
return context ;
} else {
return new Context (
context . prev ,
new Var ( varname , context . vars ) ,
false
) ;
}
}
function isModifier ( name ) {
return (
name == "public" ||
name == "private" ||
name == "protected" ||
name == "abstract" ||
name == "readonly"
) ;
}
// Combinators
function Context ( prev , vars , block ) {
this . prev = prev ;
this . vars = vars ;
this . block = block ;
}
function Var ( name , next ) {
this . name = name ;
this . next = next ;
}
var defaultVars = new Var ( "this" , new Var ( "arguments" , null ) ) ;
function pushcontext ( ) {
cx . state . context = new Context (
cx . state . context ,
cx . state . localVars ,
false
) ;
cx . state . localVars = defaultVars ;
}
function pushblockcontext ( ) {
cx . state . context = new Context (
cx . state . context ,
cx . state . localVars ,
true
) ;
cx . state . localVars = null ;
}
function popcontext ( ) {
cx . state . localVars = cx . state . context . vars ;
cx . state . context = cx . state . context . prev ;
}
popcontext . lex = true ;
function pushlex ( type , info ) {
var result = function ( ) {
var state = cx . state ,
indent = state . indented ;
if ( state . lexical . type == "stat" )
indent = state . lexical . indented ;
else
for (
var outer = state . lexical ;
outer && outer . type == ")" && outer . align ;
outer = outer . prev
)
indent = outer . indented ;
state . lexical = new JSLexical (
indent ,
cx . stream . column ( ) ,
type ,
null ,
state . lexical ,
info
) ;
} ;
result . lex = true ;
return result ;
}
function poplex ( ) {
var state = cx . state ;
if ( state . lexical . prev ) {
if ( state . lexical . type == ")" )
state . indented = state . lexical . indented ;
state . lexical = state . lexical . prev ;
}
}
poplex . lex = true ;
function expect ( wanted ) {
function exp ( type ) {
if ( type == wanted ) return cont ( ) ;
else if (
wanted == ";" ||
type == "}" ||
type == ")" ||
type == "]"
)
return pass ( ) ;
else return cont ( exp ) ;
}
return exp ;
}
function statement ( type , value ) {
if ( type == "var" )
return cont (
pushlex ( "vardef" , value ) ,
vardef ,
expect ( ";" ) ,
poplex
) ;
if ( type == "keyword a" )
return cont ( pushlex ( "form" ) , parenExpr , statement , poplex ) ;
if ( type == "keyword b" )
return cont ( pushlex ( "form" ) , statement , poplex ) ;
if ( type == "keyword d" )
return cx . stream . match ( /^\s*$/ , false )
? cont ( )
: cont (
pushlex ( "stat" ) ,
maybeexpression ,
expect ( ";" ) ,
poplex
) ;
if ( type == "debugger" ) return cont ( expect ( ";" ) ) ;
if ( type == "{" )
return cont (
pushlex ( "}" ) ,
pushblockcontext ,
block ,
poplex ,
popcontext
) ;
if ( type == ";" ) return cont ( ) ;
if ( type == "if" ) {
if (
cx . state . lexical . info == "else" &&
cx . state . cc [ cx . state . cc . length - 1 ] == poplex
)
cx . state . cc . pop ( ) ( ) ;
return cont (
pushlex ( "form" ) ,
parenExpr ,
statement ,
poplex ,
maybeelse
) ;
}
if ( type == "function" ) return cont ( functiondef ) ;
if ( type == "for" )
return cont (
pushlex ( "form" ) ,
pushblockcontext ,
forspec ,
statement ,
popcontext ,
poplex
) ;
if ( type == "class" || ( isTS && value == "interface" ) ) {
cx . marked = "keyword" ;
return cont (
pushlex ( "form" , type == "class" ? type : value ) ,
className ,
poplex
) ;
}
if ( type == "variable" ) {
if ( isTS && value == "declare" ) {
cx . marked = "keyword" ;
return cont ( statement ) ;
} else if (
isTS &&
( value == "module" || value == "enum" || value == "type" ) &&
cx . stream . match ( /^\s*\w/ , false )
) {
cx . marked = "keyword" ;
if ( value == "enum" ) return cont ( enumdef ) ;
else if ( value == "type" )
return cont (
typename ,
expect ( "operator" ) ,
typeexpr ,
expect ( ";" )
) ;
else
return cont (
pushlex ( "form" ) ,
pattern ,
expect ( "{" ) ,
pushlex ( "}" ) ,
block ,
poplex ,
poplex
) ;
} else if ( isTS && value == "namespace" ) {
cx . marked = "keyword" ;
return cont ( pushlex ( "form" ) , expression , statement , poplex ) ;
} else if ( isTS && value == "abstract" ) {
cx . marked = "keyword" ;
return cont ( statement ) ;
} else {
return cont ( pushlex ( "stat" ) , maybelabel ) ;
}
}
if ( type == "switch" )
return cont (
pushlex ( "form" ) ,
parenExpr ,
expect ( "{" ) ,
pushlex ( "}" , "switch" ) ,
pushblockcontext ,
block ,
poplex ,
poplex ,
popcontext
) ;
if ( type == "case" ) return cont ( expression , expect ( ":" ) ) ;
if ( type == "default" ) return cont ( expect ( ":" ) ) ;
if ( type == "catch" )
return cont (
pushlex ( "form" ) ,
pushcontext ,
maybeCatchBinding ,
statement ,
poplex ,
popcontext
) ;
if ( type == "export" )
return cont ( pushlex ( "stat" ) , afterExport , poplex ) ;
if ( type == "import" )
return cont ( pushlex ( "stat" ) , afterImport , poplex ) ;
if ( type == "async" ) return cont ( statement ) ;
if ( value == "@" ) return cont ( expression , statement ) ;
return pass ( pushlex ( "stat" ) , expression , expect ( ";" ) , poplex ) ;
}
function maybeCatchBinding ( type ) {
if ( type == "(" ) return cont ( funarg , expect ( ")" ) ) ;
}
function expression ( type , value ) {
return expressionInner ( type , value , false ) ;
}
function expressionNoComma ( type , value ) {
return expressionInner ( type , value , true ) ;
}
function parenExpr ( type ) {
if ( type != "(" ) return pass ( ) ;
return cont ( pushlex ( ")" ) , maybeexpression , expect ( ")" ) , poplex ) ;
}
function expressionInner ( type , value , noComma ) {
if ( cx . state . fatArrowAt == cx . stream . start ) {
var body = noComma ? arrowBodyNoComma : arrowBody ;
if ( type == "(" )
return cont (
pushcontext ,
pushlex ( ")" ) ,
commasep ( funarg , ")" ) ,
poplex ,
expect ( "=>" ) ,
body ,
popcontext
) ;
else if ( type == "variable" )
return pass (
pushcontext ,
pattern ,
expect ( "=>" ) ,
body ,
popcontext
) ;
}
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma ;
if ( atomicTypes . hasOwnProperty ( type ) ) return cont ( maybeop ) ;
if ( type == "function" ) return cont ( functiondef , maybeop ) ;
if ( type == "class" || ( isTS && value == "interface" ) ) {
cx . marked = "keyword" ;
return cont ( pushlex ( "form" ) , classExpression , poplex ) ;
}
if ( type == "keyword c" || type == "async" )
return cont ( noComma ? expressionNoComma : expression ) ;
if ( type == "(" )
return cont (
pushlex ( ")" ) ,
maybeexpression ,
expect ( ")" ) ,
poplex ,
maybeop
) ;
if ( type == "operator" || type == "spread" )
return cont ( noComma ? expressionNoComma : expression ) ;
if ( type == "[" )
return cont ( pushlex ( "]" ) , arrayLiteral , poplex , maybeop ) ;
if ( type == "{" ) return contCommasep ( objprop , "}" , null , maybeop ) ;
if ( type == "quasi" ) return pass ( quasi , maybeop ) ;
if ( type == "new" ) return cont ( maybeTarget ( noComma ) ) ;
return cont ( ) ;
}
function maybeexpression ( type ) {
if ( type . match ( /[;\}\)\],]/ ) ) return pass ( ) ;
return pass ( expression ) ;
}
function maybeoperatorComma ( type , value ) {
if ( type == "," ) return cont ( maybeexpression ) ;
return maybeoperatorNoComma ( type , value , false ) ;
}
function maybeoperatorNoComma ( type , value , noComma ) {
var me =
noComma == false ? maybeoperatorComma : maybeoperatorNoComma ;
var expr = noComma == false ? expression : expressionNoComma ;
if ( type == "=>" )
return cont (
pushcontext ,
noComma ? arrowBodyNoComma : arrowBody ,
popcontext
) ;
if ( type == "operator" ) {
if ( /\+\+|--/ . test ( value ) || ( isTS && value == "!" ) )
return cont ( me ) ;
if (
isTS &&
value == "<" &&
cx . stream . match ( /^([^<>]|<[^<>]*>)*>\s*\(/ , false )
)
return cont (
pushlex ( ">" ) ,
commasep ( typeexpr , ">" ) ,
poplex ,
me
) ;
if ( value == "?" ) return cont ( expression , expect ( ":" ) , expr ) ;
return cont ( expr ) ;
}
if ( type == "quasi" ) {
return pass ( quasi , me ) ;
}
if ( type == ";" ) return ;
if ( type == "(" )
return contCommasep ( expressionNoComma , ")" , "call" , me ) ;
if ( type == "." ) return cont ( property , me ) ;
if ( type == "[" )
return cont (
pushlex ( "]" ) ,
maybeexpression ,
expect ( "]" ) ,
poplex ,
me
) ;
if ( isTS && value == "as" ) {
cx . marked = "keyword" ;
return cont ( typeexpr , me ) ;
}
if ( type == "regexp" ) {
cx . state . lastType = cx . marked = "operator" ;
cx . stream . backUp ( cx . stream . pos - cx . stream . start - 1 ) ;
return cont ( expr ) ;
}
}
function quasi ( type , value ) {
if ( type != "quasi" ) return pass ( ) ;
if ( value . slice ( value . length - 2 ) != "${" ) return cont ( quasi ) ;
return cont ( maybeexpression , continueQuasi ) ;
}
function continueQuasi ( type ) {
if ( type == "}" ) {
cx . marked = "string-2" ;
cx . state . tokenize = tokenQuasi ;
return cont ( quasi ) ;
}
}
function arrowBody ( type ) {
findFatArrow ( cx . stream , cx . state ) ;
return pass ( type == "{" ? statement : expression ) ;
}
function arrowBodyNoComma ( type ) {
findFatArrow ( cx . stream , cx . state ) ;
return pass ( type == "{" ? statement : expressionNoComma ) ;
}
function maybeTarget ( noComma ) {
return function ( type ) {
if ( type == "." ) return cont ( noComma ? targetNoComma : target ) ;
else if ( type == "variable" && isTS )
return cont (
maybeTypeArgs ,
noComma ? maybeoperatorNoComma : maybeoperatorComma
) ;
else return pass ( noComma ? expressionNoComma : expression ) ;
} ;
}
function target ( _ , value ) {
if ( value == "target" ) {
cx . marked = "keyword" ;
return cont ( maybeoperatorComma ) ;
}
}
function targetNoComma ( _ , value ) {
if ( value == "target" ) {
cx . marked = "keyword" ;
return cont ( maybeoperatorNoComma ) ;
}
}
function maybelabel ( type ) {
if ( type == ":" ) return cont ( poplex , statement ) ;
return pass ( maybeoperatorComma , expect ( ";" ) , poplex ) ;
}
function property ( type ) {
if ( type == "variable" ) {
cx . marked = "property" ;
return cont ( ) ;
}
}
function objprop ( type , value ) {
if ( type == "async" ) {
cx . marked = "property" ;
return cont ( objprop ) ;
} else if ( type == "variable" || cx . style == "keyword" ) {
cx . marked = "property" ;
if ( value == "get" || value == "set" ) return cont ( getterSetter ) ;
var m ; // Work around fat-arrow-detection complication for detecting typescript typed arrow params
if (
isTS &&
cx . state . fatArrowAt == cx . stream . start &&
( m = cx . stream . match ( /^\s*:\s*/ , false ) )
)
cx . state . fatArrowAt = cx . stream . pos + m [ 0 ] . length ;
return cont ( afterprop ) ;
} else if ( type == "number" || type == "string" ) {
cx . marked = jsonldMode ? "property" : cx . style + " property" ;
return cont ( afterprop ) ;
} else if ( type == "jsonld-keyword" ) {
return cont ( afterprop ) ;
} else if ( isTS && isModifier ( value ) ) {
cx . marked = "keyword" ;
return cont ( objprop ) ;
} else if ( type == "[" ) {
return cont ( expression , maybetype , expect ( "]" ) , afterprop ) ;
} else if ( type == "spread" ) {
return cont ( expressionNoComma , afterprop ) ;
} else if ( value == "*" ) {
cx . marked = "keyword" ;
return cont ( objprop ) ;
} else if ( type == ":" ) {
return pass ( afterprop ) ;
}
}
function getterSetter ( type ) {
if ( type != "variable" ) return pass ( afterprop ) ;
cx . marked = "property" ;
return cont ( functiondef ) ;
}
function afterprop ( type ) {
if ( type == ":" ) return cont ( expressionNoComma ) ;
if ( type == "(" ) return pass ( functiondef ) ;
}
function commasep ( what , end , sep ) {
function proceed ( type , value ) {
if ( sep ? sep . indexOf ( type ) > - 1 : type == "," ) {
var lex = cx . state . lexical ;
if ( lex . info == "call" ) lex . pos = ( lex . pos || 0 ) + 1 ;
return cont ( function ( type , value ) {
if ( type == end || value == end ) return pass ( ) ;
return pass ( what ) ;
} , proceed ) ;
}
if ( type == end || value == end ) return cont ( ) ;
if ( sep && sep . indexOf ( ";" ) > - 1 ) return pass ( what ) ;
return cont ( expect ( end ) ) ;
}
return function ( type , value ) {
if ( type == end || value == end ) return cont ( ) ;
return pass ( what , proceed ) ;
} ;
}
function contCommasep ( what , end , info ) {
for ( var i = 3 ; i < arguments . length ; i ++ ) cx . cc . push ( arguments [ i ] ) ;
return cont ( pushlex ( end , info ) , commasep ( what , end ) , poplex ) ;
}
function block ( type ) {
if ( type == "}" ) return cont ( ) ;
return pass ( statement , block ) ;
}
function maybetype ( type , value ) {
if ( isTS ) {
if ( type == ":" ) return cont ( typeexpr ) ;
if ( value == "?" ) return cont ( maybetype ) ;
}
}
function maybetypeOrIn ( type , value ) {
if ( isTS && ( type == ":" || value == "in" ) ) return cont ( typeexpr ) ;
}
function mayberettype ( type ) {
if ( isTS && type == ":" ) {
if ( cx . stream . match ( /^\s*\w+\s+is\b/ , false ) )
return cont ( expression , isKW , typeexpr ) ;
else return cont ( typeexpr ) ;
}
}
function isKW ( _ , value ) {
if ( value == "is" ) {
cx . marked = "keyword" ;
return cont ( ) ;
}
}
function typeexpr ( type , value ) {
if (
value == "keyof" ||
value == "typeof" ||
value == "infer" ||
value == "readonly"
) {
cx . marked = "keyword" ;
return cont ( value == "typeof" ? expressionNoComma : typeexpr ) ;
}
if ( type == "variable" || value == "void" ) {
cx . marked = "type" ;
return cont ( afterType ) ;
}
if ( value == "|" || value == "&" ) return cont ( typeexpr ) ;
if ( type == "string" || type == "number" || type == "atom" )
return cont ( afterType ) ;
if ( type == "[" )
return cont (
pushlex ( "]" ) ,
commasep ( typeexpr , "]" , "," ) ,
poplex ,
afterType
) ;
if ( type == "{" )
return cont ( pushlex ( "}" ) , typeprops , poplex , afterType ) ;
if ( type == "(" )
return cont ( commasep ( typearg , ")" ) , maybeReturnType , afterType ) ;
if ( type == "<" ) return cont ( commasep ( typeexpr , ">" ) , typeexpr ) ;
if ( type == "quasi" ) {
return pass ( quasiType , afterType ) ;
}
}
function maybeReturnType ( type ) {
if ( type == "=>" ) return cont ( typeexpr ) ;
}
function typeprops ( type ) {
if ( type . match ( /[\}\)\]]/ ) ) return cont ( ) ;
if ( type == "," || type == ";" ) return cont ( typeprops ) ;
return pass ( typeprop , typeprops ) ;
}
function typeprop ( type , value ) {
if ( type == "variable" || cx . style == "keyword" ) {
cx . marked = "property" ;
return cont ( typeprop ) ;
} else if ( value == "?" || type == "number" || type == "string" ) {
return cont ( typeprop ) ;
} else if ( type == ":" ) {
return cont ( typeexpr ) ;
} else if ( type == "[" ) {
return cont (
expect ( "variable" ) ,
maybetypeOrIn ,
expect ( "]" ) ,
typeprop
) ;
} else if ( type == "(" ) {
return pass ( functiondecl , typeprop ) ;
} else if ( ! type . match ( /[;\}\)\],]/ ) ) {
return cont ( ) ;
}
}
function quasiType ( type , value ) {
if ( type != "quasi" ) return pass ( ) ;
if ( value . slice ( value . length - 2 ) != "${" ) return cont ( quasiType ) ;
return cont ( typeexpr , continueQuasiType ) ;
}
function continueQuasiType ( type ) {
if ( type == "}" ) {
cx . marked = "string-2" ;
cx . state . tokenize = tokenQuasi ;
return cont ( quasiType ) ;
}
}
function typearg ( type , value ) {
if (
( type == "variable" && cx . stream . match ( /^\s*[?:]/ , false ) ) ||
value == "?"
)
return cont ( typearg ) ;
if ( type == ":" ) return cont ( typeexpr ) ;
if ( type == "spread" ) return cont ( typearg ) ;
return pass ( typeexpr ) ;
}
function afterType ( type , value ) {
if ( value == "<" )
return cont (
pushlex ( ">" ) ,
commasep ( typeexpr , ">" ) ,
poplex ,
afterType
) ;
if ( value == "|" || type == "." || value == "&" )
return cont ( typeexpr ) ;
if ( type == "[" ) return cont ( typeexpr , expect ( "]" ) , afterType ) ;
if ( value == "extends" || value == "implements" ) {
cx . marked = "keyword" ;
return cont ( typeexpr ) ;
}
if ( value == "?" ) return cont ( typeexpr , expect ( ":" ) , typeexpr ) ;
}
function maybeTypeArgs ( _ , value ) {
if ( value == "<" )
return cont (
pushlex ( ">" ) ,
commasep ( typeexpr , ">" ) ,
poplex ,
afterType
) ;
}
function typeparam ( ) {
return pass ( typeexpr , maybeTypeDefault ) ;
}
function maybeTypeDefault ( _ , value ) {
if ( value == "=" ) return cont ( typeexpr ) ;
}
function vardef ( _ , value ) {
if ( value == "enum" ) {
cx . marked = "keyword" ;
return cont ( enumdef ) ;
}
return pass ( pattern , maybetype , maybeAssign , vardefCont ) ;
}
function pattern ( type , value ) {
if ( isTS && isModifier ( value ) ) {
cx . marked = "keyword" ;
return cont ( pattern ) ;
}
if ( type == "variable" ) {
register ( value ) ;
return cont ( ) ;
}
if ( type == "spread" ) return cont ( pattern ) ;
if ( type == "[" ) return contCommasep ( eltpattern , "]" ) ;
if ( type == "{" ) return contCommasep ( proppattern , "}" ) ;
}
function proppattern ( type , value ) {
if ( type == "variable" && ! cx . stream . match ( /^\s*:/ , false ) ) {
register ( value ) ;
return cont ( maybeAssign ) ;
}
if ( type == "variable" ) cx . marked = "property" ;
if ( type == "spread" ) return cont ( pattern ) ;
if ( type == "}" ) return pass ( ) ;
if ( type == "[" )
return cont ( expression , expect ( "]" ) , expect ( ":" ) , proppattern ) ;
return cont ( expect ( ":" ) , pattern , maybeAssign ) ;
}
function eltpattern ( ) {
return pass ( pattern , maybeAssign ) ;
}
function maybeAssign ( _type , value ) {
if ( value == "=" ) return cont ( expressionNoComma ) ;
}
function vardefCont ( type ) {
if ( type == "," ) return cont ( vardef ) ;
}
function maybeelse ( type , value ) {
if ( type == "keyword b" && value == "else" )
return cont ( pushlex ( "form" , "else" ) , statement , poplex ) ;
}
function forspec ( type , value ) {
if ( value == "await" ) return cont ( forspec ) ;
if ( type == "(" ) return cont ( pushlex ( ")" ) , forspec1 , poplex ) ;
}
function forspec1 ( type ) {
if ( type == "var" ) return cont ( vardef , forspec2 ) ;
if ( type == "variable" ) return cont ( forspec2 ) ;
return pass ( forspec2 ) ;
}
function forspec2 ( type , value ) {
if ( type == ")" ) return cont ( ) ;
if ( type == ";" ) return cont ( forspec2 ) ;
if ( value == "in" || value == "of" ) {
cx . marked = "keyword" ;
return cont ( expression , forspec2 ) ;
}
return pass ( expression , forspec2 ) ;
}
function functiondef ( type , value ) {
if ( value == "*" ) {
cx . marked = "keyword" ;
return cont ( functiondef ) ;
}
if ( type == "variable" ) {
register ( value ) ;
return cont ( functiondef ) ;
}
if ( type == "(" )
return cont (
pushcontext ,
pushlex ( ")" ) ,
commasep ( funarg , ")" ) ,
poplex ,
mayberettype ,
statement ,
popcontext
) ;
if ( isTS && value == "<" )
return cont (
pushlex ( ">" ) ,
commasep ( typeparam , ">" ) ,
poplex ,
functiondef
) ;
}
function functiondecl ( type , value ) {
if ( value == "*" ) {
cx . marked = "keyword" ;
return cont ( functiondecl ) ;
}
if ( type == "variable" ) {
register ( value ) ;
return cont ( functiondecl ) ;
}
if ( type == "(" )
return cont (
pushcontext ,
pushlex ( ")" ) ,
commasep ( funarg , ")" ) ,
poplex ,
mayberettype ,
popcontext
) ;
if ( isTS && value == "<" )
return cont (
pushlex ( ">" ) ,
commasep ( typeparam , ">" ) ,
poplex ,
functiondecl
) ;
}
function typename ( type , value ) {
if ( type == "keyword" || type == "variable" ) {
cx . marked = "type" ;
return cont ( typename ) ;
} else if ( value == "<" ) {
return cont ( pushlex ( ">" ) , commasep ( typeparam , ">" ) , poplex ) ;
}
}
function funarg ( type , value ) {
if ( value == "@" ) cont ( expression , funarg ) ;
if ( type == "spread" ) return cont ( funarg ) ;
if ( isTS && isModifier ( value ) ) {
cx . marked = "keyword" ;
return cont ( funarg ) ;
}
if ( isTS && type == "this" ) return cont ( maybetype , maybeAssign ) ;
return pass ( pattern , maybetype , maybeAssign ) ;
}
function classExpression ( type , value ) {
// Class expressions may have an optional name.
if ( type == "variable" ) return className ( type , value ) ;
return classNameAfter ( type , value ) ;
}
function className ( type , value ) {
if ( type == "variable" ) {
register ( value ) ;
return cont ( classNameAfter ) ;
}
}
function classNameAfter ( type , value ) {
if ( value == "<" )
return cont (
pushlex ( ">" ) ,
commasep ( typeparam , ">" ) ,
poplex ,
classNameAfter
) ;
if (
value == "extends" ||
value == "implements" ||
( isTS && type == "," )
) {
if ( value == "implements" ) cx . marked = "keyword" ;
return cont ( isTS ? typeexpr : expression , classNameAfter ) ;
}
if ( type == "{" ) return cont ( pushlex ( "}" ) , classBody , poplex ) ;
}
function classBody ( type , value ) {
if (
type == "async" ||
( type == "variable" &&
( value == "static" ||
value == "get" ||
value == "set" ||
( isTS && isModifier ( value ) ) ) &&
cx . stream . match ( /^\s+[\w$\xa1-\uffff]/ , false ) )
) {
cx . marked = "keyword" ;
return cont ( classBody ) ;
}
if ( type == "variable" || cx . style == "keyword" ) {
cx . marked = "property" ;
return cont ( classfield , classBody ) ;
}
if ( type == "number" || type == "string" )
return cont ( classfield , classBody ) ;
if ( type == "[" )
return cont (
expression ,
maybetype ,
expect ( "]" ) ,
classfield ,
classBody
) ;
if ( value == "*" ) {
cx . marked = "keyword" ;
return cont ( classBody ) ;
}
if ( isTS && type == "(" ) return pass ( functiondecl , classBody ) ;
if ( type == ";" || type == "," ) return cont ( classBody ) ;
if ( type == "}" ) return cont ( ) ;
if ( value == "@" ) return cont ( expression , classBody ) ;
}
function classfield ( type , value ) {
if ( value == "!" ) return cont ( classfield ) ;
if ( value == "?" ) return cont ( classfield ) ;
if ( type == ":" ) return cont ( typeexpr , maybeAssign ) ;
if ( value == "=" ) return cont ( expressionNoComma ) ;
var context = cx . state . lexical . prev ,
isInterface = context && context . info == "interface" ;
return pass ( isInterface ? functiondecl : functiondef ) ;
}
function afterExport ( type , value ) {
if ( value == "*" ) {
cx . marked = "keyword" ;
return cont ( maybeFrom , expect ( ";" ) ) ;
}
if ( value == "default" ) {
cx . marked = "keyword" ;
return cont ( expression , expect ( ";" ) ) ;
}
if ( type == "{" )
return cont ( commasep ( exportField , "}" ) , maybeFrom , expect ( ";" ) ) ;
return pass ( statement ) ;
}
function exportField ( type , value ) {
if ( value == "as" ) {
cx . marked = "keyword" ;
return cont ( expect ( "variable" ) ) ;
}
if ( type == "variable" ) return pass ( expressionNoComma , exportField ) ;
}
function afterImport ( type ) {
if ( type == "string" ) return cont ( ) ;
if ( type == "(" ) return pass ( expression ) ;
if ( type == "." ) return pass ( maybeoperatorComma ) ;
return pass ( importSpec , maybeMoreImports , maybeFrom ) ;
}
function importSpec ( type , value ) {
if ( type == "{" ) return contCommasep ( importSpec , "}" ) ;
if ( type == "variable" ) register ( value ) ;
if ( value == "*" ) cx . marked = "keyword" ;
return cont ( maybeAs ) ;
}
function maybeMoreImports ( type ) {
if ( type == "," ) return cont ( importSpec , maybeMoreImports ) ;
}
function maybeAs ( _type , value ) {
if ( value == "as" ) {
cx . marked = "keyword" ;
return cont ( importSpec ) ;
}
}
function maybeFrom ( _type , value ) {
if ( value == "from" ) {
cx . marked = "keyword" ;
return cont ( expression ) ;
}
}
function arrayLiteral ( type ) {
if ( type == "]" ) return cont ( ) ;
return pass ( commasep ( expressionNoComma , "]" ) ) ;
}
function enumdef ( ) {
return pass (
pushlex ( "form" ) ,
pattern ,
expect ( "{" ) ,
pushlex ( "}" ) ,
commasep ( enummember , "}" ) ,
poplex ,
poplex
) ;
}
function enummember ( ) {
return pass ( pattern , maybeAssign ) ;
}
function isContinuedStatement ( state , textAfter ) {
return (
state . lastType == "operator" ||
state . lastType == "," ||
isOperatorChar . test ( textAfter . charAt ( 0 ) ) ||
/[,.]/ . test ( textAfter . charAt ( 0 ) )
) ;
}
function expressionAllowed ( stream , state , backUp ) {
return (
( state . tokenize == tokenBase &&
/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/ . test (
state . lastType
) ) ||
( state . lastType == "quasi" &&
/\{\s*$/ . test (
stream . string . slice ( 0 , stream . pos - ( backUp || 0 ) )
) )
) ;
}
// Interface
return {
startState : function ( basecolumn ) {
var state = {
tokenize : tokenBase ,
lastType : "sof" ,
cc : [ ] ,
lexical : new JSLexical (
( basecolumn || 0 ) - indentUnit ,
0 ,
"block" ,
false
) ,
localVars : parserConfig . localVars ,
context :
parserConfig . localVars &&
new Context ( null , null , false ) ,
indented : basecolumn || 0 ,
} ;
if (
parserConfig . globalVars &&
typeof parserConfig . globalVars == "object"
)
state . globalVars = parserConfig . globalVars ;
return state ;
} ,
token : function ( stream , state ) {
if ( stream . sol ( ) ) {
if ( ! state . lexical . hasOwnProperty ( "align" ) )
state . lexical . align = false ;
state . indented = stream . indentation ( ) ;
findFatArrow ( stream , state ) ;
}
if ( state . tokenize != tokenComment && stream . eatSpace ( ) )
return null ;
var style = state . tokenize ( stream , state ) ;
if ( type == "comment" ) return style ;
state . lastType =
type == "operator" && ( content == "++" || content == "--" )
? "incdec"
: type ;
return parseJS ( state , style , type , content , stream ) ;
} ,
indent : function ( state , textAfter ) {
if (
state . tokenize == tokenComment ||
state . tokenize == tokenQuasi
)
return CodeMirror . Pass ;
if ( state . tokenize != tokenBase ) return 0 ;
var firstChar = textAfter && textAfter . charAt ( 0 ) ,
lexical = state . lexical ,
top ;
// Kludge to prevent 'maybelse' from blocking lexical scope pops
if ( ! /^\s*else\b/ . test ( textAfter ) )
for ( var i = state . cc . length - 1 ; i >= 0 ; -- i ) {
var c = state . cc [ i ] ;
if ( c == poplex ) lexical = lexical . prev ;
else if ( c != maybeelse && c != popcontext ) break ;
}
while (
( lexical . type == "stat" || lexical . type == "form" ) &&
( firstChar == "}" ||
( ( top = state . cc [ state . cc . length - 1 ] ) &&
( top == maybeoperatorComma ||
top == maybeoperatorNoComma ) &&
! /^[,\.=+\-*:?[\(]/ . test ( textAfter ) ) )
)
lexical = lexical . prev ;
if (
statementIndent &&
lexical . type == ")" &&
lexical . prev . type == "stat"
)
lexical = lexical . prev ;
var type = lexical . type ,
closing = firstChar == type ;
if ( type == "vardef" )
return (
lexical . indented +
( state . lastType == "operator" || state . lastType == ","
? lexical . info . length + 1
: 0 )
) ;
else if ( type == "form" && firstChar == "{" )
return lexical . indented ;
else if ( type == "form" ) return lexical . indented + indentUnit ;
else if ( type == "stat" )
return (
lexical . indented +
( isContinuedStatement ( state , textAfter )
? statementIndent || indentUnit
: 0 )
) ;
else if (
lexical . info == "switch" &&
! closing &&
parserConfig . doubleIndentSwitch != false
)
return (
lexical . indented +
( /^(?:case|default)\b/ . test ( textAfter )
? indentUnit
: 2 * indentUnit )
) ;
else if ( lexical . align )
return lexical . column + ( closing ? 0 : 1 ) ;
else return lexical . indented + ( closing ? 0 : indentUnit ) ;
} ,
electricInput : /^\s*(?:case .*?:|default:|\{|\})$/ ,
blockCommentStart : jsonMode ? null : "/*" ,
blockCommentEnd : jsonMode ? null : "*/" ,
blockCommentContinue : jsonMode ? null : " * " ,
lineComment : jsonMode ? null : "//" ,
fold : "brace" ,
closeBrackets : "()[]{}''\"\"``" ,
helperType : jsonMode ? "json" : "javascript" ,
jsonldMode : jsonldMode ,
jsonMode : jsonMode ,
expressionAllowed : expressionAllowed ,
skipExpression : function ( state ) {
parseJS (
state ,
"atom" ,
"atom" ,
"true" ,
new CodeMirror . StringStream ( "" , 2 , null )
) ;
} ,
} ;
} ) ;
CodeMirror . registerHelper ( "wordChars" , "javascript" , /[\w$]/ ) ;
CodeMirror . defineMIME ( "text/javascript" , "javascript" ) ;
CodeMirror . defineMIME ( "text/ecmascript" , "javascript" ) ;
CodeMirror . defineMIME ( "application/javascript" , "javascript" ) ;
CodeMirror . defineMIME ( "application/x-javascript" , "javascript" ) ;
CodeMirror . defineMIME ( "application/ecmascript" , "javascript" ) ;
CodeMirror . defineMIME ( "application/json" , {
name : "javascript" ,
json : true ,
} ) ;
CodeMirror . defineMIME ( "application/x-json" , {
name : "javascript" ,
json : true ,
} ) ;
CodeMirror . defineMIME ( "application/manifest+json" , {
name : "javascript" ,
json : true ,
} ) ;
CodeMirror . defineMIME ( "application/ld+json" , {
name : "javascript" ,
jsonld : true ,
} ) ;
CodeMirror . defineMIME ( "text/typescript" , {
name : "javascript" ,
typescript : true ,
} ) ;
CodeMirror . defineMIME ( "application/typescript" , {
name : "javascript" ,
typescript : true ,
} ) ;
} ) ;
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
// Utility function that allows modes to be combined. The mode given
// as the base argument takes care of most of the normal mode
// functionality, but a second (typically simple) mode is used, which
// can override the style of text. Both modes get to parse all of the
// text, but when both assign a non-null style to a piece of code, the
// overlay wins, unless the combine argument was true and not overridden,
// or state.overlay.combineTokens was true, in which case the styles are
// combined.
( function ( mod ) {
mod ( window . CodeMirror ) ;
} ) ( function ( CodeMirror ) {
CodeMirror . customOverlayMode = function ( base , overlay , combine ) {
return {
startState : function ( ) {
return {
base : CodeMirror . startState ( base ) ,
overlay : CodeMirror . startState ( overlay ) ,
basePos : 0 ,
baseCur : null ,
overlayPos : 0 ,
overlayCur : null ,
streamSeen : null ,
} ;
} ,
copyState : function ( state ) {
return {
base : CodeMirror . copyState ( base , state . base ) ,
overlay : CodeMirror . copyState ( overlay , state . overlay ) ,
basePos : state . basePos ,
baseCur : null ,
overlayPos : state . overlayPos ,
overlayCur : null ,
} ;
} ,
token : function ( stream , state ) {
if (
stream != state . streamSeen ||
Math . min ( state . basePos , state . overlayPos ) < stream . start
) {
state . streamSeen = stream ;
state . basePos = state . overlayPos = stream . start ;
}
if ( stream . start == state . basePos ) {
state . baseCur = base . token ( stream , state . base ) ;
state . basePos = stream . pos ;
}
if ( stream . start == state . overlayPos ) {
stream . pos = stream . start ;
state . overlayCur = overlay . token ( stream , state . overlay ) ;
state . overlayPos = stream . pos ;
}
stream . pos = Math . min ( state . basePos , state . overlayPos ) ;
// Edge case for codeblocks in templater mode
if (
state . baseCur &&
state . overlayCur &&
state . baseCur . contains ( "line-HyperMD-codeblock" )
) {
state . overlayCur = state . overlayCur . replace (
"line-templater-inline" ,
""
) ;
state . overlayCur += ` line-background-HyperMD-codeblock-bg ` ;
}
// state.overlay.combineTokens always takes precedence over combine,
// unless set to null
if ( state . overlayCur == null ) return state . baseCur ;
else if (
( state . baseCur != null && state . overlay . combineTokens ) ||
( combine && state . overlay . combineTokens == null )
)
return state . baseCur + " " + state . overlayCur ;
else return state . overlayCur ;
} ,
indent :
base . indent &&
function ( state , textAfter , line ) {
return base . indent ( state . base , textAfter , line ) ;
} ,
electricChars : base . electricChars ,
innerMode : function ( state ) {
return { state : state . base , mode : base } ;
} ,
blankLine : function ( state ) {
var baseToken , overlayToken ;
if ( base . blankLine ) baseToken = base . blankLine ( state . base ) ;
if ( overlay . blankLine )
overlayToken = overlay . blankLine ( state . overlay ) ;
return overlayToken == null
? baseToken
: combine && baseToken != null
? baseToken + " " + overlayToken
: overlayToken ;
} ,
} ;
} ;
} ) ;
//import "editor/mode/show-hint";
const TP _CMD _TOKEN _CLASS = "templater-command" ;
const TP _INLINE _CLASS = "templater-inline" ;
const TP _OPENING _TAG _TOKEN _CLASS = "templater-opening-tag" ;
const TP _CLOSING _TAG _TOKEN _CLASS = "templater-closing-tag" ;
const TP _INTERPOLATION _TAG _TOKEN _CLASS = "templater-interpolation-tag" ;
const TP _RAW _TAG _TOKEN _CLASS = "templater-raw-tag" ;
const TP _EXEC _TAG _TOKEN _CLASS = "templater-execution-tag" ;
class Editor {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
this . cursor _jumper = new CursorJumper ( this . app ) ;
}
setup ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . registerCodeMirrorMode ( ) ;
//await this.registerHinter();
this . plugin . registerEditorSuggest ( new Autocomplete ( this . app , this . plugin ) ) ;
} ) ;
}
jump _to _next _cursor _location ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . cursor _jumper . jump _to _next _cursor _location ( ) ;
} ) ;
}
registerCodeMirrorMode ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// cm-editor-syntax-highlight-obsidian plugin
// https://codemirror.net/doc/manual.html#modeapi
// https://codemirror.net/mode/diff/diff.js
// https://codemirror.net/demo/mustache.html
// https://marijnhaverbeke.nl/blog/codemirror-mode-system.html
if ( ! this . plugin . settings . syntax _highlighting ) {
return ;
}
// TODO: Add mobile support
if ( obsidian _module . Platform . isMobileApp ) {
return ;
}
const js _mode = window . CodeMirror . getMode ( { } , "javascript" ) ;
if ( js _mode . name === "null" ) {
log _error ( new TemplaterError ( "Javascript syntax mode couldn't be found, can't enable syntax highlighting." ) ) ;
return ;
}
// Custom overlay mode used to handle edge cases
// @ts-ignore
const overlay _mode = window . CodeMirror . customOverlayMode ;
if ( overlay _mode == null ) {
log _error ( new TemplaterError ( "Couldn't find customOverlayMode, can't enable syntax highlighting." ) ) ;
return ;
}
window . CodeMirror . defineMode ( "templater" , function ( config ) {
const templaterOverlay = {
startState : function ( ) {
const js _state = window . CodeMirror . startState ( js _mode ) ;
return Object . assign ( Object . assign ( { } , js _state ) , { inCommand : false , tag _class : "" , freeLine : false } ) ;
} ,
copyState : function ( state ) {
const js _state = window . CodeMirror . startState ( js _mode ) ;
const new _state = Object . assign ( Object . assign ( { } , js _state ) , { inCommand : state . inCommand , tag _class : state . tag _class , freeLine : state . freeLine } ) ;
return new _state ;
} ,
blankLine : function ( state ) {
if ( state . inCommand ) {
return ` line-background-templater-command-bg ` ;
}
return null ;
} ,
token : function ( stream , state ) {
if ( stream . sol ( ) && state . inCommand ) {
state . freeLine = true ;
}
if ( state . inCommand ) {
let keywords = "" ;
if ( stream . match ( /[-_]{0,1}%>/ , true ) ) {
state . inCommand = false ;
state . freeLine = false ;
const tag _class = state . tag _class ;
state . tag _class = "" ;
return ` line- ${ TP _INLINE _CLASS } ${ TP _CMD _TOKEN _CLASS } ${ TP _CLOSING _TAG _TOKEN _CLASS } ${ tag _class } ` ;
}
const js _result = js _mode . token ( stream , state ) ;
if ( stream . peek ( ) == null && state . freeLine ) {
keywords += ` line-background-templater-command-bg ` ;
}
if ( ! state . freeLine ) {
keywords += ` line- ${ TP _INLINE _CLASS } ` ;
}
return ` ${ keywords } ${ TP _CMD _TOKEN _CLASS } ${ js _result } ` ;
}
const match = stream . match ( /<%[-_]{0,1}\s*([*~+]{0,1})/ , true ) ;
if ( match != null ) {
switch ( match [ 1 ] ) {
case "*" :
state . tag _class = TP _EXEC _TAG _TOKEN _CLASS ;
break ;
case "~" :
state . tag _class = TP _RAW _TAG _TOKEN _CLASS ;
break ;
default :
state . tag _class =
TP _INTERPOLATION _TAG _TOKEN _CLASS ;
break ;
}
state . inCommand = true ;
return ` line- ${ TP _INLINE _CLASS } ${ TP _CMD _TOKEN _CLASS } ${ TP _OPENING _TAG _TOKEN _CLASS } ${ state . tag _class } ` ;
}
while ( stream . next ( ) != null && ! stream . match ( /<%/ , false ) )
;
return null ;
} ,
} ;
return overlay _mode ( window . CodeMirror . getMode ( config , "hypermd" ) , templaterOverlay ) ;
} ) ;
} ) ;
}
registerHinter ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// TODO
/ *
await delay ( 1000 ) ;
var comp = [
[ "here" , "hither" ] ,
[ "asynchronous" , "nonsynchronous" ] ,
[ "completion" , "achievement" , "conclusion" , "culmination" , "expirations" ] ,
[ "hinting" , "advise" , "broach" , "imply" ] ,
[ "function" , "action" ] ,
[ "provide" , "add" , "bring" , "give" ] ,
[ "synonyms" , "equivalents" ] ,
[ "words" , "token" ] ,
[ "each" , "every" ] ,
] ;
function synonyms ( cm : any , option : any ) {
return new Promise ( function ( accept ) {
setTimeout ( function ( ) {
var cursor = cm . getCursor ( ) , line = cm . getLine ( cursor . line )
var start = cursor . ch , end = cursor . ch
while ( start && /\w/ . test ( line . charAt ( start - 1 ) ) ) -- start
while ( end < line . length && /\w/ . test ( line . charAt ( end ) ) ) ++ end
var word = line . slice ( start , end ) . toLowerCase ( )
for ( var i = 0 ; i < comp . length ; i ++ ) {
if ( comp [ i ] . indexOf ( word ) != - 1 ) {
return accept ( {
list : comp [ i ] ,
from : window . CodeMirror . Pos ( cursor . line , start ) ,
to : window . CodeMirror . Pos ( cursor . line , end )
} ) ;
}
}
return accept ( null ) ;
} , 100 )
} ) ;
}
this . app . workspace . on ( "codemirror" , cm => {
cm . setOption ( "extraKeys" , { "Ctrl-Space" : "autocomplete" } ) ;
cm . setOption ( "hintOptions" , { hint : synonyms } ) ;
} ) ;
this . app . workspace . iterateCodeMirrors ( cm => {
console . log ( "CM:" , cm ) ;
cm . setOption ( "extraKeys" , { "Space" : "autocomplete" } ) ;
cm . setOption ( "hintOptions" , { hint : synonyms } ) ;
} ) ;
* /
} ) ;
}
}
function setPrototypeOf ( obj , proto ) {
// eslint-disable-line @typescript-eslint/no-explicit-any
if ( Object . setPrototypeOf ) {
Object . setPrototypeOf ( obj , proto ) ;
}
else {
obj . _ _proto _ _ = proto ;
}
}
// This is pretty much the only way to get nice, extended Errors
// without using ES6
/ * *
* This returns a new Error with a custom prototype . Note that it ' s _not _ a constructor
*
* @ param message Error message
*
* * * Example * *
*
* ` ` ` js
* throw EtaErr ( "template not found" )
* ` ` `
* /
function EtaErr ( message ) {
var err = new Error ( message ) ;
setPrototypeOf ( err , EtaErr . prototype ) ;
return err ;
}
EtaErr . prototype = Object . create ( Error . prototype , {
name : { value : 'Eta Error' , enumerable : false }
} ) ;
/ * *
* Throws an EtaErr with a nicely formatted error and message showing where in the template the error occurred .
* /
function ParseErr ( message , str , indx ) {
var whitespace = str . slice ( 0 , indx ) . split ( /\n/ ) ;
var lineNo = whitespace . length ;
var colNo = whitespace [ lineNo - 1 ] . length + 1 ;
message +=
' at line ' +
lineNo +
' col ' +
colNo +
':\n\n' +
' ' +
str . split ( /\n/ ) [ lineNo - 1 ] +
'\n' +
' ' +
Array ( colNo ) . join ( ' ' ) +
'^' ;
throw EtaErr ( message ) ;
}
/ * *
* @ returns The global Promise function
* /
var promiseImpl = new Function ( 'return this' ) ( ) . Promise ;
/ * *
* @ returns A new AsyncFunction constuctor
* /
function getAsyncFunctionConstructor ( ) {
try {
return new Function ( 'return (async function(){}).constructor' ) ( ) ;
}
catch ( e ) {
if ( e instanceof SyntaxError ) {
throw EtaErr ( "This environment doesn't support async/await" ) ;
}
else {
throw e ;
}
}
}
/ * *
* str . trimLeft polyfill
*
* @ param str - Input string
* @ returns The string with left whitespace removed
*
* /
function trimLeft ( str ) {
// eslint-disable-next-line no-extra-boolean-cast
if ( ! ! String . prototype . trimLeft ) {
return str . trimLeft ( ) ;
}
else {
return str . replace ( /^\s+/ , '' ) ;
}
}
/ * *
* str . trimRight polyfill
*
* @ param str - Input string
* @ returns The string with right whitespace removed
*
* /
function trimRight ( str ) {
// eslint-disable-next-line no-extra-boolean-cast
if ( ! ! String . prototype . trimRight ) {
return str . trimRight ( ) ;
}
else {
return str . replace ( /\s+$/ , '' ) ; // TODO: do we really need to replace BOM's?
}
}
// TODO: allow '-' to trim up until newline. Use [^\S\n\r] instead of \s
/* END TYPES */
function hasOwnProp ( obj , prop ) {
return Object . prototype . hasOwnProperty . call ( obj , prop ) ;
}
function copyProps ( toObj , fromObj ) {
for ( var key in fromObj ) {
if ( hasOwnProp ( fromObj , key ) ) {
toObj [ key ] = fromObj [ key ] ;
}
}
return toObj ;
}
/ * *
* Takes a string within a template and trims it , based on the preceding tag ' s whitespace control and ` config.autoTrim `
* /
function trimWS ( str , config , wsLeft , wsRight ) {
var leftTrim ;
var rightTrim ;
if ( Array . isArray ( config . autoTrim ) ) {
// kinda confusing
// but _}} will trim the left side of the following string
leftTrim = config . autoTrim [ 1 ] ;
rightTrim = config . autoTrim [ 0 ] ;
}
else {
leftTrim = rightTrim = config . autoTrim ;
}
if ( wsLeft || wsLeft === false ) {
leftTrim = wsLeft ;
}
if ( wsRight || wsRight === false ) {
rightTrim = wsRight ;
}
if ( ! rightTrim && ! leftTrim ) {
return str ;
}
if ( leftTrim === 'slurp' && rightTrim === 'slurp' ) {
return str . trim ( ) ;
}
if ( leftTrim === '_' || leftTrim === 'slurp' ) {
// console.log('trimming left' + leftTrim)
// full slurp
str = trimLeft ( str ) ;
}
else if ( leftTrim === '-' || leftTrim === 'nl' ) {
// nl trim
str = str . replace ( /^(?:\r\n|\n|\r)/ , '' ) ;
}
if ( rightTrim === '_' || rightTrim === 'slurp' ) {
// full slurp
str = trimRight ( str ) ;
}
else if ( rightTrim === '-' || rightTrim === 'nl' ) {
// nl trim
str = str . replace ( /(?:\r\n|\n|\r)$/ , '' ) ; // TODO: make sure this gets \r\n
}
return str ;
}
/ * *
* A map of special HTML characters to their XML - escaped equivalents
* /
var escMap = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
'"' : '"' ,
"'" : '''
} ;
function replaceChar ( s ) {
return escMap [ s ] ;
}
/ * *
* XML - escapes an input value after converting it to a string
*
* @ param str - Input value ( usually a string )
* @ returns XML - escaped string
* /
function XMLEscape ( str ) {
// eslint-disable-line @typescript-eslint/no-explicit-any
// To deal with XSS. Based on Escape implementations of Mustache.JS and Marko, then customized.
var newStr = String ( str ) ;
if ( /[&<>"']/ . test ( newStr ) ) {
return newStr . replace ( /[&<>"']/g , replaceChar ) ;
}
else {
return newStr ;
}
}
/* END TYPES */
var templateLitReg = /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g ;
var singleQuoteReg = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g ;
var doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g ;
/** Escape special regular expression characters inside a string */
function escapeRegExp ( string ) {
// From MDN
return string . replace ( /[.*+\-?^${}()|[\]\\]/g , '\\$&' ) ; // $& means the whole matched string
}
function parse ( str , config ) {
var buffer = [ ] ;
var trimLeftOfNextStr = false ;
var lastIndex = 0 ;
var parseOptions = config . parse ;
if ( config . plugins ) {
for ( var i = 0 ; i < config . plugins . length ; i ++ ) {
var plugin = config . plugins [ i ] ;
if ( plugin . processTemplate ) {
str = plugin . processTemplate ( str , config ) ;
}
}
}
/* Adding for EJS compatibility */
if ( config . rmWhitespace ) {
// Code taken directly from EJS
// Have to use two separate replaces here as `^` and `$` operators don't
// work well with `\r` and empty lines don't work well with the `m` flag.
// Essentially, this replaces the whitespace at the beginning and end of
// each line and removes multiple newlines.
str = str . replace ( /[\r\n]+/g , '\n' ) . replace ( /^\s+|\s+$/gm , '' ) ;
}
/* End rmWhitespace option */
templateLitReg . lastIndex = 0 ;
singleQuoteReg . lastIndex = 0 ;
doubleQuoteReg . lastIndex = 0 ;
function pushString ( strng , shouldTrimRightOfString ) {
if ( strng ) {
// if string is truthy it must be of type 'string'
strng = trimWS ( strng , config , trimLeftOfNextStr , // this will only be false on the first str, the next ones will be null or undefined
shouldTrimRightOfString ) ;
if ( strng ) {
// replace \ with \\, ' with \'
// we're going to convert all CRLF to LF so it doesn't take more than one replace
strng = strng . replace ( /\\|'/g , '\\$&' ) . replace ( /\r\n|\n|\r/g , '\\n' ) ;
buffer . push ( strng ) ;
}
}
}
var prefixes = [ parseOptions . exec , parseOptions . interpolate , parseOptions . raw ] . reduce ( function ( accumulator , prefix ) {
if ( accumulator && prefix ) {
return accumulator + '|' + escapeRegExp ( prefix ) ;
}
else if ( prefix ) {
// accumulator is falsy
return escapeRegExp ( prefix ) ;
}
else {
// prefix and accumulator are both falsy
return accumulator ;
}
} , '' ) ;
var parseOpenReg = new RegExp ( '([^]*?)' + escapeRegExp ( config . tags [ 0 ] ) + '(-|_)?\\s*(' + prefixes + ')?\\s*(?![\\s+\\-_' + prefixes + '])' , 'g' ) ;
var parseCloseReg = new RegExp ( '\'|"|`|\\/\\*|(\\s*(-|_)?' + escapeRegExp ( config . tags [ 1 ] ) + ')' , 'g' ) ;
// TODO: benchmark having the \s* on either side vs using str.trim()
var m ;
while ( ( m = parseOpenReg . exec ( str ) ) ) {
lastIndex = m [ 0 ] . length + m . index ;
var precedingString = m [ 1 ] ;
var wsLeft = m [ 2 ] ;
var prefix = m [ 3 ] || '' ; // by default either ~, =, or empty
pushString ( precedingString , wsLeft ) ;
parseCloseReg . lastIndex = lastIndex ;
var closeTag = void 0 ;
var currentObj = false ;
while ( ( closeTag = parseCloseReg . exec ( str ) ) ) {
if ( closeTag [ 1 ] ) {
var content = str . slice ( lastIndex , closeTag . index ) ;
parseOpenReg . lastIndex = lastIndex = parseCloseReg . lastIndex ;
trimLeftOfNextStr = closeTag [ 2 ] ;
var currentType = prefix === parseOptions . exec
? 'e'
: prefix === parseOptions . raw
? 'r'
: prefix === parseOptions . interpolate
? 'i'
: '' ;
currentObj = { t : currentType , val : content } ;
break ;
}
else {
var char = closeTag [ 0 ] ;
if ( char === '/*' ) {
var commentCloseInd = str . indexOf ( '*/' , parseCloseReg . lastIndex ) ;
if ( commentCloseInd === - 1 ) {
ParseErr ( 'unclosed comment' , str , closeTag . index ) ;
}
parseCloseReg . lastIndex = commentCloseInd ;
}
else if ( char === "'" ) {
singleQuoteReg . lastIndex = closeTag . index ;
var singleQuoteMatch = singleQuoteReg . exec ( str ) ;
if ( singleQuoteMatch ) {
parseCloseReg . lastIndex = singleQuoteReg . lastIndex ;
}
else {
ParseErr ( 'unclosed string' , str , closeTag . index ) ;
}
}
else if ( char === '"' ) {
doubleQuoteReg . lastIndex = closeTag . index ;
var doubleQuoteMatch = doubleQuoteReg . exec ( str ) ;
if ( doubleQuoteMatch ) {
parseCloseReg . lastIndex = doubleQuoteReg . lastIndex ;
}
else {
ParseErr ( 'unclosed string' , str , closeTag . index ) ;
}
}
else if ( char === '`' ) {
templateLitReg . lastIndex = closeTag . index ;
var templateLitMatch = templateLitReg . exec ( str ) ;
if ( templateLitMatch ) {
parseCloseReg . lastIndex = templateLitReg . lastIndex ;
}
else {
ParseErr ( 'unclosed string' , str , closeTag . index ) ;
}
}
}
}
if ( currentObj ) {
buffer . push ( currentObj ) ;
}
else {
ParseErr ( 'unclosed tag' , str , m . index + precedingString . length ) ;
}
}
pushString ( str . slice ( lastIndex , str . length ) , false ) ;
if ( config . plugins ) {
for ( var i = 0 ; i < config . plugins . length ; i ++ ) {
var plugin = config . plugins [ i ] ;
if ( plugin . processAST ) {
buffer = plugin . processAST ( buffer , config ) ;
}
}
}
return buffer ;
}
/* END TYPES */
/ * *
* Compiles a template string to a function string . Most often users just use ` compile() ` , which calls ` compileToString ` and creates a new function using the result
*
* * * Example * *
*
* ` ` ` js
* compileToString ( "Hi <%= it.user %>" , eta . config )
* // "var tR='',include=E.include.bind(E),includeFile=E.includeFile.bind(E);tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR"
* ` ` `
* /
function compileToString ( str , config ) {
var buffer = parse ( str , config ) ;
var res = "var tR='',__l,__lP" +
( config . include ? ',include=E.include.bind(E)' : '' ) +
( config . includeFile ? ',includeFile=E.includeFile.bind(E)' : '' ) +
'\nfunction layout(p,d){__l=p;__lP=d}\n' +
( config . globalAwait ? 'const _prs = [];\n' : '' ) +
( config . useWith ? 'with(' + config . varName + '||{}){' : '' ) +
compileScope ( buffer , config ) +
( config . includeFile
? 'if(__l)tR=' +
( config . async ? 'await ' : '' ) +
( "includeFile(__l,Object.assign(" + config . varName + ",{body:tR},__lP))\n" )
: config . include
? 'if(__l)tR=' +
( config . async ? 'await ' : '' ) +
( "include(__l,Object.assign(" + config . varName + ",{body:tR},__lP))\n" )
: '' ) +
'if(cb){cb(null,tR)} return tR' +
( config . useWith ? '}' : '' ) ;
if ( config . plugins ) {
for ( var i = 0 ; i < config . plugins . length ; i ++ ) {
var plugin = config . plugins [ i ] ;
if ( plugin . processFnString ) {
res = plugin . processFnString ( res , config ) ;
}
}
}
return res ;
}
/ * *
* Loops through the AST generated by ` parse ` and transform each item into JS calls
*
* * * Example * *
*
* ` ` ` js
* // AST version of 'Hi <%= it.user %>'
* let templateAST = [ 'Hi ' , { val : 'it.user' , t : 'i' } ]
* compileScope ( templateAST , eta . config )
* // "tR+='Hi ';tR+=E.e(it.user);"
* ` ` `
* /
function compileScope ( buff , config ) {
var i ;
var buffLength = buff . length ;
var returnStr = '' ;
var REPLACEMENT _STR = "rJ2KqXzxQg" ;
for ( i = 0 ; i < buffLength ; i ++ ) {
var currentBlock = buff [ i ] ;
if ( typeof currentBlock === 'string' ) {
var str = currentBlock ;
// we know string exists
returnStr += "tR+='" + str + "'\n" ;
}
else {
var type = currentBlock . t ; // ~, s, !, ?, r
var content = currentBlock . val || '' ;
if ( type === 'r' ) {
// raw
if ( config . globalAwait ) {
returnStr += "_prs.push(" + content + ");\n" ;
returnStr += "tR+='" + REPLACEMENT _STR + "'\n" ;
}
else {
if ( config . filter ) {
content = 'E.filter(' + content + ')' ;
}
returnStr += 'tR+=' + content + '\n' ;
}
}
else if ( type === 'i' ) {
// interpolate
if ( config . globalAwait ) {
returnStr += "_prs.push(" + content + ");\n" ;
returnStr += "tR+='" + REPLACEMENT _STR + "'\n" ;
}
else {
if ( config . filter ) {
content = 'E.filter(' + content + ')' ;
}
returnStr += 'tR+=' + content + '\n' ;
if ( config . autoEscape ) {
content = 'E.e(' + content + ')' ;
}
returnStr += 'tR+=' + content + '\n' ;
}
}
else if ( type === 'e' ) {
// execute
returnStr += content + '\n' ; // you need a \n in case you have <% } %>
}
}
}
if ( config . globalAwait ) {
returnStr += "const _rst = await Promise.all(_prs);\ntR = tR.replace(/" + REPLACEMENT _STR + "/g, () => _rst.shift());\n" ;
}
return returnStr ;
}
/ * *
* Handles storage and accessing of values
*
* In this case , we use it to store compiled template functions
* Indexed by their ` name ` or ` filename `
* /
var Cacher = /** @class */ ( function ( ) {
function Cacher ( cache ) {
this . cache = cache ;
}
Cacher . prototype . define = function ( key , val ) {
this . cache [ key ] = val ;
} ;
Cacher . prototype . get = function ( key ) {
// string | array.
// TODO: allow array of keys to look down
// TODO: create plugin to allow referencing helpers, filters with dot notation
return this . cache [ key ] ;
} ;
Cacher . prototype . remove = function ( key ) {
delete this . cache [ key ] ;
} ;
Cacher . prototype . reset = function ( ) {
this . cache = { } ;
} ;
Cacher . prototype . load = function ( cacheObj ) {
copyProps ( this . cache , cacheObj ) ;
} ;
return Cacher ;
} ( ) ) ;
/* END TYPES */
/ * *
* Eta ' s template storage
*
* Stores partials and cached templates
* /
var templates = new Cacher ( { } ) ;
/* END TYPES */
/ * *
* Include a template based on its name ( or filepath , if it ' s already been cached ) .
*
* Called like ` include(templateNameOrPath, data) `
* /
function includeHelper ( templateNameOrPath , data ) {
var template = this . templates . get ( templateNameOrPath ) ;
if ( ! template ) {
throw EtaErr ( 'Could not fetch template "' + templateNameOrPath + '"' ) ;
}
return template ( data , this ) ;
}
/** Eta's base (global) configuration */
var config = {
async : false ,
autoEscape : true ,
autoTrim : [ false , 'nl' ] ,
cache : false ,
e : XMLEscape ,
include : includeHelper ,
parse : {
exec : '' ,
interpolate : '=' ,
raw : '~'
} ,
plugins : [ ] ,
rmWhitespace : false ,
tags : [ '<%' , '%>' ] ,
templates : templates ,
useWith : false ,
varName : 'it'
} ;
/ * *
* Takes one or two partial ( not necessarily complete ) configuration objects , merges them 1 layer deep into eta . config , and returns the result
*
* @ param override Partial configuration object
* @ param baseConfig Partial configuration object to merge before ` override `
*
* * * Example * *
*
* ` ` ` js
* let customConfig = getConfig ( { tags : [ '!#' , '#!' ] } )
* ` ` `
* /
function getConfig ( override , baseConfig ) {
// TODO: run more tests on this
var res = { } ; // Linked
copyProps ( res , config ) ; // Creates deep clone of eta.config, 1 layer deep
if ( baseConfig ) {
copyProps ( res , baseConfig ) ;
}
if ( override ) {
copyProps ( res , override ) ;
}
return res ;
}
/* END TYPES */
/ * *
* Takes a template string and returns a template function that can be called with ( data , config , [ cb ] )
*
* @ param str - The template string
* @ param config - A custom configuration object ( optional )
*
* * * Example * *
*
* ` ` ` js
* let compiledFn = eta . compile ( "Hi <%= it.user %>" )
* // function anonymous()
* let compiledFnStr = compiledFn . toString ( )
* // "function anonymous(it,E,cb\n) {\nvar tR='',include=E.include.bind(E),includeFile=E.includeFile.bind(E);tR+='Hi ';tR+=E.e(it.user);if(cb){cb(null,tR)} return tR\n}"
* ` ` `
* /
function compile ( str , config ) {
var options = getConfig ( config || { } ) ;
/* ASYNC HANDLING */
// The below code is modified from mde/ejs. All credit should go to them.
var ctor = options . async ? getAsyncFunctionConstructor ( ) : Function ;
/* END ASYNC HANDLING */
try {
return new ctor ( options . varName , 'E' , // EtaConfig
'cb' , // optional callback
compileToString ( str , options ) ) ; // eslint-disable-line no-new-func
}
catch ( e ) {
if ( e instanceof SyntaxError ) {
throw EtaErr ( 'Bad template syntax\n\n' +
e . message +
'\n' +
Array ( e . message . length + 1 ) . join ( '=' ) +
'\n' +
compileToString ( str , options ) +
'\n' // This will put an extra newline before the callstack for extra readability
) ;
}
else {
throw e ;
}
}
}
var _BOM = /^\uFEFF/ ;
/* END TYPES */
/ * *
* Get the path to the included file from the parent file path and the
* specified path .
*
* If ` name ` does not have an extension , it will default to ` .eta `
*
* @ param name specified path
* @ param parentfile parent file path
* @ param isDirectory whether parentfile is a directory
* @ return absolute path to template
* /
function getWholeFilePath ( name , parentfile , isDirectory ) {
var includePath = path . resolve ( isDirectory ? parentfile : path . dirname ( parentfile ) , // returns directory the parent file is in
name // file
) + ( path . extname ( name ) ? '' : '.eta' ) ;
return includePath ;
}
/ * *
* Get the absolute path to an included template
*
* If this is called with an absolute path ( for example , starting with '/' or 'C:\' )
* then Eta will attempt to resolve the absolute path within options . views . If it cannot ,
* Eta will fallback to options . root or '/'
*
* If this is called with a relative path , Eta will :
* - Look relative to the current template ( if the current template has the ` filename ` property )
* - Look inside each directory in options . views
*
* Note : if Eta is unable to find a template using path and options , it will throw an error .
*
* @ param path specified path
* @ param options compilation options
* @ return absolute path to template
* /
function getPath ( path , options ) {
var includePath = false ;
var views = options . views ;
var searchedPaths = [ ] ;
// If these four values are the same,
// getPath() will return the same result every time.
// We can cache the result to avoid expensive
// file operations.
var pathOptions = JSON . stringify ( {
filename : options . filename ,
path : path ,
root : options . root ,
views : options . views
} ) ;
if ( options . cache && options . filepathCache && options . filepathCache [ pathOptions ] ) {
// Use the cached filepath
return options . filepathCache [ pathOptions ] ;
}
/** Add a filepath to the list of paths we've checked for a template */
function addPathToSearched ( pathSearched ) {
if ( ! searchedPaths . includes ( pathSearched ) ) {
searchedPaths . push ( pathSearched ) ;
}
}
/ * *
* Take a filepath ( like 'partials/mypartial.eta' ) . Attempt to find the template file inside ` views ` ;
* return the resulting template file path , or ` false ` to indicate that the template was not found .
*
* @ param views the filepath that holds templates , or an array of filepaths that hold templates
* @ param path the path to the template
* /
function searchViews ( views , path ) {
var filePath ;
// If views is an array, then loop through each directory
// And attempt to find the template
if ( Array . isArray ( views ) &&
views . some ( function ( v ) {
filePath = getWholeFilePath ( path , v , true ) ;
addPathToSearched ( filePath ) ;
return fs . existsSync ( filePath ) ;
} ) ) {
// If the above returned true, we know that the filePath was just set to a path
// That exists (Array.some() returns as soon as it finds a valid element)
return filePath ;
}
else if ( typeof views === 'string' ) {
// Search for the file if views is a single directory
filePath = getWholeFilePath ( path , views , true ) ;
addPathToSearched ( filePath ) ;
if ( fs . existsSync ( filePath ) ) {
return filePath ;
}
}
// Unable to find a file
return false ;
}
// Path starts with '/', 'C:\', etc.
var match = /^[A-Za-z]+:\\|^\// . exec ( path ) ;
// Absolute path, like /partials/partial.eta
if ( match && match . length ) {
// We have to trim the beginning '/' off the path, or else
// path.resolve(dir, path) will always resolve to just path
var formattedPath = path . replace ( /^\/*/ , '' ) ;
// First, try to resolve the path within options.views
includePath = searchViews ( views , formattedPath ) ;
if ( ! includePath ) {
// If that fails, searchViews will return false. Try to find the path
// inside options.root (by default '/', the base of the filesystem)
var pathFromRoot = getWholeFilePath ( formattedPath , options . root || '/' , true ) ;
addPathToSearched ( pathFromRoot ) ;
includePath = pathFromRoot ;
}
}
else {
// Relative paths
// Look relative to a passed filename first
if ( options . filename ) {
var filePath = getWholeFilePath ( path , options . filename ) ;
addPathToSearched ( filePath ) ;
if ( fs . existsSync ( filePath ) ) {
includePath = filePath ;
}
}
// Then look for the template in options.views
if ( ! includePath ) {
includePath = searchViews ( views , path ) ;
}
if ( ! includePath ) {
throw EtaErr ( 'Could not find the template "' + path + '". Paths tried: ' + searchedPaths ) ;
}
}
// If caching and filepathCache are enabled,
// cache the input & output of this function.
if ( options . cache && options . filepathCache ) {
options . filepathCache [ pathOptions ] = includePath ;
}
return includePath ;
}
/ * *
* Reads a file synchronously
* /
function readFile ( filePath ) {
try {
return fs . readFileSync ( filePath ) . toString ( ) . replace ( _BOM , '' ) ; // TODO: is replacing BOM's necessary?
}
catch ( _a ) {
throw EtaErr ( "Failed to read template at '" + filePath + "'" ) ;
}
}
// express is set like: app.engine('html', require('eta').renderFile)
/* END TYPES */
/ * *
* Reads a template , compiles it into a function , caches it if caching isn ' t disabled , returns the function
*
* @ param filePath Absolute path to template file
* @ param options Eta configuration overrides
* @ param noCache Optionally , make Eta not cache the template
* /
function loadFile ( filePath , options , noCache ) {
var config = getConfig ( options ) ;
var template = readFile ( filePath ) ;
try {
var compiledTemplate = compile ( template , config ) ;
if ( ! noCache ) {
config . templates . define ( config . filename , compiledTemplate ) ;
}
return compiledTemplate ;
}
catch ( e ) {
throw EtaErr ( 'Loading file: ' + filePath + ' failed:\n\n' + e . message ) ;
}
}
/ * *
* Get the template from a string or a file , either compiled on - the - fly or
* read from cache ( if enabled ) , and cache the template if needed .
*
* If ` options.cache ` is true , this function reads the file from
* ` options.filename ` so it must be set prior to calling this function .
*
* @ param options compilation options
* @ return Eta template function
* /
function handleCache ( options ) {
var filename = options . filename ;
if ( options . cache ) {
var func = options . templates . get ( filename ) ;
if ( func ) {
return func ;
}
return loadFile ( filename , options ) ;
}
// Caching is disabled, so pass noCache = true
return loadFile ( filename , options , true ) ;
}
/ * *
* Get the template function .
*
* If ` options.cache ` is ` true ` , then the template is cached .
*
* This returns a template function and the config object with which that template function should be called .
*
* @ remarks
*
* It ' s important that this returns a config object with ` filename ` set .
* Otherwise , the included file would not be able to use relative paths
*
* @ param path path for the specified file ( if relative , specify ` views ` on ` options ` )
* @ param options compilation options
* @ return [ Eta template function , new config object ]
* /
function includeFile ( path , options ) {
// the below creates a new options object, using the parent filepath of the old options object and the path
var newFileOptions = getConfig ( { filename : getPath ( path , options ) } , options ) ;
// TODO: make sure properties are currectly copied over
return [ handleCache ( newFileOptions ) , newFileOptions ] ;
}
/* END TYPES */
/ * *
* Called with ` includeFile(path, data) `
* /
function includeFileHelper ( path , data ) {
var templateAndConfig = includeFile ( path , this ) ;
return templateAndConfig [ 0 ] ( data , templateAndConfig [ 1 ] ) ;
}
/* END TYPES */
function handleCache$1 ( template , options ) {
if ( options . cache && options . name && options . templates . get ( options . name ) ) {
return options . templates . get ( options . name ) ;
}
var templateFunc = typeof template === 'function' ? template : compile ( template , options ) ;
// Note that we don't have to check if it already exists in the cache;
// it would have returned earlier if it had
if ( options . cache && options . name ) {
options . templates . define ( options . name , templateFunc ) ;
}
return templateFunc ;
}
/ * *
* Render a template
*
* If ` template ` is a string , Eta will compile it to a function and then call it with the provided data .
* If ` template ` is a template function , Eta will call it with the provided data .
*
* If ` config.async ` is ` false ` , Eta will return the rendered template .
*
* If ` config.async ` is ` true ` and there ' s a callback function , Eta will call the callback with ` (err, renderedTemplate) ` .
* If ` config.async ` is ` true ` and there ' s not a callback function , Eta will return a Promise that resolves to the rendered template .
*
* If ` config.cache ` is ` true ` and ` config ` has a ` name ` or ` filename ` property , Eta will cache the template on the first render and use the cached template for all subsequent renders .
*
* @ param template Template string or template function
* @ param data Data to render the template with
* @ param config Optional config options
* @ param cb Callback function
* /
function render ( template , data , config , cb ) {
var options = getConfig ( config || { } ) ;
if ( options . async ) {
if ( cb ) {
// If user passes callback
try {
// Note: if there is an error while rendering the template,
// It will bubble up and be caught here
var templateFn = handleCache$1 ( template , options ) ;
templateFn ( data , options , cb ) ;
}
catch ( err ) {
return cb ( err ) ;
}
}
else {
// No callback, try returning a promise
if ( typeof promiseImpl === 'function' ) {
return new promiseImpl ( function ( resolve , reject ) {
try {
resolve ( handleCache$1 ( template , options ) ( data , options ) ) ;
}
catch ( err ) {
reject ( err ) ;
}
} ) ;
}
else {
throw EtaErr ( "Please provide a callback function, this env doesn't support Promises" ) ;
}
}
}
else {
return handleCache$1 ( template , options ) ( data , options ) ;
}
}
/ * *
* Render a template asynchronously
*
* If ` template ` is a string , Eta will compile it to a function and call it with the provided data .
* If ` template ` is a function , Eta will call it with the provided data .
*
* If there is a callback function , Eta will call it with ` (err, renderedTemplate) ` .
* If there is not a callback function , Eta will return a Promise that resolves to the rendered template
*
* @ param template Template string or template function
* @ param data Data to render the template with
* @ param config Optional config options
* @ param cb Callback function
* /
function renderAsync ( template , data , config , cb ) {
// Using Object.assign to lower bundle size, using spread operator makes it larger because of typescript injected polyfills
return render ( template , data , Object . assign ( { } , config , { async : true } ) , cb ) ;
}
// @denoify-ignore
config . includeFile = includeFileHelper ;
config . filepathCache = { } ;
class Parser {
parse _commands ( content , object ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
content = ( yield renderAsync ( content , object , {
varName : "tp" ,
parse : {
exec : "*" ,
interpolate : "~" ,
raw : "" ,
} ,
autoTrim : false ,
globalAwait : true ,
} ) ) ;
return content ;
} ) ;
}
}
var RunMode ;
( function ( RunMode ) {
RunMode [ RunMode [ "CreateNewFromTemplate" ] = 0 ] = "CreateNewFromTemplate" ;
RunMode [ RunMode [ "AppendActiveFile" ] = 1 ] = "AppendActiveFile" ;
RunMode [ RunMode [ "OverwriteFile" ] = 2 ] = "OverwriteFile" ;
RunMode [ RunMode [ "OverwriteActiveFile" ] = 3 ] = "OverwriteActiveFile" ;
RunMode [ RunMode [ "DynamicProcessor" ] = 4 ] = "DynamicProcessor" ;
RunMode [ RunMode [ "StartupTemplate" ] = 5 ] = "StartupTemplate" ;
} ) ( RunMode || ( RunMode = { } ) ) ;
class Templater {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
this . functions _generator = new FunctionsGenerator ( this . app , this . plugin ) ;
this . editor = new Editor ( this . app , this . plugin ) ;
this . parser = new Parser ( ) ;
this . documentation = new Documentation ( this . app ) ;
}
setup ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . editor . setup ( ) ;
yield this . functions _generator . init ( ) ;
this . plugin . registerMarkdownPostProcessor ( ( el , ctx ) => this . process _dynamic _templates ( el , ctx ) ) ;
} ) ;
}
create _running _config ( template _file , target _file , run _mode ) {
const active _file = this . app . workspace . getActiveFile ( ) ;
return {
template _file : template _file ,
target _file : target _file ,
run _mode : run _mode ,
active _file : active _file ,
} ;
}
read _and _parse _template ( config ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const template _content = yield this . app . vault . read ( config . template _file ) ;
return this . parse _template ( config , template _content ) ;
} ) ;
}
parse _template ( config , template _content ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const functions _object = yield this . functions _generator . generate _object ( config , FunctionsMode . USER _INTERNAL ) ;
this . current _functions _object = functions _object ;
const content = yield this . parser . parse _commands ( template _content , functions _object ) ;
return content ;
} ) ;
}
jump _to _next _cursor _location ( file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( this . plugin . settings . auto _jump _to _cursor &&
this . app . workspace . getActiveFile ( ) === file ) {
yield this . editor . jump _to _next _cursor _location ( ) ;
}
} ) ;
}
create _new _note _from _template ( template , folder , filename , open _new _note = true ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// TODO: Maybe there is an obsidian API function for that
if ( ! folder ) {
// TODO: Fix that
// @ts-ignore
const new _file _location = this . app . vault . getConfig ( "newFileLocation" ) ;
switch ( new _file _location ) {
case "current" : {
const active _file = this . app . workspace . getActiveFile ( ) ;
if ( active _file ) {
folder = active _file . parent ;
}
break ;
}
case "folder" :
folder = this . app . fileManager . getNewFileParent ( "" ) ;
break ;
case "root" :
folder = this . app . vault . getRoot ( ) ;
break ;
}
}
// TODO: Change that, not stable atm
// @ts-ignore
const created _note = yield this . app . fileManager . createNewMarkdownFile ( folder , filename !== null && filename !== void 0 ? filename : "Untitled" ) ;
let running _config ;
let output _content ;
if ( template instanceof obsidian _module . TFile ) {
running _config = this . create _running _config ( template , created _note , RunMode . CreateNewFromTemplate ) ;
output _content = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) { return this . read _and _parse _template ( running _config ) ; } ) , "Template parsing error, aborting." ) ;
}
else {
running _config = this . create _running _config ( undefined , created _note , RunMode . CreateNewFromTemplate ) ;
output _content = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) { return this . parse _template ( running _config , template ) ; } ) , "Template parsing error, aborting." ) ;
}
if ( output _content == null ) {
yield this . app . vault . delete ( created _note ) ;
return ;
}
yield this . app . vault . modify ( created _note , output _content ) ;
if ( open _new _note ) {
const active _leaf = this . app . workspace . activeLeaf ;
if ( ! active _leaf ) {
log _error ( new TemplaterError ( "No active leaf" ) ) ;
return ;
}
yield active _leaf . openFile ( created _note , {
state : { mode : "source" } ,
eState : { rename : "all" } ,
} ) ;
yield this . jump _to _next _cursor _location ( created _note ) ;
}
return created _note ;
} ) ;
}
append _template _to _active _file ( template _file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( active _view === null ) {
log _error ( new TemplaterError ( "No active view, can't append templates." ) ) ;
return ;
}
const running _config = this . create _running _config ( template _file , active _view . file , RunMode . AppendActiveFile ) ;
const output _content = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) { return this . read _and _parse _template ( running _config ) ; } ) , "Template parsing error, aborting." ) ;
// errorWrapper failed
if ( output _content == null ) {
return ;
}
const editor = active _view . editor ;
const doc = editor . getDoc ( ) ;
doc . replaceSelection ( output _content ) ;
yield this . jump _to _next _cursor _location ( active _view . file ) ;
} ) ;
}
write _template _to _file ( template _file , file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const running _config = this . create _running _config ( template _file , file , RunMode . OverwriteFile ) ;
const output _content = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) { return this . read _and _parse _template ( running _config ) ; } ) , "Template parsing error, aborting." ) ;
// errorWrapper failed
if ( output _content == null ) {
return ;
}
yield this . app . vault . modify ( file , output _content ) ;
yield this . jump _to _next _cursor _location ( file ) ;
} ) ;
}
overwrite _active _file _commands ( ) {
const active _view = this . app . workspace . getActiveViewOfType ( obsidian _module . MarkdownView ) ;
if ( active _view === null ) {
log _error ( new TemplaterError ( "Active view is null, can't overwrite content" ) ) ;
return ;
}
this . overwrite _file _commands ( active _view . file , true ) ;
}
overwrite _file _commands ( file , active _file = false ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const running _config = this . create _running _config ( file , file , active _file ? RunMode . OverwriteActiveFile : RunMode . OverwriteFile ) ;
const output _content = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) { return this . read _and _parse _template ( running _config ) ; } ) , "Template parsing error, aborting." ) ;
// errorWrapper failed
if ( output _content == null ) {
return ;
}
yield this . app . vault . modify ( file , output _content ) ;
yield this . jump _to _next _cursor _location ( file ) ;
} ) ;
}
process _dynamic _templates ( el , ctx ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
const dynamic _command _regex = generate _dynamic _command _regex ( ) ;
const walker = document . createNodeIterator ( el , NodeFilter . SHOW _TEXT ) ;
let node ;
let pass = false ;
let functions _object ;
while ( ( node = walker . nextNode ( ) ) ) {
let content = node . nodeValue ;
let match ;
if ( ( match = dynamic _command _regex . exec ( content ) ) != null ) {
const file = this . app . metadataCache . getFirstLinkpathDest ( "" , ctx . sourcePath ) ;
if ( ! file || ! ( file instanceof obsidian _module . TFile ) ) {
return ;
}
if ( ! pass ) {
pass = true ;
const config = this . create _running _config ( file , file , RunMode . DynamicProcessor ) ;
functions _object =
yield this . functions _generator . generate _object ( config , FunctionsMode . USER _INTERNAL ) ;
this . current _functions _object = functions _object ;
}
while ( match != null ) {
// Not the most efficient way to exclude the '+' from the command but I couldn't find something better
const complete _command = match [ 1 ] + match [ 2 ] ;
const command _output = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
return yield this . parser . parse _commands ( complete _command , functions _object ) ;
} ) , ` Command Parsing error in dynamic command ' ${ complete _command } ' ` ) ;
if ( command _output == null ) {
return ;
}
const start = dynamic _command _regex . lastIndex - match [ 0 ] . length ;
const end = dynamic _command _regex . lastIndex ;
content =
content . substring ( 0 , start ) +
command _output +
content . substring ( end ) ;
dynamic _command _regex . lastIndex +=
command _output . length - match [ 0 ] . length ;
match = dynamic _command _regex . exec ( content ) ;
}
node . nodeValue = content ;
}
}
} ) ;
}
get _new _file _template _for _folder ( folder ) {
do {
const match = this . plugin . settings . folder _templates . find ( ( e ) => e . folder == folder . path ) ;
if ( match && match . template ) {
return match . template ;
}
folder = folder . parent ;
} while ( folder ) ;
}
static on _file _creation ( templater , file ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
if ( ! ( file instanceof obsidian _module . TFile ) || file . extension !== "md" ) {
return ;
}
// Avoids template replacement when syncing template files
const template _folder = obsidian _module . normalizePath ( templater . plugin . settings . templates _folder ) ;
if ( file . path . includes ( template _folder ) && template _folder !== "/" ) {
return ;
}
// TODO: find a better way to do this
// Currently, I have to wait for the daily note plugin to add the file content before replacing
// Not a problem with Calendar however since it creates the file with the existing content
yield delay ( 300 ) ;
if ( file . stat . size == 0 &&
templater . plugin . settings . enable _folder _templates ) {
const folder _template _match = templater . get _new _file _template _for _folder ( file . parent ) ;
if ( ! folder _template _match ) {
return ;
}
const template _file = yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
return resolve _tfile ( templater . app , folder _template _match ) ;
} ) , ` Couldn't find template ${ folder _template _match } ` ) ;
// errorWrapper failed
if ( template _file == null ) {
return ;
}
yield templater . write _template _to _file ( template _file , file ) ;
}
else {
yield templater . overwrite _file _commands ( file ) ;
}
} ) ;
}
execute _startup _scripts ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
for ( const template of this . plugin . settings . startup _templates ) {
if ( ! template ) {
continue ;
}
const file = errorWrapperSync ( ( ) => resolve _tfile ( this . app , template ) , ` Couldn't find startup template " ${ template } " ` ) ;
if ( ! file ) {
continue ;
}
const running _config = this . create _running _config ( file , file , RunMode . StartupTemplate ) ;
yield errorWrapper ( ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) { return this . read _and _parse _template ( running _config ) ; } ) , ` Startup Template parsing error, aborting. ` ) ;
}
} ) ;
}
}
class EventHandler {
constructor ( app , plugin , templater , settings ) {
this . app = app ;
this . plugin = plugin ;
this . templater = templater ;
this . settings = settings ;
}
setup ( ) {
this . app . workspace . onLayoutReady ( ( ) => {
this . update _trigger _file _on _creation ( ) ;
} ) ;
this . update _syntax _highlighting ( ) ;
this . update _file _menu ( ) ;
}
update _syntax _highlighting ( ) {
if ( this . plugin . settings . syntax _highlighting ) {
this . syntax _highlighting _event = this . app . workspace . on ( "codemirror" , ( cm ) => {
cm . setOption ( "mode" , "templater" ) ;
} ) ;
this . app . workspace . iterateCodeMirrors ( ( cm ) => {
cm . setOption ( "mode" , "templater" ) ;
} ) ;
this . plugin . registerEvent ( this . syntax _highlighting _event ) ;
}
else {
if ( this . syntax _highlighting _event ) {
this . app . vault . offref ( this . syntax _highlighting _event ) ;
}
this . app . workspace . iterateCodeMirrors ( ( cm ) => {
cm . setOption ( "mode" , "hypermd" ) ;
} ) ;
}
}
update _trigger _file _on _creation ( ) {
if ( this . settings . trigger _on _file _creation ) {
this . trigger _on _file _creation _event = this . app . vault . on ( "create" , ( file ) => Templater . on _file _creation ( this . templater , file ) ) ;
this . plugin . registerEvent ( this . trigger _on _file _creation _event ) ;
}
else {
if ( this . trigger _on _file _creation _event ) {
this . app . vault . offref ( this . trigger _on _file _creation _event ) ;
this . trigger _on _file _creation _event = undefined ;
}
}
}
update _file _menu ( ) {
this . plugin . registerEvent ( this . app . workspace . on ( "file-menu" , ( menu , file ) => {
if ( file instanceof obsidian _module . TFolder ) {
menu . addItem ( ( item ) => {
item . setTitle ( "Create new note from template" )
. setIcon ( "templater-icon" )
. onClick ( ( ) => {
this . plugin . fuzzy _suggester . create _new _note _from _template ( file ) ;
} ) ;
} ) ;
}
} ) ) ;
}
}
class CommandHandler {
constructor ( app , plugin ) {
this . app = app ;
this . plugin = plugin ;
}
setup ( ) {
this . plugin . addCommand ( {
id : "insert-templater" ,
name : "Open Insert Template modal" ,
hotkeys : [
{
modifiers : [ "Alt" ] ,
key : "e" ,
} ,
] ,
callback : ( ) => {
this . plugin . fuzzy _suggester . insert _template ( ) ;
} ,
} ) ;
this . plugin . addCommand ( {
id : "replace-in-file-templater" ,
name : "Replace templates in the active file" ,
hotkeys : [
{
modifiers : [ "Alt" ] ,
key : "r" ,
} ,
] ,
callback : ( ) => {
this . plugin . templater . overwrite _active _file _commands ( ) ;
} ,
} ) ;
this . plugin . addCommand ( {
id : "jump-to-next-cursor-location" ,
name : "Jump to next cursor location" ,
hotkeys : [
{
modifiers : [ "Alt" ] ,
key : "Tab" ,
} ,
] ,
callback : ( ) => {
this . plugin . templater . editor . jump _to _next _cursor _location ( ) ;
} ,
} ) ;
this . plugin . addCommand ( {
id : "create-new-note-from-template" ,
name : "Create new note from template" ,
hotkeys : [
{
modifiers : [ "Alt" ] ,
key : "n" ,
} ,
] ,
callback : ( ) => {
this . plugin . fuzzy _suggester . create _new _note _from _template ( ) ;
} ,
} ) ;
this . register _templates _hotkeys ( ) ;
}
register _templates _hotkeys ( ) {
this . plugin . settings . enabled _templates _hotkeys . forEach ( ( template ) => {
if ( template ) {
this . add _template _hotkey ( null , template ) ;
}
} ) ;
}
add _template _hotkey ( old _template , new _template ) {
this . remove _template _hotkey ( old _template ) ;
if ( new _template ) {
this . plugin . addCommand ( {
id : new _template ,
name : ` Insert ${ new _template } ` ,
callback : ( ) => {
const template = errorWrapperSync ( ( ) => resolve _tfile ( this . app , new _template ) , ` Couldn't find the template file associated with this hotkey ` ) ;
if ( ! template ) {
return ;
}
this . plugin . templater . append _template _to _active _file ( template ) ;
} ,
} ) ;
}
}
remove _template _hotkey ( template ) {
if ( template ) {
// TODO: Find official way to do this
// @ts-ignore
this . app . commands . removeCommand ( ` ${ this . plugin . manifest . id } : ${ template } ` ) ;
}
}
}
class TemplaterPlugin extends obsidian _module . Plugin {
onload ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . load _settings ( ) ;
this . templater = new Templater ( this . app , this ) ;
yield this . templater . setup ( ) ;
this . fuzzy _suggester = new FuzzySuggester ( this . app , this ) ;
this . event _handler = new EventHandler ( this . app , this , this . templater , this . settings ) ;
this . event _handler . setup ( ) ;
this . command _handler = new CommandHandler ( this . app , this ) ;
this . command _handler . setup ( ) ;
obsidian _module . addIcon ( "templater-icon" , ICON _DATA ) ;
this . addRibbonIcon ( "templater-icon" , "Templater" , ( ) => _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . fuzzy _suggester . insert _template ( ) ;
} ) ) ;
this . addSettingTab ( new TemplaterSettingTab ( this . app , this ) ) ;
// Files might not be created yet
this . app . workspace . onLayoutReady ( ( ) => {
this . templater . execute _startup _scripts ( ) ;
} ) ;
} ) ;
}
save _settings ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
yield this . saveData ( this . settings ) ;
} ) ;
}
load _settings ( ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
this . settings = Object . assign ( { } , DEFAULT _SETTINGS , yield this . loadData ( ) ) ;
} ) ;
}
}
module . exports = TemplaterPlugin ;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInNyYy9Mb2cudHMiLCJzcmMvRXJyb3IudHMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2VudW1zLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvZ2V0Tm9kZU5hbWUuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXRXaW5kb3cuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9pbnN0YW5jZU9mLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9tb2RpZmllcnMvYXBwbHlTdHlsZXMuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL3V0aWxzL2dldEJhc2VQbGFjZW1lbnQuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL3V0aWxzL21hdGguanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXRCb3VuZGluZ0NsaWVudFJlY3QuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXRMYXlvdXRSZWN0LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvY29udGFpbnMuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXRDb21wdXRlZFN0eWxlLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvaXNUYWJsZUVsZW1lbnQuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXREb2N1bWVudEVsZW1lbnQuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXRQYXJlbnROb2RlLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvZ2V0T2Zmc2V0UGFyZW50LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9nZXRNYWluQXhpc0Zyb21QbGFjZW1lbnQuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL3V0aWxzL3dpdGhpbi5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvdXRpbHMvZ2V0RnJlc2hTaWRlT2JqZWN0LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9tZXJnZVBhZGRpbmdPYmplY3QuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL3V0aWxzL2V4cGFuZFRvSGFzaE1hcC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvbW9kaWZpZXJzL2Fycm93LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9nZXRWYXJpYXRpb24uanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL21vZGlmaWVycy9jb21wdXRlU3R5bGVzLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9tb2RpZmllcnMvZXZlbnRMaXN0ZW5lcnMuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL3V0aWxzL2dldE9wcG9zaXRlUGxhY2VtZW50LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9nZXRPcHBvc2l0ZVZhcmlhdGlvblBsYWNlbWVudC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvZG9tLXV0aWxzL2dldFdpbmRvd1Njcm9sbC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvZG9tLXV0aWxzL2dldFdpbmRvd1Njcm9sbEJhclguanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXRWaWV3cG9ydFJlY3QuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXREb2N1bWVudFJlY3QuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9pc1Njcm9sbFBhcmVudC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvZG9tLXV0aWxzL2dldFNjcm9sbFBhcmVudC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvZG9tLXV0aWxzL2xpc3RTY3JvbGxQYXJlbnRzLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9yZWN0VG9DbGllbnRSZWN0LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvZ2V0Q2xpcHBpbmdSZWN0LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9jb21wdXRlT2Zmc2V0cy5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvdXRpbHMvZGV0ZWN0T3ZlcmZsb3cuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL3V0aWxzL2NvbXB1dGVBdXRvUGxhY2VtZW50LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9tb2RpZmllcnMvZmxpcC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvbW9kaWZpZXJzL2hpZGUuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL21vZGlmaWVycy9vZmZzZXQuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL21vZGlmaWVycy9wb3BwZXJPZmZzZXRzLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi91dGlscy9nZXRBbHRBeGlzLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9tb2RpZmllcnMvcHJldmVudE92ZXJmbG93LmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvZ2V0SFRNTEVsZW1lbnRTY3JvbGwuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmUvbGliL2RvbS11dGlscy9nZXROb2RlU2Nyb2xsLmpzIiwibm9kZV9tb2R1bGVzL0Bwb3BwZXJqcy9jb3JlL2xpYi9kb20tdXRpbHMvZ2V0Q29tcG9zaXRlUmVjdC5qcyIsIm5vZGVfbW9kdWxlcy9AcG9wcGVyanMvY29yZS9saWIvdXRpbHMvb3JkZXJNb2RpZmllcnMuanMiLCJub2RlX21vZHVsZXMvQHBvcHBlcmpzL2NvcmU