mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-11-04 09:33:00 +00:00 
			
		
		
		
	Added wikitext image support
We’ve added a parser to recognise the `[img[URL or tiddler title]]` format, and an associated image widget.
This commit is contained in:
		
							
								
								
									
										137
									
								
								core/modules/parsers/wikiparser/rules/image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								core/modules/parsers/wikiparser/rules/image.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					/*\
 | 
				
			||||||
 | 
					title: $:/core/modules/parsers/wikiparser/rules/image.js
 | 
				
			||||||
 | 
					type: application/javascript
 | 
				
			||||||
 | 
					module-type: wikirule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wiki text inline rule for embedding images. For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					[img[http://tiddlywiki.com/fractalveg.jpg]]
 | 
				
			||||||
 | 
					[img width=23 height=24 [http://tiddlywiki.com/fractalveg.jpg]]
 | 
				
			||||||
 | 
					[img 23x24 [http://tiddlywiki.com/fractalveg.jpg]]
 | 
				
			||||||
 | 
					[img width={{!!width}} height={{!!height}} [http://tiddlywiki.com/fractalveg.jpg]]
 | 
				
			||||||
 | 
					[img {{!!width}}x{{!!height}} [http://tiddlywiki.com/fractalveg.jpg]]
 | 
				
			||||||
 | 
					[img[Description of image|http://tiddlywiki.com/fractalveg.jpg]]
 | 
				
			||||||
 | 
					[img[TiddlerTitle]]
 | 
				
			||||||
 | 
					[img[Description of image|TiddlerTitle]]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Generates the `<$image>` widget.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					\*/
 | 
				
			||||||
 | 
					(function(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*jslint node: true, browser: true */
 | 
				
			||||||
 | 
					/*global $tw: false */
 | 
				
			||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.name = "image";
 | 
				
			||||||
 | 
					exports.types = {inline: true};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.init = function(parser) {
 | 
				
			||||||
 | 
						this.parser = parser;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.findNextMatch = function(startPos) {
 | 
				
			||||||
 | 
						// Find the next tag
 | 
				
			||||||
 | 
						this.nextImage = this.findNextImage(this.parser.source,startPos);
 | 
				
			||||||
 | 
						return this.nextImage ? this.nextImage.start : undefined;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.parse = function() {
 | 
				
			||||||
 | 
						// Move past the match
 | 
				
			||||||
 | 
						this.parser.pos = this.nextImage.end;
 | 
				
			||||||
 | 
						var node = {
 | 
				
			||||||
 | 
							type: "element",
 | 
				
			||||||
 | 
							tag: "$image",
 | 
				
			||||||
 | 
							attributes: this.nextImage.attributes
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						return [node];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Find the next image from the current position
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					exports.findNextImage = function(source,pos) {
 | 
				
			||||||
 | 
						// A regexp for finding candidate HTML tags
 | 
				
			||||||
 | 
						var reLookahead = /(\[img)/g;
 | 
				
			||||||
 | 
						// Find the next candidate
 | 
				
			||||||
 | 
						reLookahead.lastIndex = pos;
 | 
				
			||||||
 | 
						var match = reLookahead.exec(source);
 | 
				
			||||||
 | 
						while(match) {
 | 
				
			||||||
 | 
							// Try to parse the candidate as a tag
 | 
				
			||||||
 | 
							var tag = this.parseImage(source,match.index);
 | 
				
			||||||
 | 
							// Return success
 | 
				
			||||||
 | 
							if(tag) {
 | 
				
			||||||
 | 
								return tag;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Look for the next match
 | 
				
			||||||
 | 
							reLookahead.lastIndex = match.index + 1;
 | 
				
			||||||
 | 
							match = reLookahead.exec(source);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Failed
 | 
				
			||||||
 | 
						return null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Look for an image at the specified position. Returns null if not found, otherwise returns {type: "element", name: "$image", attributes: [], isSelfClosing:, start:, end:,}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					exports.parseImage = function(source,pos) {
 | 
				
			||||||
 | 
						var token,
 | 
				
			||||||
 | 
							node = {
 | 
				
			||||||
 | 
								type: "element",
 | 
				
			||||||
 | 
								name: "$image",
 | 
				
			||||||
 | 
								start: pos,
 | 
				
			||||||
 | 
								attributes: {}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						// Skip whitespace
 | 
				
			||||||
 | 
						pos = $tw.utils.skipWhiteSpace(source,pos);
 | 
				
			||||||
 | 
						// Look for the `[img`
 | 
				
			||||||
 | 
						token = $tw.utils.parseTokenString(source,pos,"[img");
 | 
				
			||||||
 | 
						if(!token) {
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pos = token.end;
 | 
				
			||||||
 | 
						// Skip whitespace
 | 
				
			||||||
 | 
						pos = $tw.utils.skipWhiteSpace(source,pos);
 | 
				
			||||||
 | 
						// Process attributes
 | 
				
			||||||
 | 
						if(source.charAt(pos) !== "[") {
 | 
				
			||||||
 | 
							var attribute = $tw.utils.parseAttribute(source,pos);
 | 
				
			||||||
 | 
							while(attribute) {
 | 
				
			||||||
 | 
								node.attributes[attribute.name] = attribute;
 | 
				
			||||||
 | 
								pos = attribute.end;
 | 
				
			||||||
 | 
								pos = $tw.utils.skipWhiteSpace(source,pos);
 | 
				
			||||||
 | 
								if(source.charAt(pos) !== "[") {
 | 
				
			||||||
 | 
									// Get the next attribute
 | 
				
			||||||
 | 
									attribute = $tw.utils.parseAttribute(source,pos);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									attribute = null;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Skip whitespace
 | 
				
			||||||
 | 
						pos = $tw.utils.skipWhiteSpace(source,pos);
 | 
				
			||||||
 | 
						// Look for the `[` after the attributes
 | 
				
			||||||
 | 
						token = $tw.utils.parseTokenString(source,pos,"[");
 | 
				
			||||||
 | 
						if(!token) {
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pos = token.end;
 | 
				
			||||||
 | 
						// Skip whitespace
 | 
				
			||||||
 | 
						pos = $tw.utils.skipWhiteSpace(source,pos);
 | 
				
			||||||
 | 
						// Get the source up to the terminating `]]`
 | 
				
			||||||
 | 
						token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/g);
 | 
				
			||||||
 | 
						if(!token) {
 | 
				
			||||||
 | 
							return null;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pos = token.end;
 | 
				
			||||||
 | 
						if(token.match[1]) {
 | 
				
			||||||
 | 
							node.attributes.tooltip = {type: "string", value: token.match[1].trim()};
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						node.attributes.source = {type: "string", value: (token.match[2] || "").trim()};
 | 
				
			||||||
 | 
						// Update the end position
 | 
				
			||||||
 | 
						node.end = pos;
 | 
				
			||||||
 | 
						return node;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
							
								
								
									
										123
									
								
								core/modules/widgets/image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								core/modules/widgets/image.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,123 @@
 | 
				
			|||||||
 | 
					/*\
 | 
				
			||||||
 | 
					title: $:/core/modules/widgets/image.js
 | 
				
			||||||
 | 
					type: application/javascript
 | 
				
			||||||
 | 
					module-type: widget
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The image widget displays an image referenced with an external URI or with a local tiddler title.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					<$image src="TiddlerTitle" width="320" height="400" class="classnames">
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The image source can be the title of an existing tiddler or the URL of an external image.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					External images always generate an HTML `<img>` tag.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tiddlers that have a _canonical_uri field generate an HTML `<img>` tag with the src attribute containing the URI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tiddlers that contain image data generate an HTML `<img>` tag with the src attribute containing a base64 representation of the image.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tiddlers that contain wikitext could be rendered to a DIV of the usual size of a tiddler, and then transformed to the size requested.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The width and height attributes are interpreted as a number of pixels, and do not need to include the "px" suffix.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					\*/
 | 
				
			||||||
 | 
					(function(){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*jslint node: true, browser: true */
 | 
				
			||||||
 | 
					/*global $tw: false */
 | 
				
			||||||
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Widget = require("$:/core/modules/widgets/widget.js").widget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ImageWidget = function(parseTreeNode,options) {
 | 
				
			||||||
 | 
						this.initialise(parseTreeNode,options);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Inherit from the base widget class
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					ImageWidget.prototype = new Widget();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Render this widget into the DOM
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					ImageWidget.prototype.render = function(parent,nextSibling) {
 | 
				
			||||||
 | 
						this.parentDomNode = parent;
 | 
				
			||||||
 | 
						this.computeAttributes();
 | 
				
			||||||
 | 
						this.execute();
 | 
				
			||||||
 | 
						// Create element
 | 
				
			||||||
 | 
						// Determine what type of image it is
 | 
				
			||||||
 | 
						var tag = "img", src = "",
 | 
				
			||||||
 | 
							tiddler = this.wiki.getTiddler(this.imageSource);
 | 
				
			||||||
 | 
						if(!tiddler) {
 | 
				
			||||||
 | 
							// The source isn't the title of a tiddler, so we'll assume it's a URL
 | 
				
			||||||
 | 
							src = this.imageSource;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Check if it is an image tiddler
 | 
				
			||||||
 | 
							if(this.wiki.isImageTiddler(this.imageSource)) {
 | 
				
			||||||
 | 
								// Render the appropriate element for the image type
 | 
				
			||||||
 | 
								var type = tiddler.fields.type,
 | 
				
			||||||
 | 
									text = tiddler.fields.text;
 | 
				
			||||||
 | 
								switch(type) {
 | 
				
			||||||
 | 
									case "application/pdf":
 | 
				
			||||||
 | 
										tag = "embed";
 | 
				
			||||||
 | 
										src = "data:application/pdf;base64," + text;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case "image/svg+xml":
 | 
				
			||||||
 | 
										src = "data:image/svg+xml," + encodeURIComponent(text);
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									default:
 | 
				
			||||||
 | 
										src = "data:" + type + ";base64," + text;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Create the element and assign the attributes
 | 
				
			||||||
 | 
						var domNode = this.document.createElement(tag);
 | 
				
			||||||
 | 
						domNode.setAttribute("src",src);
 | 
				
			||||||
 | 
						if(this.imageClass) {
 | 
				
			||||||
 | 
							domNode.setAttribute("class",this.imageClass);		
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(this.imageWidth) {
 | 
				
			||||||
 | 
							domNode.setAttribute("width",parseInt(this.imageWidth,10) + "px");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(this.imageHeight) {
 | 
				
			||||||
 | 
							domNode.setAttribute("height",parseInt(this.imageHeight,10) + "px");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if(this.imageTooltip) {
 | 
				
			||||||
 | 
							domNode.setAttribute("title",this.imageTooltip);		
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Insert element
 | 
				
			||||||
 | 
						parent.insertBefore(domNode,nextSibling);
 | 
				
			||||||
 | 
						this.domNodes.push(domNode);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Compute the internal state of the widget
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					ImageWidget.prototype.execute = function() {
 | 
				
			||||||
 | 
						// Get our parameters
 | 
				
			||||||
 | 
						this.imageSource = this.getAttribute("source");
 | 
				
			||||||
 | 
						this.imageWidth = this.getAttribute("width");
 | 
				
			||||||
 | 
						this.imageHeight = this.getAttribute("height");
 | 
				
			||||||
 | 
						this.imageClass = this.getAttribute("class");
 | 
				
			||||||
 | 
						this.imageTooltip = this.getAttribute("tooltip");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					ImageWidget.prototype.refresh = function(changedTiddlers) {
 | 
				
			||||||
 | 
						var changedAttributes = this.computeAttributes();
 | 
				
			||||||
 | 
						if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
 | 
				
			||||||
 | 
							this.refreshSelf();
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return false;		
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.image = ImageWidget;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
							
								
								
									
										20
									
								
								editions/tw5.com/tiddlers/widgets/ImageWidget.tid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								editions/tw5.com/tiddlers/widgets/ImageWidget.tid
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					title: ImageWidget
 | 
				
			||||||
 | 
					created: 20140416160234142
 | 
				
			||||||
 | 
					modified: 20140416160234142
 | 
				
			||||||
 | 
					tags: widget
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! Introduction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The image widget displays images that can be specified as a remote URL or the title of a local tiddler containing the image.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! Content and Attributes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Any content of the `<$image>` widget is ignored.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					|!Attribute |!Description |
 | 
				
			||||||
 | 
					|source |The URL of the image, or the title of an image tiddler |
 | 
				
			||||||
 | 
					|width |The width of the image as a number |
 | 
				
			||||||
 | 
					|height |The height of the image |
 | 
				
			||||||
 | 
					|tooltip |The tooltip to be displayed over the image |
 | 
				
			||||||
 | 
					|class |CSS classes to be assigned to the `<img>` element |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,10 +1,44 @@
 | 
				
			|||||||
created: 20131205160221762
 | 
					created: 20131205160221762
 | 
				
			||||||
modified: 20131205160234142
 | 
					modified: 20140416160234142
 | 
				
			||||||
tags: wikitext
 | 
					tags: wikitext
 | 
				
			||||||
title: Images in WikiText
 | 
					title: Images in WikiText
 | 
				
			||||||
type: text/vnd.tiddlywiki
 | 
					type: text/vnd.tiddlywiki
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To display an image stored in a tiddler just transclude that tiddler:
 | 
					! Image Formatting
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Images can be included in WikiText with the following syntax:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					[img[Motovun Jack.jpg]]
 | 
				
			||||||
 | 
					[img[http://tiddlywiki.com/favicon.ico]]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If the image source is the title of an image tiddler then that tiddler is directly displayed. Otherwise it is interpreted as a URL and an HTML `<img>` tag is generated with the `src` attribute containing the URL.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A tooltip can also be specified:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					[img[An explanatory tooltip|Motovun Jack.jpg]]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Attributes can be provided to specify CSS classes and the image width and height:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					[img width=32 [Motovun Jack.jpg]]
 | 
				
			||||||
 | 
					[img width=32 class="tw-image" [Motovun Jack.jpg]]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that attributes can be specified as transclusions or variable references:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					[img width={{!!mywidth}} class=<<image-classes>> [Motovun Jack.jpg]]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The image syntax is a shorthand for invoking the ImageWidget.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! Displaying Images via Transclusion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can also display an image stored in a tiddler by transcluding that tiddler. The disadvantage of this approach is that there is no direct way to control the size of the image.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
{{Motovun Jack.jpg}}
 | 
					{{Motovun Jack.jpg}}
 | 
				
			||||||
@@ -13,3 +47,4 @@ To display an image stored in a tiddler just transclude that tiddler:
 | 
				
			|||||||
Renders as:
 | 
					Renders as:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{{Motovun Jack.jpg}}
 | 
					{{Motovun Jack.jpg}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user