1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-23 18:17:20 +00:00

Added support for wildcard references in recipes

This commit is contained in:
Jeremy Ruston 2012-01-16 19:22:10 +00:00
parent 9075b8a020
commit 2ff603da0e
5 changed files with 90 additions and 66 deletions

View File

@ -1,7 +1,7 @@
/*\
title: js/FileRetriever.js
FileRetriever can asynchronously retrieve files from HTTP URLs or the local file system
FileRetriever can asynchronously retrieve files from HTTP URLs or the local file system. Files are treated as utf-8 text or, if the filepath ends in one of the recognised binary extensions, as a base64 encoded binary string
\*/
(function(){
@ -18,13 +18,16 @@ var fs = require("fs"),
var FileRetriever = exports;
// These are the file extensions that we'll recognise as binary.
FileRetriever.binaryFileExtensions = [".jpg",".jpeg",".png",".gif"];
// Retrieve a local file and invoke callback(err,data) in the usual way
var fileRequest = function fileRequest(filepath,callback) {
fs.readFile(filepath, function (err,data) {
if(err) {
callback(err);
} else {
// Check if we need to base64 encode the file
if(FileRetriever.binaryFileExtensions.indexOf(path.extname(filepath)) !== -1) {
callback(err,data.toString("base64"));
} else {
@ -34,6 +37,7 @@ var fileRequest = function fileRequest(filepath,callback) {
});
};
// Retrieve a file over HTTP and invoke callback(err,data) in the usual way
var httpRequest = function(fileurl,callback) {
var opts = url.parse(fileurl),
httpLib = opts.protocol === "http:" ? http : https,

View File

@ -1,8 +1,6 @@
/*\
title: js/Recipe.js
FileRetriever can asynchronously retrieve files from HTTP URLs or the local file system
Recipe processing is in four parts:
1) The recipe file is parsed and any subrecipe files loaded recursively into this structure:
@ -31,7 +29,7 @@ At this point tiddlers are placed in the store so that they can be referenced by
...
}
4) Finally, the template is processed by replacing the markers with the text of the associated tiddlers
4) Finally, to actually cook the recipe, the template is processed by replacing the markers with the text of the associated tiddlers
\*/
(function(){
@ -47,6 +45,18 @@ var Tiddler = require("./Tiddler.js").Tiddler,
util = require("util"),
async = require("async");
/*
Load a recipe file. Arguments are:
options: See below
callback: Function to be called when the recipe has been loaded as callback(err), null === success
Options include:
filepath: The filepath of the recipe file to load. Can be a local path or an HTTP URL
store: Indicates the WikiStore to use to store the tiddlers (mandatory)
*/
var Recipe = function(options,callback) {
var me = this;
this.filepath = options.filepath;
@ -54,6 +64,7 @@ var Recipe = function(options,callback) {
this.callback = callback;
this.recipe = [];
this.markers = {};
// A task queue for loading recipe files
this.recipeQueue = async.queue(function(task,callback) {
retrieveFile(task.filepath,task.contextPath,function(err,data) {
if(err) {
@ -64,6 +75,7 @@ var Recipe = function(options,callback) {
}
});
},1);
// A task queue for loading tiddler files
this.tiddlerQueue = async.queue(function(task,callback) {
me.readTiddlerFile(task.filepath,task.contextPath,function(err,data) {
if(err) {
@ -76,47 +88,87 @@ var Recipe = function(options,callback) {
}
}
}
task.recipeLine.tiddlers = data;
if(!task.recipeLine.tiddlers) {
task.recipeLine.tiddlers = [];
}
Array.prototype.push.apply(task.recipeLine.tiddlers,data);
callback(null);
}
});
},1);
// Called when all the recipes have been loaded
this.recipeQueue.drain = function() {
me.loadTiddlerFiles(me.recipe);
// Initiate the loading of the tiddlers referenced by the recipe
for(var r=0; r<me.recipe.length; r++) {
me.loadTiddlerFiles(me.recipe[r]);
}
};
// Called when all the tiddlers have been loaded
this.tiddlerQueue.drain = function() {
// Select the tiddlers that are associated with each marker
me.chooseTiddlers(me.recipe);
// Sort the main content tiddlers (makes it easier to diff TiddlyWiki files)
me.sortTiddlersForMarker("tiddler");
me.callback(null);
};
// Start the process off by queueing up the loading of the initial recipe
this.recipeQueue.push({filepath: this.filepath,
contextPath: process.cwd(),
recipe: this.recipe});
};
Recipe.prototype.loadTiddlerFiles = function(recipe) {
for(var r=0; r<recipe.length; r++) {
var recipeLine = recipe[r];
if(recipeLine instanceof Array) {
this.loadTiddlerFiles(recipeLine);
/*
Recursively queue loading the tiddler files referenced by a recipe line
*/
Recipe.prototype.loadTiddlerFiles = function(recipeLine) {
var me = this;
if(recipeLine instanceof Array) {
for(var r=0; r<recipeLine.length; r++) {
me.loadTiddlerFiles(recipeLine[r]);
}
} else {
var filepath = recipeLine.filepath, // eg ../js/*.js
filedir = path.dirname(filepath), // eg ../js
filename = path.basename(filepath), // eg *.js
posStar = filename.indexOf("*");
if(posStar !== -1) {
var fileRegExp = new RegExp("^" + filename.replace(/[-[\]{}()+?.,\\^$|#\s]/g, "\\$&").replace("*",".*") + "$");
var files = fs.readdirSync(path.resolve(path.dirname(recipeLine.contextPath),filedir));
for(var f=0; f<files.length; f++) {
if(fileRegExp.test(files[f])) {
me.tiddlerQueue.push({
filepath: filedir + "/" + files[f],
contextPath: recipeLine.contextPath,
recipeLine: recipeLine
});
}
}
} else {
this.tiddlerQueue.push({filepath: recipeLine.filepath, contextPath: recipeLine.contextPath, recipeLine: recipeLine});
me.tiddlerQueue.push({filepath: filepath, contextPath: recipeLine.contextPath, recipeLine: recipeLine});
}
}
};
/*
Choose the tiddlers to be included on each marker
*/
Recipe.prototype.chooseTiddlers = function(recipe) {
// Loop through the lines of the recipe
for(var r=0; r<recipe.length; r++) {
var recipeLine = recipe[r];
if(recipeLine instanceof Array) {
// Process subrecipes recursively
this.chooseTiddlers(recipeLine);
} else {
// Choose the store and marker array to be used for this marker
var store = recipeLine.marker === "shadow" ? this.store.shadows : this.store,
markerArray = this.markers[recipeLine.marker];
// Create the marker array if necessary
if(markerArray === undefined) {
this.markers[recipeLine.marker] = [];
markerArray = this.markers[recipeLine.marker];
}
// Process each of the tiddlers referenced by the recipe line
for(var t=0; t<recipeLine.tiddlers.length; t++) {
// Only add the tiddler to the marker if it isn't already there
var found = false;
@ -127,20 +179,26 @@ Recipe.prototype.chooseTiddlers = function(recipe) {
}
if(!found) {
markerArray.push(recipeLine.tiddlers[t].title);
}
}
// Add the tiddler to the store
store.addTiddler(new Tiddler(recipeLine.tiddlers[t]));
}
}
}
};
/*
Sort the tiddlers associated with a particular marker
*/
Recipe.prototype.sortTiddlersForMarker = function(marker) {
if(this.markers[marker]) {
this.markers[marker].sort();
}
};
// Process the contents of a recipe file
/*
Process the contents of a recipe file
*/
Recipe.prototype.processRecipeFile = function(recipe,text,contextPath) {
var matchLine = function(linetext) {
var lineRegExp = /^(#?)(\s*)(#?)([^\s\:]+)\s*:\s*(.+)*\s*$/,
@ -163,7 +221,11 @@ Recipe.prototype.processRecipeFile = function(recipe,text,contextPath) {
}
if(match.marker === "recipe") {
var insertionPoint = recipe.push([]) - 1;
this.recipeQueue.push({filepath: match.value, contextPath: contextPath, recipe: recipe[insertionPoint]});
this.recipeQueue.push({
filepath: match.value,
contextPath: contextPath,
recipe: recipe[insertionPoint]
});
} else {
var fieldLines = [],
fieldMatch = matchLine(lines[line]);
@ -188,6 +250,7 @@ Recipe.prototype.readTiddlerFile = function(filepath,contextPath,callback) {
// Read the tiddler file
retrieveFile(filepath,contextPath,function(err,data) {
if (err) throw err;
// Use the filepath as the default title for the tiddler
var fields = {
title: data.path
};

View File

@ -9,8 +9,10 @@ Recipe files contain lines consisting of a marker, a colon and the pathname of a
{{{
marker: filepath
}}}
The filepath is interpreted relative to the directory containing the recipe file.
The filepath is interpreted relative to the directory containing the recipe file. The filename can contain the `*` wildcard character, for example:
{{{
tiddler: ../content/*.tid
}}}
You can use filepaths or URLs to reference recipe files and tiddlers. For example, this recipe cooks the latest TiddlyWiki components directly from the online repositories:
{{{
# Get the recipe direct from GitHub

View File

@ -1,22 +0,0 @@
tiddler: HelloThere.tid
tiddler: ThisIsAlpha.tid
tiddler: CommandLineInterface.tid
tiddler: RecipeFiles.tid
tiddler: TiddlerFiles.tid
tiddler: TiddlyWikiArchitecture.tid
tiddler: TiddlyWikiInternals.tid
tiddler: NewWikiTextFeatures.tid
tiddler: MacroInternals.tid
tiddler: ReadMe.tid
tiddler: Testing.tid
tiddler: Introduction.tid
tiddler: Motovun Jack.jpg
tiddler: SiteTitle.tid
tiddler: SiteSubtitle.tid
tiddler: SimpleTemplate.tid
tiddler: PageTemplate.tid
tiddler: StoryTiddlers.tid

View File

@ -2,7 +2,8 @@ template: tiddlywiki5.template.html
copyright: ../copyright.txt
style: styles.css
recipe: tiddlers/split.recipe
tiddler: tiddlers/*.tid
tiddler: tiddlers/*.jpg
recipe: ../parsers/split.recipe
tiddler: ../docs/High Level Architecture.svg
#tiddler: http://wikitext.tiddlyspace.com/fractalveg.jpg
@ -10,33 +11,9 @@ tiddler: ../docs/High Level Architecture.svg
jslib: ../test/tiddlywiki.2.6.5/source/tiddlywiki/jquery/jquery.js
jsmodule: ../js/JavaScriptParser.js
jsmodule: ../js/JavaScriptParseTree.js
jsmodule: ../js/ArgParser.js
jsmodule: ../js/FileRetriever.js
jsmodule: ../js/Utils.js
jsmodule: ../js/Tiddler.js
jsmodule: ../js/TiddlerInput.js
jsmodule: ../js/TiddlerOutput.js
jsmodule: ../js/WikiStore.js
jsmodule: ../js/SVGParser.js
jsmodule: ../js/BitmapParser.js
jsmodule: ../js/WikiTextParser.js
jsmodule: ../js/WikiTextRules.js
jsmodule: ../js/WikiTextParseTree.js
jsmodule: ../js/Navigators.js
jsmodule: ../js/StoryNavigator.js
jsmodule: ../js/App.js
jsmodule: ../js/*.js
jsmodule: ../js/macros/echo.js
jsmodule: ../js/macros/image.js
jsmodule: ../js/macros/info.js
jsmodule: ../js/macros/link.js
jsmodule: ../js/macros/list.js
jsmodule: ../js/macros/story.js
jsmodule: ../js/macros/tiddler.js
jsmodule: ../js/macros/version.js
jsmodule: ../js/macros/view.js
jsmodule: ../js/macros/*.js
jsmodule: ../node_modules/pegjs/lib/peg.js
title: pegjs