Introduce text editor toolbar (#2315)

Tada!
This commit is contained in:
Jeremy Ruston
2016-04-22 08:36:29 +01:00
parent 4dd701c2dd
commit 2adf09129d
234 changed files with 3899 additions and 999 deletions
+59 -58
View File
@@ -13,16 +13,22 @@ Edit-bitmap widget
"use strict";
// Default image sizes
var DEFAULT_IMAGE_WIDTH = 300,
DEFAULT_IMAGE_HEIGHT = 185;
var DEFAULT_IMAGE_WIDTH = 600,
DEFAULT_IMAGE_HEIGHT = 370;
// Configuration tiddlers
var LINE_WIDTH_TITLE = "$:/config/BitmapEditor/LineWidth",
LINE_COLOUR_TITLE = "$:/config/BitmapEditor/Colour";
LINE_COLOUR_TITLE = "$:/config/BitmapEditor/Colour",
LINE_OPACITY_TITLE = "$:/config/BitmapEditor/Opacity";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var EditBitmapWidget = function(parseTreeNode,options) {
// Initialise the editor operations if they've not been done already
if(!this.editorOperations) {
EditBitmapWidget.prototype.editorOperations = {};
$tw.modules.applyMethods("bitmapeditoroperation",this.editorOperations);
}
this.initialise(parseTreeNode,options);
};
@@ -42,7 +48,12 @@ EditBitmapWidget.prototype.render = function(parent,nextSibling) {
this.computeAttributes();
// Execute our logic
this.execute();
// Create our element
// Create the wrapper for the toolbar and render its content
this.toolbarNode = this.document.createElement("div");
this.toolbarNode.className = "tc-editor-toolbar";
parent.insertBefore(this.toolbarNode,nextSibling);
this.domNodes.push(this.toolbarNode);
// Create the on-screen canvas
this.canvasDomNode = $tw.utils.domMaker("canvas",{
document: this.document,
"class":"tc-edit-bitmapeditor",
@@ -60,29 +71,33 @@ EditBitmapWidget.prototype.render = function(parent,nextSibling) {
name: "mouseup", handlerObject: this, handlerMethod: "handleMouseUpEvent"
}]
});
this.widthDomNode = $tw.utils.domMaker("input",{
document: this.document,
"class":"tc-edit-bitmapeditor-width",
eventListeners: [{
name: "change", handlerObject: this, handlerMethod: "handleWidthChangeEvent"
}]
});
this.heightDomNode = $tw.utils.domMaker("input",{
document: this.document,
"class":"tc-edit-bitmapeditor-height",
eventListeners: [{
name: "change", handlerObject: this, handlerMethod: "handleHeightChangeEvent"
}]
});
// Insert the elements into the DOM
// Set the width and height variables
this.setVariable("tv-bitmap-editor-width",this.canvasDomNode.width + "px");
this.setVariable("tv-bitmap-editor-height",this.canvasDomNode.height + "px");
// Render toolbar child widgets
this.renderChildren(this.toolbarNode,null);
// // Insert the elements into the DOM
parent.insertBefore(this.canvasDomNode,nextSibling);
parent.insertBefore(this.widthDomNode,nextSibling);
parent.insertBefore(this.heightDomNode,nextSibling);
this.domNodes.push(this.canvasDomNode,this.widthDomNode,this.heightDomNode);
this.domNodes.push(this.canvasDomNode);
// Load the image into the canvas
if($tw.browser) {
this.loadCanvas();
}
// Add widget message listeners
this.addEventListeners([
{type: "tm-edit-bitmap-operation", handler: "handleEditBitmapOperationMessage"}
]);
};
/*
Handle an edit bitmap operation message from the toolbar
*/
EditBitmapWidget.prototype.handleEditBitmapOperationMessage = function(event) {
// Invoke the handler
var handler = this.editorOperations[event.param];
if(handler) {
handler.call(this,event);
}
};
/*
@@ -91,13 +106,28 @@ Compute the internal state of the widget
EditBitmapWidget.prototype.execute = function() {
// Get our parameters
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
// Make the child widgets
this.makeChildWidgets();
};
/*
Note that the bitmap editor intentionally doesn't try to refresh itself because it would be confusing to have the image changing spontaneously while editting it
Just refresh the toolbar
*/
EditBitmapWidget.prototype.refresh = function(changedTiddlers) {
return false;
return this.refreshChildren(changedTiddlers);
};
/*
Set the bitmap size variables and refresh the toolbar
*/
EditBitmapWidget.prototype.refreshToolbar = function() {
// Set the width and height variables
this.setVariable("tv-bitmap-editor-width",this.canvasDomNode.width + "px");
this.setVariable("tv-bitmap-editor-height",this.canvasDomNode.height + "px");
// Refresh each of our child widgets
$tw.utils.each(this.children,function(childWidget) {
childWidget.refreshSelf();
});
};
EditBitmapWidget.prototype.loadCanvas = function() {
@@ -112,7 +142,7 @@ EditBitmapWidget.prototype.loadCanvas = function() {
self.currCanvas = self.document.createElement("canvas");
self.initCanvas(self.currCanvas,currImage.width,currImage.height,currImage);
// Set the width and height input boxes
self.updateSize();
self.refreshToolbar();
};
currImage.onerror = function() {
// Set the on-screen canvas size and clear it
@@ -121,7 +151,7 @@ EditBitmapWidget.prototype.loadCanvas = function() {
self.currCanvas = self.document.createElement("canvas");
self.initCanvas(self.currCanvas,DEFAULT_IMAGE_WIDTH,DEFAULT_IMAGE_HEIGHT);
// Set the width and height input boxes
self.updateSize();
self.refreshToolbar();
};
// Get the current bitmap into an image object
currImage.src = "data:" + tiddler.fields.type + ";base64," + tiddler.fields.text;
@@ -139,14 +169,6 @@ EditBitmapWidget.prototype.initCanvas = function(canvas,width,height,image) {
}
};
/*
** Update the input boxes with the actual size of the canvas
*/
EditBitmapWidget.prototype.updateSize = function() {
this.widthDomNode.value = this.currCanvas.width;
this.heightDomNode.value = this.currCanvas.height;
};
/*
** Change the size of the canvas, preserving the current image
*/
@@ -167,28 +189,6 @@ EditBitmapWidget.prototype.changeCanvasSize = function(newWidth,newHeight) {
ctx.drawImage(this.currCanvas,0,0);
};
EditBitmapWidget.prototype.handleWidthChangeEvent = function(event) {
// Get the new width
var newWidth = parseInt(this.widthDomNode.value,10);
// Update if necessary
if(newWidth > 0 && newWidth !== this.currCanvas.width) {
this.changeCanvasSize(newWidth,this.currCanvas.height);
}
// Update the input controls
this.updateSize();
};
EditBitmapWidget.prototype.handleHeightChangeEvent = function(event) {
// Get the new width
var newHeight = parseInt(this.heightDomNode.value,10);
// Update if necessary
if(newHeight > 0 && newHeight !== this.currCanvas.height) {
this.changeCanvasSize(this.currCanvas.width,newHeight);
}
// Update the input controls
this.updateSize();
};
EditBitmapWidget.prototype.handleTouchStartEvent = function(event) {
this.brushDown = true;
this.strokeStart(event.touches[0].clientX,event.touches[0].clientY);
@@ -264,8 +264,9 @@ EditBitmapWidget.prototype.strokeMove = function(x,y) {
// Redraw the previous image
ctx.drawImage(this.currCanvas,0,0);
// Render the stroke
ctx.globalAlpha = parseFloat(this.wiki.getTiddlerText(LINE_OPACITY_TITLE,"1.0"));
ctx.strokeStyle = this.wiki.getTiddlerText(LINE_COLOUR_TITLE,"#ff0");
ctx.lineWidth = parseInt(this.wiki.getTiddlerText(LINE_WIDTH_TITLE,"3"),10);
ctx.lineWidth = parseFloat(this.wiki.getTiddlerText(LINE_WIDTH_TITLE,"3"));
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
@@ -292,7 +293,7 @@ EditBitmapWidget.prototype.saveChanges = function() {
var tiddler = this.wiki.getTiddler(this.editTitle);
if(tiddler) {
// data URIs look like "data:<type>;base64,<text>"
var dataURL = this.canvasDomNode.toDataURL(tiddler.fields.type,1.0),
var dataURL = this.canvasDomNode.toDataURL(tiddler.fields.type),
posColon = dataURL.indexOf(":"),
posSemiColon = dataURL.indexOf(";"),
posComma = dataURL.indexOf(","),
+139
View File
@@ -0,0 +1,139 @@
/*\
title: $:/core/modules/widgets/edit-shortcut.js
type: application/javascript
module-type: widget
Widget to display an editable keyboard shortcut
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var EditShortcutWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
EditShortcutWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
EditShortcutWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.inputNode = this.document.createElement("input");
// Assign classes
if(this.shortcutClass) {
this.inputNode.className = this.shortcutClass;
}
// Assign other attributes
if(this.shortcutStyle) {
this.inputNode.setAttribute("style",this.shortcutStyle);
}
if(this.shortcutTooltip) {
this.inputNode.setAttribute("title",this.shortcutTooltip);
}
if(this.shortcutPlaceholder) {
this.inputNode.setAttribute("placeholder",this.shortcutPlaceholder);
}
if(this.shortcutAriaLabel) {
this.inputNode.setAttribute("aria-label",this.shortcutAriaLabel);
}
// Assign the current shortcut
this.updateInputNode();
// Add event handlers
$tw.utils.addEventListeners(this.inputNode,[
{name: "keydown", handlerObject: this, handlerMethod: "handleKeydownEvent"}
]);
// Link into the DOM
parent.insertBefore(this.inputNode,nextSibling);
this.domNodes.push(this.inputNode);
};
/*
Compute the internal state of the widget
*/
EditShortcutWidget.prototype.execute = function() {
this.shortcutTiddler = this.getAttribute("tiddler");
this.shortcutField = this.getAttribute("field");
this.shortcutIndex = this.getAttribute("index");
this.shortcutPlaceholder = this.getAttribute("placeholder");
this.shortcutDefault = this.getAttribute("default","");
this.shortcutClass = this.getAttribute("class");
this.shortcutStyle = this.getAttribute("style");
this.shortcutTooltip = this.getAttribute("tooltip");
this.shortcutAriaLabel = this.getAttribute("aria-label");
};
/*
Update the value of the input node
*/
EditShortcutWidget.prototype.updateInputNode = function() {
if(this.shortcutField) {
var tiddler = this.wiki.getTiddler(this.shortcutTiddler);
if(tiddler && $tw.utils.hop(tiddler.fields,this.shortcutField)) {
this.inputNode.value = tiddler.getFieldString(this.shortcutField);
} else {
this.inputNode.value = this.shortcutDefault;
}
} else if(this.shortcutIndex) {
this.inputNode.value = this.wiki.extractTiddlerDataItem(this.shortcutTiddler,this.shortcutIndex,this.shortcutDefault);
} else {
this.inputNode.value = this.wiki.getTiddlerText(this.shortcutTiddler,this.shortcutDefault);
}
};
/*
Handle a dom "keydown" event
*/
EditShortcutWidget.prototype.handleKeydownEvent = function(event) {
// Ignore shift, ctrl, meta, alt
if(event.keyCode && $tw.keyboardManager.getModifierKeys().indexOf(event.keyCode) === -1) {
// Get the shortcut text representation
var value = $tw.keyboardManager.getPrintableShortcuts([{
ctrlKey: event.ctrlKey,
shiftKey: event.shiftKey,
altKey: event.altKey,
metaKey: event.metaKey,
keyCode: event.keyCode
}]);
if(value.length > 0) {
this.wiki.setText(this.shortcutTiddler,this.shortcutField,this.shortcutIndex,value[0]);
}
// Ignore the keydown if it was already handled
event.preventDefault();
event.stopPropagation();
return true;
} else {
return false;
}
};
/*
Selectively refreshes the widget if needed. Returns true if the widget needed re-rendering
*/
EditShortcutWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.placeholder || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.style || changedAttributes.tooltip || changedAttributes["aria-label"]) {
this.refreshSelf();
return true;
} else if(changedTiddlers[this.shortcutTiddler]) {
this.updateInputNode();
return true;
} else {
return false;
}
};
exports["edit-shortcut"] = EditShortcutWidget;
})();
+4 -274
View File
@@ -12,280 +12,10 @@ Edit-text widget
/*global $tw: false */
"use strict";
var DEFAULT_MIN_TEXT_AREA_HEIGHT = "100px"; // Minimum height of textareas in pixels
var editTextWidgetFactory = require("$:/core/modules/editor/factory.js").editTextWidgetFactory,
FramedEngine = require("$:/core/modules/editor/engines/framed.js").FramedEngine,
SimpleEngine = require("$:/core/modules/editor/engines/simple.js").SimpleEngine;
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var EditTextWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
EditTextWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
EditTextWidget.prototype.render = function(parent,nextSibling) {
var self = this;
// Save the parent dom node
this.parentDomNode = parent;
// Compute our attributes
this.computeAttributes();
// Execute our logic
this.execute();
// Create our element
var editInfo = this.getEditInfo(),
tag = this.editTag;
if($tw.config.htmlUnsafeElements.indexOf(tag) !== -1) {
tag = "input";
}
var domNode = this.document.createElement(tag);
if(this.editType) {
domNode.setAttribute("type",this.editType);
}
if(editInfo.value === "" && this.editPlaceholder) {
domNode.setAttribute("placeholder",this.editPlaceholder);
}
if(this.editSize) {
domNode.setAttribute("size",this.editSize);
}
if(this.editRows) {
domNode.setAttribute("rows",this.editRows);
}
// Assign classes
if(this.editClass) {
domNode.className = this.editClass;
}
// Set the text
if(this.editTag === "textarea") {
domNode.appendChild(this.document.createTextNode(editInfo.value));
} else {
domNode.value = editInfo.value;
}
// Add an input event handler
$tw.utils.addEventListeners(domNode,[
{name: "focus", handlerObject: this, handlerMethod: "handleFocusEvent"},
{name: "input", handlerObject: this, handlerMethod: "handleInputEvent"}
]);
// Insert the element into the DOM
parent.insertBefore(domNode,nextSibling);
this.domNodes.push(domNode);
if(this.postRender) {
this.postRender();
}
// Fix height
this.fixHeight();
// Focus field
if(this.editFocus === "true") {
if(domNode.focus && domNode.select) {
domNode.focus();
domNode.select();
}
}
};
/*
Get the tiddler being edited and current value
*/
EditTextWidget.prototype.getEditInfo = function() {
// Get the edit value
var self = this,
value,
update;
if(this.editIndex) {
value = this.wiki.extractTiddlerDataItem(this.editTitle,this.editIndex,this.editDefault);
update = function(value) {
var data = self.wiki.getTiddlerData(self.editTitle,{});
if(data[self.editIndex] !== value) {
data[self.editIndex] = value;
self.wiki.setTiddlerData(self.editTitle,data);
}
};
} else {
// Get the current tiddler and the field name
var tiddler = this.wiki.getTiddler(this.editTitle);
if(tiddler) {
// If we've got a tiddler, the value to display is the field string value
value = tiddler.getFieldString(this.editField);
} else {
// Otherwise, we need to construct a default value for the editor
switch(this.editField) {
case "text":
value = "Type the text for the tiddler '" + this.editTitle + "'";
break;
case "title":
value = this.editTitle;
break;
default:
value = "";
break;
}
if(this.editDefault !== undefined) {
value = this.editDefault;
}
}
update = function(value) {
var tiddler = self.wiki.getTiddler(self.editTitle),
updateFields = {
title: self.editTitle
};
updateFields[self.editField] = value;
self.wiki.addTiddler(new $tw.Tiddler(self.wiki.getCreationFields(),tiddler,updateFields,self.wiki.getModificationFields()));
};
}
return {value: value, update: update};
};
/*
Compute the internal state of the widget
*/
EditTextWidget.prototype.execute = function() {
// Get our parameters
this.editTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
this.editField = this.getAttribute("field","text");
this.editIndex = this.getAttribute("index");
this.editDefault = this.getAttribute("default");
this.editClass = this.getAttribute("class");
this.editPlaceholder = this.getAttribute("placeholder");
this.editSize = this.getAttribute("size");
this.editRows = this.getAttribute("rows");
this.editAutoHeight = this.getAttribute("autoHeight","yes") === "yes";
this.editMinHeight = this.getAttribute("minHeight",DEFAULT_MIN_TEXT_AREA_HEIGHT);
this.editFocusPopup = this.getAttribute("focusPopup");
this.editFocus = this.getAttribute("focus");
// Get the editor element tag and type
var tag,type;
if(this.editField === "text") {
tag = "textarea";
} else {
tag = "input";
var fieldModule = $tw.Tiddler.fieldModules[this.editField];
if(fieldModule && fieldModule.editTag) {
tag = fieldModule.editTag;
}
if(fieldModule && fieldModule.editType) {
type = fieldModule.editType;
}
type = type || "text";
}
// Get the rest of our parameters
this.editTag = this.getAttribute("tag",tag);
this.editType = this.getAttribute("type",type);
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
EditTextWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Completely rerender if any of our attributes have changed
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes["default"] || changedAttributes["class"] || changedAttributes.placeholder || changedAttributes.size || changedAttributes.autoHeight || changedAttributes.minHeight || changedAttributes.focusPopup || changedAttributes.rows) {
this.refreshSelf();
return true;
} else if(changedTiddlers[this.editTitle]) {
this.updateEditor(this.getEditInfo().value);
return true;
}
// Fix the height anyway in case there has been a reflow
this.fixHeight();
return false;
};
/*
Update the editor with new text. This method is separate from updateEditorDomNode()
so that subclasses can override updateEditor() and still use updateEditorDomNode()
*/
EditTextWidget.prototype.updateEditor = function(text) {
this.updateEditorDomNode(text);
};
/*
Update the editor dom node with new text
*/
EditTextWidget.prototype.updateEditorDomNode = function(text) {
// Replace the edit value if the tiddler we're editing has changed
var domNode = this.domNodes[0];
if(!domNode.isTiddlyWikiFakeDom) {
if(this.document.activeElement !== domNode) {
domNode.value = text;
}
// Fix the height if needed
this.fixHeight();
}
};
/*
Get the first parent element that has scrollbars or use the body as fallback.
*/
EditTextWidget.prototype.getScrollContainer = function(el) {
while(el.parentNode) {
el = el.parentNode;
if(el.scrollTop) {
return el;
}
}
return this.document.body;
};
/*
Fix the height of textareas to fit their content
*/
EditTextWidget.prototype.fixHeight = function() {
var domNode = this.domNodes[0];
if(this.editAutoHeight && domNode && !domNode.isTiddlyWikiFakeDom && this.editTag === "textarea") {
// Resize the textarea to fit its content, preserving scroll position
// Get the scroll container and register the current scroll position
var container = this.getScrollContainer(domNode),
scrollTop = container.scrollTop;
// Measure the specified minimum height
domNode.style.height = this.editMinHeight;
var minHeight = domNode.offsetHeight;
// Set its height to auto so that it snaps to the correct height
domNode.style.height = "auto";
// Calculate the revised height
var newHeight = Math.max(domNode.scrollHeight + domNode.offsetHeight - domNode.clientHeight,minHeight);
// Only try to change the height if it has changed
if(newHeight !== domNode.offsetHeight) {
domNode.style.height = newHeight + "px";
// Make sure that the dimensions of the textarea are recalculated
$tw.utils.forceLayout(domNode);
// Set the container to the position we registered at the beginning
container.scrollTop = scrollTop;
}
}
};
/*
Handle a dom "input" event
*/
EditTextWidget.prototype.handleInputEvent = function(event) {
this.saveChanges(this.domNodes[0].value);
this.fixHeight();
return true;
};
EditTextWidget.prototype.handleFocusEvent = function(event) {
if(this.editFocusPopup) {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
title: this.editFocusPopup,
wiki: this.wiki,
force: true
});
}
return true;
};
EditTextWidget.prototype.saveChanges = function(text) {
var editInfo = this.getEditInfo();
if(text !== editInfo.value) {
editInfo.update(text);
}
};
exports["edit-text"] = EditTextWidget;
exports["edit-text"] = editTextWidgetFactory(FramedEngine,SimpleEngine);
})();
+2 -1
View File
@@ -57,7 +57,8 @@ EditWidget.prototype.execute = function() {
index: {type: "string", value: this.editIndex},
"class": {type: "string", value: this.editClass},
"placeholder": {type: "string", value: this.editPlaceholder}
}
},
children: this.parseTreeNode.children
}]);
};
+9 -2
View File
@@ -29,7 +29,8 @@ Render this widget into the DOM
EntityWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.execute();
var textNode = this.document.createTextNode($tw.utils.entityDecode(this.parseTreeNode.entity));
var entityString = this.getAttribute("entity",this.parseTreeNode.entity || ""),
textNode = this.document.createTextNode($tw.utils.entityDecode(entityString));
parent.insertBefore(textNode,nextSibling);
this.domNodes.push(textNode);
};
@@ -44,7 +45,13 @@ EntityWidget.prototype.execute = function() {
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
EntityWidget.prototype.refresh = function(changedTiddlers) {
return false;
var changedAttributes = this.computeAttributes();
if(changedAttributes.entity) {
this.refreshSelf();
return true;
} else {
return false;
}
};
exports.entity = EntityWidget;
+2 -2
View File
@@ -41,7 +41,7 @@ KeyboardWidget.prototype.render = function(parent,nextSibling) {
domNode.className = classes.join(" ");
// Add a keyboard event handler
domNode.addEventListener("keydown",function (event) {
if($tw.utils.checkKeyDescriptor(event,self.keyInfo)) {
if($tw.keyboardManager.checkKeyDescriptors(event,self.keyInfoArray)) {
self.invokeActions(this,event);
self.dispatchMessage(event);
event.preventDefault();
@@ -68,7 +68,7 @@ KeyboardWidget.prototype.execute = function() {
this.message = this.getAttribute("message");
this.param = this.getAttribute("param");
this.key = this.getAttribute("key");
this.keyInfo = $tw.utils.parseKeyDescriptor(this.key);
this.keyInfoArray = $tw.keyboardManager.parseKeyDescriptors(this.key);
this["class"] = this.getAttribute("class");
// Make child widgets
this.makeChildWidgets();
+154
View File
@@ -0,0 +1,154 @@
/*\
title: $:/core/modules/widgets/wikify.js
type: application/javascript
module-type: widget
Widget to wikify text into a variable
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var WikifyWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
WikifyWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
WikifyWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
WikifyWidget.prototype.execute = function() {
// Get our parameters
this.wikifyName = this.getAttribute("name");
this.wikifyText = this.getAttribute("text");
this.wikifyType = this.getAttribute("type");
this.wikifyMode = this.getAttribute("mode","block");
this.wikifyOutput = this.getAttribute("output","text");
// Create the parse tree
this.wikifyParser = this.wiki.parseText(this.wikifyType,this.wikifyText,{
parseAsInline: this.wikifyMode === "inline"
});
// Create the widget tree
this.wikifyWidgetNode = this.wiki.makeWidget(this.wikifyParser,{
document: $tw.fakeDocument,
parentWidget: this
});
// Render the widget tree to the container
this.wikifyContainer = $tw.fakeDocument.createElement("div");
this.wikifyWidgetNode.render(this.wikifyContainer,null);
this.wikifyResult = this.getResult();
// Set context variable
this.setVariable(this.wikifyName,this.wikifyResult);
// Construct the child widgets
this.makeChildWidgets();
};
/*
Return the result string
*/
WikifyWidget.prototype.getResult = function() {
var result;
switch(this.wikifyOutput) {
case "text":
result = this.wikifyContainer.textContent;
break;
case "html":
result = this.wikifyContainer.innerHTML;
break;
case "parsetree":
result = JSON.stringify(this.wikifyParser.tree,0,$tw.config.preferences.jsonSpaces);
break;
case "widgettree":
result = JSON.stringify(this.getWidgetTree(),0,$tw.config.preferences.jsonSpaces);
break;
}
return result;
};
/*
Return a string of the widget tree
*/
WikifyWidget.prototype.getWidgetTree = function() {
var copyNode = function(widgetNode,resultNode) {
var type = widgetNode.parseTreeNode.type;
resultNode.type = type;
switch(type) {
case "element":
resultNode.tag = widgetNode.parseTreeNode.tag;
break;
case "text":
resultNode.text = widgetNode.parseTreeNode.text;
break;
}
if(Object.keys(widgetNode.attributes || {}).length > 0) {
resultNode.attributes = {};
$tw.utils.each(widgetNode.attributes,function(attr,attrName) {
resultNode.attributes[attrName] = widgetNode.getAttribute(attrName);
});
}
if(Object.keys(widgetNode.children || {}).length > 0) {
resultNode.children = [];
$tw.utils.each(widgetNode.children,function(widgetChildNode) {
var node = {};
resultNode.children.push(node);
copyNode(widgetChildNode,node);
});
}
},
results = {};
copyNode(this.wikifyWidgetNode,results);
return results;
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
WikifyWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
// Refresh ourselves entirely if any of our attributes have changed
if(changedAttributes.name || changedAttributes.text || changedAttributes.type || changedAttributes.mode || changedAttributes.output) {
this.refreshSelf();
return true;
} else {
// Refresh the widget tree
if(this.wikifyWidgetNode.refresh(changedTiddlers)) {
// Check if there was any change
var result = this.getResult();
if(result !== this.wikifyResult) {
// If so, save the change
this.wikifyResult = result;
this.setVariable(this.wikifyName,this.wikifyResult);
// Refresh each of our child widgets
$tw.utils.each(this.children,function(childWidget) {
childWidget.refreshSelf();
});
return true;
}
}
// Just refresh the children
return this.refreshChildren(changedTiddlers);
}
};
exports.wikify = WikifyWidget;
})();