From baff9016858133d300a9662ffd1782568454f8eb Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Tue, 15 Jan 2013 17:50:47 +0000 Subject: [PATCH] Added ability to transclude fields of data tiddlers This allows us to transclude colours into CSS --- .../wikiparser/rules/transcludeblock.js | 12 +++- .../wikiparser/rules/transcludeinline.js | 12 +++- core/modules/utils/utils.js | 38 +++++++----- core/modules/widgets/transclude.js | 27 +++++--- core/modules/wiki.js | 62 ++++++++++++++----- core/styles_new/base.tid | 2 +- core/styles_new/colours.tid | 4 ++ 7 files changed, 110 insertions(+), 47 deletions(-) create mode 100644 core/styles_new/colours.tid diff --git a/core/modules/parsers/wikiparser/rules/transcludeblock.js b/core/modules/parsers/wikiparser/rules/transcludeblock.js index ea8f5c345..8e95c5c01 100644 --- a/core/modules/parsers/wikiparser/rules/transcludeblock.js +++ b/core/modules/parsers/wikiparser/rules/transcludeblock.js @@ -33,7 +33,11 @@ exports.parse = function() { // Move past the match this.parser.pos = this.matchRegExp.lastIndex; // Get the match details - var targetTitle = $tw.utils.trim(this.match[1]), + var textRef = $tw.utils.trim(this.match[1]), + tr = $tw.utils.parseTextReference(textRef), + targetTitle = tr.title, + targetField = tr.field, + targetIndex = tr.index, tooltip = this.match[2], template = $tw.utils.trim(this.match[3]), style = this.match[4], @@ -47,6 +51,12 @@ exports.parse = function() { }, isBlock: true }; + if(targetField) { + node.attributes.field = {type: "string", value: targetField}; + } + if(targetIndex) { + node.attributes.index = {type: "string", value: targetIndex}; + } if(tooltip) { node.attributes.tooltip = {type: "string", value: tooltip}; } diff --git a/core/modules/parsers/wikiparser/rules/transcludeinline.js b/core/modules/parsers/wikiparser/rules/transcludeinline.js index 12920114f..cc23e8d75 100644 --- a/core/modules/parsers/wikiparser/rules/transcludeinline.js +++ b/core/modules/parsers/wikiparser/rules/transcludeinline.js @@ -33,7 +33,11 @@ exports.parse = function() { // Move past the match this.parser.pos = this.matchRegExp.lastIndex; // Get the match details - var targetTitle = $tw.utils.trim(this.match[1]), + var textRef = $tw.utils.trim(this.match[1]), + tr = $tw.utils.parseTextReference(textRef), + targetTitle = tr.title, + targetField = tr.field, + targetIndex = tr.index, tooltip = this.match[2], template = $tw.utils.trim(this.match[3]), style = this.match[4], @@ -46,6 +50,12 @@ exports.parse = function() { target: {type: "string", value: targetTitle} } }; + if(targetField) { + node.attributes.field = {type: "string", value: targetField}; + } + if(targetIndex) { + node.attributes.index = {type: "string", value: targetIndex}; + } if(tooltip) { node.attributes.tooltip = {type: "string", value: tooltip}; } diff --git a/core/modules/utils/utils.js b/core/modules/utils/utils.js index 78dcb8818..73476590f 100644 --- a/core/modules/utils/utils.js +++ b/core/modules/utils/utils.js @@ -349,26 +349,30 @@ exports.hyphenateCss = function(propName) { }; /* -Parse a text reference in the format "title!!field" into its constituent parts +Parse a text reference of one of these forms: +* title +* !!field +* title!!field +* title##index +* etc +Returns an object with the following fields, all optional: +* title: tiddler title +* field: tiddler field name +* index: JSON property index */ exports.parseTextReference = function(textRef) { - // Look for a metadata field separator - var pos = textRef.indexOf("!!"); - if(pos !== -1) { - if(pos === 0) { - // Just a field - return { - field: textRef.substring(2) - }; - } else { - // Field and title - return { - title: textRef.substring(0,pos), - field: textRef.substring(pos + 2) - }; - } + // Separate out the title, field name and/or JSON indices + var reTextRef = /^\s*([^\s!#]+)?(?:(?:!!([^\s]+))|(?:##([^\s]+)))?\s*/mg, + match = reTextRef.exec(textRef); + if(match && reTextRef.lastIndex === textRef.length) { + // Return the parts + return { + title: match[1], + field: match[2], + index: match[3] + }; } else { - // Otherwise, we've just got a title + // If we couldn't parse it (eg it started with a) return { title: textRef }; diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js index 313885416..5564a66a6 100644 --- a/core/modules/widgets/transclude.js +++ b/core/modules/widgets/transclude.js @@ -57,7 +57,8 @@ TranscludeWidget.prototype.generate = function() { var tr, templateParseTree, templateTiddler; // Get the render target details this.targetTitle = this.renderer.getAttribute("target",this.renderer.getContextTiddlerTitle()); - this.targetField = this.renderer.getAttribute("field","text"); + this.targetField = this.renderer.getAttribute("field"); + this.targetIndex = this.renderer.getAttribute("index"); // Get the render tree for the template this.templateTitle = undefined; if(this.renderer.parseTreeNode.children && this.renderer.parseTreeNode.children.length > 0) { @@ -67,21 +68,27 @@ TranscludeWidget.prototype.generate = function() { this.templateTitle = this.renderer.getAttribute("template",this.targetTitle); // Check for recursion if(this.renderer.checkContextRecursion({ - tiddlerTitle: this.targetTitle, - templateTitle: this.templateTitle - })) { + tiddlerTitle: this.targetTitle, + templateTitle: this.templateTitle + })) { templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}]; } else { var parser; - if(this.targetField === "text") { - parser = this.renderer.renderTree.wiki.parseTiddler(this.templateTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock}) + if(this.targetField === "text" || (!this.targetField && !this.targetIndex)) { + parser = this.renderer.renderTree.wiki.parseTiddler(this.templateTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); } else { - var tiddler = this.renderer.renderTree.wiki.getTiddler(this.targetTitle), + var tiddler,text; + if(this.targetField) { + tiddler = this.renderer.renderTree.wiki.getTiddler(this.targetTitle); text = tiddler ? tiddler.fields[this.targetField] : ""; - if(text === undefined) { - text = "" + if(text === undefined) { + text = ""; + } + parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); + } else if(this.targetIndex) { + text = this.renderer.renderTree.wiki.extractTiddlerDataItem(this.targetTitle,this.targetIndex,""); + parser = this.renderer.renderTree.wiki.parseText("text/plain",text); } - parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); } templateParseTree = parser ? parser.tree : []; } diff --git a/core/modules/wiki.js b/core/modules/wiki.js index a6ab1a1c7..2821296f5 100644 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -28,18 +28,24 @@ last dispatched. Each entry is a hashmap containing two fields: /* Get the value of a text reference. Text references can have any of these forms: - ## - ## - specifies a field of the current tiddlers + !! + !! - specifies a field of the current tiddlers + ## */ exports.getTextReference = function(textRef,defaultText,currTiddlerTitle) { var tr = $tw.utils.parseTextReference(textRef), - title = tr.title || currTiddlerTitle, - field = tr.field || "text", - tiddler = this.getTiddler(title); - if(tiddler && $tw.utils.hop(tiddler.fields,field)) { - return tiddler.fields[field]; + title = tr.title || currTiddlerTitle; + if(tr.field) { + var tiddler = this.getTiddler(title); + if(tiddler && $tw.utils.hop(tiddler.fields,tr.field)) { + return tiddler.fields[tr.field]; + } else { + return defaultText; + } + } else if(tr.index) { + return this.extractTiddlerDataItem(title,tr.index,defaultText); } else { - return defaultText; + return this.getTiddlerText(title); } }; @@ -283,24 +289,46 @@ exports.getTiddlersWithTag = function(tag) { Get a tiddlers content as a JavaScript object. How this is done depends on the type of the tiddler: application/json: the tiddler JSON is parsed into an object +application/x-tiddler-dictionary: the tiddler is parsed as sequence of name:value pairs Other types currently just return null. */ exports.getTiddlerData = function(title,defaultData) { var tiddler = this.tiddlers[title], data; - if(tiddler && tiddler.fields.text && tiddler.fields.type === "application/json") { - // JSON tiddler - try { - data = JSON.parse(tiddler.fields.text); - } catch(ex) { - return defaultData; + if(tiddler && tiddler.fields.text) { + switch(tiddler.fields.type) { + case "application/json": + // JSON tiddler + try { + data = JSON.parse(tiddler.fields.text); + } catch(ex) { + return defaultData; + } + return data; + case "application/x-tiddler-dictionary": + return $tw.utils.parseFields(tiddler.fields.text); } - return data; } return defaultData; }; +/* +Extract an indexed field from within a data tiddler +*/ +exports.extractTiddlerDataItem = function(title,index,defaultText) { + var data = this.getTiddlerData(title,{}), + text; + if(data && $tw.utils.hop(data,index)) { + text = data[index]; + } + if(typeof text === "string" || typeof text === "number") { + return text.toString(); + } else { + return defaultText; + } +}; + /* Set a tiddlers content to a JavaScript object. Currently this is done by setting the tiddler's type to "application/json" and setting the text to the JSON text of the data. */ @@ -619,11 +647,11 @@ exports.handleSyncerEvent = function(event) { /* Trigger a load for a tiddler if it is skinny. Returns the text, or undefined if the tiddler is missing, null if the tiddler is being lazily loaded. */ -exports.getTiddlerText = function(title) { +exports.getTiddlerText = function(title,defaultText) { var tiddler = this.getTiddler(title); // Return undefined if the tiddler isn't found if(!tiddler) { - return undefined; + return defaultText; } if(tiddler.fields.text !== undefined) { // Just return the text if we've got it diff --git a/core/styles_new/base.tid b/core/styles_new/base.tid index 61f9bc324..5e9c54013 100644 --- a/core/styles_new/base.tid +++ b/core/styles_new/base.tid @@ -24,7 +24,7 @@ html body { .tw-tiddler-frame { padding: 30px 30px 30px 30px; margin: 0px; - background-color: #fff; + background-color: {{$:/core/styles/colours##primary}}; border-left: 1px solid #ddd; border-right: 1px solid #ddd; border-bottom: 1px solid #ddd; diff --git a/core/styles_new/colours.tid b/core/styles_new/colours.tid new file mode 100644 index 000000000..db32ea6fa --- /dev/null +++ b/core/styles_new/colours.tid @@ -0,0 +1,4 @@ +title: $:/core/styles/colours +type: application/x-tiddler-dictionary + +primary: #80ffff