mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-25 01:20:30 +00:00
d455072f13
* Add support for JSON-formatted tiddler store, and make it the default The change to `getTiddlersAsJson()` is to allow experimentation * Move JSON tiddlers into their own store area, and fix support for encrypted tiddlers Also add a dummy old-style store area for backwards compatibility The current arrangement is that JSON tiddlers will always override old-style tiddlers. * Use the deserialiser mechanism to decode the content * Refactor $:/core/modules/deserializers.js before we start extending it Cleaning up the helper function names and ordering * Drop support for the "systemArea" div It was only used in really old v5.0.x * Update deserializer to support JSON store format and add some tests * Life UI restrictions on characters in fieldnames * Add another test case * Correct mis-merge * Remove toLowerCase() methods applied to fieldnames * Insert line breaks in output of getTiddlersAsJson (#5786) Rather than have the entire store on one line, insert a line break after each tiddler. * Refactor #5786 for backwards compatibility * Only read .tiddlywiki-tiddler-store blocks from script tags Prompted by @simonbaird's comment here: https://github.com/Jermolene/TiddlyWiki5/pull/5708#discussion_r648833367 * Clean up escaping of unsafe script characters It seems that escaping `<` is sufficient * Add docs from @saqimtiaz Thanks @saqimtiaz * Docs tweaks * Remove excess whitespace Thanks @simonbaird * Fix templates for lazy loading * Remove obsolete item from release note * Clean up whitespace * Docs for the jsontiddler widget * Fix whitespace Fixes #5840 * Comments * Fix newlines in JSON store area * Remove obsolete docs change Co-authored-by: Simon Baird <simon.baird@gmail.com>
189 lines
5.5 KiB
JavaScript
189 lines
5.5 KiB
JavaScript
/*\
|
|
title: $:/core/modules/deserializers.js
|
|
type: application/javascript
|
|
module-type: tiddlerdeserializer
|
|
|
|
Functions to deserialise tiddlers from a block of text
|
|
|
|
\*/
|
|
(function(){
|
|
|
|
/*jslint node: true, browser: true */
|
|
/*global $tw: false */
|
|
"use strict";
|
|
|
|
exports["application/x-tiddler-html-div"] = function(text,fields) {
|
|
return [deserializeTiddlerDiv(text,fields)];
|
|
};
|
|
|
|
exports["application/json"] = function(text,fields) {
|
|
var incoming,
|
|
results = [];
|
|
try {
|
|
incoming = JSON.parse(text);
|
|
} catch(e) {
|
|
incoming = [{
|
|
title: "JSON error: " + e,
|
|
text: ""
|
|
}]
|
|
}
|
|
if(!$tw.utils.isArray(incoming)) {
|
|
incoming = [incoming];
|
|
}
|
|
for(var t=0; t<incoming.length; t++) {
|
|
var incomingFields = incoming[t],
|
|
fields = {};
|
|
for(var f in incomingFields) {
|
|
if(typeof incomingFields[f] === "string") {
|
|
fields[f] = incomingFields[f];
|
|
}
|
|
}
|
|
results.push(fields);
|
|
}
|
|
return results;
|
|
};
|
|
|
|
/*
|
|
Parse an HTML file into tiddlers. There are three possibilities:
|
|
# A TiddlyWiki classic HTML file containing `text/x-tiddlywiki` tiddlers
|
|
# A TiddlyWiki5 HTML file containing `text/vnd.tiddlywiki` tiddlers
|
|
# An ordinary HTML file
|
|
*/
|
|
exports["text/html"] = function(text,fields) {
|
|
var results = [];
|
|
// Check if we've got an old-style store area
|
|
var storeAreaMarkerRegExp = /<div id=["']?storeArea['"]?( style=["']?display:none;["']?)?>/gi,
|
|
storeAreaMatch = storeAreaMarkerRegExp.exec(text);
|
|
if(storeAreaMatch) {
|
|
// If so, we've got tiddlers in classic TiddlyWiki format or unencrypted old-style TW5 format
|
|
results.push.apply(results,deserializeStoreArea(text,storeAreaMarkerRegExp.lastIndex,!!storeAreaMatch[1],fields));
|
|
}
|
|
// Check for new-style store areas
|
|
var newStoreAreaMarkerRegExp = /<script class="tiddlywiki-tiddler-store" type="([^"]*)">/gi,
|
|
newStoreAreaMatch = newStoreAreaMarkerRegExp.exec(text),
|
|
haveHadNewStoreArea = !!newStoreAreaMatch;
|
|
while(newStoreAreaMatch) {
|
|
results.push.apply(results,deserializeNewStoreArea(text,newStoreAreaMarkerRegExp.lastIndex,newStoreAreaMatch[1],fields));
|
|
newStoreAreaMatch = newStoreAreaMarkerRegExp.exec(text);
|
|
}
|
|
// Return if we had either an old-style or a new-style store area
|
|
if(storeAreaMatch || haveHadNewStoreArea) {
|
|
return results;
|
|
}
|
|
// Otherwise, check whether we've got an encrypted file
|
|
var encryptedStoreArea = $tw.utils.extractEncryptedStoreArea(text);
|
|
if(encryptedStoreArea) {
|
|
// If so, attempt to decrypt it using the current password
|
|
return $tw.utils.decryptStoreArea(encryptedStoreArea);
|
|
} else {
|
|
// It's not a TiddlyWiki so we'll return the entire HTML file as a tiddler
|
|
return deserializeHtmlFile(text,fields);
|
|
}
|
|
};
|
|
|
|
function deserializeHtmlFile(text,fields) {
|
|
var result = {};
|
|
$tw.utils.each(fields,function(value,name) {
|
|
result[name] = value;
|
|
});
|
|
result.text = text;
|
|
result.type = "text/html";
|
|
return [result];
|
|
}
|
|
|
|
function deserializeNewStoreArea(text,storeAreaEnd,type,fields) {
|
|
var endOfScriptRegExp = /<\/script>/gi;
|
|
endOfScriptRegExp.lastIndex = storeAreaEnd;
|
|
var match = endOfScriptRegExp.exec(text);
|
|
if(match) {
|
|
var scriptContent = text.substring(storeAreaEnd,match.index);
|
|
return $tw.wiki.deserializeTiddlers(type,scriptContent);
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function deserializeStoreArea(text,storeAreaEnd,isTiddlyWiki5,fields) {
|
|
var results = [],
|
|
endOfDivRegExp = /(<\/div>\s*)/gi,
|
|
startPos = storeAreaEnd,
|
|
defaultType = isTiddlyWiki5 ? undefined : "text/x-tiddlywiki";
|
|
endOfDivRegExp.lastIndex = startPos;
|
|
var match = endOfDivRegExp.exec(text);
|
|
while(match) {
|
|
var endPos = endOfDivRegExp.lastIndex,
|
|
tiddlerFields = deserializeTiddlerDiv(text.substring(startPos,endPos),fields,{type: defaultType});
|
|
if(!tiddlerFields) {
|
|
break;
|
|
}
|
|
$tw.utils.each(tiddlerFields,function(value,name) {
|
|
if(typeof value === "string") {
|
|
tiddlerFields[name] = $tw.utils.htmlDecode(value);
|
|
}
|
|
});
|
|
if(tiddlerFields.text !== null) {
|
|
results.push(tiddlerFields);
|
|
}
|
|
startPos = endPos;
|
|
match = endOfDivRegExp.exec(text);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/*
|
|
Utility function to parse an old-style tiddler DIV in a *.tid file. It looks like this:
|
|
|
|
<div title="Title" creator="JoeBloggs" modifier="JoeBloggs" created="201102111106" modified="201102111310" tags="myTag [[my long tag]]">
|
|
<pre>The text of the tiddler (without the expected HTML encoding).
|
|
</pre>
|
|
</div>
|
|
|
|
Note that the field attributes are HTML encoded, but that the body of the <PRE> tag is not encoded.
|
|
|
|
When these tiddler DIVs are encountered within a TiddlyWiki HTML file then the body is encoded in the usual way.
|
|
*/
|
|
var deserializeTiddlerDiv = function(text /* [,fields] */) {
|
|
// Slot together the default results
|
|
var result = {};
|
|
if(arguments.length > 1) {
|
|
for(var f=1; f<arguments.length; f++) {
|
|
var fields = arguments[f];
|
|
for(var t in fields) {
|
|
result[t] = fields[t];
|
|
}
|
|
}
|
|
}
|
|
// Parse the DIV body
|
|
var startRegExp = /^\s*<div\s+([^>]*)>(\s*<pre>)?/gi,
|
|
endRegExp,
|
|
match = startRegExp.exec(text);
|
|
if(match) {
|
|
// Old-style DIVs don't have the <pre> tag
|
|
if(match[2]) {
|
|
endRegExp = /<\/pre>\s*<\/div>\s*$/gi;
|
|
} else {
|
|
endRegExp = /<\/div>\s*$/gi;
|
|
}
|
|
var endMatch = endRegExp.exec(text);
|
|
if(endMatch) {
|
|
// Extract the text
|
|
result.text = text.substring(match.index + match[0].length,endMatch.index);
|
|
// Process the attributes
|
|
var attrRegExp = /\s*([^=\s]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi,
|
|
attrMatch;
|
|
do {
|
|
attrMatch = attrRegExp.exec(match[1]);
|
|
if(attrMatch) {
|
|
var name = attrMatch[1];
|
|
var value = attrMatch[2] !== undefined ? attrMatch[2] : attrMatch[3];
|
|
result[name] = value;
|
|
}
|
|
} while(attrMatch);
|
|
return result;
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
})();
|