diff --git a/core/modules/parsers/wikiparser/rules/transcludeblock.js b/core/modules/parsers/wikiparser/rules/transcludeblock.js index 8e95c5c01..39fe38123 100644 --- a/core/modules/parsers/wikiparser/rules/transcludeblock.js +++ b/core/modules/parsers/wikiparser/rules/transcludeblock.js @@ -42,34 +42,40 @@ exports.parse = function() { template = $tw.utils.trim(this.match[3]), style = this.match[4], classes = this.match[5]; - // Return the transclude widget - var node = { + // Prepare the transclude widget + var transcludeNode = { + type: "element", + tag: "$transclude", + attributes: { + title: {type: "string", value: template || targetTitle} + }, + isBlock: true + }; + var tiddlerNode = { type: "element", - tag: "$transclude", + tag: "$tiddler", attributes: { - target: {type: "string", value: targetTitle} + title: {type: "string", value: targetTitle} }, - isBlock: true + isBlock: true, + children: [transcludeNode] }; if(targetField) { - node.attributes.field = {type: "string", value: targetField}; + transcludeNode.attributes.field = {type: "string", value: targetField}; } if(targetIndex) { - node.attributes.index = {type: "string", value: targetIndex}; + transcludeNode.attributes.index = {type: "string", value: targetIndex}; } if(tooltip) { - node.attributes.tooltip = {type: "string", value: tooltip}; - } - if(template) { - node.attributes.template = {type: "string", value: template}; + transcludeNode.attributes.tooltip = {type: "string", value: tooltip}; } if(style) { - node.attributes.style = {type: "string", value: style}; + transcludeNode.attributes.style = {type: "string", value: style}; } if(classes) { - node.attributes["class"] = {type: "string", value: classes.split(".").join(" ")}; + transcludeNode.attributes["class"] = {type: "string", value: classes.split(".").join(" ")}; } - return [node]; + return [tiddlerNode]; }; })(); diff --git a/core/modules/parsers/wikiparser/rules/transcludeinline.js b/core/modules/parsers/wikiparser/rules/transcludeinline.js index cc23e8d75..372646fab 100644 --- a/core/modules/parsers/wikiparser/rules/transcludeinline.js +++ b/core/modules/parsers/wikiparser/rules/transcludeinline.js @@ -42,33 +42,38 @@ exports.parse = function() { template = $tw.utils.trim(this.match[3]), style = this.match[4], classes = this.match[5]; - // Return the transclude widget - var node = { + // Prepare the transclude widget + var transcludeNode = { + type: "element", + tag: "$transclude", + attributes: { + title: {type: "string", value: template || targetTitle} + } + }; + var tiddlerNode = { type: "element", - tag: "$transclude", + tag: "$tiddler", attributes: { - target: {type: "string", value: targetTitle} - } + title: {type: "string", value: targetTitle} + }, + children: [transcludeNode] }; if(targetField) { - node.attributes.field = {type: "string", value: targetField}; + transcludeNode.attributes.field = {type: "string", value: targetField}; } if(targetIndex) { - node.attributes.index = {type: "string", value: targetIndex}; + transcludeNode.attributes.index = {type: "string", value: targetIndex}; } if(tooltip) { - node.attributes.tooltip = {type: "string", value: tooltip}; - } - if(template) { - node.attributes.template = {type: "string", value: template}; + transcludeNode.attributes.tooltip = {type: "string", value: tooltip}; } if(style) { - node.attributes.style = {type: "string", value: style}; + transcludeNode.attributes.style = {type: "string", value: style}; } if(classes) { - node.attributes["class"] = {type: "string", value: classes.split(".").join(" ")}; + transcludeNode.attributes["class"] = {type: "string", value: classes.split(".").join(" ")}; } - return [node]; + return [tiddlerNode]; }; })(); diff --git a/core/modules/widgets/grid.js b/core/modules/widgets/grid.js index 5065ca482..8f393fe7f 100644 --- a/core/modules/widgets/grid.js +++ b/core/modules/widgets/grid.js @@ -61,7 +61,7 @@ GridWidget.prototype.generateTable = function() { type: "element", tag: "$transclude", attributes: { - target: {type: "string", value: this.getTableCellTitle(col,row)} + title: {type: "string", value: this.getTableCellTitle(col,row)} } }] }; diff --git a/core/modules/widgets/info.js b/core/modules/widgets/info.js index 2fd72cf54..4ea696f8d 100644 --- a/core/modules/widgets/info.js +++ b/core/modules/widgets/info.js @@ -41,7 +41,7 @@ InfoWidget.types.currentfielddescription = function(options) { tag: "$transclude", isBlock: false, attributes: { - target: {type: "string", value: descriptionTitle} + title: {type: "string", value: descriptionTitle} } }]; }; @@ -71,7 +71,7 @@ InfoWidget.types.modules = function(options) { tag: "$transclude", isBlock: false, attributes: { - target: {type: "string", value: MODULE_TYPE_DESCRIPTION_PREFIX + moduleType} + title: {type: "string", value: MODULE_TYPE_DESCRIPTION_PREFIX + moduleType} } }); // List each module diff --git a/core/modules/widgets/list/list.js b/core/modules/widgets/list/list.js index 4649173e9..a2b5746ba 100644 --- a/core/modules/widgets/list/list.js +++ b/core/modules/widgets/list/list.js @@ -197,23 +197,37 @@ ListWidget.prototype.createListElementTransclusion = function(title) { }]; } } - // Create the transclude widget - var widget = { - type: "element", - tag: "$transclude", - isBlock: this.renderer.parseTreeNode.isBlock, - attributes: {}, - children: templateTree - }; - // Set the target if needed - if(!this.renderer.hasAttribute("hackTemplate")) { - widget.attributes.target = {type: "string", value: title}; - widget.attributes.template = {type: "string", value: template}; + // Create the element widgets + if(this.renderer.hasAttribute("hackTemplate")) { + return { + type: "element", + tag: "$transclude", + isBlock: this.renderer.parseTreeNode.isBlock, + attributes: { + title: {type: "string", value: title} + } + }; } else { - widget.attributes.template = {type: "string", value: title}; - widget.children = undefined; + if(!templateTree) { + templateTree = [{ + type: "element", + tag: "$transclude", + attributes: { + title: {type: "string", value: template} + }, + children: templateTree + }]; + } + return { + type: "element", + tag: "$tiddler", + isBlock: this.renderer.parseTreeNode.isBlock, + attributes: { + title: {type: "string", value: title} + }, + children: templateTree + }; } - return widget; }; /* @@ -245,10 +259,13 @@ ListWidget.prototype.findListElementByTitle = function(startIndex,title) { var testNode = this.macro ? function(node) { // We're looking for a macro list element return node.widget.children[0].parseTreeNode.params[0].value === title; - } : function(node) { - // We're looking for a transclusion list element - return node.widget.children[0].attributes.target === title; - }; + } : (this.renderer.hasAttribute("hackTemplate") ? function(node) { + // We're looking for a transclusion list element + return node.widget.children[0].attributes.title === title; + } : function(node) { + // We're looking for a transclusion list element + return node.widget.children[0].attributes.title === title; + }); // Search for the list element while(startIndex < this.children.length) { if(testNode(this.children[startIndex])) { diff --git a/core/modules/widgets/tiddler.js b/core/modules/widgets/tiddler.js new file mode 100644 index 000000000..70ba00bfc --- /dev/null +++ b/core/modules/widgets/tiddler.js @@ -0,0 +1,82 @@ +/*\ +title: $:/core/modules/widgets/tiddler.js +type: application/javascript +module-type: widget + +The tiddler widget sets the current tiddler to a specified title. + +Attributes: + title: the title of the current tiddler + class: CSS classes + + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var TiddlerWidget = function(renderer) { + // Save state + this.renderer = renderer; + // Generate child nodes + this.generate(); +}; + +TiddlerWidget.prototype.generate = function() { + var self = this; + this.tiddlerTitle = this.renderer.getAttribute("title",""); + // Set up the attributes for the wrapper element + var classes = ["tw-tiddler"]; + if(this.renderer.hasAttribute("class")) { + $tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" ")); + } + if(!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle) && !this.renderer.renderTree.wiki.isShadowTiddler(this.tiddlerTitle)) { + $tw.utils.pushTop(classes,"tw-tiddler-missing"); + } + // Save the context for this renderer node + this.renderer.context = { + tiddlerTitle: this.tiddlerTitle + }; + // Initialise events + this.events = []; + // Trap and update tag modification events + this.events.push({name: "tw-remove-tag", handlerFunction: function(event) { + event.currentTag = self.tiddlerTitle; + return true; + }}); + // Set the element + this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span"; + this.attributes = {}; + if(classes.length > 0) { + this.attributes["class"] = classes.join(" "); + } + this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children); +}; + +TiddlerWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) { + // Set the class for missing tiddlers + if(this.tiddlerTitle && changedTiddlers[this.tiddlerTitle]) { + $tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle)); + } + // Check if any of our attributes have changed, or if a tiddler we're interested in has changed + if(changedAttributes.title) { + // Regenerate and rerender the widget and replace the existing DOM node + this.generate(); + var oldDomNode = this.renderer.domNode, + newDomNode = this.renderer.renderInDom(); + oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode); + } else { + // We don't need to refresh ourselves, so just refresh any child nodes + $tw.utils.each(this.children,function(node) { + if(node.refreshInDom) { + node.refreshInDom(changedTiddlers); + } + }); + } +}; + +exports.tiddler = TiddlerWidget; + +})(); diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js index 0ab49ff95..d493a0457 100644 --- a/core/modules/widgets/transclude.js +++ b/core/modules/widgets/transclude.js @@ -6,38 +6,7 @@ module-type: widget The transclude widget includes another tiddler into the tiddler being rendered. Attributes: - target: the title of the tiddler to transclude - template: the title of the tiddler to use as a template for the transcluded tiddler - -The simplest case is to just supply a target tiddler: - -{{{ -<$transclude target="Foo"/> -}}} - -This will render the tiddler Foo within the current tiddler. If the tiddler Foo includes -the view widget (or other widget that reference the fields of the current tiddler), then the -fields of the tiddler Foo will be accessed. - -If you want to transclude the tiddler as a template, so that the fields referenced by the view -widget are those of the tiddler doing the transcluding, then you can instead specify the tiddler -as a template: - -{{{ -<$transclude template="Foo"/> -}}} - -The effect is the same as the previous example: the text of the tiddler Foo is rendered. The -difference is that the view widget will access the fields of the tiddler doing the transcluding. - -The `target` and `template` attributes may be combined: - -{{{ -<$transclude template="Bar" target="Foo"/> -}}} - -Here, the text of the tiddler `Bar` will be transcluded, with the widgets within it accessing the fields -of the tiddler `Foo`. + title: the title of the tiddler to transclude \*/ (function(){ @@ -55,65 +24,49 @@ var TranscludeWidget = function(renderer) { TranscludeWidget.prototype.generate = function() { var self = this, - tr, templateParseTree, templateTiddler; + templateParseTree; // Get the render target details - this.targetTitle = this.renderer.getAttribute("target",this.renderer.tiddlerTitle); - 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) { - // Use the child nodes as the template if we've got them - templateParseTree = this.renderer.parseTreeNode.children; + this.transcludeTitle = this.renderer.getAttribute("title",this.renderer.tiddlerTitle); + this.transcludeField = this.renderer.getAttribute("field"); + this.transcludeIndex = this.renderer.getAttribute("index"); + // Check for recursion + if(this.renderer.renderTree.checkContextRecursion(this.renderer.parentRenderer,{ + transcludeTitle: this.transcludeTitle, + transcludeField: this.transcludeField, + transcludeIndex: this.transcludeIndex + })) { + templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}]; } else { - this.templateTitle = this.renderer.getAttribute("template",this.targetTitle); - // Check for recursion - if(this.renderer.renderTree.checkContextRecursion(this.renderer.parentRenderer,{ - tiddlerTitle: this.targetTitle, - templateTitle: this.templateTitle - })) { - templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}]; + var parser; + if(this.transcludeField === "text" || (!this.transcludeField && !this.transcludeIndex)) { + parser = this.renderer.renderTree.wiki.parseTiddler(this.transcludeTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); } else { - var parser; - if(this.targetField === "text" || (!this.targetField && !this.targetIndex)) { - parser = this.renderer.renderTree.wiki.parseTiddler(this.templateTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); - } else { - var tiddler,text; - if(this.targetField) { - tiddler = this.renderer.renderTree.wiki.getTiddler(this.targetTitle); - text = tiddler ? tiddler.fields[this.targetField] : ""; - 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/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); + var tiddler,text; + if(this.transcludeField) { + tiddler = this.renderer.renderTree.wiki.getTiddler(this.transcludeTitle); + text = tiddler ? tiddler.fields[this.transcludeField] : ""; + if(text === undefined) { + text = ""; } + parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); + } else if(this.transcludeIndex) { + text = this.renderer.renderTree.wiki.extractTiddlerDataItem(this.transcludeTitle,this.transcludeIndex,""); + parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock}); } - templateParseTree = parser ? parser.tree : []; } + templateParseTree = parser ? parser.tree : []; } // Set up the attributes for the wrapper element var classes = ["tw-transclude"]; if(this.renderer.hasAttribute("class")) { $tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" ")); } - if(!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle) && !this.renderer.renderTree.wiki.isShadowTiddler(this.targetTitle)) { - $tw.utils.pushTop(classes,"tw-tiddler-missing"); - } // Save the context for this renderer node this.renderer.context = { - tiddlerTitle: this.targetTitle, - templateTitle: this.templateTitle + transcludeTitle: this.transcludeTitle, + transcludeField: this.transcludeField, + transcludeIndex: this.transcludeIndex }; - // Initialise events - this.events = []; - // Trap and update tag modification events - this.events.push({name: "tw-remove-tag", handlerFunction: function(event) { - event.currentTag = self.targetTitle; - return true; - }}); // Set the element this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span"; this.attributes = {}; @@ -130,12 +83,8 @@ TranscludeWidget.prototype.generate = function() { }; TranscludeWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) { - // Set the class for missing tiddlers - if(this.targetTitle && changedTiddlers[this.targetTitle]) { - $tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle)); - } // Check if any of our attributes have changed, or if a tiddler we're interested in has changed - if(changedAttributes.target || changedAttributes.template || (this.templateTitle && changedTiddlers[this.templateTitle])) { + if(changedAttributes.transcludeField || changedAttributes.transcludeIndex || (this.transcludeTitle && changedTiddlers[this.transcludeTitle])) { // Regenerate and rerender the widget and replace the existing DOM node this.generate(); var oldDomNode = this.renderer.domNode, diff --git a/core/ui/EditTemplate.tid b/core/ui/EditTemplate.tid index 971f5e180..d6fe4a8e7 100644 --- a/core/ui/EditTemplate.tid +++ b/core/ui/EditTemplate.tid @@ -9,10 +9,10 @@ modifier: JeremyRuston <$edit field="draft.title"/> @@ -<$transclude template="$:/core/ui/TagsEditor"/> +<$transclude title="$:/core/ui/TagsEditor"/> <$reveal state="$:/ShowEditPreview" type="match" text="yes"> -<$transclude template="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="no">hide preview$button> +<$transclude title="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="no">hide preview$button>