Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
This is the first day I am using Tiddly - and I hope it will become an incredible resource for many years to come. It is easily customized and free. Lightweight and portable and browser friendly as well.
Look for many more ideas from me, and my friend who created this!
Between yesterday and today, I built this TiddlyWiki - it was not hard at all for me because of my web design background. And although I do not think it is finished it has started for sure!
Today I finalized this site, and then created another for a business opportunity. It is my hope, that I can utilize this tool for the benefit of others, and charge them to program, design and consult. I am confident that this can and will be an important tool for many, and it is my intention to share its many uses and possibilities.
Treat it like a document and share it like a web site
manage info
set of docs to work together
information software, client list, save online and share together!
private site
public site (document sharing/download)
instruction manual online, editable and update it too
Help file site, download
TiddlyWiki worth value - application - value of me too
many uses!
This site and Tiddly is ALL ABOUT AdamRock - and all the things I think about, do or want to do. I love the internet and the internet loves me too - just sayin!
Anyway, as far as I go, if you would like to know more ABOUT me just ask - you can find my contact info on the contact link to the left of this tiddler.
If you would like to know more about TiddlyWiki PLEASE ask me or just visit the site and download a copy and have some fun. Its pretty easy to get the hang of, really very incredibly USEFUL and believe it or not, its ALL FREE too. I know, amazing right? Well, this is a true story, and I can only hope that you will participate in the journey.
This is me - no kidding, for real!
{{center{[img(50%+,auto)[http://lh4.ggpht.com/_I-6MAVwe7WY/S_m14bGJW0I/AAAAAAAAJDo/K5fwZeBNBKI/s640/P1120284.JPG]]}}}
I know this picture is crazy just by itself, but if you mouse over it, click and hold and drag either left or right, it will get smaller or larger.
Coding credit goes to http://www.tiddlytools.com, which is actually just one man, but an incredible man he is!
This is from when I went over the handle bars on my dual sport motorcycle in the U.P. Michigan, yes, it was fun which is why I am smiling, even though it hurt. If you would like to read more about this event - check out the thread http://www.meetup.com/Chicago-dual-sport/calendar/12900367/?from=list&offset=0
I do have a radio show and I am also getting into the voice over business as well, and ALL of that information can be found on my other web site @ http://www.StraightFocus.com
Thanks for visiting, and hope we can work in the future.
AdamRock
<<tiddler ContactsFormTemplate>><data>{}</data>
AdamRock is the same as Adam Warden, the creator of this site and StraightFocus.com
This would refer to me and my radio show - Adam ... .Rockin'
AdamRock(s) = a term to explain me. My name is Adam and I Rock - well, according to me anyway. I am sure there are people that do not like me, but I like to think that I Rock, so there you have it, its done, I have officially named myself .... AdamRock.
It was before my radio show, it was before I really even grew up, but I have always been, and hopefully always will be known, as AdamRock.
This is my Tiddler (my Tiddler on my TiddlyWiki) and it is like no other, mostly because I will just rant here about endless things that are constantly on my mind - not daily, but just from time to time I will update this section when I want. I may talk about my wife or kids, or it may be my radio show or my dual sport motorcycle, but whatever it is, it is going to be my tiddly or tiddler, whatever you may call it. Anyway, this is my personal TiddlyWiki, and its all for fun, please note that, seriously, but just for fun.
Hope you can see the value of this document, like I can - it really is an amazing thing of technology!
AdamRocks today, and most days :)
P.S. I have started other TiddlyWiki's as well as this one, check out http://tiddlystix.tiddlyspot.com (this will be my marketing plan, to sell this site on a memory stick, and then continuously update and program mini sites) - and I will program it as well as design for a price of course. Feel free to check out the other site and contact me anytime if you feel the need for a lightweight, portable, easily editable and search-able document with no advertising and no software needed to control it. If you use post-it notes, this could be an electronic search-able post it note. One that you will not loose, and you can keep building on them and increasing their value. Not misplace them on your desk somewhere. Then there is also http://PortaDoc.tiddlyspot.com which is another site that has a completely different design.
/%
!info
|Name|AllThumbs|
|Source|http://www.TiddlyTools.com/#AllThumbs|
|Version|1.2.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|transclusion|
|Requires|ThumbThing|
|Description|display a set of thumbnails with full-sized popup images|
Usage:
<<<
{{{
<<tiddler AllThumbs with: TiddlerName thumbsPerRow thumbWidth>>
}}}
*{{{TiddlerName}}} is the title of a tiddler containing a space-separated list of images
*{{{thumbsPerRow}}} is the number of images to show on each row of output. Omit or use 'auto' for a single row
*{{{thumbWidth, thumbHeight, fullHeight, fullWidth}}} applies CSS to scale thumbnails and popup images. Omit or use 'auto' for stretch-to-fit/full-size images
See [[ThumbThing]] for additional details
<<<
Examples:
<<<
{{{
<<tiddler AllThumbs with: [[AllThumbs##SampleList]]>>
}}}
Sample List:
<<tiddler AllThumbs##show with: {{
'{{{\n'+store.getTiddlerText('AllThumbs##SampleList')+'\n}\}\}\n';
}}>>
<<tiddler AllThumbs with: [[AllThumbs##SampleList]]>>
<<<
!end info
!SampleList
images/california.gif
images/cool_illusion.gif
AttachFileSample2
images/fish.jpg
images/sunset.jpg
!end SampleList
!show
$1
!end show
%/<<tiddler {{'$1'=='$'+'1'?'AllThumbs##info':'AllThumbs##show'}} with: {{
var list=store.getTiddlerText('$1','').readBracketedList(false);
var rowsize='$2'; if (rowsize=='$'+'2' || rowsize=='auto') rowsize=list.length;
var width='$3'; if ( width=='$'+'3' || width=='auto') width=95/rowsize+'%';
var out=[];
var thumb='<<tiddler ThumbThing with: [[%0]] [[%1]] [[%2] [[%3]] [[%4]]>\>';
for (var i=0; i<list.length; i++) {
if (i && i%rowsize==0) out.push('\n');
out.push(thumb.format([list[i],width,'$4','$5','$6']));
}
!out.length||!store.tiddlerExists('ThumbThing')?'<<tiddler AllThumbs##info>\>':out.join('');
}}>>
/***
''Name:'' Calendar plugin
''Version:'' 0.5
''Author:'' SteveRumsby
''Syntax:''
{{{<<calendar>>}}} or {{{<<calendar year>>}}} or {{{<<calendar year month>>}}} or {{{<<calendar thismonth>>}}}
''Description:''
The first form produces an full-year calendar for the current year. The second produces a full-year calendar for the given year. The third produces a single month calendar for the given month and year. The fourth form produces a single month calendar for the current month.
Weekends and holidays are highlighted (see below for how to specify holdays).
''Configuration:''
Modify this section to change the text displayed for the month and day names, to a different language for example. You can also change the format of the tiddler names linked to from each date, and the colours used.
''Changes by ELS 2005.10.30:''
config.macros.calendar.handler()
^^use "tbody" element for IE compatibility^^
^^IE returns 2005 for current year, FF returns 105... fix year adjustment accordingly^^
createCalendarDays()
^^use showDate() function (if defined) to render autostyled date with linked popup^^
calendar stylesheet definition
^^use .calendar class-specific selectors, add text centering and margin settings^^
***/
//{{{
config.macros.calendar = {};
config.macros.calendar.monthnames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.macros.calendar.daynames = ["M", "T", "W", "T", "F", "S", "S"];
config.macros.calendar.firstday = 6;
config.macros.calendar.firstweekend = 5;
config.macros.calendar.weekendbg = "#eeeebb";
config.macros.calendar.monthbg = "#770000";
config.macros.calendar.holidaybg = "#ffc0c0";
//}}}
/***
!Code section:
***/
// (you should not need to alter anything below here)//
//{{{
config.macros.calendar.tiddlerformat = "0DD/0MM/YYYY"; // This used to be changeable - for now, it isn't// <<smiley :-(>>
version.extensions.calendar = { major: 0, minor: 5, revision: 0, date: new Date(2006, 0, 11)};
config.macros.calendar.monthdays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
config.macros.calendar.holidays = [ ]; // Not sure this is required anymore - use reminders instead
//}}}
// //Is the given date a holiday?
//{{{
function calendarIsHoliday(date)
{
var longHoliday = date.formatString("0DD/0MM/YYYY");
var shortHoliday = date.formatString("0DD/0MM");
for(var i = 0; i < config.macros.calendar.holidays.length; i++) {
if(config.macros.calendar.holidays[i] == longHoliday || config.macros.calendar.holidays[i] == shortHoliday) {
return true;
}
}
return false;
}
//}}}
// //The main entry point - the macro handler.
// //Decide what sort of calendar we are creating (month or year, and which month or year)
// // Create the main calendar container and pass that to sub-ordinate functions to create the structure.
// ELS 2005.10.30: added creation and use of "tbody" for IE compatibility and fixup for year >1900//
// ELS 2005.10.30: fix year calculation for IE's getYear() function (which returns '2005' instead of '105')//
//{{{
config.macros.calendar.handler = function(place,macroName,params)
{
var calendar = createTiddlyElement(place, "table", null, "calendar", null);
var tbody = createTiddlyElement(calendar, "tbody", null, null, null);
var today = new Date();
var year = today.getYear();
if (year<1900) year+=1900;
if (params[0] == "thismonth")
createCalendarOneMonth(tbody, year, today.getMonth());
else if (params[0] == "lastmonth") {
var month = today.getMonth()-1; if (month==-1) { month=11; year--; }
createCalendarOneMonth(tbody, year, month);
}
else if (params[0] == "nextmonth") {
var month = today.getMonth()+1; if (month>11) { month=0; year++; }
createCalendarOneMonth(tbody, year, month);
}
else {
if (params[0]) year = params[0];
if(params[1])
createCalendarOneMonth(tbody, year, params[1]-1);
else
createCalendarYear(tbody, year);
}
}
//}}}
//{{{
function createCalendarOneMonth(calendar, year, mon)
{
var row = createTiddlyElement(calendar, "tr", null, "calenderMonthTitle", null);
createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, true, year, mon);
row = createTiddlyElement(calendar, "tr", null, "calendarDaysOfWeek", null);
createCalendarDayHeader(row, 1);
createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarMonth(calendar, year, mon)
{
var row = createTiddlyElement(calendar, "tr", null, null, null);
createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, false, year, mon);
row = createTiddlyElement(calendar, "tr", null, null, null);
createCalendarDayHeader(row, 1);
createCalendarDayRowsSingle(calendar, year, mon);
}
//}}}
//{{{
function createCalendarYear(calendar, year)
{
var row;
row = createTiddlyElement(calendar, "tr", null, null, null);
var back = createTiddlyElement(row, "td", null, null, null);
var backHandler = function() {
removeChildren(calendar);
createCalendarYear(calendar, year-1);
};
createTiddlyButton(back, "<", "Back", backHandler);
back.align = "center";
var yearHeader = createTiddlyElement(row, "td", null, "calendarYear", year);
yearHeader.align = "center";
yearHeader.setAttribute("colSpan", 19);
var fwd = createTiddlyElement(row, "td", null, null, null);
var fwdHandler = function() {
removeChildren(calendar);
createCalendarYear(calendar, year+1);
};
createTiddlyButton(fwd, ">", "Fwd", fwdHandler);
fwd.align = "center";
createCalendarMonthRow(calendar, year, 0);
createCalendarMonthRow(calendar, year, 3);
createCalendarMonthRow(calendar, year, 6);
createCalendarMonthRow(calendar, year, 9);
}
//}}}
//{{{
function createCalendarMonthRow(cal, year, mon)
{
var row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon], false, year, mon);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+1], false, year, mon);
createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+2], false, year, mon);
row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarDayHeader(row, 3);
createCalendarDayRows(cal, year, mon);
}
//}}}
//{{{
function createCalendarMonthHeader(cal, row, name, nav, year, mon)
{
var month;
if(nav) {
var back = createTiddlyElement(row, "td", null, null, null);
var backHandler = function() {
var newyear = year;
var newmon = mon-1;
if(newmon == -1) { newmon = 11; newyear = newyear-1;}
removeChildren(cal);
createCalendarOneMonth(cal, newyear, newmon);
};
createTiddlyButton(back, "<", "Back", backHandler);
back.align = "center";
back.style.background = config.macros.calendar.monthbg;
month = createTiddlyElement(row, "td", null, "calendarMonthname", name)
month.setAttribute("colSpan", 5);
var fwd = createTiddlyElement(row, "td", null, null, null);
var fwdHandler = function() {
var newyear = year;
var newmon = mon+1;
if(newmon == 12) { newmon = 0; newyear = newyear+1;}
removeChildren(cal);
createCalendarOneMonth(cal, newyear, newmon);
};
createTiddlyButton(fwd, ">", "Fwd", fwdHandler);
fwd.align = "center";
fwd.style.background = config.macros.calendar.monthbg;
} else {
month = createTiddlyElement(row, "td", null, "calendarMonthname", name)
month.setAttribute("colSpan", 7);
}
month.align = "center";
month.style.background = config.macros.calendar.monthbg;
}
//}}}
//{{{
function createCalendarDayHeader(row, num)
{
var cell;
for(var i = 0; i < num; i++) {
for(var j = 0; j < 7; j++) {
var d = j + config.macros.calendar.firstday;
if(d > 6) d = d - 7;
cell = createTiddlyElement(row, "td", null, null, config.macros.calendar.daynames[d]);
if(d == config.macros.calendar.firstweekend || d == config.macros.calendar.firstweekend+1)
cell.className = "calendarWeekend";
}
}
}
//}}}
//{{{
function createCalendarDays(row, col, first, max, year, mon)
{
var i;
for(i = 0; i < col; i++) {
createTiddlyElement(row, "td", null, null, null);
}
var day = first;
for(i = col; i < 7; i++) {
var d = i + config.macros.calendar.firstday;
if(d > 6) d = d - 7;
var daycell = createTiddlyElement(row, "td", null, null, null);
var isaWeekend = ((d == config.macros.calendar.firstweekend || d == (config.macros.calendar.firstweekend+1))? true:false);
if(day > 0 && day <= max) {
var celldate = new Date(year, mon, day);
// ELS 2005.10.30: use <<date>> macro's showDate() function to create popup
if (window.showDate) {
showDate(daycell,celldate,"popup","DD","DD-MMM-YYYY",true, isaWeekend);
} else {
if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;
var title = celldate.formatString(config.macros.calendar.tiddlerformat);
if(calendarIsHoliday(celldate)) {
daycell.style.background = config.macros.calendar.holidaybg;
}
if(window.findTiddlersWithReminders == null) {
var link = createTiddlyLink(daycell, title, false);
link.appendChild(document.createTextNode(day));
} else {
var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);
}
}
}
day++;
}
}
//}}}
// //We've clicked on a day in a calendar - create a suitable pop-up of options.
// //The pop-up should contain:
// // * a link to create a new entry for that date
// // * a link to create a new reminder for that date
// // * an <hr>
// // * the list of reminders for that date
//{{{
function onClickCalendarDate(e)
{
var button = this;
var date = button.getAttribute("title");
var dat = new Date(date.substr(6,4), date.substr(3,2)-1, date.substr(0, 2));
date = dat.formatString(config.macros.calendar.tiddlerformat);
var popup = createTiddlerPopup(this);
popup.appendChild(document.createTextNode(date));
var newReminder = function() {
var t = store.getTiddlers(date);
displayTiddler(null, date, 2, null, null, false, false);
if(t) {
document.getElementById("editorBody" + date).value += "\n<<reminder day:" + dat.getDate() +
" month:" + (dat.getMonth()+1) +
" year:" + (dat.getYear()+1900) + " title: >>";
} else {
document.getElementById("editorBody" + date).value = "<<reminder day:" + dat.getDate() +
" month:" + (dat.getMonth()+1) +
" year:" + (dat.getYear()+1900) + " title: >>";
}
};
var link = createTiddlyButton(popup, "New reminder", null, newReminder);
popup.appendChild(document.createElement("hr"));
var t = findTiddlersWithReminders(dat, 0, null, null);
for(var i = 0; i < t.length; i++) {
link = createTiddlyLink(popup, t[i].tiddler, false);
link.appendChild(document.createTextNode(t[i].tiddler));
}
}
//}}}
//{{{
function calendarMaxDays(year, mon)
{
var max = config.macros.calendar.monthdays[mon];
if(mon == 1 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
max++;
}
return max;
}
//}}}
//{{{
function createCalendarDayRows(cal, year, mon)
{
var row = createTiddlyElement(cal, "tr", null, null, null);
var first1 = (new Date(year, mon, 1)).getDay() -1 - config.macros.calendar.firstday;
if(first1 < 0) first1 = first1 + 7;
var day1 = -first1 + 1;
var first2 = (new Date(year, mon+1, 1)).getDay() -1 - config.macros.calendar.firstday;
if(first2 < 0) first2 = first2 + 7;
var day2 = -first2 + 1;
var first3 = (new Date(year, mon+2, 1)).getDay() -1 - config.macros.calendar.firstday;
if(first3 < 0) first3 = first3 + 7;
var day3 = -first3 + 1;
var max1 = calendarMaxDays(year, mon);
var max2 = calendarMaxDays(year, mon+1);
var max3 = calendarMaxDays(year, mon+2);
while(day1 <= max1 || day2 <= max2 || day3 <= max3) {
row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
createCalendarDays(row, 0, day2, max2, year, mon+1); day2 += 7;
createCalendarDays(row, 0, day3, max3, year, mon+2); day3 += 7;
}
}
//}}}
//{{{
function createCalendarDayRowsSingle(cal, year, mon)
{
var row = createTiddlyElement(cal, "tr", null, null, null);
var first1 = (new Date(year, mon, 1)).getDay() -1 - config.macros.calendar.firstday;
if(first1 < 0) first1 = first1+ 7;
var day1 = -first1 + 1;
var max1 = calendarMaxDays(year, mon);
while(day1 <= max1) {
row = createTiddlyElement(cal, "tr", null, null, null);
createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;
}
}
//}}}
// //ELS 2005.10.30: added styles
//{{{
setStylesheet(".calendar, .calendar table, .calendar th, .calendar tr, .calendar td { font-size:10pt; text-align:center; } .calendar { margin:0px !important; }", "calendarStyles");
//}}}
If you would like to contact me for any reason, please email me direct - Adam at StraightFocus dot com - there is a good reason I type my email address, if you would like to know why, send me an email and I will explain how you will get less spam by typing it this way.
/***
|''Name:''|DataTiddlerPlugin|
|''Version:''|1.0.6 (2006-08-26)|
|''Source:''|http://tiddlywiki.abego-software.de/#DataTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).
Such tiddler data can be used in various applications. E.g. you may create tables that collect data from various tiddlers.
''//Example: "Table with all December Expenses"//''
{{{
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
}}}
//(This assumes that expenses are stored in tiddlers tagged with "expense".)//
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
For other examples see DataTiddlerExamples.
''Access and Modify Tiddler Data''
You can "attach" data to every tiddler by assigning a JavaScript value (such as a string, boolean, number, or even arrays and compound objects) to named fields.
These values can be accessed and modified through the following Tiddler methods:
|!Method|!Example|!Description|
|{{{data(field)}}}|{{{t.data("age")}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{data(field,defaultValue)}}}|{{{t.data("isVIP",false)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{data()}}}|{{{t.data()}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{setData(field,value)}}}|{{{t.setData("age",42)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{setData(field,value,defaultValue)}}}|{{{t.setData("isVIP",flag,false)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
Alternatively you may use the following functions to access and modify the data. In this case the tiddler argument is either a tiddler or the name of a tiddler.
|!Method|!Description|
|{{{DataTiddler.getData(tiddler,field)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{DataTiddler.getData(tiddler,field,defaultValue)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{DataTiddler.getDataObject(tiddler)}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{DataTiddler.setData(tiddler,field,value)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{DataTiddler.setData(tiddler,field,value,defaultValue)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
//(For details on the various functions see the detailed comments in the source code.)//
''Data Representation in a Tiddler''
The data of a tiddler is stored as plain text in the tiddler's content/text, inside a "data" section that is framed by a {{{<data>...</data>}}} block. Inside the data section the information is stored in the [[JSON format|http://www.crockford.com/JSON/index.html]].
//''Data Section Example:''//
{{{
<data>{"isVIP":true,"user":"John Brown","age":34}</data>
}}}
The data section is not displayed when viewing the tiddler (see also "The showData Macro").
Beside the data section a tiddler may have all kind of other content.
Typically you will not access the data section text directly but use the methods given above. Nevertheless you may retrieve the text of the data section's content through the {{{DataTiddler.getDataText(tiddler)}}} function.
''Saving Changes''
The "setData" methods respect the "ForceMinorUpdate" and "AutoSave" configuration values. I.e. when "ForceMinorUpdate" is true changing a value using setData will not affect the "modifier" and "modified" attributes. With "AutoSave" set to true every setData will directly save the changes after a setData.
''Notifications''
No notifications are sent when a tiddler's data value is changed through the "setData" methods.
''Escape Data Section''
In case that you want to use the text {{{<data>}}} or {{{</data>}}} in a tiddler text you must prefix the text with a tilde ('~'). Otherwise it may be wrongly considered as the data section. The tiddler text {{{~<data>}}} is displayed as {{{<data>}}}.
''The showData Macro''
By default the data of a tiddler (that is stored in the {{{<data>...</data>}}} section of the tiddler) is not displayed. If you want to display this data you may used the {{{<<showData ...>>}}} macro:
''Syntax:''
|>|{{{<<}}}''showData '' [''JSON''] [//tiddlerName//] {{{>>}}}|
|''JSON''|By default the data is rendered as a table with a "Name" and "Value" column. When defining ''JSON'' the data is rendered in JSON format|
|//tiddlerName//|Defines the tiddler holding the data to be displayed. When no tiddler is given the tiddler containing the showData macro is used. When the tiddler name contains spaces you must quote the name (or use the {{{[[...]]}}} syntax.)|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
!Revision history
* v1.0.6 (2006-08-26)
** Removed misleading comment
* v1.0.5 (2006-02-27) (Internal Release Only)
** Internal
*** Make "JSLint" conform
* v1.0.4 (2006-02-05)
** Bugfix: showData fails in TiddlyWiki 2.0
* v1.0.3 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.2 (2005-12-22)
** Enhancements:
*** Handle texts "<data>" or "</data>" more robust when used in a tiddler text or as a field value.
*** Improved (JSON) error messages.
** Bugs fixed:
*** References are not updated when using the DataTiddler.
*** Changes to compound objects are not always saved.
*** "~</data>" is not rendered correctly (expected "</data>")
* v1.0.1 (2005-12-13)
** Features:
*** The showData macro supports an optional "tiddlername" argument to specify the tiddler containing the data to be displayed
** Bugs fixed:
*** A script immediately following a data section is deleted when the data is changed. (Thanks to GeoffS for reporting.)
* v1.0.0 (2005-12-12)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// DataTiddlerPlugin
//============================================================================
//============================================================================
// Ensure that the DataTiddler Plugin is only installed once.
//
if (!version.extensions.DataTiddlerPlugin) {
version.extensions.DataTiddlerPlugin = {
major: 1, minor: 0, revision: 6,
date: new Date(2006, 7, 26),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#DataTiddlerPlugin"
};
// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window;
if (!TiddlyWiki.prototype.getTiddler) {
TiddlyWiki.prototype.getTiddler = function(title) {
var t = this.tiddlers[title];
return (t !== undefined && t instanceof Tiddler) ? t : null;
};
}
//============================================================================
// DataTiddler Class
//============================================================================
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
function DataTiddler() {
}
DataTiddler = {
// Function to stringify a JavaScript value, producing the text for the data section content.
// (Must match the implementation of DataTiddler.parse.)
//
stringify : null,
// Function to parse the text for the data section content, producing a JavaScript value.
// (Must match the implementation of DataTiddler.stringify.)
//
parse : null
};
// Ensure access for IE
window.DataTiddler = DataTiddler;
// ---------------------------------------------------------------------------
// Data Accessor and Mutator
// ---------------------------------------------------------------------------
// Returns the value of the given data field of the tiddler.
// When no such field is defined or its value is undefined
// the defaultValue is returned.
//
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.getData = function(tiddler, field, defaultValue) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.getTiddlerDataValue(t, field, defaultValue);
};
// Sets the value of the given data field of the tiddler to
// the value. When the value is equal to the defaultValue
// no value is set (and the field is removed)
//
// Changing data of a tiddler will not trigger notifications.
//
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.setData = function(tiddler, field, value, defaultValue) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler+ "("+t+")";
}
DataTiddler.setTiddlerDataValue(t, field, value, defaultValue);
};
// Returns the data object of the tiddler, with a property for every field.
//
// The properties of the returned data object may only be read and
// not be modified. To modify the data use DataTiddler.setData(...)
// or the corresponding Tiddler method.
//
// If no data section is defined a new (empty) object is returned.
//
// @param tiddler either a tiddler name or a Tiddler
//
DataTiddler.getDataObject = function(tiddler) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.getTiddlerDataObject(t);
};
// Returns the text of the content of the data section of the tiddler.
//
// When no data section is defined for the tiddler null is returned
//
// @param tiddler either a tiddler name or a Tiddler
// @return [may be null]
//
DataTiddler.getDataText = function(tiddler) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.readDataSectionText(t);
};
// ---------------------------------------------------------------------------
// Internal helper methods (must not be used by code from outside this plugin)
// ---------------------------------------------------------------------------
// Internal.
//
// The original JSONError is not very user friendly,
// especially it does not define a toString() method
// Therefore we extend it here.
//
DataTiddler.extendJSONError = function(ex) {
if (ex.name == 'JSONError') {
ex.toString = function() {
return ex.name + ": "+ex.message+" ("+ex.text+")";
};
}
return ex;
};
// Internal.
//
// @param t a Tiddler
//
DataTiddler.getTiddlerDataObject = function(t) {
if (t.dataObject === undefined) {
var data = DataTiddler.readData(t);
t.dataObject = (data) ? data : {};
}
return t.dataObject;
};
// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.getTiddlerDataValue = function(tiddler, field, defaultValue) {
var value = DataTiddler.getTiddlerDataObject(tiddler)[field];
return (value === undefined) ? defaultValue : value;
};
// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.setTiddlerDataValue = function(tiddler, field, value, defaultValue) {
var data = DataTiddler.getTiddlerDataObject(tiddler);
var oldValue = data[field];
if (value == defaultValue) {
if (oldValue !== undefined) {
delete data[field];
DataTiddler.save(tiddler);
}
return;
}
data[field] = value;
DataTiddler.save(tiddler);
};
// Internal.
//
// Reads the data section from the tiddler's content and returns its text
// (as a String).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readDataSectionText = function(tiddler) {
var matches = DataTiddler.getDataTiddlerMatches(tiddler);
if (matches === null || !matches[2]) {
return null;
}
return matches[2];
};
// Internal.
//
// Reads the data section from the tiddler's content and returns it
// (as an internalized object).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readData = function(tiddler) {
var text = DataTiddler.readDataSectionText(tiddler);
try {
return text ? DataTiddler.parse(text) : null;
} catch(ex) {
throw DataTiddler.extendJSONError(ex);
}
};
// Internal.
//
// Returns the serialized text of the data of the given tiddler, as it
// should be stored in the data section.
//
// @param tiddler a Tiddler
//
DataTiddler.getDataTextOfTiddler = function(tiddler) {
var data = DataTiddler.getTiddlerDataObject(tiddler);
return DataTiddler.stringify(data);
};
// Internal.
//
DataTiddler.indexOfNonEscapedText = function(s, subString, startIndex) {
var index = s.indexOf(subString, startIndex);
while ((index > 0) && (s[index-1] == '~')) {
index = s.indexOf(subString, index+1);
}
return index;
};
// Internal.
//
DataTiddler.getDataSectionInfo = function(text) {
// Special care must be taken to handle "<data>" and "</data>" texts inside
// a data section.
// Also take care not to use an escaped <data> (i.e. "~<data>") as the start
// of a data section. (Same for </data>)
// NOTE: we are explicitly searching for a data section that contains a JSON
// string, i.e. framed with braces. This way we are little bit more robust in
// case the tiddler contains unescaped texts "<data>" or "</data>". This must
// be changed when using a different stringifier.
var startTagText = "<data>{";
var endTagText = "}</data>";
var startPos = 0;
// Find the first not escaped "<data>".
var startDataTagIndex = DataTiddler.indexOfNonEscapedText(text, startTagText, 0);
if (startDataTagIndex < 0) {
return null;
}
// Find the *last* not escaped "</data>".
var endDataTagIndex = text.indexOf(endTagText, startDataTagIndex);
if (endDataTagIndex < 0) {
return null;
}
var nextEndDataTagIndex;
while ((nextEndDataTagIndex = text.indexOf(endTagText, endDataTagIndex+1)) >= 0) {
endDataTagIndex = nextEndDataTagIndex;
}
return {
prefixEnd: startDataTagIndex,
dataStart: startDataTagIndex+(startTagText.length)-1,
dataEnd: endDataTagIndex,
suffixStart: endDataTagIndex+(endTagText.length)
};
};
// Internal.
//
// Returns the "matches" of a content of a DataTiddler on the
// "data" regular expression. Return null when no data is defined
// in the tiddler content.
//
// Group 1: text before data section (prefix)
// Group 2: content of data section
// Group 3: text behind data section (suffix)
//
// @param tiddler a Tiddler
// @return [may be null] null when the tiddler contains no data section, otherwise see above.
//
DataTiddler.getDataTiddlerMatches = function(tiddler) {
var text = tiddler.text;
var info = DataTiddler.getDataSectionInfo(text);
if (!info) {
return null;
}
var prefix = text.substr(0,info.prefixEnd);
var data = text.substr(info.dataStart, info.dataEnd-info.dataStart+1);
var suffix = text.substr(info.suffixStart);
return [text, prefix, data, suffix];
};
// Internal.
//
// Saves the data in a <data> block of the given tiddler (as a minor change).
//
// The "chkAutoSave" and "chkForceMinorUpdate" options are respected.
// I.e. the TiddlyWiki *file* is only saved when AutoSave is on.
//
// Notifications are not send.
//
// This method should only be called when the data really has changed.
//
// @param tiddler
// the tiddler to be saved.
//
DataTiddler.save = function(tiddler) {
var matches = DataTiddler.getDataTiddlerMatches(tiddler);
var prefix;
var suffix;
if (matches === null) {
prefix = tiddler.text;
suffix = "";
} else {
prefix = matches[1];
suffix = matches[3];
}
var dataText = DataTiddler.getDataTextOfTiddler(tiddler);
var newText =
(dataText !== null)
? prefix + "<data>" + dataText + "</data>" + suffix
: prefix + suffix;
if (newText != tiddler.text) {
// make the change in the tiddlers text
// ... see DataTiddler.MyTiddlerChangedFunction
tiddler.isDataTiddlerChange = true;
// ... do the action change
tiddler.set(
tiddler.title,
newText,
config.options.txtUserName,
config.options.chkForceMinorUpdate? undefined : new Date(),
tiddler.tags);
// ... see DataTiddler.MyTiddlerChangedFunction
delete tiddler.isDataTiddlerChange;
// Mark the store as dirty.
store.dirty = true;
// AutoSave if option is selected
if(config.options.chkAutoSave) {
saveChanges();
}
}
};
// Internal.
//
DataTiddler.MyTiddlerChangedFunction = function() {
// Remove the data object from the tiddler when the tiddler is changed
// by code other than DataTiddler code.
//
// This is necessary since the data object is just a "cached version"
// of the data defined in the data section of the tiddler and the
// "external" change may have changed the content of the data section.
// Thus we are not sure if the data object reflects the data section
// contents.
//
// By deleting the data object we ensure that the data object is
// reconstructed the next time it is needed, with the data defined by
// the data section in the tiddler's text.
// To indicate that a change is a "DataTiddler change" a temporary
// property "isDataTiddlerChange" is added to the tiddler.
if (this.dataObject && !this.isDataTiddlerChange) {
delete this.dataObject;
}
// call the original code.
DataTiddler.originalTiddlerChangedFunction.apply(this, arguments);
};
//============================================================================
// Formatters
//============================================================================
// This formatter ensures that "~<data>" is rendered as "<data>". This is used to
// escape the "<data>" of a data section, just in case someone really wants to use
// "<data>" as a text in a tiddler and not start a data section.
//
// Same for </data>.
//
config.formatters.push( {
name: "data-escape",
match: "~<\\/?data>",
handler: function(w) {
w.outputText(w.output,w.matchStart + 1,w.nextMatch);
}
} );
// This formatter ensures that <data>...</data> sections are not rendered.
//
config.formatters.push( {
name: "data",
match: "<data>",
handler: function(w) {
var info = DataTiddler.getDataSectionInfo(w.source);
if (info && info.prefixEnd == w.matchStart) {
w.nextMatch = info.suffixStart;
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
}
} );
//============================================================================
// Tiddler Class Extension
//============================================================================
// "Hijack" the changed method ---------------------------------------------------
DataTiddler.originalTiddlerChangedFunction = Tiddler.prototype.changed;
Tiddler.prototype.changed = DataTiddler.MyTiddlerChangedFunction;
// Define accessor methods -------------------------------------------------------
// Returns the value of the given data field of the tiddler. When no such field
// is defined or its value is undefined the defaultValue is returned.
//
// When field is undefined (or null) the data object is returned. (See
// DataTiddler.getDataObject.)
//
// @param field [may be null, undefined]
// @param defaultValue [may be null, undefined]
// @return [may be null, undefined]
//
Tiddler.prototype.data = function(field, defaultValue) {
return (field)
? DataTiddler.getTiddlerDataValue(this, field, defaultValue)
: DataTiddler.getTiddlerDataObject(this);
};
// Sets the value of the given data field of the tiddler to the value. When the
// value is equal to the defaultValue no value is set (and the field is removed).
//
// @param value [may be null, undefined]
// @param defaultValue [may be null, undefined]
//
Tiddler.prototype.setData = function(field, value, defaultValue) {
DataTiddler.setTiddlerDataValue(this, field, value, defaultValue);
};
//============================================================================
// showData Macro
//============================================================================
config.macros.showData = {
// Standard Properties
label: "showData",
prompt: "Display the values stored in the data section of the tiddler"
};
config.macros.showData.handler = function(place,macroName,params) {
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the optional "JSON"
var showInJSONFormat = false;
if ((i < params.length) && params[i] == "JSON") {
i++;
showInJSONFormat = true;
}
var tiddlerName = story.findContainingTiddler(place).id.substr(7);
if (i < params.length) {
tiddlerName = params[i];
i++;
}
// --- Processing ------------------------------------------
try {
if (showInJSONFormat) {
this.renderDataInJSONFormat(place, tiddlerName);
} else {
this.renderDataAsTable(place, tiddlerName);
}
} catch (e) {
this.createErrorElement(place, e);
}
};
config.macros.showData.renderDataInJSONFormat = function(place,tiddlerName) {
var text = DataTiddler.getDataText(tiddlerName);
if (text) {
createTiddlyElement(place,"pre",null,null,text);
}
};
config.macros.showData.renderDataAsTable = function(place,tiddlerName) {
var text = "|!Name|!Value|\n";
var data = DataTiddler.getDataObject(tiddlerName);
if (data) {
for (var i in data) {
var value = data[i];
text += "|"+i+"|"+DataTiddler.stringify(value)+"|\n";
}
}
wikify(text, place);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.showData.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"showDataError","<<showData ...>>: "+message);
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".showDataError{color: #ffffff;background-color: #880000;}",
"showData");
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... TiddlyWiki Core
/*global createTiddlyElement, saveChanges, store, story, wikify */
// ... DataTiddler
/*global DataTiddler */
// ... JSON
/*global JSON */
/***
!JSON Code, used to serialize the data
***/
/*
Copyright (c) 2005 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
The global object JSON contains two methods.
JSON.stringify(value) takes a JavaScript value and produces a JSON text.
The value must not be cyclical.
JSON.parse(text) takes a JSON text and produces a JavaScript value. It will
throw a 'JSONError' exception if there is an error.
*/
var JSON = {
copyright: '(c)2005 JSON.org',
license: 'http://www.crockford.com/JSON/license.html',
/*
Stringify a JavaScript value, producing a JSON text.
*/
stringify: function (v) {
var a = [];
/*
Emit a string.
*/
function e(s) {
a[a.length] = s;
}
/*
Convert a value.
*/
function g(x) {
var c, i, l, v;
switch (typeof x) {
case 'object':
if (x) {
if (x instanceof Array) {
e('[');
l = a.length;
for (i = 0; i < x.length; i += 1) {
v = x[i];
if (typeof v != 'undefined' &&
typeof v != 'function') {
if (l < a.length) {
e(',');
}
g(v);
}
}
e(']');
return;
} else if (typeof x.toString != 'undefined') {
e('{');
l = a.length;
for (i in x) {
v = x[i];
if (x.hasOwnProperty(i) &&
typeof v != 'undefined' &&
typeof v != 'function') {
if (l < a.length) {
e(',');
}
g(i);
e(':');
g(v);
}
}
return e('}');
}
}
e('null');
return;
case 'number':
e(isFinite(x) ? +x : 'null');
return;
case 'string':
l = x.length;
e('"');
for (i = 0; i < l; i += 1) {
c = x.charAt(i);
if (c >= ' ') {
if (c == '\\' || c == '"') {
e('\\');
}
e(c);
} else {
switch (c) {
case '\b':
e('\\b');
break;
case '\f':
e('\\f');
break;
case '\n':
e('\\n');
break;
case '\r':
e('\\r');
break;
case '\t':
e('\\t');
break;
default:
c = c.charCodeAt();
e('\\u00' + Math.floor(c / 16).toString(16) +
(c % 16).toString(16));
}
}
}
e('"');
return;
case 'boolean':
e(String(x));
return;
default:
e('null');
return;
}
}
g(v);
return a.join('');
},
/*
Parse a JSON text, producing a JavaScript value.
*/
parse: function (text) {
var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,
token,
operator;
function error(m, t) {
throw {
name: 'JSONError',
message: m,
text: t || operator || token
};
}
function next(b) {
if (b && b != operator) {
error("Expected '" + b + "'");
}
if (text) {
var t = p.exec(text);
if (t) {
if (t[2]) {
token = null;
operator = t[2];
} else {
operator = null;
try {
token = eval(t[1]);
} catch (e) {
error("Bad token", t[1]);
}
}
text = text.substring(t[0].length);
} else {
error("Unrecognized token", text);
}
} else {
token = operator = undefined;
}
}
function val() {
var k, o;
switch (operator) {
case '{':
next('{');
o = {};
if (operator != '}') {
for (;;) {
if (operator || typeof token != 'string') {
error("Missing key");
}
k = token;
next();
next(':');
o[k] = val();
if (operator != ',') {
break;
}
next(',');
}
}
next('}');
return o;
case '[':
next('[');
o = [];
if (operator != ']') {
for (;;) {
o.push(val());
if (operator != ',') {
break;
}
next(',');
}
}
next(']');
return o;
default:
if (operator !== null) {
error("Missing value");
}
k = token;
next();
return k;
}
}
next();
return val();
}
};
/***
!Setup the data serialization
***/
DataTiddler.format = "JSON";
DataTiddler.stringify = JSON.stringify;
DataTiddler.parse = JSON.parse;
//}}}
[[AdamRocks Tiddly]]
[[Home]]
[[About]]
[[Contact]]
/***
<<checkForDataTiddlerPlugin>>
|''Name:''|FormTiddlerPlugin|
|''Version:''|1.0.6 (2007-06-24)|
|''Source:''|http://tiddlywiki.abego-software.de/#FormTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''Macros:''|formTiddler, checkForDataTiddlerPlugin, newTiddlerWithForm|
|''Requires:''|DataTiddlerPlugin|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Use form-based tiddlers to enter your tiddler data using text fields, listboxes, checkboxes etc. (All standard HTML Form input elements supported).
''Syntax:''
|>|{{{<<}}}''formTiddler'' //tiddlerName//{{{>>}}}|
|//tiddlerName//|The name of the FormTemplate tiddler to be used to edit the data of the tiddler containing the macro.|
|>|{{{<<}}}''newTiddlerWithForm'' //formTemplateName// //buttonLabel// [//titleExpression// [''askUser'']] {{{>>}}}|
|//formTemplateName//|The name of the tiddler that defines the form the new tiddler should use.|
|//buttonLabel//|The label of the button|
|//titleExpression//|A (quoted) JavaScript String expression that defines the title (/name) of the new tiddler.|
|''askUser''|Typically the user is not asked for the title when a title is specified (and not yet used). When ''askUser'' is given the user will be asked in any case. This may be used when the calculated title is just a suggestion that must be confirmed by the user|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
For details and how to use the macros see the [[introduction|FormTiddler Introduction]] and the [[examples|FormTiddler Examples]].
!Revision history
* v1.0.6 (2007-06-24)
** Fixed problem when using SELECT component in Internet Explorer (thanks to MaikBoenig for reporting)
* v1.0.5 (2006-02-24)
** Removed "debugger;" instruction
* v1.0.4 (2006-02-07)
** Bug: On IE no data is written to data section when field values changed (thanks to KenGirard for reporting)
* v1.0.3 (2006-02-05)
** Bug: {{{"No form template specified in <<formTiddler>>"}}} when using formTiddler macro on InternetExplorer (thanks to KenGirard for reporting)
* v1.0.2 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.1 (2005-12-22)
** Features:
*** Support InternetExplorer
*** Added newTiddlerWithForm Macro
* v1.0.0 (2005-12-14)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// FormTiddlerPlugin
//============================================================================
//============================================================================
if (!window.abego) window.abego = {};
abego.getOptionsValue = function(element,i) {
var v = element.options[i].value;
if (!v && element.options[i].text)
v = element.options[i].text;
return v;
};
version.extensions.FormTiddlerPlugin = {
major: 1, minor: 0, revision: 5,
date: new Date(2006, 2, 24),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#FormTiddlerPlugin"
};
// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window;
if (!TiddlyWiki.prototype.getTiddler) TiddlyWiki.prototype.getTiddler = function(title) { return t = this.tiddlers[title]; return (t != undefined && t instanceof Tiddler) ? t : null; }
//============================================================================
// formTiddler Macro
//============================================================================
// -------------------------------------------------------------------------------
// Configurations and constants
// -------------------------------------------------------------------------------
config.macros.formTiddler = {
// Standard Properties
label: "formTiddler",
version: {major: 1, minor: 0, revision: 4, date: new Date(2006, 2, 7)},
prompt: "Edit tiddler data using forms",
// Define the "setters" that set the values of INPUT elements of a given type
// (must match the corresponding "getter")
setter: {
button: function(e, value) {/*contains no data */ },
checkbox: function(e, value) {e.checked = value;},
file: function(e, value) {try {e.value = value;} catch(e) {/* ignore, possibly security error*/}},
hidden: function(e, value) {e.value = value;},
password: function(e, value) {e.value = value;},
radio: function(e, value) {e.checked = (e.value == value);},
reset: function(e, value) {/*contains no data */ },
"select-one": function(e, value) {config.macros.formTiddler.setSelectOneValue(e,value);},
"select-multiple": function(e, value) {config.macros.formTiddler.setSelectMultipleValue(e,value);},
submit: function(e, value) {/*contains no data */},
text: function(e, value) {e.value = value;},
textarea: function(e, value) {e.value = value;}
},
// Define the "getters" that return the value of INPUT elements of a given type
// Return undefined to not store any data.
getter: {
button: function(e, value) {return undefined;},
checkbox: function(e, value) {return e.checked;},
file: function(e, value) {return e.value;},
hidden: function(e, value) {return e.value;},
password: function(e, value) {return e.value;},
radio: function(e, value) {return e.checked ? e.value : undefined;},
reset: function(e, value) {return undefined;},
"select-one": function(e, value) {return config.macros.formTiddler.getSelectOneValue(e);},
"select-multiple": function(e, value) {return config.macros.formTiddler.getSelectMultipleValue(e);},
submit: function(e, value) {return undefined;},
text: function(e, value) {return e.value;},
textarea: function(e, value) {return e.value;}
}
};
// -------------------------------------------------------------------------------
// The formTiddler Macro Handler
// -------------------------------------------------------------------------------
config.macros.formTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (!config.macros.formTiddler.checkForExtensions(place, macroName)) {
return;
}
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// get the name of the form template tiddler
var formTemplateName = undefined;
if (i < params.length) {
formTemplateName = params[i];
i++;
}
if (!formTemplateName) {
config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
return;
}
// --- Processing ------------------------------------------
// Get the form template text.
// (This contains the INPUT elements for the form.)
var formTemplateTiddler = store.getTiddler(formTemplateName);
if (!formTemplateTiddler) {
config.macros.formTiddler.createErrorElement(place, "Form template '" + formTemplateName + "' not found.");
return;
}
var templateText = formTemplateTiddler.text;
if(!templateText) {
// Shortcut: when template text is empty we do nothing.
return;
}
// Get the name of the tiddler containing this "formTiddler" macro
// (i.e. the tiddler, that will be edited and that contains the data)
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
// Append a "form" element.
var formName = "form"+formTemplateName+"__"+tiddlerName;
var e = document.createElement("form");
e.setAttribute("name", formName);
place.appendChild(e);
// "Embed" the elements defined by the templateText (i.e. the INPUT elements)
// into the "form" element we just created
wikify(templateText, e);
// Initialize the INPUT elements.
config.macros.formTiddler.initValuesAndHandlersInFormElements(formName, DataTiddler.getDataObject(tiddlerName));
}
// -------------------------------------------------------------------------------
// Form Data Access
// -------------------------------------------------------------------------------
// Internal.
//
// Initialize the INPUT elements of the form with the values of their "matching"
// data fields in the tiddler. Also setup the onChange handler to ensure that
// changes in the INPUT elements are stored in the tiddler's data.
//
config.macros.formTiddler.initValuesAndHandlersInFormElements = function(formName, data) {
// config.macros.formTiddler.trace("initValuesAndHandlersInFormElements(formName="+formName+", data="+data+")");
// find the form
var form = config.macros.formTiddler.findForm(formName);
if (!form) {
return;
}
try {
var elems = form.elements;
for (var i = 0; i < elems.length; i++) {
var c = elems[i];
var setter = config.macros.formTiddler.setter[c.type];
if (setter) {
var value = data[c.name];
if (value != null) {
setter(c, value);
}
c.onchange = onFormTiddlerChange;
} else {
config.macros.formTiddler.displayFormTiddlerError("No setter defined for INPUT element of type '"+c.type+"'. (Element '"+c.name+"' in form '"+formName+"')");
}
}
} catch(e) {
config.macros.formTiddler.displayFormTiddlerError("Error when updating elements with new formData. "+e);
}
}
// Internal.
//
// @return [may be null]
//
config.macros.formTiddler.findForm = function(formName) {
// We must manually iterate through the document's forms, since
// IE does not support the "document[formName]" approach
var forms = window.document.forms;
for (var i = 0; i < forms.length; i++) {
var form = forms[i];
if (form.name == formName) {
return form;
}
}
return null;
}
// Internal.
//
config.macros.formTiddler.setSelectOneValue = function(element,value) {
var n = element.options.length;
for (var i = 0; i < n; i++) {
element.options[i].selected = abego.getOptionsValue(element,i) == value;
}
}
// Internal.
//
config.macros.formTiddler.setSelectMultipleValue = function(element,value) {
var values = {};
for (var i = 0; i < value.length; i++) {
values[value[i]] = true;
}
var n = element.length;
for (var i = 0; i < n; i++) {
element.options[i].selected = !(!values[abego.getOptionsValue(element,i)]);
}
}
// Internal.
//
config.macros.formTiddler.getSelectOneValue = function(element) {
var i = element.selectedIndex;
return (i >= 0) ? abego.getOptionsValue(element,i) : null;
}
// Internal.
//
config.macros.formTiddler.getSelectMultipleValue = function(element) {
var values = [];
var n = element.length;
for (var i = 0; i < n; i++) {
if (element.options[i].selected) {
values.push(abego.getOptionsValue(element,i));
}
}
return values;
}
// -------------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------------
// Internal.
//
config.macros.formTiddler.checkForExtensions = function(place,macroName) {
if (!version.extensions.DataTiddlerPlugin) {
config.macros.formTiddler.createErrorElement(place, "<<" + macroName + ">> requires the DataTiddlerPlugin. (You can get it from http://tiddlywiki.abego-software.de/#DataTiddlerPlugin)");
return false;
}
return true;
}
// Internal.
//
// Displays a trace message in the "TiddlyWiki" message pane.
// (used for debugging)
//
config.macros.formTiddler.trace = function(s) {
displayMessage("Trace: "+s);
}
// Internal.
//
// Display some error message in the "TiddlyWiki" message pane.
//
config.macros.formTiddler.displayFormTiddlerError = function(s) {
alert("FormTiddlerPlugin Error: "+s);
}
// Internal.
//
// Creates an element that holds an error message
//
config.macros.formTiddler.createErrorElement = function(place, message) {
return createTiddlyElement(place,"span",null,"formTiddlerError",message);
}
// Internal.
//
// Returns the name of the tiddler containing the given element.
//
config.macros.formTiddler.getContainingTiddlerName = function(element) {
return story.findContainingTiddler(element).id.substr(7);
}
// -------------------------------------------------------------------------------
// Event Handlers
// -------------------------------------------------------------------------------
// This function must be called by the INPUT elements whenever their
// data changes. Typically this is done through an "onChange" handler.
//
function onFormTiddlerChange (e) {
// config.macros.formTiddler.trace("onFormTiddlerChange "+e);
if (!e) var e = window.event;
var target = resolveTarget(e);
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(target);
var getter = config.macros.formTiddler.getter[target.type];
if (getter) {
var value = getter(target);
DataTiddler.setData(tiddlerName, target.name, value);
} else {
config.macros.formTiddler.displayFormTiddlerError("No getter defined for INPUT element of type '"+target.type+"'. (Element '"+target.name+"' used in tiddler '"+tiddlerName+"')");
}
}
// ensure that the function can be used in HTML event handler
window.onFormTiddlerChange = onFormTiddlerChange;
// -------------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// -------------------------------------------------------------------------------
setStylesheet(
".formTiddlerError{color: #ffffff;background-color: #880000;}",
"formTiddler");
//============================================================================
// checkForDataTiddlerPlugin Macro
//============================================================================
config.macros.checkForDataTiddlerPlugin = {
// Standard Properties
label: "checkForDataTiddlerPlugin",
version: {major: 1, minor: 0, revision: 0, date: new Date(2005, 12, 14)},
prompt: "Check if the DataTiddlerPlugin exists"
}
config.macros.checkForDataTiddlerPlugin.handler = function(place,macroName,params) {
config.macros.formTiddler.checkForExtensions(place, config.macros.formTiddler.label);
}
//============================================================================
// newTiddlerWithForm Macro
//============================================================================
config.macros.newTiddlerWithForm = {
// Standard Properties
label: "newTiddlerWithForm",
version: {major: 1, minor: 0, revision: 1, date: new Date(2006, 1, 6)},
prompt: "Creates a new Tiddler with a <<formTiddler ...>> macro"
}
config.macros.newTiddlerWithForm.handler = function(place,macroName,params) {
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// get the name of the form template tiddler
var formTemplateName = undefined;
if (i < params.length) {
formTemplateName = params[i];
i++;
}
if (!formTemplateName) {
config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
return;
}
// get the button label
var buttonLabel = undefined;
if (i < params.length) {
buttonLabel = params[i];
i++;
}
if (!buttonLabel) {
config.macros.formTiddler.createErrorElement(place, "No button label specified in <<" + macroName + ">>.");
return;
}
// get the (optional) tiddlerName script and "askUser"
var tiddlerNameScript = undefined;
var askUser = false;
if (i < params.length) {
tiddlerNameScript = params[i];
i++;
if (i < params.length && params[i] == "askUser") {
askUser = true;
i++;
}
}
// --- Processing ------------------------------------------
if(!readOnly) {
var onClick = function() {
var tiddlerName;
if (tiddlerNameScript) {
try {
tiddlerName = eval(tiddlerNameScript);
} catch (ex) {
}
}
if (!tiddlerName || askUser) {
tiddlerName = prompt("Please specify a tiddler name.", askUser ? tiddlerName : "");
}
while (tiddlerName && store.getTiddler(tiddlerName)) {
tiddlerName = prompt("A tiddler named '"+tiddlerName+"' already exists.\n\n"+"Please specify a tiddler name.", tiddlerName);
}
// tiddlerName is either null (user canceled) or a name that is not yet in the store.
if (tiddlerName) {
var body = "<<formTiddler [["+formTemplateName+"]]>>";
var tags = [];
store.saveTiddler(tiddlerName,tiddlerName,body,config.options.txtUserName,new Date(),tags);
story.displayTiddler(null,tiddlerName,1);
}
}
createTiddlyButton(place,buttonLabel,buttonLabel,onClick);
}
}
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
/%
!info
|Name|GetTheFAQs|
|Source|http://www.TiddlyTools.com/#GetTheFAQs|
|Version|2.0.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|transclusion |
|Requires|LoadTiddlersPlugin, FAQViewerPlugin|
|Description|autoload FAQ articles from external document|
Usage
<<<
{{{
<<tiddler GetTheFAQs>>
<<tiddler GetTheFAQs with: location tag tiddlername>>
}}}
*''location'' (optional)<br>is a URL, a relative path/file, or a TiddlerName that refers to a TiddlyWiki document. If omitted (use {{{[[]]}}} as a placeholder), the contents of [[SiteFAQ]] (if any) will be used. If [[SiteFAQ]] doesnt exist, a default of 'faq.html' will be used.
*''tag'' (optional)<br>indicates the tag to match when retrieving tiddlers (default="faq").
*''tiddlername'' (optional)<br>specifies a desired 'target' tiddler title (default=none).
Tiddlers are loaded only if the specified target tiddler or tag value is not present in the current document. If suitable tiddlers are already present, the [[FAQViewer]] is automatically displayed.
<<<
Example
<<<
{{{<<tiddler GetTheFAQs>>}}}
{{smallform{<<tiddler GetTheFAQs##show>>}}}
<<<
!end
!notloaded
//There are no ''%0'' tiddlers currently loaded.//
''Please <html><nowiki><a href="javascript:;" onclick="
var here=story.findContainingTiddler(this);
if (here) story.refreshTiddler(here.getAttribute('tiddler'),null,true);
return false;
">refresh this tiddler</a></html> to import the %0 archive...''
//To view the archive directly, please visit://
{{indent{[[%1|%1]]}}}
!end
!show
<<tiddler {{
var out='';
var src='$1'!='$'+'1'?'$1':'';
var tag='$2'!='$'+'2'?'$2':'faq';
var target='$3'!='$'+'3'?'$3':'';
var defaultsrc ='faq.html';
var askmsg ="Enter the location of a TiddlyWiki document containing '%0' tiddlers";
var confirmmsg ="Press OK to import '%0' tiddlers from:";
var loadingmsg ="'%0' tiddlers are being imported... please wait...";
// if target or tagged tiddlers already exists, just show the viewer
if (store.tiddlerExists(target)||store.getTaggedTiddlers(tag,'excludeLists').length)
out='<<faqViewer %0 %2 outline -modified [[YYYY.0MM.0DD - ]]>\>';
else {
src=store.getTiddlerText(src,src); // get src from tiddler (if any)
if (!src.length) // fallback: use [[SiteFAQ]] or default (faq.html)
src=store.getTiddlerText('SiteFAQ',defaultsrc);
var s=prompt(confirmmsg.format([tag]),src); // ask for permission (or change src)
if (!s||!s.length) // cancelled
out=store.getTiddlerText('GetTheFAQs##notloaded');
else {
src=store.getTiddlerText(s,s); // get src from tiddler (if any)
// show 'please wait' message if remote async load
var async=document.location.protocol!='file:'||src.substr(0,4)=='http';
if (async) setTimeout('displayMessage("'+loadingmsg.format([tag])+'")',1);
out='<<loadTiddlers [[tag:%0]] [[%1]] quiet nodirty noreport temporary>\>';
}
}
wikify(out.format([tag,src,target.length?'startwith:'+target:'']),place);
'';}}>>
!end
%/<<tiddler {{var src='GetTheFAQs'; src+(tiddler&&tiddler.title==src?'##info':'##show')}}
with: [[$1]] [[$2]] [[$3]]>>
To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
{{{
-------------------OO---------
--------------------OO--------
-------------------O----------
------------------------------
--------------------------O---
--------------------------O-O-
--------------------------OO--
------------------------------
------OO----------------------
-----OO-----------------------
-------O----------------------
------------------------------
-------------------------O----
--------------------------OO--
-------------------------OO---
----OOO-----------------------
------O-----------------------
-----O------------------------
------------------------------
-----------O-O----------------
-----------OO-----------------
------------O-----------------
------------------------------
--------------------OOO-------
--------------------O---------
---------------------O--------
------------------------------
-----------O------------------
------------O-----------------
----------OOO-----------------
}}}
{{{
--------------------------------------
------------------------O-------------
----------------------O-O-------------
------------OO------OO------------OO--
-----------O---O----OO------------OO--
OO--------O-----O---OO----------------
OO--------O---O-OO----O-O-------------
----------O-----O-------O-------------
-----------O---O----------------------
------------OO------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
--------------------------------------
-------------------------------------#
-------------------------------------#
-----------------------------------###
}}}
/***
|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.
!!!Documentation
>see [[GotoPluginInfo]]
!!!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
//}}}
<<<
!!!Revisions
<<<
2009.05.22 [1.9.2] use reverseLookup() for IncludePlugin
|please see [[GotoPluginInfo]] for additional revision details|
2006.05.05 [0.0.0] started
<<<
!!!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=3;
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 autocomplete="off" accesskey="G" style="%instyle%"\
title="Enter title text... ENTER=goto, SHIFT-ENTER=search for text, DOWN=select from 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','excludeLists');
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;
}
}
//}}}
/***
|Name|GotoPluginInfo|
|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|documentation|
|Description|Documentation for GotoPlugin|
''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.
!!!!!Usage/Examples
<<<
syntax: {{{<<gotoTiddler quiet search inputstyle:... liststyle:... filter:...>>}}}
All parameters are optional.
* ''quiet'' (//keyword//)<br>list will not be automatically display as each character is typed. Use //down// or //enter// to view the list.
* ''showlist'' (//keyword//)<br>list will always be displayed, inline, directly below the input field.
* ''search'' (//keyword//)<br>adds an extra 'command item' to the list that can be used to invoke a full-text search using the entered value. This can be especially useful when no matching tiddler titles have been found.
* ''inputstyle:'' and ''liststyle:''<br>are CSS declarations that modify the default input and listbox styles, respectively. Note: the CSS styles must be surrounded by ({{{"..."}}} or {{{'...'}}}) or ({{{[[...]]}}}) (e.g., {{{liststyle:"border:1px dotted blue;color:green;..."}}}.
* ''filter:''<br>is a single tag value (or a boolean tag expression if MatchTagsPlugin is installed), and is used to limit the search to only those tiddlers matching the indicated tag or tag expression (e.g., {{{<<gotoTiddler filter:"faq or help">>}}})
{{{<<gotoTiddler>>}}}
<<gotoTiddler>>
{{{<<gotoTiddler search>>}}}
<<gotoTiddler search>>
{{{<<gotoTiddler showlist filter:"pluginInfo" liststyle:"height:10em;width:auto;">>}}}
<<gotoTiddler showlist filter:"pluginInfo" liststyle:"height:10em;width:auto;">>
<<<
!!!!!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
//}}}
<<<
!!!!!Revisions
<<<
2009.05.22 1.9.2 use reverseLookup() for IncludePlugin
2009.04.12 1.9.1 support multiple instances with different filters by using per-element tiddler cache instead of shared static cache
2009.04.05 1.9.0 added 'showlist' parameter for inline display with listbox always visible.
2009.03.23 1.8.0 added txtIncrementalSearchMin (default=3). Avoids fetching long lists. Use down arrow to force search with short input.
2008.12.15 1.7.1 up arrow from input field now moves to end of droplist (search for input). Also, shift+enter cam now be used to quickly invoke search for text.
2008.10.16 1.7.0 in macro handler(), changed to use //named// params instead of positional params, and added optional "filter:" param for tag filtering. Removed 'insert' handling (now provided by [[QuickEditPlugin]]).
2008.10.02 1.6.1 for IE, wrap controls in a table. Corrects placement of listbox so it is below input field.
2008.10.02 1.6.0 added 'search' param for optional "Search for:" item that invokes full text search (especially useful when no title matches are found)
2008.02.17 1.5.0 ENTER key always displays tiddler based on current input regardless of whether input matches any existing tiddler
2007.10.31 1.4.3 removed extra trailing comma on last property of config.macros.gotoTiddler object. This fixes an error under InternetExplorer that was introduced 6 days ago... sure, I should have found it sooner, but... WHY DON'T PEOPLE TELL ME WHEN THINGS ARE BROKEN!!!!
2007.10.25 1.4.2 added onclick handler for input field, so that clicking in field hides the listbox.
2007.10.25 1.4.1 re-wrote getItems() to cache list of tiddlers/shadows/tags and use case-folded simple text match instead of regular expression to find matching tiddlers. This *vastly* reduces processing overhead between keystrokes, especially for documents with many (>1000) tiddlers. Also, removed local definition of replaceSelection(), now supported directly by the TW2.2+ core, as well as via backward-compatible plugin
2007.04.25 1.4.0 renamed macro from "goto" to "gotoTiddler". This was necessary to avoid a fatal syntax error in Opera (and other browsers) that require strict adherence to ECMAScript 1.5 standards which defines the identifier "goto" as "reserved for FUTURE USE"... *sigh*
2007.04.21 1.3.2 in html definition, removed DIV around droplist (see 1.2.6 below). It created more layout problems then it solved. :-(
2007.04.01 1.3.1 in processItem(), ensure that correct textarea field is found by checking for edit=="text" attribute
2007.03.30 1.3.0 tweak SideBarOptions shadow to automatically add {{{<<goto>>}}} when using default sidebar content
2007.03.30 1.2.6 in html definition, added DIV around droplist to fix IE problem where list appears next to input field instead of below it.
2007.03.28 1.2.5 in processItem(), set focus to text area before setting selection (needed for IE to get correct selection 'range')
2007.03.28 1.2.4 added prompt for 'pretty text' when inserting a link into tiddler content
2007.03.28 1.2.3 added local copy of core replaceSelection() and modified for different replace logic
2007.03.27 1.2.2 in processItem(), use story.getTiddlerField() to retrieve textarea control
2007.03.26 1.2.1 in html, use either 'onkeydown' (IE) or 'onkeypress' (Moz) event to process <esc> key sooner, to prevent <esc> from 'bubbling up' to the tiddler (which will close the current editor).
2007.03.26 1.2.0 added support for optional "insert" keyword param.
2006.05.10 1.1.2 when filling listbox, set selection to 'heading' item... auto-select first tiddler title when down/enter moves focus into listbox
2006.05.08 1.1.1 added accesskey ("G") to input field html (also set when field gets focus). Also, inputKeyHandler() skips non-printing/non-editing keys.
2006.05.08 1.1.0 added heading to listbox for better feedback (also avoids problems with 1-line droplist)
2006.05.07 1.0.0 list matches against tiddlers/shadows/tags. input field auto-completion... 1st enter=complete matching input (or show list)... 2nd enter=view tiddler. "quiet" param controls when listbox appears. handling for enter (13), escape(27), and down(40) keys. Change 'ondblclick' to 'onclick' to avoid unintended triggering of tiddler editor). Shadow titles inserted into list instead of appended to the end.
2006.05.05 0.0.0 started
<<<
This is a hidden section, for me, or others that may know about it - now, its time for a prize!
soon :)
Welcome to my Tiddly!
Thanks a ton for visiting - just so you know, this site will continuously change. I am always working on it and will continue to do just that for any reason I see fit. Of course I will keep it clean, but that is just because I am a nice guy and I like to be honest, clean and all around good to people in general. My main web site is @ StraightFocus.com and I am trying to break into the voice over business, until then I do still have a full time job. I have also owned a web design company for over 10 years, which is why this document became so important to me in my travels, life and the pursuit of my own personal TiddlyHappiness.
The "Quote" of the day provided by http://www.tiddlytools.com - it will go to another when its ready.
<<QOTD Quotations 10000>>
Seriously, if you want a TiddlyWiki of your own, just visit the main site @ http://www.tiddlywiki.com as it is a FREE download, and there are incredible resources for help. BUT, on the other hand, if you need to hire me to make your TiddlyWiki, I would be happy to help. I have started 2 new TiddlyWiki's and I will do this for hire. Please take a look at these 2 sites - http://TiddlyStix.tiddlyspot.com and http://PortaDoc.tiddlyspot.com (PortaDoc is in the process of development still)
Thanks for visiting, and if you have any questions for any reason, about this site or any of my other web sites, please feel free to email me anytime.
/***
|Name|ImageSizePlugin|
|Source|http://www.TiddlyTools.com/#ImageSizePlugin|
|Version|1.2.2|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|adds support for resizing images|
This plugin adds optional syntax to scale an image to a specified width and height and/or interactively resize the image with the mouse.
!!!!!Usage
<<<
The extended image syntax is:
{{{
[img(w+,h+)[...][...]]
}}}
where ''(w,h)'' indicates the desired width and height (in CSS units, e.g., px, em, cm, in, or %). Use ''auto'' (or a blank value) for either dimension to scale that dimension proportionally (i.e., maintain the aspect ratio). You can also calculate a CSS value 'on-the-fly' by using a //javascript expression// enclosed between """{{""" and """}}""". Appending a plus sign (+) to a dimension enables interactive resizing in that dimension (by dragging the mouse inside the image). Use ~SHIFT-click to show the full-sized (un-scaled) image. Use ~CTRL-click to restore the starting size (either scaled or full-sized).
<<<
!!!!!Examples
<<<
{{{
[img(100px+,75px+)[images/meow2.jpg]]
}}}
[img(100px+,75px+)[images/meow2.jpg]]
{{{
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img( 1%+,+)[images/meow.gif]]
}}}
[<img(34%+,+)[images/meow.gif]]
[<img(21% ,+)[images/meow.gif]]
[<img(13%+, )[images/meow.gif]]
[<img( 8%+, )[images/meow.gif]]
[<img( 5% , )[images/meow.gif]]
[<img( 3% , )[images/meow.gif]]
[<img( 2% , )[images/meow.gif]]
[img( 1%+,+)[images/meow.gif]]
{{tagClear{
}}}
<<<
!!!!!Revisions
<<<
2010.07.24 [1.2.2] moved tip/dragtip text to config.formatterHelpers.imageSize object to enable customization
2009.02.24 [1.2.1] cleanup width/height regexp, use '+' suffix for resizing
2009.02.22 [1.2.0] added stretchable images
2008.01.19 [1.1.0] added evaluated width/height values
2008.01.18 [1.0.1] regexp for "(width,height)" now passes all CSS values to browser for validation
2008.01.17 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImageSizePlugin= {major: 1, minor: 2, revision: 2, date: new Date(2010,7,24)};
//}}}
//{{{
var f=config.formatters[config.formatters.findByField("name","image")];
f.match="\\[[<>]?[Ii][Mm][Gg](?:\\([^,]*,[^\\)]*\\))?\\[";
f.lookaheadRegExp=/\[([<]?)(>?)[Ii][Mm][Gg](?:\(([^,]*),([^\)]*)\))?\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg;
f.handler=function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var floatLeft=lookaheadMatch[1];
var floatRight=lookaheadMatch[2];
var width=lookaheadMatch[3];
var height=lookaheadMatch[4];
var tooltip=lookaheadMatch[5];
var src=lookaheadMatch[6];
var link=lookaheadMatch[7];
// Simple bracketted link
var e = w.output;
if(link) { // LINKED IMAGE
if (config.formatterHelpers.isExternalLink(link)) {
if (config.macros.attach && config.macros.attach.isAttachment(link)) {
// see [[AttachFilePluginFormatters]]
e = createExternalLink(w.output,link);
e.href=config.macros.attach.getAttachment(link);
e.title = config.macros.attach.linkTooltip + link;
} else
e = createExternalLink(w.output,link);
} else
e = createTiddlyLink(w.output,link,false,null,w.isStatic);
addClass(e,"imageLink");
}
var img = createTiddlyElement(e,"img");
if(floatLeft) img.align="left"; else if(floatRight) img.align="right";
if(width||height) {
var x=width.trim(); var y=height.trim();
var stretchW=(x.substr(x.length-1,1)=='+'); if (stretchW) x=x.substr(0,x.length-1);
var stretchH=(y.substr(y.length-1,1)=='+'); if (stretchH) y=y.substr(0,y.length-1);
if (x.substr(0,2)=="{{")
{ try{x=eval(x.substr(2,x.length-4))} catch(e){displayMessage(e.description||e.toString())} }
if (y.substr(0,2)=="{{")
{ try{y=eval(y.substr(2,y.length-4))} catch(e){displayMessage(e.description||e.toString())} }
img.style.width=x.trim(); img.style.height=y.trim();
config.formatterHelpers.addStretchHandlers(img,stretchW,stretchH);
}
if(tooltip) img.title = tooltip;
// GET IMAGE SOURCE
if (config.macros.attach && config.macros.attach.isAttachment(src))
src=config.macros.attach.getAttachment(src); // see [[AttachFilePluginFormatters]]
else if (config.formatterHelpers.resolvePath) { // see [[ImagePathPlugin]]
if (config.browser.isIE || config.browser.isSafari) {
img.onerror=(function(){
this.src=config.formatterHelpers.resolvePath(this.src,false);
return false;
});
} else
src=config.formatterHelpers.resolvePath(src,true);
}
img.src=src;
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
config.formatterHelpers.imageSize={
tip: 'SHIFT-CLICK=show full size, CTRL-CLICK=restore initial size',
dragtip: 'DRAG=stretch/shrink, '
}
config.formatterHelpers.addStretchHandlers=function(e,stretchW,stretchH) {
e.title=((stretchW||stretchH)?this.imageSize.dragtip:'')+this.imageSize.tip;
e.statusMsg='width=%0, height=%1';
e.style.cursor='move';
e.originalW=e.style.width;
e.originalH=e.style.height;
e.minW=Math.max(e.offsetWidth/20,10);
e.minH=Math.max(e.offsetHeight/20,10);
e.stretchW=stretchW;
e.stretchH=stretchH;
e.onmousedown=function(ev) { var ev=ev||window.event;
this.sizing=true;
this.startX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
this.startY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
this.startW=this.offsetWidth;
this.startH=this.offsetHeight;
return false;
};
e.onmousemove=function(ev) { var ev=ev||window.event;
if (this.sizing) {
var s=this.style;
var currX=!config.browser.isIE?ev.pageX:(ev.clientX+findScrollX());
var currY=!config.browser.isIE?ev.pageY:(ev.clientY+findScrollY());
var newW=(currX-this.offsetLeft)/(this.startX-this.offsetLeft)*this.startW;
var newH=(currY-this.offsetTop )/(this.startY-this.offsetTop )*this.startH;
if (this.stretchW) s.width =Math.floor(Math.max(newW,this.minW))+'px';
if (this.stretchH) s.height=Math.floor(Math.max(newH,this.minH))+'px';
clearMessage(); displayMessage(this.statusMsg.format([s.width,s.height]));
}
return false;
};
e.onmouseup=function(ev) { var ev=ev||window.event;
if (ev.shiftKey) { this.style.width=this.style.height=''; }
if (ev.ctrlKey) { this.style.width=this.originalW; this.style.height=this.originalH; }
this.sizing=false;
clearMessage();
return false;
};
e.onmouseout=function(ev) { var ev=ev||window.event;
this.sizing=false;
clearMessage();
return false;
};
}
//}}}
/***
|Name|ImportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.6.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|interactive controls for import/export with filtering.|
Combine tiddlers from any two TiddlyWiki documents. Interactively select and copy tiddlers from another TiddlyWiki source document. Includes prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles. When done, a list of all imported tiddlers is written into [[ImportedTiddlers]].
!!!!!Documentation
<<<
see [[ImportTiddlersPluginInfo]] for details
<<<
!!!!!interactive control panel
<<<
<<importTiddlers inline>>
{{clear{
^^(see also: [[ImportTiddlers]] shadow tiddler)^^}}}
<<<
!!!!!Revisions
<<<
2009.10.10 4.6.1 in createImportPanel, Use {{{window.Components}}} instead of {{{config.browser.isGecko}}} to avoid applying FF3 'file browse' fixup in Chrome.
2009.10.06 4.6.0 added createTiddlerFromFile (import text files)
|please see [[ImportTiddlersPluginInfo]] for additional revision details|
2005.07.20 1.0.0 Initial Release
<<<
!!!!!Code
***/
//{{{
version.extensions.ImportTiddlersPlugin= {major: 4, minor: 6, revision: 1, date: new Date(2009,10,10)};
// IE needs explicit global scoping for functions/vars called from browser events
window.onClickImportButton=onClickImportButton;
window.refreshImportList=refreshImportList;
// default cookie/option values
if (!config.options.chkImportReport) config.options.chkImportReport=true;
// default shadow definition
config.shadowTiddlers.ImportTiddlers='<<importTiddlers inline>>';
// use shadow tiddler content in backstage panel
if (config.tasks) config.tasks.importTask.content='<<tiddler ImportTiddlers>>' // TW2.2 or above
//}}}
//{{{
// backward-compatiblity for TW2.0.x and TW1.2.x
if (config.macros.importTiddlers==undefined) config.macros.importTiddlers={};
if (typeof merge=='undefined') {
function merge(dst,src,preserveExisting) {
for(var i in src) { if(!preserveExisting || dst[i] === undefined) dst[i] = src[i]; }
return dst;
}
}
if (config.browser.isGecko===undefined)
config.browser.isGecko=(config.userAgent.indexOf('gecko')!=-1);
//}}}
//{{{
merge(config.macros.importTiddlers,{
$: function(id) { return document.getElementById(id); }, // abbreviation
label: 'import tiddlers',
prompt: 'Copy tiddlers from another document',
openMsg: 'Opening %0',
openErrMsg: 'Could not open %0 - error=%1',
readMsg: 'Read %0 bytes from %1',
foundMsg: 'Found %0 tiddlers in %1',
filterMsg: "Filtered %0 tiddlers matching '%1'",
summaryMsg: '%0 tiddler%1 in the list',
summaryFilteredMsg: '%0 of %1 tiddler%2 in the list',
plural: 's are',
single: ' is',
countMsg: '%0 tiddlers selected for import',
processedMsg: 'Processed %0 tiddlers',
importedMsg: 'Imported %0 of %1 tiddlers from %2',
loadText: 'please load a document...',
closeText: 'close',
doneText: 'done',
startText: 'import',
stopText: 'stop',
local: true, // default to import from local file
src: '', // path/filename or URL of document to import (retrieved from SiteUrl)
proxy: '', // URL for remote proxy script (retrieved from SiteProxy)
useProxy: false, // use specific proxy script in front of remote URL
inbound: null, // hash-indexed array of tiddlers from other document
newTags: '', // text of tags added to imported tiddlers
addTags: true, // add new tags to imported tiddlers
listsize: 10, // # of lines to show in imported tiddler list
importTags: true, // include tags from remote source document when importing a tiddler
keepTags: true, // retain existing tags when replacing a tiddler
sync: false, // add 'server' fields to imported tiddlers (for sync function)
lastFilter: '', // most recent filter (URL hash) applied
lastAction: null, // most recent collision button performed
index: 0, // current processing index in import list
sort: '' // sort order for imported tiddler listbox
});
//}}}
//{{{
// hijack core macro handler
if (config.macros.importTiddlers.coreHandler==undefined)
config.macros.importTiddlers.coreHandler=config.macros.importTiddlers.handler;
config.macros.importTiddlers.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if (!params[0] || params[0].toLowerCase()=='core') { // default to built in
if (config.macros.importTiddlers.coreHandler)
config.macros.importTiddlers.coreHandler.apply(this,arguments);
else
createTiddlyButton(place,this.label,this.prompt,onClickImportMenu);
} else if (params[0]=='link') { // show link to floating panel
createTiddlyButton(place,params[1]||this.label,params[2]||this.prompt,onClickImportMenu);
} else if (params[0]=='inline') {// show panel as INLINE tiddler content
createImportPanel(place);
this.$('importPanel').style.position='static';
this.$('importPanel').style.display='block';
} else if (config.macros.loadTiddlers)
config.macros.loadTiddlers.handler(place,macroName,params); // any other params: loadtiddlers
}
//}}}
//{{{
// Handle link click to create/show/hide control panel
function onClickImportMenu(e) { var e=e||window.event;
var parent=resolveTarget(e).parentNode;
var panel=document.getElementById('importPanel');
if (panel==undefined || panel.parentNode!=parent) panel=createImportPanel(parent);
var isOpen=panel.style.display=='block';
if(config.options.chkAnimate)
anim.startAnimating(new Slider(panel,!isOpen,false,'none'));
else
panel.style.display=isOpen?'none':'block';
e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); return(false);
}
//}}}
//{{{
// Create control panel: HTML, CSS
function createImportPanel(place) {
var cmi=config.macros.importTiddlers; // abbrev
var panel=cmi.$('importPanel');
if (panel) { panel.parentNode.removeChild(panel); }
setStylesheet(store.getTiddlerText('ImportTiddlersPlugin##css'),'importTiddlers');
panel=createTiddlyElement(place,'span','importPanel',null,null)
panel.innerHTML=store.getTiddlerText('ImportTiddlersPlugin##html');
refreshImportList();
if (!cmi.src.length) cmi.src=store.getTiddlerText('SiteUrl')||'';
cmi.$('importSourceURL').value=cmi.src;
if (!cmi.proxy.length) cmi.proxy=store.getTiddlerText('SiteProxy')||'SiteProxy';
cmi.$('importSiteProxy').value=cmi.proxy;
if (window.Components) { // FF3 FIXUP
cmi.$('fileImportSource').style.display='none';
cmi.$('importLocalPanelFix').style.display='block';
}
cmi.$('chkSync').checked=cmi.sync;
cmi.$('chkImportTags').checked=cmi.importTags;
cmi.$('chkKeepTags').checked=cmi.keepTags;
cmi.$('chkAddTags').checked=cmi.addTags;
cmi.$('txtNewTags').value=cmi.newTags;
cmi.$('txtNewTags').style.display=cmi.addTags?'block':'none';
cmi.$('chkSync').checked=cmi.sync;
cmi.$('chkImportReport').checked=config.options.chkImportReport;
return panel;
}
//}}}
//{{{
// process control interactions
function onClickImportButton(which,event) {
var cmi=config.macros.importTiddlers; // abbreviation
var list=cmi.$('importList'); if (!list) return false;
var thePanel=cmi.$('importPanel');
var theCollisionPanel=cmi.$('importCollisionPanel');
var theNewTitle=cmi.$('importNewTitle');
var count=0;
switch (which.id)
{
case 'importFromFile': // show local panel
case 'importFromWeb': // show HTTP panel
cmi.local=(which.id=='importFromFile');
cmi.showPanel('importLocalPanel',cmi.local);
cmi.showPanel('importHTTPPanel',!cmi.local);
break;
case 'importOptions': // show/hide options panel
cmi.showPanel('importOptionsPanel',cmi.$('importOptionsPanel').style.display=='none');
break;
case 'fileImportSource':
case 'importLoad': // load import source into hidden frame
importReport(); // if an import was in progress, generate a report
cmi.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
if (cmi.src=='') break;
// Load document, read it's DOM and fill the list
cmi.loadRemoteFile(cmi.src,cmi.filterTiddlerList);
break;
case 'importSelectFeed': // select a pre-defined systemServer feed URL
var p=Popup.create(which); if (!p) return false;
var tids=store.getTaggedTiddlers('systemServer');
if (!tids.length)
createTiddlyText(createTiddlyElement(p,'li'),'no pre-defined server feeds');
for (var t=0; t<tids.length; t++) {
var u=store.getTiddlerSlice(tids[t].title,'URL');
var d=store.getTiddlerSlice(tids[t].title,'Description');
if (!d||!d.length) d=store.getTiddlerSlice(tids[t].title,'description');
if (!d||!d.length) d=u;
createTiddlyButton(createTiddlyElement(p,'li'),tids[t].title,d,
function(){
var u=this.getAttribute('url');
document.getElementById('importSourceURL').value=u;
config.macros.importTiddlers.src=u;
document.getElementById('importLoad').onclick();
},
null,null,null,{url:u});
}
Popup.show();
event.cancelBubble = true;
if (event.stopPropagation) event.stopPropagation();
return false;
// create popup with feed list
// onselect, insert feed URL into input field.
break;
case 'importSelectAll': // select all tiddler list items (i.e., not headings)
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
if (list.options[t].value=='') continue;
list.options[t].selected=true;
count++;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importSelectNew': // select tiddlers not in current document
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value=='') continue;
list.options[t].selected=!store.tiddlerExists(list.options[t].value);
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importSelectChanges': // select tiddlers that are updated from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value==''||!store.tiddlerExists(list.options[t].value)) continue;
for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified>0); // updated tiddler
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importSelectDifferences': // select tiddlers that are new or different from existing tiddlers
importReport(); // if an import was in progress, generate a report
for (var t=0,count=0; t < list.options.length; t++) {
list.options[t].selected=false;
if (list.options[t].value=='') continue;
if (!store.tiddlerExists(list.options[t].value)) { list.options[t].selected=true; count++; continue; }
for (var i=0; i<cmi.inbound.length; i++) // find matching inbound tiddler
{ var inbound=cmi.inbound[i]; if (inbound.title==list.options[t].value) break; }
list.options[t].selected=(inbound.modified-store.getTiddler(list.options[t].value).modified!=0); // changed tiddler
count+=list.options[t].selected?1:0;
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
cmi.$('importStart').disabled=!count;
break;
case 'importApplyFilter': // filter list to include only matching tiddlers
importReport(); // if an import was in progress, generate a report
clearMessage();
if (!cmi.all) // no tiddlers loaded = '0 selected'
{ displayMessage(cmi.countMsg.format([0])); return false; }
var hash=cmi.$('importLastFilter').value;
cmi.inbound=cmi.filterByHash('#'+hash,cmi.all);
refreshImportList(); // reset/resize the listbox
break;
case 'importStart': // initiate the import processing
importReport(); // if an import was in progress, generate a report
cmi.$('importApplyToAll').checked=false;
cmi.$('importStart').value=cmi.stopText;
if (cmi.index>0) cmi.index=-1; // stop processing
else cmi.index=importTiddlers(0); // or begin processing
importStopped();
break;
case 'importClose': // unload imported tiddlers or hide the import control panel
// if imported tiddlers not loaded, close the import control panel
if (!cmi.inbound) { thePanel.style.display='none'; break; }
importReport(); // if an import was in progress, generate a report
cmi.inbound=null; // clear the imported tiddler buffer
refreshImportList(); // reset/resize the listbox
break;
case 'importSkip': // don't import the tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
theImported.status='skipped after asking'; // mark item as skipped
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index+1); // resume with NEXT item
importStopped();
break;
case 'importRename': // change name of imported tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
theImported.status = 'renamed from '+theImported.title; // mark item as renamed
theImported.set(theNewTitle.value,null,null,null,null); // change the tiddler title
theItem.value = theNewTitle.value; // change the listbox item text
theItem.text = theNewTitle.value; // change the listbox item text
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with THIS item
importStopped();
break;
case 'importMerge': // join existing and imported tiddler content
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
var theExisting = store.getTiddler(theItem.value);
var theText = theExisting.text+'\n----\n^^merged from: ';
theText +='[['+cmi.src+'#'+theItem.value+'|'+cmi.src+'#'+theItem.value+']]^^\n';
theText +='^^'+theImported.modified.toLocaleString()+' by '+theImported.modifier+'^^\n'+theImported.text;
var theDate = new Date();
var theTags = theExisting.getTags()+' '+theImported.getTags();
theImported.set(null,theText,null,theDate,theTags);
theImported.status = 'merged with '+theExisting.title; // mark item as merged
theImported.status += ' - '+theExisting.modified.formatString('MM/DD/YYYY 0hh:0mm:0ss');
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with this item
importStopped();
break;
case 'importReplace': // substitute imported tiddler for existing tiddler
cmi.lastAction=which;
var theItem = list.options[cmi.index];
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==theItem.value) break;
var theImported = cmi.inbound[j];
var theExisting = store.getTiddler(theItem.value);
theImported.status = 'replaces '+theExisting.title; // mark item for replace
theImported.status += ' - '+theExisting.modified.formatString('MM/DD/YYYY 0hh:0mm:0ss');
theImported.status += ' by '+theExisting.modifier;
theCollisionPanel.style.display='none';
cmi.index=importTiddlers(cmi.index); // resume with THIS item
importStopped();
break;
case 'importListSmaller': // decrease current listbox size, minimum=5
if (list.options.length==1) break;
list.size-=(list.size>5)?1:0;
cmi.listsize=list.size;
break;
case 'importListLarger': // increase current listbox size, maximum=number of items in list
if (list.options.length==1) break;
list.size+=(list.size<list.options.length)?1:0;
cmi.listsize=list.size;
break;
case 'importListMaximize': // toggle listbox size between current and maximum
if (list.options.length==1) break;
list.size=(list.size==list.options.length)?cmi.listsize:list.options.length;
break;
}
}
//}}}
//{{{
config.macros.importTiddlers.showPanel=function(place,show,skipAnim) {
if (typeof place=='string') var place=document.getElementById(place);
if (!place||!place.style) return;
if(!skipAnim && anim && config.options.chkAnimate) anim.startAnimating(new Slider(place,show,false,'none'));
else place.style.display=show?'block':'none';
}
//}}}
//{{{
function refreshImportList(selectedIndex) {
var cmi=config.macros.importTiddlers; // abbrev
var list=cmi.$('importList'); if (!list) return;
// if nothing to show, reset list content and size
if (!cmi.inbound) {
while (list.length > 0) { list.options[0] = null; }
list.options[0]=new Option(cmi.loadText,'',false,false);
list.size=cmi.listsize;
cmi.$('importLoad').disabled=false;
cmi.$('importLoad').style.display='inline';
cmi.$('importStart').disabled=true;
cmi.$('importOptions').disabled=true;
cmi.$('importOptions').style.display='none';
cmi.$('fileImportSource').disabled=false;
cmi.$('importFromFile').disabled=false;
cmi.$('importFromWeb').disabled=false;
cmi.$('importStart').value=cmi.startText;
cmi.$('importClose').value=cmi.doneText;
cmi.$('importSelectPanel').style.display='none';
cmi.$('importOptionsPanel').style.display='none';
return;
}
// there are inbound tiddlers loaded...
cmi.$('importLoad').disabled=true;
cmi.$('importLoad').style.display='none';
cmi.$('importOptions').style.display='inline';
cmi.$('importOptions').disabled=false;
cmi.$('fileImportSource').disabled=true;
cmi.$('importFromFile').disabled=true;
cmi.$('importFromWeb').disabled=true;
cmi.$('importClose').value=cmi.closeText;
if (cmi.$('importSelectPanel').style.display=='none')
cmi.showPanel('importSelectPanel',true);
// get the sort order
if (!selectedIndex) selectedIndex=0;
if (selectedIndex==0) cmi.sort='title'; // heading
if (selectedIndex==1) cmi.sort='title';
if (selectedIndex==2) cmi.sort='modified';
if (selectedIndex==3) cmi.sort='tags';
if (selectedIndex>3) {
// display selected tiddler count
for (var t=0,count=0; t < list.options.length; t++) {
if (!list.options[t].selected) continue;
if (list.options[t].value!='')
count+=1;
else { // if heading is selected, deselect it, and then select and count all in section
list.options[t].selected=false;
for ( t++; t<list.options.length && list.options[t].value!=''; t++) {
list.options[t].selected=true;
count++;
}
}
}
clearMessage(); displayMessage(cmi.countMsg.format([count]));
}
cmi.$('importStart').disabled=!count;
if (selectedIndex>3) return; // no refresh needed
// get the alphasorted list of tiddlers
var tiddlers=cmi.inbound;
tiddlers.sort(function (a,b) {if(a['title'] == b['title']) return(0); else return (a['title'] < b['title']) ? -1 : +1; });
// clear current list contents
while (list.length > 0) { list.options[0] = null; }
// add heading and control items to list
var i=0;
var indent=String.fromCharCode(160)+String.fromCharCode(160);
if (cmi.all.length==tiddlers.length)
var summary=cmi.summaryMsg.format([tiddlers.length,(tiddlers.length!=1)?cmi.plural:cmi.single]);
else
var summary=cmi.summaryFilteredMsg.format([tiddlers.length,cmi.all.length,(cmi.all.length!=1)?cmi.plural:cmi.single]);
list.options[i++]=new Option(summary,'',false,false);
list.options[i++]=new Option(((cmi.sort=='title' )?'>':indent)+' [by title]','',false,false);
list.options[i++]=new Option(((cmi.sort=='modified')?'>':indent)+' [by date]','',false,false);
list.options[i++]=new Option(((cmi.sort=='tags')?'>':indent)+' [by tags]','',false,false);
// output the tiddler list
switch(cmi.sort) {
case 'title':
for(var t = 0; t < tiddlers.length; t++)
list.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
break;
case 'modified':
// sort descending for newest date first
tiddlers.sort(function (a,b) {if(a['modified'] == b['modified']) return(0); else return (a['modified'] > b['modified']) ? -1 : +1; });
var lastSection = '';
for(var t = 0; t < tiddlers.length; t++) {
var tiddler = tiddlers[t];
var theSection = tiddler.modified.toLocaleDateString();
if (theSection != lastSection) {
list.options[i++] = new Option(theSection,'',false,false);
lastSection = theSection;
}
list.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
}
break;
case 'tags':
var theTitles = {}; // all tiddler titles, hash indexed by tag value
var theTags = new Array();
for(var t=0; t<tiddlers.length; t++) {
var title=tiddlers[t].title;
var tags=tiddlers[t].tags;
if (!tags || !tags.length) {
if (theTitles['untagged']==undefined) { theTags.push('untagged'); theTitles['untagged']=new Array(); }
theTitles['untagged'].push(title);
}
else for(var s=0; s<tags.length; s++) {
if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
theTitles[tags[s]].push(title);
}
}
theTags.sort();
for(var tagindex=0; tagindex<theTags.length; tagindex++) {
var theTag=theTags[tagindex];
list.options[i++]=new Option(theTag,'',false,false);
for(var t=0; t<theTitles[theTag].length; t++)
list.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
}
break;
}
list.selectedIndex=selectedIndex; // select current control item
if (list.size<cmi.listsize) list.size=cmi.listsize;
if (list.size>list.options.length) list.size=list.options.length;
}
//}}}
//{{{
// re-entrant processing for handling import with interactive collision prompting
function importTiddlers(startIndex) {
var cmi=config.macros.importTiddlers; // abbrev
if (!cmi.inbound) return -1;
var list=cmi.$('importList'); if (!list) return;
var t;
// if starting new import, reset import status flags
if (startIndex==0)
for (var t=0;t<cmi.inbound.length;t++)
cmi.inbound[t].status='';
for (var i=startIndex; i<list.options.length; i++) {
// if list item is not selected or is a heading (i.e., has no value), skip it
if ((!list.options[i].selected) || ((t=list.options[i].value)==''))
continue;
for (var j=0;j<cmi.inbound.length;j++)
if (cmi.inbound[j].title==t) break;
var inbound = cmi.inbound[j];
var theExisting = store.getTiddler(inbound.title);
// avoid redundant import for tiddlers that are listed multiple times (when 'by tags')
if (inbound.status=='added')
continue;
// don't import the 'ImportedTiddlers' history from the other document...
if (inbound.title=='ImportedTiddlers')
continue;
// if tiddler exists and import not marked for replace or merge, stop importing
if (theExisting && (inbound.status.substr(0,7)!='replace') && (inbound.status.substr(0,5)!='merge'))
return i;
// assemble tags (remote + existing + added)
var newTags = '';
if (cmi.importTags)
newTags+=inbound.getTags() // import remote tags
if (cmi.keepTags && theExisting)
newTags+=' '+theExisting.getTags(); // keep existing tags
if (cmi.addTags && cmi.newTags.trim().length)
newTags+=' '+cmi.newTags; // add new tags
inbound.set(null,null,null,null,newTags.trim());
// set the status to 'added' (if not already set by the 'ask the user' UI)
inbound.status=(inbound.status=='')?'added':inbound.status;
// set sync fields
if (cmi.sync) {
if (!inbound.fields) inbound.fields={}; // for TW2.1.x backward-compatibility
inbound.fields['server.page.revision']=inbound.modified.convertToYYYYMMDDHHMM();
inbound.fields['server.type']='file';
inbound.fields['server.host']=(cmi.local&&!cmi.src.startsWith('file:')?'file:///':'')+cmi.src;
}
// do the import!
store.suspendNotifications();
store.saveTiddler(inbound.title, inbound.title, inbound.text, inbound.modifier, inbound.modified, inbound.tags, inbound.fields, true, inbound.created);
store.fetchTiddler(inbound.title).created = inbound.created; // force creation date to imported value (needed for TW2.1.x and earlier)
store.resumeNotifications();
}
return(-1); // signals that we really finished the entire list
}
function importStopped() {
var cmi=config.macros.importTiddlers; // abbrev
var list=cmi.$('importList'); if (!list) return;
var theNewTitle=cmi.$('importNewTitle');
if (cmi.index==-1){
cmi.$('importStart').value=cmi.startText;
importReport(); // import finished... generate the report
} else {
// import collision...
// show the collision panel and set the title edit field
cmi.$('importStart').value=cmi.stopText;
cmi.showPanel('importCollisionPanel',true);
theNewTitle.value=list.options[cmi.index].value;
if (cmi.$('importApplyToAll').checked && cmi.lastAction && cmi.lastAction.id!='importRename')
onClickImportButton(cmi.lastAction);
}
}
//}}}
//{{{
function importReport() {
var cmi=config.macros.importTiddlers; // abbrev
if (!cmi.inbound) return;
// if import was not completed, the collision panel will still be open... close it now.
var panel=cmi.$('importCollisionPanel'); if (panel) panel.style.display='none';
// get the alphasorted list of tiddlers
var tiddlers = cmi.inbound;
// gather the statistics
var count=0; var total=0;
for (var t=0; t<tiddlers.length; t++) {
if (!tiddlers[t].status || !tiddlers[t].status.trim().length) continue;
if (tiddlers[t].status.substr(0,7)!='skipped') count++;
total++;
}
// generate a report
if (total) displayMessage(cmi.processedMsg.format([total]));
if (count && config.options.chkImportReport) {
// get/create the report tiddler
var theReport = store.getTiddler('ImportedTiddlers');
if (!theReport) { theReport=new Tiddler(); theReport.title='ImportedTiddlers'; theReport.text=''; }
// format the report content
var now = new Date();
var newText = 'On '+now.toLocaleString()+', '+config.options.txtUserName
newText +=' imported '+count+' tiddler'+(count==1?'':'s')+' from\n[['+cmi.src+'|'+cmi.src+']]:\n';
if (cmi.addTags && cmi.newTags.trim().length)
newText += 'imported tiddlers were tagged with: "'+cmi.newTags+'"\n';
newText += '<<<\n';
for (var t=0; t<tiddlers.length; t++) if (tiddlers[t].status)
newText += '#[['+tiddlers[t].title+']] - '+tiddlers[t].status+'\n';
newText += '<<<\n';
// update the ImportedTiddlers content and show the tiddler
theReport.text = newText+((theReport.text!='')?'\n----\n':'')+theReport.text;
theReport.modifier = config.options.txtUserName;
theReport.modified = new Date();
store.saveTiddler(theReport.title, theReport.title, theReport.text, theReport.modifier, theReport.modified, theReport.tags, theReport.fields);
story.displayTiddler(null,theReport.title,1,null,null,false);
story.refreshTiddler(theReport.title,1,true);
}
// reset status flags
for (var t=0; t<cmi.inbound.length; t++) cmi.inbound[t].status='';
// mark document as dirty and let display update as needed
if (count) { store.setDirty(true); store.notifyAll(); }
// always show final message when tiddlers were actually loaded
if (count) displayMessage(cmi.importedMsg.format([count,tiddlers.length,cmi.src.replace(/%20/g,' ')]));
}
//}}}
//{{{
// // File and XMLHttpRequest I/O
config.macros.importTiddlers.askForFilename=function(here) {
var msg=here.title; // use tooltip as dialog box message
var path=getLocalPath(document.location.href);
var slashpos=path.lastIndexOf('/'); if (slashpos==-1) slashpos=path.lastIndexOf('\\');
if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
var file='';
var result='';
if(window.Components) { // moz
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
picker.init(window, msg, nsIFilePicker.modeOpen);
var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
thispath.initWithPath(path);
picker.displayDirectory=thispath;
picker.defaultExtension='html';
picker.defaultString=file;
picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.persistentDescriptor;
}
catch(e) { alert('error during local file access: '+e.toString()) }
}
else { // IE
try { // XPSP2 IE only
var s = new ActiveXObject('UserAccounts.CommonDialog');
s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|';
s.FilterIndex=3; // default to HTML files;
s.InitialDir=path;
s.FileName=file;
if (s.showOpen()) var result=s.FileName;
}
catch(e) { // fallback
var result=prompt(msg,path+file);
}
}
return result;
}
config.macros.importTiddlers.loadRemoteFile = function(src,callback) {
if (src==undefined || !src.length) return null; // filename is required
var original=src; // URL as specified
var hashpos=src.indexOf('#'); if (hashpos!=-1) src=src.substr(0,hashpos); // URL with #... suffix removed (needed for IE)
clearMessage();
displayMessage(this.openMsg.format([src.replace(/%20/g,' ')]));
if (src.substr(0,5)!='http:' && src.substr(0,5)!='file:') { // if not a URL, read from local filesystem
var txt=loadFile(src);
if (!txt) { // file didn't load, might be relative path.. try fixup
var pathPrefix=document.location.href; // get current document path and trim off filename
var slashpos=pathPrefix.lastIndexOf('/'); if (slashpos==-1) slashpos=pathPrefix.lastIndexOf('\\');
if (slashpos!=-1 && slashpos!=pathPrefix.length-1) pathPrefix=pathPrefix.substr(0,slashpos+1);
src=pathPrefix+src;
if (pathPrefix.substr(0,5)!='http:') src=getLocalPath(src);
var txt=loadFile(src);
}
if (!txt) { // file still didn't load, report error
displayMessage(config.macros.importTiddlers.openErrMsg.format([src.replace(/%20/g,' '),'(filesystem error)']));
} else {
displayMessage(config.macros.importTiddlers.readMsg.format([txt.length,src.replace(/%20/g,' ')]));
if (version.major+version.minor*.1+version.revision*.01!=2.52) txt=convertUTF8ToUnicode(txt);
if (callback) callback(true,original,txt,src,null);
}
} else {
doHttp('GET',src,null,null,config.options.txtRemoteUsername,config.options.txtRemotePassword,callback,original,null);
}
}
config.macros.importTiddlers.readTiddlersFromHTML=function(html){
var remoteStore=new TiddlyWiki();
remoteStore.importTiddlyWiki(html);
return remoteStore.getTiddlers('title');
}
config.macros.importTiddlers.readTiddlersFromCSV=function(CSV){
var remoteStore=new TiddlyWiki();
// GET NAMES
var lines=CSV.replace(/\r/g,'').split('\n');
var names=lines.shift().replace(/"/g,'').split(',');
CSV=lines.join('\n');
// ENCODE commas and newlines within quoted values
var comma='!~comma~!'; var commaRE=new RegExp(comma,'g');
var newline='!~newline~!'; var newlineRE=new RegExp(newline,'g');
CSV=CSV.replace(/"([^"]*?)"/g,
function(x){ return x.replace(/\,/g,comma).replace(/\n/g,newline); });
// PARSE lines
var lines=CSV.split('\n');
for (var i=0; i<lines.length; i++) { if (!lines[i].length) continue;
var values=lines[i].split(',');
// DECODE commas, newlines, and doubled-quotes, and remove enclosing quotes (if any)
for (var v=0; v<values.length; v++)
values[v]=values[v].replace(commaRE,',').replace(newlineRE,'\n')
.replace(/^"|"$/g,'').replace(/""/g,'"');
// EXTRACT tiddler values
var title=''; var text=''; var tags=[]; var fields={};
var created=null; var when=new Date(); var who=config.options.txtUserName;
for (var v=0; v<values.length; v++) { var val=values[v];
if (names[v]) switch(names[v].toLowerCase()) {
case 'title': title=val.replace(/\[\]\|/g,'_'); break;
case 'created': created=new Date(val); break;
case 'modified':when=new Date(val); break;
case 'modifier':who=val; break;
case 'text': text=val; break;
case 'tags': tags=val.readBracketedList(); break;
default: fields[names[v].toLowerCase()]=val; break;
}
}
// CREATE tiddler in temporary store
if (title.length)
remoteStore.saveTiddler(title,title,text,who,when,tags,fields,true,created||when);
}
return remoteStore.getTiddlers('title');
}
config.macros.importTiddlers.createTiddlerFromFile=function(src,txt) {
var t=new Tiddler();
var pos=src.lastIndexOf("/"); if (pos==-1) pos=src.lastIndexOf("\\");
t.title=pos==-1?src:src.substr(pos+1);
t.text=txt;
t.created=t.modified=new Date();
t.modifier=config.options.txtUserName;
if (src.substr(src.length-3,3)=='.js') t.tags=['systemConfig'];
return [t];
}
config.macros.importTiddlers.filterTiddlerList=function(success,params,txt,src,xhr){
var cmi=config.macros.importTiddlers; // abbreviation
var src=src.replace(/%20/g,' ');
if (!success) { displayMessage(cmi.openErrMsg.format([src,xhr.status])); return; }
cmi.all=cmi.readTiddlersFromHTML(txt);
if (!cmi.all||!cmi.all.length) cmi.all=cmi.readTiddlersFromCSV(txt)
if (!cmi.all||!cmi.all.length) cmi.all=cmi.createTiddlerFromFile(src,txt)
var count=cmi.all?cmi.all.length:0;
var querypos=src.lastIndexOf('?'); if (querypos!=-1) src=src.substr(0,querypos);
displayMessage(cmi.foundMsg.format([count,src]));
cmi.inbound=cmi.filterByHash(params,cmi.all); // use full URL including hash (if any)
cmi.$('importLastFilter').value=cmi.lastFilter;
window.refreshImportList(0);
}
config.macros.importTiddlers.filterByHash=function(src,tiddlers){
var hashpos=src.lastIndexOf('#'); if (hashpos==-1) return tiddlers;
var hash=src.substr(hashpos+1); if (!hash.length) return tiddlers;
var tids=[];
var params=hash.parseParams('anon',null,true,false,false);
for (var p=1; p<params.length; p++) {
switch (params[p].name) {
case 'anon':
case 'open':
tids.pushUnique(params[p].value);
break;
case 'tag':
if (store.getMatchingTiddlers) { // for boolean expressions - see MatchTagsPlugin
var r=store.getMatchingTiddlers(params[p].value,null,tiddlers);
for (var t=0; t<r.length; t++) tids.pushUnique(r[t].title);
} else for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].isTagged(params[p].value))
tids.pushUnique(tiddlers[t].title);
break;
case 'story':
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].title==params[p].value) {
tiddlers[t].changed();
for (var s=0; s<tiddlers[t].links.length; s++)
tids.pushUnique(tiddlers[t].links[s]);
break;
}
break;
case 'search':
for (var t=0; t<tiddlers.length; t++)
if (tiddlers[t].text.indexOf(params[p].value)!=-1)
tids.pushUnique(tiddlers[t].title);
break;
}
}
var matches=[];
for (var t=0; t<tiddlers.length; t++)
if (tids.contains(tiddlers[t].title))
matches.push(tiddlers[t]);
displayMessage(config.macros.importTiddlers.filterMsg.format([matches.length,hash]));
config.macros.importTiddlers.lastFilter=hash;
return matches;
}
//}}}
/***
!!!Control panel CSS
//{{{
!css
#importPanel {
display: none; position:absolute; z-index:11; width:35em; right:105%; top:3em;
background-color: #eee; color:#000; font-size: 8pt; line-height:110%;
border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;
padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em;
}
#importPanel a, #importPanel td a { color:#009; display:inline; margin:0px; padding:1px; }
#importPanel table { width:100%; border:0px; padding:0px; margin:0px; font-size:8pt; line-height:110%; background:transparent; }
#importPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }
#importPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }
#importPanel select { width:100%;margin:0px;font-size:8pt;line-height:110%;}
#importPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%}
#importPanel .box { border:1px solid #000; background-color:#eee; padding:3px 5px; margin-bottom:5px; -moz-border-radius:5px;-webkit-border-radius:5px;}
#importPanel .topline { border-top:1px solid #999; padding-top:2px; margin-top:2px; }
#importPanel .rad { width:auto; }
#importPanel .chk { width:auto; margin:1px;border:0; }
#importPanel .btn { width:auto; }
#importPanel .btn1 { width:98%; }
#importPanel .btn2 { width:48%; }
#importPanel .btn3 { width:32%; }
#importPanel .btn4 { width:23%; }
#importPanel .btn5 { width:19%; }
#importPanel .importButton { padding: 0em; margin: 0px; font-size:8pt; }
#importPanel .importListButton { padding:0em 0.25em 0em 0.25em; color: #000000; display:inline }
#backstagePanel #importPanel { left:10%; right:auto; }
!end
//}}}
!!!Control panel HTML
//{{{
!html
<!-- source and report -->
<table><tr><td align=left>
import from
<input type="radio" class="rad" name="importFrom" id="importFromFile" value="file" CHECKED
onclick="onClickImportButton(this,event)" title="show file controls"> local file
<input type="radio" class="rad" name="importFrom" id="importFromWeb" value="http"
onclick="onClickImportButton(this,event)" title="show web controls"> web server
</td><td align=right>
<input type=checkbox class="chk" id="chkImportReport"
onClick="config.options['chkImportReport']=this.checked;"> create report
</td></tr></table>
<div class="box" id="importSourcePanel" style="margin:.5em">
<div id="importLocalPanel" style="display:block;margin-bottom:2px;"><!-- import from local file -->
enter or browse for source path/filename<br>
<input type="file" id="fileImportSource" size=57 style="width:100%"
onKeyUp="config.macros.importTiddlers.src=this.value"
onChange="config.macros.importTiddlers.src=this.value;document.getElementById('importLoad').onclick()">
<div id="importLocalPanelFix" style="display:none"><!-- FF3 FIXUP -->
<input type="text" id="fileImportSourceFix" style="width:90%"
title="Enter a path/file to import"
onKeyUp="config.macros.importTiddlers.src=this.value"
onChange="config.macros.importTiddlers.src=this.value;document.getElementById('importLoad').onclick()">
<input type="button" id="fileImportSourceFixButton" style="width:7%" value="..."
title="Select a path/file to import"
onClick="var r=config.macros.importTiddlers.askForFilename(this); if (!r||!r.length) return;
document.getElementById('fileImportSourceFix').value=r;
config.macros.importTiddlers.src=r;
document.getElementById('importLoad').onclick()">
</div><!--end FF3 FIXUP-->
</div><!--end local-->
<div id="importHTTPPanel" style="display:none;margin-bottom:2px;"><!-- import from http server -->
<table><tr><td align=left>
enter a URL or <a href="javascript:;" id="importSelectFeed"
onclick="return onClickImportButton(this,event)" title="select a pre-defined 'systemServer' URL">
select a server</a><br>
</td><td align=right>
<input type="checkbox" class="chk" id="importUsePassword"
onClick="config.macros.importTiddlers.usePassword=this.checked;
config.macros.importTiddlers.showPanel('importIDPWPanel',this.checked,true);">password
<input type="checkbox" class="chk" id="importUseProxy"
onClick="config.macros.importTiddlers.useProxy=this.checked;
config.macros.importTiddlers.showPanel('importSiteProxy',this.checked,true);">proxy
</td></tr></table>
<input type="text" id="importSiteProxy" style="display:none;margin-bottom:1px" onfocus="this.select()" value="SiteProxy"
onKeyUp="config.macros.importTiddlers.proxy=this.value"
onChange="config.macros.importTiddlers.proxy=this.value;">
<input type="text" id="importSourceURL" onfocus="this.select()" value="SiteUrl"
onKeyUp="config.macros.importTiddlers.src=this.value"
onChange="config.macros.importTiddlers.src=this.value;">
<div id="importIDPWPanel" style="text-align:center;margin-top:2px;display:none";>
username: <input type=text id="txtImportID" style="width:25%"
onChange="config.options.txtRemoteUsername=this.value;">
password: <input type=password id="txtImportPW" style="width:25%"
onChange="config.options.txtRemotePassword=this.value;">
</div><!--end idpw-->
</div><!--end http-->
</div><!--end source-->
<div class="box" id="importSelectPanel" style="display:none;margin:.5em;">
<table><tr><td align=left>
select:
<a href="javascript:;" id="importSelectAll"
onclick="return onClickImportButton(this)" title="SELECT all tiddlers">
all</a>
<a href="javascript:;" id="importSelectNew"
onclick="return onClickImportButton(this)" title="SELECT tiddlers not already in destination document">
added</a>
<a href="javascript:;" id="importSelectChanges"
onclick="return onClickImportButton(this)" title="SELECT tiddlers that have been updated in source document">
changes</a>
<a href="javascript:;" id="importSelectDifferences"
onclick="return onClickImportButton(this)" title="SELECT tiddlers that have been added or are different from existing tiddlers">
differences</a>
</td><td align=right>
<a href="javascript:;" id="importListSmaller"
onclick="return onClickImportButton(this)" title="SHRINK list size">
– </a>
<a href="javascript:;" id="importListLarger"
onclick="return onClickImportButton(this)" title="GROW list size">
+ </a>
<a href="javascript:;" id="importListMaximize"
onclick="return onClickImportButton(this)" title="MAXIMIZE/RESTORE list size">
= </a>
</td></tr></table>
<select id="importList" size=8 multiple
onchange="setTimeout('refreshImportList('+this.selectedIndex+')',1)">
<!-- NOTE: delay refresh so list is updated AFTER onchange event is handled -->
</select>
<div style="text-align:center">
<a href="javascript:;"
title="click for help using filters..."
onclick="alert('A filter consists of one or more space-separated combinations of: tiddlertitle, tag:[[tagvalue]], tag:[[tag expression]] (requires MatchTagsPlugin), story:[[TiddlerName]], and/or search:[[searchtext]]. Use a blank filter to restore the list of all tiddlers.'); return false;"
>filter</a>
<input type="text" id="importLastFilter" style="margin-bottom:1px; width:65%"
title="Enter a combination of one or more filters. Use a blank filter for all tiddlers."
onfocus="this.select()" value=""
onKeyUp="config.macros.importTiddlers.lastFilter=this.value"
onChange="config.macros.importTiddlers.lastFilter=this.value;">
<input type="button" id="importApplyFilter" style="width:20%" value="apply"
title="filter list of tiddlers to include only those that match certain criteria"
onclick="return onClickImportButton(this)">
</div>
</div><!--end select-->
<div class="box" id="importOptionsPanel" style="text-align:center;margin:.5em;display:none;">
apply tags: <input type=checkbox class="chk" id="chkImportTags" checked
onClick="config.macros.importTiddlers.importTags=this.checked;">from source
<input type=checkbox class="chk" id="chkKeepTags" checked
onClick="config.macros.importTiddlers.keepTags=this.checked;">keep existing
<input type=checkbox class="chk" id="chkAddTags"
onClick="config.macros.importTiddlers.addTags=this.checked;
config.macros.importTiddlers.showPanel('txtNewTags',this.checked,false);
if (this.checked) document.getElementById('txtNewTags').focus();">add tags<br>
<input type=text id="txtNewTags" style="margin-top:4px;display:none;" size=15 onfocus="this.select()"
title="enter tags to be added to imported tiddlers"
onKeyUp="config.macros.importTiddlers.newTags=this.value;
document.getElementById('chkAddTags').checked=this.value.length>0;" autocomplete=off>
<nobr><input type=checkbox class="chk" id="chkSync"
onClick="config.macros.importTiddlers.sync=this.checked;">
link tiddlers to source document (for sync later)</nobr>
</div><!--end options-->
<div id="importButtonPanel" style="text-align:center">
<input type=button id="importLoad" class="importButton btn3" value="open"
title="load listbox with tiddlers from source document"
onclick="onClickImportButton(this)">
<input type=button id="importOptions" class="importButton btn3" value="options..."
title="set options for tags, sync, etc."
onclick="onClickImportButton(this)">
<input type=button id="importStart" class="importButton btn3" value="import"
title="start/stop import of selected source tiddlers into current document"
onclick="onClickImportButton(this)">
<input type=button id="importClose" class="importButton btn3" value="done"
title="clear listbox or hide control panel"
onclick="onClickImportButton(this)">
</div>
<div class="none" id="importCollisionPanel" style="display:none;margin:.5em 0 .5em .5em;">
<table><tr><td style="width:65%" align="left">
<table><tr><td align=left>
tiddler already exists:
</td><td align=right>
<input type=checkbox class="chk" id="importApplyToAll"
onclick="document.getElementById('importRename').disabled=this.checked;"
checked>apply to all
</td></tr></table>
<input type=text id="importNewTitle" size=15 autocomplete=off">
</td><td style="width:34%" align="center">
<input type=button id="importMerge"
class="importButton" style="width:47%" value="merge"
title="append the incoming tiddler to the existing tiddler"
onclick="onClickImportButton(this)"><!--
--><input type=button id="importSkip"
class="importButton" style="width:47%" value="skip"
title="do not import this tiddler"
onclick="onClickImportButton(this)"><!--
--><br><input type=button id="importRename"
class="importButton" style="width:47%" value="rename"
title="rename the incoming tiddler"
onclick="onClickImportButton(this)"><!--
--><input type=button id="importReplace"
class="importButton" style="width:47%" value="replace"
title="discard the existing tiddler"
onclick="onClickImportButton(this)">
</td></tr></table>
</div><!--end collision-->
!end
//}}}
***/
/***
|Name|ImportTiddlersPluginInfo|
|Source|http://www.TiddlyTools.com/#ImportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ImportTiddlersPluginInfo|
|Version|4.6.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|documentation|
|Description|documentation for ImportTiddlersPlugin|
Combine tiddlers from any two TiddlyWiki documents. An interactive control panel lets you pick a source document and import selected tiddlers, with prompting for skip, rename, merge or replace actions when importing tiddlers that match existing titles. Generates a detailed report of import 'history' in ImportedTiddlers.
!!!!!Usage
<<<
{{{<<importTiddlers>>}}} or {{{<<importTiddlers core>>}}}
invokes the built-in importTiddlers macro (TW2.1.x+). If installed in documents using TW2.0.x or earlier, fallback is to use 'link' display (see below)
{{{<<importTiddlers link label tooltip>>}}}
The ''link'' keyword creates an "import tiddlers" link that when clicked to show/hide import control panel. ''label'' and ''tooltip'' are optional text parameters (enclosed in quotes or {{{[[...]]}}}, and allow you to override the default display text for the link and the mouseover help text, respectively.
{{{<<importTiddlers inline>>}}}
creates import control panel directly in tiddler content
<<importTiddlers inline>>
Enter a document URL or press "..." to select a TiddlyWiki file to import, and then press ''[open]''. //Note: There may be a delay before the list of tiddlers appears.// Use the ''[-]'', ''[+]'', or ''[=]'' links to adjust the listbox size so you can view more (or less) tiddler titles at one time.
Select one or more titles from the listbox. Use CTRL-click or SHIFT-click to select/deselect individual titles. Click on ''all'', ''new'', ''changes'', or ''differences'' to automatically select a subset of tiddlers from the list, based on a comparison of the two documents:
*''all'' selects ALL tiddlers from the import source document, even if they have not been changed.
*''new'' selects only tiddlers that are found in the import source document, but do not yet exist in the destination document
*''changes'' selects only tiddlers that exist in both documents but that are newer in the source document
*''differences'' selects all new and existing tiddlers that are different from the destination document (even if destination tiddler is newer)
Press ''[import]'' to begin copying tiddlers to the current document. If an 'inbound' tiddler matches one that already exists in the document, the import process pauses and the tiddler title is displayed in an input field, along with four push buttons: ''skip'', ''rename'', ''merge'' and ''replace''.
* to bypass importing the tiddler, press ''skip''
* to give the inbound tiddler a different name, so that both the old and new tiddlers will exist when the import is done, enter a new title in the input field and press ''rename''
* to combine the content from both tiddlers into a single tiddler so you can then edit it later to eliminate unwanted content, press ''merge''
* to overwrite the existing tiddler with the imported one (discarding the previous content), press ''[replace]''
''Import Report History''
Whenever tiddlers are imported, a report is generated into a tiddler named [[ImportedTiddlers]], recording when the latest import was performed, the number of tiddlers successfully imported, from what location, and by whom, as well as a list of the tiddlers that were processed. When more tiddlers are imported at a later time, a new report is //added// to the existing [[ImportedTiddlers]], above the previous report (i.e., at the top of the tiddler), so that a history of imports is maintained. If this record is not desired, you can delete [[ImportedTiddlers]] at any time.
Note: You can prevent a report from being generated for any given import activity by clearing the "create a report" checkbox before pressing the ''import'' button
<<<
!!!!!Installation Notes
<<<
* As of 6/27/2007, support for TW2.1.x and earlier have been moved to [[ImportTiddlersPluginPatch]]. ''//Only install the patch plugin when using TW2.1.x or earlier.//''
<<<
!!!!!Revisions
<<<
2009.10.06 4.6.0 added createTiddlerFromFile (import text files)
2009.09.27 4.5.5 in readTiddlersFromCSV(), strip \r from input and fixed handling for quoted values
2009.09.12 4.5.4 fixed 'return false' to prevent IE page transition. Also, moved html/css definitions to separate sections
2009.08.23 4.5.3 in importTiddlers(), add 'file:///' to local server.host sync field only if not already present in URL
2009.08.20 4.5.2 only use SiteURL/SiteProxy values if control panel value has not yet been set
2009.07.03 4.5.1 fixups for TW252: doHttp() doesn't return XHR and convertUTF8ToUnicode() not needed for local I/O
2009.05.04 4.5.0 import from CSV-formatted files
2009.03.04 4.4.2 in createImportPanel(), init option checkboxes so display matches internal state variables
2009.02.26 4.4.1 use macro-specific definition of $() function abbreviation (avoids conflict with JQuery)
2008.09.30 4.4.0 added fallback definition of merge() for use with TW2.0.x and TW1.2.x
2008.08.12 4.3.3 rewrite backstage and shadow tiddler definitions for easier customization
2008.08.05 4.3.2 rewrote loadRemoteFile() to eliminate use of platform-specific fileExists() function
2008.06.29 4.3.1 More layout/animation work for simpler sequential interaction. Code reduction/cleanup
2008.06.28 4.3.0 HTML and CSS cleanup and tweaks to layout. Added animation to panels
2008.06.22 4.2.0 For FireFox, use HTML with separate text+button control instead of type='file' control
2008.06.05 4.1.0 in filterByHash(), added support for boolean tag expressions using getMatchingTiddlers() (defined by MatchTagsPlugin)
2008.05.12 4.0.2 automatically tweak the backstage "import" task to add the ImportTiddlers control panel
2008.04.30 4.0.1 trim #... suffix for loading files/URLs in IE
2008.04.30 4.0.0 added source filtering (using URL paramifiers). Also, abbreviations for code-size reduction.
2008.04.13 3.9.0 added 'apply to all' checkbox for collision processing
2008.03.26 3.8.0 added support for selecting pre-defined systemServer URLs
2008.03.25 3.7.0 added support for setting 'server' fields on imported tiddlers (for later synchronizing of changes)
2008.01.03 3.6.0 in loadRemoteFile(), use lower-level doHttp() instead of loadRemoteFile() in order to support username/password access to remote server
2007.10.30 3.5.6 update [[ImportTiddlers]] shadow tiddler definition to include "inline" link
2007.06.27 3.5.5 added missing 'fields' params to saveTiddler() calls. Fixes problem where importing tiddlers would lose the custom fields. Also, moved functions for TW2.1.x to [[ImportTiddlersPluginPatch2.1.x]].
2007.06.25 3.5.4 added calls to store.suspendNotifications() and store.resumeNotifications(). Eliminates redisplay processing overhead DURING import activities
2007.04.29 3.5.3 in refreshImportList() when inbound tiddlers are loaded, change "close" button to "done", and disable certain controls to creates a modal condition, so that actions that reload tiddlers cannot be performed unless "done" is first pressed to end the mode..
2007.04.28 3.5.2 in handler(), added param support for custom link label/prompt
2007.04.19 3.5.1 in readTiddlersFromHTML(), for TW2.2 and above, use importTiddlyWiki() (new core functionality) to get tiddlers from remote file content. Also, copied updated TW21Loader.prototype.internalizeTiddler() definition from TW2.2b5 so plugin can read tiddlers from TW2.2+ even when running under TW2.1.x
2007.03.22 3.5.0 in refreshImportList(), add handling for 'select section' when a heading is selected. Makes it really easy to import by tag or date!
2007.03.21 3.4.0 split loadTiddlers functionality into separate plugin (see [[LoadTiddlersPlugin]])
2007.03.20 3.3.1 tweak to previous change to allow relative file references via http: (bypasses getLocalPath() so remote URL will be used)
2007.03.20 3.3.0 added support for local, relative file references: in loadRemoteFile(), check for fileExists(). If not found, prepend relative path and retry.
2007.02.24 3.2.1 re-labeled control panel "open" button to "load"
2007.02.09 3.2.0 loadTiddlers: added support for "noReload" tag (prevents overwriting existing tiddler, even if inbound tiddler is newer)
2007.02.08 3.1.3 loadTiddlers: added missing code and documentation for "newTags" handling (a feature change from long, long ago that somehow got lost!)
2006.11.14 3.1.2 fix macro handler parameter declaration (double-pasted param list corrupts IE)
2006.11.13 3.1.1 use apply() method to invoke hijacked core handler
2006.11.13 3.1.0 hijack built-in importTiddlers.handler() to co-exist with plugin interface. If no params or 'core' keyword, display core interface. "link" param embeds "import tiddlers" link that shows floating panel when clicked.
2006.10.12 3.0.8 in readTiddlersFromHTML(), fallback to find end of store area by matching "/body" when POST-BODY-START is not present (backward compatibility for older documents)
2006.09.10 3.0.7 in readTiddlersFromHTML(), find end of store area by matching "POST-BODY-START" instead of "/body"
2006.08.16 3.0.6 Use higher-level store.saveTiddler() instead of store.addTiddler() to avoid conflicts with adaptations that hijack low-level tiddler handling. in CreateImportPanel(), removed "refresh listbox after every tiddler change".
2006.07.29 3.0.5 added noChangeMsg to loadTiddlers processing. if not 'quiet' mode, reports skipped tiddlers.
2006.04.18 3.0.4 in loadTiddlers.handler, fixed parsing of "prompt:" param. Also, corrected parameters mismatch in loadTiddlers() callback function definition (order of params was wrong, resulting in filters NOT being applied)
2006.04.12 3.0.3 moved many display messages to macro properties for easier L10N translations via 'lingo' definitions.
2006.04.12 3.0.2 more work on 'core candidate' code. Proposed API now defines "loadRemoteFile()" for XMLHttpRequest processing with built in fallback for handling local filesystem access, and readTiddlersFromHTML() to process the resulting source HTML content.
2006.04.04 3.0.1 in refreshImportList(), when using [by tags], tiddlers without tags are now included in a new "untagged" psuedo-tag list section
2006.04.04 3.0.0 Separate non-interactive {{{<<importTiddlers...>>}}} macro functionality for incorporation into TW2.1 core and renamed as {{{<<loadTiddlers>>}}} macro. New parameters for loadTiddlers: ''label:text'' and ''prompt:text'' for link creation, ''ask'' for filename/URL, ''tag:text'' for filtering, "confirm" for accept/reject of individual inbound tiddlers. Removed support for "importReplace/importPublic" tags and "force" param (unused feature).
2006.03.30 2.9.1 when extracting store area from remote URL, look for "</body>" instead of "</body>\n</html>" so it will match even if the "\n" is absent from the source.
2006.03.30 2.9.0 added optional 'force' macro param. When present, autoImportTiddlers() bypasses the checks for importPublic and importReplace. Based on a request from Tom Otvos.
2006.03.28 2.8.1 in loadImportFile(), added checks to see if 'netscape' and 'x.overrideMimeType()' are defined (not in IE). Also, when extracting store area, look for "</body>\n</html>" and omit extra content that may have been added to the end of the file.
2006.02.21 2.8.0 added support for "tiddler:TiddlerName" filtering parameter in auto-import processing
2006.02.21 2.7.1 Clean up layout problems with IE. (Use tables for alignment instead of SPANs styled with float:left and float:right)
2006.02.21 2.7.0 Added "local file" and "web server" radio buttons. Default remote URL uses value from [[SiteURL]]. Also, added 'proxy' option, using value from [[SiteProxy]] as prefix to permit cross-domain document access via server-side scripting.
2006.02.17 2.6.0 Removed "differences only" listbox display mode, replaced with selection filter 'presets': all/new/changes/differences. fixed init of "add new tags" checkbox
2006.02.16 2.5.4 added checkbox options to control "import remote tags" and "keep existing tags" behavior, in addition to existing "add new tags" functionality.
2006.02.14 2.5.3 FF1501 corrected unintended global 't' (loop index) in importReport() and autoImportTiddlers()
2006.02.10 2.5.2 corrected unintended global variable in importReport().
2006.02.05 2.5.1 moved globals from window.* to config.macros.importTiddlers.* to avoid FireFox 1.5.0.1 crash bug when referencing globals
2006.01.18 2.5.0 added checkbox for "create a report". Default is to create/update the ImportedTiddlers report.
2006.01.15 2.4.1 added "importPublic" tag and inverted default so that auto sharing is NOT done unless tagged with importPublic
2006.01.15 2.4.0 Added support for tagging tiddlers with importSkip, importReplace, and/or importPrivate to enable/disable overwriting or sharing with others when using auto-import macro syntax. Defaults: don't overwrite existing tiddlers, and allow your tiddlers to be auto-imported by others.
2006.01.15 2.3.2 Added "ask" parameter to confirm each tiddler before importing (for use with auto-importing)
2006.01.15 2.3.1 Strip TW core scripts from import source content and load just the storeArea into the hidden IFRAME to prevent imported document's core code from being invoked. Also, when importing local documents, use convertUTF8ToUnicode() to support international characters sets.
2006.01.12 2.3.0 Reorganized code to use callback function for loading import files to support event-driven I/O via an ASYNCHRONOUS XMLHttpRequest instead of waiting for remote hosts to respond to URL requests. Added non-interactive 'batch' mode, using macro parameters to specify source path/file or URL, and select tiddlers to import. Improved messages and added optional 'quiet' switch for batch mode to eliminate //most// feedback.
2006.01.11 2.2.0 Added "[by tags]" to list of tiddlers, based on code submitted by BradleyMeck
2006.01.08 2.1.0 IMPORT FROM ANYWHERE!!! re-write getImportedTiddlers() logic to either read a local file (using local I/O), OR... read a remote file, using a combination of XML and an iframe to permit cross-domain reading of DOM elements. Adapted from example code and techniques courtesy of Jonny LeRoy.
2006.01.06 2.0.2 When refreshing list contents, fixed check for tiddlerExists() when "show differences only" is selected, so that imported tiddlers that don't exist in the current file will be recognized as differences and included in the list.
2006.01.04 2.0.1 When "show differences only" is NOT checked, import all tiddlers that have been selected even when they have a matching title and date.
2005.12.27 2.0.0 Update for TW2.0
Defer initial panel creation and only register a notification function when panel first is created
2005.12.22 1.3.1 tweak formatting in importReport() and add 'discard report' link to output
2005.12.03 1.3.0 Dynamically create/remove importPanel as needed to ensure only one instance of interface elements exists, even if there are multiple instances of macro embedding. Also, dynamically create/recreate importFrame each time an external TW document is loaded for importation (reduces DOM overhead and ensures a 'fresh' frame for each document)
2005.11.29 1.2.1 fixed formatting of 'detail info' in importReport()
2005.11.11 1.2.0 added 'inline' param to embed controls in a tiddler
2005.11.09 1.1.0 only load HTML and CSS the first time the macro handler is called. Allows for redundant placement of the macro without creating multiple instances of controls with the same ID's.
2005.10.25 1.0.5 fixed typo in importReport() that prevented reports from being generated
2005.10.09 1.0.4 combined documentation with plugin code instead of using separate tiddlers
2005.08.05 1.0.3 moved CSS and HTML definitions into plugin code instead of using separate tiddlers
2005.07.27 1.0.2 core update 1.2.29: custom overlayStyleSheet() replaced with new core setStylesheet()
2005.07.23 1.0.1 added parameter checks and corrected addNotification() usage
2005.07.20 1.0.0 Initial Release
<<<
On Monday, November 15, 2010 11:55:39 AM, AdamWarden imported 3 tiddlers from
[[http://www.tiddly-twab.com|http://www.tiddly-twab.com]]:
<<<
#[[DataTiddlerPlugin]] - added
#[[FormTiddlerPlugin]] - added
#[[PartTiddlerPlugin]] - added
<<<
----
On Monday, November 15, 2010 11:43:56 AM, AdamWarden imported 1 tiddler from
[[http://www.tiddly-twab.com/#TwabPlugin|http://www.tiddly-twab.com/#TwabPlugin]]:
<<<
#[[TwabPlugin]] - added
<<<
----
On Monday, November 15, 2010 11:15:16 AM, AdamWarden imported 1 tiddler from
[[http://www.checkettsweb.com/tw/gtd_tiddlywiki.htm|http://www.checkettsweb.com/tw/gtd_tiddlywiki.htm]]:
<<<
#[[CalendarPlugin]] - added
<<<
----
On Monday, November 15, 2010 10:52:57 AM, AdamWarden imported 2 tiddlers from
[[http://www.checkettsweb.com/tw/gtd_tiddlywiki.htm|http://www.checkettsweb.com/tw/gtd_tiddlywiki.htm]]:
<<<
#[[PageTemplate]] - replaces PageTemplate - 11/11/2010 16:56:00 by Adam Warden
#[[StyleSheet]] - replaces StyleSheet - 11/13/2010 10:01:00 by Adam Warden
<<<
----
On Sunday, November 14, 2010 12:58:02 AM, Adam Warden imported 1 tiddler from
[[http://www.remotely-helpful.com/TiddlyWiki/TiddlerEncryptionPlugin.html|http://www.remotely-helpful.com/TiddlyWiki/TiddlerEncryptionPlugin.html]]:
<<<
#[[TiddlerEncryptionPlugin]] - added
<<<
----
On Saturday, November 13, 2010 10:47:01 PM, Adam Warden imported 2 tiddlers from
[[http://tiddlytools.com|http://tiddlytools.com]]:
<<<
#[[InlineJavascriptPlugin]] - added
#[[InlineJavascriptPluginInfo]] - added
<<<
----
On Saturday, November 13, 2010 10:28:39 PM, Adam Warden imported 2 tiddlers from
[[http://www.tiddlytools.com|http://www.tiddlytools.com]]:
<<<
#[[SinglePageModePlugin]] - added
#[[SinglePageModePluginInfo]] - added
<<<
----
On Saturday, November 13, 2010 12:12:17 PM, Adam Warden imported 2 tiddlers from
[[http://www.tiddlytools.com|http://www.tiddlytools.com]]:
<<<
#[[QuoteOfTheDayPlugin]] - added
#[[QuoteOfTheDayPluginInfo]] - added
<<<
----
On Saturday, November 13, 2010 10:55:07 AM, Adam Warden imported 2 tiddlers from
[[http://tiddlytools.com|http://tiddlytools.com]]:
<<<
#[[AllThumbs]] - added
#[[ThumbThing]] - added
<<<
----
On Saturday, November 13, 2010 10:20:42 AM, Adam Warden imported 2 tiddlers from
[[http://tiddlytools.com|http://tiddlytools.com]]:
<<<
#[[GotoPlugin]] - added
#[[GotoPluginInfo]] - added
<<<
----
On Saturday, November 13, 2010 10:14:27 AM, Adam Warden imported 1 tiddler from
[[http://www.tiddlytools.com|http://www.tiddlytools.com]]:
<<<
#[[ImageSizePlugin]] - added
<<<
----
On Saturday, November 13, 2010 9:56:11 AM, Adam Warden imported 1 tiddler from
[[http://www.tiddlytools.com|http://www.tiddlytools.com]]:
<<<
#[[StyleSheetShortcuts]] - added
<<<
----
On Saturday, November 13, 2010 9:11:08 AM, Adam Warden imported 1 tiddler from
[[http://www.tiddlytools.com|http://www.tiddlytools.com]]:
<<<
#[[GetTheFAQs]] - added
<<<
----
On Saturday, November 13, 2010 9:10:05 AM, Adam Warden imported 3 tiddlers from
[[http://www.tiddlytools.com|http://www.tiddlytools.com]]:
<<<
#[[GliderDance]] - added
#[[GliderGun]] - added
#[[TiddlyLifePlugin]] - added
<<<
/***
|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]]
!!!!!Revisions
<<<
2009.04.11 [1.9.5] pass current tiddler object into wrapper code so it can be referenced from within 'onclick' scripts
2009.02.26 [1.9.4] in $(), handle leading '#' on ID for compatibility with JQuery syntax
|please see [[InlineJavascriptPluginInfo]] for additional revision details|
2005.11.08 [1.0.0] initial release
<<<
!!!!!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|InlineJavascriptPluginInfo|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.4|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|documentation|
|Description|Documentation for InlineJavascriptPlugin|
''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.
!!!!!Usage
<<<
This plugin adds wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be recognized as embedded javascript code.
<script show>
/* javascript code goes here... */
</script>Every time the tiddler content is rendered, the javascript code is automatically evaluated, allowing you to invoke 'side-effect' processing and/or produce dynamically-generated content that is then inserted into the tiddler content, immediately following the script (see below). By including the optional ''show'' keyword as the final parameter in a {{{<script>}}} marker, the plugin will also include the script source code in the output that it displays in the tiddler. This is helpful when creating examples for documentation purposes (such as used in this tiddler!)
__''Deferred execution from an 'onClick' link''__
<script label="click here" title="mouseover tooltip text" key="X" show>
/* javascript code goes here... */
alert('you clicked on the link!');
</script>
By including a {{{label="..."}}} parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered. You may also include a {{{title="..."}}} parameter to specify the 'tooltip' text that will appear whenever the mouse is moved over the onClick link text, and a {{{key="X"}}} parameter to specify an //access key// (which must be a //single// letter or numeric digit only).
__''Loading scripts from external source files''__
<script src="URL" show>
/* optional javascript code goes here... */
</script>You can also load javascript directly from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}). This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins. The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.
In addition to loading the javascript from the external file, you can also use this feature to invoke javascript code contained within the {{{<script>...</script>}}} markers. This code is invoked //after// the external script file has been processed, and can make immediate use of the functions and/or global variables defined by the external script file.
>Note: To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that is rendered as soon as your TiddlyWiki document is opened, such as MainMenu. For example: put your {{{<script src="..."></script>}}} syntax into a separate 'library' tiddler (e.g., LoadScripts), and then add {{{<<tiddler LoadScripts>>}}} to MainMenu so that the library is loaded before any other tiddlers that rely upon the functions it defines.
>
>Normally, loading external javascript in this way does not produce any direct output, and should not have any impact on the appearance of your MainMenu. However, if your LoadScripts tiddler contains notes or other visible content, you can suppress this output by using 'inline CSS' in the MainMenu, like this: {{{@@display:none;<<tiddler LoadScripts>>@@}}}
<<<
!!!!!Creating dynamic tiddler content and accessing the ~TiddlyWiki DOM
<<<
An important difference between TiddlyWiki inline scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document: in a typical web document, you use the {{{document.write()}}} (or {{{document.writeln()}}}) function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.
However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and instead will //completely replace the entire ~TiddlyWiki document in your browser window (which is clearly not a good thing!)//. In order to allow scripts to use {{{document.write()}}}, the plugin automatically converts and buffers all HTML output so it can be safely inserted into your tiddler content, immediately following the script.
''Note that {{{document.write()}}} can only be used to output "pure HTML" syntax. To produce //wiki-formatted// output, your script should instead return a text value containing the desired wiki-syntax content'', which will then be automatically rendered immediately following the script. If returning a text value is not sufficient for your needs, the plugin also provides an automatically-defined variable, 'place', that gives the script code ''direct access to the //containing DOM element//'' into which the tiddler output is being rendered. You can use this variable to ''perform direct DOM manipulations'' that can, for example:
* generate wiki-formatted output using {{{wikify("...content...",place)}}}
* vary the script's actions based upon the DOM element in which it is embedded
* access 'tiddler-relative' DOM information using {{{story.findContainingTiddler(place)}}}
Note:
''When using an 'onclick' script, the 'place' element actually refers to the onclick //link text// itself, instead of the containing DOM element.'' This permits you to directly reference or modify the link text to reflect any 'stateful' conditions that might set by the script. To refer to the containing DOM element from within an 'onclick' script, you can use "place.parentNode" instead.
<<<
!!!!!Instant "bookmarklets"
<<<
You can also use an 'onclick' link to define a "bookmarklet": a small piece of javascript that can be ''invoked directly from the browser without having to be defined within the current document.'' This allows you to create 'stand-alone' commands that can be applied to virtually ANY TiddlyWiki document... even remotely-hosted documents that have been written by others!! To create a bookmarklet, simply define an 'onclick' script and then grab the resulting link text and drag-and-drop it onto your browser's toolbar (or right-click and use the 'bookmark this link' command to add it to the browser's menu).
Notes:
*When writing scripts intended for use as bookmarklets, due to the ~URI-encoding required by the browser, ''you cannot not use ANY double-quotes (") within the bookmarklet script code.''
*All comments embedded in the bookmarklet script must ''use the fully-delimited {{{/* ... */}}} comment syntax,'' rather than the shorter {{{//}}} comment syntax.
*Most importantly, because bookmarklets are invoked directly from the browser interface and are not embedded within the TiddlyWiki document, there is NO containing 'place' DOM element surrounding the script. As a result, ''you cannot use a bookmarklet to generate dynamic output in your document,'' and using {{{document.write()}}} or returning wiki-syntax text or making reference to the 'place' DOM element will halt the script and report a "Reference Error" when that bookmarklet is invoked.
Please see [[InstantBookmarklets]] for many examples of 'onclick' scripts that can also be used as bookmarklets.
<<<
!!!!!Special reserved function name
<<<
The plugin 'wraps' all inline javascript code inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler. To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.
<<<
!!!!!$(...) 'shorthand' function
<<<
As described by Dustin Diaz [[here|http://www.dustindiaz.com/top-ten-javascript/]], the plugin defines a 'shorthand' function that allows you to write:
{{{
$(id)
}}}
in place of the normal standard javascript syntax:
{{{
document.getElementById(id)
}}}
This function is provided merely as a convenience for javascript coders that may be familiar with this abbreviation, in order to allow them to save a few bytes when writing their own inline script code.
<<<
!!!!!Examples
<<<
simple dynamic output:
><script show>
document.write("The current date/time is: "+(new Date())+"<br>");
return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
dynamic output using 'place' to get size information for current tiddler:
><script show>
if (!window.story) window.story=window;
var title=story.findContainingTiddler(place).getAttribute("tiddler");
var size=store.getTiddlerText(title).length;
return title+" is using "+size+" bytes";
</script>
dynamic output from an 'onclick' script, using {{{document.write()}}} and/or {{{return "..."}}}
><script label="click here" show>
document.write("<br>The current date/time is: "+(new Date())+"<br>");
return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
creating an 'onclick' button/link that accesses the link text AND the containing tiddler:
><script label="click here" title="clicking this link will show an 'alert' box" key="H" show>
if (!window.story) window.story=window;
var txt=place.firstChild.data;
var tid=story.findContainingTiddler(place).getAttribute('tiddler');
alert('Hello World!\nlinktext='+txt+'\ntiddler='+tid);
</script>
dynamically setting onclick link text based on stateful information:
>{{block{
{{{
<script label="click here">
/* toggle "txtSomething" value */
var on=(config.txtSomething=="ON");
place.innerHTML=on?"enable":"disable";
config.txtSomething=on?"OFF":"ON";
return "\nThe current value is: "+config.txtSomething;
</script><script>
/* initialize onclick link text based on current "txtSomething" value */
var on=(config.txtSomething=="ON");
place.lastChild.previousSibling.innerHTML=on?"disable":"enable";
</script>
}}}
<script label="click here">
/* toggle "txtSomething" value */
var on=(config.txtSomething=="ON");
place.innerHTML=on?"enable":"disable";
config.txtSomething=on?"OFF":"ON";
return "\nThe current value is: "+config.txtSomething;
</script><script>
/* initialize onclick link text based on current "txtSomething" value */
var on=(config.txtSomething=="ON");
place.lastChild.innerHTML=on?"enable":"disable";
</script>
}}}
loading a script from a source url:
>http://www.TiddlyTools.com/demo.js contains:
>>{{{function inlineJavascriptDemo() { alert('Hello from demo.js!!') } }}}
>>{{{displayMessage('InlineJavascriptPlugin: demo.js has been loaded');}}}
>note: When using this example on your local system, you will need to download the external script file from the above URL and install it into the same directory as your document.
>
><script src="demo.js" show>
return "inlineJavascriptDemo() function has been defined"
</script>
><script label="click to invoke inlineJavascriptDemo()" key="D" show>
inlineJavascriptDemo();
</script>
<<<
!!!!!Revisions
<<<
2009.02.26 1.9.4 in $(), handle leading '#' on ID for compatibility with JQuery syntax
2008.06.11 1.9.3 added $(...) function as 'shorthand' for document.getElementById()
2008.03.03 1.9.2 corrected fallback declaration of wikifyPlainText() (fixes Safari "parse error")
2008.02.23 1.9.1 in onclick function, use string instead of array for 'bufferedHTML' (fixes IE errors)
2008.02.21 1.9.0 output from 'onclick' scripts (return value or document.write() calls) are now buffered and rendered into into a span following the script. Also, added default 'return false' handling if no return value provided (prevents HREF from being triggered -- return TRUE to allow HREF to be processed). Thanks to Xavier Verges for suggestion and preliminary code.
2008.02.14 1.8.1 added backward-compatibility for use of wikifyPlainText() in TW2.1.3 and earlier
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info tiddler
2007.12.28 1.8.0 added support for key="X" syntax to specify custom access key definitions
2007.12.15 1.7.0 autogenerate URI encoded HREF on links for onclick scripts. Drag links to browser toolbar to create bookmarklets. IMPORTANT NOTE: place is NOT defined when scripts are used as bookmarklets. In addition, double-quotes will cause syntax errors. Thanks to PaulReiber for debugging and brainstorming.
2007.11.26 1.6.2 when converting "document.write()" function calls in inline code, allow whitespace between "write" and "(" so that "document.write ( foobar )" is properly converted.
2007.11.16 1.6.1 when rendering "onclick scripts", pass label text through wikifyPlainText() to parse any embedded wiki-syntax to enable use of HTML entities or even TW macros to generate dynamic label text.
2007.02.19 1.6.0 added support for title="..." to specify mouseover tooltip when using an onclick (label="...") script
2006.10.16 1.5.2 add newline before closing '}' in 'function out_' wrapper. Fixes error caused when last line of script is a comment.
2006.06.01 1.5.1 when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
2006.04.19 1.5.0 added 'show' parameter to force display of javascript source code in tiddler output
2006.01.05 1.4.0 added support 'onclick' scripts. When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked. 'place' value is set to match the clicked button/link element.
2005.12.13 1.3.1 when catching eval error in IE, e.description contains the error text, instead of e.toString(). Fixed error reporting so IE shows the correct response text. Based on a suggestion by UdoBorkowski
2005.11.09 1.3.0 for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content. Based on a suggestion by BradleyMeck
2005.11.08 1.2.0 handle loading of javascript from an external URL via src="..." syntax
2005.11.08 1.1.0 pass 'place' param into scripts to provide direct DOM access
2005.11.08 1.0.0 initial release
<<<
/***
|''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);
}
//}}}
[[Home]]
[[AdamRocks Tiddly]]
[[About]]
[[Contact]]
<<calendar thismonth>>
There are a few accounts with multiple accounts within each account. For example, Aldi has at least 4 delivery locations within one building, and even though it is one account, just that location has 4 locations within that location.
Thats right, I said it, my wife ROCKS - I just had to say how I feel, and this is a good place to do just that.
This is a place where I will talk/brag about my wife, tell her how much I love her, and explain all the great food she makes too. What the heck, this is my tiddly and I can do what I want - ha ha.
Peas,
Adam
<div id='header'>
<div id='titleLine'>
<span id='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span id='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='sidebar'>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div><div id="licensePanel">
<a rel="license" href="http://shared.snapgrid.com/gtd_tiddlywiki.html#RevisionHistory" target="_new">GTDTW Version <span macro="version"></span></a>
<a rel="license" href="http://www.tiddlywiki.com" target="_new">
TiddlyWiki is published by Jeremy Ruston at Osmosoft under a BSD open source license</a>
<a rel="license" href="http://snapgrid.com" target="_new">GTD TiddlyWiki is a modification by Nathan Bowers at Snapgrid under the same license terms.</a>
<a rel="license" href="http://davidco.com" target="_new">"Getting Things Done" is © David Allen at Davidco. Davidco has no affiliation with TiddlyWiki or GTD TiddlyWiki.</a></div></div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
''Syntax:''
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any tiddler content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations".
Wrap every citation with a part and a proper name.
''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.</part>
<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.</part>
<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.</part>
}}}
You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|
<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}
Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}
Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler
sortBy 'tiddler.modified' descending
write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Code<html><a name="Code"/></html>
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
//{{{
//============================================================================
// PartTiddlerPlugin
// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {
version.extensions.PartTiddlerPlugin = {
major: 1, minor: 0, revision: 9,
date: new Date(2007, 6, 14),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};
if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
//============================================================================
// Common Helpers
// Looks for the next newline, starting at the index-th char of text.
//
// If there are only whitespaces between index and the newline
// the index behind the newline is returned,
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
var re = /(\n|[^\s])/g;
re.lastIndex = index;
var result = re.exec(text);
return (result && text.charAt(result.index) == '\n')
? result.index+1
: index;
}
//============================================================================
// Constants
var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";
//============================================================================
// Plugin Specific Helpers
// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
var params = paramText.readMacroParams();
if (params.length == 0 || params[0].length == 0) return null;
var name = params[0];
var paramsIndex = 1;
var hidden = false;
if (paramsIndex < params.length) {
hidden = params[paramsIndex] == "hidden";
paramsIndex++;
}
return {
partName: name,
isHidden: hidden
};
}
// Returns the match to the next (end or start) part tag in the text,
// starting the search at startIndex.
//
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
var re = new RegExp(partEndOrStartTagRE);
re.lastIndex = startIndex;
var match = re.exec(text);
return match;
}
//============================================================================
// Formatter
// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
if (!tagMatch) return false;
if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
// Parse the start tag parameters
var arguments = parseStartTagParams(tagMatch[3]);
if (!arguments) return false;
// Continue processing
var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
if (endMatch && endMatch[1]) {
if (!arguments.isHidden) {
w.nextMatch = startTagEndIndex;
w.subWikify(w.output,partEndTagREString);
}
w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
return true;
}
return false;
}
config.formatters.push( {
name: "part",
match: "<part\\s+[^>]+>",
handler: function(w) {
if (!handlePartSection(w)) {
w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
}
}
} )
//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers
// as tiddlers.
var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
var i = 0;
while (true) {
var tagMatch = findNextPartEndOrStartTagMatch(text, i);
if (!tagMatch) return null;
if (tagMatch[2]) {
// Is start tag
// Check the name
var arguments = parseStartTagParams(tagMatch[3]);
if (arguments && arguments.partName == partName) {
return tagMatch;
}
}
i = tagMatch.index+tagMatch[0].length;
}
}
// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler
// object, using fullName as the Tiddler's title.
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from
// the parentTiddler.
//
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
var text = parentTiddler.text;
var startTag = findPartStartTagByName(text, partName);
if (!startTag) return null;
var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
if (indexOfEndTag >= 0) {
var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
var partTiddler = new Tiddler();
partTiddler.set(
fullName,
partTiddlerText,
parentTiddler.modifier,
parentTiddler.modified,
parentTiddler.tags,
parentTiddler.created);
partTiddler.abegoIsPartTiddler = true;
return partTiddler;
}
return null;
}
// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
var oldFetchTiddler = store.fetchTiddler ;
store.fetchTiddler = function(title) {
var result = oldFetchTiddler.apply(this, arguments);
if (!result && title) {
var i = title.lastIndexOf('/');
if (i > 0) {
var parentName = title.substring(0, i);
var partName = title.substring(i+1);
var parent = (parentName == ".")
? store.resolveTiddler(currentParent)
: oldFetchTiddler.apply(this, [parentName]);
if (parent) {
return getPart(parent, partName, parent.title+"/"+partName);
}
}
}
return result;
};
};
// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag.
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
var oldRestartFunc = restart;
window.restart = function() {
hijackFetchTiddler();
oldRestartFunc.apply(this,arguments);
};
} else
hijackFetchTiddler();
// The user must not edit a readOnly/partTiddler
//
config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
// Tiddler.isReadOnly was introduced with TW 2.0.6.
// For older version we explicitly check the global readOnly flag
if (config.commands.editTiddler.oldIsReadOnlyFunction) {
if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
} else {
if (readOnly) return true;
}
return this.abegoIsPartTiddler;
}
config.commands.editTiddler.handler = function(event,src,title)
{
var t = store.getTiddler(title);
// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
// or the tiddler is not readOnly
if(!t || !t.abegoIsPartTiddler)
{
clearMessage();
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
story.focusTiddler(title,"text");
return false;
}
}
// To allow the "./partName" syntax in macros we need to hijack
// the invokeMacro to define the "currentParent" while it is running.
//
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
var oldCurrentParent = currentParent;
if (tiddler) currentParent = tiddler;
try {
oldInvokeMacro.apply(this, arguments);
} finally {
currentParent = oldCurrentParent;
}
}
window.invokeMacro = myInvokeMacro;
// To correctly support the "./partName" syntax while refreshing we need to hijack
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
//
(function() {
var oldTiddlerRefresher= config.refreshers.tiddler;
config.refreshers.tiddler = function(e,changeList) {
var oldCurrentParent = currentParent;
try {
currentParent = e.getAttribute("tiddler");
return oldTiddlerRefresher.apply(this,arguments);
} finally {
currentParent = oldCurrentParent;
}
};
})();
// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
var extendRelativeNames = function(e, title) {
var nodes = e.getElementsByTagName("a");
for(var i=0; i<nodes.length; i++) {
var node = nodes[i];
var s = node.getAttribute("content");
if (s && s.indexOf("./") == 0)
node.setAttribute("content",title+s.substr(1));
}
};
var oldHandler = config.macros.tabs.handler;
config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var result = oldHandler.apply(this,arguments);
if (tiddler)
extendRelativeNames(place, tiddler.title);
return result;
};
})();
// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
var tiddlerElem = null;
if (tiddler) {
tiddlerElem = document.getElementById(story.idPrefix + tiddler);
}
if (!tiddlerElem && evt) {
var target = resolveTarget(evt);
tiddlerElem = story.findContainingTiddler(target);
}
if (!tiddlerElem) return;
var children = tiddlerElem.getElementsByTagName("a");
for (var i = 0; i < children.length; i++) {
var child = children[i];
var name = child.getAttribute("name");
if (name == anchorName) {
var y = findPosY(child);
window.scrollTo(0,y);
return;
}
}
}
} // of "install only once"
//}}}
/***
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
/***
|''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"
});
*/
//}}}
PuTTY is the system used by Source One Office Products. The following is a guide for the most used codes that they use:
To enter the system, use your Username and Password then hit enter. Then type 3 and hit enter. The next screen you will choose the function.
To enter orders:
Type 1 for sales orders. Then type 1 for daily work and then 20 to enter the sales order section.
The way to learn to use TiddlyWiki is to forget that you don't know how to use TiddlyWiki...
----
"Knock, Knock"... "Who's there?"... "Oh! I guess you know that one..."
----
In theory, there is no difference between theory and practice. But, in practice, there is.
----
Sometimes when reading Goethe I have the paralyzing suspicion that he is trying to be funny.
----
A witty saying proves nothing.
----
The problem with some people is that when they aren't drunk, they're sober.
----
The great thing about television is that if something important happens anywhere in the world, day or night, you can always change the channel.
----
Don't be so humble - you are not that great.
----
The combination "ough" can be pronounced in nine different ways. The following sentence contains them all: "A rough-coated, dough-faced, thoughtful ploughman strode through the streets of Scarborough; after falling into a slough, he coughed and hiccoughed."
----
If you lived here, you'd be home by now...
----
I love my dust. It shows I had something better to do.
----
Time flies like an arrow... Fruit flies like a banana [Groucho Marx]
----
If you don't know where you're going, you'll probably end up somewhere else.
----
Weebles wobble... but they don't fall down!
----
It's all very simple... or else it's all very complex... or perhaps it's neither... or both.
----
Accepting reality is the first step to insanity
----
Oops! Sorry. My karma ran over your dogma.
----
If it looks like a duck, walks like a duck and sounds like a duck, it's probably something else.
----
Some times you feel like a nut, some times you bolt.
----
Santaclaustrophobia... The feeling that Christmas is closing in all around you.
----
Why is abbreviation such a long word?
----
Diplomacy is the art of saying 'Nice doggie!'... until you can find a rock.
----
Scientists working on cloning technology have done what the american educational system has been doing for years - producing identical sheep.
----
Umm... What's another word for thesaurus?
----
Give me ambiguity or give me something else
----
Don't anthropomorphize your computer, it doesn't like it.
----
Ninth Law of Cartoon Physics: Everything falls faster than an anvil.
----
Oh yeah? Far out!
----
Boycott Shampoo - Demand the REAL poo!
----
Remember: Don't sweat the petty things... and don't pet the sweaty things.
----
Sign in lunch room: "Shoes are required to eat in the cafeteria..." (and hand-written below: "...socks can eat anywhere they want.")
----
Don't be irreplaceable, if you can't be replaced, you can't be promoted.
----
5 out of 4 people have problems with fractions...
----
Osborne's Law of Mathematics: Variables won't; constants aren't.
----
Always remember you're unique, just like everyone else.
----
Something to ponder: What was the best thing *before* sliced bread? Hmmm...
----
Physics Joke #27: Two hydrogen atoms bump into each other while walking down the street: 'Are you alright?'... 'No, I lost an electron!'... 'Are you sure?'... 'Yeah, I'm Positive!'
----
The one thing I can't stand is intolerant people!
----
Old programmers never die. . . they just can't C as well.
----
World Shortest 'Bar' joke: A guy walks into a bar... OUCH! he says...
----
Never judge a man 'til you have walked a mile in his shoes, because by then, he's a mile away, you've got his shoes, and you can say whatever the hell you want to.
----
The problem with troubleshooting is that real trouble shoots back.
----
These are the roots of rhythm: boom, de boom, de-boom, ding-dong, ooh! [Leila ~Gallagher-Breen]
----
These are the roots of music: boom-chicky-chicky boom, meow, meow, baddom, baddom, ding-aling-a-long, chicky-boom! [Leila ~Gallagher-Breen]
----
Classified ad (in Physics Quarterly): Got Mole problems? Call Avagadro Exterminators: 602-1023
----
He who laughs last thinks slowest
----
Would a fly without wings be called a walk?
----
The winner of the World's Worst Analogy contest: 'The little boat glided across the water in exactly the way a bowling ball wouldn't.'
----
From the 'say what?!?' files: We are ready for any unforeseen event that may or may not occur. [Dan Quayle 9/22/90]
----
I believe you should live each day as if it is your last, which is why I don't have any clean laundry because, come on, who wants to wash clothes on the last day of their life? [Jack Handy]
----
PRINTER - A device consisting of three main parts: the case, the jammed paper tray, and the blinking red light.
----
Dilbert's Laws of Work: The more crap you put up with, the more crap you are going to get.
----
Q: Why'd the electron cross the road? A: 'Cause he was already on the other side...
----
When the going gets tough, remember STRESSED spelled backwards is DESSERTS.
----
Foobar... baz. Mumble: frotz, gronk, snork. Snerfle? Natter and gromish!
----
Pentiums melt in your PC, not in your hand.
----
everyone is cute... even me. But in purple... I'm STUNNING!! [Londo Mollari]
----
I always wanted to be somebody, but I should have been more specific. [Lily Tomlin]
----
It's OK to change horses in mid-stream, especially if the first horse can't swim! [Laura Shulman]
----
Work is the curse of the drinking classes. [Oscar Wilde]
----
We have only two things to worry about: That things will never get back to normal, and that they already have.
----
Intelligence is a funny thing: those who don't have it think they do, and those who do are smart enough to know better.
----
Life is hard, Love is strange. Nothing lasts, people change. Dream of a long and wonderful future, but live now.
----
'Can you prove any of the stuff you believe in?', my friend asked. When I said that's not how belief works, he nodded and said that's what he thought but he was just checking to make sure he hadn't missed a key point.
----
Life ... It's a good place to be!
----
Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail.
----
History is made at night... character is what you are in the dark. [Dr. Emilio Lizardo, aka Lord John Worfin]
----
You can't always get what you want... but if you try sometimes, you just may find, that you get what you need. [The Rolling Stones]
----
The only man who behaved sensibly was my tailor; he took my measurement anew every time he saw me, while all the rest went on with their old measurements and expected them to fit me. [George Bernard Shaw]
----
In matters of style, swim with the current: in matters of principle, stand like a rock. [Thomas Jefferson]
----
Great things are not done by impulse, but by a series of small things brought together. [Vincent van Gogh]
----
'I think there is a world market for maybe five computers.' [Thomas Watson, chairman of IBM, 1943]
----
We have to live today by what truth we can get today, and be ready tomorrow to call it falsehood. [William James]
----
There are two ways of spreading light: to be the candle or the mirror that reflects it. [Edith Wharton]
----
Talent develops in tranquility, character in the full current of human life. [Johann Wolfgang von Goethe (1749-1832)]
----
Our firmest convictions are apt to be the most suspect; they mark our limitations and our bounds. Life is a petty thing unless it is moved by the indomitable urge to extend its boundaries. [Jose Ortega y Gasset]
----
It takes courage to grow up and turn out to be who you really are. [e e cummings (1894-1962)]
----
If we knew what it was we were doing, it would not be called research, would it? [Albert Einstein]
----
To invent, you need a good imagination and a pile of junk. [Thomas Edison]
----
Dare to be naive. [R. Buckminster Fuller]
----
Quiet minds cannot be perplexed or frightened, but go on in fortune or misfortune at their own private pace, like a clock during a thunderstorm. [Robert Louis Stevenson]
----
In the struggle between yourself and the world, second the world. [Franz Kafka]
----
Every man's condition is a solution in hieroglyph to those inquiries he would put. He acts it as life before he apprehends it as truth. [Ralph Waldo Emerson]
----
One can remain alive...if one is unafraid of change, insatiable in intellectual curiosity, interested in big things, and happy in small ways. [Edith Wharton]
----
a rock is like space because it doesnt move; and space is like a rock because it is empty. words are buddhas. [Jack Kerouac]
----
The whole problem with the world is that fools and fanatics are always so certain of themselves, and wiser people so full of doubts. [Bertrand Russell]
----
Doubt is not a pleasant state of mind, but certainty is absurd. [Voltaire]
----
Education is not the filling of a pail, but the lighting of a fire. [William Butler Yeats]
----
If you take yourself too seriously, no one else will take you seriously enough... [Eric Shulman]
----
There are only four questions of value in life... What is sacred? Of what is the spirit made? What is worth living for, and what is worth dying for? The answer to each is the same: only love. [Don Juan ~DeMarco]
----
If you are a dreamer, come in. If you are a dreamer, a wisher, a liar, a hope-er, a pray-er, a magic bean buyer... If you're a pretender, come sit by my fire... For we have some flax-golden tales to spin. Come in! Come in! [Shel Silverstein]
----
O money, money, money, I'm not necessarily one of those who think thee holy, But I often stop to wonder how thou canst go out so fast when thou comest in so slowly. [Ogden Nash]
----
There is no progress without struggle. [Frederick Douglass]
----
Before you can do something, you must become something. [Goethe]
----
The man who follows the crowd will usually get no further than the crowd. The man who walks alone is likely to find himself in places no one has ever been. [Alan ~Ashley-Pit]
----
Weep not for the dead for they are but empty cages from which the bird has flown. [Tibetan Book of The Dead]
----
I wish it would dawn upon engineers that, in order to be an engineer, it is not enough to be an engineer. [Jose Ortega y Gasset]
----
Every exit is an entry somewhere. [Tom Stoppard]
----
Wherever we are, it is but a stage on the way to somewhere else, and whatever we do, however well we do it, it is only a preparation to do something else that shall be different. [Robert Louis Stevenson]
----
We judge ourselves by what we feel capable of doing, while others judge us by what we have already done. [Henry Wadsworth Longfellow]
----
All our words are but crumbs that fall down from the feast of the mind. [Kahlil Gibran, "Sand and Foam"]
----
It's a lot harder to find joy than it is to find bread. Feed your soul first, then go shopping. [Eric Shulman]
----
Why do you hasten to remove anything which hurts your eye, while if something affects your soul you postpone the cure until next year? [Horace]
----
Not to know is bad. Not to want to know is worse. Not to hope is unthinkable. Not to care is unforgivable. [Nigerian folk saying]
----
Only half of writing is saying what you mean. The other half is preventing people from reading what they expected you to mean. [James Richardson, 'Ploughshares']
----
There are no shortcuts to anyplace worth going. [Beverly Sills]
----
Somewhere, something incredible is waiting to be known. [Carl Sagan]
----
Courage is the price that Life extracts for granting peace. [Amelia Earhart]
----
Great things are not done by impulse, but by a series of small things brought together. [Vincent van Gogh]
----
Choose a job you love, and you will never have to work a day in your life. [Confucius]
----
There is only one success - to be able to spend your life in your own way. [Christopher Morley]
----
We choose our joys and sorrows long before we experience them. [Kahlil Gibran]
----
It is all a frame of mind, this enjoyment of living. [Lin Yutang]
----
Work for something because it is good, not just because it stands a chance to succeed. [Vaclav Havel]
----
The easiest way to get what you want is to help others get what they want. [Deepak Chopra]
----
All generalizations are false, including this one. [Bucky Radfish]
----
In science, 'fact' can only mean 'confirmed to such a degree that it would be perverse to withhold provisional assent.' I suppose that apples might start to rise tomorrow, but the possibility does not merit equal time in physics classrooms. [Stephen Jay Gould (1941 - 2002)]
----
Twenty years from now, you will be more disappointed by the things you didn't do, than by the ones you did. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore, dream... [Mark Twain]
----
To live only for some future goal is shallow. It's the sides of the mountain that sustain life, not the top. [Robert M. Pirsig]
----
Nothing is so contagious as enthusiasm; it moves stones, it charms brutes. Enthusiasm is the genius of sincerity, and truth accomplishes no victories without it. [Edward G. E. ~Bulwer-Lytton (1803 - 1873)]
----
The past is but the beginning of a beginning, and all that is and has been is but the twilight of the dawn. [Herbert George Wells]
----
We grow great by dreams. All big men are dreamers. They see things in the soft haze of a spring day or in the red fire of a long winter's evening. Some of us let these great dreams die, but others nourish and protect them; nurse them through bad days till they bring them to the sunshine and light, which comes always to those who sincerely hope that their dreams will come true. [Woodrow Wilson]
----
Be brave enough to live life creatively. The creative is the place where no one else has ever been. You have to leave the city of your comfort and go into the wilderness of your intuition. You can't get there by bus, only by hard work and risk and by not quite knowing what you're doing. What you'll discover will be wonderful. What you'll discover will be yourself. [Alan Alda]
----
If we had no winter, the spring would not be so pleasant; if we did not sometimes taste of adversity, prosperity would not be so welcome.
----
Solitude, if rightly used, becomes not only a privilege but a necessity. Only a superficial soul fears to fraternize with itself.
----
One of the symptoms of an approaching nervous breakdown is the belief that one's work is terribly important.
----
Hope is like a road in the country: there was never a road, but when many people walk on it, the road comes into existence.
----
You can only find truth with logic if you have already found truth without it.
----
Love is always bestowed as a gift - freely, willingly and without expectation. We don't love to be loved; we love to love.
----
Always listen to experts. They'll tell you what can't be done, and why. Then do it.
----
Distance between two hearts is not an obstacle; rather a great reminder of just how strong true love can be.
----
Not everything that can be counted counts, and not everything that counts can be counted.
----
Life's challenges are not supposed to paralyze you, they're supposed to help you discover who you are.
----
Nothing splendid has ever been achieved except by those who dared believe that something inside them was superior to circumstances.
----
Whether you think that you can, or that you can't, you are usually right.
----
The only time you run out of chances is when you stop taking them.
----
Knowledge speaks, but wisdom listens.
----
Too often we give our children answers to remember rather than problems to solve.
----
Books are the shoes with which we tread the footsteps of great minds. A book may lie dormant for fifty years or for two thousand years in a forgotten corner of a library, only to reveal, upon being opened, the marvels or the abysses that it contains, or the line that seems to have been written for me alone. In this respect the writer is not different from any other human being: whatever we say or do can have far-reaching consequences.
----
A people that values its privileges above its principles soon loses both.
----
Treat everyone with politeness, even those who are rude to you. Not because they are nice, but because you are.
----
The ultimate measure of a man is not where he stands in moments of comfort and convenience, but where he stands in times of challenge and controversy. What lies behind us and what lies before us, are only small matters compared to what lies within us.
----
In your presence even my shadow acquires the sensation of touch.
----
Being in charge of your own reality is a little tougher; it takes a lot of thinking and brutal honesty. [Phil Glatz]
----
There are always going to be amazingly creative people doing wonderful things. [Phil Glatz]
----
The trick is to rise above the symptoms of being aware, and actually BE aware. [Phil Glatz]
----
A revolution is coming - a revolution which will be peaceful if we are wise enough; compassionate if we care enough; successful if we are fortunate enough - but a revolution which is coming whether we will it or not. We can affect its character, we cannot alter its inevitability. [John F. Kennedy]
----
On two occasions I have been asked (by members of Parliament!), 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question. [Charles Babbage]
----
Those parts of the system that you can hit with a hammer (not advised) are called hardware; those program instructions that you can only curse at are called software.
----
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies, and the other way to make it so complicated that there are no obvious deficiencies. The first method is far more difficult. [Charles A. R. Hoare]
/***
|Name|QuoteOfTheDayPlugin|
|Source|http://www.TiddlyTools.com/#QuoteOfTheDayPlugin|
|Documentation|http://www.TiddlyTools.com/#QuoteOfTheDayPluginInfo|
|Version|1.4.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Display a randomly selected "quote of the day" from a list defined in a separate tiddler|
!!!!!Documentation
>see [[QuoteOfTheDayPluginInfo]]
!!!!!Revisions
<<<
2008.03.21 [1.4.1] in showNextItem(), corrected handling for random selection so that //initial// index value will randomized correctly instead of always showing first item, even when randomizing. Thanks to Riccardo Gherardi for finding this.
| Please see [[QuoteOfTheDayPluginInfo]] for previous revision details |
2005.10.21 [1.0.0] Initial Release. Based on a suggestion by M.Russula
<<<
!!!!!Code
***/
//{{{
version.extensions.QuoteOfTheDayPlugin= {major: 1, minor: 4, revision: 1, date: new Date(2008,3,21)};
config.macros.QOTD = {
clickTooltip: "click to view another item",
timerTooltip: "auto-timer stopped... 'mouseout' to restart timer",
timerClickTooltip: "auto-timer stopped... click to view another item, or 'mouseout' to restart timer",
handler:
function(place,macroName,params) {
var tid=params.shift(); // source tiddler containing HR-separated quotes
var p=params.shift();
var click=true; // allow click for next item
var inline=false; // wrap in slider for animation effect
var random=true; // pick an item at random (default for "quote of the day" usage)
var folder=false; // use local filesystem folder list
var cookie=""; // default to no cookie
var next=0; // default to first item (or random item)
while (p) {
if (p.toLowerCase()=="noclick") var click=false;
if (p.toLowerCase()=="inline") var inline=true;
if (p.toLowerCase()=="norandom") var random=false;
if (p.toLowerCase().substr(0,7)=="cookie:") var cookie=p.substr(8);
if (!isNaN(p)) var delay=p;
p=params.shift();
}
if ((click||delay) && !inline) {
var panel = createTiddlyElement(null,"div",null,"sliderPanel");
panel.style.display="none";
place.appendChild(panel);
var here=createTiddlyElement(panel,click?"a":"span",null,"QOTD");
}
else
var here=createTiddlyElement(place,click?"a":"span",null,"QOTD");
here.id=(new Date()).convertToYYYYMMDDHHMMSSMMM()+Math.random().toString(); // unique ID
// get items from tiddler or file list
var list=store.getTiddlerText(tid,"");
if (!list||!list.length) { // not a tiddler... maybe an image directory?
var list=this.getImageFileList(tid);
if (!list.length) { // maybe relative path... fixup and try again
var h=document.location.href;
var p=getLocalPath(decodeURIComponent(h.substr(0,h.lastIndexOf("/")+1)));
var list=this.getImageFileList(p+tid);
}
}
if (!list||!list.length) return false; // no contents... nothing to display!
here.setAttribute("list",list);
if (delay) here.setAttribute("delay",delay);
here.setAttribute("random",random);
here.setAttribute("cookie",cookie);
if (click) {
here.title=this.clickTooltip
if (!inline) here.style.display="block";
here.setAttribute("href","javascript:;");
here.onclick=function(event)
{ config.macros.QOTD.showNextItem(this); }
}
if (config.options["txtQOTD_"+cookie]!=undefined) next=parseInt(config.options["txtQOTD_"+cookie]);
here.setAttribute("nextItem",next);
config.macros.QOTD.showNextItem(here);
if (delay) {
here.title=click?this.timerClickTooltip:this.timerTooltip
here.onmouseover=function(event)
{ clearTimeout(this.ticker); };
here.onmouseout=function(event)
{ this.ticker=setTimeout("config.macros.QOTD.tick('"+this.id+"')",this.getAttribute("delay")); };
here.ticker=setTimeout("config.macros.QOTD.tick('"+here.id+"')",delay);
}
},
tick: function(id) {
var here=document.getElementById(id); if (!here) return;
config.macros.QOTD.showNextItem(here);
here.ticker=setTimeout("config.macros.QOTD.tick('"+id+"')",here.getAttribute("delay"));
},
showNextItem:
function (here) {
// hide containing slider panel (if any)
var p=here.parentNode;
if (p.className=="sliderPanel") p.style.display = "none"
// get a new quote
var index=here.getAttribute("nextItem");
var items=here.getAttribute("list").split("\n----\n");
if (index<0||index>=items.length) index=0;
if (here.getAttribute("random")=="true") index=Math.floor(Math.random()*items.length);
var txt=items[index];
// re-render quote display element, and advance index counter
removeChildren(here); wikify(txt,here);
index++; here.setAttribute("nextItem",index);
var cookie=here.getAttribute("cookie");
if (cookie.length) {
config.options["txtQOTD_"+cookie]=index.toString();
saveOptionCookie("txtQOTD_"+cookie);
}
// redisplay slider panel (if any)
if (p.className=="sliderPanel") {
if(anim && config.options.chkAnimate)
anim.startAnimating(new Slider(p,true,false,"none"));
else p.style.display="block";
}
},
getImageFileList: function(cwd) { // returns HR-separated list of image files
function isImage(fn) {
var ext=fn.substr(fn.length-3,3).toLowerCase();
return ext=="jpg"||ext=="gif"||ext=="png";
}
var files=[];
if (config.browser.isIE) {
cwd=cwd.replace(/\//g,"\\");
// IE uses ActiveX to read filesystem info
var fso = new ActiveXObject("Scripting.FileSystemObject");
if(!fso.FolderExists(cwd)) return [];
var dir=fso.GetFolder(cwd);
for(var f=new Enumerator(dir.Files); !f.atEnd(); f.moveNext())
if (isImage(f.item().path)) files.push("[img[%0]]".format(["file:///"+f.item().path.replace(/\\/g,"/")]));
} else {
// FireFox (mozilla) uses "components" to read filesystem info
// get security access
if(!window.Components) return;
try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); }
catch(e) { alert(e.description?e.description:e.toString()); return []; }
// open/validate directory
var file=Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
try { file.initWithPath(cwd); } catch(e) { return []; }
if (!file.exists() || !file.isDirectory()) { return []; }
var folder=file.directoryEntries;
while (folder.hasMoreElements()) {
var f=folder.getNext().QueryInterface(Components.interfaces.nsILocalFile);
if (f instanceof Components.interfaces.nsILocalFile)
if (isImage(f.path)) files.push("[img[%0]]".format(["file:///"+f.path.replace(/\\/g,"/")]));
}
}
return files.join("\n----\n");
}
}
//}}}
/***
|Name|QuoteOfTheDayPluginInfo|
|Source|http://www.TiddlyTools.com/#QuoteOfTheDayPlugin|
|Documentation|http://www.TiddlyTools.com/#QuoteOfTheDayPluginInfo|
|Version|1.4.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|documentation|
|Description|Documentation for QuoteOfTheDayPlugin|
!!!!!Usage
<<<
{{{<<QOTD tiddlername norandom noclick inline cookie:cookiename delay>>}}}
* ''tiddlername'' is the name of a tiddler containing your list of quotes, each separated by a horizontal line (use {{{----}}} on a line by itself).
* Each time the macro is rendered it will display a different quotation, selected at random from the specified tiddler. To display quotes in the sequence in which they occur in the tiddler, you can use the ''norandom'' keyword.
* When using ''norandom'', you can also specify an optional ''cookie:cookiename'' parameter which will be used to track the //index// of the next quote to be displayed, so that each subsequent rendering of the macro can continue the sequence of quotes as entered in the source tiddler, even in between browser sessions.
* By default, clicking on the rendered quote will select and display another random quote. Use the optional ''noclick'' keyword parameter to disable this "onClick" handling.
* By default, a clickable or timed quote will be displayed insider a 'slider' panel, so that standard TW animation effects will be used. However, slider panels are always rendered as "block-level" content, forcing a newline both before and after the slider panel. Use the ''inline'' keyword parameter to bypass this side-effect and display a clickable/timed quote without automatically adding surrounding linebreaks.
* The quote can also be refreshed automatically, by specifying a numeric ''delay'' parameter (in milliseconds) which enables a countdown timer. When the mouse is over the quote, the timer is automatically stopped. Moving the mouse away from the quote content restarts the timer.
<<<
!!!!!Example
<<<
{{{<<QOTD Quotations 10000>>}}}
<<QOTD Quotations 10000>>
<<<
!!!!!Revisions
<<<
2008.03.21 1.4.1 in showNextItem(), corrected handling for random selection so that //initial// index value will randomized correctly instead of always showing first item, even when randomizing. Thanks to Riccardo Gherardi for finding this.
2008.01.16 1.4.0 support using a local image file directory instead of tiddler name for getting list of items. If specified tiddler does not exist in the document, macro attempts to use tiddlername as a local directory name (using either absolute or relative path) and get list of all JPG/GIF/PNG files.
2007.08.06 1.3.0 added support for "cookie:cookiename" param
2007.05.03 1.2.1 corrected logic for handling "inline" display (i.e., bypass slider and use of 'block' for quote elements)
2007.05.03 1.2.0 added sliderPanel wrapper around quote to take advantage of core-supported slider animation. Use "noslider"/"inline" keyword param to suppress use of slider. Also added tooltips for click, delay, and click+delay modes.
2007.04.16 1.1.2 code cleanup
2007.04.16 1.1.1 onClick handling now supports sequential as well as random order
2007.04.14 1.1.0 added onClick handling for selecting and display a new random quote
2005.10.21 1.0.0 Initial Release. Based on a suggestion by M.Russula
<<<
config.macros.selectTheme = {
label: {
selectTheme:"select theme",
selectPalette:"select palette"
},
prompt: {
selectTheme:"Select the current theme",
selectPalette:"Select the current palette"
},
tags: {
selectTheme:'systemTheme',
selectPalette:'systemPalette'
}
};
config.macros.selectTheme.handler = function(place,macroName)
{
var btn = createTiddlyButton(place,this.label[macroName],this.prompt[macroName],this.onClick);
// want to handle palettes and themes with same code. use mode attribute to distinguish
btn.setAttribute('mode',macroName);
};
config.macros.selectTheme.onClick = function(ev)
{
var e = ev ? ev : window.event;
var popup = Popup.create(this);
var mode = this.getAttribute('mode');
var tiddlers = store.getTaggedTiddlers(config.macros.selectTheme.tags[mode]);
// for default
if (mode == "selectPalette") {
var btn = createTiddlyButton(createTiddlyElement(popup,'li'),"(default)","default color palette",config.macros.selectTheme.onClickTheme);
btn.setAttribute('theme',"(default)");
btn.setAttribute('mode',mode);
}
for(var i=0; i<tiddlers.length; i++) {
var t = tiddlers[i].title;
var name = store.getTiddlerSlice(t,'Name');
var desc = store.getTiddlerSlice(t,'Description');
var btn = createTiddlyButton(createTiddlyElement(popup,'li'), name?name:t, desc?desc:config.macros.selectTheme.label['mode'], config.macros.selectTheme.onClickTheme);
btn.setAttribute('theme',t);
btn.setAttribute('mode',mode);
}
Popup.show();
return stopEvent(e);
};
config.macros.selectTheme.onClickTheme = function(ev)
{
var mode = this.getAttribute('mode');
var theme = this.getAttribute('theme');
if (mode == 'selectTheme')
story.switchTheme(theme);
else // selectPalette
config.macros.selectTheme.updatePalette(theme);
return false;
};
config.macros.selectTheme.updatePalette = function(title)
{
if (title != "") {
store.deleteTiddler("ColorPalette");
if (title != "(default)")
store.saveTiddler("ColorPalette","ColorPalette",store.getTiddlerText(title),
config.options.txtUserName,undefined,"");
refreshAll();
if(config.options.chkAutoSave)
saveChanges(true);
}
};
config.macros.applyTheme = {
label: "apply",
prompt: "apply this theme or palette" // i'm lazy
};
config.macros.applyTheme.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var useTiddler = params[0] ? params[0] : tiddler.title;
var btn = createTiddlyButton(place,this.label,this.prompt,config.macros.selectTheme.onClickTheme);
btn.setAttribute('theme',useTiddler);
btn.setAttribute('mode',macroName=="applyTheme"?"selectTheme":"selectPalette"); // a bit untidy here
}
config.macros.selectPalette = config.macros.selectTheme;
config.macros.applyPalette = config.macros.applyTheme;
config.macros.refreshAll = { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
createTiddlyButton(place,"refresh","refresh layout and styles",function() { refreshAll(); });
}};
{{button{goto}}}
<<gotoTiddler>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<tiddler TspotSidebar>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
<<slider chkContents SideBarTabs##contents contents contents>>/%
!contents
<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>
!end
%/
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)
Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2008.10.17 [2.9.6] changed chkSinglePageAutoScroll default to false
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 [1.0.0] Initial Release. Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 6, date: new Date(2008,10,17)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
config.options.chkSinglePageMode=eval(v);
if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
config.lastURL = window.location.hash;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
config.options.chkSinglePageMode=false;
if (config.options.chkSinglePagePermalink==undefined)
config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
config.options.chkSinglePageAutoScroll=false;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
if (!config.options.chkSinglePageMode)
{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
if (config.lastURL == window.location.hash) return; // no change in hash
var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
if (tids.length==1) // permalink (single tiddler in URL)
story.displayTiddler(null,tids[0]);
else { // restore permaview or default view
config.lastURL = window.location.hash;
if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
story.closeAllTiddlers();
story.displayTiddlers(null,tids);
}
}
if (Story.prototype.SPM_coreDisplayTiddler==undefined)
Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
var tiddlerElem=document.getElementById(story.idPrefix+title); // ==null unless tiddler is already displayed
var opt=config.options;
var single=opt.chkSinglePageMode && !startingUp;
var top=opt.chkTopOfPageMode && !startingUp;
var bottom=opt.chkBottomOfPageMode && !startingUp;
if (single) {
story.forEachTiddler(function(tid,elem) {
// skip current tiddler and, optionally, tiddlers that are folded.
if ( tid==title
|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
return;
// if a tiddler is being edited, ask before closing
if (elem.getAttribute("dirty")=="true") {
if (opt.chkSinglePageKeepEditedTiddlers) return;
// if tiddler to be displayed is already shown, then leave active tiddler editor as is
// (occurs when switching between view and edit modes)
if (tiddlerElem) return;
// otherwise, ask for permission
var msg="'"+tid+"' is currently being edited.\n\n";
msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
if (!confirm(msg)) return; else story.saveTiddler(tid);
}
story.closeTiddler(tid);
});
}
else if (top)
arguments[0]=null;
else if (bottom)
arguments[0]="bottom";
if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
config.lastURL = window.location.hash;
document.title = wikifyPlain("SiteTitle") + " - " + title;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
var isTopTiddler=(tiddlerElem.previousSibling==null);
if (!isTopTiddler && (single || top))
tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
else if (bottom)
tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
} else
this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
var tiddlerElem=document.getElementById(story.idPrefix+title);
if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
// scroll to top of page or top of tiddler
var isTopTiddler=(tiddlerElem.previousSibling==null);
var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
// if animating, defer scroll until after animation completes
var delay=opt.chkAnimate?config.animDuration+10:0;
setTimeout("window.scrollTo(0,"+yPos+")",delay);
}
}
if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
// suspend single/top/bottom modes when showing multiple tiddlers
var opt=config.options;
var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
this.SPM_coreDisplayTiddlers.apply(this,arguments);
opt.chkBottomOfPageMode=saveBPM;
opt.chkTopOfPageMode=saveTPM;
opt.chkSinglePageMode=saveSPM;
}
//}}}
/***
|Name|SinglePageModePluginInfo|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|documentation|
|Description|Documentation for SinglePageModePlugin|
Normally, as you click on the links in TiddlyWiki, more and more tiddlers are displayed on the page. The order of this tiddler display depends upon when and where you have clicked. Some people like this non-linear method of reading the document, while others have reported that when many tiddlers have been opened, it can get somewhat confusing. SinglePageModePlugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one item displayed at a time.
!!!!!Usage
<<<
When the plugin is enabled, only one tiddler will be displayed at a time and the browser window's titlebar is updated to include the current tiddler title. The browser's location URL is also updated with a 'permalink' for the current tiddler so that it is easier to create a browser 'bookmark' for the current tiddler. Alternatively, even when displaying multiple tiddlers //is// permitted, you can still reduce the potential for confusion by forcing tiddlers to always open at the top (or bottom) of the page instead of being displayed following the tiddler containing the link that was clicked.
<<<
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)
Notes:
* {{block{
The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}. You can also use {{{SPM:expression}}}, where 'expression' is any javascript statement that evaluates to true or false. This allows you to create hard-coded links in other documents that can selectively enable/disable the use of this option based on various programmatic conditions, such as the current username. For example, using
{{{#SPM:config.options.txtUserName!="SomeName"}}}
enables 'one tiddler at a time' display for all users //other than// "~SomeName")}}}
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2008.10.17 2.9.6 changed chkSinglePageAutoScroll default to false
2008.06.12 2.9.5 corrected 'scroll to top of page' logic in auto-scroll handling
2008.06.11 2.9.4 added chkSinglePageKeepEditedTiddlers option
2008.06.05 2.9.3 in displayTiddler(), bypass single/top/bottom mode handling if startingUp. Allows multiple tiddlers to be displayed during startup processing (e.g., #story:DefaultTiddlers), even if single/top/bottom mode is enabled.
2008.04.18 2.9.2 in displayTiddler() and checkLastURL(), handling for Unicode in tiddler titles (remove explicit conversion between Unicode and UTF, as this is apparently done automatically by encode/decodeURIComponent, resulting in double-encoding!
2008.04.08 2.9.1 don't automatically add options to AdvancedOptions shadow tiddler
2008.04.02 2.9.0 in displayTiddler(), when single-page mode is in use and a tiddler is being edited, ask for permission to save-and-close that tiddler, instead of just leaving it open.
2008.03.29 2.8.3 in displayTiddler(), get title from tiddler object (if needed). Fixes errors caused when calling function passes a tiddler *object* instead of a tiddler *title*
2008.03.14 2.8.2 in displayTiddler(), if editing specified tiddler, just move it to top/bottom of story *without* re-rendering (prevents discard of partial edits).
2008.03.06 2.8.1 in paramifier handler, start 'checkURL' timer if chkSinglePageMode is enabled
2008.03.06 2.8.0 added option, {{{config.options.chkSinglePageKeepFoldedTiddlers}}}, so folded tiddlers won't be closed when using single-page mode. Also, in checkURL(), if hash is a ''permaview'' (e.g., "#foo bar baz"), then display multiple tiddlers rather than attempting to display "foo bar baz" as a single tiddler
2008.03.05 2.7.0 added support for "SPM:" URL paramifier
2008.03.01 2.6.0 in hijack of displayTiddler(), added 'title' argument to closeAllTiddlers() so that target tiddler isn't closed-and-reopened if it was already displayed. Also, added config.options.chkSinglePageAutoScrolloption to bypass automatic 'scroll into view' logic (note: core still does it's own ensureVisible() handling)
2007.12.22 2.5.3 in checkLastURL(), use decodeURIComponent() instead of decodeURI so that tiddler titles with commas (and/or other punctuation) are correctly handled.
2007.10.26 2.5.2 documentation cleanup
2007.10.08 2.5.1 in displayTiddler(), when using single-page or top-of-page mode, scrollTo(0,0) to ensure that page header is in view.
2007.09.13 2.5.0 for TPM/BPM modes, don't force tiddler to redisplay if already shown. Allows transition between view/edit or collapsed/view templates, without repositioning displayed tiddler.
2007.09.12 2.4.0 added option to disable automatic permalink feature. Also, Safari is now excluded from permalinking action to avoid bug where tiddlers don't display after hash is updated.
2007.03.03 2.3.1 fix typo when adding BPM option to AdvancedOptions (prevented checkbox from appearing)
2007.03.03 2.3.0 added support for BottomOfPageMode (BPM) based on request from DaveGarbutt
2007.02.06 2.2.3 in Story.prototype.displayTiddler(), use convertUnicodeToUTF8() for correct I18N string handling when creating URL hash string from tiddler title (based on bug report from BidiX)
2007.01.08 2.2.2 use apply() to invoke hijacked core functions
2006.07.04 2.2.1 in hijack for displayTiddlers(), suspend TPM as well as SPM so that DefaultTiddlers displays in the correct order.
2006.06.01 2.2.0 added chkTopOfPageMode (TPM) handling
2006.02.04 2.1.1 moved global variable declarations to config.* to avoid FireFox 1.5.0.1 crash bug when assigning to globals
2005.12.27 2.1.0 hijack displayTiddlers() so that SPM can be suspended during startup while displaying the DefaultTiddlers (or #hash list). Also, corrected initialization for undefined SPM flag to "false", so default behavior is to display multiple tiddlers
2005.12.27 2.0.0 Update for TW2.0
2005.11.24 1.1.2 When the back and forward buttons are used, the page now changes to match the URL. Based on code added by Clint Checketts
2005.10.14 1.1.1 permalink creation now calls encodeTiddlyLink() to handle tiddler titles with spaces in them
2005.10.14 1.1.0 added automatic setting of window title and location bar ('auto-permalink'). feature suggestion by David Dickens.
2005.10.09 1.0.1 combined documentation and code in a single tiddler
2005.08.15 1.0.0 Initial Release
<<<
my reusable non-linear personal web notebook
[img[http://lh4.ggpht.com/_I-6MAVwe7WY/TN962uk6pLI/AAAAAAAAJys/KV7AB__rq7k/smileySun.png]] AdamRocks TiddlyWiki
StraightFocus was born to be a web design company. It survived for many years but the dot com crash and lots of work going overseas, it was time for a change. And so with my voice I decided to change for the better. Because I am such a positive person, and creative, I thought that StraightFocus could be either a web design company OR a voice over business for myself. So, I did it, if you visit http://www.StraightFocus.com you will see, Voice Over business started - now, time to get some gigs and make some money - I hope!
/***
!Calendar CSS
***/
/*{{{*/
.calendar{
border-bottom: 1px solid #550000;
}
.viewer .calendar{
width: 220px;
}
#mainMenu .calendar{
font-size: 8px;
cursor: pointer;
width: 100%;
border: 0;
border-collapse: collapse;
}
#mainMenu .calendar .button{
border: 0;
}
#mainMenu .calendar td{
font-size: 8pt;
padding: 0;
background: #fff;
border: 0;
}
#mainMenu .calendar a{
margin: 0;
color: #000;
background: transparent;
}
#mainMenu .calendar a:hover{
color: #000;
background: transparent;
}
#mainMenu .calendarMonthname,
#mainMenu .calendar .calendarMonthTitle td a{
color: #fff;
}
#mainMenu .calendarDaysOfWeek td{
background: #500;
color: #fff;
}
/*}}}*/
/***
!GTD Style
!Generic rules /%==================================================================== %/
***/
/*{{{*/
body {
background: #464646 url('http://shared.snapgrid.com/images/tiddlywiki/bodygradient.png') repeat-x top fixed;
color: #000;
font: .82em/1.25em "Bitstream Vera Sans", Verdana, Helvetica, Arial, sans-serif;
/*"Lucida Sans Unicode", "Lucida Grande","Trebuchet MS", */
}
/*}}}*/
/***
!Header rules /%====================================================================== %/
***/
/*{{{*/
#contentWrapper
{
margin: 0 auto;
width: 59em;
position: relative;
}
#header
{
color: #fff;
padding: 1.5em 1em .6em 0;
}
#siteTitle {
font-size: 1.8em;
margin: 0;
}
#siteSubtitle {
font-size: 1em;
padding-left: .8em;;
}
#titleLine{
background: transparent;
padding: 0;
}
#titleLine a {
color: #cf6;
background: transparent;
}
/*}}}*/
/***
!Sidebar rules /%====================================================================== %/
***/
/*{{{*/
#sidebar{
left: 0;
width: 18em;
margin: .9em .9em 0 0;
color: #000;
background: transparent;
}
/*}}}*/
/***
!Main menu rules /%=================================================================== %/
***/
/*{{{*/
#mainMenu{
position: static;
width: auto;
background: #600;
border-right: 3px solid #500;
padding: 0;
text-align: left;
font-size: 1em;
}
#mainMenu h1{
padding: 0;
margin: 0;
font-size: 1em;
font-weight: normal;
}
#mainMenu ul{
padding: 0;
margin: 0;
list-style: none;
}
#mainMenu h1 a,
#mainMenu li a,
#mainMenu li a.button{
display: block;
padding: 0 5px 0 10px;
border: 0;
border-bottom: 1px solid #500;
border-top: 1px solid #900;
margin: 0;
}
#mainMenu a,
#mainMenu a.button{
height: 22px;
height: 1.83em;
line-height: 22px;
color: #fff;
background: #700;
margin-left: 1em;
}
#mainMenu a:hover,
#mainMenu a.button:hover {
background: #b00;
color: #fff;
}
/*}}}*/
/***
!Sidebar options rules /%============================================================ %/
***/
/*{{{*/
#sidebarOptions {
background: #eeb;
border-right: 3px solid #bb8;
color: #B4C675;
padding: .5em 0;
}
#sidebarOptions a {
color: #700;
margin: .2em .8em;
padding: 0;
border: 0;
}
#sidebarOptions a:hover, #sidebarOptions a:active {
color: #fff;
background: #700;
border: 0;
}
#sidebarOptions input{
margin: 2px 10px;
border: 1px inset #333;
padding: 0;
}
#sidebarOptions .sliderPanel {
background: #fff;
color: #000;
padding: 5px 10px;
font-size: .9em;
}
#sidebarOptions .sliderPanel a{
font-weight: normal;
margin: 0;
}
#sidebarOptions .sliderPanel a:link,#sidebarOptions .sliderPanel a:visited {
color: #700;
}
#sidebarOptions .sliderPanel a:hover,#sidebarOptions .sliderPanel a:active {
color: #fff;
background: #700;
}
/*}}}*/
/***
!Sidebar tabs rules /%===================================================================== %/
***/
/*{{{*/
#sidebarTabs {
background: transparent;
border-right: 3px solid #740;
border-bottom: 3px solid #520;
border: 0;
padding: 0;
}
#contentWrapper #sidebarTabs a,
#contentWrapper #displayArea .tabContents a{
color: #fff;
}
#contentWrapper #sidebarTabs a:hover,
#contentWrapper #displayArea .tabContents a:hover {
background: #000;
color: #fff;
}
#contentWrapper #sidebarTabs a:active,
#contentWrapper #displayArea .tabContents a:active{
color: #000;
}
#contentWrapper .tabSelected {
background: #960;
}
#contentWrapper .tabUnselected{
background: #660;
}
#contentWrapper #sidebar .tabset{
background: #eeb;
border-right: 3px solid #bb8;
padding: 0 0 0 .75em;
}
#contentWrapper .tabContents{
font-size: .95em;
background: #960;
border:0;
border-right: 3px solid #740;
border-bottom: 3px solid #520;
padding: .75em;
}
#contentWrapper .tabContents{
width: auto;
}
#contentWrapper #sidebarTabs .tabContents .tabset,
#contentWrapper .tabContents .tabset{
border: 0;
padding: 0;
background: transparent;
}
#contentWrapper .tabContents .tabSelected,
#contentWrapper .tabContents .tabContents {
background: #700;
border: 0;
}
#contentWrapper .tabContents .tabUnselected {
background: #440;
}
#contentWrapper .tabset a {
color: #fff;
padding: .2em .7em;
margin: 0 .17em 0 0;
height: 2em;
position: static;
}
#contentWrapper .tabset a:hover {
background: #000;
color: #fff;
}
#contentWrapper .tabset a:active {
color: #000;
}
#contentWrapper .tabContents ul{
margin: 0;
padding: 0;
list-style: none;
}
#contentWrapper .tabContents .tabContents ul{
color: #eeb;
}
.tabContents ul a,
.tabContents ul .button{
color: #fff;
display: block;
padding: .1em 0 .1em .7em;
background: transparent;
border: 0;
}
.tabContents ul a:hover {
color: #fff;
background: #000;
}
/*}}}*/
/***
!License panel rules /%==================================================================== %/
***/
/*{{{*/
#licensePanel {
padding: 0px 1em;
font-size: .9em;
}
#licensePanel a {
color: #960;
display: block;
margin-top: .9em;
}
#licensePanel a:hover {
color: #fff;
background: transparent;
}
/*}}}*/
/***
!Popup rules /%================================================================= %/
***/
/*{{{*/
.popup {
font-size: .8em;
padding: 0em;
background: #333;
border: 1px solid #000;
}
.popup hr {
margin: 1px 0 0 0;
visibility: hidden;
}
.popup li.disabled {
color: #666;
}
.popup li a,
.popup li a:visited{
color: #000;
border: .1em outset #cf6;
background: #cf6;
}
.popup li a:hover {
border: .1em outset #cf6;
background: #ef9;
color: #000;
}
/*}}}*/
/***
!Message area rules /%================================================================= %/
***/
/*{{{*/
#messageArea{
font-size: .9em;
padding: .4em;
background: #FFE72F;
border-right: .25em solid #da1;
border-bottom: .25em solid #a80;
position: fixed;
top: 10px;
right: 10px;
color: #000;
}
#contentWrapper #messageArea a{
color: #00e;
text-decoration: none;
}
#contentWrapper #messageArea a:hover{
color: #00e;
text-decoration: underline;
background: transparent;
}
#contentWrapper #messageArea .messageToolbar a.button{
border: 1px solid #da1;
}
#contentWrapper #messageArea .messageToolbar a.button:hover{
color: #00e;
text-decoration: none;
border: 1px solid #000;
background: #fff;
}
/*}}}*/
/***
!Tiddler display rules /%================================================================== %/
***/
/*{{{*/
#displayArea {
width: 39.75em;
margin: 0 0 0 17em;
}
.tiddler {
margin: 0 0 .9em 0;
padding: 0 1em;
border-right: .25em solid #aaa;
border-bottom: .25em solid #555;
background: #fff;
}
.title {
font-size: 1.5em;
font-weight: bold;
color: #900;
}
.toolbar {
font-size: .8em;
padding: .5em 0;
}
.toolbar .button{
padding: .1em .3em;
color: #000;
border: .1em outset #cf6;
background: #cf6;
margin: .1em;
}
.toolbar .button:hover {
background: #ef9;
color: #000;
}
.toolbar .button:active {
background: #ff0;
}
/*}}}*/
/***
!Viewer rules /% ------------------------------------------------------------------------------------------ %/
***/
/*{{{*/
.viewer {
line-height: 1.4em;
font-size: 1em;
}
.viewer a:link, .viewer a:visited {
color: #15b;
}
.viewer a:hover {
color: #fff;
background: #000;
}
.viewer .button{
background: transparent;
border-top: 1px solid #eee;
border-left: 1px solid #eee;
border-bottom: 1px solid #000;
border-right: 1px solid #000;
}
.viewer .button:hover{
background: #eee;
color: #000;
}
.viewer .button:active{
background: #ccc;
border-bottom: 1px solid #eee;
border-right: 1px solid #eee;
border-top: 1px solid #111;
border-left: 1px solid #111;
}
.viewer blockquote {
border-left: 3px solid #777;
margin: .3em;
padding: .3em;
}
.viewer pre{
background: #fefefe;
border: 1px solid #f1f1f1;
}
.viewer pre, .viewer code{
color: #000;
}
.viewer ul {
padding-left: 30px;
}
.viewer ol {
padding-left: 30px;
}
ul{
list-style-type: asquare;
}
ol{
list-style-type: decimal;
}
ol ol{
list-style-type: lower-alpha;
}
ol ol ol{
list-style-type: lower-roman;
}
.viewer ul, .viewer ol, .viewer p {
margin: .0;
}
.viewer li {
margin: .2em 0;
}
h1,h2,h3,h4,h5,h6 {
color: #000;
font-weight: bold;
background: #eee;
padding: 2px 10px;
margin: 5px 0;
}
.viewer h1 {font-size: 1.3em;}
.viewer h2 {font-size: 1.2em;}
.viewer h3 {font-size: 1.1em;}
.viewer h4 {font-size: 1em;}
.viewer h5 { font-size: .9em;}
.viewer h6 { font-size: .8em;}
.viewer table {
border: 2px solid #303030;
font-size: 11px;
margin: 10px 0;
}
.viewer th, .viewer thead td{
color: #000;
background: #eee;
border: 1px solid #aaa;
padding: 0 3px;
}
.viewer td {
border: 1px solid #aaa;
padding: 0 3px;
}
.viewer caption {
padding: 3px;
}
.viewer hr {
border: none;
border-top: dotted 1px #777;
height: 1px;
color: #777;
margin: 7px 0;
}
.viewer
{
margin: .5em 0 0 0;
padding: .5em 0;
border-top: 1px solid #ccc;
}
.highlight {
color: #000;
background: #ffe72f;
}
/*}}}*/
/***
!Editor rules /% ----------------------------------------------------------------------------------------- %/
***/
/*{{{*/
.editor {
font-size: .8em;
color: #402C74;
padding: .3em 0;
}
.editor input, .editor textarea {
font: 1.1em/130% "Andale Mono", "Monaco", "Lucida Console", "Courier New", monospace;
margin: 0;
border: 1px inset #333;
padding: 2px 0;
}
.editor textarea {
height: 42em;
width: 100%;
}
input:focus, textarea:focus
{
background: #ffe;
border: 1px solid #000;
}
.footer
{
padding: .5em 0;
margin: .5em 0;
border-top: 1px solid #ddd;
color: #555;
text-align: center;
}
/*}}}*/
/***
!IE Display hacks /% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~%/
***/
/*{{{*/
body{
_text-align: center;
}
#contentWrapper
{
/* _width: 770px; CSS UNDERSCORE HACK FOR PROPER WIN/IE DISPLAY */
_text-align: left; /* CSS UNDERSCORE HACK FOR PROPER WIN/IE DISPLAY */
}
#messageArea{
_position: absolute;
}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}
body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}
h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}
hr {height:1px;}
a {text-decoration:none;}
dt {font-weight:bold;}
ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}
.txtOptionInput {width:11em;}
#contentWrapper .chkOptionInput {border:0;}
.externalLink {text-decoration:underline;}
.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}
.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}
/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}
#mainMenu .tiddlyLinkExisting,
#mainMenu .tiddlyLinkNonExisting,
#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}
.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0 1em 1em; left:0px; top:0px;}
.siteTitle {font-size:2em;}
.siteSubtitle {font-size:1.2em;}
#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}
#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 0.3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}
.wizard {padding:0.1em 1em 0 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0; margin:0.4em 0 0.2em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0 0; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0;}
.wizardFooter .status {padding:0 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em;}
#messageArea {position:fixed; top:2em; right:0; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em;}
#messageArea a {text-decoration:underline;}
.tiddlerPopupButton {padding:0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em; margin:0;}
.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}
.tabset {padding:1em 0 0 0.5em;}
.tab {margin:0 0 0 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}
#contentWrapper {display:block;}
#splashScreen {display:none;}
#displayArea {margin:1em 17em 0 14em;}
.toolbar {text-align:right; font-size:.9em;}
.tiddler {padding:1em 1em 0;}
.missing .viewer,.missing .title {font-style:italic;}
.title {font-size:1.6em; font-weight:bold;}
.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}
.tiddler .button {padding:0.2em 0.4em;}
.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em;}
.tagged li, .tagged ul {
display:inline;
}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}
.footer {font-size:.9em;}
.footer li {display:inline;}
.annotation {padding:0.5em; margin:0.5em;}
* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0 0.25em; padding:0 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}
.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}
.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}
.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}
.fieldsetFix {border:0; padding:0; margin:1px 0px;}
.sparkline {line-height:1em;}
.sparktick {outline:0;}
.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}
* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0; right:0;}
#backstageButton a {padding:0.1em 0.4em; margin:0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin-left:3em; padding:1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}
.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
|Name|StyleSheetShortcuts|
|Source|http://www.TiddlyTools.com/#StyleSheetShortcuts|
|Version||
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|CSS|
|Description|'convenience' classes for common formatting, alignment, boxes, tables, etc.|
These 'style tweaks' can be easily included in other stylesheet tiddler so they can share a baseline look-and-feel that can then be customized to create a wide variety of 'flavors'.
***/
/*{{{*/
/* text alignments */
.left
{ display:block;text-align:left; }
.center
{ display:block;text-align:center; }
.center table
{ margin:auto !important; }
.right
{ display:block;text-align:right; }
.justify
{ display:block;text-align:justify; }
.indent
{ display:block;margin:0;padding:0;border:0;margin-left:2em; }
.floatleft
{ float:left; }
.floatright
{ float:right; }
.valignTop, .valignTop table, .valignTop tbody, .valignTop th, .valignTop tr, .valignTop td
{ vertical-align:top; }
.valignBottom, .valignBottom table, .valignBottom tbody, .valignBottom th, .valignBottom tr, .valignBottom td
{ vertical-align:bottom; }
.clear
{ clear:both; }
.wrap
{ white-space:normal; }
.nowrap
{ white-space:nowrap; }
.hidden
{ display:none; }
.show
{ display:inline !important; }
.span
{ display:span; }
.block
{ display:block; }
.relative
{ position:relative; }
.absolute
{ position:absolute; }
/* font sizes */
.big
{ font-size:14pt;line-height:120% }
.medium
{ font-size:12pt;line-height:120% }
.normal
{ font-size:9pt;line-height:120% }
.small
{ font-size:8pt;line-height:120% }
.fine
{ font-size:7pt;line-height:120% }
.tiny
{ font-size:6pt;line-height:120% }
.larger
{ font-size:120%; }
.smaller
{ font-size:80%; }
/* font styles */
.bold
{ font-weight:bold; }
.italic
{ font-style:italic; }
.underline
{ text-decoration:underline; }
/* plain list items (no bullets or indent) */
.nobullets li { list-style-type: none; margin-left:-2em; }
/* vertical tabsets - courtesy of Tobias Beer */
.vTabs .tabset {float:left;display:block;padding:0px;margin-top:.5em;min-width:20%;}
.vTabs .tabset .tab {display:block;text-align:right;padding:2px 3px 2px 7px; margin:0 1px 1px 0;}
.vTabs .tabContents {margin-left:20%;max-width:80%;padding:5px;}
.vTabs .tabContents .tabContents {border:none; background:transparent;}
/* multi-column tiddler content (not supported in Internet Explorer) */
.twocolumns { display:block;
-moz-column-count:2; -moz-column-gap:1em; -moz-column-width:50%; /* FireFox */
-webkit-column-count:2; -webkit-column-gap:1em; -webkit-column-width:50%; /* Safari */
column-count:2; column-gap:1em; column-width:50%; /* Opera */
}
.threecolumns { display:block;
-moz-column-count:3; -moz-column-gap:1em; -moz-column-width:33%; /* FireFox */
-webkit-column-count:3; -webkit-column-gap:1em; -webkit-column-width:33%; /* Safari */
column-count:3; column-gap:1em; column-width:33%; /* Opera */
}
.fourcolumns { display:block;
-moz-column-count:4; -moz-column-gap:1em; -moz-column-width:25%; /* FireFox */
-webkit-column-count:4; -webkit-column-gap:1em; -webkit-column-width:25%; /* Safari */
column-count:4; column-gap:1em; column-width:25%; /* Opera */
}
/* page breaks */
.breakbefore { page-break-before:always; }
.breakafter { page-break-before:always; }
/* show/hide browser-specific content for InternetExplorer vs. non-IE ("moz") browsers */
*[class="ieOnly"]
{ display:none; } /* hide in moz (uses CSS selector) */
* html .mozOnly, *:first-child+html .mozOnly
{ display: none; } /* hide in IE (uses IE6/IE7 CSS hacks) */
/* borderless tables */
.borderless, .borderless table, .borderless td, .borderless tr, .borderless th, .borderless tbody
{ border:0 !important; margin:0 !important; padding:0 !important; }
.widetable, .widetable table
{ width:100%; }
/* thumbnail images (fixed-sized scaled images) */
.thumbnail img { height:5em !important; }
/* stretchable images (auto-size to fit tiddler) */
.stretch img { width:95%; }
/* grouped content */
.outline
{ display:block; padding:1em; -moz-border-radius:1em;-webkit-border-radius:1em; border:1px solid; }
.menubox
{ display:block; padding:1em; -moz-border-radius:1em;-webkit-border-radius:1em; border:1px solid; background:#fff; color:#000; }
.menubox .button, .menubox .tiddlyLinkExisting, .menubox .tiddlyLinkNonExisting
{ color:#009 !important; }
.groupbox
{ display:block; padding:1em; -moz-border-radius:1em;-webkit-border-radius:1em; border:1px solid; background:#ffe; color:#000; }
.groupbox a, .groupbox .button, .groupbox .tiddlyLinkExisting, .groupbox .tiddlyLinkNonExisting
{ color:#009 !important; }
.groupbox code
{ color:#333 !important; }
.borderleft
{ margin:0;padding:0;border:0;margin-left:1em; border-left:1px dotted; padding-left:.5em; }
.borderright
{ margin:0;padding:0;border:0;margin-right:1em; border-right:1px dotted; padding-right:.5em; }
.borderbottom
{ margin:0;padding:1px 0;border:0;border-bottom:1px dotted; margin-bottom:1px; padding-bottom:1px; }
.bordertop
{ margin:0;padding:0;border:0;border-top:1px dotted; margin-top:1px; padding-top:1px; }
/* scrolled content */
.scrollbars { overflow:auto; }
.height10em { height:10em; }
.height15em { height:15em; }
.height20em { height:20em; }
.height25em { height:25em; }
.height30em { height:30em; }
.height35em { height:35em; }
.height40em { height:40em; }
/* compact form */
.smallform
{ white-space:nowrap; }
.smallform input, .smallform textarea, .smallform button, .smallform checkbox, .smallform radio, .smallform select
{ font-size:8pt; }
/* stretchable edit fields and textareas (auto-size to fit tiddler) */
.stretch input { width:99%; }
.stretch textarea { width:99%; }
/* compact input fields (limited to a few characters for entering percentages and other small values) */
.onechar input { width:1em; }
.twochar input { width:2em; }
.threechar input { width:3em; }
.fourchar input { width:4em; }
.fivechar input { width:5em; }
/* text colors */
.white { color:#fff !important }
.gray { color:#999 !important }
.black { color:#000 !important }
.red { color:#f66 !important }
.green { color:#0c0 !important }
.blue { color:#99f !important }
/* rollover highlighting */
.mouseover
{color:[[ColorPalette::TertiaryLight]] !important;}
.mouseover a
{color:[[ColorPalette::TertiaryLight]] !important;}
.selected .mouseover
{color:[[ColorPalette::Foreground]] !important;}
.selected .mouseover .button, .selected .mouseover a
{color:[[ColorPalette::PrimaryDark]] !important;}
/* rollover zoom text */
.zoomover
{ font-size:80% !important; }
.selected .zoomover
{ font-size:100% !important; }
/* [[ColorPalette]] text colors */
.Background { color:[[ColorPalette::Background]]; }
.Foreground { color:[[ColorPalette::Foreground]]; }
.PrimaryPale { color:[[ColorPalette::PrimaryPale]]; }
.PrimaryLight { color:[[ColorPalette::PrimaryLight]]; }
.PrimaryMid { color:[[ColorPalette::PrimaryMid]]; }
.PrimaryDark { color:[[ColorPalette::PrimaryDark]]; }
.SecondaryPale { color:[[ColorPalette::SecondaryPale]]; }
.SecondaryLight { color:[[ColorPalette::SecondaryLight]];}
.SecondaryMid { color:[[ColorPalette::SecondaryMid]]; }
.SecondaryDark { color:[[ColorPalette::SecondaryDark]]; }
.TertiaryPale { color:[[ColorPalette::TertiaryPale]]; }
.TertiaryLight { color:[[ColorPalette::TertiaryLight]]; }
.TertiaryMid { color:[[ColorPalette::TertiaryMid]]; }
.TertiaryDark { color:[[ColorPalette::TertiaryDark]]; }
.Error { color:[[ColorPalette::Error]]; }
/* [[ColorPalette]] background colors */
.BGBackground { background-color:[[ColorPalette::Background]]; }
.BGForeground { background-color:[[ColorPalette::Foreground]]; }
.BGPrimaryPale { background-color:[[ColorPalette::PrimaryPale]]; }
.BGPrimaryLight { background-color:[[ColorPalette::PrimaryLight]]; }
.BGPrimaryMid { background-color:[[ColorPalette::PrimaryMid]]; }
.BGPrimaryDark { background-color:[[ColorPalette::PrimaryDark]]; }
.BGSecondaryPale { background-color:[[ColorPalette::SecondaryPale]]; }
.BGSecondaryLight { background-color:[[ColorPalette::SecondaryLight]]; }
.BGSecondaryMid { background-color:[[ColorPalette::SecondaryMid]]; }
.BGSecondaryDark { background-color:[[ColorPalette::SecondaryDark]]; }
.BGTertiaryPale { background-color:[[ColorPalette::TertiaryPale]]; }
.BGTertiaryLight { background-color:[[ColorPalette::TertiaryLight]]; }
.BGTertiaryMid { background-color:[[ColorPalette::TertiaryMid]]; }
.BGTertiaryDark { background-color:[[ColorPalette::TertiaryDark]]; }
.BGError { background-color:[[ColorPalette::Error]]; }
/*}}}*/
/%
!info
|Name|ThumbThing|
|Source|http://www.TiddlyTools.com/#ThumbThing|
|Version|1.2.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|transclusion|
|Description|display a scaled 'thumbnail' image with popup for full-sized image|
Usage:
<<<
{{{
<<tiddler ThumbThing with: image thumbWidth thumbHeight fullWidth fullHeight>>
}}}
*{{{image}}} is one of:
**a local path/file
**a remote URL reference
**the title of a tiddler containing an attached image (see [[AttachFilePlugin]])
*{{{thumbWidth, thumbHeight, fullWidth, fullHeight}}} are CSS measurements. Use 'auto' (or omit values) for default (full-sized) image height and/or width. For proportional scaling, specify a fixed-width OR fixed-height and use 'auto' for the other dimension.
<<<
Example:
<<<
{{{
<<tiddler ThumbThing with: images/meow2.jpg 100px>>
}}}
<<tiddler ThumbThing with: images/meow2.jpg 100px>>
<<<
See also:
>[[AllThumbs]]
!end
!show
<html><hide linebreaks>
<img src="$1" style="border=0;width:$2;height:$3;" title="$1" onclick="
var p=Popup.create(this); if (!p) return; var s=p.style; s.border=s.padding='0'; s.background='none';
p.innerHTML='<img src=\x22$1\x22 style=\x22border:1px solid #999;background:#eee;width:$4;height:$5\x22>';
Popup.show(); event.cancelBubble=true; if(event.stopPropagation)event.stopPropagation(); return(false);
"></html>
!end
%/<<tiddler {{'ThumbThing##'+('$1'=='$'+'1'?'info':'show')}} with:
{{var cma=config.macros.attach; (cma&&cma.isAttachment('$1'))?cma.getAttachment('$1'):'$1';}}
[[$2]] [[$3]] [[$4]] [[$5]]>>
/***
|Name|TiddlerEncryptionPlugin|
|Author|Lyall Pearce|
|Source|http://www.Remotely-Helpful.com/TiddlyWiki/TiddlerEncryptionPlugin.html|
|License|[[Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|Version|3.2.1|
|~CoreVersion|2.4.0|
|Requires|None|
|Overrides|store.getSaver().externalizeTiddler(), store.getTiddler() and store.getTiddlerText()|
|Description|Encrypt/Decrypt Tiddlers with a Password key|
!!!!!Usage
<<<
* Tag a tiddler with Encrypt(prompt)
** Consider the 'prompt' something to help you remember the password with. If multiple tiddlers can be encrypted with the same 'prompt' and you will only be asked for the password once.
* Upon save, the Tiddler will be encrypted and the tag replaced with Decrypt(prompt).
** Failure to encrypt (by not entering a password) will leave the tiddler unencrypted and will leave the Encrypt(prompt) tag in place. This means that the next time you save, you will be asked for the password again.
** To have multiple tiddlers use the same password - simply use the same 'prompt'.
** Tiddlers that are encrypted may be automatically tagged 'excludeSearch' as there is no point in searching encrypted data - this is configurable by an option - you still may want to search the titles of encrypted tiddlers
** Tiddlers that are encrypted may be automatically tagged 'excludeLists', if you have them encrypted, you may also want to keep them 'hidden' - this is configurable by an option.
** Automatic removal of excludeLists and excludeSearch tags is performed, if the above two options are set, only if these two tags are the last 2 tags for a tiddler, if they are positioned somewhere else in the tags list, they will be left in place, meaning that the decrypted tiddler will not be searchable and/or will not appear in lists.
** Encrypted tiddlers are stored as displayable hex, to keep things visibly tidy, should you display an encrypted tiddler. There is nothing worse than seeing a pile of gobbledy gook on your screen. Additionally, the encrypted data is easily cut/paste/emailed if displayed in hex form.
* Tiddlers are decrypted only if you click the decrypt button or the decryptAll button, not when you load the TiddlyWiki
** If you don't display a tiddler, you won't have the option to decrypt it (unless you use the {{{<<EncryptionDecryptAll>>}}} macro)
** Tiddlers will re-encrypt automatically on save.
** Decryption of Tiddlers does not make your TiddlyWiki 'dirty' - you will not be asked to save if you leave the page.
* Errors are reported via diagnostic messages.
** Empty passwords, on save, will result in the tiddler being saved unencrypted - this should only occur with new tiddlers, decrypted tiddlers or with tiddlers who have had their 'prompt' tag changed.
** Encrypted tiddlers know if they are decrypted successfully - failure to decrypt a tiddler will ''not'' lose your data.
** Editing of an encrypted (that has not been unencrypted) tiddler will result in loss of that tiddler as the SHA1 checksums will no longer match, upon decryption. To this end, it is best that you do not check the option. You can, however edit an encrypted tiddler tag list - just do ''not'' change the tiddler contents.
** To change the password on a Tiddler, change the Encrypt('prompt') tag to a new prompt value, after decrypting the tiddler.
** You can edit the tags of an encrypted tiddler, so long as you do not edit the text.
** To change the password for all tiddlers of a particular prompt, use the {{{<<EncryptionChangePassword ["button text" ["tooltip text" ["prompt string" ["accessKey"]]]]>>}}} macro.
** To decrypt all tiddlers of a particular "prompt string", use the {{{<<EncryptionDecryptAll ["button text" ["tooltip text" ["prompt string" ["accessKey"]]]]>>}}} macro - this will make tiddlers encrypted with "prompt string" searchable - or prompt for all 'prompt strings', if none is supplied.
<<<
!!!!!Configuration
<<<
Useful Buttons:
<<EncryptionChangePassword>> - Change passwords of encrypted tiddlers.
<<EncryptionDecryptAll>> - Decrypt ALL tiddlers - enables searching contents of encrypted tiddlers.
<<option chkExcludeEncryptedFromSearch>> - If set, Encrypted Tiddlers are excluded from searching by tagging with excludeSearch. If Clear, excludeSearch is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Searching of Encrypted Tiddlers is only meaningful for the Title and Tags.
<<option chkExcludeEncryptedFromLists>> - If set, Encrypted Tiddlers are excluded from lists by tagging with excludeLists. If Clear, excludeLists is not added and it is also removed from existing Encrypted Tiddlers only if it is the last Tag. Preventing encrypted tiddlers from appearing in lists effectively hides them.
<<option chkShowDecryptButtonInContent>> - If set, Encrypted Tiddlers content is replaced by <<EncryptionDecryptThis>> button. This has consequences, in the current version as, if you edit the tiddler without decrypting it, you lose the contents.
<<<
!!!!!Revision History
<<<
* 3.2.1 - Returned the <<EncryptionDecryptThis>> button as an option.
* 3.2.0 - Ditched the 'Decrypt' button showing up in the tiddler contents if the tiddler is encrypted. It caused too much pain if you edit the tiddler without decrypting it - you lost your data as it was replaced by a Decrypt Macro call! Additionally, a 'decrypt' button will now appear in the toolbar, just before the edit button, if the tiddler is encrypted. This button only appears if using core TiddlyWiki version 2.4 or above.
* 3.1.1 - Obscure bug whereby if an encrypted tiddler was a certain length, it would refuse to decrypt.
* 3.1.0 - When creating a new Encrypt(prompt) tiddler and you have not previously decrypted a tiddler with the same prompt, on save, you will be prompted for the password to encrypt the tiddler. Prior to encrypting, an attempt to decrypt all other tiddlers with the same prompt, is performed. If any tiddler fails to decrypt, the save is aborted - this is so you don't accidentally have 2 (or more!) passwords for the same prompt. Either you enter the correct password, change the prompt string and try re-saving or you cancel (and the tiddler is saved unencrypted).
* 3.0.1 - Allow Enter to be used for password entry, rather than having to press the OK button.
* 3.0.0 - Major revamp internally to support entry of passwords using forms such that passwords are no longer visible on entry. Completely backward compatible with old encrypted tiddlers. No more using the javascript prompt() function.
<<<
!!!!!Additional work
***/
//{{{
version.extensions.TiddlerEncryptionPlugin = {major: 3, minor: 2, revision: 1, date: new Date(2008,10,26)};
// where I cache the passwords - for want of a better place.
config.encryptionPasswords = new Array();
config.encryptionReEnterPasswords = false;
if(config.options.chkExcludeEncryptedFromSearch == undefined) config.options.chkExcludeEncryptedFromSearch = false;
if(config.options.chkExcludeEncryptedFromLists == undefined) config.options.chkExcludeEncryptedFromLists = false;
if(config.options.chkShowDecryptButtonInContent == undefined) config.options.chkShowDecryptButtonInContent = false;
config.macros.EncryptionChangePassword = {};
config.macros.EncryptionChangePassword.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] && params[0].length > 0) ? params[0] : "Change Passwords",
(params[1] && params[1].length > 0) ? params[1] : "Change Passwords" + (params[2] ? " for prompt "+params[2] : ""),
onClickEncryptionChangePassword,
null,
null,
params[3]);
if(params[2] && params[2].length > 0) {
theButton.setAttribute("promptString", params[2]);
}
};
config.macros.EncryptionDecryptAll = {};
config.macros.EncryptionDecryptAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] && params[0].length > 0) ? params[0] : "Decrypt All",
(params[1] && params[1].length > 0) ? params[1] : "Decrypt All Tiddlers" + ((params[2] && params[2].length > 0) ? " for prompt "+params[2] : " for a given 'prompt string'"),
onClickEncryptionDecryptAll,
null,
null,
params[3]);
if(params[2] && params[2].length > 0) {
theButton.setAttribute("promptString", params[2]);
}
};
config.macros.EncryptionDecryptThis = {};
config.macros.EncryptionDecryptThis.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var theButton = createTiddlyButton(place,
(params[0] && params[0].length > 0) ? params[0] : "Decrypt",
(params[1] && params[1].length > 0) ? params[1] : "Decrypt this Tiddler",
onClickEncryptionDecryptThis,
null,
null,
params[3]);
if(params[2] && params[2].length > 0) {
theButton.setAttribute("theTiddler", params[2]);
}
};
// toolbar button to decrypt tiddlers.
config.commands.decryptThis = {
text: "decrypt",
tooltip: "Decrypt this tiddler",
isEnabled : function(tiddler) {
// Only show decrypt button if tiddler is tagged as Decrypt(
if(tiddler.tags.join().indexOf('Decrypt(') == -1) {
return false;
} else {
return true;
}
},
handler: function(event, src, title) {
encryptionGetAndDecryptTiddler(title);
return false;
}
};
// core version 2.4 or above get a 'decrypt' button in the toolbar.
if(config.shadowTiddlers && config.shadowTiddlers.ToolbarCommands && config.shadowTiddlers.ToolbarCommands.indexOf('decryptThis') == -1) {
// put our toolbar button in before the edit button.
// won't work if editTiddler is not the default item (prefixed with plus)
config.shadowTiddlers.ToolbarCommands.replace(/\+editTiddler/,'decryptThis +editTiddler');
}
// Called by the EncryptionChangePassword macro/button
// Also invoked by the callback for password entry
function onClickEncryptionChangePassword(eventObject) {
var promptString;
if(!promptString && this.getAttribute) {
promptString = this.getAttribute("promptString");
}
// I do call this function directly
if(!promptString && typeof(eventObject) == "string") {
promptString = eventObject;
}
if(!promptString) {
promptString = prompt("Enter 'prompt string' to change password for:","");
}
if(!promptString) {
return;
}
if(! config.encryptionPasswords[promptString]) {
var changePasswordContext = {changePasswordPromptString: promptString,
callbackFunction: MyChangePasswordPromptCallback_TiddlerEncryptionPlugin};
MyPrompt_TiddlerEncryptionPlugin(promptString,"",changePasswordContext);
return;
// Callback function will re-invoke this function
}
// Decrypt ALL tiddlers for that prompt
onClickEncryptionDecryptAll(promptString);
// Now ditch the cached password, this will force the re-request for the new password, on save.
displayMessage("Save TiddlyWiki to set new password for '"+promptString+"'");
config.encryptionPasswords[promptString] = null;
// mark store as dirty so a save will be requrested.
store.setDirty(true);
autoSaveChanges();
return;
};
// Called by the password entry form when the user clicks 'OK' button.
function MyChangePasswordPromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
onClickEncryptionChangePassword(context.changePasswordPromptString);
return;
}
// Called by the EncryptionDecryptThis macro/button
function onClickEncryptionDecryptThis() {
var theTiddler = this.getAttribute("theTiddler");
if(!theTiddler) {
return;
}
encryptionGetAndDecryptTiddler(theTiddler);
return;
};
function encryptionGetAndDecryptTiddler(title) {
config.encryptionReEnterPasswords = true;
try {
theTiddler = store.getTiddler(title);
config.encryptionReEnterPasswords = false;
story.refreshAllTiddlers();
} catch (e) {
if(e == "DecryptionFailed") {
displayMessage("Decryption failed");
return;
}
} // catch
return;
};
// called by the EncryptionDecryptAlll macro/button
// Also called by the callback after the user clicks 'OK' button on the password entry form
function onClickEncryptionDecryptAll(eventObject) {
var promptString;
if(!promptString && this.getAttribute) {
promptString = this.getAttribute("promptString");
}
// I do call this function directly
if(!promptString && typeof(eventObject) == "string") {
promptString = eventObject;
}
if(!promptString) {
promptString = "";
}
// Loop through all tiddlers, looking to see if there are any Decrypt(promptString) tagged tiddlers
// If there are, check to see if their password has been cached.
// If not, ask for the first one that is missing, that we find
// the call back function will store that password then invoke this function again,
// which will repeat the whole process. If we find all passwords have been cached
// then we will finally do the decryptAll functionality, which will then
// be able to decrypt all the required tiddlers, without prompting.
// We have to do this whole rigmarole because we are using a 'form' to enter the password
// rather than the 'prompt()' function - which shows the value of the password.
var tagToSearchFor="Decrypt("+promptString;
config.encryptionReEnterPasswords = true;
var promptGenerated = false;
store.forEachTiddler(function(store,tiddler) {
// Note, there is no way to stop the forEachTiddler iterations
if(!promptGenerated && tiddler && tiddler.tags) {
for(var ix=0; ix<tiddler.tags.length && !promptGenerated; ix++) {
if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
var tag = tiddler.tags[ix];
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPromptString=tag.substring(8,lastBracket);
if(!config.encryptionPasswords[passwordPromptString]) {
// no password cached, prompt and cache it, rather than decryptAll
// callback from prompting form will resume decryptAll attempt.
var decryptAllContext = {decryptAllPromptString: promptString,
callbackFunction: MyDecryptAllPromptCallback_TiddlerEncryptionPlugin};
MyPrompt_TiddlerEncryptionPlugin(passwordPromptString,"",decryptAllContext);
promptGenerated = true;
} // if(!config.encryptionPasswords
} // if(lastBracket
} // if(tiddler.tags[ix]..
} // for
} // if
}); // store.forEachTiddler
// If we get here, all passwords have been cached.
if(!promptGenerated) {
config.encryptionReEnterPasswords = false;
// Now do the decrypt all functionality
try {
store.forEachTiddler(function(store,tiddler) {
// Note, there is no way to stop the forEachTiddler iterations
if(tiddler && tiddler.tags) {
for(var ix=0; ix<tiddler.tags.length; ix++) {
if(tiddler.tags[ix].indexOf(tagToSearchFor) == 0) {
try {
CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
} catch (e) {
displayMessage("Decryption of '"+tiddler.title+"' failed.");
// throw e;
}
} // if(tiddler.tags
} // for
} // if
}); // store.forEachTiddler
displayMessage("All tiddlers" + (promptString != "" ? " for '"+promptString+"'" : "") + " have been decrypted");
} catch (e) {
if(e == "DecryptionFailed") {
return;
}
} // catch
}
return;
};
function MyDecryptAllPromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// restart the decryptAll process again after the user has entered a password.
onClickEncryptionDecryptAll(context.decryptAllPromptString);
return;
}
saveChanges_TiddlerEncryptionPlugin = saveChanges;
saveChanges = function(onlyIfDirty,tiddlers) {
// Loop through all tiddlers, looking to see if there are any Encrypt(string) tagged tiddlers
// If there are, check to see if their password has been cached.
// If not, ask for the first one that is missing, that we find
// the call back function will store that password then invoke this function again,
// which will repeat the whole process. If we find all passwords have been cached
// then we will finally call the original saveChanges() function, which will then
// be able to save the tiddlers.
// We have to do this whole rigmarole because we are using a 'form' to enter the password
// rather than the 'prompt()' function - which shows the value of the password.
config.encryptionReEnterPasswords = true;
var promptGenerated = false;
store.forEachTiddler(function(store,tiddler) {
if(!promptGenerated && tiddler && tiddler.tags) {
for(var ix=0; ix<tiddler.tags.length && !promptGenerated; ix++) {
if(tiddler.tags[ix].indexOf("Encrypt(") == 0) {
var tag = tiddler.tags[ix];
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
if(!config.encryptionPasswords[passwordPrompt]) {
// no password cached, prompt and cache it, rather than save
var saveContext = {onlyIfDirty: onlyIfDirty,
tiddlers: tiddlers,
callbackFunction: MySavePromptCallback_TiddlerEncryptionPlugin};
MyPrompt_TiddlerEncryptionPlugin(passwordPrompt,"",saveContext);
promptGenerated = true;
} // if(!config.encryptionPasswords
} // if(lastBracket
} // if(tiddler.tags[ix]..
} // for
} // if
}); // store.forEachTiddler
// If we get here, all passwords have been cached.
if(!promptGenerated) {
config.encryptionReEnterPasswords = false;
saveChanges_TiddlerEncryptionPlugin(onlyIfDirty,tiddlers);
}
return;
}
function MySavePromptCallback_TiddlerEncryptionPlugin(context) {
config.encryptionPasswords[context.passwordPrompt] = context.password;
// validate the password entered by attempting to decrypt all tiddlers
// with the same encryption prompt string.
onClickEncryptionDecryptAll(context.passwordPrompt);
// restart the save process again
saveChanges(context.onlyIfDirty, context.tiddlers);
return;
}
store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin = store.getSaver().externalizeTiddler;
store.getSaver().externalizeTiddler = function(store, tiddler) {
// Ok, got the tiddler, track down the passwordPrompt in the tags.
// track down the Encrypt(passwordPrompt) tag
if(tiddler && tiddler.tags) {
for(var g=0; g<tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
if(tag.indexOf("Encrypt(") == 0) {
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
// Ok, tagged with Encrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
// Ok, Encrypt this tiddler!
var decryptedSHA1 = Crypto.hexSha1Str(tiddler.text);
var password = GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(passwordPrompt);
if(password) {
var encryptedText = TEAencrypt(tiddler.text, password);
encryptedText = StringToHext_TiddlerEncryptionPlugin(encryptedText);
tiddler.text = "Encrypted("+decryptedSHA1+")\n"+encryptedText;
// Replace the Tag with the Decrypt() tag
tiddler.tags[g]="Decrypt("+passwordPrompt+")";
// let the store know it's dirty
store.setDirty(tiddler.title, true);
// prevent searches on encrypted tiddlers, still nice to search on title though.
if(config.options.chkExcludeEncryptedFromSearch == true) {
tiddler.tags.push("excludeSearch");
}
// prevent lists of encrypted tiddlers
if(config.options.chkExcludeEncryptedFromLists == true) {
tiddler.tags.push("excludeLists");
}
} else {
// do not encrypt - no password entered
}
break;
} // if (lastBracket...
} // if(tag.indexOf(...
} // for(var g=0;...
} // if(tiddler.tags...
// Then, finally, do the save by calling the function we override.
return store.getSaver().externalizeTiddler_TiddlerEncryptionPlugin(store, tiddler);
};
function CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler) {
if(tiddler && tiddler.tags) {
for(var g=0; g<tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
if(tag.indexOf("Decrypt(") == 0) {
var lastBracket=tag.lastIndexOf(")");
if(lastBracket >= 0) {
if(tiddler.text.substr(0,10) == "Encrypted(") {
var closingSHA1Bracket = tiddler.text.indexOf(")");
var decryptedSHA1 = tiddler.text.substring(10, closingSHA1Bracket);
// Ok, tagged with Decrypt(passwordPrompt)
// extract the passwordPrompt name
var passwordPrompt=tag.substring(8,lastBracket);
// Ok, Decrypt this tiddler!
var decryptedText = tiddler.text.substr(closingSHA1Bracket+2);
decryptedText = HexToString_TiddlerEncryptionPlugin(decryptedText);
// prompt("Decryption request for Tiddler '"+tiddler.title+"'");
var password = GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(passwordPrompt);
if(password) {
decryptedText = TEAdecrypt(decryptedText, password );
var thisDecryptedSHA1 = Crypto.hexSha1Str(decryptedText);
if(decryptedSHA1 == thisDecryptedSHA1) {
tiddler.text = decryptedText;
// Replace the Tag with the Encrypt() tag
tiddler.tags[g]="Encrypt("+passwordPrompt+")";
if(tiddler.tags[tiddler.tags.length-1] == 'excludeLists') {
// Remove exclude lists only if it's the last entry
// as it's automatically put there by encryption
tiddler.tags.length--;
}
if(tiddler.tags[tiddler.tags.length-1] == 'excludeSearch') {
// Remove exclude search only if it's the last entry
// as it's automatically put there by encryption
tiddler.tags.length--;
}
} else {
// Did not decrypt, discard the password from the cache
config.encryptionPasswords[passwordPrompt] = null;
config.encryptionReEnterPasswords = false;
throw "DecryptionFailed";
}
} else {
// no password supplied, dont bother trying to decrypt
config.encryptionReEnterPasswords = false;
throw "DecryptionFailed";
}
} else {
// Tagged as encrypted but not expected format, just leave it unchanged
}
break; // out of for loop
} // if (lastBracket...
} // if(tag.indexOf(...
} // for(var g=0;...
} // if (tiddler && tags)
return tiddler;
};
store.getTiddler_TiddlerEncryptionPlugin = store.getTiddler;
store.getTiddler = function(title) {
var tiddler = store.getTiddler_TiddlerEncryptionPlugin(title);
if(tiddler) { // shadow tiddlers are not expected to be encrypted.
try {
return CheckTiddlerForDecryption_TiddlerEncryptionPlugin(tiddler);
} catch (e) {
if (config.options.chkShowDecryptButtonInContent == true) {
if(e == "DecryptionFailed") {
var tiddler = store.getTiddler("DecryptionFailed");
if(!tiddler) {
tiddler = new Tiddler();
tiddler.set(title,
"<<EncryptionDecryptThis \"Decrypt\" \"Decrypt this tiddler\" \""+title+"\">>",
config.views.wikified.shadowModifier,
version.date,[],version.date);
}
return tiddler;
} // if(e)
}
return(tiddler);
} // catch
} // if(tiddler) {
return null;
};
store.getTiddlerText_TiddlerEncryptionPlugin = store.getTiddlerText;
store.getTiddlerText = function(title,defaultText) {
// Simply retrieve the tiddler, normally, if it requires decryption, it will be decrypted
var decryptedTiddler = store.getTiddler(title);
if(decryptedTiddler) {
return decryptedTiddler.text;
}
//Ok, rather than duplicate all the core code, the above code should fail if we reach here
// let the core code take over.
return store.getTiddlerText_TiddlerEncryptionPlugin(title,defaultText);
};
// Given a prompt, search our cache to see if we have already entered the password.
// Can return null if the user enters nothing.
function MyPrompt_TiddlerEncryptionPlugin(promptString,defaultValue,context) {
if(!context) {
context = {};
}
context.passwordPrompt = promptString;
PasswordPrompt.prompt(MyPromptCallback_TiddlerEncryptionPlugin, context);
return;
}
function MyPromptCallback_TiddlerEncryptionPlugin(context) {
if(context.callbackFunction) {
context.callbackFunction(context);
} else {
config.encryptionPasswords[context.passwordPrompt] = context.password;
story.refreshAllTiddlers(true);
}
return;
}
function GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString) {
if(!config.encryptionPasswords[promptString]) {
config.encryptionPasswords[promptString] = MyPrompt_TiddlerEncryptionPlugin(promptString, "");
}
return config.encryptionPasswords[promptString]; // may be null, prompt can be cancelled.
}
function GetAndSetPasswordForPromptToDecrypt_TiddlerEncryptionPlugin(promptString) {
if(config.encryptionReEnterPasswords) {
return GetAndSetPasswordForPrompt_TiddlerEncryptionPlugin(promptString);
} else {
return config.encryptionPasswords[promptString];
}
}
// Make the encrypted tiddlies look a little more presentable.
function StringToHext_TiddlerEncryptionPlugin(theString) {
var theResult = "";
for(var i=0; i<theString.length; i++) {
var theHex = theString.charCodeAt(i).toString(16);
if(theHex.length<2) {
theResult += "0"+theHex;
} else {
theResult += theHex;
}
if(i && i % 32 == 0)
theResult += "\n";
}
return theResult;
}
function HexToString_TiddlerEncryptionPlugin(theString) {
var theResult = "";
for(var i=0; i<theString.length; i+=2) {
if(theString.charAt(i) == "\n") {
i--; // cause us to skip over the newline and resume
continue;
}
theResult += String.fromCharCode(parseInt(theString.substr(i, 2),16));
}
return theResult;
}
//
// Heavily leveraged from http://trac.tiddlywiki.org/browser/Trunk/contributors/SaqImtiaz/verticals/Hesperian/PasswordPromptPlugin.js Revision 5635
//
PasswordPrompt ={
prompt : function(callback,context){
if (!context) {
context = {};
}
var box = createTiddlyElement(document.getElementById("contentWrapper"),'div','passwordPromptBox');
box.innerHTML = store.getTiddlerText('PasswordPromptTemplate');
box.style.position = 'absolute';
this.center(box);
document.getElementById('promptDisplayField').value = context.passwordPrompt;
var passwordInputField = document.getElementById('passwordInputField');
passwordInputField.onkeyup = function(ev) {
var e = ev || window.event;
if(e.keyCode == 10 || e.keyCode == 13) { // Enter
PasswordPrompt.submit(callback, context);
}
};
passwordInputField.focus();
document.getElementById('passwordPromptSubmitBtn').onclick = function(){PasswordPrompt.submit(callback,context);};
document.getElementById('passwordPromptCancelBtn').onclick = function(){PasswordPrompt.cancel(callback,context);};
},
center : function(el){
var size = this.getsize(el);
el.style.left = (Math.round(findWindowWidth()/2) - (size.width /2) + findScrollX())+'px';
el.style.top = (Math.round(findWindowHeight()/2) - (size.height /2) + findScrollY())+'px';
},
getsize : function (el){
var x = {};
x.width = el.offsetWidth || el.style.pixelWidth;
x.height = el.offsetHeight || el.style.pixelHeight;
return x;
},
submit : function(cb,context){
context.passwordPrompt = document.getElementById('promptDisplayField').value;
context.password = document.getElementById('passwordInputField').value;
var box = document.getElementById('passwordPromptBox');
box.parentNode.removeChild(box);
cb(context);
return false;
},
cancel : function(cb,context){
var box = document.getElementById('passwordPromptBox');
box.parentNode.removeChild(box);
return false;
},
setStyles : function(){
setStylesheet(
"#passwordPromptBox dd.submit {margin-left:0; font-weight: bold; margin-top:1em;}\n"+
"#passwordPromptBox dd.submit .button {padding:0.5em 1em; border:1px solid #ccc;}\n"+
"#passwordPromptBox dt.heading {margin-bottom:0.5em; font-size:1.2em;}\n"+
"#passwordPromptBox {border:1px solid #ccc;background-color: #eee;padding:1em 2em;}",'passwordPromptStyles');
},
template : '<form action="" onsubmit="return false;" id="passwordPromptForm">\n'+
' <dl>\n'+
' <dt class="heading">Please enter the password:</dt>\n'+
' <dt>Prompt:</dt>\n'+
' <dd><input type="text" readonly id="promptDisplayField" class="display"/></dd>\n'+
' <dt>Password:</dt>\n'+
' <dd><input type="password" tabindex="1" class="input" id="passwordInputField"/></dd>\n'+
' <dd class="submit">\n'+
' <a tabindex="2" href="javascript:;" class="button" id="passwordPromptSubmitBtn">OK</a>\n'+
' <a tabindex="3" href="javascript:;" class="button" id="passwordPromptCancelBtn">Cancel</a>\n'+
' </dd>\n'+
' </dl>\n'+
'</form>',
init : function(){
config.shadowTiddlers.PasswordPromptTemplate = this.template;
this.setStyles();
}
};
PasswordPrompt.init();
// http://www.movable-type.co.uk/scripts/tea-block.html
//
// TEAencrypt: Use Corrected Block TEA to encrypt plaintext using password
// (note plaintext & password must be strings not string objects)
//
// Return encrypted text as string
//
function TEAencrypt(plaintext, password)
{
if (plaintext.length == 0) return(''); // nothing to encrypt
// 'escape' plaintext so chars outside ISO-8859-1 work in single-byte packing, but keep
// spaces as spaces (not '%20') so encrypted text doesn't grow too long (quick & dirty)
var asciitext = escape(plaintext).replace(/%20/g,' ');
var v = strToLongs(asciitext); // convert string to array of longs
if (v.length <= 1) v[1] = 0; // algorithm doesn't work for n<2 so fudge by adding a null
var k = strToLongs(password.slice(0,16)); // simply convert first 16 chars of password as key
var n = v.length;
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = 0;
while (q-- > 0) { // 6 + 52/n operations gives between 6 & 32 mixes on each word
sum += delta;
e = sum>>>2 & 3;
for (var p = 0; p < n; p++) {
y = v[(p+1)%n];
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
z = v[p] += mx;
}
}
var ciphertext = longsToStr(v);
return escCtrlCh(ciphertext);
}
//
// TEAdecrypt: Use Corrected Block TEA to decrypt ciphertext using password
//
function TEAdecrypt(ciphertext, password)
{
if (ciphertext.length == 0) return('');
var v = strToLongs(unescCtrlCh(ciphertext));
var k = strToLongs(password.slice(0,16));
var n = v.length;
var z = v[n-1], y = v[0], delta = 0x9E3779B9;
var mx, e, q = Math.floor(6 + 52/n), sum = q*delta;
while (sum != 0) {
e = sum>>>2 & 3;
for (var p = n-1; p >= 0; p--) {
z = v[p>0 ? p-1 : n-1];
mx = (z>>>5 ^ y<<2) + (y>>>3 ^ z<<4) ^ (sum^y) + (k[p&3 ^ e] ^ z);
y = v[p] -= mx;
}
sum -= delta;
}
var plaintext = longsToStr(v);
// strip trailing null chars resulting from filling 4-char blocks:
plaintext = plaintext.replace(/\0+$/,'');
return unescape(plaintext);
}
// supporting functions
function strToLongs(s) { // convert string to array of longs, each containing 4 chars
// note chars must be within ISO-8859-1 (with Unicode code-point < 256) to fit 4/long
var l = new Array(Math.ceil(s.length/4));
for (var i=0; i<l.length; i++) {
// note little-endian encoding - endianness is irrelevant as long as
// it is the same in longsToStr()
l[i] = s.charCodeAt(i*4) + (s.charCodeAt(i*4+1)<<8) +
(s.charCodeAt(i*4+2)<<16) + (s.charCodeAt(i*4+3)<<24);
}
return l; // note running off the end of the string generates nulls since
} // bitwise operators treat NaN as 0
function longsToStr(l) { // convert array of longs back to string
var a = new Array(l.length);
for (var i=0; i<l.length; i++) {
a[i] = String.fromCharCode(l[i] & 0xFF, l[i]>>>8 & 0xFF,
l[i]>>>16 & 0xFF, l[i]>>>24 & 0xFF);
}
return a.join(''); // use Array.join() rather than repeated string appends for efficiency
}
function escCtrlCh(str) { // escape control chars etc which might cause problems with encrypted texts
return str.replace(/[\0\t\n\v\f\r\xa0'"!]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; });
}
function unescCtrlCh(str) { // unescape potentially problematic nulls and control characters
return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); });
}
//}}}
TiddlyHappiness is a term I use to explain how it feels to make a Tiddler, once you get started its just a ride you have to be on to understand, so get one today and be happy!
/***
|Name|TiddlyLifePlugin|
|Source|http://www.TiddlyTools.com/#TiddlyLifePlugin|
|Documentation|http://www.TiddlyTools.com/#TiddlyLifePlugin|
|Version|1.6.5|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Cellular Automata: Conway's "Game of Life"|
!!!!!Documentation
<<<
[[TiddlyLife]] is a TiddlyWiki-enabled javascript version of Conway's "Game of Life" cellular automata simulator. It provides a "life matrix" on which to place cells, run the simulation, and observe the results. The speed of the simulation is related to the total size of the matrix (i.e., rows X cols): the larger the matrix, the longer it takes to compute each 'generation' of cells.
You can set the number of rows and columns in the matrix, as well as the size of each cell and the color of the cells, grid, and background. You can use the mouse to click/drag over the grid to add/delete cells (hold shift to add "walls"). The current life matrix can be saved as text in a tiddler and then reloaded later from a popup list of tiddlers tagged with<<tag tiddlyLife>>
Please see Wikipedia for an overview of [[Conway's "Game of Life"|http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life]].
<<<
!!!!!Syntax
<<<
{{{
<<life cellcolor:... gridcolor:... bgcolor:... wallcolor:...
cellsize:... gridwidth:... delay:... limit:... stability:...
autostart nomenu nostats noedit width:... height:... tid:...>>
}}}
where all parameters are optional (default values are shown in parentheses):
*''cellcolor:'' (//green//), ''gridcolor:'' (//#111//), ''bgcolor:'' (//black//) and ''wallcolor:'' (//gray//)<br>are CSS color names or RGB values (e.g.: "black", "blue", "#fff", "#9af", etc.)
*''cellsize:'' (//1em//), and ''gridwidth:'' (//1px//)<br>are CSS dimensions, including units (e.g., px,em,cm,in)
*''delay:'' (//0//)<br>delay time (in msec) between simulation ticks (a larger number results in a slower simulation, but also leaves more CPU cycles available for other processes)
*''limit:'' (//10000//)<br>automatically stop stimulation after the indicated number of generations (use 0 for no limit)
*''stability:'' (//500//)<br>automatically stop simulation if population count remains stable for the indicated number of generations (use 0 for no limit)
*''autostart'' (//keyword//)<br>when present, causes the simulation to begin running as soon as the macro is rendered
*''nomenu'' (//keyword//)<br>when present, suppresses display of command menu (use with ''autostart'')
*''nostats'' (//keyword//)<br>when present, suppresses display of the current matrix statistics (generation #, population count/min/avg/max, birthrate/deathrate, average age)
*''noedit'' (//keyword//)<br>when present, prevent hand editing of cells in the matrix. Instead, clicking on the matrix starts/stops the simulation (useful with ''nomenu'' and ''autostart'')
*''width:'' (//30//) and ''height:'' (//30//)<br>are dimensions for an empty life matrix
*''tid:'' (//no default//)<br>specifies a tiddler containing a saved life matrix definition. note: when using a saved matrix, the width/height are determined by the stored definition and any width/height macro parameters that are present will be ignored.
<<<
!!!!!Examples
<<<
"Multi-cellular organisms" can be constructed by arranging blocks in specific patterns that exhibit emergent behaviors such as movement, symmetry, oscillation and generative abilities. Two well-known organisms that are [[discussed in the Wikipedia article|http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life]] are ''//gliders//'' and ''//Gosper's glider gun//'':
[[GliderDance]]: many small moving organisms just missing each other!
{{{<<life cellsize:.8em tid:GliderDance>>}}}
<<life cellsize:.8em tid:GliderDance>>
[[GliderGun]]: generates a stream of gliders that hits a wall
{{{<<life cellsize:.6em tid:GliderGun>>}}}
<<life cellsize:.6em tid:GliderGun>>
... and here's an ''empty life matrix'' for you to play with:
{{{<<life>>}}}
<<life>>
<<<
!!!!!Revisions
<<<
2008.10.11 [1.6.5] added 'step' command. Also, for performance, removed birth/death stats and don't display average age (but //do// calculate it)
2008.10.10 [1.6.0] added birthrate, deathrate, and average age to statistics
2008.10.09 [1.5.0] use //named// params. changed matrix values: 0==empty, >0==alive, <0==wall, where value=generation # in which cell was created
2008.10.08 [1.4.0] added 'stability' and 'limit' options (replaces 'autostop' checkbox)
2008.10.08 [1.3.0] added optional 'autostart', 'nomenu' and 'nostats' macro params
2008.10.07 [1.2.1] fixed update handling so multiple timers will no longer be created
2008.10.06 [1.2.0] added support for walls (unchanging dead cells) using dead="-", alive="O", wall="#"
2008.10.06 [1.1.1] redraw optimization: 300% speed improvement by setting CSS only when cell state *changes*
2008.10.05 [1.1.0] drag to draw (set/clear) multiple cells, new option controls (rows,cols,cellsize,delay,autostop), popup list for opening saved matrix
2008.10.04 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TiddlyLifePlugin= {major: 1, minor: 6, revision: 5, date: new Date(2008,10,11)};
config.shadowTiddlers.TiddlyLife="<<life>>";
config.macros.life={
//}}}
// // DEFAULTS
//{{{
cellcolor: "green",
cellsize: "1em",
gridcolor: "#111",
gridwidth: "1px",
bgcolor: "black",
wallcolor: "gray",
width: 30,
height: 30,
stability: 300,
limit: 5000,
delay: 0,
//}}}
// // TRANSLATE
//{{{
lifeTag: "tiddlyLife",
titlePrompt: "Enter a new tiddler title",
openErr: "Could not open '%0'",
limitMsg: "stopped: completed %0 generations",
stableMsg: "stopped: no growth for %0 generations",
cellEditTip: "CLICK=set/clear, SHIFT-CLICK=set wall",
noEditTip: "CLICK=start/stop simulation",
startLabel: "start",
stopLabel: "<b>STOP</b>",
stats: "gen: <b>%0</b> pop: <b>%1</b> min: <b>%2</b> avg: <b>%3</b> max: <b>%4</b> %5",
cmds: "<a href='#' title='start/stop simulation'\
onclick='return config.macros.life.toggle(\"%0\")'>%1</a> \
| <a href='#' title='advance simulation by one generation'\
onclick='return config.macros.life.step(\"%0\")'>step</a> \
| <a href='#' title='reload the starting life matrix'\
onclick='return config.macros.life.reset(\"%0\")'>reset</a> \
| <a href='#' title='clear the life matrix'\
onclick='return config.macros.life.clear(\"%0\")'>clear</a> \
| <a href='#' title='load a life matrix from a tiddler'\
onclick='return config.macros.life.open(this,event,\"%0\")'>open</a> \
| <a href='#' title='save the current life matrix to a tiddler'\
onclick='return config.macros.life.save(\"%0\")'>save</a> \
| <a href='#' title='change simulation option settings'\
onclick='var s=this.nextSibling.style; var show=s.display==\"none\"; \
s.display=show?\"block\":\"none\"; \
return false;'>options</a><span style='display:none'>%2</span>",
opts: "delay:<input type='text' title='delay between generations (msec)' \
value='%1' style='width:4em;font-size:90%;text-align:center;'>\
limit:<input type='text' title='automatically stop after N generations (0=no limit)' \
value='%2' style='width:4em;font-size:90%;text-align:center;'>\
stability:<input type='text' title='stop if population count is stable for N generations (0=no limit)'\
value='%3' style='width:4em;font-size:90%;text-align:center;'><br>\
rows:<input type='text' title='matrix height' \
value='%4' style='width:3em;font-size:90%;text-align:center;'>\
cols:<input type='text' title='matrix width' \
value='%5' style='width:3em;font-size:90%;text-align:center;'>\
cells:<input type='text' title='cellsize' \
value='%6' style='width:3em;font-size:90%;text-align:center;'>\
<input type='button' value='OK' style='font-size:90%;' \
title='change the life matrix dimensions' \
onclick='var ins=this.parentNode.getElementsByTagName(\"input\"); \
var t=ins[0].value; var l=ins[1].value; var a=ins[2].value; \
var h=ins[3].value; var w=ins[4].value; var s=ins[5].value; \
return config.macros.life.setoptions(\"%0\",w,h,s,t,a,l)'>",
msgfmt: "<br><span title='use \"options\" command to change autostop settings' \
onclick='this.style.display=\"none\"' \
style='display:block;position:absolute;padding:0 .5em;cursor:pointer; \
margin:.5em;color:%1;background-color:%2;border:1px solid %1'>%3</span>",
//}}}
// // GENERAL UTILITIES
//{{{
empty: function(w,h) { // generate an empty matrix
var m=[]; for (var r=0; r<h; r++) { m[r]=[]; for (var c=0; c<w; c++) m[r][c]=0; } return m;
},
paste: function(row,col,m1,m2) { // copy one matrix into another
for (var r=row; r<m1.length && r<m2.length; r++)
for (var c=col; c<m1[r].length && c<m2[r].length; c++)
m2[r][c]=m1[r][c];
},
zeroPad: function(v,m) { // formatting for population stats
var t=("0000"+v.toString());
return t.substr(t.length-Math.max(m.toString().length,v.toString().length));
},
getValue: function(s) { // cell value from stored matrix symbol
return s=='O'?1:s=='#'?-1:0;
},
getSymbol: function(v) { // stored matrix symbol from cell value
return v>0?'O':v<0?'#':'-';
},
getColor: function(v,d) { // color from cell value
return v>0?d.cellcolor:v<0?d.wallcolor:'';
},
getAge: function(v,d) { // age of a cell or wall
return v?(d.gen||1)-Math.abs(v):0;
},
isAlive: function(v) { // 0 if dead, 1 if alive
return v>0;
},
isWall: function(v) { // 1 if cell is a wall
return v<0;
},
isAncient: function(v,d) { // true if cell age is more than ten times the average age
return d.avgage>0 && this.getAge(v,d)>10*d.avgage;
},
//}}}
// // MACRO HANDLER
//{{{
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var autostart =params.contains("autostart");
var nomenu =params.contains("nomenu");
var nostats =params.contains("nostats");
var noedit =params.contains("noedit");
params = paramString.parseParams("anon",null,true,false,false);
var cellcolor =getParam(params,"cellcolor",this.cellcolor);
var wallcolor =getParam(params,"wallcolor",this.wallcolor);
var cellsize =getParam(params,"cellsize",this.cellsize);
var gridcolor =getParam(params,"gridcolor",this.gridcolor);
var gridwidth =getParam(params,"gridwidth",this.gridwidth);
var bgcolor =getParam(params,"bgcolor",this.bgcolor);
var tid =getParam(params,"tid",this.tid);
var w =getParam(params,"rows",this.width);
var h =getParam(params,"cols",this.height);
var delay =getParam(params,"delay",this.delay);
var stability =getParam(params,"stability",this.stability);
var limit =getParam(params,"limit",this.limit);
var m=this.load(tid); if (!m) var m=this.empty(w,h);
var id="tiddlyLife_"+new Date().getTime()+Math.random();
var e=createTiddlyElement(place,"span",id,"tiddlyLife");
e.data={w:w, h:h, tid:tid, matrix:m, gen:0, stopped:!autostart,
gencount:0, stable:0, total:0, birthrate:0, deathrate:0, age:0,
cellcolor:cellcolor, wallcolor:wallcolor, gridcolor:gridcolor, bgcolor:bgcolor,
cellsize:cellsize, gridwidth:gridwidth, delay:delay, stability:stability, limit:limit,
nostats:nostats, nomenu:nomenu, noedit:noedit };
this.draw(id); if (autostart) this.go(id);
},
//}}}
// // COMMAND HANDLERS
//{{{
toggle: function(id) { // toggle simulation
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
if (d.stopped) this.go(id); else this.stop(id);
return false;
},
go: function(id) { // start simulation and set command text
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
var b=e.getElementsByTagName("a")[0]; if (b) b.innerHTML=this.stopLabel;
d.stopped=false; d.stable=0; d.gencount=0; clearTimeout(d.timer); this.refresh(id);
return false;
},
stop: function(id) { // stop simulation and set command text
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
var b=e.getElementsByTagName("a")[0]; if (b) b.innerHTML=this.startLabel;
d.stopped=true; clearTimeout(d.timer);
return false;
},
reset: function(id) { // reload initial matrix
var e=document.getElementById(id); if (!e) return; var d=e.data;
var m=this.load(d.tid); if (!m) var m=this.empty(d.w,d.h);
this.stop(id); d.matrix=m; d.gen=0; this.draw(id);
return false;
},
clear: function(id) { // load empty matrix
var e=document.getElementById(id); if (!e) return; var d=e.data;
var tid=d.tid; d.tid=""; this.reset(id); d.tid=tid;
return false;
},
setoptions: function(id,w,h,s,t,a,l) { // options: width,height,cellsize,delaytime,autostop,limit
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
clearTimeout(d.timer); // stop simulation while changing matrix content
d.w=w; d.h=h; d.stability=a; d.limit=l; d.cellsize=s; d.delay=t;
var m2=this.empty(w,h); this.paste(0,0,m,m2); d.matrix=m2; this.draw(id);
d.min=Math.min(d.min,d.count); d.max=Math.max(d.max,d.count);
if (!d.stopped) d.timer=setTimeout('config.macros.life.refresh("'+id+'")',d.delay);
return false;
},
//}}}
// // I/O HANDLERS
//{{{
load: function(tid) { // read tiddler into matrix
var t=store.getTiddlerText(tid); if (!t) return;
var lines=t.split("\n"); var m=[];
if (lines[0]=="{{{") lines.shift();
if (lines[lines.length-1]=="}}}") lines.pop();
for (var r=0; r<lines.length; r++) { m[r]=[];
for (var c=0; c<lines[r].length; c++) m[r].push(this.getValue(lines[r].substr(c,1)));
}
return m;
},
save: function(id) { // write matrix to tiddler
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
var tid=d.tid; var msg=config.messages.overwriteWarning.format([tid]);
while (!tid||!tid.length ||(store.tiddlerExists(tid)&&!confirm(msg)))
{ tid=prompt(this.titlePrompt,tid); if (!tid||!tid.length) return false; }
d.tid=tid;
var out=[];
out.push('{{{');
for (var r=0; r<m.length; r++) { var row='';
for (var c=0; c<m[r].length; c++) row+=this.getSymbol(m[r][c]);
out.push(row);
}
out.push('}}}');
var t=store.getTiddler(tid);
var txt=out.join('\n');
var who=t&&config.options.chkForceMinorUpdate?t.modifier:config.options.txtUserName;
var when=t&&config.options.chkForceMinorUpdate?t.modified:new Date();
var tags=t?t.tags:[]; tags.pushUnique(this.lifeTag);
var fields=t?t.fields:{};
store.saveTiddler(tid,tid,txt,who,when,tags,fields);
story.displayTiddler(null,tid); story.refreshTiddler(tid,null,true);
return false;
},
open: function(here,event,id) { // select from a list of saved matrix tiddlers
var p=Popup.create(here); if (!p) return false;
p.style.padding="2px .5em";
var tids=store.getTaggedTiddlers(this.lifeTag);
for (var t=0; t<tids.length; t++) {
var b=createTiddlyButton(createTiddlyElement(p,"li"),tids[t].title,tids[t].title,
function() {
var cml=config.macros.life;
var id=this.getAttribute("id");
var e=document.getElementById(id); if (!e) return false; var d=e.data;
var tid=this.getAttribute("tid");
var m=cml.load(tid);
if (!m) { displayMessage(this.openErr.format([tid])); return false; }
cml.stop(id); d.tid=tid; d.matrix=m; d.gen=0; cml.draw(id);
return false;
});
b.setAttribute("id",id);
b.setAttribute("tid",tids[t].title);
}
Popup.show();
event.cancelBubble=true;if(event.stopPropagation)event.stopPropagation();
return false;
},
//}}}
// // EDIT HANDLERS
//{{{
mousedown: function(here,ev,id,r,c) { // start manual edit
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
d.savedstop=d.stopped; this.stop(id); d.draw=!this.isAlive(m[r][c])?(d.gen||1):0;
return this.setcell(here,id,r,c,ev&&ev.shiftKey?-(d.gen||1):d.draw);
},
mouseover: function(here,ev,id,r,c) { // drag edit
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
if (d.draw!==undefined) this.setcell(here,id,r,c,ev&&ev.shiftKey?-(d.gen||1):d.draw);
return false;
},
mouseup: function(here,ev,id,r,c) { // end manual edit
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
if (d.savedstop!==undefined) d.stopped=d.savedstop; if (!d.stopped) this.go(id);
d.draw=undefined; d.savedstop=undefined;
return false;
},
setcell: function(here,id,r,c,v) { // set cell content and revise stats display
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
if (m[r][c]==v) return;
if (this.isAlive(m[r][c]) && !this.isAlive(v)) { d.count--; d.min=Math.min(d.min,d.count); }
if (!this.isAlive(m[r][c]) && this.isAlive(v)) { d.count++; d.max=Math.max(d.max,d.count); }
m[r][c]=v; here.style.background=this.getColor(v,d);
this.showstats(id,'');
return false;
},
//}}}
// // RENDER
//{{{
draw: function(id) { // render entire tiddlyLife container (menu, stats, and table)
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
var out=[]; var count=0; var maxwidth=0;
var style="border:%0 solid %1;background:%2;height:%3 !important;width:%3; !important";
var onmousedown=d.noedit?"":"return config.macros.life.mousedown(this,event,'%4',%5,%6);";
var onmouseover=d.noedit?"":"return config.macros.life.mouseover(this,event,'%4',%5,%6);";
var onmouseup =d.noedit?"":"return config.macros.life.mouseup(this,event,'%4',%5,%6);";
var onclick =d.noedit?"return config.macros.life.toggle('%4');":"";
var tip="[%7,%8] "+(d.noedit?this.noEditTip:this.cellEditTip);
var cell='<td style="margin:0;padding:0;'+style +'" title="'+tip+'" onclick="'+onclick
+'" onmousedown="'+onmousedown+'" onmouseover="'+onmouseover+'" onmouseup="'+onmouseup+'"></td>';
out.push('<table style="table-layout:fixed;border-collapse:collapse;'
+'margin:0;padding:0;border:0;background-color:'+d.bgcolor+'">');
for (var r=0; r<m.length; r++) {
if (m[r].length>maxwidth) maxwidth=m[r].length;
out.push('<tr style="margin:0;padding:0;border:0;">');
for (var c=0; c<m[r].length; c++) {
out.push(cell.format([d.gridwidth,d.gridcolor,this.getColor(m[r][c],d),
d.cellsize,id,r,c,r+1,c+1]));
count+=this.isAlive(m[r][c]);
}
out.push('</tr>');
}
out.push('</table>');
d.count=count;
if (!d.gen) { d.gencount=d.stable=d.total=d.oldest=d.maxage=d.avgage=0; d.min=d.max=d.avg=count; }
var hdr=[];
if (!d.nomenu) hdr.push(this.cmds.format([id,d.stopped?this.startLabel:this.stopLabel,
this.opts.format([id,d.delay,d.limit,d.stability,m.length,maxwidth,d.cellsize])]));
if (!d.nostats) hdr.push('<div style="font-size:90%">'
+this.stats.format([d.gen,d.count,d.min,d.avg,d.max])+'</div>');
e.innerHTML=hdr.join('')+out.join('');
return false;
},
//}}}
// // RUN SIMULATION
//{{{
refresh: function(id) { // timer-based refresh cycle
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
this.step(id); if (!d.stopped) d.timer=setTimeout('config.macros.life.refresh("'+id+'")',d.delay);
return false;
},
step: function(id) { // calc new matrix, gather stats and display changes
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
// calculate next generation
var m2=[]; var count=agecount=agetotal=oldest=0; d.gen++; d.gencount++;
var table=e.getElementsByTagName("table")[0]; if (!table) return;
var rows=table.getElementsByTagName("tr");
for (var r=0; r<m.length; r++) {
m2[r]=[];
var cells=rows[r].getElementsByTagName("td");
for (var c=0; c<m[r].length; c++) {
var v=this.tick(d.gen,m,r,c); // apply Conway's 23/3 rule
m2[r].push(v);
var color=this.getColor(v,d);
if (cells[c].style.backgroundColor!=color)
cells[c].style.backgroundColor=color;
if (this.isAlive(v)) {
var a=this.getAge(v,d);
if (!this.isAncient(v,d)) { agecount++; agetotal+=a; }
oldest=Math.max(oldest,a);
count++;
}
}
}
d.matrix=m2; // update matrix
this.calcstats(id,count,agecount,agetotal,oldest); // calculate statistics
var msg=this.autostop(id); // autostop if conditions apply
this.showstats(id,msg); // show statistics and message (if any)
return false;
},
tick: function(gen,m,r,c) { // apply Conway's 23/3 rule
if (this.isWall(m[r][c])) return m[r][c]; // walls don't change
var prevrow=r>0?r-1:(m.length-1);
var nextrow=r<m.length-1?r+1:0;
var prevcol=c>0?c-1:(m[r].length-1);
var nextcol=c<m[r].length-1?c+1:0;
var near=this.isAlive(m[prevrow][prevcol]) + this.isAlive(m[prevrow][c]) + this.isAlive(m[prevrow][nextcol])
+this.isAlive(m[r][prevcol]) + this.isAlive(m[r][nextcol])
+this.isAlive(m[nextrow][prevcol]) + this.isAlive(m[nextrow][c]) + this.isAlive(m[nextrow][nextcol]);
if (!this.isAlive(m[r][c])&&near==3) return gen; // birth
if (this.isAlive(m[r][c])&&near==2||near==3) return m[r][c]; // stay alive
return 0; // death
},
autostop: function(id) { // autostop if run limit reached or no changes for N generations
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
var msg='';
var limited=d.limit>0 && d.gencount>=d.limit;
var stabilized=d.stability>0 && d.stable>=d.stability;
if (limited || stabilized) {
this.stop(id);
msg=stabilized?this.stableMsg.format([d.stability]):this.limitMsg.format([d.limit]);
msg=this.msgfmt.format([id,d.cellcolor,d.bgcolor,msg]);
}
return msg;
},
calcstats: function(id,count,agecount,agetotal,oldest) {
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
d.stable+=(count==d.count)?1:-d.stable; // add one or reset to zero
d.count=count; d.total+=count;
d.min=Math.min(d.min,count); d.max=Math.max(d.max,count); d.avg=Math.floor(d.total/d.gen);
d.avgage=agecount?agetotal/agecount:0; d.oldest=oldest; d.maxage=Math.max(d.maxage,oldest);
return false;
},
showstats: function(id,msg) {
var e=document.getElementById(id); if (!e) return; var d=e.data; var m=d.matrix;
var stats=e.getElementsByTagName("div")[0];
if (stats) stats.innerHTML=this.stats.format([d.gen,this.zeroPad(d.count,d.max),d.min,d.avg,d.max,msg]);
return false;
}
}
//}}}
TiddlyWiki WHAT? What the heck is a TiddlyWiki anyway you MUST wonder, I can only imagine. Well, I did too, and then when I did find out what it was, being from a web design background, I was like WHOA, this is incredible - so here I am - and you can be too, if you want. Its all FREE! Take a look for yourself, http://www.tiddlywiki.com and then decide, is it right for you?
/***
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 = 'adamrock';
// 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")
});
//}}}
/***
| Name|TwabPlugin|
| Author|Vincent ~DiBartolo ([[vadibart@gmail.com|mailto:vadibart@gmail.com]])|
| Version|2.2|
| Date|12/08/2008|
| Source|http://www.tiddly-twab.com/#TwabPlugin|
| License|BSD License - http://www.tiddly-twab.com/#TheBSDLicense|
| Requires|~TW2.x, [[DataTiddlerPlugin]], [[FormTiddlerPlugin]], [[InlineJavascriptPlugin]], [[PartTiddlerPlugin]], and any Tiddlers with the [[twab]] tag in the source file |
!Description
Elegant system for keeping your Address Book inside a TiddlyWiki document. Supports import and export of contacts via CSV data. Built-in support for Google, Yahoo, MSN, and Outlook CSV formats. Supports customized formats for those not built-in.
!History
* 08-Dec-08, version 2.2 - use company name if neither first name or last name exists on import, allow for multiple contacts with same title
* 21-Jul-08, version 2.1 - use contact's first and last name in email link if it's present (Thanks to Lyall)
* 20-Feb-08, version 2.0 - import and export Google, Yahoo, MSN, Outlook, or custom CSV formats
* 29-Jun-07, version 1.1 - adding support for mailto:, http:, and map directions
* 20-Jun-07, version 1.0 - preparing for release, changed name to twab
* 19-Jun-07, version 0.4 - modified how new contacts are added
* 21-Nov-06, version 0.3 - changed name from ContactParserMacro to AddressBookMacro
* 10-Oct-06, version 0.2 - converted from regex parsing for title of contact Tiddler to JSON
* 09-Oct-06, version 0.1 - created file
!Example of Adding New Contact
Place the following code in any Tiddler:
{{{
<<twab>>
}}}
Which will result in: <<twab>>
You can add extra parameters to change the button's name as in:
{{{
<<twab press this button>>
}}}
Which will result in: <<twab press this button>>
See [[About:twab:Overview]] for more information.
!Example of Import
Place the following code in any Tiddler:
{{{
<<twab Import AddressBook>>
}}}
Which will result in: <<twab Import AddressBook>>
See [[About:twab:Import]] for more information.
!Example of Export
Place the following code in any Tiddler:
{{{
<<twab Export AddressBook>>
}}}
Which will result in: <<twab Export AddressBook>>
See [[About:twab:Export]] for more information.
! Generate Test Data for Import
Place the following code in any Tiddler:
{{{
<<twab ImportTest>>
}}}
Which will result in: <<twab ImportTest>>
See [[About:twab:Import]] for more information.
!Code
***/
//{{{
version.extensions.TwabMacro = {
major: 2,
minor: 0,
revision: 0,
date: new Date(2008,02,16),
source: "http://www.tiddly-twab.com"
};
config.macros.twab = {};
config.macros.twab.newButtonText = "new contact";
config.macros.twab.importButtonText = "import contacts";
config.macros.twab.exportButtonText = "export contacts";
config.macros.twab.preClean = true;
config.macros.twab.importTiddler = "TwabImport";
config.macros.twab.exportTiddler = "TwabExport";
config.macros.twab.importTags = "AddressBook";
config.macros.twab.exportTags = "AddressBook";
config.macros.twab.fnameField = "first.name";
config.macros.twab.lnameField = "last.name";
config.macros.twab.companyNameField = "company";
config.macros.twab.mapTagPrefix = "format:";
config.macros.twab.skipFlag = "<skip>";
config.macros.twab.unmappedFlag = "unmapped";
config.macros.twab.handler = function (place, macroName, params) {
if( (params.length == 1) && (params[0] == "ImportTest") ){
//if they want to create an import test button
createTiddlyButton(place, "Populate " + config.macros.twab.importTiddler, "", this.testImport);
return;
} else if( (params.length >= 1) && (params[0] == "Import") ){
//any other params are interpreted as tags to be placed on imported tiddlers
if( params.length >= 2 ){
config.macros.twab.importTags = "";
for( var i=1; i<params.length; i++){
config.macros.twab.importTags += params[i] + " ";
}//for
}//if
//if they want to import data
createTiddlyButton(place, config.macros.twab.importButtonText, "", this.importContacts);
} else if( (params.length >= 1) && (params[0] == "Export") ){
//the first tag of the remainder is the one to use for exports
if( params.length >= 2 ){
config.macros.twab.exportTags = params[1];
}//if
//if they want to export data
createTiddlyButton(place, config.macros.twab.exportButtonText, "", this.exportContacts);
} else {
//assume want to create a new contact - any extra params are button name
var buttonText = "";
for( var i=0; i<params.length; i++){
buttonText += " " + params[i] + " ";
}//for
createTiddlyButton(place, ((buttonText == "") ? config.macros.twab.newButtonText : buttonText), "", this.newContact);
}//if-else ifs
}//function handler
//from: http://www.nicknettleton.com/zine/javascript/trim-a-string-in-javascript
config.macros.twab.trim = function(word) { return word.replace(/^\s+|\s+$/g, ''); }
//proxy that retrieves some data and sets null to empty string
config.macros.twab.getData = function(tiddlerName, fieldName){
var data = DataTiddler.getData(tiddlerName, fieldName);
if( data == null ){
data = "";
}//if
return data;
}//function getData
config.macros.twab.testImport = function(e){
var title = config.macros.twab.importTiddler;
var text = "first.name,last.name,job.title,webpage,phone,email\nPrincess,Leia,\"Leader, Rebels\",http://starwars.wikia.com/wiki/Leia_Organa,,leia@alderaan.com\nDarth,Vader,\"Sith Lord\",http://starwars.wikia.com/wiki/Darth_Vader,555-1212,darth@deathstar.com\nLuke,Skywalker,Jedi Master,http://starwars.wikia.com/wiki/Luke_Skywalker,,luke@tatooine.com";
store.saveTiddler(title, title, text, config.options.txtUserName);
story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
return true;
}//function import
config.macros.twab.newContact = function(e){
var title = prompt("Please enter contact's name", "");
if( (title == null) || (title == "") ){
return;
}//if
store.saveTiddler(title, title, "<<tiddler ContactsFormTemplate>><data>{}</data>", config.options.txtUserName, new Date(), config.macros.twab.importTags);
story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
}//function new
config.macros.twab.importContacts = function(e){
config.macros.twab.deleteAll();
config.macros.twab.parseAll( config.macros.twab.getImportedCSVText() );
alert("Contacts successfully imported.");
return true;
}//function importContacts
config.macros.twab.exportContacts = function(e){
var title = config.macros.twab.exportTiddler;
var mapping = config.macros.twab.getMapping(title, true);
store.saveTiddler(title, title, config.macros.twab.getExportedCSVText(mapping), config.options.txtUserName );
story.displayTiddler(null, title, DEFAULT_VIEW_TEMPLATE);
return true;
}//function exportContacts
config.macros.twab.deleteAll = function(){
if( !config.macros.twab.preClean ){
return;
}//if
//only remove tiddlers tagged with only first tag if contacts each get more than one
var tags = config.macros.twab.importTags.split(" ");
var tag = tags[0];
if( !confirm("Are you sure you want to clear existing Tiddlers tagged \"" + tag + "\"?") ){
return;
}//if
var contacts = store.getTaggedTiddlers(tag);
for( var i=0; i<contacts.length; i++ ){
store.removeTiddler( contacts[i].title );
}//for
}//function deleteAll
config.macros.twab.getImportedCSVText = function(){
return store.getTiddler(config.macros.twab.importTiddler).text;
}//function getImportedCSVText
config.macros.twab.getExportedCSVText = function(mapping){
var returnStr = "";
//get the mapped header columns
for( var i=0; i<mapping.length; i++ ){
if( mapping[i] && (mapping[i] != config.macros.twab.unmappedFlag) ){
var twabCol = mapping[i];
var mapCol = mapping[twabCol];
returnStr += '"' + mapCol + '",';
}//if
}//for
//get the unmapped header columns
var unmappedStr = mapping[config.macros.twab.unmappedFlag];
if( unmappedStr ){
var unmappedArr = unmappedStr.split(",");
for( var i=0; i<unmappedArr.length; i++ ){
returnStr += '"' + unmappedArr[i] + '",';
}//for
//strip off the last ","
returnStr = returnStr.substring(0, returnStr.length-1);
}//if
returnStr += "\n";
//get all contacts
var tags = config.macros.twab.exportTags.split(" ");
var contacts = store.getTaggedTiddlers( tags[0] );
for( var i=0; i<contacts.length; i++ ){
returnStr += config.macros.twab.exportContact(contacts[i], mapping) + "\n";
}//for
return returnStr;
}//function getExportedCSVText
config.macros.twab.parseAll = function (contactStr){
var rows = contactStr.split("\n");
if( rows.length < 2 ){
alert("Two or more rows must be present to parse contacts.");
return;
}//if
var header = config.macros.twab.parseHeader(rows[0]);
if( header.length == 0 ){
return;
}//if
var contacts = new Array();
for( i=1; i<rows.length; i++){
contacts[ i-1 ] = config.macros.twab.parseContact(header, rows[i]);
}//for
//uncomment this to get contact-by-contact alerts
//config.macros.twab.debugAll(contacts);
config.macros.twab.addAll(contacts);
}//function parseAll
config.macros.twab.parseHeader = function(row){
var mappedHeader = new Array();
//get the raw data
var unmappedHeader = config.macros.twab.parseCSV(row);
//get the appropriate mapping
var mapping = config.macros.twab.getMapping(config.macros.twab.importTiddler, false);
//now convert the unmapped header to the mapped header
for( var i=0; i<unmappedHeader.length; i++ ){
var colName = unmappedHeader[i].replace(/ /g, ".").toLowerCase();
if( mapping[colName] ){
mappedHeader[i] = mapping[colName];
} else {
mappedHeader[i] = config.macros.twab.skipFlag;
}//if
//uncomment this to get field-by-field alerts from the header
//alert("Header field " + i + " is '" + mappedHeader[i] + "'");
}//for
return mappedHeader;
}//function parseHeader
config.macros.twab.getMapping = function(tiddlerName, isExport){
var mapTiddlerName = "";
if( store.getTiddler(tiddlerName) ){
//see if they've declared a mapping (preset or custom) by looking
//at a tag on the tiddler passed in
var tagStr = ""+store.getTiddler(tiddlerName).tags;
var tags = tagStr.split(',');
for( var i=0; i<tags.length; i++ ){
if( tags[i].indexOf( config.macros.twab.mapTagPrefix ) == 0 ){
mapTiddlerName = tags[i].replace(config.macros.twab.mapTagPrefix, "");
}//if
}//for
}//if
//parse the mapping Tiddler
var mapTiddler = config.macros.twab.getMappingTiddler(mapTiddlerName);
if( !mapTiddler ){
alert("Import/Export Format Tiddler " + mapTiddler + " does not exist. Can't proceed.");
return new Array();
}//if
var mapText = ""+store.getTiddler(mapTiddler).text;
var mapArr = mapText.split("\n");
var mapping = new Array();
for( var i=0; i<mapArr.length; i++ ){
var rule = mapArr[i].split("=");
if( !rule || (rule.length < 2) ){
continue;
}//if
//uncomment this to see what mapping is being applied to your import file
//alert("Twab column '" + twabCol + "' is mapped to input tiddler column '" + mapCol + "'");
if( isExport ){
var twabCol = config.macros.twab.trim( rule[0] );
var mapCol = config.macros.twab.trim( rule[1] );
if( mapCol == config.macros.twab.skipFlag ){
continue;
}//inner if
mapping[twabCol] = mapCol;
mapping[i] = twabCol;
} else {
var twabCol = config.macros.twab.trim( rule[0].replace(/ /g, ".").toLowerCase() );
var mapCol = config.macros.twab.trim( rule[1].replace(/ /g, ".").toLowerCase() );
mapping[mapCol] = twabCol;
mapping[i] = mapCol;
}//outer if-else
}//for
return mapping;
}//function getMapping
config.macros.twab.getMappingTiddler = function(name){
var tiddlerName = "TwabDefaultFieldMap";
if( name == "google" ){
tiddlerName = "TwabGoogleFieldMap";
} else if( name == "yahoo" ){
tiddlerName = "TwabYahooFieldMap";
} else if( name == "msn" ){
tiddlerName = "TwabMSNFieldMap";
} else if( name == "outlook" ){
tiddlerName = "TwabOutlookFieldMap";
} else {
//see if a Tiddler by this name exists, if not use the default
if( (name != "default") && store.getTiddler(name) ){
tiddlerName = name;
} else {
tiddlerName = "TwabDefaultFieldMap";
}//inner if-else
}//outer if-else ifs
return tiddlerName;
}//function getMappingTiddler
config.macros.twab.parseCSV = function(row){
var scrubbed = new Array();
var fields = row.split(",");
for( var i=0; i<fields.length; i++){
//if starts with quote but doesn't end with a quote, likely had a comma in the middle
if( (fields[i].charAt(0) == '"') && (fields[i].charAt( fields[i].length-1) != '"') ){
//Hotmail bug: last contact doesn't have ending double-quote
if( i == (fields.length-1) ){
scrubbed[ scrubbed.length ] = fields[i].replace(/"/g, "");
continue;
}//if
var quoted = fields[i++];
if( !fields[i] ){
continue;
}//if
while( fields[i].charAt( fields[i].length-1 ) != '"' ){
quoted += "," + fields[i++];
}//while
quoted += "," + fields[i];
scrubbed[ scrubbed.length ] = quoted.replace(/"/g, "");
} else {
scrubbed[ scrubbed.length ] = fields[i].replace(/"/g, "");
}//if-else
}//for
return scrubbed;
}//function parseCSV
config.macros.twab.parseContact = function (header, row){
var returnStr = "";
var fields = config.macros.twab.parseCSV(row);
for( var i=0; i<fields.length; i++ ){
if( header[i] == config.macros.twab.skipFlag ){
continue;
} else if( fields[i] ){
returnStr += "\"" + header[i] + "\":\"" + fields[i] + "\",";
}//if-else
}//for
return returnStr.substr(0, returnStr.length-1);
}//function parseContact
//export a particular contact to a particular mapping
config.macros.twab.exportContact = function(contactTiddler, mapping){
var returnStr = "";
var text = contactTiddler.text;
//have to strip out FormTiddler stuff
text = text.replace(/<<tiddler ContactsFormTemplate>>/g, "");
text = text.replace(/<data>/g, "");
text = text.replace(/<\/data>/g, "");
text = text.replace(/\n/g, "");
//use JSON format to our advantage
var contact = eval( "("+text+")" );
for( var i=0; i<mapping.length; i++){
var twabCol = mapping[i];
if( !(twabCol) ||
!(mapping[twabCol]) ||
(twabCol == config.macros.twab.unmappedFlag) ||
(mapping[twabCol] == config.macros.twab.skipFlag)){
continue;
}//if
//Google hack - on export, "Name" should be "first.name" and "last.name" together.
//Know this because "first.name" is mapped to "Name" and "last.name" is not mapped
if( (twabCol == config.macros.twab.fnameField) && !mapping[config.macros.twab.lnameField] ){
returnStr += '"' + contact[config.macros.twab.fnameField] + " " + contact[config.macros.twab.lnameField] + '",'
} else if( contact[twabCol] ){
returnStr += '"' + contact[twabCol] + '",';
} else {
returnStr += ",";
}//if-else
}//for
//get the unmapped columns
var unmappedStr = mapping[config.macros.twab.unmappedFlag];
if( unmappedStr ){
var unmappedArr = unmappedStr.split(",");
for( var i=0; i<unmappedArr.length; i++ ){
returnStr += ",";
}//for
//strip out the last ","
returnStr = returnStr.substr(0, returnStr.length-1);
}//if
return returnStr.replace(/\n/g, "");
}//function exportContact
//add/overwrite existing contacts with the data parsed out of the import tiddler
config.macros.twab.addAll = function(contacts){
for( var i=0; i<contacts.length; i++ ){
if( !contacts[i] ){
continue;
}//if
//use JSON format to our advantage
var toEval = "({" + contacts[i] + "})";
var contact = eval(toEval);
var title = config.macros.twab.getSaveTitle(contact);
if( title == "" ){
continue;
}//if
//add it now
var text = config.macros.twab.toTiddlyFormat(contacts[i]);
store.saveTiddler(title, title, text, config.options.txtUserName, new Date(), config.macros.twab.importTags);
}//for
}//function addAll
config.macros.twab.getSaveTitle = function(contact){
var title = "";
if( contact[config.macros.twab.fnameField] ){
title += contact[config.macros.twab.fnameField];
}//if
if( contact[config.macros.twab.lnameField] ){
title += contact[config.macros.twab.lnameField];
}//if
if( title == "" ){
title = contact[config.macros.twab.companyNameField];
}//if
if( title == "" ){
alert("Contact missing name field - could not be added: \n" + contacts[i]);
return "";
}//if
if( store.tiddlerExists(title) ){
//try up to 50 times
var seqTitle = "";
var foundOne = false;
for( var i=2; i<51; i++){
seqTitle = title + " (" + i + ")";
if( !store.tiddlerExists(seqTitle) ){
title = seqTitle;
foundOne = true;
break;
}//innermost if
}//inner if
//if got to 50 then there's a problem
if( !foundOne ){
alert("Seriously, you really have that many contacts named '" + title + "'? Wow.");
return "";
}//if
}//if
return title;
}//function getSaveTitle
config.macros.twab.toTiddlyFormat = function(contact){
var tpl = config.macros.twab.getContactTemplate();
return tpl.replace( config.macros.twab.getMacroName(), contact);
}//function toTiddlyFormat
config.macros.twab.getContactTemplate = function(){
var macroName = config.macros.twab.getMacroName();
return "<<tiddler ContactsFormTemplate>>\n<data>{" + macroName + "}</data>";
}//function getContactTemplate
config.macros.twab.getMacroName = function(){
return "#thisContact#";
}//function getMacroName
config.macros.twab.debugAll = function(contacts){
for( var i=0; i<contacts.length; i++ ){
//alert( contacts[i] );
alert( config.macros.twab.toTiddlyFormat(contacts[i]) );
}//for
}//function debugAll
config.macros.twab.populateLinks = function(place){
config.macros.twab.populateEmail(place, "email");
config.macros.twab.populateEmail(place, "other.email");
config.macros.twab.populateEmail(place, "business.email");
config.macros.twab.populateHref(place, "webpage");
config.macros.twab.populateMap(place, "home");
config.macros.twab.populateHref(place, "business.webpage");
config.macros.twab.populateMap(place, "business");
}//function populateLinks
config.macros.twab.populateEmail = function(place, fieldName){
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldName);
if( element ){
element.innerHTML = config.macros.twab.formatEmail(tiddlerName, fieldName);
}//if
}//function populateEmail
//Thanks to Udo for the idea for this solution and to Lyall for the code to use display name
config.macros.twab.formatEmail = function(tiddlerName, fieldName){
var returnStr = "";
var mailTo = config.macros.twab.getData(tiddlerName, fieldName);
var displayName = config.macros.twab.getData(tiddlerName, "first.name") + " " + config.macros.twab.getData(tiddlerName, "last.name");
if( mailTo != "" ){
if( displayName != "" ){
mailTo = displayName + "<" + mailTo + ">";
}//if
returnStr = "<a href=\"mailto:" + mailTo + "\" content=\"\">(email)</a>";
}//if
return returnStr;
}//function formatEmail
config.macros.twab.populateHref = function(place, fieldName){
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldName);
if( element ){
element.innerHTML = config.macros.twab.formatHref(tiddlerName, fieldName);
}//if
}//function populateHref
//Thanks to Udo for the idea for this solution
config.macros.twab.formatHref = function(tiddlerName, fieldName){
var returnStr = "";
var href = config.macros.twab.getData(tiddlerName, fieldName);
if( href != "" ){
if( href.indexOf("http") != 0 ){
href = "http://" + href;
}//inner if
returnStr = "<a href=\"" + href + "\" content=\"\" target=\"twab\">(visit)</a>";
}//if
return returnStr;
}//function formatHref
config.macros.twab.populateMap = function(place, fieldType){
var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);
var element = document.getElementById("twab." + fieldType + ".map");
if( element ){
element.innerHTML = config.macros.twab.formatMap(tiddlerName, fieldType);
}//if
}//function populateHref
//Thanks to Udo for the idea for this solution
config.macros.twab.formatMap = function(tiddlerName, fieldType){
//hack for lack of planning of home versus business labels
if( fieldType == "business"){
fieldType += ".";
} else if( fieldType == "home" ){
fieldType = "";
}//if-else if
var returnStr = "";
var address = "";
if( DataTiddler.getData(tiddlerName, fieldType+"address") ){
address += DataTiddler.getData(tiddlerName, fieldType+"address") + " ";
}//if
if( DataTiddler.getData(tiddlerName, fieldType+"city") ){
address += DataTiddler.getData(tiddlerName, fieldType+"city") + " ";
}//if
if( DataTiddler.getData(tiddlerName, fieldType+"state") ){
address += DataTiddler.getData(tiddlerName, fieldType+"state") + " ";
}//if
if( DataTiddler.getData(tiddlerName, fieldType+"postal") ){
address += DataTiddler.getData(tiddlerName, fieldType+"postal") + " ";
}//if
if( address == "" ){
return "";
}//if
var href = "http://maps.google.com/maps?ie=UTF8&hl=en&q=" + address + "&f=q&sampleq=1";
returnStr = "<a href=\"" + href + "\" content=\"\" target=\"twab\">(map)</a>";
returnStr.replace(/ /g, "\+");
return returnStr;
}//function formatMap
//}}}
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 18/11/2010 16:02:44 | AdamRock | [[/|http://adamrock.tiddlyspot.com/#Home]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . |
| 19/11/2010 10:31:39 | Adam Warden | [[/|http://adamrock.tiddlyspot.com/#%5B%5BWant%20or%20Need%5D%5D]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . |
| 20/11/2010 11:29:50 | Adam Warden | [[/|http://adamrock.tiddlyspot.com/#AdamRock]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . |
| 07/12/2010 08:38:21 | YourName | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . | failed |
| 07/12/2010 08:38:38 | YourName | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . | failed |
| 07/12/2010 08:39:43 | AdamRock | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . |
| 31/12/2010 13:26:56 | AdamRock | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . | ok |
| 31/12/2010 13:28:02 | AdamRock | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . | ok |
| 31/12/2010 13:28:58 | AdamRock | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.tiddlyspot.com/index.html]] | . | ok |
| 31/12/2010 13:29:24 | AdamRock | [[/|http://adamrock.tiddlyspot.com/]] | [[store.cgi|http://adamrock.tiddlyspot.com/store.cgi]] | . | [[index.html | http://adamrock.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='tagging' macro='tagging'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<div class='tagged' macro='tags'></div>
<!--}}}-->
Things I NEED:
Voice Over Business ...
Software
General ....
Car stuff - both cars need work
Motorcycle - needs new stuff
Things I WANT:
Laptop
Things to do:
Call insurance company AllState about the roof!
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://adamrock.tiddlyspot.com/controlpanel]] (your control panel username is //adamrock//).
<<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.
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://adamrock.tiddlyspot.com/controlpanel]] (your control panel username is //adamrock//).
<<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.