mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-01-25 04:14:40 +00:00
Compare commits
48 Commits
conditiona
...
allow-prag
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
055c775b71 | ||
|
|
d1c7f79dd2 | ||
|
|
862cb01be7 | ||
|
|
a416197512 | ||
|
|
f22e047fa2 | ||
|
|
0716ed4c30 | ||
|
|
78d125f6b5 | ||
|
|
62bb8affa4 | ||
|
|
4c2979286b | ||
|
|
de6b866f22 | ||
|
|
06b1cc4bca | ||
|
|
0cd3c9a8ac | ||
|
|
37c625384a | ||
|
|
a4850ba3d9 | ||
|
|
145a8d6992 | ||
|
|
9012d36859 | ||
|
|
ab72cc7b09 | ||
|
|
bf8b3cff03 | ||
|
|
e4bf7c5f44 | ||
|
|
215bd4e015 | ||
|
|
758089cbb3 | ||
|
|
6567843927 | ||
|
|
4b56cb4298 | ||
|
|
902c7f55ba | ||
|
|
801e8e312c | ||
|
|
23a576b8bd | ||
|
|
6a52081d6b | ||
|
|
246751be1b | ||
|
|
4ebaba8e89 | ||
|
|
efaa8dd1e8 | ||
|
|
326ae61929 | ||
|
|
c185e373c5 | ||
|
|
2ffbfd84a5 | ||
|
|
faef02df7a | ||
|
|
c93d56667e | ||
|
|
3855a9f013 | ||
|
|
4d548580e6 | ||
|
|
9773dff1e3 | ||
|
|
c6604c0c56 | ||
|
|
e521ee2133 | ||
|
|
a7bd134c35 | ||
|
|
d3f5695601 | ||
|
|
efa4f34131 | ||
|
|
fffbedd6b9 | ||
|
|
ff1437e439 | ||
|
|
32966c9e91 | ||
|
|
96b0543351 | ||
|
|
b7562f0c7b |
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -5,7 +5,7 @@ on:
|
||||
- master
|
||||
- tiddlywiki-com
|
||||
env:
|
||||
NODE_VERSION: "12"
|
||||
NODE_VERSION: "18"
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -14,7 +14,13 @@ jobs:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: "${{ env.NODE_VERSION }}"
|
||||
- run: "./bin/test.sh"
|
||||
- run: "./bin/ci-test.sh"
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
||||
build-prerelease:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master'
|
||||
@@ -54,6 +60,7 @@ jobs:
|
||||
TW5_BUILD_TIDDLYWIKI: "./node_modules/tiddlywiki/tiddlywiki.js"
|
||||
TW5_BUILD_MAIN_EDITION: "./editions/tw5.com"
|
||||
TW5_BUILD_OUTPUT: "./output"
|
||||
TW5_BUILD_ARCHIVE: "./output"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,4 +5,6 @@
|
||||
tmp/
|
||||
output/
|
||||
node_modules/
|
||||
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
||||
@@ -84,10 +84,27 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n"
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Core distribution
|
||||
# Core distributions
|
||||
#
|
||||
######################################################
|
||||
|
||||
# Conditionally build archive if $TW5_BUILD_ARCHIVE variable is set, otherwise do nothing
|
||||
#
|
||||
# /archive/Empty-TiddlyWiki-<version>.html Empty archived version
|
||||
# /archive/TiddlyWiki-<version>.html Full archived version
|
||||
|
||||
if [ -n "$TW5_BUILD_ARCHIVE" ]; then
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
--version \
|
||||
--load $TW5_BUILD_OUTPUT/build.tid \
|
||||
--output $TW5_BUILD_ARCHIVE \
|
||||
--build archive \
|
||||
|| exit 1
|
||||
fi
|
||||
|
||||
# /index.html Main site
|
||||
# /favicon.ico Favicon for main site
|
||||
# /static.html Static rendering of default tiddlers
|
||||
@@ -95,6 +112,7 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n"
|
||||
# /static/* Static single tiddlers
|
||||
# /static/static.css Static stylesheet
|
||||
# /static/favicon.ico Favicon for static pages
|
||||
|
||||
node $TW5_BUILD_TIDDLYWIKI \
|
||||
$TW5_BUILD_MAIN_EDITION \
|
||||
--verbose \
|
||||
|
||||
16
bin/ci-test.sh
Executable file
16
bin/ci-test.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
# test TiddlyWiki5 for tiddlywiki.com
|
||||
|
||||
node ./tiddlywiki.js \
|
||||
./editions/test \
|
||||
--verbose \
|
||||
--version \
|
||||
--rendertiddler $:/core/save/all test.html text/plain \
|
||||
--test \
|
||||
|| exit 1
|
||||
|
||||
npm install playwright @playwright/test
|
||||
npx playwright install chromium firefox --with-deps
|
||||
|
||||
npx playwright test
|
||||
@@ -18,16 +18,20 @@ Export our filter functions
|
||||
|
||||
exports.decodebase64 = function(source,operator,options) {
|
||||
var results = [];
|
||||
var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1;
|
||||
var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1;
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.base64Decode(title));
|
||||
results.push($tw.utils.base64Decode(title,binary,urlsafe));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
exports.encodebase64 = function(source,operator,options) {
|
||||
var results = [];
|
||||
var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1;
|
||||
var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1;
|
||||
source(function(tiddler,title) {
|
||||
results.push($tw.utils.base64Encode(title));
|
||||
results.push($tw.utils.base64Encode(title,binary,urlsafe));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
@@ -68,6 +68,54 @@ exports["jsontype"] = function(source,operator,options) {
|
||||
return results;
|
||||
};
|
||||
|
||||
exports["jsonset"] = function(source,operator,options) {
|
||||
var suffixes = operator.suffixes || [],
|
||||
type = suffixes[0] && suffixes[0][0],
|
||||
indexes = operator.operands.slice(0,-1),
|
||||
value = operator.operands[operator.operands.length - 1],
|
||||
results = [];
|
||||
if(operator.operands.length === 1 && operator.operands[0] === "") {
|
||||
value = undefined; // Prevents the value from being assigned
|
||||
}
|
||||
switch(type) {
|
||||
case "string":
|
||||
// Use value unchanged
|
||||
break;
|
||||
case "boolean":
|
||||
value = (value === "true" ? true : (value === "false" ? false : undefined));
|
||||
break;
|
||||
case "number":
|
||||
value = $tw.utils.parseNumber(value);
|
||||
break;
|
||||
case "array":
|
||||
indexes = operator.operands;
|
||||
value = [];
|
||||
break;
|
||||
case "object":
|
||||
indexes = operator.operands;
|
||||
value = {};
|
||||
break;
|
||||
case "null":
|
||||
indexes = operator.operands;
|
||||
value = null;
|
||||
break;
|
||||
case "json":
|
||||
value = $tw.utils.parseJSONSafe(value,function() {return undefined;});
|
||||
break;
|
||||
default:
|
||||
// Use value unchanged
|
||||
break;
|
||||
}
|
||||
source(function(tiddler,title) {
|
||||
var data = $tw.utils.parseJSONSafe(title,title);
|
||||
if(data) {
|
||||
data = setDataItem(data,indexes,value);
|
||||
results.push(JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return an array of the string representation of the values at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
@@ -165,6 +213,18 @@ function getDataItemType(data,indexes) {
|
||||
}
|
||||
}
|
||||
|
||||
function getItemAtIndex(item,index) {
|
||||
if($tw.utils.hop(item,index)) {
|
||||
return item[index];
|
||||
} else if($tw.utils.isArray(item)) {
|
||||
index = $tw.utils.parseInt(index);
|
||||
if(index < 0) { index = index + item.length };
|
||||
return item[index]; // Will be undefined if index was out-of-bounds
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid
|
||||
*/
|
||||
@@ -177,7 +237,7 @@ function getDataItem(data,indexes) {
|
||||
for(var i=0; i<indexes.length; i++) {
|
||||
if(item !== undefined) {
|
||||
if(item !== null && ["number","string","boolean"].indexOf(typeof item) === -1) {
|
||||
item = item[indexes[i]];
|
||||
item = getItemAtIndex(item,indexes[i]);
|
||||
} else {
|
||||
item = undefined;
|
||||
}
|
||||
@@ -186,5 +246,36 @@ function getDataItem(data,indexes) {
|
||||
return item;
|
||||
}
|
||||
|
||||
/*
|
||||
Given a JSON data structure, an array of index strings and a value, return the data structure with the value added at the end of the index chain. If any of the index strings are invalid then the JSON data structure is returned unmodified. If the root item is targetted then a different data object will be returned
|
||||
*/
|
||||
function setDataItem(data,indexes,value) {
|
||||
// Ignore attempts to assign undefined
|
||||
if(value === undefined) {
|
||||
return data;
|
||||
}
|
||||
// Check for the root item
|
||||
if(indexes.length === 0 || (indexes.length === 1 && indexes[0] === "")) {
|
||||
return value;
|
||||
}
|
||||
// Traverse the JSON data structure using the index chain
|
||||
var current = data;
|
||||
for(var i = 0; i < indexes.length - 1; i++) {
|
||||
current = getItemAtIndex(current,indexes[i]);
|
||||
if(current === undefined) {
|
||||
// Return the original JSON data structure if any of the index strings are invalid
|
||||
return data;
|
||||
}
|
||||
}
|
||||
// Add the value to the end of the index chain
|
||||
var lastIndex = indexes[indexes.length - 1];
|
||||
if($tw.utils.isArray(current)) {
|
||||
lastIndex = $tw.utils.parseInt(lastIndex);
|
||||
if(lastIndex < 0) { lastIndex = lastIndex + current.length };
|
||||
}
|
||||
current[lastIndex] = value;
|
||||
return data;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -58,6 +58,7 @@ Last entry/entries in list
|
||||
exports.last = function(source,operator,options) {
|
||||
var count = $tw.utils.getInt(operator.operand,1),
|
||||
results = [];
|
||||
if(count === 0) return results;
|
||||
source(function(tiddler,title) {
|
||||
results.push(title);
|
||||
});
|
||||
|
||||
@@ -14,10 +14,12 @@ The plain text parser processes blocks of source text into a degenerate parse tr
|
||||
|
||||
var TextParser = function(type,text,options) {
|
||||
this.tree = [{
|
||||
type: "codeblock",
|
||||
type: "genesis",
|
||||
attributes: {
|
||||
code: {type: "string", value: text},
|
||||
language: {type: "string", value: type}
|
||||
$type: {name: "$type", type: "string", value: "$codeblock"},
|
||||
code: {name: "code", type: "string", value: text},
|
||||
language: {name: "language", type: "string", value: type},
|
||||
$remappable: {name: "$remappable", type:"string", value: "no"}
|
||||
}
|
||||
}];
|
||||
this.source = text;
|
||||
@@ -32,4 +34,3 @@ exports["text/css"] = TextParser;
|
||||
exports["application/x-tiddler-dictionary"] = TextParser;
|
||||
|
||||
})();
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ Parse any pragmas at the beginning of a block of parse text
|
||||
WikiParser.prototype.parsePragmas = function() {
|
||||
var currentTreeBranch = this.tree;
|
||||
while(true) {
|
||||
var savedPos = this.pos;
|
||||
// Skip whitespace
|
||||
this.skipWhitespace();
|
||||
// Check for the end of the text
|
||||
@@ -204,6 +205,7 @@ WikiParser.prototype.parsePragmas = function() {
|
||||
var nextMatch = this.findNextMatch(this.pragmaRules,this.pos);
|
||||
// If not, just exit
|
||||
if(!nextMatch || nextMatch.matchIndex !== this.pos) {
|
||||
this.pos = savedPos;
|
||||
break;
|
||||
}
|
||||
// Process the pragma rule
|
||||
|
||||
@@ -31,7 +31,7 @@ GitHubSaver.prototype.save = function(text,method,callback) {
|
||||
headers = {
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Authorization": "Basic " + window.btoa(username + ":" + password),
|
||||
"Authorization": "Basic " + $tw.utils.base64Encode(username + ":" + password),
|
||||
"If-None-Match": ""
|
||||
};
|
||||
// Bail if we don't have everything we need
|
||||
|
||||
@@ -40,7 +40,7 @@ exports.startup = function() {
|
||||
variables = $tw.utils.extend({},paramObject,{currentTiddler: title, "tv-window-id": windowID});
|
||||
// Open the window
|
||||
var srcWindow,
|
||||
srcDocument;
|
||||
srcDocument;
|
||||
// In case that popup blockers deny opening a new window
|
||||
try {
|
||||
srcWindow = window.open("","external-" + windowID,"scrollbars,width=" + width + ",height=" + height + (top ? ",top=" + top : "" ) + (left ? ",left=" + left : "" )),
|
||||
@@ -52,6 +52,7 @@ exports.startup = function() {
|
||||
$tw.windows[windowID] = srcWindow;
|
||||
// Check for reopening the same window
|
||||
if(srcWindow.haveInitialisedWindow) {
|
||||
srcWindow.focus();
|
||||
return;
|
||||
}
|
||||
// Initialise the document
|
||||
|
||||
@@ -313,7 +313,7 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
|
||||
variables["dom-" + attribute.name] = attribute.value.toString();
|
||||
});
|
||||
|
||||
if(selectedNode.offsetLeft) {
|
||||
if("offsetLeft" in selectedNode) {
|
||||
// Add variables with a (relative and absolute) popup coordinate string for the selected node
|
||||
var nodeRect = {
|
||||
left: selectedNode.offsetLeft,
|
||||
@@ -338,12 +338,12 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
|
||||
}
|
||||
}
|
||||
|
||||
if(domNode && domNode.offsetWidth) {
|
||||
if(domNode && ("offsetWidth" in domNode)) {
|
||||
variables["tv-widgetnode-width"] = domNode.offsetWidth.toString();
|
||||
variables["tv-widgetnode-height"] = domNode.offsetHeight.toString();
|
||||
}
|
||||
|
||||
if(event && event.clientX && event.clientY) {
|
||||
if(event && ("clientX" in event) && ("clientY" in event)) {
|
||||
if(selectedNode) {
|
||||
// Add variables for event X and Y position relative to selected node
|
||||
selectedNodeRect = selectedNode.getBoundingClientRect();
|
||||
|
||||
@@ -187,7 +187,7 @@ HttpClientRequest.prototype.send = function(callback) {
|
||||
for (var i=0; i<len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
resultVariables.data = window.btoa(binary);
|
||||
resultVariables.data = $tw.utils.base64Encode(binary,true);
|
||||
}
|
||||
self.wiki.addTiddler(new $tw.Tiddler(self.wiki.getTiddler(requestTrackerTitle),{
|
||||
status: completionCode,
|
||||
|
||||
@@ -104,7 +104,11 @@ TW_Element.prototype.setAttribute = function(name,value) {
|
||||
if(this.isRaw) {
|
||||
throw "Cannot setAttribute on a raw TW_Element";
|
||||
}
|
||||
this.attributes[name] = value + "";
|
||||
if(name === "style") {
|
||||
this.style = value;
|
||||
} else {
|
||||
this.attributes[name] = value + "";
|
||||
}
|
||||
};
|
||||
|
||||
TW_Element.prototype.setAttributeNS = function(namespace,name,value) {
|
||||
|
||||
@@ -819,18 +819,41 @@ exports.hashString = function(str) {
|
||||
},0);
|
||||
};
|
||||
|
||||
/*
|
||||
Base64 utility functions that work in either browser or Node.js
|
||||
*/
|
||||
if(typeof window !== 'undefined') {
|
||||
exports.btoa = function(binstr) { return window.btoa(binstr); }
|
||||
exports.atob = function(b64) { return window.atob(b64); }
|
||||
} else {
|
||||
exports.btoa = function(binstr) {
|
||||
return Buffer.from(binstr, 'binary').toString('base64');
|
||||
}
|
||||
exports.atob = function(b64) {
|
||||
return Buffer.from(b64, 'base64').toString('binary');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Decode a base64 string
|
||||
*/
|
||||
exports.base64Decode = function(string64) {
|
||||
return base64utf8.base64.decode.call(base64utf8,string64);
|
||||
exports.base64Decode = function(string64,binary,urlsafe) {
|
||||
var encoded = urlsafe ? string64.replace(/_/g,'/').replace(/-/g,'+') : string64;
|
||||
if(binary) return exports.atob(encoded)
|
||||
else return base64utf8.base64.decode.call(base64utf8,encoded);
|
||||
};
|
||||
|
||||
/*
|
||||
Encode a string to base64
|
||||
*/
|
||||
exports.base64Encode = function(string64) {
|
||||
return base64utf8.base64.encode.call(base64utf8,string64);
|
||||
exports.base64Encode = function(string64,binary,urlsafe) {
|
||||
var encoded;
|
||||
if(binary) encoded = exports.btoa(string64);
|
||||
else encoded = base64utf8.base64.encode.call(base64utf8,string64);
|
||||
if(urlsafe) {
|
||||
encoded = encoded.replace(/\+/g,'-').replace(/\//g,'_');
|
||||
}
|
||||
return encoded;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -70,6 +70,11 @@ BrowseWidget.prototype.render = function(parent,nextSibling) {
|
||||
}
|
||||
return false;
|
||||
},false);
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
@@ -95,6 +100,11 @@ BrowseWidget.prototype.execute = function() {
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
BrowseWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -59,6 +59,11 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
|
||||
$tw.utils.pushTop(classes,"tc-popup-handle");
|
||||
}
|
||||
domNode.className = classes.join(" ");
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Assign other attributes
|
||||
if(this.style) {
|
||||
domNode.setAttribute("style",this.style);
|
||||
@@ -250,7 +255,7 @@ ButtonWidget.prototype.updateDomNodeClasses = function() {
|
||||
//Add new classes from updated class attribute.
|
||||
$tw.utils.pushTop(domNodeClasses,newClasses);
|
||||
this.domNode.className = domNodeClasses.join(" ");
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
@@ -260,8 +265,15 @@ ButtonWidget.prototype.refresh = function(changedTiddlers) {
|
||||
if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
} else {
|
||||
if(changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
}
|
||||
this.assignAttributes(this.domNodes[0],{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
@@ -53,6 +53,11 @@ CheckboxWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.labelDomNode.appendChild(this.inputDomNode);
|
||||
this.spanDomNode = this.document.createElement("span");
|
||||
this.labelDomNode.appendChild(this.spanDomNode);
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(this.inputDomNode,[
|
||||
{name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"}
|
||||
@@ -325,6 +330,11 @@ CheckboxWidget.prototype.refresh = function(changedTiddlers) {
|
||||
$tw.utils.removeClass(this.labelDomNode,"tc-checkbox-checked");
|
||||
}
|
||||
}
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
return this.refreshChildren(changedTiddlers) || refreshed;
|
||||
}
|
||||
};
|
||||
@@ -332,3 +342,4 @@ CheckboxWidget.prototype.refresh = function(changedTiddlers) {
|
||||
exports.checkbox = CheckboxWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -52,6 +52,11 @@ DraggableWidget.prototype.render = function(parent,nextSibling) {
|
||||
classes.push("tc-draggable");
|
||||
}
|
||||
domNode.setAttribute("class",classes.join(" "));
|
||||
// Assign data- attributes and style. attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Insert the node into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
@@ -108,13 +113,19 @@ DraggableWidget.prototype.updateDomNodeClasses = function() {
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
DraggableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
changedAttributesCount = $tw.utils.count(changedAttributes);
|
||||
if(changedAttributesCount === 1 && changedAttributes["class"]) {
|
||||
this.updateDomNodeClasses();
|
||||
} else if(changedAttributesCount > 0) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.tag || changedAttributes.selector || changedAttributes.dragimagetype || changedAttributes.enable || changedAttributes.startactions || changedAttributes.endactions) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
this.assignAttributes(this.domNodes[0],{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
@@ -42,6 +42,11 @@ DroppableWidget.prototype.render = function(parent,nextSibling) {
|
||||
domNode = this.document.createElement(tag);
|
||||
this.domNode = domNode;
|
||||
this.assignDomNodeClasses();
|
||||
// Assign data- attributes and style. attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Add event handlers
|
||||
if(this.droppableEnable) {
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
@@ -166,8 +171,15 @@ DroppableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
if(changedAttributes.tag || changedAttributes.enable || changedAttributes.disabledClass || changedAttributes.actions || changedAttributes.effect) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
} else {
|
||||
if(changedAttributes["class"]) {
|
||||
this.assignDomNodeClasses();
|
||||
}
|
||||
this.assignAttributes(this.domNodes[0],{
|
||||
changedAttributes: changedAttributes,
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
@@ -58,24 +58,25 @@ ImageWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this.wiki.isImageTiddler(this.imageSource)) {
|
||||
var type = tiddler.fields.type,
|
||||
text = tiddler.fields.text,
|
||||
_canonical_uri = tiddler.fields._canonical_uri;
|
||||
_canonical_uri = tiddler.fields._canonical_uri,
|
||||
typeInfo = $tw.config.contentTypeInfo[type] || {},
|
||||
deserializerType = typeInfo.deserializerType || type;
|
||||
// If the tiddler has body text then it doesn't need to be lazily loaded
|
||||
if(text) {
|
||||
// Render the appropriate element for the image type
|
||||
switch(type) {
|
||||
case "application/pdf":
|
||||
// Render the appropriate element for the image type by looking up the encoding in the content type info
|
||||
var encoding = typeInfo.encoding || "utf8";
|
||||
if (encoding === "base64") {
|
||||
// .pdf .png .jpg etc.
|
||||
src = "data:" + deserializerType + ";base64," + text;
|
||||
if (deserializerType === "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;
|
||||
}
|
||||
} else {
|
||||
// .svg .tid .xml etc.
|
||||
src = "data:" + deserializerType + "," + encodeURIComponent(text);
|
||||
}
|
||||
} else if(_canonical_uri) {
|
||||
switch(type) {
|
||||
switch(deserializerType) {
|
||||
case "application/pdf":
|
||||
tag = "embed";
|
||||
src = _canonical_uri;
|
||||
@@ -99,6 +100,9 @@ ImageWidget.prototype.render = function(parent,nextSibling) {
|
||||
if(this.imageClass) {
|
||||
domNode.setAttribute("class",this.imageClass);
|
||||
}
|
||||
if(this.imageUsemap) {
|
||||
domNode.setAttribute("usemap",this.imageUsemap);
|
||||
}
|
||||
if(this.imageWidth) {
|
||||
domNode.setAttribute("width",this.imageWidth);
|
||||
}
|
||||
@@ -138,6 +142,7 @@ ImageWidget.prototype.execute = function() {
|
||||
this.imageWidth = this.getAttribute("width");
|
||||
this.imageHeight = this.getAttribute("height");
|
||||
this.imageClass = this.getAttribute("class");
|
||||
this.imageUsemap = this.getAttribute("usemap");
|
||||
this.imageTooltip = this.getAttribute("tooltip");
|
||||
this.imageAlt = this.getAttribute("alt");
|
||||
this.lazyLoading = this.getAttribute("loading");
|
||||
@@ -148,7 +153,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
ImageWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
|
||||
if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.usemap || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -43,6 +43,11 @@ LinkWidget.prototype.render = function(parent,nextSibling) {
|
||||
} else {
|
||||
// Just insert the link text
|
||||
var domNode = this.document.createElement("span");
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
@@ -138,6 +143,11 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) {
|
||||
widget: this
|
||||
});
|
||||
}
|
||||
// Assign data- attributes
|
||||
this.assignAttributes(domNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Insert the link into the DOM and render any children
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
@@ -207,8 +217,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
*/
|
||||
LinkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(changedAttributes.to || changedTiddlers[this.to] || changedAttributes["aria-label"] || changedAttributes.tooltip ||
|
||||
changedAttributes["class"] || changedAttributes.tabindex || changedAttributes.draggable || changedAttributes.tag) {
|
||||
if($tw.utils.count(changedAttributes) > 0) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
@@ -218,3 +227,4 @@ LinkWidget.prototype.refresh = function(changedTiddlers) {
|
||||
exports.link = LinkWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -28,6 +28,18 @@ Inherit from the base widget class
|
||||
*/
|
||||
ListWidget.prototype = new Widget();
|
||||
|
||||
ListWidget.prototype.initialise = function(parseTreeNode,options) {
|
||||
// Bail if parseTreeNode is undefined, meaning that the ListWidget constructor was called without any arguments so that it can be subclassed
|
||||
if(parseTreeNode === undefined) {
|
||||
return;
|
||||
}
|
||||
// First call parent constructor to set everything else up
|
||||
Widget.prototype.initialise.call(this,parseTreeNode,options);
|
||||
// Now look for <$list-template> and <$list-empty> widgets as immediate child widgets
|
||||
// This is safe to do during initialization because parse trees never change after creation
|
||||
this.findExplicitTemplates();
|
||||
}
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
@@ -68,8 +80,6 @@ ListWidget.prototype.execute = function() {
|
||||
this.counterName = this.getAttribute("counter");
|
||||
this.storyViewName = this.getAttribute("storyview");
|
||||
this.historyTitle = this.getAttribute("history");
|
||||
// Look for <$list-template> and <$list-empty> widgets as immediate child widgets
|
||||
this.findExplicitTemplates();
|
||||
// Compose the list elements
|
||||
this.list = this.getTiddlerList();
|
||||
var members = [],
|
||||
@@ -92,6 +102,7 @@ ListWidget.prototype.findExplicitTemplates = function() {
|
||||
var self = this;
|
||||
this.explicitListTemplate = null;
|
||||
this.explicitEmptyTemplate = null;
|
||||
this.hasTemplateInBody = false;
|
||||
var searchChildren = function(childNodes) {
|
||||
$tw.utils.each(childNodes,function(node) {
|
||||
if(node.type === "list-template") {
|
||||
@@ -100,6 +111,8 @@ ListWidget.prototype.findExplicitTemplates = function() {
|
||||
self.explicitEmptyTemplate = node.children;
|
||||
} else if(node.type === "element" && node.tag === "p") {
|
||||
searchChildren(node.children);
|
||||
} else {
|
||||
self.hasTemplateInBody = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -160,11 +173,11 @@ ListWidget.prototype.makeItemTemplate = function(title,index) {
|
||||
// Check for a <$list-item> widget
|
||||
if(this.explicitListTemplate) {
|
||||
templateTree = this.explicitListTemplate;
|
||||
} else if (!this.explicitEmptyTemplate) {
|
||||
} else if(this.hasTemplateInBody) {
|
||||
templateTree = this.parseTreeNode.children;
|
||||
}
|
||||
}
|
||||
if(!templateTree) {
|
||||
if(!templateTree || templateTree.length === 0) {
|
||||
// Default template is a link to the title
|
||||
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
||||
{type: "text", text: title}
|
||||
@@ -414,4 +427,27 @@ ListItemWidget.prototype.refresh = function(changedTiddlers) {
|
||||
|
||||
exports.listitem = ListItemWidget;
|
||||
|
||||
/*
|
||||
Make <$list-template> and <$list-empty> widgets that do nothing
|
||||
*/
|
||||
var ListTemplateWidget = function(parseTreeNode,options) {
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
ListTemplateWidget.prototype = new Widget();
|
||||
ListTemplateWidget.prototype.render = function() {}
|
||||
ListTemplateWidget.prototype.refresh = function() { return false; }
|
||||
|
||||
exports["list-template"] = ListTemplateWidget;
|
||||
|
||||
var ListEmptyWidget = function(parseTreeNode,options) {
|
||||
// Main initialisation inherited from widget.js
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
ListEmptyWidget.prototype = new Widget();
|
||||
ListEmptyWidget.prototype.render = function() {}
|
||||
ListEmptyWidget.prototype.refresh = function() { return false; }
|
||||
|
||||
exports["list-empty"] = ListEmptyWidget;
|
||||
|
||||
})();
|
||||
|
||||
@@ -40,6 +40,10 @@ RadioWidget.prototype.render = function(parent,nextSibling) {
|
||||
);
|
||||
this.inputDomNode = this.document.createElement("input");
|
||||
this.inputDomNode.setAttribute("type","radio");
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
if(isChecked) {
|
||||
this.inputDomNode.checked = true;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,10 @@ RangeWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.inputDomNode.setAttribute("disabled",true);
|
||||
}
|
||||
this.inputDomNode.value = this.getValue();
|
||||
this.assignAttributes(this.inputDomNode,{
|
||||
sourcePrefix: "data-",
|
||||
destPrefix: "data-"
|
||||
});
|
||||
// Add a click event handler
|
||||
$tw.utils.addEventListeners(this.inputDomNode,[
|
||||
{name:"mousedown", handlerObject:this, handlerMethod:"handleMouseDownEvent"},
|
||||
|
||||
@@ -171,6 +171,46 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) {
|
||||
parent.insertBefore(this.outerDomNode,nextSibling);
|
||||
this.renderChildren(this.innerDomNode,null);
|
||||
this.domNodes.push(this.outerDomNode);
|
||||
// If the scroll position is bound to a tiddler
|
||||
if(this.scrollableBind) {
|
||||
// After a delay for rendering, scroll to the bound position
|
||||
setTimeout(this.updateScrollPositionFromBoundTiddler.bind(this),50);
|
||||
// Save scroll position on DOM scroll event
|
||||
this.outerDomNode.addEventListener("scroll",function(event) {
|
||||
var existingTiddler = self.wiki.getTiddler(self.scrollableBind),
|
||||
newTiddlerFields = {
|
||||
title: self.scrollableBind,
|
||||
"scroll-left": self.outerDomNode.scrollLeft.toString(),
|
||||
"scroll-top": self.outerDomNode.scrollTop.toString()
|
||||
};
|
||||
if(!existingTiddler || (existingTiddler.fields["scroll-left"] !== newTiddlerFields["scroll-left"] || existingTiddler.fields["scroll-top"] !== newTiddlerFields["scroll-top"])) {
|
||||
self.wiki.addTiddler(new $tw.Tiddler(existingTiddler,newTiddlerFields));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ScrollableWidget.prototype.updateScrollPositionFromBoundTiddler = function() {
|
||||
// Bail if we're running on the fakedom
|
||||
if(!this.outerDomNode.scrollTo) {
|
||||
return;
|
||||
}
|
||||
var tiddler = this.wiki.getTiddler(this.scrollableBind);
|
||||
if(tiddler) {
|
||||
var scrollLeftTo = this.outerDomNode.scrollLeft;
|
||||
if(parseFloat(tiddler.fields["scroll-left"]).toString() === tiddler.fields["scroll-left"]) {
|
||||
scrollLeftTo = parseFloat(tiddler.fields["scroll-left"]);
|
||||
}
|
||||
var scrollTopTo = this.outerDomNode.scrollTop;
|
||||
if(parseFloat(tiddler.fields["scroll-top"]).toString() === tiddler.fields["scroll-top"]) {
|
||||
scrollTopTo = parseFloat(tiddler.fields["scroll-top"]);
|
||||
}
|
||||
this.outerDomNode.scrollTo({
|
||||
top: scrollTopTo,
|
||||
left: scrollLeftTo,
|
||||
behavior: "instant"
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -178,6 +218,7 @@ Compute the internal state of the widget
|
||||
*/
|
||||
ScrollableWidget.prototype.execute = function() {
|
||||
// Get attributes
|
||||
this.scrollableBind = this.getAttribute("bind");
|
||||
this.fallthrough = this.getAttribute("fallthrough","yes");
|
||||
this["class"] = this.getAttribute("class");
|
||||
// Make child widgets
|
||||
@@ -193,6 +234,9 @@ ScrollableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
}
|
||||
if(changedAttributes.bind || changedTiddlers[this.getAttribute("bind")]) {
|
||||
this.updateScrollPositionFromBoundTiddler();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
|
||||
@@ -40,7 +40,31 @@ SelectWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
this.renderChildren(parent,nextSibling);
|
||||
//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.selectSize) {
|
||||
domNode.setAttribute("size",this.selectSize);
|
||||
}
|
||||
if(this.selectTabindex) {
|
||||
domNode.setAttribute("tabindex",this.selectTabindex);
|
||||
}
|
||||
if(this.selectTooltip) {
|
||||
domNode.setAttribute("title",this.selectTooltip);
|
||||
}
|
||||
this.renderChildren(domNode,nextSibling);
|
||||
this.parentDomNode.insertBefore(domNode,nextSibling);
|
||||
this.domNodes.push(domNode);
|
||||
this.setSelectValue();
|
||||
if(this.selectFocus == "yes") {
|
||||
this.getSelectDomNode().focus();
|
||||
@@ -113,7 +137,7 @@ SelectWidget.prototype.setSelectValue = function() {
|
||||
Get the DOM node of the select element
|
||||
*/
|
||||
SelectWidget.prototype.getSelectDomNode = function() {
|
||||
return this.children[0].domNodes[0];
|
||||
return this.domNodes[0];
|
||||
};
|
||||
|
||||
// Return an array of the selected opion values
|
||||
@@ -149,27 +173,7 @@ SelectWidget.prototype.execute = function() {
|
||||
this.selectTooltip = this.getAttribute("tooltip");
|
||||
this.selectFocus = this.getAttribute("focus");
|
||||
// Make the child widgets
|
||||
var selectNode = {
|
||||
type: "element",
|
||||
tag: "select",
|
||||
children: this.parseTreeNode.children
|
||||
};
|
||||
if(this.selectClass) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"class",this.selectClass);
|
||||
}
|
||||
if(this.selectMultiple) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"multiple","multiple");
|
||||
}
|
||||
if(this.selectSize) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"size",this.selectSize);
|
||||
}
|
||||
if(this.selectTabindex) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"tabindex",this.selectTabindex);
|
||||
}
|
||||
if(this.selectTooltip) {
|
||||
$tw.utils.addAttributeToParseTreeNode(selectNode,"title",this.selectTooltip);
|
||||
}
|
||||
this.makeChildWidgets([selectNode]);
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -178,17 +182,21 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
|
||||
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) {
|
||||
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tooltip || changedAttributes.tabindex) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
// If the target tiddler value has changed, just update setting and refresh the children
|
||||
} 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();
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ TranscludeWidget.prototype.collectAttributes = function() {
|
||||
this.recursionMarker = this.getAttribute("recursionMarker","yes");
|
||||
} else {
|
||||
this.transcludeVariable = this.getAttribute("$variable");
|
||||
this.transcludeVariableIsFunction = false;
|
||||
this.transcludeType = this.getAttribute("$type");
|
||||
this.transcludeOutput = this.getAttribute("$output","text/html");
|
||||
this.transcludeTitle = this.getAttribute("$tiddler",this.getVariable("currentTiddler"));
|
||||
@@ -184,7 +185,9 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
|
||||
if(this.transcludeVariable) {
|
||||
// Transcluding a variable
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()});
|
||||
this.transcludeVariableIsFunction = variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition;
|
||||
text = variableInfo.text;
|
||||
this.transcludeFunctionResult = text;
|
||||
return {
|
||||
text: variableInfo.text,
|
||||
type: this.transcludeType
|
||||
@@ -219,21 +222,24 @@ TranscludeWidget.prototype.parseTransclusionTarget = function(parseAsInline) {
|
||||
// Transcluding a variable
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}),
|
||||
srcVariable = variableInfo && variableInfo.srcVariable;
|
||||
if(srcVariable && srcVariable.isFunctionDefinition) {
|
||||
this.transcludeVariableIsFunction = true;
|
||||
this.transcludeFunctionResult = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
|
||||
}
|
||||
if(variableInfo.text) {
|
||||
if(srcVariable && srcVariable.isFunctionDefinition) {
|
||||
var result = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
|
||||
parser = {
|
||||
tree: [{
|
||||
type: "text",
|
||||
text: result
|
||||
text: this.transcludeFunctionResult
|
||||
}],
|
||||
source: result,
|
||||
source: this.transcludeFunctionResult,
|
||||
type: "text/vnd.tiddlywiki"
|
||||
};
|
||||
if(parseAsInline) {
|
||||
parser.tree[0] = {
|
||||
type: "text",
|
||||
text: result
|
||||
text: this.transcludeFunctionResult
|
||||
};
|
||||
} else {
|
||||
parser.tree[0] = {
|
||||
@@ -241,7 +247,7 @@ TranscludeWidget.prototype.parseTransclusionTarget = function(parseAsInline) {
|
||||
tag: "p",
|
||||
children: [{
|
||||
type: "text",
|
||||
text: result
|
||||
text: this.transcludeFunctionResult
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -430,12 +436,19 @@ TranscludeWidget.prototype.parserNeedsRefresh = function() {
|
||||
return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType)
|
||||
};
|
||||
|
||||
TranscludeWidget.prototype.functionNeedsRefresh = function() {
|
||||
var oldResult = this.transcludeFunctionResult;
|
||||
var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()});
|
||||
var newResult = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || "";
|
||||
return oldResult !== newResult;
|
||||
}
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
TranscludeWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes();
|
||||
if(($tw.utils.count(changedAttributes) > 0) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
|
||||
if(($tw.utils.count(changedAttributes) > 0) || (this.transcludeVariableIsFunction && this.functionNeedsRefresh()) || (!this.transcludeVariable && changedTiddlers[this.transcludeTitle] && this.parserNeedsRefresh())) {
|
||||
this.refreshSelf();
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -413,16 +413,34 @@ Widget.prototype.getAttribute = function(name,defaultText) {
|
||||
};
|
||||
|
||||
/*
|
||||
Assign the computed attributes of the widget to a domNode
|
||||
Assign the common attributes of the widget to a domNode
|
||||
options include:
|
||||
excludeEventAttributes: ignores attributes whose name begins with "on"
|
||||
sourcePrefix: prefix of attributes that are to be directly assigned (defaults to the empty string meaning all attributes)
|
||||
destPrefix: prefix to be applied to attribute names that are to be directly assigned (defaults to the emtpy string which means no prefix is added)
|
||||
changedAttributes: hashmap by attribute name of attributes to process (if missing, process all attributes)
|
||||
excludeEventAttributes: ignores attributes whose name would begin with "on"
|
||||
*/
|
||||
Widget.prototype.assignAttributes = function(domNode,options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var self = this,
|
||||
changedAttributes = options.changedAttributes || this.attributes,
|
||||
sourcePrefix = options.sourcePrefix || "",
|
||||
destPrefix = options.destPrefix || "",
|
||||
EVENT_ATTRIBUTE_PREFIX = "on";
|
||||
var assignAttribute = function(name,value) {
|
||||
// Process any style attributes before considering sourcePrefix and destPrefix
|
||||
if(name.substr(0,6) === "style." && name.length > 6) {
|
||||
domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value;
|
||||
return;
|
||||
}
|
||||
// Check if the sourcePrefix is a match
|
||||
if(name.substr(0,sourcePrefix.length) === sourcePrefix) {
|
||||
name = destPrefix + name.substr(sourcePrefix.length);
|
||||
} else {
|
||||
value = undefined;
|
||||
}
|
||||
// Check for excluded attribute names
|
||||
if(options.excludeEventAttributes && name.substr(0,2) === "on") {
|
||||
if(options.excludeEventAttributes && name.substr(0,2).toLowerCase() === EVENT_ATTRIBUTE_PREFIX) {
|
||||
value = undefined;
|
||||
}
|
||||
if(value !== undefined) {
|
||||
@@ -432,26 +450,24 @@ Widget.prototype.assignAttributes = function(domNode,options) {
|
||||
namespace = "http://www.w3.org/1999/xlink";
|
||||
name = name.substr(6);
|
||||
}
|
||||
// Handle styles
|
||||
if(name.substr(0,6) === "style." && name.length > 6) {
|
||||
domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value;
|
||||
} else {
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
domNode.setAttributeNS(namespace,name,value);
|
||||
} catch(e) {
|
||||
}
|
||||
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
||||
try {
|
||||
domNode.setAttributeNS(namespace,name,value);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Not all parse tree nodes have the orderedAttributes property
|
||||
};
|
||||
// If the parse tree node has the orderedAttributes property then use that order
|
||||
if(this.parseTreeNode.orderedAttributes) {
|
||||
$tw.utils.each(this.parseTreeNode.orderedAttributes,function(attribute,index) {
|
||||
assignAttribute(attribute.name,self.attributes[attribute.name]);
|
||||
});
|
||||
if(attribute.name in changedAttributes) {
|
||||
assignAttribute(attribute.name,self.getAttribute(attribute.name));
|
||||
}
|
||||
});
|
||||
// Otherwise update each changed attribute irrespective of order
|
||||
} else {
|
||||
$tw.utils.each(Object.keys(self.attributes).sort(),function(name) {
|
||||
assignAttribute(name,self.attributes[name]);
|
||||
$tw.utils.each(changedAttributes,function(value,name) {
|
||||
assignAttribute(name,self.getAttribute(name));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1287,7 +1287,7 @@ exports.search = function(text,options) {
|
||||
console.log("Regexp error parsing /(" + text + ")/" + flags + ": ",e);
|
||||
}
|
||||
} else if(options.some) {
|
||||
terms = text.trim().split(/ +/);
|
||||
terms = text.trim().split(/[^\S\xA0]+/);
|
||||
if(terms.length === 1 && terms[0] === "") {
|
||||
searchTermsRegExps = null;
|
||||
} else {
|
||||
@@ -1298,7 +1298,7 @@ exports.search = function(text,options) {
|
||||
searchTermsRegExps.push(new RegExp("(" + regExpStr + ")",flags));
|
||||
}
|
||||
} else { // default: words
|
||||
terms = text.split(/ +/);
|
||||
terms = text.split(/[^\S\xA0]+/);
|
||||
if(terms.length === 1 && terms[0] === "") {
|
||||
searchTermsRegExps = null;
|
||||
} else {
|
||||
|
||||
@@ -26,10 +26,10 @@ caption: {{$:/language/ControlPanel/Basics/Caption}}
|
||||
|<$link to="$:/SiteSubtitle"><<lingo Subtitle/Prompt>></$link> |<$edit-text tiddler="$:/SiteSubtitle" default="" tag="input"/> |
|
||||
|<$link to="$:/status/UserName"><<lingo Username/Prompt>></$link> |<$edit-text tiddler="$:/status/UserName" default="" tag="input"/> |
|
||||
|<$link to="$:/config/AnimationDuration"><<lingo AnimDuration/Prompt>></$link> |<$edit-text tiddler="$:/config/AnimationDuration" default="" tag="input"/> |
|
||||
|<$link to="$:/DefaultTiddlers"><<lingo DefaultTiddlers/Prompt>></$link> |<<lingo DefaultTiddlers/TopHint>><br> <$edit class="tc-edit-texteditor" tiddler="$:/DefaultTiddlers"/><br>//<<lingo DefaultTiddlers/BottomHint>>// |
|
||||
|<$link to="$:/DefaultTiddlers"><<lingo DefaultTiddlers/Prompt>></$link> |<<lingo DefaultTiddlers/TopHint>><br> <$edit class="tc-edit-texteditor" tiddler="$:/DefaultTiddlers" autoHeight="yes"/><br>//<<lingo DefaultTiddlers/BottomHint>>// |
|
||||
|<$link to="$:/language/DefaultNewTiddlerTitle"><<lingo NewTiddler/Title/Prompt>></$link> |<$edit-text tiddler="$:/language/DefaultNewTiddlerTitle" default="" tag="input"/> |
|
||||
|<$link to="$:/config/NewJournal/Title"><<lingo NewJournal/Title/Prompt>></$link> |<$edit-text tiddler="$:/config/NewJournal/Title" default="" tag="input"/> |
|
||||
|<$link to="$:/config/NewJournal/Text"><<lingo NewJournal/Text/Prompt>></$link> |<$edit tiddler="$:/config/NewJournal/Text" class="tc-edit-texteditor" default=""/> |
|
||||
|<$link to="$:/config/NewJournal/Text"><<lingo NewJournal/Text/Prompt>></$link> |<$edit tiddler="$:/config/NewJournal/Text" class="tc-edit-texteditor" default="" autoHeight="yes"/> |
|
||||
|<$link to="$:/config/NewTiddler/Tags"><<lingo NewTiddler/Tags/Prompt>></$link> |<$vars currentTiddler="$:/config/NewTiddler/Tags" tagField="text">{{||$:/core/ui/EditTemplate/tags}}<$list filter="[<currentTiddler>tags[]] +[limit[1]]" variable="ignore"><$button tooltip={{$:/language/ControlPanel/Basics/RemoveTags/Hint}}><<lingo RemoveTags>><$action-listops $tiddler=<<currentTiddler>> $field="text" $subfilter={{{ [<currentTiddler>get[tags]] }}}/><$action-setfield $tiddler=<<currentTiddler>> tags=""/></$button></$list></$vars> |
|
||||
|<$link to="$:/config/NewJournal/Tags"><<lingo NewJournal/Tags/Prompt>></$link> |<$vars currentTiddler="$:/config/NewJournal/Tags" tagField="text">{{||$:/core/ui/EditTemplate/tags}}<$list filter="[<currentTiddler>tags[]] +[limit[1]]" variable="ignore"><$button tooltip={{$:/language/ControlPanel/Basics/RemoveTags/Hint}}><<lingo RemoveTags>><$action-listops $tiddler=<<currentTiddler>> $field="text" $subfilter={{{ [<currentTiddler>get[tags]] }}}/><$action-setfield $tiddler=<<currentTiddler>> tags=""/></$button></$list></$vars> |
|
||||
|<$link to="$:/config/AutoFocus"><<lingo AutoFocus/Prompt>></$link> |{{$:/snippets/minifocusswitcher}} |
|
||||
|
||||
@@ -18,7 +18,7 @@ $:/config/EditorToolbarButtons/Visibility/$(currentTiddler)$
|
||||
importState=<<qualify $:/state/ImportImage>> >
|
||||
<$dropzone importTitle=<<importTitle>> autoOpenOnImport="no" contentTypesFilter={{$:/config/Editor/ImportContentTypesFilter}} class="tc-dropzone-editor" enable={{{ [{$:/config/DragAndDrop/Enable}match[no]] :else[subfilter{$:/config/Editor/EnableImportFilter}then[yes]else[no]] }}} filesOnly="yes" actions=<<importFileActions>> >
|
||||
<div>
|
||||
<div class={{{ [function[edit-preview-state]match[yes]then[tc-tiddler-preview]] +[join[ ]] }}}>
|
||||
<div class={{{ [function[edit-preview-state]match[yes]then[tc-tiddler-preview]else[tc-tiddler-preview-hidden]] [[tc-tiddler-editor]] +[join[ ]] }}}>
|
||||
|
||||
<$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/>
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ $:/config/EditTemplateFields/Visibility/$(currentField)$
|
||||
\whitespace trim
|
||||
<$vars name={{{ [<newFieldNameTiddler>get[text]] }}}>
|
||||
<$reveal type="nomatch" text="" default=<<name>>>
|
||||
<$button tooltip=<<lingo Fields/Add/Button/Hint>>>
|
||||
<$button tooltip={{$:/language/EditTemplate/Fields/Add/Button/Hint}}>
|
||||
<$action-sendmessage $message="tm-add-field"
|
||||
$name=<<name>>
|
||||
$value={{{ [subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
||||
@@ -89,7 +89,7 @@ $value={{{ [subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
||||
</td>
|
||||
<td class="tc-edit-field-remove">
|
||||
<$button class="tc-btn-invisible" tooltip={{$:/language/EditTemplate/Field/Remove/Hint}} aria-label={{$:/language/EditTemplate/Field/Remove/Caption}}>
|
||||
<$action-deletefield $field=<<currentField>>/><$set name="currentTiddlerCSSescaped" value={{{ [<currentTiddler>escapecss[]] }}}><$action-sendmessage $message="tm-focus-selector" $param=<<current-tiddler-new-field-selector>>/></$set>
|
||||
<$action-deletefield $field=<<currentField>>/>
|
||||
{{$:/core/images/delete-button}}
|
||||
</$button>
|
||||
</td>
|
||||
|
||||
@@ -13,7 +13,7 @@ tags: $:/tags/Macro
|
||||
|
||||
$(colour-picker-update-recent)$
|
||||
|
||||
$actions$
|
||||
<$transclude $variable="__actions__"/>
|
||||
|
||||
<span style="display:inline-block; background-color: $(colour-picker-value)$; width: 100%; height: 100%; border-radius: 50%;"/>
|
||||
|
||||
@@ -23,7 +23,7 @@ $actions$
|
||||
\define colour-picker-recent-inner(actions)
|
||||
\whitespace trim
|
||||
<$set name="colour-picker-value" value="$(recentColour)$">
|
||||
<$macrocall $name="colour-picker-inner" actions="""$actions$"""/>
|
||||
<$macrocall $name="colour-picker-inner" actions=<<__actions__>>/>
|
||||
</$set>
|
||||
\end
|
||||
|
||||
@@ -31,7 +31,7 @@ $actions$
|
||||
\whitespace trim
|
||||
{{$:/language/ColourPicker/Recent}}<$list filter="[list[$:/config/ColourPicker/Recent]]" variable="recentColour">
|
||||
 
|
||||
<$macrocall $name="colour-picker-recent-inner" actions="""$actions$"""/>
|
||||
<$macrocall $name="colour-picker-recent-inner" actions=<<__actions__>>/>
|
||||
</$list>
|
||||
\end
|
||||
|
||||
@@ -39,13 +39,13 @@ $actions$
|
||||
\whitespace trim
|
||||
<div class="tc-colour-chooser">
|
||||
|
||||
<$macrocall $name="colour-picker-recent" actions="""$actions$"""/>
|
||||
<$macrocall $name="colour-picker-recent" actions=<<__actions__>>/>
|
||||
|
||||
---
|
||||
|
||||
<$list filter="LightPink Pink Crimson LavenderBlush PaleVioletRed HotPink DeepPink MediumVioletRed Orchid Thistle Plum Violet Magenta Fuchsia DarkMagenta Purple MediumOrchid DarkViolet DarkOrchid Indigo BlueViolet MediumPurple MediumSlateBlue SlateBlue DarkSlateBlue Lavender GhostWhite Blue MediumBlue MidnightBlue DarkBlue Navy RoyalBlue CornflowerBlue LightSteelBlue LightSlateGrey SlateGrey DodgerBlue AliceBlue SteelBlue LightSkyBlue SkyBlue DeepSkyBlue LightBlue PowderBlue CadetBlue Azure LightCyan PaleTurquoise Cyan Aqua DarkTurquoise DarkSlateGrey DarkCyan Teal MediumTurquoise LightSeaGreen Turquoise Aquamarine MediumAquamarine MediumSpringGreen MintCream SpringGreen MediumSeaGreen SeaGreen Honeydew LightGreen PaleGreen DarkSeaGreen LimeGreen Lime ForestGreen Green DarkGreen Chartreuse LawnGreen GreenYellow DarkOliveGreen YellowGreen OliveDrab Beige LightGoldenrodYellow Ivory LightYellow Yellow Olive DarkKhaki LemonChiffon PaleGoldenrod Khaki Gold Cornsilk Goldenrod DarkGoldenrod FloralWhite OldLace Wheat Moccasin Orange PapayaWhip BlanchedAlmond NavajoWhite AntiqueWhite Tan BurlyWood Bisque DarkOrange Linen Peru PeachPuff SandyBrown Chocolate SaddleBrown Seashell Sienna LightSalmon Coral OrangeRed DarkSalmon Tomato MistyRose Salmon Snow LightCoral RosyBrown IndianRed Red Brown FireBrick DarkRed Maroon White WhiteSmoke Gainsboro LightGrey Silver DarkGrey Grey DimGrey Black" variable="colour-picker-value">
|
||||
 
|
||||
<$macrocall $name="colour-picker-inner" actions="""$actions$"""/>
|
||||
<$macrocall $name="colour-picker-inner" actions=<<__actions__>>/>
|
||||
</$list>
|
||||
|
||||
---
|
||||
@@ -54,7 +54,7 @@ $actions$
|
||||
 
|
||||
<$edit-text tiddler="$:/config/ColourPicker/New" type="color" tag="input"/>
|
||||
<$set name="colour-picker-value" value={{$:/config/ColourPicker/New}}>
|
||||
<$macrocall $name="colour-picker-inner" actions="""$actions$"""/>
|
||||
<$macrocall $name="colour-picker-inner" actions=<<__actions__>>/>
|
||||
</$set>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -5,13 +5,13 @@ title: $:/core/macros/image-picker
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
\define image-picker-thumbnail(actions)
|
||||
<$button tag="a" tooltip="""$(imageTitle)$""">$actions$<$transclude tiddler=<<imageTitle>>/></$button>
|
||||
<$button tag="a" tooltip="""$(imageTitle)$"""><$transclude $variable="__actions__"/><$transclude tiddler=<<imageTitle>>/></$button>
|
||||
\end
|
||||
|
||||
\define image-picker-list(filter,actions)
|
||||
\whitespace trim
|
||||
<$list filter="""$filter$""" variable="imageTitle">
|
||||
<$macrocall $name="image-picker-thumbnail" actions="""$actions$"""/>
|
||||
<$macrocall $name="image-picker-thumbnail" actions=<<__actions__>>/>
|
||||
 
|
||||
</$list>
|
||||
\end
|
||||
@@ -25,15 +25,15 @@ type: text/vnd.tiddlywiki
|
||||
{{$:/language/SystemTiddlers/Include/Prompt}}
|
||||
</$checkbox>
|
||||
<$reveal state=<<state-system>> type="match" text="hide" default="hide" tag="div">
|
||||
<$macrocall $name="image-picker-list" filter="""$filter$ +[!is[system]]""" actions="""$actions$"""/>
|
||||
<$macrocall $name="image-picker-list" filter="""$filter$ +[!is[system]]""" actions=<<__actions__>>/>
|
||||
</$reveal>
|
||||
<$reveal state=<<state-system>> type="nomatch" text="hide" default="hide" tag="div">
|
||||
<$macrocall $name="image-picker-list" filter="""$filter$""" actions="""$actions$"""/>
|
||||
<$macrocall $name="image-picker-list" filter="""$filter$""" actions=<<__actions__>>/>
|
||||
</$reveal>
|
||||
</$vars>
|
||||
</div>
|
||||
\end
|
||||
|
||||
\define image-picker-include-tagged-images(actions)
|
||||
<$macrocall $name="image-picker" filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" actions="""$actions$"""/>
|
||||
<$macrocall $name="image-picker" filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" actions=<<__actions__>>/>
|
||||
\end
|
||||
|
||||
@@ -4,17 +4,17 @@ tags: $:/tags/Macro
|
||||
\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage,field:"caption")
|
||||
\whitespace trim
|
||||
<$genesis $type=<<__type__>> class=<<__class__>>>
|
||||
<$list filter=<<__filter__>> emptyMessage=<<__emptyMessage__>>>
|
||||
<$genesis $type=<<__subtype__>>>
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field=<<__field__>>>
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$genesis>
|
||||
</$list>
|
||||
<$list filter=<<__filter__>> emptyMessage=<<__emptyMessage__>>>
|
||||
<$genesis $type=<<__subtype__>>>
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field=<<__field__>>>
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$genesis>
|
||||
</$list>
|
||||
</$genesis>
|
||||
\end
|
||||
|
||||
@@ -25,34 +25,42 @@ tags: $:/tags/Macro
|
||||
\define list-links-draggable(tiddler,field:"list",emptyMessage,type:"ul",subtype:"li",class:"",itemTemplate)
|
||||
\whitespace trim
|
||||
<span class="tc-links-draggable-list">
|
||||
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
|
||||
<$genesis $type=<<__type__>> class="$class$">
|
||||
<$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>>
|
||||
<$droppable actions=<<list-links-draggable-drop-actions>> tag="""$subtype$""" enable=<<tv-enable-drag-and-drop>>>
|
||||
<div class="tc-droppable-placeholder"/>
|
||||
<div>
|
||||
<$transclude tiddler="""$itemTemplate$""">
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field="caption">
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$transclude>
|
||||
</div>
|
||||
</$droppable>
|
||||
</$list>
|
||||
<$tiddler tiddler="">
|
||||
<$droppable actions=<<list-links-draggable-drop-actions>> tag="div" enable=<<tv-enable-drag-and-drop>>>
|
||||
<div class="tc-droppable-placeholder">
|
||||
{{$:/core/images/blank}}
|
||||
</div>
|
||||
<div style="height:0.5em;"/>
|
||||
</$droppable>
|
||||
</$tiddler>
|
||||
</$genesis>
|
||||
</$vars>
|
||||
<$vars targetTiddler="""$tiddler$""" targetField="""$field$""">
|
||||
<$genesis $type=<<__type__>> class="$class$">
|
||||
<$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>>
|
||||
<$droppable
|
||||
actions=<<list-links-draggable-drop-actions>>
|
||||
tag="""$subtype$"""
|
||||
enable=<<tv-enable-drag-and-drop>>
|
||||
>
|
||||
<div class="tc-droppable-placeholder"/>
|
||||
<div>
|
||||
<$transclude tiddler="""$itemTemplate$""">
|
||||
<$link to={{!!title}}>
|
||||
<$let tv-wikilinks="no">
|
||||
<$transclude field="caption">
|
||||
<$view field="title"/>
|
||||
</$transclude>
|
||||
</$let>
|
||||
</$link>
|
||||
</$transclude>
|
||||
</div>
|
||||
</$droppable>
|
||||
</$list>
|
||||
<$tiddler tiddler="">
|
||||
<$droppable
|
||||
actions=<<list-links-draggable-drop-actions>>
|
||||
tag="div"
|
||||
enable=<<tv-enable-drag-and-drop>>
|
||||
>
|
||||
<div class="tc-droppable-placeholder">
|
||||
{{$:/core/images/blank}}
|
||||
</div>
|
||||
<div style="height:0.5em;"/>
|
||||
</$droppable>
|
||||
</$tiddler>
|
||||
</$genesis>
|
||||
</$vars>
|
||||
</span>
|
||||
\end
|
||||
|
||||
@@ -60,50 +68,59 @@ tags: $:/tags/Macro
|
||||
\whitespace trim
|
||||
<!-- Save the current ordering of the tiddlers with this tag -->
|
||||
<$set name="order" filter="[<__tag__>tagging[]]">
|
||||
<!-- Remove any list-after or list-before fields from the tiddlers with this tag -->
|
||||
<$list filter="[<__tag__>tagging[]]">
|
||||
<$action-deletefield $field="list-before"/>
|
||||
<$action-deletefield $field="list-after"/>
|
||||
</$list>
|
||||
<!-- Save the new order to the Tag Tiddler -->
|
||||
<$action-listops $tiddler=<<__tag__>> $field="list" $filter="+[enlist<order>] +[insertbefore<actionTiddler>,<currentTiddler>]"/>
|
||||
<!-- Make sure the newly added item has the right tag -->
|
||||
<!-- Removing this line makes dragging tags within the dropdown work as intended -->
|
||||
<!--<$action-listops $tiddler=<<actionTiddler>> $tags=<<__tag__>>/>-->
|
||||
<!-- Using the following 5 lines as replacement makes dragging titles from outside into the dropdown apply the tag -->
|
||||
<$list filter="[<actionTiddler>!contains:tags<__tag__>]">
|
||||
<$fieldmangler tiddler=<<actionTiddler>>>
|
||||
<$action-sendmessage $message="tm-add-tag" $param=<<__tag__>>/>
|
||||
</$fieldmangler>
|
||||
</$list>
|
||||
<!-- Remove any list-after or list-before fields from the tiddlers with this tag -->
|
||||
<$list filter="[<__tag__>tagging[]]">
|
||||
<$action-deletefield $field="list-before"/>
|
||||
<$action-deletefield $field="list-after"/>
|
||||
</$list>
|
||||
<!-- Save the new order to the Tag Tiddler -->
|
||||
<$action-listops $tiddler=<<__tag__>> $field="list" $filter="+[enlist<order>] +[insertbefore<actionTiddler>,<currentTiddler>]"/>
|
||||
<!-- Make sure the newly added item has the right tag -->
|
||||
<!-- Removing this line makes dragging tags within the dropdown work as intended -->
|
||||
<!--<$action-listops $tiddler=<<actionTiddler>> $tags=<<__tag__>>/>-->
|
||||
<!-- Using the following 5 lines as replacement makes dragging titles from outside into the dropdown apply the tag -->
|
||||
<$list filter="[<actionTiddler>!contains:tags<__tag__>]">
|
||||
<$fieldmangler tiddler=<<actionTiddler>>>
|
||||
<$action-sendmessage $message="tm-add-tag" $param=<<__tag__>>/>
|
||||
</$fieldmangler>
|
||||
</$list>
|
||||
</$set>
|
||||
\end
|
||||
|
||||
\define list-tagged-draggable(tag,subFilter,emptyMessage,itemTemplate,elementTag:"div",storyview:"")
|
||||
\whitespace trim
|
||||
<span class="tc-tagged-draggable-list">
|
||||
<$set name="tag" value=<<__tag__>>>
|
||||
<$list filter="[<__tag__>tagging[]$subFilter$]" emptyMessage=<<__emptyMessage__>> storyview=<<__storyview__>>>
|
||||
<$genesis $type=<<__elementTag__>> class="tc-menu-list-item">
|
||||
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<<tv-enable-drag-and-drop>>>
|
||||
<$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/>
|
||||
<$genesis $type=<<__elementTag__>>>
|
||||
<$transclude tiddler="""$itemTemplate$""">
|
||||
<$link to={{!!title}}>
|
||||
<$view field="title"/>
|
||||
</$link>
|
||||
</$transclude>
|
||||
</$genesis>
|
||||
</$droppable>
|
||||
</$genesis>
|
||||
</$list>
|
||||
<$tiddler tiddler="">
|
||||
<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<<tv-enable-drag-and-drop>>>
|
||||
<$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/>
|
||||
<$genesis $type=<<__elementTag__>> style="height:0.5em;">
|
||||
</$genesis>
|
||||
</$droppable>
|
||||
</$tiddler>
|
||||
</$set>
|
||||
<$set name="tag" value=<<__tag__>>>
|
||||
<$list
|
||||
filter="[<__tag__>tagging[]$subFilter$]"
|
||||
emptyMessage=<<__emptyMessage__>>
|
||||
storyview=<<__storyview__>>
|
||||
>
|
||||
<$genesis $type=<<__elementTag__>> class="tc-menu-list-item">
|
||||
<$droppable
|
||||
actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>"""
|
||||
enable=<<tv-enable-drag-and-drop>>
|
||||
>
|
||||
<$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/>
|
||||
<$genesis $type=<<__elementTag__>>>
|
||||
<$transclude tiddler="""$itemTemplate$""">
|
||||
<$link to={{!!title}}>
|
||||
<$view field="title"/>
|
||||
</$link>
|
||||
</$transclude>
|
||||
</$genesis>
|
||||
</$droppable>
|
||||
</$genesis>
|
||||
</$list>
|
||||
<$tiddler tiddler="">
|
||||
<$droppable
|
||||
actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>"""
|
||||
enable=<<tv-enable-drag-and-drop>>
|
||||
>
|
||||
<$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/>
|
||||
<$genesis $type=<<__elementTag__>> style="height:0.5em;"/>
|
||||
</$droppable>
|
||||
</$tiddler>
|
||||
</$set>
|
||||
</span>
|
||||
\end
|
||||
|
||||
@@ -4,7 +4,15 @@ code-body: yes
|
||||
|
||||
\define tabs-button()
|
||||
\whitespace trim
|
||||
<$button set=<<tabsState>> setTo=<<currentTab>> default=<<__default__>> selectedClass="tc-tab-selected" tooltip={{!!tooltip}} role="switch">
|
||||
<$button
|
||||
set=<<tabsState>>
|
||||
setTo=<<currentTab>>
|
||||
default=<<__default__>>
|
||||
selectedClass="tc-tab-selected"
|
||||
tooltip={{!!tooltip}}
|
||||
role="switch"
|
||||
data-tab-title=<<currentTab>>
|
||||
>
|
||||
<$tiddler tiddler=<<save-currentTiddler>>>
|
||||
<$set name="tv-wikilinks" value="no">
|
||||
<$transclude tiddler=<<__buttonTemplate__>> mode="inline">
|
||||
|
||||
@@ -16,7 +16,7 @@ second-search-filter: [tags[]is[system]search:title<userInput>sort[]]
|
||||
emptyMessage="<$action-listops $tiddler=<<saveTiddler>> $field=<<__tagField__>> $subfilter='-[<tag>]'/>"
|
||||
>
|
||||
<$action-listops $tiddler=<<saveTiddler>> $field=<<__tagField__>> $subfilter="[<tag>trim[]]"/>
|
||||
$actions$
|
||||
<$transclude $variable="__actions__"/>
|
||||
</$list>
|
||||
</$set>
|
||||
<<delete-tag-state-tiddlers>>
|
||||
@@ -102,7 +102,7 @@ second-search-filter: [tags[]is[system]search:title<userInput>sort[]]
|
||||
<$set name="tag" value={{{ [<newTagNameTiddler>get[text]] }}}>
|
||||
<$button set=<<newTagNameTiddler>> setTo="" class="">
|
||||
<$action-listops $tiddler=<<saveTiddler>> $field=<<__tagField__>> $subfilter="[<tag>trim[]]"/>
|
||||
$actions$
|
||||
<$transclude $variable="__actions__"/>
|
||||
<$set name="currentTiddlerCSSEscaped" value={{{ [<saveTiddler>escapecss[]] }}}>
|
||||
<<delete-tag-state-tiddlers>><$action-sendmessage $message="tm-focus-selector" $param=<<get-tagpicker-focus-selector>>/>
|
||||
</$set>
|
||||
|
||||
@@ -118,7 +118,7 @@ tags: $:/tags/Macro
|
||||
<$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item" >
|
||||
<li class=<<toc-item-class>>>
|
||||
<$link to={{{ [<currentTiddler>get[target]else<currentTiddler>] }}}>
|
||||
<$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}</$button>">
|
||||
<$list filter="[all[current]tagging[]$sort$limit[1]] -[subfilter<__exclude__>]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}</$button>">
|
||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||
<$transclude tiddler=<<toc-closed-icon>> />
|
||||
@@ -145,7 +145,7 @@ tags: $:/tags/Macro
|
||||
<$qualify name="toc-state" title={{{ [[$:/state/toc]addsuffix<__path__>addsuffix[-]addsuffix<currentTiddler>] }}}>
|
||||
<$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item">
|
||||
<li class=<<toc-item-class>>>
|
||||
<$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="""<$button class="tc-btn-invisible">{{$:/core/images/blank}}</$button><span class="toc-item-muted"><<toc-caption>></span>""">
|
||||
<$list filter="[all[current]tagging[]$sort$limit[1]] -[subfilter<__exclude__>]" variable="ignore" emptyMessage="""<$button class="tc-btn-invisible">{{$:/core/images/blank}}</$button><span class="toc-item-muted"><<toc-caption>></span>""">
|
||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||
<$transclude tiddler=<<toc-closed-icon>> />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: 5.3.2
|
||||
created: 20230820114855583
|
||||
modified: 20230820114855583
|
||||
created: 20231016122502955
|
||||
modified: 20231016122502955
|
||||
tags: ReleaseNotes
|
||||
title: Release 5.3.2
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -8,6 +8,47 @@ description: Under development
|
||||
|
||||
//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.1...master]]//
|
||||
|
||||
! Major Improvements
|
||||
|
||||
!! Conditional Shortcut Syntax
|
||||
|
||||
<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7710">> a new [[shortcut syntax|Conditional Shortcut Syntax]] for concisely expressing if-then-else logic. For example:
|
||||
|
||||
```
|
||||
<% if [<animal>match[Elephant]] %>
|
||||
It is an elephant
|
||||
<% elseif [<animal>match[Giraffe]] %>
|
||||
It is a giraffe
|
||||
<% else %>
|
||||
It is completely unknown
|
||||
<% endif %>
|
||||
```
|
||||
|
||||
!! Explicit Templates for the ListWidget
|
||||
|
||||
<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7784">> support for `<$list-template>` and `<$list-empty>` as immediate children of the <<.wid "ListWidget">> widget to specify the list item template and/or the empty template. For example:
|
||||
|
||||
```
|
||||
<$list filter=<<filter>>>
|
||||
<$list-template>
|
||||
<$text text=<<currentTiddler>>/>
|
||||
</$list-template>
|
||||
<$list-empty>
|
||||
None!
|
||||
</$list-empty>
|
||||
</$list>
|
||||
```
|
||||
|
||||
Note that the <<.attr "emptyMessage">> and <<.attr "template">> attributes take precedence if they are present.
|
||||
|
||||
!! jsonset operator
|
||||
|
||||
<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7742">> [[jsonset Operator]] for setting values within JSON objects
|
||||
|
||||
!! QR Code Reader
|
||||
|
||||
<<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/7746">> QR Code plugin to be able to read QR codes and a number of other bar code formats
|
||||
|
||||
! Translation improvement
|
||||
|
||||
Improvements to the following translations:
|
||||
@@ -18,7 +59,8 @@ Improvements to the following translations:
|
||||
|
||||
! Plugin Improvements
|
||||
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/1be8f0a9336952d4745d2bd4f2327e353580a272">> comments plugin to use predefined palette colours
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/1be8f0a9336952d4745d2bd4f2327e353580a272">> Comments Plugin to use predefined palette colours
|
||||
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7785">> Evernote Importer Plugin to support images and other attachments
|
||||
|
||||
! Widget Improvements
|
||||
|
||||
@@ -43,6 +85,8 @@ Improvements to the following translations:
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7749">> editor "type" dropdown state tiddlers
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7712">> handling of "counter-last" variable when appending items with the ListWidget
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6088">> upgrade download link in Firefox
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7698">> refreshing of transcluded functions
|
||||
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7789">> resizing of height of textareas in control panel
|
||||
|
||||
! Node.js Improvements
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
title: $:/my-scroll-position
|
||||
scroll-left: 0
|
||||
scroll-top: 100
|
||||
25
editions/test/playwright.spec.js
Normal file
25
editions/test/playwright.spec.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const {resolve} = require('path');
|
||||
|
||||
const indexPath = resolve(__dirname, 'output', 'test.html');
|
||||
const crossPlatformIndexPath = indexPath.replace(/^\/+/, '');
|
||||
|
||||
|
||||
test('get started link', async ({ page }) => {
|
||||
// The tests can take a while to run
|
||||
const timeout = 1000 * 30;
|
||||
test.setTimeout(timeout);
|
||||
|
||||
// Load the generated test TW html
|
||||
await page.goto(`file:///${crossPlatformIndexPath}`);
|
||||
|
||||
// Sanity check
|
||||
await expect(page.locator('.tc-site-title'), "Expected correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5');
|
||||
|
||||
// Wait for jasmine results bar to appear
|
||||
await expect(page.locator('.jasmine-overall-result'), "Expected jasmine test results bar to be present").toBeVisible({timeout});
|
||||
|
||||
// Assert the tests have passed
|
||||
await expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Expected jasmine tests to not have failed").not.toBeVisible();
|
||||
await expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Expected jasmine tests to have passed").toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
title: Transclude/CustomWidget/CodeblockOverride-TextParser
|
||||
description: Test that overriding codeblock widget does not impact text parser
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
\widget $codeblock(code)
|
||||
<$transclude $variable="copy-to-clipboard" src=<<code>>/>
|
||||
<$genesis $type="$codeblock" $remappable="no" code=<<code>>/>
|
||||
\end
|
||||
|
||||
\procedure myvariable() hello
|
||||
|
||||
<$transclude $variable="myvariable" $type="text/plain" $output="text/plain"/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>hello</p>
|
||||
@@ -0,0 +1,27 @@
|
||||
title: Transclude/Variable/Refreshing
|
||||
description: Transcluding and refreshing a function
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\function list-join(filter, sep:", ") [subfilter<filter>join<sep>]
|
||||
|
||||
<$tiddler tiddler="TestData">
|
||||
|
||||
<<list-join "[enlist{!!items}]">>
|
||||
|
||||
</$tiddler>
|
||||
|
||||
+
|
||||
title: TestData
|
||||
|
||||
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="TestData" items={{{ [range[10]join[ ]] }}}/>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>1, 2, 3, 4, 5, 6, 7, 8, 9, 10</p>
|
||||
@@ -0,0 +1,15 @@
|
||||
title: Transclude/Variable/Static
|
||||
description: Transcluding a function
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
items: 1 2 3 4 5 6 7 8 9 10
|
||||
|
||||
\function list-join(filter, sep:", ") [subfilter<filter>join<sep>]
|
||||
|
||||
<<list-join "[enlist{!!items}]">>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>1, 2, 3, 4, 5, 6, 7, 8, 9, 10</p>
|
||||
@@ -0,0 +1,27 @@
|
||||
title: Widgets/DataAttributes/ButtonWidget
|
||||
description: Data Attributes for ButtonWidget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$button tag="div" class="myclass" data-title="mytiddler" style.color="red" onclick="clicked">
|
||||
my tiddler
|
||||
</$button>
|
||||
<$button tag="div" class="myclass" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}}>
|
||||
hello
|
||||
</$button>
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/>
|
||||
+
|
||||
title: Temp
|
||||
color: black
|
||||
|
||||
Title1
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div class="myclass" data-title="mytiddler" style="color:red;">my tiddler</div><div class="myclass" data-title="Title2" style="color:red;">hello</div></p>
|
||||
@@ -0,0 +1,22 @@
|
||||
title: Widgets/DataAttributes/CheckboxWidget
|
||||
description: Data Attributes for CheckboxWidget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$checkbox tag="done" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}} onclick="clicked"> Is it done?</$checkbox>
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/>
|
||||
+
|
||||
title: Temp
|
||||
color: black
|
||||
|
||||
Title1
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><label class="tc-checkbox "><input data-title="Title2" type="checkbox" style="color:red;"><span>Is it done?</span></label></p>
|
||||
@@ -0,0 +1,27 @@
|
||||
title: Widgets/DataAttributes/DraggableWidget
|
||||
description: Data Attributes for DraggableWidget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$draggable tag="div" class="myclass" data-title="mytiddler" style.color="red" onclick="clicked">
|
||||
my tiddler
|
||||
</$draggable>
|
||||
<$draggable tag="div" class="myclass" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}}>
|
||||
hello
|
||||
</$draggable>
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/>
|
||||
+
|
||||
title: Temp
|
||||
color: black
|
||||
|
||||
Title1
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div class="myclass tc-draggable" data-title="mytiddler" draggable="true" style="color:red;">my tiddler</div><div class="myclass tc-draggable" data-title="Title2" draggable="true" style="color:red;">hello</div></p>
|
||||
@@ -0,0 +1,27 @@
|
||||
title: Widgets/DataAttributes/DroppableWidget
|
||||
description: Data Attributes for DroppableWidget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$droppable tag="div" class="myclass" data-title="mytiddler" style.color="red" onclick="clicked">
|
||||
my tiddler
|
||||
</$droppable>
|
||||
<$droppable tag="div" class="myclass" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}}>
|
||||
hello
|
||||
</$droppable>
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/>
|
||||
+
|
||||
title: Temp
|
||||
color: black
|
||||
|
||||
Title1
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div class="myclass tc-droppable" data-title="mytiddler" style="color:red;">my tiddler</div><div class="myclass tc-droppable" data-title="Title2" style="color:red;">hello</div></p>
|
||||
@@ -0,0 +1,27 @@
|
||||
title: Widgets/DataAttributes/LinkWidget
|
||||
description: Data Attributes for LinkWidget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$link data-id="mytiddler" style.color="red" to="Temp" onclick="clicked">
|
||||
link to Temp
|
||||
</$link>
|
||||
<$link tag="button" data-id={{Temp}} style.color={{{ [[Temp]get[color]] }}} to="SomeTiddler">
|
||||
some tiddler
|
||||
</$link>
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/>
|
||||
+
|
||||
title: Temp
|
||||
color: black
|
||||
|
||||
Title1
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><a class="tc-tiddlylink tc-tiddlylink-resolves" data-id="mytiddler" href="#Temp" style="color:red;">link to Temp</a><button class="tc-tiddlylink tc-tiddlylink-missing" data-id="Title2" draggable="true" style="color:red;">some tiddler</button></p>
|
||||
@@ -0,0 +1,15 @@
|
||||
title: Widgets/DataAttributes/OrderedStyleAttributes
|
||||
description: Ordered style attributes
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<div style="background:red;color:blue;" style.background="green">
|
||||
hello
|
||||
</div>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div style="background:green;color:blue;">hello</div></p>
|
||||
@@ -0,0 +1,27 @@
|
||||
title: Widgets/DataAttributes/SelectWidget
|
||||
description: Data Attributes for SelectWidget
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<$select tiddler='New Tiddler' field='text' default='Choose a new text' data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}} onclick="clicked">
|
||||
<option disabled>Choose a new text</option>
|
||||
<option>A Tale of Two Cities</option>
|
||||
<option>A New Kind of Science</option>
|
||||
<option>The Dice Man</option>
|
||||
</$select>
|
||||
+
|
||||
title: Actions
|
||||
|
||||
<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/>
|
||||
+
|
||||
title: Temp
|
||||
color: black
|
||||
|
||||
Title1
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><select data-title="Title2" value="Choose a new text" style="color:red;"><option disabled="true">Choose a new text</option><option>A Tale of Two Cities</option><option>A New Kind of Science</option><option>The Dice Man</option></select></p>
|
||||
@@ -0,0 +1,15 @@
|
||||
title: Widgets/ElementWidgetEventAttributes
|
||||
description: Element widget should not support event attributes starting with "on"
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<div class="hello" onclick="clicked">
|
||||
TiddlyWiki
|
||||
</div>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div class="hello">TiddlyWiki</div></p>
|
||||
@@ -0,0 +1,15 @@
|
||||
title: Widgets/ElementWidgetStyleAttributes
|
||||
description: Element widget should support style.* attributes
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
\whitespace trim
|
||||
<div class="hello" onclick="clicked" style.color="blue" style.color="red" style.background="yellow">
|
||||
TiddlyWiki
|
||||
</div>
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p><div class="hello" style="color:red;background:yellow;">TiddlyWiki</div></p>
|
||||
@@ -365,6 +365,7 @@ Tests the filtering mechanism.
|
||||
expect(wiki.filterTiddlers("[sort[title]first[8]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three");
|
||||
expect(wiki.filterTiddlers("[sort[title]first[x]]").join(",")).toBe("$:/ShadowPlugin");
|
||||
expect(wiki.filterTiddlers("[sort[title]last[]]").join(",")).toBe("TiddlerOne");
|
||||
expect(wiki.filterTiddlers("[sort[title]last[0]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[sort[title]last[2]]").join(",")).toBe("Tiddler Three,TiddlerOne");
|
||||
expect(wiki.filterTiddlers("[sort[title]last[8]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne");
|
||||
expect(wiki.filterTiddlers("[sort[title]last[x]]").join(",")).toBe("TiddlerOne");
|
||||
|
||||
@@ -53,6 +53,11 @@ describe("json filter tests", function() {
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[2]]")).toEqual(["true"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[3]]")).toEqual(["false"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[4]]")).toEqual(["null"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-5]]")).toEqual(["five"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-4]]")).toEqual(["six"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-3]]")).toEqual(["true"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-2]]")).toEqual(["false"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-1]]")).toEqual(["null"]);
|
||||
});
|
||||
|
||||
it("should support the jsonextract operator", function() {
|
||||
@@ -70,6 +75,11 @@ describe("json filter tests", function() {
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[2]]")).toEqual(["true"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[3]]")).toEqual(["false"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[4]]")).toEqual(["null"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-5]]")).toEqual(['"five"']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-4]]")).toEqual(['"six"']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-3]]")).toEqual(["true"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-2]]")).toEqual(["false"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-1]]")).toEqual(["null"]);
|
||||
});
|
||||
|
||||
it("should support the jsonindexes operator", function() {
|
||||
@@ -85,6 +95,11 @@ describe("json filter tests", function() {
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[2]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[3]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[4]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-5]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-4]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-3]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-2]]")).toEqual([]);
|
||||
expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-1]]")).toEqual([]);
|
||||
});
|
||||
|
||||
it("should support the jsontype operator", function() {
|
||||
@@ -101,6 +116,30 @@ describe("json filter tests", function() {
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[2]]")).toEqual(["boolean"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[3]]")).toEqual(["boolean"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[4]]")).toEqual(["null"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-5]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-4]]")).toEqual(["string"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-3]]")).toEqual(["boolean"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-2]]")).toEqual(["boolean"]);
|
||||
expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-1]]")).toEqual(["null"]);
|
||||
});
|
||||
|
||||
it("should support the jsonset operator", function() {
|
||||
expect(wiki.filterTiddlers("[{First}jsonset[]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset[],[Antelope]]")).toEqual(['"Antelope"']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:number[],[not a number]]")).toEqual(['0']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset[id],[Antelope]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":"Antelope"}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:notatype[id],[Antelope]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":"Antelope"}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:boolean[id],[false]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":false}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:boolean[id],[Antelope]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:number[id],[42]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":42}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:null[id]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":null}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:array[d],[f],[5]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,[]]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:object[d],[f],[5]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,{}]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:number[d],[f],[-1],[42]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,42]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset[missing],[id],[Antelope]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:json[\"Antelope\"]]")).toEqual(['"Antelope"']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:json[id],[{\"a\":313}]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":{"a":313}}']);
|
||||
expect(wiki.filterTiddlers("[{First}jsonset:json[notjson]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']);
|
||||
});
|
||||
|
||||
it("should support the format:json operator", function() {
|
||||
|
||||
@@ -48,6 +48,29 @@ describe("Utility tests", function() {
|
||||
expect($tw.utils.base64Decode($tw.utils.base64Encode(booksEmoji))).toBe(booksEmoji, "should round-trip correctly");
|
||||
});
|
||||
|
||||
it("should handle base64 encoding emojis in URL-safe variant", function() {
|
||||
var booksEmoji = "📚";
|
||||
expect($tw.utils.base64Encode(booksEmoji, false, true)).toBe("8J-Tmg==", "if surrogate pairs are correctly treated as a single code unit then base64 should be 8J+Tmg==");
|
||||
expect($tw.utils.base64Decode("8J-Tmg==", false, true)).toBe(booksEmoji);
|
||||
expect($tw.utils.base64Decode($tw.utils.base64Encode(booksEmoji, false, true), false, true)).toBe(booksEmoji, "should round-trip correctly");
|
||||
});
|
||||
|
||||
it("should handle base64 encoding binary data", function() {
|
||||
var binaryData = "\xff\xfe\xfe\xff";
|
||||
var encoded = $tw.utils.base64Encode(binaryData,true);
|
||||
expect(encoded).toBe("//7+/w==");
|
||||
var decoded = $tw.utils.base64Decode(encoded,true);
|
||||
expect(decoded).toBe(binaryData, "Binary data did not round-trip correctly");
|
||||
});
|
||||
|
||||
it("should handle base64 encoding binary data in URL-safe variant", function() {
|
||||
var binaryData = "\xff\xfe\xfe\xff";
|
||||
var encoded = $tw.utils.base64Encode(binaryData,true,true);
|
||||
expect(encoded).toBe("__7-_w==");
|
||||
var decoded = $tw.utils.base64Decode(encoded,true,true);
|
||||
expect(decoded).toBe(binaryData, "Binary data did not round-trip correctly");
|
||||
});
|
||||
|
||||
it("should handle stringifying a string array", function() {
|
||||
var str = $tw.utils.stringifyList;
|
||||
expect(str([])).toEqual("");
|
||||
|
||||
20
editions/tw5.com/tiddlers/community/Chinese Community.tid
Normal file
20
editions/tw5.com/tiddlers/community/Chinese Community.tid
Normal file
@@ -0,0 +1,20 @@
|
||||
title: 中文社区 - Chinese Community
|
||||
tags: Community
|
||||
|
||||
# A Chinese community tutorial program that people can edit together:
|
||||
#* Main site: [ext[https://tw-cn.netlify.app/]]
|
||||
#* Accelerated access: [ext[https://tw-cn.cpolar.top/]]
|
||||
#* Alternate: [ext[https://tiddly-wiki-chinese-tutorial.vercel.app]]
|
||||
# Tiddlywiki Chinese Chat Forum: [ext[https://talk.tidgi.fun/topic/6]]
|
||||
# Chinese translation of Tiddlywiki official website [ext[https://bramchen.github.io/tw5-docs/zh-Hans/]]
|
||||
# The best Chinese introductory tutorial for newbies [ext[https://keatonlao.github.io/tiddlywiki-xp/]]
|
||||
|
||||
---
|
||||
|
||||
# 大家可以一起编辑的中文社区教程项目:
|
||||
#* 主站:[ext[https://tw-cn.netlify.app/]]
|
||||
#* 加速访问:[ext[https://tw-cn.cpolar.top/]]
|
||||
#* 备用:[ext[https://tiddly-wiki-chinese-tutorial.vercel.app]]
|
||||
# 太微中文交流论坛:[ext[https://talk.tidgi.fun/topic/6]]
|
||||
# 太微官网汉化版:[ext[https://bramchen.github.io/tw5-docs/zh-Hans/]]
|
||||
# 最适合新手的中文入门教程:[ext[https://keatonlao.github.io/tiddlywiki-xp/]]
|
||||
@@ -1,16 +0,0 @@
|
||||
created: 20220417010615742
|
||||
modified: 20220417011547812
|
||||
tags: [[Community Plugins]] [[Community Editions]] Resources
|
||||
title: TiddlyMemo by oflg
|
||||
type: text/vnd.tiddlywiki
|
||||
url: https://tiddlymemo.org/
|
||||
|
||||
Lifelong knowledge, deep in the Sea of Mind.
|
||||
|
||||
{{!!url}}
|
||||
|
||||
~TiddlyMemo uses advanced [[Incremental Learning|https://help.supermemo.org/wiki/Incremental_learning]] concepts to make it your powerful second brain for acquiring lifelong knowledge.
|
||||
|
||||
* [[Read Articles|https://tiddlymemo.org/#Read%20Articles]] like ~SuperMemo
|
||||
* [[Learn languages|https://tiddlymemo.org/#Learn%20languages]] like ~LingQ
|
||||
* [[Memory Notes|https://tiddlymemo.org/#Memory%20Notes]] like Anki
|
||||
@@ -0,0 +1,16 @@
|
||||
created: 20220417010615742
|
||||
modified: 20231005060241771
|
||||
tags: [[Community Editions]]
|
||||
title: Tidme by oflg
|
||||
type: text/vnd.tiddlywiki
|
||||
url: https://github.com/oflg/Tidme
|
||||
|
||||
Lifelong knowledge, deep in Mind.
|
||||
|
||||
{{!!url}}
|
||||
|
||||
Tidme uses advanced [[Incremental Learning|https://help.supermemo.org/wiki/Incremental_learning]] concepts to make it your powerful second brain for acquiring lifelong knowledge.
|
||||
|
||||
* Read Articles like SuperMemo
|
||||
* Learn languages like LingQ
|
||||
* Memory Notes like Anki
|
||||
@@ -0,0 +1,10 @@
|
||||
created: 20220417010615742
|
||||
modified: 20231005060241771
|
||||
tags: [[Community Plugins]]
|
||||
title: Free Spaced Repetition Scheduler for TiddlyWiki by oflg
|
||||
type: text/vnd.tiddlywiki
|
||||
url: https://github.com/open-spaced-repetition/fsrs4tw
|
||||
|
||||
TiddlyWiki-based memory programme using advanced FSRS algorithm
|
||||
|
||||
{{!!url}}
|
||||
@@ -1,6 +1,7 @@
|
||||
caption: decodebase64
|
||||
op-input: a [[selection of titles|Title Selection]]
|
||||
op-output: the input with base 64 decoding applied
|
||||
op-suffix: optional: `binary` to produce binary output, `urlsafe` for URL-safe input
|
||||
op-parameter:
|
||||
op-parameter-name:
|
||||
op-purpose: apply base 64 decoding to a string
|
||||
@@ -11,6 +12,10 @@ from-version: 5.2.6
|
||||
|
||||
See Mozilla Developer Network for details of [[base 64 encoding|https://developer.mozilla.org/en-US/docs/Glossary/Base64]]. TiddlyWiki uses [[library code from @nijikokun|https://gist.github.com/Nijikokun/5192472]] to handle the conversion.
|
||||
|
||||
The input strings must be base64 encoded. The output strings are binary data.
|
||||
The input strings must be base64 encoded. The output strings are the text (or binary data) decoded from base64 format.
|
||||
|
||||
The optional `binary` suffix, if present, changes how the input is processed. The input is normally assumed to be [[UTF-8|https://developer.mozilla.org/en-US/docs/Glossary/UTF-8]] text encoded in base64 form (such as what the <<.op "encodebase64">> operator produces), so only certain byte sequences in the input are valid. If the input is binary data encoded in base64 format (such as an image, audio file, video file, etc.), then use the optional `binary` suffix, which will allow all byte sequences. Note that the output will then be binary, ''not'' text, and should probably not be passed into further filter operators.
|
||||
|
||||
The optional `urlsafe` suffix, if present, causes the decoder to assume that the base64 input uses `-` and `_` instead of `+` and `/` for the 62nd and 63rd characters of the base64 "alphabet", which is usually referred to as "URL-safe base64" or "bae64url".
|
||||
|
||||
<<.operator-examples "decodebase64">>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
caption: encodebase64
|
||||
op-input: a [[selection of titles|Title Selection]]
|
||||
op-output: the input with base 64 encoding applied
|
||||
op-suffix: optional: `binary` to treat input as binary data, `urlsafe` for URL-safe output
|
||||
op-parameter:
|
||||
op-parameter-name:
|
||||
op-purpose: apply base 64 encoding to a string
|
||||
@@ -11,6 +12,10 @@ from-version: 5.2.6
|
||||
|
||||
See Mozilla Developer Network for details of [[base 64 encoding|https://developer.mozilla.org/en-US/docs/Glossary/Base64]]. TiddlyWiki uses [[library code from @nijikokun|https://gist.github.com/Nijikokun/5192472]] to handle the conversion.
|
||||
|
||||
The input strings are interpreted as binary data. The output strings are base64 encoded.
|
||||
The input strings are interpreted as [[UTF-8 encoded|https://developer.mozilla.org/en-US/docs/Glossary/UTF-8]] text (or binary data instead if the `binary` suffix is present). The output strings are base64 encoded.
|
||||
|
||||
The optional `binary` suffix, if present, causes the input string to be interpreted as binary data instead of text. Normally, an extra UTF-8 encoding step will be added before the base64 output is produced, so that emojis and other Unicode characters will be encoded correctly. If the input is binary data, such as an image, audio file, video, etc., then the UTF-8 encoding step would produce incorrect results, so using the `binary` suffix causes the UTF-8 encoding step to be skipped.
|
||||
|
||||
The optional `urlsafe` suffix, if present, will use the alternate "URL-safe" base64 encoding, where `-` and `_` are used instead of `+` and `/` respectively, allowing the result to be used in URL query parameters or filenames.
|
||||
|
||||
<<.operator-examples "encodebase64">>
|
||||
|
||||
@@ -53,6 +53,14 @@ The <<.op jsonextract>> operator uses multiple operands to specify the indexes o
|
||||
[<jsondata>jsonextract[d],[g]] --> {"x":"max","y":"may","z":"maize"}
|
||||
```
|
||||
|
||||
<<.from-version "5.3.2">> Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on:
|
||||
|
||||
```
|
||||
[<jsondata>jsonextract[d],[f],[-1]] --> null
|
||||
[<jsondata>jsonextract[d],[f],[-2]] --> false
|
||||
[<jsondata>jsonextract[d],[f],[-4]] --> "six"
|
||||
```
|
||||
|
||||
Indexes can be dynamically composed from variables and transclusions:
|
||||
|
||||
```
|
||||
|
||||
@@ -51,6 +51,14 @@ The <<.op jsonget>> operator uses multiple operands to specify the indexes of th
|
||||
[<jsondata>jsonget[d],[f],[0]] --> "five"
|
||||
```
|
||||
|
||||
<<.from-version "5.3.2">> Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on:
|
||||
|
||||
```
|
||||
[<jsondata>jsonget[d],[f],[-1]] --> null
|
||||
[<jsondata>jsonget[d],[f],[-2]] --> false
|
||||
[<jsondata>jsonget[d],[f],[-4]] --> "six"
|
||||
```
|
||||
|
||||
Indexes can be dynamically composed from variables and transclusions:
|
||||
|
||||
```
|
||||
|
||||
104
editions/tw5.com/tiddlers/filters/jsonset.tid
Normal file
104
editions/tw5.com/tiddlers/filters/jsonset.tid
Normal file
@@ -0,0 +1,104 @@
|
||||
created: 20230915121010948
|
||||
modified: 20230915121010948
|
||||
tags: [[Filter Operators]] [[JSON Operators]]
|
||||
title: jsonset Operator
|
||||
caption: jsonset
|
||||
op-purpose: set the value of a property in JSON strings
|
||||
op-input: a selection of JSON strings
|
||||
op-parameter: one or more indexes of the property to retrieve and sometimes a value to assign
|
||||
op-output: the JSON strings with the specified property assigned
|
||||
|
||||
<<.from-version "5.3.2">> See [[JSON in TiddlyWiki]] for background.
|
||||
|
||||
The <<.op jsonset>> operator is used to set a property value in JSON strings. See also the following related operators:
|
||||
|
||||
* <<.olink jsonget>> to retrieve the values of a property in JSON data
|
||||
* <<.olink jsontype>> to retrieve the type of a JSON value
|
||||
* <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array
|
||||
* <<.olink jsonextract>> to retrieve a JSON value as a string of JSON
|
||||
|
||||
Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`.
|
||||
|
||||
```
|
||||
{
|
||||
"a": "one",
|
||||
"b": "",
|
||||
"c": "three",
|
||||
"d": {
|
||||
"e": "four",
|
||||
"f": [
|
||||
"five",
|
||||
"six",
|
||||
true,
|
||||
false,
|
||||
null
|
||||
],
|
||||
"g": {
|
||||
"x": "max",
|
||||
"y": "may",
|
||||
"z": "maize"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following examples assume that this JSON data is contained in a variable called `jsondata`.
|
||||
|
||||
The <<.op jsonset>> operator uses multiple operands to specify the indexes of the property to set. When used to assign strings the final operand is interpreted as the value to assign. For example:
|
||||
|
||||
```
|
||||
[<jsondata>jsonset[d],[Jaguar]] --> {"a": "one","b": "","c": "three","d": "Jaguar"}
|
||||
[<jsondata>jsonset[d],[f],[Panther]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": "Panther","g": {"x": "max","y": "may","z": "maize"}}"}
|
||||
```
|
||||
|
||||
Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on:
|
||||
|
||||
```
|
||||
[<jsondata>jsonset[d],[f],[-1],[Elephant]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": ["five","six",true,false,"Elephant"],"g": {"x": "max","y": "may","z": "maize"}}"}
|
||||
[<jsondata>jsonset[d],[f],[-2],[Elephant]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": ["five","six",true,"Elephant",null],"g": {"x": "max","y": "may","z": "maize"}}"}
|
||||
[<jsondata>jsonset[d],[f],[-4],[Elephant]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": ["five","Elephant",true,false,null],"g": {"x": "max","y": "may","z": "maize"}}"}
|
||||
```
|
||||
|
||||
Indexes can be dynamically composed from variables and transclusions:
|
||||
|
||||
```
|
||||
[<jsondata>jsonset<variable>,{!!field},[0],{CurrentResult}]
|
||||
```
|
||||
|
||||
The data type of the value to be assigned to the property can be specified with an optional suffix:
|
||||
|
||||
|!Suffix |!Description |
|
||||
|''string'' |The string is specified as the final operand |
|
||||
|''boolean'' |The boolean value is true if the final operand is the string "true" and false if the final operand is the string "false". Any other value for the final string results prevents the property from being assigned |
|
||||
|''number'' |The numeric value is taken from the final operand. Invalid numbers are interpreted as zero |
|
||||
|''json'' |The JSON string value is taken from the final operand. Invalid JSON prevents the property from being assigned |
|
||||
|''object'' |An empty object is assigned to the property. The final operand is not used as a value |
|
||||
|''array'' |An empty array is assigned to the property. The final operand is not used as a value |
|
||||
|''null'' |The special value null is assigned to the property. The final operand is not used as a value |
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
Input string:
|
||||
{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}
|
||||
|
||||
[jsonset[]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}
|
||||
[jsonset[],[Antelope]] --> "Antelope"
|
||||
[jsonset:number[],[not a number]] --> 0
|
||||
[jsonset[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":"Antelope"}
|
||||
[jsonset:notatype[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":"Antelope"}
|
||||
[jsonset:boolean[id],[false]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":false}
|
||||
[jsonset:boolean[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}
|
||||
[jsonset:number[id],[42]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":42}
|
||||
[jsonset:null[id]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":null}
|
||||
[jsonset:array[d],[f],[5]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,[]]}}
|
||||
[jsonset:object[d],[f],[5]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,{}]}}
|
||||
[jsonset[missing],[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}
|
||||
```
|
||||
|
||||
A subtlety is that the special case of a single operand sets the value of that operand as the new JSON string, entirely replacing the input object. If that operand is blank, the operation is ignored and no assignment takes place. Thus:
|
||||
|
||||
```
|
||||
[<jsondata>jsonset[Panther]] --> "Panther"
|
||||
[<jsondata>jsonset[]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": ["five", "six", true, false, null],"g": {"x": "max","y": "may","z": "maize"}}"}
|
||||
```
|
||||
@@ -61,6 +61,14 @@ The <<.op jsontype>> operator uses multiple operands to specify the indexes of t
|
||||
[<jsondata>jsontype[d],[f],[2]] --> "boolean"
|
||||
```
|
||||
|
||||
<<.from-version "5.3.2">> Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on:
|
||||
|
||||
```
|
||||
[<jsondata>jsontype[d],[f],[-1]] --> "null"
|
||||
[<jsondata>jsontype[d],[f],[-2]] --> "boolean"
|
||||
[<jsondata>jsontype[d],[f],[-4]] --> "string"
|
||||
```
|
||||
|
||||
Indexes can be dynamically composed from variables and transclusions:
|
||||
|
||||
```
|
||||
|
||||
@@ -18,6 +18,11 @@ TiddlyWiki lets you choose where to keep your data, guaranteeing that in the dec
|
||||
<$macrocall $name="flex-card" bordercolor={{!!color}} textcolor={{!!text-color}} backgroundcolor={{!!background-color}} captionField="caption" descriptionField="text"/>
|
||||
</$list>
|
||||
</div>
|
||||
<div class="tc-cards tc-small">
|
||||
<$link to="中文社区 - Chinese Community" class="tc-btn-big-green tc-card">
|
||||
中文社区 - Chinese Community
|
||||
</$link>
|
||||
</div>
|
||||
|
||||
!! ''Find Out More''
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: tm-open-window
|
||||
created: 20160424181447704
|
||||
modified: 20220301162140993
|
||||
modified: 20230831201518773
|
||||
tags: Messages
|
||||
title: WidgetMessage: tm-open-window
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -20,10 +20,17 @@ The `tm-open-window` [[message|Messages]] opens a tiddler in a new //browser// w
|
||||
|
||||
The `tm-open-window` message is best generated with the ActionSendMessageWidget, which in turn is triggered by a widget such as the ButtonWidget. The message is handled by the core itself.
|
||||
|
||||
<<.tip """When used with the ActionSendMessageWidget, <<.param 'param'>> becomes <<.param '$param'>> """>>
|
||||
<<.tip """Parameters <<.param template>>, <<.param windowTitle>>, <<.param width>>, <<.param height>>, <<.param left>> and <<.param top>> require the ActionSendMessageWidget.""">>
|
||||
<<.tip """<<.from-version 5.2.2>> To close a window opened with tm-open-window use [[WidgetMessage: tm-close-window]]""">>
|
||||
<<.tip """<<.from-version 5.2.2>> To open a tiddler in more than one new window, use a unique value for <<.param windowID>>""">>
|
||||
<<.tip """When used with the ActionSendMessageWidget, <<.param 'param'>> becomes <<.param '$param'>>.<br>
|
||||
Parameters <<.param template>>, <<.param windowTitle>>, <<.param width>>, <<.param height>>, <<.param left>> and <<.param top>> require the ActionSendMessageWidget. """>>
|
||||
|
||||
<<.tip """<<.from-version 5.2.2>>
|
||||
To close a window opened with tm-open-window use [[WidgetMessage: tm-close-window]]<br>
|
||||
To open a tiddler in more than one new window, use a unique value for <<.param windowID>>
|
||||
""">>
|
||||
|
||||
<<.tip """<<.from-version 5.3.2>>
|
||||
If the new window is hidden by other windows, clicking the "open" button again will bring it to the foreground and set focus to the new window. This behaviour should be consistent for all browsers now
|
||||
""">>
|
||||
|
||||
<$macrocall $name='wikitext-example-without-html'
|
||||
src="""
|
||||
|
||||
@@ -147,6 +147,10 @@ type: text/vnd.tiddlywiki
|
||||
gap: 1em;
|
||||
}
|
||||
|
||||
.tc-cards.tc-small {
|
||||
font-size: 0.7em;
|
||||
}
|
||||
|
||||
.tc-cards.tc-action-card {
|
||||
text-align: center;
|
||||
background: none;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: browse
|
||||
created: 20131024141900000
|
||||
modified: 20200421221304177
|
||||
modified: 20231113093304323
|
||||
tags: Widgets
|
||||
title: BrowseWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -20,6 +20,8 @@ The content of the <<.wid BrowseWidget>> widget is ignored.
|
||||
|accept |<<.from-version "5.1.23">> Optional comma delimited [[list of file accepted extensions|https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers]] and/or MIME types |
|
||||
|message |Optional override of widget message to be generated. The files will be passed in the JavaScript object `event.target.files` |
|
||||
|param |Optional parameter to be passed with the custom message |
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element |
|
||||
|
||||
On iPhone/iPad choosing the multiple option will remove the ability to take photographs/videos directly into TiddlyWiki.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: button
|
||||
created: 20131024141900000
|
||||
modified: 20220810192251345
|
||||
modified: 20231113093304323
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: ButtonWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -41,6 +41,8 @@ The content of the `<$button>` widget is displayed within the button.
|
||||
|aria-label |Optional [[Accessibility]] label |
|
||||
|tooltip |Optional tooltip |
|
||||
|class |An optional CSS class name to be assigned to the HTML element|
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element |
|
||||
|style |An optional CSS style attribute to be assigned to the HTML element |
|
||||
|tag |An optional html tag to use instead of the default "button" |
|
||||
|dragTiddler |An optional tiddler title making the button draggable and identifying the payload tiddler. See DraggableWidget for details |
|
||||
|
||||
@@ -3,7 +3,7 @@ colors: red orange yellow blue
|
||||
created: 20131024141900000
|
||||
fruits: bananas oranges grapes
|
||||
list: [[CheckboxWidget (tag Mode)]] [[CheckboxWidget (field Mode)]] [[CheckboxWidget (listField Mode)]] [[CheckboxWidget (index Mode)]] [[CheckboxWidget (listIndex Mode)]] [[CheckboxWidget (filter Mode)]] [[CheckboxWidget (indeterminate)]]
|
||||
modified: 20230316192632667
|
||||
modified: 20231113093304323
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: CheckboxWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -38,5 +38,7 @@ The content of the `<$checkbox>` widget is displayed within an HTML `<label>` el
|
||||
|<<.attr uncheckactions>> |<<.from-version "5.1.16">> A string containing ActionWidgets to be triggered when the checkbox is unchecked |
|
||||
|<<.attr checkactions>> |<<.from-version "5.1.20">> A string containing ActionWidgets to be triggered when the checkbox is checked |
|
||||
|<<.attr disabled>> |<<.from-version "5.1.23">> Optionally disables the checkbox if set to <<.value yes>> (defaults to <<.value no>>)|
|
||||
|<<.attr data-*>> |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML `<input>` element |
|
||||
|<<.attr style.*>> |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML `<input>` element |
|
||||
|
||||
<<.doc-tabs>>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: draggable
|
||||
created: 20170406081938627
|
||||
modified: 20220715120213777
|
||||
modified: 20231121101431149
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: DraggableWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -20,10 +20,11 @@ See DragAndDropMechanism for an overview.
|
||||
|selector|<<.from-version 5.2.2>> Optional CSS Selector to identify a DOM element within the widget that will be used as the drag handle |
|
||||
|class |Optional CSS classes to assign to the DOM element created by the widget. The class `tc-draggable` is added to the the DOM element created by the widget unless the <<.param selector>> attribute is used. The class `tc-dragging` is applied to the DOM element created by the widget while the element is being dragged |
|
||||
|enable |<<.from-version 5.2.3>> Optional value "no" to disable the draggable functionality (defaults to "yes") |
|
||||
|
||||
|startactions |Optional action string that gets invoked when dragging ''starts'' |
|
||||
|endactions |Optional action string that gets invoked when dragging ''ends'' |
|
||||
|dragimagetype |<<.from-version "5.2.0">> Optional type of drag image: `dom` (the default) or `blank` to disable the drag image |
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element |
|
||||
|
||||
Either or both of the ''tiddler'' and ''filter'' attributes must be specified in order for there to be a payload to drag.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: droppable
|
||||
created: 20170406082820317
|
||||
modified: 20211009122023265
|
||||
modified: 20231121101447359
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: DroppableWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -31,6 +31,8 @@ See DragAndDropMechanism for an overview.
|
||||
|class |Optional CSS classes to assign to the draggable element. The class `tc-droppable` is added automatically, and the class `tc-dragover` is applied while an item is being dragged over the droppable element |
|
||||
|tag |Optional tag to override the default of a "div" element when the widget is rendered in block mode, or a "span" element when it is rendered in inline mode |
|
||||
|enable |<<.from-version "5.1.22">> Optional value "no" to disable the droppable functionality (defaults to "yes") |
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element |
|
||||
|
||||
Within the action string, there are two Variables generated by the DroppableWidget:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: image
|
||||
created: 20140416160234142
|
||||
modified: 20220721102303815
|
||||
modified: 20231121114351165
|
||||
tags: Widgets
|
||||
title: ImageWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -21,6 +21,7 @@ Any content of the `<$image>` widget is ignored.
|
||||
|alt |The alternative text to be associated with the image |
|
||||
|class |CSS classes to be assigned to the `<img>` element |
|
||||
|loading|<<.from-version "5.2.3">>Optional. Set to `lazy` to enable lazy loading of images loaded from an external URI |
|
||||
|usemap|<<.from-version "5.3.2">>Optional usemap attribute to be assigned to the `<img>` element for use with HTML image maps |
|
||||
|
||||
The width and the height can be specified as pixel values (eg "23" or "23px") or percentages (eg "23%"). They are both optional; if not provided the browser will use CSS rules to size the image.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: link
|
||||
created: 20131024141900000
|
||||
modified: 20190610195105615
|
||||
modified: 20231113093304323
|
||||
tags: Widgets
|
||||
title: LinkWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -23,6 +23,8 @@ The content of the link widget is rendered within the `<a>` tag representing the
|
||||
|tag |Optional tag to override the default "a" element |
|
||||
|class|Optional CSS classes //in addition to// the default classes (see below)|
|
||||
|overrideClass|<<.from-version "5.1.16">> Optional CSS classes //instead of// the default classes |
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element |
|
||||
|
||||
The draggable functionality is equivalent to using the DraggableWidget with the ''tiddler'' attribute set to the link target.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: radio
|
||||
created: 20131212195353929
|
||||
modified: 20211009121654734
|
||||
modified: 20231113093304323
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: RadioWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -22,6 +22,8 @@ The content of the `<$radio>` widget is displayed within an HTML `<label>` eleme
|
||||
|class |The CSS classes assigned to the label around the radio button <<.from-version "5.1.14">> `tc-radio` is always applied by default, as well as `tc-radio-selected` when selected |
|
||||
|actions|<<.from-version "5.1.23">> Optional string containing ActionWidgets to be triggered when the value changes. <br>The variable: ''actionValue'' is available for the actions |
|
||||
|disabled|<<.from-version "5.1.23">> Optional. Set to "yes" to disable the radio input. Defaults to "no" |
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the `<input>` HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the `<input>` HTML element |
|
||||
|
||||
!! Field Mode
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: range
|
||||
created: 20171102134825376
|
||||
modified: 20211009121606405
|
||||
modified: 20231113093304323
|
||||
tags: Widgets TriggeringWidgets
|
||||
title: RangeWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -26,6 +26,8 @@ The content of the `<$range>` widget is ignored.
|
||||
|actionsStart|<<.from-version "5.1.23">> Optional, A string containing ~ActionWidgets to be triggered when the "handle" is ''clicked''. <br>The variable: ''actionValueHasChanged'' is always `no` here|
|
||||
|actionsStop|<<.from-version "5.1.23">> Optional, A string containing ~ActionWidgets to be triggered when the "handle" is ''released''. <br>The variable: ''actionValueHasChanged'' is `yes`, ''if'' the new-value is different to the start-value |
|
||||
|disabled|<<.from-version "5.1.23">> Optional, disables the range input if set to "yes". Defaults to "no"|
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the `<input>` HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the `<input>` HTML element |
|
||||
|
||||
! Examples
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: scrollable
|
||||
created: 20140324223413403
|
||||
modified: 20220620115347910
|
||||
modified: 20230731100903977
|
||||
tags: Widgets
|
||||
title: ScrollableWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -16,12 +16,15 @@ The content of the `<$scrollable>` widget is displayed within a pair of wrapper
|
||||
|!Attribute |!Description |
|
||||
|class |The CSS class(es) to be applied to the outer DIV |
|
||||
|fallthrough |See below |
|
||||
|bind |<<.from-version "5.3.2">> Optional title of tiddler to which the scroll position should be bound |
|
||||
|
||||
Binding the scroll position to a tiddler automatically copies the scroll coordinates into the `scroll-left` and `scroll-top` fields as scrolling occurs. Conversely, setting those field values will automatically cause the scrollable to scroll if it can.
|
||||
|
||||
<$macrocall $name=".note" _="""If a scrollable widget can't handle the `tm-scroll` message because the inner DIV fits within the outer DIV, then by default the message falls through to the parent widget. Setting the ''fallthrough'' attribute to `no` prevents this behaviour."""/>
|
||||
|
||||
! Examples
|
||||
|
||||
This example requires the following CSS definitions from [[$:/_tw5.com-styles]]:
|
||||
These examples require the following CSS definitions from [[$:/_tw5.com-styles]]:
|
||||
|
||||
```
|
||||
.tc-scrollable-demo {
|
||||
@@ -33,6 +36,8 @@ This example requires the following CSS definitions from [[$:/_tw5.com-styles]]:
|
||||
}
|
||||
```
|
||||
|
||||
!! Simple Usage
|
||||
|
||||
This wiki text shows how to display a list within the scrollable widget:
|
||||
|
||||
<<wikitext-example-without-html "<$scrollable class='tc-scrollable-demo'>
|
||||
@@ -46,3 +51,23 @@ This wiki text shows how to display a list within the scrollable widget:
|
||||
</$scrollable>
|
||||
">>
|
||||
|
||||
!! Binding scroll position to a tiddler
|
||||
|
||||
[[Current scroll position|$:/my-scroll-position]]: {{$:/my-scroll-position!!scroll-left}}, {{$:/my-scroll-position!!scroll-top}}
|
||||
|
||||
<$button>
|
||||
<$action-setfield $tiddler="$:/my-scroll-position" scroll-left="100" scroll-top="100"/>
|
||||
Set current scroll position to 100,100
|
||||
</$button>
|
||||
|
||||
<<wikitext-example-without-html "<$scrollable class='tc-scrollable-demo' bind='$:/my-scroll-position'>
|
||||
<$list filter='[tag[Reference]]'>
|
||||
|
||||
<$view field='title'/>: <$list filter='[all[current]links[]sort[title]]' storyview='pop'>
|
||||
<$link><$view field='title'/></$link>
|
||||
</$list>
|
||||
|
||||
</$list>
|
||||
</$scrollable>
|
||||
">>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
caption: select
|
||||
created: 20131024141900000
|
||||
modified: 20211108165037846
|
||||
modified: 20231113093304323
|
||||
tags: TriggeringWidgets Widgets
|
||||
title: SelectWidget
|
||||
type: text/vnd.tiddlywiki
|
||||
@@ -42,6 +42,9 @@ The content of the `<$select>` widget should be one or more HTML `<option>` or `
|
||||
|actions |A string containing ActionWidgets to be triggered when the key combination is detected |
|
||||
|focus |<<.from-version "5.2.4">> Optional. Set to "yes" to automatically focus the HTML select element after creation |
|
||||
|tabindex |<<.from-version "5.3.1">> Optional. Sets the `tabindex` attribute of the HTML select element to the given value |
|
||||
|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element |
|
||||
|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element |
|
||||
|
||||
! Examples
|
||||
|
||||
!! Simple Lists
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"tiddlywiki/railroad",
|
||||
"tiddlywiki/evernote",
|
||||
"tiddlywiki/internals",
|
||||
"tiddlywiki/menubar"
|
||||
"tiddlywiki/menubar",
|
||||
"tiddlywiki/qrcode"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
@@ -53,7 +54,12 @@
|
||||
"--render","$:/core/templates/static.template.css","static/static.css","text/plain"],
|
||||
"external-js": [
|
||||
"--render","$:/core/save/offline-external-js","[[external-]addsuffix<version>addsuffix[.html]]","text/plain",
|
||||
"--render","$:/core/templates/tiddlywiki5.js","[[tiddlywikicore-]addsuffix<version>addsuffix[.js]]","text/plain"]
|
||||
"--render","$:/core/templates/tiddlywiki5.js","[[tiddlywikicore-]addsuffix<version>addsuffix[.js]]","text/plain"],
|
||||
"archive":[
|
||||
"--render","$:/core/save/all","[[archive/TiddlyWiki-]addsuffix<version>addsuffix[.html]]","text/plain",
|
||||
"--render","$:/editions/tw5.com/download-empty","[[archive/Empty-TiddlyWiki-]addsuffix<version>addsuffix[.html]]","text/plain",
|
||||
"--render","[[TiddlyWiki Archive]]","archive/index.html","text/plain","$:/core/templates/static.tiddler.html",
|
||||
"--render","$:/core/templates/static.template.css","archive/static.css","text/plain"]
|
||||
},
|
||||
"config": {
|
||||
"retain-original-tiddler-path": true
|
||||
|
||||
@@ -551,3 +551,5 @@ Eric Haberstroh, @pille1842, 2023/07/23
|
||||
BuckarooBanzay, @BuckarooBanzay, 2023/09/01
|
||||
|
||||
Timur, @T1mL3arn, 2023/10/04
|
||||
|
||||
Wang Ke, @Gk0Wk, 2023/10/17
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
"node": ">=0.8.2"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "node ./tiddlywiki.js ./editions/tw5.com-server --listen",
|
||||
"test": "node ./tiddlywiki.js ./editions/test --verbose --version --build index",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"lint": "eslint ."
|
||||
}
|
||||
}
|
||||
|
||||
46
playwright.config.js
Normal file
46
playwright.config.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const { defineConfig, devices } = require('@playwright/test');
|
||||
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*/
|
||||
module.exports = defineConfig({
|
||||
testDir: './editions/test/',
|
||||
|
||||
// Allow parallel tests
|
||||
fullyParallel: true,
|
||||
|
||||
// Prevent accidentally committed "test.only" from wrecking havoc
|
||||
forbidOnly: !!process.env.CI,
|
||||
|
||||
// Do not retry tests on failure
|
||||
retries: 0,
|
||||
|
||||
// How many parallel workers
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
|
||||
// Reporter to use. See https://playwright.dev/docs/test-reporters
|
||||
reporter: 'html',
|
||||
|
||||
// Settings shared with all the tests
|
||||
use: {
|
||||
// Take a screenshot when the test fails
|
||||
screenshot: {
|
||||
mode: 'only-on-failure',
|
||||
fullPage: true
|
||||
}
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
@@ -56,6 +56,11 @@ name: tiddlywiki
|
||||
rendering-intent: auto;
|
||||
}
|
||||
|
||||
.tc-tiddler-frame .tc-tiddler-editor .tc-edit-texteditor,
|
||||
.tc-tiddler-frame .tc-tiddler-editor .tc-tiddler-preview-preview {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.cm-s-tiddlywiki.CodeMirror, .cm-s-tiddlywiki .CodeMirror-gutters { background-color: <<colour tiddler-editor-background>>; color: <<colour foreground>>; }
|
||||
.cm-s-tiddlywiki .CodeMirror-gutters {background: <<colour tiddler-editor-background>>; border-right: 1px solid <<colour tiddler-editor-border>>;}
|
||||
.cm-s-tiddlywiki .CodeMirror-linenumber {color: <<colour foreground>>;}
|
||||
|
||||
@@ -23,6 +23,7 @@ The `<$dynannotate>` widget uses the selection tracker to support a popup that d
|
||||
|filter |Filter identifying the annotation tiddlers applying to this content (see below) |
|
||||
|actions |Action string to be executed when an annotation is clicked. The variable `annotationTiddler` contains the title of the tiddler corresponding to the annotation that was clicked, and the variable `modifierKey` contains "ctrl", "shift", "ctrl-shift", "normal" according to which modifier keys were pressed |
|
||||
|popup |Popup state tiddler to be used to trigger a popup when an annotation is clicked |
|
||||
|floating |Setting to `yes` makes the popup need to be closed explicitly. |
|
||||
|search |Search text to be highlighted within the widget |
|
||||
|searchDisplay |"overlay" or "snippet" (see below) |
|
||||
|searchMode |"literal" (default), "regexp", "whitespace", "words" or "some" (see below) |
|
||||
|
||||
@@ -65,7 +65,7 @@ DynannotateWidget.prototype.render = function(parent,nextSibling) {
|
||||
this.domNodes.push(this.domWrapper);
|
||||
// Apply the selection tracker data to the DOM
|
||||
if(!isSnippetMode) {
|
||||
this.applySelectionTrackerData();
|
||||
this.applySelectionTrackerData();
|
||||
}
|
||||
// Render our child widgets
|
||||
this.renderChildren(this.domContent,null);
|
||||
@@ -79,7 +79,7 @@ DynannotateWidget.prototype.render = function(parent,nextSibling) {
|
||||
// Apply annotations
|
||||
this.applyAnnotations();
|
||||
// Apply search overlays
|
||||
this.applySearch();
|
||||
this.applySearch();
|
||||
}
|
||||
}
|
||||
// Save the width of the wrapper so that we can tell when it changes
|
||||
@@ -205,10 +205,11 @@ DynannotateWidget.prototype.applyAnnotations = function() {
|
||||
if(self.hasAttribute("popup")) {
|
||||
$tw.popup.triggerPopup({
|
||||
domNode: domOverlay,
|
||||
title: self.getAttribute("popup"),
|
||||
title: self.getAttribute("popup"),
|
||||
floating: self.getAttribute("floating"),
|
||||
wiki: self.wiki
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
// Draw the overlay for the "target" attribute
|
||||
@@ -224,7 +225,7 @@ DynannotateWidget.prototype.applyAnnotations = function() {
|
||||
className: "tc-dynannotation-annotation-overlay",
|
||||
onclick: clickHandlerFn(null)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Draw the overlays for each annotation tiddler
|
||||
$tw.utils.each(this.annotationTiddlers,function(title) {
|
||||
@@ -361,7 +362,7 @@ DynannotateWidget.prototype.applySnippets = function() {
|
||||
if(!merged) {
|
||||
container = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -382,7 +383,7 @@ DynannotateWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var childrenDidRefresh = this.refreshChildren(changedTiddlers);
|
||||
// Reapply the selection tracker data to the DOM
|
||||
if(changedAttributes.selection || changedAttributes.selectionPrefix || changedAttributes.selectionSuffix || changedAttributes.selectionPopup) {
|
||||
this.applySelectionTrackerData();
|
||||
this.applySelectionTrackerData();
|
||||
}
|
||||
// Reapply the annotations if the children refreshed or the main wrapper resized
|
||||
var wrapperWidth = this.domWrapper.offsetWidth,
|
||||
@@ -390,14 +391,14 @@ DynannotateWidget.prototype.refresh = function(changedTiddlers) {
|
||||
oldAnnotationTiddlers = this.annotationTiddlers;
|
||||
this.getAnnotationTiddlers();
|
||||
if(!isSnippetMode && (
|
||||
childrenDidRefresh ||
|
||||
hasResized ||
|
||||
changedAttributes.target ||
|
||||
changedAttributes.targetPrefix ||
|
||||
changedAttributes.targetSuffix ||
|
||||
changedAttributes.filter ||
|
||||
changedAttributes.actions ||
|
||||
changedAttributes.popup ||
|
||||
childrenDidRefresh ||
|
||||
hasResized ||
|
||||
changedAttributes.target ||
|
||||
changedAttributes.targetPrefix ||
|
||||
changedAttributes.targetSuffix ||
|
||||
changedAttributes.filter ||
|
||||
changedAttributes.actions ||
|
||||
changedAttributes.popup ||
|
||||
!$tw.utils.isArrayEqual(oldAnnotationTiddlers,this.annotationTiddlers) ||
|
||||
this.annotationTiddlers.find(function(title) {
|
||||
return changedTiddlers[title];
|
||||
@@ -406,23 +407,23 @@ DynannotateWidget.prototype.refresh = function(changedTiddlers) {
|
||||
this.applyAnnotations();
|
||||
}
|
||||
if(!isSnippetMode && (
|
||||
childrenDidRefresh ||
|
||||
hasResized ||
|
||||
changedAttributes.search ||
|
||||
changedAttributes.searchMinLength ||
|
||||
changedAttributes.searchClass ||
|
||||
changedAttributes.searchMode ||
|
||||
childrenDidRefresh ||
|
||||
hasResized ||
|
||||
changedAttributes.search ||
|
||||
changedAttributes.searchMinLength ||
|
||||
changedAttributes.searchClass ||
|
||||
changedAttributes.searchMode ||
|
||||
changedAttributes.searchCaseSensitive
|
||||
)) {
|
||||
this.applySearch();
|
||||
}
|
||||
if(isSnippetMode && (
|
||||
childrenDidRefresh ||
|
||||
hasResized ||
|
||||
changedAttributes.search ||
|
||||
changedAttributes.searchMinLength ||
|
||||
changedAttributes.searchClass ||
|
||||
changedAttributes.searchMode ||
|
||||
childrenDidRefresh ||
|
||||
hasResized ||
|
||||
changedAttributes.search ||
|
||||
changedAttributes.searchMinLength ||
|
||||
changedAttributes.searchClass ||
|
||||
changedAttributes.searchMode ||
|
||||
changedAttributes.searchCaseSensitive
|
||||
)) {
|
||||
this.applySnippets();
|
||||
|
||||
@@ -15,6 +15,7 @@ For details see: https://blog.evernote.com/tech/2013/08/08/evernote-export-forma
|
||||
"use strict";
|
||||
|
||||
// DOMParser = require("$:/plugins/tiddlywiki/xmldom/dom-parser").DOMParser;
|
||||
var illegalFilenameCharacters = /[\[\]<>;\:\"\/\\\|\?\*\^\?\$\(\)\s~]/g;
|
||||
|
||||
/*
|
||||
Parse an ENEX file into tiddlers
|
||||
@@ -23,10 +24,13 @@ exports["application/enex+xml"] = function(text,fields) {
|
||||
// Collect output tiddlers in an array
|
||||
var results = [];
|
||||
// Parse the XML document
|
||||
var parser = new DOMParser(),
|
||||
doc = parser.parseFromString(text,"application/xml");
|
||||
var doc = new DOMParser().parseFromString(text,"application/xml");
|
||||
// Output a report tiddler with information about the import
|
||||
var enex = doc.querySelector("en-export");
|
||||
if(!enex) {
|
||||
// Firefox's DOMParser have problem in some cases.
|
||||
throw new Error('Failed to parse ENEX file, no "en-export" node found, try use Chrome/Edge to export again.');
|
||||
}
|
||||
results.push({
|
||||
title: "Evernote Import Report",
|
||||
text: "Evernote file imported on " + enex.getAttribute("export-date") + " from " + enex.getAttribute("application") + " (" + enex.getAttribute("version") + ")"
|
||||
@@ -34,47 +38,102 @@ exports["application/enex+xml"] = function(text,fields) {
|
||||
// Get all the "note" nodes
|
||||
var noteNodes = doc.querySelectorAll("note");
|
||||
$tw.utils.each(noteNodes,function(noteNode) {
|
||||
var result = {
|
||||
title: getTextContent(noteNode,"title"),
|
||||
type: "text/html",
|
||||
var noteTitle = getTextContent(noteNode,"title");
|
||||
// get real note content node
|
||||
var contentNode = noteNode.querySelector("content")
|
||||
var contentText = (contentNode.textContent || "").replace(/ /g, ' ').trim();
|
||||
if(contentText) {
|
||||
// The final content will be HTML instead of xml. And we will save it as wikitext, to make wiki syntax work, and remaining HTML will also work.
|
||||
try {
|
||||
// may error if content is not valid XML
|
||||
contentNode = new DOMParser().parseFromString(contentText,"application/xml").querySelector("en-note") || contentNode;
|
||||
} catch(e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
// process main content and metadata, and save as wikitext tiddler.
|
||||
var noteResult = {
|
||||
title: noteTitle.replace(illegalFilenameCharacters,"_"),
|
||||
tags: [],
|
||||
text: getTextContent(noteNode,"content"),
|
||||
modified: convertDate(getTextContent(noteNode,"created")),
|
||||
created: convertDate(getTextContent(noteNode,"created"))
|
||||
|
||||
modified: convertDate(getTextContent(noteNode,"updated") || getTextContent(noteNode,"created")),
|
||||
modifier: getTextContent(noteNode,"author"),
|
||||
created: convertDate(getTextContent(noteNode,"created")),
|
||||
creator: getTextContent(noteNode,"author")
|
||||
};
|
||||
// process resources (images, PDFs, etc.)
|
||||
$tw.utils.each(noteNode.querySelectorAll("resource"),function(resourceNode) {
|
||||
// hash generated by applying https://github.com/vzhd1701/evernote-backup/pull/54
|
||||
var hash = resourceNode.querySelector("data").getAttribute("hash");
|
||||
var text = getTextContent(resourceNode,"data");
|
||||
var mimeType = getTextContent(resourceNode,"mime");
|
||||
var contentTypeInfo = $tw.config.contentTypeInfo[mimeType] || {extension:""};
|
||||
var title = getTextContent(resourceNode,"resource-attributes>file-name")
|
||||
// a few resources don't have title, use hash as fallback
|
||||
title = title || (hash + contentTypeInfo.extension);
|
||||
// replace all system reserved characters in title
|
||||
title = title.replace(illegalFilenameCharacters,"_");
|
||||
// prefix image title with note title, to avoid name conflicts which is quite common in web-clipped content
|
||||
title = noteResult.title + "/" + title;
|
||||
results.push({
|
||||
title: title,
|
||||
type: mimeType,
|
||||
width: getTextContent(resourceNode,"width"),
|
||||
height: getTextContent(resourceNode,"height"),
|
||||
text: text,
|
||||
// give image same modified and modifier as the note, so they can be grouped together in the "Recent"
|
||||
modified: noteResult.modified,
|
||||
modifier: noteResult.modifier,
|
||||
created: noteResult.created,
|
||||
creator: noteResult.creator
|
||||
});
|
||||
if(hash) {
|
||||
fixAttachmentReference(contentNode, hash, mimeType, title);
|
||||
}
|
||||
});
|
||||
// export mixed content of wikitext and HTML
|
||||
noteResult.text = contentNode.innerHTML;
|
||||
// remove all ` xmlns="http://www.w3.org/1999/xhtml"` attributes to save some space
|
||||
noteResult.text = noteResult.text.replace(/ xmlns="http:\/\/www.w3.org\/1999\/xhtml"/g, "");
|
||||
$tw.utils.each(noteNode.querySelectorAll("tag"),function(tagNode) {
|
||||
result.tags.push(tagNode.textContent);
|
||||
noteResult.tags.push(tagNode.textContent);
|
||||
});
|
||||
// If there's an update date, set modifiy date accordingly
|
||||
var update = getTextContent(noteNode,"updated");
|
||||
if(update) {
|
||||
result.modified = convertDate(update);
|
||||
noteResult.modified = convertDate(update);
|
||||
}
|
||||
$tw.utils.each(noteNode.querySelectorAll("note-attributes>*"),function(attrNode) {
|
||||
result[attrNode.tagName] = attrNode.textContent;
|
||||
});
|
||||
results.push(result);
|
||||
$tw.utils.each(noteNode.querySelectorAll("resource"),function(resourceNode) {
|
||||
results.push({
|
||||
title: getTextContent(resourceNode,"resource-attributes>file-name"),
|
||||
type: getTextContent(resourceNode,"mime"),
|
||||
width: getTextContent(resourceNode,"width"),
|
||||
height: getTextContent(resourceNode,"height"),
|
||||
text: getTextContent(resourceNode,"data")
|
||||
});
|
||||
noteResult[attrNode.tagName] = attrNode.textContent;
|
||||
});
|
||||
results.push(noteResult);
|
||||
});
|
||||
// Return the output tiddlers
|
||||
return results;
|
||||
};
|
||||
|
||||
function getTextContent(node,selector) {
|
||||
return (node.querySelector(selector) || {}).textContent;
|
||||
return (node.querySelector(selector) || {}).textContent || "";
|
||||
}
|
||||
|
||||
function convertDate(isoDate) {
|
||||
return (isoDate || "").replace("T","").replace("Z","") + "000"
|
||||
}
|
||||
|
||||
function fixAttachmentReference(contentNode, md5Hash, mimeType, name) {
|
||||
if(!contentNode) return;
|
||||
var mediaNode = contentNode.querySelector('en-media[hash="' + md5Hash + '"]');
|
||||
if(!name) {
|
||||
throw new Error("name is empty for resource hash" + md5Hash);
|
||||
}
|
||||
if(!mediaNode) return;
|
||||
if(mimeType.indexOf("image/") === 0) {
|
||||
// find en-media node, replace with image syntax
|
||||
mediaNode.parentNode.replaceChild($tw.utils.domMaker("p", {text: "[img["+ name + "]]"}), mediaNode);
|
||||
} else {
|
||||
// For other than image attachments, we make a link to the tiddler
|
||||
mediaNode.parentNode.replaceChild($tw.utils.domMaker("p", {text: "[["+ name + "]]"}), mediaNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})();
|
||||
|
||||
@@ -5,6 +5,7 @@ This plugin contains tool to assist migration of content from Evernote ENEX file
|
||||
!! Instructions
|
||||
|
||||
# Download or save your ENEX file from Evernote
|
||||
## Use [ext[evernote-backup|https://github.com/vzhd1701/evernote-backup]] to export ENEX file with resource hash, so images can be linked in the note
|
||||
# Rename the file to have an `.enex` extension
|
||||
# Drag the file into the TiddlyWiki browser window
|
||||
## Alternatively, click the "Import" button in the "Tools" sidebar tab
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@ describe("Wiki-based tests", function() {
|
||||
});
|
||||
|
||||
function readMultipleTiddlersTiddler(title) {
|
||||
var rawTiddlers = $tw.wiki.getTiddlerText(title).split("\n+\n");
|
||||
var rawTiddlers = $tw.wiki.getTiddlerText(title).split(/\r?\n+\r?\n/mg);
|
||||
var tiddlers = [];
|
||||
$tw.utils.each(rawTiddlers,function(rawTiddler) {
|
||||
var fields = Object.create(null),
|
||||
|
||||
90
plugins/tiddlywiki/qrcode/barcodereader.js
Normal file
90
plugins/tiddlywiki/qrcode/barcodereader.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/qrcode/barcodereader.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
barcodereader widget for reading barcodes
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var nextID = 0;
|
||||
|
||||
var BarCodeReaderWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
BarCodeReaderWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
BarCodeReaderWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
this.parentDomNode = parent;
|
||||
this.computeAttributes();
|
||||
// Make the child widgets
|
||||
this.makeChildWidgets();
|
||||
// Generate an ID for this element
|
||||
var id = "capture-widget-internal-" + nextID;
|
||||
nextID += 1;
|
||||
// Create the DOM node and render children
|
||||
var domNode = this.document.createElement("div");
|
||||
domNode.className = "tc-readcode-widget";
|
||||
domNode.setAttribute("width","300px");
|
||||
domNode.setAttribute("height","300px");
|
||||
domNode.id = id;
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
// Setup the qrcode library
|
||||
if($tw.browser) {
|
||||
var __Html5QrcodeLibrary__ = require("$:/plugins/tiddlywiki/qrcode/html5-qrcode/html5-qrcode.js").__Html5QrcodeLibrary__;
|
||||
function onScanSuccess(decodedText, decodedResult) {
|
||||
self.invokeActionString(self.getAttribute("actionsSuccess",""),self,{},{
|
||||
format: decodedResult.result.format.formatName,
|
||||
text: decodedText
|
||||
});
|
||||
console.log("Scan result",decodedResult,decodedText);
|
||||
}
|
||||
function onScanFailure(errorMessage) {
|
||||
self.invokeActionString(self.getAttribute("actionsFailure",""),self,{},{
|
||||
error: errorMessage
|
||||
});
|
||||
console.log("Scan error",errorMessage);
|
||||
}
|
||||
var html5QrcodeScanner = new __Html5QrcodeLibrary__.Html5QrcodeScanner(
|
||||
id,
|
||||
{
|
||||
fps: 10,
|
||||
qrbox: 250
|
||||
}
|
||||
);
|
||||
html5QrcodeScanner.render(onScanSuccess,onScanFailure);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
BarCodeReaderWidget.prototype.refresh = function(changedTiddlers) {
|
||||
var changedAttributes = this.computeAttributes(),
|
||||
hasChangedAttributes = $tw.utils.count(changedAttributes) > 0;
|
||||
if(hasChangedAttributes) {
|
||||
return this.refreshSelf();
|
||||
}
|
||||
return this.refreshChildren(changedTiddlers) || hasChangedAttributes;
|
||||
};
|
||||
|
||||
exports.barcodereader = BarCodeReaderWidget;
|
||||
|
||||
})();
|
||||
@@ -1,3 +0,0 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/examples
|
||||
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/MakeQR]!has[draft.of]]" "$:/plugins/tiddlywiki/qrcode/MakeGenericQR">>
|
||||
@@ -1,13 +0,0 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/readme
|
||||
|
||||
The QR code plugin provides a macro that enables any text to be rendered as a [[QR code|https://en.wikipedia.org/wiki/QR_code]]. QR codes are a type of 2-dimensional bar code that encodes arbitrary data: text, numbers, links. QR code readers are available or built-in for smartphones, making them a convenient means to transfer information between devices
|
||||
|
||||
The QR code plugin adds the following features to TiddlyWiki:
|
||||
|
||||
* A new [[makeqr Macro]] that renders specified text as a QR code image that can be displayed or printed
|
||||
* A new toolbar button that can display several QR code renderings of the content of a tiddler:
|
||||
** Raw content
|
||||
** Rendered, formatted content
|
||||
** URL of tiddler
|
||||
|
||||
The QR code plugin is based on the library [[qrcode.js by Zeno Zeng|https://github.com/zenozeng/node-yaqrcode]].
|
||||
3
plugins/tiddlywiki/qrcode/docs.tid
Normal file
3
plugins/tiddlywiki/qrcode/docs.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/docs
|
||||
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/QRCodeDocs]!has[draft.of]]" "$:/plugins/tiddlywiki/qrcode/docs/barcodereader">>
|
||||
44
plugins/tiddlywiki/qrcode/docs/barcodereader.tid
Normal file
44
plugins/tiddlywiki/qrcode/docs/barcodereader.tid
Normal file
@@ -0,0 +1,44 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/docs/barcodereader
|
||||
tags: $:/tags/QRCodeDocs
|
||||
caption: barcodereader Widget
|
||||
|
||||
The `<$barcodereader>` widget allows barcodes to be read from the device camera or from an image file. In the case of the camera, a live preview feed is shown to allow the barcode to be framed.
|
||||
|
||||
Note that for security reasons browsers restrict the operation of the camera to only work with web pages that have been loaded via HTTPS, or via localhost. Safari and Firefox allow usage from a file URI, but Chrome crashes when attempting to use the barcode reader from a file URI.
|
||||
|
||||
The `<$barcodereader>` widget has the following attributes:
|
||||
|
||||
|!Name |!Description |
|
||||
|actionsSuccess |Action string to be executed when a code is successfully decoded |
|
||||
|actionsFailure |Action string to be executed in the event of an error |
|
||||
|
||||
The following variables are passed to the ''actionsSuccess'' handler:
|
||||
|
||||
|!Name |!Description |
|
||||
|format |Barcode format (see below) |
|
||||
|text |Decoded text |
|
||||
|
||||
The following barcode formats are supported:
|
||||
|
||||
* 0: "QR_CODE"
|
||||
* 1: "AZTEC"
|
||||
* 2: "CODABAR"
|
||||
* 3: "CODE_39"
|
||||
* 4: "CODE_93"
|
||||
* 5: "CODE_128"
|
||||
* 6: "DATA_MATRIX"
|
||||
* 7: "MAXICODE"
|
||||
* 8: "ITF"
|
||||
* 9: "EAN_13"
|
||||
* 10: "EAN_8"
|
||||
* 11: "PDF_417"
|
||||
* 12: "RSS_14"
|
||||
* 13: "RSS_EXPANDED"
|
||||
* 14: "UPC_A"
|
||||
* 15: "UPC_E"
|
||||
* 16: "UPC_EAN_EXTENSION"
|
||||
|
||||
The following variables are passed to the ''actionsFailure'' handler:
|
||||
|
||||
|!Name |!Description |
|
||||
|error |Error message |
|
||||
@@ -1,8 +1,8 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/usage
|
||||
title: $:/plugins/tiddlywiki/qrcode/docs/qrcode
|
||||
tags: $:/tags/QRCodeDocs
|
||||
caption: makeqr Macro
|
||||
|
||||
! `makeqr` Macro
|
||||
|
||||
The <<.def makeqr>> [[macro|Macros]] converts text data into an image of the corresponding QR code. The image is returned as [[base64-encoded data URI|https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs]].
|
||||
The ''makeqr'' [[macro|Macros]] converts text data into an image of the corresponding QR code. The image is returned as [[base64-encoded data URI|https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs]].
|
||||
|
||||
!! Parameters
|
||||
|
||||
3
plugins/tiddlywiki/qrcode/examples.tid
Normal file
3
plugins/tiddlywiki/qrcode/examples.tid
Normal file
@@ -0,0 +1,3 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/examples
|
||||
|
||||
<<tabs "[all[shadows+tiddlers]tag[$:/tags/QRCodeExample]!has[draft.of]]" "$:/plugins/tiddlywiki/qrcode/examples/read">>
|
||||
@@ -1,4 +1,4 @@
|
||||
title: $:/plugins/tiddlywiki/qrcode/MakeContactQR
|
||||
title: $:/plugins/tiddlywiki/qrcode/make/MakeContactQR
|
||||
tags: $:/tags/MakeQR
|
||||
caption: Contact
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user