2012-04-30 11:23:03 +00:00
/ * \
2013-05-31 15:53:19 +00:00
title : $ : / b o o t / b o o t . j s
2012-04-30 11:23:03 +00:00
type : application / javascript
2012-11-14 11:23:43 +00:00
The main boot kernel for TiddlyWiki . This single file creates a barebones TW environment that is just sufficient to bootstrap the modules containing the main logic of the application .
2012-04-30 11:23:03 +00:00
2012-11-14 11:23:43 +00:00
On the server this file is executed directly to boot TiddlyWiki . In the browser , this file is packed into a single HTML file along with other elements :
2012-04-30 11:23:03 +00:00
# bootprefix . js
# < module definitions >
# boot . js
The module definitions on the browser look like this :
$tw . defineModule ( "MyModule" , "moduletype" , function ( module , exports , require ) {
// Module code inserted here
return exports ;
} ) ;
In practice , each module is wrapped in a separate script block .
\ * /
2013-10-12 17:44:09 +00:00
var _boot = ( function ( $tw ) {
2012-04-30 11:23:03 +00:00
2012-05-04 17:49:04 +00:00
/*jslint node: true, browser: true */
/*global modules: false, $tw: false */
2012-04-30 11:23:03 +00:00
"use strict" ;
2013-10-12 17:44:09 +00:00
// Include bootprefix if we're not given module data
if ( ! $tw ) {
$tw = require ( "./bootprefix.js" ) . bootprefix ( ) ;
2012-11-15 10:40:03 +00:00
}
2014-04-06 21:49:20 +00:00
$tw . utils = $tw . utils || Object . create ( null ) ;
$tw . boot = $tw . boot || Object . create ( null ) ;
2012-04-30 11:23:03 +00:00
2013-03-22 20:02:19 +00:00
/////////////////////////// Standard node.js libraries
var fs , path , vm ;
2014-01-14 14:09:04 +00:00
if ( $tw . node ) {
2013-03-22 20:02:19 +00:00
fs = require ( "fs" ) ;
path = require ( "path" ) ;
vm = require ( "vm" ) ;
}
2012-04-30 11:23:03 +00:00
/////////////////////////// Utility functions
2012-10-10 15:32:37 +00:00
/ *
Check if an object has a property
* /
$tw . utils . hop = function ( object , property ) {
2013-05-15 16:32:17 +00:00
return object ? Object . prototype . hasOwnProperty . call ( object , property ) : false ;
2012-10-10 15:32:37 +00:00
} ;
2012-11-16 17:44:47 +00:00
/ *
2012-12-13 21:31:19 +00:00
Determine if a value is an array
* /
$tw . utils . isArray = function ( value ) {
return Object . prototype . toString . call ( value ) == "[object Array]" ;
} ;
2013-08-06 14:26:10 +00:00
/ *
Determine if a value is a date
* /
$tw . utils . isDate = function ( value ) {
return Object . prototype . toString . call ( value ) === "[object Date]" ;
} ;
2012-12-13 21:31:19 +00:00
/ *
Iterate through all the own properties of an object or array . Callback is invoked with ( element , title , object )
2012-11-16 17:44:47 +00:00
* /
$tw . utils . each = function ( object , callback ) {
2012-12-13 21:31:19 +00:00
var f ;
2012-11-16 17:44:47 +00:00
if ( object ) {
2014-04-05 16:31:36 +00:00
if ( Object . prototype . toString . call ( object ) == "[object Array]" ) {
object . forEach ( callback ) ;
2012-12-13 21:31:19 +00:00
} else {
for ( f in object ) {
2014-04-05 16:31:36 +00:00
if ( Object . prototype . hasOwnProperty . call ( object , f ) ) {
2012-12-13 21:31:19 +00:00
callback ( object [ f ] , f , object ) ;
}
}
2012-11-16 17:44:47 +00:00
}
}
2012-05-04 17:49:04 +00:00
} ;
2012-04-30 11:23:03 +00:00
2013-06-26 22:52:21 +00:00
/ *
Helper for making DOM elements
tag : tag name
options : see below
Options include :
attributes : hashmap of attribute values
text : text to add as a child node
children : array of further child nodes
innerHTML : optional HTML for element
class : class name ( s )
document : defaults to current document
2013-10-26 12:13:45 +00:00
eventListeners : array of event listeners ( this option won ' t work until $tw . utils . addEventListeners ( ) has been loaded )
2013-06-26 22:52:21 +00:00
* /
$tw . utils . domMaker = function ( tag , options ) {
var doc = options . document || document ;
var element = doc . createElement ( tag ) ;
if ( options [ "class" ] ) {
element . className = options [ "class" ] ;
}
if ( options . text ) {
2014-03-08 10:43:01 +00:00
element . appendChild ( doc . createTextNode ( options . text ) ) ;
2013-06-26 22:52:21 +00:00
}
$tw . utils . each ( options . children , function ( child ) {
element . appendChild ( child ) ;
} ) ;
if ( options . innerHTML ) {
element . innerHTML = options . innerHTML ;
}
$tw . utils . each ( options . attributes , function ( attribute , name ) {
element . setAttribute ( name , attribute ) ;
} ) ;
2013-10-26 12:13:45 +00:00
if ( options . eventListeners ) {
$tw . utils . addEventListeners ( element , options . eventListeners ) ;
}
2013-06-26 22:52:21 +00:00
return element ;
} ;
/ *
Display an error and exit
* /
$tw . utils . error = function ( err ) {
// Prepare the error message
var errHeading = "Internal JavaScript Error" ,
promptMsg = "Well, this is embarrassing. It is recommended that you restart TiddlyWiki by refreshing your browser" ;
// Log the error to the console
console . error ( err ) ;
if ( $tw . browser ) {
// Display an error message to the user
var dm = $tw . utils . domMaker ,
heading = dm ( "h1" , { text : errHeading } ) ,
prompt = dm ( "div" , { text : promptMsg , "class" : "tw-error-prompt" } ) ,
message = dm ( "div" , { text : err } ) ,
button = dm ( "button" , { text : "close" } ) ,
form = dm ( "form" , { children : [ heading , prompt , message , button ] , "class" : "tw-error-form" } ) ;
document . body . insertBefore ( form , document . body . firstChild ) ;
form . addEventListener ( "submit" , function ( event ) {
document . body . removeChild ( form ) ;
event . preventDefault ( ) ;
return false ;
} , true ) ;
return null ;
} else {
// Exit if we're under node.js
process . exit ( 1 ) ;
}
} ;
/ *
Use our custom error handler if we ' re in the browser
* /
if ( $tw . browser ) {
window . onerror = function ( errorMsg , url , lineNumber ) {
$tw . utils . error ( errorMsg ) ;
return false ;
} ;
}
2013-03-22 19:27:09 +00:00
/ *
Extend an object with the properties from a list of source objects
* /
$tw . utils . extend = function ( object /*, sourceObjectList */ ) {
$tw . utils . each ( Array . prototype . slice . call ( arguments , 1 ) , function ( source ) {
if ( source ) {
for ( var p in source ) {
object [ p ] = source [ p ] ;
}
}
} ) ;
return object ;
} ;
/ *
Fill in any null or undefined properties of an object with the properties from a list of source objects . Each property that is an object is called recursively
* /
$tw . utils . deepDefaults = function ( object /*, sourceObjectList */ ) {
$tw . utils . each ( Array . prototype . slice . call ( arguments , 1 ) , function ( source ) {
if ( source ) {
for ( var p in source ) {
if ( object [ p ] == null ) {
object [ p ] = source [ p ] ;
}
if ( typeof object [ p ] === "object" && typeof source [ p ] === "object" ) {
$tw . utils . deepDefaults ( object [ p ] , source [ p ] ) ;
}
}
}
} ) ;
return object ;
} ;
2012-10-10 15:32:37 +00:00
/ *
2014-01-12 20:11:51 +00:00
Convert "&" to & , " " to nbsp , "<" to < , ">" to > and """ to "
2012-10-10 15:32:37 +00:00
* /
2012-04-30 11:23:03 +00:00
$tw . utils . htmlDecode = function ( s ) {
2014-01-28 15:23:58 +00:00
return s . toString ( ) . replace ( /</mg , "<" ) . replace ( / /mg , "\xA0" ) . replace ( />/mg , ">" ) . replace ( /"/mg , "\"" ) . replace ( /&/mg , "&" ) ;
2012-04-30 11:23:03 +00:00
} ;
/ *
Pad a string to a given length with "0" s . Length defaults to 2
* /
$tw . utils . pad = function ( value , length ) {
length = length || 2 ;
var s = value . toString ( ) ;
2012-05-04 17:49:04 +00:00
if ( s . length < length ) {
2012-04-30 11:23:03 +00:00
s = "000000000000000000000000000" . substr ( 0 , length - s . length ) + s ;
2012-05-04 17:49:04 +00:00
}
2012-04-30 11:23:03 +00:00
return s ;
} ;
2013-11-19 12:14:37 +00:00
// Convert a date into UTC YYYYMMDDHHMMSSmmm format
2012-04-30 11:23:03 +00:00
$tw . utils . stringifyDate = function ( value ) {
return value . getUTCFullYear ( ) +
2012-05-05 13:17:51 +00:00
$tw . utils . pad ( value . getUTCMonth ( ) + 1 ) +
$tw . utils . pad ( value . getUTCDate ( ) ) +
$tw . utils . pad ( value . getUTCHours ( ) ) +
2013-11-07 20:04:54 +00:00
$tw . utils . pad ( value . getUTCMinutes ( ) ) +
2013-11-19 12:14:37 +00:00
$tw . utils . pad ( value . getUTCSeconds ( ) ) +
2013-11-07 20:04:54 +00:00
$tw . utils . pad ( value . getUTCMilliseconds ( ) , 3 ) ;
2012-04-30 11:23:03 +00:00
} ;
2013-11-19 12:14:37 +00:00
// Parse a date from a UTC YYYYMMDDHHMMSSmmm format string
2012-04-30 11:23:03 +00:00
$tw . utils . parseDate = function ( value ) {
if ( typeof value === "string" ) {
return new Date ( Date . UTC ( parseInt ( value . substr ( 0 , 4 ) , 10 ) ,
parseInt ( value . substr ( 4 , 2 ) , 10 ) - 1 ,
parseInt ( value . substr ( 6 , 2 ) , 10 ) ,
parseInt ( value . substr ( 8 , 2 ) || "00" , 10 ) ,
parseInt ( value . substr ( 10 , 2 ) || "00" , 10 ) ,
parseInt ( value . substr ( 12 , 2 ) || "00" , 10 ) ,
parseInt ( value . substr ( 14 , 3 ) || "000" , 10 ) ) ) ;
2014-01-03 10:50:00 +00:00
} else if ( $tw . utils . isDate ( value ) ) {
2012-04-30 11:23:03 +00:00
return value ;
} else {
return null ;
}
} ;
2013-08-07 15:05:56 +00:00
// Stringify an array of tiddler titles into a list string
$tw . utils . stringifyList = function ( value ) {
var result = [ ] ;
for ( var t = 0 ; t < value . length ; t ++ ) {
if ( value [ t ] . indexOf ( " " ) !== - 1 ) {
result . push ( "[[" + value [ t ] + "]]" ) ;
} else {
result . push ( value [ t ] ) ;
}
}
return result . join ( " " ) ;
} ;
2012-08-30 13:43:13 +00:00
// Parse a string array from a bracketted list. For example "OneTiddler [[Another Tiddler]] LastOne"
2012-04-30 11:23:03 +00:00
$tw . utils . parseStringArray = function ( value ) {
if ( typeof value === "string" ) {
2013-12-17 14:53:02 +00:00
var memberRegExp = /(?:^|\s)(?:\[\[(.*?)\]\])(?=\s|$)|(\S+)/mg ,
2012-04-30 11:23:03 +00:00
results = [ ] ,
match ;
do {
match = memberRegExp . exec ( value ) ;
if ( match ) {
2013-07-04 15:55:47 +00:00
var item = match [ 1 ] || match [ 2 ] ;
if ( results . indexOf ( item ) === - 1 ) {
results . push ( item ) ;
}
2012-04-30 11:23:03 +00:00
}
} while ( match ) ;
return results ;
2014-01-03 10:50:00 +00:00
} else if ( $tw . utils . isArray ( value ) ) {
2012-04-30 11:23:03 +00:00
return value ;
} else {
return null ;
}
} ;
// Parse a block of name:value fields. The `fields` object is used as the basis for the return value
$tw . utils . parseFields = function ( text , fields ) {
2014-04-06 21:49:20 +00:00
fields = fields || Object . create ( null ) ;
2012-05-09 12:48:34 +00:00
text . split ( /\r?\n/mg ) . forEach ( function ( line ) {
2012-11-17 20:18:03 +00:00
if ( line . charAt ( 0 ) !== "#" ) {
var p = line . indexOf ( ":" ) ;
if ( p !== - 1 ) {
var field = line . substr ( 0 , p ) . trim ( ) ,
value = line . substr ( p + 1 ) . trim ( ) ;
fields [ field ] = value ;
}
2012-04-30 11:23:03 +00:00
}
} ) ;
return fields ;
} ;
/ *
Resolves a source filepath delimited with ` / ` relative to a specified absolute root filepath .
In relative paths , the special folder name ` .. ` refers to immediate parent directory , and the
name ` . ` refers to the current directory
* /
$tw . utils . resolvePath = function ( sourcepath , rootpath ) {
// If the source path starts with ./ or ../ then it is relative to the root
2013-10-12 17:44:09 +00:00
2012-04-30 11:23:03 +00:00
if ( sourcepath . substr ( 0 , 2 ) === "./" || sourcepath . substr ( 0 , 3 ) === "../" ) {
var src = sourcepath . split ( "/" ) ,
root = rootpath . split ( "/" ) ;
// Remove the filename part of the root
root . splice ( root . length - 1 , 1 ) ;
// Process the source path bit by bit onto the end of the root path
while ( src . length > 0 ) {
var c = src . shift ( ) ;
if ( c === ".." ) { // Slice off the last root entry for a double dot
if ( root . length > 0 ) {
root . splice ( root . length - 1 , 1 ) ;
}
} else if ( c !== "." ) { // Ignore dots
root . push ( c ) ; // Copy other elements across
}
}
return root . join ( "/" ) ;
} else {
// If it isn't relative, just return the path
2013-10-12 17:44:09 +00:00
if ( rootpath ) {
var root = rootpath . split ( "/" ) ;
// Remove the filename part of the root
root . splice ( root . length - 1 , 1 ) ;
return root . join ( "/" ) + "/" + sourcepath ;
} else {
return sourcepath ;
}
2012-04-30 11:23:03 +00:00
}
} ;
2012-10-01 15:50:42 +00:00
/ *
2013-12-02 09:57:19 +00:00
Parse a semantic version string into its constituent parts
2012-10-01 15:50:42 +00:00
* /
2013-12-02 09:57:19 +00:00
$tw . utils . parseVersion = function ( version ) {
var match = /^((\d+)\.(\d+)\.(\d+))(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?$/ . exec ( version ) ;
if ( match ) {
return {
version : match [ 1 ] ,
major : parseInt ( match [ 2 ] , 10 ) ,
minor : parseInt ( match [ 3 ] , 10 ) ,
patch : parseInt ( match [ 4 ] , 10 ) ,
prerelease : match [ 5 ] ,
build : match [ 6 ]
} ;
} else {
return null ;
}
} ;
/ *
2014-04-17 10:59:42 +00:00
Returns true if the version string A is greater than the version string B . Returns true if the versions are the same
2013-12-02 09:57:19 +00:00
* /
$tw . utils . checkVersions = function ( versionStringA , versionStringB ) {
2014-03-14 10:43:22 +00:00
var defaultVersion = {
major : 0 ,
minor : 0 ,
patch : 0
} ,
versionA = $tw . utils . parseVersion ( versionStringA ) || defaultVersion ,
versionB = $tw . utils . parseVersion ( versionStringB ) || defaultVersion ,
2013-12-02 09:57:19 +00:00
diff = [
versionA . major - versionB . major ,
versionA . minor - versionB . minor ,
versionA . patch - versionB . patch
] ;
2012-10-01 15:50:42 +00:00
return ( diff [ 0 ] > 0 ) ||
( diff [ 0 ] === 0 && diff [ 1 ] > 0 ) ||
2014-04-17 10:59:42 +00:00
( diff [ 0 ] === 0 && diff [ 1 ] === 0 && diff [ 2 ] > 0 ) ||
( diff [ 0 ] === 0 && diff [ 1 ] === 0 && diff [ 2 ] === 0 ) ;
2012-10-01 15:50:42 +00:00
} ;
2013-03-22 21:12:39 +00:00
/ *
Register file type information
2014-04-08 21:26:01 +00:00
options : { flags : flags , deserializerType : deserializerType }
2014-04-08 21:20:51 +00:00
flags : "image" for image types
2014-04-08 21:26:01 +00:00
deserializerType : defaults to type if not specified
2013-03-22 21:12:39 +00:00
* /
2014-04-08 21:20:51 +00:00
$tw . utils . registerFileType = function ( type , encoding , extension , options ) {
options = options || { } ;
$tw . config . fileExtensionInfo [ extension ] = { type : type } ;
2014-04-08 21:26:01 +00:00
$tw . config . contentTypeInfo [ type ] = { encoding : encoding , extension : extension , flags : options . flags || [ ] , deserializerType : options . deserializerType || type } ;
2014-04-08 21:20:51 +00:00
} ;
/ *
Given an extension , get the correct encoding for that file .
defaults to utf8
* /
$tw . utils . getTypeEncoding = function ( ext ) {
var extensionInfo = $tw . config . fileExtensionInfo [ ext ] ,
type = extensionInfo ? extensionInfo . type : null ,
typeInfo = type ? $tw . config . contentTypeInfo [ type ] : null ;
return typeInfo ? typeInfo . encoding : "utf8" ;
2013-10-03 13:43:58 +00:00
} ;
2013-03-22 21:12:39 +00:00
2013-04-25 08:05:02 +00:00
/ *
Run code globally with specified context variables in scope
* /
$tw . utils . evalGlobal = function ( code , context , filename ) {
2014-04-06 21:49:20 +00:00
var contextCopy = $tw . utils . extend ( Object . create ( null ) , context ) ;
2013-04-25 08:05:02 +00:00
// Get the context variables as a pair of arrays of names and values
var contextNames = [ ] , contextValues = [ ] ;
$tw . utils . each ( contextCopy , function ( value , name ) {
contextNames . push ( name ) ;
contextValues . push ( value ) ;
} ) ;
// Add the code prologue and epilogue
code = "(function(" + contextNames . join ( "," ) + ") {(function(){\n" + code + ";})();\nreturn exports;\n})\n" ;
// Compile the code into a function
var fn ;
if ( $tw . browser ) {
2014-04-06 21:36:51 +00:00
fn = window [ "eval" ] ( code + "\n\n//# sourceURL=" + filename ) ;
2013-04-25 08:05:02 +00:00
} else {
fn = vm . runInThisContext ( code , filename ) ;
}
// Call the function and return the exports
return fn . apply ( null , contextValues ) ;
} ;
/ *
Run code in a sandbox with only the specified context variables in scope
* /
$tw . utils . evalSandboxed = $tw . browser ? $tw . utils . evalGlobal : function ( code , context , filename ) {
2014-04-06 21:49:20 +00:00
var sandbox = $tw . utils . extend ( Object . create ( null ) , context ) ;
2013-04-25 08:05:02 +00:00
vm . runInNewContext ( code , sandbox , filename ) ;
return sandbox . exports ;
} ;
2012-11-16 16:59:47 +00:00
/ *
Creates a PasswordPrompt object
* /
$tw . utils . PasswordPrompt = function ( ) {
// Store of pending password prompts
this . passwordPrompts = [ ] ;
// Create the wrapper
2013-06-26 22:52:21 +00:00
this . promptWrapper = $tw . utils . domMaker ( "div" , { "class" : "tw-password-wrapper" } ) ;
2012-11-16 16:59:47 +00:00
document . body . appendChild ( this . promptWrapper ) ;
// Hide the empty wrapper
this . setWrapperDisplay ( ) ;
} ;
/ *
Hides or shows the wrapper depending on whether there are any outstanding prompts
* /
$tw . utils . PasswordPrompt . prototype . setWrapperDisplay = function ( ) {
if ( this . passwordPrompts . length ) {
this . promptWrapper . style . display = "block" ;
} else {
this . promptWrapper . style . display = "none" ;
}
} ;
/ *
2013-03-08 17:50:28 +00:00
Adds a new password prompt . Options are :
submitText : text to use for submit button ( defaults to "Login" )
serviceName : text of the human readable service name
noUserName : set true to disable username prompt
2014-01-19 18:43:02 +00:00
canCancel : set true to enable a cancel button ( callback called with null )
2013-03-08 17:50:28 +00:00
callback : function to be called on submission with parameter of object { username : , password : } . Callback must return ` true ` to remove the password prompt
2012-11-16 16:59:47 +00:00
* /
$tw . utils . PasswordPrompt . prototype . createPrompt = function ( options ) {
// Create and add the prompt to the DOM
2014-01-19 18:43:02 +00:00
var self = this ,
submitText = options . submitText || "Login" ,
2013-06-26 22:52:21 +00:00
dm = $tw . utils . domMaker ,
children = [ dm ( "h1" , { text : options . serviceName } ) ] ;
2012-11-16 16:59:47 +00:00
if ( ! options . noUserName ) {
2013-06-26 22:52:21 +00:00
children . push ( dm ( "input" , {
attributes : { type : "text" , name : "username" , placeholder : "Username" } ,
"class" : "input-small"
} ) ) ;
2012-11-16 16:59:47 +00:00
}
2013-06-26 22:52:21 +00:00
children . push ( dm ( "input" , {
attributes : { type : "password" , name : "password" , placeholder : "Password" } ,
"class" : "input-small"
} ) ) ;
2014-01-19 18:43:02 +00:00
if ( options . canCancel ) {
children . push ( dm ( "button" , {
text : "Cancel" ,
"class" : "btn" ,
eventListeners : [ {
name : "click" ,
handlerFunction : function ( event ) {
self . removePrompt ( promptInfo ) ;
options . callback ( null ) ;
}
} ]
} ) ) ;
}
2013-06-26 22:52:21 +00:00
children . push ( dm ( "button" , {
attributes : { type : "submit" } ,
text : submitText ,
"class" : "btn"
} ) ) ;
var form = dm ( "form" , {
"class" : "form-inline" ,
attributes : { autocomplete : "off" } ,
children : children
} ) ;
2012-11-16 16:59:47 +00:00
this . promptWrapper . appendChild ( form ) ;
2012-11-16 17:23:10 +00:00
window . setTimeout ( function ( ) {
form . elements [ 0 ] . focus ( ) ;
} , 10 ) ;
2012-11-16 16:59:47 +00:00
// Add a submit event handler
var self = this ;
form . addEventListener ( "submit" , function ( event ) {
// Collect the form data
2012-11-16 22:40:56 +00:00
var data = { } , t ;
2013-03-22 21:12:39 +00:00
$tw . utils . each ( form . elements , function ( element ) {
if ( element . name && element . value ) {
data [ element . name ] = element . value ;
2012-11-16 16:59:47 +00:00
}
2013-03-22 21:12:39 +00:00
} ) ;
2012-11-16 16:59:47 +00:00
// Call the callback
if ( options . callback ( data ) ) {
// Remove the prompt if the callback returned true
2014-01-19 18:43:02 +00:00
self . removePrompt ( promptInfo ) ;
2012-11-16 22:40:56 +00:00
} else {
// Clear the password if the callback returned false
2013-03-22 21:12:39 +00:00
$tw . utils . each ( form . elements , function ( element ) {
if ( element . name === "password" ) {
2013-05-07 14:52:21 +00:00
element . value = "" ;
2012-11-16 22:40:56 +00:00
}
2013-03-22 21:12:39 +00:00
} ) ;
2012-11-16 16:59:47 +00:00
}
event . preventDefault ( ) ;
return false ;
} , true ) ;
// Add the prompt to the list
var promptInfo = {
serviceName : options . serviceName ,
callback : options . callback ,
form : form
} ;
this . passwordPrompts . push ( promptInfo ) ;
// Make sure the wrapper is displayed
this . setWrapperDisplay ( ) ;
} ;
2014-01-19 18:43:02 +00:00
$tw . utils . PasswordPrompt . prototype . removePrompt = function ( promptInfo ) {
var i = this . passwordPrompts . indexOf ( promptInfo ) ;
if ( i !== - 1 ) {
this . passwordPrompts . splice ( i , 1 ) ;
promptInfo . form . parentNode . removeChild ( promptInfo . form ) ;
this . setWrapperDisplay ( ) ;
}
}
2013-01-31 10:20:13 +00:00
/ *
Crypto helper object for encrypted content . It maintains the password text in a closure , and provides methods to change
the password , and to encrypt / decrypt a block of text
* /
2012-11-16 16:59:47 +00:00
$tw . utils . Crypto = function ( ) {
var sjcl = $tw . browser ? window . sjcl : require ( "./sjcl.js" ) ,
2014-01-20 13:35:55 +00:00
currentPassword = null ,
callSjcl = function ( method , inputText , password ) {
password = password || currentPassword ;
2012-11-16 16:59:47 +00:00
var outputText ;
try {
2014-01-19 18:43:02 +00:00
if ( password ) {
outputText = sjcl [ method ] ( password , inputText ) ;
}
2012-11-16 16:59:47 +00:00
} catch ( ex ) {
console . log ( "Crypto error:" + ex ) ;
outputText = null ;
}
return outputText ;
} ;
this . setPassword = function ( newPassword ) {
2014-01-20 13:35:55 +00:00
currentPassword = newPassword ;
2013-01-31 10:20:13 +00:00
this . updateCryptoStateTiddler ( ) ;
} ;
this . updateCryptoStateTiddler = function ( ) {
2014-02-21 18:21:08 +00:00
if ( $tw . wiki ) {
var state = currentPassword ? "yes" : "no" ,
tiddler = $tw . wiki . getTiddler ( "$:/isEncrypted" ) ;
if ( ! tiddler || tiddler . fields . text !== state ) {
$tw . wiki . addTiddler ( new $tw . Tiddler ( { title : "$:/isEncrypted" , text : state } ) ) ;
}
2013-03-22 19:44:12 +00:00
}
2012-11-16 16:59:47 +00:00
} ;
2013-01-31 10:20:13 +00:00
this . hasPassword = function ( ) {
2014-01-20 13:35:55 +00:00
return ! ! currentPassword ;
2013-01-31 10:20:13 +00:00
}
2014-01-20 13:35:55 +00:00
this . encrypt = function ( text , password ) {
return callSjcl ( "encrypt" , text , password ) ;
2012-11-16 16:59:47 +00:00
} ;
2014-01-20 13:35:55 +00:00
this . decrypt = function ( text , password ) {
return callSjcl ( "decrypt" , text , password ) ;
2012-11-16 16:59:47 +00:00
} ;
} ;
2012-08-03 14:09:48 +00:00
/////////////////////////// Module mechanism
2012-04-30 11:23:03 +00:00
2013-04-25 08:05:02 +00:00
/ *
Execute the module named 'moduleName' . The name can optionally be relative to the module named 'moduleRoot'
* /
$tw . modules . execute = function ( moduleName , moduleRoot ) {
2013-10-12 17:44:09 +00:00
var name = moduleName [ 0 ] === "." ? $tw . utils . resolvePath ( moduleName , moduleRoot ) : moduleName ,
moduleInfo = $tw . modules . titles [ name ] || $tw . modules . titles [ name + ".js" ] || $tw . modules . titles [ moduleName ] || $tw . modules . titles [ moduleName + ".js" ] ,
tiddler = $tw . wiki . getTiddler ( name ) || $tw . wiki . getTiddler ( name + ".js" ) || $tw . wiki . getTiddler ( moduleName ) || $tw . wiki . getTiddler ( moduleName + ".js" ) ,
_exports = { } ,
2013-04-25 08:05:02 +00:00
sandbox = {
2013-10-12 17:44:09 +00:00
module : { } ,
//moduleInfo: moduleInfo,
exports : _exports ,
2013-04-25 08:05:02 +00:00
console : console ,
setInterval : setInterval ,
clearInterval : clearInterval ,
setTimeout : setTimeout ,
clearTimeout : clearTimeout ,
2013-11-28 14:03:08 +00:00
Buffer : $tw . browser ? { } : Buffer ,
2013-04-25 08:05:02 +00:00
$tw : $tw ,
require : function ( title ) {
2013-10-12 17:44:09 +00:00
return $tw . modules . execute ( title , name ) ;
2013-04-25 08:05:02 +00:00
}
} ;
2013-10-12 17:44:09 +00:00
Object . defineProperty ( sandbox . module , "id" , {
value : name ,
writable : false ,
enumerable : true ,
configurable : false
} ) ;
2013-04-25 08:05:02 +00:00
if ( ! $tw . browser ) {
$tw . utils . extend ( sandbox , {
process : process
} ) ;
2013-10-12 17:44:09 +00:00
} else {
/ *
CommonJS optional require . main property :
In a browser we offer a fake main module which points back to the boot function
( Theoretically , this may allow TW to eventually load itself as a module in the browser )
* /
Object . defineProperty ( sandbox . require , "main" , {
value : ( typeof ( require ) !== "undefined" ) ? require . main : { TiddlyWiki : _boot } ,
writable : false ,
enumerable : true ,
configurable : false
} ) ;
2013-04-25 08:05:02 +00:00
}
if ( ! moduleInfo ) {
2013-10-12 17:44:09 +00:00
// We could not find the module on this path
// Try to defer to browserify etc, or node
var deferredModule ;
2013-04-25 08:05:02 +00:00
if ( $tw . browser ) {
2013-10-12 17:44:09 +00:00
if ( window . require ) {
try {
return window . require ( moduleName ) ;
} catch ( e ) { }
}
throw "Cannot find module named '" + moduleName + "' required by module '" + moduleRoot + "', resolved to " + name ;
2013-04-25 08:05:02 +00:00
} else {
// If we don't have a module with that name, let node.js try to find it
return require ( moduleName ) ;
}
}
// Execute the module if we haven't already done so
if ( ! moduleInfo . exports ) {
try {
// Check the type of the definition
2013-07-03 17:36:51 +00:00
if ( typeof moduleInfo . definition === "function" ) { // Function
2013-10-12 17:44:09 +00:00
moduleInfo . exports = _exports ;
2013-07-03 17:36:51 +00:00
moduleInfo . definition ( moduleInfo , moduleInfo . exports , sandbox . require ) ;
} else if ( typeof moduleInfo . definition === "string" ) { // String
2013-10-12 17:44:09 +00:00
moduleInfo . exports = _exports ;
$tw . utils . evalSandboxed ( moduleInfo . definition , sandbox , tiddler . fields . title ) ;
2013-04-25 08:05:02 +00:00
} else { // Object
moduleInfo . exports = moduleInfo . definition ;
}
} catch ( e ) {
$tw . utils . error ( "Error executing boot module " + name + ":\n" + e ) ;
}
}
// Return the exports of the module
return moduleInfo . exports ;
} ;
2012-04-30 11:23:03 +00:00
/ *
2012-11-14 11:23:43 +00:00
Apply a callback to each module of a particular type
moduleType : type of modules to enumerate
callback : function called as callback ( title , moduleExports ) for each module
2012-04-30 11:23:03 +00:00
* /
2012-11-14 11:23:43 +00:00
$tw . modules . forEachModuleOfType = function ( moduleType , callback ) {
var modules = $tw . modules . types [ moduleType ] ;
2014-03-16 21:23:10 +00:00
$tw . utils . each ( modules , function ( element , title ) {
2012-11-16 17:44:47 +00:00
callback ( title , $tw . modules . execute ( title ) ) ;
} ) ;
2012-11-14 11:23:43 +00:00
} ;
/ *
Get all the modules of a particular type in a hashmap by their ` name ` field
* /
$tw . modules . getModulesByTypeAsHashmap = function ( moduleType , nameField ) {
nameField = nameField || "name" ;
2014-04-06 21:49:20 +00:00
var results = Object . create ( null ) ;
2012-11-14 11:23:43 +00:00
$tw . modules . forEachModuleOfType ( moduleType , function ( title , module ) {
results [ module [ nameField ] ] = module ;
} ) ;
2012-04-30 11:23:03 +00:00
return results ;
} ;
/ *
2012-08-03 14:09:48 +00:00
Apply the exports of the modules of a particular type to a target object
2012-04-30 11:23:03 +00:00
* /
2012-11-16 17:44:47 +00:00
$tw . modules . applyMethods = function ( moduleType , targetObject ) {
2012-12-13 21:31:19 +00:00
if ( ! targetObject ) {
2014-04-06 21:49:20 +00:00
targetObject = Object . create ( null ) ;
2012-12-13 21:31:19 +00:00
}
2012-11-14 11:23:43 +00:00
$tw . modules . forEachModuleOfType ( moduleType , function ( title , module ) {
2012-11-16 17:44:47 +00:00
$tw . utils . each ( module , function ( element , title , object ) {
targetObject [ title ] = module [ title ] ;
} ) ;
2012-11-14 11:23:43 +00:00
} ) ;
2012-12-13 21:31:19 +00:00
return targetObject ;
2012-04-30 11:23:03 +00:00
} ;
2012-12-26 22:02:59 +00:00
/ *
Return an array of classes created from the modules of a specified type . Each module should export the properties to be added to those of the optional base class
* /
$tw . modules . createClassesFromModules = function ( moduleType , subType , baseClass ) {
2014-04-06 21:49:20 +00:00
var classes = Object . create ( null ) ;
2012-12-26 22:02:59 +00:00
$tw . modules . forEachModuleOfType ( moduleType , function ( title , moduleExports ) {
if ( ! subType || moduleExports . types [ subType ] ) {
var newClass = function ( ) { } ;
if ( baseClass ) {
newClass . prototype = new baseClass ( ) ;
newClass . prototype . constructor = baseClass ;
}
$tw . utils . extend ( newClass . prototype , moduleExports ) ;
classes [ moduleExports . name ] = newClass ;
}
} ) ;
return classes ;
} ;
2012-04-30 11:23:03 +00:00
/////////////////////////// Barebones tiddler object
/ *
Construct a tiddler object from a hashmap of tiddler fields . If multiple hasmaps are provided they are merged ,
taking precedence to the right
* /
$tw . Tiddler = function ( /* [fields,] fields */ ) {
2014-04-06 21:49:20 +00:00
this . fields = Object . create ( null ) ;
2012-04-30 11:23:03 +00:00
for ( var c = 0 ; c < arguments . length ; c ++ ) {
var arg = arguments [ c ] ,
2012-06-06 11:20:48 +00:00
src = ( arg instanceof $tw . Tiddler ) ? arg . fields : arg ;
2012-04-30 11:23:03 +00:00
for ( var t in src ) {
2014-04-17 13:43:12 +00:00
if ( src [ t ] === undefined || src [ t ] === null ) {
2012-04-30 11:23:03 +00:00
if ( t in this . fields ) {
delete this . fields [ t ] ; // If we get a field that's undefined, delete any previous field value
}
} else {
2012-08-03 14:09:48 +00:00
// Parse the field with the associated field module (if any)
2014-03-17 08:58:42 +00:00
var fieldModule = $tw . Tiddler . fieldModules [ t ] ,
value ;
2013-06-13 08:16:07 +00:00
if ( fieldModule && fieldModule . parse ) {
2014-03-17 08:58:42 +00:00
value = fieldModule . parse . call ( this , src [ t ] ) ;
2012-04-30 11:23:03 +00:00
} else {
2014-03-17 08:58:42 +00:00
value = src [ t ] ;
2012-04-30 11:23:03 +00:00
}
2014-03-17 08:58:42 +00:00
// Freeze the field to keep it immutable
if ( typeof value === "object" ) {
Object . freeze ( value ) ;
}
this . fields [ t ] = value ;
2012-04-30 11:23:03 +00:00
}
}
}
2014-03-12 08:33:13 +00:00
// Freeze the tiddler against modification
Object . freeze ( this . fields ) ;
2012-04-30 11:23:03 +00:00
} ;
2012-11-12 22:16:49 +00:00
$tw . Tiddler . prototype . hasField = function ( field ) {
return $tw . utils . hop ( this . fields , field ) ;
} ;
2012-11-15 10:40:03 +00:00
2012-04-30 11:23:03 +00:00
/ *
2012-08-03 14:09:48 +00:00
Register and install the built in tiddler field modules
2012-04-30 11:23:03 +00:00
* /
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerfields/modified" , "tiddlerfield" , {
2012-04-30 11:23:03 +00:00
name : "modified" ,
parse : $tw . utils . parseDate ,
stringify : $tw . utils . stringifyDate
} ) ;
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerfields/created" , "tiddlerfield" , {
2012-04-30 11:23:03 +00:00
name : "created" ,
parse : $tw . utils . parseDate ,
stringify : $tw . utils . stringifyDate
} ) ;
2013-06-13 08:16:07 +00:00
$tw . modules . define ( "$:/boot/tiddlerfields/color" , "tiddlerfield" , {
name : "color" ,
2013-11-07 19:39:50 +00:00
editTag : "input" ,
2013-06-13 08:16:07 +00:00
editType : "color"
} ) ;
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerfields/tags" , "tiddlerfield" , {
2012-04-30 11:23:03 +00:00
name : "tags" ,
parse : $tw . utils . parseStringArray ,
2013-08-07 15:05:56 +00:00
stringify : $tw . utils . stringifyList
} ) ;
$tw . modules . define ( "$:/boot/tiddlerfields/list" , "tiddlerfield" , {
name : "list" ,
parse : $tw . utils . parseStringArray ,
stringify : $tw . utils . stringifyList
2012-04-30 11:23:03 +00:00
} ) ;
/////////////////////////// Barebones wiki store
/ *
2014-03-17 10:50:18 +00:00
Wiki constructor . State is stored in private members that only a small number of privileged accessor methods have direct access . Methods added via the prototype have to use these accessors and cannot access the state data directly .
options include :
shadowTiddlers : Array of shadow tiddlers to be added
2012-04-30 11:23:03 +00:00
* /
2014-03-17 10:50:18 +00:00
$tw . Wiki = function ( options ) {
options = options || { } ;
2014-03-16 21:23:10 +00:00
var self = this ,
2014-04-06 21:43:10 +00:00
tiddlers = Object . create ( null ) , // Hashmap of tiddlers
2014-03-17 10:50:18 +00:00
pluginTiddlers = [ ] , // Array of tiddlers containing registered plugins, ordered by priority
2014-04-06 21:43:10 +00:00
pluginInfo = Object . create ( null ) , // Hashmap of parsed plugin content
shadowTiddlers = options . shadowTiddlers || Object . create ( null ) ; // Hashmap by title of {source:, tiddler:}
2014-03-17 10:50:18 +00:00
// Add a tiddler to the store
2014-03-16 21:23:10 +00:00
this . addTiddler = function ( tiddler ) {
if ( ! ( tiddler instanceof $tw . Tiddler ) ) {
tiddler = new $tw . Tiddler ( tiddler ) ;
}
// Save the tiddler
2014-03-17 10:50:18 +00:00
if ( tiddler ) {
var title = tiddler . fields . title ;
if ( title ) {
tiddlers [ title ] = tiddler ;
this . clearCache ( title ) ;
this . clearGlobalCache ( ) ;
this . enqueueTiddlerEvent ( title ) ;
}
2014-03-16 21:23:10 +00:00
}
} ;
2014-03-17 10:50:18 +00:00
// Delete a tiddler
this . deleteTiddler = function ( title ) {
delete tiddlers [ title ] ;
this . clearCache ( title ) ;
this . clearGlobalCache ( ) ;
this . enqueueTiddlerEvent ( title , true ) ;
} ;
// Get a tiddler from the store
2014-03-16 21:23:10 +00:00
this . getTiddler = function ( title ) {
var t = tiddlers [ title ] ;
if ( t instanceof $tw . Tiddler ) {
return t ;
2014-04-05 16:31:36 +00:00
} else if ( title !== undefined && Object . prototype . hasOwnProperty . call ( shadowTiddlers , title ) ) {
2014-03-17 10:50:18 +00:00
return shadowTiddlers [ title ] . tiddler ;
2014-03-16 21:23:10 +00:00
} else {
return undefined ;
}
} ;
2014-03-17 10:50:18 +00:00
2014-04-05 16:31:36 +00:00
// Get an array of all tiddler titles
this . allTitles = function ( ) {
return Object . keys ( tiddlers ) ;
2014-03-16 21:23:10 +00:00
} ;
2014-03-17 10:50:18 +00:00
// Iterate through all tiddler titles
2014-03-16 21:23:10 +00:00
this . each = function ( callback ) {
for ( var title in tiddlers ) {
callback ( tiddlers [ title ] , title ) ;
}
} ;
2014-03-17 10:50:18 +00:00
2014-04-05 16:31:36 +00:00
// Get an array of all shadow tiddler titles
this . allShadowTitles = function ( ) {
return Object . keys ( shadowTiddlers ) ;
} ;
2014-03-17 10:50:18 +00:00
// Iterate through all shadow tiddler titles
this . eachShadow = function ( callback ) {
for ( var title in shadowTiddlers ) {
var shadowInfo = shadowTiddlers [ title ] ;
callback ( shadowInfo . tiddler , title ) ;
}
} ;
2014-04-30 21:49:28 +00:00
// Iterate through all tiddlers and then the shadows
this . eachTiddlerPlusShadows = function ( callback ) {
for ( var title in tiddlers ) {
callback ( tiddlers [ title ] , title ) ;
}
for ( var title in shadowTiddlers ) {
if ( ! Object . prototype . hasOwnProperty . call ( tiddlers , title ) ) {
var shadowInfo = shadowTiddlers [ title ] ;
callback ( shadowInfo . tiddler , title ) ;
}
}
} ;
// Iterate through all the shadows and then the tiddlers
this . eachShadowPlusTiddlers = function ( callback ) {
for ( var title in shadowTiddlers ) {
var shadowInfo = shadowTiddlers [ title ] ;
callback ( shadowInfo . tiddler , title ) ;
}
for ( var title in tiddlers ) {
if ( ! Object . prototype . hasOwnProperty . call ( shadowTiddlers , title ) ) {
callback ( tiddlers [ title ] , title ) ;
}
}
} ;
2014-03-17 10:50:18 +00:00
// Test for the existence of a tiddler
2014-03-16 21:23:10 +00:00
this . tiddlerExists = function ( title ) {
return ! ! $tw . utils . hop ( tiddlers , title ) ;
} ;
2014-03-17 10:50:18 +00:00
// Determines if a tiddler is a shadow tiddler, regardless of whether it has been overridden by a real tiddler
this . isShadowTiddler = function ( title ) {
return $tw . utils . hop ( shadowTiddlers , title ) ;
2014-03-16 21:23:10 +00:00
} ;
2012-04-30 11:23:03 +00:00
2014-03-17 10:50:18 +00:00
this . getShadowSource = function ( title ) {
if ( $tw . utils . hop ( shadowTiddlers , title ) ) {
return shadowTiddlers [ title ] . source ;
}
return null ;
} ;
2012-04-30 11:23:03 +00:00
2014-03-17 10:50:18 +00:00
// Read plugin info for all plugins
this . readPluginInfo = function ( ) {
for ( var title in tiddlers ) {
var tiddler = tiddlers [ title ] ;
if ( tiddler . fields . type === "application/json" && tiddler . hasField ( "plugin-type" ) ) {
pluginInfo [ tiddler . fields . title ] = JSON . parse ( tiddler . fields . text ) ;
}
2012-04-30 11:23:03 +00:00
2014-03-14 15:23:12 +00:00
}
2014-03-17 10:50:18 +00:00
} ;
2014-03-14 15:23:12 +00:00
2014-03-18 21:18:30 +00:00
// Get plugin info for a plugin
this . getPluginInfo = function ( title ) {
return pluginInfo [ title ] ;
} ;
2014-03-17 10:50:18 +00:00
// Register the plugin tiddlers of a particular type, optionally restricting registration to an array of tiddler titles. Return the array of titles affected
this . registerPluginTiddlers = function ( pluginType , titles ) {
var self = this ,
registeredTitles = [ ] ,
checkTiddler = function ( tiddler ) {
if ( tiddler && tiddler . fields . type === "application/json" && tiddler . fields [ "plugin-type" ] === pluginType ) {
pluginTiddlers . push ( tiddler ) ;
registeredTitles . push ( tiddler . fields . title ) ;
}
} ;
if ( titles ) {
$tw . utils . each ( titles , function ( title ) {
checkTiddler ( self . getTiddler ( title ) ) ;
} ) ;
} else {
this . each ( function ( tiddler , title ) {
checkTiddler ( tiddler ) ;
} ) ;
2012-08-06 21:34:16 +00:00
}
2014-03-17 10:50:18 +00:00
return registeredTitles ;
2013-04-30 21:56:17 +00:00
} ;
2014-03-17 10:50:18 +00:00
// Unregister the plugin tiddlers of a particular type, returning an array of the titles affected
this . unregisterPluginTiddlers = function ( pluginType ) {
var self = this ,
titles = [ ] ;
// Remove any previous registered plugins of this type
for ( var t = pluginTiddlers . length - 1 ; t >= 0 ; t -- ) {
var tiddler = pluginTiddlers [ t ] ;
if ( tiddler . fields [ "plugin-type" ] === pluginType ) {
titles . push ( tiddler . fields . title ) ;
pluginTiddlers . splice ( t , 1 ) ;
}
2013-04-30 21:56:17 +00:00
}
2014-03-17 10:50:18 +00:00
return titles ;
} ;
2013-04-30 21:56:17 +00:00
2014-03-17 10:50:18 +00:00
// Unpack the currently registered plugins, creating shadow tiddlers for their constituent tiddlers
this . unpackPluginTiddlers = function ( ) {
var self = this ;
// Sort the plugin titles by the `plugin-priority` field
pluginTiddlers . sort ( function ( a , b ) {
if ( "plugin-priority" in a . fields && "plugin-priority" in b . fields ) {
return a . fields [ "plugin-priority" ] - b . fields [ "plugin-priority" ] ;
} else if ( "plugin-priority" in a . fields ) {
return - 1 ;
} else if ( "plugin-priority" in b . fields ) {
return + 1 ;
} else if ( a . fields . title < b . fields . title ) {
return - 1 ;
} else if ( a . fields . title === b . fields . title ) {
return 0 ;
} else {
return + 1 ;
2014-01-12 21:48:18 +00:00
}
2013-04-08 17:37:49 +00:00
} ) ;
2014-03-17 10:50:18 +00:00
// Now go through the plugins in ascending order and assign the shadows
2014-04-06 21:49:20 +00:00
shadowTiddlers = Object . create ( null ) ;
2014-03-17 10:50:18 +00:00
$tw . utils . each ( pluginTiddlers , function ( tiddler ) {
// Extract the constituent tiddlers
2014-03-31 17:30:45 +00:00
if ( $tw . utils . hop ( pluginInfo , tiddler . fields . title ) ) {
$tw . utils . each ( pluginInfo [ tiddler . fields . title ] . tiddlers , function ( constituentTiddler , constituentTitle ) {
// Save the tiddler object
if ( constituentTitle ) {
shadowTiddlers [ constituentTitle ] = {
source : tiddler . fields . title ,
tiddler : new $tw . Tiddler ( constituentTiddler , { title : constituentTitle } )
} ;
}
} ) ;
}
2014-03-17 10:50:18 +00:00
} ) ;
} ;
} ;
// Dummy methods that will be filled in after boot
$tw . Wiki . prototype . clearCache =
$tw . Wiki . prototype . clearGlobalCache =
$tw . Wiki . prototype . enqueueTiddlerEvent = function ( ) { } ;
// Add an array of tiddlers
$tw . Wiki . prototype . addTiddlers = function ( tiddlers ) {
for ( var t = 0 ; t < tiddlers . length ; t ++ ) {
this . addTiddler ( tiddlers [ t ] ) ;
}
2012-08-06 21:34:16 +00:00
} ;
2012-08-30 14:00:08 +00:00
/ *
2012-11-15 10:40:03 +00:00
Define all modules stored in ordinary tiddlers
2012-08-30 14:00:08 +00:00
* /
2012-11-15 10:40:03 +00:00
$tw . Wiki . prototype . defineTiddlerModules = function ( ) {
2014-03-16 21:23:10 +00:00
this . each ( function ( tiddler , title ) {
2012-11-17 13:08:25 +00:00
if ( tiddler . hasField ( "module-type" ) ) {
switch ( tiddler . fields . type ) {
case "application/javascript" :
2013-04-10 15:56:17 +00:00
// We only define modules that haven't already been defined, because in the browser modules in system tiddlers are defined in inline script
if ( ! $tw . utils . hop ( $tw . modules . titles , tiddler . fields . title ) ) {
2012-11-17 13:08:25 +00:00
$tw . modules . define ( tiddler . fields . title , tiddler . fields [ "module-type" ] , tiddler . fields . text ) ;
}
break ;
case "application/json" :
$tw . modules . define ( tiddler . fields . title , tiddler . fields [ "module-type" ] , JSON . parse ( tiddler . fields . text ) ) ;
break ;
case "application/x-tiddler-dictionary" :
$tw . modules . define ( tiddler . fields . title , tiddler . fields [ "module-type" ] , $tw . utils . parseFields ( tiddler . fields . text ) ) ;
break ;
}
2012-08-31 11:34:39 +00:00
}
2012-11-16 17:44:47 +00:00
} ) ;
2012-11-15 10:40:03 +00:00
} ;
/ *
Register all the module tiddlers that have a module type
* /
2013-04-30 21:56:17 +00:00
$tw . Wiki . prototype . defineShadowModules = function ( ) {
2012-11-16 17:44:47 +00:00
var self = this ;
2014-03-17 10:50:18 +00:00
this . eachShadow ( function ( tiddler , title ) {
// Don't define the module if it is overidden by an ordinary tiddler
if ( ! self . tiddlerExists ( title ) && tiddler . hasField ( "module-type" ) ) {
// Define the module
$tw . modules . define ( tiddler . fields . title , tiddler . fields [ "module-type" ] , tiddler . fields . text ) ;
2012-08-30 14:00:08 +00:00
}
2012-11-16 17:44:47 +00:00
} ) ;
2012-08-30 14:00:08 +00:00
} ;
2014-04-19 08:36:08 +00:00
/ *
Enable safe mode by deleting any tiddlers that override a shadow tiddler
* /
$tw . Wiki . prototype . processSafeMode = function ( ) {
var self = this ,
overrides = [ ] ;
// Find the overriding tiddlers
this . each ( function ( tiddler , title ) {
if ( self . isShadowTiddler ( title ) ) {
console . log ( title ) ;
overrides . push ( title ) ;
}
} ) ;
// Assemble a report tiddler
var titleReportTiddler = "TiddlyWiki Safe Mode" ,
report = [ ] ;
report . push ( "TiddlyWiki has been started in [[safe mode|http://tiddlywiki.com/static/SafeMode.html]]. Most customisations have been disabled by renaming the following tiddlers:" )
// Delete the overrides
overrides . forEach ( function ( title ) {
var tiddler = self . getTiddler ( title ) ,
newTitle = "SAFE: " + title ;
self . deleteTiddler ( title ) ;
self . addTiddler ( new $tw . Tiddler ( tiddler , { title : newTitle } ) ) ;
report . push ( "* [[" + title + "|" + newTitle + "]]" ) ;
} ) ;
report . push ( )
this . addTiddler ( new $tw . Tiddler ( { title : titleReportTiddler , text : report . join ( "\n\n" ) } ) ) ;
// Set $:/DefaultTiddlers to point to our report
this . addTiddler ( new $tw . Tiddler ( { title : "$:/DefaultTiddlers" , text : "[[" + titleReportTiddler + "]]" } ) ) ;
} ;
2012-04-30 11:23:03 +00:00
/ *
Extracts tiddlers from a typed block of text , specifying default field values
* /
$tw . Wiki . prototype . deserializeTiddlers = function ( type , text , srcFields ) {
2014-04-06 21:49:20 +00:00
srcFields = srcFields || Object . create ( null ) ;
2012-08-03 14:09:48 +00:00
var deserializer = $tw . Wiki . tiddlerDeserializerModules [ type ] ,
2014-04-06 21:49:20 +00:00
fields = Object . create ( null ) ;
2012-06-08 10:47:05 +00:00
if ( ! deserializer && $tw . config . fileExtensionInfo [ type ] ) {
2012-04-30 11:23:03 +00:00
// If we didn't find the serializer, try converting it from an extension to a content type
2012-06-08 10:47:05 +00:00
type = $tw . config . fileExtensionInfo [ type ] . type ;
2012-08-03 14:09:48 +00:00
deserializer = $tw . Wiki . tiddlerDeserializerModules [ type ] ;
2012-04-30 11:23:03 +00:00
}
2014-04-08 21:20:51 +00:00
if ( ! deserializer && $tw . config . contentTypeInfo [ type ] ) {
// see if this type has a different deserializer registered with it
2014-04-08 21:26:01 +00:00
type = $tw . config . contentTypeInfo [ type ] . deserializerType ;
2014-04-08 21:20:51 +00:00
deserializer = $tw . Wiki . tiddlerDeserializerModules [ type ] ;
}
2012-05-05 13:17:51 +00:00
if ( ! deserializer ) {
// If we still don't have a deserializer, treat it as plain text
2012-08-03 14:09:48 +00:00
deserializer = $tw . Wiki . tiddlerDeserializerModules [ "text/plain" ] ;
2012-05-05 13:17:51 +00:00
}
2012-04-30 11:23:03 +00:00
for ( var f in srcFields ) {
2012-05-04 17:49:04 +00:00
fields [ f ] = srcFields [ f ] ;
}
2012-04-30 11:23:03 +00:00
if ( deserializer ) {
2013-04-01 17:55:49 +00:00
return deserializer . call ( this , text , fields , type ) ;
2012-04-30 11:23:03 +00:00
} else {
// Return a raw tiddler for unknown types
fields . text = text ;
return [ fields ] ;
}
} ;
/ *
2012-08-03 14:09:48 +00:00
Register the built in tiddler deserializer modules
2012-04-30 11:23:03 +00:00
* /
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/js" , "tiddlerdeserializer" , {
2012-05-03 20:47:16 +00:00
"application/javascript" : function ( text , fields ) {
2012-08-22 11:33:21 +00:00
var headerCommentRegExp = new RegExp ( $tw . config . jsModuleHeaderRegExpString , "mg" ) ,
2012-04-30 11:23:03 +00:00
match = headerCommentRegExp . exec ( text ) ;
fields . text = text ;
if ( match ) {
2012-05-09 12:48:34 +00:00
fields = $tw . utils . parseFields ( match [ 1 ] . split ( /\r?\n\r?\n/mg ) [ 0 ] , fields ) ;
2012-04-30 11:23:03 +00:00
}
return [ fields ] ;
}
} ) ;
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/tid" , "tiddlerdeserializer" , {
2012-05-03 20:47:16 +00:00
"application/x-tiddler" : function ( text , fields ) {
2012-05-09 12:48:34 +00:00
var split = text . split ( /\r?\n\r?\n/mg ) ;
2012-12-20 09:20:44 +00:00
if ( split . length >= 1 ) {
2012-05-09 12:48:34 +00:00
fields = $tw . utils . parseFields ( split [ 0 ] , fields ) ;
2012-12-20 09:20:44 +00:00
}
if ( split . length >= 2 ) {
2012-05-09 12:48:34 +00:00
fields . text = split . slice ( 1 ) . join ( "\n\n" ) ;
2012-04-30 11:23:03 +00:00
} else {
2012-12-20 09:20:44 +00:00
fields . text = "" ;
2012-04-30 11:23:03 +00:00
}
return [ fields ] ;
}
} ) ;
2014-02-09 20:34:42 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/tids" , "tiddlerdeserializer" , {
"application/x-tiddlers" : function ( text , fields ) {
2014-02-21 09:24:36 +00:00
var titles = [ ] ,
tiddlers = [ ] ,
match = /\r?\n\r?\n/mg . exec ( text ) ;
if ( match ) {
fields = $tw . utils . parseFields ( text . substr ( 0 , match . index ) , fields ) ;
var lines = text . substr ( match . index + match [ 0 ] . length ) . split ( /\r?\n/mg ) ;
2014-02-09 20:34:42 +00:00
for ( var t = 0 ; t < lines . length ; t ++ ) {
var line = lines [ t ] ;
if ( line . charAt ( 0 ) !== "#" ) {
2014-02-21 09:24:36 +00:00
var colonPos = line . indexOf ( ": " ) ;
if ( colonPos !== - 1 ) {
2014-04-06 21:49:20 +00:00
var tiddler = $tw . utils . extend ( Object . create ( null ) , fields ) ;
2014-02-21 09:24:36 +00:00
tiddler . title = ( tiddler . title || "" ) + line . substr ( 0 , colonPos ) ;
if ( titles . indexOf ( tiddler . title ) !== - 1 ) {
console . log ( "Warning: .multids file contains multiple definitions for " + tiddler . title ) ;
}
titles . push ( tiddler . title ) ;
tiddler . text = line . substr ( colonPos + 2 ) ;
2014-02-09 20:34:42 +00:00
tiddlers . push ( tiddler ) ;
}
}
}
}
return tiddlers ;
}
} ) ;
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/txt" , "tiddlerdeserializer" , {
2013-04-01 17:55:49 +00:00
"text/plain" : function ( text , fields , type ) {
2012-06-06 20:42:14 +00:00
fields . text = text ;
2013-04-01 17:55:49 +00:00
fields . type = type || "text/plain" ;
2012-06-06 20:42:14 +00:00
return [ fields ] ;
}
} ) ;
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/html" , "tiddlerdeserializer" , {
2012-06-06 20:42:14 +00:00
"text/html" : function ( text , fields ) {
fields . text = text ;
fields . type = "text/html" ;
return [ fields ] ;
}
} ) ;
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/json" , "tiddlerdeserializer" , {
2012-10-27 13:51:43 +00:00
"application/json" : function ( text , fields ) {
var tiddlers = JSON . parse ( text ) ;
return tiddlers ;
}
} ) ;
2012-06-06 20:42:14 +00:00
2012-04-30 11:23:03 +00:00
/////////////////////////// Browser definitions
2012-05-19 10:29:51 +00:00
if ( $tw . browser ) {
2012-04-30 11:23:03 +00:00
2012-11-16 16:59:47 +00:00
/ *
2013-01-31 10:20:13 +00:00
Decrypt any tiddlers stored within the element with the ID "encryptedArea" . The function is asynchronous to allow the user to be prompted for a password
callback : function to be called the decryption is complete
2012-11-16 16:59:47 +00:00
* /
2012-11-16 17:23:10 +00:00
$tw . boot . decryptEncryptedTiddlers = function ( callback ) {
2013-01-31 10:20:13 +00:00
var encryptedArea = document . getElementById ( "encryptedStoreArea" ) ;
2012-11-16 16:59:47 +00:00
if ( encryptedArea ) {
2013-01-31 10:20:13 +00:00
var encryptedText = encryptedArea . innerHTML ;
2012-11-16 17:23:10 +00:00
// Prompt for the password
$tw . passwordPrompt . createPrompt ( {
serviceName : "Enter a password to decrypt this TiddlyWiki" ,
noUserName : true ,
submitText : "Decrypt" ,
callback : function ( data ) {
// Attempt to decrypt the tiddlers
$tw . crypto . setPassword ( data . password ) ;
2013-01-31 10:20:13 +00:00
var decryptedText = $tw . crypto . decrypt ( encryptedText ) ;
if ( decryptedText ) {
var json = JSON . parse ( decryptedText ) ;
for ( var title in json ) {
$tw . preloadTiddler ( json [ title ] ) ;
2012-11-16 17:23:10 +00:00
}
// Call the callback
callback ( ) ;
// Exit and remove the password prompt
return true ;
} else {
// We didn't decrypt everything, so continue to prompt for password
return false ;
}
}
} ) ;
2012-11-16 16:59:47 +00:00
} else {
2013-01-31 10:20:13 +00:00
// Just invoke the callback straight away if there weren't any encrypted tiddlers
2012-11-16 17:23:10 +00:00
callback ( ) ;
2012-11-16 16:59:47 +00:00
}
} ;
2012-04-30 11:23:03 +00:00
/ *
Register a deserializer that can extract tiddlers from the DOM
* /
2012-11-15 10:40:03 +00:00
$tw . modules . define ( "$:/boot/tiddlerdeserializer/dom" , "tiddlerdeserializer" , {
2012-05-03 20:47:16 +00:00
"(DOM)" : function ( node ) {
2012-09-02 19:29:47 +00:00
var extractTextTiddlers = function ( node ) {
2012-04-30 11:23:03 +00:00
var e = node . firstChild ;
while ( e && e . nodeName . toLowerCase ( ) !== "pre" ) {
e = e . nextSibling ;
}
var title = node . getAttribute ? node . getAttribute ( "title" ) : null ;
if ( e && title ) {
var attrs = node . attributes ,
tiddler = {
text : $tw . utils . htmlDecode ( e . innerHTML )
} ;
for ( var i = attrs . length - 1 ; i >= 0 ; i -- ) {
2012-08-31 10:38:30 +00:00
tiddler [ attrs [ i ] . name ] = attrs [ i ] . value ;
2012-04-30 11:23:03 +00:00
}
2012-09-02 19:29:47 +00:00
return [ tiddler ] ;
2012-04-30 11:23:03 +00:00
} else {
return null ;
}
} ,
2012-09-02 19:29:47 +00:00
extractModuleTiddlers = function ( node ) {
2012-04-30 11:23:03 +00:00
if ( node . hasAttribute && node . hasAttribute ( "data-tiddler-title" ) ) {
var text = node . innerHTML ,
s = text . indexOf ( "{" ) ,
e = text . lastIndexOf ( "}" ) ;
2012-05-05 16:42:42 +00:00
if ( node . hasAttribute ( "data-module" ) && s !== - 1 && e !== - 1 ) {
2012-07-10 22:18:07 +00:00
text = text . substring ( s + 1 , e ) ;
2012-04-30 11:23:03 +00:00
}
2012-05-05 16:42:42 +00:00
var fields = { text : text } ,
attributes = node . attributes ;
for ( var a = 0 ; a < attributes . length ; a ++ ) {
if ( attributes [ a ] . nodeName . substr ( 0 , 13 ) === "data-tiddler-" ) {
2012-08-31 10:38:30 +00:00
fields [ attributes [ a ] . nodeName . substr ( 13 ) ] = attributes [ a ] . value ;
2012-05-05 16:42:42 +00:00
}
}
2012-09-02 19:29:47 +00:00
return [ fields ] ;
} else {
return null ;
}
} ,
t , result = [ ] ;
2012-10-10 15:32:37 +00:00
if ( node ) {
for ( t = 0 ; t < node . childNodes . length ; t ++ ) {
2013-06-27 08:50:46 +00:00
var childNode = node . childNodes [ t ] ,
tiddlers = extractTextTiddlers ( childNode ) ;
2012-10-10 15:32:37 +00:00
tiddlers = tiddlers || extractModuleTiddlers ( childNode ) ;
if ( tiddlers ) {
result . push . apply ( result , tiddlers ) ;
}
}
2012-04-30 11:23:03 +00:00
}
2012-09-02 19:29:47 +00:00
return result ;
2012-04-30 11:23:03 +00:00
}
} ) ;
2012-11-15 10:40:03 +00:00
2014-01-14 14:09:04 +00:00
$tw . loadTiddlersBrowser = function ( ) {
2012-11-15 10:40:03 +00:00
// In the browser, we load tiddlers from certain elements
var containerIds = [
"libraryModules" ,
"modules" ,
"bootKernelPrefix" ,
"bootKernel" ,
"styleArea" ,
"storeArea" ,
2013-03-15 22:00:19 +00:00
"systemArea"
2012-11-15 10:40:03 +00:00
] ;
for ( var t = 0 ; t < containerIds . length ; t ++ ) {
$tw . wiki . addTiddlers ( $tw . wiki . deserializeTiddlers ( "(DOM)" , document . getElementById ( containerIds [ t ] ) ) ) ;
}
// Load any preloaded tiddlers
if ( $tw . preloadTiddlers ) {
$tw . wiki . addTiddlers ( $tw . preloadTiddlers ) ;
}
} ;
2012-04-30 11:23:03 +00:00
2012-05-19 10:29:51 +00:00
// End of if($tw.browser)
2012-04-30 11:23:03 +00:00
}
/////////////////////////// Server definitions
2012-05-19 10:29:51 +00:00
if ( ! $tw . browser ) {
2012-04-30 11:23:03 +00:00
2012-11-16 16:59:47 +00:00
/ *
Get any encrypted tiddlers
* /
2012-11-16 17:23:10 +00:00
$tw . boot . decryptEncryptedTiddlers = function ( callback ) {
2012-11-16 16:59:47 +00:00
// Storing encrypted tiddlers on the server isn't supported yet
2012-11-16 17:23:10 +00:00
callback ( ) ;
2012-11-16 16:59:47 +00:00
} ;
2014-01-14 14:09:04 +00:00
}
/////////////////////////// Node definitions
if ( $tw . node ) {
2012-04-30 11:23:03 +00:00
/ *
2013-03-23 22:11:23 +00:00
Load the tiddlers contained in a particular file ( and optionally extract fields from the accompanying . meta file ) returned as { filepath : , type : , tiddlers : [ ] , hasMetaFile : }
2012-04-30 11:23:03 +00:00
* /
2012-11-12 22:16:49 +00:00
$tw . loadTiddlersFromFile = function ( filepath , fields ) {
2012-08-31 15:36:57 +00:00
var ext = path . extname ( filepath ) ,
2012-06-08 10:47:05 +00:00
extensionInfo = $tw . config . fileExtensionInfo [ ext ] ,
2013-03-23 22:11:23 +00:00
type = extensionInfo ? extensionInfo . type : null ,
typeInfo = type ? $tw . config . contentTypeInfo [ type ] : null ,
2012-11-13 08:46:44 +00:00
data = fs . readFileSync ( filepath , typeInfo ? typeInfo . encoding : "utf8" ) ,
2012-04-30 11:23:03 +00:00
tiddlers = $tw . wiki . deserializeTiddlers ( ext , data , fields ) ,
2013-03-23 22:11:23 +00:00
metafile = filepath + ".meta" ,
metadata ;
2012-07-13 16:08:15 +00:00
if ( ext !== ".json" && tiddlers . length === 1 && fs . existsSync ( metafile ) ) {
2013-03-23 22:11:23 +00:00
metadata = fs . readFileSync ( metafile , "utf8" ) ;
2012-04-30 11:23:03 +00:00
if ( metadata ) {
tiddlers = [ $tw . utils . parseFields ( metadata , tiddlers [ 0 ] ) ] ;
}
}
2013-03-23 22:11:23 +00:00
return { filepath : filepath , type : type , tiddlers : tiddlers , hasMetaFile : ! ! metadata } ;
2012-04-30 11:23:03 +00:00
} ;
2013-10-13 16:58:10 +00:00
/ *
A default set of files for TiddlyWiki to ignore during load .
This matches what NPM ignores , and adds "*.meta" to ignore tiddler
metadata files .
* /
$tw . boot . excludeRegExp = /^\.DS_Store$|^.*\.meta$|^\..*\.swp$|^\._.*$|^\.git$|^\.hg$|^\.lock-wscript$|^\.svn$|^\.wafpickle-.*$|^CVS$|^npm-debug\.log$/ ;
2012-04-30 11:23:03 +00:00
/ *
2013-03-23 22:43:05 +00:00
Load all the tiddlers recursively from a directory , including honouring ` tiddlywiki.files ` files for drawing in external files . Returns an array of { filepath : , type : , tiddlers : [ { . . fields ... } ] , hasMetaFile : } . Note that no file information is returned for externally loaded tiddlers , just the ` tiddlers ` property .
2012-04-30 11:23:03 +00:00
* /
2012-11-12 22:16:49 +00:00
$tw . loadTiddlersFromPath = function ( filepath , excludeRegExp ) {
2013-10-13 16:58:10 +00:00
excludeRegExp = excludeRegExp || $tw . boot . excludeRegExp ;
2013-03-23 21:37:48 +00:00
var tiddlers = [ ] ;
2012-07-13 16:08:15 +00:00
if ( fs . existsSync ( filepath ) ) {
2013-03-23 21:37:48 +00:00
var stat = fs . statSync ( filepath ) ;
2012-05-04 17:24:54 +00:00
if ( stat . isDirectory ( ) ) {
2013-03-23 21:37:48 +00:00
var files = fs . readdirSync ( filepath ) ;
2012-08-31 14:04:08 +00:00
// Look for a tiddlywiki.files file
2012-11-12 22:16:49 +00:00
if ( files . indexOf ( "tiddlywiki.files" ) !== - 1 ) {
2012-05-05 10:21:59 +00:00
// If so, process the files it describes
2014-03-31 16:17:36 +00:00
var filesInfo = JSON . parse ( fs . readFileSync ( filepath + path . sep + "tiddlywiki.files" , "utf8" ) ) ;
2013-03-23 21:37:48 +00:00
$tw . utils . each ( filesInfo . tiddlers , function ( tidInfo ) {
var typeInfo = $tw . config . contentTypeInfo [ tidInfo . fields . type || "text/plain" ] ,
pathname = path . resolve ( filepath , tidInfo . file ) ,
text = fs . readFileSync ( pathname , typeInfo ? typeInfo . encoding : "utf8" ) ;
2013-04-25 08:05:02 +00:00
if ( tidInfo . prefix ) {
text = tidInfo . prefix + text ;
}
2013-07-14 22:06:37 +00:00
if ( tidInfo . suffix ) {
text = text + tidInfo . suffix ;
}
2012-08-31 16:25:18 +00:00
tidInfo . fields . text = text ;
2013-04-03 20:10:57 +00:00
tiddlers . push ( { tiddlers : [ tidInfo . fields ] } ) ;
2013-03-23 21:37:48 +00:00
} ) ;
2012-05-05 10:21:59 +00:00
} else {
// If not, read all the files in the directory
2013-03-23 21:37:48 +00:00
$tw . utils . each ( files , function ( file ) {
2012-05-05 10:21:59 +00:00
if ( ! excludeRegExp . test ( file ) ) {
2014-03-31 16:17:36 +00:00
tiddlers . push . apply ( tiddlers , $tw . loadTiddlersFromPath ( filepath + path . sep + file , excludeRegExp ) ) ;
2012-05-05 10:21:59 +00:00
}
2013-03-23 21:37:48 +00:00
} ) ;
2012-04-30 11:23:03 +00:00
}
2012-05-04 17:24:54 +00:00
} else if ( stat . isFile ( ) ) {
2013-03-23 22:11:23 +00:00
tiddlers . push ( $tw . loadTiddlersFromFile ( filepath ) ) ;
2012-04-30 11:23:03 +00:00
}
}
2012-08-31 15:36:57 +00:00
return tiddlers ;
} ;
/ *
2013-03-23 16:34:12 +00:00
Load the tiddlers from a plugin folder , and package them up into a proper JSON plugin tiddler
2012-08-31 15:36:57 +00:00
* /
2013-03-23 16:34:12 +00:00
$tw . loadPluginFolder = function ( filepath , excludeRegExp ) {
2013-10-13 16:58:10 +00:00
excludeRegExp = excludeRegExp || $tw . boot . excludeRegExp ;
2013-03-23 16:34:12 +00:00
var stat , files , pluginInfo , pluginTiddlers = [ ] , f , file , titlePrefix , t ;
2012-11-13 08:46:44 +00:00
if ( fs . existsSync ( filepath ) ) {
stat = fs . statSync ( filepath ) ;
if ( stat . isDirectory ( ) ) {
// Read the plugin information
2014-03-31 16:17:36 +00:00
pluginInfo = JSON . parse ( fs . readFileSync ( filepath + path . sep + "plugin.info" , "utf8" ) ) ;
2013-03-23 16:34:12 +00:00
// Read the plugin files
2013-03-23 22:11:23 +00:00
files = fs . readdirSync ( filepath ) ;
2012-11-13 08:46:44 +00:00
for ( f = 0 ; f < files . length ; f ++ ) {
file = files [ f ] ;
2013-03-23 16:34:12 +00:00
if ( ! excludeRegExp . test ( file ) && file !== "plugin.info" && file !== "tiddlywiki.files" ) {
2014-03-31 16:17:36 +00:00
var tiddlerFiles = $tw . loadTiddlersFromPath ( filepath + path . sep + file , excludeRegExp ) ;
2013-03-23 21:37:48 +00:00
$tw . utils . each ( tiddlerFiles , function ( tiddlerFile ) {
pluginTiddlers . push . apply ( pluginTiddlers , tiddlerFile . tiddlers ) ;
} ) ;
2012-11-13 08:46:44 +00:00
}
}
2013-03-23 16:34:12 +00:00
// Save the plugin tiddlers into the plugin info
2014-04-06 21:49:20 +00:00
pluginInfo . tiddlers = pluginInfo . tiddlers || Object . create ( null ) ;
2013-03-23 16:34:12 +00:00
for ( t = 0 ; t < pluginTiddlers . length ; t ++ ) {
2014-01-12 21:48:18 +00:00
if ( pluginTiddlers [ t ] . title ) {
pluginInfo . tiddlers [ pluginTiddlers [ t ] . title ] = pluginTiddlers [ t ] ;
}
2012-11-13 08:46:44 +00:00
}
2012-11-12 22:16:49 +00:00
}
}
2013-08-26 12:28:23 +00:00
// Give the plugin the same version number as the core if it doesn't have one
2014-01-28 18:58:01 +00:00
if ( pluginInfo && ! ( "version" in pluginInfo ) ) {
2013-08-26 12:28:23 +00:00
pluginInfo . version = $tw . packageInfo . version ;
}
2013-03-23 16:34:12 +00:00
// Save the plugin tiddler
2013-04-08 17:37:49 +00:00
if ( pluginInfo ) {
var fields = {
title : pluginInfo . title ,
type : "application/json" ,
2013-12-14 09:46:02 +00:00
text : JSON . stringify ( { tiddlers : pluginInfo . tiddlers } , null , 4 ) ,
2013-04-28 21:52:26 +00:00
"plugin-priority" : pluginInfo [ "plugin-priority" ] ,
2013-05-01 11:01:43 +00:00
"name" : pluginInfo [ "name" ] ,
2013-08-26 12:28:23 +00:00
"version" : pluginInfo [ "version" ] ,
2013-05-01 11:01:43 +00:00
"thumbnail" : pluginInfo [ "thumbnail" ] ,
"description" : pluginInfo [ "description" ] ,
2013-12-14 09:46:02 +00:00
"plugin-type" : pluginInfo [ "plugin-type" ] || "plugin" ,
"dependents" : $tw . utils . stringifyList ( pluginInfo [ "dependents" ] || [ ] )
2013-04-08 17:37:49 +00:00
}
return fields ;
} else {
return null ;
}
2012-05-04 17:49:04 +00:00
} ;
2012-04-30 11:23:03 +00:00
2013-03-25 18:55:41 +00:00
/ *
path : path of wiki directory
parentPaths : array of parent paths that we mustn ' t recurse into
* /
$tw . loadWikiTiddlers = function ( wikiPath , parentPaths ) {
parentPaths = parentPaths || [ ] ;
var wikiInfoPath = path . resolve ( wikiPath , $tw . config . wikiInfo ) ,
2013-12-18 21:11:00 +00:00
wikiInfo ,
2013-03-25 18:55:41 +00:00
pluginFields ;
2013-03-28 14:06:50 +00:00
// Bail if we don't have a wiki info file
2013-12-18 21:11:00 +00:00
if ( fs . existsSync ( wikiInfoPath ) ) {
wikiInfo = JSON . parse ( fs . readFileSync ( wikiInfoPath , "utf8" ) ) ;
} else {
2014-02-22 16:38:34 +00:00
return null ;
2013-03-28 14:06:50 +00:00
}
// Load any parent wikis
if ( wikiInfo . includeWikis ) {
parentPaths = parentPaths . slice ( 0 ) ;
parentPaths . push ( wikiPath ) ;
$tw . utils . each ( wikiInfo . includeWikis , function ( includedWikiPath ) {
var resolvedIncludedWikiPath = path . resolve ( wikiPath , includedWikiPath ) ;
if ( parentPaths . indexOf ( resolvedIncludedWikiPath ) === - 1 ) {
$tw . loadWikiTiddlers ( resolvedIncludedWikiPath , parentPaths ) ;
} else {
$tw . utils . error ( "Cannot recursively include wiki " + resolvedIncludedWikiPath ) ;
}
} ) ;
}
// Load any plugins listed in the wiki info file
if ( wikiInfo . plugins ) {
2013-03-28 17:07:30 +00:00
var pluginBasePath = path . resolve ( $tw . boot . corePath , $tw . config . pluginsPath ) ;
2013-03-28 14:06:50 +00:00
for ( var t = 0 ; t < wikiInfo . plugins . length ; t ++ ) {
pluginFields = $tw . loadPluginFolder ( path . resolve ( pluginBasePath , "./" + wikiInfo . plugins [ t ] ) ) ;
if ( pluginFields ) {
$tw . wiki . addTiddler ( pluginFields ) ;
2013-03-25 18:55:41 +00:00
}
}
}
2013-04-28 08:42:48 +00:00
// Load any themes listed in the wiki info file
if ( wikiInfo . themes ) {
var themesBasePath = path . resolve ( $tw . boot . corePath , $tw . config . themesPath ) ;
for ( var t = 0 ; t < wikiInfo . themes . length ; t ++ ) {
pluginFields = $tw . loadPluginFolder ( path . resolve ( themesBasePath , "./" + wikiInfo . themes [ t ] ) ) ;
if ( pluginFields ) {
$tw . wiki . addTiddler ( pluginFields ) ;
}
}
}
2014-02-09 19:18:46 +00:00
// Load any languages listed in the wiki info file
if ( wikiInfo . languages ) {
var languagesBasePath = path . resolve ( $tw . boot . corePath , $tw . config . languagesPath ) ;
for ( var t = 0 ; t < wikiInfo . languages . length ; t ++ ) {
pluginFields = $tw . loadPluginFolder ( path . resolve ( languagesBasePath , "./" + wikiInfo . languages [ t ] ) ) ;
if ( pluginFields ) {
$tw . wiki . addTiddler ( pluginFields ) ;
}
}
}
2013-03-23 22:11:23 +00:00
// Load the wiki files, registering them as writable
2013-03-25 18:55:41 +00:00
var resolvedWikiPath = path . resolve ( wikiPath , $tw . config . wikiTiddlersSubDir ) ;
$tw . utils . each ( $tw . loadTiddlersFromPath ( resolvedWikiPath ) , function ( tiddlerFile ) {
2013-03-23 22:43:05 +00:00
if ( tiddlerFile . filepath ) {
$tw . utils . each ( tiddlerFile . tiddlers , function ( tiddler ) {
$tw . boot . files [ tiddler . title ] = {
filepath : tiddlerFile . filepath ,
type : tiddlerFile . type ,
hasMetaFile : tiddlerFile . hasMetaFile
} ;
} ) ;
}
2014-02-27 16:30:05 +00:00
$tw . wiki . addTiddlers ( tiddlerFile . tiddlers ) ;
2013-03-23 21:37:48 +00:00
} ) ;
2014-02-27 16:30:05 +00:00
// Save the original tiddler file locations if requested
if ( wikiInfo . config && wikiInfo . config [ "retain-original-tiddler-path" ] ) {
var output = [ ] ;
for ( var title in $tw . boot . files ) {
output . push ( title + ": " + path . relative ( resolvedWikiPath , $tw . boot . files [ title ] . filepath ) + "\n" ) ;
}
$tw . wiki . addTiddler ( { title : "$:/config/OriginalTiddlerPaths" , type : "application/x-tiddler-dictionary" , text : output . join ( "" ) } ) ;
}
2013-08-21 08:42:51 +00:00
// Save the path to the tiddlers folder for the filesystemadaptor
$tw . boot . wikiTiddlersPath = path . resolve ( $tw . boot . wikiPath , $tw . config . wikiTiddlersSubDir ) ;
2012-11-14 11:23:43 +00:00
// Load any plugins within the wiki folder
2013-03-25 18:55:41 +00:00
var wikiPluginsPath = path . resolve ( wikiPath , $tw . config . wikiPluginsSubDir ) ;
2012-11-14 11:23:43 +00:00
if ( fs . existsSync ( wikiPluginsPath ) ) {
var pluginFolders = fs . readdirSync ( wikiPluginsPath ) ;
for ( t = 0 ; t < pluginFolders . length ; t ++ ) {
2013-03-23 16:34:12 +00:00
pluginFields = $tw . loadPluginFolder ( path . resolve ( wikiPluginsPath , "./" + pluginFolders [ t ] ) ) ;
if ( pluginFields ) {
$tw . wiki . addTiddler ( pluginFields ) ;
2012-11-14 11:23:43 +00:00
}
2012-11-13 08:46:44 +00:00
}
}
2013-07-14 22:08:04 +00:00
// Load any themes within the wiki folder
var wikiThemesPath = path . resolve ( wikiPath , $tw . config . wikiThemesSubDir ) ;
if ( fs . existsSync ( wikiThemesPath ) ) {
var themeFolders = fs . readdirSync ( wikiThemesPath ) ;
for ( t = 0 ; t < themeFolders . length ; t ++ ) {
pluginFields = $tw . loadPluginFolder ( path . resolve ( wikiThemesPath , "./" + themeFolders [ t ] ) ) ;
if ( pluginFields ) {
$tw . wiki . addTiddler ( pluginFields ) ;
}
}
}
2014-02-09 19:18:46 +00:00
// Load any languages within the wiki folder
var wikiLanguagesPath = path . resolve ( wikiPath , $tw . config . wikiLanguagesSubDir ) ;
if ( fs . existsSync ( wikiLanguagesPath ) ) {
var languageFolders = fs . readdirSync ( wikiLanguagesPath ) ;
for ( t = 0 ; t < languageFolders . length ; t ++ ) {
pluginFields = $tw . loadPluginFolder ( path . resolve ( wikiLanguagesPath , "./" + languageFolders [ t ] ) ) ;
if ( pluginFields ) {
$tw . wiki . addTiddler ( pluginFields ) ;
}
}
}
2013-03-25 18:55:41 +00:00
return wikiInfo ;
} ;
2014-01-14 14:09:04 +00:00
$tw . loadTiddlersNode = function ( ) {
2013-03-28 17:07:30 +00:00
// Load the boot tiddlers
2013-03-25 18:55:41 +00:00
$tw . utils . each ( $tw . loadTiddlersFromPath ( $tw . boot . bootPath ) , function ( tiddlerFile ) {
$tw . wiki . addTiddlers ( tiddlerFile . tiddlers ) ;
} ) ;
2013-03-28 17:07:30 +00:00
// Load the core tiddlers
$tw . wiki . addTiddler ( $tw . loadPluginFolder ( $tw . boot . corePath ) ) ;
2013-03-25 18:55:41 +00:00
// Load the tiddlers from the wiki directory
2013-08-21 08:42:51 +00:00
if ( $tw . boot . wikiPath ) {
$tw . boot . wikiInfo = $tw . loadWikiTiddlers ( $tw . boot . wikiPath ) ;
}
2012-11-15 10:40:03 +00:00
} ;
2014-01-14 14:09:04 +00:00
// End of if($tw.node)
2012-11-12 22:16:49 +00:00
}
2013-03-22 20:02:19 +00:00
/////////////////////////// Main startup function called once tiddlers have been decrypted
2012-08-06 21:34:16 +00:00
2014-01-14 14:09:04 +00:00
/ *
Startup TiddlyWiki . Options are :
2014-01-15 14:51:04 +00:00
readBrowserTiddlers : whether to read tiddlers from the HTML file we ' re executing within ; if not , tiddlers are read from the file system with Node . js APIs
2014-01-14 14:09:04 +00:00
* /
$tw . boot . startup = function ( options ) {
options = options || { } ;
2014-05-02 18:21:32 +00:00
// Get the URL hash and check for safe mode
$tw . locationHash = "#" ;
if ( $tw . browser ) {
if ( location . hash === "#:safe" ) {
$tw . safeMode = true ;
} else {
$tw . locationHash = location . hash ;
}
}
2013-03-25 10:43:18 +00:00
// Initialise some more $tw properties
$tw . utils . deepDefaults ( $tw , {
modules : { // Information about each module
2014-04-06 21:49:20 +00:00
titles : Object . create ( null ) , // hashmap by module title of {fn:, exports:, moduleType:}
2013-03-25 10:43:18 +00:00
types : { } // hashmap by module type of hashmap of exports
} ,
config : { // Configuration overridables
pluginsPath : "../plugins/" ,
2013-04-28 08:42:48 +00:00
themesPath : "../themes/" ,
2014-02-09 19:18:46 +00:00
languagesPath : "../languages/" ,
2014-02-23 22:58:17 +00:00
editionsPath : "../editions/" ,
2013-03-25 10:43:18 +00:00
wikiInfo : "./tiddlywiki.info" ,
wikiPluginsSubDir : "./plugins" ,
2013-07-14 22:08:04 +00:00
wikiThemesSubDir : "./themes" ,
2014-02-09 19:18:46 +00:00
wikiLanguagesSubDir : "./languages" ,
2013-03-25 10:43:18 +00:00
wikiTiddlersSubDir : "./tiddlers" ,
2014-04-25 21:41:59 +00:00
wikiOutputSubDir : "./output" ,
2013-06-27 08:43:04 +00:00
jsModuleHeaderRegExpString : "^\\/\\*\\\\(?:\\r?\\n)((?:^[^\\r\\n]*(?:\\r?\\n))+?)(^\\\\\\*\\/$(?:\\r?\\n)?)" ,
2014-04-06 21:49:20 +00:00
fileExtensionInfo : Object . create ( null ) , // Map file extension to {type:}
contentTypeInfo : Object . create ( null ) // Map type to {encoding:,extension:}
2013-03-25 10:43:18 +00:00
}
} ) ;
2014-01-18 14:53:26 +00:00
if ( ! options . readBrowserTiddlers ) {
2013-03-23 22:11:23 +00:00
// For writable tiddler files, a hashmap of title to {filepath:,type:,hasMetaFile:}
2014-04-06 21:49:20 +00:00
$tw . boot . files = Object . create ( null ) ;
2013-03-22 20:02:19 +00:00
// System paths and filenames
$tw . boot . bootPath = path . dirname ( module . filename ) ;
2013-03-28 17:07:30 +00:00
$tw . boot . corePath = path . resolve ( $tw . boot . bootPath , "../core" ) ;
2014-03-10 18:20:07 +00:00
// If there's no arguments then default to `--help`
if ( $tw . boot . argv . length === 0 ) {
$tw . boot . argv = [ "--help" ] ;
}
2013-03-22 20:02:19 +00:00
// If the first command line argument doesn't start with `--` then we
// interpret it as the path to the wiki folder, which will otherwise default
// to the current folder
2014-02-22 16:38:34 +00:00
if ( $tw . boot . argv [ 0 ] && $tw . boot . argv [ 0 ] . indexOf ( "--" ) !== 0 ) {
2013-03-22 20:02:19 +00:00
$tw . boot . wikiPath = $tw . boot . argv [ 0 ] ;
$tw . boot . argv = $tw . boot . argv . slice ( 1 ) ;
} else {
$tw . boot . wikiPath = process . cwd ( ) ;
}
// Read package info
2014-01-15 14:51:04 +00:00
$tw . packageInfo = require ( "../package.json" ) ;
2013-03-22 20:02:19 +00:00
// Check node version number
if ( $tw . utils . checkVersions ( $tw . packageInfo . engines . node . substr ( 2 ) , process . version . substr ( 1 ) ) ) {
2013-07-21 20:11:48 +00:00
$tw . utils . error ( "TiddlyWiki5 requires node.js version " + $tw . packageInfo . engines . node ) ;
2013-03-22 20:02:19 +00:00
}
}
2013-03-22 21:12:39 +00:00
// Add file extension information
$tw . utils . registerFileType ( "text/vnd.tiddlywiki" , "utf8" , ".tid" ) ;
$tw . utils . registerFileType ( "application/x-tiddler" , "utf8" , ".tid" ) ;
2014-02-20 10:27:02 +00:00
$tw . utils . registerFileType ( "application/x-tiddlers" , "utf8" , ".multids" ) ;
2013-03-22 21:12:39 +00:00
$tw . utils . registerFileType ( "application/x-tiddler-html-div" , "utf8" , ".tiddler" ) ;
2013-05-13 16:42:07 +00:00
$tw . utils . registerFileType ( "text/vnd.tiddlywiki2-recipe" , "utf8" , ".recipe" ) ;
2013-03-22 21:12:39 +00:00
$tw . utils . registerFileType ( "text/plain" , "utf8" , ".txt" ) ;
$tw . utils . registerFileType ( "text/css" , "utf8" , ".css" ) ;
$tw . utils . registerFileType ( "text/html" , "utf8" , ".html" ) ;
2014-04-08 21:26:01 +00:00
$tw . utils . registerFileType ( "application/hta" , "utf16le" , ".hta" , { deserializerType : "text/html" } ) ;
2013-03-22 21:12:39 +00:00
$tw . utils . registerFileType ( "application/javascript" , "utf8" , ".js" ) ;
$tw . utils . registerFileType ( "application/json" , "utf8" , ".json" ) ;
2014-04-08 21:20:51 +00:00
$tw . utils . registerFileType ( "application/pdf" , "base64" , ".pdf" , { flags : [ "image" ] } ) ;
$tw . utils . registerFileType ( "image/jpeg" , "base64" , ".jpg" , { flags : [ "image" ] } ) ;
$tw . utils . registerFileType ( "image/png" , "base64" , ".png" , { flags : [ "image" ] } ) ;
$tw . utils . registerFileType ( "image/gif" , "base64" , ".gif" , { flags : [ "image" ] } ) ;
$tw . utils . registerFileType ( "image/svg+xml" , "utf8" , ".svg" , { flags : [ "image" ] } ) ;
$tw . utils . registerFileType ( "image/x-icon" , "base64" , ".ico" , { flags : [ "image" ] } ) ;
2013-05-01 12:04:27 +00:00
$tw . utils . registerFileType ( "application/font-woff" , "base64" , ".woff" ) ;
2013-03-22 20:02:19 +00:00
// Create the wiki store for the app
2013-03-22 19:44:12 +00:00
$tw . wiki = new $tw . Wiki ( ) ;
// Install built in tiddler fields modules
$tw . Tiddler . fieldModules = $tw . modules . getModulesByTypeAsHashmap ( "tiddlerfield" ) ;
// Install the tiddler deserializer modules
2014-04-06 21:49:20 +00:00
$tw . Wiki . tiddlerDeserializerModules = Object . create ( null ) ;
2013-03-22 19:44:12 +00:00
$tw . modules . applyMethods ( "tiddlerdeserializer" , $tw . Wiki . tiddlerDeserializerModules ) ;
// Load tiddlers
2014-01-15 14:51:04 +00:00
if ( options . readBrowserTiddlers ) {
2014-01-14 14:09:04 +00:00
$tw . loadTiddlersBrowser ( ) ;
} else {
$tw . loadTiddlersNode ( ) ;
}
2013-03-23 16:34:12 +00:00
// Unpack plugin tiddlers
2014-03-14 15:23:12 +00:00
$tw . wiki . readPluginInfo ( ) ;
2013-04-30 21:56:17 +00:00
$tw . wiki . registerPluginTiddlers ( "plugin" ) ;
$tw . wiki . unpackPluginTiddlers ( ) ;
2014-04-19 08:36:08 +00:00
// Process "safe mode"
if ( $tw . safeMode ) {
$tw . wiki . processSafeMode ( ) ;
}
2013-03-22 19:44:12 +00:00
// Register typed modules from the tiddlers we've just loaded
$tw . wiki . defineTiddlerModules ( ) ;
2013-03-23 16:34:12 +00:00
// And any modules within plugins
2013-04-30 21:56:17 +00:00
$tw . wiki . defineShadowModules ( ) ;
2013-03-22 19:44:12 +00:00
// Make sure the crypto state tiddler is up to date
2014-01-14 14:09:04 +00:00
if ( $tw . crypto ) {
$tw . crypto . updateCryptoStateTiddler ( ) ;
}
2013-03-22 19:44:12 +00:00
// Run any startup modules
$tw . modules . forEachModuleOfType ( "startup" , function ( title , module ) {
if ( module . startup ) {
module . startup ( ) ;
}
} ) ;
} ;
2013-08-20 14:17:57 +00:00
/////////////////////////// Main boot function to decrypt tiddlers and then startup
$tw . boot . boot = function ( ) {
// Initialise crypto object
$tw . crypto = new $tw . utils . Crypto ( ) ;
// Initialise password prompter
if ( $tw . browser ) {
$tw . passwordPrompt = new $tw . utils . PasswordPrompt ( ) ;
}
// Preload any encrypted tiddlers
$tw . boot . decryptEncryptedTiddlers ( function ( ) {
// Startup
2014-01-15 14:51:04 +00:00
$tw . boot . startup ( {
readBrowserTiddlers : ! ! $tw . browser
} ) ;
2013-08-20 14:17:57 +00:00
} ) ;
} ;
/////////////////////////// Autoboot in the browser
2013-03-22 19:27:09 +00:00
2014-01-14 14:09:04 +00:00
if ( $tw . browser && ! $tw . boot . suppressBoot ) {
2013-08-20 14:17:57 +00:00
$tw . boot . boot ( ) ;
2012-11-16 16:59:47 +00:00
}
2013-06-12 11:40:48 +00:00
2013-10-12 17:44:09 +00:00
return $tw ;
} ) ;
if ( typeof ( exports ) !== "undefined" ) {
exports . TiddlyWiki = _boot ;
} else {
_boot ( window . $tw ) ;
}