2012-04-30 11:23:03 +00:00
/ * \
2012-05-03 20:47:16 +00:00
title : $ : / c o r e / m o d u l e s / w i k i . j s
2012-04-30 11:23:03 +00:00
type : application / javascript
module - type : wikimethod
Extension methods for the $tw . Wiki object
Adds the following properties to the wiki object :
2013-03-16 10:42:46 +00:00
* ` eventListeners ` is a hashmap by type of arrays of listener functions
2013-11-13 23:33:32 +00:00
* ` changedTiddlers ` is a hashmap describing changes to named tiddlers since wiki change events were last dispatched . Each entry is a hashmap containing two fields :
2012-04-30 11:23:03 +00:00
modified : true / false
deleted : true / false
2013-11-13 23:33:32 +00:00
* ` changeCount ` is a hashmap by tiddler title containing a numerical index that starts at zero and is incremented each time a tiddler is created changed or deleted
* ` caches ` is a hashmap by tiddler title containing a further hashmap of named cache objects . Caches are automatically cleared when a tiddler is modified or deleted
* ` globalCache ` is a hashmap by cache name of cache objects that are cleared whenever any tiddler change occurs
2012-04-30 11:23:03 +00:00
\ * /
( function ( ) {
2012-05-04 17:49:04 +00:00
/*jslint node: true, browser: true */
/*global $tw: false */
2012-04-30 11:23:03 +00:00
"use strict" ;
2013-11-08 08:47:00 +00:00
var widget = require ( "$:/core/modules/widgets/widget.js" ) ;
2013-10-12 16:05:13 +00:00
2016-11-23 18:20:31 +00:00
var USER _NAME _TITLE = "$:/status/UserName" ,
TIMESTAMP _DISABLE _TITLE = "$:/config/TimestampDisable" ;
2013-08-04 13:02:07 +00:00
2019-05-24 20:07:37 +00:00
/ *
Add available indexers to this wiki
* /
exports . addIndexersToWiki = function ( ) {
var self = this ;
$tw . utils . each ( $tw . modules . applyMethods ( "indexer" ) , function ( Indexer , name ) {
self . addIndexer ( new Indexer ( self ) , name ) ;
} ) ;
} ;
2012-06-19 07:56:15 +00:00
/ *
2012-10-18 22:20:27 +00:00
Get the value of a text reference . Text references can have any of these forms :
< tiddlertitle >
2013-01-15 17:50:47 +00:00
< tiddlertitle > ! ! < fieldname >
! ! < fieldname > - specifies a field of the current tiddlers
2013-07-08 20:44:12 +00:00
< tiddlertitle > # # < index >
2012-06-19 07:56:15 +00:00
* /
exports . getTextReference = function ( textRef , defaultText , currTiddlerTitle ) {
2012-12-13 21:34:31 +00:00
var tr = $tw . utils . parseTextReference ( textRef ) ,
2013-01-15 17:50:47 +00:00
title = tr . title || currTiddlerTitle ;
if ( tr . field ) {
var tiddler = this . getTiddler ( title ) ;
2013-07-08 14:16:55 +00:00
if ( tr . field === "title" ) { // Special case so we can return the title of a non-existent tiddler
2022-06-14 07:33:47 +00:00
return title || defaultText ;
2013-07-08 14:16:55 +00:00
} else if ( tiddler && $tw . utils . hop ( tiddler . fields , tr . field ) ) {
2013-05-27 16:55:23 +00:00
return tiddler . getFieldString ( tr . field ) ;
2013-01-15 17:50:47 +00:00
} else {
return defaultText ;
}
} else if ( tr . index ) {
return this . extractTiddlerDataItem ( title , tr . index , defaultText ) ;
2012-06-19 07:56:15 +00:00
} else {
2013-03-27 13:48:17 +00:00
return this . getTiddlerText ( title , defaultText ) ;
2012-06-19 07:56:15 +00:00
}
} ;
2012-11-11 14:31:45 +00:00
exports . setTextReference = function ( textRef , value , currTiddlerTitle ) {
2012-12-13 21:34:31 +00:00
var tr = $tw . utils . parseTextReference ( textRef ) ,
2012-06-19 15:47:25 +00:00
title = tr . title || currTiddlerTitle ;
2014-05-31 17:37:27 +00:00
this . setText ( title , tr . field , tr . index , value ) ;
} ;
2015-08-06 17:17:06 +00:00
exports . setText = function ( title , field , index , value , options ) {
options = options || { } ;
2015-08-09 10:09:02 +00:00
var creationFields = options . suppressTimestamp ? { } : this . getCreationFields ( ) ,
modificationFields = options . suppressTimestamp ? { } : this . getModificationFields ( ) ;
2014-03-12 21:54:43 +00:00
// Check if it is a reference to a tiddler field
2014-05-31 17:37:27 +00:00
if ( index ) {
2014-04-06 21:43:10 +00:00
var data = this . getTiddlerData ( title , Object . create ( null ) ) ;
2015-09-25 13:20:37 +00:00
if ( value !== undefined ) {
data [ index ] = value ;
} else {
delete data [ index ] ;
}
2021-10-24 19:19:42 +00:00
this . setTiddlerData ( title , data , { } , { suppressTimestamp : options . suppressTimestamp } ) ;
2014-03-12 21:54:43 +00:00
} else {
var tiddler = this . getTiddler ( title ) ,
fields = { title : title } ;
2014-05-31 17:37:27 +00:00
fields [ field || "text" ] = value ;
2015-08-06 17:17:06 +00:00
this . addTiddler ( new $tw . Tiddler ( creationFields , tiddler , fields , modificationFields ) ) ;
2012-06-19 15:47:25 +00:00
}
2012-06-19 07:56:15 +00:00
} ;
2012-06-19 15:47:25 +00:00
exports . deleteTextReference = function ( textRef , currTiddlerTitle ) {
2012-12-13 21:34:31 +00:00
var tr = $tw . utils . parseTextReference ( textRef ) ,
2012-06-19 15:47:25 +00:00
title , tiddler , fields ;
// Check if it is a reference to a tiddler
if ( tr . title && ! tr . field ) {
this . deleteTiddler ( tr . title ) ;
// Else check for a field reference
} else if ( tr . field ) {
title = tr . title || currTiddlerTitle ;
tiddler = this . getTiddler ( title ) ;
if ( tiddler && $tw . utils . hop ( tiddler . fields , tr . field ) ) {
2014-04-06 21:43:10 +00:00
fields = Object . create ( null ) ;
2012-06-19 15:47:25 +00:00
fields [ tr . field ] = undefined ;
2013-08-04 13:02:07 +00:00
this . addTiddler ( new $tw . Tiddler ( tiddler , fields , this . getModificationFields ( ) ) ) ;
2012-06-19 15:47:25 +00:00
}
}
2012-06-19 07:56:15 +00:00
} ;
2013-03-16 10:42:46 +00:00
exports . addEventListener = function ( type , listener ) {
this . eventListeners = this . eventListeners || { } ;
this . eventListeners [ type ] = this . eventListeners [ type ] || [ ] ;
2021-05-30 18:20:17 +00:00
this . eventListeners [ type ] . push ( listener ) ;
2013-03-16 10:42:46 +00:00
} ;
exports . removeEventListener = function ( type , listener ) {
var listeners = this . eventListeners [ type ] ;
if ( listeners ) {
var p = listeners . indexOf ( listener ) ;
if ( p !== - 1 ) {
listeners . splice ( p , 1 ) ;
}
}
2012-04-30 11:23:03 +00:00
} ;
2013-03-16 10:42:46 +00:00
exports . dispatchEvent = function ( type /*, args */ ) {
var args = Array . prototype . slice . call ( arguments , 1 ) ,
listeners = this . eventListeners [ type ] ;
if ( listeners ) {
for ( var p = 0 ; p < listeners . length ; p ++ ) {
var listener = listeners [ p ] ;
listener . apply ( listener , args ) ;
2012-04-30 11:23:03 +00:00
}
}
} ;
/ *
Causes a tiddler to be marked as changed , incrementing the change count , and triggers event handlers .
This method should be called after the changes it describes have been made to the wiki . tiddlers [ ] array .
title : Title of tiddler
isDeleted : defaults to false ( meaning the tiddler has been created or modified ) ,
2014-03-18 21:18:37 +00:00
true if the tiddler has been deleted
2012-04-30 11:23:03 +00:00
* /
2013-03-05 19:59:55 +00:00
exports . enqueueTiddlerEvent = function ( title , isDeleted ) {
2012-04-30 11:23:03 +00:00
// Record the touch in the list of changed tiddlers
2014-04-06 21:43:10 +00:00
this . changedTiddlers = this . changedTiddlers || Object . create ( null ) ;
this . changedTiddlers [ title ] = this . changedTiddlers [ title ] || Object . create ( null ) ;
2012-04-30 11:23:03 +00:00
this . changedTiddlers [ title ] [ isDeleted ? "deleted" : "modified" ] = true ;
// Increment the change count
2014-04-06 21:43:10 +00:00
this . changeCount = this . changeCount || Object . create ( null ) ;
2012-05-09 07:51:16 +00:00
if ( $tw . utils . hop ( this . changeCount , title ) ) {
2012-04-30 11:23:03 +00:00
this . changeCount [ title ] ++ ;
} else {
this . changeCount [ title ] = 1 ;
}
// Trigger events
2015-05-02 16:22:23 +00:00
this . eventListeners = this . eventListeners || { } ;
2012-04-30 11:23:03 +00:00
if ( ! this . eventsTriggered ) {
2013-03-16 10:42:46 +00:00
var self = this ;
2012-04-30 11:23:03 +00:00
$tw . utils . nextTick ( function ( ) {
2013-03-16 10:42:46 +00:00
var changes = self . changedTiddlers ;
2014-04-06 21:43:10 +00:00
self . changedTiddlers = Object . create ( null ) ;
2013-03-16 10:42:46 +00:00
self . eventsTriggered = false ;
2014-04-30 21:48:36 +00:00
if ( $tw . utils . count ( changes ) > 0 ) {
self . dispatchEvent ( "change" , changes ) ;
}
2012-04-30 11:23:03 +00:00
} ) ;
this . eventsTriggered = true ;
}
} ;
2014-08-29 08:58:30 +00:00
exports . getSizeOfTiddlerEventQueue = function ( ) {
return $tw . utils . count ( this . changedTiddlers ) ;
} ;
2014-04-30 21:49:02 +00:00
exports . clearTiddlerEventQueue = function ( ) {
this . changedTiddlers = Object . create ( null ) ;
2014-08-30 19:44:26 +00:00
this . changeCount = Object . create ( null ) ;
2014-04-30 21:49:02 +00:00
} ;
2012-04-30 11:23:03 +00:00
exports . getChangeCount = function ( title ) {
2014-04-06 21:43:10 +00:00
this . changeCount = this . changeCount || Object . create ( null ) ;
2012-05-09 07:51:16 +00:00
if ( $tw . utils . hop ( this . changeCount , title ) ) {
2012-04-30 11:23:03 +00:00
return this . changeCount [ title ] ;
} else {
return 0 ;
}
} ;
2013-10-25 20:15:20 +00:00
/ *
Generate an unused title from the specified base
2021-05-02 18:26:50 +00:00
options . prefix must be a string
2013-10-25 20:15:20 +00:00
* /
2014-02-14 07:53:41 +00:00
exports . generateNewTitle = function ( baseTitle , options ) {
options = options || { } ;
2024-09-10 09:49:41 +00:00
var title = baseTitle ,
template = options . template || "" ,
// test if .startCount is a positive integer. If not set to 0
c = ( parseInt ( options . startCount , 10 ) > 0 ) ? parseInt ( options . startCount , 10 ) : 0 ,
2021-05-02 18:26:50 +00:00
prefix = ( typeof ( options . prefix ) === "string" ) ? options . prefix : " " ;
2024-09-10 09:49:41 +00:00
2021-05-02 18:26:50 +00:00
if ( template ) {
// "count" is important to avoid an endless loop in while(...)!!
template = ( /\$count:?(\d+)?\$/i . test ( template ) ) ? template : template + "$count$" ;
2024-09-10 09:49:41 +00:00
// .formatTitleString() expects strings as input
title = $tw . utils . formatTitleString ( template , { "base" : baseTitle , "separator" : prefix , "counter" : c + "" } ) ;
2021-05-02 18:26:50 +00:00
while ( this . tiddlerExists ( title ) || this . isShadowTiddler ( title ) || this . findDraft ( title ) ) {
2024-09-10 09:49:41 +00:00
title = $tw . utils . formatTitleString ( template , { "base" : baseTitle , "separator" : prefix , "counter" : ( ++ c ) + "" } ) ;
2021-05-02 18:26:50 +00:00
}
} else {
2024-09-10 09:49:41 +00:00
if ( c > 0 ) {
title = baseTitle + prefix + c ;
}
2021-05-02 18:26:50 +00:00
while ( this . tiddlerExists ( title ) || this . isShadowTiddler ( title ) || this . findDraft ( title ) ) {
title = baseTitle + prefix + ( ++ c ) ;
}
2014-08-30 19:44:26 +00:00
}
2013-10-25 20:15:20 +00:00
return title ;
} ;
2013-04-03 13:29:12 +00:00
exports . isSystemTiddler = function ( title ) {
2016-01-05 18:48:04 +00:00
return title && title . indexOf ( "$:/" ) === 0 ;
2013-04-03 13:29:12 +00:00
} ;
exports . isTemporaryTiddler = function ( title ) {
2016-01-05 18:48:04 +00:00
return title && title . indexOf ( "$:/temp/" ) === 0 ;
2013-04-03 13:29:12 +00:00
} ;
2021-05-21 07:51:15 +00:00
exports . isVolatileTiddler = function ( title ) {
return title && title . indexOf ( "$:/temp/volatile/" ) === 0 ;
} ;
2014-01-29 09:04:41 +00:00
exports . isImageTiddler = function ( title ) {
var tiddler = this . getTiddler ( title ) ;
2021-05-30 18:20:17 +00:00
if ( tiddler ) {
2014-01-29 09:04:41 +00:00
var contentTypeInfo = $tw . config . contentTypeInfo [ tiddler . fields . type || "text/vnd.tiddlywiki" ] ;
return ! ! contentTypeInfo && contentTypeInfo . flags . indexOf ( "image" ) !== - 1 ;
} else {
return null ;
}
} ;
2019-09-16 15:15:26 +00:00
exports . isBinaryTiddler = function ( title ) {
var tiddler = this . getTiddler ( title ) ;
2021-05-30 18:20:17 +00:00
if ( tiddler ) {
2019-09-16 15:15:26 +00:00
var contentTypeInfo = $tw . config . contentTypeInfo [ tiddler . fields . type || "text/vnd.tiddlywiki" ] ;
return ! ! contentTypeInfo && contentTypeInfo . encoding === "base64" ;
} else {
return null ;
}
} ;
2013-12-02 09:59:17 +00:00
/ *
Like addTiddler ( ) except it will silently reject any plugin tiddlers that are older than the currently loaded version . Returns true if the tiddler was imported
* /
exports . importTiddler = function ( tiddler ) {
var existingTiddler = this . getTiddler ( tiddler . fields . title ) ;
// Check if we're dealing with a plugin
if ( tiddler && tiddler . hasField ( "plugin-type" ) && tiddler . hasField ( "version" ) && existingTiddler && existingTiddler . hasField ( "plugin-type" ) && existingTiddler . hasField ( "version" ) ) {
// Reject the incoming plugin if it is older
2015-02-12 23:04:20 +00:00
if ( ! $tw . utils . checkVersions ( tiddler . fields . version , existingTiddler . fields . version ) ) {
2013-12-02 09:59:17 +00:00
return false ;
}
}
// Fall through to adding the tiddler
this . addTiddler ( tiddler ) ;
return true ;
} ;
2013-08-04 13:02:07 +00:00
/ *
2013-08-06 14:27:02 +00:00
Return a hashmap of the fields that should be set when a tiddler is created
2013-08-04 13:02:07 +00:00
* /
exports . getCreationFields = function ( ) {
2016-11-23 18:20:31 +00:00
if ( this . getTiddlerText ( TIMESTAMP _DISABLE _TITLE , "" ) . toLowerCase ( ) !== "yes" ) {
var fields = {
created : new Date ( )
} ,
creator = this . getTiddlerText ( USER _NAME _TITLE ) ;
if ( creator ) {
fields . creator = creator ;
}
return fields ;
} else {
return { } ;
2013-08-04 13:02:07 +00:00
}
} ;
/ *
2013-08-06 14:27:02 +00:00
Return a hashmap of the fields that should be set when a tiddler is modified
2013-08-04 13:02:07 +00:00
* /
exports . getModificationFields = function ( ) {
2016-11-23 18:20:31 +00:00
if ( this . getTiddlerText ( TIMESTAMP _DISABLE _TITLE , "" ) . toLowerCase ( ) !== "yes" ) {
var fields = Object . create ( null ) ,
modifier = this . getTiddlerText ( USER _NAME _TITLE ) ;
fields . modified = new Date ( ) ;
if ( modifier ) {
fields . modifier = modifier ;
}
return fields ;
} else {
return { } ;
2013-08-04 13:02:07 +00:00
}
} ;
2012-04-30 11:23:03 +00:00
/ *
2013-12-11 22:01:02 +00:00
Return a sorted array of tiddler titles . Options include :
sortField : field to sort by
excludeTag : tag to exclude
includeSystem : whether to include system tiddlers ( defaults to false )
2012-04-30 11:23:03 +00:00
* /
2013-12-11 22:01:02 +00:00
exports . getTiddlers = function ( options ) {
2014-04-06 21:43:10 +00:00
options = options || Object . create ( null ) ;
2013-12-11 22:01:02 +00:00
var self = this ,
sortField = options . sortField || "title" ,
tiddlers = [ ] , t , titles = [ ] ;
2014-03-16 21:23:10 +00:00
this . each ( function ( tiddler , title ) {
if ( options . includeSystem || ! self . isSystemTiddler ( title ) ) {
if ( ! options . excludeTag || ! tiddler . hasTag ( options . excludeTag ) ) {
tiddlers . push ( tiddler ) ;
2013-12-11 22:01:02 +00:00
}
2012-06-06 12:21:20 +00:00
}
2014-03-16 21:23:10 +00:00
} ) ;
2012-04-30 11:23:03 +00:00
tiddlers . sort ( function ( a , b ) {
2012-10-16 20:41:12 +00:00
var aa = a . fields [ sortField ] . toLowerCase ( ) || "" ,
bb = b . fields [ sortField ] . toLowerCase ( ) || "" ;
2012-04-30 11:23:03 +00:00
if ( aa < bb ) {
return - 1 ;
} else {
if ( aa > bb ) {
return 1 ;
} else {
return 0 ;
}
}
} ) ;
for ( t = 0 ; t < tiddlers . length ; t ++ ) {
2013-03-15 20:02:31 +00:00
titles . push ( tiddlers [ t ] . fields . title ) ;
2012-04-30 11:23:03 +00:00
}
return titles ;
} ;
2013-03-21 22:21:00 +00:00
exports . countTiddlers = function ( excludeTag ) {
2013-12-11 22:01:02 +00:00
var tiddlers = this . getTiddlers ( { excludeTag : excludeTag } ) ;
2013-03-21 22:21:00 +00:00
return $tw . utils . count ( tiddlers ) ;
} ;
2014-04-03 19:49:16 +00:00
/ *
Returns a function iterator ( callback ) that iterates through the specified titles , and invokes the callback with callback ( tiddler , title )
* /
exports . makeTiddlerIterator = function ( titles ) {
var self = this ;
if ( ! $tw . utils . isArray ( titles ) ) {
titles = Object . keys ( titles ) ;
} else {
titles = titles . slice ( 0 ) ;
}
return function ( callback ) {
titles . forEach ( function ( title ) {
callback ( self . getTiddler ( title ) , title ) ;
} ) ;
} ;
} ;
2012-05-08 15:02:24 +00:00
/ *
Sort an array of tiddler titles by a specified field
titles : array of titles ( sorted in place )
sortField : name of field to sort by
isDescending : true if the sort should be descending
2012-06-14 10:36:26 +00:00
isCaseSensitive : true if the sort should consider upper and lower case letters to be different
2012-05-08 15:02:24 +00:00
* /
2018-03-05 11:09:25 +00:00
exports . sortTiddlers = function ( titles , sortField , isDescending , isCaseSensitive , isNumeric , isAlphaNumeric ) {
2015-01-23 11:24:47 +00:00
var self = this ;
2021-09-22 12:44:35 +00:00
if ( sortField === "title" ) {
if ( ! isNumeric && ! isAlphaNumeric ) {
if ( isCaseSensitive ) {
if ( isDescending ) {
titles . sort ( function ( a , b ) {
return b . localeCompare ( a ) ;
} ) ;
} else {
titles . sort ( function ( a , b ) {
return a . localeCompare ( b ) ;
} ) ;
}
2014-09-17 11:17:43 +00:00
} else {
2021-09-22 12:44:35 +00:00
if ( isDescending ) {
titles . sort ( function ( a , b ) {
return b . toLowerCase ( ) . localeCompare ( a . toLowerCase ( ) ) ;
} ) ;
} else {
titles . sort ( function ( a , b ) {
return a . toLowerCase ( ) . localeCompare ( b . toLowerCase ( ) ) ;
} ) ;
}
2014-09-17 11:17:43 +00:00
}
2012-05-08 15:02:24 +00:00
} else {
2021-09-22 12:44:35 +00:00
titles . sort ( function ( a , b ) {
var x , y ;
if ( isNumeric ) {
x = Number ( a ) ;
y = Number ( b ) ;
if ( isNaN ( x ) ) {
if ( isNaN ( y ) ) {
// If neither value is a number then fall through to a textual comparison
} else {
return isDescending ? - 1 : 1 ;
}
} else {
if ( isNaN ( y ) ) {
return isDescending ? 1 : - 1 ;
} else {
return isDescending ? y - x : x - y ;
}
}
}
if ( isAlphaNumeric ) {
return isDescending ? b . localeCompare ( a , undefined , { numeric : true , sensitivity : "base" } ) : a . localeCompare ( b , undefined , { numeric : true , sensitivity : "base" } ) ;
}
if ( ! isCaseSensitive ) {
a = a . toLowerCase ( ) ;
b = b . toLowerCase ( ) ;
}
return isDescending ? b . localeCompare ( a ) : a . localeCompare ( b ) ;
} ) ;
}
} else {
titles . sort ( function ( a , b ) {
var x , y ;
if ( sortField !== "title" ) {
var tiddlerA = self . getTiddler ( a ) ,
tiddlerB = self . getTiddler ( b ) ;
if ( tiddlerA ) {
a = tiddlerA . fields [ sortField ] || "" ;
} else {
a = "" ;
}
if ( tiddlerB ) {
b = tiddlerB . fields [ sortField ] || "" ;
} else {
b = "" ;
}
}
if ( isNumeric ) {
x = Number ( a ) ;
y = Number ( b ) ;
if ( isNaN ( x ) ) {
if ( isNaN ( y ) ) {
// If neither value is a number then fall through to a textual comparison
} else {
return isDescending ? - 1 : 1 ;
}
} else {
if ( isNaN ( y ) ) {
return isDescending ? 1 : - 1 ;
} else {
return isDescending ? y - x : x - y ;
}
}
}
if ( Object . prototype . toString . call ( a ) === "[object Date]" && Object . prototype . toString . call ( b ) === "[object Date]" ) {
return isDescending ? b - a : a - b ;
}
2014-04-17 19:15:52 +00:00
a = String ( a ) ;
b = String ( b ) ;
2021-09-22 12:44:35 +00:00
if ( isAlphaNumeric ) {
return isDescending ? b . localeCompare ( a , undefined , { numeric : true , sensitivity : "base" } ) : a . localeCompare ( b , undefined , { numeric : true , sensitivity : "base" } ) ;
}
2014-04-17 19:15:52 +00:00
if ( ! isCaseSensitive ) {
a = a . toLowerCase ( ) ;
b = b . toLowerCase ( ) ;
2012-05-08 15:02:24 +00:00
}
2014-04-17 19:15:52 +00:00
return isDescending ? b . localeCompare ( a ) : a . localeCompare ( b ) ;
2021-09-22 12:44:35 +00:00
} ) ;
}
2012-05-08 15:02:24 +00:00
} ;
2013-12-11 22:01:02 +00:00
/ *
For every tiddler invoke a callback ( title , tiddler ) with ` this ` set to the wiki object . Options include :
sortField : field to sort by
excludeTag : tag to exclude
includeSystem : whether to include system tiddlers ( defaults to false )
* /
exports . forEachTiddler = function ( /* [options,]callback */ ) {
2012-04-30 11:23:03 +00:00
var arg = 0 ,
2013-12-11 22:01:02 +00:00
options = arguments . length >= 2 ? arguments [ arg ++ ] : { } ,
2012-04-30 11:23:03 +00:00
callback = arguments [ arg ++ ] ,
2013-12-11 22:01:02 +00:00
titles = this . getTiddlers ( options ) ,
2012-04-30 11:23:03 +00:00
t , tiddler ;
for ( t = 0 ; t < titles . length ; t ++ ) {
2014-03-16 21:23:10 +00:00
tiddler = this . getTiddler ( titles [ t ] ) ;
2012-04-30 11:23:03 +00:00
if ( tiddler ) {
callback . call ( this , tiddler . fields . title , tiddler ) ;
}
}
} ;
2020-03-26 13:15:02 +00:00
/ *
Return an array of tiddler titles that are directly linked within the given parse tree
* /
exports . extractLinks = function ( parseTreeRoot ) {
// Count up the links
var links = [ ] ,
checkParseTree = function ( parseTree ) {
for ( var t = 0 ; t < parseTree . length ; t ++ ) {
var parseTreeNode = parseTree [ t ] ;
if ( parseTreeNode . type === "link" && parseTreeNode . attributes . to && parseTreeNode . attributes . to . type === "string" ) {
var value = parseTreeNode . attributes . to . value ;
if ( links . indexOf ( value ) === - 1 ) {
links . push ( value ) ;
}
}
if ( parseTreeNode . children ) {
checkParseTree ( parseTreeNode . children ) ;
}
}
} ;
checkParseTree ( parseTreeRoot ) ;
return links ;
} ;
2013-03-19 10:14:44 +00:00
/ *
Return an array of tiddler titles that are directly linked from the specified tiddler
* /
exports . getTiddlerLinks = function ( title ) {
var self = this ;
// We'll cache the links so they only get computed if the tiddler changes
return this . getCacheForTiddler ( title , "links" , function ( ) {
// Parse the tiddler
2013-11-08 08:51:14 +00:00
var parser = self . parseTiddler ( title ) ;
2013-03-19 10:14:44 +00:00
if ( parser ) {
2020-03-26 13:15:02 +00:00
return self . extractLinks ( parser . tree ) ;
2013-03-19 10:14:44 +00:00
}
2020-03-26 13:15:02 +00:00
return [ ] ;
2013-03-19 10:14:44 +00:00
} ) ;
} ;
2013-03-19 16:45:07 +00:00
/ *
Return an array of tiddler titles that link to the specified tiddler
* /
exports . getTiddlerBacklinks = function ( targetTitle ) {
var self = this ,
2024-02-18 09:14:23 +00:00
backIndexer = this . getIndexer ( "BackIndexer" ) ,
backlinks = backIndexer && backIndexer . subIndexers . link . lookup ( targetTitle ) ;
2020-03-26 13:15:02 +00:00
if ( ! backlinks ) {
2013-03-19 16:45:07 +00:00
backlinks = [ ] ;
2020-03-26 13:15:02 +00:00
this . forEachTiddler ( function ( title , tiddler ) {
var links = self . getTiddlerLinks ( title ) ;
if ( links . indexOf ( targetTitle ) !== - 1 ) {
backlinks . push ( title ) ;
}
} ) ;
}
2013-03-19 16:45:07 +00:00
return backlinks ;
} ;
2024-02-18 09:14:23 +00:00
/ *
2024-06-19 08:38:02 +00:00
Return an array of tiddler titles that are directly transcluded within the given parse tree . ` title ` is the tiddler being parsed , we will ignore its self - referential transclusions , only return
2024-02-18 09:14:23 +00:00
* /
2024-06-19 08:38:02 +00:00
exports . extractTranscludes = function ( parseTreeRoot , title ) {
2024-02-18 09:14:23 +00:00
// Count up the transcludes
var transcludes = [ ] ,
checkParseTree = function ( parseTree , parentNode ) {
for ( var t = 0 ; t < parseTree . length ; t ++ ) {
var parseTreeNode = parseTree [ t ] ;
2024-06-19 08:38:02 +00:00
if ( parseTreeNode . type === "transclude" ) {
2024-07-15 15:38:27 +00:00
if ( parseTreeNode . attributes . $tiddler ) {
if ( parseTreeNode . attributes . $tiddler . type === "string" ) {
var value ;
// if it is Transclusion with Templates like `{{Index||$:/core/ui/TagTemplate}}`, the `$tiddler` will point to the template. We need to find the actual target tiddler from parent node
if ( parentNode && parentNode . type === "tiddler" && parentNode . attributes . tiddler && parentNode . attributes . tiddler . type === "string" ) {
// Empty value (like `{{!!field}}`) means self-referential transclusion.
value = parentNode . attributes . tiddler . value || title ;
} else {
value = parseTreeNode . attributes . $tiddler . value ;
}
}
} else if ( parseTreeNode . attributes . tiddler ) {
if ( parseTreeNode . attributes . tiddler . type === "string" ) {
// Old transclude widget usage
value = parseTreeNode . attributes . tiddler . value ;
2024-06-19 08:38:02 +00:00
}
} else if ( parseTreeNode . attributes . $field && parseTreeNode . attributes . $field . type === "string" ) {
// Empty value (like `<$transclude $field='created'/>`) means self-referential transclusion.
value = title ;
} else if ( parseTreeNode . attributes . field && parseTreeNode . attributes . field . type === "string" ) {
// Old usage with Empty value (like `<$transclude field='created'/>`)
value = title ;
2024-02-18 09:14:23 +00:00
}
2024-06-19 08:38:02 +00:00
// Deduplicate the result.
if ( value && transcludes . indexOf ( value ) === - 1 ) {
$tw . utils . pushTop ( transcludes , value ) ;
2024-02-18 09:14:23 +00:00
}
}
if ( parseTreeNode . children ) {
2024-06-19 08:38:02 +00:00
checkParseTree ( parseTreeNode . children , parseTreeNode ) ;
2024-02-18 09:14:23 +00:00
}
}
} ;
checkParseTree ( parseTreeRoot ) ;
return transcludes ;
} ;
/ *
Return an array of tiddler titles that are transcluded from the specified tiddler
* /
exports . getTiddlerTranscludes = function ( title ) {
var self = this ;
// We'll cache the transcludes so they only get computed if the tiddler changes
return this . getCacheForTiddler ( title , "transcludes" , function ( ) {
// Parse the tiddler
var parser = self . parseTiddler ( title ) ;
if ( parser ) {
2024-06-19 08:38:02 +00:00
// this will ignore self-referential transclusions from `title`
return self . extractTranscludes ( parser . tree , title ) ;
2024-02-18 09:14:23 +00:00
}
return [ ] ;
} ) ;
} ;
/ *
Return an array of tiddler titles that transclude to the specified tiddler
* /
exports . getTiddlerBacktranscludes = function ( targetTitle ) {
var self = this ,
backIndexer = this . getIndexer ( "BackIndexer" ) ,
backtranscludes = backIndexer && backIndexer . subIndexers . transclude . lookup ( targetTitle ) ;
if ( ! backtranscludes ) {
backtranscludes = [ ] ;
}
return backtranscludes ;
} ;
2013-03-19 10:14:44 +00:00
/ *
Return a hashmap of tiddler titles that are referenced but not defined . Each value is the number of times the missing tiddler is referenced
* /
2012-04-30 11:23:03 +00:00
exports . getMissingTitles = function ( ) {
2013-03-19 10:14:44 +00:00
var self = this ,
missing = [ ] ;
// We should cache the missing tiddler list, even if we recreate it every time any tiddler is modified
this . forEachTiddler ( function ( title , tiddler ) {
var links = self . getTiddlerLinks ( title ) ;
$tw . utils . each ( links , function ( link ) {
2013-05-31 16:53:11 +00:00
if ( ( ! self . tiddlerExists ( link ) && ! self . isShadowTiddler ( link ) ) && missing . indexOf ( link ) === - 1 ) {
2013-03-19 10:14:44 +00:00
missing . push ( link ) ;
}
} ) ;
} ) ;
return missing ;
2012-04-30 11:23:03 +00:00
} ;
exports . getOrphanTitles = function ( ) {
2013-03-19 10:14:44 +00:00
var self = this ,
orphans = this . getTiddlers ( ) ;
this . forEachTiddler ( function ( title , tiddler ) {
var links = self . getTiddlerLinks ( title ) ;
$tw . utils . each ( links , function ( link ) {
var p = orphans . indexOf ( link ) ;
if ( p !== - 1 ) {
orphans . splice ( p , 1 ) ;
}
} ) ;
} ) ;
return orphans ; // Todo
2012-04-30 11:23:03 +00:00
} ;
2012-06-13 08:10:03 +00:00
/ *
Retrieves a list of the tiddler titles that are tagged with a given tag
* /
exports . getTiddlersWithTag = function ( tag ) {
2019-05-24 20:07:37 +00:00
// Try to use the indexer
var self = this ,
tagIndexer = this . getIndexer ( "TagIndexer" ) ,
2019-07-28 15:39:34 +00:00
results = tagIndexer && tagIndexer . subIndexers [ 3 ] . lookup ( tag ) ;
2019-05-24 20:07:37 +00:00
if ( ! results ) {
// If not available, perform a manual scan
results = this . getGlobalCache ( "taglist-" + tag , function ( ) {
var tagmap = self . getTagMap ( ) ;
return self . sortByList ( tagmap [ tag ] , tag ) ;
} ) ;
}
return results ;
2013-11-13 23:33:32 +00:00
} ;
/ *
Get a hashmap by tag of arrays of tiddler titles
* /
exports . getTagMap = function ( ) {
var self = this ;
return this . getGlobalCache ( "tagmap" , function ( ) {
2014-04-06 21:43:10 +00:00
var tags = Object . create ( null ) ,
2014-03-16 21:23:10 +00:00
storeTags = function ( tagArray , title ) {
2014-03-10 19:41:38 +00:00
if ( tagArray ) {
for ( var index = 0 ; index < tagArray . length ; index ++ ) {
var tag = tagArray [ index ] ;
if ( $tw . utils . hop ( tags , tag ) ) {
2014-08-30 19:44:26 +00:00
tags [ tag ] . push ( title ) ;
2014-03-10 19:41:38 +00:00
} else {
tags [ tag ] = [ title ] ;
}
2013-11-13 23:33:32 +00:00
}
}
2014-03-10 19:41:38 +00:00
} ,
title , tiddler ;
// Collect up all the tags
2014-03-17 10:50:18 +00:00
self . eachShadow ( function ( tiddler , title ) {
2014-03-16 21:23:10 +00:00
if ( ! self . tiddlerExists ( title ) ) {
2014-03-17 10:50:18 +00:00
tiddler = self . getTiddler ( title ) ;
2014-03-16 21:23:10 +00:00
storeTags ( tiddler . fields . tags , title ) ;
2013-11-13 23:33:32 +00:00
}
2014-03-17 10:50:18 +00:00
} ) ;
2014-03-16 21:23:10 +00:00
self . each ( function ( tiddler , title ) {
storeTags ( tiddler . fields . tags , title ) ;
} ) ;
2013-11-13 23:33:32 +00:00
return tags ;
} ) ;
2013-08-08 16:39:34 +00:00
} ;
2013-08-16 08:31:05 +00:00
/ *
2014-06-13 09:58:19 +00:00
Lookup a given tiddler and return a list of all the tiddlers that include it in the specified list field
2013-08-16 08:31:05 +00:00
* /
2014-06-13 09:58:19 +00:00
exports . findListingsOfTiddler = function ( targetTitle , fieldName ) {
fieldName = fieldName || "list" ;
2022-02-21 15:07:30 +00:00
var wiki = this ;
var listings = this . getGlobalCache ( "listings-" + fieldName , function ( ) {
var listings = Object . create ( null ) ;
wiki . each ( function ( tiddler , title ) {
var list = $tw . utils . parseStringArray ( tiddler . fields [ fieldName ] ) ;
if ( list ) {
for ( var i = 0 ; i < list . length ; i ++ ) {
var listItem = list [ i ] ,
listing = listings [ listItem ] || [ ] ;
if ( listing . indexOf ( title ) === - 1 ) {
listing . push ( title ) ;
}
listings [ listItem ] = listing ;
}
}
} ) ;
return listings ;
2014-03-16 21:23:10 +00:00
} ) ;
2022-02-21 15:07:30 +00:00
return listings [ targetTitle ] || [ ] ;
2013-08-16 08:31:05 +00:00
} ;
2013-08-08 16:39:34 +00:00
/ *
Sorts an array of tiddler titles according to an ordered list
* /
exports . sortByList = function ( array , listTitle ) {
2018-11-20 11:50:12 +00:00
var self = this ,
replacedTitles = Object . create ( null ) ;
2020-04-14 16:49:10 +00:00
// Given a title, this function will place it in the correct location
// within titles.
function moveItemInList ( title ) {
2018-11-20 11:50:12 +00:00
if ( ! $tw . utils . hop ( replacedTitles , title ) ) {
replacedTitles [ title ] = true ;
var newPos = - 1 ,
tiddler = self . getTiddler ( title ) ;
if ( tiddler ) {
var beforeTitle = tiddler . fields [ "list-before" ] ,
afterTitle = tiddler . fields [ "list-after" ] ;
if ( beforeTitle === "" ) {
newPos = 0 ;
} else if ( afterTitle === "" ) {
newPos = titles . length ;
} else if ( beforeTitle ) {
2020-04-14 16:49:10 +00:00
// if this title is placed relative
// to another title, make sure that
// title is placed before we place
// this one.
moveItemInList ( beforeTitle ) ;
2018-11-20 11:50:12 +00:00
newPos = titles . indexOf ( beforeTitle ) ;
} else if ( afterTitle ) {
2020-04-14 16:49:10 +00:00
// Same deal
moveItemInList ( afterTitle ) ;
2018-11-20 11:50:12 +00:00
newPos = titles . indexOf ( afterTitle ) ;
if ( newPos >= 0 ) {
++ newPos ;
}
}
2020-04-14 16:49:10 +00:00
// If a new position is specified, let's move it
if ( newPos !== - 1 ) {
// get its current Pos, and make sure
// sure that it's _actually_ in the list
// and that it would _actually_ move
// (#4275) We don't bother calling
// indexOf unless we have a new
// position to work with
var currPos = titles . indexOf ( title ) ;
if ( currPos >= 0 && newPos !== currPos ) {
// move it!
titles . splice ( currPos , 1 ) ;
if ( newPos >= currPos ) {
newPos -- ;
}
titles . splice ( newPos , 0 , title ) ;
2018-11-20 11:50:12 +00:00
}
}
}
}
}
2013-08-08 16:39:34 +00:00
var list = this . getTiddlerList ( listTitle ) ;
2013-11-13 23:33:32 +00:00
if ( ! array || array . length === 0 ) {
return [ ] ;
2014-04-14 20:30:30 +00:00
} else {
2015-03-11 19:17:14 +00:00
var titles = [ ] , t , title ;
2013-08-08 16:39:34 +00:00
// First place any entries that are present in the list
for ( t = 0 ; t < list . length ; t ++ ) {
title = list [ t ] ;
if ( array . indexOf ( title ) !== - 1 ) {
titles . push ( title ) ;
}
}
2015-03-11 19:17:14 +00:00
// Then place any remaining entries
2013-08-08 16:39:34 +00:00
for ( t = 0 ; t < array . length ; t ++ ) {
title = array [ t ] ;
if ( list . indexOf ( title ) === - 1 ) {
2015-03-11 19:17:14 +00:00
titles . push ( title ) ;
2014-04-14 20:30:30 +00:00
}
}
// Finally obey the list-before and list-after fields of each tiddler in turn
2018-11-20 11:50:12 +00:00
var sortedTitles = titles . slice ( 0 ) ;
2018-10-07 11:15:33 +00:00
for ( t = 0 ; t < sortedTitles . length ; t ++ ) {
title = sortedTitles [ t ] ;
2020-04-14 16:49:10 +00:00
moveItemInList ( title ) ;
2013-08-08 16:39:34 +00:00
}
return titles ;
}
2012-06-13 08:10:03 +00:00
} ;
2014-07-17 17:41:20 +00:00
exports . getSubTiddler = function ( title , subTiddlerTitle ) {
2015-07-10 15:43:50 +00:00
var bundleInfo = this . getPluginInfo ( title ) || this . getTiddlerDataCached ( title ) ;
2014-09-26 20:33:47 +00:00
if ( bundleInfo && bundleInfo . tiddlers ) {
2014-07-17 17:41:20 +00:00
var subTiddler = bundleInfo . tiddlers [ subTiddlerTitle ] ;
if ( subTiddler ) {
return new $tw . Tiddler ( subTiddler ) ;
}
}
return null ;
} ;
2013-04-08 17:47:46 +00:00
/ *
Retrieve a tiddler as a JSON string of the fields
* /
exports . getTiddlerAsJson = function ( title ) {
var tiddler = this . getTiddler ( title ) ;
if ( tiddler ) {
2014-04-06 21:43:10 +00:00
var fields = Object . create ( null ) ;
2013-04-08 17:47:46 +00:00
$tw . utils . each ( tiddler . fields , function ( value , name ) {
fields [ name ] = tiddler . getFieldString ( name ) ;
} ) ;
return JSON . stringify ( fields ) ;
} else {
return JSON . stringify ( { title : title } ) ;
}
} ;
2020-02-04 13:57:24 +00:00
exports . getTiddlersAsJson = function ( filter , spaces ) {
2018-10-01 10:27:45 +00:00
var tiddlers = this . filterTiddlers ( filter ) ,
2020-02-04 13:57:24 +00:00
spaces = ( spaces === undefined ) ? $tw . config . preferences . jsonSpaces : spaces ,
2018-10-01 10:27:45 +00:00
data = [ ] ;
for ( var t = 0 ; t < tiddlers . length ; t ++ ) {
var tiddler = this . getTiddler ( tiddlers [ t ] ) ;
if ( tiddler ) {
var fields = new Object ( ) ;
for ( var field in tiddler . fields ) {
fields [ field ] = tiddler . getFieldString ( field ) ;
}
data . push ( fields ) ;
}
}
2020-02-04 13:57:24 +00:00
return JSON . stringify ( data , null , spaces ) ;
2018-10-01 10:27:45 +00:00
} ;
2012-07-22 21:03:06 +00:00
/ *
2014-07-17 17:41:20 +00:00
Get the content of a tiddler as a JavaScript object . How this is done depends on the type of the tiddler :
2012-07-22 21:03:06 +00:00
application / json : the tiddler JSON is parsed into an object
2013-01-15 17:50:47 +00:00
application / x - tiddler - dictionary : the tiddler is parsed as sequence of name : value pairs
2012-07-22 21:03:06 +00:00
Other types currently just return null .
2014-07-17 17:41:20 +00:00
titleOrTiddler : string tiddler title or a tiddler object
defaultData : default data to be returned if the tiddler is missing or doesn ' t contain data
2015-07-10 15:43:50 +00:00
Note that the same value is returned for repeated calls for the same tiddler data . The value is frozen to prevent modification ; otherwise modifications would be visible to all callers
* /
exports . getTiddlerDataCached = function ( titleOrTiddler , defaultData ) {
var self = this ,
tiddler = titleOrTiddler ;
if ( ! ( tiddler instanceof $tw . Tiddler ) ) {
2021-05-30 18:20:17 +00:00
tiddler = this . getTiddler ( tiddler ) ;
2015-07-10 15:43:50 +00:00
}
if ( tiddler ) {
return this . getCacheForTiddler ( tiddler . fields . title , "data" , function ( ) {
// Return the frozen value
2017-02-27 22:39:37 +00:00
var value = self . getTiddlerData ( tiddler . fields . title , undefined ) ;
2015-07-10 15:43:50 +00:00
$tw . utils . deepFreeze ( value ) ;
return value ;
2017-02-27 22:39:37 +00:00
} ) || defaultData ;
2015-07-10 15:43:50 +00:00
} else {
return defaultData ;
}
} ;
/ *
Alternative , uncached version of getTiddlerDataCached ( ) . The return value can be mutated freely and reused
2012-07-22 21:03:06 +00:00
* /
2014-07-17 17:41:20 +00:00
exports . getTiddlerData = function ( titleOrTiddler , defaultData ) {
var tiddler = titleOrTiddler ,
2012-07-22 21:03:06 +00:00
data ;
2014-07-17 17:41:20 +00:00
if ( ! ( tiddler instanceof $tw . Tiddler ) ) {
2021-05-30 18:20:17 +00:00
tiddler = this . getTiddler ( tiddler ) ;
2014-07-17 17:41:20 +00:00
}
2013-01-15 17:50:47 +00:00
if ( tiddler && tiddler . fields . text ) {
2015-07-10 15:43:50 +00:00
switch ( tiddler . fields . type ) {
case "application/json" :
// JSON tiddler
2022-02-21 15:29:25 +00:00
return $tw . utils . parseJSONSafe ( tiddler . fields . text , defaultData ) ;
2015-07-10 15:43:50 +00:00
case "application/x-tiddler-dictionary" :
return $tw . utils . parseFields ( tiddler . fields . text ) ;
}
2012-07-22 21:03:06 +00:00
}
return defaultData ;
} ;
2013-01-15 17:50:47 +00:00
/ *
Extract an indexed field from within a data tiddler
* /
2014-07-17 17:41:20 +00:00
exports . extractTiddlerDataItem = function ( titleOrTiddler , index , defaultText ) {
2017-02-27 22:40:09 +00:00
var data = this . getTiddlerDataCached ( titleOrTiddler , Object . create ( null ) ) ,
2013-01-15 17:50:47 +00:00
text ;
if ( data && $tw . utils . hop ( data , index ) ) {
text = data [ index ] ;
}
if ( typeof text === "string" || typeof text === "number" ) {
return text . toString ( ) ;
} else {
return defaultText ;
}
} ;
2012-07-22 21:03:06 +00:00
/ *
Set a tiddlers content to a JavaScript object . Currently this is done by setting the tiddler ' s type to "application/json" and setting the text to the JSON text of the data .
2014-02-13 17:59:33 +00:00
title : title of tiddler
data : object that can be serialised to JSON
fields : optional hashmap of additional tiddler fields to be set
2021-10-24 19:19:42 +00:00
options : optional hashmap of options including :
suppressTimestamp : if true , don ' t set the creation / modification timestamps
2012-07-22 21:03:06 +00:00
* /
2021-10-24 19:19:42 +00:00
exports . setTiddlerData = function ( title , data , fields , options ) {
options = options || { } ;
2014-02-23 12:33:00 +00:00
var existingTiddler = this . getTiddler ( title ) ,
2021-10-24 19:19:42 +00:00
creationFields = options . suppressTimestamp ? { } : this . getCreationFields ( ) ,
modificationFields = options . suppressTimestamp ? { } : this . getModificationFields ( ) ,
2014-02-23 12:33:00 +00:00
newFields = {
title : title
2021-10-24 19:19:42 +00:00
} ;
2014-02-23 12:33:00 +00:00
if ( existingTiddler && existingTiddler . fields . type === "application/x-tiddler-dictionary" ) {
newFields . text = $tw . utils . makeTiddlerDictionary ( data ) ;
} else {
newFields . type = "application/json" ;
newFields . text = JSON . stringify ( data , null , $tw . config . preferences . jsonSpaces ) ;
}
2021-10-24 19:19:42 +00:00
this . addTiddler ( new $tw . Tiddler ( creationFields , existingTiddler , fields , newFields , modificationFields ) ) ;
2012-07-22 21:03:06 +00:00
} ;
2012-10-25 13:57:33 +00:00
/ *
Return the content of a tiddler as an array containing each line
* /
2014-01-25 18:14:30 +00:00
exports . getTiddlerList = function ( title , field , index ) {
if ( index ) {
return $tw . utils . parseStringArray ( this . extractTiddlerDataItem ( title , index , "" ) ) ;
}
field = field || "list" ;
2012-10-25 13:57:33 +00:00
var tiddler = this . getTiddler ( title ) ;
2014-01-25 18:14:30 +00:00
if ( tiddler ) {
return ( $tw . utils . parseStringArray ( tiddler . fields [ field ] ) || [ ] ) . slice ( 0 ) ;
2012-10-25 13:57:33 +00:00
}
return [ ] ;
} ;
2013-11-13 23:33:32 +00:00
// Return a named global cache object. Global cache objects are cleared whenever a tiddler change occurs
exports . getGlobalCache = function ( cacheName , initializer ) {
2014-04-06 21:43:10 +00:00
this . globalCache = this . globalCache || Object . create ( null ) ;
2013-11-13 23:33:32 +00:00
if ( $tw . utils . hop ( this . globalCache , cacheName ) ) {
return this . globalCache [ cacheName ] ;
} else {
this . globalCache [ cacheName ] = initializer ( ) ;
return this . globalCache [ cacheName ] ;
}
} ;
exports . clearGlobalCache = function ( ) {
2014-04-06 21:43:10 +00:00
this . globalCache = Object . create ( null ) ;
2014-08-30 19:44:26 +00:00
} ;
2013-11-13 23:33:32 +00:00
2012-04-30 11:23:03 +00:00
// Return the named cache object for a tiddler. If the cache doesn't exist then the initializer function is invoked to create it
exports . getCacheForTiddler = function ( title , cacheName , initializer ) {
2015-07-05 16:48:18 +00:00
this . caches = this . caches || Object . create ( null ) ;
var caches = this . caches [ title ] ;
2021-09-10 20:17:35 +00:00
if ( caches && caches [ cacheName ] !== undefined ) {
2015-07-05 16:48:18 +00:00
return caches [ cacheName ] ;
} else {
if ( ! caches ) {
caches = Object . create ( null ) ;
this . caches [ title ] = caches ;
}
caches [ cacheName ] = initializer ( ) ;
return caches [ cacheName ] ;
}
2012-04-30 11:23:03 +00:00
} ;
2015-08-02 21:22:33 +00:00
// Clear all caches associated with a particular tiddler, or, if the title is null, clear all the caches for all the tiddlers
2012-04-30 11:23:03 +00:00
exports . clearCache = function ( title ) {
2015-08-02 21:22:33 +00:00
if ( title ) {
this . caches = this . caches || Object . create ( null ) ;
if ( $tw . utils . hop ( this . caches , title ) ) {
delete this . caches [ title ] ;
}
} else {
this . caches = Object . create ( null ) ;
2012-04-30 11:23:03 +00:00
}
} ;
2012-12-28 22:08:32 +00:00
exports . initParsers = function ( moduleType ) {
2013-04-25 16:40:12 +00:00
// Install the parser modules
$tw . Wiki . parsers = { } ;
2012-12-28 22:08:32 +00:00
var self = this ;
2013-01-16 13:56:11 +00:00
$tw . modules . forEachModuleOfType ( "parser" , function ( title , module ) {
2012-12-28 22:08:32 +00:00
for ( var f in module ) {
if ( $tw . utils . hop ( module , f ) ) {
2013-04-25 16:40:12 +00:00
$tw . Wiki . parsers [ f ] = module [ f ] ; // Store the parser class
2012-12-28 22:08:32 +00:00
}
}
} ) ;
2018-05-18 16:53:07 +00:00
// Use the generic binary parser for any binary types not registered so far
if ( $tw . Wiki . parsers [ "application/octet-stream" ] ) {
Object . keys ( $tw . config . contentTypeInfo ) . forEach ( function ( type ) {
if ( ! $tw . utils . hop ( $tw . Wiki . parsers , type ) && $tw . config . contentTypeInfo [ type ] . encoding === "base64" ) {
$tw . Wiki . parsers [ type ] = $tw . Wiki . parsers [ "application/octet-stream" ] ;
}
2021-05-30 18:20:17 +00:00
} ) ;
2018-05-18 16:53:07 +00:00
}
2012-12-28 22:08:32 +00:00
} ;
2012-12-13 21:34:31 +00:00
/ *
Parse a block of text of a specified MIME type
2012-12-20 15:07:38 +00:00
type : content type of text to be parsed
text : text
options : see below
Options include :
parseAsInline : if true , the text of the tiddler will be parsed as an inline run
2014-06-12 07:36:30 +00:00
_canonical _uri : optional string of the canonical URI of this content
2012-12-13 21:34:31 +00:00
* /
2015-07-05 16:48:18 +00:00
exports . parseText = function ( type , text , options ) {
2016-05-02 09:12:08 +00:00
text = text || "" ;
2012-12-26 22:02:59 +00:00
options = options || { } ;
2012-12-27 17:08:29 +00:00
// Select a parser
2013-04-25 16:40:12 +00:00
var Parser = $tw . Wiki . parsers [ type ] ;
2015-02-01 18:33:40 +00:00
if ( ! Parser && $tw . utils . getFileExtensionInfo ( type ) ) {
Parser = $tw . Wiki . parsers [ $tw . utils . getFileExtensionInfo ( type ) . type ] ;
2012-12-27 17:08:29 +00:00
}
if ( ! Parser ) {
2013-04-25 16:40:12 +00:00
Parser = $tw . Wiki . parsers [ options . defaultType || "text/vnd.tiddlywiki" ] ;
2012-12-27 17:08:29 +00:00
}
if ( ! Parser ) {
return null ;
}
// Return the parser instance
return new Parser ( type , text , {
2012-12-26 22:02:59 +00:00
parseAsInline : options . parseAsInline ,
2014-06-12 07:36:30 +00:00
wiki : this ,
Parameterised transclusions (#6666)
* Initial commit
Everything is draft.
* Fix test execution
* Fix and test missing target handling
* Use the ubertransclude widget for the wikitext transclusion syntax
* Changed transclude widget in binary parser to ubertransclude
* Add a test for custom action widgets
* Don't worry about ordered attributes
The changes in 0bffae21088aafc0cdebafe6a5de7907d7c52a3a mean that we don't need to explicitly maintain the ordered attributes
* Remove need to explicitly clear widget mapping variable when invoking overridden widget
* Use ts- prefix for system slot names
* Add a definition for the value widget just so that it doesn't cause errors
Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget...
* Add support for positional parameters
* Ubertransclusion positional parameters should be based on name, not position
* Add support for shortcut syntax for positional transclusion parameters
* Importvariables should skip parameters widgets
* Refactor transclude widget before uberfying it
* Refactor ubertransclude functionality into transclude widget
* Replace ubertransclude widget with transclude widget
* Add wikitext shortcut for new-style function definitions
* Allow brackets to be omitted for function definitions with no parameters
* Add pragma rule for parameters declarations
* Remove erroneous "tag" property
* Add support for accessing function parameters as name/value pairs
* Be as permissive as possible with parameter names
Previously restricted to upper and lower case, digits and dash and underscore
* Rewrite some tests to use the shortcut syntaxes
* Mustn't allow commas in parameter names
* Fix crash when transcluding an undefined variable
Thanks @pmario
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359
* Unquoted parameters should not eat a succeeding comma
Fixes #6672
* Remove extraneous code
* Allow the let widget to create variables starting with $
* Fix addAttributeToParseTreeNode handling of ordered attributes
* Reuse attribute objects when executing custom widgets
* Fix importing of function definitions
* Fix parameter handling
* Introduce genesis widget for dynamically creating widgets
See the "RedefineLet" test for a contrived example of usage
* Change tiddler separator used in wikitext tests
Underscore looked ambiguous; I kept typing dashes by accident
* Cache parse trees when transcluding variables
* Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray
I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code.
* Fixes to enable the transclude widget itself to be overridden
There are two big changes here:
Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped.
We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
* Fix genesis widget example
* Use enlist:raw to preserve duplicates
* Don't create variables with value undefined for missing parameters
* Fix variable retrieval bug with test harness
* Improve recursion detection
While retaining backwards compatibility
* Experimental support for custom filter operators
Just as we can define custom widgets we can also define custom parameterised filter operators
* Add visible transclusions component and demo
Very useful to see transclusions explicitly
Makes a good demo of a super-complicated widget override.
* Genesis widget should pass raw attributes onto child widget...
...so that it can more efficiently handle refreshing itself.
* Use consistent parse tree node property for params
* Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax
* Clarify that the recent changes allow functions to be invoked with the double bracket syntax
In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately
* Make the macrocall widget delegate to the transclude widget
* Switch to using \procedure to define new-style macros, and \function for custom filter operator functions
I now need to update the OP!
* Fix visible transclusion example
* Remove obsolete code
Left over after refactoring
* Better backwards compatibility for legacy recursion marker
Fixes the problem with tag dropdowns @btheado
* Fix stringifying/parsing string arrays containing newlines
A very old bug.
Fixes the ActionListOpsWidget problem @btheado
* Transclude: replace paramNames/paramValues with more robust JSON payload
More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153
* Rename internal "unknown" filter operator so that users cannot invoke it
* Detect recursion by tracking widget tree depth
The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid
* Use \widget for custom widget definitions, and remove need for angle brackets
Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property
* Rename <$value> widget to <$fill>
* Require $$ for custom widgets, and that overridden JS widgets must exist
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763
* Fix invocation of JS macros
* Experimental update of the parse-tree preview visualisation
An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget.
As usual with these experiments, this one is going to require quite a lot more work to finish up:
* The formatting is via direct styles rather than classes
* The formatting for attributes and properties is not yet completed
* The same thing needs to also be done to the widget tree preview
* Procedures and widgets inherit whitespace trim setting from their definition
* Missed off 22e7ec23811b137a119295b5ce72bccdc18a697a
* Require period prefix for custom filter operator functions
To ensure that custom filter operators cannot clash with future core operators.
* Allow custom functions to be invoked as attributes
* WIP
* Remove unneeded test tiddler
* Make is[variable] and variables[] operators resilient to fake widgets
* Fix importvariables to work with setvariables as well as set (they are aliases)
* Add support for $:/tags/Global
* Remove accidental commit 6cc99fcbe33da47cfcb1335d0742b16ea1b9ce03
* Add utility function for parsing macro parameter definitions
* Introduce true global variables
The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists.
This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro.
In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro
* Convert the tabs macro into a global
So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR.
* Change to `?` for conditional definitions
* Fix tabs global so it doesn't crash when viewed directly
* Test showing how to un-override a core widget
* Cleaning up after f63634900724eda937286d946b2e6f65fcf6d503
* Minor cleanups
* Clean up unknown filter
* Introduce function operator for calling functions
Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter).
* Use underscores for new system fields for global variable tiddlers
For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code.
* Refactor $parameters widget
The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself.
* Fix refreshing of global variables
Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions.
* Remove support for $:/tags/Global
It is not needed now that we have true global variables
* Typo from f513b403fe911442bcbaf0628fa47d3d2ed3cf93
* Make slot fill data available to transclusions
Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion
* Mark docs as v5.3.0
* Simplify metaparameters implementation
* Fix typo
* Adjust naming of transclusion metaparameter
* Fix up handling of slot/fill for custom widgets
Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget
* Add format:json operator
I've been finding this useful for debugging, and it kind of goes with the JSON operators
* Docs: JSON operators and tweaks to genesis widget
* Docs: format:json
Also tweak to the behaviour of format:json if the input string is not valid JSON
* Fix #6721
* Revert "Fix #6721"
This reverts commit b216579255d6e6214f3cf71ab771fcc57240aa74 which was committed to the wrong branch
* Fix new selection tracker to return relative coordinates
* Make use of type attribute consistent
* Docs: Transclude widget
* Simplify the fill widget
We can rely on the default processing in the base class
* Slot widget: be more defensive about negative depth values
* Parameters widget: Be defensive about negative depths
* Protect against excessively recursive functions
* FIx transcluding of functions
This first implementation concatenates the results of the filter (with no separator) and then wikifies the result.
The test in this commit is quite interesting...
* Tweak semantics of JSON operators to match #6932
This allows us to later bring in the optimisations without breaking backwards compatibility.
* Revert obsolete changes to boot.js
* Fix inadvertent whitespace change
* Remove tests related to obsolete changes to boot.js
Should have been part of 2f494ba15246edd356bfc591b0115d30592e7eb8
* Revert changes to parse tree preview
This implementation requires #6666
* Add test to show that global widgets need not use the _parameters field
* Disable overriding core widgets in safe mode
* Coding style tweak
* More comments
* Fix caching of parse variables/macros/procedures
* Transcluded functions should operate on the entire store
* Refactor filter recursion detection to avoid an unneeded wrapper function
* Fix error in 25312b3e3218c1002c483a1fc995d2b65509b993
* WIP
* Revert "WIP"
This reverts commit 8654dfc679ea12d30ffd8b14a30165d826be06b7.
* When transcluding functions, pass an empty item list to the filter, and just return the first item
* Rejig genesis widget to be easier to use
* Parameters widget: protect against negative $depth
* Docs updates
* Docs updates
* Tweak comments
* Add custom view template body for globals, and a new sidebar tab under "more"
And also a custom view template title that greys out the $:/global/ part of the title
* Update function operator to return the input list if the function is missing
* Remove negation from function operator
This implementation was not useful.
* Tests and docs for function operator
* Docs tweaks
* Improve indentation
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251
* Missing tests for parameters widget
* Fix visible transclude
* Docs update
* Docs typo
* Huge Documentation Update
Not quite finished, but definitely on the home stretch
* Slight optimisation to user defined widgets
* Remove implementation of $:/globals/
Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created.
* Docs clarification
* Docs update
* Some widget.js cleanups
* Remove support for conditional definitions
It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c887ba849958c8980623d8ff45bb8a36
* Docs updates
* Revert change to setwidget docs
* Docs update
* Docs updates
* Clarify/simplify some tests
* More docs updates
* Fix doc file locations
* Docs updates
* Revert modified date of docs that have only had minor tweaks
* Docs typo
https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220
Thanks @btheado
* Transcluding functions: fix missing parameters passed as undefined
Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372
* Parameter parenthesis should be mandatory in function/procedure/widget definitions
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387
* Attempt to build this branch with CI
* Add release note etc for 5.3.0
* Temporary new release banner for v5.3.0
* New New Release Banner
* New test for undefined parameters
* Adjust modified times of docs tiddlers to make them easier to find
* Update release note
* Add parenthesis to the visible transclusion definition
Parenthesis were made mandatory in 5194b24108efda6da95daf4261ffd80473073a65
Fixes #6998
* Fix macrocall refresh issue
It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable
Fixes #7001
* Filter run prefixes should use widget.makeFakeWidgetWithVariables
* Docs typo
Thanks @twMat
* Docs: clarify function operator invocation
See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599
* Docs: Update \define pragma to cover named ends
* Docs: move tiddlers to correct directory
* Add support for named end markers for procedures, functions and widgets
* Docs note about nested macro definitions
* Rename test
* Fix detection of empty transclusions
See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston
* New test missed off a45349cc996390192114fed486bfa6900da641d7
* Refactor wikified function tests
* Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text
* Refactor the function operator and unknown operator to use the new helper
* Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated
* Refactor the transclude widget to use the new helper
* Update tests
* Fix positional parameters in widget.evaluateVariable()
This should clear up the remaining anomalies in #7009, let me know how you get on @btheado
* Remove 5.2.8 release note
* Fix nonstandard initialisation code for fill/parameter/slot widgets
* Update modification times of doc tiddlers
So that they are at the top of the recent tab
* Update 5.3.0 release note
* Remove custom CI step for this branch
* Restore standard sitetitle
2023-04-19 10:55:25 +00:00
_canonical _uri : options . _canonical _uri ,
configTrimWhiteSpace : options . configTrimWhiteSpace
2012-12-26 22:02:59 +00:00
} ) ;
2012-12-13 21:34:31 +00:00
} ;
/ *
Parse a tiddler according to its MIME type
* /
2015-07-05 16:48:18 +00:00
exports . parseTiddler = function ( title , options ) {
2014-06-12 07:36:30 +00:00
options = $tw . utils . extend ( { } , options ) ;
2015-07-08 07:26:19 +00:00
var cacheType = options . parseAsInline ? "inlineParseTree" : "blockParseTree" ,
2012-12-20 15:07:38 +00:00
tiddler = this . getTiddler ( title ) ,
2012-12-13 21:34:31 +00:00
self = this ;
2012-12-20 15:07:38 +00:00
return tiddler ? this . getCacheForTiddler ( title , cacheType , function ( ) {
2014-06-12 07:36:30 +00:00
if ( tiddler . hasField ( "_canonical_uri" ) ) {
options . _canonical _uri = tiddler . fields . _canonical _uri ;
}
2015-07-05 16:48:18 +00:00
return self . parseText ( tiddler . fields . type , tiddler . fields . text , options ) ;
2012-12-13 21:34:31 +00:00
} ) : null ;
} ;
2013-11-08 08:51:14 +00:00
exports . parseTextReference = function ( title , field , index , options ) {
2021-06-02 20:45:06 +00:00
var tiddler ,
text ,
parserInfo ;
if ( ! options . subTiddler ) {
2014-07-17 17:41:20 +00:00
tiddler = this . getTiddler ( title ) ;
if ( field === "text" || ( ! field && ! index ) ) {
this . getTiddlerText ( title ) ; // Force the tiddler to be lazily loaded
return this . parseTiddler ( title , options ) ;
}
2021-06-02 20:45:06 +00:00
}
parserInfo = this . getTextReferenceParserInfo ( title , field , index , options ) ;
if ( parserInfo . sourceText !== null ) {
return this . parseText ( parserInfo . parserType , parserInfo . sourceText , options ) ;
} else {
return null ;
}
} ;
exports . getTextReferenceParserInfo = function ( title , field , index , options ) {
Parameterised transclusions (#6666)
* Initial commit
Everything is draft.
* Fix test execution
* Fix and test missing target handling
* Use the ubertransclude widget for the wikitext transclusion syntax
* Changed transclude widget in binary parser to ubertransclude
* Add a test for custom action widgets
* Don't worry about ordered attributes
The changes in 0bffae21088aafc0cdebafe6a5de7907d7c52a3a mean that we don't need to explicitly maintain the ordered attributes
* Remove need to explicitly clear widget mapping variable when invoking overridden widget
* Use ts- prefix for system slot names
* Add a definition for the value widget just so that it doesn't cause errors
Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget...
* Add support for positional parameters
* Ubertransclusion positional parameters should be based on name, not position
* Add support for shortcut syntax for positional transclusion parameters
* Importvariables should skip parameters widgets
* Refactor transclude widget before uberfying it
* Refactor ubertransclude functionality into transclude widget
* Replace ubertransclude widget with transclude widget
* Add wikitext shortcut for new-style function definitions
* Allow brackets to be omitted for function definitions with no parameters
* Add pragma rule for parameters declarations
* Remove erroneous "tag" property
* Add support for accessing function parameters as name/value pairs
* Be as permissive as possible with parameter names
Previously restricted to upper and lower case, digits and dash and underscore
* Rewrite some tests to use the shortcut syntaxes
* Mustn't allow commas in parameter names
* Fix crash when transcluding an undefined variable
Thanks @pmario
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359
* Unquoted parameters should not eat a succeeding comma
Fixes #6672
* Remove extraneous code
* Allow the let widget to create variables starting with $
* Fix addAttributeToParseTreeNode handling of ordered attributes
* Reuse attribute objects when executing custom widgets
* Fix importing of function definitions
* Fix parameter handling
* Introduce genesis widget for dynamically creating widgets
See the "RedefineLet" test for a contrived example of usage
* Change tiddler separator used in wikitext tests
Underscore looked ambiguous; I kept typing dashes by accident
* Cache parse trees when transcluding variables
* Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray
I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code.
* Fixes to enable the transclude widget itself to be overridden
There are two big changes here:
Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped.
We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
* Fix genesis widget example
* Use enlist:raw to preserve duplicates
* Don't create variables with value undefined for missing parameters
* Fix variable retrieval bug with test harness
* Improve recursion detection
While retaining backwards compatibility
* Experimental support for custom filter operators
Just as we can define custom widgets we can also define custom parameterised filter operators
* Add visible transclusions component and demo
Very useful to see transclusions explicitly
Makes a good demo of a super-complicated widget override.
* Genesis widget should pass raw attributes onto child widget...
...so that it can more efficiently handle refreshing itself.
* Use consistent parse tree node property for params
* Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax
* Clarify that the recent changes allow functions to be invoked with the double bracket syntax
In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately
* Make the macrocall widget delegate to the transclude widget
* Switch to using \procedure to define new-style macros, and \function for custom filter operator functions
I now need to update the OP!
* Fix visible transclusion example
* Remove obsolete code
Left over after refactoring
* Better backwards compatibility for legacy recursion marker
Fixes the problem with tag dropdowns @btheado
* Fix stringifying/parsing string arrays containing newlines
A very old bug.
Fixes the ActionListOpsWidget problem @btheado
* Transclude: replace paramNames/paramValues with more robust JSON payload
More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153
* Rename internal "unknown" filter operator so that users cannot invoke it
* Detect recursion by tracking widget tree depth
The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid
* Use \widget for custom widget definitions, and remove need for angle brackets
Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property
* Rename <$value> widget to <$fill>
* Require $$ for custom widgets, and that overridden JS widgets must exist
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763
* Fix invocation of JS macros
* Experimental update of the parse-tree preview visualisation
An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget.
As usual with these experiments, this one is going to require quite a lot more work to finish up:
* The formatting is via direct styles rather than classes
* The formatting for attributes and properties is not yet completed
* The same thing needs to also be done to the widget tree preview
* Procedures and widgets inherit whitespace trim setting from their definition
* Missed off 22e7ec23811b137a119295b5ce72bccdc18a697a
* Require period prefix for custom filter operator functions
To ensure that custom filter operators cannot clash with future core operators.
* Allow custom functions to be invoked as attributes
* WIP
* Remove unneeded test tiddler
* Make is[variable] and variables[] operators resilient to fake widgets
* Fix importvariables to work with setvariables as well as set (they are aliases)
* Add support for $:/tags/Global
* Remove accidental commit 6cc99fcbe33da47cfcb1335d0742b16ea1b9ce03
* Add utility function for parsing macro parameter definitions
* Introduce true global variables
The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists.
This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro.
In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro
* Convert the tabs macro into a global
So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR.
* Change to `?` for conditional definitions
* Fix tabs global so it doesn't crash when viewed directly
* Test showing how to un-override a core widget
* Cleaning up after f63634900724eda937286d946b2e6f65fcf6d503
* Minor cleanups
* Clean up unknown filter
* Introduce function operator for calling functions
Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter).
* Use underscores for new system fields for global variable tiddlers
For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code.
* Refactor $parameters widget
The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself.
* Fix refreshing of global variables
Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions.
* Remove support for $:/tags/Global
It is not needed now that we have true global variables
* Typo from f513b403fe911442bcbaf0628fa47d3d2ed3cf93
* Make slot fill data available to transclusions
Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion
* Mark docs as v5.3.0
* Simplify metaparameters implementation
* Fix typo
* Adjust naming of transclusion metaparameter
* Fix up handling of slot/fill for custom widgets
Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget
* Add format:json operator
I've been finding this useful for debugging, and it kind of goes with the JSON operators
* Docs: JSON operators and tweaks to genesis widget
* Docs: format:json
Also tweak to the behaviour of format:json if the input string is not valid JSON
* Fix #6721
* Revert "Fix #6721"
This reverts commit b216579255d6e6214f3cf71ab771fcc57240aa74 which was committed to the wrong branch
* Fix new selection tracker to return relative coordinates
* Make use of type attribute consistent
* Docs: Transclude widget
* Simplify the fill widget
We can rely on the default processing in the base class
* Slot widget: be more defensive about negative depth values
* Parameters widget: Be defensive about negative depths
* Protect against excessively recursive functions
* FIx transcluding of functions
This first implementation concatenates the results of the filter (with no separator) and then wikifies the result.
The test in this commit is quite interesting...
* Tweak semantics of JSON operators to match #6932
This allows us to later bring in the optimisations without breaking backwards compatibility.
* Revert obsolete changes to boot.js
* Fix inadvertent whitespace change
* Remove tests related to obsolete changes to boot.js
Should have been part of 2f494ba15246edd356bfc591b0115d30592e7eb8
* Revert changes to parse tree preview
This implementation requires #6666
* Add test to show that global widgets need not use the _parameters field
* Disable overriding core widgets in safe mode
* Coding style tweak
* More comments
* Fix caching of parse variables/macros/procedures
* Transcluded functions should operate on the entire store
* Refactor filter recursion detection to avoid an unneeded wrapper function
* Fix error in 25312b3e3218c1002c483a1fc995d2b65509b993
* WIP
* Revert "WIP"
This reverts commit 8654dfc679ea12d30ffd8b14a30165d826be06b7.
* When transcluding functions, pass an empty item list to the filter, and just return the first item
* Rejig genesis widget to be easier to use
* Parameters widget: protect against negative $depth
* Docs updates
* Docs updates
* Tweak comments
* Add custom view template body for globals, and a new sidebar tab under "more"
And also a custom view template title that greys out the $:/global/ part of the title
* Update function operator to return the input list if the function is missing
* Remove negation from function operator
This implementation was not useful.
* Tests and docs for function operator
* Docs tweaks
* Improve indentation
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251
* Missing tests for parameters widget
* Fix visible transclude
* Docs update
* Docs typo
* Huge Documentation Update
Not quite finished, but definitely on the home stretch
* Slight optimisation to user defined widgets
* Remove implementation of $:/globals/
Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created.
* Docs clarification
* Docs update
* Some widget.js cleanups
* Remove support for conditional definitions
It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c887ba849958c8980623d8ff45bb8a36
* Docs updates
* Revert change to setwidget docs
* Docs update
* Docs updates
* Clarify/simplify some tests
* More docs updates
* Fix doc file locations
* Docs updates
* Revert modified date of docs that have only had minor tweaks
* Docs typo
https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220
Thanks @btheado
* Transcluding functions: fix missing parameters passed as undefined
Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372
* Parameter parenthesis should be mandatory in function/procedure/widget definitions
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387
* Attempt to build this branch with CI
* Add release note etc for 5.3.0
* Temporary new release banner for v5.3.0
* New New Release Banner
* New test for undefined parameters
* Adjust modified times of docs tiddlers to make them easier to find
* Update release note
* Add parenthesis to the visible transclusion definition
Parenthesis were made mandatory in 5194b24108efda6da95daf4261ffd80473073a65
Fixes #6998
* Fix macrocall refresh issue
It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable
Fixes #7001
* Filter run prefixes should use widget.makeFakeWidgetWithVariables
* Docs typo
Thanks @twMat
* Docs: clarify function operator invocation
See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599
* Docs: Update \define pragma to cover named ends
* Docs: move tiddlers to correct directory
* Add support for named end markers for procedures, functions and widgets
* Docs note about nested macro definitions
* Rename test
* Fix detection of empty transclusions
See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston
* New test missed off a45349cc996390192114fed486bfa6900da641d7
* Refactor wikified function tests
* Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text
* Refactor the function operator and unknown operator to use the new helper
* Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated
* Refactor the transclude widget to use the new helper
* Update tests
* Fix positional parameters in widget.evaluateVariable()
This should clear up the remaining anomalies in #7009, let me know how you get on @btheado
* Remove 5.2.8 release note
* Fix nonstandard initialisation code for fill/parameter/slot widgets
* Update modification times of doc tiddlers
So that they are at the top of the recent tab
* Update 5.3.0 release note
* Remove custom CI step for this branch
* Restore standard sitetitle
2023-04-19 10:55:25 +00:00
var defaultType = options . defaultType || "text/vnd.tiddlywiki" ,
tiddler ,
2021-06-02 20:45:06 +00:00
parserInfo = {
sourceText : null ,
Parameterised transclusions (#6666)
* Initial commit
Everything is draft.
* Fix test execution
* Fix and test missing target handling
* Use the ubertransclude widget for the wikitext transclusion syntax
* Changed transclude widget in binary parser to ubertransclude
* Add a test for custom action widgets
* Don't worry about ordered attributes
The changes in 0bffae21088aafc0cdebafe6a5de7907d7c52a3a mean that we don't need to explicitly maintain the ordered attributes
* Remove need to explicitly clear widget mapping variable when invoking overridden widget
* Use ts- prefix for system slot names
* Add a definition for the value widget just so that it doesn't cause errors
Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget...
* Add support for positional parameters
* Ubertransclusion positional parameters should be based on name, not position
* Add support for shortcut syntax for positional transclusion parameters
* Importvariables should skip parameters widgets
* Refactor transclude widget before uberfying it
* Refactor ubertransclude functionality into transclude widget
* Replace ubertransclude widget with transclude widget
* Add wikitext shortcut for new-style function definitions
* Allow brackets to be omitted for function definitions with no parameters
* Add pragma rule for parameters declarations
* Remove erroneous "tag" property
* Add support for accessing function parameters as name/value pairs
* Be as permissive as possible with parameter names
Previously restricted to upper and lower case, digits and dash and underscore
* Rewrite some tests to use the shortcut syntaxes
* Mustn't allow commas in parameter names
* Fix crash when transcluding an undefined variable
Thanks @pmario
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359
* Unquoted parameters should not eat a succeeding comma
Fixes #6672
* Remove extraneous code
* Allow the let widget to create variables starting with $
* Fix addAttributeToParseTreeNode handling of ordered attributes
* Reuse attribute objects when executing custom widgets
* Fix importing of function definitions
* Fix parameter handling
* Introduce genesis widget for dynamically creating widgets
See the "RedefineLet" test for a contrived example of usage
* Change tiddler separator used in wikitext tests
Underscore looked ambiguous; I kept typing dashes by accident
* Cache parse trees when transcluding variables
* Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray
I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code.
* Fixes to enable the transclude widget itself to be overridden
There are two big changes here:
Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped.
We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
* Fix genesis widget example
* Use enlist:raw to preserve duplicates
* Don't create variables with value undefined for missing parameters
* Fix variable retrieval bug with test harness
* Improve recursion detection
While retaining backwards compatibility
* Experimental support for custom filter operators
Just as we can define custom widgets we can also define custom parameterised filter operators
* Add visible transclusions component and demo
Very useful to see transclusions explicitly
Makes a good demo of a super-complicated widget override.
* Genesis widget should pass raw attributes onto child widget...
...so that it can more efficiently handle refreshing itself.
* Use consistent parse tree node property for params
* Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax
* Clarify that the recent changes allow functions to be invoked with the double bracket syntax
In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately
* Make the macrocall widget delegate to the transclude widget
* Switch to using \procedure to define new-style macros, and \function for custom filter operator functions
I now need to update the OP!
* Fix visible transclusion example
* Remove obsolete code
Left over after refactoring
* Better backwards compatibility for legacy recursion marker
Fixes the problem with tag dropdowns @btheado
* Fix stringifying/parsing string arrays containing newlines
A very old bug.
Fixes the ActionListOpsWidget problem @btheado
* Transclude: replace paramNames/paramValues with more robust JSON payload
More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153
* Rename internal "unknown" filter operator so that users cannot invoke it
* Detect recursion by tracking widget tree depth
The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid
* Use \widget for custom widget definitions, and remove need for angle brackets
Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property
* Rename <$value> widget to <$fill>
* Require $$ for custom widgets, and that overridden JS widgets must exist
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763
* Fix invocation of JS macros
* Experimental update of the parse-tree preview visualisation
An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget.
As usual with these experiments, this one is going to require quite a lot more work to finish up:
* The formatting is via direct styles rather than classes
* The formatting for attributes and properties is not yet completed
* The same thing needs to also be done to the widget tree preview
* Procedures and widgets inherit whitespace trim setting from their definition
* Missed off 22e7ec23811b137a119295b5ce72bccdc18a697a
* Require period prefix for custom filter operator functions
To ensure that custom filter operators cannot clash with future core operators.
* Allow custom functions to be invoked as attributes
* WIP
* Remove unneeded test tiddler
* Make is[variable] and variables[] operators resilient to fake widgets
* Fix importvariables to work with setvariables as well as set (they are aliases)
* Add support for $:/tags/Global
* Remove accidental commit 6cc99fcbe33da47cfcb1335d0742b16ea1b9ce03
* Add utility function for parsing macro parameter definitions
* Introduce true global variables
The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists.
This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro.
In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro
* Convert the tabs macro into a global
So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR.
* Change to `?` for conditional definitions
* Fix tabs global so it doesn't crash when viewed directly
* Test showing how to un-override a core widget
* Cleaning up after f63634900724eda937286d946b2e6f65fcf6d503
* Minor cleanups
* Clean up unknown filter
* Introduce function operator for calling functions
Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter).
* Use underscores for new system fields for global variable tiddlers
For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code.
* Refactor $parameters widget
The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself.
* Fix refreshing of global variables
Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions.
* Remove support for $:/tags/Global
It is not needed now that we have true global variables
* Typo from f513b403fe911442bcbaf0628fa47d3d2ed3cf93
* Make slot fill data available to transclusions
Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion
* Mark docs as v5.3.0
* Simplify metaparameters implementation
* Fix typo
* Adjust naming of transclusion metaparameter
* Fix up handling of slot/fill for custom widgets
Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget
* Add format:json operator
I've been finding this useful for debugging, and it kind of goes with the JSON operators
* Docs: JSON operators and tweaks to genesis widget
* Docs: format:json
Also tweak to the behaviour of format:json if the input string is not valid JSON
* Fix #6721
* Revert "Fix #6721"
This reverts commit b216579255d6e6214f3cf71ab771fcc57240aa74 which was committed to the wrong branch
* Fix new selection tracker to return relative coordinates
* Make use of type attribute consistent
* Docs: Transclude widget
* Simplify the fill widget
We can rely on the default processing in the base class
* Slot widget: be more defensive about negative depth values
* Parameters widget: Be defensive about negative depths
* Protect against excessively recursive functions
* FIx transcluding of functions
This first implementation concatenates the results of the filter (with no separator) and then wikifies the result.
The test in this commit is quite interesting...
* Tweak semantics of JSON operators to match #6932
This allows us to later bring in the optimisations without breaking backwards compatibility.
* Revert obsolete changes to boot.js
* Fix inadvertent whitespace change
* Remove tests related to obsolete changes to boot.js
Should have been part of 2f494ba15246edd356bfc591b0115d30592e7eb8
* Revert changes to parse tree preview
This implementation requires #6666
* Add test to show that global widgets need not use the _parameters field
* Disable overriding core widgets in safe mode
* Coding style tweak
* More comments
* Fix caching of parse variables/macros/procedures
* Transcluded functions should operate on the entire store
* Refactor filter recursion detection to avoid an unneeded wrapper function
* Fix error in 25312b3e3218c1002c483a1fc995d2b65509b993
* WIP
* Revert "WIP"
This reverts commit 8654dfc679ea12d30ffd8b14a30165d826be06b7.
* When transcluding functions, pass an empty item list to the filter, and just return the first item
* Rejig genesis widget to be easier to use
* Parameters widget: protect against negative $depth
* Docs updates
* Docs updates
* Tweak comments
* Add custom view template body for globals, and a new sidebar tab under "more"
And also a custom view template title that greys out the $:/global/ part of the title
* Update function operator to return the input list if the function is missing
* Remove negation from function operator
This implementation was not useful.
* Tests and docs for function operator
* Docs tweaks
* Improve indentation
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251
* Missing tests for parameters widget
* Fix visible transclude
* Docs update
* Docs typo
* Huge Documentation Update
Not quite finished, but definitely on the home stretch
* Slight optimisation to user defined widgets
* Remove implementation of $:/globals/
Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created.
* Docs clarification
* Docs update
* Some widget.js cleanups
* Remove support for conditional definitions
It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c887ba849958c8980623d8ff45bb8a36
* Docs updates
* Revert change to setwidget docs
* Docs update
* Docs updates
* Clarify/simplify some tests
* More docs updates
* Fix doc file locations
* Docs updates
* Revert modified date of docs that have only had minor tweaks
* Docs typo
https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220
Thanks @btheado
* Transcluding functions: fix missing parameters passed as undefined
Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372
* Parameter parenthesis should be mandatory in function/procedure/widget definitions
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387
* Attempt to build this branch with CI
* Add release note etc for 5.3.0
* Temporary new release banner for v5.3.0
* New New Release Banner
* New test for undefined parameters
* Adjust modified times of docs tiddlers to make them easier to find
* Update release note
* Add parenthesis to the visible transclusion definition
Parenthesis were made mandatory in 5194b24108efda6da95daf4261ffd80473073a65
Fixes #6998
* Fix macrocall refresh issue
It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable
Fixes #7001
* Filter run prefixes should use widget.makeFakeWidgetWithVariables
* Docs typo
Thanks @twMat
* Docs: clarify function operator invocation
See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599
* Docs: Update \define pragma to cover named ends
* Docs: move tiddlers to correct directory
* Add support for named end markers for procedures, functions and widgets
* Docs note about nested macro definitions
* Rename test
* Fix detection of empty transclusions
See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston
* New test missed off a45349cc996390192114fed486bfa6900da641d7
* Refactor wikified function tests
* Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text
* Refactor the function operator and unknown operator to use the new helper
* Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated
* Refactor the transclude widget to use the new helper
* Update tests
* Fix positional parameters in widget.evaluateVariable()
This should clear up the remaining anomalies in #7009, let me know how you get on @btheado
* Remove 5.2.8 release note
* Fix nonstandard initialisation code for fill/parameter/slot widgets
* Update modification times of doc tiddlers
So that they are at the top of the recent tab
* Update 5.3.0 release note
* Remove custom CI step for this branch
* Restore standard sitetitle
2023-04-19 10:55:25 +00:00
parserType : defaultType
2021-06-02 20:45:06 +00:00
} ;
if ( options . subTiddler ) {
tiddler = this . getSubTiddler ( title , options . subTiddler ) ;
} else {
tiddler = this . getTiddler ( title ) ;
2014-07-17 17:41:20 +00:00
}
if ( field === "text" || ( ! field && ! index ) ) {
2014-07-31 17:07:52 +00:00
if ( tiddler && tiddler . fields ) {
2021-06-02 20:45:06 +00:00
parserInfo . sourceText = tiddler . fields . text || "" ;
if ( tiddler . fields . type ) {
parserInfo . parserType = tiddler . fields . type ;
}
2014-07-31 17:07:52 +00:00
}
2014-07-17 17:41:20 +00:00
} else if ( field ) {
if ( field === "title" ) {
2021-06-02 20:45:06 +00:00
parserInfo . sourceText = title ;
} else if ( tiddler && tiddler . fields ) {
2021-06-29 11:07:14 +00:00
parserInfo . sourceText = tiddler . hasField ( field ) ? tiddler . fields [ field ] . toString ( ) : null ;
2014-07-17 17:41:20 +00:00
}
} else if ( index ) {
2014-10-31 07:24:49 +00:00
this . getTiddlerText ( title ) ; // Force the tiddler to be lazily loaded
2021-06-02 20:45:06 +00:00
parserInfo . sourceText = this . extractTiddlerDataItem ( tiddler , index , null ) ;
2021-06-02 12:58:30 +00:00
}
2021-06-02 20:45:06 +00:00
if ( parserInfo . sourceText === null ) {
parserInfo . parserType = null ;
}
return parserInfo ;
}
2013-10-12 16:05:13 +00:00
2023-06-24 13:57:15 +00:00
/ *
Parse a block of text of a specified MIME type
text : text on which to perform substitutions
widget
options : see below
Options include :
substitutions : an optional array of substitutions
* /
exports . getSubstitutedText = function ( text , widget , options ) {
options = options || { } ;
text = text || "" ;
var self = this ,
substitutions = options . substitutions || [ ] ,
output ;
// Evaluate embedded filters and substitute with first result
output = text . replace ( /\$\{([\S\s]+?)\}\$/g , function ( match , filter ) {
return self . filterTiddlers ( filter , widget ) [ 0 ] || "" ;
} ) ;
// Process any substitutions provided in options
$tw . utils . each ( substitutions , function ( substitute ) {
output = $tw . utils . replaceString ( output , new RegExp ( "\\$" + $tw . utils . escapeRegExp ( substitute . name ) + "\\$" , "mg" ) , substitute . value ) ;
} ) ;
// Substitute any variable references with their values
2023-07-14 20:42:31 +00:00
return output . replace ( /\$\(([^\)\$]+)\)\$/g , function ( match , varname ) {
2023-06-24 13:57:15 +00:00
return widget . getVariable ( varname , { defaultValue : "" } )
} ) ;
} ;
2013-10-29 14:48:24 +00:00
/ *
Make a widget tree for a parse tree
parser : parser object
options : see below
Options include :
document : optional document to use
variables : hashmap of variables to set
parentWidget : optional parent widget for the root node
* /
exports . makeWidget = function ( parser , options ) {
options = options || { } ;
var widgetNode = {
type : "widget" ,
children : [ ]
} ,
currWidgetNode = widgetNode ;
Parameterised transclusions (#6666)
* Initial commit
Everything is draft.
* Fix test execution
* Fix and test missing target handling
* Use the ubertransclude widget for the wikitext transclusion syntax
* Changed transclude widget in binary parser to ubertransclude
* Add a test for custom action widgets
* Don't worry about ordered attributes
The changes in 0bffae21088aafc0cdebafe6a5de7907d7c52a3a mean that we don't need to explicitly maintain the ordered attributes
* Remove need to explicitly clear widget mapping variable when invoking overridden widget
* Use ts- prefix for system slot names
* Add a definition for the value widget just so that it doesn't cause errors
Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget...
* Add support for positional parameters
* Ubertransclusion positional parameters should be based on name, not position
* Add support for shortcut syntax for positional transclusion parameters
* Importvariables should skip parameters widgets
* Refactor transclude widget before uberfying it
* Refactor ubertransclude functionality into transclude widget
* Replace ubertransclude widget with transclude widget
* Add wikitext shortcut for new-style function definitions
* Allow brackets to be omitted for function definitions with no parameters
* Add pragma rule for parameters declarations
* Remove erroneous "tag" property
* Add support for accessing function parameters as name/value pairs
* Be as permissive as possible with parameter names
Previously restricted to upper and lower case, digits and dash and underscore
* Rewrite some tests to use the shortcut syntaxes
* Mustn't allow commas in parameter names
* Fix crash when transcluding an undefined variable
Thanks @pmario
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359
* Unquoted parameters should not eat a succeeding comma
Fixes #6672
* Remove extraneous code
* Allow the let widget to create variables starting with $
* Fix addAttributeToParseTreeNode handling of ordered attributes
* Reuse attribute objects when executing custom widgets
* Fix importing of function definitions
* Fix parameter handling
* Introduce genesis widget for dynamically creating widgets
See the "RedefineLet" test for a contrived example of usage
* Change tiddler separator used in wikitext tests
Underscore looked ambiguous; I kept typing dashes by accident
* Cache parse trees when transcluding variables
* Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray
I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code.
* Fixes to enable the transclude widget itself to be overridden
There are two big changes here:
Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped.
We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
* Fix genesis widget example
* Use enlist:raw to preserve duplicates
* Don't create variables with value undefined for missing parameters
* Fix variable retrieval bug with test harness
* Improve recursion detection
While retaining backwards compatibility
* Experimental support for custom filter operators
Just as we can define custom widgets we can also define custom parameterised filter operators
* Add visible transclusions component and demo
Very useful to see transclusions explicitly
Makes a good demo of a super-complicated widget override.
* Genesis widget should pass raw attributes onto child widget...
...so that it can more efficiently handle refreshing itself.
* Use consistent parse tree node property for params
* Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax
* Clarify that the recent changes allow functions to be invoked with the double bracket syntax
In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately
* Make the macrocall widget delegate to the transclude widget
* Switch to using \procedure to define new-style macros, and \function for custom filter operator functions
I now need to update the OP!
* Fix visible transclusion example
* Remove obsolete code
Left over after refactoring
* Better backwards compatibility for legacy recursion marker
Fixes the problem with tag dropdowns @btheado
* Fix stringifying/parsing string arrays containing newlines
A very old bug.
Fixes the ActionListOpsWidget problem @btheado
* Transclude: replace paramNames/paramValues with more robust JSON payload
More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153
* Rename internal "unknown" filter operator so that users cannot invoke it
* Detect recursion by tracking widget tree depth
The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid
* Use \widget for custom widget definitions, and remove need for angle brackets
Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property
* Rename <$value> widget to <$fill>
* Require $$ for custom widgets, and that overridden JS widgets must exist
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763
* Fix invocation of JS macros
* Experimental update of the parse-tree preview visualisation
An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget.
As usual with these experiments, this one is going to require quite a lot more work to finish up:
* The formatting is via direct styles rather than classes
* The formatting for attributes and properties is not yet completed
* The same thing needs to also be done to the widget tree preview
* Procedures and widgets inherit whitespace trim setting from their definition
* Missed off 22e7ec23811b137a119295b5ce72bccdc18a697a
* Require period prefix for custom filter operator functions
To ensure that custom filter operators cannot clash with future core operators.
* Allow custom functions to be invoked as attributes
* WIP
* Remove unneeded test tiddler
* Make is[variable] and variables[] operators resilient to fake widgets
* Fix importvariables to work with setvariables as well as set (they are aliases)
* Add support for $:/tags/Global
* Remove accidental commit 6cc99fcbe33da47cfcb1335d0742b16ea1b9ce03
* Add utility function for parsing macro parameter definitions
* Introduce true global variables
The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists.
This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro.
In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro
* Convert the tabs macro into a global
So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR.
* Change to `?` for conditional definitions
* Fix tabs global so it doesn't crash when viewed directly
* Test showing how to un-override a core widget
* Cleaning up after f63634900724eda937286d946b2e6f65fcf6d503
* Minor cleanups
* Clean up unknown filter
* Introduce function operator for calling functions
Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter).
* Use underscores for new system fields for global variable tiddlers
For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code.
* Refactor $parameters widget
The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself.
* Fix refreshing of global variables
Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions.
* Remove support for $:/tags/Global
It is not needed now that we have true global variables
* Typo from f513b403fe911442bcbaf0628fa47d3d2ed3cf93
* Make slot fill data available to transclusions
Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion
* Mark docs as v5.3.0
* Simplify metaparameters implementation
* Fix typo
* Adjust naming of transclusion metaparameter
* Fix up handling of slot/fill for custom widgets
Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget
* Add format:json operator
I've been finding this useful for debugging, and it kind of goes with the JSON operators
* Docs: JSON operators and tweaks to genesis widget
* Docs: format:json
Also tweak to the behaviour of format:json if the input string is not valid JSON
* Fix #6721
* Revert "Fix #6721"
This reverts commit b216579255d6e6214f3cf71ab771fcc57240aa74 which was committed to the wrong branch
* Fix new selection tracker to return relative coordinates
* Make use of type attribute consistent
* Docs: Transclude widget
* Simplify the fill widget
We can rely on the default processing in the base class
* Slot widget: be more defensive about negative depth values
* Parameters widget: Be defensive about negative depths
* Protect against excessively recursive functions
* FIx transcluding of functions
This first implementation concatenates the results of the filter (with no separator) and then wikifies the result.
The test in this commit is quite interesting...
* Tweak semantics of JSON operators to match #6932
This allows us to later bring in the optimisations without breaking backwards compatibility.
* Revert obsolete changes to boot.js
* Fix inadvertent whitespace change
* Remove tests related to obsolete changes to boot.js
Should have been part of 2f494ba15246edd356bfc591b0115d30592e7eb8
* Revert changes to parse tree preview
This implementation requires #6666
* Add test to show that global widgets need not use the _parameters field
* Disable overriding core widgets in safe mode
* Coding style tweak
* More comments
* Fix caching of parse variables/macros/procedures
* Transcluded functions should operate on the entire store
* Refactor filter recursion detection to avoid an unneeded wrapper function
* Fix error in 25312b3e3218c1002c483a1fc995d2b65509b993
* WIP
* Revert "WIP"
This reverts commit 8654dfc679ea12d30ffd8b14a30165d826be06b7.
* When transcluding functions, pass an empty item list to the filter, and just return the first item
* Rejig genesis widget to be easier to use
* Parameters widget: protect against negative $depth
* Docs updates
* Docs updates
* Tweak comments
* Add custom view template body for globals, and a new sidebar tab under "more"
And also a custom view template title that greys out the $:/global/ part of the title
* Update function operator to return the input list if the function is missing
* Remove negation from function operator
This implementation was not useful.
* Tests and docs for function operator
* Docs tweaks
* Improve indentation
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251
* Missing tests for parameters widget
* Fix visible transclude
* Docs update
* Docs typo
* Huge Documentation Update
Not quite finished, but definitely on the home stretch
* Slight optimisation to user defined widgets
* Remove implementation of $:/globals/
Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created.
* Docs clarification
* Docs update
* Some widget.js cleanups
* Remove support for conditional definitions
It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c887ba849958c8980623d8ff45bb8a36
* Docs updates
* Revert change to setwidget docs
* Docs update
* Docs updates
* Clarify/simplify some tests
* More docs updates
* Fix doc file locations
* Docs updates
* Revert modified date of docs that have only had minor tweaks
* Docs typo
https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220
Thanks @btheado
* Transcluding functions: fix missing parameters passed as undefined
Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372
* Parameter parenthesis should be mandatory in function/procedure/widget definitions
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387
* Attempt to build this branch with CI
* Add release note etc for 5.3.0
* Temporary new release banner for v5.3.0
* New New Release Banner
* New test for undefined parameters
* Adjust modified times of docs tiddlers to make them easier to find
* Update release note
* Add parenthesis to the visible transclusion definition
Parenthesis were made mandatory in 5194b24108efda6da95daf4261ffd80473073a65
Fixes #6998
* Fix macrocall refresh issue
It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable
Fixes #7001
* Filter run prefixes should use widget.makeFakeWidgetWithVariables
* Docs typo
Thanks @twMat
* Docs: clarify function operator invocation
See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599
* Docs: Update \define pragma to cover named ends
* Docs: move tiddlers to correct directory
* Add support for named end markers for procedures, functions and widgets
* Docs note about nested macro definitions
* Rename test
* Fix detection of empty transclusions
See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston
* New test missed off a45349cc996390192114fed486bfa6900da641d7
* Refactor wikified function tests
* Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text
* Refactor the function operator and unknown operator to use the new helper
* Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated
* Refactor the transclude widget to use the new helper
* Update tests
* Fix positional parameters in widget.evaluateVariable()
This should clear up the remaining anomalies in #7009, let me know how you get on @btheado
* Remove 5.2.8 release note
* Fix nonstandard initialisation code for fill/parameter/slot widgets
* Update modification times of doc tiddlers
So that they are at the top of the recent tab
* Update 5.3.0 release note
* Remove custom CI step for this branch
* Restore standard sitetitle
2023-04-19 10:55:25 +00:00
// Create let variable widget for variables
if ( $tw . utils . count ( options . variables ) > 0 ) {
var letVariableWidget = {
type : "let" ,
2013-10-29 14:48:24 +00:00
attributes : {
} ,
children : [ ]
} ;
Parameterised transclusions (#6666)
* Initial commit
Everything is draft.
* Fix test execution
* Fix and test missing target handling
* Use the ubertransclude widget for the wikitext transclusion syntax
* Changed transclude widget in binary parser to ubertransclude
* Add a test for custom action widgets
* Don't worry about ordered attributes
The changes in 0bffae21088aafc0cdebafe6a5de7907d7c52a3a mean that we don't need to explicitly maintain the ordered attributes
* Remove need to explicitly clear widget mapping variable when invoking overridden widget
* Use ts- prefix for system slot names
* Add a definition for the value widget just so that it doesn't cause errors
Of course, it doesn't actually need to be a JS widget, it could be a wikitext widget...
* Add support for positional parameters
* Ubertransclusion positional parameters should be based on name, not position
* Add support for shortcut syntax for positional transclusion parameters
* Importvariables should skip parameters widgets
* Refactor transclude widget before uberfying it
* Refactor ubertransclude functionality into transclude widget
* Replace ubertransclude widget with transclude widget
* Add wikitext shortcut for new-style function definitions
* Allow brackets to be omitted for function definitions with no parameters
* Add pragma rule for parameters declarations
* Remove erroneous "tag" property
* Add support for accessing function parameters as name/value pairs
* Be as permissive as possible with parameter names
Previously restricted to upper and lower case, digits and dash and underscore
* Rewrite some tests to use the shortcut syntaxes
* Mustn't allow commas in parameter names
* Fix crash when transcluding an undefined variable
Thanks @pmario
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1114692359
* Unquoted parameters should not eat a succeeding comma
Fixes #6672
* Remove extraneous code
* Allow the let widget to create variables starting with $
* Fix addAttributeToParseTreeNode handling of ordered attributes
* Reuse attribute objects when executing custom widgets
* Fix importing of function definitions
* Fix parameter handling
* Introduce genesis widget for dynamically creating widgets
See the "RedefineLet" test for a contrived example of usage
* Change tiddler separator used in wikitext tests
Underscore looked ambiguous; I kept typing dashes by accident
* Cache parse trees when transcluding variables
* Fix bug with empty strings ignored in $tw.utils.stringifyList/parseStringArray
I will pull this out into a separate PR. Fixing it doesn't cause problems for the core but I imagine it might cause issues for 3rd party code.
* Fixes to enable the transclude widget itself to be overridden
There are two big changes here:
Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped.
We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
* Fix genesis widget example
* Use enlist:raw to preserve duplicates
* Don't create variables with value undefined for missing parameters
* Fix variable retrieval bug with test harness
* Improve recursion detection
While retaining backwards compatibility
* Experimental support for custom filter operators
Just as we can define custom widgets we can also define custom parameterised filter operators
* Add visible transclusions component and demo
Very useful to see transclusions explicitly
Makes a good demo of a super-complicated widget override.
* Genesis widget should pass raw attributes onto child widget...
...so that it can more efficiently handle refreshing itself.
* Use consistent parse tree node property for params
* Extend transclude widget to work with old-style macros and use it for the macrocall shortcut syntax
* Clarify that the recent changes allow functions to be invoked with the double bracket syntax
In other words, the transclude widget distinguishes between functions and macros and handles the parameters appropriately
* Make the macrocall widget delegate to the transclude widget
* Switch to using \procedure to define new-style macros, and \function for custom filter operator functions
I now need to update the OP!
* Fix visible transclusion example
* Remove obsolete code
Left over after refactoring
* Better backwards compatibility for legacy recursion marker
Fixes the problem with tag dropdowns @btheado
* Fix stringifying/parsing string arrays containing newlines
A very old bug.
Fixes the ActionListOpsWidget problem @btheado
* Transclude: replace paramNames/paramValues with more robust JSON payload
More details at https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1123719153
* Rename internal "unknown" filter operator so that users cannot invoke it
* Detect recursion by tracking widget tree depth
The old recursion marker approach was very slow, and didn't catch test cases like editions/test/tiddlers/tests/data/transclude/Recursion.tid
* Use \widget for custom widget definitions, and remove need for angle brackets
Need to do some refactoring of all those isFunctionDefinition/isProcedureDefinition/isWidgetDefinition flags into a single property
* Rename <$value> widget to <$fill>
* Require $$ for custom widgets, and that overridden JS widgets must exist
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1133637763
* Fix invocation of JS macros
* Experimental update of the parse-tree preview visualisation
An experiment to try out using the new JSON operators for rendering the JSON parse tree that we get back from the wikify widget.
As usual with these experiments, this one is going to require quite a lot more work to finish up:
* The formatting is via direct styles rather than classes
* The formatting for attributes and properties is not yet completed
* The same thing needs to also be done to the widget tree preview
* Procedures and widgets inherit whitespace trim setting from their definition
* Missed off 22e7ec23811b137a119295b5ce72bccdc18a697a
* Require period prefix for custom filter operator functions
To ensure that custom filter operators cannot clash with future core operators.
* Allow custom functions to be invoked as attributes
* WIP
* Remove unneeded test tiddler
* Make is[variable] and variables[] operators resilient to fake widgets
* Fix importvariables to work with setvariables as well as set (they are aliases)
* Add support for $:/tags/Global
* Remove accidental commit 6cc99fcbe33da47cfcb1335d0742b16ea1b9ce03
* Add utility function for parsing macro parameter definitions
* Introduce true global variables
The basic idea is that if we don't find a variable `foo` then we fallback to retrieving the value from the tiddler `$:/global/foo`, if it exists.
This allows us to replace the usual importvariables-based mechanism for global definitions, avoiding cluttering up the variable namespace with every macro.
In order to permit subprocedures to be overridden, we also introduce a mechanism for conditional definitions: preceding the word definition|procedure|function|widget with a + causes the definition only to occur if the specified variable doesn't already exist. In the next commit we'll apply this mechanism to the tabs macro
* Convert the tabs macro into a global
So far it appears to be totally backwards compatible... In practice, I think maybe this and the conversion of the other macros should go into a separate subsequent PR.
* Change to `?` for conditional definitions
* Fix tabs global so it doesn't crash when viewed directly
* Test showing how to un-override a core widget
* Cleaning up after f63634900724eda937286d946b2e6f65fcf6d503
* Minor cleanups
* Clean up unknown filter
* Introduce function operator for calling functions
Can invoke any functions, not just those start with a period. And can pass zero parameters (in contrast when invoked as a custom filter operator there's no way to omit the first parameter).
* Use underscores for new system fields for global variable tiddlers
For consistency with `_canonical_uri`; unlike many system fields, the behaviour of these fields is baked into the core JS code.
* Refactor $parameters widget
The objective is to add a $depth attribute so that it is possible to reach up to retrieve the parameters of ancestor transclusions. However, doing so requires changing the encoding of parameter names so that it is not possible for a user parameter to clash with an attribute like $depth. So now we have to double up dollars on any attribute names seen by the parameters widget, just like with the transclude widget itself.
* Fix refreshing of global variables
Global variables access within attributes will automatically trigger a refresh if the attribute text changes, but that wasn't happening for transclusions.
* Remove support for $:/tags/Global
It is not needed now that we have true global variables
* Typo from f513b403fe911442bcbaf0628fa47d3d2ed3cf93
* Make slot fill data available to transclusions
Allows transcluded content to dynamically process <$fill> widgets within the calling transclusion
* Mark docs as v5.3.0
* Simplify metaparameters implementation
* Fix typo
* Adjust naming of transclusion metaparameter
* Fix up handling of slot/fill for custom widgets
Previously we were wrapping the body in an implicit `<$fill $name="ts-body">` widget
* Add format:json operator
I've been finding this useful for debugging, and it kind of goes with the JSON operators
* Docs: JSON operators and tweaks to genesis widget
* Docs: format:json
Also tweak to the behaviour of format:json if the input string is not valid JSON
* Fix #6721
* Revert "Fix #6721"
This reverts commit b216579255d6e6214f3cf71ab771fcc57240aa74 which was committed to the wrong branch
* Fix new selection tracker to return relative coordinates
* Make use of type attribute consistent
* Docs: Transclude widget
* Simplify the fill widget
We can rely on the default processing in the base class
* Slot widget: be more defensive about negative depth values
* Parameters widget: Be defensive about negative depths
* Protect against excessively recursive functions
* FIx transcluding of functions
This first implementation concatenates the results of the filter (with no separator) and then wikifies the result.
The test in this commit is quite interesting...
* Tweak semantics of JSON operators to match #6932
This allows us to later bring in the optimisations without breaking backwards compatibility.
* Revert obsolete changes to boot.js
* Fix inadvertent whitespace change
* Remove tests related to obsolete changes to boot.js
Should have been part of 2f494ba15246edd356bfc591b0115d30592e7eb8
* Revert changes to parse tree preview
This implementation requires #6666
* Add test to show that global widgets need not use the _parameters field
* Disable overriding core widgets in safe mode
* Coding style tweak
* More comments
* Fix caching of parse variables/macros/procedures
* Transcluded functions should operate on the entire store
* Refactor filter recursion detection to avoid an unneeded wrapper function
* Fix error in 25312b3e3218c1002c483a1fc995d2b65509b993
* WIP
* Revert "WIP"
This reverts commit 8654dfc679ea12d30ffd8b14a30165d826be06b7.
* When transcluding functions, pass an empty item list to the filter, and just return the first item
* Rejig genesis widget to be easier to use
* Parameters widget: protect against negative $depth
* Docs updates
* Docs updates
* Tweak comments
* Add custom view template body for globals, and a new sidebar tab under "more"
And also a custom view template title that greys out the $:/global/ part of the title
* Update function operator to return the input list if the function is missing
* Remove negation from function operator
This implementation was not useful.
* Tests and docs for function operator
* Docs tweaks
* Improve indentation
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r967655251
* Missing tests for parameters widget
* Fix visible transclude
* Docs update
* Docs typo
* Huge Documentation Update
Not quite finished, but definitely on the home stretch
* Slight optimisation to user defined widgets
* Remove implementation of $:/globals/
Performance with this implementation is inherently poor because of the need to perform a wiki lookup for each child widget created.
* Docs clarification
* Docs update
* Some widget.js cleanups
* Remove support for conditional definitions
It was introduced for use cases associated with the global mechanism that was dropped in e3d13696c887ba849958c8980623d8ff45bb8a36
* Docs updates
* Revert change to setwidget docs
* Docs update
* Docs updates
* Clarify/simplify some tests
* More docs updates
* Fix doc file locations
* Docs updates
* Revert modified date of docs that have only had minor tweaks
* Docs typo
https://github.com/Jermolene/TiddlyWiki5/pull/6666#discussion_r990811220
Thanks @btheado
* Transcluding functions: fix missing parameters passed as undefined
Thanks @btheado – see https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1276187372
* Parameter parenthesis should be mandatory in function/procedure/widget definitions
See https://github.com/Jermolene/TiddlyWiki5/pull/6666#issuecomment-1280404387
* Attempt to build this branch with CI
* Add release note etc for 5.3.0
* Temporary new release banner for v5.3.0
* New New Release Banner
* New test for undefined parameters
* Adjust modified times of docs tiddlers to make them easier to find
* Update release note
* Add parenthesis to the visible transclusion definition
Parenthesis were made mandatory in 5194b24108efda6da95daf4261ffd80473073a65
Fixes #6998
* Fix macrocall refresh issue
It turns out that this.transcludeTitle is always truthy, even if we are transcluding a variable
Fixes #7001
* Filter run prefixes should use widget.makeFakeWidgetWithVariables
* Docs typo
Thanks @twMat
* Docs: clarify function operator invocation
See discussion at https://github.com/Jermolene/TiddlyWiki5/issues/6991#issuecomment-1301703599
* Docs: Update \define pragma to cover named ends
* Docs: move tiddlers to correct directory
* Add support for named end markers for procedures, functions and widgets
* Docs note about nested macro definitions
* Rename test
* Fix detection of empty transclusions
See https://talk.tiddlywiki.org/t/exploring-default-tiddler-links-hackability-in-v5-3-0/5745/25?u=jeremyruston
* New test missed off a45349cc996390192114fed486bfa6900da641d7
* Refactor wikified function tests
* Refactor function invocation
* Introduce new widget helper function to evaluate variables.Functions are evaluated as parameterised filter strings, macros as text with textual substitution of parameters and variables, and procedures and widgets as plain text
* Refactor the function operator and unknown operator to use the new helper
* Use the new helper to evaluate variables within filter strings, thus fixing a bug whereby functions called in such a way were being returned as plain text instead of being evaluated
* Refactor the transclude widget to use the new helper
* Update tests
* Fix positional parameters in widget.evaluateVariable()
This should clear up the remaining anomalies in #7009, let me know how you get on @btheado
* Remove 5.2.8 release note
* Fix nonstandard initialisation code for fill/parameter/slot widgets
* Update modification times of doc tiddlers
So that they are at the top of the recent tab
* Update 5.3.0 release note
* Remove custom CI step for this branch
* Restore standard sitetitle
2023-04-19 10:55:25 +00:00
$tw . utils . each ( options . variables , function ( value , name ) {
$tw . utils . addAttributeToParseTreeNode ( letVariableWidget , name , "" + value ) ;
} ) ;
currWidgetNode . children = [ letVariableWidget ] ;
currWidgetNode = letVariableWidget ;
}
2013-10-29 14:48:24 +00:00
// Add in the supplied parse tree nodes
currWidgetNode . children = parser ? parser . tree : [ ] ;
// Create the widget
return new widget . widget ( widgetNode , {
wiki : this ,
2014-01-15 14:57:35 +00:00
document : options . document || $tw . fakeDocument ,
2013-10-29 14:48:24 +00:00
parentWidget : options . parentWidget
} ) ;
} ;
2014-05-06 09:14:22 +00:00
/ *
Make a widget tree for transclusion
title : target tiddler title
2014-07-04 20:03:11 +00:00
options : as for wiki . makeWidget ( ) plus :
options . field : optional field to transclude ( defaults to "text" )
2015-03-18 11:39:50 +00:00
options . mode : transclusion mode "inline" or "block"
2020-11-07 09:51:01 +00:00
options . recursionMarker : optional flag to set a recursion marker , defaults to "yes"
2014-07-04 20:03:11 +00:00
options . children : optional array of children for the transclude widget
2016-10-18 15:39:18 +00:00
options . importVariables : optional importvariables filter string for macros to be included
options . importPageMacros : optional boolean ; if true , equivalent to passing "[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]" to options . importVariables
2014-05-06 09:14:22 +00:00
* /
exports . makeTranscludeWidget = function ( title , options ) {
options = options || { } ;
2016-10-18 15:39:18 +00:00
var parseTreeDiv = { tree : [ {
2014-07-04 20:03:11 +00:00
type : "element" ,
tag : "div" ,
2016-10-18 15:39:18 +00:00
children : [ ] } ] } ,
parseTreeImportVariables = {
type : "importvariables" ,
attributes : {
filter : {
name : "filter" ,
type : "string"
}
} ,
isBlock : false ,
children : [ ] } ,
parseTreeTransclude = {
type : "transclude" ,
attributes : {
2020-11-07 09:51:01 +00:00
recursionMarker : {
name : "recursionMarker" ,
type : "string" ,
value : options . recursionMarker || "yes"
} ,
2016-10-18 15:39:18 +00:00
tiddler : {
name : "tiddler" ,
type : "string" ,
2020-11-07 09:51:01 +00:00
value : title
}
} ,
2016-10-18 15:39:18 +00:00
isBlock : ! options . parseAsInline } ;
if ( options . importVariables || options . importPageMacros ) {
if ( options . importVariables ) {
parseTreeImportVariables . attributes . filter . value = options . importVariables ;
} else if ( options . importPageMacros ) {
2023-05-09 14:46:54 +00:00
parseTreeImportVariables . attributes . filter . value = this . getTiddlerText ( "$:/core/config/GlobalImportFilter" ) ;
2016-10-18 15:39:18 +00:00
}
parseTreeDiv . tree [ 0 ] . children . push ( parseTreeImportVariables ) ;
parseTreeImportVariables . children . push ( parseTreeTransclude ) ;
} else {
parseTreeDiv . tree [ 0 ] . children . push ( parseTreeTransclude ) ;
}
2014-07-04 20:03:11 +00:00
if ( options . field ) {
2016-10-18 15:39:18 +00:00
parseTreeTransclude . attributes . field = { type : "string" , value : options . field } ;
2014-07-04 20:03:11 +00:00
}
2015-03-18 11:39:50 +00:00
if ( options . mode ) {
2016-10-18 15:39:18 +00:00
parseTreeTransclude . attributes . mode = { type : "string" , value : options . mode } ;
2015-03-18 11:39:50 +00:00
}
2014-07-04 20:03:11 +00:00
if ( options . children ) {
2016-10-18 15:39:18 +00:00
parseTreeTransclude . children = options . children ;
2014-07-04 20:03:11 +00:00
}
2020-05-06 10:27:50 +00:00
return this . makeWidget ( parseTreeDiv , options ) ;
2014-05-06 09:14:22 +00:00
} ;
2013-10-12 16:05:13 +00:00
/ *
Parse text in a specified format and render it into another format
outputType : content type for the output
textType : content type of the input text
text : input text
2013-10-29 14:48:24 +00:00
options : see below
Options include :
variables : hashmap of variables to set
parentWidget : optional parent widget for the root node
2013-10-12 16:05:13 +00:00
* /
2013-11-08 08:51:14 +00:00
exports . renderText = function ( outputType , textType , text , options ) {
2013-10-29 14:48:24 +00:00
options = options || { } ;
2013-11-12 19:52:25 +00:00
var parser = this . parseText ( textType , text , options ) ,
2013-10-29 14:48:24 +00:00
widgetNode = this . makeWidget ( parser , options ) ;
2014-01-15 14:57:35 +00:00
var container = $tw . fakeDocument . createElement ( "div" ) ;
2013-10-12 16:05:13 +00:00
widgetNode . render ( container , null ) ;
return outputType === "text/html" ? container . innerHTML : container . textContent ;
} ;
/ *
Parse text from a tiddler and render it into another format
outputType : content type for the output
title : title of the tiddler to be rendered
2013-10-29 14:48:24 +00:00
options : see below
Options include :
variables : hashmap of variables to set
parentWidget : optional parent widget for the root node
2013-10-12 16:05:13 +00:00
* /
2013-11-08 08:51:14 +00:00
exports . renderTiddler = function ( outputType , title , options ) {
2013-10-29 14:48:24 +00:00
options = options || { } ;
2014-02-25 14:49:07 +00:00
var parser = this . parseTiddler ( title , options ) ,
2013-10-29 14:48:24 +00:00
widgetNode = this . makeWidget ( parser , options ) ;
2014-01-15 14:57:35 +00:00
var container = $tw . fakeDocument . createElement ( "div" ) ;
2013-10-12 16:05:13 +00:00
widgetNode . render ( container , null ) ;
2014-02-25 14:49:07 +00:00
return outputType === "text/html" ? container . innerHTML : ( outputType === "text/plain-formatted" ? container . formattedTextContent : container . textContent ) ;
2013-10-12 16:05:13 +00:00
} ;
2012-10-17 13:34:59 +00:00
/ *
Return an array of tiddler titles that match a search string
text : The text string to search for
2012-10-17 13:57:13 +00:00
options : see below
Options available :
2022-02-21 15:05:34 +00:00
source : an iterator function for the source tiddlers , called source ( iterator ) ,
where iterator is called as iterator ( tiddler , title )
2012-10-17 13:57:13 +00:00
exclude : An array of tiddler titles to exclude from the search
invert : If true returns tiddlers that do not contain the specified string
2012-10-17 19:38:36 +00:00
caseSensitive : If true forces a case sensitive search
2018-10-30 17:39:18 +00:00
field : If specified , restricts the search to the specified field , or an array of field names
2019-07-31 20:36:12 +00:00
anchored : If true , forces all but regexp searches to be anchored to the start of text
2018-10-30 17:39:18 +00:00
excludeField : If true , the field options are inverted to specify the fields that are not to be searched
2022-02-21 15:05:34 +00:00
2018-10-30 17:39:18 +00:00
The search mode is determined by the first of these boolean flags to be true
literal : searches for literal string
whitespace : same as literal except runs of whitespace are treated as a single space
regexp : treats the search term as a regular expression
2022-02-21 15:05:34 +00:00
words : ( default ) treats search string as a list of tokens , and matches if all tokens are found ,
regardless of adjacency or ordering
some : treats search string as a list of tokens , and matches if at least ONE token is found
2012-10-17 13:34:59 +00:00
* /
2012-10-17 13:57:13 +00:00
exports . search = function ( text , options ) {
options = options || { } ;
2014-08-30 19:44:26 +00:00
var self = this ,
t ,
2022-02-21 15:05:34 +00:00
regExpStr = "" ,
2014-08-30 19:44:26 +00:00
invert = ! ! options . invert ;
2012-10-17 19:38:36 +00:00
// Convert the search string into a regexp for each term
var terms , searchTermsRegExps ,
2019-07-31 20:36:12 +00:00
flags = options . caseSensitive ? "" : "i" ,
anchor = options . anchored ? "^" : "" ;
2012-10-17 19:38:36 +00:00
if ( options . literal ) {
if ( text . length === 0 ) {
2013-08-24 15:51:54 +00:00
searchTermsRegExps = null ;
} else {
2019-07-31 20:36:12 +00:00
searchTermsRegExps = [ new RegExp ( "(" + anchor + $tw . utils . escapeRegExp ( text ) + ")" , flags ) ] ;
2012-10-17 19:38:36 +00:00
}
2018-10-30 17:39:18 +00:00
} else if ( options . whitespace ) {
terms = [ ] ;
$tw . utils . each ( text . split ( /\s+/g ) , function ( term ) {
if ( term ) {
terms . push ( $tw . utils . escapeRegExp ( term ) ) ;
}
} ) ;
2019-07-31 20:36:12 +00:00
searchTermsRegExps = [ new RegExp ( "(" + anchor + terms . join ( "\\s+" ) + ")" , flags ) ] ;
2018-10-30 17:39:18 +00:00
} else if ( options . regexp ) {
try {
2021-05-30 18:20:17 +00:00
searchTermsRegExps = [ new RegExp ( "(" + text + ")" , flags ) ] ;
2018-10-30 17:39:18 +00:00
} catch ( e ) {
searchTermsRegExps = null ;
console . log ( "Regexp error parsing /(" + text + ")/" + flags + ": " , e ) ;
}
2022-02-21 15:05:34 +00:00
} else if ( options . some ) {
2023-11-21 11:58:12 +00:00
terms = text . trim ( ) . split ( /[^\S\xA0]+/ ) ;
2022-02-21 15:05:34 +00:00
if ( terms . length === 1 && terms [ 0 ] === "" ) {
searchTermsRegExps = null ;
} else {
searchTermsRegExps = [ ] ;
for ( t = 0 ; t < terms . length ; t ++ ) {
regExpStr += ( t === 0 ) ? anchor + $tw . utils . escapeRegExp ( terms [ t ] ) : "|" + anchor + $tw . utils . escapeRegExp ( terms [ t ] ) ;
}
searchTermsRegExps . push ( new RegExp ( "(" + regExpStr + ")" , flags ) ) ;
}
} else { // default: words
2023-11-21 11:58:12 +00:00
terms = text . split ( /[^\S\xA0]+/ ) ;
2013-08-24 15:51:54 +00:00
if ( terms . length === 1 && terms [ 0 ] === "" ) {
searchTermsRegExps = null ;
} else {
searchTermsRegExps = [ ] ;
for ( t = 0 ; t < terms . length ; t ++ ) {
2019-07-31 20:36:12 +00:00
searchTermsRegExps . push ( new RegExp ( "(" + anchor + $tw . utils . escapeRegExp ( terms [ t ] ) + ")" , flags ) ) ;
2013-08-24 15:51:54 +00:00
}
2012-10-17 19:38:36 +00:00
}
}
2022-02-21 15:05:34 +00:00
// Accumulate the array of fields to be searched or excluded from the search
2018-10-30 17:39:18 +00:00
var fields = [ ] ;
if ( options . field ) {
if ( $tw . utils . isArray ( options . field ) ) {
$tw . utils . each ( options . field , function ( fieldName ) {
2018-11-01 12:56:56 +00:00
if ( fieldName ) {
2021-05-30 18:20:17 +00:00
fields . push ( fieldName ) ;
2018-11-01 12:56:56 +00:00
}
2018-10-30 17:39:18 +00:00
} ) ;
} else {
fields . push ( options . field ) ;
}
}
// Use default fields if none specified and we're not excluding fields (excluding fields with an empty field array is the same as searching all fields)
if ( fields . length === 0 && ! options . excludeField ) {
fields . push ( "title" ) ;
fields . push ( "tags" ) ;
fields . push ( "text" ) ;
}
2012-10-17 13:34:59 +00:00
// Function to check a given tiddler for the search term
var searchTiddler = function ( title ) {
2013-08-24 15:51:54 +00:00
if ( ! searchTermsRegExps ) {
2013-11-11 15:26:41 +00:00
return true ;
2013-08-24 15:51:54 +00:00
}
2018-12-17 11:19:48 +00:00
var notYetFound = searchTermsRegExps . slice ( ) ;
2013-03-17 15:06:09 +00:00
var tiddler = self . getTiddler ( title ) ;
2012-10-17 19:38:36 +00:00
if ( ! tiddler ) {
2013-08-24 15:51:54 +00:00
tiddler = new $tw . Tiddler ( { title : title , text : "" , type : "text/vnd.tiddlywiki" } ) ;
2012-10-17 19:38:36 +00:00
}
2013-10-18 15:09:10 +00:00
var contentTypeInfo = $tw . config . contentTypeInfo [ tiddler . fields . type ] || $tw . config . contentTypeInfo [ "text/vnd.tiddlywiki" ] ,
2018-10-30 17:39:18 +00:00
searchFields ;
// Get the list of fields we're searching
if ( options . excludeField ) {
searchFields = Object . keys ( tiddler . fields ) ;
$tw . utils . each ( fields , function ( fieldName ) {
var p = searchFields . indexOf ( fieldName ) ;
if ( p !== - 1 ) {
searchFields . splice ( p , 1 ) ;
}
} ) ;
} else {
searchFields = fields ;
}
2018-12-17 11:19:48 +00:00
for ( var fieldIndex = 0 ; notYetFound . length > 0 && fieldIndex < searchFields . length ; fieldIndex ++ ) {
2018-10-30 17:39:18 +00:00
// Don't search the text field if the content type is binary
var fieldName = searchFields [ fieldIndex ] ;
if ( fieldName === "text" && contentTypeInfo . encoding !== "utf8" ) {
break ;
}
2018-12-17 11:19:48 +00:00
var str = tiddler . fields [ fieldName ] ,
2018-10-30 17:39:18 +00:00
t ;
if ( str ) {
if ( $tw . utils . isArray ( str ) ) {
// If the field value is an array, test each regexp against each field array entry and fail if each regexp doesn't match at least one field array entry
2018-12-17 11:19:48 +00:00
for ( var s = 0 ; s < str . length ; s ++ ) {
for ( t = 0 ; t < notYetFound . length ; ) {
if ( notYetFound [ t ] . test ( str [ s ] ) ) {
notYetFound . splice ( t , 1 ) ;
} else {
t ++ ;
2018-10-30 17:39:18 +00:00
}
}
}
} else {
// If the field isn't an array, force it to a string and test each regexp against it and fail if any do not match
str = tiddler . getFieldString ( fieldName ) ;
2018-12-17 11:19:48 +00:00
for ( t = 0 ; t < notYetFound . length ; ) {
if ( notYetFound [ t ] . test ( str ) ) {
notYetFound . splice ( t , 1 ) ;
} else {
t ++ ;
2018-10-30 17:39:18 +00:00
}
}
2014-11-06 20:56:32 +00:00
}
2012-10-17 19:38:36 +00:00
}
2018-10-30 17:39:18 +00:00
} ;
2018-12-17 11:19:48 +00:00
return notYetFound . length == 0 ;
2012-11-06 17:21:56 +00:00
} ;
2012-10-17 13:34:59 +00:00
// Loop through all the tiddlers doing the search
2014-04-03 19:49:16 +00:00
var results = [ ] ,
source = options . source || this . each ;
source ( function ( tiddler , title ) {
2021-03-12 21:12:06 +00:00
if ( searchTiddler ( title ) !== invert ) {
2014-04-03 19:49:16 +00:00
results . push ( title ) ;
2012-10-17 13:34:59 +00:00
}
2014-04-03 19:49:16 +00:00
} ) ;
2012-10-17 13:57:13 +00:00
// Remove any of the results we have to exclude
if ( options . exclude ) {
for ( t = 0 ; t < options . exclude . length ; t ++ ) {
var p = results . indexOf ( options . exclude [ t ] ) ;
if ( p !== - 1 ) {
results . splice ( p , 1 ) ;
}
}
}
2012-10-17 13:34:59 +00:00
return results ;
} ;
2012-11-18 13:14:28 +00:00
/ *
Trigger a load for a tiddler if it is skinny . Returns the text , or undefined if the tiddler is missing , null if the tiddler is being lazily loaded .
* /
2013-01-15 17:50:47 +00:00
exports . getTiddlerText = function ( title , defaultText ) {
2012-11-18 13:14:28 +00:00
var tiddler = this . getTiddler ( title ) ;
// Return undefined if the tiddler isn't found
if ( ! tiddler ) {
2013-01-15 17:50:47 +00:00
return defaultText ;
2012-11-18 13:14:28 +00:00
}
2020-03-30 14:24:05 +00:00
if ( ! tiddler . hasField ( "_is_skinny" ) ) {
2012-11-18 13:14:28 +00:00
// Just return the text if we've got it
2020-03-30 14:24:05 +00:00
return tiddler . fields . text || "" ;
2012-11-18 13:14:28 +00:00
} else {
2013-03-16 10:50:36 +00:00
// Tell any listeners about the need to lazily load this tiddler
this . dispatchEvent ( "lazyLoad" , title ) ;
2012-11-18 13:14:28 +00:00
// Indicate that the text is being loaded
return null ;
}
} ;
2017-02-09 15:43:28 +00:00
/ *
Check whether the text of a tiddler matches a given value . By default , the comparison is case insensitive , and any spaces at either end of the tiddler text is trimmed
* /
exports . checkTiddlerText = function ( title , targetText , options ) {
options = options || { } ;
var text = this . getTiddlerText ( title , "" ) ;
if ( ! options . noTrim ) {
text = text . trim ( ) ;
}
if ( ! options . caseSensitive ) {
text = text . toLowerCase ( ) ;
targetText = targetText . toLowerCase ( ) ;
}
return text === targetText ;
}
2023-06-13 09:35:55 +00:00
/ *
Execute an action string without an associated context widget
* /
exports . invokeActionString = function ( actions , event , variables , options ) {
var widget = this . makeWidget ( null , { parentWidget : options . parentWidget } ) ;
widget . invokeActionString ( actions , null , event , variables ) ;
} ;
2013-10-25 20:15:20 +00:00
/ *
2013-12-23 08:55:11 +00:00
Read an array of browser File objects , invoking callback ( tiddlerFieldsArray ) once they ' re all read
2013-10-25 20:15:20 +00:00
* /
2017-07-12 15:42:16 +00:00
exports . readFiles = function ( files , options ) {
var callback ;
if ( typeof options === "function" ) {
callback = options ;
options = { } ;
} else {
callback = options . callback ;
}
2013-12-23 08:55:11 +00:00
var result = [ ] ,
2017-07-12 15:42:16 +00:00
outstanding = files . length ,
readFileCallback = function ( tiddlerFieldsArray ) {
2013-12-23 08:55:11 +00:00
result . push . apply ( result , tiddlerFieldsArray ) ;
if ( -- outstanding === 0 ) {
callback ( result ) ;
}
2017-07-12 15:42:16 +00:00
} ;
for ( var f = 0 ; f < files . length ; f ++ ) {
2018-09-21 09:56:01 +00:00
this . readFile ( files [ f ] , $tw . utils . extend ( { } , options , { callback : readFileCallback } ) ) ;
2014-08-30 19:44:26 +00:00
}
2014-01-24 14:09:06 +00:00
return files . length ;
2013-10-25 20:15:20 +00:00
} ;
/ *
2013-12-23 08:55:11 +00:00
Read a browser File object , invoking callback ( tiddlerFieldsArray ) with an array of tiddler fields objects
2013-10-25 20:15:20 +00:00
* /
2017-07-12 15:42:16 +00:00
exports . readFile = function ( file , options ) {
var callback ;
if ( typeof options === "function" ) {
callback = options ;
options = { } ;
} else {
callback = options . callback ;
}
2013-10-25 20:15:20 +00:00
// Get the type, falling back to the filename extension
var self = this ,
type = file . type ;
if ( type === "" || ! type ) {
var dotPos = file . name . lastIndexOf ( "." ) ;
if ( dotPos !== - 1 ) {
2015-02-01 18:33:40 +00:00
var fileExtensionInfo = $tw . utils . getFileExtensionInfo ( file . name . substr ( dotPos ) ) ;
2013-10-25 20:15:20 +00:00
if ( fileExtensionInfo ) {
type = fileExtensionInfo . type ;
}
}
}
// Figure out if we're reading a binary file
var contentTypeInfo = $tw . config . contentTypeInfo [ type ] ,
isBinary = contentTypeInfo ? contentTypeInfo . encoding === "base64" : false ;
2014-11-08 08:37:08 +00:00
// Log some debugging information
2014-11-14 10:33:41 +00:00
if ( $tw . log . IMPORT ) {
2014-11-08 08:37:08 +00:00
console . log ( "Importing file '" + file . name + "', type: '" + type + "', isBinary: " + isBinary ) ;
}
2017-10-11 16:52:37 +00:00
// Give the hook a chance to process the drag
if ( $tw . hooks . invokeHook ( "th-importing-file" , {
file : file ,
type : type ,
isBinary : isBinary ,
callback : callback
} ) !== true ) {
this . readFileContent ( file , type , isBinary , options . deserializer , callback ) ;
}
} ;
/ *
Lower level utility to read the content of a browser File object , invoking callback ( tiddlerFieldsArray ) with an array of tiddler fields objects
* /
exports . readFileContent = function ( file , type , isBinary , deserializer , callback ) {
var self = this ;
2013-10-25 20:15:20 +00:00
// Create the FileReader
var reader = new FileReader ( ) ;
// Onload
reader . onload = function ( event ) {
2014-01-19 18:43:02 +00:00
var text = event . target . result ,
2020-11-14 12:05:35 +00:00
tiddlerFields = { title : file . name || "Untitled" } ;
2013-10-25 20:15:20 +00:00
if ( isBinary ) {
2014-01-19 18:43:02 +00:00
var commaPos = text . indexOf ( "," ) ;
2013-10-25 20:15:20 +00:00
if ( commaPos !== - 1 ) {
2016-08-20 16:09:22 +00:00
text = text . substr ( commaPos + 1 ) ;
2013-10-25 20:15:20 +00:00
}
2016-08-20 16:09:22 +00:00
}
// Check whether this is an encrypted TiddlyWiki file
var encryptedJson = $tw . utils . extractEncryptedStoreArea ( text ) ;
if ( encryptedJson ) {
// If so, attempt to decrypt it with the current password
$tw . utils . decryptStoreAreaInteractive ( encryptedJson , function ( tiddlers ) {
callback ( tiddlers ) ;
} ) ;
2013-10-25 20:15:20 +00:00
} else {
2016-08-20 16:09:22 +00:00
// Otherwise, just try to deserialise any tiddlers in the file
2017-10-11 16:52:37 +00:00
callback ( self . deserializeTiddlers ( type , text , tiddlerFields , { deserializer : deserializer } ) ) ;
2013-10-25 20:15:20 +00:00
}
} ;
// Kick off the read
if ( isBinary ) {
reader . readAsDataURL ( file ) ;
} else {
reader . readAsText ( file ) ;
}
} ;
2014-10-09 16:30:53 +00:00
/ *
Find any existing draft of a specified tiddler
* /
exports . findDraft = function ( targetTitle ) {
var draftTitle = undefined ;
this . forEachTiddler ( { includeSystem : true } , function ( title , tiddler ) {
if ( tiddler . fields [ "draft.title" ] && tiddler . fields [ "draft.of" ] === targetTitle ) {
draftTitle = title ;
}
} ) ;
return draftTitle ;
}
2014-04-28 14:16:31 +00:00
/ *
2015-12-28 10:29:43 +00:00
Check whether the specified draft tiddler has been modified .
If the original tiddler doesn ' t exist , create a vanilla tiddler variable ,
2015-12-28 10:31:36 +00:00
to check if additional fields have been added .
2014-04-28 14:16:31 +00:00
* /
2014-05-03 10:32:55 +00:00
exports . isDraftModified = function ( title ) {
2014-06-23 07:10:32 +00:00
var tiddler = this . getTiddler ( title ) ;
2014-04-28 14:16:31 +00:00
if ( ! tiddler . isDraft ( ) ) {
return false ;
}
2014-06-23 22:28:22 +00:00
var ignoredFields = [ "created" , "modified" , "title" , "draft.title" , "draft.of" ] ,
2015-12-28 10:29:43 +00:00
origTiddler = this . getTiddler ( tiddler . fields [ "draft.of" ] ) || new $tw . Tiddler ( { text : "" , tags : [ ] } ) ,
titleModified = tiddler . fields [ "draft.title" ] !== tiddler . fields [ "draft.of" ] ;
return titleModified || ! tiddler . isEqual ( origTiddler , ignoredFields ) ;
2014-04-28 14:16:31 +00:00
} ;
2014-05-03 10:32:55 +00:00
/ *
Add a new record to the top of the history stack
title : a title string or an array of title strings
fromPageRect : page coordinates of the origin of the navigation
historyTitle : title of history tiddler ( defaults to $ : / H i s t o r y L i s t )
* /
exports . addToHistory = function ( title , fromPageRect , historyTitle ) {
2020-01-27 17:59:13 +00:00
var story = new $tw . Story ( { wiki : this , historyTitle : historyTitle } ) ;
2021-05-30 18:20:17 +00:00
story . addToHistory ( title , fromPageRect ) ;
2020-11-02 22:52:02 +00:00
console . log ( "$tw.wiki.addToHistory() is deprecated since V5.1.23! Use the this.story.addToHistory() from the story-object!" )
2014-05-03 10:32:55 +00:00
} ;
2018-08-23 17:31:48 +00:00
/ *
Add a new tiddler to the story river
title : a title string or an array of title strings
fromTitle : the title of the tiddler from which the navigation originated
storyTitle : title of story tiddler ( defaults to $ : / S t o r y L i s t )
options : see story . js
* /
exports . addToStory = function ( title , fromTitle , storyTitle , options ) {
2020-01-27 17:59:13 +00:00
var story = new $tw . Story ( { wiki : this , storyTitle : storyTitle } ) ;
2020-11-02 22:52:02 +00:00
story . addToStory ( title , fromTitle , options ) ;
console . log ( "$tw.wiki.addToStory() is deprecated since V5.1.23! Use the this.story.addToStory() from the story-object!" )
2018-08-23 17:31:48 +00:00
} ;
2019-06-04 11:33:01 +00:00
/ *
Generate a title for the draft of a given tiddler
* /
exports . generateDraftTitle = function ( title ) {
var c = 0 ,
draftTitle ,
username = this . getTiddlerText ( "$:/status/UserName" ) ,
attribution = username ? " by " + username : "" ;
do {
draftTitle = "Draft " + ( c ? ( c + 1 ) + " " : "" ) + "of '" + title + "'" + attribution ;
c ++ ;
} while ( this . tiddlerExists ( draftTitle ) ) ;
return draftTitle ;
} ;
2014-07-12 08:09:36 +00:00
/ *
Invoke the available upgrader modules
titles : array of tiddler titles to be processed
tiddlers : hashmap by title of tiddler fields of pending import tiddlers . These can be modified by the upgraders . An entry with no fields indicates a tiddler that was pending import has been suppressed . When entries are added to the pending import the tiddlers hashmap may have entries that are not present in the titles array
Returns a hashmap of messages keyed by tiddler title .
* /
exports . invokeUpgraders = function ( titles , tiddlers ) {
// Collect up the available upgrader modules
var self = this ;
if ( ! this . upgraderModules ) {
this . upgraderModules = [ ] ;
$tw . modules . forEachModuleOfType ( "upgrader" , function ( title , module ) {
if ( module . upgrade ) {
self . upgraderModules . push ( module ) ;
}
} ) ;
}
// Invoke each upgrader in turn
var messages = { } ;
for ( var t = 0 ; t < this . upgraderModules . length ; t ++ ) {
var upgrader = this . upgraderModules [ t ] ,
upgraderMessages = upgrader . upgrade ( this , titles , tiddlers ) ;
$tw . utils . extend ( messages , upgraderMessages ) ;
}
return messages ;
} ;
2019-09-27 15:47:55 +00:00
// Determine whether a plugin by title is dynamically loadable
exports . doesPluginRequireReload = function ( title ) {
2021-05-23 10:19:46 +00:00
var tiddler = this . getTiddler ( title ) ;
if ( tiddler && tiddler . fields . type === "application/json" && tiddler . fields [ "plugin-type" ] ) {
if ( tiddler . fields [ "plugin-type" ] === "import" ) {
// The import plugin never requires reloading
return false ;
}
}
2019-09-27 15:47:55 +00:00
return this . doesPluginInfoRequireReload ( this . getPluginInfo ( title ) || this . getTiddlerDataCached ( title ) ) ;
2019-09-16 11:15:39 +00:00
} ;
2019-09-27 15:47:55 +00:00
// Determine whether a plugin info structure is dynamically loadable
exports . doesPluginInfoRequireReload = function ( pluginInfo ) {
2019-09-16 11:15:39 +00:00
if ( pluginInfo ) {
var foundModule = false ;
$tw . utils . each ( pluginInfo . tiddlers , function ( tiddler ) {
if ( tiddler . type === "application/javascript" && $tw . utils . hop ( tiddler , "module-type" ) ) {
foundModule = true ;
}
} ) ;
return foundModule ;
} else {
return null ;
}
} ;
2020-05-09 14:54:44 +00:00
exports . slugify = function ( title , options ) {
var tiddler = this . getTiddler ( title ) ,
slug ;
if ( tiddler && tiddler . fields . slug ) {
slug = tiddler . fields . slug ;
} else {
slug = $tw . utils . transliterate ( title . toString ( ) . toLowerCase ( ) ) // Replace diacritics with basic lowercase ASCII
. replace ( /\s+/g , "-" ) // Replace spaces with -
. replace ( /[^\w\-\.]+/g , "" ) // Remove all non-word chars except dash and dot
. replace ( /\-\-+/g , "-" ) // Replace multiple - with single -
. replace ( /^-+/ , "" ) // Trim - from start of text
. replace ( /-+$/ , "" ) ; // Trim - from end of text
}
// If the resulting slug is blank (eg because the title is just punctuation characters)
if ( ! slug ) {
// ...then just use the character codes of the title
var result = [ ] ;
$tw . utils . each ( title . split ( "" ) , function ( char ) {
result . push ( char . charCodeAt ( 0 ) . toString ( ) ) ;
} ) ;
slug = result . join ( "-" ) ;
}
return slug ;
} ;
2016-08-20 16:09:22 +00:00
} ) ( ) ;