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
2013-08-04 13:02:07 +00:00
var USER _NAME _TITLE = "$:/status/UserName" ;
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
return title ;
} 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-03-12 21:54:43 +00:00
// Check if it is a reference to a tiddler field
if ( tr . index ) {
2014-04-06 21:43:10 +00:00
var data = this . getTiddlerData ( title , Object . create ( null ) ) ;
2014-03-12 21:54:43 +00:00
data [ tr . index ] = value ;
this . setTiddlerData ( title , data , this . getModificationFields ( ) ) ;
} else {
var tiddler = this . getTiddler ( title ) ,
fields = { title : title } ;
fields [ tr . field || "text" ] = value ;
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
} ;
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 ] || [ ] ;
this . eventListeners [ type ] . push ( listener ) ;
} ;
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
this . eventListeners = this . eventListeners || [ ] ;
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 ;
self . dispatchEvent ( "change" , changes ) ;
2012-04-30 11:23:03 +00:00
} ) ;
this . eventsTriggered = true ;
}
} ;
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
* /
2014-02-14 07:53:41 +00:00
exports . generateNewTitle = function ( baseTitle , options ) {
options = options || { } ;
2013-12-19 19:38:59 +00:00
var c = 0 ,
2013-12-18 22:48:04 +00:00
title = baseTitle ;
2014-02-20 09:58:01 +00:00
while ( this . tiddlerExists ( title ) || this . isShadowTiddler ( title ) ) {
2014-02-14 07:53:41 +00:00
title = baseTitle +
( options . prefix || " " ) +
( ++ c ) ;
2013-12-18 22:48:04 +00:00
} ;
2013-10-25 20:15:20 +00:00
return title ;
} ;
2013-04-03 13:29:12 +00:00
exports . isSystemTiddler = function ( title ) {
return title . indexOf ( "$:/" ) === 0 ;
} ;
exports . isTemporaryTiddler = function ( title ) {
return title . indexOf ( "$:/temp/" ) === 0 ;
} ;
2014-01-29 09:04:41 +00:00
exports . isImageTiddler = function ( title ) {
var tiddler = this . getTiddler ( title ) ;
if ( tiddler ) {
var contentTypeInfo = $tw . config . contentTypeInfo [ tiddler . fields . type || "text/vnd.tiddlywiki" ] ;
return ! ! contentTypeInfo && contentTypeInfo . flags . indexOf ( "image" ) !== - 1 ;
} 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
if ( $tw . utils . checkVersions ( existingTiddler . fields . version , tiddler . fields . version ) ) {
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 ( ) {
var fields = {
created : new Date ( )
} ,
creator = this . getTiddlerText ( USER _NAME _TITLE ) ;
if ( creator ) {
fields . creator = creator ;
}
return fields ;
} ;
/ *
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 ( ) {
2014-04-06 21:43:10 +00:00
var fields = Object . create ( null ) ,
2013-08-04 13:02:07 +00:00
modifier = this . getTiddlerText ( USER _NAME _TITLE ) ;
2013-08-06 14:27:02 +00:00
fields . modified = new Date ( ) ;
2013-08-04 13:02:07 +00:00
if ( modifier ) {
fields . modifier = modifier ;
}
return fields ;
} ;
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
* /
2013-12-28 23:15:11 +00:00
exports . sortTiddlers = function ( titles , sortField , isDescending , isCaseSensitive , isNumeric ) {
2012-05-08 15:02:24 +00:00
var self = this ;
titles . sort ( function ( a , b ) {
2012-06-19 09:40:05 +00:00
if ( sortField !== "title" ) {
2012-10-16 20:41:12 +00:00
a = self . getTiddler ( a ) . fields [ sortField ] || "" ;
b = self . getTiddler ( b ) . fields [ sortField ] || "" ;
2012-06-19 09:40:05 +00:00
}
2014-04-17 19:15:52 +00:00
if ( isNumeric ) {
a = Number ( a ) ;
b = Number ( b ) ;
return isDescending ? b - a : a - b ;
2014-04-17 21:52:57 +00:00
} else if ( $tw . utils . isDate ( a ) && $tw . utils . isDate ( b ) ) {
return isDescending ? b - a : a - b ;
2012-05-08 15:02:24 +00:00
} else {
2014-04-17 19:15:52 +00:00
a = String ( a ) ;
b = String ( b ) ;
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 ) ;
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 ) ;
}
}
} ;
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
// Count up the links
var links = [ ] ,
checkParseTree = function ( parseTree ) {
for ( var t = 0 ; t < parseTree . length ; t ++ ) {
var parseTreeNode = parseTree [ t ] ;
2013-10-27 22:55:36 +00:00
if ( parseTreeNode . type === "link" && parseTreeNode . attributes . to && parseTreeNode . attributes . to . type === "string" ) {
2013-03-19 10:14:44 +00:00
var value = parseTreeNode . attributes . to . value ;
if ( links . indexOf ( value ) === - 1 ) {
links . push ( value ) ;
}
}
if ( parseTreeNode . children ) {
checkParseTree ( parseTreeNode . children ) ;
}
}
} ;
if ( parser ) {
2013-03-21 22:21:00 +00:00
checkParseTree ( parser . tree ) ;
2013-03-19 10:14:44 +00:00
}
return links ;
} ) ;
} ;
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 ,
backlinks = [ ] ;
this . forEachTiddler ( function ( title , tiddler ) {
var links = self . getTiddlerLinks ( title ) ;
if ( links . indexOf ( targetTitle ) !== - 1 ) {
backlinks . push ( title ) ;
}
} ) ;
return backlinks ;
} ;
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 ) {
2013-11-13 23:33:32 +00:00
var self = this ;
return this . getGlobalCache ( "taglist-" + tag , function ( ) {
var tagmap = self . getTagMap ( ) ;
return self . sortByList ( tagmap [ tag ] , tag ) ;
} ) ;
} ;
/ *
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 ) ) {
tags [ tag ] . push ( title )
} 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
/ *
Lookup a given tiddler and return a list of all the tiddlers that include it in their list
* /
exports . findListingsOfTiddler = function ( targetTitle ) {
// Get the list associated with the tag
var titles = [ ] ;
2014-03-16 21:23:10 +00:00
this . each ( function ( tiddler , title ) {
2013-08-16 08:31:05 +00:00
if ( $tw . utils . isArray ( tiddler . fields . list ) && tiddler . fields . list . indexOf ( targetTitle ) !== - 1 ) {
titles . push ( title ) ;
}
2014-03-16 21:23:10 +00:00
} ) ;
2013-08-16 08:31:05 +00:00
return titles ;
} ;
2013-08-08 16:39:34 +00:00
/ *
Sorts an array of tiddler titles according to an ordered list
* /
exports . sortByList = function ( array , listTitle ) {
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 {
2013-08-08 16:39:34 +00:00
var titles = [ ] , t , title ;
// 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 ) ;
}
}
// Then place any remaining entries
for ( t = 0 ; t < array . length ; t ++ ) {
title = array [ t ] ;
if ( list . indexOf ( title ) === - 1 ) {
2014-04-14 20:30:30 +00:00
titles . push ( title ) ;
}
}
// Finally obey the list-before and list-after fields of each tiddler in turn
var sortedTitles = titles . slice ( 0 ) ;
for ( t = 0 ; t < sortedTitles . length ; t ++ ) {
title = sortedTitles [ t ] ;
var currPos = titles . indexOf ( title ) ,
newPos = - 1 ,
tiddler = this . getTiddler ( title ) ;
if ( tiddler ) {
var beforeTitle = tiddler . fields [ "list-before" ] ,
afterTitle = tiddler . fields [ "list-after" ] ;
if ( beforeTitle === "" ) {
newPos = 0 ;
} else if ( beforeTitle ) {
newPos = titles . indexOf ( beforeTitle ) ;
} else if ( afterTitle ) {
newPos = titles . indexOf ( afterTitle ) ;
if ( newPos >= 0 ) {
++ newPos ;
2014-03-12 14:19:26 +00:00
}
}
2014-04-14 20:30:30 +00:00
if ( newPos === - 1 ) {
newPos = currPos ;
}
if ( newPos !== currPos ) {
titles . splice ( currPos , 1 ) ;
if ( newPos >= currPos ) {
newPos -- ;
}
titles . splice ( newPos , 0 , title ) ;
2014-03-12 14:19:26 +00:00
}
2013-08-08 16:39:34 +00:00
}
2014-04-14 20:30:30 +00:00
2013-08-08 16:39:34 +00:00
}
return titles ;
}
2012-06-13 08:10:03 +00:00
} ;
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 } ) ;
}
} ;
2012-07-22 21:03:06 +00:00
/ *
Get a tiddlers content as a JavaScript object . How this is done depends on the type of the tiddler :
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 .
* /
exports . getTiddlerData = function ( title , defaultData ) {
2013-03-28 17:07:30 +00:00
var tiddler = this . getTiddler ( title ) ,
2012-07-22 21:03:06 +00:00
data ;
2013-01-15 17:50:47 +00:00
if ( tiddler && tiddler . fields . text ) {
switch ( tiddler . fields . type ) {
case "application/json" :
// JSON tiddler
try {
data = JSON . parse ( tiddler . fields . text ) ;
} catch ( ex ) {
return defaultData ;
}
return data ;
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
* /
exports . extractTiddlerDataItem = function ( title , index , defaultText ) {
2014-04-06 21:43:10 +00:00
var data = this . getTiddlerData ( title , 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
2012-07-22 21:03:06 +00:00
* /
2014-02-13 17:59:33 +00:00
exports . setTiddlerData = function ( title , data , fields ) {
2014-02-23 12:33:00 +00:00
var existingTiddler = this . getTiddler ( title ) ,
newFields = {
title : title
} ;
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 ) ;
}
this . addTiddler ( new $tw . Tiddler ( existingTiddler , fields , newFields , this . getModificationFields ( ) ) ) ;
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 ) ;
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 ) {
2013-10-12 16:05:13 +00:00
// Temporarily disable caching so that tweakParseTreeNode() works
return initializer ( ) ;
2014-04-06 21:43:10 +00:00
this . caches = this . caches || Object . create ( null ) ;
2012-04-30 11:23:03 +00:00
var caches = this . caches [ title ] ;
if ( caches && caches [ cacheName ] ) {
return caches [ cacheName ] ;
} else {
if ( ! caches ) {
2014-04-06 21:43:10 +00:00
caches = Object . create ( null ) ;
2012-04-30 11:23:03 +00:00
this . caches [ title ] = caches ;
}
caches [ cacheName ] = initializer ( ) ;
return caches [ cacheName ] ;
}
} ;
// Clear all caches associated with a particular tiddler
exports . clearCache = function ( title ) {
2014-04-06 21:43:10 +00:00
this . caches = this . caches || Object . create ( null ) ;
2012-05-09 07:51:16 +00:00
if ( $tw . utils . hop ( this . caches , title ) ) {
2012-04-30 11:23:03 +00:00
delete this . caches [ title ] ;
}
} ;
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
}
}
} ) ;
} ;
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
2012-12-13 21:34:31 +00:00
* /
2013-10-27 22:55:36 +00:00
exports . old _parseText = function ( type , text , options ) {
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 ] ;
2012-12-27 17:08:29 +00:00
if ( ! Parser && $tw . config . fileExtensionInfo [ type ] ) {
2013-04-25 16:40:12 +00:00
Parser = $tw . Wiki . parsers [ $tw . config . fileExtensionInfo [ 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 ,
2012-12-27 17:08:29 +00:00
wiki : this
2012-12-26 22:02:59 +00:00
} ) ;
2012-12-13 21:34:31 +00:00
} ;
/ *
Parse a tiddler according to its MIME type
* /
2013-10-27 22:55:36 +00:00
exports . old _parseTiddler = function ( title , options ) {
2012-12-20 15:07:38 +00:00
options = options || { } ;
var cacheType = options . parseAsInline ? "newInlineParseTree" : "newBlockParseTree" ,
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 ( ) {
2013-10-27 22:55:36 +00:00
return self . old _parseText ( tiddler . fields . type , tiddler . fields . text , options ) ;
2012-12-13 21:34:31 +00:00
} ) : null ;
} ;
2013-10-12 16:05:13 +00:00
// We need to tweak parse trees generated by the existing parser because of the change from {type:"element",tag:"$tiddler",...} to {type:"tiddler",...}
var tweakParseTreeNode = function ( node ) {
2013-10-13 21:39:05 +00:00
if ( node . type === "element" && node . tag . charAt ( 0 ) === "$" ) {
node . type = node . tag . substr ( 1 ) ;
2013-10-27 22:55:36 +00:00
delete node . tag ;
2013-10-13 21:39:05 +00:00
}
tweakParseTreeNodes ( node . children ) ;
} ;
var tweakParseTreeNodes = function ( nodeList ) {
$tw . utils . each ( nodeList , tweakParseTreeNode ) ;
} ;
var tweakMacroDefinition = function ( nodeList ) {
if ( nodeList && nodeList [ 0 ] && nodeList [ 0 ] . type === "macrodef" ) {
2013-11-15 18:31:39 +00:00
nodeList [ 0 ] . type = "set" ;
2013-10-13 22:40:11 +00:00
nodeList [ 0 ] . attributes = {
name : { type : "string" , value : nodeList [ 0 ] . name } ,
value : { type : "string" , value : nodeList [ 0 ] . text }
} ;
2013-10-13 21:39:05 +00:00
nodeList [ 0 ] . children = nodeList . slice ( 1 ) ;
nodeList . splice ( 1 , nodeList . length - 1 ) ;
2013-10-21 17:32:29 +00:00
tweakMacroDefinition ( nodeList [ 0 ] . children ) ;
2013-10-13 21:39:05 +00:00
}
} ;
var tweakParser = function ( parser ) {
// Move any macro definitions to contain the body tree
tweakMacroDefinition ( parser . tree ) ;
// Tweak widgets
tweakParseTreeNodes ( parser . tree ) ;
} ;
2013-10-12 16:05:13 +00:00
2013-11-08 08:51:14 +00:00
exports . parseText = function ( type , text , options ) {
2013-10-27 22:55:36 +00:00
var parser = this . old _parseText ( type , text , options ) ;
2013-10-12 16:05:13 +00:00
if ( parser ) {
2013-10-13 21:39:05 +00:00
tweakParser ( parser )
2013-10-12 16:05:13 +00:00
} ;
return parser ;
} ;
2013-11-08 08:51:14 +00:00
exports . parseTiddler = function ( title , options ) {
2013-10-27 22:55:36 +00:00
var parser = this . old _parseTiddler ( title , options ) ;
2013-10-12 16:05:13 +00:00
if ( parser ) {
2013-10-13 21:39:05 +00:00
tweakParser ( parser )
2013-10-12 16:05:13 +00:00
} ;
return parser ;
} ;
2013-11-08 08:51:14 +00:00
exports . parseTextReference = function ( title , field , index , options ) {
2013-10-12 16:05:13 +00:00
if ( field === "text" || ( ! field && ! index ) ) {
2013-11-08 20:18:26 +00:00
// Force the tiddler to be lazily loaded
this . getTiddlerText ( title ) ;
// Parse it
2013-11-08 08:51:14 +00:00
return this . parseTiddler ( title , options ) ;
2013-10-12 16:05:13 +00:00
} else {
2014-02-20 09:58:01 +00:00
var text ;
2013-10-12 16:05:13 +00:00
if ( field ) {
2014-02-20 09:58:01 +00:00
if ( field === "title" ) {
text = title ;
} else {
var tiddler = this . getTiddler ( title ) ;
if ( ! tiddler || ! tiddler . hasField ( field ) ) {
return null ;
}
text = tiddler . fields [ field ] ;
2013-10-12 16:05:13 +00:00
}
2013-12-11 15:51:32 +00:00
return this . parseText ( "text/vnd.tiddlywiki" , text . toString ( ) , options ) ;
2013-10-12 16:05:13 +00:00
} else if ( index ) {
text = this . extractTiddlerDataItem ( title , index , "" ) ;
2014-02-12 21:38:37 +00:00
if ( text === undefined ) {
return null ;
}
2013-11-08 08:51:14 +00:00
return this . parseText ( "text/vnd.tiddlywiki" , text , options ) ;
2013-10-12 16:05:13 +00:00
}
}
} ;
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 ;
2013-11-15 18:31:39 +00:00
// Create set variable widgets for each variable
2013-10-29 14:48:24 +00:00
$tw . utils . each ( options . variables , function ( value , name ) {
var setVariableWidget = {
2013-11-15 18:31:39 +00:00
type : "set" ,
2013-10-29 14:48:24 +00:00
attributes : {
name : { type : "string" , value : name } ,
value : { type : "string" , value : value }
} ,
children : [ ]
} ;
currWidgetNode . children = [ setVariableWidget ] ;
currWidgetNode = setVariableWidget ;
} ) ;
// 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
} ) ;
} ;
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 :
2014-04-03 19:49:16 +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
literal : If true , searches for literal string , rather than separate search terms
2012-10-17 13:34:59 +00:00
* /
2012-10-17 13:57:13 +00:00
exports . search = function ( text , options ) {
options = options || { } ;
2013-03-17 15:06:09 +00:00
var self = this , t ;
2012-10-17 19:38:36 +00:00
// Convert the search string into a regexp for each term
var terms , searchTermsRegExps ,
flags = options . caseSensitive ? "" : "i" ;
if ( options . literal ) {
if ( text . length === 0 ) {
2013-08-24 15:51:54 +00:00
searchTermsRegExps = null ;
} else {
searchTermsRegExps = [ new RegExp ( "(" + $tw . utils . escapeRegExp ( text ) + ")" , flags ) ] ;
2012-10-17 19:38:36 +00:00
}
} else {
2013-12-20 18:14:11 +00:00
terms = text . split ( / +/ ) ;
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 ++ ) {
searchTermsRegExps . push ( new RegExp ( "(" + $tw . utils . escapeRegExp ( terms [ t ] ) + ")" , flags ) ) ;
}
2012-10-17 19:38:36 +00:00
}
}
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
}
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" ] ,
match ;
2013-10-17 15:34:50 +00:00
for ( var t = 0 ; t < searchTermsRegExps . length ; t ++ ) {
2013-10-18 15:09:10 +00:00
// Search title, tags and body
match = false ;
if ( contentTypeInfo . encoding === "utf8" ) {
match = match || searchTermsRegExps [ t ] . test ( tiddler . fields . text ) ;
}
var tags = tiddler . fields . tags ? tiddler . fields . tags . join ( "\0" ) : "" ;
match = match || searchTermsRegExps [ t ] . test ( tags ) || searchTermsRegExps [ t ] . test ( tiddler . fields . title ) ;
if ( ! match ) {
return false ;
2012-10-17 19:38:36 +00:00
}
}
2013-10-18 15:09:10 +00:00
return true ;
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 ) {
if ( ! ! searchTiddler ( title ) === ! options . invert ) {
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
}
2012-11-23 13:08:10 +00:00
if ( tiddler . fields . text !== undefined ) {
2012-11-18 13:14:28 +00:00
// Just return the text if we've got it
return tiddler . fields . text ;
} 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 ;
}
} ;
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
* /
exports . readFiles = function ( files , callback ) {
2013-12-23 08:55:11 +00:00
var result = [ ] ,
outstanding = files . length ;
2013-10-25 20:15:20 +00:00
for ( var f = 0 ; f < files . length ; f ++ ) {
2013-12-23 08:55:11 +00:00
this . readFile ( files [ f ] , function ( tiddlerFieldsArray ) {
result . push . apply ( result , tiddlerFieldsArray ) ;
if ( -- outstanding === 0 ) {
callback ( result ) ;
}
} ) ;
2013-10-25 20:15:20 +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
* /
exports . readFile = function ( file , callback ) {
// 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 ) {
var fileExtensionInfo = $tw . config . fileExtensionInfo [ file . name . substr ( dotPos ) ] ;
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 ;
// Create the FileReader
var reader = new FileReader ( ) ;
// Onload
reader . onload = function ( event ) {
// Deserialise the file contents
2014-01-19 18:43:02 +00:00
var text = event . target . result ,
tiddlerFields = { title : file . name || "Untitled" , type : type } ;
2013-10-25 20:15:20 +00:00
// Are we binary?
if ( isBinary ) {
// The base64 section starts after the first comma in the data URI
2014-01-19 18:43:02 +00:00
var commaPos = text . indexOf ( "," ) ;
2013-10-25 20:15:20 +00:00
if ( commaPos !== - 1 ) {
2014-01-19 18:43:02 +00:00
tiddlerFields . text = text . substr ( commaPos + 1 ) ;
2013-12-23 08:55:11 +00:00
callback ( [ tiddlerFields ] ) ;
2013-10-25 20:15:20 +00:00
}
} else {
2014-01-19 18:43:02 +00:00
// Check whether this is an encrypted TiddlyWiki file
2014-01-19 20:13:55 +00:00
var encryptedJson = $tw . utils . extractEncryptedStoreArea ( text ) ;
if ( encryptedJson ) {
// If so, attempt to decrypt it with the current password
$tw . utils . decryptStoreAreaInteractive ( encryptedJson , function ( tiddlers ) {
2014-01-19 18:43:02 +00:00
callback ( tiddlers ) ;
2014-01-19 20:13:55 +00:00
} ) ;
2014-01-19 18:43:02 +00:00
} else {
2014-01-19 20:13:55 +00:00
// Otherwise, just try to deserialise any tiddlers in the file
2014-01-19 18:43:02 +00:00
callback ( self . deserializeTiddlers ( type , text , tiddlerFields ) ) ;
}
2013-10-25 20:15:20 +00:00
}
} ;
// Kick off the read
if ( isBinary ) {
reader . readAsDataURL ( file ) ;
} else {
reader . readAsText ( file ) ;
}
} ;
2012-04-30 11:23:03 +00:00
} ) ( ) ;