Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
/***
|Name|CommentsPlugin|
|Description|Macro for nested comments, where each comment is a separate tiddler.|
|Source|http://tiddlyguv.org/CommentsPlugin.html#CommentsPlugin|
|Documentation|http://tiddlyguv.org/CommentsPlugin.html#CommentsPluginInfo|
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
***/
/*{{{*/
if(!version.extensions.CommentsPlugin) {
version.extensions.CommentsPlugin = {installed:true};
(function(plugin) {
var cmacro = config.macros.comments = {
init: function() {
var stylesheet = store.getTiddlerText(tiddler.title + "##StyleSheet");
if (stylesheet) { // check necessary because it happens more than once for some reason
config.shadowTiddlers["StyleSheetCommentsPlugin"] = stylesheet;
store.addNotification("StyleSheetCommentsPlugin", refreshStyles);
}
if (!version.extensions.CommentsPlugin.retainViewTemplate) cmacro.enhanceViewTemplate();
},
enhanceViewTemplate: function() {
var template = config.shadowTiddlers.ViewTemplate;
if ((/commentBreadcrumb/g).test(template)) return; // already enhanced
var TITLE_DIV = "<div class='title' macro='view title'></div>";
var commentsDiv = "<div class='commentBreadcrumb' macro='commentBreadcrumb'></div>";
config.shadowTiddlers.ViewTemplate = template.replace(TITLE_DIV,commentsDiv+"\n"+TITLE_DIV);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var macroParams = paramString.parseParams();
var tiddlerParam = getParam(macroParams, "tiddler");
tiddler = tiddlerParam ? store.getTiddler(tiddlerParam) : tiddler;
if (!tiddler || !store.getTiddler(tiddler.title)) return;
cmacro.buildCommentsArea(tiddler, place, macroParams);
// cmacro.refreshCommentsFromRoot(story.getTiddler(tiddler.title).commentsEl, tiddler, macroParams);
cmacro.refreshCommentsFromRoot(place.commentsEl, tiddler, macroParams);
},
buildCommentsArea: function(rootTiddler, place, macroParams) {
var commentsArea = createTiddlyElement(place, "div", null, "comments");
var heading = getParam(macroParams, "heading");
if (heading) createTiddlyElement(commentsArea, "h1", null, null, heading);
var comments = createTiddlyElement(commentsArea, "div", null, "");
place.commentsEl = comments;
if (cmacro.editable(macroParams)) {
var newCommentArea = createTiddlyElement(commentsArea, "div", null, "newCommentArea", "New comment:");
cmacro.forceLoginIfRequired(params, newCommentArea, function() {
var newCommentEl = cmacro.makeTextArea(newCommentArea, macroParams);
// var addComment = createTiddlyElement(newCommentArea, "button", null, "addComment button", "Add Comment");
var addComment = createTiddlyButton(newCommentArea, "Add Comment", null, function() {
var comment = cmacro.createComment(newCommentEl.value, rootTiddler, macroParams);
newCommentEl.value = "";
cmacro.refreshCommentsFromRoot(comments, rootTiddler, macroParams);
}, "addComment button");
});
}
},
makeTextArea: function(container, macroParams) {
var textArea = createTiddlyElement(container, "textarea");
textArea.rows = getParam(macroParams, "textRows") || 4;
textArea.cols = getParam(macroParams, "textCols") || 20;
textArea.value = getParam(macroParams, "text") || "";
return textArea;
},
refreshCommentsFromRoot: function(rootCommentsEl, rootTiddler, macroParams) {
cmacro.treeifyComments(rootTiddler);
cmacro.refreshComments(rootCommentsEl, rootTiddler, macroParams);
},
refreshComments: function(daddyCommentsEl, tiddler, macroParams) {
var commentsEl;
if (tiddler.fields.daddy) {
var commentEl = cmacro.buildCommentEl(daddyCommentsEl, tiddler, macroParams);
daddyCommentsEl.appendChild(commentEl);
commentsEl = commentEl.commentsEl;
} else { // root element
removeChildren(daddyCommentsEl);
// refreshedEl = story.getTiddler(tiddler.title);
commentsEl = daddyCommentsEl;
}
for (var child = tiddler.firstChild; child; child = child.next) {
cmacro.refreshComments(commentsEl, child, macroParams);
}
},
// This has become more complex due to "confused comments" - multiple comments
// pointing back to the same daddy (which implies they all think they're the first
// child) or a single "2nd-last" sibling (which implies they all think they're the
// last sibling). This happens in the typical "atomic transaction 101" scenario -
// user A opens wiki, user B opens wiki, one of the users submits a comment,
// the other user submits a comment.
//
// Normally, each comment says "make my daddy's first child be me", or "make my prev
// sibling's next sibling be me". That's how the tree gets built. But to deal
// with confused comments, we now have to check if daddy/prev is already pointing
// to something. If so, we will have to walk through the list to find the right place
// for the new item.
//
// We begin by sorting by date; if we can assume we are walking through the comments by date,
// the confused comments will appear in the right order.
treeifyComments: function(rootTiddler) {
// First, clear the tree data
// We sort the comments to ensure "confused" comments
var comments = cmacro.findCommentsFromRoot(rootTiddler).sort(function(a,b) {
return a.modified > b.modified;
});
var nodes=comments.concat(rootTiddler);
for (var i=0; i<nodes.length; i++) {
delete nodes[i]["firstChild"];
delete nodes[i]["next"];
}
// Now walk through each comment
cmacro.forEach(comments, function(comment) {
var prev = comment.fields.prev;
var daddy = comment.fields.daddy;
if (prev) {
var prevTiddler = store.getTiddler(prev);
if (prevTiddler.next) {
for (var lastChild=prevTiddler.next; lastChild.next; lastChild=lastChild.next)
;
lastChild.next = comment;
// } else {
} else {
prevTiddler.next = comment;
}
} else {
var daddyTiddler = store.getTiddler(daddy);
if (daddyTiddler.firstChild) {
for (var lastChild=daddyTiddler.firstChild; lastChild.next; lastChild=lastChild.next)
;
lastChild.next = comment;
} else {
daddyTiddler.firstChild = comment;
}
}
});
for (var i=0; i<comments.length; i++) {
var c=comments.sort()[i];
}
},
logComments: function(comments) {
for (var i=0; i<comments.length; i++) {
var comment = comments[i];
}
},
findCommentsFromRoot: function(rootTiddler) {
var comments = [];
store.forEachTiddler(function(title,tiddler) {
if (tiddler.fields.root==rootTiddler.title) comments.push(tiddler);
});
return comments;
},
findChildren: function(daddyTiddler) {
var comments = [];
store.forEachTiddler(function(title,tiddler) {
if (tiddler.fields.daddy==daddyTiddler.title) comments.push(tiddler);
});
return comments;
},
buildCommentEl: function(daddyCommentsEl, comment, macroParams) {
// COMMENT ELEMENT
var commentEl = document.createElement("div");
commentEl.className = "comment";
// HEADING <- METAINFO AND DELETE
var headingEl = createTiddlyElement(commentEl, "div", null, "heading");
var metaInfoEl = createTiddlyElement(headingEl, "div", null, "commentTitle", comment.modifier + '@' + comment.modified.formatString(getParam(macroParams,"dateFormat") || "DDD, MMM DDth, YYYY hh12:0mm:0ss am"));
metaInfoEl.onclick = function() {
// story.closeAllTiddlers();
story.displayTiddler("top", comment.title, null, true);
// document.location.hash = "#" + comment.title;
};
var deleteEl = createTiddlyElement(headingEl, "div", null, "deleteComment", "X");
deleteEl.onclick = function() {
if (true || confirm("Delete this comment and all of its replies?")) {
cmacro.deleteTiddlerAndDescendents(comment);
commentEl.parentNode.removeChild(commentEl);
}
};
// TEXT
commentEl.text = createTiddlyElement(commentEl, "div", null, "commentText");
wikify(comment.text, commentEl.text);
// REPLY LINK
if (cmacro.editable(macroParams)) {
var replyLinkZone = createTiddlyElement(commentEl, "div", null, "replyLinkZone");
var replyLink = createTiddlyElement(replyLinkZone, "span", null, "replyLink", "reply to this comment");
replyLink.onclick = function() { cmacro.openReplyLink(comment, commentEl, replyLink, macroParams); };
}
// var clearance = createTiddlyElement(commentEl, "clearance", null, "clearance");
// clearance.innerHTML = " ";
// COMMENTS AREA
commentEl.commentsEl = createTiddlyElement(commentEl, "div", null, "comments");
// RETURN
return commentEl;
},
openReplyLink: function(commentTiddler, commentEl, replyLink, macroParams) {
if (commentEl.replyEl) {
commentEl.replyEl.style.display = "block";
return;
}
commentEl.replyEl = document.createElement("div");
commentEl.replyEl.className = "reply";
replyLink.style.display = "none";
var newReplyHeading = createTiddlyElement(commentEl.replyEl, "div", null, "newReply");
createTiddlyElement(newReplyHeading, "div", null, "newReplyLabel", "New Reply:");
var closeNewReply = createTiddlyElement(newReplyHeading, "div", null, "closeNewReply", "close");
closeNewReply.onclick = function() {
commentEl.replyEl.style.display = "none";
replyLink.style.display = "block";
};
cmacro.forceLoginIfRequired(params, commentEl.replyEl, function() {
var replyText = cmacro.makeTextArea(commentEl.replyEl, macroParams);
var submitReply = createTiddlyButton(commentEl.replyEl, "Reply", null, function() {
var newComment = cmacro.createComment(replyText.value, commentTiddler, macroParams);
replyText.value = "";
closeNewReply.onclick();
cmacro.refreshComments(commentEl.commentsEl, newComment, macroParams);
});
});
commentEl.insertBefore(commentEl.replyEl, commentEl.commentsEl);
},
createComment: function(text, daddy, macroParams) {
var rootTitle = daddy.fields.root ? daddy.fields.root : daddy.title;
// second case is the situation where daddy *is* root
var newComment = cmacro.createCommentTiddler(macroParams, rootTitle);
var fieldsParam = getParam(macroParams, "fields") || "";
var fields = fieldsParam.decodeHashMap();
var inheritedFields = (getParam(macroParams, "inheritedFields") || "").split(",");
cmacro.forEach(inheritedFields, function(field) {
if (field!="") fields[field] = daddy.fields[field];
});
var tagsParam = getParam(macroParams, "tags") || "comment";
var now = new Date();
newComment.set(null, text, config.options.txtUserName, now, tagsParam.split(","), now, fields);
var youngestSibling = cmacro.findYoungestChild(daddy)
if (youngestSibling) newComment.fields.prev = youngestSibling.title;
newComment.fields.daddy = daddy.title;
newComment.fields.root = rootTitle;
cmacro.saveTiddler(newComment.title);
autoSaveChanges(false);
return newComment;
},
findYoungestChild: function(daddy) {
var siblingCount = 0;
var elderSiblings = cmacro.mapize(cmacro.selectTiddlers(function(tiddler) {
isChild = (tiddler.fields.daddy==daddy.title);
if (isChild) siblingCount++;
return isChild;
}));
if (!siblingCount) return null;
// Find the only sibling that doesn't have a prev pointing at it
var youngestSiblings = cmacro.clone(elderSiblings) // as a starting point
cmacro.forEachMap(elderSiblings, function(tiddler) {
delete youngestSiblings[tiddler.fields.prev];
});
for (title in youngestSiblings) { return youngestSiblings[title]; }
},
// The recursive delete is run by a separate function (nested inside
// this one, for encapsulation purposes).
deleteTiddlerAndDescendents: function(tiddler) {
function deleteRecursively(tiddler) {
for (var child = tiddler.firstChild; child; child = child.next) {
deleteRecursively(child);
}
store.removeTiddler(tiddler.title);
}
cmacro.treeifyComments(store.getTiddler(tiddler.fields.root));
// save some info prior to deleting
var prev = tiddler.fields.prev;
var next = tiddler.next;
deleteRecursively(tiddler);
// used saved info
if (next) {
next.fields.prev = prev;
cmacro.saveTiddler(next.title);
}
autoSaveChanges(false);
},
forEach: function(list, visitor) { for (var i=0; i<list.length; i++) visitor(list[i]); },
forEachMap: function(map, visitor) { for (var key in map) visitor(map[key]); },
select: function(list, selector) {
var selection = [];
cmacro.forEach(list, function(currentItem) {
if (selector(currentItem)) { selection.push(currentItem); }
});
return selection;
},
selectTiddlers: function(selector) {
var tiddlers = [];
store.forEachTiddler(function(title, tiddler) {
var wanted = selector(tiddler);
if (wanted) tiddlers.push(tiddler);
});
return tiddlers;
},
map: function(list, mapper) {
var mapped = [];
cmacro.forEach(list, function(currentItem) { mapped.push(mapper(currentItem)); });
return mapped;
},
remove: function(list, unwantedItem) {
return cmacro.select(list,
function(currentItem) { return currentItem!=unwantedItem; });
},
mapize: function(tiddlerList) {
var map = {};
cmacro.forEach(tiddlerList, function(tiddler) { map[tiddler.title] = tiddler; });
return map;
},
clone: function(map) { return merge({}, map); },
editable: function(params) {
var editable = getParam(params, "editable");
return (!editable || editable!="false");
},
needsLogin: function(params) {
var loginCheck = getParam(params, "loginCheck");
return loginCheck && !window[loginCheck]();
},
forceLoginIfRequired: function(params, loginPromptContainer, authenticatedBlock) {
if (cmacro.needsLogin(params)) wikify("<<"+getParam(macroParams, "loginPrompt")+">>", loginPromptContainer);
else authenticatedBlock();
},
mergeReadOnly: function(first, second) {
var merged = {};
for (var field in first) { merged[field] = first[field]; }
for (var field in second) { merged[field] = second[field]; }
return merged;
},
// callers may replace this with their own ID generation algorithm
createCommentTiddler: function(macroParams, rootTitle) {
// var titleFormat = getParam(macroParams, "titleFormat") || "%root%Comment";
var prefix = rootTitle+"Comment"; // was "_comment"
if (!store.createGuidTiddler) return store.createTiddler(prefix+((new Date()).getTime()));
return store.createGuidTiddler(prefix);
},
saveTiddler: function(tiddler) {
var tiddler = (typeof(tiddler)=="string") ? store.getTiddler(tiddler) : tiddler;
store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, cmacro.mergeReadOnly(config.defaultCustomFields, tiddler.fields), false, tiddler.created)
},
log: function() { if (console && console.firebug) console.log.apply(console, arguments); },
assert: function() { if (console && console.firebug) console.assert.apply(console, arguments); },
copyFields: function(fromTiddler, toTiddler, field1, field2, fieldN) {
for (var i=2; i<arguments.length; i++) {
fieldKey = arguments[i];
if (fromTiddler.fields[fieldKey]) toTiddler.fields[fieldKey] = fromTiddler.fields[fieldKey];
}
}
}
config.macros.commentsCount = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var count = 0;
if (tiddler && store.getTiddler(tiddler.title)||paramString.length) {
var rootTiddler = paramString.length ? paramString : tiddler.title;
count = config.macros.comments.findCommentsFromRoot(store.getTiddler(rootTiddler)).length;
}
createTiddlyText(place, count);
}
},
config.macros.commentBreadcrumb = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if (!tiddler.fields.root) return;
var rootLink = createTiddlyElement(place, "span", null, null);
createTiddlyLink(rootLink, tiddler.fields.root, true);
var rootIsParent = tiddler.fields.daddy==tiddler.fields.root;
var rootIsGrandparent = (store.getTiddler(tiddler.fields.daddy)).fields.daddy==tiddler.fields.root;
if (!rootIsParent) {
if (!rootIsGrandparent) createTiddlyElement(place, "span", null, null, " > ... ");
createTiddlyElement(place, "span", null, null, " > ");
var daddyLink = createTiddlyElement(place, "span", null, null);
createTiddlyLink(daddyLink, tiddler.fields.daddy, true);
}
createTiddlyElement(place, "span", null, null, " > ");
// place.appendChild(createTiddlyLink(tiddler.fields.root));
}
}
config.macros.tiddlyWebComments = {};
config.macros.tiddlyWebComments.handler =
function(place,macroName,params,wikifier,paramString,tiddler) {
paramString = "fields:'server.workspace:bags/comments' inheritedFields:'server.host,server.type'";
config.macros.comments.handler(place,macroName,params,wikifier, paramString,tiddler);
};
function log() { if (console && console.firebug) console.log.apply(console, arguments); }
})(version.extensions.CommentsPlugin);
/***
!StyleSheet
.comments h1 { margin-bottom: 0; padding-bottom: 0; }
.comments { padding: 0; }
.comment .comments { margin-left: 1em; }
.comment { padding: 0; margin: 1em 0 0; }
.comment .comment { margin 0; }
.comment .toolbar .button { border: 0; color: #9a4; }
.comment .heading { background: [[ColorPalette::PrimaryPale]]; color: [[ColorPalette::PrimaryDark]]; border-bottom: 1px solid [[ColorPalette::PrimaryLight]]; border-right: 1px solid [[ColorPalette::PrimaryLight]]; padding: 0.5em; height: 1.3em; }
.commentTitle { float: left; }
.commentTitle:hover { text-decoration: underline; cursor: pointer; }
.commentText { clear: both; padding: 1em 1em; }
.deleteComment { float: right; cursor: pointer; text-decoration:underline; color:[[ColorPalette::SecondaryDark]]; padding-right: 0.3em; }
.comment .reply { margin-left: 1em; }
.comment .replyLink { color:[[ColorPalette::SecondaryDark]]; font-style: italic;
cursor: pointer; text-decoration: underline; margin: 0 1em; }
.comment .created { }
.comment .newReply { color:[[ColorPalette::SecondaryDark]]; margin-top: 1em; }
.newReplyLabel { float: left; }
.closeNewReply { cursor: pointer; float: right; text-decoration: underline; }
.comments textarea { width: 100%; padding: 0.3em; margin-bottom: 0.6em; }
.newCommentArea { margin-top: 0.5em; }
.clearance { clear: both; }
!(end of StyleSheet)
***/
config.macros.comments.init();
} // end of 'install only once'
/*}}}*/
// function log() { if (console && console.firebug) console.log.apply(console, arguments); }
!Usage
<pre>This plugin provides a comments macro. Usage:
<<comments>>
There are various options available, and all are optional. All the options are used in the following example:
<<comments text:"initial text" textRows:10 textCols:20 tags:specialComment,brilliant,amazing fields:"testField:done foo:bar" inheritedFields:"healthy,food,nonExistent" dateFormat:"MMM DD hh:0mm">> editable:false tiddler:cake
You may only include a single <<comments>> macro per tiddler. If you wish to include more than one, you could create new container tiddlers, each with a <<comments>> tag, and then collect them in a single tiddler using <<tiddler>> tags. (Note that this is an intrinsic limitation of the manner in which comments refer to the tiddler in which they are contained, and vice-versa. The only way to get around it would be to require the caller to include a unique ID for each comments block, and this would be virtually the same thing as the workaround mentioned here.)
The plugin also manipulates the ViewTemplate so that comments show a breacrumb (it uses a regexp to inject a breadcrumb macro that processes anything if this is a comment). To retain the original template instead, create a tiddler titled "_Tweaks", tag it "SystemConfig", and add the line: "version.extensions.CommentsPlugin.retainViewTemplate=true".
There is also a commentCount macro. Usage:
<<commentsCount>>
It simply outputs the number of comments in the current tiddler. To show the number of comments for another tiddler, use <<commentsCount tiddlerName>>.
!Usage in TiddlyWeb
There's a special additional macro included for TiddlyWeb developers:
<<tiddlyWebComments>>
which does the right thing, as long as you are happy to put all comments into a bag you have made called "comments". It effectively does the following:
<<comments fields:"server.workspace:bag/comments" inheritedFields:"server.host,server.type">>
A typical tiddlyweb setup would be for the comments bag's policy to be set with all users having only "create" permissions. All other permissions should be admin-only. This is how comments typically work on a public website - users can append, but not edit or delete.
[[Welcome]]
[[TopicsByDate]]
/***
|''Name:''|FE2|
|''Description:''|create, edit, view and delete commands in toolbar <<toolbar fields>>|
|''Version:''|1.0.2|
|''Date:''|Oct. 21,2009|
|''Source:''|http://tbGTD.tiddlyspot.com|
|''Author:''|Tobias Beer|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.5.3|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Note
This is a refactored version of [[FieldsEditorPlugin|http://visualtw.ouvaton.org/VisualTW.html#FieldsEditorPlugin]] by Pascal Collin
!Installation:
*import this tiddler, keep tagged as systemConfig, save and reload
!Code
***/
//{{{
//language settings for header, also see language options for macro.FE2 below!
config.commands.fields.lang={field:"field",actions:"actions",value:"value"};
config.commands.fields.handlePopup=function(popup,title){
addClass(popup,'FE2');
var tid=store.fetchTiddler(title);
if(!tid)return;
var t='[['+title+']]';
var fields={};
store.forEachField(tid,function(tid,field,v){fields[field]=v;},true);
var list=[];
for(var f in fields){
var e='[['+f+']]';
list.push({
field:f,
value:fields[f],
actions:"<<FE2 "+t+" rename "+e+">> <<FE2 "+t+" delete "+e+">> <<FE2 "+t+" edit "+e+">>"
});
}
list.sort(function(a,b){return a.field<b.field?-1:(a.field==b.field?0:+1);});
list.push({field:'',value:"",actions:"<<FE2 "+t+" create>>"});
var table=ListView.create(popup,list,{
columns:[
{name:'Field',field:'field',title:this.lang.field,type:'String'},
{name: 'Actions',field:'actions',title:this.lang.actions,type: 'WikiText'},
{name:'Value',field:'value',title:this.lang.value,type:'WikiText'}
],
rowClasses:[
{field:'actions'}
],
buttons:[ //can't use button for selected then delete, because click on checkbox will hide the popup
]
});
}
config.macros.FE2={
//language options
lang:{
'delete':'delete', //string since keyword!
rename:'<rename',
create:'Create a new field...',
edit:'edit>',
lineBreaks:"The field value contains linebreaks.\nEditing here will convert it to a single line!\nProceed?",
enterName:"Enter new field name...",
enterNewName:"Enter new name for '%1'...",
enterVal:"Enter field value for '%1'...",
enterNewVal:"Enter new value for '%1'..",
doDelete:"Delete field '%1' from tiddler '%2' ?",
existing:"This field already exists! "
},
handler:function(place,macroName,params,wikifier,paramString,tiddler){
if(!readOnly){
var tid=params[0];
var mode=params[1];
var field=params[2];
var btn=createTiddlyButton(place,this.lang[mode],this.lang[mode]+" "+field,this.clicked);
btn.setAttribute("tiddler",tid);
btn.setAttribute("field",field);
btn.setAttribute("mode",mode);
}
},
clicked:function(){
var lng=config.macros.FE2.lang;
var title=this.getAttribute("tiddler");
var field=this.getAttribute("field");
var mode=this.getAttribute("mode");
var tid=store.getTiddler(title);
if(!tid)return;
switch(mode){
case'create':
var first="";
do{
field=prompt(first+ lng.enterName,"");
first=lng.existing;
}while(store.getValue(tid,field));
if(field){
var v=prompt(lng.enterVal.replace(/%1/,field),"");
if(v)tid.fields[field]=v;else return;
}else return;
break;
case'delete':
if(confirm(lng.doDelete.replace(/%1/,field).replace(/%2/,title))){
delete tid.fields[field];
}else return;
break;
case'edit':
var v=tid.fields[field]||'';
if(!v.match(/\n/mg)||confirm(lng.lineBreaks)){
var v=prompt(lng.enterNewVal.replace(/%1/,field),v);
if(v||v=='')tid.fields[field]=v;else return;
}else return;
break;
case'rename':
var name=prompt(lng.enterNewName.replace(/%1/,field),field);
if(name){
tid.fields[name]=tid.fields[field];
delete tid.fields[field];
}else return;
break;
default:return;
}
store.saveTiddler(tid.title,tid.title,tid.text,tid.modifier,tid.modified,tid.tags,tid.fields);
story.refreshTiddler(title,null,true);
return false;
}
}
config.shadowTiddlers.StyleSheetFE2=
".FE2 td br{display:block;}\n"+
".FE2 td {font-size:12px;padding:1px 3px}\n"+
".FE2 .button {border:0;padding:0 0.2em;}\n"+
".FE2 .twtable,.FE2 .twtable thead, .FE2 .twtable tr{border:0}\n"+
".FE2 .twtable tr:hover{background:"+store.getTiddlerSlice('ColorPalette','TertiaryDark')+"}\n"+
".FE2 .twtable thead{font-size:13px;}";
store.addNotification("StyleSheetFE2",refreshStyles);
//}}}
/***
|Name|GotoPlugin|
|Source|http://www.TiddlyTools.com/#GotoPlugin|
|Documentation|http://www.TiddlyTools.com/#GotoPluginInfo|
|Version|1.9.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|view any tiddler by entering it's title - displays list of possible matches|
''View a tiddler by typing its title and pressing //enter//.'' As you type, a list of possible matches is displayed. You can scroll-and-click (or use arrows+enter) to select/view a tiddler, or press escape to close the listbox to resume typing. When the listbox is not displayed, pressing //escape// clears the current input.
!!!Configuration
<<<
*Match titles only after {{twochar{<<option txtIncrementalSearchMin>>}}} or more characters are entered.<br>Use down-arrow to start matching with shorter input. //Note: This option value is also set/used by [[SearchOptionsPlugin]]//.
*To set the maximum height of the listbox, you can create a tiddler tagged with <<tag systemConfig>>, containing:
//{{{
config.macros.gotoTiddler.listMaxSize=10; // change this number
//}}}
!!!Code
***/
//{{{
version.extensions.GotoPlugin= {major: 1, minor: 9, revision: 2, date: new Date(2009,5,22)};
// automatically tweak shadow SideBarOptions to add <<gotoTiddler>> macro above <<search>>
config.shadowTiddlers.SideBarOptions=config.shadowTiddlers.SideBarOptions.replace(/<<search>>/,"{{button{goto}}}\n<<gotoTiddler>><<search>>");
if (config.options.txtIncrementalSearchMin===undefined) config.options.txtIncrementalSearchMin=2;
config.macros.gotoTiddler= {
listMaxSize: 10,
listHeading: 'Found %0 matching title%1...',
searchItem: "Search for '%0'...",
handler:
function(place,macroName,params,wikifier,paramString,tiddler) {
var quiet =params.contains("quiet");
var showlist =params.contains("showlist");
var search =params.contains("search");
params = paramString.parseParams("anon",null,true,false,false);
var instyle =getParam(params,"inputstyle","");
var liststyle =getParam(params,"liststyle","");
var filter =getParam(params,"filter","");
var html=this.html;
var keyevent=window.event?"onkeydown":"onkeypress"; // IE event fixup for ESC handling
html=html.replace(/%keyevent%/g,keyevent);
html=html.replace(/%search%/g,search);
html=html.replace(/%quiet%/g,quiet);
html=html.replace(/%showlist%/g,showlist);
html=html.replace(/%display%/g,showlist?'block':'none');
html=html.replace(/%position%/g,showlist?'static':'absolute');
html=html.replace(/%instyle%/g,instyle);
html=html.replace(/%liststyle%/g,liststyle);
html=html.replace(/%filter%/g,filter);
//if (config.browser.isIE) html=this.IEtableFixup.format([html]);
var span=createTiddlyElement(place,'span');
span.innerHTML=html; var form=span.getElementsByTagName("form")[0];
if (showlist) this.fillList(form.list,'',filter,search,0);
},
html:
'<form onsubmit="return false" style="display:inline;margin:0;padding:0">\
<input name=gotoTiddler type=text value="search..." autocomplete="off" accesskey="G" style="%instyle%"\
title="ENTER=search | SHIFT+ENTER=open | DOWN=list"\
onfocus="this.select(); this.setAttribute(\'accesskey\',\'G\');"\
%keyevent%="return config.macros.gotoTiddler.inputEscKeyHandler(event,this,this.form.list,%search%,%showlist%);"\
onkeyup="return config.macros.gotoTiddler.inputKeyHandler(event,this,%quiet%,%search%,%showlist%);">\
<select name=list style="display:%display%;position:%position%;%liststyle%"\
onchange="if (!this.selectedIndex) this.selectedIndex=1;"\
onblur="this.style.display=%showlist%?\'block\':\'none\';"\
%keyevent%="return config.macros.gotoTiddler.selectKeyHandler(event,this,this.form.gotoTiddler,%showlist%);"\
onclick="return config.macros.gotoTiddler.processItem(this.value,this.form.gotoTiddler,this,%showlist%);">\
</select><input name="filter" type="hidden" value="%filter%">\
</form>',
IEtableFixup:
"<table style='width:100%;display:inline;padding:0;margin:0;border:0;'>\
<tr style='padding:0;margin:0;border:0;'><td style='padding:0;margin:0;border:0;'>\
%0</td></tr></table>",
getItems:
function(list,val,filter) {
if (!list.cache || !list.cache.length || val.length<=config.options.txtIncrementalSearchMin) {
// starting new search, fetch and cache list of tiddlers/shadows/tags
list.cache=new Array();
if (filter.length) {
var fn=store.getMatchingTiddlers||store.getTaggedTiddlers;
var tiddlers=store.sortTiddlers(fn.apply(store,[filter]),'title');
} else
var tiddlers=store.reverseLookup('tags','');
for(var t=0; t<tiddlers.length; t++) list.cache.push(tiddlers[t].title);
if (!filter.length) {
for (var t in config.shadowTiddlers) list.cache.pushUnique(t);
var tags=store.getTags();
for(var t=0; t<tags.length; t++) list.cache.pushUnique(tags[t][0]);
}
}
var found = [];
var match=val.toLowerCase();
for(var i=0; i<list.cache.length; i++)
if (list.cache[i].toLowerCase().indexOf(match)!=-1) found.push(list.cache[i]);
return found;
},
getItemSuffix:
function(t) {
if (store.tiddlerExists(t)) return ""; // tiddler
if (store.isShadowTiddler(t)) return " (shadow)"; // shadow
return " (tag)"; // tag
},
fillList:
function(list,val,filter,search,key) {
if (list.style.display=="none") return; // not visible... do nothing!
var indent='\xa0\xa0\xa0';
var found = this.getItems(list,val,filter); // find matching items...
found.sort(); // alpha by title
while (list.length > 0) list.options[0]=null; // clear list
var hdr=this.listHeading.format([found.length,found.length==1?"":"s"]);
list.options[0]=new Option(hdr,"",false,false);
for (var t=0; t<found.length; t++) list.options[list.length]=
new Option(indent+found[t]+this.getItemSuffix(found[t]),found[t],false,false);
if (search)
list.options[list.length]=new Option(this.searchItem.format([val]),"*",false,false);
list.size=(list.length<this.listMaxSize?list.length:this.listMaxSize); // resize list...
list.selectedIndex=key==38?list.length-1:key==40?1:0;
},
keyProcessed:
function(ev) { // utility function
ev.cancelBubble=true; // IE4+
try{event.keyCode=0;}catch(e){}; // IE5
if (window.event) ev.returnValue=false; // IE6
if (ev.preventDefault) ev.preventDefault(); // moz/opera/konqueror
if (ev.stopPropagation) ev.stopPropagation(); // all
return false;
},
inputEscKeyHandler:
function(event,here,list,search,showlist) {
if (event.keyCode==27) {
if (showlist) { // clear input, reset list
here.value=here.defaultValue;
this.fillList(list,'',here.form.filter.value,search,0);
}
else if (list.style.display=="none") // clear input
here.value=here.defaultValue;
else list.style.display="none"; // hide list
return this.keyProcessed(event);
}
return true; // key bubbles up
},
inputKeyHandler:
function(event,here,quiet,search,showlist) {
var key=event.keyCode;
var list=here.form.list;
var filter=here.form.filter;
// non-printing chars bubble up, except for a few:
if (key<48) switch(key) {
// backspace=8, enter=13, space=32, up=38, down=40, delete=46
case 8: case 13: case 32: case 38: case 40: case 46: break; default: return true;
}
// blank input... if down/enter... fall through (list all)... else, and hide or reset list
if (!here.value.length && !(key==40 || key==13)) {
if (showlist) this.fillList(here.form.list,'',here.form.filter.value,search,0);
else list.style.display="none";
return this.keyProcessed(event);
}
// hide list if quiet, or below input minimum (and not showlist)
list.style.display=(!showlist&&(quiet||here.value.length<config.options.txtIncrementalSearchMin))?'none':'block';
// non-blank input... enter=show/create tiddler, SHIFT-enter=search for text
if (key==13 && here.value.length) return this.processItem(event.shiftKey?here.value:'*',here,list,showlist);
// up or down key, or enter with blank input... shows and moves to list...
if (key==38 || key==40 || key==13) { list.style.display="block"; list.focus(); }
this.fillList(list,here.value,filter.value,search,key);
return true; // key bubbles up
},
selectKeyHandler:
function(event,list,editfield,showlist) {
if (event.keyCode==27) // escape... hide list, move to edit field
{ editfield.focus(); list.style.display=showlist?'block':'none'; return this.keyProcessed(event); }
if (event.keyCode==13 && list.value.length) // enter... view selected item
{ this.processItem(list.value,editfield,list,showlist); return this.keyProcessed(event); }
return true; // key bubbles up
},
processItem:
function(title,here,list,showlist) {
if (!title.length) return;
list.style.display=showlist?'block':'none';
if (title=="*") { story.search(here.value); return false; } // do full-text search
if (!showlist) here.value=title;
story.displayTiddler(null,title); // show selected tiddler
return false;
}
}
//}}}
<<importTiddlers inline>>
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.5|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Insert Javascript executable code directly into your tiddler content.|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Documentation
>see [[InlineJavascriptPluginInfo]]
!!!!!Code
***/
//{{{
version.extensions.InlineJavascriptPlugin= {major: 1, minor: 9, revision: 5, date: new Date(2009,4,11)};
config.formatters.push( {
name: "inlineJavascript",
match: "\\<script",
lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?(?: title=\\\"((?:.|\\n)*?)\\\")?(?: key=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",
handler: function(w) {
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var src=lookaheadMatch[1];
var label=lookaheadMatch[2];
var tip=lookaheadMatch[3];
var key=lookaheadMatch[4];
var show=lookaheadMatch[5];
var code=lookaheadMatch[6];
if (src) { // external script library
var script = document.createElement("script"); script.src = src;
document.body.appendChild(script); document.body.removeChild(script);
}
if (code) { // inline code
if (show) // display source in tiddler
wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
if (label) { // create 'onclick' command link
var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
var fixup=code.replace(/document.write\s*\(/gi,'place.bufferedHTML+=(');
link.code="function _out(place,tiddler){"+fixup+"\n};_out(this,this.tiddler);"
link.tiddler=w.tiddler;
link.onclick=function(){
this.bufferedHTML="";
try{ var r=eval(this.code);
if(this.bufferedHTML.length || (typeof(r)==="string")&&r.length)
var s=this.parentNode.insertBefore(document.createElement("span"),this.nextSibling);
if(this.bufferedHTML.length)
s.innerHTML=this.bufferedHTML;
if((typeof(r)==="string")&&r.length) {
wikify(r,s,null,this.tiddler);
return false;
} else return r!==undefined?r:false;
} catch(e){alert(e.description||e.toString());return false;}
};
link.setAttribute("title",tip||"");
var URIcode='javascript:void(eval(decodeURIComponent(%22(function(){try{';
URIcode+=encodeURIComponent(encodeURIComponent(code.replace(/\n/g,' ')));
URIcode+='}catch(e){alert(e.description||e.toString())}})()%22)))';
link.setAttribute("href",URIcode);
link.style.cursor="pointer";
if (key) link.accessKey=key.substr(0,1); // single character only
}
else { // run script immediately
var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
var c="function _out(place,tiddler){"+fixup+"\n};_out(w.output,w.tiddler);";
try { var out=eval(c); }
catch(e) { out=e.description?e.description:e.toString(); }
if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
}
}
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
} )
//}}}
// // Backward-compatibility for TW2.1.x and earlier
//{{{
if (typeof(wikifyPlainText)=="undefined") window.wikifyPlainText=function(text,limit,tiddler) {
if(limit > 0) text = text.substr(0,limit);
var wikifier = new Wikifier(text,formatter,null,tiddler);
return wikifier.wikifyPlain();
}
//}}}
// // GLOBAL FUNCTION: $(...) -- 'shorthand' convenience syntax for document.getElementById()
//{{{
if (typeof($)=='undefined') { function $(id) { return document.getElementById(id.replace(/^#/,'')); } }
//}}}
/***
|Name:|LessBackupsPlugin|
|Description:|Intelligently limit the number of backup files you create|
|Version:|3.0.1 ($Rev: 2320 $)|
|Date:|$Date: 2007-06-18 22:37:46 +1000 (Mon, 18 Jun 2007) $|
|Source:|http://mptw.tiddlyspot.com/#LessBackupsPlugin|
|Author:|Simon Baird|
|Email:|simon.baird@gmail.com|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Description
You end up with just backup one per year, per month, per weekday, per hour, minute, and second. So total number won't exceed about 200 or so. Can be reduced by commenting out the seconds/minutes/hours line from modes array
!!Notes
Works in IE and Firefox only. Algorithm by Daniel Baird. IE specific code by by Saq Imtiaz.
***/
//{{{
var MINS = 60 * 1000;
var HOURS = 60 * MINS;
var DAYS = 24 * HOURS;
if (!config.lessBackups) {
config.lessBackups = {
// comment out the ones you don't want or set config.lessBackups.modes in your 'tweaks' plugin
modes: [
["YYYY", 365*DAYS], // one per year for ever
["MMM", 31*DAYS], // one per month
["ddd", 7*DAYS], // one per weekday
//["d0DD", 1*DAYS], // one per day of month
["h0hh", 24*HOURS], // one per hour
//["m0mm", 1*HOURS], // one per minute
//["s0ss", 1*MINS], // one per second
["latest",0] // always keep last version. (leave this).
]
};
}
window.getSpecialBackupPath = function(backupPath) {
var now = new Date();
var modes = config.lessBackups.modes;
for (var i=0;i<modes.length;i++) {
// the filename we will try
var specialBackupPath = backupPath.replace(/(\.)([0-9]+\.[0-9]+)(\.html)$/,
'$1'+now.formatString(modes[i][0]).toLowerCase()+'$3')
// open the file
try {
if (config.browser.isIE) {
var fsobject = new ActiveXObject("Scripting.FileSystemObject")
var fileExists = fsobject.FileExists(specialBackupPath);
if (fileExists) {
var fileObject = fsobject.GetFile(specialBackupPath);
var modDate = new Date(fileObject.DateLastModified).valueOf();
}
}
else {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(specialBackupPath);
var fileExists = file.exists();
if (fileExists) {
var modDate = file.lastModifiedTime;
}
}
}
catch(e) {
// give up
return backupPath;
}
// expiry is used to tell if it's an 'old' one. Eg, if the month is June and there is a
// June file on disk that's more than an month old then it must be stale so overwrite
// note that "latest" should be always written because the expiration period is zero (see above)
var expiry = new Date(modDate + modes[i][1]);
if (!fileExists || now > expiry)
return specialBackupPath;
}
}
// hijack the core function
window.getBackupPath_mptw_orig = window.getBackupPath;
window.getBackupPath = function(localPath) {
return getSpecialBackupPath(getBackupPath_mptw_orig(localPath));
}
//}}}
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
major: 1, minor: 1, revision: 0,
date: new Date("mar 17, 2007"),
source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};
if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};
bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){
url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
}
return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
*[[Welcome]]
*[[TopicsByDate]]
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date("Apr 19, 2007"),
source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
coreVersion: '2.2.0 (Beta 5)'
};
config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");
merge(config.macros.option.types, {
'pas': {
elementType: "input",
valueField: "value",
eventName: "onkeyup",
className: "pasOptionInput",
typeValue: config.macros.option.passwordInputType,
create: function(place,type,opt,className,desc) {
// password field
config.macros.option.genericCreate(place,'pas',opt,className,desc);
// checkbox linked with this password "save this password on this computer"
config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);
// text savePasswordCheckboxLabel
place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
},
onChange: config.macros.option.genericOnChange
}
});
merge(config.optionHandlers['chk'], {
get: function(name) {
// is there an option linked with this chk ?
var opt = name.substr(3);
if (config.options[opt])
saveOptionCookie(opt);
return config.options[name] ? "true" : "false";
}
});
merge(config.optionHandlers, {
'pas': {
get: function(name) {
if (config.options["chk"+name]) {
return encodeCookie(config.options[name].toString());
} else {
return "";
}
},
set: function(name,value) {config.options[name] = decodeCookie(value);}
}
});
// need to reload options to load passwordOptions
loadOptionsCookie();
/*
if (!config.options['pasPassword'])
config.options['pasPassword'] = '';
merge(config.optionsDesc,{
pasPassword: "Test password"
});
*/
//}}}
<<gotoTiddler>><<closeAll>><<permaview>><<newTiddler>><<saveChanges>><<tiddler TspotSidebar>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
<<tabs txtMainTab Tags "All tags" TabTags Timeline Timeline TabTimeline All "All tiddlers" TabAll More "More lists" TabMore>>
lists [[topics|topic]] by their modified date...
.headerForeground, .headerShadow{padding:1em;color:white;}
.headerForeground{display:none;}
.headerShadow a{color:white;}
.noTitle .listTitle{display:none;}
|~ViewToolbar|closeTiddler closeOthers fields +editTiddler > syncing permalink references jump|
|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|
<<tiddler TopicsByDate##OUT with: {{
//CHANGE THESE OPTIONS TO YOUR LIKING
var topicTag="topic",
dtFmt='0DD.mmm.\'YY - 0hh:0mm',
headerFmt='| !Topic| !Last modified | !Comments | !By |',
itemFmt="\n| [[%0]]| %1 | <<commentsCount %0>\> |%2|",
exclude="archived",
//END OF OPTIONS
dt,out='',t,ti,tids,mod=[],topics=[],sorted=[];
tids=store.forEachTiddler(function(ti,t){
if(t.tags&&
t.tags.contains(topicTag)&&
!t.tags.containsAny(exclude.readBracketedList()))
topics[ti]=t.modified;
mod[ti]=t.modifier;
});
tids=store.forEachTiddler(function(ti,t){
var val=store.getValue(ti,'root');
if(val&&t.modified>topics[val])
topics[val]=t.modified;
mod[val]=t.modifier;
});
for (t in topics) {
if((typeof topics[t])!="function")
sorted.push({v:t,c:topics[t]});
}
sorted.sort(function (x,y) {return y.c - x.c;});
out=headerFmt;
for(t in sorted){
if((typeof topics[t])!="function"){
ti=sorted[t].v;
dt=sorted[t].c;
out+= itemFmt.format([ti,dt.formatString(dtFmt),mod[ti]]);
}
}
out;}}>>/%
!OUT
$1
!END%/
<<tiddler TopicsByDate##OUT with: {{
//CHANGE THESE OPTIONS TO YOUR LIKING
var topicTag="topic",
dtFmt='0DD.MMM',
headerFmt='!!!Recent topics',
itemFmt="\n* [[%0]] (%1, <<commentsCount %0>\> comments)",
exclude="",
//END OF OPTIONS
out='',t,tids,topics=[],sorted=[];
tids=store.forEachTiddler(function(ti,t){
if(t.tags&&
t.tags.contains(topicTag)&&
!t.tags.containsAny(exclude.readBracketedList()))
topics[ti]=t.modified;
});
tids=store.forEachTiddler(function(ti,t){
var val=store.getValue(ti,'root');
if(val&&t.modified>topics[val])
topics[val]=t.modified;
});
for (t in topics) {
if((typeof topics[t])!="function")
sorted.push({v:t,c:topics[t]});
}
sorted.sort(function (x,y) {return y.c - x.c;});
out=headerFmt;
for(t in sorted){
if((typeof topics[t])!="function"){
out+= itemFmt.format([sorted[t].v,sorted[t].c.formatString(dtFmt)]);
}
}
out;}}>>/%
!OUT
$1
!END%/
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{
// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'topicsbydate';
// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
// disable autosave in d3
if (window.location.protocol != "file:")
config.options.chkGTDLazyAutoSave = false;
// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}
// create some shadow tiddler content
merge(config.shadowTiddlers,{
'TspotOptions':[
"tiddlyspot password:",
"<<option pasUploadPassword>>",
""
].join("\n"),
'TspotControls':[
"| tiddlyspot password:|<<option pasUploadPassword>>|",
"| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
"| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),
'WelcomeToTiddlyspot':[
"This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //What now?// @@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
"<<tiddler TspotControls>>",
"See also GettingStarted.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working online// @@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// @@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Help!// @@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// @@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),
'TspotSidebar':[
"<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n")
});
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 18/09/2010 00:59:59 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 18/09/2010 01:02:37 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 18/09/2010 12:12:36 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | failed |
| 18/09/2010 12:16:28 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 18/09/2010 12:17:04 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 18/09/2010 12:20:37 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 18/09/2010 12:22:24 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 04/10/2010 17:18:07 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | failed |
| 04/10/2010 17:18:20 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . | ok |
| 04/10/2010 17:24:04 | Tobias | [[topicsbydate.html|file:///C:/xTiddle/tiddlyspot/TopicsByDate/topicsbydate.html]] | [[store.cgi|http://topicsbydate.tiddlyspot.com/store.cgi]] | . | [[index.html | http://topicsbydate.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
major: 4, minor: 1, revision: 3,
date: new Date("Feb 24, 2008"),
source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0'
};
//
// Environment
//
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false; // true to activate both in Plugin and UploadService
//
// Upload Macro
//
config.macros.upload = {
// default values
defaultBackupDir: '', //no backup
defaultStoreScript: "store.php",
defaultToFilename: "index.html",
defaultUploadDir: ".",
authenticateUser: true // UploadService Authenticate User
};
config.macros.upload.label = {
promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
promptParamMacro: "Save and Upload this TiddlyWiki in %0",
saveLabel: "save to web",
saveToDisk: "save to disk",
uploadLabel: "upload"
};
config.macros.upload.messages = {
noStoreUrl: "No store URL in parmeters or options",
usernameOrPasswordMissing: "Username or password missing"
};
config.macros.upload.handler = function(place,macroName,params) {
if (readOnly)
return;
var label;
if (document.location.toString().substr(0,4) == "http")
label = this.label.saveLabel;
else
label = this.label.uploadLabel;
var prompt;
if (params[0]) {
prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
} else {
prompt = this.label.promptOption;
}
createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};
config.macros.upload.action = function(params)
{
// for missing macro parameter set value from options
if (!params) params = {};
var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
var username = params[4] ? params[4] : config.options.txtUploadUserName;
var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
// for still missing parameter set default value
if ((!storeUrl) && (document.location.toString().substr(0,4) == "http"))
storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
if (storeUrl.substr(0,4) != "http")
storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
if (!toFilename)
toFilename = bidix.basename(window.location.toString());
if (!toFilename)
toFilename = config.macros.upload.defaultToFilename;
if (!uploadDir)
uploadDir = config.macros.upload.defaultUploadDir;
if (!backupDir)
backupDir = config.macros.upload.defaultBackupDir;
// report error if still missing
if (!storeUrl) {
alert(config.macros.upload.messages.noStoreUrl);
clearMessage();
return false;
}
if (config.macros.upload.authenticateUser && (!username || !password)) {
alert(config.macros.upload.messages.usernameOrPasswordMissing);
clearMessage();
return false;
}
bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
return false;
};
config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
{
if (!storeUrl)
return null;
var dest = bidix.dirname(storeUrl);
if (uploadDir && uploadDir != '.')
dest = dest + '/' + uploadDir;
dest = dest + '/' + toFilename;
return dest;
};
//
// uploadOptions Macro
//
config.macros.uploadOptions = {
handler: function(place,macroName,params) {
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue("listWrapper",listWrapper);
this.refreshOptions(listWrapper,false);
var uploadCaption;
if (document.location.toString().substr(0,4) == "http")
uploadCaption = config.macros.upload.label.saveLabel;
else
uploadCaption = config.macros.upload.label.uploadLabel;
wizard.setButtons([
{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
onClick: config.macros.upload.action},
{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
]);
},
options: [
"txtUploadUserName",
"pasUploadPassword",
"txtUploadStoreUrl",
"txtUploadDir",
"txtUploadFilename",
"txtUploadBackupDir",
"chkUploadLog",
"txtUploadLogMaxLine"
],
refreshOptions: function(listWrapper) {
var opts = [];
for(i=0; i<this.options.length; i++) {
var opt = {};
opts.push();
opt.option = "";
n = this.options[i];
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
opts.push(opt);
}
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n<opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h && h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
}
}
},
onCancel: function(e)
{
backstage.switchTab(null);
return false;
},
wizardTitle: "Upload with options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br>",
cancelButton: "Cancel",
cancelButtonPrompt: "Cancel prompt",
listViewTemplate: {
columns: [
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
};
//
// upload functions
//
if (!bidix.upload) bidix.upload = {};
if (!bidix.upload.messages) bidix.upload.messages = {
//from saving
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to upload backup file",
rssSaved: "RSS feed uploaded",
rssFailed: "Failed to upload RSS feed file",
emptySaved: "Empty template uploaded",
emptyFailed: "Failed to upload empty template file",
mainSaved: "Main TiddlyWiki file uploaded",
mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
//specific upload
loadOriginalHttpPostError: "Can't get original file",
aboutToSaveOnHttpPost: 'About to upload on %0 ...',
storePhpNotFound: "The store script '%0' was not found."
};
bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
var callback = function(status,uploadParams,original,url,xhr) {
if (!status) {
displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
return;
}
if (bidix.debugMode)
alert(original.substr(0,500)+"\n...");
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
bidix.upload.uploadRss(uploadParams,original,posDiv);
};
if(onlyIfDirty && !store.isDirty())
return;
clearMessage();
// save on localdisk ?
if (document.location.toString().substr(0,4) == "file") {
var path = document.location.toString();
var localPath = getLocalPath(path);
saveChanges();
}
// get original
var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
var originalPath = document.location.toString();
// If url is a directory : add index.html
if (originalPath.charAt(originalPath.length-1) == "/")
originalPath = originalPath + "index.html";
var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
var log = new bidix.UploadLog();
log.startUpload(storeUrl, dest, uploadDir, backupDir);
displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
if (bidix.debugMode)
alert("about to execute Http - GET on "+originalPath);
var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
bidix.upload.uploadRss = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
if(status) {
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
bidix.upload.uploadMain(params[0],params[1],params[2]);
} else {
displayMessage(bidix.upload.messages.rssFailed);
}
};
// do uploadRss
if(config.options.chkGenerateAnRssFeed) {
var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
var rssString = generateRss();
// no UnicodeToUTF8 conversion needed when location is "file" !!!
if (document.location.toString().substr(0,4) != "file")
rssString = convertUnicodeToUTF8(rssString);
bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
} else {
bidix.upload.uploadMain(uploadParams,original,posDiv);
}
};
bidix.upload.uploadMain = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
var log = new bidix.UploadLog();
if(status) {
// if backupDir specified
if ((params[3]) && (responseText.indexOf("backupfile:") > -1)) {
var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
}
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
store.setDirty(false);
log.endUpload("ok");
} else {
alert(bidix.upload.messages.mainFailed);
displayMessage(bidix.upload.messages.mainFailed);
log.endUpload("failed");
}
};
// do uploadMain
var revised = bidix.upload.updateOriginal(original,posDiv);
bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};
bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
var localCallback = function(status,params,responseText,url,xhr) {
url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
if (xhr.status == 404)
alert(bidix.upload.messages.storePhpNotFound.format([url]));
if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
alert(responseText);
if (responseText.indexOf("Debug mode") >= 0 )
responseText = responseText.substring(responseText.indexOf("\n\n")+2);
} else if (responseText.charAt(0) != '0')
alert(responseText);
if (responseText.charAt(0) != '0')
status = null;
callback(status,params,responseText,url,xhr);
};
// do httpUpload
var boundary = "---------------------------"+"AaB03x";
var uploadFormName = "UploadPlugin";
// compose headers data
var sheader = "";
sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
sheader += uploadFormName +"\"\r\n\r\n";
sheader += "backupDir="+uploadParams[3] +
";user=" + uploadParams[4] +
";password=" + uploadParams[5] +
";uploaddir=" + uploadParams[2];
if (bidix.debugMode)
sheader += ";debug=1";
sheader += ";;\r\n";
sheader += "\r\n" + "--" + boundary + "\r\n";
sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
sheader += "Content-Length: " + data.length + "\r\n\r\n";
// compose trailer data
var strailer = new String();
strailer = "\r\n--" + boundary + "--\r\n";
data = sheader + data + strailer;
if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
if (!posDiv)
posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
store.allTiddlersAsHtml() + "\n" +
original.substr(posDiv[1]);
var newSiteTitle = getPageTitle().htmlEncode();
revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
return revised;
};
//
// UploadLog
//
// config.options.chkUploadLog :
// false : no logging
// true : logging
// config.options.txtUploadLogMaxLine :
// -1 : no limit
// 0 : no Log lines but UploadLog is still in place
// n : the last n lines are only kept
// NaN : no limit (-1)
bidix.UploadLog = function() {
if (!config.options.chkUploadLog)
return; // this.tiddler = null
this.tiddler = store.getTiddler("UploadLog");
if (!this.tiddler) {
this.tiddler = new Tiddler();
this.tiddler.title = "UploadLog";
this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
this.tiddler.created = new Date();
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
}
return this;
};
bidix.UploadLog.prototype.addText = function(text) {
if (!this.tiddler)
return;
// retrieve maxLine when we need it
var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
if (isNaN(maxLine))
maxLine = -1;
// add text
if (maxLine != 0)
this.tiddler.text = this.tiddler.text + text;
// Trunck to maxLine
if (maxLine >= 0) {
var textArray = this.tiddler.text.split('\n');
if (textArray.length > maxLine + 1)
textArray.splice(1,textArray.length-1-maxLine);
this.tiddler.text = textArray.join('\n');
}
// update tiddler fields
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
// refresh and notifiy for immediate update
story.refreshTiddler(this.tiddler.title);
store.notify(this.tiddler.title, true);
};
bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {
if (!this.tiddler)
return;
var now = new Date();
var text = "\n| ";
var filename = bidix.basename(document.location.toString());
if (!filename) filename = '/';
text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
text += config.options.txtUserName + " | ";
text += "[["+filename+"|"+location + "]] |";
text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
text += uploadDir + " | ";
text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
text += backupDir + " |";
this.addText(text);
};
bidix.UploadLog.prototype.endUpload = function(status) {
if (!this.tiddler)
return;
this.addText(" "+status+" |");
};
//
// Utilities
//
bidix.checkPlugin = function(plugin, major, minor, revision) {
var ext = version.extensions[plugin];
if (!
(ext &&
((ext.major > major) ||
((ext.major == major) && (ext.minor > minor)) ||
((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
// write error in PluginManager
if (pluginInfo)
pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
}
};
bidix.dirname = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(0, lastpos);
} else {
return filePath.substring(0, filePath.lastIndexOf("\\"));
}
};
bidix.basename = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("#")) != -1)
filePath = filePath.substring(0, lastpos);
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(lastpos + 1);
} else
return filePath.substring(filePath.lastIndexOf("\\")+1);
};
bidix.initOption = function(name,value) {
if (!config.options[name])
config.options[name] = value;
};
//
// Initializations
//
// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);
// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");
//optionsDesc
merge(config.optionsDesc,{
txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
txtUploadUserName: "Upload Username",
pasUploadPassword: "Upload Password",
chkUploadLog: "do Logging in UploadLog (default: true)",
txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});
// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');
// Backstage
merge(config.tasks,{
uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
TopicsByDate is a [[transclusion|http://tiddlywiki.org/wiki/Transclusion]] which...
*is meant to be an extension to Michael Mahemoff's CommentsPlugin
*fetches all tiddlers intended to be commented on ...here marked [[topic]]
*sorts them by their modified date or - if comments exist - by the latest comment's modified date.
Transclude TopicsByDate via {{{<<tiddler TopicsByDate>>}}} to get...
<<tiddler TopicsByDate>>
!Example topics in this wiki
Here's a list of all tiddlers using CommentsPlugin in this ~TiddlyWiki that are tagged [[topic]]...
{{noTitle{<<tagging topic>>}}}Notice, how [[house]] is not being displayed in the above table as it is tagged <<tag archived>> and thus excluded ...see options below.
!Options
Have a look at beginning of the code of TopicsByDate for configuration options...
| !Option | !Default | !Description |
|dtFmt|{{{0DD.mmm.\'YY - 0hh:0mm}}}|date format for column {{{Last modified}}}|
|headerFmt|{{{| !Topic| !Last modified | !Comments | !By |}}}|format for the table-,list- or section-header|
|itemFmt|{{{\n| [[%0]]| %1 | <<commentsCount %0>\> |%2|}}}|format for the list-items or table-rows<br>- %0 is the topic title<br>- %1 the fetched date<br>- %2 the username for the last modification or comment|
|exclude|{{{archived}}}|bracketedList() of tags to be excluded|
!How to set options
# change the values directly in the code of TopicsByDate
# use your zzConfig Tiddler (tagged systemConfig to set your options like so:
Using the options above you could realize a whole different styling, e.g. as a list by using the following settings...
{{{
dtFmt='0DD.MMM',
headerFmt='!Recent topics',
itemFmt="\n* [[%0]] (%1, <<commentsCount %0>\> comments)",
}}}
...which result in:
<<tiddler TopicsByDateVariant>>...see [[TopicsByDateVariant]]
!Related discussion on google groups
[[Discuss TopicsByDate here...|http://groups.google.com/group/tiddlywiki/browse_frm/thread/18f219886dfdef5d/94cf7f29b8aa433f]]
!Source code
<<tiddler SHOW with: {{'{{{\n'+store.getTiddlerText('TopicsByDate')+'\n}\}\}';}}>>
Burgers. They rock. Do you like them too?
This demo auto-adds tags and fields and other goodies to the comment. Create a comment and click on its title to inspect tags and fields.
<<comments heading:"Comments With Custom Options" text:"My name:
My job:
Comments:
" textRows:10 tags:specialComment,brilliant,amazing fields:"testField:done foo:bar" inheritedFields:"healthy,food,nonExistent" dateFormat:"MMM DD hh:0mm">>
My name: carlos
My job: hunter
Comments: no gatherer
I like cake. Do you like cake too?
!Comments with default options (<<commentsCount>> comments)
<<comments editable>>
<div class="comment">
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title'>
<span class="modifier" macro='view modifier link'></span>
<span class="created" macro='view created date'>@</span>
</div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
</div>
Cake has been enriched with <<commentsCount cake>> comments, while Burger managed to attract <<commentsCount burger>> of them.
This demontrates the <<commentsCount>> macro. Given no parameters, it refers to comments in the containing tiddler (<<commentsCount>>). Given a parameter, it refers to comments in the tiddler by that name (<<commentsCount>>).
!Comments with default options (<<commentsCount>> comments)
<<comments editable>>