mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-02-11 12:40:22 +00:00
* Introduced preliminary idea for infinite recurse exception * Better handling of infinite recursion But it could be better still... * the TransclusionError is a proper error Moved the magic number to be on the error's class. Not sure if that's a great idea. * Fixed minor minor issue that came up in conflict The minor fix to the jasmine regexp that escaped a '+' somehow broke some random test. * Removing patch fix for recursion errors * Fixed issue where buttton and other widgets don't clean up * Added release notes for #9548 * Update test-widget.js If I don't fix those indentations, the entire TW codebase will explode or soemthing. * Update test-widget.js These lint problems are wasting my time. * Fixed all core widgets to not leak when renderChildren fails * Updated release notes to reflect what I'm actually fixing * Update test-widget.js Added warning not to use for-of loop for defining tests. The iterating variable needs to have its own method scope, or it risks being the same value for all tests.
218 lines
6.2 KiB
JavaScript
218 lines
6.2 KiB
JavaScript
/*\
|
|
title: $:/core/modules/widgets/select.js
|
|
type: application/javascript
|
|
module-type: widget
|
|
|
|
Select widget:
|
|
|
|
```
|
|
<$select tiddler="MyTiddler" field="text">
|
|
<$list filter="[tag[chapter]]">
|
|
<option value=<<currentTiddler>>>
|
|
<$view field="description"/>
|
|
</option>
|
|
</$list>
|
|
</$select>
|
|
```
|
|
|
|
\*/
|
|
|
|
"use strict";
|
|
|
|
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
|
|
|
var SelectWidget = function(parseTreeNode,options) {
|
|
this.initialise(parseTreeNode,options);
|
|
};
|
|
|
|
/*
|
|
Inherit from the base widget class
|
|
*/
|
|
SelectWidget.prototype = new Widget();
|
|
|
|
/*
|
|
Render this widget into the DOM
|
|
*/
|
|
SelectWidget.prototype.render = function(parent,nextSibling) {
|
|
this.parentDomNode = parent;
|
|
this.computeAttributes();
|
|
this.execute();
|
|
//Create element
|
|
var domNode = this.document.createElement("select");
|
|
if(this.selectClass) {
|
|
domNode.className = this.selectClass;
|
|
}
|
|
// Assign data- attributes
|
|
this.assignAttributes(domNode,{
|
|
sourcePrefix: "data-",
|
|
destPrefix: "data-"
|
|
});
|
|
if(this.selectMultiple) {
|
|
domNode.setAttribute("multiple","multiple");
|
|
}
|
|
if(this.isDisabled === "yes") {
|
|
domNode.setAttribute("disabled", true);
|
|
}
|
|
if(this.selectSize) {
|
|
domNode.setAttribute("size",this.selectSize);
|
|
}
|
|
if(this.selectTabindex) {
|
|
domNode.setAttribute("tabindex",this.selectTabindex);
|
|
}
|
|
if(this.selectTooltip) {
|
|
domNode.setAttribute("title",this.selectTooltip);
|
|
}
|
|
this.parentDomNode.insertBefore(domNode,nextSibling);
|
|
this.domNodes.push(domNode);
|
|
this.renderChildren(domNode,null);
|
|
this.setSelectValue();
|
|
if(this.selectFocus == "yes") {
|
|
this.getSelectDomNode().focus();
|
|
}
|
|
$tw.utils.addEventListeners(this.getSelectDomNode(),[
|
|
{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}
|
|
]);
|
|
};
|
|
|
|
/*
|
|
Handle a change event
|
|
*/
|
|
SelectWidget.prototype.handleChangeEvent = function(event) {
|
|
// Get the new value and assign it to the tiddler
|
|
if(this.selectMultiple == false) {
|
|
var value = this.getSelectDomNode().value;
|
|
} else {
|
|
var value = this.getSelectValues();
|
|
value = $tw.utils.stringifyList(value);
|
|
}
|
|
this.wiki.setText(this.selectTitle,this.selectField,this.selectIndex,value);
|
|
// Trigger actions
|
|
if(this.selectActions) {
|
|
this.invokeActionString(this.selectActions,this,event);
|
|
}
|
|
};
|
|
|
|
/*
|
|
If necessary, set the value of the select element to the current value
|
|
*/
|
|
SelectWidget.prototype.setSelectValue = function() {
|
|
var value = this.selectDefault;
|
|
// Get the value
|
|
if(this.selectIndex) {
|
|
value = this.wiki.extractTiddlerDataItem(this.selectTitle,this.selectIndex,value);
|
|
} else {
|
|
var tiddler = this.wiki.getTiddler(this.selectTitle);
|
|
if(tiddler) {
|
|
if(this.selectField === "text") {
|
|
// Calling getTiddlerText() triggers lazy loading of skinny tiddlers
|
|
value = this.wiki.getTiddlerText(this.selectTitle);
|
|
} else {
|
|
if($tw.utils.hop(tiddler.fields,this.selectField)) {
|
|
value = tiddler.getFieldString(this.selectField);
|
|
}
|
|
}
|
|
} else {
|
|
if(this.selectField === "title") {
|
|
value = this.selectTitle;
|
|
}
|
|
}
|
|
}
|
|
// Assign it to the select element if it's different than the current value
|
|
if(this.selectMultiple) {
|
|
value = value === undefined ? "" : value;
|
|
var select = this.getSelectDomNode();
|
|
var child,
|
|
values = Array.isArray(value) ? value : $tw.utils.parseStringArray(value);
|
|
for(var i=0; i < select.children.length; i++){
|
|
child=select.children[i];
|
|
if(child.children.length === 0){
|
|
child.selected = values.indexOf(child.value) !== -1;
|
|
} else {
|
|
// grouped options
|
|
for(var y=0; y < child.children.length; y++){
|
|
child.children[y].selected = values.indexOf(child.children[y].value) !== -1;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
var domNode = this.getSelectDomNode();
|
|
if(domNode.value !== value) {
|
|
domNode.value = value;
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
Get the DOM node of the select element
|
|
*/
|
|
SelectWidget.prototype.getSelectDomNode = function() {
|
|
return this.domNodes[0];
|
|
};
|
|
|
|
// Return an array of the selected opion values
|
|
// select is an HTML select element
|
|
SelectWidget.prototype.getSelectValues = function() {
|
|
var select, result, options, opt;
|
|
select = this.getSelectDomNode();
|
|
result = [];
|
|
options = select && select.options;
|
|
for(var i=0; i<options.length; i++) {
|
|
opt = options[i];
|
|
if(opt.selected) {
|
|
result.push(opt.value || opt.text);
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
|
|
/*
|
|
Compute the internal state of the widget
|
|
*/
|
|
SelectWidget.prototype.execute = function() {
|
|
// Get our parameters
|
|
this.selectActions = this.getAttribute("actions");
|
|
this.selectTitle = this.getAttribute("tiddler",this.getVariable("currentTiddler"));
|
|
this.selectField = this.getAttribute("field","text");
|
|
this.selectIndex = this.getAttribute("index");
|
|
this.selectClass = this.getAttribute("class");
|
|
this.selectDefault = this.getAttribute("default");
|
|
this.selectMultiple = this.getAttribute("multiple", false);
|
|
this.selectSize = this.getAttribute("size");
|
|
this.selectTabindex = this.getAttribute("tabindex");
|
|
this.selectTooltip = this.getAttribute("tooltip");
|
|
this.selectFocus = this.getAttribute("focus");
|
|
this.isDisabled = this.getAttribute("disabled","no");
|
|
// Make the child widgets
|
|
this.makeChildWidgets();
|
|
};
|
|
|
|
/*
|
|
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
|
*/
|
|
SelectWidget.prototype.refresh = function(changedTiddlers) {
|
|
var changedAttributes = this.computeAttributes();
|
|
// If we're using a different tiddler/field/index then completely refresh ourselves
|
|
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tooltip || changedAttributes.default || changedAttributes.tabindex || changedAttributes.disabled) {
|
|
this.refreshSelf();
|
|
return true;
|
|
} else {
|
|
if(changedAttributes.class) {
|
|
this.selectClass = this.getAttribute("class");
|
|
this.getSelectDomNode().setAttribute("class",this.selectClass);
|
|
}
|
|
this.assignAttributes(this.getSelectDomNode(),{
|
|
changedAttributes: changedAttributes,
|
|
sourcePrefix: "data-",
|
|
destPrefix: "data-"
|
|
});
|
|
var childrenRefreshed = this.refreshChildren(changedTiddlers);
|
|
// If the target tiddler value has changed, just update setting and refresh the children
|
|
if(changedTiddlers[this.selectTitle] || childrenRefreshed) {
|
|
this.setSelectValue();
|
|
}
|
|
return childrenRefreshed;
|
|
}
|
|
};
|
|
|
|
exports.select = SelectWidget;
|