commit f96708a846169bcbd0ab263d2db55fdd4cc79d29 Author: Jeremy Ruston Date: Tue Nov 22 14:29:29 2011 +0000 Initial commit diff --git a/cook.js b/cook.js new file mode 100644 index 000000000..8d1e48680 --- /dev/null +++ b/cook.js @@ -0,0 +1,15 @@ +// Cook a TiddlyWiki recipe and send it to STDOUT +// +// Usage: node cook.js + +var sys = require("sys"), + tiddlywiki = require("./js/TiddlyWiki.js"), + recipe = require("./js/Recipe.js"); + +var filename = process.argv[2]; + +var store = new tiddlywiki.TiddlyWiki(); + +var theRecipe = new recipe.Recipe(store,filename); + +console.log(theRecipe.cook()); diff --git a/js/ArgParser.js b/js/ArgParser.js new file mode 100644 index 000000000..5a5ee2cc7 --- /dev/null +++ b/js/ArgParser.js @@ -0,0 +1,90 @@ +/* + Parse a space-separated string of name:value parameters + The parameters are returned in a structure that can be referenced like this: + + (return).byName["name"][0] - First occurance of parameter with a given name + (return).byPos[0].n - Name of parameter in first position + (return).byPos[0].v - Value of parameter in first position + +Options and their defaults are: + + defaultName: null, + defaultValue: null, + noNames: false, + cascadeDefaults: false + +*/ + +var ArgParser = function(argString,options) { + var parseToken = function(match,p) { + var n; + if(match[p]) // Double quoted + n = match[p]; + else if(match[p+1]) // Single quoted + n = match[p+1]; + else if(match[p+2]) // Double-square-bracket quoted + n = match[p+2]; + else if(match[p+3]) // Double-brace quoted + n = match[p+3]; + else if(match[p+4]) // Unquoted + n = match[p+4]; + else if(match[p+5]) // empty quote + n = ""; + return n; + }; + this.byPos = []; + var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")"; + var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')"; + var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])"; + var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})"; + var unQuoted = options.noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)"; + var emptyQuote = "((?:\"\")|(?:''))"; + var skipSpace = "(?:\\s*)"; + var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")"; + var re = options.noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg"); + do { + var match = re.exec(argString); + if(match) { + var n = parseToken(match,1); + if(options.noNames) { + this.byPos.push({n:"", v:n}); + } else { + var v = parseToken(match,8); + if(v === null && options.defaultName) { + v = n; + n = options.defaultName; + } else if(v === null && options.defaultValue) { + v = options.defaultValue; + } + this.byPos.push({n:n, v:v}); + if(options.cascadeDefaults) { + options.defaultName = n; + options.defaultValue = v; + } + } + } + } while(match); + this.byName = {}; + for(var t=0; t 0 ? v[0] : defaultValue; +} + +// Retrieve all the values of a named parameter as an array +ArgParser.prototype.getValuesByName = function(n,defaultValue) { + var v = this.byName[n]; + return v && v.length > 0 ? v : defaultValue; +} + +exports.ArgParser = ArgParser diff --git a/js/Recipe.js b/js/Recipe.js new file mode 100644 index 000000000..d0c2e1368 --- /dev/null +++ b/js/Recipe.js @@ -0,0 +1,205 @@ +/* + +Recipe files consist of recipe lines consisting of a marker, a colon and the pathname of an ingredient: + +marker: pathname + +The pathname is interpreted relative to the directory containing the recipe file. + +The special marker "recipe" is used to load a sub-recipe file. + +The special marker "template" is used to identify the HTML template. The HTML template contains +markers in two different forms: + + +<!--@@marker@@--> + +Recipe processing is in two parts. First the recipe file is parsed and the referenced files are loaded into tiddlers. +Second, the template is processed by replacing the markers with the text of the tiddlers indicated in the recipe file. + +The recipe is parsed into the 'ingredients' hashmap like this: + +this.ingredients = { + "marker1": [Tiddler1,Tiddler2,Tiddler3,...], + "marker2": [TiddlerA,TiddlerB,TiddlerC,...], + .... +}; + +*/ + +var tiddler = require("./Tiddler.js"), + tiddlerUtils = require("./TiddlerUtils.js"), + tiddlywiki = require("./TiddlyWiki.js"), + fs = require("fs"), + path = require("path"), + util = require("util"); + +// Create a new Recipe object from the specified recipe file, storing the tiddlers in a specified TiddlyWiki store +var Recipe = function(store,filepath) { + this.store = store; // Save a reference to the store + this.ingredients = {}; // Hashmap of array of ingredients + this.readRecipe(filepath); // Read the recipe file +} + +// Specialised configuration and handlers for particular ingredient markers +var specialMarkers = { + shadow: { + readIngredientPostProcess: function(fields) { + // Add ".shadow" to the name of shadow tiddlers + fields.title = fields.title + ".shadow"; + return fields; + } + } +}; + +// Process the contents of a recipe file +Recipe.prototype.readRecipe = function(filepath) { + var dirname = path.dirname(filepath), + me = this; + fs.readFileSync(filepath,"utf8").split("\n").forEach(function(line) { + var p = line.indexOf(":"); + if(p !== -1) { + var marker = line.substr(0, p).trim(), + value = line.substr(p+1).trim(); + if(marker === "recipe") { + me.readRecipe(path.resolve(dirname,value)); + } else { + var fields = me.readIngredient(dirname,value), + postProcess = me.readIngredientPostProcess[marker]; + if(postProcess) + fields = postProcess(fields); + me.addIngredient(marker,fields); + } + } + }); +} + +// Special post-processing required for certain ingredient types +Recipe.prototype.readIngredientPostProcess = { + "shadow": function(fields) { + // Add ".shadow" to the name of shadow tiddlers + fields.title = fields.title + ".shadow"; + return fields; + } +}; + +Recipe.prototype.addIngredient = function(marker,tiddlerFields) { + var ingredientTiddler = new tiddler.Tiddler(tiddlerFields); + this.store.addTiddler(ingredientTiddler); + if(marker in this.ingredients) { + this.ingredients[marker].push(ingredientTiddler); + } else { + this.ingredients[marker] = [ingredientTiddler]; + } +} + +// Read an ingredient file and return it as a hashmap of tiddler fields. Also read the .meta file, if present +Recipe.prototype.readIngredient = function(dirname,filepath) { + var fullpath = path.resolve(dirname,filepath), + extname = path.extname(filepath), + basename = path.basename(filepath,extname), + fields = { + title: basename + }; + // Read the tiddler file + fields = tiddlerUtils.parseTiddler(fs.readFileSync(fullpath,"utf8"),extname,fields); + // Check for the .meta file + var metafile = fullpath + ".meta"; + if(path.existsSync(metafile)) { + fields = tiddlerUtils.parseMetaDataBlock(fs.readFileSync(metafile,"utf8"),fields); + } + return fields; +} + +// Return a string of the cooked recipe +Recipe.prototype.cook = function() { + var template = this.ingredients.template ? this.ingredients.template[0].fields.text : ""; + var out = []; + var me = this; + template.split("\n").forEach(function(line) { + var templateRegExp = /^(?:)|(?:<!--@@(.*)@@-->)$/gi; + var match = templateRegExp.exec(line); + if(match) { + var marker = match[1] === undefined ? match[2] : match[1]; + me.outputIngredient(out,marker); + } else { + out.push(line); + } + }); +// out.push("\nRecipe:\n" + util.inspect(this.ingredients,false,4)); + return out.join("\n"); +} + +// Output all the tiddlers in the recipe with a particular marker +Recipe.prototype.outputIngredient = function(out,marker) { + var ingredient = this.ingredients[marker]; + var outputType = Recipe.ingredientOutputMapper[marker] || "raw"; + var outputter = Recipe.ingredientOutputter[outputType]; + if(outputter && ingredient) { + outputter(out,ingredient); + } +} + +// Allows for specialised processing for certain markers +Recipe.ingredientOutputMapper = { + tiddler: "div", + js: "javascript", + jsdeprecated: "javascript", + jquery: "javascript", + shadow: "shadow" +}; + +Recipe.ingredientOutputter = { + raw: function(out,ingredient) { + // The default is just to output the raw text of the tiddler, ignoring any metadata + for(var t=0; t + for(var t=0; t with the the ".shadow" suffix removed from the title + for(var t=0; t +
The text of the tiddler (without the expected HTML encoding).
+
+ + +Note that the field attributes are HTML encoded, but that the body of the
 tag is not.
+*/
+tiddlerUtils.parseTiddlerDiv = function(text,fields) {
+	if(fields === undefined) {
+		var fields = {};
+	}
+	var divRegExp = /^\s*]*)>((?:.|\n)*)<\/div>\s*$/gi;
+	var subDivRegExp = /^(?:\s*
)((?:.|\n)*)(?:<\/pre>\s*)$/gi;
+	var attrRegExp = /\s*([^=\s]+)\s*=\s*"([^"]*)"/gi;
+	var match = divRegExp.exec(text);
+	if(match) {
+		var subMatch = subDivRegExp.exec(match[2]); // Body of the 
tag + if(subMatch) { + fields.text = subMatch[1]; + } else { + fields.text = match[2]; + } + do { + var attrMatch = attrRegExp.exec(match[1]); + if(attrMatch) { + var name = attrMatch[1]; + var value = attrMatch[2]; + fields[name] = tiddlerUtils.parseMetaDataItem(name,value); + } + } while(attrMatch); + } + return fields; +} + +// Output a tiddler as an HTML
+// out - array to push the output strings +// tid - the tiddler to be output +// options - options: +// omitPrecedingLineFeed - determines if a linefeed is inserted between the
 tag and the text
+tiddlerUtils.outputTiddlerDiv = function(out,tid,options) {
+	var result = [];
+	var outputAttribute = function(name,value) {
+		result.push(" " + name + "=\"" + value + "\"");
+	};
+	result.push("\n
");
+	if(!(options && options.omitPrecedingLineFeed))
+		result.push("\n");
+	result.push(tiddlerUtils.htmlEncode(tid.fields.text));
+	result.push("
\n
"); + out.push(result.join("")); +} + +tiddlerUtils.stringifyTags = function(tags) { + var results = []; + for(var t=0; t to ">" and " to """ +tiddlerUtils.htmlEncode = function(s) +{ + return s.replace(/&/mg,"&").replace(//mg,">").replace(/\"/mg,"""); +}; + +// Convert "&" to &, "<" to <, ">" to > and """ to " +tiddlerUtils.htmlDecode = function(s) +{ + return s.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&"); +}; + diff --git a/js/TiddlyWiki.js b/js/TiddlyWiki.js new file mode 100644 index 000000000..e8f8fbd59 --- /dev/null +++ b/js/TiddlyWiki.js @@ -0,0 +1,33 @@ +var tiddler = require("./Tiddler.js"); + +var TiddlyWiki = function() { + this.tiddlers = {}; +}; + +TiddlyWiki.prototype.clear = function() { + this.tiddlers = {}; +} + +TiddlyWiki.prototype.fetchTiddler = function(title) { + var t = this.tiddlers[title]; + return t instanceof tiddler.Tiddler ? t : null; +} + +TiddlyWiki.prototype.deleteTiddler = function(title) { + delete this.tiddlers[title]; +} + +TiddlyWiki.prototype.addTiddler = function(tiddler) { + this.tiddlers[tiddler.title] = tiddler; +} + +TiddlyWiki.prototype.forEachTiddler = function(callback) { + var t; + for(t in this.tiddlers) { + var tiddler = this.tiddlers[t]; + if(tiddler instanceof tiddler.Tiddler) + callback.call(this,t,tiddler); + } +} + +exports.TiddlyWiki = TiddlyWiki diff --git a/readme.md b/readme.md new file mode 100644 index 000000000..a198bd45e --- /dev/null +++ b/readme.md @@ -0,0 +1,21 @@ +# cook.js + +This is an attempt to modernise TiddlyWiki's build system, which is based on tools written in Ruby called Cook and Ginsu (see https://github.com/TiddlyWiki/cooker for details). This new version is written in JavaScript for node.js, with the intention that it can share code with TiddlyWiki itself. + +## Usage + + node cook.js + +Cooks a recipe file and sends the output to STDOUT + + node server.js + +Cooks a recipe file and serves it over HTTP port 8000 + +## Testing + +`test.sh` contains a simple test that cooks the main tiddlywiki.com recipe, first with the old Ruby-based tool, and then the new one. It uses OS X's opendiff to display the differences between the two files. + +## Current status + +As of 22nd November 2011, cook.js can now build a fully functional TiddlyWiki from the existing recipe files. There are still some minor differences in the layout of tiddler attributes, and some whitespace issues that prevent full byte-for-byte compatibility. diff --git a/server.js b/server.js new file mode 100644 index 000000000..cc0649393 --- /dev/null +++ b/server.js @@ -0,0 +1,22 @@ +// Cook a TiddlyWiki recipe and serve the result over HTTP +// +// Usage: node server.js + +var tiddlywiki = require("./js/TiddlyWiki.js"), + recipe = require("./js/Recipe.js"), + sys = require("sys"), + http = require("http"), + fs = require("fs"), + url = require("url"), + path = require("path"); + +var filename = process.argv[2]; + +http.createServer(function (request, response) { + response.writeHead(200, {"Content-Type": "text/html"}); + var store = new tiddlywiki.TiddlyWiki(); + var theRecipe = new recipe.Recipe(store,filename); + response.end(theRecipe.cook(), "utf-8"); +}).listen(8000); + +sys.puts("Server running at http://127.0.0.1:8000/"); diff --git a/test.sh b/test.sh new file mode 100755 index 000000000..51d16b7af --- /dev/null +++ b/test.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# cook the recipe with the old cook.rb (assuming you have set it up as described in github.com/TiddlyWiki/cooker) +cook $PWD/test/data/tiddlywiki.com/index.html.recipe -d $PWD -o oldcooked.html + +# cook it with cook.js +node cook.js $PWD/test/data/tiddlywiki.com/index.html.recipe > newcooked.html + +# compare the two +opendiff oldcooked.html newcooked.html diff --git a/test/data/.DS_Store b/test/data/.DS_Store new file mode 100644 index 000000000..32370b4cf Binary files /dev/null and b/test/data/.DS_Store differ diff --git a/test/data/samples/tiddlers/Test1.tiddler b/test/data/samples/tiddlers/Test1.tiddler new file mode 100755 index 000000000..799f631fb --- /dev/null +++ b/test/data/samples/tiddlers/Test1.tiddler @@ -0,0 +1,3 @@ +
+
This is a testing tiddler.
+
\ No newline at end of file diff --git a/test/data/samples/tiddlers/TestContainsTag.tiddler b/test/data/samples/tiddlers/TestContainsTag.tiddler new file mode 100755 index 000000000..26b022c17 --- /dev/null +++ b/test/data/samples/tiddlers/TestContainsTag.tiddler @@ -0,0 +1,3 @@ +
+
This is a testing tiddler with 
in it.
+ \ No newline at end of file diff --git a/test/data/samples/tiddlers/TestNoPreTag.tiddler b/test/data/samples/tiddlers/TestNoPreTag.tiddler new file mode 100644 index 000000000..b2932189d --- /dev/null +++ b/test/data/samples/tiddlers/TestNoPreTag.tiddler @@ -0,0 +1 @@ +
This is a tiddler that hasn't got a {{{
}}} tag
\ No newline at end of file diff --git a/test/data/samples/tiddlers/TestWithTags.tiddler b/test/data/samples/tiddlers/TestWithTags.tiddler new file mode 100644 index 000000000..2165816e8 --- /dev/null +++ b/test/data/samples/tiddlers/TestWithTags.tiddler @@ -0,0 +1 @@ +
I welcome contributions to the TiddlyWiki core: code, documentation, or anything else that helps to make it a better product.\n\nIn terms of code, I'm happy to accept small snippets inline in emails or group messages. For larger changes, I prefer to receive a modified version of the entire TiddlyWiki HTML file via email; I can then easily analyse the differences.\n\nTo maximise the chances of your work being accepted into the core, you should consider these points:\n* Is the code reasonably short?\n* Is the functionality nearly universally useful?\n* Is the functionality impractical to offer as a plugin?\n* Have you used the same coding conventions as the rest of the core?\n* Have you tested the change on all major browsers?\n
\ No newline at end of file diff --git a/test/data/samples/tiddlers/test.recipe b/test/data/samples/tiddlers/test.recipe new file mode 100755 index 000000000..eec465140 --- /dev/null +++ b/test/data/samples/tiddlers/test.recipe @@ -0,0 +1,5 @@ +recipe: ../../tiddlywiki.com/index.html.recipe +tiddler: Test1.tiddler +tiddler: TestNoPreTag.tiddler +tiddler: TestWithTags.tiddler +tiddler: TestContainsTag.tiddler diff --git a/test/data/tiddlywiki.com/README.md b/test/data/tiddlywiki.com/README.md new file mode 100755 index 000000000..06cd6af15 --- /dev/null +++ b/test/data/tiddlywiki.com/README.md @@ -0,0 +1,63 @@ +TiddlyWiki +========== + +https://github.com/TiddlyWiki/tiddlywiki.com + + +Description +----------- + +This repository contains the tools required to create the site http://tiddlywiki.com/ + +The content for tiddlywiki.com is obtained from a [TiddlySpace](http://tiddlyspace.com/). + + +Prerequisites +------------- + +Ensure that you have downloaded and installed TiddlyWiki as described at https://github.com/TiddlyWiki/tiddlywiki + +You need perl to build tiddlywiki.com. If you do not have it installed, it can be downloaded [here](http://www.perl.org/get.html). + +You need to set up `ginsu`. Copy the `ginsu` script file to somewhere that is on your path. Edit this file according to the instructions in the file. + +You need to set up the `tiddler2tid`. Copy the `tiddler2tid` script file to somewhere that is on your path. + + +Building tiddlywiki.com +----------------------- + +After downloading and installing TiddlyWiki checkout the version of TiddlyWiki that you wish to use for tiddlywiki.com. Ongoing development occurs in the tiddlywiki repository, so you need to checkout a tagged release version of TiddlyWiki. Change to the tiddlywiki directory and checkout the required version, eg: + + git checkout tags/v2.6.5 + +Change back to the tiddlywiki.com directory. + +Pull down the tiddlywiki.com content form TiddlySpace by invoking the `pull.sh` script: + + ./pull.sh + +Edit the build script `bld` setting the correct version number for TiddlyWiki. + +Invoke the build script: + + ./bld + +You now need to generate the TiddlyWiki RSS file. To do this open the TiddlyWiki file index.html in Firefox, ensure the AdvancedOption "Generate an RSS feed when saving changes" is set, and then save the TiddlyWiki. Doing this also causes TiddlyWiki to generate some static HTML for display when Javascript is not enabled. + +Edit the upload script `upload` setting the correct version number for TiddlyWiki. + +Finally you need to upload the TiddlyWiki files to tiddlywiki.com. If this is the first time you are uploading, then you will need to create a `tmp` directory on tiddlywiki.com: + + ssh user@tiddlywiki.com + [enter your password when prompted] + mkdir tmp + exit + +You can now upload the TiddlyWiki files, run the upload script: + + ./upload + +You will be prompted for your password on several occasions during the upload process. To do this you will of course need an account on tiddlywiki.com. The upload script assumes your remote user name is the same as your local user name, if it is not then you may specify your remote user name as the first parameter to the upload script. + +Migrated from http://svn.tiddlywiki.org on 20110719. diff --git a/test/data/tiddlywiki.com/bld b/test/data/tiddlywiki.com/bld new file mode 100755 index 000000000..cd8c49fc4 --- /dev/null +++ b/test/data/tiddlywiki.com/bld @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +# Usage: +# bld [release] + +DEFAULT_RELEASE="2.6.5" +RELEASE=${1:-$DEFAULT_RELEASE} +DEST=$PWD/cooked/tiddlywiki.com +mkdir -p cooked +mkdir -p cooked/tiddlywiki.com +cook $PWD/index.html.recipe -d $DEST -o index.$RELEASE.html +cook $PWD/empty.html.recipe -d $DEST -o empty.$RELEASE.html +cp ../tiddlywiki/java/TiddlySaver.jar $DEST/TiddlySaver.jar +rm $DEST/empty.$RELEASE.zip +cp $DEST/empty.$RELEASE.html tmp/empty.html +zip -j $DEST/empty.$RELEASE.zip tmp/empty.html $DEST/TiddlySaver.jar +rm tmp/empty.html diff --git a/test/data/tiddlywiki.com/empty.html.recipe b/test/data/tiddlywiki.com/empty.html.recipe new file mode 100755 index 000000000..c25233e11 --- /dev/null +++ b/test/data/tiddlywiki.com/empty.html.recipe @@ -0,0 +1 @@ +recipe: ../tiddlywiki/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki.com/ginsu b/test/data/tiddlywiki.com/ginsu new file mode 100755 index 000000000..2cb334d8a --- /dev/null +++ b/test/data/tiddlywiki.com/ginsu @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This is a sample ginsu script to be used with the tiddlywiki.com scripts +# Adjust this script and then install it somewhere on your $PATH, such as ~/bin. +# +# You will need to adjust COOKER_TRUNK below. +# +# Change this to where you have the cooker code installed +COOKER_TRUNK=$HOME/Documents/Code/GitHub/tiddlywiki/cooker +DEFAULT_FILENAME=index +FILENAME=${1:-$DEFAULT_FILENAME} +DEST=$PWD +RECIPE=$PWD/$FILENAME.html +ruby -C $COOKER_TRUNK ginsu.rb $RECIPE -d$DEST $2 $3 $4 $5 diff --git a/test/data/tiddlywiki.com/index.html.recipe b/test/data/tiddlywiki.com/index.html.recipe new file mode 100755 index 000000000..a493049eb --- /dev/null +++ b/test/data/tiddlywiki.com/index.html.recipe @@ -0,0 +1,3 @@ +recipe: ../tiddlywiki/tiddlywikinonoscript.html.recipe +recipe: tiddlywiki-com-ref/split.recipe +recipe: tiddlywiki-com/split.recipe diff --git a/test/data/tiddlywiki.com/pull.sh b/test/data/tiddlywiki.com/pull.sh new file mode 100755 index 000000000..469b990ba --- /dev/null +++ b/test/data/tiddlywiki.com/pull.sh @@ -0,0 +1,58 @@ +#!/bin/sh +# +# this hack pulls down the wikis for each bag, splitting the wiki into tiddlers using ginsu +# long term plan is to use the "fat" JSON for a bag +# + +set -e +export space +export dir + +mkdir -p tmp + +for space in tiddlywiki-com-ref tiddlywiki-com +do + mkdir -p $space + + dir=tmp/${space}.html.0 + curl -s http://${space}.tiddlyspace.com/bags/${space}_public/tiddlers.wiki > tmp/$space.html + + # clear out the space directory so we can see deleted files when we commit + rm -f $space/* + + # backup any existing exploded content + mkdir -p backups + [ -d $dir ] && mv $dir backups/$$ + + # split into tiddlers + ( + cd tmp + ginsu $space > /dev/null + ) + + # convert .tiddler files into .tid files + ( + cd "$dir" + + tiddler2tid *.tiddler + find . -name \*.tid -o -name \*.js -o -name \*.meta | + while read file + do + sed -e '/^server.*: /d' -e '/^_hash:/d' < "$file" > "../../$space/$file" + done + ) + + # make recipe based on files in the space directory + ( + cd $space + + find . -name \*.tid -o -name \*.js | + grep -v '\.jpg\.' | + grep -v 'PageTemplate' | + grep -v 'SplashScreen' | + grep -v 'SiteSubtitle' | + sed 's/^/tiddler: /' > split.recipe + ) +done + +cook $PWD/index.html.recipe diff --git a/test/data/tiddlywiki.com/tiddler2tid b/test/data/tiddlywiki.com/tiddler2tid new file mode 100755 index 000000000..7e45ceb8b --- /dev/null +++ b/test/data/tiddlywiki.com/tiddler2tid @@ -0,0 +1,53 @@ +#!/usr//bin/env perl + +# +# convert .tiddler into .tid files +# useful for ginsu a TiddlyWiki, then HTTP PUT them to TiddlyWeb/TiddlySpaces +# + +use strict; + +sub read_file { + my ($filename) = @_; + undef $/; + local *FILE; + open FILE, "< $filename"; + binmode(FILE, ":utf8"); + my $c = ; + close FILE; + return $c; +} + +foreach my $file (@ARGV) { + + my $tid = $file; + my $text = ""; + + if ($file =~ /.tiddler$/) { + + $tid =~ s/dler$//; + $text = read_file($file, encoding => 'utf8'); + + my $attrs = $text; + $attrs =~ s/\s*]*)>.*$/$1/s; + $attrs =~ s/\s*(\w+)\s*=\s*["']([^"']*)["']\s*/$1: $2\n/gs; + + $text =~ s/^\s*]*>\s*<\s*pre>\s*(.*)\s*<\/pre\s*>\s*<\/div\s*>\s*$/$1/s; + + $text = $attrs . "\n" . $text; + + } elsif ($file =~ /.js$/) { + + $tid =~ s/.js$/.tid/; + $text = read_file($file . ".meta") . "\n" . read_file($file); + + } + + if ($text) { + print "$tid\n"; + open(FILE, "> $tid"); + binmode(FILE, ":utf8"); + print FILE $text; + close(FILE); + } +} diff --git a/test/data/tiddlywiki.com/upload b/test/data/tiddlywiki.com/upload new file mode 100755 index 000000000..3a3883c63 --- /dev/null +++ b/test/data/tiddlywiki.com/upload @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Usage: +# upload [user] [release] [cleanup] + +# default values +DEFAULT_RELEASE="2.6.5" +REMOTE_USER=${1:-$USER} +RELEASE=${2:-$DEFAULT_RELEASE} +DEST=$PWD/cooked/tiddlywiki.com +HOST="tiddlywiki.com" +DIR="/var/www/www.tiddlywiki.com/htdocs" +ARCHIVE_DIR="$DIR/archive" +OWNER="www-data:www-data" +PERM="664" + +# setPermissions() +# Usage: +# setPermissions file +function setPermissions() { + COMMANDS="$COMMANDS sudo chown $OWNER $1;" + COMMANDS="$COMMANDS sudo chmod $PERM $1;" +} + +# upload files to temporary folder +echo +echo "uploading files" +echo +FILES="$DEST/index.$RELEASE.html $DEST/index.$RELEASE.xml $DEST/empty.$RELEASE.html $DEST/TiddlySaver.jar $DEST/empty.$RELEASE.zip" +scp $FILES "$REMOTE_USER@$HOST:./tmp/" + +# transfer files to their respective folders +echo +echo "transferring files" +echo +COMMANDS="ssh $REMOTE_USER@$HOST" +# Index +COMMANDS="$COMMANDS sudo cp ./tmp/index.$RELEASE.html $ARCHIVE_DIR/;" +setPermissions "$ARCHIVE_DIR/index.$RELEASE.html" +COMMANDS="$COMMANDS sudo mv ./tmp/index.$RELEASE.html $DIR/index.html;" +setPermissions "$DIR/index.html" +COMMANDS="$COMMANDS sudo mv ./tmp/index.$RELEASE.xml $DIR/index.xml;" +setPermissions "$DIR/index.xml" +# Empty +COMMANDS="$COMMANDS sudo cp ./tmp/empty.$RELEASE.html $ARCHIVE_DIR/;" +setPermissions "$ARCHIVE_DIR/empty.$RELEASE.html" +COMMANDS="$COMMANDS sudo cp ./tmp/empty.$RELEASE.html $DIR/empty.html;" +setPermissions "$DIR/empty.html" +# Upgrade +COMMANDS="$COMMANDS sudo mv ./tmp/empty.$RELEASE.html $DIR/upgrade/index.html;" +setPermissions "$DIR/upgrade/index.html" +# TiddlySaver +COMMANDS="$COMMANDS sudo mv ./tmp/TiddlySaver.jar $DIR/TiddlySaver.jar;" +setPermissions "$DIR/TiddlySaver.jar" +# ZIP package +COMMANDS="$COMMANDS sudo mv ./tmp/empty.$RELEASE.zip $DIR/empty.zip;" +setPermissions "$DIR/empty.zip" +# execute +$COMMANDS + +# cleanup +if [ "$3" = "true" ]; then + echo "cleaning up (removing cooked files)" + echo "removing index.$RELEASE.html" + rm "index.$RELEASE.html" + echo "removing empty.$RELEASE.html" + rm "empty.$RELEASE.html" + echo "removing TiddlySaver.jar" + rm "TiddlySaver.jar" +fi diff --git a/test/data/tiddlywiki/ALPHA b/test/data/tiddlywiki/ALPHA new file mode 100755 index 000000000..cd2d1e195 --- /dev/null +++ b/test/data/tiddlywiki/ALPHA @@ -0,0 +1 @@ +2.6.6.A1 diff --git a/test/data/tiddlywiki/Makefile b/test/data/tiddlywiki/Makefile new file mode 100755 index 000000000..999c116b2 --- /dev/null +++ b/test/data/tiddlywiki/Makefile @@ -0,0 +1,23 @@ +# Start at a Makefile or managing build activities. +# Expects a 'cook' script somewhere on the $PATH. +# See 'cook' in this directory for a sample you can use. +# For now users the OSX specific "open" to run a test file. This +# will need to change. +# + +clean: + rm cooked/*.html || true + rm cooked/*.jar || true + rm cooked/*.js || true + rmdir cooked || true + +test: clean tests.html + ln -sf test/recipes/sample.txt . + open cooked/tests.html + +tests.html: + mkdir -p cooked + cook $(PWD)/test/recipes/tests.html.recipe -d $(PWD)/cooked -o tests.html + +alpha: + ./bldalpha diff --git a/test/data/tiddlywiki/README.md b/test/data/tiddlywiki/README.md new file mode 100755 index 000000000..7bc92a606 --- /dev/null +++ b/test/data/tiddlywiki/README.md @@ -0,0 +1,75 @@ +TiddlyWiki +========== + +https://github.com/TiddlyWiki/tiddlywiki + + +Description +----------- + +This is the master repository for the core of TiddlyWiki. It is for the development and maintenance of TiddlyWiki. + +If you simply wish to use TiddlyWiki, it is easiest to download it from: http://tiddlywiki.com/ + +This site also gives details about TiddlyWiki. + +TiddlyWiki code and issues used to be stored at http://svn.tiddlywiki.org and http://trac.tiddlywiki.org . + + +Installation +------------ + +Install the [git](http://git-scm.com/download) version control system, if you do not already have it. + +Install the [ruby](http://www.ruby-lang.org/en/downloads/) programming language, if you do not already have it. Ruby is required for some of the TiddlyWiki tools (cook and ginsu). + +TiddlyWiki has 4 important parallel repositories: + +1. [tiddlywiki](https://github.com/TiddlyWiki/tiddlywiki), this includes the source and test code for TiddlyWiki itself. + +2. [cooker](https://github.com/TiddlyWiki/cooker), this includes the tool for building a TiddlyWiki from a set of tiddlers (`cook`) and the tool for splitting a TiddlyWiki into separate tiddlers (`ginsu`). You need to download this repository for any TiddlyWiki development. + +3. [translations](https://github.com/TiddlyWiki/translations), this includes the translations of TiddlyWiki into a number of different languages. + +4. [tiddlywiki.com](https://github.com/TiddlyWiki/tiddlywiki.com), this includes what is required to build and upload the http://tiddlywiki.com website. + + +To do any serious work on TiddlyWiki you will probably want to download all four of these repositories. They should be downloaded into parallel directories, since some of the build scripts used relative links to the other repositories: + + git clone git@github.com:TiddlyWiki/tiddlywiki.git + git clone git@github.com:TiddlyWiki/cooker.git + git clone git@github.com:TiddlyWiki/tiddlywiki.com.git + git clone git@github.com:TiddlyWiki/translations.git + + +Next you need to set up `cook`. Copy the `cook` script file to somewhere that is on your path. Edit this file according to the instructions in the file. + + +Building and testing +-------------------- + +There is a `Makefile` for managing testing. To build and execute the TiddlyWiki tests, type: + + make test + +Note that, depending on your machine configuration, the tests may take a while to run (perhaps as long as a 30 seconds to a minute). + +To set the version number of the alpha build output files edit the file `ALPHA`. This should normally be set to the same version as in the TiddlyWiki Version.js file. + +To build an alpha version of TiddlyWiki incorporating any changes you have made, run the `bldalpha` script by typing: + + ./bldalpha + + +Contributing +------------ + +Pull requests for feature requests/bug fixes are being accepted for this project. Pull requests should be accompanied by tests. If no tests existed for the given functionality previously, please include these in your contribution to ensure that the TiddlyWiki code base is as reliable as possible going forward. Any pull requests without tests will not generally be folded into the core codebase. See https://github.com/TiddlyWiki/tiddlywiki/wiki for more information. + + +License +------- + +TiddlyWiki is Copyright 2011 UneMesa Assocation + +It is licensed under a BSD License. diff --git a/test/data/tiddlywiki/bldalpha b/test/data/tiddlywiki/bldalpha new file mode 100755 index 000000000..d120a9061 --- /dev/null +++ b/test/data/tiddlywiki/bldalpha @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Usage: +# bldalpha [release] + +# Requires a cook script on $PATH. +# See 'cook' in this directory for a sample. + +DEFAULT_RELEASE=`cat ALPHA` +OUTPUT_DIR=$PWD/cooked +mkdir -p $OUTPUT_DIR +RELEASE=${1:-$DEFAULT_RELEASE} +RECIPE=$PWD/tiddlywiki.html.recipe +RECIPE_EXT_JS=$PWD/tiddlywiki_externaljs.html.recipe +RECIPE_EXT_JS_TS=$PWD/tiddlywiki_externaljs_tiddlyspace_alpha.html.recipe +cook $RECIPE -d $OUTPUT_DIR -o tiddlywiki.$RELEASE.html +cp java/TiddlySaver.jar $OUTPUT_DIR +cook $RECIPE -d $OUTPUT_DIR -o tiddlywiki_compressed.$RELEASE.html -cr -Cr -Dr +cook $RECIPE -d $OUTPUT_DIR -o twcore.$RELEASE.js -j +cook $RECIPE_EXT_JS -d $OUTPUT_DIR -o tiddlywiki_externaljs.$RELEASE.html +cook $RECIPE_EXT_JS_TS -d $OUTPUT_DIR -o tiddlywiki_externaljs_tiddlyspace.$RELEASE.html diff --git a/test/data/tiddlywiki/bldxjs b/test/data/tiddlywiki/bldxjs new file mode 100755 index 000000000..78937f83c --- /dev/null +++ b/test/data/tiddlywiki/bldxjs @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Build version of TiddlyWiki with external javascript +# Usage: +# bldxjs [release] + +DEFAULT_RELEASE=2.6.3.A2 +RELEASE=${1:-$DEFAULT_RELEASE} +DEST=$PWD +RECIPE=$PWD/tiddlywiki.html.recipe +RECIPE_EXT_JS=$PWD/tiddlywiki_externaljs.html.recipe +RECIPE_EXT_JS_TS=$PWD/tiddlywiki_externaljs_tiddlyspace_alpha.html.recipe +ruby -Ku -C ../tools/cooker cook.rb $RECIPE -d$DEST -q -j -o twcore.$RELEASE.js $2 $3 $4 $5 +cp -f twcore.$RELEASE.js twcore.js +cp -f jquery/jquery.js jquery.js +cp -f jquery/plugins/jQuery.twStylesheet.js jQuery.twStylesheet.js +ruby -Ku -C ../tools/cooker cook.rb $RECIPE_EXT_JS -d$DEST -q -o tiddlywiki_externaljs.$RELEASE.html$2 $3 $4 $5 +ruby -Ku -C ../tools/cooker cook.rb $RECIPE_EXT_JS_TS -d$DEST -q -o tiddlywiki_externaljs_tiddlyspace.$RELEASE.html$2 $3 $4 $5 diff --git a/test/data/tiddlywiki/cook b/test/data/tiddlywiki/cook new file mode 100755 index 000000000..fb72b9800 --- /dev/null +++ b/test/data/tiddlywiki/cook @@ -0,0 +1,21 @@ +#!/bin/sh + +# This is a sample cook script to be used with the build scripts +# and Makefile for TiddlyWiki. Adjust this script and then install +# it somewhere on your $PATH, such as ~/bin. +# +# You will need to adjust DEFAULT_TRUNK and COOKER_TRUNK below. + +# change this to where your tiddlywiki repo lives, +# unless you have TW_TRUNKDIR set. +DEFAULT_TRUNK=$HOME/Documents/Code/Github/tiddlywiki + +# Change this to where you have the cooker code installed +COOKER_TRUNK=$HOME/Documents/Code/Github/tiddlywiki/cooker + +if [ -z "$TW_TRUNKDIR" ] +then + export TW_TRUNKDIR=$DEFAULT_TRUNK +fi +RECIPE=$1 && shift +ruby -Ku -C $COOKER_TRUNK cook.rb $RECIPE "$@" diff --git a/test/data/tiddlywiki/deprecated/Crypto.js b/test/data/tiddlywiki/deprecated/Crypto.js new file mode 100755 index 000000000..17459cc05 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Crypto.js @@ -0,0 +1,44 @@ +//-- +//-- Deprecated Crypto functions and associated conversion routines. +//-- Use the jQuery.encoding functions directly instead. +//-- + +// Crypto 'namespace' +function Crypto() {} + +// Convert a string to an array of big-endian 32-bit words +Crypto.strToBe32s = function(str) +{ + return jQuery.encoding.strToBe32s(str); +}; + +// Convert an array of big-endian 32-bit words to a string +Crypto.be32sToStr = function(be) +{ + return jQuery.encoding.be32sToStr(be); +}; + +// Convert an array of big-endian 32-bit words to a hex string +Crypto.be32sToHex = function(be) +{ + return jQuery.encoding.be32sToHex(be); +}; + +// Return, in hex, the SHA-1 hash of a string +Crypto.hexSha1Str = function(str) +{ + return jQuery.encoding.digests.hexSha1Str(str); +}; + +// Return the SHA-1 hash of a string +Crypto.sha1Str = function(str) +{ + return jQuery.encoding.digests.sha1Str(str); +}; + +// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words +Crypto.sha1 = function(x,blen) +{ + return jQuery.encoding.digests.sha1(x,blen); +}; + diff --git a/test/data/tiddlywiki/deprecated/Deprecated.js b/test/data/tiddlywiki/deprecated/Deprecated.js new file mode 100755 index 000000000..ab2f1cb00 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Deprecated.js @@ -0,0 +1,90 @@ +//-- +//-- Deprecated code +//-- + +// @Deprecated: Use createElementAndWikify and this.termRegExp instead +config.formatterHelpers.charFormatHelper = function(w) +{ + w.subWikify(createTiddlyElement(w.output,this.element),this.terminator); +}; + +// @Deprecated: Use enclosedTextHelper and this.lookaheadRegExp instead +config.formatterHelpers.monospacedByLineHelper = function(w) +{ + var lookaheadRegExp = new RegExp(this.lookahead,"mg"); + lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { + var text = lookaheadMatch[1]; + if(config.browser.isIE) + text = text.replace(/\n/g,"\r"); + createTiddlyElement(w.output,"pre",null,null,text); + w.nextMatch = lookaheadRegExp.lastIndex; + } +}; + +// @Deprecated: Use
or
instead of <
> +config.macros.br = {}; +config.macros.br.handler = function(place) +{ + createTiddlyElement(place,"br"); +}; + +// Find an entry in an array. Returns the array index or null +// @Deprecated: Use indexOf instead +Array.prototype.find = function(item) +{ + var i = this.indexOf(item); + return i == -1 ? null : i; +}; + +// Load a tiddler from an HTML DIV. The caller should make sure to later call Tiddler.changed() +// @Deprecated: Use store.getLoader().internalizeTiddler instead +Tiddler.prototype.loadFromDiv = function(divRef,title) +{ + return store.getLoader().internalizeTiddler(store,this,title,divRef); +}; + +// Format the text for storage in an HTML DIV +// @Deprecated Use store.getSaver().externalizeTiddler instead. +Tiddler.prototype.saveToDiv = function() +{ + return store.getSaver().externalizeTiddler(store,this); +}; + +// @Deprecated: Use store.allTiddlersAsHtml() instead +function allTiddlersAsHtml() +{ + return store.allTiddlersAsHtml(); +} + +// @Deprecated: Use refreshPageTemplate instead +function applyPageTemplate(title) +{ + refreshPageTemplate(title); +} + +// @Deprecated: Use story.displayTiddlers instead +function displayTiddlers(srcElement,titles,template,unused1,unused2,animate,unused3) +{ + story.displayTiddlers(srcElement,titles,template,animate); +} + +// @Deprecated: Use story.displayTiddler instead +function displayTiddler(srcElement,title,template,unused1,unused2,animate,unused3) +{ + story.displayTiddler(srcElement,title,template,animate); +} + +// @Deprecated: Use functions on right hand side directly instead +var createTiddlerPopup = Popup.create; +var scrollToTiddlerPopup = Popup.show; +var hideTiddlerPopup = Popup.remove; + +// @Deprecated: Use right hand side directly instead +var regexpBackSlashEn = new RegExp("\\\\n","mg"); +var regexpBackSlash = new RegExp("\\\\","mg"); +var regexpBackSlashEss = new RegExp("\\\\s","mg"); +var regexpNewLine = new RegExp("\n","mg"); +var regexpCarriageReturn = new RegExp("\r","mg"); + diff --git a/test/data/tiddlywiki/deprecated/FileAdaptor.js b/test/data/tiddlywiki/deprecated/FileAdaptor.js new file mode 100755 index 000000000..30ee96b2c --- /dev/null +++ b/test/data/tiddlywiki/deprecated/FileAdaptor.js @@ -0,0 +1,20 @@ +//-- +//-- Deprecated FileAdaptor functions +//-- + +FileAdaptor.loadTiddlyWikiCallback = function(status,context,responseText,url,xhr) +{ + context.status = status; + if(!status) { + context.statusText = "Error reading file"; + } else { + //# Load the content into a TiddlyWiki() object + context.adaptor.store = new TiddlyWiki(); + if(!context.adaptor.store.importTiddlyWiki(responseText)) { + context.statusText = config.messages.invalidFileError.format([url]); + context.status = false; + } + } + context.complete(context,context.userParams); +}; + diff --git a/test/data/tiddlywiki/deprecated/Http.js b/test/data/tiddlywiki/deprecated/Http.js new file mode 100755 index 000000000..5d1709f55 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Http.js @@ -0,0 +1,26 @@ +//-- +//-- Deprecated HTTP request code +//-- Use the jQuery ajax functions directly instead +//-- + +//# Load a file over http +//# url - the source url +//# callback - function to call when there is a response +//# params - parameter object that gets passed to the callback for storing it's state +//# Return value is the underlying XMLHttpRequest object, or a string if there was an error +//# Callback function is called like this: +//# callback(status,params,responseText,xhr) +//# status - true if OK, false if error +//# params - the parameter object provided to loadRemoteFile() +//# responseText - the text of the file +//# xhr - the underlying XMLHttpRequest object +function loadRemoteFile(url,callback,params) +{ + return httpReq("GET",url,callback,params); +} + +function doHttp(type,url,data,contentType,username,password,callback,params,headers,allowCache) +{ + return httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache); +} + diff --git a/test/data/tiddlywiki/deprecated/Numbers.js b/test/data/tiddlywiki/deprecated/Numbers.js new file mode 100755 index 000000000..3b70f0846 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Numbers.js @@ -0,0 +1,16 @@ +//-- +//-- Deprecated Number functions +//-- + +// @Deprecated: no direct replacement, since not used in core code +// Clamp a number to a range +Number.prototype.clamp = function(min,max) +{ + var c = this; + if(c < min) + c = min; + if(c > max) + c = max; + return Number(c); +}; + diff --git a/test/data/tiddlywiki/deprecated/Strings.js b/test/data/tiddlywiki/deprecated/Strings.js new file mode 100755 index 000000000..5bcaf4bce --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Strings.js @@ -0,0 +1,29 @@ +//-- +//-- Deprecated String functions +//-- + +// @Deprecated: no direct replacement, since not used in core code +String.prototype.toJSONString = function() +{ + // Convert a string to it's JSON representation by encoding control characters, double quotes and backslash. See json.org + var m = { + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '"' : '\\"', + '\\': '\\\\' + }; + var replaceFn = function(a,b) { + var c = m[b]; + if(c) + return c; + c = b.charCodeAt(); + return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16); + }; + if(/["\\\x00-\x1f]/.test(this)) + return '"' + this.replace(/([\x00-\x1f\\"])/g,replaceFn) + '"'; + return '"' + this + '"'; +}; + diff --git a/test/data/tiddlywiki/deprecated/Tiddler.js b/test/data/tiddlywiki/deprecated/Tiddler.js new file mode 100755 index 000000000..073b5c349 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Tiddler.js @@ -0,0 +1,22 @@ +//-- +//-- Deprecated Tiddler code +//-- + +// @Deprecated: Use tiddlerToRssItem(tiddler,uri) instead +Tiddler.prototype.toRssItem = function(uri) +{ + return tiddlerToRssItem(this,uri); +}; + +// @Deprecated: Use "\n" + tiddlerToRssItem(tiddler,uri) + "\n" instead +Tiddler.prototype.saveToRss = function(uri) +{ + return "\n" + tiddlerToRssItem(this,uri) + "\n"; +}; + +// @Deprecated: Use jQuery.encoding.digests.hexSha1Str instead +Tiddler.prototype.generateFingerprint = function() +{ + return "0x" + Crypto.hexSha1Str(this.text); +}; + diff --git a/test/data/tiddlywiki/deprecated/Utilities.js b/test/data/tiddlywiki/deprecated/Utilities.js new file mode 100755 index 000000000..cf6b8b602 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Utilities.js @@ -0,0 +1,38 @@ +//-- +//-- Deprecated utility functions +//-- Use the jQuery functions directly instead +//-- + +// Remove all children of a node +function removeChildren(e) +{ + jQuery(e).empty(); +} + +// Remove a node and all it's children +function removeNode(e) +{ + jQuery(e).remove(); +} + +// Return the content of an element as plain text with no formatting +function getPlainText(e) +{ + return jQuery(e).text(); +} + +function addClass(e,className) +{ + jQuery(e).addClass(className); +} + +function removeClass(e,className) +{ + jQuery(e).removeClass(className); +} + +function hasClass(e,className) +{ + return jQuery(e).hasClass(className); +} + diff --git a/test/data/tiddlywiki/deprecated/Wikifier.js b/test/data/tiddlywiki/deprecated/Wikifier.js new file mode 100755 index 000000000..335625360 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/Wikifier.js @@ -0,0 +1,16 @@ +//-- +//-- Deprecated Wikifier code +//-- + +//# Wikify a named tiddler to plain text +function wikifyPlain(title,theStore,limit) +{ + if(!theStore) + theStore = store; + if(theStore.tiddlerExists(title) || theStore.isShadowTiddler(title)) { + return wikifyPlainText(theStore.getTiddlerText(title),limit,tiddler); + } else { + return ""; + } +} + diff --git a/test/data/tiddlywiki/deprecated/split.recipe b/test/data/tiddlywiki/deprecated/split.recipe new file mode 100755 index 000000000..288ec2134 --- /dev/null +++ b/test/data/tiddlywiki/deprecated/split.recipe @@ -0,0 +1,10 @@ +jsdeprecated: Crypto.js +jsdeprecated: Deprecated.js +jsdeprecated: FileAdaptor.js +jsdeprecated: Http.js +jsdeprecated: Strings.js +jsdeprecated: Tiddler.js +jsdeprecated: Numbers.js +jsdeprecated: Utilities.js +jsdeprecated: Wikifier.js + diff --git a/test/data/tiddlywiki/html/copyright.txt b/test/data/tiddlywiki/html/copyright.txt new file mode 100755 index 000000000..5d50e92aa --- /dev/null +++ b/test/data/tiddlywiki/html/copyright.txt @@ -0,0 +1,29 @@ +TiddlyWiki created by Jeremy Ruston, (jeremy [at] osmosoft [dot] com) + +Copyright (c) Jeremy Ruston 2004-2007 +Copyright (c) UnaMesa Association 2007-2011 + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +Neither the name of the UnaMesa Association nor the names of its contributors may be +used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/test/data/tiddlywiki/html/externaljs.txt b/test/data/tiddlywiki/html/externaljs.txt new file mode 100755 index 000000000..b4b88826e --- /dev/null +++ b/test/data/tiddlywiki/html/externaljs.txt @@ -0,0 +1,3 @@ + + + diff --git a/test/data/tiddlywiki/html/externaljs_tiddlyspace.txt b/test/data/tiddlywiki/html/externaljs_tiddlyspace.txt new file mode 100755 index 000000000..9873ba982 --- /dev/null +++ b/test/data/tiddlywiki/html/externaljs_tiddlyspace.txt @@ -0,0 +1,3 @@ + + + diff --git a/test/data/tiddlywiki/html/externaljs_tiddlyspace_alpha.txt b/test/data/tiddlywiki/html/externaljs_tiddlyspace_alpha.txt new file mode 100755 index 000000000..bde0205e9 --- /dev/null +++ b/test/data/tiddlywiki/html/externaljs_tiddlyspace_alpha.txt @@ -0,0 +1,3 @@ + + + diff --git a/test/data/tiddlywiki/html/externaljs_tiddlyspace_beta.txt b/test/data/tiddlywiki/html/externaljs_tiddlyspace_beta.txt new file mode 100755 index 000000000..44ab9c0da --- /dev/null +++ b/test/data/tiddlywiki/html/externaljs_tiddlyspace_beta.txt @@ -0,0 +1,3 @@ + + + diff --git a/test/data/tiddlywiki/html/noscript.txt b/test/data/tiddlywiki/html/noscript.txt new file mode 100755 index 000000000..a9dad34aa --- /dev/null +++ b/test/data/tiddlywiki/html/noscript.txt @@ -0,0 +1,3 @@ +
+This page requires JavaScript to function properly.

If you are using Microsoft Internet Explorer you may need to click on the yellow bar above and select 'Allow Blocked Content'. You must then click 'Yes' on the following security warning. +
diff --git a/test/data/tiddlywiki/html/rss.link.html b/test/data/tiddlywiki/html/rss.link.html new file mode 100755 index 000000000..4da2bc3dd --- /dev/null +++ b/test/data/tiddlywiki/html/rss.link.html @@ -0,0 +1 @@ + diff --git a/test/data/tiddlywiki/html/split.recipe b/test/data/tiddlywiki/html/split.recipe new file mode 100755 index 000000000..9503dc82c --- /dev/null +++ b/test/data/tiddlywiki/html/split.recipe @@ -0,0 +1,5 @@ +template: tiddlywiki.template.html +copyright: copyright.txt +style: style.txt +noscript: noscript.txt +jsext: tiddlysaver.txt diff --git a/test/data/tiddlywiki/html/splitnonoscript.recipe b/test/data/tiddlywiki/html/splitnonoscript.recipe new file mode 100755 index 000000000..e1dee93d9 --- /dev/null +++ b/test/data/tiddlywiki/html/splitnonoscript.recipe @@ -0,0 +1,3 @@ +template: tiddlywiki.template.html +copyright: copyright.txt +style: style.txt diff --git a/test/data/tiddlywiki/html/style.txt b/test/data/tiddlywiki/html/style.txt new file mode 100755 index 000000000..a70bd458c --- /dev/null +++ b/test/data/tiddlywiki/html/style.txt @@ -0,0 +1,7 @@ +#saveTest {display:none;} +#messageArea {display:none;} +#copyright {display:none;} +#storeArea {display:none;} +#storeArea div {padding:0.5em; margin:1em 0em 0em 0em; border-color:#fff #666 #444 #ddd; border-style:solid; border-width:2px; overflow:auto;} +#shadowArea {display:none;} +#javascriptWarning {width:100%; text-align:center; font-weight:bold; background-color:#dd1100; color:#fff; padding:1em 0em;} diff --git a/test/data/tiddlywiki/html/tiddlysaver.txt b/test/data/tiddlywiki/html/tiddlysaver.txt new file mode 100755 index 000000000..a43069962 --- /dev/null +++ b/test/data/tiddlywiki/html/tiddlysaver.txt @@ -0,0 +1,6 @@ + diff --git a/test/data/tiddlywiki/html/tiddlywiki.template.html b/test/data/tiddlywiki/html/tiddlywiki.template.html new file mode 100755 index 000000000..5d8ccb12c --- /dev/null +++ b/test/data/tiddlywiki/html/tiddlywiki.template.html @@ -0,0 +1,86 @@ + + + + + + + + + + +<!--@@title@@--> + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ +
+ +
+ + + +
+ + + + + + + + + + + + + + diff --git a/test/data/tiddlywiki/java/TiddlySaver$1.class b/test/data/tiddlywiki/java/TiddlySaver$1.class new file mode 100755 index 000000000..9f4be242b Binary files /dev/null and b/test/data/tiddlywiki/java/TiddlySaver$1.class differ diff --git a/test/data/tiddlywiki/java/TiddlySaver$2.class b/test/data/tiddlywiki/java/TiddlySaver$2.class new file mode 100755 index 000000000..c6d303a03 Binary files /dev/null and b/test/data/tiddlywiki/java/TiddlySaver$2.class differ diff --git a/test/data/tiddlywiki/java/TiddlySaver.class b/test/data/tiddlywiki/java/TiddlySaver.class new file mode 100755 index 000000000..64cac6801 Binary files /dev/null and b/test/data/tiddlywiki/java/TiddlySaver.class differ diff --git a/test/data/tiddlywiki/java/TiddlySaver.jar b/test/data/tiddlywiki/java/TiddlySaver.jar new file mode 100755 index 000000000..e42d06d3a Binary files /dev/null and b/test/data/tiddlywiki/java/TiddlySaver.jar differ diff --git a/test/data/tiddlywiki/java/TiddlySaver.java b/test/data/tiddlywiki/java/TiddlySaver.java new file mode 100755 index 000000000..df7d17316 --- /dev/null +++ b/test/data/tiddlywiki/java/TiddlySaver.java @@ -0,0 +1,71 @@ +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public class TiddlySaver extends java.applet.Applet { + public String loadFile(final String filename, final String charset) { + return (String)AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + if (charset.length() == 0) { + StringBuffer data = new StringBuffer(); + BufferedReader r = new BufferedReader(new FileReader(filename)); + String line; + while ((line = r.readLine()) != null) data.append(line).append("\n"); + r.close(); + return data.toString(); + } else { + File f = new File(filename); + FileInputStream i = new FileInputStream(f); + byte[] b = new byte[(f.length() > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)f.length()]; + int offset = 0; + int num = 0; + while (offset < b.length && (num = i.read(b, offset, b.length - offset)) >= 0) { + offset += num; + } + i.close(); + return new String(b, 0, offset, charset); + } + } catch (Exception x) { + x.printStackTrace(); + return null; + } + } + }); + } + public int saveFile(final String filename, final String charset, final String data) { + return ((Integer)AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + if (charset.length() == 0) { + int e, s = 0; + BufferedWriter w = new BufferedWriter(new FileWriter(filename)); + do { + e = data.indexOf('\n', s); + if (e == -1) e = data.length(); + w.write(data, s, e - s); + w.newLine(); + s = e + 1; + } while (s < data.length()); + w.close(); + return new Integer(1); + } else { + FileOutputStream o = new FileOutputStream(filename); + o.write(data.getBytes(charset)); + o.close(); + return new Integer(1); + } + } catch (Exception x) { + x.printStackTrace(); + return new Integer(0); + } + } + })).intValue(); + } +} \ No newline at end of file diff --git a/test/data/tiddlywiki/java/TiddlySaverVerify.keystore b/test/data/tiddlywiki/java/TiddlySaverVerify.keystore new file mode 100755 index 000000000..65fdce53b Binary files /dev/null and b/test/data/tiddlywiki/java/TiddlySaverVerify.keystore differ diff --git a/test/data/tiddlywiki/java/UnaMesa-2.cer b/test/data/tiddlywiki/java/UnaMesa-2.cer new file mode 100755 index 000000000..677e59757 --- /dev/null +++ b/test/data/tiddlywiki/java/UnaMesa-2.cer @@ -0,0 +1,38 @@ +-----BEGIN PKCS #7 SIGNED DATA----- +MIAGCSqGSIb3DQEHAqCAMIACAQExADALBgkqhkiG9w0BBwGggDCCAyIwggKLoAMC +AQICEEtycpHmJVBWKTR1fltuZYgwDQYJKoZIhvcNAQEFBQAwVTELMAkGA1UEBhMC +WkExJTAjBgNVBAoTHFRoYXd0ZSBDb25zdWx0aW5nIChQdHkpIEx0ZC4xHzAdBgNV +BAMTFlRoYXd0ZSBDb2RlIFNpZ25pbmcgQ0EwHhcNMTAwMzA0MDAwMDAwWhcNMTIw +MzAzMjM1OTU5WjBvMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTES +MBAGA1UEBxMJUGFsbyBBbHRvMRAwDgYDVQQKFAdVbmFNZXNhMRMwEQYDVQQLFApU +aWRkbHlXaWtpMRAwDgYDVQQDFAdVbmFNZXNhMIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDy7syNTVAWoApezeC5oKYWsS19uO87TgHcwqAG2R6U8pBVuHoKpzL9 +YpOnwIL3p+l/fHy5T8ghmxdX4d73RtHiHMFIL6ABrSvt/WxUKtyImleBv521pK5P +S0sdBJWgWCSIV76YKxHdHTZfU83rNih1IGzxP+96MflXh4wPsVuvcwIDAQABo4HY +MIHVMAwGA1UdEwEB/wQCMAAwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC50 +aGF3dGUuY29tL1RoYXd0ZUNvZGVTaWduaW5nQ0EuY3JsMB8GA1UdJQQYMBYGCCsG +AQUFBwMDBgorBgEEAYI3AgEWMB0GA1UdBAQWMBQwDjAMBgorBgEEAYI3AgEWAwIH +gDAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0 +ZS5jb20wEQYJYIZIAYb4QgEBBAQDAgQQMA0GCSqGSIb3DQEBBQUAA4GBAHgyGc8J +hNHrtizy6e4bWDlYBwVJiGPe1h0qTNrL9qGexHfF9Msik9CYCFHHv0NlatkP0g0L +ZNkR4pTg7QFPBxfV/fh74SxnzadgyX5vsuohC3n7r7XLZy+vh/jeZR2Qt9QTkyxY +AONfQLWpTUvFy6Rqb6KcPmW70s6t7NORkWF1MIIDTjCCAregAwIBAgIBCjANBgkq +hkiG9w0BAQUFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2Fw +ZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGlu +ZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEh +MB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkB +FhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29tMB4XDTAzMDgwNjAwMDAwMFoXDTEz +MDgwNTIzNTk1OVowVTELMAkGA1UEBhMCWkExJTAjBgNVBAoTHFRoYXd0ZSBDb25z +dWx0aW5nIChQdHkpIEx0ZC4xHzAdBgNVBAMTFlRoYXd0ZSBDb2RlIFNpZ25pbmcg +Q0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMa4uSdgrwvjkWll236N7ZHm +qvG+1e3+bdQsf9Fwd/smmVe03T8wuNwh6miNgZL8LkuRNYQg8tpKurT85tqI8iDF +IZIJR5WgCRymeb6xTB388YpuVNJpofFMkzpB/n3UZHtjRfdgYB0xHaTp0w+L+24m +JLOo/+XlkNS0wtxQYK5ZAgMBAAGjgbMwgbAwEgYDVR0TAQH/BAgwBgEB/wIBADBA +BgNVHR8EOTA3MDWgM6Axhi9odHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJl +bWl1bVNlcnZlckNBLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwMw +DgYDVR0PAQH/BAQDAgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFQcml2YXRl +TGFiZWwyLTE0NDANBgkqhkiG9w0BAQUFAAOBgQB2spzuE58b9i00kpRFczTcjmsu +XPxMfYnrw2jx15kPLh0XyLUWi77NigUG8hlJOgNbBckgjm1S4XaBoMNliiJn5BxT +UzdGv7zXL+t7ntAURWxAIQjiXXV2ZjAe9N+Cii+986IMvx3bnxSimnI3TbB3SOhK +PwnOVRks7+YHJOGv7AAAMQAAAAAAAAA= +-----END PKCS #7 SIGNED DATA----- \ No newline at end of file diff --git a/test/data/tiddlywiki/java/UnaMesa-3.cer b/test/data/tiddlywiki/java/UnaMesa-3.cer new file mode 100755 index 000000000..193cd2c02 Binary files /dev/null and b/test/data/tiddlywiki/java/UnaMesa-3.cer differ diff --git a/test/data/tiddlywiki/java/UnaMesa.old.cer b/test/data/tiddlywiki/java/UnaMesa.old.cer new file mode 100755 index 000000000..e460840e3 Binary files /dev/null and b/test/data/tiddlywiki/java/UnaMesa.old.cer differ diff --git a/test/data/tiddlywiki/java/build b/test/data/tiddlywiki/java/build new file mode 100755 index 000000000..b4bae1a78 --- /dev/null +++ b/test/data/tiddlywiki/java/build @@ -0,0 +1,5 @@ +javac -source 1.2 -target 1.2 -g:none TiddlySaver.java +jar cf TiddlySaver.jar TiddlySaver*.class +# assume you have UnaMesa.keystore in the same directory +# and Passphrase for keystore +jarsigner -keystore UnaMesa.keystore TiddlySaver.jar BidiX diff --git a/test/data/tiddlywiki/java/sign.readme b/test/data/tiddlywiki/java/sign.readme new file mode 100755 index 000000000..ceaa17b2b --- /dev/null +++ b/test/data/tiddlywiki/java/sign.readme @@ -0,0 +1,323 @@ + + Readme file for signing TiddlySaver applet + +for verifying see verify.readme file + +1 - HISTORY + +2010 03 06 - BidiX : Signing TiddlySaver.jar with a new Signing Certificate + The files were updated with the new process used for signing (sorry my Macbook was configured in French) + New signed TiddlySaver.jar was tested with Safari 4.0.4 on MacOS 10.6.2 +2008 04 06 - BidiX : documentation +2008 04 06 - BidiX : create TiddlySaverVerify.keystore +2008 03 27 - BidiX : Signing TiddlySaver.jar +2008 03 26 - BidiX : obtaining UnaMesa Signing Certificate +2008 03 17 - BidiX : Issuing a certificate request to Thawte with a BidiX CSR +2003 03 12 - BidiX : Create UnaMesa.keystore with BidiX alias and Private key + + +2 - UNAMESA.KEYSTORE CREATION + +Using this command: +---------------- +> keytool -genkey -keyalg RSA -alias BidiX -keystore UnaMesa.keystore +Tapez le mot de passe du Keystore : +Ressaisissez le nouveau mot de passe : +Quels sont vos pr?nom et nom ? + [Unknown] : BidiX +Quel est le nom de votre unit? organisationnelle ? + [Unknown] : TiddlyWiki +Quelle est le nom de votre organisation ? + [Unknown] : UnaMesa +Quel est le nom de votre ville de r?sidence ? + [Unknown] : Palo Alto +Quel est le nom de votre ?tat ou province ? + [Unknown] : California +Quel est le code de pays ? deux lettres pour cette unit? ? + [Unknown] : US +Est-ce CN=BidiX, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US ? + [non] : OUI + +Sp?cifiez le mot de passe de la cl? pour + (appuyez sur Entr?e s'il s'agit du mot de passe du Keystore) : +Ressaisissez le nouveau mot de passe : +--------------- + +For security reasons the Keystore is kept in a safe place in BidiX environment (BidiX @ bidix.info) + + +3 - CERTICATE REQUEST +Using this command : +-------------- +> keytool -certreq -alias BidiX -file certreq -keystore UnaMesa.keystore -storepass "???" +> cat certreq +-----BEGIN NEW CERTIFICATE REQUEST----- +MIIBrTCCARYCAQAwbTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcT +... +sxoX+IbLVSs4Ye4HDqFodRkmehWBJsdWpQa/yji72pY+eA3fCgTt57VL+san9pPaLcwPfAiL23cD +R1j/y2RjQYLpE0PH+vQXn26xNeUDo2OONijyG0RLIX57yA== +-----END NEW CERTIFICATE REQUEST----- +--------------- +Certificate received +-----BEGIN PKCS #7 SIGNED DATA----- +MIAGCSqGSIb3DQEHAqCAMIACAQExADALBgkqhkiG9w0BBwGggDCCAyIwggKLoAMC +... +PwnOVRks7+YHJOGv7AAAMQAAAAAAAAA= +-----END PKCS #7 SIGNED DATA----- +copied in UnaMesa-2.cer + +4 - ADDING CERTICATE TO KEYSTORE +--------------- +> keytool -import -alias BidiX -trustcacerts -file UnaMesa-2.cer -keystore UnaMesa.keystore +Tapez le mot de passe du Keystore : +R?ponse de certificat install?e dans le Keystore +--------------- +List Keystore +--------------- +> keytool -list -v -alias BidiX -keystore UnaMesa.keystore +Tapez le mot de passe du Keystore : +Nom d'alias : BidiX +Date de cr?ation : 6 mars 2010 +Type dentr?e?: {0} +Longueur de cha?ne du certificat : 3 +Certificat[1]: +Propri?taire?: CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US +?metteur?: CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA +Num?ro de s?rie?: 4b727291e62550562934757e5b6e6588 +Valide du?: Thu Mar 04 01:00:00 CET 2010 au?: Sun Mar 04 00:59:59 CET 2012 +Empreintes du certificat?: + MD5?: 1B:79:CE:47:BE:A9:E4:04:2A:DD:04:F5:BA:62:64:AD + SHA1?: 42:A9:6F:4D:C3:20:F8:7F:90:1A:1F:A5:66:92:ED:06:38:19:1E:D4 + Nom de lalgorithme de signature?: {7} + Version?: {8} + +Extensions?: + +#1: ObjectId: 2.5.29.19 Criticality=true +BasicConstraints:[ + CA:false + PathLen: undefined +] + +#2: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false +AuthorityInfoAccess [ + [accessMethod: 1.3.6.1.5.5.7.48.1 + accessLocation: URIName: http://ocsp.thawte.com] +] + +#3: ObjectId: 2.5.29.4 Criticality=false + +#4: ObjectId: 2.5.29.31 Criticality=false +CRLDistributionPoints [ + [DistributionPoint: + [URIName: http://crl.thawte.com/ThawteCodeSigningCA.crl] +]] + +#5: ObjectId: 2.5.29.37 Criticality=false +ExtendedKeyUsages [ + codeSigning + 1.3.6.1.4.1.311.2.1.22 +] + +#6: ObjectId: 2.16.840.1.113730.1.1 Criticality=false +NetscapeCertType [ + Object Signing +] + +Certificat[2]: +Propri?taire?: CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA +?metteur?: EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA +Num?ro de s?rie?: a +Valide du?: Wed Aug 06 02:00:00 CEST 2003 au?: Tue Aug 06 01:59:59 CEST 2013 +Empreintes du certificat?: + MD5?: D4:A7:BF:00:7B:6A:0C:20:D9:23:CD:5B:60:7B:7C:12 + SHA1?: A7:06:BA:1E:CA:B6:A2:AB:18:69:9F:C0:D7:DD:8C:7D:E3:6F:29:0F + Nom de lalgorithme de signature?: {7} + Version?: {8} + +Extensions?: + +#1: ObjectId: 2.5.29.15 Criticality=true +KeyUsage [ + Key_CertSign + Crl_Sign +] + +#2: ObjectId: 2.5.29.19 Criticality=true +BasicConstraints:[ + CA:true + PathLen:0 +] + +#3: ObjectId: 2.5.29.31 Criticality=false +CRLDistributionPoints [ + [DistributionPoint: + [URIName: http://crl.thawte.com/ThawtePremiumServerCA.crl] +]] + +#4: ObjectId: 2.5.29.37 Criticality=false +ExtendedKeyUsages [ + clientAuth + codeSigning +] + +#5: ObjectId: 2.5.29.17 Criticality=false +SubjectAlternativeName [ + CN=PrivateLabel2-144 +] + +Certificat[3]: +Propri?taire?: EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA +?metteur?: EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA +Num?ro de s?rie?: 1 +Valide du?: Thu Aug 01 02:00:00 CEST 1996 au?: Fri Jan 01 00:59:59 CET 2021 +Empreintes du certificat?: + MD5?: 06:9F:69:79:16:66:90:02:1B:8C:8C:A2:C3:07:6F:3A + SHA1?: 62:7F:8D:78:27:65:63:99:D2:7D:7F:90:44:C9:FE:B3:F3:3E:FA:9A + Nom de lalgorithme de signature?: {7} + Version?: {8} + +Extensions?: + +#1: ObjectId: 2.5.29.19 Criticality=true +BasicConstraints:[ + CA:true + PathLen:2147483647 +] +--------------- + +5 - SIGNING TIDDLYSAVER.JAR + +Get TiddlySaver.jar from http://trac.tiddlywiki.org/browser/Trunk/core/java/TiddlySaver.jar. +TiddlySaver.jar contained classes compiled on Thu Dec 07 14:48:00 CET 2006 + +With UnaMesa.keystore in the current directory Signing jar on Sam 6 mar 2010 15:16:04 CET using this command : +--------------- +> jarsigner -keystore UnaMesa.keystore TiddlySaver.jar BidiX +Enter Passphrase for keystore: +--------------- + +6 - VERIFYING SIGNATURE WITHOUT KEYSTORE +--------------- +> jarsigner -verify -verbose TiddlySaver.jar + + 284 Thu Mar 27 07:59:12 CET 2008 META-INF/MANIFEST.MF + 395 Sat Mar 06 15:16:04 CET 2010 META-INF/BIDIX.SF + 2798 Sat Mar 06 15:16:04 CET 2010 META-INF/BIDIX.RSA + 0 Thu Dec 07 14:48:00 CET 2006 META-INF/ +sm 1271 Thu Dec 07 14:48:00 CET 2006 TiddlySaver$1.class +sm 1184 Thu Dec 07 14:48:00 CET 2006 TiddlySaver$2.class +sm 775 Thu Dec 07 14:48:00 CET 2006 TiddlySaver.class + + s = signature was verified + m = entry is listed in manifest + k = at least one certificate was found in keystore + i = at least one certificate was found in identity scope + +jar verified. +--------------- + +7 - CREATE TiddlySaverVerify.keystore KEYSTORE +export SigningCertificate +--------------- +> keytool -export -alias BidiX -file UnaMesa-3.cer -keystore UnaMesa.keystore +Tapez le mot de passe du Keystore : +Certificat enregistr? dans le fichier +--------------- +create keystore "TiddlySaverVerify.keystore" with "tiddlywiki" as password and import SigningCertificate +--------------- +> keytool -import -alias BidiX -keystore TiddlySaverVerify.keystore -storepass tiddlywiki -file UnaMesa-3.cer +Propri?taire?: CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US +?metteur?: CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA +Num?ro de s?rie?: 4b727291e62550562934757e5b6e6588 +Valide du?: Thu Mar 04 01:00:00 CET 2010 au?: Sun Mar 04 00:59:59 CET 2012 +Empreintes du certificat?: + MD5?: 1B:79:CE:47:BE:A9:E4:04:2A:DD:04:F5:BA:62:64:AD + SHA1?: 42:A9:6F:4D:C3:20:F8:7F:90:1A:1F:A5:66:92:ED:06:38:19:1E:D4 + Nom de lalgorithme de signature?: {7} + Version?: {8} + +Extensions?: + +#1: ObjectId: 2.5.29.19 Criticality=true +BasicConstraints:[ + CA:false + PathLen: undefined +] + +#2: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false +AuthorityInfoAccess [ + [accessMethod: 1.3.6.1.5.5.7.48.1 + accessLocation: URIName: http://ocsp.thawte.com] +] + +#3: ObjectId: 2.5.29.4 Criticality=false + +#4: ObjectId: 2.5.29.31 Criticality=false +CRLDistributionPoints [ + [DistributionPoint: + [URIName: http://crl.thawte.com/ThawteCodeSigningCA.crl] +]] + +#5: ObjectId: 2.5.29.37 Criticality=false +ExtendedKeyUsages [ + codeSigning + 1.3.6.1.4.1.311.2.1.22 +] + +#6: ObjectId: 2.16.840.1.113730.1.1 Criticality=false +NetscapeCertType [ + Object Signing +] + +Faire confiance ? ce certificat ? [non] : Y +R?ponse incorrecte, recommencez +Faire confiance ? ce certificat ? [non] : oui +Certificat ajout? au Keystore +--------------- + +8 - VERIFYING SIGNATURE WITH TiddlySaverVerify.keystore +--------------- +> jarsigner -verify -verbose -certs -keystore TiddlySaverVerify.keystore TiddlySaver.jar + 284 Thu Mar 27 07:59:12 CET 2008 META-INF/MANIFEST.MF + 395 Sat Mar 06 15:16:04 CET 2010 META-INF/BIDIX.SF + 2798 Sat Mar 06 15:16:04 CET 2010 META-INF/BIDIX.RSA + 0 Thu Dec 07 14:48:00 CET 2006 META-INF/ +smk 1271 Thu Dec 07 14:48:00 CET 2006 TiddlySaver$1.class + + X.509, CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US (bidix) + [certificate is valid from 04/03/10 01:00 to 04/03/12 00:59] + X.509, CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA + [certificate is valid from 06/08/03 02:00 to 06/08/13 01:59] + [KeyUsage extension does not support code signing] + X.509, EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA + [certificate is valid from 01/08/96 02:00 to 01/01/21 00:59] + +smk 1184 Thu Dec 07 14:48:00 CET 2006 TiddlySaver$2.class + + X.509, CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US (bidix) + [certificate is valid from 04/03/10 01:00 to 04/03/12 00:59] + X.509, CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA + [certificate is valid from 06/08/03 02:00 to 06/08/13 01:59] + [KeyUsage extension does not support code signing] + X.509, EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA + [certificate is valid from 01/08/96 02:00 to 01/01/21 00:59] + +smk 775 Thu Dec 07 14:48:00 CET 2006 TiddlySaver.class + + X.509, CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US (bidix) + [certificate is valid from 04/03/10 01:00 to 04/03/12 00:59] + X.509, CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA + [certificate is valid from 06/08/03 02:00 to 06/08/13 01:59] + [KeyUsage extension does not support code signing] + X.509, EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA + [certificate is valid from 01/08/96 02:00 to 01/01/21 00:59] + + + s = signature was verified + m = entry is listed in manifest + k = at least one certificate was found in keystore + i = at least one certificate was found in identity scope + +jar verified. +--------------- \ No newline at end of file diff --git a/test/data/tiddlywiki/java/verify b/test/data/tiddlywiki/java/verify new file mode 100755 index 000000000..71d4e9431 --- /dev/null +++ b/test/data/tiddlywiki/java/verify @@ -0,0 +1,2 @@ +# assume TiddlySaverVerify.keystore and TiddlySaver.jar in the current directory +jarsigner -verify -verbose -certs -keystore TiddlySaverVerify.keystore -keypass tiddlywiki TiddlySaver.jar diff --git a/test/data/tiddlywiki/java/verify.readme b/test/data/tiddlywiki/java/verify.readme new file mode 100755 index 000000000..0f555556f --- /dev/null +++ b/test/data/tiddlywiki/java/verify.readme @@ -0,0 +1,46 @@ + +Above the verifying command and the expected return: + + +> jarsigner -verify -verbose -certs -keystore TiddlySaverVerify.keystore TiddlySaver.jar + 284 Thu Mar 27 07:59:12 CET 2008 META-INF/MANIFEST.MF + 395 Sat Mar 06 15:16:04 CET 2010 META-INF/BIDIX.SF + 2798 Sat Mar 06 15:16:04 CET 2010 META-INF/BIDIX.RSA + 0 Thu Dec 07 14:48:00 CET 2006 META-INF/ +smk 1271 Thu Dec 07 14:48:00 CET 2006 TiddlySaver$1.class + + X.509, CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US (bidix) + [certificate is valid from 04/03/10 01:00 to 04/03/12 00:59] + X.509, CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA + [certificate is valid from 06/08/03 02:00 to 06/08/13 01:59] + [KeyUsage extension does not support code signing] + X.509, EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA + [certificate is valid from 01/08/96 02:00 to 01/01/21 00:59] + +smk 1184 Thu Dec 07 14:48:00 CET 2006 TiddlySaver$2.class + + X.509, CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US (bidix) + [certificate is valid from 04/03/10 01:00 to 04/03/12 00:59] + X.509, CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA + [certificate is valid from 06/08/03 02:00 to 06/08/13 01:59] + [KeyUsage extension does not support code signing] + X.509, EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA + [certificate is valid from 01/08/96 02:00 to 01/01/21 00:59] + +smk 775 Thu Dec 07 14:48:00 CET 2006 TiddlySaver.class + + X.509, CN=UnaMesa, OU=TiddlyWiki, O=UnaMesa, L=Palo Alto, ST=California, C=US (bidix) + [certificate is valid from 04/03/10 01:00 to 04/03/12 00:59] + X.509, CN=Thawte Code Signing CA, O=Thawte Consulting (Pty) Ltd., C=ZA + [certificate is valid from 06/08/03 02:00 to 06/08/13 01:59] + [KeyUsage extension does not support code signing] + X.509, EMAILADDRESS=premium-server@thawte.com, CN=Thawte Premium Server CA, OU=Certification Services Division, O=Thawte Consulting cc, L=Cape Town, ST=Western Cape, C=ZA + [certificate is valid from 01/08/96 02:00 to 01/01/21 00:59] + + + s = signature was verified + m = entry is listed in manifest + k = at least one certificate was found in keystore + i = at least one certificate was found in identity scope + +jar verified. diff --git a/test/data/tiddlywiki/jquery/jquery.js b/test/data/tiddlywiki/jquery/jquery.js new file mode 100755 index 000000000..628ed9b31 --- /dev/null +++ b/test/data/tiddlywiki/jquery/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v1.6.4 http://jquery.com/ | http://jquery.org/license */ +(function(a,b){function cu(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cr(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cq(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cp(){cn=b}function co(){setTimeout(cp,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bv(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bl(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bd,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bk(a){f.nodeName(a,"input")?bj(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bj)}function bj(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bi(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bh(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bg(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function U(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function M(a,b){return(a&&a!=="*"?a+".":"")+b.replace(y,"`").replace(z,"&")}function L(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function J(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function D(){return!0}function C(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function K(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(K,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,x=/^-ms-/,y=function(a,b){return(b+"").toUpperCase()},z=d.userAgent,A,B,C,D=Object.prototype.toString,E=Object.prototype.hasOwnProperty,F=Array.prototype.push,G=Array.prototype.slice,H=String.prototype.trim,I=Array.prototype.indexOf,J={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.4",length:0,size:function(){return this.length},toArray:function(){return G.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?F.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),B.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(G.apply(this,arguments),"slice",G.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:F,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;B.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!B){B=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",C,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",C),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&K()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):J[D.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!E.call(a,"constructor")&&!E.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||E.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(x,"ms-").replace(w,y)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},m&&f.extend(p,{position:"absolute",left:"-1000px",top:"-1000px"});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i=f.expando,j=typeof c=="string",k=a.nodeType,l=k?f.cache:a,m=k?a[f.expando]:a[f.expando]&&f.expando;if((!m||e&&m&&l[m]&&!l[m][i])&&j&&d===b)return;m||(k?a[f.expando]=m=++f.uuid:m=f.expando),l[m]||(l[m]={},k||(l[m].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?l[m][i]=f.extend(l[m][i],c):l[m]=f.extend(l[m],c);g=l[m],e&&(g[i]||(g[i]={}),g=g[i]),d!==b&&(g[f.camelCase(c)]=d);if(c==="events"&&!g[c])return g[i]&&g[i].events;j?(h=g[c],h==null&&(h=g[f.camelCase(c)])):h=g;return h}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e=f.expando,g=a.nodeType,h=g?f.cache:a,i=g?a[f.expando]:f.expando;if(!h[i])return;if(b){d=c?h[i][e]:h[i];if(d){d[b]||(b=f.camelCase(b)),delete d[b];if(!l(d))return}}if(c){delete h[i][e];if(!l(h[i]))return}var j=h[i][e];f.support.deleteExpando||!h.setInterval?delete h[i]:h[i]=null,j?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=j):g&&(f.support.deleteExpando?delete a[f.expando]:a.removeAttribute?a.removeAttribute(f.expando):a[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=v:u&&(i=u)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.attr(a,b,""),a.removeAttribute(b),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(u&&f.nodeName(a,"button"))return u.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(u&&f.nodeName(a,"button"))return u.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==null?g:a[c]},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabIndex=f.propHooks.tabIndex,v={get:function(a,c){var d;return f.prop(a,c)===!0||(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(u=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var w=/\.(.*)$/,x=/^(?:textarea|input|select)$/i,y=/\./g,z=/ /g,A=/[^\w\s.|`]/g,B=function(a){return a.replace(A,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=C;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=C);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),B).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},I=function(c){var d=c.target,e,g;if(!!x.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=H(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:I,beforedeactivate:I,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&I.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&I.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",H(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in G)f.event.add(this,c+".specialChange",G[c]);return x.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return x.test(this.nodeName)}},G=f.event.special.change.filters,G.focus=G.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=S.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(U(c[0])||U(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=R.call(arguments);N.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!T[a]?f.unique(e):e,(this.length>1||P.test(d))&&O.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};be.optgroup=be.option,be.tbody=be.tfoot=be.colgroup=be.caption=be.thead,be.th=be.td,f.support.htmlSerialize||(be._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!be[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bh(a,d),e=bi(a),g=bi(d);for(h=0;e[h];++h)g[h]&&bh(e[h],g[h])}if(b){bg(a,d);if(c){e=bi(a),g=bi(d);for(h=0;e[h];++h)bg(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=be[l]||be._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bn.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bm,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bm.test(g)?g.replace(bm,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bv(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bw=function(a,c){var d,e,g;c=c.replace(bo,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bx=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bp.test(d)&&bq.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bv=bw||bx,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bz=/%20/g,bA=/\[\]$/,bB=/\r?\n/g,bC=/#.*$/,bD=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bE=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bF=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bG=/^(?:GET|HEAD)$/,bH=/^\/\//,bI=/\?/,bJ=/)<[^<]*)*<\/script>/gi,bK=/^(?:select|textarea)/i,bL=/\s+/,bM=/([?&])_=[^&]*/,bN=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bO=f.fn.load,bP={},bQ={},bR,bS,bT=["*/"]+["*"];try{bR=e.href}catch(bU){bR=c.createElement("a"),bR.href="",bR=bR.href}bS=bN.exec(bR.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bO)return bO.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bJ,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bK.test(this.nodeName)||bE.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bB,"\r\n")}}):{name:b.name,value:c.replace(bB,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?bX(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),bX(a,b);return a},ajaxSettings:{url:bR,isLocal:bF.test(bS[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bT},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bV(bP),ajaxTransport:bV(bQ),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?bZ(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=b$(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bD.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bC,"").replace(bH,bS[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bL),d.crossDomain==null&&(r=bN.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bS[1]&&r[2]==bS[2]&&(r[3]||(r[1]==="http:"?80:443))==(bS[3]||(bS[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bW(bP,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bG.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bI.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bM,"$1_="+x);d.url=y+(y===d.url?(bI.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bT+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bW(bQ,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){s<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bz,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cq("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=ct.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!ct.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cu(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cu(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNaN(j)?i:j}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/test/data/tiddlywiki/jquery/plugins/doc/encoding.digests.sha1.html b/test/data/tiddlywiki/jquery/plugins/doc/encoding.digests.sha1.html new file mode 100755 index 000000000..8a5cb7879 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/encoding.digests.sha1.html @@ -0,0 +1,43 @@ + + + + + + jQuery.encoding.digests.sha1 + + + + +

jQuery.encoding.digests.sha1

+

+ This jQuery plugin implements the SHA-1 cryptographic hash function. +

+ +

Source

+

+ The source code is currently hosted in TiddlyWiki's + Subversion repository. +

+

+ Feedback is welcome. +

+ +

API

+
    +
  • +

    $.encoding.digests.hexSha1Str(str): Return, in hex, the SHA-1 hash of a string

    +
  • +
  • +

    $.encoding.digests.sha1Str(str): Return the SHA-1 hash of a string

    +
  • +
  • +

    $.encoding.digests.sha1(x,blen): Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words

    +
  • +
+

(full documentation in the code comments)

+ +

Demo

+ [TBD] + + + diff --git a/test/data/tiddlywiki/jquery/plugins/doc/index.html b/test/data/tiddlywiki/jquery/plugins/doc/index.html new file mode 100755 index 000000000..8c11a8e45 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/index.html @@ -0,0 +1,31 @@ + + + + + + TiddlyWiki jQuery plugins + + + + +

TiddlyWiki jQuery plugins

+

+ With jQuery being integrated into the + TiddlyWiki core, the community has + begun extracting TiddlyWiki functionality into generic components in + the form of jQuery plugins: +

+ + + + diff --git a/test/data/tiddlywiki/jquery/plugins/doc/styles/main.css b/test/data/tiddlywiki/jquery/plugins/doc/styles/main.css new file mode 100755 index 000000000..d0969d959 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/styles/main.css @@ -0,0 +1,78 @@ +* { + margin: 0; + padding: 0; +} + +html { + background-color: #000; +} + +body { + width: 50%; + margin: 0 auto; + padding: 10px; + background-color: #FFF; +} + +h1, +h2 { + margin-bottom: 10px; +} + +h2 { + margin-top: 20px; +} + +p, +ul { + margin-bottom: 0.5em; +} + +ul { + margin-left: 1em; +} + +ul ul { + margin-bottom: 0; +} + +li p { + margin-bottom: 0.2em; +} + +code { + color: #0A0; +} + +fieldset, +legend { + border: 1px solid #AAA; +} + +fieldset { + margin: 30px 10px 10px; + padding: 10px 5px 5px 10px; +} + +legend, +.editor { + background-color: #EEE; +} + +legend { + margin-top: -1em; + border-bottom: none; + padding: 1px 3px 0; + line-height: 1em; +} + +fieldset textarea { + display: block; + width: 98%; +} + +fieldset input { + width: 5em; + margin: 10px 10px 5px 0; + font-size: 1.1em; +} diff --git a/test/data/tiddlywiki/jquery/plugins/doc/twFile.html b/test/data/tiddlywiki/jquery/plugins/doc/twFile.html new file mode 100755 index 000000000..b4378580c --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/twFile.html @@ -0,0 +1,124 @@ + + + + + + jQuery.twFile + + + + +

jQuery.twFile

+

+ This jQuery plugin provides access to + the local file system (for documents loaded from a file:// + URI) to load and save file contents from the browser. +

+ +

+ The code is based on TiddlyWiki's + self-saving capabilities. +

+ +

+ Note that the TiddlySaver + applet is required on Opera and WebKit-based browsers (Safari, Chrome). + The applet has to be present in the same folder as the respective HTML document. +

+ +

Source

+

+ The source code is currently hosted in TiddlyWiki's + Subversion repository. +

+

+ Feedback is welcome. +

+ +

API Summary

+
    +
  • +

    $.twFile.load(filePath): load contents from file

    +
  • +
  • +

    $.twFile.save(filePath, content): save contents to file

    +
  • +
  • +

    $.twFile.copy(dest, source): duplicate existing file

    +

    N.B.: This is not supported on all browsers.

    +
  • +
  • +

    + $.twFile.convertUriToLocalPath(filePath): + normalizes specified absolute file path +

    +
  • +
+

+ N.B.: All file paths must be absolute (e.g. + /tmp/foo.txt or C:\temp\foo.txt). +

+

(full documentation in the code comments)

+ +

Limitations

+
    +
  • + plugin unavailable until + document.ready + handlers have completed +

    + Since the TiddlySaver applet cannot be injected synchronously + into the document, this is done asynchronously during + document.ready processing. +

    +

    + This means that the plugin is not guaranteed to work properly + until after all these handlers have completed. +

    +
  • +
  • + currently unreliable UTF-8 support on Internet Explorer +

    + The plugin is designed to work with UTF-8 encoded text files. + However, support in Internet Explorer is broken, and can only + reliably save files that are encoded with the ANSI subset of + UTF-8. In the case of HTML files, this problem can often be + avoided by using HTML entity encodings. +

    +
  • +
+ +

Internals

+

+ Internally, the plugin uses four separate drivers to implement the functionality on different browsers: +

    +
  • + activeX: uses the FileSystemObject built into Internet Explorer 5 and above +
  • +
  • + mozilla: uses the XUL libraries built into Firefox +
  • +
  • + tiddlySaver: uses a custom Java applet that works on Safari, Chrome and Opera +
  • +
  • + javaLiveConnect: uses an ancient (and slow) binding technology to call Java runtime library routines directly - only works on Opera +
  • +
+

+ +

Demo

+

+ Download this document (and + TiddlySaver if necessary) and open it from + the local disk. +

+

+ This demo illustrates self-saving capabilities by passing + document.location.href to $.twFile.convertUriToLocalPath, + using the return value in load and save functions. +

+ + + + diff --git a/test/data/tiddlywiki/jquery/plugins/doc/twFileDemo.html b/test/data/tiddlywiki/jquery/plugins/doc/twFileDemo.html new file mode 100755 index 000000000..24545b721 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/twFileDemo.html @@ -0,0 +1,435 @@ + + + + + jQuery.twFileDemo + + + +

jQuery.twFileDemo

+

+ This is an example file that can be used to test out the saving and loading system. + Press the edit button to view the page HTML and you can then make changes. Press save + to update the page. +

+

Dummy Text

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris viverra nisl sagittis lorem + rutrum eu lacinia elit ultrices. In laoreet tortor in dolor dignissim vitae lacinia massa dignissim. + Sed luctus, tortor a aliquam vulputate, orci tellus vulasdasdputate risus, sed vehicula quam massa ac orci. + Cras in nibh purus, ac tempor est. In tincidunt nulla et velit accumsan non tempor massa vulputate. + Nunc condimentum aliquam ligulasdasda, vel tincidunt nunc fringilla eget. Ut arcu nisl, auctor viverra tempor ut, + dapibus et mi. Etiam porttitor, quam venenatis luctus porta, risus sapien rutrum ipsum, quis luctus sem mi + posuere urna. Duis interdum, justo quis sodales vulputate, mauris nisi hendrerit lacus, + porttitor consequat diam tortor ut lorem. Nullam in justo sit amet orci ornare porta quis nec nulla. + Vestibulum eu mattis arcu. In nulla purus, vestibulum id ornare placerat, sagittis quis lacus. + Aenean a eros eu sem pulvinar iaculis. Quisque molestie, sapien ac luctus tempus, eros dui posuere nulla, + quis euismod dolor nisl nec dolor. Duis laoreet varius fermentum. Pellentesque habitant morbi tristique + senectus et netus et malesuada fames ac turpis egestas. Mauris at nulla quam, et eleifend mi. + Maecenas magna arcu, posuere sit amet ullamcorper vehicula, ornare nec lectus. Curabitur nec interdum nisi. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. +

+

Tools

+ +
Cannot load file
+ + + + + + + + + + diff --git a/test/data/tiddlywiki/jquery/plugins/doc/twStylesheet.html b/test/data/tiddlywiki/jquery/plugins/doc/twStylesheet.html new file mode 100755 index 000000000..3b8b6a7d6 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/twStylesheet.html @@ -0,0 +1,94 @@ + + + + + + jQuery.twStylesheet + + + + + + + +

jQuery.twStylesheet

+

+ This jQuery plugin allows the application + of CSS rule sets to the document. +

+

+ In contrast to jQuery's CSS methods, + it applies styles to the document rather than to individual elements (just + like defining a static style sheet in the document head). +

+

+ The code is based on TiddlyWiki's dynamic + style-sheet capabilities, where it is used to allow users to customize their + documents on the fly. +

+ +

Source

+

+ The source code is currently hosted in TiddlyWiki's + Subversion repository. +

+

+ Feedback is welcome. +

+ +

API Summary

+
    +
  • +

    $.twStylesheet(css[, options]): adds or replaces a style sheet

    +

    + css is a string containing the CSS rule sets, while + options.id is an optional name identifying the style sheet, allowing + co-existence of multiple style sheets +

    +
  • +
  • +

    + $.twStylesheet.remove([options]): delete an existing style sheet +

    +

    + The options argument is identical to $.twStylesheet's. +

    +
  • +
+

(full documentation in the code comments)

+ +

Demo

+ This will apply the CSS rule sets below to the entire document. +
+ CSS + + + +
+ + + diff --git a/test/data/tiddlywiki/jquery/plugins/doc/update b/test/data/tiddlywiki/jquery/plugins/doc/update new file mode 100755 index 000000000..1be8cde57 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/doc/update @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# Usage: +# upload [user] + +REMOTE_USER=${1:-$USER} +HOST="jquery.tiddlywiki.org" +DIR="/var/www/www.jquery.tiddlywiki.org/htdocs/" + +COMMANDS="ssh $REMOTE_USER@$HOST" +COMMANDS="$COMMANDS cd $DIR;" +COMMANDS="$COMMANDS sudo svn update;" +$COMMANDS diff --git a/test/data/tiddlywiki/jquery/plugins/jQuery.encoding.digests.sha1.js b/test/data/tiddlywiki/jquery/plugins/jQuery.encoding.digests.sha1.js new file mode 100755 index 000000000..db026415c --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/jQuery.encoding.digests.sha1.js @@ -0,0 +1,155 @@ +/* +jQuery.encoding.digests.sha1.js + +SHA-1 digest and associated utility functions + +Copyright (c) UnaMesa Association 2009 + +Dual licensed under the MIT and GPL licenses: + http://www.opensource.org/licenses/mit-license.php + http://www.gnu.org/licenses/gpl.html +*/ + +(function($) { + +if(!$.encoding) + $.encoding = {}; + $.extend($.encoding,{ + strToBe32s: function(str) { + // Convert a string to an array of big-endian 32-bit words + var be=[]; + var len=Math.floor(str.length/4); + var i, j; + for(i=0, j=0; i>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32); + j++; + } + return be; + }, + be32sToStr: function(be) { + // Convert an array of big-endian 32-bit words to a string + var str=''; + for(var i=0;i>5]>>>(24-i%32)) & 0xff); + } + return str; + }, + be32sToHex: function(be) { + // Convert an array of big-endian 32-bit words to a hex string + var hex='0123456789ABCDEF'; + var str=''; + for(var i=0;i>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF); + } + return str; + } + }); +})(jQuery); + + +(function($) { + +if(!$.encoding.digests) + $.encoding.digests = {}; + $.extend($.encoding.digests,{ + hexSha1Str: function(str) { + // Return, in hex, the SHA-1 hash of a string + return $.encoding.be32sToHex($.encoding.digests.sha1Str(str)); + }, + sha1Str: function(str) { + // Return the SHA-1 hash of a string + return sha1($.encoding.strToBe32s(str),str.length); + }, + sha1: function(x,blen) { + // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words + return sha1($.encoding.strToBe32s(str),str.length); + } + }); + + // Private functions. + function sha1(x,blen) { + // Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words + function add32(a,b) { + // Add 32-bit integers, wrapping at 32 bits + // Uses 16-bit operations internally to work around bugs in some JavaScript interpreters. + var lsw=(a&0xFFFF)+(b&0xFFFF); + var msw=(a>>16)+(b>>16)+(lsw>>16); + return (msw<<16)|(lsw&0xFFFF); + } + function AA(a,b,c,d,e) { + // Cryptographic round helper function. Add five 32-bit integers, wrapping at 32 bits, second parameter is rotated left 5 bits before the addition + // Uses 16-bit operations internally to work around bugs in some JavaScript interpreters. + b=(b>>>27)|(b<<5); + var lsw=(a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF); + var msw=(a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16); + return (msw<<16)|(lsw&0xFFFF); + } + function RR(w,j) { + // Cryptographic round helper function. + var n=w[j-3]^w[j-8]^w[j-14]^w[j-16]; + return (n>>>31)|(n<<1); + } + + var len=blen*8; + //# Append padding so length in bits is 448 mod 512 + x[len>>5] |= 0x80 << (24-len%32); + //# Append length + x[((len+64>>9)<<4)+15]=len; + var w=new Array(80); + + var k1=0x5A827999; + var k2=0x6ED9EBA1; + var k3=0x8F1BBCDC; + var k4=0xCA62C1D6; + + var h0=0x67452301; + var h1=0xEFCDAB89; + var h2=0x98BADCFE; + var h3=0x10325476; + var h4=0xC3D2E1F0; + + for(var i=0;i>>2)|(b<<30); b=a; a=t; j++; + } + while(j<20) { + w[j]=RR(w,j); + t=AA(e,a,d^(b&(c^d)),w[j],k1); + e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++; + } + while(j<40) { + w[j]=RR(w,j); + t=AA(e,a,b^c^d,w[j],k2); + e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++; + } + while(j<60) { + w[j]=RR(w,j); + t=AA(e,a,(b&c)|(d&(b|c)),w[j],k3); + e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++; + } + while(j<80) { + w[j]=RR(w,j); + t=AA(e,a,b^c^d,w[j],k4); + e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++; + } + h0=add32(h0,a); + h1=add32(h1,b); + h2=add32(h2,c); + h3=add32(h3,d); + h4=add32(h4,e); + } + return [h0,h1,h2,h3,h4]; + } +})(jQuery); diff --git a/test/data/tiddlywiki/jquery/plugins/jQuery.twFile.js b/test/data/tiddlywiki/jquery/plugins/jQuery.twFile.js new file mode 100755 index 000000000..1967e30c4 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/jQuery.twFile.js @@ -0,0 +1,333 @@ +/* +jQuery.twFile.js + +jQuery plugin for loading a file and saving data to a file + +Copyright (c) UnaMesa Association 2009 + +Triple licensed under the BSD, MIT and GPL licenses: + http://www.opensource.org/licenses/bsd-license.php + http://www.opensource.org/licenses/mit-license.php + http://www.gnu.org/licenses/gpl.html +*/ + + +(function($) { + if(!$.twFile) { + $.twFile = {}; + } + + $.extend($.twFile,{ + currentDriver: null, + driverList: ["tiddlySaver", "activeX","javaLiveConnect", "mozilla"], + + // Loads the contents of a text file from the local file system + // filePath is the path to the file in these formats: + // x:\path\path\path\filename - PC local file + // \\server\share\path\path\path\filename - PC network file + // /path/path/path/filename - Mac/Unix local file + // returns the text of the file, or null if the operation cannot be performed or false if there was an error + load: function(filePath) { + var d = this.getDriver(); + return d ? d.loadFile(filePath) : null; + }, + // Saves a string to a text file on the local file system + // filePath is the path to the file in the format described above + // content is the string to save + // returns true if the file was saved successfully, or null if the operation cannot be performed or false if there was an error + save: function(filePath,content) { + var d = this.getDriver(); + return d ? d.saveFile(filePath,content) : null; + }, + // Copies a file on the local file system + // dest is the path to the destination file in the format described above + // source is the path to the source file in the format described above + // returns true if the file was copied successfully, or null if the operation cannot be performed or false if there was an error + copy: function(dest,source) { + var d = this.getDriver(); + if(d && d.copyFile) + return d.copyFile(dest,source); + else + return null; + }, + // Converts a local file path from the format returned by document.location into the format expected by this plugin + // url is the original URL of the file + // returns the equivalent local file path + convertUriToLocalPath: function (url) { + // Remove any location or query part of the URL + var originalPath = url.split("#")[0].split("?")[0]; + // Convert file://localhost/ to file:/// + if(originalPath.indexOf("file://localhost/") == 0) + originalPath = "file://" + originalPath.substr(16); + // Convert to a native file format + //# "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..." + //# "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..." + //# "file:///path/path/path..." - mac/unix local file --> "/path/path/path..." + //# "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..." + var localPath; + if(originalPath.charAt(9) == ":") // PC local file + localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\"); + else if(originalPath.indexOf("file://///") == 0) // Firefox PC network file + localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\"); + else if(originalPath.indexOf("file:///") == 0) // Mac/UNIX local file + localPath = unescape(originalPath.substr(7)); + else if(originalPath.indexOf("file:/") == 0) // Mac/UNIX local file + localPath = unescape(originalPath.substr(5)); + else if(originalPath.indexOf("//") == 0) // PC network file + localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\"); + return localPath || originalPath; + }, + + // Deferred initialization for any drivers that need it + // returns a Deferred object so callback that executes as soon + // as twFile is ready can be attached + initialize: function() { + return $.Deferred(function(dfd) { + for(var t in drivers) { + if(drivers[t].deferredInit) + drivers[t].deferredInit(); + } + // Kludge: give the some time to load + setTimeout(dfd.resolve, 0); + }); + }, + + // Private functions + + // Returns a reference to the current driver + getDriver: function() { + if(this.currentDriver === null) { + for(var t=0; t=0;i--) { + if(!fso.FolderExists(scan[i])) { + fso.CreateFolder(scan[i]); + } + } + return true; + } catch(ex) { + } + return false; + }, + copyFile: function(dest,source) { + drivers.activeX.createPath(dest); + try { + var fso = new ActiveXObject("Scripting.FileSystemObject"); + fso.GetFile(source).Copy(dest); + } catch(ex) { + return false; + } + return true; + }, + saveFile: function(filePath,content) { + // Returns null if it can't do it, false if there's an error, true if it saved OK + drivers.activeX.createPath(filePath); + try { + var fso = new ActiveXObject("Scripting.FileSystemObject"); + var file = fso.OpenTextFile(filePath,2,-1,0); + file.Write(content); + file.Close(); + } catch (ex) { + return null; + } + return true; + } + }; + + // Mozilla driver + + drivers.mozilla = { + name: "mozilla", + isAvailable: function() { + return !!window.Components; + }, + loadFile: function(filePath) { + // Returns null if it can't do it, false if there's an error, or a string of the content if successful + if(window.Components) { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); + file.initWithPath(filePath); + if(!file.exists()) + return null; + var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream); + inputStream.init(file,0x01,00004,null); + var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream); + sInputStream.init(inputStream); + var contents = sInputStream.read(sInputStream.available()); + sInputStream.close(); + inputStream.close(); + return contents; + } catch(ex) { + //# alert("Exception while attempting to load\n\n" + ex); + return false; + } + } + return null; + }, + saveFile: function(filePath,content) { + // Returns null if it can't do it, false if there's an error, true if it saved OK + if(window.Components) { + try { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); + file.initWithPath(filePath); + if(!file.exists()) + file.create(0,0664); + var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream); + out.init(file,0x20|0x02,00004,null); + out.write(content,content.length); + out.flush(); + out.close(); + return true; + } catch(ex) { + alert("Exception while attempting to save\n\n" + ex); + return false; + } + } + return null; + } + }; + + // TiddlySaver driver + + drivers.tiddlySaver = { + name: "tiddlySaver", + deferredInit: function() { + if(!document.applets["TiddlySaver"] && /* !$.browser.mozilla && !$.browser.msie && */ document.location.toString().substr(0,5) == "file:") { + $(document.body).append(""); + } + }, + isAvailable: function() { + var isReady = false; + try { + isReady = !!document.applets["TiddlySaver"] && + ($.browser.msie || document.applets["TiddlySaver"].isActive) && + ( document.applets["TiddlySaver"].isActive() ); + } catch (ex) { + isReady = false; + } + return isReady; + }, + loadFile: function(filePath) { + var r; + try { + if(document.applets["TiddlySaver"]) { + r = document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8"); + return (r === undefined || r === null) ? null : String(r); + } + } catch(ex) { + } + return null; + }, + saveFile: function(filePath,content) { + try { + if(document.applets["TiddlySaver"]) + return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content); + } catch(ex) { + } + return null; + } + }; + + // Java LiveConnect driver + + drivers.javaLiveConnect = { + name: "javaLiveConnect", + isAvailable: function() { + return !!window.java && !!window.java.io && !!window.java.io.FileReader; + }, + loadFile: function(filePath) { + var r; + var content = []; + try { + r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath))); + var line; + while((line = r.readLine()) != null) + content.push(new String(line)); + r.close(); + } catch(ex) { + return null; + } + return content.join("\n") + "\n"; + }, + saveFile: function(filePath,content) { + try { + var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath))); + s.print(content); + s.close(); + } catch(ex) { + return null; + } + return true; + } + }; + + // Private utilities + + function javaUrlToFilename(url) { + var f = "//localhost"; + if(url.indexOf(f) == 0) + return url.substring(f.length); + var i = url.indexOf(":"); + return i > 0 ? url.substring(i-1) : url; + } + +})(jQuery); + diff --git a/test/data/tiddlywiki/jquery/plugins/jQuery.twStylesheet.js b/test/data/tiddlywiki/jquery/plugins/jQuery.twStylesheet.js new file mode 100755 index 000000000..7ef4da4b9 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/jQuery.twStylesheet.js @@ -0,0 +1,64 @@ +/* +jQuery.twStylesheet.js + +jQuery plugin to dynamically insert CSS rules into a document + +Usage: + jQuery.twStylesheet applies style definitions + jQuery.twStylesheet.remove neutralizes style definitions + +Copyright (c) UnaMesa Association 2009 + +Triple licensed under the BSD, MIT and GPL licenses: + http://www.opensource.org/licenses/bsd-license.php + http://www.opensource.org/licenses/mit-license.php + http://www.gnu.org/licenses/gpl.html +*/ + +(function($) { + +var defaultId = "customStyleSheet"; // XXX: rename to dynamicStyleSheet? + +// Add or replace a style sheet +// css argument is a string of CSS rule sets +// options.id is an optional name identifying the style sheet +// options.doc is an optional document reference +// N.B.: Uses DOM methods instead of jQuery to ensure cross-browser comaptibility. +$.twStylesheet = function(css, options) { + options = options || {}; + var id = options.id || defaultId; + var doc = options.doc || document; + var el = doc.getElementById(id); + if(doc.createStyleSheet) { // IE-specific handling + if(el) { + el.parentNode.removeChild(el); + } + doc.getElementsByTagName("head")[0].insertAdjacentHTML("beforeEnd", + ' '); // fails without   + } else { // modern browsers + if(el) { + el.replaceChild(doc.createTextNode(css), el.firstChild); + } else { + el = doc.createElement("style"); + el.type = "text/css"; + el.id = id; + el.appendChild(doc.createTextNode(css)); + doc.getElementsByTagName("head")[0].appendChild(el); + } + } +}; + +// Remove existing style sheet +// options.id is an optional name identifying the style sheet +// options.doc is an optional document reference +$.twStylesheet.remove = function(options) { + options = options || {}; + var id = options.id || defaultId; + var doc = options.doc || document; + var el = doc.getElementById(id); + if(el) { + el.parentNode.removeChild(el); + } +}; + +})(jQuery); diff --git a/test/data/tiddlywiki/jquery/plugins/jquery.gettext-1.0.4.js b/test/data/tiddlywiki/jquery/plugins/jquery.gettext-1.0.4.js new file mode 100755 index 000000000..a0d661182 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/jquery.gettext-1.0.4.js @@ -0,0 +1,139 @@ +/** + * gettext for jQuery + * + * Copyright (c) 2008 Sabin Iacob (m0n5t3r) + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * @license http://www.gnu.org/licenses/gpl.html + * @project jquery.gettext + * + * Usage: + * + * This plugin expects its input data to be a JSON object like + * {"": header, "string": "translation", ...} + * + * After getting the server side set up (either as a static file - my choice - or + * as a web service), the client side is simple: + * * add to the head section of the page something like + * + * * in your script, use $.gt.gettext(string) or _(string); for plural forms, use + * $.gt.ngettext(sg, pl1[, pl2, ...], count) or n_(sg, pl1[, pl2, ...], count) + * * to extract strings to a .po file, you can use standard gettext utilities like + * xgettext and msgfmt; to generate the JSON, one could use the following Python + * snippet, assuming a domain.mo file exists under path/lang/LC_MESSAGES: + * + * import simplejson as enc + * + * def gettext_json(domain, path, lang = [], indent = False): + * try: + * tr = gettext.translation(domain, path, lang) + * return enc.dumps(tr._catalog, ensure_ascii = False, indent = indent) + * except IOError: + * return None + * + * why go through the additional hassle of gettext? well, it's a matter of + * preference, the main advantags I see are: + * * well known editing tools like KBabel, poEdit, gtranslator, Emacs PO mode, + * etc. + * * translation memory, fuzzy matches and other features that get really + * helpful when your application is big and you have hundreds of strings + */ +(function($) { + $.gt = $.gt || {}; + + $.extend($.gt, { + messages: {}, + lang: 'C', + setLang: function(code) { $.gt.lang = typeof code == 'string' && code != ' ' ? code : 'C'; }, + pl_re: /^Plural-Forms:\s*nplurals\s*=\s*(\d+);\s*plural\s*=\s*([^a-zA-Z0-9\$]*([a-zA-Z0-9\$]+).+)$/m, + plural: function(n) {return n != 1;}, + load: function() { + $('link[rel=gettext]').each(function(){ + var lang = this.lang; + $.get(this.href, function(data){ + $.gt.messages[lang] = $.gt.messages[lang] || {}; + try { + var messages = eval('(' + data + ')'); + } catch(e) { + return; + } + + $.extend($.gt.messages[lang], messages); + + var pl = $.gt.pl_re.exec($.gt.messages[lang]['']); + if(pl){ + var expr = pl[2]; + var np = pl[1]; + var v = pl[3]; + try { + var fn = eval('(function(' + v + ') {return ' + expr + ';})'); + } catch(e) { + return; + } + $.gt.plural = fn; + } + }); + }); + $.gt.setLang($('html').attr('lang')); + }, + gettext: function(msgstr) { + var lang = $.gt.lang; + + if(lang == 'C' || typeof $.gt.messages[lang] == 'undefined') { + return msgstr; + } + + var trans = $.gt.messages[lang][msgstr]; + + if(typeof trans == 'string') { // regular action + return trans; + } else if(typeof trans == 'object' && trans.constructor == Array) { // the translation contains plural(s), yet gettext was called + return trans[0]; + } + return msgstr; + }, + ngettext: function() { + var lang = $.gt.lang; + var argv = Array.apply(null, arguments); + var cnt = argv[argv.length - 1]; + var sg = argv[0]; + var pls = argv.slice(0, -1); + + var trans = pls; + + if(lang != 'C' && typeof $.gt.messages[lang] != 'undefined') { + trans = $.gt.messages[lang][sg]; + } + + if(typeof trans == 'string') { // called ngettext, but no plural forms available :-? + return trans; + } else if(typeof trans == 'object' && trans.constructor == Array) { + var pl = $.gt.plural(cnt); + if(typeof pl == 'boolean' && pls.length == 2) { + pl = pl ? 1 : 0; + } + if(typeof pl == 'number' && pl < trans.length) { + return trans[pl]; + } + } + return sg; + } + }); + + $('document').ready($.gt.load); +})(jQuery); + +if(typeof _ == 'undefined') { + var _ = jQuery.gt.gettext; +} +if(typeof n_ == 'undefined') { + var n_ = jQuery.gt.ngettext; +} diff --git a/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.js b/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.js new file mode 100755 index 000000000..e15bc079d --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.js @@ -0,0 +1,10 @@ +/* +jquery.tw.js +addition of tw 'namespace' +*/ +(function($) { + if(!$.tw) { + $.tw = {}; + $.tw.extend = $.extend; + } +})(jQuery); diff --git a/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.macro.js b/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.macro.js new file mode 100755 index 000000000..35dc62d7a --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.macro.js @@ -0,0 +1,58 @@ +/* +jquery.tw.macro.js +macro parameter expansion +*/ +(function($) { + $.tw.extend({ + expandMacroParams: function(params) { + // expand the macro parameters into a name:value hash + // all parameters are also given a numeric name, starting at 1 + var opts = {}; + var unnamed = 1; + var name,val; + var t = $.trim(params); + var s = 0; + var i = findNakedSpace(t,s); + var param = i==-1 ? t.substr(s) : t.substring(s,i); + while(true) { + var ci = param.indexOf(':'); + if(ci==-1) { + // parameter is unnamed + name = param ? unnamed++ : null; + val = param; + } else { + name = param.substr(0,ci); + val = param.substr(ci+1); + } + val = $.trim(val); + if(val.charAt(0)=='"' && val.charAt(val.length-1)=='"') { + val = val.substr(1,val.length-2); + } + if(name) + opts[name] = val; + if(i==-1) + break; + s = i+1; + i = findNakedSpace(t,s); + param = i==-1 ? t.substr(s) : t.substring(s,i); + } + return opts; + } + }); + + // Private functions. + function findNakedSpace(text,start) { + // find the next space not surrounded by quotes + var d = text.indexOf(' ',start); + if(d==-1) + return -1; + var qs = text.indexOf('"',start); + if(qs==-1 || qs > d) + return d; + var qe = text.indexOf('"',qs+1); + if(qe==-1) + return d; + return findNakedSpace(text,qe+1); + } + +})(jQuery); diff --git a/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.macro.today.js b/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.macro.today.js new file mode 100755 index 000000000..33b187437 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/obsolete/jquery.tw.macro.today.js @@ -0,0 +1,15 @@ +/* +jquery.tw.macro.today.js +jQuery TiddlyWiki <> macro +*/ +(function($) { + $.fn.tw_today = function(args) { + args.format = args.format || args[1]; + var opts = $.extend({},$.fn.tw_today.defaults,args); + var now = new Date(); + var text = now.formatString(opts.format.trim()); + this.append(""+text+""); + return this; + }; + $.fn.tw_today.defaults = {format:"ddd mmm 0DD 0hh:0mm:0ss YYYY"}; +})(jQuery); diff --git a/test/data/tiddlywiki/jquery/plugins/split.recipe b/test/data/tiddlywiki/jquery/plugins/split.recipe new file mode 100755 index 000000000..363112e06 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/split.recipe @@ -0,0 +1,2 @@ +jquery: jQuery.encoding.digests.sha1.js +jquery: jQuery.twStylesheet.js diff --git a/test/data/tiddlywiki/jquery/plugins/test/TiddlySaver.jar b/test/data/tiddlywiki/jquery/plugins/test/TiddlySaver.jar new file mode 100755 index 000000000..7ff2c9da3 Binary files /dev/null and b/test/data/tiddlywiki/jquery/plugins/test/TiddlySaver.jar differ diff --git a/test/data/tiddlywiki/jquery/plugins/test/jQuery.twFile.html b/test/data/tiddlywiki/jquery/plugins/test/jQuery.twFile.html new file mode 100755 index 000000000..69c7d0a42 --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/test/jQuery.twFile.html @@ -0,0 +1,25 @@ + + + + + + Tests + + + + + + + + + + + + + +

+
    +
    + + + diff --git a/test/data/tiddlywiki/jquery/plugins/test/jQuery.twStylesheet.html b/test/data/tiddlywiki/jquery/plugins/test/jQuery.twStylesheet.html new file mode 100755 index 000000000..1eeb2098d --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/test/jQuery.twStylesheet.html @@ -0,0 +1,25 @@ + + + + + + Tests + + + + + + + + + + + + + +

    +
      +
      + + + diff --git a/test/data/tiddlywiki/jquery/plugins/test/js/jQuery.twFile.js b/test/data/tiddlywiki/jquery/plugins/test/js/jQuery.twFile.js new file mode 100755 index 000000000..f478f887e --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/test/js/jQuery.twFile.js @@ -0,0 +1,86 @@ +jQuery(document).ready(function() { + module("jQuery.twFile"); + + test("load", function() { + var actual, expected, filepath; + + actual = jQuery.twFile.load(); + expected = null; + same(actual, expected, "returns null if no argument is specified"); + + filepath = getDocumentPath() + "/sample.txt"; + actual = jQuery.twFile.load(filepath); + expected = "lorem ipsum\n" + + "dolor sit amet\n" + + "\n" + + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\n" + + "foo bar baz\n"; + same(actual, expected, "returns contents of specified file"); + + filepath = "/null"; + actual = jQuery.twFile.load(filepath); + expected = null; + same(actual, expected, "returns null if the specified file does not exist"); + + filepath = "sample.txt"; + actual = jQuery.twFile.load(filepath); + expected = null; + same(actual, expected, "returns null if specified file path is not absolute"); + }); + + test("save", function() { + var actual, expression, expected, str; + var filepath = getDocumentPath() + "/savetest.txt"; + + /* disabled as browser-level exceptions cannot be trapped + expression = function() { jQuery.twFile.save(); }; + expected = "ReferenceError"; + raises(expression, expected, "raises exception if no argument is specified"); + */ + + /* disabled as browser-level exceptions cannot be trapped + expression = function() { jQuery.twFile.save(filepath); }; + expected = "TypeError"; + raises(expression, expected, "raises exception if no content argument is specified"); + */ + + /* disabled as browser-level exceptions cannot be trapped + expression = function() { jQuery.twFile.save("foo.txt", "sample content"); }; + expected = "ReferenceError"; + raises(expression, expected, "raises exception if specified file path is not absolute"); + */ + + str = "lorem ipsum\n" + + "dolor sit amet\n" + + "\n" + + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\n" + + //"\xa9\u010d\u010c\n" + + "foo bar baz\n" + + (new Date).toString(); + saveAndLoadString(filepath, str, "writes given ANSI text content to specified file"); + + //str = "\xa9\u010d\u010c"; + //saveAndLoadString(filepath, str, "writes given UTF-8 text content to specified file"); + + //jQuery.twFile.save(filepath, ""); // teardown: blank file contents (deletion impossible) + }); + + // helper function to save and load back a string to a file + var saveAndLoadString = function(filepath,str,desc) { + jQuery.twFile.save(filepath, str); + var actual = jQuery.twFile.load(filepath); + same(actual, str, desc); + } + + // helper function to retrieve current document's file path + var getDocumentPath = function() { + var path = document.location.pathname; + var startpos = 0; + var endpos = path.lastIndexOf("/"); + if(path.charAt(2) == ":") { + startpos = 1; + path = path.replace(new RegExp("/","g"),"\\") + } + return unescape(path.substring(startpos, endpos)); + }; +}); diff --git a/test/data/tiddlywiki/jquery/plugins/test/js/jQuery.twStylesheet.js b/test/data/tiddlywiki/jquery/plugins/test/js/jQuery.twStylesheet.js new file mode 100755 index 000000000..56a00cbae --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/test/js/jQuery.twStylesheet.js @@ -0,0 +1,69 @@ +jQuery(document).ready(function() { + module("jQuery.twStylesheet"); + + test("apply", function() { + var actual, expected, el; + + el = jQuery('
      ').appendTo(document.body); + jQuery.twStylesheet("div { overflow: hidden; }"); + actual = jQuery(el).css("overflow"); + expected = "hidden"; + same(actual, expected, "applies style definitions to document"); + // teardown + jQuery(el).remove(); + jQuery.twStylesheet.remove(); + + el = jQuery('
      ').appendTo(document.body); + jQuery.twStylesheet("div { font-style: italic; }"); + actual = jQuery(el).css("font-style"); + expected = "italic"; + same(actual, expected, "applies style definitions to newly-created elements"); + // teardown + jQuery(el).remove(); + jQuery.twStylesheet.remove(); + + jQuery.twStylesheet("", { id: "dummyStyleSheet" }); + actual = jQuery("#dummyStyleSheet").length; + expected = 1; + same(actual, expected, "generates style element using given ID"); + // teardown + jQuery.twStylesheet.remove({ id: "dummyStyleSheet" }); + + // TODO: test for options.doc argument + + }); + + test("remove", function() { + var actual, expected; + + // setup + el = jQuery('
      ').appendTo(document.body); + jQuery.twStylesheet("div { overflow: hidden; }"); + // test + jQuery.twStylesheet.remove(); + actual = jQuery(el).css("overflow"); + expected = "visible"; + same(actual, expected, "neutralizes style definitions"); + // teardown + jQuery(el).remove(); + + // setup + jQuery.twStylesheet(""); + // test + jQuery.twStylesheet.remove(); + actual = jQuery("#customStyleSheet").length; + expected = 0; + same(actual, expected, "removes default style sheet if no ID is given"); + + // setup + jQuery.twStylesheet("", { id: "dummyStyleSheet" }); + // test + jQuery.twStylesheet.remove({ id: "dummyStyleSheet" }); + actual = jQuery("#dummyStyleSheet").length; + expected = 0; + same(actual, expected, "removes style element using given ID"); + + // TODO: test for options.doc argument + + }); +}); diff --git a/test/data/tiddlywiki/jquery/plugins/test/sample.txt b/test/data/tiddlywiki/jquery/plugins/test/sample.txt new file mode 100755 index 000000000..9036986bd --- /dev/null +++ b/test/data/tiddlywiki/jquery/plugins/test/sample.txt @@ -0,0 +1,5 @@ +lorem ipsum +dolor sit amet + + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~ +foo bar baz diff --git a/test/data/tiddlywiki/jquery/split.recipe b/test/data/tiddlywiki/jquery/split.recipe new file mode 100755 index 000000000..269987a30 --- /dev/null +++ b/test/data/tiddlywiki/jquery/split.recipe @@ -0,0 +1,2 @@ +jslib: jquery.js +recipe: plugins/split.recipe diff --git a/test/data/tiddlywiki/js/AdaptorBase.js b/test/data/tiddlywiki/js/AdaptorBase.js new file mode 100755 index 000000000..d63c76b4c --- /dev/null +++ b/test/data/tiddlywiki/js/AdaptorBase.js @@ -0,0 +1,90 @@ +//-- +//-- Server adaptor base class +//-- + +function AdaptorBase() +{ + this.host = null; + this.store = null; + return this; +} + +AdaptorBase.prototype.close = function() +{ + return true; +}; + +AdaptorBase.prototype.fullHostName = function(host) +{ + if(!host) + return ''; + host = host.trim(); + if(!host.match(/:\/\//)) + host = 'http://' + host; + if(host.substr(host.length-1) == '/') + host = host.substr(0,host.length-1); + return host; +}; + +AdaptorBase.minHostName = function(host) +{ + return host; +}; + +AdaptorBase.prototype.setContext = function(context,userParams,callback) +{ + if(!context) context = {}; + context.userParams = userParams; + if(callback) context.callback = callback; + context.adaptor = this; + if(!context.host) + context.host = this.host; + context.host = this.fullHostName(context.host); + if(!context.workspace) + context.workspace = this.workspace; + return context; +}; + +// Open the specified host +//# host - uri of host (eg, "http://www.tiddlywiki.com/" or "www.tiddlywiki.com") +//# context is itself passed on as a parameter to the callback function +//# userParams - user settable object object that is passed on unchanged to the callback function +//# callback - optional function to be called on completion +//# Return value is true if the request was successfully issued, false if this connector doesn't support openHost(), +//# or an error description string if there was a problem +//# The callback parameters are callback(context) +//# context.status - true if OK, string if error +//# context.adaptor - reference to this adaptor object +//# userParams - parameters as originally passed into the openHost function +AdaptorBase.prototype.openHost = function(host,context,userParams,callback) +{ + this.host = host; + context = this.setContext(context,userParams,callback); + context.status = true; + if(callback) + window.setTimeout(function() {context.callback(context,userParams);},10); + return true; +}; + +// Open the specified workspace +//# workspace - name of workspace to open +//# context - passed on as a parameter to the callback function +//# userParams - user settable object object that is passed on unchanged to the callback function +//# callback - function to be called on completion +//# Return value is true if the request was successfully issued +//# or an error description string if there was a problem +//# The callback parameters are callback(context,userParams) +//# context.status - true if OK, false if error +//# context.statusText - error message if there was an error +//# context.adaptor - reference to this adaptor object +//# userParams - parameters as originally passed into the openWorkspace function +AdaptorBase.prototype.openWorkspace = function(workspace,context,userParams,callback) +{ + this.workspace = workspace; + context = this.setContext(context,userParams,callback); + context.status = true; + if(callback) + window.setTimeout(function() {callback(context,userParams);},10); + return true; +}; + diff --git a/test/data/tiddlywiki/js/Animator.js b/test/data/tiddlywiki/js/Animator.js new file mode 100755 index 000000000..f6263a88c --- /dev/null +++ b/test/data/tiddlywiki/js/Animator.js @@ -0,0 +1,46 @@ +//- +//- Animation engine +//- + +function Animator() +{ + this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled + this.timerID = 0; // ID of the timer used for animating + this.animations = []; // List of animations in progress + return this; +} + +// Start animation engine +Animator.prototype.startAnimating = function() //# Variable number of arguments +{ + var t; + for(t=0; t 0) + ListView.create(popup,items,this.listViewTemplate); + else + createTiddlyElement(popup,"div",null,null,this.emptyText); +}; + diff --git a/test/data/tiddlywiki/js/Config.js b/test/data/tiddlywiki/js/Config.js new file mode 100755 index 000000000..23aad000e --- /dev/null +++ b/test/data/tiddlywiki/js/Config.js @@ -0,0 +1,227 @@ +//-- +//-- Configuration repository +//-- + +// Miscellaneous options +var config = { + numRssItems: 20, // Number of items in the RSS feed + animDuration: 400, // Duration of UI animations in milliseconds + cascadeFast: 20, // Speed for cascade animations (higher == slower) + cascadeSlow: 60, // Speed for EasterEgg cascade animations + cascadeDepth: 5, // Depth of cascade animation + locale: "en" // W3C language tag +}; + +// Hashmap of alternative parsers for the wikifier +config.parsers = {}; + +// Adaptors +config.adaptors = {}; +config.defaultAdaptor = null; + +// Backstage tasks +config.tasks = {}; + +// Annotations +config.annotations = {}; + +// Custom fields to be automatically added to new tiddlers +config.defaultCustomFields = {}; + +// Messages +config.messages = { + messageClose: {}, + dates: {}, + tiddlerPopup: {} +}; + +// Options that can be set in the options panel and/or cookies +config.options = { + chkRegExpSearch: false, + chkCaseSensitiveSearch: false, + chkIncrementalSearch: true, + chkAnimate: true, + chkSaveBackups: true, + chkAutoSave: false, + chkGenerateAnRssFeed: false, + chkSaveEmptyTemplate: false, + chkOpenInNewWindow: true, + chkToggleLinks: false, + chkHttpReadOnly: true, + chkForceMinorUpdate: false, + chkConfirmDelete: true, + chkInsertTabs: false, + chkUsePreForStorage: true, // Whether to use
       format for storage
      +	chkDisplayInstrumentation: false,
      +	txtBackupFolder: "",
      +	txtEditorFocus: "text",
      +	txtMainTab: "tabTimeline",
      +	txtMoreTab: "moreTabAll",
      +	txtMaxEditRows: "30",
      +	txtFileSystemCharSet: "UTF-8",
      +	txtTheme: ""
      +	};
      +config.optionsDesc = {};
      +
      +//# config.optionSource["chkAnimate"] can be:
      +//# 	cookie: the option gets stored in a cookie, with the default value coming from SystemSettings
      +//#		volatile: the option isn't persisted at all, and reverts to the default specified in SystemSettings when the document is reloaded
      +//#		setting: the option is stored in the SystemSettings tiddler
      +//#	The default is "setting"
      +config.optionsSource = {};
      +
      +// Default tiddler templates
      +var DEFAULT_VIEW_TEMPLATE = 1;
      +var DEFAULT_EDIT_TEMPLATE = 2;
      +config.tiddlerTemplates = {
      +	1: "ViewTemplate",
      +	2: "EditTemplate"
      +};
      +
      +// More messages (rather a legacy layout that should not really be like this)
      +config.views = {
      +	wikified: {
      +		tag: {}
      +	},
      +	editor: {
      +		tagChooser: {}
      +	}
      +};
      +
      +// Backstage tasks
      +config.backstageTasks = ["save","sync","importTask","tweak","upgrade","plugins"];
      +
      +// Extensions
      +config.extensions = {};
      +
      +// Macros; each has a 'handler' member that is inserted later
      +config.macros = {
      +	today: {},
      +	version: {},
      +	search: {sizeTextbox: 15},
      +	tiddler: {},
      +	tag: {},
      +	tags: {},
      +	tagging: {},
      +	timeline: {},
      +	allTags: {},
      +	list: {
      +		all: {},
      +		missing: {},
      +		orphans: {},
      +		shadowed: {},
      +		touched: {},
      +		filter: {}
      +	},
      +	closeAll: {},
      +	permaview: {},
      +	saveChanges: {},
      +	slider: {},
      +	option: {},
      +	options: {},
      +	newTiddler: {},
      +	newJournal: {},
      +	tabs: {},
      +	gradient: {},
      +	message: {},
      +	view: {defaultView: "text"},
      +	edit: {},
      +	tagChooser: {},
      +	toolbar: {},
      +	plugins: {},
      +	refreshDisplay: {},
      +	importTiddlers: {},
      +	upgrade: {
      +		source: "http://tiddlywiki-releases.tiddlyspace.com/upgrade",
      +		backupExtension: "pre.core.upgrade"
      +	},
      +	sync: {},
      +	annotations: {}
      +};
      +
      +// Commands supported by the toolbar macro
      +config.commands = {
      +	closeTiddler: {},
      +	closeOthers: {},
      +	editTiddler: {},
      +	saveTiddler: {hideReadOnly: true},
      +	cancelTiddler: {},
      +	deleteTiddler: {hideReadOnly: true},
      +	permalink: {},
      +	references: {type: "popup"},
      +	jump: {type: "popup"},
      +	syncing: {type: "popup"},
      +	fields: {type: "popup"}
      +};
      +
      +// Control of macro parameter evaluation
      +config.evaluateMacroParameters = "all";
      +
      +// Basic regular expressions
      +config.textPrimitives = {
      +	upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
      +	lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
      +	anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
      +	anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
      +};
      +if(!((new RegExp("[\u0150\u0170]","g")).test("\u0150"))) {
      +	config.textPrimitives = {
      +		upperLetter: "[A-Z\u00c0-\u00de]",
      +		lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
      +		anyLetter:   "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
      +		anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
      +	};
      +}
      +config.textPrimitives.sliceSeparator = "::";
      +config.textPrimitives.sectionSeparator = "##";
      +config.textPrimitives.urlPattern = "(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)";
      +config.textPrimitives.unWikiLink = "~";
      +config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
      +	config.textPrimitives.lowerLetter + "+" +
      +	config.textPrimitives.upperLetter +
      +	config.textPrimitives.anyLetter + "*)|(?:" +
      +	config.textPrimitives.upperLetter + "{2,}" +
      +	config.textPrimitives.lowerLetter + "+))";
      +
      +config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
      +config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
      +
      +config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
      +config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
      +config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
      +	config.textPrimitives.brackettedLink + ")|(?:" +
      +	config.textPrimitives.urlPattern + ")","mg");
      +config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
      +	config.textPrimitives.titledBrackettedLink + ")|(?:" +
      +	config.textPrimitives.brackettedLink + ")|(?:" +
      +	config.textPrimitives.urlPattern + ")","mg");
      +
      +config.glyphs = {
      +	currBrowser: null,
      +	browsers: [],
      +	codes: {}
      +};
      +
      +//--
      +//-- Shadow tiddlers
      +//--
      +
      +config.shadowTiddlers = {
      +	StyleSheet: "",
      +	MarkupPreHead: "",
      +	MarkupPostHead: "",
      +	MarkupPreBody: "",
      +	MarkupPostBody: "",
      +	TabTimeline: '<>',
      +	TabAll: '<>',
      +	TabTags: '<>',
      +	TabMoreMissing: '<>',
      +	TabMoreOrphans: '<>',
      +	TabMoreShadowed: '<>',
      +	AdvancedOptions: '<>',
      +	PluginManager: '<>',
      +	SystemSettings: '',
      +	ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|',
      +	WindowTitle: '<> - <>'
      +};
      +
      diff --git a/test/data/tiddlywiki/js/ConfigBrowser.js b/test/data/tiddlywiki/js/ConfigBrowser.js
      new file mode 100755
      index 000000000..f995b8da3
      --- /dev/null
      +++ b/test/data/tiddlywiki/js/ConfigBrowser.js
      @@ -0,0 +1,30 @@
      +// Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
      +config.userAgent = navigator.userAgent.toLowerCase();
      +config.browser = {
      +	isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
      +	isGecko: navigator.product == "Gecko" && config.userAgent.indexOf("WebKit") == -1,
      +	ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
      +	isSafari: config.userAgent.indexOf("applewebkit") != -1,
      +	isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
      +	firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
      +	isOpera: config.userAgent.indexOf("opera") != -1,
      +	isChrome: config.userAgent.indexOf('chrome') > -1,
      +	isLinux: config.userAgent.indexOf("linux") != -1,
      +	isUnix: config.userAgent.indexOf("x11") != -1,
      +	isMac: config.userAgent.indexOf("mac") != -1,
      +	isWindows: config.userAgent.indexOf("win") != -1
      +};
      +
      +merge(config.glyphs,{
      +	browsers: [
      +		function() {return config.browser.isIE;},
      +		function() {return true;}
      +		],
      +	codes: {
      +		downTriangle: ["\u25BC","\u25BE"],
      +		downArrow: ["\u2193","\u2193"],
      +		bentArrowLeft: ["\u2190","\u21A9"],
      +		bentArrowRight: ["\u2192","\u21AA"]
      +	}
      +});
      +
      diff --git a/test/data/tiddlywiki/js/Crypto.js b/test/data/tiddlywiki/js/Crypto.js
      new file mode 100755
      index 000000000..fed672330
      --- /dev/null
      +++ b/test/data/tiddlywiki/js/Crypto.js
      @@ -0,0 +1,143 @@
      +//--
      +//-- Crypto functions and associated conversion routines
      +//--
      +
      +// Crypto 'namespace'
      +function Crypto() {}
      +
      +// Convert a string to an array of big-endian 32-bit words
      +Crypto.strToBe32s = function(str)
      +{
      +	var be=[];
      +	var len=Math.floor(str.length/4);
      +	var i, j;
      +	for(i=0, j=0; i>2] |= (str.charCodeAt(j)&0xff)<<(24-(j*8)%32);
      +		j++;
      +	}
      +	return be;
      +};
      +
      +// Convert an array of big-endian 32-bit words to a string
      +Crypto.be32sToStr = function(be)
      +{
      +	var str='';
      +	for(var i=0;i>5]>>>(24-i%32)) & 0xff);
      +	}
      +	return str;
      +};
      +
      +// Convert an array of big-endian 32-bit words to a hex string
      +Crypto.be32sToHex = function(be)
      +{
      +	var hex='0123456789ABCDEF';
      +	var str='';
      +	for(var i=0;i>2]>>((3-i%4)*8+4))&0xF) + hex.charAt((be[i>>2]>>((3-i%4)*8))&0xF);
      +	}
      +	return str;
      +};
      +
      +// Return, in hex, the SHA-1 hash of a string
      +Crypto.hexSha1Str = function(str)
      +{
      +	return Crypto.be32sToHex(Crypto.sha1Str(str));
      +};
      +
      +// Return the SHA-1 hash of a string
      +Crypto.sha1Str = function(str)
      +{
      +	return Crypto.sha1(Crypto.strToBe32s(str),str.length);
      +};
      +
      +// Calculate the SHA-1 hash of an array of blen bytes of big-endian 32-bit words
      +Crypto.sha1 = function(x,blen)
      +{
      +	// Add 32-bit integers, wrapping at 32 bits
      +	//# Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
      +	function add32(a,b)
      +	{
      +		var lsw=(a&0xFFFF)+(b&0xFFFF);
      +		var msw=(a>>16)+(b>>16)+(lsw>>16);
      +		return (msw<<16)|(lsw&0xFFFF);
      +	}
      +	//# Cryptographic round helper function. Add five 32-bit integers, wrapping at 32 bits, second parameter is rotated left 5 bits before the addition
      +	//# Uses 16-bit operations internally to work around bugs in some JavaScript interpreters.
      +	function AA(a,b,c,d,e)
      +	{
      +		b=(b>>>27)|(b<<5);
      +		var lsw=(a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF);
      +		var msw=(a>>16)+(b>>16)+(c>>16)+(d>>16)+(e>>16)+(lsw>>16);
      +		return (msw<<16)|(lsw&0xFFFF);
      +	}
      +	//# Cryptographic round helper function.
      +	function RR(w,j)
      +	{
      +		var n=w[j-3]^w[j-8]^w[j-14]^w[j-16];
      +		return (n>>>31)|(n<<1);
      +	}
      +
      +	var len=blen*8;
      +	//# Append padding so length in bits is 448 mod 512
      +	x[len>>5] |= 0x80 << (24-len%32);
      +	//# Append length
      +	x[((len+64>>9)<<4)+15]=len;
      +	var w=new Array(80);
      +
      +	var k1=0x5A827999;
      +	var k2=0x6ED9EBA1;
      +	var k3=0x8F1BBCDC;
      +	var k4=0xCA62C1D6;
      +
      +	var h0=0x67452301;
      +	var h1=0xEFCDAB89;
      +	var h2=0x98BADCFE;
      +	var h3=0x10325476;
      +	var h4=0xC3D2E1F0;
      +
      +	for(var i=0;i>>2)|(b<<30); b=a; a=t; j++;
      +		}
      +		while(j<20) {
      +			w[j]=RR(w,j);
      +			t=AA(e,a,d^(b&(c^d)),w[j],k1);
      +			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
      +		}
      +		while(j<40) {
      +			w[j]=RR(w,j);
      +			t=AA(e,a,b^c^d,w[j],k2);
      +			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
      +		}
      +		while(j<60) {
      +			w[j]=RR(w,j);
      +			t=AA(e,a,(b&c)|(d&(b|c)),w[j],k3);
      +			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
      +		}
      +		while(j<80) {
      +			w[j]=RR(w,j);
      +			t=AA(e,a,b^c^d,w[j],k4);
      +			e=d; d=c; c=(b>>>2)|(b<<30); b=a; a=t; j++;
      +		}
      +		h0=add32(h0,a);
      +		h1=add32(h1,b);
      +		h2=add32(h2,c);
      +		h3=add32(h3,d);
      +		h4=add32(h4,e);
      +	}
      +	return [h0,h1,h2,h3,h4];
      +};
      +
      diff --git a/test/data/tiddlywiki/js/Dates.js b/test/data/tiddlywiki/js/Dates.js
      new file mode 100755
      index 000000000..8cb2179e4
      --- /dev/null
      +++ b/test/data/tiddlywiki/js/Dates.js
      @@ -0,0 +1,119 @@
      +//--
      +//-- Augmented methods for the JavaScript Date() object
      +//--
      +
      +// Substitute date components into a string
      +Date.prototype.formatString = function(template)
      +{
      +	var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
      +	t = t.replace(/hh12/g,this.getHours12());
      +	t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
      +	t = t.replace(/hh/g,this.getHours());
      +	t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
      +	t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
      +	t = t.replace(/mm/g,this.getMinutes());
      +	t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
      +	t = t.replace(/ss/g,this.getSeconds());
      +	t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
      +	t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
      +	t = t.replace(/wYYYY/g,this.getYearForWeekNo());
      +	t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
      +	t = t.replace(/YYYY/g,this.getFullYear());
      +	t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
      +	t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
      +	t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
      +	t = t.replace(/MM/g,this.getMonth()+1);
      +	t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
      +	t = t.replace(/WW/g,this.getWeek());
      +	t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
      +	t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
      +	t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
      +	t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
      +	t = t.replace(/DD/g,this.getDate());
      +	var tz = this.getTimezoneOffset();
      +	var atz = Math.abs(tz);
      +	t = t.replace(/TZD/g,(tz < 0 ? '+' : '-') + String.zeroPad(Math.floor(atz / 60),2) + ':' + String.zeroPad(atz % 60,2));
      +	t = t.replace(/\\/g,"");
      +	return t;
      +};
      +
      +Date.prototype.getWeek = function()
      +{
      +	var dt = new Date(this.getTime());
      +	var d = dt.getDay();
      +	if(d==0) d=7;// JavaScript Sun=0, ISO Sun=7
      +	dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
      +	var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
      +	return Math.floor(n/7)+1;
      +};
      +
      +Date.prototype.getYearForWeekNo = function()
      +{
      +	var dt = new Date(this.getTime());
      +	var d = dt.getDay();
      +	if(d==0) d=7;// JavaScript Sun=0, ISO Sun=7
      +	dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
      +	return dt.getFullYear();
      +};
      +
      +Date.prototype.getHours12 = function()
      +{
      +	var h = this.getHours();
      +	return h > 12 ? h-12 : ( h > 0 ? h : 12 );
      +};
      +
      +Date.prototype.getAmPm = function()
      +{
      +	return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
      +};
      +
      +Date.prototype.daySuffix = function()
      +{
      +	return config.messages.dates.daySuffixes[this.getDate()-1];
      +};
      +
      +// Convert a date to local YYYYMMDDHHMM string format
      +Date.prototype.convertToLocalYYYYMMDDHHMM = function()
      +{
      +	return this.getFullYear() + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2);
      +};
      +
      +// Convert a date to UTC YYYYMMDDHHMM string format
      +Date.prototype.convertToYYYYMMDDHHMM = function()
      +{
      +	return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2);
      +};
      +
      +// Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
      +Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
      +{
      +	return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),3) +"0";
      +};
      +
      +// Static method to create a date from a UTC YYYYMMDDHHMM format string
      +Date.convertFromYYYYMMDDHHMM = function(d)
      +{
      +	d = d?d.replace(/[^0-9]/g, ""):"";
      +	return Date.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,12));
      +};
      +
      +// Static method to create a date from a UTC YYYYMMDDHHMMSS format string
      +Date.convertFromYYYYMMDDHHMMSS = function(d)
      +{
      +	d = d?d.replace(/[^0-9]/g, ""):"";
      +	return Date.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,14));
      +};
      +
      +// Static method to create a date from a UTC YYYYMMDDHHMMSSMMM format string
      +Date.convertFromYYYYMMDDHHMMSSMMM = function(d)
      +{
      +	d = d ? d.replace(/[^0-9]/g, "") : "";
      +	return new Date(Date.UTC(parseInt(d.substr(0,4),10),
      +			parseInt(d.substr(4,2),10)-1,
      +			parseInt(d.substr(6,2),10),
      +			parseInt(d.substr(8,2)||"00",10),
      +			parseInt(d.substr(10,2)||"00",10),
      +			parseInt(d.substr(12,2)||"00",10),
      +			parseInt(d.substr(14,3)||"000",10)));
      +};
      +
      diff --git a/test/data/tiddlywiki/js/Dom.js b/test/data/tiddlywiki/js/Dom.js
      new file mode 100755
      index 000000000..d288d34de
      --- /dev/null
      +++ b/test/data/tiddlywiki/js/Dom.js
      @@ -0,0 +1,264 @@
      +//--
      +//-- DOM utilities - many derived from www.quirksmode.org
      +//--
      +
      +function drawGradient(place,horiz,locolors,hicolors)
      +{
      +	if(!hicolors)
      +		hicolors = locolors;
      +	var t;
      +	for(t=0; t<= 100; t+=2) {
      +		var bar = document.createElement("div");
      +		place.appendChild(bar);
      +		bar.style.position = "absolute";
      +		bar.style.left = horiz ? t + "%" : 0;
      +		bar.style.top = horiz ? 0 : t + "%";
      +		bar.style.width = horiz ? (101-t) + "%" : "100%";
      +		bar.style.height = horiz ? "100%" : (101-t) + "%";
      +		bar.style.zIndex = -1;
      +		var p = t/100*(locolors.length-1);
      +		var hc = hicolors[Math.floor(p)];
      +		if(typeof hc == "string")
      +			hc = new RGB(hc);
      +		var lc = locolors[Math.ceil(p)];
      +		if(typeof lc == "string")
      +			lc = new RGB(lc);
      +		bar.style.backgroundColor = hc.mix(lc,p-Math.floor(p)).toString();
      +	}
      +}
      +
      +//# Add an event handler
      +//# Thanks to John Resig, via QuirksMode
      +function addEvent(obj,type,fn)
      +{
      +	if(obj.attachEvent) {
      +		obj["e"+type+fn] = fn;
      +		obj[type+fn] = function(){obj["e"+type+fn](window.event);};
      +		obj.attachEvent("on"+type,obj[type+fn]);
      +	} else {
      +		obj.addEventListener(type,fn,false);
      +	}
      +}
      +
      +//# Remove an event handler
      +//# Thanks to John Resig, via QuirksMode
      +function removeEvent(obj,type,fn)
      +{
      +	if(obj.detachEvent) {
      +		obj.detachEvent("on"+type,obj[type+fn]);
      +		obj[type+fn] = null;
      +	} else {
      +		obj.removeEventListener(type,fn,false);
      +	}
      +}
      +
      +// Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
      +function findRelated(e,value,name,relative)
      +{
      +	name = name || "tagName";
      +	relative = relative || "parentNode";
      +	if(name == "className") {
      +		while(e && !jQuery(e).hasClass(value)) {
      +			e = e[relative];
      +		}
      +	} else {
      +		while(e && e[name] != value) {
      +			e = e[relative];
      +		}
      +	}
      +	return e;
      +}
      +
      +// Get the scroll position for window.scrollTo necessary to scroll a given element into view
      +function ensureVisible(e)
      +{
      +	var posTop = findPosY(e);
      +	var posBot = posTop + e.offsetHeight;
      +	var winTop = findScrollY();
      +	var winHeight = findWindowHeight();
      +	var winBot = winTop + winHeight;
      +	if(posTop < winTop) {
      +		return posTop;
      +	} else if(posBot > winBot) {
      +		if(e.offsetHeight < winHeight)
      +			return posTop - (winHeight - e.offsetHeight);
      +		else
      +			return posTop;
      +	} else {
      +		return winTop;
      +	}
      +}
      +
      +// Get the current width of the display window
      +function findWindowWidth()
      +{
      +	return window.innerWidth || document.documentElement.clientWidth;
      +}
      +
      +// Get the current height of the display window
      +function findWindowHeight()
      +{
      +	return window.innerHeight || document.documentElement.clientHeight;
      +}
      +
      +// Get the current horizontal page scroll position
      +function findScrollX()
      +{
      +	return window.scrollX || document.documentElement.scrollLeft;
      +}
      +
      +// Get the current vertical page scroll position
      +function findScrollY()
      +{
      +	return window.scrollY || document.documentElement.scrollTop;
      +}
      +
      +function findPosX(obj)
      +{
      +	var curleft = 0;
      +	while(obj.offsetParent) {
      +		curleft += obj.offsetLeft;
      +		obj = obj.offsetParent;
      +	}
      +	return curleft;
      +}
      +
      +function findPosY(obj)
      +{
      +	var curtop = 0;
      +	while(obj.offsetParent) {
      +		curtop += obj.offsetTop;
      +		obj = obj.offsetParent;
      +	}
      +	return curtop;
      +}
      +
      +// Blur a particular element
      +function blurElement(e)
      +{
      +	if(e && e.focus && e.blur) {
      +		e.focus();
      +		e.blur();
      +	}
      +}
      +
      +// Create a non-breaking space
      +function insertSpacer(place)
      +{
      +	var e = document.createTextNode(String.fromCharCode(160));
      +	if(place)
      +		place.appendChild(e);
      +	return e;
      +}
      +
      +// Replace the current selection of a textarea or text input and scroll it into view
      +function replaceSelection(e,text)
      +{
      +	if(e.setSelectionRange) {
      +		var oldpos = e.selectionStart;
      +		var isRange = e.selectionEnd > e.selectionStart;
      +		e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
      +		e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
      +		var linecount = e.value.split("\n").length;
      +		var thisline = e.value.substr(0,e.selectionStart).split("\n").length-1;
      +		e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
      +	} else if(document.selection) {
      +		var range = document.selection.createRange();
      +		if(range.parentElement() == e) {
      +			var isCollapsed = range.text == "";
      +			range.text = text;
      +			if(!isCollapsed) {
      +				range.moveStart("character", -text.length);
      +				range.select();
      +			}
      +		}
      +	}
      +}
      +
      +// Set the caret position in a text area
      +function setCaretPosition(e,pos)
      +{
      +	if(e.selectionStart || e.selectionStart == '0') {
      +		e.selectionStart = pos;
      +		e.selectionEnd = pos;
      +		e.focus();
      +	} else if(document.selection) {
      +		// IE support
      +		e.focus ();
      +		var sel = document.selection.createRange();
      +		sel.moveStart('character', -e.value.length);
      +		sel.moveStart('character',pos);
      +		sel.moveEnd('character',0);
      +		sel.select();
      +	}
      +}
      +
      +// Returns the text of the given (text) node, possibly merging subsequent text nodes
      +function getNodeText(e)
      +{
      +	var t = "";
      +	while(e && e.nodeName == "#text") {
      +		t += e.nodeValue;
      +		e = e.nextSibling;
      +	}
      +	return t;
      +}
      +
      +// Returns true if the element e has a given ancestor element
      +function isDescendant(e,ancestor)
      +{
      +	while(e) {
      +		if(e === ancestor)
      +			return true;
      +		e = e.parentNode;
      +	}
      +	return false;
      +}
      +
      +
      +// deprecate the following...
      +
      +// Prevent an event from bubbling
      +function stopEvent(e)
      +{
      +	var ev = e || window.event;
      +	ev.cancelBubble = true;
      +	if(ev.stopPropagation) ev.stopPropagation();
      +	return false;
      +}
      +
      +// Remove any event handlers or non-primitve custom attributes
      +function scrubNode(e)
      +{
      +	if(!config.browser.isIE)
      +		return;
      +	var att = e.attributes;
      +	if(att) {
      +		var t;
      +		for(t=0; t=0;i--) {
      +		if(!fso.FolderExists(scan[i])) {
      +			fso.CreateFolder(scan[i]);
      +		}
      +	}
      +	return true;
      +}
      +
      +// Returns null if it can't do it, false if there's an error, true if it saved OK
      +function ieSaveFile(filePath,content)
      +{
      +	ieCreatePath(filePath);
      +	try {
      +		var fso = new ActiveXObject("Scripting.FileSystemObject");
      +	} catch(ex) {
      +		//# alert("Exception while attempting to save\n\n" + ex.toString());
      +		return null;
      +	}
      +	var file = fso.OpenTextFile(filePath,2,-1,0);
      +	file.Write(content);
      +	file.Close();
      +	return true;
      +}
      +
      +// Returns null if it can't do it, false if there's an error, or a string of the content if successful
      +function ieLoadFile(filePath)
      +{
      +	try {
      +		var fso = new ActiveXObject("Scripting.FileSystemObject");
      +		var file = fso.OpenTextFile(filePath,1);
      +		var content = file.ReadAll();
      +		file.Close();
      +	} catch(ex) {
      +		//# alert("Exception while attempting to load\n\n" + ex.toString());
      +		return null;
      +	}
      +	return content;
      +}
      +
      +function ieCopyFile(dest,source)
      +{
      +	ieCreatePath(dest);
      +	try {
      +		var fso = new ActiveXObject("Scripting.FileSystemObject");
      +		fso.GetFile(source).Copy(dest);
      +	} catch(ex) {
      +		return false;
      +	}
      +	return true;
      +}
      +
      +// Returns null if it can't do it, false if there's an error, true if it saved OK
      +function mozillaSaveFile(filePath,content)
      +{
      +	if(window.Components) {
      +		try {
      +			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      +			var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
      +			file.initWithPath(filePath);
      +			if(!file.exists())
      +				file.create(0,0x01B4);// 0x01B4 = 0664
      +			var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
      +			out.init(file,0x22,0x04,null);
      +			out.write(content,content.length);
      +			out.flush();
      +			out.close();
      +			return true;
      +		} catch(ex) {
      +			//# alert("Exception while attempting to save\n\n" + ex);
      +			return false;
      +		}
      +	}
      +	return null;
      +}
      +
      +// Returns null if it can't do it, false if there's an error, or a string of the content if successful
      +function mozillaLoadFile(filePath)
      +{
      +	if(window.Components) {
      +		try {
      +			netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      +			var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
      +			file.initWithPath(filePath);
      +			if(!file.exists())
      +				return null;
      +			var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
      +			inputStream.init(file,0x01,0x04,null);
      +			var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
      +			sInputStream.init(inputStream);
      +			var contents = sInputStream.read(sInputStream.available());
      +			sInputStream.close();
      +			inputStream.close();
      +			return contents;
      +		} catch(ex) {
      +			//# alert("Exception while attempting to load\n\n" + ex);
      +			return false;
      +		}
      +	}
      +	return null;
      +}
      +
      +function javaUrlToFilename(url)
      +{
      +	var f = "//localhost";
      +	if(url.indexOf(f) == 0)
      +		return url.substring(f.length);
      +	var i = url.indexOf(":");
      +	return i > 0 ? url.substring(i-1) : url;
      +}
      +
      +function javaSaveFile(filePath,content)
      +{
      +	try {
      +		if(document.applets["TiddlySaver"])
      +			return document.applets["TiddlySaver"].saveFile(javaUrlToFilename(filePath),"UTF-8",content);
      +	} catch(ex) {
      +	}
      +	try {
      +		var s = new java.io.PrintStream(new java.io.FileOutputStream(javaUrlToFilename(filePath)));
      +		s.print(content);
      +		s.close();
      +	} catch(ex2) {
      +		return null;
      +	}
      +	return true;
      +}
      +
      +function javaLoadFile(filePath)
      +{
      +	try {
      +		if(document.applets["TiddlySaver"]) {
      +			var ret = document.applets["TiddlySaver"].loadFile(javaUrlToFilename(filePath),"UTF-8");
      +			if(!ret)
      +				return null;
      +			return String(ret);
      +		}
      +	} catch(ex) {
      +	}
      +	var content = [];
      +	try {
      +		var r = new java.io.BufferedReader(new java.io.FileReader(javaUrlToFilename(filePath)));
      +		var line;
      +		while((line = r.readLine()) != null)
      +			content.push(String(line));
      +		r.close();
      +	} catch(ex2) {
      +		return null;
      +	}
      +	return content.join("\n");
      +}
      +
      diff --git a/test/data/tiddlywiki/js/FileSystemUtils.js b/test/data/tiddlywiki/js/FileSystemUtils.js
      new file mode 100755
      index 000000000..605836a7e
      --- /dev/null
      +++ b/test/data/tiddlywiki/js/FileSystemUtils.js
      @@ -0,0 +1,103 @@
      +//--
      +//-- Filesystem utilities
      +//--
      +
      +function convertUTF8ToUnicode(u)
      +{
      +	return config.browser.isOpera || !window.netscape ? manualConvertUTF8ToUnicode(u) : mozConvertUTF8ToUnicode(u);
      +}
      +
      +//# UTF-8 encoding rules:
      +//# 0x0000 - 0x007F:  0xxxxxxx
      +//# 0x0080 - 0x07FF:  110xxxxx 10xxxxxx
      +//# 0x0800 - 0xFFFF:  1110xxxx 10xxxxxx 10xxxxxx
      +
      +function manualConvertUTF8ToUnicode(utf)
      +{
      +	var uni = utf;
      +	var src = 0;
      +	var dst = 0;
      +	var b1, b2, b3;
      +	var c;
      +	while(src < utf.length) {
      +		b1 = utf.charCodeAt(src++);
      +		if(b1 < 0x80) {
      +			dst++;
      +		} else if(b1 < 0xE0) {
      +			b2 = utf.charCodeAt(src++);
      +			c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
      +			uni = uni.substring(0,dst++).concat(c,utf.substr(src));
      +		} else {
      +			b2 = utf.charCodeAt(src++);
      +			b3 = utf.charCodeAt(src++);
      +			c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
      +			uni = uni.substring(0,dst++).concat(c,utf.substr(src));
      +		}
      +	}
      +	return uni;
      +}
      +
      +function mozConvertUTF8ToUnicode(u)
      +{
      +	try {
      +		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      +		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
      +		converter.charset = "UTF-8";
      +	} catch(ex) {
      +		return manualConvertUTF8ToUnicode(u);
      +	} // fallback
      +	var s = converter.ConvertToUnicode(u);
      +	var fin = converter.Finish();
      +	return fin.length > 0 ? s+fin : s;
      +}
      +
      +//# convert unicode string to a format suitable for saving to file
      +//# this should be UTF8, unless the browser does not support saving non-ASCII characters
      +function convertUnicodeToFileFormat(s)
      +{
      +	return config.browser.isOpera || !window.netscape ? (config.browser.isIE ? convertUnicodeToHtmlEntities(s) : s) : mozConvertUnicodeToUTF8(s);
      +}
      +
      +function convertUnicodeToHtmlEntities(s)
      +{
      +	var re = /[^\u0000-\u007F]/g;
      +	return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
      +}
      +
      +function convertUnicodeToUTF8(s)
      +{
      +// return convertUnicodeToFileFormat to allow plugin migration
      +	return convertUnicodeToFileFormat(s);
      +}
      +
      +function manualConvertUnicodeToUTF8(s)
      +{
      +	return unescape(encodeURIComponent(s));
      +}
      +
      +function mozConvertUnicodeToUTF8(s)
      +{
      +	try {
      +		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      +		var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
      +		converter.charset = "UTF-8";
      +	} catch(ex) {
      +		return manualConvertUnicodeToUTF8(s);
      +	} // fallback
      +	var u = converter.ConvertFromUnicode(s);
      +	var fin = converter.Finish();
      +	return fin.length > 0 ? u + fin : u;
      +}
      +
      +function convertUriToUTF8(uri,charSet)
      +{
      +	if(window.netscape == undefined || charSet == undefined || charSet == "")
      +		return uri;
      +	try {
      +		netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      +		var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
      +	} catch(ex) {
      +		return uri;
      +	}
      +	return converter.convertURISpecToUTF8(uri,charSet);
      +}
      diff --git a/test/data/tiddlywiki/js/Filters.js b/test/data/tiddlywiki/js/Filters.js
      new file mode 100755
      index 000000000..ad285d363
      --- /dev/null
      +++ b/test/data/tiddlywiki/js/Filters.js
      @@ -0,0 +1,61 @@
      +//--
      +//-- Filter a list of tiddlers
      +//--
      +
      +//# Extensible filter functions
      +config.filters = {
      +	tiddler: function(results,match) {
      +		var title = match[1]||match[4];
      +		var tiddler = this.fetchTiddler(title);
      +		if(tiddler) {
      +			results.pushUnique(tiddler);
      +		} else if(this.isShadowTiddler(title)) {
      +			tiddler = new Tiddler();
      +			tiddler.set(title,this.getTiddlerText(title));
      +			results.pushUnique(tiddler);
      +		} else {
      +			results.pushUnique(new Tiddler(title));
      +		}
      +		return results;
      +	},
      +	tag: function(results,match) {
      +		var m,matched = this.getTaggedTiddlers(match[3]);
      +		for(m=0; m 1) {
      +						last.element.setAttribute("colspan",colSpanCount);
      +						last.element.setAttribute("colSpan",colSpanCount); // Needed for IE
      +						colSpanCount = 1;
      +					}
      +				}
      +				w.nextMatch = this.cellRegExp.lastIndex-1;
      +			} else if(cellMatch[1] == ">") {
      +				// Colspan
      +				colSpanCount++;
      +				w.nextMatch = this.cellRegExp.lastIndex-1;
      +			} else if(cellMatch[2]) {
      +				// End of row
      +				if(prevCell && colSpanCount > 1) {
      +					prevCell.setAttribute("colspan",colSpanCount);
      +					prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
      +				}
      +				w.nextMatch = this.cellRegExp.lastIndex;
      +				break;
      +			} else {
      +				// Cell
      +				w.nextMatch++;
      +				var styles = config.formatterHelpers.inlineCssHelper(w);
      +				var spaceLeft = false;
      +				var chr = w.source.substr(w.nextMatch,1);
      +				while(chr == " ") {
      +					spaceLeft = true;
      +					w.nextMatch++;
      +					chr = w.source.substr(w.nextMatch,1);
      +				}
      +				var cell;
      +				if(chr == "!") {
      +					cell = createTiddlyElement(e,"th");
      +					w.nextMatch++;
      +				} else {
      +					cell = createTiddlyElement(e,"td");
      +				}
      +				prevCell = cell;
      +				prevColumns[col] = {rowSpanCount:1,element:cell};
      +				if(colSpanCount > 1) {
      +					cell.setAttribute("colspan",colSpanCount);
      +					cell.setAttribute("colSpan",colSpanCount); // Needed for IE
      +					colSpanCount = 1;
      +				}
      +				config.formatterHelpers.applyCssHelper(cell,styles);
      +				w.subWikifyTerm(cell,this.cellTermRegExp);
      +				if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
      +					cell.align = spaceLeft ? "center" : "left";
      +				else if(spaceLeft)
      +					cell.align = "right";
      +				w.nextMatch--;
      +			}
      +			col++;
      +			this.cellRegExp.lastIndex = w.nextMatch;
      +			cellMatch = this.cellRegExp.exec(w.source);
      +		}
      +	}
      +},
      +
      +{
      +	name: "heading",
      +	match: "^!{1,6}",
      +	termRegExp: /(\n)/mg,
      +	handler: function(w)
      +	{
      +		w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
      +	}
      +},
      +
      +{
      +	name: "list",
      +	match: "^(?:[\\*#;:]+)",
      +	lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
      +	termRegExp: /(\n)/mg,
      +	handler: function(w)
      +	{
      +		var stack = [w.output];
      +		var currLevel = 0, currType = null;
      +		var listLevel, listType, itemType, baseType;
      +		w.nextMatch = w.matchStart;
      +		this.lookaheadRegExp.lastIndex = w.nextMatch;
      +		var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
      +		while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
      +			if(lookaheadMatch[1]) {
      +				listType = "ul";
      +				itemType = "li";
      +			} else if(lookaheadMatch[2]) {
      +				listType = "ol";
      +				itemType = "li";
      +			} else if(lookaheadMatch[3]) {
      +				listType = "dl";
      +				itemType = "dt";
      +			} else if(lookaheadMatch[4]) {
      +				listType = "dl";
      +				itemType = "dd";
      +			}
      +			if(!baseType)
      +				baseType = listType;
      +			listLevel = lookaheadMatch[0].length;
      +			w.nextMatch += lookaheadMatch[0].length;
      +			var t;
      +			if(listLevel > currLevel) {
      +				for(t=currLevel; tlistLevel; t--)
      +					stack.pop();
      +			} else if(listLevel == currLevel && listType != currType) {
      +				stack.pop();
      +				stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
      +			}
      +			currLevel = listLevel;
      +			currType = listType;
      +			var e = createTiddlyElement(stack[stack.length-1],itemType);
      +			w.subWikifyTerm(e,this.termRegExp);
      +			this.lookaheadRegExp.lastIndex = w.nextMatch;
      +			lookaheadMatch = this.lookaheadRegExp.exec(w.source);
      +		}
      +	}
      +},
      +
      +{
      +	name: "quoteByBlock",
      +	match: "^<<<\\n",
      +	termRegExp: /(^<<<(\n|$))/mg,
      +	element: "blockquote",
      +	handler: config.formatterHelpers.createElementAndWikify
      +},
      +
      +{
      +	name: "quoteByLine",
      +	match: "^>+",
      +	lookaheadRegExp: /^>+/mg,
      +	termRegExp: /(\n)/mg,
      +	element: "blockquote",
      +	handler: function(w)
      +	{
      +		var stack = [w.output];
      +		var currLevel = 0;
      +		var newLevel = w.matchLength;
      +		var t,matched;
      +		do {
      +			if(newLevel > currLevel) {
      +				for(t=currLevel; tnewLevel; t--)
      +					stack.pop();
      +			}
      +			currLevel = newLevel;
      +			w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
      +			createTiddlyElement(stack[stack.length-1],"br");
      +			this.lookaheadRegExp.lastIndex = w.nextMatch;
      +			var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
      +			matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
      +			if(matched) {
      +				newLevel = lookaheadMatch[0].length;
      +				w.nextMatch += lookaheadMatch[0].length;
      +			}
      +		} while(matched);
      +	}
      +},
      +
      +{
      +	name: "rule",
      +	match: "^----+$\\n?|
      \\n?", + handler: function(w) + { + createTiddlyElement(w.output,"hr"); + } +}, + +{ + name: "monospacedByLine", + match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|)\\n", + element: "pre", + handler: function(w) + { + switch(w.matchText) { + case "/*{{{*/\n": // CSS + this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\*\}\}\}\*\/$\n?)/mg; + break; + case "{{{\n": // monospaced block + this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\f*\}\}\}$\n?)/mg; + break; + case "//{{{\n": // plugin + this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\/\}\}\}$\n?)/mg; + break; + case "\n": //template + this.lookaheadRegExp = /\n*((?:^[^\n]*\n)+?)(\n*^\f*$\n?)/mg; + break; + default: + break; + } + config.formatterHelpers.enclosedTextHelper.call(this,w); + } +}, + +{ + name: "wikifyComment", + match: "^(?:/\\*\\*\\*|\n)/mg); + w.subWikifyTerm(w.output,termRegExp); + } +}, + +{ + name: "macro", + match: "<<", + lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg, + handler: function(w) + { + this.lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = this.lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) { + w.nextMatch = this.lookaheadRegExp.lastIndex; + invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler); + } + } +}, + +{ + name: "prettyLink", + match: "\\[\\[", + lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg, + handler: function(w) + { + this.lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = this.lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { + var e; + var text = lookaheadMatch[1]; + if(lookaheadMatch[3]) { + // Pretty bracketted link + var link = lookaheadMatch[3]; + e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ? + createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler); + } else { + // Simple bracketted link + e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler); + } + createTiddlyText(e,text); + w.nextMatch = this.lookaheadRegExp.lastIndex; + } + } +}, + +{ + name: "wikiLink", + match: config.textPrimitives.unWikiLink+"?"+config.textPrimitives.wikiLink, + handler: function(w) + { + if(w.matchText.substr(0,1) == config.textPrimitives.unWikiLink) { + w.outputText(w.output,w.matchStart+1,w.nextMatch); + return; + } + if(w.matchStart > 0) { + var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg"); + preRegExp.lastIndex = w.matchStart-1; + var preMatch = preRegExp.exec(w.source); + if(preMatch.index == w.matchStart-1) { + w.outputText(w.output,w.matchStart,w.nextMatch); + return; + } + } + if(w.autoLinkWikiWords || store.isShadowTiddler(w.matchText)) { + var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler); + w.outputText(link,w.matchStart,w.nextMatch); + } else { + w.outputText(w.output,w.matchStart,w.nextMatch); + } + } +}, + +{ + name: "urlLink", + match: config.textPrimitives.urlPattern, + handler: function(w) + { + w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch); + } +}, + +{ + name: "image", + match: "\\[[<>]?[Ii][Mm][Gg]\\[", + //# [<] sequence below is to avoid lessThan-questionMark sequence so TiddlyWikis can be included in PHP files + lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg, + handler: function(w) + { + this.lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = this.lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { + var e = w.output; + if(lookaheadMatch[5]) { + var link = lookaheadMatch[5]; + e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler); + jQuery(e).addClass("imageLink"); + } + var img = createTiddlyElement(e,"img"); + if(lookaheadMatch[1]) + img.align = "left"; + else if(lookaheadMatch[2]) + img.align = "right"; + if(lookaheadMatch[3]) { + img.title = lookaheadMatch[3]; + img.setAttribute("alt",lookaheadMatch[3]); + } + img.src = lookaheadMatch[4]; + w.nextMatch = this.lookaheadRegExp.lastIndex; + } + } +}, + +{ + name: "html", + match: "<[Hh][Tt][Mm][Ll]>", + lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg, + handler: function(w) + { + this.lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = this.lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { + createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1]; + w.nextMatch = this.lookaheadRegExp.lastIndex; + } + } +}, + +{ + name: "commentByBlock", + match: "/%", + lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg, + handler: function(w) + { + this.lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = this.lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) + w.nextMatch = this.lookaheadRegExp.lastIndex; + } +}, + +{ + name: "characterFormat", + match: "''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{", + handler: function(w) + { + switch(w.matchText) { + case "''": + w.subWikifyTerm(w.output.appendChild(document.createElement("strong")),/('')/mg); + break; + case "//": + w.subWikifyTerm(createTiddlyElement(w.output,"em"),/(\/\/)/mg); + break; + case "__": + w.subWikifyTerm(createTiddlyElement(w.output,"u"),/(__)/mg); + break; + case "^^": + w.subWikifyTerm(createTiddlyElement(w.output,"sup"),/(\^\^)/mg); + break; + case "~~": + w.subWikifyTerm(createTiddlyElement(w.output,"sub"),/(~~)/mg); + break; + case "--": + w.subWikifyTerm(createTiddlyElement(w.output,"strike"),/(--)/mg); + break; + case "{{{": + var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg; + lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { + createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]); + w.nextMatch = lookaheadRegExp.lastIndex; + } + break; + } + } +}, + +{ + name: "customFormat", + match: "@@|\\{\\{", + handler: function(w) + { + switch(w.matchText) { + case "@@": + var e = createTiddlyElement(w.output,"span"); + var styles = config.formatterHelpers.inlineCssHelper(w); + if(styles.length == 0) + e.className = "marked"; + else + config.formatterHelpers.applyCssHelper(e,styles); + w.subWikifyTerm(e,/(@@)/mg); + break; + case "{{": + var lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg; + lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = lookaheadRegExp.exec(w.source); + if(lookaheadMatch) { + w.nextMatch = lookaheadRegExp.lastIndex; + e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]); + w.subWikifyTerm(e,/(\}\}\})/mg); + } + break; + } + } +}, + +{ + name: "mdash", + match: "--", + handler: function(w) + { + createTiddlyElement(w.output,"span").innerHTML = "—"; + } +}, + +{ + name: "lineBreak", + match: "\\n|
      ", + handler: function(w) + { + createTiddlyElement(w.output,"br"); + } +}, + +{ + name: "rawText", + match: "\"{3}|", + lookaheadRegExp: /(?:\"{3}|)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg, + handler: function(w) + { + this.lookaheadRegExp.lastIndex = w.matchStart; + var lookaheadMatch = this.lookaheadRegExp.exec(w.source); + if(lookaheadMatch && lookaheadMatch.index == w.matchStart) { + createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]); + w.nextMatch = this.lookaheadRegExp.lastIndex; + } + } +}, + +{ + name: "htmlEntitiesEncoding", + match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)", + handler: function(w) + { + createTiddlyElement(w.output,"span").innerHTML = w.matchText; + } +} + +]; + diff --git a/test/data/tiddlywiki/js/FormatterHelpers.js b/test/data/tiddlywiki/js/FormatterHelpers.js new file mode 100755 index 000000000..b48b96429 --- /dev/null +++ b/test/data/tiddlywiki/js/FormatterHelpers.js @@ -0,0 +1,94 @@ +//-- +//-- Formatter helpers +//-- + +function Formatter(formatters) +{ + var n; + this.formatters = []; + var pattern = []; + for(n=0; n= 200 && xhr.status < 300) || + xhr.status === 304 || xhr.status === 1223; + } catch(e) {} + return false; + }; + + var options = { + type:type, + url:url, + processData:false, + data:data, + cache:!!allowCache, + beforeSend: function(xhr) { + var i; + for(i in headers) + xhr.setRequestHeader(i,headers[i]); + xhr.setRequestHeader("X-Requested-With", "TiddlyWiki " + formatVersion()); + } + }; + + if(callback) { + options.complete = function(xhr,textStatus) { + if(httpSuccess(xhr)) + callback(true,params,xhr.responseText,url,xhr); + else + callback(false,params,null,url,xhr); + }; + } + if(contentType) + options.contentType = contentType; + if(username) + options.username = username; + if(password) + options.password = password; + if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1) + window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); + return jQuery.ajax(options); +} diff --git a/test/data/tiddlywiki/js/Import.js b/test/data/tiddlywiki/js/Import.js new file mode 100755 index 000000000..005a57642 --- /dev/null +++ b/test/data/tiddlywiki/js/Import.js @@ -0,0 +1,390 @@ +//-- +//-- ImportTiddlers macro +//-- + +config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) +{ + if(readOnly) { + createTiddlyElement(place,"div",null,"marked",this.readOnlyWarning); + return; + } + var w = new Wizard(); + w.createWizard(place,this.wizardTitle); + this.restart(w); +}; + +config.macros.importTiddlers.onCancel = function(e) +{ + var wizard = new Wizard(this); + wizard.clear(); + config.macros.importTiddlers.restart(wizard); + return false; +}; + +config.macros.importTiddlers.onClose = function(e) +{ + backstage.hidePanel(); + return false; +}; + +config.macros.importTiddlers.restart = function(wizard) +{ + var me = config.macros.importTiddlers; + wizard.addStep(this.step1Title,this.step1Html); + var t,s = wizard.getElement("selTypes"); + for(t in config.adaptors) { + var e = createTiddlyElement(s,"option",null,null,config.adaptors[t].serverLabel || t); + e.value = t; + } + if(config.defaultAdaptor) + s.value = config.defaultAdaptor; + s = wizard.getElement("selFeeds"); + var feeds = this.getFeeds(); + for(t in feeds) { + e = createTiddlyElement(s,"option",null,null,t); + e.value = t; + } + wizard.setValue("feeds",feeds); + s.onchange = me.onFeedChange; + var fileInput = wizard.getElement("txtBrowse"); + fileInput.onchange = me.onBrowseChange; + fileInput.onkeyup = me.onBrowseChange; + wizard.setButtons([{caption: this.openLabel, tooltip: this.openPrompt, onClick: me.onOpen}]); + wizard.formElem.action = "javascript:;"; + wizard.formElem.onsubmit = function() { + if(!this.txtPath || this.txtPath.value.length) //# check for manually entered path in first step + this.lastChild.firstChild.onclick(); + }; +}; + +config.macros.importTiddlers.getFeeds = function() +{ + var feeds = {}; + var t,tagged = store.getTaggedTiddlers("systemServer","title"); + for(t=0; t 0) { + if(!confirm(me.confirmOverwriteText.format([overwrite.join(", ")]))) + return false; + } + wizard.addStep(me.step4Title.format([rowNames.length]),me.step4Html); + for(t=0; t>'}, + importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<>'}, + tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<>'}, + upgrade: {text: "upgrade", tooltip: "Upgrade TiddlyWiki core code", content: '<>'}, + plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<>'} +}); + +// Options that can be set in the options panel and/or cookies +merge(config.optionsDesc,{ + txtUserName: "Username for signing your edits", + chkRegExpSearch: "Enable regular expressions for searches", + chkCaseSensitiveSearch: "Case-sensitive searching", + chkIncrementalSearch: "Incremental key-by-key searching", + chkAnimate: "Enable animations", + chkSaveBackups: "Keep backup file when saving changes", + chkAutoSave: "Automatically save changes", + chkGenerateAnRssFeed: "Generate an RSS feed when saving changes", + chkSaveEmptyTemplate: "Generate an empty template when saving changes", + chkOpenInNewWindow: "Open external links in a new window", + chkToggleLinks: "Clicking on links to open tiddlers causes them to close", + chkHttpReadOnly: "Hide editing features when viewed over HTTP", + chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers", + chkConfirmDelete: "Require confirmation before deleting tiddlers", + chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields", + txtBackupFolder: "Name of folder to use for backups", + txtMaxEditRows: "Maximum number of rows in edit boxes", + txtTheme: "Name of the theme to use", + txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"}); + +merge(config.messages,{ + customConfigError: "Problems were encountered loading plugins. See PluginManager for details", + pluginError: "Error: %0", + pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag", + pluginForced: "Executed because forced via 'systemConfigForce' tag", + pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki", + nothingSelected: "Nothing is selected. You must select one or more items first", + savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#Download for details", + subtitleUnknown: "(unknown)", + undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist", + shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value", + tiddlerLinkTooltip: "%0 - %1, %2", + externalLinkTooltip: "External link to %0", + noTags: "There are no tagged tiddlers", + notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes", + cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed", + invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki", + backupSaved: "Backup saved", + backupFailed: "Failed to save backup file", + rssSaved: "RSS feed saved", + rssFailed: "Failed to save RSS feed file", + emptySaved: "Empty template saved", + emptyFailed: "Failed to save empty template file", + mainSaved: "Main TiddlyWiki file saved", + mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved", + macroError: "Error in macro <<%0>>", + macroErrorDetails: "Error while executing macro <<%0>>:\n%1", + missingMacro: "No such macro", + overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it", + unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard", + confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------", + saveInstructions: "SaveChanges", + unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'", + tiddlerSaveError: "Error when saving tiddler '%0'", + tiddlerLoadError: "Error when loading tiddler '%0'", + wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.", + invalidFieldName: "Invalid field name %0", + fieldCannotBeChanged: "Field '%0' cannot be changed", + loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'", + upgradeDone: "The upgrade to version %0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki", + invalidCookie: "Invalid cookie '%0'"}); + +merge(config.messages.messageClose,{ + text: "close", + tooltip: "close this message area"}); + +config.messages.backstage = { + open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"}, + close: {text: "close", tooltip: "Close the backstage area"}, + prompt: "backstage: ", + decal: { + edit: {text: "edit", tooltip: "Edit the tiddler '%0'"} + } +}; + +config.messages.listView = { + tiddlerTooltip: "Click for the full text of this tiddler", + previewUnavailable: "(preview not available)" +}; + +config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"]; +config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; +config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; +config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; +// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st" +config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th", + "th","th","th","th","th","th","th","th","th","th", + "st","nd","rd","th","th","th","th","th","th","th", + "st"]; +config.messages.dates.am = "am"; +config.messages.dates.pm = "pm"; + +merge(config.messages.tiddlerPopup,{ + }); + +merge(config.views.wikified.tag,{ + labelNoTags: "no tags", + labelTags: "tags: ", + openTag: "Open tag '%0'", + tooltip: "Show tiddlers tagged with '%0'", + openAllText: "Open all", + openAllTooltip: "Open all of these tiddlers", + popupNone: "No other tiddlers tagged with '%0'"}); + +merge(config.views.wikified,{ + defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it", + defaultModifier: "(missing)", + shadowModifier: "(built-in shadow tiddler)", + dateFormat: "DD MMM YYYY", + createdPrompt: "created"}); + +merge(config.views.editor,{ + tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing", + defaultText: "Type the text for '%0'"}); + +merge(config.views.editor.tagChooser,{ + text: "tags", + tooltip: "Choose existing tags to add to this tiddler", + popupNone: "There are no tags defined", + tagTooltip: "Add the tag '%0'"}); + +merge(config.messages,{ + sizeTemplates: + [ + {unit: 1024*1024*1024, template: "%0\u00a0GB"}, + {unit: 1024*1024, template: "%0\u00a0MB"}, + {unit: 1024, template: "%0\u00a0KB"}, + {unit: 1, template: "%0\u00a0B"} + ]}); + +merge(config.macros.search,{ + label: "search", + prompt: "Search this TiddlyWiki", + placeholder: "", + accessKey: "F", + successMsg: "%0 tiddlers found matching %1", + failureMsg: "No tiddlers found matching %0"}); + +merge(config.macros.tagging,{ + label: "tagging: ", + labelNotTag: "not tagging", + tooltip: "List of tiddlers tagged with '%0'"}); + +merge(config.macros.timeline,{ + dateFormat: "DD MMM YYYY"}); + +merge(config.macros.allTags,{ + tooltip: "Show tiddlers tagged with '%0'", + noTags: "There are no tagged tiddlers"}); + +config.macros.list.all.prompt = "All tiddlers in alphabetical order"; +config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined"; +config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers"; +config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents"; +config.macros.list.touched.prompt = "Tiddlers that have been modified locally"; + +merge(config.macros.closeAll,{ + label: "close all", + prompt: "Close all displayed tiddlers (except any that are being edited)"}); + +merge(config.macros.permaview,{ + label: "permaview", + prompt: "Link to an URL that retrieves all the currently displayed tiddlers"}); + +merge(config.macros.saveChanges,{ + label: "save changes", + prompt: "Save all tiddlers to create a new TiddlyWiki", + accessKey: "S"}); + +merge(config.macros.newTiddler,{ + label: "new tiddler", + prompt: "Create a new tiddler", + title: "New Tiddler", + accessKey: "N"}); + +merge(config.macros.newJournal,{ + label: "new journal", + prompt: "Create a new tiddler from the current date and time", + accessKey: "J"}); + +merge(config.macros.options,{ + wizardTitle: "Tweak advanced options", + step1Title: "These options are saved in cookies in your browser", + step1Html: "
      Show unknown options", + unknownDescription: "//(unknown)//", + listViewTemplate: { + columns: [ + {name: 'Option', field: 'option', title: "Option", type: 'String'}, + {name: 'Description', field: 'description', title: "Description", type: 'WikiText'}, + {name: 'Name', field: 'name', title: "Name", type: 'String'} + ], + rowClasses: [ + {className: 'lowlight', field: 'lowlight'} + ]} + }); + +merge(config.macros.plugins,{ + wizardTitle: "Manage plugins", + step1Title: "Currently loaded plugins", + step1Html: "", // DO NOT TRANSLATE + skippedText: "(This plugin has not been executed because it was added since startup)", + noPluginText: "There are no plugins installed", + confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0", + removeLabel: "remove systemConfig tag", + removePrompt: "Remove systemConfig tag", + deleteLabel: "delete", + deletePrompt: "Delete these tiddlers forever", + listViewTemplate: { + columns: [ + {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'}, + {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'}, + {name: 'Description', field: 'Description', title: "Description", type: 'String'}, + {name: 'Version', field: 'Version', title: "Version", type: 'String'}, + {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'}, + {name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'}, + {name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'}, + {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"}, + {name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'}, + {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"}, + {name: 'Log', field: 'log', title: "Log", type: 'StringList'} + ], + rowClasses: [ + {className: 'error', field: 'error'}, + {className: 'warning', field: 'warning'} + ]}, + listViewTemplateReadOnly: { + columns: [ + {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'}, + {name: 'Description', field: 'Description', title: "Description", type: 'String'}, + {name: 'Version', field: 'Version', title: "Version", type: 'String'}, + {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'}, + {name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"}, + {name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'}, + {name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"}, + {name: 'Log', field: 'log', title: "Log", type: 'StringList'} + ], + rowClasses: [ + {className: 'error', field: 'error'}, + {className: 'warning', field: 'warning'} + ]} + }); + +merge(config.macros.toolbar,{ + moreLabel: "more", + morePrompt: "Show additional commands", + lessLabel: "less", + lessPrompt: "Hide additional commands", + separator: "|" + }); + +merge(config.macros.refreshDisplay,{ + label: "refresh", + prompt: "Redraw the entire TiddlyWiki display" + }); + +merge(config.macros.importTiddlers,{ + readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL", + wizardTitle: "Import tiddlers from another file or server", + step1Title: "Step 1: Locate the server or TiddlyWiki file", + step1Html: "Specify the type of the server:
      Enter the URL or pathname here:
      ...or browse for a file:

      ...or select a pre-defined feed: ", + openLabel: "open", + openPrompt: "Open the connection to this file or server", + statusOpenHost: "Opening the host", + statusGetWorkspaceList: "Getting the list of available workspaces", + step2Title: "Step 2: Choose the workspace", + step2Html: "Enter a workspace name:
      ...or select a workspace: ", + cancelLabel: "cancel", + cancelPrompt: "Cancel this import", + statusOpenWorkspace: "Opening the workspace", + statusGetTiddlerList: "Getting the list of available tiddlers", + errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again", + errorGettingTiddlerListHttp404: "Error retrieving tiddlers from url, please ensure the url exists. Click Cancel to try again.", + errorGettingTiddlerListHttp: "Error retrieving tiddlers from url, please ensure this url exists and is CORS enabled", + errorGettingTiddlerListFile: "Error retrieving tiddlers from local file, please make sure the file is in the same directory as your TiddlyWiki. Click Cancel to try again.", + step3Title: "Step 3: Choose the tiddlers to import", + step3Html: "
      Keep these tiddlers linked to this server so that you can synchronise subsequent changes
      Save the details of this server in a 'systemServer' tiddler called: ", + importLabel: "import", + importPrompt: "Import these tiddlers", + confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0", + step4Title: "Step 4: Importing %0 tiddler(s)", + step4Html: "", // DO NOT TRANSLATE + doneLabel: "done", + donePrompt: "Close this wizard", + statusDoingImport: "Importing tiddlers", + statusDoneImport: "All tiddlers imported", + systemServerNamePattern: "%2 on %1", + systemServerNamePatternNoWorkspace: "%1", + confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged", + serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server", + serverSaveModifier: "(System)", + listViewTemplate: { + columns: [ + {name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'}, + {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'}, + {name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'}, + {name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'} + ], + rowClasses: [ + ]} + }); + +merge(config.macros.upgrade,{ + wizardTitle: "Upgrade TiddlyWiki core code", + step1Title: "Update or repair this TiddlyWiki to the latest release", + step1Html: "You are about to upgrade to the latest release of the TiddlyWiki core code (from %1). Your content will be preserved across the upgrade.

      Note that core upgrades have been known to interfere with older plugins. If you run into problems with the upgraded file, see http://www.tiddlywiki.org/wiki/CoreUpgrades", + errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally", + errorNotSaved: "You must save changes before you can perform an upgrade", + step2Title: "Confirm the upgrade details", + step2Html_downgrade: "You are about to downgrade to TiddlyWiki version %0 from %1.

      Downgrading to an earlier version of the core code is not recommended", + step2Html_restore: "This TiddlyWiki appears to be already using the latest version of the core code (%0).

      You can continue to upgrade anyway to ensure that the core code hasn't been corrupted or damaged", + step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %0 from %1", + upgradeLabel: "upgrade", + upgradePrompt: "Prepare for the upgrade process", + statusPreparingBackup: "Preparing backup", + statusSavingBackup: "Saving backup file", + errorSavingBackup: "There was a problem saving the backup file", + statusLoadingCore: "Loading core code", + errorLoadingCore: "Error loading the core code", + errorCoreFormat: "Error with the new core code", + statusSavingCore: "Saving the new core code", + statusReloadingCore: "Reloading the new core code", + startLabel: "start", + startPrompt: "Start the upgrade process", + cancelLabel: "cancel", + cancelPrompt: "Cancel the upgrade process", + step3Title: "Upgrade cancelled", + step3Html: "You have cancelled the upgrade process" + }); + +merge(config.macros.sync,{ + listViewTemplate: { + columns: [ + {name: 'Selected', field: 'selected', rowName: 'title', type: 'Selector'}, + {name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'}, + {name: 'Server Type', field: 'serverType', title: "Server type", type: 'String'}, + {name: 'Server Host', field: 'serverHost', title: "Server host", type: 'String'}, + {name: 'Server Workspace', field: 'serverWorkspace', title: "Server workspace", type: 'String'}, + {name: 'Status', field: 'status', title: "Synchronisation status", type: 'String'}, + {name: 'Server URL', field: 'serverUrl', title: "Server URL", text: "View", type: 'Link'} + ], + rowClasses: [ + ], + buttons: [ + {caption: "Sync these tiddlers", name: 'sync'} + ]}, + wizardTitle: "Synchronize with external servers and files", + step1Title: "Choose the tiddlers you want to synchronize", + step1Html: "", // DO NOT TRANSLATE + syncLabel: "sync", + syncPrompt: "Sync these tiddlers", + hasChanged: "Changed while unplugged", + hasNotChanged: "Unchanged while unplugged", + syncStatusList: { + none: {text: "...", display:'none', className:'notChanged'}, + changedServer: {text: "Changed on server", display:null, className:'changedServer'}, + changedLocally: {text: "Changed while unplugged", display:null, className:'changedLocally'}, + changedBoth: {text: "Changed while unplugged and on server", display:null, className:'changedBoth'}, + notFound: {text: "Not found on server", display:null, className:'notFound'}, + putToServer: {text: "Saved update on server", display:null, className:'putToServer'}, + gotFromServer: {text: "Retrieved update from server", display:null, className:'gotFromServer'} + } + }); + +merge(config.macros.annotations,{ + }); + +merge(config.commands.closeTiddler,{ + text: "close", + tooltip: "Close this tiddler"}); + +merge(config.commands.closeOthers,{ + text: "close others", + tooltip: "Close all other tiddlers"}); + +merge(config.commands.editTiddler,{ + text: "edit", + tooltip: "Edit this tiddler", + readOnlyText: "view", + readOnlyTooltip: "View the source of this tiddler"}); + +merge(config.commands.saveTiddler,{ + text: "done", + tooltip: "Save changes to this tiddler"}); + +merge(config.commands.cancelTiddler,{ + text: "cancel", + tooltip: "Undo changes to this tiddler", + warning: "Are you sure you want to abandon your changes to '%0'?", + readOnlyText: "done", + readOnlyTooltip: "View this tiddler normally"}); + +merge(config.commands.deleteTiddler,{ + text: "delete", + tooltip: "Delete this tiddler", + warning: "Are you sure you want to delete '%0'?"}); + +merge(config.commands.permalink,{ + text: "permalink", + tooltip: "Permalink for this tiddler"}); + +merge(config.commands.references,{ + text: "references", + tooltip: "Show tiddlers that link to this one", + popupNone: "No references"}); + +merge(config.commands.jump,{ + text: "jump", + tooltip: "Jump to another open tiddler"}); + +merge(config.commands.syncing,{ + text: "syncing", + tooltip: "Control synchronisation of this tiddler with a server or external file", + currentlySyncing: "
      Currently syncing via '%0' to:
      host: %1
      workspace: %2", // Note escaping of closing
      tag + notCurrentlySyncing: "Not currently syncing", + captionUnSync: "Stop synchronising this tiddler", + chooseServer: "Synchronise this tiddler with another server:", + currServerMarker: "\u25cf ", + notCurrServerMarker: " "}); + +merge(config.commands.fields,{ + text: "fields", + tooltip: "Show the extended fields of this tiddler", + emptyText: "There are no extended fields for this tiddler", + listViewTemplate: { + columns: [ + {name: 'Field', field: 'field', title: "Field", type: 'String'}, + {name: 'Value', field: 'value', title: "Value", type: 'String'} + ], + rowClasses: [ + ], + buttons: [ + ]}}); + +merge(config.shadowTiddlers,{ + DefaultTiddlers: "[[GettingStarted]]", + MainMenu: "[[GettingStarted]]", + SiteTitle: "My TiddlyWiki", + SiteSubtitle: "a reusable non-linear personal web notebook", + SiteUrl: "", + SideBarOptions: '<><><><><><><>', + SideBarTabs: '<>', + TabMore: '<>' + }); + +merge(config.annotations,{ + AdvancedOptions: "This shadow tiddler provides access to several advanced options", + ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface", + DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up", + EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited", + GettingStarted: "This shadow tiddler provides basic usage instructions", + ImportTiddlers: "This shadow tiddler provides access to importing tiddlers", + MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen", + MarkupPreHead: "This tiddler is inserted at the top of the section of the TiddlyWiki HTML file", + MarkupPostHead: "This tiddler is inserted at the bottom of the section of the TiddlyWiki HTML file", + MarkupPreBody: "This tiddler is inserted at the top of the section of the TiddlyWiki HTML file", + MarkupPostBody: "This tiddler is inserted at the end of the section of the TiddlyWiki HTML file immediately after the script block", + OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar", + PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout", + PluginManager: "This shadow tiddler provides access to the plugin manager", + SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar", + SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar", + SiteSubtitle: "This shadow tiddler is used as the second part of the page title", + SiteTitle: "This shadow tiddler is used as the first part of the page title", + SiteUrl: "This shadow tiddler should be set to the full target URL for publication", + StyleSheetColors: "This shadow tiddler contains CSS definitions related to the color of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler", + StyleSheet: "This tiddler can contain custom CSS definitions", + StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler", + StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale", + StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing", + SystemSettings: "This tiddler is used to store configuration options for this TiddlyWiki document", + TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar", + TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar", + TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar", + TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar", + TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar", + TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar", + TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar", + ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars", + ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look" + }); diff --git a/test/data/tiddlywiki/js/ListView.js b/test/data/tiddlywiki/js/ListView.js new file mode 100755 index 000000000..f48950543 --- /dev/null +++ b/test/data/tiddlywiki/js/ListView.js @@ -0,0 +1,275 @@ +//-- +//-- ListView gadget +//-- + +var ListView = {}; + +// Create a listview +//# place - where in the DOM tree to insert the listview +//# listObject - array of objects to be included in the listview +//# listTemplate - template for the listview +//# callback - callback for a command being selected +//# className - optional classname for the element +ListView.create = function(place,listObject,listTemplate,callback,className) +{ + var table = createTiddlyElement(place,"table",null,className || "listView twtable"); + var thead = createTiddlyElement(table,"thead"); + var t,r = createTiddlyElement(thead,"tr"); + for(t=0; t element of listView +//# callback(checkboxElement,rowName) +//# where +//# checkboxElement - DOM element of checkbox +//# rowName - name of this row as assigned by the column template +//# result: true if at least one selector was checked +ListView.forEachSelector = function(view,callback) +{ + var checkboxes = view.getElementsByTagName("input"); + var t,hadOne = false; + for(t=0; t>) + window.tiddler = tiddlerElem ? store.getTiddler(tiddlerElem.getAttribute("tiddler")) : null; + window.place = place; + var allowEval = true; + if(config.evaluateMacroParameters=="system") { + if(!tiddler || tiddler.tags.indexOf("systemAllowEval") == -1) { + allowEval = false; + } + } + m.handler(place,macro,m.noPreParse?null:params.readMacroParams(!allowEval),wikifier,params,tiddler); + } else { + createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro])); + } + } catch(ex) { + createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()])); + } +} + +config.macros.version.handler = function(place) +{ + jQuery("").text(formatVersion()).appendTo(place); +}; + +config.macros.today.handler = function(place,macroName,params) +{ + var now = new Date(); + var text = params[0] ? now.formatString(params[0].trim()) : now.toLocaleString(); + jQuery("").text(text).appendTo(place); +}; + +config.macros.list.template = "<>"; +config.macros.list.handler = function(place,macroName,params,wikifier,paramString) +{ + var list = document.createElement("ul"); + jQuery(list).attr({ refresh: "macro", macroName: macroName }).data("params", paramString); + place.appendChild(list); + this.refresh(list); +}; + +config.macros.list.refresh = function(list) { + var paramString = jQuery(list).data("params"); + var params = paramString.readMacroParams(); + var args = paramString.parseParams("anon", null, null)[0]; + var type = args.anon ? args.anon[0] : "all"; + jQuery(list).empty().addClass("list list-" + type); + var template = args.template ? store.getTiddlerText(args.template[0]) : false; + if(!template) { + template = config.macros.list.template; + } + if(this[type].prompt) + createTiddlyElement(list,"li",null,"listTitle",this[type].prompt); + var results; + if(this[type].handler) + results = this[type].handler(params); + var t; + for(t = 0; t < results.length; t++) { + var li = document.createElement("li"); + list.appendChild(li); + var tiddler = results[t]; + if(typeof(tiddler) == 'string') { // deal with missing etc.. + tiddler = store.getTiddler(tiddler) || new Tiddler(tiddler); + } + wikify(template, li, null, tiddler); + } + if(results.length === 0 && args.emptyMessage) { + jQuery(list).addClass("emptyList"); + jQuery("
    1. ").text(args.emptyMessage[0]).appendTo(list); + } +}; + +config.macros.list.all.handler = function(params) +{ + return store.reverseLookup("tags","excludeLists",false,"title"); +}; + +config.macros.list.missing.handler = function(params) +{ + return store.getMissingLinks(); +}; + +config.macros.list.orphans.handler = function(params) +{ + return store.getOrphans(); +}; + +config.macros.list.shadowed.handler = function(params) +{ + return store.getShadowed(); +}; + +config.macros.list.touched.handler = function(params) +{ + return store.getTouched(); +}; + +config.macros.list.filter.handler = function(params) +{ + var filter = params[1]; + var results = []; + if(filter) { + var tiddlers = store.filterTiddlers(filter); + var t; + for(t=0; t").attr("params", paramString). + attr("macroName", macroName).appendTo(place)[0]; + macro.refresh(container); + }, + refresh: function(container) { + jQuery(container).attr("refresh", "macro").empty(); + var paramString = jQuery(container).attr("params"); + var args = paramString.parseParams("anon", null, null)[0]; + var params = args.anon || []; + + var field = params[0] || "modified"; + var dateFormat = params[2] || this.dateFormat; + var groupTemplate = macro.groupTemplate.format(field, dateFormat); + groupTemplate = args.groupTemplate ? store.getTiddlerText(args.groupTemplate[0]) || groupTemplate : + groupTemplate; + + var itemTemplate = macro.itemTemplate; + itemTemplate = args.template ? store.getTiddlerText(args.template[0]) || itemTemplate : + itemTemplate; + + var tiddlers = args.filter ? store.sortTiddlers(store.filterTiddlers(args.filter[0]), field) : + store.reverseLookup("tags", "excludeLists", false, field); + var lastGroup = "", ul; + var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1],10)) : 0; + var t; + for(t=tiddlers.length-1; t>=last; t--) { + var tiddler = tiddlers[t]; + var theGroup = wikifyPlainText(groupTemplate,0,tiddler); + if(typeof(ul) == "undefined" || theGroup != lastGroup) { + ul = document.createElement("ul"); + jQuery(ul).addClass("timeline"); + container.appendChild(ul); + createTiddlyElement(ul,"li",null,"listTitle",theGroup); + lastGroup = theGroup; + } + var item = createTiddlyElement(ul,"li",null,"listLink"); + wikify(itemTemplate,item,null,tiddler); + } + }, + groupTemplate: "<>", + itemTemplate: "<>" +}); + +config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) +{ + var allowEval = true; + var stack = config.macros.tiddler.tiddlerStack; + if(stack.length > 0 && config.evaluateMacroParameters == "system") { + // included tiddler and "system" evaluation required, so check tiddler tagged appropriately + var title = stack[stack.length-1]; + var pos = title.indexOf(config.textPrimitives.sectionSeparator); + if(pos != -1) { + title = title.substr(0,pos); // get the base tiddler title + } + var t = store.getTiddler(title); + if(!t || t.tags.indexOf("systemAllowEval") == -1) { + allowEval = false; + } + } + params = paramString.parseParams("name",null,allowEval,false,true); + var names = params[0]["name"]; + var tiddlerName = names[0]; + var className = names[1] || null; + var args = params[0]["with"]; + var wrapper = createTiddlyElement(place,"span",null,className,null,{ + refresh: "content", tiddler: tiddlerName + }); + if(args!==undefined) + wrapper.setAttribute("args","[["+args.join("]] [[")+"]]"); + this.transclude(wrapper,tiddlerName,args); +}; + +config.macros.tiddler.transclude = function(wrapper,tiddlerName,args) +{ + var text = store.getTiddlerText(tiddlerName); + if(!text) + return; + var stack = config.macros.tiddler.tiddlerStack; + if(stack.indexOf(tiddlerName) !== -1) + return; + stack.push(tiddlerName); + try { + if(typeof args == "string") + args = args.readBracketedList(); + var n = args ? Math.min(args.length,9) : 0; + var i; + for(i=0; i> +config.macros.gradient.handler = function(place,macroName,params,wikifier,paramString,tiddler) +{ + var panel = wikifier ? createTiddlyElement(place,"div",null,"gradient") : place; + panel.style.position = "relative"; + panel.style.overflow = "hidden"; + panel.style.zIndex = "0"; + if(wikifier) { + var styles = config.formatterHelpers.inlineCssHelper(wikifier); + config.formatterHelpers.applyCssHelper(panel,styles); + } + params = paramString.parseParams("color"); + var locolors = [], hicolors = []; + var t; + for(t=2; t>"); + if(document.all) { + panel.style.height = "100%"; + panel.style.width = "100%"; + } +}; + +config.macros.message.handler = function(place,macroName,params) +{ + if(params[0]) { + var names = params[0].split("."); + var lookupMessage = function(root,nameIndex) { + if(root[names[nameIndex]]) { + if(nameIndex < names.length-1) + return (lookupMessage(root[names[nameIndex]],nameIndex+1)); + else + return root[names[nameIndex]]; + } else + return null; + }; + var m = lookupMessage(config,0); + if(m == null) + m = lookupMessage(window,0); + createTiddlyText(place,m.toString().format(params.splice(1))); + } +}; + + +config.macros.view.depth = 0; +config.macros.view.values = []; +config.macros.view.views = { + text: function(value,place,params,wikifier,paramString,tiddler) { + highlightify(value,place,highlightHack,tiddler); + }, + link: function(value,place,params,wikifier,paramString,tiddler) { + createTiddlyLink(place,value,true); + }, + wikified: function(value,place,params,wikifier,paramString,tiddler) { + if(config.macros.view.depth>50) + return; + if(config.macros.view.depth>0) { + if (value==config.macros.view.values[config.macros.view.depth-1]) { + return; + } + } + config.macros.view.values[config.macros.view.depth] = value; + config.macros.view.depth++; + if(params[2]) + value=params[2].unescapeLineBreaks().format([value]); + wikify(value,place,highlightHack,tiddler); + config.macros.view.depth--; + config.macros.view.values[config.macros.view.depth] = null; + }, + date: function(value,place,params,wikifier,paramString,tiddler) { + value = Date.convertFromYYYYMMDDHHMM(value); + createTiddlyText(place,value.formatString(params[2] || config.views.wikified.dateFormat)); + } +}; + +config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler) +{ + if((tiddler instanceof Tiddler) && params[0]) { + var value = store.getValue(tiddler,params[0]); + if(value) { + var type = params[1] || config.macros.view.defaultView; + var handler = config.macros.view.views[type]; + if(handler) + handler(value,place,params,wikifier,paramString,tiddler); + } + } +}; + +config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler) +{ + var field = params[0]; + var rows = params[1] || 0; + var defVal = params[2] || ''; + if((tiddler instanceof Tiddler) && field) { + story.setDirty(tiddler.title,true); + var e,v; + if(field != "text" && !rows) { + e = createTiddlyElement(null,"input",null,null,null,{ + type: "text", edit: field, size: "40", autocomplete: "off" + }); + e.value = store.getValue(tiddler,field) || defVal; + place.appendChild(e); + } else { + var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix"); + var wrapper2 = createTiddlyElement(wrapper1,"div"); + e = createTiddlyElement(wrapper2,"textarea"); + e.value = v = store.getValue(tiddler,field) || defVal; + rows = rows || 10; + var lines = v.match(/\n/mg); + var maxLines = Math.max(parseInt(config.options.txtMaxEditRows,10),5); + if(lines != null && lines.length > rows) + rows = lines.length + 5; + rows = Math.min(rows,maxLines); + e.setAttribute("rows",rows); + e.setAttribute("edit",field); + place.appendChild(wrapper1); + } + if(tiddler.isReadOnly()) { + e.setAttribute("readOnly","readOnly"); + jQuery(e).addClass("readOnly"); + } + return e; + } +}; + +config.macros.tagChooser.onClick = function(ev) +{ + var e = ev || window.event; + var lingo = config.views.editor.tagChooser; + var popup = Popup.create(this); + var tags = store.getTags(this.getAttribute("tags")); + if(tags.length == 0) + jQuery("
    2. ").text(lingo.popupNone).appendTo(popup); + var t; + for(t=0; t= this.endTime) { + this.stop(); + return false; + } + return true; +}; + diff --git a/test/data/tiddlywiki/js/NewTiddler.js b/test/data/tiddlywiki/js/NewTiddler.js new file mode 100755 index 000000000..0fe8584e2 --- /dev/null +++ b/test/data/tiddlywiki/js/NewTiddler.js @@ -0,0 +1,81 @@ +//-- +//-- NewTiddler and NewJournal macros +//-- + +config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal) +{ + var tags = []; + var t; + for(t=1; t 0) + btn.setAttribute("params",tags.join("|")); + btn.setAttribute("newFocus",newFocus); + btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE)); + if(customFields !== "") + btn.setAttribute("customFields",customFields); + var text = getParam(params,"text"); + if(text !== undefined) + btn.setAttribute("newText",text); + return btn; +}; + +config.macros.newTiddler.onClickNewTiddler = function() +{ + var title = this.getAttribute("newTitle"); + if(this.getAttribute("isJournal") == "true") { + title = new Date().formatString(title.trim()); + } + var params = this.getAttribute("params"); + var tags = params ? params.split("|") : []; + var focus = this.getAttribute("newFocus"); + var template = this.getAttribute("newTemplate"); + var customFields = this.getAttribute("customFields"); + if(!customFields && !store.isShadowTiddler(title)) + customFields = String.encodeHashMap(config.defaultCustomFields); + story.displayTiddler(null,title,template,false,null,null); + var tiddlerElem = story.getTiddler(title); + if(customFields) + story.addCustomFields(tiddlerElem,customFields); + var text = this.getAttribute("newText"); + if(typeof text == "string" && story.getTiddlerField(title,"text")) + story.getTiddlerField(title,"text").value = text.format([title]); + var t; + for(t=0;t max) + c = max; + return Number(c); +}; + diff --git a/test/data/tiddlywiki/js/Options.js b/test/data/tiddlywiki/js/Options.js new file mode 100755 index 000000000..f2903a520 --- /dev/null +++ b/test/data/tiddlywiki/js/Options.js @@ -0,0 +1,312 @@ +//-- +//-- Option handling +//-- + +config.optionHandlers = { + 'txt': { + get: function(name) {return encodeCookie(config.options[name].toString());}, + set: function(name,value) {config.options[name] = decodeCookie(value);} + }, + 'chk': { + get: function(name) {return config.options[name] ? 'true' : 'false';}, + set: function(name,value) {config.options[name] = value == 'true';} + } +}; + +function setOption(name,value) +{ + var optType = name.substr(0,3); + if(config.optionHandlers[optType] && config.optionHandlers[optType].set) + config.optionHandlers[optType].set(name,value); +} + +// Gets the value of an option as a string. Most code should just read from config.options.* directly +function getOption(name) +{ + var optType = name.substr(0,3); + return config.optionHandlers[optType] && config.optionHandlers[optType].get ? config.optionHandlers[optType].get(name) : null; +} + +//# Loads config.options from cookies and SystemSettings +function loadOptions() +{ + if(safeMode) + return; + loadCookies(); + loadSystemSettings(); +} +// @Deprecated; retained for backwards compatibility +var loadOptionsCookie = loadOptions; + +function getCookies() +{ + var cookieList = document.cookie.split(';'); + var i,cookies = {}; + for(i=0; i= 0 && !valign && !halign) { + offset.x = offset.x + root.offsetWidth; + } else { + offset.x = (halign == "right") ? offset.x + root.offsetWidth : offset.x; + offset.y = (valign == "top") ? offset.y : offset.y + root.offsetHeight; + } + var rootLeft = findPosX(root); + var rootTop = findPosY(root); + var popupLeft = rootLeft + offset.x; + var popupTop = rootTop + offset.y; + var winWidth = findWindowWidth(); + if(popup.offsetWidth > winWidth*0.75) + popup.style.width = winWidth*0.75 + "px"; + var popupWidth = popup.offsetWidth; + var scrollWidth = winWidth - document.body.offsetWidth; + if(popupLeft + popupWidth > winWidth - scrollWidth - 1) { + if(halign == "right") + popupLeft = popupLeft - root.offsetWidth - popupWidth; + else + popupLeft = winWidth - popupWidth - scrollWidth - 1; + } + popup.style.left = popupLeft + "px"; + popup.style.top = popupTop + "px"; + popup.style.display = "block"; +}; + +Popup.find = function(e) +{ + var t,pos = -1; + for(t=this.stack.length-1; t>=0; t--) { + if(isDescendant(e,this.stack[t].popup)) + pos = t; + } + return pos; +}; + +Popup.remove = function(pos) +{ + if(!pos) pos = 0; + if(Popup.stack.length > pos) { + Popup.removeFrom(pos); + } +}; + +Popup.removeFrom = function(from) +{ + var t; + for(t=Popup.stack.length-1; t>=from; t--) { + var p = Popup.stack[t]; + jQuery(p.root).removeClass("highlight"); + jQuery(p.popup).remove(); + } + Popup.stack = Popup.stack.slice(0,from); +}; + diff --git a/test/data/tiddlywiki/js/RGB.js b/test/data/tiddlywiki/js/RGB.js new file mode 100755 index 000000000..672b84d20 --- /dev/null +++ b/test/data/tiddlywiki/js/RGB.js @@ -0,0 +1,59 @@ +//-- +//-- RGB colour object +//-- + +// Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values +function RGB(r,g,b) +{ + this.r = 0; + this.g = 0; + this.b = 0; + if(typeof r == "string") { + if(r.substr(0,1) == "#") { + if(r.length == 7) { + this.r = parseInt(r.substr(1,2),16)/255; + this.g = parseInt(r.substr(3,2),16)/255; + this.b = parseInt(r.substr(5,2),16)/255; + } else { + this.r = parseInt(r.substr(1,1),16)/15; + this.g = parseInt(r.substr(2,1),16)/15; + this.b = parseInt(r.substr(3,1),16)/15; + } + } else { + var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/; + var c = r.match(rgbPattern); + if(c) { + this.r = parseInt(c[1],10)/255; + this.g = parseInt(c[2],10)/255; + this.b = parseInt(c[3],10)/255; + } + } + } else { + this.r = r; + this.g = g; + this.b = b; + } + return this; +} + +// Mixes this colour with another in a specified proportion +// c = other colour to mix +// f = 0..1 where 0 is this colour and 1 is the new colour +// Returns an RGB object +RGB.prototype.mix = function(c,f) +{ + return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f); +}; + +// Return an rgb colour as a #rrggbb format hex string +RGB.prototype.toString = function() +{ + var clamp = function(x,min,max) { + return x < min ? min : (x > max ? max : x); + }; + return "#" + + ("0" + Math.floor(clamp(this.r,0,1) * 255).toString(16)).right(2) + + ("0" + Math.floor(clamp(this.g,0,1) * 255).toString(16)).right(2) + + ("0" + Math.floor(clamp(this.b,0,1) * 255).toString(16)).right(2); +}; + diff --git a/test/data/tiddlywiki/js/Refresh.js b/test/data/tiddlywiki/js/Refresh.js new file mode 100755 index 000000000..5857aa16f --- /dev/null +++ b/test/data/tiddlywiki/js/Refresh.js @@ -0,0 +1,192 @@ +//-- +//-- Refresh mechanism +//-- + +//# List of notification functions to be called when certain tiddlers are changed or deleted +config.notifyTiddlers = [ + {name: "SystemSettings", notify: onSystemSettingsChange}, + {name: "StyleSheetLayout", notify: refreshStyles}, + {name: "StyleSheetColors", notify: refreshStyles}, + {name: "StyleSheet", notify: refreshStyles}, + {name: "StyleSheetPrint", notify: refreshStyles}, + {name: "PageTemplate", notify: refreshPageTemplate}, + {name: "SiteTitle", notify: refreshPageTitle}, + {name: "SiteSubtitle", notify: refreshPageTitle}, + {name: "WindowTitle", notify: refreshPageTitle}, + {name: "ColorPalette", notify: refreshColorPalette}, + {name: null, notify: refreshDisplay} +]; + +//# refresher functions +config.refreshers = { + link: function(e,changeList) + { + var title = e.getAttribute("tiddlyLink"); + refreshTiddlyLink(e,title); + return true; + }, + + tiddler: function(e,changeList) + { + var title = e.getAttribute("tiddler"); + var template = e.getAttribute("template"); + if(changeList && (changeList.indexOf && changeList.indexOf(title) != -1) && !story.isDirty(title)) + story.refreshTiddler(title,template,true); + else + refreshElements(e,changeList); + return true; + }, + + content: function(e,changeList) + { + var title = e.getAttribute("tiddler"); + var force = e.getAttribute("force"); + var args = e.getAttribute("args"); + if(force != null || changeList == null || (changeList.indexOf && changeList.indexOf(title) != -1)) { + jQuery(e).empty(); + config.macros.tiddler.transclude(e,title,args); + return true; + } else + return false; + }, + + macro: function(e,changeList) + { + var macro = e.getAttribute("macroName"); + var params = e.getAttribute("params"); + if(macro) + macro = config.macros[macro]; + if(macro && macro.refresh) + macro.refresh(e,params); + return true; + } +}; + +config.refresherData = { + styleSheet: "StyleSheet", + defaultStyleSheet: "StyleSheet", + pageTemplate: "PageTemplate", + defaultPageTemplate: "PageTemplate", + colorPalette: "ColorPalette", + defaultColorPalette: "ColorPalette" +}; + +function refreshElements(root,changeList) +{ + var c,nodes = root.childNodes; + for(c=0; c").appendTo("body").hide()[0]; + var display = story.getContainer(); + var nodes,t; + if(display) { + nodes = display.childNodes; + for(t=nodes.length-1; t>=0; t--) + stash.appendChild(nodes[t]); + } + var wrapper = document.getElementById("contentWrapper"); + + var isAvailable = function(title) { + var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1; + if(s!=-1) + title = title.substr(0,s); + return store.tiddlerExists(title) || store.isShadowTiddler(title); + }; + //# protect against non-existent pageTemplate + if(!title || !isAvailable(title)) + title = config.refresherData.pageTemplate; + if(!isAvailable(title)) + title = config.refresherData.defaultPageTemplate; //# this one is always avaialable + wrapper.innerHTML = store.getRecursiveTiddlerText(title,null,10); + applyHtmlMacros(wrapper); + refreshElements(wrapper); + display = story.getContainer(); + jQuery(display).empty(); + if(!display) + display = createTiddlyElement(wrapper,"div",story.containerId()); + nodes = stash.childNodes; + for(t=nodes.length-1; t>=0; t--) + display.appendChild(nodes[t]); + jQuery(stash).remove(); +} + +function refreshDisplay(hint) +{ + if(typeof hint == "string") + hint = [hint]; + var e = document.getElementById("contentWrapper"); + refreshElements(e,hint); + if(backstage.isPanelVisible()) { + e = document.getElementById("backstage"); + refreshElements(e,hint); + } +} + +function refreshPageTitle() +{ + document.title = getPageTitle(); +} + +function getPageTitle() +{ + return wikifyPlainText(store.getTiddlerText("WindowTitle",""),null,tiddler); +} + +function refreshStyles(title,doc) +{ + setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc || document); +} + +function refreshColorPalette(title) +{ + if(!startingUp) + refreshAll(); +} + +function refreshAll() +{ + refreshPageTemplate(); + refreshDisplay(); + refreshStyles("StyleSheetLayout"); + refreshStyles("StyleSheetColors"); + refreshStyles(config.refresherData.styleSheet); + refreshStyles("StyleSheetPrint"); +} + diff --git a/test/data/tiddlywiki/js/Saving.js b/test/data/tiddlywiki/js/Saving.js new file mode 100755 index 000000000..1051c99ef --- /dev/null +++ b/test/data/tiddlywiki/js/Saving.js @@ -0,0 +1,248 @@ +//-- +//-- Saving +//-- + +var saveUsingSafari = false; + +var startSaveArea = '
      '; // Split up into two so that indexOf() of this source doesn't find it +var startSaveAreaRE = /<((div)|(DIV)) ((id)|(ID))=["']?storeArea['"]?>/; // Used for IE6 +var endSaveArea = ''; +var endSaveAreaCaps = ''; + +// If there are unsaved changes, force the user to confirm before exitting +function confirmExit() +{ + hadConfirmExit = true; + if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty())) + return config.messages.confirmExit; +} + +// Give the user a chance to save changes before exitting +function checkUnsavedChanges() +{ + if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) { + if(confirm(config.messages.unsavedChangesWarning)) + saveChanges(); + } +} + +function updateLanguageAttribute(s) +{ + if(config.locale) { + var mRE = /(/; + var m = mRE.exec(s); + if(m) { + var t = m[1]; + if(m[2]) + t += ' xml:lang="' + config.locale + '"'; + if(m[3]) + t += ' lang="' + config.locale + '"'; + t += ">"; + s = s.substr(0,m.index) + t + s.substr(m.index+m[0].length); + } + } + return s; +} + +function updateMarkupBlock(s,blockName,tiddlerName) +{ + return s.replaceChunk( + "".format([blockName]), + "".format([blockName]), + "\n" + convertUnicodeToFileFormat(store.getRecursiveTiddlerText(tiddlerName,"")) + "\n"); +} + +function updateOriginal(original,posDiv,localPath) +{ + if(!posDiv) + posDiv = locateStoreArea(original); + if(!posDiv) { + alert(config.messages.invalidFileError.format([localPath])); + return null; + } + var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" + + convertUnicodeToFileFormat(store.allTiddlersAsHtml()) + "\n" + + original.substr(posDiv[1]); + var newSiteTitle = convertUnicodeToFileFormat(getPageTitle()).htmlEncode(); + revised = revised.replaceChunk("",""," " + newSiteTitle + " "); + revised = updateLanguageAttribute(revised); + revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead"); + revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead"); + revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody"); + revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody"); + return revised; +} + +function locateStoreArea(original) +{ + // Locate the storeArea divs + if(!original) + return null; + var posOpeningDiv = original.search(startSaveAreaRE); + var limitClosingDiv = original.indexOf("<"+"!--POST-STOREAREA--"+">"); + if(limitClosingDiv == -1) + limitClosingDiv = original.indexOf("<"+"!--POST-BODY-START--"+">"); + var start = limitClosingDiv == -1 ? original.length : limitClosingDiv; + var posClosingDiv = original.lastIndexOf(endSaveArea,start); + if(posClosingDiv == -1) + posClosingDiv = original.lastIndexOf(endSaveAreaCaps,start); + return (posOpeningDiv != -1 && posClosingDiv != -1) ? [posOpeningDiv,posClosingDiv] : null; +} + +function autoSaveChanges(onlyIfDirty,tiddlers) +{ + if(config.options.chkAutoSave) + saveChanges(onlyIfDirty,tiddlers); +} + +function loadOriginal(localPath) +{ + return loadFile(localPath); +} + +// Save this tiddlywiki with the pending changes +function saveChanges(onlyIfDirty,tiddlers) +{ + if(onlyIfDirty && !store.isDirty()) + return; + clearMessage(); + var t0 = new Date(); + var msg = config.messages; + //# Get the URL of the document + var originalPath = document.location.toString(); + //# Check we were loaded from a file URL + if(originalPath.substr(0,5) != "file:") { + alert(msg.notFileUrlError); + if(store.tiddlerExists(msg.saveInstructions)) + story.displayTiddler(null,msg.saveInstructions); + return; + } + var localPath = getLocalPath(originalPath); + //# Load the original file + var original = loadOriginal(localPath); + if(original == null) { + alert(msg.cantSaveError); + if(store.tiddlerExists(msg.saveInstructions)) + story.displayTiddler(null,msg.saveInstructions); + return; + } + //# Locate the storeArea div's + var posDiv = locateStoreArea(original); + if(!posDiv) { + alert(msg.invalidFileError.format([localPath])); + return; + } + saveMain(localPath,original,posDiv); + if(config.options.chkSaveBackups) + saveBackup(localPath,original); + if(config.options.chkSaveEmptyTemplate) + saveEmpty(localPath,original,posDiv); + if(config.options.chkGenerateAnRssFeed && saveRss instanceof Function) + saveRss(localPath); + if(config.options.chkDisplayInstrumentation) + displayMessage("saveChanges " + (new Date()-t0) + " ms"); +} + +function saveMain(localPath,original,posDiv) +{ + var save; + try { + //# Save new file + var revised = updateOriginal(original,posDiv,localPath); + save = saveFile(localPath,revised); + } catch (ex) { + showException(ex); + } + if(save) { + displayMessage(config.messages.mainSaved,"file://" + localPath); + store.setDirty(false); + } else { + alert(config.messages.mainFailed); + } +} + +function saveBackup(localPath,original) +{ + //# Save the backup + var backupPath = getBackupPath(localPath); + var backup = copyFile(backupPath,localPath); + //# Browser does not support copy, so use save instead + if(!backup) + backup = saveFile(backupPath,original); + if(backup) + displayMessage(config.messages.backupSaved,"file://" + backupPath); + else + alert(config.messages.backupFailed); +} + +function saveEmpty(localPath,original,posDiv) +{ + //# Save empty template + var emptyPath,p; + if((p = localPath.lastIndexOf("/")) != -1) + emptyPath = localPath.substr(0,p) + "/"; + else if((p = localPath.lastIndexOf("\\")) != -1) + emptyPath = localPath.substr(0,p) + "\\"; + else + emptyPath = localPath + "."; + emptyPath += "empty.html"; + var empty = original.substr(0,posDiv[0] + startSaveArea.length) + original.substr(posDiv[1]); + var emptySave = saveFile(emptyPath,empty); + if(emptySave) + displayMessage(config.messages.emptySaved,"file://" + emptyPath); + else + alert(config.messages.emptyFailed); +} + +function getLocalPath(origPath) +{ + var originalPath = convertUriToUTF8(origPath,config.options.txtFileSystemCharSet); + // Remove any location or query part of the URL + var argPos = originalPath.indexOf("?"); + if(argPos != -1) + originalPath = originalPath.substr(0,argPos); + var hashPos = originalPath.indexOf("#"); + if(hashPos != -1) + originalPath = originalPath.substr(0,hashPos); + // Convert file://localhost/ to file:/// + if(originalPath.indexOf("file://localhost/") == 0) + originalPath = "file://" + originalPath.substr(16); + // Convert to a native file format + //# "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..." + //# "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..." + //# "file:///path/path/path..." - mac/unix local file --> "/path/path/path..." + //# "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..." + var localPath; + if(originalPath.charAt(9) == ":") // pc local file + localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\"); + else if(originalPath.indexOf("file://///") == 0) // FireFox pc network file + localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\"); + else if(originalPath.indexOf("file:///") == 0) // mac/unix local file + localPath = unescape(originalPath.substr(7)); + else if(originalPath.indexOf("file:/") == 0) // mac/unix local file + localPath = unescape(originalPath.substr(5)); + else // pc network file + localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\"); + return localPath; +} + +function getBackupPath(localPath,title,extension) +{ + var slash = "\\"; + var dirPathPos = localPath.lastIndexOf("\\"); + if(dirPathPos == -1) { + dirPathPos = localPath.lastIndexOf("/"); + slash = "/"; + } + var backupFolder = config.options.txtBackupFolder; + if(!backupFolder || backupFolder == "") + backupFolder = "."; + var backupPath = localPath.substr(0,dirPathPos) + slash + backupFolder + localPath.substr(dirPathPos); + backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "."; + //# replace illegal filename characters(// \/:*?"<>|) and space with underscore + if(title) + backupPath += title.replace(/[\\\/\*\?\":<> ]/g,"_") + "."; + backupPath += (new Date()).convertToYYYYMMDDHHMMSSMMM() + "." + (extension || "html"); + return backupPath; +} + diff --git a/test/data/tiddlywiki/js/SavingRSS.js b/test/data/tiddlywiki/js/SavingRSS.js new file mode 100755 index 000000000..9f144d3c8 --- /dev/null +++ b/test/data/tiddlywiki/js/SavingRSS.js @@ -0,0 +1,57 @@ +//-- +//-- RSS Saving +//-- + +function saveRss(localPath) +{ + var rssPath = localPath.substr(0,localPath.lastIndexOf(".")) + ".xml"; + if(saveFile(rssPath,convertUnicodeToFileFormat(generateRss()))) + displayMessage(config.messages.rssSaved,"file://" + rssPath); + else + alert(config.messages.rssFailed); +} + +tiddlerToRssItem = function(tiddler,uri) +{ + var s = "" + tiddler.title.htmlEncode() + "\n"; + s += "" + wikifyStatic(tiddler.text,null,tiddler).htmlEncode() + "\n"; + var i; + for(i=0; i\n"; + s += "" + uri + "#" + encodeURIComponent(String.encodeTiddlyLink(tiddler.title)) + "\n"; + s +="" + tiddler.modified.toGMTString() + "\n"; + return s; +}; + +function generateRss() +{ + var s = []; + var d = new Date(); + var u = store.getTiddlerText("SiteUrl"); + // Assemble the header + s.push("<" + "?xml version=\"1.0\"?" + ">"); + s.push(""); + s.push(""); + s.push("" + wikifyPlainText(store.getTiddlerText("SiteTitle",""),null,tiddler).htmlEncode() + ""); + if(u) + s.push("" + u.htmlEncode() + ""); + s.push("" + wikifyPlainText(store.getTiddlerText("SiteSubtitle",""),null,tiddler).htmlEncode() + ""); + s.push("" + config.locale + ""); + s.push("Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + ""); + s.push("" + d.toGMTString() + ""); + s.push("" + d.toGMTString() + ""); + s.push("http://blogs.law.harvard.edu/tech/rss"); + s.push("TiddlyWiki " + formatVersion() + ""); + // The body + var tiddlers = store.getTiddlers("modified","excludeLists"); + var i,n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems; + for(i=tiddlers.length-1; i>=n; i--) { + s.push("\n" + tiddlerToRssItem(tiddlers[i],u) + "\n"); + } + // And footer + s.push(""); + s.push(""); + // Save it all + return s.join("\n"); +} + diff --git a/test/data/tiddlywiki/js/Scroller.js b/test/data/tiddlywiki/js/Scroller.js new file mode 100755 index 000000000..ef7c004e9 --- /dev/null +++ b/test/data/tiddlywiki/js/Scroller.js @@ -0,0 +1,10 @@ +//-- +//-- Scroller animation +//-- + +function Scroller(targetElement) +{ + var p = [{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}]; + return new Morpher(targetElement,config.animDuration,p); +} + diff --git a/test/data/tiddlywiki/js/Search.js b/test/data/tiddlywiki/js/Search.js new file mode 100755 index 000000000..55fbd8e5c --- /dev/null +++ b/test/data/tiddlywiki/js/Search.js @@ -0,0 +1,81 @@ +//-- +//-- Search macro +//-- + +config.macros.search.handler = function(place,macroName,params,wikifier,paramString,tiddler) +{ + params = paramString.parseParams("anon",null,false,false,false); + createTiddlyButton(place,this.label,this.prompt,this.onClick,"searchButton"); + var txt = createTiddlyElement(null,"input",null,"txtOptionInput searchField"); + txt.value = getParam(params,"anon",""); + if(config.browser.isSafari) { + txt.setAttribute("type","search"); + txt.setAttribute("results","5"); + } else { + txt.setAttribute("type","text"); + } + place.appendChild(txt); + txt.onkeyup = this.onKeyPress; + txt.onfocus = this.onFocus; + txt.setAttribute("size",this.sizeTextbox); + txt.setAttribute("accessKey",getParam(params,"accesskey",this.accessKey)); + txt.setAttribute("autocomplete","off"); + txt.setAttribute("lastSearchText",""); + txt.setAttribute("placeholder",getParam(params,"placeholder",this.placeholder)); +}; + +// Global because there's only ever one outstanding incremental search timer +config.macros.search.timeout = null; + +config.macros.search.doSearch = function(txt) +{ + if(txt.value.length > 0) { + story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch); + txt.setAttribute("lastSearchText",txt.value); + } +}; + +config.macros.search.onClick = function(e) +{ + config.macros.search.doSearch(this.nextSibling); + return false; +}; + +config.macros.search.onKeyPress = function(ev) +{ + var me = config.macros.search; + var e = ev || window.event; + switch(e.keyCode) { + case 9: // Tab + return; + case 13: // Ctrl-Enter + case 10: // Ctrl-Enter on IE PC + me.doSearch(this); + break; + case 27: // Escape + this.value = ""; + clearMessage(); + break; + } + if(config.options.chkIncrementalSearch) { + if(this.value.length > 2) { + if(this.value != this.getAttribute("lastSearchText")) { + if(me.timeout) { + clearTimeout(me.timeout); + } + var txt = this; + me.timeout = setTimeout(function() {me.doSearch(txt);},500); + } + } else { + if(me.timeout) { + clearTimeout(me.timeout); + } + } + } +}; + +config.macros.search.onFocus = function(e) +{ + this.select(); +}; + diff --git a/test/data/tiddlywiki/js/Slider.js b/test/data/tiddlywiki/js/Slider.js new file mode 100755 index 000000000..9871dbbb3 --- /dev/null +++ b/test/data/tiddlywiki/js/Slider.js @@ -0,0 +1,35 @@ +//-- +//-- Slider animation +//-- + +// deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element] +function Slider(element,opening,unused,deleteMode) +{ + element.style.overflow = 'hidden'; + if(opening) + element.style.height = '0px'; // Resolves a Firefox flashing bug + element.style.display = 'block'; + var height = element.scrollHeight; + var p = []; + var c = null; + if(opening) { + p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'}); + p.push({style: 'opacity', start: 0, end: 1, template: '%0'}); + p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'}); + } else { + p.push({style: 'height', start: height, end: 0, template: '%0px'}); + p.push({style: 'display', atEnd: 'none'}); + p.push({style: 'opacity', start: 1, end: 0, template: '%0'}); + p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'}); + switch(deleteMode) { + case "all": + c = function(element,properties) {jQuery(element).remove();}; + break; + case "children": + c = function(element,properties) {jQuery(element).empty();}; + break; + } + } + return new Morpher(element,config.animDuration,p,c); +} + diff --git a/test/data/tiddlywiki/js/Sparkline.js b/test/data/tiddlywiki/js/Sparkline.js new file mode 100755 index 000000000..3bb5235fe --- /dev/null +++ b/test/data/tiddlywiki/js/Sparkline.js @@ -0,0 +1,56 @@ +//-- +//-- Sparklines +//-- + +config.macros.sparkline = {}; + +config.shadowTiddlers.StyleSheetSparklines = "/*{{{*/\n" + + ".sparkline {\n" + + "\tline-height: 1em;\n" + + "\tborder: 0;\n" + + "\tbackground: [[ColorPalette::PrimaryPale]];\n" + + "}\n\n" + + ".sparktick {\n" + + "\toutline: 0;\n" + + "\tbackground: [[ColorPalette::PrimaryDark]];\n" + + "}\n" + + "/*}}}*/"; +store.addNotification("StyleSheetSparklines", refreshStyles); + +config.macros.sparkline.handler = function(place,macroName,params) +{ + var data = []; + var min = 0; + var max = 0; + var v; + for(var t=0; t max) + max = v; + data.push(v); + } + if(data.length < 1) + return; + var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160)); + box.title = data.join(","); + var w = box.offsetWidth; + var h = box.offsetHeight; + box.style.paddingRight = (data.length * 2 - w) + "px"; + box.style.position = "relative"; + for(var d=0; d=0;t--) + this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields); +}; + +//# Display a given tiddler with a given template. If the tiddler is already displayed but with a different +//# template, it is switched to the specified template. If the tiddler does not exist, and if server hosting +//# custom fields were provided, then an attempt is made to retrieve the tiddler from the server +//# srcElement - reference to element from which this one is being opened -or- +//# special positions "top", "bottom" +//# tiddler - tiddler or title of tiddler to display +//# template - the name of the tiddler containing the template -or- +//# one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or- +//# null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not +//# animate - whether to perform animations +//# customFields - an optional list of name:"value" pairs to be assigned as tiddler fields (for edit templates) +//# toggle - if true, causes the tiddler to be closed if it is already opened +//# animationSrc - optional. If provided, this will specify the element which is to act as the start of the animation -or- +//# the source of the animation will be the srcElement. +Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc) +{ + var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler; + var tiddlerElem = this.getTiddler(title); + if(tiddlerElem) { + if(toggle) { + if(tiddlerElem.getAttribute("dirty") != "true") + this.closeTiddler(title,true); + } else { + this.refreshTiddler(title,template,false,customFields); + } + } else { + var place = this.getContainer(); + var before = this.positionTiddler(srcElement); + tiddlerElem = this.createTiddler(place,before,title,template,customFields); + } + if(animationSrc && typeof animationSrc !== "string") { + srcElement = animationSrc; + } + if(srcElement && typeof srcElement !== "string") { + if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function") + anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem)); + else + window.scrollTo(0,ensureVisible(tiddlerElem)); + } + return tiddlerElem; +}; + +//# Figure out the appropriate position for a newly opened tiddler +//# srcElement - reference to the element containing the link to the tiddler -or- +//# special positions "top", "bottom" +//# returns - reference to the tiddler that the new one should appear before (null for the bottom of the story) +Story.prototype.positionTiddler = function(srcElement) +{ + var place = this.getContainer(); + var before = null; + if(typeof srcElement == "string") { + switch(srcElement) { + case "top": + before = place.firstChild; + break; + case "bottom": + before = null; + break; + } + } else { + var after = this.findContainingTiddler(srcElement); + if(after == null) { + before = place.firstChild; + } else if(after.nextSibling) { + before = after.nextSibling; + if(before.nodeType != 1) + before = null; + } + } + return before; +}; + +//# Create a tiddler frame at the appropriate place in a story column. If the tiddler doesn't exist, +//# triggers an attempt to load it as a missing tiddler +//# place - reference to parent element +//# before - null, or reference to element before which to insert new tiddler +//# title - title of new tiddler +//# template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE +//# customFields - an optional list of name:"value" pairs to be assigned as tiddler fields +Story.prototype.createTiddler = function(place,before,title,template,customFields) +{ + var tiddlerElem = createTiddlyElement(null,"div",this.tiddlerId(title),"tiddler"); + tiddlerElem.setAttribute("refresh","tiddler"); + if(customFields) + tiddlerElem.setAttribute("tiddlyFields",customFields); + place.insertBefore(tiddlerElem,before); + var defaultText = null; + if(!store.tiddlerExists(title) && !store.isShadowTiddler(title)) + defaultText = this.loadMissingTiddler(title,customFields); + this.refreshTiddler(title,template,false,customFields,defaultText); + return tiddlerElem; +}; + +//# Attempts to load a missing tiddler from the server specified in the custom fields +//# title - title of the missing tiddler +//# fields - string of name:"value" pairs or hashmap +//# callback - optional function invoked with context argument upon completion; context provides context.tiddler if successful +Story.prototype.loadMissingTiddler = function(title,fields,callback) +{ + var getTiddlerCallback = function(context) + { + if(context.status) { + var t = context.tiddler; + if(!t.created) + t.created = new Date(); + if(!t.modified) + t.modified = t.created; + var dirty = store.isDirty(); + context.tiddler = store.saveTiddler(t.title,t.title,t.text,t.modifier,t.modified,t.tags,t.fields,true,t.created,t.creator); + if(window.location.protocol != "file:") + store.setDirty(dirty); + autoSaveChanges(); + } else { + story.refreshTiddler(context.title,null,true); + } + context.adaptor.close(); + if(callback) { + callback(context); + } + }; + var tiddler = new Tiddler(title); + tiddler.fields = typeof fields == "string" ? fields.decodeHashMap() : fields||{}; + var context = {serverType:tiddler.getServerType()}; + if(!context.serverType) + return ""; + context.host = tiddler.fields['server.host']; + context.workspace = tiddler.fields['server.workspace']; + var adaptor = new config.adaptors[context.serverType](); + adaptor.getTiddler(title,context,null,getTiddlerCallback); + return config.messages.loadingMissingTiddler.format([title,context.serverType,context.host,context.workspace]); +}; + +//# Overridable for choosing the name of the template to apply for a tiddler +Story.prototype.chooseTemplateForTiddler = function(title,template) +{ + if(!template) + template = DEFAULT_VIEW_TEMPLATE; + if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE) + template = config.tiddlerTemplates[template]; + return template; +}; + +//# Overridable for extracting the text of a template from a tiddler +Story.prototype.getTemplateForTiddler = function(title,template,tiddler) +{ + return store.getRecursiveTiddlerText(template,null,10); +}; + +//# Apply a template to an existing tiddler if it is not already displayed using that template +//# title - title of tiddler to update +//# template - the name of the tiddler containing the template or one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE +//# force - if true, forces the refresh even if the template hasn't changed +//# customFields - an optional list of name/value pairs to be assigned as tiddler fields (for edit templates) +//# defaultText - an optional string to replace the default text for non-existent tiddlers +Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText) +{ + var tiddlerElem = this.getTiddler(title); + if(tiddlerElem) { + if(tiddlerElem.getAttribute("dirty") == "true" && !force) + return tiddlerElem; + template = this.chooseTemplateForTiddler(title,template); + var currTemplate = tiddlerElem.getAttribute("template"); + if((template != currTemplate) || force) { + var tiddler = store.getTiddler(title); + if(!tiddler) { + tiddler = new Tiddler(); + if(store.isShadowTiddler(title)) { + var tags = []; + tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,tags,version.date); + } else { + var text = template=="EditTemplate" ? + config.views.editor.defaultText.format([title]) : + config.views.wikified.defaultText.format([title]); + text = defaultText || text; + var fields = customFields ? customFields.decodeHashMap() : null; + tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields); + } + } + tiddlerElem.setAttribute("tags",tiddler.tags.join(" ")); + tiddlerElem.setAttribute("tiddler",title); + tiddlerElem.setAttribute("template",template); + tiddlerElem.onmouseover = this.onTiddlerMouseOver; + tiddlerElem.onmouseout = this.onTiddlerMouseOut; + tiddlerElem.ondblclick = this.onTiddlerDblClick; + tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress; + tiddlerElem.innerHTML = this.getTemplateForTiddler(title,template,tiddler); + applyHtmlMacros(tiddlerElem,tiddler); + if(store.getTaggedTiddlers(title).length > 0) + jQuery(tiddlerElem).addClass("isTag"); + else + jQuery(tiddlerElem).removeClass("isTag"); + if(store.tiddlerExists(title)) { + jQuery(tiddlerElem).removeClass("shadow"); + jQuery(tiddlerElem).removeClass("missing"); + } else { + jQuery(tiddlerElem).addClass(store.isShadowTiddler(title) ? "shadow" : "missing"); + } + if(customFields) + this.addCustomFields(tiddlerElem,customFields); + } + } + return tiddlerElem; +}; + +//# Add hidden input elements for the custom fields of a tiddler +Story.prototype.addCustomFields = function(place,customFields) +{ + var fields = customFields.decodeHashMap(); + var w = createTiddlyElement(place,"div",null,"customFields"); + w.style.display = "none"; + var t; + for(t in fields) { + var e = document.createElement("input"); + e.setAttribute("type","text"); + e.setAttribute("value",fields[t]); + w.appendChild(e); + e.setAttribute("edit",t); + } +}; + +//# Refresh all tiddlers in the Story +Story.prototype.refreshAllTiddlers = function(force) +{ + var e = this.getContainer().firstChild; + while(e) { + var template = e.getAttribute("template"); + if(template && e.getAttribute("dirty") != "true") { + this.refreshTiddler(e.getAttribute("tiddler"),force ? null : template,true); + } + e = e.nextSibling; + } +}; + +//# Default tiddler onmouseover/out event handlers +Story.prototype.onTiddlerMouseOver = function(e) +{ + jQuery(this).addClass("selected"); +}; + +Story.prototype.onTiddlerMouseOut = function(e) +{ + jQuery(this).removeClass("selected"); +}; + +//# Default tiddler ondblclick event handler +Story.prototype.onTiddlerDblClick = function(ev) +{ + var e = ev || window.event; + var target = resolveTarget(e); + if(target && target.nodeName.toLowerCase() != "input" && target.nodeName.toLowerCase() != "textarea") { + if(document.selection && document.selection.empty) + document.selection.empty(); + config.macros.toolbar.invokeCommand(this,"defaultCommand",e); + e.cancelBubble = true; + if(e.stopPropagation) e.stopPropagation(); + return true; + } + return false; +}; + +Story.prototype.onTiddlerKeyPress = function(ev) +{ + var e = ev || window.event; + clearMessage(); + var consume = false; + var title = this.getAttribute("tiddler"); + var target = resolveTarget(e); + switch(e.keyCode) { + case 9: // Tab + var ed = story.getTiddlerField(title,"text"); + if(target.tagName.toLowerCase() == "input" && ed.value==config.views.editor.defaultText.format([title])) { + // moving from input field and editor still contains default text, so select it + ed.focus(); + ed.select(); + consume = true; + } + if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") { + replaceSelection(target,String.fromCharCode(9)); + consume = true; + } + if(config.isOpera) { + target.onblur = function() { + this.focus(); + this.onblur = null; + }; + } + break; + case 13: // Ctrl-Enter + case 10: // Ctrl-Enter on IE PC + case 77: // Ctrl-Enter is "M" on some platforms + if(e.ctrlKey) { + blurElement(this); + config.macros.toolbar.invokeCommand(this,"defaultCommand",e); + consume = true; + } + break; + case 27: // Escape + blurElement(this); + config.macros.toolbar.invokeCommand(this,"cancelCommand",e); + consume = true; + break; + } + e.cancelBubble = consume; + if(consume) { + if(e.stopPropagation) e.stopPropagation(); // Stop Propagation + e.returnValue = true; // Cancel The Event in IE + if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz + } + return !consume; +}; + +//# Returns the specified field (input or textarea element) in a tiddler, otherwise the first edit field it finds +//# or null if it found no edit field at all +Story.prototype.getTiddlerField = function(title,field) +{ + var tiddlerElem = this.getTiddler(title); + var e = null; + if(tiddlerElem) { + var t,children = tiddlerElem.getElementsByTagName("*"); + for(t=0; t 0) + displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q])); + else + displayMessage(config.macros.search.failureMsg.format([q + text + q])); +}; + +//# Determine if the specified element is within a tiddler in this story +//# e - reference to an element +//# returns: reference to a tiddler element or null if none +Story.prototype.findContainingTiddler = function(e) +{ + while(e && !jQuery(e).hasClass("tiddler")) { + e = jQuery(e).hasClass("popup") && Popup.stack[0] ? Popup.stack[0].root : e.parentNode; + } + return e; +}; + +//# Gather any saveable fields from a tiddler element +//# e - reference to an element to scan recursively +//# fields - object to contain gathered field values +Story.prototype.gatherSaveFields = function(e,fields) +{ + if(e && e.getAttribute) { + var f = e.getAttribute("edit"); + if(f) + fields[f] = e.value.replace(/\r/mg,""); + if(e.hasChildNodes()) { + var t,c = e.childNodes; + for(t=0; t "backgroundColor") +String.prototype.unDash = function() +{ + var t,s = this.split("-"); + if(s.length > 1) { + for(t=1; t currPos) + r.push(this.substring(currPos,match.index)); + r.push(substrings[parseInt(match[1],10)]); + currPos = subRegExp.lastIndex; + } + } while(match); + if(currPos < this.length) + r.push(this.substring(currPos,this.length)); + return r.join(""); +}; + +// Escape any special RegExp characters with that character preceded by a backslash +String.prototype.escapeRegExp = function() +{ + var s = "\\^$*+?()=!|,{}[]."; + var t,c = this; + for(t=0; t to ">" and " to """ +String.prototype.htmlEncode = function() +{ + return this.replace(/&/mg,"&").replace(//mg,">").replace(/\"/mg,"""); +}; + +// Convert "&" to &, "<" to <, ">" to > and """ to " +String.prototype.htmlDecode = function() +{ + return this.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&"); +}; + +// Parse a space-separated string of name:value parameters +//# where: +//# - the name or the value can be optional (in which case separate defaults are used instead) +//# - in case of ambiguity, a lone word is taken to be a value +//# - if 'cascadeDefaults' is set to true, then the defaults are modified by updated by each specified name or value +//# - name prefixes are not allowed if the 'noNames' parameter is true +//# - if both the name and value are present they must be separated by a colon +//# - the name and the value may both be quoted with single- or double-quotes, double-square brackets +//# - names or values quoted with {{double-curly braces}} are evaluated as a JavaScript expression +//# - as long as the 'allowEval' parameter is true +// The result is an array of objects: +// result[0] = object with a member for each parameter name, value of that member being an array of values +// result[1..n] = one object for each parameter, with 'name' and 'value' members +String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults) +{ + var parseToken = function(match,p) { + var n; + if(match[p]) // Double quoted + n = match[p]; + else if(match[p+1]) // Single quoted + n = match[p+1]; + else if(match[p+2]) // Double-square-bracket quoted + n = match[p+2]; + else if(match[p+3]) // Double-brace quoted + try { + n = match[p+3]; + if(allowEval && config.evaluateMacroParameters != "none") { + if(config.evaluateMacroParameters == "restricted") { + if(window.restrictedEval) { + n = window.restrictedEval(n); + } + } else { + n = window.eval(n); + } + } + } catch(ex) { + throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex); + } + else if(match[p+4]) // Unquoted + n = match[p+4]; + else if(match[p+5]) // empty quote + n = ""; + return n; + }; + var r = [{}]; + var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")"; + var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')"; + var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])"; + var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})"; + var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)"; + var emptyQuote = "((?:\"\")|(?:''))"; + var skipSpace = "(?:\\s*)"; + var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")"; + var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg"); + var match; + do { + match = re.exec(this); + if(match) { + var n = parseToken(match,1); + if(noNames) { + r.push({name:"",value:n}); + } else { + var v = parseToken(match,8); + if(v == null && defaultName) { + v = n; + n = defaultName; + } else if(v == null && defaultValue) { + v = defaultValue; + } + r.push({name:n,value:v}); + if(cascadeDefaults) { + defaultName = n; + defaultValue = v; + } + } + } + } while(match); + // Summarise parameters into first element + var t; + for(t=1; t si.tiddler.fields['server.page.revision']) { + si.syncStatus = me.syncStatusList[si.isTouched ? 'changedBoth' : 'changedServer']; + } + } else { + si.syncStatus = me.syncStatusList.notFound; + } + me.updateSyncStatus(si); + } + return true; + }; + + var openWorkspaceCallback = function(context,syncItems) { + if(context.status) { + context.adaptor.getTiddlerList(context,syncItems,getTiddlerListCallback); + return true; + } + displayMessage(context.statusText); + return false; + }; + + var context = {host:st.serverHost,workspace:st.serverWorkspace}; + syncItem.adaptor.openHost(st.serverHost); + syncItem.adaptor.openWorkspace(st.serverWorkspace,context,st.syncItems,openWorkspaceCallback); + return st; +}; + +config.macros.sync.updateSyncStatus = function(syncItem) +{ + var e = syncItem.colElements["status"]; + jQuery(e).empty(); + createTiddlyText(e,syncItem.syncStatus.text); + syncItem.rowElement.style.display = syncItem.syncStatus.display; + if(syncItem.syncStatus.className) + syncItem.rowElement.className = syncItem.syncStatus.className; +}; + +config.macros.sync.doSync = function(e) +{ + var me = config.macros.sync; + var getTiddlerCallback = function(context,syncItem) { + if(syncItem) { + var tiddler = context.tiddler; + store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields,true,tiddler.created); + syncItem.syncStatus = me.syncStatusList.gotFromServer; + me.updateSyncStatus(syncItem); + } + }; + var putTiddlerCallback = function(context,syncItem) { + if(syncItem) { + store.resetTiddler(context.title); + syncItem.syncStatus = me.syncStatusList.putToServer; + me.updateSyncStatus(syncItem); + } + }; + + var rowNames = ListView.getSelectedRows(currSync.listView); + var i,sl = me.syncStatusList; + for(i=0; i= 0; i--) { + var name = attrs[i].name; + if(attrs[i].specified && !TiddlyWiki.isStandardField(name)) { + fields[name] = attrs[i].value.unescapeLineBreaks(); + } + } + tiddler.assign(title,text,modifier,modified,tags,created,fields,creator); + return tiddler; +}; + diff --git a/test/data/tiddlywiki/js/TW21Saver.js b/test/data/tiddlywiki/js/TW21Saver.js new file mode 100755 index 000000000..294a82e75 --- /dev/null +++ b/test/data/tiddlywiki/js/TW21Saver.js @@ -0,0 +1,42 @@ +//-- +//-- TW21Saver (inherits from SaverBase) +//-- + +function TW21Saver() {} + +TW21Saver.prototype = new SaverBase(); + +TW21Saver.prototype.externalizeTiddler = function(store,tiddler) +{ + try { + var extendedAttributes = ""; + var usePre = config.options.chkUsePreForStorage; + store.forEachField(tiddler, + function(tiddler,fieldName,value) { + // don't store stuff from the temp namespace + if(typeof value != "string") + value = ""; + if(!fieldName.match(/^temp\./)) + extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]); + },true); + var created = tiddler.created; + var modified = tiddler.modified; + var attributes = tiddler.creator ? ' creator="' + tiddler.creator.htmlEncode() + '"' : ""; + attributes += tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : ""; + attributes += (usePre && created == version.date) ? "" :' created="' + created.convertToYYYYMMDDHHMM() + '"'; + attributes += (usePre && modified == created) ? "" : ' modified="' + modified.convertToYYYYMMDDHHMM() +'"'; + var tags = tiddler.getTags(); + if(!usePre || tags) + attributes += ' tags="' + tags.htmlEncode() + '"'; + return ('
      %4').format([ + usePre ? "title" : "tiddler", + tiddler.title.htmlEncode(), + attributes, + extendedAttributes, + usePre ? "\n
      " + tiddler.text.htmlEncode() + "
      \n" : tiddler.text.escapeLineBreaks().htmlEncode() + ]); + } catch (ex) { + throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title])); + } +}; + diff --git a/test/data/tiddlywiki/js/Tabs.js b/test/data/tiddlywiki/js/Tabs.js new file mode 100755 index 000000000..cc9aebc03 --- /dev/null +++ b/test/data/tiddlywiki/js/Tabs.js @@ -0,0 +1,65 @@ +//-- +//-- Tabs macro +//-- + +config.macros.tabs.handler = function(place,macroName,params) +{ + var cookie = params[0]; + var numTabs = (params.length-1)/3; + var wrapper = createTiddlyElement(null,"div",null,"tabsetWrapper " + cookie); + var tabset = createTiddlyElement(wrapper,"div",null,"tabset"); + tabset.setAttribute("cookie",cookie); + var validTab = false; + var t; + for(t=0; t 0; +}; + +// Change the text and other attributes of a tiddler +Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields,creator) +{ + this.assign(title,text,modifier,modified,tags,created,fields,creator); + this.changed(); + return this; +}; + +// Change the text and other attributes of a tiddler without triggered a tiddler.changed() call +Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields,creator) +{ + if(title != undefined) + this.title = title; + if(text != undefined) + this.text = text; + if(modifier != undefined) + this.modifier = modifier; + if(modified != undefined) + this.modified = modified; + if(creator != undefined) + this.creator = creator; + if(created != undefined) + this.created = created; + if(fields != undefined) + this.fields = fields; + if(tags != undefined) + this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags; + else if(this.tags == undefined) + this.tags = []; + return this; +}; + +// Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces) +Tiddler.prototype.getTags = function() +{ + return String.encodeTiddlyLinkList(this.tags); +}; + +// Test if a tiddler carries a tag +Tiddler.prototype.isTagged = function(tag) +{ + return this.tags.indexOf(tag) != -1; +}; + +// Static method to convert "\n" to newlines, "\s" to "\" +Tiddler.unescapeLineBreaks = function(text) +{ + return text ? text.unescapeLineBreaks() : ""; +}; + +// Convert newlines to "\n", "\" to "\s" +Tiddler.prototype.escapeLineBreaks = function() +{ + return this.text.escapeLineBreaks(); +}; + +// Updates the secondary information (like links[] array) after a change to a tiddler +Tiddler.prototype.changed = function() +{ + this.links = []; + var text = this.text; + // remove 'quoted' text before scanning tiddler source + text = text.replace(/\/%((?:.|\n)*?)%\//g,""). + replace(/\{{3}((?:.|\n)*?)\}{3}/g,""). + replace(/"""((?:.|\n)*?)"""/g,""). + replace(/((?:.|\n)*?)<\/nowiki\>/g,""). + replace(/((?:.|\n)*?)<\/html\>/g,""). + replace(//g,""); + var t = this.autoLinkWikiWords() ? 0 : 1; + var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp; + tiddlerLinkRegExp.lastIndex = 0; + var formatMatch = tiddlerLinkRegExp.exec(text); + while(formatMatch) { + var lastIndex = tiddlerLinkRegExp.lastIndex; + if(t==0 && formatMatch[1] && formatMatch[1] != this.title) { + // wikiWordLink + if(formatMatch.index > 0) { + var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg"); + preRegExp.lastIndex = formatMatch.index-1; + var preMatch = preRegExp.exec(text); + if(preMatch.index != formatMatch.index-1) + this.links.pushUnique(formatMatch[1]); + } else { + this.links.pushUnique(formatMatch[1]); + } + } + else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink + this.links.pushUnique(formatMatch[3-t]); + else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink + this.links.pushUnique(formatMatch[4-t]); + //# Do not add link if match urlPattern (formatMatch[5-t]) + tiddlerLinkRegExp.lastIndex = lastIndex; + formatMatch = tiddlerLinkRegExp.exec(text); + } + this.linksUpdated = true; +}; + +Tiddler.prototype.getSubtitle = function() +{ + var modifier = this.modifier; + if(!modifier) + modifier = config.messages.subtitleUnknown || ""; + var modified = this.modified; + if(modified) + modified = modified.toLocaleString(); + else + modified = config.messages.subtitleUnknown || ""; + var f = config.messages.tiddlerLinkTooltip || "%0 - %1, %2"; + return f.format([this.title,modifier,modified]); +}; + +Tiddler.prototype.isReadOnly = function() +{ + return readOnly; +}; + +Tiddler.prototype.autoLinkWikiWords = function() +{ + return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing")); +}; + +Tiddler.prototype.getServerType = function() +{ + var serverType = null; + if(this.fields['server.type']) + serverType = this.fields['server.type']; + if(!serverType) + serverType = this.fields['wikiformat']; + if(serverType && !config.adaptors[serverType]) + serverType = null; + return serverType; +}; + +Tiddler.prototype.getAdaptor = function() +{ + var serverType = this.getServerType(); + return serverType ? new config.adaptors[serverType]() : null; +}; + diff --git a/test/data/tiddlywiki/js/TiddlerFields.js b/test/data/tiddlywiki/js/TiddlerFields.js new file mode 100755 index 000000000..338475459 --- /dev/null +++ b/test/data/tiddlywiki/js/TiddlerFields.js @@ -0,0 +1,173 @@ +// Returns true if path is a valid field name (path), +// i.e. a sequence of identifiers, separated by "." +TiddlyWiki.isValidFieldName = function(name) +{ + var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name); + return match && (match[0] == name); +}; + +// Throws an exception when name is not a valid field name. +TiddlyWiki.checkFieldName = function(name) +{ + if(!TiddlyWiki.isValidFieldName(name)) + throw config.messages.invalidFieldName.format([name]); +}; + +function StringFieldAccess(n,readOnly) +{ + this.set = readOnly ? + function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} : + function(t,v) {if(v != t[n]) {t[n] = v; return true;}}; + this.get = function(t) {return t[n];}; +} + +function DateFieldAccess(n) +{ + this.set = function(t,v) { + var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v); + if(d != t[n]) { + t[n] = d; return true; + } + }; + this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();}; +} + +function LinksFieldAccess(n) +{ + this.set = function(t,v) { + var s = (typeof v == "string") ? v.readBracketedList() : v; + if(s.toString() != t[n].toString()) { + t[n] = s; return true; + } + }; + this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);}; +} + +TiddlyWiki.standardFieldAccess = { + // The set functions return true when setting the data has changed the value. + "title": new StringFieldAccess("title",true), + // Handle the "tiddler" field name as the title + "tiddler": new StringFieldAccess("title",true), + "text": new StringFieldAccess("text"), + "modifier": new StringFieldAccess("modifier"), + "modified": new DateFieldAccess("modified"), + "creator": new StringFieldAccess("creator"), + "created": new DateFieldAccess("created"), + "tags": new LinksFieldAccess("tags") +}; + +TiddlyWiki.isStandardField = function(name) +{ + return TiddlyWiki.standardFieldAccess[name] != undefined; +}; + +// Sets the value of the given field of the tiddler to the value. +// Setting an ExtendedField's value to null or undefined removes the field. +// Setting a namespace to undefined removes all fields of that namespace. +// The fieldName is case-insensitive. +// All values will be converted to a string value. +TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value) +{ + TiddlyWiki.checkFieldName(fieldName); + var t = this.resolveTiddler(tiddler); + if(!t) + return; + fieldName = fieldName.toLowerCase(); + var isRemove = (value === undefined) || (value === null); + var accessor = TiddlyWiki.standardFieldAccess[fieldName]; + if(accessor) { + if(isRemove) + // don't remove StandardFields + return; + var h = TiddlyWiki.standardFieldAccess[fieldName]; + if(!h.set(t,value)) + return; + } else { + var oldValue = t.fields[fieldName]; + if(isRemove) { + if(oldValue !== undefined) { + // deletes a single field + delete t.fields[fieldName]; + } else { + // no concrete value is defined for the fieldName + // so we guess this is a namespace path. + // delete all fields in a namespace + var re = new RegExp("^"+fieldName+"\\."); + var dirty = false; + var n; + for(n in t.fields) { + if(n.match(re)) { + delete t.fields[n]; + dirty = true; + } + } + if(!dirty) + return; + } + } else { + // the "normal" set case. value is defined (not null/undefined) + // For convenience provide a nicer conversion Date->String + value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value); + if(oldValue == value) + return; + t.fields[fieldName] = value; + } + } + // When we are here the tiddler/store really was changed. + this.notify(t.title,true); + if(!fieldName.match(/^temp\./)) + this.setDirty(true); +}; + +// Returns the value of the given field of the tiddler. +// The fieldName is case-insensitive. +// Will only return String values (or undefined). +TiddlyWiki.prototype.getValue = function(tiddler,fieldName) +{ + var t = this.resolveTiddler(tiddler); + if(!t) + return undefined; + if(fieldName.indexOf(config.textPrimitives.sectionSeparator) === 0 || fieldName.indexOf(config.textPrimitives.sliceSeparator) === 0) { + var sliceType = fieldName.substr(0, 2); + var sliceName = fieldName.substring(2); + return store.getTiddlerText("%0%1%2".format(t.title,sliceType,sliceName)); + } else { + fieldName = fieldName.toLowerCase(); + var accessor = TiddlyWiki.standardFieldAccess[fieldName]; + if(accessor) { + return accessor.get(t); + } + } + return t.fields[fieldName]; +}; + +// Calls the callback function for every field in the tiddler. +// When callback function returns a non-false value the iteration stops +// and that value is returned. +// The order of the fields is not defined. +// @param callback a function(tiddler,fieldName,value). +TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields) +{ + var t = this.resolveTiddler(tiddler); + if(!t) + return undefined; + var n,result; + for(n in t.fields) { + result = callback(t,n,t.fields[n]); + if(result) + return result; + } + if(onlyExtendedFields) + return undefined; + for(n in TiddlyWiki.standardFieldAccess) { + if(n != "tiddler") { + // even though the "title" field can also be referenced through the name "tiddler" + // we only visit this field once. + result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t)); + if(result) + return result; + } + } + return undefined; +}; + diff --git a/test/data/tiddlywiki/js/TiddlyWiki.js b/test/data/tiddlywiki/js/TiddlyWiki.js new file mode 100755 index 000000000..1adc24836 --- /dev/null +++ b/test/data/tiddlywiki/js/TiddlyWiki.js @@ -0,0 +1,643 @@ +//-- +//-- TiddlyWiki instance contains TiddlerS +//-- + +function TiddlyWiki(params) +{ + var tiddlers = {}; // Hashmap by name of tiddlers + if(params && params.config) { + this.config = config; + } + this.tiddlersUpdated = false; + this.namedNotifications = []; // Array of {name:,notify:} of notification functions + this.notificationLevel = 0; + this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy. + this.clear = function() { + tiddlers = {}; + this.setDirty(false); + }; + this.fetchTiddler = function(title) { + var t = tiddlers[title]; + return t instanceof Tiddler ? t : null; + }; + this.deleteTiddler = function(title) { + delete this.slices[title]; + delete tiddlers[title]; + }; + this.addTiddler = function(tiddler) { + delete this.slices[tiddler.title]; + tiddlers[tiddler.title] = tiddler; + }; + this.forEachTiddler = function(callback) { + var t; + for(t in tiddlers) { + var tiddler = tiddlers[t]; + if(tiddler instanceof Tiddler) + callback.call(this,t,tiddler); + } + }; +} + +//# Set the dirty flag +TiddlyWiki.prototype.setDirty = function(dirty) +{ + this.dirty = dirty; +}; + +TiddlyWiki.prototype.isDirty = function() +{ + return this.dirty; +}; + +TiddlyWiki.prototype.tiddlerExists = function(title) +{ + var t = this.fetchTiddler(title); + return t != undefined; +}; + +TiddlyWiki.prototype.isShadowTiddler = function(title) +{ + return config.shadowTiddlers[title] === undefined ? false : true; +}; + +TiddlyWiki.prototype.createTiddler = function(title) +{ + var tiddler = this.fetchTiddler(title); + if(!tiddler) { + tiddler = new Tiddler(title); + this.addTiddler(tiddler); + this.setDirty(true); + } + return tiddler; +}; + +TiddlyWiki.prototype.getTiddler = function(title) +{ + var t = this.fetchTiddler(title); + if(t != undefined) + return t; + else + return null; +}; + +TiddlyWiki.prototype.getShadowTiddlerText = function(title) +{ + if(typeof config.shadowTiddlers[title] == "string") + return config.shadowTiddlers[title]; + else + return ""; +}; + +// Retrieve tiddler contents +//# Supports tiddler slices or sections, encoded in {{{title}}} argument using +//# the respective separator characters ({{{::}}} or {{{##}}}). +TiddlyWiki.prototype.getTiddlerText = function(title,defaultText) +{ + if(!title) + return defaultText; + var pos = title.indexOf(config.textPrimitives.sectionSeparator); + var section = null; + if(pos != -1) { + section = title.substr(pos + config.textPrimitives.sectionSeparator.length); + title = title.substr(0,pos); + } + pos = title.indexOf(config.textPrimitives.sliceSeparator); + if(pos != -1) { + var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length)); + if(slice) + return slice; + } + var tiddler = this.fetchTiddler(title); + var text = tiddler ? tiddler.text : null; + if(!tiddler && this.isShadowTiddler(title)) { + text = this.getShadowTiddlerText(title); + } + if(text) { + if(!section) + return text; + var re = new RegExp("(^!{1,6}[ \t]*" + section.escapeRegExp() + "[ \t]*\n)","mg"); + re.lastIndex = 0; + var match = re.exec(text); + if(match) { + var t = text.substr(match.index+match[1].length); + var re2 = /^!/mg; + re2.lastIndex = 0; + match = re2.exec(t); //# search for the next heading + if(match) + t = t.substr(0,match.index-1);//# don't include final \n + return t; + } + return defaultText; + } + if(defaultText != undefined) + return defaultText; + return null; +}; + +TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth) +{ + var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg"); + var text = this.getTiddlerText(title,null); + if(text == null) + return defaultText; + var textOut = []; + var match,lastPos = 0; + do { + match = bracketRegExp.exec(text); + if(match) { + textOut.push(text.substr(lastPos,match.index-lastPos)); + if(match[1]) { + if(depth <= 0) + textOut.push(match[1]); + else + textOut.push(this.getRecursiveTiddlerText(match[1],"",depth-1)); + } + lastPos = match.index + match[0].length; + } else { + textOut.push(text.substr(lastPos)); + } + } while(match); + return textOut.join(""); +}; + +//TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]+)[\t\x20]*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|[\t\x20]*([^\n]+)[\t\x20]*\|$)/gm; +TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]*)[\t\x20]*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|[\t\x20]*([^\|\n]*)[\t\x20]*\|$)/gm; +// @internal +TiddlyWiki.prototype.calcAllSlices = function(title) +{ + var slices = {}; + var text = this.getTiddlerText(title,""); + this.slicesRE.lastIndex = 0; + var m = this.slicesRE.exec(text); + while(m) { + if(m[2]) + slices[m[2]] = m[3]; + else + slices[m[5]] = m[6]; + m = this.slicesRE.exec(text); + } + return slices; +}; + +// Returns the slice of text of the given name +//# +//# A text slice is a substring in the tiddler's text that is defined +//# either like this +//# aName: textSlice +//# or +//# |aName:| textSlice | +//# or +//# |aName| textSlice | +//# +//# In the text the name (or name:) may be decorated with '' or // +//# ie this would also a valid text slice: +//# +//# |''aName:''| textSlice | +//# +//# @param name should only contain "word characters" (i.e. "a-ZA-Z_0-9") +//# @return [may be undefined] the (trimmed) text of the specified slice. +TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName) +{ + var slices = this.slices[title]; + if(!slices) { + slices = this.calcAllSlices(title); + this.slices[title] = slices; + } + return slices[sliceName]; +}; + +// Build an hashmap of the specified named slices of a tiddler +TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames) +{ + var t,r = {}; + for(t=0; t<" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>"; + // Create the iframe + var iframe = document.createElement("iframe"); + iframe.style.display = "none"; + document.body.appendChild(iframe); + var doc = iframe.document; + if(iframe.contentDocument) + doc = iframe.contentDocument; // For NS6 + else if(iframe.contentWindow) + doc = iframe.contentWindow.document; // For IE5.5 and IE6 + // Put the content in the iframe + doc.open(); + doc.writeln(content); + doc.close(); + // Load the content into a TiddlyWiki() object + var storeArea = doc.getElementById("storeArea"); + this.loadFromDiv(storeArea,"store"); + // Get rid of the iframe + iframe.parentNode.removeChild(iframe); + return this; +}; + +TiddlyWiki.prototype.updateTiddlers = function() +{ + this.tiddlersUpdated = true; + this.forEachTiddler(function(title,tiddler) { + tiddler.changed(); + }); +}; + +// Return an array of tiddlers matching a search regular expression +TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match) +{ + var candidates = this.reverseLookup("tags",excludeTag,!!match); + var t,results = []; + for(t=0; t": + btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore); + jQuery(btn).addClass("moreCommand"); + var e = createTiddlyElement(place,"span",null,"moreCommand"); + e.style.display = "none"; + place = e; + break; + default: + var className = ""; + switch(c.substr(0,1)) { + case "+": + className = "defaultCommand"; + c = c.substr(1); + break; + case "-": + className = "cancelCommand"; + c = c.substr(1); + break; + } + if(config.commands[c]) { + this.createCommand(place,c,tiddler,className); + } else { + this.customCommand(place,c,wikifier,tiddler); + } + break; + } + } +}; + +// Overrideable function to extend toolbar handler +config.macros.toolbar.customCommand = function(place,command,wikifier,tiddler) +{ +}; + diff --git a/test/data/tiddlywiki/js/Upgrade.js b/test/data/tiddlywiki/js/Upgrade.js new file mode 100755 index 000000000..7399d5b30 --- /dev/null +++ b/test/data/tiddlywiki/js/Upgrade.js @@ -0,0 +1,117 @@ +//-- +//-- Upgrade macro +//-- + +config.macros.upgrade.handler = function(place) +{ + var w = new Wizard(); + w.createWizard(place,this.wizardTitle); + w.addStep(this.step1Title,this.step1Html.format([this.source,this.source])); + w.setButtons([{caption: this.upgradeLabel, tooltip: this.upgradePrompt, onClick: this.onClickUpgrade}]); +}; + +config.macros.upgrade.onClickUpgrade = function(e) +{ + var me = config.macros.upgrade; + var w = new Wizard(this); + if(window.location.protocol != "file:") { + alert(me.errorCantUpgrade); + return false; + } + if(story.areAnyDirty() || store.isDirty()) { + alert(me.errorNotSaved); + return false; + } + var localPath = getLocalPath(document.location.toString()); + var backupPath = getBackupPath(localPath,me.backupExtension); + w.setValue("backupPath",backupPath); + w.setButtons([],me.statusPreparingBackup); + var original = loadOriginal(localPath); + w.setButtons([],me.statusSavingBackup); + var backup = copyFile(backupPath,localPath); + if(!backup) + backup = saveFile(backupPath,original); + if(!backup) { + w.setButtons([],me.errorSavingBackup); + alert(me.errorSavingBackup); + return false; + } + w.setButtons([],me.statusLoadingCore); + var options = { + type:"GET", + url:me.source, + processData:false, + success:function(data,textStatus,jqXHR) { + me.onLoadCore(true,w,jqXHR.responseText,me.source,jqXHR); + }, + error:function(jqXHR,textStatus,errorThrown) { + me.onLoadCore(false,w,null,me.source,jqXHR); + } + }; + ajaxReq(options); + return false; +}; + +config.macros.upgrade.onLoadCore = function(status,params,responseText,url,xhr) +{ + var me = config.macros.upgrade; + var w = params; + var errMsg; + if(!status) + errMsg = me.errorLoadingCore; + var newVer = me.extractVersion(responseText); + if(!newVer) + errMsg = me.errorCoreFormat; + if(errMsg) { + w.setButtons([],errMsg); + alert(errMsg); + return; + } + var onStartUpgrade = function(e) { + w.setButtons([],me.statusSavingCore); + var localPath = getLocalPath(document.location.toString()); + saveFile(localPath,responseText); + w.setButtons([],me.statusReloadingCore); + var backupPath = w.getValue("backupPath"); + var newLoc = document.location.toString() + "?time=" + new Date().convertToYYYYMMDDHHMM() + "#upgrade:[[" + encodeURI(backupPath) + "]]"; + window.setTimeout(function () {window.location = newLoc;},10); + }; + var step2 = [me.step2Html_downgrade,me.step2Html_restore,me.step2Html_upgrade][compareVersions(version,newVer) + 1]; + w.addStep(me.step2Title,step2.format([formatVersion(newVer),formatVersion(version)])); + w.setButtons([{caption: me.startLabel, tooltip: me.startPrompt, onClick: onStartUpgrade},{caption: me.cancelLabel, tooltip: me.cancelPrompt, onClick: me.onCancel}]); +}; + +config.macros.upgrade.onCancel = function(e) +{ + var me = config.macros.upgrade; + var w = new Wizard(this); + w.addStep(me.step3Title,me.step3Html); + w.setButtons([]); + return false; +}; + +config.macros.upgrade.extractVersion = function(upgradeFile) +{ + var re = /^var version = \{title: "([^"]+)", major: (\d+), minor: (\d+), revision: (\d+)(, beta: (\d+)){0,1}, date: new Date\("([^"]+)"\)/mg; + var m = re.exec(upgradeFile); + return m ? {title: m[1], major: m[2], minor: m[3], revision: m[4], beta: m[6], date: new Date(m[7])} : null; +}; + +function upgradeFrom(path) +{ + var importStore = new TiddlyWiki(); + var tw = loadFile(path); + if(window.netscape !== undefined) + tw = convertUTF8ToUnicode(tw); + importStore.importTiddlyWiki(tw); + importStore.forEachTiddler(function(title,tiddler) { + if(!store.getTiddler(title)) { + store.addTiddler(tiddler); + } + }); + refreshDisplay(); + saveChanges(); //# To create appropriate Markup* sections + alert(config.messages.upgradeDone.format([formatVersion()])); + window.location = window.location.toString().substr(0,window.location.toString().lastIndexOf("?")); +} + diff --git a/test/data/tiddlywiki/js/Utilities.js b/test/data/tiddlywiki/js/Utilities.js new file mode 100755 index 000000000..f97569d1f --- /dev/null +++ b/test/data/tiddlywiki/js/Utilities.js @@ -0,0 +1,285 @@ +//-- +//-- TiddlyWiki-specific utility functions +//-- + +// Returns TiddlyWiki version string +function formatVersion(v) +{ + v = v || version; + return v.major + "." + v.minor + "." + v.revision + + (v.alpha ? " (alpha " + v.alpha + ")" : "") + + (v.beta ? " (beta " + v.beta + ")" : ""); +} + +//# Compares two TiddlyWiki version objects +//# Returns +1 if v2 is later than v1 +//# 0 if v2 is the same as v1 +//# -1 if v2 is earlier than v1 +//# version without a beta number is later than a version with a beta number +function compareVersions(v1,v2) +{ + var x1,x2,i,a = ["major","minor","revision"]; + for(i = 0; ix2) + return -1; + } + x1 = v1.beta || 9999; + x2 = v2.beta || 9999; + if(x1 x2 ? -1 : 0; +} + +function merge(dst,src,preserveExisting) +{ + var i; + for(i in src) { + if(!preserveExisting || dst[i] === undefined) + dst[i] = src[i]; + } + return dst; +} + +// Resolve the target object of an event +function resolveTarget(e) +{ + var obj; + if(e.target) + obj = e.target; + else if(e.srcElement) + obj = e.srcElement; + if(obj.nodeType == 3) // defeat Safari bug + obj = obj.parentNode; + return obj; +} + +// Returns a string containing the description of an exception, optionally prepended by a message +function exceptionText(e,message) +{ + var s = e.description || e.toString(); + return message ? "%0:\n%1".format([message,s]) : s; +} + +// Displays an alert of an exception description with optional message +function showException(e,message) +{ + alert(exceptionText(e,message)); +} + +function alertAndThrow(m) +{ + alert(m); + throw(m); +} + +function glyph(name) +{ + var g = config.glyphs; + var b = g.currBrowser; + if(b == null) { + b = 0; + while(b < g.browsers.length-1 && !g.browsers[b]()) + b++; + g.currBrowser = b; + } + if(!g.codes[name]) + return ""; + return g.codes[name][b]; +} + +function createTiddlyText(parent,text) +{ + return parent.appendChild(document.createTextNode(text)); +} + +function createTiddlyCheckbox(parent,caption,checked,onChange) +{ + var cb = document.createElement("input"); + cb.setAttribute("type","checkbox"); + cb.onclick = onChange; + parent.appendChild(cb); + cb.checked = checked; + cb.className = "chkOptionInput"; + if(caption) + wikify(caption,parent); + return cb; +} + +function createTiddlyElement(parent,element,id,className,text,attribs) +{ + var n,e = document.createElement(element); + if(className != null) + e.className = className; + if(id != null) + e.setAttribute("id",id); + if(text != null) + e.appendChild(document.createTextNode(text)); + if(attribs) { + for(n in attribs) { + e.setAttribute(n,attribs[n]); + } + } + if(parent != null) + parent.appendChild(e); + return e; +} + +function createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs) +{ + var i,btn = document.createElement("a"); + btn.setAttribute("href","javascript:;"); + if(action) { + btn.onclick = action; + } + if(tooltip) + btn.setAttribute("title",tooltip); + if(text) + btn.appendChild(document.createTextNode(text)); + btn.className = className || "button"; + if(id) + btn.id = id; + if(attribs) { + for(i in attribs) { + btn.setAttribute(i,attribs[i]); + } + } + if(parent) + parent.appendChild(btn); + if(accessKey) + btn.setAttribute("accessKey",accessKey); + return btn; +} + +//# Create a link to an external resource +//# place - element where the link should be created +//# url - link target +//# label - link text (optional) +function createExternalLink(place,url,label) +{ + var link = document.createElement("a"); + link.className = "externalLink"; + link.href = url; + var f = config.messages.externalLinkTooltip; + link.title = f ? f.format([url]) : url; + if(config.options.chkOpenInNewWindow) + link.target = "_blank"; + place.appendChild(link); + if(label) + createTiddlyText(link, label); + return link; +} + +function getTiddlyLinkInfo(title,currClasses) +{ + var classes = currClasses ? currClasses.split(" ") : []; + classes.pushUnique("tiddlyLink"); + var tiddler = store.fetchTiddler(title); + var subTitle; + if(tiddler) { + subTitle = tiddler.getSubtitle(); + classes.pushUnique("tiddlyLinkExisting"); + classes.remove("tiddlyLinkNonExisting"); + classes.remove("shadow"); + } else { + var f; + classes.remove("tiddlyLinkExisting"); + classes.pushUnique("tiddlyLinkNonExisting"); + if(store.isShadowTiddler(title)) { + f = config.messages.shadowedTiddlerToolTip; + classes.pushUnique("shadow"); + } else { + f = config.messages.undefinedTiddlerToolTip; + classes.remove("shadow"); + } + subTitle = f ? f.format([title]) : ""; + } + if(typeof config.annotations[title]=="string") + subTitle = config.annotations[title]; + return {classes: classes.join(" "),subTitle: subTitle}; +} + +// Event handler for clicking on a tiddly link +function onClickTiddlerLink(ev) +{ + var e = ev || window.event; + var target = resolveTarget(e); + var link = target; + var title = null; + var fields = null; + var noToggle = null; + do { + title = link.getAttribute("tiddlyLink"); + fields = link.getAttribute("tiddlyFields"); + noToggle = link.getAttribute("noToggle"); + link = link.parentNode; + } while(title == null && link != null); + if(!store.isShadowTiddler(title)) { + var f = fields ? fields.decodeHashMap() : {}; + fields = String.encodeHashMap(merge(f,config.defaultCustomFields,true)); + } + if(title) { + var toggling = e.metaKey || e.ctrlKey; + if(config.options.chkToggleLinks) + toggling = !toggling; + if(noToggle) + toggling = false; + if(store.getTiddler(title)) + fields = null; + story.displayTiddler(target,title,null,true,null,fields,toggling); + } + clearMessage(); + return false; +} + +//# Create a link to a particular tiddler +//# place - element where the link should be created +//# title - title of target tiddler +//# includeText - flag for whether to include the title as the text of the link +//# className - custom CSS class for the link +//# linkedFromTiddler - tiddler from which to inherit extended fields +//# noToggle - flag to force the link to open the target, even if chkToggleLinks is on +function createTiddlyLink(place,title,includeText,className,isStatic,linkedFromTiddler,noToggle) +{ + var title = jQuery.trim(title); + var text = includeText ? title : null; + var i = getTiddlyLinkInfo(title,className); + var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes); + if(isStatic) + btn.className += ' ' + className; + btn.setAttribute("refresh","link"); + btn.setAttribute("tiddlyLink",title); + if(noToggle) + btn.setAttribute("noToggle","true"); + if(linkedFromTiddler) { + var fields = linkedFromTiddler.getInheritedFields(); + if(fields) + btn.setAttribute("tiddlyFields",fields); + } + return btn; +} + +function refreshTiddlyLink(e,title) +{ + var i = getTiddlyLinkInfo(title,e.className); + e.className = i.classes; + e.title = i.subTitle; +} + +function createTiddlyDropDown(place,onchange,options,defaultValue) +{ + var sel = createTiddlyElement(place,"select"); + sel.onchange = onchange; + var t; + for(t=0; t 0) { + var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll); + openAll.setAttribute("tag",tag); + openAll.setAttribute("sortby",sortby); + createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div"); + for(r=0; r this.nextMatch) + this.outputText(this.output,this.nextMatch,formatterMatch.index); + // Set the match parameters for the handler + this.matchStart = formatterMatch.index; + this.matchLength = formatterMatch[0].length; + this.matchText = formatterMatch[0]; + this.nextMatch = this.formatter.formatterRegExp.lastIndex; + //# Figure out which formatter matched and call its handler + var t; + for(t=1; t this.nextMatch) + this.outputText(this.output,this.nextMatch,terminatorMatch.index); + //# Set the match parameters + this.matchText = terminatorMatch[1]; + this.matchLength = terminatorMatch[1].length; + this.matchStart = terminatorMatch.index; + this.nextMatch = this.matchStart + this.matchLength; + //# Restore the output pointer + this.output = oldOutput; + return; + } + //# It must be a formatter match; output any text before the match + if(formatterMatch.index > this.nextMatch) + this.outputText(this.output,this.nextMatch,formatterMatch.index); + //# Set the match parameters + this.matchStart = formatterMatch.index; + this.matchLength = formatterMatch[0].length; + this.matchText = formatterMatch[0]; + this.nextMatch = this.formatter.formatterRegExp.lastIndex; + //# Figure out which formatter matched and call its handler + var t; + for(t=1; t startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) { + //# Deal with any plain text before the highlight + if(this.highlightMatch.index > startPos) { + createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index)); + startPos = this.highlightMatch.index; + } + //# Deal with the highlight + var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos); + createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd)); + startPos = highlightEnd; + //# Nudge along to the next highlight if we're done with this one + if(startPos >= this.highlightRegExp.lastIndex) + this.highlightMatch = this.highlightRegExp.exec(this.source); + } + //# Do the unhighlighted text left over + if(startPos < endPos) { + createTiddlyText(place,this.source.substring(startPos,endPos)); + } +}; + +function wikify(source,output,highlightRegExp,tiddler) +{ + if(source) { + var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler); + var t0 = new Date(); + wikifier.subWikify(output); + if(tiddler && config.options.chkDisplayInstrumentation) + displayMessage("wikify:" +tiddler.title+ " in " + (new Date()-t0) + " ms"); + } +} + +function wikifyStatic(source,highlightRegExp,tiddler,format) +{ + var e = createTiddlyElement(document.body,"pre"); + e.style.display = "none"; + var html = ""; + if(source && source != "") { + if(!tiddler) + tiddler = new Tiddler("temp"); + var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler); + wikifier.isStatic = true; + wikifier.subWikify(e); + html = e.innerHTML; + jQuery(e).remove(); + } + return html; +} + +//# Wikify a string to plain text +//# text - text to wikify +//# limit - maximum number of characters to generate +//# tiddler - optional reference to the tiddler containing this text +function wikifyPlainText(text,limit,tiddler) +{ + if(limit > 0) + text = text.substr(0,limit); + var wikifier = new Wikifier(text,formatter,null,tiddler); + return wikifier.wikifyPlain(); +} + +//# Highlight plain text into an element +function highlightify(source,output,highlightRegExp,tiddler) +{ + if(source) { + var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler); + wikifier.outputText(output,0,source.length); + } +} + diff --git a/test/data/tiddlywiki/js/Wizard.js b/test/data/tiddlywiki/js/Wizard.js new file mode 100755 index 000000000..db2243056 --- /dev/null +++ b/test/data/tiddlywiki/js/Wizard.js @@ -0,0 +1,69 @@ +//-- +//-- Wizard support +//-- + +function Wizard(elem) +{ + if(elem) { + this.formElem = findRelated(elem,"wizard","className"); + this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling"); + this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling"); + } else { + this.formElem = null; + this.bodyElem = null; + this.footElem = null; + } +} + +Wizard.prototype.setValue = function(name,value) +{ + jQuery(this.formElem).data(name, value); +}; + +Wizard.prototype.getValue = function(name) +{ + return this.formElem ? jQuery(this.formElem).data(name) : null; +}; + +Wizard.prototype.createWizard = function(place,title) +{ + this.formElem = createTiddlyElement(place,"form",null,"wizard"); + createTiddlyElement(this.formElem,"h1",null,null,title); + this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody"); + this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter"); + return this.formElem; +}; + +Wizard.prototype.clear = function() +{ + jQuery(this.bodyElem).empty(); +}; + +Wizard.prototype.setButtons = function(buttonInfo,status) +{ + jQuery(this.footElem).empty(); + var t; + for(t=0; t 0) { + plugin.log.push(config.messages.pluginVersionError); + return false; + } + } + return true; +} + +function isPluginEnabled(plugin) +{ + if(plugin.tiddler.isTagged("systemConfigDisable")) { + plugin.log.push(config.messages.pluginDisabled); + return false; + } + return true; +} + diff --git a/test/data/tiddlywiki/js/split.recipe b/test/data/tiddlywiki/js/split.recipe new file mode 100755 index 000000000..dbc407a84 --- /dev/null +++ b/test/data/tiddlywiki/js/split.recipe @@ -0,0 +1,55 @@ +version: Version.js +js: Guide.js +js: Config.js +js: ConfigBrowser.js +js: Lingo.js +js: main.js +js: Paramifiers.js +js: FormatterHelpers.js +js: Formatter.js +js: Wikifier.js +js: Macros.js +js: NewTiddler.js +js: Search.js +js: Tabs.js +js: Toolbar.js +js: Commands.js +js: Tiddler.js +js: TiddlyWiki.js +js: Filters.js +js: TiddlerFields.js +js: Story.js +js: Backstage.js +js: Import.js +js: Upgrade.js +js: Sync.js +js: Manager.js +js: Messages.js +js: Refresh.js +js: Options.js +js: Saving.js +js: SavingRSS.js +js: FileSystem.js +js: FileSystemUtils.js +js: AdaptorBase.js +js: FileAdaptor.js +js: Http.js +js: Utilities.js +js: UtilitiesPopup.js +js: Animator.js +js: Morpher.js +js: Zoomer.js +js: Scroller.js +js: Slider.js +js: Popup.js +js: Wizard.js +js: ListView.js +js: BasicTypes.js +js: Strings.js +js: Dates.js +js: RGB.js +js: Dom.js +js: LoaderSaver.js +js: TW21Loader.js +js: TW21Saver.js + diff --git a/test/data/tiddlywiki/recipes/empty.2.0.11.html.recipe b/test/data/tiddlywiki/recipes/empty.2.0.11.html.recipe new file mode 100755 index 000000000..1f569b101 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.0.11.html.recipe @@ -0,0 +1,2 @@ +recipe: ../../../Tags/2.0.11/core/tiddlywiki.html.recipe +title: ../../../Tags/2.0.11/core/html/title.html diff --git a/test/data/tiddlywiki/recipes/empty.2.1.0.html.recipe b/test/data/tiddlywiki/recipes/empty.2.1.0.html.recipe new file mode 100755 index 000000000..22cfaa37c --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.1.0.html.recipe @@ -0,0 +1,3 @@ +recipe: ../../../Tags/REL-2.1.0_Final/core/tiddlywiki.html.recipe +title: ../../../Tags/REL-2.1.0_Final/core/html/title.html +tiddler: ../../association/plugins/LegacyStrikeThroughPlugin/LegacyStrikeThroughPlugin.js \ No newline at end of file diff --git a/test/data/tiddlywiki/recipes/empty.2.1.3.html.recipe b/test/data/tiddlywiki/recipes/empty.2.1.3.html.recipe new file mode 100755 index 000000000..e583b92f5 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.1.3.html.recipe @@ -0,0 +1,3 @@ +recipe: ../../../Tags/REL-2.1.3_Final/core/tiddlywiki.html.recipe +title: ../../../Tags/REL-2.1.3_Final/core/html/title.html +tiddler: ../../association/plugins/LegacyStrikeThroughPlugin/LegacyStrikeThroughPlugin.js \ No newline at end of file diff --git a/test/data/tiddlywiki/recipes/empty.2.2.0.html.recipe b/test/data/tiddlywiki/recipes/empty.2.2.0.html.recipe new file mode 100755 index 000000000..e8343f8f4 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.2.0.html.recipe @@ -0,0 +1,3 @@ +recipe: ../../../Tags/REL-2.2.0_Final/core/tiddlywiki.html.recipe +title: ../../../Tags/REL-2.2.0_Final/core/html/title.html +tiddler: ../../association/plugins/LegacyStrikeThroughPlugin/LegacyStrikeThroughPlugin.js \ No newline at end of file diff --git a/test/data/tiddlywiki/recipes/empty.2.2.5.html.recipe b/test/data/tiddlywiki/recipes/empty.2.2.5.html.recipe new file mode 100755 index 000000000..dba25b175 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.2.5.html.recipe @@ -0,0 +1,3 @@ +recipe: ../../../Tags/REL-2.2.5_Final/core/tiddlywiki.html.recipe +title: ../../../Tags/REL-2.2.5_Final/core/html/title.txt +tiddler: ../../association/plugins/LegacyStrikeThroughPlugin/LegacyStrikeThroughPlugin.js \ No newline at end of file diff --git a/test/data/tiddlywiki/recipes/empty.2.2.6.html.recipe b/test/data/tiddlywiki/recipes/empty.2.2.6.html.recipe new file mode 100755 index 000000000..5743657a6 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.2.6.html.recipe @@ -0,0 +1,3 @@ +recipe: ../../../Tags/core/REL-2.2.6_Final/tiddlywiki.html.recipe +title: ../../../Tags/core/REL-2.2.6_Final/html/title.txt +tiddler: ../../association/plugins/LegacyStrikeThroughPlugin/LegacyStrikeThroughPlugin.js \ No newline at end of file diff --git a/test/data/tiddlywiki/recipes/empty.2.3.0.html.recipe b/test/data/tiddlywiki/recipes/empty.2.3.0.html.recipe new file mode 100755 index 000000000..db123623c --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.3.0.html.recipe @@ -0,0 +1,3 @@ +recipe: ../../../Tags/core/REL-2.3.0_Final/tiddlywiki.html.recipe +title: ../../../Tags/core/REL-2.3.0_Final/html/title.txt +tiddler: ../../association/plugins/LegacyStrikeThroughPlugin/LegacyStrikeThroughPlugin.js \ No newline at end of file diff --git a/test/data/tiddlywiki/recipes/empty.2.4.0.html.recipe b/test/data/tiddlywiki/recipes/empty.2.4.0.html.recipe new file mode 100755 index 000000000..422d0f505 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.4.0.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.4.0_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.4.1.html.recipe b/test/data/tiddlywiki/recipes/empty.2.4.1.html.recipe new file mode 100755 index 000000000..54def12d0 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.4.1.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.4.1_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.4.2.html.recipe b/test/data/tiddlywiki/recipes/empty.2.4.2.html.recipe new file mode 100755 index 000000000..468a5fe71 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.4.2.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.4.2_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.4.3.html.recipe b/test/data/tiddlywiki/recipes/empty.2.4.3.html.recipe new file mode 100755 index 000000000..7ded46a4f --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.4.3.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.4.3_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.5.0.html.recipe b/test/data/tiddlywiki/recipes/empty.2.5.0.html.recipe new file mode 100755 index 000000000..af37f99ae --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.5.0.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.5.0_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.5.1.html.recipe b/test/data/tiddlywiki/recipes/empty.2.5.1.html.recipe new file mode 100755 index 000000000..6ed11d004 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.5.1.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.5.1_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.5.2.html.recipe b/test/data/tiddlywiki/recipes/empty.2.5.2.html.recipe new file mode 100755 index 000000000..e0e9f2a94 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.5.2.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.5.2_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.5.3.html.recipe b/test/data/tiddlywiki/recipes/empty.2.5.3.html.recipe new file mode 100755 index 000000000..a2c4d43a5 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.5.3.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.5.3_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/recipes/empty.2.6.0.html.recipe b/test/data/tiddlywiki/recipes/empty.2.6.0.html.recipe new file mode 100755 index 000000000..9493a5f37 --- /dev/null +++ b/test/data/tiddlywiki/recipes/empty.2.6.0.html.recipe @@ -0,0 +1 @@ +recipe: ../../../Tags/core/REL-2.6.0_Final/tiddlywiki.html.recipe diff --git a/test/data/tiddlywiki/shadows/ColorPalette.tid b/test/data/tiddlywiki/shadows/ColorPalette.tid new file mode 100755 index 000000000..c15cf4c9f --- /dev/null +++ b/test/data/tiddlywiki/shadows/ColorPalette.tid @@ -0,0 +1,17 @@ +title: ColorPalette + +Background: #fff +Foreground: #000 +PrimaryPale: #8cf +PrimaryLight: #18f +PrimaryMid: #04b +PrimaryDark: #014 +SecondaryPale: #ffc +SecondaryLight: #fe8 +SecondaryMid: #db4 +SecondaryDark: #841 +TertiaryPale: #eee +TertiaryLight: #ccc +TertiaryMid: #999 +TertiaryDark: #666 +Error: #f88 diff --git a/test/data/tiddlywiki/shadows/EditTemplate.tid b/test/data/tiddlywiki/shadows/EditTemplate.tid new file mode 100755 index 000000000..49f8997aa --- /dev/null +++ b/test/data/tiddlywiki/shadows/EditTemplate.tid @@ -0,0 +1,10 @@ +title: EditTemplate + + +
      +
      +
      +
      +
      +
      + diff --git a/test/data/tiddlywiki/shadows/GettingStarted.tid b/test/data/tiddlywiki/shadows/GettingStarted.tid new file mode 100755 index 000000000..5ad7b469d --- /dev/null +++ b/test/data/tiddlywiki/shadows/GettingStarted.tid @@ -0,0 +1,7 @@ +title: GettingStarted + +To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers: +* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar) +* [[MainMenu]]: The menu (usually on the left) +* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened +You'll also need to enter your username for signing your edits: <
    3. ab
      cd
      '; + actual = wikifyStatic("|a|b|\n|c|d|").toLowerCase(); + equals(actual,expected,'testing table formatting'); + }); + + test('Wikifier: wikifyStatic() 2', function() { + var expected = ""; + var actual = wikifyStatic(null); + equals(actual,expected,'it should return an empty string if source does not exist'); + actual = wikifyStatic(""); + equals(actual,expected,'it should return an empty string if source is an empty string'); + + source = "some text"; + actual = wikifyStatic(source); + ok(actual,'it should not require a tiddler to work'); + + /*'it should call subWikify() with the pre block as the only parameter': function() { + var funcToMock = 'Wikifier.prototype.subWikify'; + tests_mock.before(funcToMock,function() { + tests_mock.frame[funcToMock].funcArgs = arguments; + }); + wikifyStatic(source); + var tests_mock_return = tests_mock.after(funcToMock); + var expected = "PRE"; + equals(tests_mock_return.called,true); + equals(tests_mock_return.funcArgs.length,1); + equals(tests_mock_return.funcArgs[0].nodeName,expected); + },*/ + + expected = "string"; + actual = typeof wikifyStatic(source); + equals(actual,expected,'it should return a text string'); + + place = document.createElement("div"); + d = document.body.appendChild(place); + d.style.display = "none"; + expected = document.body.childNodes.length; + var html = wikifyStatic(source); + actual = document.body.childNodes.length; + equals(actual,expected,'it should not leave any elements attached to the document body after returning'); + removeNode(d); + }); + + test('Wikifier: wikifyStatic() 3 htmlEntitiesEncoding', function() { + wikifier_input_strings = { + illegal01:"&a#x0301;", + e160:" ", + e161:"¡", + e162:"¢", + e163:"£", + e255:"ÿ", + e8800:"≠", + e0x300:"̀", + e0x0300:"̀" + }; + + wikifier_output_strings = { + illegal01:"&a#x0301;", + e160:" ", + e161:"¡", + e162:"¢", + e163:"£", + e255:"ÿ", + e8800:"", + e0x300:"̀", + e0x0300:"̀" + }; + + formatter = new Formatter(config.formatters); + var actual = ""; + var expected = ""; + for (var i in wikifier_input_strings) { + actual = wikifyStatic(wikifier_input_strings[i]).toLowerCase(); + expected = wikifier_output_strings[i]; + equals(actual,expected,'testing input strings for Formatter.htmlEntitiesEncoding'+wikifier_input_strings[i]); + } + }); +}); diff --git a/test/data/tiddlywiki/test/js/Wizard.js b/test/data/tiddlywiki/test/js/Wizard.js new file mode 100755 index 000000000..de0517ff7 --- /dev/null +++ b/test/data/tiddlywiki/test/js/Wizard.js @@ -0,0 +1,59 @@ +jQuery(document).ready(function(){ + + module("Wizard.js"); + + test("Wizard: construction", function() { + expect(1); + + var w = new Wizard(); + var actual = w.formElem===null && w.bodyElem===null && w.footElem===null; + ok(actual==true,'properties should be null when constructed with no parameters'); + + }); + + test("Wizard: setValue / getValue (no formEl)", function() { + var w = new Wizard(); + var val1 = w.getValue("test"); + w.setValue("test", "foo"); + var val2 = w.getValue("test"); + strictEqual(val1, null, "no value set"); + strictEqual(val2, null, "value could not be set as no formEl"); + }); + + test("Wizard: setValue / getValue (formEl)", function() { + var w = new Wizard(); + w.createWizard($("
      ")[0], "My Title"); + var val1 = w.getValue("test1"); + var elem = $("
      ").addClass("foo")[0]; + w.setValue("test1", "foo"); + w.setValue("test2", ["a list", "of items"]); + w.setValue("test3", { name: "data", val: "foo" }); + w.setValue("test4", elem); + var val2 = w.getValue("test1"); + var val3 = w.getValue("test2"); + var val4 = w.getValue("test3"); + var val5 = w.getValue("test4"); + + strictEqual(val1, undefined, "no value set but returns undefined if formEl exists"); + strictEqual(val2, "foo", "value should be set in this situation"); + strictEqual(val3.length, 2, "array set successfully (1/2)"); + strictEqual(val3[0], "a list", "array set successfully (2/2)"); + strictEqual(val4.name, "data", "object set successfully"); + strictEqual($(val5).hasClass("foo"), true, "element set successfully"); + }); + + test("Wizard: createWizard", function() { + var elem = $(place)[0]; + var wizard = new Wizard(); + wizard.createWizard(place, 'Import a TiddlyWiki'); + strictEqual(wizard.formElem.nodeName, "FORM", "a form element set."); + }); + + test("Wizard: setValue of existing property name on node", function() { + var w = new Wizard(); + w.createWizard($("
      ")[0], "My Title"); + w.setValue("nodeName", "foo"); + var mode = w.getValue("nodeName"); + strictEqual(mode, "foo", "reserved names should be possible to set.") + }); +}); diff --git a/test/data/tiddlywiki/test/js/Zoomer.js b/test/data/tiddlywiki/test/js/Zoomer.js new file mode 100755 index 000000000..3260ea900 --- /dev/null +++ b/test/data/tiddlywiki/test/js/Zoomer.js @@ -0,0 +1,32 @@ +jQuery(document).ready(function(){ + + module("Zoomer"); + + test("Zoomer functions", function() { + + var zoomer_elem = document.body.appendChild(document.createElement("div")); + var zoomer_text = "hi!"; + var actual = new Zoomer(zoomer_text,zoomer_elem,zoomer_elem); + + ok(actual,'it should return a Morpher object'); + + delete zoomer_elem; + delete zoomer_text; + + zoomer_elem = document.body.appendChild(document.createElement("div")); + zoomer_text = "hi!"; + + var before = document.body.childNodes.length; + var z = new Zoomer(zoomer_text,zoomer_elem,zoomer_elem); + var after = document.body.childNodes.length; + actual = after - before; + var expected = 1; + same(actual,expected,'it should create a div as child of the body'); + actual = document.body.childNodes[document.body.childNodes.length-1].nodeName; + expected = "DIV"; + same(actual,expected,'it should create a div with the class of "zoomer"'); + + delete zoomer_elem; + delete zoomer_text; + }); +}); diff --git a/test/data/tiddlywiki/test/js/split.recipe b/test/data/tiddlywiki/test/js/split.recipe new file mode 100755 index 000000000..a444c4ada --- /dev/null +++ b/test/data/tiddlywiki/test/js/split.recipe @@ -0,0 +1,26 @@ +jquery: Animator.js +jquery: SavingRSS.js +jquery: Numbers.js +jquery: Arrays.js +jquery: Crypto.js +jquery: DOM.js +jquery: Dates.js +jquery: Encoding.js +jquery: FileSystem.js +jquery: LoadingSaving.js +jquery: Macros.js +#jquery: main.js +jquery: Options.js +jquery: RGB.js +jquery: Shadows.js +jquery: Strings.js +jquery: Tiddler.js +jquery: TiddlerFields.js +jquery: TiddlyWiki.js +jquery: Filters.js +jquery: TW21Saver.js +jquery: Utilities.js +jquery: Version.js +jquery: Wikifier.js +jquery: Wizard.js +#jquery: Zoomer.js diff --git a/test/data/tiddlywiki/test/js/template.js b/test/data/tiddlywiki/test/js/template.js new file mode 100755 index 000000000..2285a824c --- /dev/null +++ b/test/data/tiddlywiki/test/js/template.js @@ -0,0 +1,11 @@ +jQuery(document).ready(function() { + module(""); + + test("
      ", function() { + var actual, expected; + + actual = <...>; + expected = <...>; + same(actual, expected, ""); + }); +}); diff --git a/test/data/tiddlywiki/test/qunit/delayTestExecution.js b/test/data/tiddlywiki/test/qunit/delayTestExecution.js new file mode 100755 index 000000000..2f3817b7e --- /dev/null +++ b/test/data/tiddlywiki/test/qunit/delayTestExecution.js @@ -0,0 +1,9 @@ +jQuery(document).ready(function() { + test("Wait until TiddlyWiki starts", function() { + stop(); + }); +}); + +jQuery().bind("startup", function() { + start(); +}); diff --git a/test/data/tiddlywiki/test/qunit/qunit.css b/test/data/tiddlywiki/test/qunit/qunit.css new file mode 100755 index 000000000..10e765761 --- /dev/null +++ b/test/data/tiddlywiki/test/qunit/qunit.css @@ -0,0 +1,163 @@ +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; +} + +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-tests { font-size: smaller; } + + +/** Resets */ + +#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { + margin: 0; + padding: 0; +} + + +/** Header */ + +#qunit-header { + padding: 0.5em 0 0.5em 1em; + + color: #8699a4; + background-color: #0d3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: normal; + + border-radius: 15px 15px 0 0; + -moz-border-radius: 15px 15px 0 0; + -webkit-border-top-right-radius: 15px; + -webkit-border-top-left-radius: 15px; +} + +#qunit-header a { + text-decoration: none; + color: #c2ccd1; +} + +#qunit-header a:hover, +#qunit-header a:focus { + color: #fff; +} + +#qunit-banner { + height: 5px; +} + +#qunit-testrunner-toolbar { + padding: 0em 0 0.5em 2em; +} + +#qunit-userAgent { + padding: 0.5em 0 0.5em 2.5em; + background-color: #2b81af; + color: #fff; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + + +/** Tests: Pass/Fail */ + +#qunit-tests { + list-style-position: inside; +} + +#qunit-tests li { + padding: 0.4em 0.5em 0.4em 2.5em; + border-bottom: 1px solid #fff; + list-style-position: inside; +} + +#qunit-tests li strong { + cursor: pointer; +} + +#qunit-tests ol { + margin-top: 0.5em; + padding: 0.5em; + + background-color: #fff; + + border-radius: 15px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + + box-shadow: inset 0px 2px 13px #999; + -moz-box-shadow: inset 0px 2px 13px #999; + -webkit-box-shadow: inset 0px 2px 13px #999; +} + +/*** Test Counts */ + +#qunit-tests b.counts { color: black; } +#qunit-tests b.passed { color: #5E740B; } +#qunit-tests b.failed { color: #710909; } + +#qunit-tests li li { + margin: 0.5em; + padding: 0.4em 0.5em 0.4em 0.5em; + background-color: #fff; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + +#qunit-tests li li.pass { + color: #5E740B; + background-color: #fff; + border-left: 26px solid #C6E746; +} + +#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } +#qunit-tests .pass .test-name { color: #366097; } + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected { color: #999999; } + +#qunit-banner.qunit-pass { background-color: #C6E746; } + +/*** Failing Styles */ + +#qunit-tests li li.fail { + color: #710909; + background-color: #fff; + border-left: 26px solid #EE5757; +} + +#qunit-tests .fail { color: #000000; background-color: #EE5757; } +#qunit-tests .fail .test-name, +#qunit-tests .fail .module-name { color: #000000; } + +#qunit-tests .fail .test-actual { color: #EE5757; } +#qunit-tests .fail .test-expected { color: green; } + +#qunit-banner.qunit-fail, +#qunit-testrunner-toolbar { background-color: #EE5757; } + + +/** Footer */ + +#qunit-testresult { + padding: 0.5em 0.5em 0.5em 2.5em; + + color: #2b81af; + background-color: #D2E0E6; + + border-radius: 0 0 15px 15px; + -moz-border-radius: 0 0 15px 15px; + -webkit-border-bottom-right-radius: 15px; + -webkit-border-bottom-left-radius: 15px; +} + +/** Fixture */ + +#qunit-fixture { + position: absolute; + top: -10000px; + left: -10000px; +} diff --git a/test/data/tiddlywiki/test/qunit/qunit.js b/test/data/tiddlywiki/test/qunit/qunit.js new file mode 100755 index 000000000..d54b72ae6 --- /dev/null +++ b/test/data/tiddlywiki/test/qunit/qunit.js @@ -0,0 +1,1297 @@ +/* + * QUnit - A JavaScript Unit Testing Framework + * + * http://docs.jquery.com/QUnit + * + * Copyright (c) 2009 John Resig, Jörn Zaefferer + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + */ + +(function(window) { + +var QUnit = { + + // call on start of module test to prepend name to all tests + module: function(name, testEnvironment) { + config.currentModule = name; + + synchronize(function() { + if ( config.previousModule ) { + QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); + } + + config.previousModule = config.currentModule; + config.currentModule = name; + config.moduleTestEnvironment = testEnvironment; + config.moduleStats = { all: 0, bad: 0 }; + + QUnit.moduleStart( name, testEnvironment ); + }); + }, + + asyncTest: function(testName, expected, callback) { + if ( arguments.length === 2 ) { + callback = expected; + expected = 0; + } + + QUnit.test(testName, expected, callback, true); + }, + + test: function(testName, expected, callback, async) { + var name = '' + testName + '', testEnvironment, testEnvironmentArg; + + if ( arguments.length === 2 ) { + callback = expected; + expected = null; + } + // is 2nd argument a testEnvironment? + if ( expected && typeof expected === 'object') { + testEnvironmentArg = expected; + expected = null; + } + + if ( config.currentModule ) { + name = '' + config.currentModule + ": " + name; + } + + if ( !validTest(config.currentModule + ": " + testName) ) { + return; + } + + synchronize(function() { + + testEnvironment = extend({ + setup: function() {}, + teardown: function() {} + }, config.moduleTestEnvironment); + if (testEnvironmentArg) { + extend(testEnvironment,testEnvironmentArg); + } + + QUnit.testStart( testName, testEnvironment ); + + // allow utility functions to access the current test environment + QUnit.current_testEnvironment = testEnvironment; + + config.assertions = []; + config.expected = expected; + + var tests = id("qunit-tests"); + if (tests) { + var b = document.createElement("strong"); + b.innerHTML = "Running " + name; + var li = document.createElement("li"); + li.appendChild( b ); + li.id = "current-test-output"; + tests.appendChild( li ); + } + + try { + if ( !config.pollution ) { + saveGlobal(); + } + + testEnvironment.setup.call(testEnvironment); + } catch(e) { + QUnit.ok( false, "Setup failed on " + name + ": " + e.message ); + } + }); + + synchronize(function() { + if ( async ) { + QUnit.stop(); + } + + try { + callback.call(testEnvironment); + } catch(e) { + fail("Test " + name + " died, exception and test follows", e, callback); + QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); + // else next test will carry the responsibility + saveGlobal(); + + // Restart the tests if they're blocking + if ( config.blocking ) { + start(); + } + } + }); + + synchronize(function() { + try { + checkPollution(); + testEnvironment.teardown.call(testEnvironment); + } catch(e) { + QUnit.ok( false, "Teardown failed on " + name + ": " + e.message ); + } + }); + + synchronize(function() { + var good = 0, bad = 0, + tests = id("qunit-tests"); + + config.stats.all += config.assertions.length; + config.moduleStats.all += config.assertions.length; + + if ( tests ) { + var ol = document.createElement("ol"); + + for ( var i = 0; i < config.assertions.length; i++ ) { + var assertion = config.assertions[i]; + + var li = document.createElement("li"); + li.className = assertion.result ? "pass" : "fail"; + li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); + ol.appendChild( li ); + + if ( assertion.result ) { + good++; + } else { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + if (bad == 0) { + ol.style.display = "none"; + } + + var b = document.createElement("strong"); + b.innerHTML = name + " (" + bad + ", " + good + ", " + config.assertions.length + ")"; + + addEvent(b, "click", function() { + var next = b.nextSibling, display = next.style.display; + next.style.display = display === "none" ? "block" : "none"; + }); + + addEvent(b, "dblclick", function(e) { + var target = e && e.target ? e.target : window.event.srcElement; + if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { + target = target.parentNode; + } + if ( window.location && target.nodeName.toLowerCase() === "strong" ) { + window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, "")); + } + }); + + var li = id("current-test-output"); + li.id = ""; + li.className = bad ? "fail" : "pass"; + li.style.display = resultDisplayStyle(!bad); + li.removeChild( li.firstChild ); + li.appendChild( b ); + li.appendChild( ol ); + + if ( bad ) { + var toolbar = id("qunit-testrunner-toolbar"); + if ( toolbar ) { + toolbar.style.display = "block"; + id("qunit-filter-pass").disabled = null; + id("qunit-filter-missing").disabled = null; + } + } + + } else { + for ( var i = 0; i < config.assertions.length; i++ ) { + if ( !config.assertions[i].result ) { + bad++; + config.stats.bad++; + config.moduleStats.bad++; + } + } + } + + try { + QUnit.reset(); + } catch(e) { + fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, QUnit.reset); + } + + if ( config.expected && config.expected != config.assertions.length ) { + QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" ); + } + + QUnit.testDone( testName, bad, config.assertions.length ); + + if ( !window.setTimeout && !config.queue.length ) { + done(); + } + }); + + synchronize( done ); + }, + + /** + * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. + */ + expect: function(asserts) { + config.expected = asserts; + }, + + /** + * Asserts true. + * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); + */ + ok: function(a, msg) { + a = !!a; + var details = { + result: a, + message: msg + }; + msg = escapeHtml(msg); + QUnit.log(a, msg, details); + config.assertions.push({ + result: a, + message: msg + }); + }, + + /** + * Checks that the first two arguments are equal, with an optional message. + * Prints out both actual and expected values. + * + * Prefered to ok( actual == expected, message ) + * + * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); + * + * @param Object actual + * @param Object expected + * @param String message (optional) + */ + equal: function(actual, expected, message) { + QUnit.push(expected == actual, actual, expected, message); + }, + + notEqual: function(actual, expected, message) { + QUnit.push(expected != actual, actual, expected, message); + }, + + deepEqual: function(actual, expected, message) { + QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); + }, + + notDeepEqual: function(actual, expected, message) { + QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); + }, + + strictEqual: function(actual, expected, message) { + QUnit.push(expected === actual, actual, expected, message); + }, + + notStrictEqual: function(actual, expected, message) { + QUnit.push(expected !== actual, actual, expected, message); + }, + + raises: function(fn, message) { + try { + fn(); + ok( false, message ); + } + catch (e) { + ok( true, message ); + } + }, + + start: function() { + // A slight delay, to avoid any current callbacks + if ( window.setTimeout ) { + window.setTimeout(function() { + if ( config.timeout ) { + clearTimeout(config.timeout); + } + + config.blocking = false; + process(); + }, 13); + } else { + config.blocking = false; + process(); + } + }, + + stop: function(timeout) { + config.blocking = true; + + if ( timeout && window.setTimeout ) { + config.timeout = window.setTimeout(function() { + QUnit.ok( false, "Test timed out" ); + QUnit.start(); + }, timeout); + } + } + +}; + +// Backwards compatibility, deprecated +QUnit.equals = QUnit.equal; +QUnit.same = QUnit.deepEqual; + +// Maintain internal state +var config = { + // The queue of tests to run + queue: [], + + // block until document ready + blocking: true +}; + +// Load paramaters +(function() { + var location = window.location || { search: "", protocol: "file:" }, + GETParams = location.search.slice(1).split('&'); + + for ( var i = 0; i < GETParams.length; i++ ) { + GETParams[i] = decodeURIComponent( GETParams[i] ); + if ( GETParams[i] === "noglobals" ) { + GETParams.splice( i, 1 ); + i--; + config.noglobals = true; + } else if ( GETParams[i].search('=') > -1 ) { + GETParams.splice( i, 1 ); + i--; + } + } + + // restrict modules/tests by get parameters + config.filters = GETParams; + + // Figure out if we're running the tests from a server or not + QUnit.isLocal = !!(location.protocol === 'file:'); +})(); + +// Expose the API as global variables, unless an 'exports' +// object exists, in that case we assume we're in CommonJS +if ( typeof exports === "undefined" || typeof require === "undefined" ) { + extend(window, QUnit); + window.QUnit = QUnit; +} else { + extend(exports, QUnit); + exports.QUnit = QUnit; +} + +// define these after exposing globals to keep them in these QUnit namespace only +extend(QUnit, { + config: config, + + // Initialize the configuration options + init: function() { + extend(config, { + stats: { all: 0, bad: 0 }, + moduleStats: { all: 0, bad: 0 }, + started: +new Date, + updateRate: 1000, + blocking: false, + autostart: true, + autorun: false, + assertions: [], + filters: [], + queue: [] + }); + + var tests = id("qunit-tests"), + banner = id("qunit-banner"), + result = id("qunit-testresult"); + + if ( tests ) { + tests.innerHTML = ""; + } + + if ( banner ) { + banner.className = ""; + } + + if ( result ) { + result.parentNode.removeChild( result ); + } + }, + + /** + * Resets the test setup. Useful for tests that modify the DOM. + * + * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. + */ + reset: function() { + if ( window.jQuery ) { + jQuery( "#main, #qunit-fixture" ).html( config.fixture ); + } else { + var main = id( 'main' ) || id( 'qunit-fixture' ); + if ( main ) { + main.innerHTML = config.fixture; + } + } + }, + + /** + * Trigger an event on an element. + * + * @example triggerEvent( document.body, "click" ); + * + * @param DOMElement elem + * @param String type + */ + triggerEvent: function( elem, type, event ) { + if ( document.createEvent ) { + event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + elem.dispatchEvent( event ); + + } else if ( elem.fireEvent ) { + elem.fireEvent("on"+type); + } + }, + + // Safe object type checking + is: function( type, obj ) { + return QUnit.objectType( obj ) == type; + }, + + objectType: function( obj ) { + if (typeof obj === "undefined") { + return "undefined"; + + // consider: typeof null === object + } + if (obj === null) { + return "null"; + } + + var type = Object.prototype.toString.call( obj ) + .match(/^\[object\s(.*)\]$/)[1] || ''; + + switch (type) { + case 'Number': + if (isNaN(obj)) { + return "nan"; + } else { + return "number"; + } + case 'String': + case 'Boolean': + case 'Array': + case 'Date': + case 'RegExp': + case 'Function': + return type.toLowerCase(); + } + if (typeof obj === "object") { + return "object"; + } + return undefined; + }, + + push: function(result, actual, expected, message) { + var details = { + result: result, + message: message, + actual: actual, + expected: expected + }; + + message = escapeHtml(message) || (result ? "okay" : "failed"); + message = '' + message + ""; + expected = escapeHtml(QUnit.jsDump.parse(expected)); + actual = escapeHtml(QUnit.jsDump.parse(actual)); + var output = message + ', expected: ' + expected + ''; + if (actual != expected) { + output += ' result: ' + actual + ', diff: ' + QUnit.diff(expected, actual); + } + + QUnit.log(result, message, details); + + config.assertions.push({ + result: !!result, + message: output + }); + }, + + // Logging callbacks + begin: function() {}, + done: function(failures, total) {}, + log: function(result, message) {}, + testStart: function(name, testEnvironment) {}, + testDone: function(name, failures, total) {}, + moduleStart: function(name, testEnvironment) {}, + moduleDone: function(name, failures, total) {} +}); + +if ( typeof document === "undefined" || document.readyState === "complete" ) { + config.autorun = true; +} + +addEvent(window, "load", function() { + QUnit.begin(); + + // Initialize the config, saving the execution queue + var oldconfig = extend({}, config); + QUnit.init(); + extend(config, oldconfig); + + config.blocking = false; + + var userAgent = id("qunit-userAgent"); + if ( userAgent ) { + userAgent.innerHTML = navigator.userAgent; + } + var banner = id("qunit-header"); + if ( banner ) { + var paramsIndex = location.href.lastIndexOf(location.search); + if ( paramsIndex > -1 ) { + var mainPageLocation = location.href.slice(0, paramsIndex); + if ( mainPageLocation == location.href ) { + banner.innerHTML = ' ' + banner.innerHTML + ' '; + } else { + var testName = decodeURIComponent(location.search.slice(1)); + banner.innerHTML = '' + banner.innerHTML + '' + testName + ''; + } + } + } + + var toolbar = id("qunit-testrunner-toolbar"); + if ( toolbar ) { + toolbar.style.display = "none"; + + var filter = document.createElement("input"); + filter.type = "checkbox"; + filter.id = "qunit-filter-pass"; + filter.disabled = true; + addEvent( filter, "click", function() { + var li = document.getElementsByTagName("li"); + for ( var i = 0; i < li.length; i++ ) { + if ( li[i].className.indexOf("pass") > -1 ) { + li[i].style.display = filter.checked ? "none" : ""; + } + } + }); + toolbar.appendChild( filter ); + + var label = document.createElement("label"); + label.setAttribute("for", "qunit-filter-pass"); + label.innerHTML = "Hide passed tests"; + toolbar.appendChild( label ); + + var missing = document.createElement("input"); + missing.type = "checkbox"; + missing.id = "qunit-filter-missing"; + missing.disabled = true; + addEvent( missing, "click", function() { + var li = document.getElementsByTagName("li"); + for ( var i = 0; i < li.length; i++ ) { + if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) { + li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block"; + } + } + }); + toolbar.appendChild( missing ); + + label = document.createElement("label"); + label.setAttribute("for", "qunit-filter-missing"); + label.innerHTML = "Hide missing tests (untested code is broken code)"; + toolbar.appendChild( label ); + } + + var main = id('main') || id('qunit-fixture'); + if ( main ) { + config.fixture = main.innerHTML; + } + + if (config.autostart) { + QUnit.start(); + } +}); + +function done() { + if ( config.doneTimer && window.clearTimeout ) { + window.clearTimeout( config.doneTimer ); + config.doneTimer = null; + } + + if ( config.queue.length ) { + config.doneTimer = window.setTimeout(function(){ + if ( !config.queue.length ) { + done(); + } else { + synchronize( done ); + } + }, 13); + + return; + } + + config.autorun = true; + + // Log the last module results + if ( config.currentModule ) { + QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); + } + + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + html = ['Tests completed in ', + +new Date - config.started, ' milliseconds.
      ', + '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join(''); + + if ( banner ) { + banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); + } + + if ( tests ) { + var result = id("qunit-testresult"); + + if ( !result ) { + result = document.createElement("p"); + result.id = "qunit-testresult"; + result.className = "result"; + tests.parentNode.insertBefore( result, tests.nextSibling ); + } + + result.innerHTML = html; + } + + QUnit.done( config.stats.bad, config.stats.all ); +} + +function validTest( name ) { + var i = config.filters.length, + run = false; + + if ( !i ) { + return true; + } + + while ( i-- ) { + var filter = config.filters[i], + not = filter.charAt(0) == '!'; + + if ( not ) { + filter = filter.slice(1); + } + + if ( name.indexOf(filter) !== -1 ) { + return !not; + } + + if ( not ) { + run = true; + } + } + + return run; +} + +function resultDisplayStyle(passed) { + return passed && id("qunit-filter-pass") && id("qunit-filter-pass").checked ? 'none' : ''; +} + +function escapeHtml(s) { + if (!s) { + return ""; + } + s = s + ""; + return s.replace(/[\&"<>\\]/g, function(s) { + switch(s) { + case "&": return "&"; + case "\\": return "\\\\"; + case '"': return '\"'; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); +} + +function synchronize( callback ) { + config.queue.push( callback ); + + if ( config.autorun && !config.blocking ) { + process(); + } +} + +function process() { + var start = (new Date()).getTime(); + + while ( config.queue.length && !config.blocking ) { + if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { + config.queue.shift()(); + + } else { + setTimeout( process, 13 ); + break; + } + } +} + +function saveGlobal() { + config.pollution = []; + + if ( config.noglobals ) { + for ( var key in window ) { + config.pollution.push( key ); + } + } +} + +function checkPollution( name ) { + var old = config.pollution; + saveGlobal(); + + var newGlobals = diff( old, config.pollution ); + if ( newGlobals.length > 0 ) { + ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); + config.expected++; + } + + var deletedGlobals = diff( config.pollution, old ); + if ( deletedGlobals.length > 0 ) { + ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); + config.expected++; + } +} + +// returns a new Array with the elements that are in a but not in b +function diff( a, b ) { + var result = a.slice(); + for ( var i = 0; i < result.length; i++ ) { + for ( var j = 0; j < b.length; j++ ) { + if ( result[i] === b[j] ) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; +} + +function fail(message, exception, callback) { + if ( typeof console !== "undefined" && console.error && console.warn ) { + console.error(message); + console.error(exception); + console.warn(callback.toString()); + + } else if ( window.opera && opera.postError ) { + opera.postError(message, exception, callback.toString); + } +} + +function extend(a, b) { + for ( var prop in b ) { + a[prop] = b[prop]; + } + + return a; +} + +function addEvent(elem, type, fn) { + if ( elem.addEventListener ) { + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, fn ); + } else { + fn(); + } +} + +function id(name) { + return !!(typeof document !== "undefined" && document && document.getElementById) && + document.getElementById( name ); +} + +// Test for equality any JavaScript type. +// Discussions and reference: http://philrathe.com/articles/equiv +// Test suites: http://philrathe.com/tests/equiv +// Author: Philippe Rathé +QUnit.equiv = function () { + + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + var parents = []; // stack to avoiding loops from circular referencing + + // Call the o related callback with the given arguments. + function bindCallbacks(o, callbacks, args) { + var prop = QUnit.objectType(o); + if (prop) { + if (QUnit.objectType(callbacks[prop]) === "function") { + return callbacks[prop].apply(callbacks, args); + } else { + return callbacks[prop]; // or undefined + } + } + } + + var callbacks = function () { + + // for string, boolean, number and null + function useStrictEquality(b, a) { + if (b instanceof a.constructor || a instanceof b.constructor) { + // to catch short annotaion VS 'new' annotation of a declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } else { + return a === b; + } + } + + return { + "string": useStrictEquality, + "boolean": useStrictEquality, + "number": useStrictEquality, + "null": useStrictEquality, + "undefined": useStrictEquality, + + "nan": function (b) { + return isNaN(b); + }, + + "date": function (b, a) { + return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); + }, + + "regexp": function (b, a) { + return QUnit.objectType(b) === "regexp" && + a.source === b.source && // the regex itself + a.global === b.global && // and its modifers (gmi) ... + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline; + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function": function () { + var caller = callers[callers.length - 1]; + return caller !== Object && + typeof caller !== "undefined"; + }, + + "array": function (b, a) { + var i, j, loop; + var len; + + // b could be an object literal here + if ( ! (QUnit.objectType(b) === "array")) { + return false; + } + + len = a.length; + if (len !== b.length) { // safe and faster + return false; + } + + //track reference to avoid circular references + parents.push(a); + for (i = 0; i < len; i++) { + loop = false; + for(j=0;j= 0) { + type = "array"; + } else { + type = typeof obj; + } + return type; + }, + separator:function() { + return this.multiline ? this.HTML ? '
      ' : '\n' : this.HTML ? ' ' : ' '; + }, + indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing + if ( !this.multiline ) + return ''; + var chr = this.indentChar; + if ( this.HTML ) + chr = chr.replace(/\t/g,' ').replace(/ /g,' '); + return Array( this._depth_ + (extra||0) ).join(chr); + }, + up:function( a ) { + this._depth_ += a || 1; + }, + down:function( a ) { + this._depth_ -= a || 1; + }, + setParser:function( name, parser ) { + this.parsers[name] = parser; + }, + // The next 3 are exposed so you can use them + quote:quote, + literal:literal, + join:join, + // + _depth_: 1, + // This is the list of parsers, to modify them, use jsDump.setParser + parsers:{ + window: '[Window]', + document: '[Document]', + error:'[ERROR]', //when no parser is found, shouldn't happen + unknown: '[Unknown]', + 'null':'null', + undefined:'undefined', + 'function':function( fn ) { + var ret = 'function', + name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE + if ( name ) + ret += ' ' + name; + ret += '('; + + ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join(''); + return join( ret, this.parse(fn,'functionCode'), '}' ); + }, + array: array, + nodelist: array, + arguments: array, + object:function( map ) { + var ret = [ ]; + this.up(); + for ( var key in map ) + ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) ); + this.down(); + return join( '{', ret, '}' ); + }, + node:function( node ) { + var open = this.HTML ? '<' : '<', + close = this.HTML ? '>' : '>'; + + var tag = node.nodeName.toLowerCase(), + ret = open + tag; + + for ( var a in this.DOMAttrs ) { + var val = node[this.DOMAttrs[a]]; + if ( val ) + ret += ' ' + a + '=' + this.parse( val, 'attribute' ); + } + return ret + close + open + '/' + tag + close; + }, + functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function + var l = fn.length; + if ( !l ) return ''; + + var args = Array(l); + while ( l-- ) + args[l] = String.fromCharCode(97+l);//97 is 'a' + return ' ' + args.join(', ') + ' '; + }, + key:quote, //object calls it internally, the key part of an item in a map + functionCode:'[code]', //function calls it internally, it's the content of the function + attribute:quote, //node calls it internally, it's an html attribute value + string:quote, + date:quote, + regexp:literal, //regex + number:literal, + 'boolean':literal + }, + DOMAttrs:{//attributes to dump from nodes, name=>realName + id:'id', + name:'name', + 'class':'className' + }, + HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) + indentChar:' ',//indentation unit + multiline:false //if true, items in a collection, are separated by a \n, else just a space. + }; + + return jsDump; +})(); + +// from Sizzle.js +function getText( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += getText( elem.childNodes ); + } + } + + return ret; +}; + +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + * + * Usage: QUnit.diff(expected, actual) + * + * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" + */ +QUnit.diff = (function() { + function diff(o, n){ + var ns = new Object(); + var os = new Object(); + + for (var i = 0; i < n.length; i++) { + if (ns[n[i]] == null) + ns[n[i]] = { + rows: new Array(), + o: null + }; + ns[n[i]].rows.push(i); + } + + for (var i = 0; i < o.length; i++) { + if (os[o[i]] == null) + os[o[i]] = { + rows: new Array(), + n: null + }; + os[o[i]].rows.push(i); + } + + for (var i in ns) { + if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { + n[ns[i].rows[0]] = { + text: n[ns[i].rows[0]], + row: os[i].rows[0] + }; + o[os[i].rows[0]] = { + text: o[os[i].rows[0]], + row: ns[i].rows[0] + }; + } + } + + for (var i = 0; i < n.length - 1; i++) { + if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && + n[i + 1] == o[n[i].row + 1]) { + n[i + 1] = { + text: n[i + 1], + row: n[i].row + 1 + }; + o[n[i].row + 1] = { + text: o[n[i].row + 1], + row: i + 1 + }; + } + } + + for (var i = n.length - 1; i > 0; i--) { + if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && + n[i - 1] == o[n[i].row - 1]) { + n[i - 1] = { + text: n[i - 1], + row: n[i].row - 1 + }; + o[n[i].row - 1] = { + text: o[n[i].row - 1], + row: i - 1 + }; + } + } + + return { + o: o, + n: n + }; + } + + return function(o, n){ + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); + + var str = ""; + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = [" "]; + } + else { + oSpace.push(" "); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = [" "]; + } + else { + nSpace.push(" "); + } + + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + str += '' + out.o[i] + oSpace[i] + ""; + } + } + else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + str += '' + out.o[n] + oSpace[n] + ""; + } + } + + for (var i = 0; i < out.n.length; i++) { + if (out.n[i].text == null) { + str += '' + out.n[i] + nSpace[i] + ""; + } + else { + var pre = ""; + + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { + pre += '' + out.o[n] + oSpace[n] + ""; + } + str += " " + out.n[i].text + nSpace[i] + pre; + } + } + } + + return str; + }; +})(); + +})(this); diff --git a/test/data/tiddlywiki/test/qunit/raiseAssertion.js b/test/data/tiddlywiki/test/qunit/raiseAssertion.js new file mode 100755 index 000000000..1ceaafb9f --- /dev/null +++ b/test/data/tiddlywiki/test/qunit/raiseAssertion.js @@ -0,0 +1,35 @@ +/* + * extension of the QUnit framework to support exception handling + * cf. http://dev.jquery.com/ticket/4318 + */ + +(function($) { + +$.extend(window, { + raises: raises +}); + +/** + * Checks that the given expression throws an exception of the expected type, with an optional message. + * + * @example raises( function() { return foo.bar; }, "TypeError", "invalid property access raises TypeError exception" ); + * + * @param Function expression + * @param String expected exception type + * @param String message (optional) + */ +function raises(expression, expected, message) { + try { + push(false, expression(), expected, message); + } catch(ex) { + push(ex.name == expected, ex.name, expected, message); + } +} + +// duplicated private function from testrunner.js +function push(result, actual, expected, message) { + message = message || (result ? "okay" : "failed"); + QUnit.ok( result, result ? message + ": " + expected : message + ", expected: " + jsDump.parse(expected) + " result: " + jsDump.parse(actual) ); +} + +})(jQuery); diff --git a/test/data/tiddlywiki/test/qunit/split.recipe b/test/data/tiddlywiki/test/qunit/split.recipe new file mode 100755 index 000000000..bfd2f60aa --- /dev/null +++ b/test/data/tiddlywiki/test/qunit/split.recipe @@ -0,0 +1,4 @@ +#jslib: delayTestExecution.js +jslib: raiseAssertion.js +jslib: qunit.js +style: qunit.css diff --git a/test/data/tiddlywiki/test/recipes/sample.txt b/test/data/tiddlywiki/test/recipes/sample.txt new file mode 100755 index 000000000..0cd850f13 --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/sample.txt @@ -0,0 +1,7 @@ +lorem ipsum +dolor sit amet + + !"#$%&'()*+,-./0123456789:;<=>? +@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +©čČ +foo bar baz diff --git a/test/data/tiddlywiki/test/recipes/shadows/DefaultTiddlers.tiddler b/test/data/tiddlywiki/test/recipes/shadows/DefaultTiddlers.tiddler new file mode 100755 index 000000000..3cfea3077 --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/shadows/DefaultTiddlers.tiddler @@ -0,0 +1,3 @@ +
      +
      
      +
      diff --git a/test/data/tiddlywiki/test/recipes/shadows/MainMenu.tiddler b/test/data/tiddlywiki/test/recipes/shadows/MainMenu.tiddler new file mode 100755 index 000000000..f6600dc71 --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/shadows/MainMenu.tiddler @@ -0,0 +1,3 @@ +
      +
      
      +
      diff --git a/test/data/tiddlywiki/test/recipes/shadows/SideBarOptions.tiddler b/test/data/tiddlywiki/test/recipes/shadows/SideBarOptions.tiddler new file mode 100755 index 000000000..7ac832697 --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/shadows/SideBarOptions.tiddler @@ -0,0 +1,3 @@ +
      +
      
      +
      diff --git a/test/data/tiddlywiki/test/recipes/shadows/SideBarTabs.tiddler b/test/data/tiddlywiki/test/recipes/shadows/SideBarTabs.tiddler new file mode 100755 index 000000000..64a502456 --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/shadows/SideBarTabs.tiddler @@ -0,0 +1,3 @@ +
      +
      
      +
      diff --git a/test/data/tiddlywiki/test/recipes/shadows/split.recipe b/test/data/tiddlywiki/test/recipes/shadows/split.recipe new file mode 100755 index 000000000..4cb9beb8e --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/shadows/split.recipe @@ -0,0 +1,4 @@ +shadow: DefaultTiddlers.tiddler +shadow: MainMenu.tiddler +shadow: SideBarOptions.tiddler +shadow: SideBarTabs.tiddler diff --git a/test/data/tiddlywiki/test/recipes/tests.html.recipe b/test/data/tiddlywiki/test/recipes/tests.html.recipe new file mode 100755 index 000000000..d7bdb9b92 --- /dev/null +++ b/test/data/tiddlywiki/test/recipes/tests.html.recipe @@ -0,0 +1,18 @@ +# TiddlyWiki components +recipe: ../../tiddlywiki.html.recipe +recipe: ../../deprecated/split.recipe +recipe: shadows/split.recipe + +# jQuery +recipe: ../../jquery/split.recipe + +# test framework +recipe: ../qunit/split.recipe +recipe: ../jqMock/split.recipe +recipe: ../html/split.recipe + +# tests +recipe: ../js/split.recipe + +# testdata +recipe: ../testdata/split.recipe diff --git a/test/data/tiddlywiki/test/reporter.html b/test/data/tiddlywiki/test/reporter.html new file mode 100755 index 000000000..8306ff9c4 --- /dev/null +++ b/test/data/tiddlywiki/test/reporter.html @@ -0,0 +1,55 @@ + + + + + + Test Suite + + + + + + + + + + diff --git a/test/data/tiddlywiki/test/reporter.php b/test/data/tiddlywiki/test/reporter.php new file mode 100755 index 000000000..1ea86d537 --- /dev/null +++ b/test/data/tiddlywiki/test/reporter.php @@ -0,0 +1,16 @@ + diff --git a/test/data/tiddlywiki/test/testdata/split.recipe b/test/data/tiddlywiki/test/testdata/split.recipe new file mode 100755 index 000000000..05ac3143b --- /dev/null +++ b/test/data/tiddlywiki/test/testdata/split.recipe @@ -0,0 +1,3 @@ +tiddler: testTiddler1.tid +tiddler: testTiddler2.tid +tiddler: testTiddler3.tid diff --git a/test/data/tiddlywiki/test/testdata/testTiddler1.tid b/test/data/tiddlywiki/test/testdata/testTiddler1.tid new file mode 100755 index 000000000..6b8b65ad2 --- /dev/null +++ b/test/data/tiddlywiki/test/testdata/testTiddler1.tid @@ -0,0 +1,6 @@ +tags: testTag oneTag +fieldvalue: one +modified: 201012010340 +created: 200910211355 + +hello diff --git a/test/data/tiddlywiki/test/testdata/testTiddler2.tid b/test/data/tiddlywiki/test/testdata/testTiddler2.tid new file mode 100755 index 000000000..cb9160679 --- /dev/null +++ b/test/data/tiddlywiki/test/testdata/testTiddler2.tid @@ -0,0 +1,17 @@ +tags: testTag twoTag +fieldvalue: two +modified: 19951201034020 +created: 19940722031200 + +hello + +slice:4t + +!section +bar + +!section2 +test + +!Section3 +welcome diff --git a/test/data/tiddlywiki/test/testdata/testTiddler3.tid b/test/data/tiddlywiki/test/testdata/testTiddler3.tid new file mode 100755 index 000000000..f8a5d4820 --- /dev/null +++ b/test/data/tiddlywiki/test/testdata/testTiddler3.tid @@ -0,0 +1,10 @@ +tags: testTag threeTag +fieldvalue: three +server.bag: foo +field-thing: bar +creator: martin +modified: 20101201094020 +created: 200910191255 + +hello +[[testTiddler2]] diff --git a/test/data/tiddlywiki/tiddlywiki.html.recipe b/test/data/tiddlywiki/tiddlywiki.html.recipe new file mode 100755 index 000000000..e861efa7b --- /dev/null +++ b/test/data/tiddlywiki/tiddlywiki.html.recipe @@ -0,0 +1,5 @@ +recipe: html/split.recipe +recipe: shadows/split.recipe +recipe: js/split.recipe +recipe: deprecated/split.recipe +recipe: jquery/split.recipe diff --git a/test/data/tiddlywiki/tiddlywiki_externaljs.html.recipe b/test/data/tiddlywiki/tiddlywiki_externaljs.html.recipe new file mode 100755 index 000000000..d5d4e8a0b --- /dev/null +++ b/test/data/tiddlywiki/tiddlywiki_externaljs.html.recipe @@ -0,0 +1,4 @@ +recipe: html/split.recipe +recipe: shadows/split.recipe +version: js/Version.js +jsext: html/externaljs.txt diff --git a/test/data/tiddlywiki/tiddlywiki_externaljs_tiddlyspace.html.recipe b/test/data/tiddlywiki/tiddlywiki_externaljs_tiddlyspace.html.recipe new file mode 100755 index 000000000..0d46385c6 --- /dev/null +++ b/test/data/tiddlywiki/tiddlywiki_externaljs_tiddlyspace.html.recipe @@ -0,0 +1,4 @@ +recipe: html/split.recipe +recipe: shadows/split.recipe +version: js/Version.js +jsext: html/externaljs_tiddlyspace.txt diff --git a/test/data/tiddlywiki/tiddlywiki_externaljs_tiddlyspace_alpha.html.recipe b/test/data/tiddlywiki/tiddlywiki_externaljs_tiddlyspace_alpha.html.recipe new file mode 100755 index 000000000..e6d40bc78 --- /dev/null +++ b/test/data/tiddlywiki/tiddlywiki_externaljs_tiddlyspace_alpha.html.recipe @@ -0,0 +1,4 @@ +recipe: html/split.recipe +recipe: shadows/split.recipe +version: js/Version.js +jsext: html/externaljs_tiddlyspace_alpha.txt diff --git a/test/data/tiddlywiki/tiddlywikinonoscript.html.recipe b/test/data/tiddlywiki/tiddlywikinonoscript.html.recipe new file mode 100755 index 000000000..3bcebace3 --- /dev/null +++ b/test/data/tiddlywiki/tiddlywikinonoscript.html.recipe @@ -0,0 +1,5 @@ +recipe: html/splitnonoscript.recipe +recipe: shadows/split.recipe +recipe: js/split.recipe +recipe: deprecated/split.recipe +recipe: jquery/split.recipe diff --git a/test/data/tiddlywiki/uploadalpha b/test/data/tiddlywiki/uploadalpha new file mode 100755 index 000000000..42d3583b6 --- /dev/null +++ b/test/data/tiddlywiki/uploadalpha @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +# Usage: +# uploadalpha [release] [username] + +# default values +USERNAME=${2:-$LOGNAME} +DEFAULT_RELEASE=`cat ALPHA` +RELEASE=${1:-$DEFAULT_RELEASE} +HOST="$USERNAME@tiddlywiki.com" +DIR="/var/www/www.tiddlywiki.com/htdocs" +OWNER="www-data:www-data" +PERM="664" +TW_DIR="cooked" + +# setPermissions() +# Usage: +# setPermissions file +function setPermissions() { + COMMANDS="$COMMANDS sudo chown $OWNER $1;" + COMMANDS="$COMMANDS sudo chmod $PERM $1;" +} + +# upload files to temporary folder +echo +echo "uploading files" +echo +cd +FILES="$TW_DIR/tiddlywiki.$RELEASE.html $TW_DIR/tiddlywiki_compressed.$RELEASE.html $TW_DIR/tiddlywiki_externaljs.$RELEASE.html $TW_DIR/tiddlywiki_externaljs_tiddlyspace.$RELEASE.html $TW_DIR/twcore.$RELEASE.js jquery/jquery.js jquery/plugins/jQuery.twStylesheet.js" +scp $FILES "$HOST:./tmp/" + +# transfer files to their respective folders +echo +echo "transferring files" +echo +stty -echo +COMMANDS="ssh $HOST" +# Empty +COMMANDS="$COMMANDS sudo cp ./tmp/tiddlywiki.$RELEASE.html $DIR/alpha/empty.html;" +setPermissions "$DIR/alpha/empty.html" +# Compressed +COMMANDS="$COMMANDS sudo mv ./tmp/tiddlywiki_compressed.$RELEASE.html $DIR/alpha/tiddlywiki_compressed.html;" +setPermissions "$DIR/alpha/tiddlywiki_compressed.html" +COMMANDS="$COMMANDS sudo mv ./tmp/tiddlywiki_externaljs.$RELEASE.html $DIR/alpha/tiddlywiki_externaljs.html;" +setPermissions "$DIR/alpha/tiddlywiki_externaljs.html" +COMMANDS="$COMMANDS sudo mv ./tmp/tiddlywiki_externaljs_tiddlyspace.$RELEASE.html $DIR/alpha/tiddlywiki_externaljs_tiddlyspace.html;" +setPermissions "$DIR/alpha/tiddlywiki_externaljs_tiddlyspace.html" +COMMANDS="$COMMANDS sudo mv ./tmp/twcore.$RELEASE.js $DIR/alpha/twcore.js;" +setPermissions "$DIR/alpha/twcore.js" +COMMANDS="$COMMANDS sudo mv ./tmp/jquery.js $DIR/alpha/jquery.js;" +setPermissions "$DIR/alpha/jquery.js" +COMMANDS="$COMMANDS sudo mv ./tmp/jQuery.twStylesheet.js $DIR/alpha/jQuery.twStylesheet.js;" +setPermissions "$DIR/alpha/jQuery.twStylesheet.js" +# execute +$COMMANDS +stty echo