OcttKB/Wiki-OcttKB/plugins/condition/$__plugins_ebalster_condition_widgets_condition.js

163 lines
4.3 KiB
JavaScript

/*\
title: $:/plugins/ebalster/condition/widgets/condition.js
type: application/javascript
module-type: widget
Base class for condition widgets.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var ConditionWidget = function(parseTreeNode,options) {
if(arguments.length > 0) {
this.initialise(parseTreeNode,options);
}
};
/*
Inherit from the base widget class
*/
ConditionWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
ConditionWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.rerender(parent,nextSibling);
};
ConditionWidget.prototype.rerender = function(parent,nextSibling) {
this.removeChildDomNodes();
if (this.conditionError) {
// Show an error.
var parseTreeNodes = [{type: "element", tag: "span", attributes: {
"class": {type: "string", value: "tc-error"}
}, children: [
{type: "text", text: this.conditionError}
]}];
this.makeChildWidgets(parseTreeNodes);
}
else if (this.isOpen) {
// Construct and render the child widgets.
this.makeChildWidgets(this.parseTreeNode.children);
}
else {
// Destroy the child widgets.
this.children = [];
}
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget (default behavior)
*/
ConditionWidget.prototype.execute = function() {
this.executeIf("$condition");
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
ConditionWidget.prototype.refresh = function(changedTiddlers) {
var currentlyOpen = this.isOpen;
var changedAttributes = this.computeAttributes();
this.execute();
if(this.isOpen !== currentlyOpen) {
var nextSibling = this.findNextSiblingDomNode();
this.rerender(this.parentDomNode,nextSibling);
return true;
}
return this.refreshChildren(changedTiddlers);
};
/*
Utility: Is a value "truthy"?
*/
ConditionWidget.prototype.valueIsTruthy = function(value) {
// It's truthy if it's not falsy, ie, undefined, false, blank or zero.
return !(/^\s*(undefined|false|null|0+|0*\.0+|0+\.0*|)\s*$/i.test(value));
};
/*
Utility: Find a preceding non-text widget for an "else" widget.
*/
ConditionWidget.prototype.findPrecedingConditionWidget = function() {
var siblings = (this.parentWidget ? this.parentWidget.children : null);
var sibling;
if (siblings) {
for (var i = siblings.indexOf(this)-1; i >= 0; --i) {
sibling = siblings[i];
if (sibling.parseTreeNode.type == "text") continue;
if (sibling.isOpen != null || sibling.list != null) return sibling;
return null;
}
}
return null;
};
/*
Utility: Test if another widget triggers an "else"; ie, false conditions, closed reveals, empty lists.
*/
ConditionWidget.prototype.widgetTriggersElse = function(widget) {
// Condition widgets
if (widget.triggerElse != null) return widget.triggerElse;
// Reveal widget
if (widget.isOpen != null) return !widget.isOpen;
// List widget
if (widget.list != null) return (widget.list instanceof Array) && widget.list.length == 0;
};
/*
Utility: Execute as an "else" condition, computing isOpen and conditionError accordingly.
*/
ConditionWidget.prototype.executeElse = function(widgetName) {
this.isOpen = false;
this.conditionError = null;
this.triggerElse = false;
var predicate = this.findPrecedingConditionWidget();
if (!predicate) {
this.conditionError = (widgetName||"$else") + " widget must follow $if, $else-if, $reveal or $list.";
return;
}
this.isOpen = this.widgetTriggersElse(predicate);
};
/*
Utility: Execute as an "if" condition, computing isOpen and conditionError accordingly.
*/
ConditionWidget.prototype.executeIf = function(widgetName) {
this.isOpen = false;
this.conditionError = null;
this.triggerElse = false;
// Re-check our "if" condition.
var value = this.getAttribute("value");
var match = this.getAttribute("match");
if (value == null) {
this.conditionError = (widgetName||"$condition") + " widget requires a 'value' attribute.";
return;
}
else if (match == null) {
// Open if the value is truthy.
this.isOpen = this.valueIsTruthy(value);
}
else {
this.isOpen = (value == match);
}
if (this.getAttribute("not")) {
this.isOpen = !this.isOpen;
}
this.triggerElse = !this.isOpen;
};
exports.condition = ConditionWidget;
})();