Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
/***
|''Name:''|AlternateBackupPlugin|
|''Version:''|1.0.2 (6-Mar-2006)|
|''Source:''||
|''Author:''|KyleHale|
|''Type:''|Plugin|
!Description
Hijacks core backupPath function, replacing the datetime naming system with a rotating set of backups (1 through 10 and then back to 1), whose number is limited by the user. Default number of backups is 10, but can be change by modifying the line ''backupFolder = "10"'' to what ever number you want.
!Issues/Todos
* Possible to insert an option into the AdvancedOptions Tiddler? That'd make editing the number of backups that much easier.
!Revision History
* 1.0.2 (6-Mar-2006)
** Wrote code, totally stole SimonBaird's documentation system
* 1.0.1 (3-Mar-2006)
** Wrote code
!Code
***/
{{{
var MaxBackups = config.options.txtMaxBackups;
if(!MaxBackups || MaxBackups == "")
MaxBackups = "10";
if (!config.options.txtCurrentBackup)
config.options.txtCurrentBackup="1";
getBackupPath = function(localPath)
{
var backSlash = true;
var dirPathPos = localPath.lastIndexOf("\\");
if(dirPathPos == -1)
{
dirPathPos = localPath.lastIndexOf("/");
backSlash = false;
}
var backupFolder = config.options.txtBackupFolder;
if(!backupFolder || backupFolder == "")
backupFolder = ".";
var backupPath = localPath.substr(0,dirPathPos) + (backSlash ? "\\" :
"/") + backupFolder + localPath.substr(dirPathPos);
backupNum = config.options["txtCurrentBackup"];
backupNum++;
if (backupNum>MaxBackups) {
backupNum=1;
}
backupPath = backupPath.substr(0,backupPath.lastIndexOf(".")) + "." +
backupNum + ".html";
config.options["txtCurrentBackup"]=backupNum;
saveOptionCookie("txtCurrentBackup");
return backupPath;
}
}}}
Solution to this was adding a beep for the alert, which is a new feature
This is really two bugs in one
* Origin unclear, this is really a new feature
* Properties file, so dependency approach would do nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin unclear, if any
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin unclear, if any
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.9
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Only added lines, so text approach would return nothing
* Dependency approach would say 1.1
* Found in initial revision
* Origin 1.1
* Text approach would return 1.1
* DA would see line as not mapping, and return 1.1
* Found in initial revision
* Origin 1.1
* Added line only, so text approach would return nothing
* Dependency approach would //probably// return 1.1
* Found in initial version
* Origin unclear, possibly 1.5, possibly none
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin unclear, if any
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Update to existing fix
* Properties file, so dependency approach would return nothing
* Text approach would identify 1.10, the previous fix
* Update to existing fix
* Properties file, so dependency approach would return nothing
* Text approach would identify 1.10, the previous fix
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Both approaches would say 1.1
* Actually a separate bug. False positive in commit linking
* Both approaches would return something
* Origin in different [[file|Commit 1.12|/rachota/src/org/cesilko/rachota/core/Day.java]]
* Only added lines, so text approach would return nothing
* Dependency approach would return 1.2 (intraprocedural). Interprocedural //might// return other file
* Origin hard to determine. Either when the ChangeListener was first added or when updateTotalTime was added. May not be a bug at all, might be a new feature
* Only contains added lines, so text-based approach would discover nothing
* Only adds new method calls, so intraprocedural dependency approach would return when containing method was added, 1.3
* Interprocedural dependency approach would //probably// give 1.3, when the ChangeListener was added, if it returned anything
* Update to existing fix
* Properties file, so dependency approach would return nothing
* Text approach would identify 1.11, the previous fix
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Both approaches would say 1.1, assuming DA sees entire line as not mapping
* Found in initial revision
* Appears to be origin of 1.2
* Contains formatting changes, and unrelated-changes
* Text-based approach would return 1.2, 1.3 & 1.8
* Dependencies that were added in 1.8 were removed
* Only added lines, so TA would return Nothing
* No clear origin
* Only added lines, so text approach would return nothing
* Properties file, so dependency approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Only added lines, so text approach would return nothing
* Properties file, so dependency approach would return nothing
* No clear origin
* Only added lines, so text approach would return nothing
* Properties file, so dependency approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin probably 1.3
* Text approach would return 1.3 & 1.12
* Entire method deleted, so dependency approach would return nothing
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Only added lines, so TA would return Nothing
* Only added lines, so TA would return Nothing
* Origin 1.1
* Both approaches would return 1.1
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Multiple bugs, but all have origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Origin 1.5
* Text approach would return 1.5
* DA would see line as not mapping, and return 1.5
* No clear origin
* Possibly contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.4, 1.14
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.13
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would say 1.1, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.13
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would say 1.5, 1.6, 1.7, 1.8, 1.9, 1.11, 1.13, 1.14
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.13
* Properties file, so dependency approach would return nothing
* Text approach would say 1.13
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Origin 1.14
* Both approaches would return 1.14
* Origin 1.5
* Multiple fixes in single commit
** [[1356348|Commit 1.15|/rachota/src/org/cesilko/rachota/gui/HistoryView.java - 1356348]]
** [[1356352|Commit 1.15|/rachota/src/org/cesilko/rachota/gui/HistoryView.java - 1356352]]
* TA will say 1.12 & 1.14
* DA will say 1.14
* Multiple fixes in single commit
** [[1356346|Commit 1.15|/rachota/src/org/cesilko/rachota/gui/HistoryView.java - 1356346]]
** [[1356352|Commit 1.15|/rachota/src/org/cesilko/rachota/gui/HistoryView.java - 1356352]]
* TA will say 1.12 & 1.14
* DA will say 1.14
* Multiple fixes in single commit
** [[1356346|Commit 1.15|/rachota/src/org/cesilko/rachota/gui/HistoryView.java - 1356346]]
** [[1356348|Commit 1.15|/rachota/src/org/cesilko/rachota/gui/HistoryView.java - 1356348]]
* Origin 1.5
* Text approach would return 1.5
* DA would see line as not mapping, and return 1.5
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.15
* Properties file, so DA will return Nothing
* Contains unrelated changes
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.14
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would say 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* TA would say 1.1, 1.3 & 1.15
* DA would say 1.15
* Update to existing fix
* Contains formatting changes
* Text approach would return 1.15
* No dependencies changed, so dependency approach would return nothing
* Origin 1.6, and to a lesser extent 1.3 and 1.1
* Text approach would return 1.6
* * DA would see line as not mapping, and return 1.6
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.16
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.15
* Properties file, so dependency approach would return nothing
* Text approach would say 1.15
* Multiple bugs, spread across 1.1, 1.4
* TA would say 1.4
* DA would say 1.4
* Origin 1.5
* Text approach would return 1.12, which had formatting changes, or 1.5
* Dependency approach would return 1.5
* No clear origin, essentially an added feature
* Contains unrelated changes
* Text approach would return 1.8
* DA would see removed chained method as line not mapping, return 1.8
* Appears to be unrelated
* Both approaches would return 1.1
* Lots of bugs, spread over 1.1, 1.2, 1.5, 1.6
* Both approaches would say 1.1, due to lines being removed
* Origin probably 1.14, possibly 1.16
* Contains formatting changes
* Only added lines, except formatting change, so text approach would return nothing
* Dependency approach would return 1.16
* Unrelated to fix
* No dependencies changed, so dependency approach would return nothing
* Text approach would return 1.17
* Origin 1.1
* Contains formatting changes
* Previous change to lines contained formatting changes
* Both approaches would return 1.1
* Origin 1.1
* Contains unrelated changes
* Dependency approach would say 1.1
* Text approach would say 1.1
* Found in initial revision
* New file, so neither approach would return anything
* New file, so neither approach would return anything
** ''Notes':
* No clear origin
* New file, so neither approach would return anything
** ''Notes':
* No clear origin
* New file, so neither approach would return anything
** ''Notes':
* No clear origin
* New file, so neither approach would return anything
* New file, so Nothing for either approach
* New file, so Nothing for either approach
* Origin 1.1
* Contains unrelated changes
* Both approaches would return 1.1
* Found in initial revision
* Origin 1.5
* Only added lines, so text approach would return nothing
* Dependency approach would probably return 1.5
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.15
* Properties file, so dependency approach would return nothing
* Text approach would return 1.15
* Unrelated to fix
* Dependencies unchanged, so dependency approach would return nothing
* Text approach would return 1.20
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Properties file, so DA will return Nothing
* Only added lines, so TA will return Nothing
* Properties file, so DA will return Nothing
* Only added lines, so TA will return Nothing
* Properties file, so DA will return Nothing
* Only added lines, so TA will return Nothing
* Origin 1.1
* Both approaches would say 1.1
* Found in initial revision
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Properties file, so DA will return Nothing
* Only added lines, so TA will return Nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Properties file, so DA will return Nothing
* Only added lines, so TA will return Nothing
* Origin 1.23
* Both approaches would say 1.23
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.24
* Properties file, so dependency approach would return nothing
* Text approach would say 1.24
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Only added lines, so TA will return nothing
* Bug in helper method, originally extracted from various places each with their own origin
* DA would return 1.21
* Origin 1.24
* Both approaches would say 1.24
* Origin 1.1
* Contains formatting changes
* Text approach would return 1.1
* Dependency approach would return 1.20, as new lines add returns, removing control dependencies from later lines which were added in 1.20
* Found in initial revision
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.26
* Properties file, so dependency approach would return nothing
* Text approach would say 1.26
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Origins 1.1 & 1.13
* Contains unrelated changes
* Text approach would return 1.1 and 1.13
* Dependency approach would return 1.13
* Origin 1.1
* No dependencies changed, but text approach would identify 1.1
* Added synchronised keywords to methods, and synchronized block on this, so dependencies unchanged
* Contains comment changes
* Found in initial commit
* Technically, origin is in other [[file|Commit 1.11|/rachota/src/org/cesilko/rachota/core/Task.java]]
* XML file, so dependency approach would return nothing
* Text approach would return 1.1
* Origin 1.1
* Contains unrelated changes
* Contains formatting changes
* Dependencies removed, but both approaches would identify 1.1
* Found in initial revision
* No clear origin
* Only added lines, so text approach would return nothing
* Properties file, so dependency approach would return nothing
* No clear origin
* Only added lines, so text approach would return nothing
* Properties file, so dependency approach would return nothing
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Origin 1.1
* Contains formatting changes
* Dependencies unchanged, but text approach would identify 1.1
* Found in initial revision
* Multiple bugs, all in same revision
* Origin 1.1
* Both approaches would return 1.1
* Contains changes to comments
* Found in initial revision
* Origin 1.1
* Text approach would return 1.1
* DA would see line as not mapping, and return 1.1
* Found in initial revision
* Origin 1.1
* XML file so dependency approach would return nothing
* Text approach would return 1.1
* Unrelated to fix
* Contains comment changes
* Text approach would return 1.1
* No dependencies changed, so dependency approach would return nothing
* Origin 1.1
* XML file, so dependency approach would return nothing
* Text approach would return 1.1
* Found in initial version
* Origin 1.1
* Contains formatting changes
* Added control dependency, so dependency approach would return 1.1
*Only added lines, so text approach would return nothing
* Found in initial version
* Unrelated to fix
* Only comment changes
* Both approaches would return nothing
* Origin 1.1
* Text approach would return 1.1
* Dependencies unchanged, so dependency approach would return nothing
* Unrelated to fix
* Only added lines, so text approach would return nothing
* Dependency approach would return 1.1
* Seems to be unrelated to fix
* In XML, so dependency approach would identify nothing
* Line removed, so text approach would identify revision 1.1
* Origin 1.1
* Dependencies removed, but both approaches would identify 1.1
* Found in initial commit
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Properties file, so DA will return Nothing
* Only added lines, so TA will return Nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* TA would return 1.1, 1.6, 1.13
* DA would return 1.13
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Unrelated to bug
* Text approach would return 1.33
* No dependencies changed, so dependency approach would return nothing
* Properties file, so DA will return Nothing
* Contains unrelated changes
* Multiple bugs, all in 1.1
* Contains unrelated changes
* TA would return 1.34
* Unrelated to change
* Text approach would return 1.36
* No dependencies changed, so dependency approach would return nothing
* Unrelated change
* No dependency changes, so dependency approach would return nothing
* Text approach would return 1.37
* Origin 1.18, and 1.1 to a lesser extent
* Contains comment changes
* Text approach would return 1.18
* DA would see line as not mapping, and return 1.18
* Origin 1.1
* No dependencies changed, but text approach would identify 1.1
* Added synchronised keywords to methods, so dependencies unchanged
* Contains comment changes
* Found in initial commit
* Origin 1.1
* Text approach would say 1.1
* In field, so dependency approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Properties file, so DA will return Nothing
* Contains unrelated changes
* No clear origin
* Properties file, so dependency approach would return nothing
* Comment changes
* Only added lines (except comment), so text approach would return nothing
* No clear origin
* Contains comment changes
* Properties file, so dependency approach would return nothing
* Only added lines, except comment, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Only added lines, so text approach would return nothing
* Properties file, so dependency approach would return nothing
* Origin 1.1
* Contains unrelated changes
* Contains comment changes
* Dependencies removed, but both approaches would return 1.1
* Found in initial version
* Origin 1.1
* Text approach would say 1.1
* In field, so dependency approach would return nothing
* Origin 1.1
* Dependencies removed, but both approaches would identify 1.1
* Found in initial commit
* XML file so DA will return Nothing
* Only added lines, so TA will return Nothing
* Seems to be un-related to fix
* XML, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Text approach would say 1.1
* XML file, so dependency approach would return nothing
* Found in initial revision
* No clear origin
* XML file, so dependency approach would return nothing
* Text approach would return 1.2
* No clear origin
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Only lines removed were commented out, so TA would see only added lines and return Nothing
* Dependency approach would probably return 1.1
* Contains unrelated changes
* Origin 1.44
* Contains formatting changes
* Both approaches would return 1.44
* Origin 1.23
* Dependency approach would return 1.23
* Only added lines, so text approach would return Nothing
* Origin 1.17, and 1.4 to a lesser extent
* Text approach would return 1.17
* DA would see line as not mapping, so return 1.17
* Origin 1.1
* Dependencies removed, but both approaches would identify 1.1
* Found in initial commit
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.3
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Possibly contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.3
* Origin 1.3
* Contains unrelated changes
* Contains comment changes
* Text approach would return 1.1 & 1.3
* Dependency approach would return 1.3
* Origin 1.1
* Both approaches would say 1.1
* Found in initial revision
* Unrelated to fix
* XML file, so dependency approach would return nothing
* Text approach would return 1.1
* No clear origin
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* XML file so DA will return Nothing
* Only added lines, so TA will return Nothing
* Multiple bugs, all in 1.1
* Only added lines, so TA would return Nothing
* Control dependencies added on method entry
* No clear origin
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Text approach would say 1.1
* No dependencies altered, so dependency approach would return nothing
* Found in initial revision
* Origin 1.1
* Both approaches would return 1.1
* Origin 1.1
* Only added lines
* Dependency approach would return 1.1
* Origin 1.1
* Dependencies added, so DA would use source of dependency and return when those lines were added
* Found in initial commit
* Contains unrelated changes
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.4
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.4
* Update to existing fix
* Properties file, so dependency approach would return nothing
* Text approach would return 1.4
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.1
* Contains comment changes
* Both approaches would return 1.1
* Found in initial revision
* Origin 1.4
* Both approaches would say 1.4
* Origin 1.1
* Only added lines, so text-based approach would return nothing
* Dependency approach would return 1.1
* Found in initial revision
* Unrelated to fix
* XML file, so dependency approach would return nothing
* Text approach would return 1.1
* Contains formatting changes
* Origin 1.1
* Text-based approach would probably return 1.2
* Found in initial version
* Multiple instances of bug, but all in 1.1
* Only added lines, so TA will return Nothing
* Origin 1.2
* Both approaches would return 1.2
* XML file so DA will return Nothing
* Only added lines, so TA will return Nothing
* Origin 1.3
* Only added lines, so text approach would return nothing
* Dependency approach would return 1.3
* Lots of instances of bug, but all in 1.1
* Only added lines, so TA will return nothing
* Origin 1.1
* Contains unrelated changes
* Considering guard clause would give Incorrect
* Assuming try block removes no dependencies, catch block adds data dependency on method parameter, so would give 1.1 (by coincidence, that change had nothing to do with the bug)
* Found in initial revision
* Unrelated to fix
* Text approach would return 1.67
* No dependencies changed, so dependency approach would return nothing
* Origin probably 1.4
* Text approach would say 1.4 & 1.5
* Dependency approach would say 1.5
* No clear origin
* Properties file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.5
* Update to existing fix
* Only added lines, so text approach would return nothing
* Dependency approach would probably return 1.1
* Origin 1.1
* Contains comment changes
* Text approach would say 1.1, 1.2
* Dependency approach would probably say 1.2
* Found in initial revision
* Origin 1.1
* Contains unrelated changes
* Both approaches would return 1.1
* Origin 1.1
* Could be calculated by text-based approach
* Found in initial revision
* XML file so DA will return Nothing
* Only added lines, so TA will return Nothing
* Lots of instances of bugs, spread over 3 origins: 1.1, 1.2, 1.3
* TA 1.3
* DA 1.3
* Appears to be origin of 1.1
* Contains formatting changes
* Dependencies removed, but text-based approach would return 1.1 & 1.5
* Try-catch block added
* Origin 1.62
* Contains unrelated changes
* Text approach would return 1.62, 1.69
* Dependencies probably unchanged (try block added) so dependency approach might return nothing, or might return 1.62. Assuming sees entire line as unmapped, will return 1.62
* Origin 1.73
* Contains unrelated changes
* Both approaches would return 1.73
* Origin 1.76
* Contains unrelated changes
* Text approach would return 1.62
* Dependency approach would return 1.76
* This is actually an update to fix a regression introduced by commit 1.6. Both approaches would identify this origin correctly, but it's not actually the bug in question
* Both approaches would say 1.6
* No clear origin
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would return 1.6
* Multiple bugs, all have origin 1.1
* Text approach would say 1.1, 1.6
* Dependency approach would say 1.6
* Found in initial revision
* Assumes DA would see new method in chain as lines not mapping
* Origin 1.1
* Contains comment changes
* Previous change includes formatting change
* Text based approach would return 1.1 (ignoring formatting change)
* No dependency changes, so dependency approach would return nothing
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Contains unrelated changes, which is actually what TA uses to get Correct
* Origin 1.1 & 1.3
* Text approach would return 1.1, 1.3
* Dependency approach would return 1.3
* Unrelated to fix
* XML file, so dependency approach would return nothing
* Text approach would return 1.4
* Unrelated to fix
* XML file, so dependency approach would return nothing
* Text approach would return 1.1
* Origin 1.1
* Text approach would return 1.1
* No dependencies changed, so dependency change would return nothing
* Found in initial revision
* Origin 1.1
* Both approaches would return 1.1
* Found in initial revision
* Contains unrelated changes
* Unrelated to fix
* Text approach would return 1.80
* No dependencies changed, so dependency approach would return nothing
* Contains unrelated changes
* TA would return 1.62, 1.84
* DA would return 1.84
* Origin 1.23
* Dependency approach might return 1.48, when a try-catch block was added, or might return 1.23
* Origin 1.62
* Contains unrelated changes
* Text approach would return 1.63, 1.76 & 1.77
* Unclear what dependency approach would return. Probably 1.77
* Origin 1.23
* Dependency approach might return 1.48, when a try-catch block was added, or might return 1.23
* Origin 1.1
* Contains comment changes
* Found in initial revision
* Both approaches would say 1.1
* Origin 1.1
* Formatting changes in previous version
* Both approaches would return 1.1
* Properties file, so DA will return Nothing
* Contains unrelated changes
* Only added lines, so text approach would return nothing
* Dependency approach would probably return 1.1
* Seems unrelated to bug
* Both approaches would say 1.2
* Only added lines, so TA would return Nothing
* Seems unrelated
* XML file, so dependency approach would return nothing
* Text approach would return 1.1
* Origin 1.35
* Contains unrelated changes
* Text approach would return 1.35, 1.87
* DA would return 1.87
* Origin 1.63
* Text approach would return 1.63, 1.76 & 1.77
* Unclear what dependency approach would return. Probably 1.77
* Origin 1.1
* Contains comment changes
* Only added lines, so text approach would return nothing
* DA would return 1.1
* Found in initial revision
* Origin 1.62
* Contains unrelated changes
* Text approach would return 1.62, 1.76, 1.87
* Dependency approach would probably return 1.76
* Origin 1.2
* Both approaches would return 1.2
* Origin unclear, this is really a new feature
* Properties file, so dependency approach would do nothing
* Origin unclear, this is really a new feature
* Properties file, so dependency approach would do nothing
* Only added lines, so text approach would do nothing
* Bug is that modifier is wrong, this is really just documentation for modifier, so no real origin. Below is what it would be if this were considered Single
* Origin 1.6
* Contains unrelated changes
* Properties file, so dependency approach would return nothing
* Text approach would say 1.1, 1.7 (but line mapping approach could trace back to 1.6)
* Origin 1.1
* Contains unrelated changes
* Only added lines, so text-based approach would return nothing
* Dependency approach might return 1.3, might return 1.1
* Found in initial revision
* Origin 1.4
* Text approach would return 1.4
* DA should see line as unmapped, so return 1.4
* No clear origin
* XML file, so dependency approach would return nothing
* Only added lines, so text approach would return nothing
* Origin 1.8
* Both approaches would say 1.8
/***
<<tiddler CookieManager>>
***/
/***
!!![[Portable cookies:|CookieSaverPlugin]] {{fine{<<option chkPortableCookies>>enable <<option chkMonitorCookieJar>>monitor}}}
^^This section is ''//__automatically maintained__//'' by [[CookieSaverPlugin]]. To block specific cookies, see [[CookieSaverPluginConfig]].^^
***/
//{{{
if (config.options.txtUserName=="spd" && config.options.chkPortableCookies) {
config.options["txtCurrentBackup"]="5";
config.options["txtSavedStory"]="";
}
//}}}
// // /% end portable cookies %/
/***
!!![[Baked cookies:|CookieManagerPlugin]]
^^Press {{smallform{<<cookieManager button>>}}} to save the current browser cookies... then hand-edit this section to customize the results.^^
***/
// The following settings are applied as defaults for all users: //
//{{{
config.options["txtMaxEditRows"]=25;
config.options["txtTiddlerLinkTootip"]="%0 - %2 (%3 bytes) - %4";
if (!config.options.txtTheme.length) config.options.txtTheme='StyleSheet';
// Make sure FAQ panels are *always* closed on startup (so load-on-demand can be used later on):
config.options.chkSliderWelcomeShowFAQ=false;
saveOptionCookie('chkSliderWelcomeShowFAQ');
config.options.chkSliderMainMenu_faq=false;
saveOptionCookie('chkSliderMainMenu_faq');
config.options.chkSliderSiteMenu_faq=false;
saveOptionCookie('chkSliderSiteMenu_faq');
config.options["chkAutoSave"]=true;
config.options["chkBackstage"]=true;
config.options["chkSaveStory"]=true;
config.options["chkSliderMainMenu_faq"]=false;
config.options["chkSliderSiteMenu_faq"]=false;
config.options["chkSliderWelcomeShowFAQ"]=false;
config.options["chkStoryAllowAdd"]=false;
config.options["chkStoryClose"]=false;
config.options["chkStoryFold"]=false;
config.options["chkStoryTop"]=false;
config.options["txtBackupFolder"]="backups";
config.options["txtCurrentBackup"]="7";
config.options["txtMainTab"]="Tags";
config.options["txtSavedStory"]="[[CookieJar]]";
config.options["txtUserName"]="spd";
//}}}
// 0 cookies saved on Tuesday, February 1st 2011 at 10:11:13 by spd//
//^^(edit/remove username check and/or individual option settings as desired)^^//
//{{{
if (config.options.txtUserName=="spd") {
}
//}}}
// 0 cookies saved on Tuesday, February 1st 2011 at 10:22:53 by spd//
//^^(edit/remove username check and/or individual option settings as desired)^^//
//{{{
if (config.options.txtUserName=="spd") {
}
//}}}
/***
|Name|CookieManagerPlugin|
|Source|http://www.TiddlyTools.com/#CookieManagerPlugin|
|Version|2.4.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|view/add/delete browser-based cookies and "bake" cookies to CookieJar tiddler for 'sticky' settings|
!!!!!Usage
<<<
This plugin provides an interactive control panel that lets you select, view, modify, or delete any of the current values for TiddlyWiki options that have been stored as local, private, //browser cookies//. You can also use the control panel to "bake cookies", which generates a set of javascript statements that assign hard-coded option values to the TiddlyWiki internal variables that correspond to the current browser cookie settings. These hard-coded values are then stored in the [[CookieJar]] tiddler, which is tagged with<<tag systemConfig>> so that each time the document is loaded, the baked cookie settings will be automatically applied.
When a set of baked cookies is added to the [[CookieJar]], it is automatically surrounded by a conditional test so that the hard-coded settings will only be applied for the username that was in effect when they were initially generated. In this way, if you publish or share your document with others, //your// particular baked cookie settings are not automatically applied to others, so that their own browser-based cookie settings (if defined) will be applied as usual.
Whenever you "bake cookies", new hard-coded javascript assignment statements are *appended* to the end of the [[CookieJar]]. However, any baked cookies that were previously generated and stored in the [[CookieJar]] are not automatically removed from the tiddler. As a result, because the most recently baked cookie settings in the [[CookieJar]] are always the last to be processed, the values assigned by older baked cookies are immediately overridden by the values from the newest baked cookies, so that the newest values will be in effect when the CookieJar startup processing is completed.
Each time you bake a new batch of cookies, it is recommended that you should review and hand-edit the [[CookieJar]] to remove any "stale cookies" or merge the old and new sets of baked cookies into a single block to simplify readability (as well as saving a little tiddler storage space). Of course, you can also hand-edit the [[CookieJar]] tiddler at any time simply to remove a few individual //baked cookies// if they are no longer needed, and you can even delete the entire [[CookieJar]] tiddler and start fresh, if that is appropriate. Please note that changing or deleting a baked cookie does not alter the current value of the corresponding option setting, and any changes you make to the [[CookieJar]] will only be applied after you have saved and reloaded the document in your browser.
<<<
!!!!!Examples
<<<
{{{<<cookieManager>>}}}
{{smallform small center{
@@display:block;width:35em;<<cookieManager>>@@}}}
<<<
!!!!!Configuration
<<<
<<option chkAllowBrowserCookies>> store ~TiddlyWiki option settings using private browser cookies
<<option chkMonitorBrowserCookies>> monitor browser cookie activity (show a message whenever a cookie is set or deleted)
<<option chkCookieManagerAddToAdvancedOptions>> display [[CookieManager]] in [[AdvancedOptions]]
//note: this setting does not take effect until you reload the document//
<<<
!!!!!Revisions
<<<
2011.01.16 2.4.1 in init(), corrected double addition of CookieManager to backstage
2009.08.05 2.4.0 changed CookieJar output format to support odd symbols in option names (e.g. '@')
2008.09.14 2.3.2 fixed handling for blocked cookies (was still allowing some blocked cookies to be set)
2008.09.12 2.3.1 added blocked[] array and allowBrowserCookie() test function for selective blocking of changes to browser cookies based on cookie name
2008.09.08 2.3.0 extensive code cleanup: defined removeCookie(), renamed cookies, added 'button' param for stand-alone "bake cookies" button, improved init of shadow [[CookieManager]] and [[CookieJar]] tiddlers for compatibility with new [[CookieSaverPlugin]].
2008.07.11 2.2.1 fixed recursion error in hijack for saveOptionCookie()
2008.06.26 2.2.0 added chkCookieManagerNoNewCookies option
2008.06.05 2.1.3 replaced hard-coded definition for "CookieJar" title with option variable
2008.05.12 2.1.2 add "cookies" task to backstage (moved from BackstageTasks)
2008.04.09 2.1.0 added options: chkCookieManagerAddToAdvancedOptions
2008.04.08 2.0.1 automatically include CookieManager control panel in AdvancedOptions shadow tiddler
2007.08.02 2.0.0 converted from inline script
2007.04.29 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.CookieManagerPlugin= {major: 2, minor: 4, revision: 1, date: new Date(2011,1,16)};
//}}}
//{{{
config.macros.cookieManager = {
target:
config.options.txtCookieJar||"CookieJar",
blockedCookies:
[],
allowBrowserCookie: function(name) {
return true;
},
displayStatus: function(msg) {
if (config.options.chkMonitorBrowserCookies && !startingUp)
displayMessage("CookieManager: "+msg);
},
init: function() {
if (config.options.txtCookieJar===undefined)
config.options.txtCookieJar=this.target;
if (config.options.chkAllowBrowserCookies===undefined)
config.options.chkAllowBrowserCookies=true;
if (config.options.chkMonitorBrowserCookies===undefined)
config.options.chkMonitorBrowserCookies=false;
config.shadowTiddlers.CookieManager=
"/***\n"
+"!!![[Browser cookies:|CookieManagerPlugin]] "
+"{{fine{<<option chkAllowBrowserCookies>>enable <<option chkMonitorBrowserCookies>>monitor}}}\n"
+"^^Review, modify, or delete browser cookies..."
+"To block specific cookies, see [[CookieManagerPluginConfig]].^^\n"
+"@@display:block;width:30em;{{smallform small{\n<<cookieManager>>}}}@@\n"
+"***/\n";
// add CookieManager to shadow CookieJar
var h="/***\n<<tiddler CookieManager>>\n***/\n";
var t=(config.shadowTiddlers[this.target]||"").replace(new RegExp(h.replace(/\*/g,'\\*'),''),'')
config.shadowTiddlers[this.target]=h+t;
if (config.options.chkCookieManagerAddToAdvancedOptions===undefined)
config.options.chkCookieManagerAddToAdvancedOptions=true;
if (config.options.chkCookieManagerAddToAdvancedOptions)
config.shadowTiddlers.AdvancedOptions+="\n!!CookieManager\n><<tiddler CookieManager>>";
// add "cookies" backstage task
if (config.tasks && !config.tasks.cookies) { // for TW2.2b3 or above
config.tasks.cookies = {
text: "cookies",
tooltip: "manage cookie-based option settings",
content: "{{groupbox{<<tiddler [["+this.target+"]]>>}}}"
}
config.backstageTasks.push("cookies");
}
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var span=createTiddlyElement(place,"span");
span.innerHTML=(params[0]&¶ms[0].toLowerCase()=="button")?this.button:this.panel;
this.setList(span.firstChild.list);
},
panel: '<form style="display:inline;margin:0;padding:0" onsubmit="return false"><!--\
--><select style="width:99%" name="list" \
onchange="this.form.val.value=this.value.length?config.options[this.value]:\'\';"><!--\
--></select><br>\
<input type="text" style="width:98%;margin:0;" name="val" title="enter an option value"><br>\
<input type="button" style="width:33%;margin:0;" value="get" title="refresh list" \
onclick="config.macros.cookieManager.setList(this.form.list);"><!--\
--><input type="button" style="width:33%;margin:0;" value="set" title="save cookie value" \
onclick="var cmc=config.macros.cookieManager;\
var opt=this.form.list.value; var v=this.form.val.value; \
var msg=opt+\' is a blocked cookie. OK to proceed?\';\
if ((!cmc.blockedCookies.contains(opt) && cmc.allowBrowserCookie(opt))||confirm(msg)) {\
config.options[opt]=opt.substr(0,3)==\'txt\'?v:(v.toLowerCase()==\'true\'); \
saveOptionCookie(opt);config.macros.cookieManager.setList(this.form.list);\
}"><!--\
--><input type="button" style="width:33%;margin:0;" value="del" title="remove cookie" \
onclick="var cmc=config.macros.cookieManager; var opt=this.form.list.value; \
var msg=opt+\' is a blocked cookie. OK to proceed?\';\
if ((!cmc.blockedCookies.contains(opt) && cmc.allowBrowserCookie(opt))||confirm(msg)) {\
removeCookie(this.form.list.value,true); \
cmc.setList(this.form.list);\
}"><br>\
<input type="button" style="width:50%;margin:0;" value="bake cookies" \
title="save current cookie-based option values into a tiddler" \
onclick="return config.macros.cookieManager.bake(this,false)"><!--\
--><input type="button" style="width:50%;margin:0;" value="bake all options" \
title="save ALL option values (including NON-COOKIE values) into a tiddler" \
onclick="return config.macros.cookieManager.bake(this,true)"><!--\
--></form>\
',
button: '<form style="display:inline;margin:0;padding:0" onsubmit="return false"><!--\
--><input type="button" style="margin:0;" value="bake cookies" \
title="save current browser-based cookie values into a tiddler" \
onclick="return config.macros.cookieManager.bake(this,false)"><!--\
--></form>\
',
getCookieList: function() {
var cookies = { };
if (document.cookie != "") {
var p = document.cookie.split("; ");
for (var i=0; i < p.length; i++) {
var pos=p[i].indexOf("=");
if (pos==-1) cookies[p[i]]="";
else cookies[p[i].substr(0,pos)]=unescape(p[i].slice(pos+1));
}
}
var opt=new Array(); for (var i in config.options) if (cookies[i]) opt.push(i); opt.sort();
return opt;
},
setList: function(list) {
if (!list) return false;
var opt=this.getCookieList();
var sel=list.selectedIndex;
while (list.options.length > 1) { list.options[1]=null; } // clear list (except for header item)
list.options[0]=new Option("There are "+opt.length+" cookies...","",false,false);
if (!opt.length) { list.form.val.value=""; return; } // no cookies
var c=1;
for(var i=0; i<opt.length; i++) {
var txt="";
if (opt[i].substr(0,3)=="chk")
txt+="["+(config.options[opt[i]]?"\u221A":"_")+"] ";
txt+=opt[i];
list.options[c++]=new Option(txt,opt[i],false,false);
}
list.selectedIndex=sel>0?sel:0;
list.form.val.value=sel>0?config.options[list.options[sel].value]:"";
},
header:
"/***\n"
+"!!![[Baked cookies:|CookieManagerPlugin]]\n"
+"^^Press {{smallform{<<cookieManager button>>}}} to save the current browser cookies... "
+"then hand-edit this section to customize the results.^^\n"
+"***/\n",
format: function(name) {
if (name.substr(0,3)=='chk')
return '\tconfig.options["'+name+'"]='+(config.options[name]?'true;':'false;');
return '\tconfig.options["'+name+'"]="'+config.options[name]+'";';
},
bake: function(here,all) {
if (story.isDirty(this.target)) return false; // target is being hand-edited... do nothing
var text=store.getTiddlerText(this.target);
if (text.indexOf(this.header)==-1) {
text+=this.header;
displayMessage("CookieManager: added 'Baked Cookies' section to CookieJar");
}
var who=config.options.txtUserName;
var when=new Date();
var tags=['systemConfig'];
var tid=store.getTiddler(this.target)||store.saveTiddler(this.target,this.target,text,who,when,tags,{});
if (!tid) return false; // if no target... do nothing
if (all) {
var opts=new Array();
for (var i in config.options) if (i.substr(0,3)=='chk'||i.substr(0,3)=='txt') opts.push(i);
opts.sort();
}
else var opts=this.getCookieList();
var t=tid.text;
if (t.indexOf(this.header)==-1) t+=this.header;
t+='\n// '+opts.length+(all?' options':' cookies')+' saved ';
t+=when.formatString('on DDD, MMM DDth YYYY at 0hh:0mm:0ss');
t+=' by '+who+'//\n';
t+='//^^(edit/remove username check and/or individual option settings as desired)^^//\n';
t+='//{{{\n';
t+='if (config.options.txtUserName=="'+who+'") {\n';
for (i=0; i<opts.length; i++) t+=config.macros.cookieManager.format(opts[i])+"\n";
t+='}\n//}}}\n';
store.saveTiddler(this.target,this.target,t,who,when,tags,tid?tid.fields:{});
story.displayTiddler(story.findContainingTiddler(this),this.target);
story.refreshTiddler(this.target,null,true);
var msg=opts.length+(all?' options':' cookies')+' have been saved in '+this.target+'. ';
msg+='Please review all stored settings.';
displayMessage(msg);
return false;
}
}
//}}}
//{{{
// Hijack saveOptionCookie() to add cookie blocking and monitoring messages
config.macros.cookieManager.saveOptionCookie=saveOptionCookie;
window.saveOptionCookie=function(name,force)
{
var cmc=config.macros.cookieManager; // abbrev
if (force || ((config.options.chkAllowBrowserCookies || name=="chkAllowBrowserCookies")
&& !cmc.blockedCookies.contains(name) && cmc.allowBrowserCookie(name))) {
cmc.saveOptionCookie.apply(this,arguments);
cmc.displayStatus(name+"="+config.options[name]);
} else cmc.displayStatus("setting of '"+name+"' is blocked");
}
// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
window.removeCookie=function(name) {
document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
}
// ... and then hijack it to add cookie blocking and monitoring messages
config.macros.cookieManager.removeCookie=removeCookie;
window.removeCookie=function(name,force)
{
var cmc=config.macros.cookieManager; // abbrev
if (!cmc.getCookieList().contains(name))
return; // not a current cookie!
if (force || ((config.options.chkAllowBrowserCookies || name=="chkAllowBrowserCookies")
&& !cmc.blockedCookies.contains(name) && cmc.allowBrowserCookie(name))) {
cmc.removeCookie.apply(this,arguments);
cmc.displayStatus("deleted "+name);
} else cmc.displayStatus("deletion of '"+name+"' is blocked");
}
//}}}
/***
|Name|CookieManagerPluginConfig|
|Source|http://www.TiddlyTools.com/#CookieManagerPluginConfig|
|Requires|CookieManagerPlugin|
|Description|custom settings for [[CookieManagerPlugin]]|
!!!!!Browser Cookie Configuration:
***/
// // <<option chkAllowBrowserCookies>> store ~TiddlyWiki option settings using private browser cookies
// // <<option chkMonitorBrowserCookies>> monitor browser cookie activity (shows a message whenever a cookie is updated)
//{{{
// default settings:
config.options.chkAllowBrowserCookies=true; // if FALSE, this blocks *all* cookies
config.options.chkMonitorBrowserCookies=false;
//}}}
// // Individual cookie names can be prevented from being created, modified, or deleted in your browser's stored cookies by adding them to the {{{config.macros.cookieManager.blockedCookies}}} array:
//{{{
var bc=config.macros.cookieManager.blockedCookies; // abbreviation
bc.push("txtMainTab"); // TiddlyWiki: SideBarTabs
bc.push("txtTOCSortBy"); // TiddlyTools: TableOfContentsPlugin
bc.push("txtCatalogTab"); // TiddlyTools: CatalogTabs
//}}}
// // You can also define a javascript test function that determines whether or not any particular cookie name should be blocked or allowed. The following function should return FALSE if the browser cookie should be blocked, or TRUE if changes to the cookie should be allowed:
//{{{
config.macros.cookieManager.allowBrowserCookie=function(name) {
// add tests based on specific cookie names and runtime conditions
return true;
}
//}}}
/***
|Name|CookieSaverPlugin|
|Source|http://www.TiddlyTools.com/#CookieSaverPlugin|
|Version|1.1.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|automatically save TiddlyWiki cookie options to [[CookieJar]] tiddler for portable settings|
!!!!!Usage
<<<
Whenever TiddlyWiki option settings are changed, a browser-based cookie value is added, removed, or changed. Each time this occurs, the CookieSaverPlugin generates an equivalent ''portable cookie'': a single line of javascript code that assigns a stored value directly to the specific TiddlyWiki internal config.options.* variable corresponding to the browser cookie with the same name.
The portable cookies are automatically written into a tiddler named [[CookieJar]] that is tagged with<<tag systemConfig>>so that their values will be applied as soon as the document is saved and reloaded. You can change or delete an individual portable cookie by editing the contents of the [[CookieJar]] and removing the appropriate line of javascript from the tiddler source code. Note: editing the portable cookie definitions does not alter the values of any corresponding browser cookies, nor does it update the internal value that is in use within the current TiddlyWiki document session. Changes made directly to the [[CookieJar]] are only applied after saving and reloading the document. In any case, whenever a browser cookie value is updated, any modifications you made to the equivalent portable cookie are immediately rewritten to reflect the current browser cookie value.
Browser cookies are, obviously, stored with your browser... and are kept separate from the document itself. In contrast, because your portable cookies are stored in a [[CookieJar]] within the document, they remain part of that document.
When the document is copied and shared with others, each copy includes the [[CookieJar]] containing //your// stored portable cookies. Fortunately, CookieSaverPlugin can generate and maintain several separate sets of portable cookies in the same [[CookieJar]] tiddler, where each set is associated with a different TiddlyWiki username. As long as other readers have not chosen the same username, your portable cookie values will not be automatically applied when they are reading the document. Rather, as they interact with the document, a new set of portable cookies, associated with //their// username, will be automatically added to the [[CookieJar]].
In addition to tracking and applying separate portable cookies for each user, CookieSaverPlugin can also be configured so that sensitive data (such as internal URLs, email addresses, login IDs and passwords, etc.) will never be inadvertently stored in the [[CookieJar]]. To achieve this, you can selectively prevent specific cookienames from being used as portable cookies by placing a special javascript function definition in a tiddler named [[CookieSaverPluginConfig]], tagged with 'systemConfig':
{{{
config.macros.cookieSaver.allowPortableCookie=function(name){
if ( ... some test using 'name' ...) return false;
if ( ... another test with 'name' ...) return true;
etc.
return true; // default=allow
}
}}}
The allowPortableCookie() function offers a flexible method for plugin developers and other technically skilled document authors to implement their own custom-defined, application-specific cookie data protection by applying sophisticated logic for deciding which cookies should be allowed or blocked based on variety of different conditions. The basic operation of this function is to accept a cookie name as text input, apply some tests based on that cookie name (combined with any other useful criteria), and then return //true// if saving the portable cookie should be permitted, or //false// if the cookie should be excluded from the [[CookieJar]].
Unfortunately, although the technical expertise needed to write this test function is relatively minor, the level of programming ability that is needed can nonetheless be beyond the skills that many people possess. To help address this, CookieSaverPlugin also supports an alternative syntax that allows you to define a simple array of cookie names that is used by the plugin to automatically block the indicated names from being included as portable cookies in the [[CookieJar]]. The array definition syntax looks like this:
{{{
// define a complete set of blocked cookie names
config.macros.cookieSaver.blockedCookies=['cookie','cookie','cookie',etc...];
}}}
or
{{{
// add individual cookies names to the current set of blocked cookies
config.macros.cookieSaver.blockedCookies.push('cookie');
config.macros.cookieSaver.blockedCookies.push('cookie');
etc...
}}}
Note: the allowPortableCookie() function and the blockedCookies[] array are only used to limit the creation of portable cookies within the [[CookieJar]], and are //not// applied when creating normal browser cookies. Thus, regardless of whether or not a given portable cookie has been excluded or permitted, all the usual TiddlyWiki settings and internal state data can still be saved as secure, private, local browser cookies that are never made visible to others, even when the document is shared.
<<<
!!!!!Configuration
<<<
<<option chkPortableCookies>> allow ~CookieSaver to store //''portable cookies''// in [[CookieJar]] tiddler
<<option chkMonitorCookieJar>> monitor ~CookieSaver activity (show messages whenever [[CookieJar]] is updated)
<<option chkCookieJarAddToAdvancedOptions>> display [[CookieJar]] in [[AdvancedOptions]]
^^//note: changing this setting does not take effect until you reload the document//^^
<<<
!!!!!Revisions
<<<
2009.08.05 [1.1.0] changed CookieJar output format to support odd symbols in option names (e.g. '@')
2008.09.11 [1.0.2] automatically add portable cookies header to existing CookieJar (if any). Also, added chkMonitorCookieJar option to display CookieJar activity messages
2008.09.10 [1.0.1] documentation, code cleanup, improvements in 'allowPortableCookie()' function handling
2008.09.09 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.CookieSaverPlugin= {major: 1, minor: 1, revision: 0, date: new Date(2009,8,5)};
config.macros.cookieSaver = {
target:
config.options.txtCookieJar||"CookieJar",
init: function() {
if (config.options.chkPortableCookies===undefined)
config.options.chkPortableCookies=false;
if (config.options.txtCookieJar===undefined)
config.options.txtCookieJar=this.target;
if (config.options.chkCookieJarAddToAdvancedOptions===undefined)
config.options.chkCookieJarAddToAdvancedOptions=true;
if (config.options.chkCookieJarAddToAdvancedOptions)
config.shadowTiddlers.AdvancedOptions+="\n!!%0\n><<tiddler [[%0]]>>".format([this.target]);
if (config.options.chkMonitorCookieJar===undefined)
config.options.chkMonitorCookieJar=false;
// add empty Portable Cookies section to shadow CookieJar
var h="/***\n<<tiddler CookieManager>>\n***/\n";
var t=(config.shadowTiddlers[this.target]||"").replace(new RegExp(h.replace(/\*/g,'\\*'),''),'')
config.shadowTiddlers[this.target]=this.header+this.footer+t;
// add empty Portable Cookies section to real CookieJar (if one exists)
if (store.tiddlerExists(this.target) && !readOnly) {
var tid=this.get(this.target);
var t=tid.text;
if (t.indexOf(this.header)==-1){
t=this.header+this.footer+t.replace(new RegExp(h.replace(/\*/g,'\\*'),''),'');
var who=config.options.chkForceMinorUpdate?tid.modifier:config.options.txtUserName;
var when=config.options.chkForceMinorUpdate?tid.modified:new Date();
store.saveTiddler(tid.title,tid.title,t,who,when,tid.tags,tid.fields);
displayMessage("CookieSaver: added 'Portable Cookies' section to CookieJar");
}
}
// add "cookies" backstage task
if (config.tasks && !config.tasks.cookies) { // for TW2.2b3 or above
config.tasks.cookies = {
text: "cookies",
tooltip: "manage cookie-based option settings",
content: "{{groupbox{<<tiddler CookieManager>><<tiddler [[%0]]>>}}}".format([this.target])
}
config.backstageTasks.push("cookies");
}
},
header:
"/***\n<<tiddler CookieManager>>\n***/\n"
+"/***\n"
+"!!![[Portable cookies:|CookieSaverPlugin]] "
+"{{fine{<<option chkPortableCookies>>enable <<option chkMonitorCookieJar>>monitor}}}\n"
+"^^This section is ''//__automatically maintained__//'' by [[CookieSaverPlugin]]. "
+"To block specific cookies, see [[CookieSaverPluginConfig]].^^\n"
+"***/\n",
startUser:
"//{{{\n"
+"if (config.options.txtUserName==\"%0\" && config.options.chkPortableCookies) {",
endUser:
"\n}\n//}}}\n",
footer:
"// // /% end portable cookies %/\n",
get: function(tid) { // create or retrieve tiddler
if (story.isDirty(tid)) return null; // tiddler is being hand-edited... leave it alone.
var text=config.shadowTiddlers[this.target];
var who=config.options.txtUserName;
var when=new Date();
var tags=['systemConfig'];
return store.getTiddler(tid)||store.saveTiddler(tid,tid,text,who,when,tags,{});
},
format: function(name) {
if (name.substr(0,3)=='chk')
return '\tconfig.options["'+name+'"]='+(config.options[name]?'true;':'false;');
return '\tconfig.options["'+name+'"]="'+config.options[name]+'";';
},
blockedCookies: [],
allowPortableCookie: function(name) {
return true;
},
set: function(name) {
if (!name||!name.trim().length) return;
if (name=='txtUserName' || this.blockedCookies.contains(name) || !this.allowPortableCookie(name)) {
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: blocked '"+name+"'");
return false; // don't save excluded cookies
}
var tid=this.get(this.target);
if (!tid) return false; // if no tiddler... do nothing
var t=tid.text;
if (t.indexOf(this.header)==-1) { // re-add Portable Cookies section if it was deleted by hand edit
var h="/***\n<<tiddler CookieManager>>\n***/\n";
t=this.header+this.footer+t.replace(new RegExp(h.replace(/\*/g,'\\*'),''),'');
}
var who=config.options.txtUserName;
var when=new Date();
var startmark=this.startUser.format([who]);
var endmark=this.endUser;
var startpos=t.indexOf(startmark);
if (startpos==-1) { // insert new user (just before footer)
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: added new user '"+who+"'");
var addpos=t.indexOf(this.footer); if (addpos==-1) addpos=t.length;
t=t.substr(0,addpos)+startmark+endmark+t.substr(addpos);
startpos=addpos;
}
startpos+=startmark.length;
var endpos=t.indexOf(endmark,startpos);
var pre=t.substr(0,startpos);
var lines=t.substring(startpos,endpos).split('\n');
var post=t.substr(endpos);
var code=this.format(name);
var match='\tconfig.options["'+name+'"]=';
var found=false; var changed=false;
for (var i=0; i<lines.length; i++) { // find and replace existing setting
if (lines[i].substr(0,match.length)==match) {
found=true;
if (changed=lines[i]!=code) lines[i]=code; // replace value
if (config.options.chkMonitorCookieJar && !startingUp && changed)
displayMessage("CookieJar: "+code);
}
}
if (!found && code.length) { // OR, add new setting
lines[lines.length]=code;
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: "+code);
}
if (found && !changed) return; // don't alter tiddler unless necessary
t=pre+lines.join('\n')+post;
var who=config.options.chkForceMinorUpdate?tid.modifier:config.options.txtUserName;
var when=config.options.chkForceMinorUpdate?tid.modified:new Date();
store.saveTiddler(this.target,this.target,t,who,when,tid.tags,tid.fields);
story.refreshTiddler(this.target,null,true);
},
remove: function(name) {
if (!name||!name.trim().length) return;
var who=config.options.txtUserName;
var when=new Date();
var tid=store.getTiddler(this.target);
if (!tid) return false; // if no tiddler... do nothing
var t=tid.text;
var who=config.options.txtUserName
var startmark=this.startUser.format([who]);
var endmark=this.endUser;
var startpos=t.indexOf(startmark);
if (startpos==-1) return false; // no such user... do nothing
startpos+=startmark.length;
var endpos=t.indexOf(endmark,startpos);
var pre=t.substr(0,startpos);
var lines=t.substring(startpos,endpos).split('\n');
var post=t.substr(endpos);
var match='\tconfig.options["'+name+'"]';
var found=false; var changed=false;
for (var i=0; i<lines.length; i++) { // find and remove setting
if (lines[i].substr(0,match.length)==match) {
lines.splice(i,1);
changed=true;
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: deleted '"+name+"'");
break;
}
}
if (!changed) return; // not found... do nothing
t=pre+lines.join('\n')+post;
if (lines.length==1) { // no cookies left, remove user
t=pre.substr(0,pre.length-startmark.length)+post.substr(endmark.length);
if (config.options.chkMonitorCookieJar && !startingUp)
displayMessage("CookieJar: removed user '"+who+"'");
}
var who=config.options.chkForceMinorUpdate?tid.modifier:config.options.txtUserName;
var when=config.options.chkForceMinorUpdate?tid.modified:new Date();
store.saveTiddler(this.target,this.target,t,who,when,tid.tags,tid.fields);
story.refreshTiddler(this.target,null,true);
}
}
//}}}
//{{{
// Hijack saveOptionCookie() to add CookieSaver processing
config.macros.cookieSaver.saveOptionCookie=saveOptionCookie;
window.saveOptionCookie=function(name)
{
config.macros.cookieSaver.saveOptionCookie.apply(this,arguments);
if (!readOnly && (config.options.chkPortableCookies || name=="chkPortableCookies"))
config.macros.cookieSaver.set(name);
}
// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
window.removeCookie=function(name) {
document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
}
// ... and then hijack it to also remove any corresponding PortableCookie
config.macros.cookieSaver.removeCookie=removeCookie;
window.removeCookie=function(name)
{
if (config.options.chkPortableCookies && !readOnly)
config.macros.cookieSaver.remove(name);
config.macros.cookieSaver.removeCookie.apply(this,arguments);
}
//}}}
/***
|Name|CookieSaverPluginConfig|
|Source|http://www.TiddlyTools.com/#CookieSaverPluginConfig|
|Requires|CookieSaverPlugin|
|Description|custom settings for [[CookieSaverPlugin]]|
!!!!!Portable Cookie Configuration:
***/
// // <<option chkPortableCookies>> allow ~CookieSaver to store //''portable cookies''// in [[CookieJar]] tiddler
// // <<option chkMonitorCookieJar>> monitor ~CookieSaver activity (show messages whenever [[CookieJar]] is updated)
//{{{
// default to these settings:
// config.options.chkPortableCookies=false; // when FALSE this blocks ALL portable cookies
// config.options.chkMonitorCookieJar=false;
//}}}
// // Individual cookie names can be added to the {{{config.macros.cookieSaver.blockedCookies}}} array to prevent them from being stored in the [[CookieJar]].
//{{{
var bc=config.macros.cookieSaver.blockedCookies;
bc.push("chkBackstage"); // core
bc.push("txtMainTab"); // TiddlyWiki: SideBarTabs
bc.push("txtTOCSortBy"); // TiddlyTools: TableOfContentsPlugin
bc.push("txtCatalogTab"); // TiddlyTools: CatalogTabs
bc.push("txtUploadFilename"); // BidiX: UploadPlugin
bc.push("txtUploadDir"); // BidiX: UploadPlugin
bc.push("pasPassword"); // BidiX: UploadPlugin
bc.push("pasUploadPassword"); // BidiX: UploadPlugin
//}}}
// // You can also define a javascript test function that determines whether or not any particular cookie name should be stored in the [[CookieJar]]. The following function should return FALSE if the portable cookie should be blocked, or TRUE if the cookie should be allowed:
//{{{
config.macros.cookieSaver.allowPortableCookie=function(name) {
// add tests based on specific cookie names and runtime conditions
if (name.substr(0,9)=="chkSlider") return false; // NestedSlidersPlugin
if (name.substr(0,13)=="txtFirstVisit") return false; // VisitCounter
if (name.substr(0,12)=="txtLastVisit") return false; // VisitCounter
if (name.substr(0,13)=="txtVisitCount") return false; // VisitCounter
return true;
}
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Summary</span>: <span macro='edit summary'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Description</span>: <span macro='edit description'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Date</span>: <span macro='edit datesubmitted'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Status</span>: <span macro='edit status'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Priority</span>: <span macro='edit priority'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Category</span>: <span macro='edit category'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Type</span>: <span macro='edit type'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">File</span>: <span macro='edit file wikified'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Revision</span>: <span macro='edit revision wikified'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Date</span>: <span macro='edit date'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Message</span>: <span macro='edit message'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Origin Class</span>: <span macro='edit originclass'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">TA</span>: <span macro='edit taresult'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">TA Class</span>: <span macro='edit ta'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">DA</span>: <span macro='edit daresult'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">DA Class</span>: <span macro='edit da'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Origin</span>: <span macro='edit origin'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Born</span>: <span macro='edit born'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Age</span>: <span macro='edit age'></span></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<!--}}}-->
{{{
FieldToTagPlugin = {};
FieldToTagPlugin.blacklist = [
'changecount',
'server.page.revision',
'server.host',
'server.type',
'summary',
'description',
'datesubmitted',
'file',
'revision',
'message',
'date',
'origin',
'born',
'age',
'taresult',
'daresult'
];
FieldToTagPlugin.assign = Tiddler.prototype.assign;
Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields,creator) {
if(fields != undefined && tags != undefined){
tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
outer:
for(var propertyName in fields) {
for(var j=0; j<FieldToTagPlugin.blacklist.length; j++){
if(propertyName == FieldToTagPlugin.blacklist[j]) continue outer;
}
for(var i=0; i<tags.length; i++){
tag = tags[i];
if(tag.indexOf(propertyName)==0){
tags.splice(i,1);
}
}
if(fields[propertyName]){
tags.splice(0,0,propertyName+'-'+fields[propertyName]);
}
}
}
FieldToTagPlugin.assign.call(this, title,text,modifier,modified,tags,created,fields,creator);
};
}}}
/***
|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:|HideWhenPlugin|
|Description:|Allows conditional inclusion/exclusion in templates|
|Version:|3.1 ($Rev: 3919 $)|
|Date:|$Date: 2008-03-13 02:03:12 +1000 (Thu, 13 Mar 2008) $|
|Source:|http://mptw.tiddlyspot.com/#HideWhenPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
For use in ViewTemplate and EditTemplate. Example usage:
{{{<div macro="showWhenTagged Task">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}
***/
//{{{
window.hideWhenLastTest = false;
window.removeElementWhen = function(test,place) {
window.hideWhenLastTest = test;
if (test) {
removeChildren(place);
place.parentNode.removeChild(place);
}
};
merge(config.macros,{
hideWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( eval(paramString), place);
}},
showWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !eval(paramString), place);
}},
hideWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAll(params), place);
}},
showWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAll(params), place);
}},
hideWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAny(params), place);
}},
showWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAny(params), place);
}},
hideWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAll(params), place);
}},
showWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAll(params), place);
}},
hideWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0]), place);
}},
showWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !(store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0])), place);
}},
hideWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.title == params[0], place);
}},
showWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.title != params[0], place);
}},
'else': { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !window.hideWhenLastTest, place);
}}
});
//}}}
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.6|
|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
<<<
2010.12.15 1.9.6 allow (but ignore) type="..." 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: 6, date: new Date(2010,12,15)};
config.formatters.push( {
name: "inlineJavascript",
match: "\\<script",
lookahead: "\\<script(?: type=\\\"[^\\\"]*\\\")?(?: src=\\\"([^\\\"]*)\\\")?(?: label=\\\"([^\\\"]*)\\\")?(?: title=\\\"([^\\\"]*)\\\")?(?: key=\\\"([^\\\"]*)\\\")?( 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:''|IntelliTaggerPlugin|
|''Version:''|1.0.2 (2007-07-25)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IntelliTaggerPlugin Documentation]]|
|''~SourceCode:''|[[IntelliTaggerPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Version History
* 1.0.2 (2007-07-25):
** Feature: "Return" key may be used to accept first tag suggestion (beside "Alt-1")
** Bugfix: Keyboard shortcuts (Alt+3 etc.) shifted
* 1.0.1 (2007-05-18): Improvement: Speedup when using TiddlyWikis with many tags
* 1.0.0 (2006-04-26): Initial release
***/
/***
!Source Code
***/
//{{{
// Ensure the Plugin is only installed once.
//
if (!version.extensions.IntelliTaggerPlugin) {
// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};
if (!abego.internal) abego.internal = {};
// Opens an alert with the given string and throws an exception
// with the same string after the alert is closed.
//
abego.alertAndThrow = function(s) {
alert(s);
throw s;
};
if (version.major < 2) {
abego.alertAndThrow("Use TiddlyWiki 2.0.8 or better to run the IntelliTagger Plugin.");
}
version.extensions.IntelliTaggerPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date(2007, 6, 25),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin",
documentation: "[[IntelliTaggerPlugin Documentation]]",
sourcecode: "[[IntelliTaggerPlugin SourceCode]]",
author: "Udo Borkowski (ub [at] abego-software [dot] de)",
licence: "[[BSD open source license (abego Software)]]",
tiddlywiki: "Version 2.0.8 or better",
browser: "Firefox 1.5.0.2 or better"
};
//}}}
//#startOf: MainCode
//{{{
// ========================================================================
// Utilities ==============================================================
// ========================================================================
// ========================================================================
// Popup
//
// A Popup is an HTML element floating on top of the main HTML page.
//
// The HTML element (typically a "div" element) is added as a direct child
// of the document.body.
//
// A Popup element should respect the following style conventions:
//
// position = "absolute"; // required.
// left = aDimension; // required. E.g. "10px"
// // When not defined the Popup is not displayed.
// top = aDimension; // required. E.g. "10px"
// // When not defined the Popup is not displayed.
// background = aColor; // optional. E.g. "white"
// // When not defined the Popup is transparent.
// border = aBorderSpec; // optional. E.g. "1px solid DarkGray"
// width = aDimension; // optional. E.g. "200px"
// // When not defined the width is calculated
// // automatically.
// height = aDimension; // optional. E.g. "200px"
// // When not defined the height is calculated
// // automatically.
// ========================================================================
abego.createEllipsis = function(place) {
var e = createTiddlyElement(place,"span");
e.innerHTML = "…";
};
// Returns true iff the given element is "opened as a popup",
// i.e. a direct child of the document.body.
//
// @param element [may be null/undefined]
// an HTML element
//
abego.isPopupOpen = function(element) {
return element && element.parentNode == document.body;
};
// Opens the given element as a popup.
//
// @param element
// an HTML element
//
abego.openAsPopup = function(element) {
if (element.parentNode != document.body)
document.body.appendChild(element);
};
// Closes the given popup.
// Does nothing when the element is not a popup or not open.
//
// @param element [may be null/undefined]
// an HTML element
//
abego.closePopup = function(element) {
if (abego.isPopupOpen(element))
document.body.removeChild(element);
};
// Returns the rectangle of the (browser) window
//
// @return {left,top,height,width}
//
abego.getWindowRect = function() {
return {
left: findScrollX(),
top: findScrollY(),
height: findWindowHeight(),
width: findWindowWidth()
};
};
// Moves the given element to the given position (in pixel).
//
abego.moveElement = function(element, left, top) {
element.style.left = left + "px";
element.style.top = top + "px";
};
// Centers the given element on the window.
//
// The element must have absolute position
//
abego.centerOnWindow = function(element) {
if (element.style.position != "absolute")
throw "abego.centerOnWindow: element must have absolute position";
var winRect = abego.getWindowRect();
abego.moveElement(
element,
winRect.left + (winRect.width - element.offsetWidth) / 2,
winRect.top + (winRect.height - element.offsetHeight) / 2);
};
// Returns true if e is either self or a descendant (child, grandchild,...) of self.
//
// @param self DOM:Element
// @param e DOM:Element [may be null]
//
abego.isDescendantOrSelf = function(self, e) {
while (e) {
if (self == e) return true;
e = e.parentNode;
}
return false;
};
// Returns a set containing the items of the array.
//
// It is an object that has a property for every item of the array.
// The name of the property is the "toString" representation of
// the item. The value of the property is "true".
//
// Duplicate items are removed.
//
abego.toSet = function(array) {
var result = {};
for (var i = 0; i < array.length; i++)
result[array[i]] = true;
return result;
};
// Returns an array with all strings from strings that match the filterRE.
//
// @param maxCount [optional] if defined at most maxCount strings are returned.
abego.filterStrings = function(strings, filterRE, maxCount) {
var result =[];
for (var i = 0; i < strings.length && (maxCount === undefined || result.length < maxCount); i++) {
var s = strings[i];
if (s.match(filterRE))
result.push(s);
}
return result;
};
// @param a [may be null/undefined] Object[]
// @param b [may be null/undefined] Object[]
abego.arraysAreEqual = function(a,b) {
if (!a)
return !b;
if (!b)
return false;
var n = a.length;
if (n != b.length)
return false;
for (var i = 0; i < n; i++)
if (a[i] != b[i])
return false;
return true;
};
// Adjusts the element's position to appear below the anchorElement,
// and ensures the element fits into the window.
//
abego.moveBelowAndClip = function(element, anchorElement) {
if (!anchorElement)
return;
// Position the result below the anchor and resize it if necessary.
var anchorLeft = findPosX(anchorElement);
var anchorTop = findPosY(anchorElement);
var anchorHeight = anchorElement.offsetHeight;
var elementLeft = anchorLeft;
var elementTop = anchorTop + anchorHeight;
// Make sure the result is not wider than the window
var winWidth = findWindowWidth();
if (winWidth < element.offsetWidth) {
element.style.width = (winWidth - 100)+"px";
}
// Ensure that the left and right of the result are not
// clipped by the window. Move it to the left or right, if necessary.
var elementWidth = element.offsetWidth;
if(elementLeft + elementWidth > winWidth)
elementLeft = winWidth - elementWidth-30;
if (elementLeft < 0)
elementLeft = 0;
// Do the actual moving
element.style.left = elementLeft + "px";
element.style.top = elementTop + "px";
element.style.display = "block";
};
abego.compareStrings = function(a, b) {
return (a == b) ? 0 : (a < b) ? -1 : 1;
};
// Sorts the given array alphabetically, ignoring the case.
//
abego.sortIgnoreCase = function(arr) {
var result =[];
// To avoid toLowerCase to be called twice for every comparison
// we convert the strings once and sort the lowercase.
// After sorting we replace them with the cased ones.
//
// Benchmarks have shown that this is significantly faster
// than the ad hoc solution, even for small arrays
// (like 5 Strings (10 chars each))
var n = arr.length;
for (var i = 0; i < n; i++) {
var s = arr[i];
result.push([s.toString().toLowerCase(),s]);
}
result.sort(function(a,b) {
return (a[0] == b[0]) ? 0 : (a[0] < b[0]) ? -1 : 1;
});
for (i = 0; i < n; i++)
arr[i] = result[i][1];
};
// Returns the specified field (input or textarea element), otherwise the first edit field it finds
// or null if it found no edit field at all
//
abego.getTiddlerField = function(story,title,field) {
var tiddler = document.getElementById(story.idPrefix + title);
var e = null;
if (tiddler != null) {
var children = tiddler.getElementsByTagName("*");
for (var t=0; t<children.length; t++) {
var c = children[t];
if(c.tagName.toLowerCase() == "input" || c.tagName.toLowerCase() == "textarea") {
if(!e)
e = c;
if(c.getAttribute("edit") == field)
e = c;
// break; // adding this break would not be 100% compatible to <= TW 2.0.9. when a
}
}
}
return e;
};
abego.setRange = function(element, start, end) {
// adapted from TaskMacroPlugin by LukeBlanshard.
// http://labwiki.sourceforge.net/#CopyrightAndLicense.
if (element.setSelectionRange) { // Mozilla
element.setSelectionRange(start, end);
// Damn mozilla doesn't scroll to visible. Approximate.
var max = 0.0 + element.scrollHeight;
var len = element.textLength;
var top = max*start/len, bot = max*end/len;
element.scrollTop = Math.min(top, (bot+top-element.clientHeight)/2);
} else if (element.createTextRange != undefined) { // IE
var range = element.createTextRange();
range.collapse();
range.moveEnd("character", end);
range.moveStart("character", start);
range.select();
} else // Other? Too bad, just select the whole thing.
element.select();
};
// TiddlerSet: an object with one property per tiddler in the set.
// The name of the property corresponds to the tiddler name,
// the value is "not false" (e.g. true or a non-zero number).
//
// TagMap<X>: an object that maps a tag to an object of type X (access through properties)
//
abego.internal.TagManager = function() {
var tagReferences = null; // TagMap<{count: natural, tiddlers: TiddlerSet}>
var ensureTagsAreLoaded = function() {
if (tagReferences)
return;
tagReferences = {};
store.forEachTiddler(function(title,tiddler) {
for(var i=0; i<tiddler.tags.length; i++) {
var tag = tiddler.tags[i];
var refedBy = tagReferences[tag];
if (!refedBy) {
refedBy = tagReferences[tag] = {count:0, tiddlers: {}};
}
refedBy.tiddlers[tiddler.title] = true;
refedBy.count += 1;
}
});
};
// When any tags are changed reset the TagManager.
//
var oldTiddlyWikiSaveTiddler = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags) {
var tiddler = this.fetchTiddler(title);
var oldTags = tiddler ? tiddler.tags : [];
var newTags = (typeof tags == "string") ? tags.readBracketedList() : tags;
oldTiddlyWikiSaveTiddler.apply(this, arguments);
if (!abego.arraysAreEqual(oldTags, newTags))
abego.internal.getTagManager().reset();
};
// When a tiddler is removed that had tags reset the TagManager.
//
var oldTiddlyWikiRemoveTiddler = TiddlyWiki.prototype.removeTiddler;
TiddlyWiki.prototype.removeTiddler = function(title) {
var tiddler = this.fetchTiddler(title);
var resetTagManager = tiddler && tiddler.tags.length > 0;
oldTiddlyWikiRemoveTiddler.apply(this, arguments);
if (resetTagManager)
abego.internal.getTagManager().reset();
};
// Resets the TagManager, thus ensures that cached tagging
// information is discarded and the most recent tag state is used.
//
this.reset = function () {
tagReferences = null;
};
// Returns a TiddlerSet with all tiddlers that have the given tag,
// or null when the tag is not used in any tiddler.
//
// @return [may be null]
//
this.getTiddlersWithTag = function(tag) {
ensureTagsAreLoaded();
var tagInfo = tagReferences[tag];
return tagInfo ? tagInfo.tiddlers : null;
};
// Returns an array with the names of all tags defined
// plus the (optional) extraTags.
//
// The tags are sorted alphabetically (caseinsensitive).
//
// @params [optional] an array of tags to be added to the list
//
//
this.getAllTags = function(extraTags) {
ensureTagsAreLoaded();
var result =[];
for (var i in tagReferences)
result.push(i);
for (i = 0; extraTags && i < extraTags.length; i++)
result.pushUnique(extraTags[i], true);
abego.sortIgnoreCase(result);
return result;
};
// An array with two items per tag
// result[i][0] : the tag name
// result[i][1] : TiddlerSet, with tiddlers that are tagged with that tag
//
this.getTagInfos = function() {
ensureTagsAreLoaded();
var result = [];
for (var tiddler in tagReferences) {
result.push([tiddler, tagReferences[tiddler]]);
}
return result;
};
var compareTiddlerCountAndTagName = function(a,b) {
var a1 = a[1];
var b1 = b[1];
var d = b[1].count - a[1].count;
return d != 0 ? d : abego.compareStrings(a[0].toLowerCase(), b[0].toLowerCase());
};
this.getSortedTagInfos = function() {
ensureTagsAreLoaded();
var result = this.getTagInfos();
result.sort(compareTiddlerCountAndTagName);
return result;
};
// @return an array of the tags that "partner" the activeTags,
// sorted by the number of conjoint occurances.
//
this.getPartnerRankedTags = function(activeTags) {
var partnerTagCounts = {};
for (var i = 0; i < activeTags.length; i++) {
var tiddlersWithTag = this.getTiddlersWithTag(activeTags[i]);
for (var name in tiddlersWithTag) {
var tiddler = store.getTiddler(name);
// It may happen that a tiddler is "gone" in the meantime
if (!(tiddler instanceof Tiddler))
continue;
for(var j=0; j<tiddler.tags.length; j++) {
var tag = tiddler.tags[j];
var c = partnerTagCounts[tag];
partnerTagCounts[tag] = c ? c+1 : 1;
}
}
}
var currentTagSet = abego.toSet(activeTags);
var result = [];
for (var n in partnerTagCounts) {
if (!currentTagSet[n])
result.push(n);
}
// Sort the tags by their partner tag count, then alphabetically
result.sort(function (a,b) {
var d = partnerTagCounts[b] - partnerTagCounts[a];
return d != 0 ? d : abego.compareStrings(a.toLowerCase(), b.toLowerCase());
});
return result;
};
}; // of abego.internal.TagManager
abego.internal.getTagManager = function() {
if (!abego.internal.gTagManager) abego.internal.gTagManager = new abego.internal.TagManager();
return abego.internal.gTagManager;
};
// ========================================================================
// IntelliTagger ==========================================================
// ========================================================================
(function(){
var PADDING = 2;
var BORDERWIDTH = 1;
var MAX_FAVORITE_TAGS = 30;
var fSuggestionPopup; // DOM:Element
var fAnchorElement; // DOM:Element
var fOnTagSelected; // function(e) {...}
var fSuggestedTags; // [Tag]
var fActiveTagSet; // TagSet
var fFavoriteTags; // array of Tags, [optional]
if (!abego.IntelliTagger) abego.IntelliTagger = {};
var getAnchorElement = function() {
return fAnchorElement;
};
var isCurrentTag = function(tag) {
return fActiveTagSet[tag];
};
var removeLastWord = function(s) {
var i = s.lastIndexOf(" ");
return (i >= 0) ? s.substr(0,i) : "";
};
var lastWordIsFilter = function(inputField) {
var s = inputField.value;
var len = s.length;
return (len > 0 && s[len-1] != ' ');
};
var ensureFieldEndsWithSpace = function(field) {
var s = field.value;
var len = s.length;
if (len > 0 && s[len-1] != ' ') {
field.value += ' ';
}
};
var updateTag = function(tag, inputField, tiddler) {
if (lastWordIsFilter(inputField))
inputField.value = removeLastWord(inputField.value);
story.setTiddlerTag (tiddler.title,tag,0);
ensureFieldEndsWithSpace(inputField);
abego.IntelliTagger.assistTagging(inputField, tiddler);
};
// returns the n-th suggestion, first counting the favorites, then the normal suggestions
//
// @param n zero-based.
// @return [may be null]
var getNthSuggestion = function(n) {
if (fFavoriteTags && fFavoriteTags.length > n)
return fFavoriteTags[n];
return (fSuggestedTags && fSuggestedTags.length > n)
? fSuggestedTags[n]
: null;
};
var useNthSuggestion = function(n, inputField, tiddler) {
var suggestion = getNthSuggestion(n);
if (suggestion)
updateTag(suggestion, inputField, tiddler);
};
var getFilter = function(inputField) {
var pos = inputField.value.lastIndexOf(" ");
var filter = (pos >= 0) ? inputField.value.substr(++pos,inputField.value.length) : inputField.value;
return new RegExp(filter.escapeRegExp(),"i");
};
var countExpectedTags = function(tags, expectedTagsAsProperties) {
var result = 0;
for (var i = 0; i<tags.length;i++)
if (expectedTagsAsProperties[tags[i]])
result++;
return result;
};
// Returns the number tags that have the same count of tiddlers
// as the index-th tagInfo.
//
// The index-th tag is included in the returned number.
//
// @param sortedTagInfo Array of TagInfos, sorted by count of tiddlers.
//
var getNumberOfTagsWithSameCount = function(sortedTagInfos, index, filterRE) {
var result = 1;
var c = sortedTagInfos[index];
for (var i = index+1; i < sortedTagInfos.length; i++)
if (sortedTagInfos[i][1].count == c) {
if (sortedTagInfos[i][0].match(filterRE))
result++;
} else
break;
return result;
};
var getInitialTagSuggestions = function(filterRE, maxCount) {
var tagInfos = abego.internal.getTagManager().getSortedTagInfos();
var result =[];
var lastCount = 0;
for (var i = 0; i < tagInfos.length; i++) {
var c = tagInfos[i][1].count;
// Stop adding tags to the result if not all tags with that count of tiddlers would fit into the result.
if (c != lastCount) {
if (maxCount && (result.length + getNumberOfTagsWithSameCount(tagInfos, i, filterRE) > maxCount))
break;
lastCount = c;
}
// Don't add tags that are only used in one tiddler.
if (c == 1)
break;
var s = tagInfos[i][0];
if (s.match(filterRE))
result.push(s);
}
return result;
};
var getAllFilteredTags = function(filterRE, extraTags) {
return abego.filterStrings(
abego.internal.getTagManager().getAllTags(extraTags),
filterRE);
};
// Refreshes the tagSuggestions window
//
var refreshPopup = function() {
if (!fSuggestionPopup)
return;
// Load the template for the YourSearchResult
var html = store.getTiddlerText("IntelliTaggerMainTemplate");
if (!html)
html = "<b>Tiddler IntelliTaggerMainTemplate not found</b>";
fSuggestionPopup.innerHTML = html;
// Expand the template macros etc.
applyHtmlMacros(fSuggestionPopup,null);
refreshElements(fSuggestionPopup,null);
};
var onTagClicked = function(e) {
if (!e) var e = window.event;
var tag = this.getAttribute("tag");
if (fOnTagSelected)
fOnTagSelected.call(this,tag, e);
return false;
};
var addSeparator = function(place) {
createTiddlyElement(place,"span",null,"tagSeparator", " | ");
};
var appendTags = function(place, tags, suggestionIndex, excludeTags, maxCount) {
if (!tags)
return;
var excludeTagSet = excludeTags ? abego.toSet(excludeTags) : {};
var n = tags.length;
var c = 0;
for (var i = 0; i < n; i++) {
var tag = tags[i];
if (excludeTagSet[tag])
continue;
if (c > 0)
addSeparator(place);
if (maxCount && c >= maxCount) {
abego.createEllipsis(place);
break;
}
c++;
var shortcutText = "";
var placeForButton = place;
if (suggestionIndex < 10) {
// create a wrapping span that ensures the number and the text are not linebreaked.
placeForButton = createTiddlyElement(place,"span",null,"numberedSuggestion");
suggestionIndex++;
var key = suggestionIndex < 10 ? ""+(suggestionIndex) : "0";
createTiddlyElement(placeForButton,"span",null,"suggestionNumber", key+") ");
var fastKeyText = suggestionIndex == 1 ? "Return or " : "";
shortcutText = " (Shortcut: %1Alt-%0)".format([key, fastKeyText]);
}
var shiftClickToolTip = config.views.wikified.tag.tooltip.format([tag]);
var normalClickToolTip = (isCurrentTag(tag) ? "Remove tag '%0'%1" : "Add tag '%0'%1").format([tag,shortcutText]);
var tooltip = "%0; Shift-Click: %1".format([normalClickToolTip, shiftClickToolTip]);
var btn = createTiddlyButton(
placeForButton,
tag,
tooltip,
onTagClicked,
isCurrentTag(tag) ? "currentTag" : null);
btn.setAttribute("tag",tag);
}
};
var scrollVisible = function() {
// Scroll the window to make the fSuggestionPopup page (and the anchorElement) visible.
if (fSuggestionPopup) window.scrollTo(0,ensureVisible(fSuggestionPopup));
if (getAnchorElement()) window.scrollTo(0,ensureVisible(getAnchorElement()));
};
// Close the suggestions window when the user clicks on the document
// (and not into the getAnchorElement or in the suggestions window)
//
var onDocumentClick = function(e) {
if (!e) var e = window.event;
if (!fSuggestionPopup)
return;
var target = resolveTarget(e);
if (target == getAnchorElement()) return;
if (abego.isDescendantOrSelf(fSuggestionPopup, target)) return;
abego.IntelliTagger.close();
};
addEvent(document,"click",onDocumentClick);
// We added a space to the tags edit field. To avoid that the
// tiddler is marked as "changed" just because of that we trim
// the field value
//
var oldGatherSaveFields = Story.prototype.gatherSaveFields;
Story.prototype.gatherSaveFields = function(e,fields) {
oldGatherSaveFields.apply(this, arguments);
var tags = fields.tags;
if (tags)
fields.tags = tags.trim();
};
var focusTagsField = function(title) {
story.focusTiddler(title,"tags");
var tags = abego.getTiddlerField(story, title, "tags");
if (tags) {
var len = tags.value.length;
abego.setRange(tags, len, len);
window.scrollTo(0,ensureVisible(tags));
}
};
// Attach the assistTagging to the "tags" edit field.
//
var oldEditHandler = config.macros.edit.handler;
config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
oldEditHandler.apply(this, arguments);
var field = params[0];
if((tiddler instanceof Tiddler) && field == "tags") {
// Just added the "edit tags" field.
// Attach it to the "Tag Suggestions" feature.
var inputField = place.lastChild;
inputField.onfocus = function(e) {
abego.IntelliTagger.assistTagging(inputField, tiddler);
setTimeout(
function() {
focusTagsField(tiddler.title);
}, 100);
};
inputField.onkeyup = function(e) {
if (!e) var e = window.event;
if (e.altKey && !e.ctrlKey && !e.metaKey && (e.keyCode >= 48 && e.keyCode <= 57)) {
useNthSuggestion(e.keyCode == 48 ? 9 : e.keyCode-49, inputField, tiddler);
} else if (e.ctrlKey && e.keyCode == 32) {
useNthSuggestion(0, inputField, tiddler);
} if (!e.ctrlKey && (e.keyCode == 13 || e.keyCode == 10)) {
useNthSuggestion(0, inputField, tiddler);
}
setTimeout(
function() {
abego.IntelliTagger.assistTagging(inputField, tiddler);
}, 100);
return false;
};
// ensure that the tags text ends with a space
// (otherwise the last word is used as a filter when the field gets the focus)
ensureFieldEndsWithSpace(inputField);
}
};
var onEditTags = function(e) {
if (!e) var e = window.event;
var target = resolveTarget(e);
var title = target.getAttribute("tiddler");
if (title) {
story.displayTiddler(target,title,"IntelliTaggerEditTagsTemplate", false);
focusTagsField(title);
}
return false;
};
// Add an "[edit]" button to the "tags" field that is displayed with the tiddler in the ViewTemplate.
// Pressing the button allows editing the tags only, with the text still being displayed in wikified form.
//
var oldTagsHandler = config.macros.tags.handler;
config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
oldTagsHandler.apply(this, arguments);
abego.IntelliTagger.createEditTagsButton(tiddler, createTiddlyElement(place.lastChild,"li"));
};
// close the Suggestion Window when the tiddler is no longer edited
// (i.e. the tag edit inputfield is gone.)
//
// (Note: we must poll this condition since onblur on the input field
// cannot be used since every click into the suggestion window results
// in a lost focus/blur)
//
var closeIfAnchorElementIsHidden = function() {
if (fSuggestionPopup && fAnchorElement && !abego.isDescendantOrSelf(document, fAnchorElement))
abego.IntelliTagger.close();
};
setInterval(closeIfAnchorElementIsHidden, 100);
//----------------------------------------------------------------------------
// The public API
//----------------------------------------------------------------------------
// @param suggestedTags
// array of strings representing the tags to be suggested.
//
// @param activeTags
// array of strings representing the tags currently "active".
//
// @param favoriteTags [optional]
// a subset of the suggested tags that are "favorites".
// I.e. They should be presented first etc.
//
// @param anchorElement [optional]
// when defined the suggestions are displayed "close" to the anchorElement.
// The page is scrolled to make the anchorElement visible.
// When the anchorElement is not defined the suggestions are displayed in the
// center of the window.
//
// @param onTagSelected [optional]
// function(tag, e) to be called when a tag is selected.
//
abego.IntelliTagger.displayTagSuggestions = function(suggestedTags, activeTags, favoriteTags, anchorElement, onTagSelected) {
fSuggestedTags = suggestedTags;
fActiveTagSet = abego.toSet(activeTags);
fFavoriteTags = favoriteTags;
fAnchorElement = anchorElement;
fOnTagSelected = onTagSelected;
if (!fSuggestionPopup) {
fSuggestionPopup = createTiddlyElement(document.body,"div",null,"intelliTaggerSuggestions");
fSuggestionPopup.style.position = "absolute";
}
refreshPopup();
abego.openAsPopup(fSuggestionPopup);
if (getAnchorElement()) {
var w = getAnchorElement().offsetWidth;
if (fSuggestionPopup.offsetWidth < w) {
fSuggestionPopup.style.width = (w-2*(PADDING+BORDERWIDTH)) + "px";
}
abego.moveBelowAndClip(fSuggestionPopup, getAnchorElement());
} else {
abego.centerOnWindow(fSuggestionPopup);
}
scrollVisible();
};
// Shows the Tag Suggestion Popup for the given tiddler, below the specified inputField.
//
abego.IntelliTagger.assistTagging = function(inputField, tiddler) {
var filterRE = getFilter(inputField);
var s = inputField.value;
if (lastWordIsFilter(inputField))
s = removeLastWord(s);
var activeTags = s.readBracketedList();
var favoriteTags = activeTags.length > 0
? abego.filterStrings(abego.internal.getTagManager().getPartnerRankedTags(activeTags), filterRE, MAX_FAVORITE_TAGS)
: getInitialTagSuggestions(filterRE, MAX_FAVORITE_TAGS);
abego.IntelliTagger.displayTagSuggestions(
getAllFilteredTags(filterRE,activeTags),
activeTags,
favoriteTags,
inputField,
function(tag, e) {
if (e.shiftKey) {
onClickTag.call(this,e);
} else
updateTag(tag, inputField, tiddler);
});
};
// Closes the Tag Suggestions Popup
//
abego.IntelliTagger.close = function() {
abego.closePopup(fSuggestionPopup);
fSuggestionPopup = null;
return false;
};
// Creates an TiddlyButton at the given place to edit the tags of the given tiddler.
//
abego.IntelliTagger.createEditTagsButton = function(tiddler, place, text, tooltip, className, id, accessKey) {
if (!text) text = "[edit]";
if (!tooltip) tooltip = "Edit the tags";
if (!className) className = "editTags";
var editButton = createTiddlyButton(place,text,tooltip, onEditTags, className, id, accessKey);
editButton.setAttribute("tiddler", (tiddler instanceof Tiddler) ? tiddler.title : String(tiddler));
return editButton;
};
abego.IntelliTagger.getSuggestionTagsMaxCount = function() {
return 100;
};
//----------------------------------------------------------------------------
// Macros
//----------------------------------------------------------------------------
// ====Macro intelliTagger ================================================
config.macros.intelliTagger = {
// Standard Properties
label: "intelliTagger",
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
var namesAndValues = paramString.parseParams("list",null, true);
var actions = namesAndValues[0]["action"];
for (var i = 0; actions && i < actions.length; i++) {
var actionName = actions[i];
var action = config.macros.intelliTagger.subhandlers[actionName];
if (!action)
abego.alertAndThrow("Unsupported action '%0'".format([actionName]));
action(place,macroName,params,wikifier,paramString,tiddler);
}
},
subhandlers: {
showTags : function(place,macroName,params,wikifier,paramString,tiddler) {
appendTags(place, fSuggestedTags, fFavoriteTags ? fFavoriteTags.length : 0, fFavoriteTags,abego.IntelliTagger.getSuggestionTagsMaxCount());
},
showFavorites : function(place,macroName,params,wikifier,paramString,tiddler) {
appendTags(place, fFavoriteTags, 0);
},
closeButton : function(place,macroName,params,wikifier,paramString,tiddler) {
var button = createTiddlyButton(place, "close", "Close the suggestions", abego.IntelliTagger.close);
},
version : function(place) {
var t = "IntelliTagger %0.%1.%2".format(
[version.extensions.IntelliTaggerPlugin.major,
version.extensions.IntelliTaggerPlugin.minor,
version.extensions.IntelliTaggerPlugin.revision]);
var e = createTiddlyElement(place, "a");
e.setAttribute("href", "http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin");
e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">'+t+'<font>';
},
copyright : function(place) {
var e = createTiddlyElement(place, "a");
e.setAttribute("href", "http://tiddlywiki.abego-software.de");
e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">© 2006-2007 <b><font color="red">abego</font></b> Software<font>';
}
}
};
})();
//}}}
//#endOf: MainCode
//{{{
config.shadowTiddlers["IntelliTaggerStyleSheet"] =
"/***\n"+
"!~IntelliTagger Stylesheet\n"+
"***/\n"+
"/*{{{*/\n"+
".intelliTaggerSuggestions {\n"+
"\tposition: absolute;\n"+
"\twidth: 600px;\n"+
"\n"+
"\tpadding: 2px;\n"+
"\tlist-style: none;\n"+
"\tmargin: 0;\n"+
"\n"+
"\tbackground: #eeeeee;\n"+
"\tborder: 1px solid DarkGray;\n"+
"}\n"+
"\n"+
".intelliTaggerSuggestions .currentTag {\n"+
"\tfont-weight: bold;\n"+
"}\n"+
"\n"+
".intelliTaggerSuggestions .suggestionNumber {\n"+
"\tcolor: #808080;\n"+
"}\n"+
"\n"+
".intelliTaggerSuggestions .numberedSuggestion{\n"+
"\twhite-space: nowrap;\n"+
"}\n"+
"\n"+
".intelliTaggerSuggestions .intelliTaggerFooter {\n"+
"\tmargin-top: 4px;\n"+
"\tborder-top-width: thin;\n"+
"\tborder-top-style: solid;\n"+
"\tborder-top-color: #999999;\n"+
"}\n"+
".intelliTaggerSuggestions .favorites {\n"+
"\tborder-bottom-width: thin;\n"+
"\tborder-bottom-style: solid;\n"+
"\tborder-bottom-color: #999999;\n"+
"\tpadding-bottom: 2px;\n"+
"}\n"+
"\n"+
".intelliTaggerSuggestions .normalTags {\n"+
"\tpadding-top: 2px;\n"+
"}\n"+
"\n"+
".intelliTaggerSuggestions .intelliTaggerFooter .button {\n"+
"\tfont-size: 10px;\n"+
"\n"+
"\tpadding-left: 0.3em;\n"+
"\tpadding-right: 0.3em;\n"+
"}\n"+
"\n"+
"/*}}}*/\n";
config.shadowTiddlers["IntelliTaggerMainTemplate"] =
"<!--\n"+
"{{{\n"+
"-->\n"+
"<div class=\"favorites\" macro=\"intelliTagger action: showFavorites\"></div>\n"+
"<div class=\"normalTags\" macro=\"intelliTagger action: showTags\"></div>\n"+
"<!-- The Footer (with the Navigation) ============================================ -->\n"+
"<table class=\"intelliTaggerFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n"+
" <tr>\n"+
"\t<td align=\"left\">\n"+
"\t\t<span macro=\"intelliTagger action: closeButton\"></span>\n"+
"\t</td>\n"+
"\t<td align=\"right\">\n"+
"\t\t<span macro=\"intelliTagger action: version\"></span>, <span macro=\"intelliTagger action: copyright \"></span>\n"+
"\t</td>\n"+
" </tr>\n"+
"</tbody></table>\n"+
"<!--\n"+
"}}}\n"+
"-->\n";
config.shadowTiddlers["IntelliTaggerEditTagsTemplate"] =
"<!--\n"+
"{{{\n"+
"-->\n"+
"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+
"<div class='title' macro='view title'></div>\n"+
"<div class='tagged' macro='tags'></div>\n"+
"<div class='viewer' macro='view text wikified'></div>\n"+
"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+
"<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n"+
"<!--\n"+
"}}}\n"+
"-->\n";
config.shadowTiddlers["BSD open source license (abego Software)"] = "See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";
config.shadowTiddlers["IntelliTaggerPlugin Documentation"] = "[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].";
config.shadowTiddlers["IntelliTaggerPlugin SourceCode"] = "[[Plugin source code on abego Software website|http://tiddlywiki.abego-software.de/archive/IntelliTaggerPlugin/Plugin-IntelliTagger-src.1.0.2.js]]\n";
//}}}
//{{{
(function() {
var oldRestart = restart;
restart = function() {
setStylesheet(store.getTiddlerText('IntelliTaggerStyleSheet'),'IntelliTaggerStyleSheet');
oldRestart.apply(this,arguments);
}
})();
//}}}
//{{{
} // of single install
//}}}
/%<<list filter [tag[Bug]]>>%/
/%
[[Bug 1266799]]
[[Bug 1267990]]
[[Bug 1303163]]
[[Bug 1305603]]
[[Bug 1307729]]
[[Bug 1307778]]
[[Bug 1308136]]
[[Bug 1314894]]
[[Bug 1325535]]
[[Bug 1327610]]
[[Bug 1327612]]
[[Bug 1327617]]
[[Bug 1330789]]
[[Bug 1335033]]
[[Bug 1336288]]
[[Bug 1339823]]
[[Bug 1344643]]
[[Bug 1356346]]
[[Bug 1356348]]
[[Bug 1358867]]
[[Bug 1362684]]
[[Bug 1366120]]
[[Bug 1413724]]
[[Bug 1414454]]
[[Bug 1448677]]
[[Bug 1464106]]
[[Bug 1466197]]
[[Bug 1467559]]
[[Bug 1467562]]
[[Bug 1472219]]
[[Bug 1474261]]
[[Bug 1507890]]
[[Bug 1522746]]
[[Bug 1522751]]
[[Bug 1522753]]
[[Bug 1523410]]
[[Bug 1548327]]
[[Bug 1551008]]
[[Bug 1555993]]
[[Bug 1558210]]
[[Bug 1571154]]
[[Bug 1585584]]
[[Bug 1585585]]
[[Bug 1591878]]
[[Bug 1600636]]
[[Bug 1627068]]
[[Bug 1628074]]
[[Bug 1652553]]
[[Bug 1678056]]
[[Bug 1682196]]
[[Bug 1684165]]
[[Bug 1684372]]
[[Bug 1684385]]
[[Bug 1728513]]
[[Bug 1730240]]
[[Bug 1734076]]
[[Bug 1734799]]
[[Bug 1738262]]
[[Bug 1784965]]
[[Bug 1816388]]
[[Bug 1823996]]
[[Bug 1843180]]
[[Bug 1849867]]
[[Bug 1855661]]
[[Bug 1877218]]
[[Bug 1883342]]
[[Bug 1904642]] %/
/***
|Name|MatchTagsPlugin|
|Source|http://www.TiddlyTools.com/#MatchTagsPlugin|
|Documentation|http://www.TiddlyTools.com/#MatchTagsPluginInfo|
|Version|2.0.5|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|'tag matching' with full boolean expressions (AND, OR, NOT, and nested parentheses)|
!!!!!Documentation
> see [[MatchTagsPluginInfo]]
!!!!!Revisions
<<<
2011.01.23 2.0.5 fix core tweak for TW262+: adjust code in config.filters['tag'] instead of filterTiddlers()
2010.08.11 2.0.4 in getMatchingTiddlers(), fixed sorting for descending order (e.g, "-created")
| please see [[MatchTagsPluginInfo]] for additional revision details |
2008.02.28 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.MatchTagsPlugin= {major: 2, minor: 0, revision: 5, date: new Date(2011,23,11)};
// store.getMatchingTiddlers() processes boolean expressions for tag matching
// sortfield (optional) sets sort order for tiddlers - default=title
// tiddlers (optional) use alternative set of tiddlers (instead of current store)
TiddlyWiki.prototype.getMatchingTiddlers = function(tagexpr,sortfield,tiddlers) {
var debug=config.options.chkDebug; // abbreviation
var cmm=config.macros.matchTags; // abbreviation
var r=[]; // results are an array of tiddlers
var tids=tiddlers||store.getTiddlers();
if (tids && sortfield) tids=store.sortTiddlers(tids,sortfield);
if (debug) displayMessage(cmm.msg1.format([tids.length]));
// try simple lookup to quickly find single tags or tags that
// contain boolean operators as literals, e.g. "foo and bar"
for (var t=0; t<tids.length; t++)
if (tids[t].isTagged(tagexpr)) r.pushUnique(tids[t]);
if (r.length) {
if (debug) displayMessage(cmm.msg4.format([r.length,tagexpr]));
return r;
}
// convert expression into javascript code with regexp tests,
// so that "tag1 AND ( tag2 OR NOT tag3 )" becomes
// "/\~tag1\~/.test(...) && ( /\~tag2\~/.test(...) || ! /\~tag3\~/.test(...) )"
// normalize whitespace, tokenize operators, delimit with "~"
var c=tagexpr.trim(); // remove leading/trailing spaces
c = c.replace(/\s+/ig," "); // reduce multiple spaces to single spaces
c = c.replace(/\(\s?/ig,"~(~"); // open parens
c = c.replace(/\s?\)/ig,"~)~"); // close parens
c = c.replace(/(\s|~)?&&(\s|~)?/ig,"~&&~"); // &&
c = c.replace(/(\s|~)AND(\s|~)/ig,"~&&~"); // AND
c = c.replace(/(\s|~)?\|\|(\s|~)?/ig,"~||~"); // ||
c = c.replace(/(\s|~)OR(\s|~)/ig,"~||~"); // OR
c = c.replace(/(\s|~)?!(\s|~)?/ig,"~!~"); // !
c = c.replace(/(^|~|\s)NOT(\s|~)/ig,"~!~"); // NOT
c = c.replace(/(^|~|\s)NOT~\(/ig,"~!~("); // NOT(
// change tag terms to regexp tests
var terms=c.split("~"); for (var i=0; i<terms.length; i++) { var t=terms[i];
if (/(&&)|(\|\|)|[!\(\)]/.test(t) || t=="") continue; // skip operators/parens/spaces
if (t==config.macros.matchTags.untaggedKeyword)
terms[i]="tiddlertags=='~~'"; // 'untagged' tiddlers
else
terms[i]="/\\~"+t+"\\~/.test(tiddlertags)";
}
c=terms.join(" ");
if (debug) { displayMessage(cmm.msg2.format([tagexpr])); displayMessage(cmm.msg3.format([c])); }
// scan tiddlers for matches
for (var t=0; t<tids.length; t++) {
// assemble tags from tiddler into string "~tag1~tag2~tag3~"
var tiddlertags = "~"+tids[t].tags.join("~")+"~";
try { if(eval(c)) r.push(tids[t]); } // test tags
catch(e) { // error in test
displayMessage(cmm.msg2.format([tagexpr]));
displayMessage(cmm.msg3.format([c]));
displayMessage(e.toString());
break; // skip remaining tiddlers
}
}
if (debug) displayMessage(cmm.msg4.format([r.length,tagexpr]));
return r;
}
//}}}
//{{{
config.macros.matchTags = {
msg1: "scanning %0 input tiddlers",
msg2: "looking for '%0'",
msg3: "using expression: '%0'",
msg4: "found %0 tiddlers matching '%1'",
noMatch: "no matching tiddlers",
untaggedKeyword: "-",
untaggedLabel: "no tags",
untaggedPrompt: "show tiddlers with no tags",
defTiddler: "MatchingTiddlers",
defTags: "",
defFormat: "[[%0]]",
defSeparator: "\n",
reportHeading: "Found %0 tiddlers tagged with: '{{{%1}}}'\n----\n",
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var mode=params[0]?params[0].toLowerCase():'';
if (mode=="inline")
params.shift();
if (mode=="report" || mode=="panel") {
params.shift();
var target=params.shift()||this.defTiddler;
}
if (mode=="popup") {
params.shift();
if (params[0]&¶ms[0].substr(0,6)=="label:") var label=params.shift().substr(6);
if (params[0]&¶ms[0].substr(0,7)=="prompt:") var prompt=params.shift().substr(7);
} else {
var fmt=(params.shift()||this.defFormat).unescapeLineBreaks();
var sep=(params.shift()||this.defSeparator).unescapeLineBreaks();
}
var sortBy="+title";
if (params[0]&¶ms[0].substr(0,5)=="sort:") sortBy=params.shift().substr(5);
var expr = params.join(" ");
if (mode!="panel" && (!expr||!expr.trim().length)) return;
if (expr==this.untaggedKeyword)
{ var label=this.untaggedLabel; var prompt=this.untaggedPrompt };
switch (mode) {
case "popup": this.createPopup(place,label,expr,prompt,sortBy); break;
case "panel": this.createPanel(place,expr,fmt,sep,sortBy,target); break;
case "report": this.createReport(target,this.defTags,expr,fmt,sep,sortBy); break;
case "inline": default: this.createInline(place,expr,fmt,sep,sortBy); break;
}
},
formatList: function(tids,fmt,sep) {
var out=[];
for (var i=0; i<tids.length; i++) { var t=tids[i];
var title=t.title;
var who=t.modifier;
var when=t.modified.toLocaleString();
var text=t.text;
var first=t.text.split("\n")[0];
var desc=store.getTiddlerSlice(t.title,"description");
desc=desc||store.getTiddlerSlice(t.title,"Description");
desc=desc||store.getTiddlerText(t.title+"##description");
desc=desc||store.getTiddlerText(t.title+"##Description");
var tags=t.tags.length?'[['+t.tags.join(']] [[')+']]':'';
out.push(fmt.format([title,who,when,text,first,desc,tags]));
}
return out.join(sep);
},
createInline: function(place,expr,fmt,sep,sortBy) {
wikify(this.formatList(store.sortTiddlers(store.getMatchingTiddlers(expr),sortBy),fmt,sep),place);
},
createPopup: function(place,label,expr,prompt,sortBy) {
var btn=createTiddlyButton(place,
(label||expr).format([expr]),
(prompt||config.views.wikified.tag.tooltip).format([expr]),
function(ev){ return config.macros.matchTags.showPopup(this,ev||window.event); });
btn.setAttribute("sortBy",sortBy);
btn.setAttribute("expr",expr);
},
showPopup: function(here,ev) {
var p=Popup.create(here); if (!p) return false;
var tids=store.getMatchingTiddlers(here.getAttribute("expr"));
store.sortTiddlers(tids,here.getAttribute("sortBy"));
var list=[]; for (var t=0; t<tids.length; t++) list.push(tids[t].title);
if (!list.length) createTiddlyText(p,this.noMatch);
else {
var b=createTiddlyButton(createTiddlyElement(p,"li"),
config.views.wikified.tag.openAllText,
config.views.wikified.tag.openAllTooltip,
function() {
var list=this.getAttribute("list").readBracketedList();
story.displayTiddlers(null,tids);
});
b.setAttribute("list","[["+list.join("]] [[")+"]]");
createTiddlyElement(p,"hr");
}
var out=this.formatList(tids," [[%0]] ","\n"); wikify(out,p);
Popup.show();
ev.cancelBubble=true;
if(ev.stopPropagation) ev.stopPropagation();
return false;
},
createReport: function(target,tags,expr,fmt,sep,sortBy) {
var tids=store.sortTiddlers(store.getMatchingTiddlers(expr),sortBy);
if (!tids.length) { displayMessage('no matches for: '+expr); return false; }
var msg=config.messages.overwriteWarning.format([target]);
if (store.tiddlerExists(target) && !confirm(msg)) return false;
var out=this.reportHeading.format([tids.length,expr])
out+=this.formatList(tids,fmt,sep);
store.saveTiddler(target,target,out,config.options.txtUserName,new Date(),tags,{});
story.closeTiddler(target); story.displayTiddler(null,target);
},
createPanel: function(place,expr,fmt,sep,sortBy,tid) {
var s=createTiddlyElement(place,"span"); s.innerHTML=store.getTiddlerText("MatchTagsPlugin##html");
var f=s.getElementsByTagName("form")[0];
f.expr.value=expr; f.fmt.value=fmt; f.sep.value=sep.escapeLineBreaks();
f.tid.value=tid; f.tags.value=this.defTags;
}
};
//}}}
/***
//{{{
!html
<form style='display:inline;white-space:nowrap'>
<input type='text' name='expr' style='width:50%' title='tag expression'><!--
--><input type='text' name='fmt' style='width:10%' title='list item format'><!--
--><input type='text' name='sep' style='width:5%' title='list item separator'><!--
--><input type='text' name='tid' style='width:12%' title='target tiddler title'><!--
--><input type='text' name='tags' style='width:10%' title='target tiddler tags'><!--
--><input type='button' name='go' style='width:8%' value='go' onclick="
var expr=this.form.expr.value;
if (!expr.length) { alert('Enter a boolean tag expression'); return false; }
var fmt=this.form.fmt.value;
if (!fmt.length) { alert('Enter the list item output format'); return false; }
var sep=this.form.sep.value.unescapeLineBreaks();
var tid=this.form.tid.value;
if (!tid.length) { alert('Enter a target tiddler title'); return false; }
var tags=this.form.tags.value;
config.macros.matchTags.createReport(tid,tags,expr,fmt,sep,'title');
return false;">
</form>
!end
//}}}
***/
//{{{
// SHADOW TIDDLER for displaying default panel input form
config.shadowTiddlers.MatchTags="<<matchTags panel>>";
//}}}
//{{{
// TWEAK core filterTiddlers() or config.filters['tag'] (in TW262+)
// to use getMatchingTiddlers instead getTaggedTiddlers
// for enhanced boolean matching in [tag[...]] syntax
var TW262=config.filters && config.filters['tag']; // detect TW262+
var fname=TW262?"config.filters['tag']":"TiddlyWiki.prototype.filterTiddlers";
var code=eval(fname).toString().replace(/getTaggedTiddlers/g,'getMatchingTiddlers');
eval(fname+'='+code);
//}}}
//{{{
// REDEFINE core handler for enhanced boolean matching in tag:"..." paramifier
// use filterTiddlers() instead of getTaggedTiddlers() to get list of tiddlers.
config.paramifiers.tag = {
onstart: function(v) {
var tagged = store.filterTiddlers("[tag["+v+"]]");
story.displayTiddlers(null,tagged,null,false,null);
}
};
//}}}
/***
|Name|MatchTagsPluginInfo|
|Source|http://www.TiddlyTools.com/#MatchTagsPlugin|
|Documentation|http://www.TiddlyTools.com/#MatchTagsPluginInfo|
|Version|2.0.5|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|documentation|
|Description|documentation for MatchTagsPlugin|
!!!!!Usage
<<<
This plugin extends the {{{[tag[tagname]]}}} macro parameter syntax used by the TiddlyWiki core {{{<<list>>}}} macro so that, instead of a simple tagname value, you can specify a complex combination of tagname values using a //boolean expression// containing AND, OR, and NOT operators, enclosed in nested parentheses if needed.
{{{
<<list filter "[tag[expression]]">>
}}}
In addition, the plugin defines a new macro, {{{<<matchTags ...>>}}} that can be used instead of the core {{{<<list>>}}} macro to output a list of matching tiddlers //using a custom 'item format' and 'separator'//. You can also use this macro to create a command link that displays the matching tiddlers within a popup list, similar to the standard {{{<<tag tagName>>}}} macro, but matching a combination of tag values rather than a single tag value.
{{{
<<matchTags inline "format" "separator" sort:fieldname tag expression>>
<<matchTags popup "label:..." "prompt:..." sort:fieldname tag expression>>
<<matchTags report TiddlerName "format" "separator" sort:fieldname tag expression>>
<<matchTags panel Tiddlername "format" "separator" sort:fieldname tag expression>>
}}}
where:
* ''inline'', ''report'', ''panel'', and ''popup''<br>are keywords that indicate the type of output that the macro should produce:
** ''inline'' //(default)// - displays a list of matching tiddlers embedded directly in tiddler content
** ''popup'' - embeds a command button that, when clicked, lists matching tiddlers in a ~TiddlyWiki popup display
** ''report'' - generates a list of matching tiddler in a separate [[MatchingTiddlers]] report tiddler
** ''panel'' - displays an interactive form for generating a [[MatchingTiddlers]] report
* ''format''<br>defines the wiki-syntax for rendering list items. The following //substitution markers// can be used to insert tiddler-specific information for each matched tiddler:
** {{{%0}}} - title
** {{{%1}}} - modifier (author)
** {{{%2}}} - modified (date of last change)
** {{{%3}}} - text (all tiddler content)
** {{{%4}}} - firstline (tiddler content up to the first newline)
** {{{%5}}} - description (tiddler slice or section content named "description" or "Description")
** {{{%6}}} - tags (space-separated, bracketed list)
* ''separator''<br>defines the wiki-syntax to use //between// each matching title (e.g., ", " creates a comma-separated list, while "\n" displays one tiddler per line).
* ''sort:fieldname'' (optional)<br>specifies the sort order for the resulting list of tiddlers. You can specify any tiddler field name (standard or custom-defined). Standard tiddler fieldnames include: //title, created, modified, modifier//. If not specified, tiddlers are sorted by title. You can prefix the fieldname with "+" or "-" to indicate ascending or descending order, respectively.
* ''tag expression''<br>the remaining parameter(s) are joined together to define the boolean expression to be matched.
When using the ''popup'' option, there are two additional (and optional) parameters you can specify:
* ''"label:..."''(optional)<br> indicates the text for the popup command link. The default is to display the specified tag expression itself.
* ''"prompt:..."'' (optional)<br>indicates the mouseover 'tooltip' for the popup command link.
When using the ''report'' or ''panel'' option, an additional parameter may be provided:
* ''~TiddlerName''<br>specifies the target tiddler into which the output will be generated (default: [[MatchingTiddlers]])
Notes:
*A tag expression can use any combination of text operators: ''AND'', ''OR'', ''NOT'' (or their equivalent javascript operators: ''&&'', ''||'', ''!''), contained in nested parentheses as needed.
*Operators should be delimited by spaces or parentheses.
*Before matching, leading/trailing spaces are automatically trimmed and multiple spaces are reduced to single spaces.
*Tag values containing embedded spaces do //not// have to be enclosed in {{{[[...]]}}}.
*Tag values that contain boolean operators as ''literal text'' (e.g., {{{"foo and bar"}}} or {{{"foo && bar"}}} cannot be used within a compound boolean expression, but //can// be matched if specified by themselves, without any other tag values or operators.
*To match tiddlers that are untagged, use "-" as a special tag value within the expression.
*You can match "wildcard" tags by using //regular expression// (i.e., "text pattern") syntax within a tag value, e.g. {{{[Tt]agvalue.*}}}
<<<
!!!!!Examples:
<<<
display a popup list:
{{{
<<matchTags popup sample OR (settings AND systemConfig)>>
}}}
><<matchTags popup sample OR (settings AND systemConfig)>>
display a popup list with custom label:
{{{
<<matchTags popup "label:samples and settings" sample OR (settings AND systemConfig)>>
}}}
><<matchTags popup "label:samples and settings" sample OR (settings AND systemConfig)>>
display a popup list of untagged tiddlers:
{{{
<<matchTags popup ->>
}}}
><<matchTags popup ->>
generate a report using interactive form control panel
{{{
<<matchTags panel "MatchingTiddlers" "[[%0]]" "\n" sample OR (settings AND systemConfig)>>
}}}
>{{smallform{<<matchTags panel "MatchingTiddlers" "[[%0]]" "\n" sample OR (settings AND systemConfig)>>}}}
comma-separated list:
{{{
<<matchTags "[[%0]]" ", " sample OR (settings AND systemConfig)>>
}}}
><<matchTags "[[%0]]" ", " sample OR (settings AND systemConfig)>>
numbered list (sorted by modification date, most recent first):
{{{
<<matchTags "#[[%0]] (%2)<br>^^%5^^" "\n" sort:-modified sample OR (settings AND systemConfig)>>
}}}
><<matchTags "#[[%0]] (%2)<br>^^%5^^" "\n" sort:-modified sample OR (settings AND systemConfig)>>
bullet-item list (using the TiddlyWiki core {{{<<list filter ...>>}}} macro):
//(Note: when using the core {{{<<list>>}}} macro, you should always enclose the entire tag filter parameter within quotes)//
{{{
<<list filter "[tag[sample OR (settings AND systemConfig)]]">>
}}}
><<list filter "[tag[sample OR (settings AND systemConfig)]]">>
<<<
!!!!!Revisions
<<<
2011.01.22 2.0.5 fix core tweak for TW262+: adjust code in config.filters['tag'] instead of filterTiddlers()
2010.08.11 2.0.4 in getMatchingTiddlers(), fixed sorting for descending order (e.g, "-created")
2010.03.02 2.0.3 added %6 format (tags)
2010.03.01 2.0.2 in formatList(), don't automatically put '[[' and ']]' around title (%0) in formatted output
2009.08.29 2.0.1 added support for {{{config.macros.matchTags.defTags}}} to auto-tag [[MatchingTiddlers]] output
2008.09.04 2.0.0 added "report" and "panel" options to generate formatted results and store in a tiddler. Also, added config.macros.matchTags.formatList(place,fmt,sep) API to return formatted output for use with other plugins/scripts
2008.09.01 1.9.2 fixed return value from popup button handler so IE doesn't attempt to leave the page
2008.08.31 1.9.1 improved expression conversion handling to permit use of regular expressions for "wildcard" matching within tag values
2008.06.12 1.9.0 added support for formatted output of: title, who, when, text, firstline, description (slice or section)
2008.06.05 1.8.0 in getMatchingTiddlers(), added optional sortfield and tiddlers params to support use of alternative set of tiddlers instead of using current store content (provides filtering support for ImportTiddlersPlugin)
2008.06.04 1.7.1 in getMatchingTiddlers(), reworked conversion of expression for more robust parsing of whitespace, parentheses and javascript operators and allow use of "-" (untagged) //within// expressions
2008.05.19 1.7.0 in getMatchingTiddlers(), use reverseLookup() instead of forEachTiddler() to permit access to tiddlers included via [[IncludePlugin|http://tiddlywiki.abego-software.de/#IncludePlugin]]
2008.05.17 1.6.0 in getMatchingTiddlers(), rewrote expression conversion to handle tags with spaces tag values that are substrings of other tag values.
2008.05.16 1.5.0 added special case using "-" to find UNTAGGED tiddlers
2008.05.15 1.4.0 added "popup" output option
2008.05.14 1.3.4 instead of hijacking getTaggedTiddlers(), added tweak of filterTiddlers() prototype to replace getTaggedTiddlers() with getMatchingTiddler() so that core use of getTaggedTiddlers() does not perform boolean processing of tiddler titles such as [[To Be or not To Be]]. Also, improved "filter error" messages in getMatchingTiddlers() to report tag expression in addition to actual eval error.
2008.04.25 1.3.3 in getTaggedTiddlers(), fixed handling for "not" embedded within a tag
2008.04.21 1.3.2 in getTaggedTiddlers(), fixed handling for initial "NOT" and "NOT(expr)" syntax
2008.04.20 1.3.1 in getTaggedTiddlers(), corrected check for boolean expression to avoid excess processing of tags containing spaces. Also, improved handling for non-existing tags that contain text of existing tags
2008.04.19 1.3.0 in filterTiddlers(), use getTaggedTiddlers() instead of matchTags(), and then hijack getTaggedTiddlers() to add matchTags() handling
2008.04.19 [*.*.*] plugin size reduction: moved documentation to [[MatchTagsPluginInfo]]
2008.03.25 1.2.0 added optional "sort:fieldname" parameter
2008.03.20 1.1.2 in handler(), replace 'encodeTiddlyLink' with explicit [[...]] brackets to ensure that one-word tiddler titles are properly rendered as TiddlyLinks
2008.02.29 1.1.1 in matchTags(), added handling to skip remaining tiddlers if expression has an error
2008.02.29 1.1.0 refactored to define store.matchTags() and extend store.filterTiddlers()
2008.02.28 1.0.0 initial release
<<<
/***
|Name|StorySaverPlugin|
|Source|http://www.TiddlyTools.com/#StorySaverPlugin|
|Documentation|http://www.TiddlyTools.com/#StorySaverPluginInfo|
|Version|1.8.3|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Requires|MarkupPostBody|
|Description|save/restore current tiddler view between browser sessions|
Automatically save a list of currently viewed tiddlers (the 'story') in a local cookie, {{{txtSavedStory}}} and then open those tiddlers when the document is reloaded, so you can resume working from the same place you left off!! Also, use {{{<<saveStory>>}}} and {{{<<openStory>>}}} macros to quickly save/re-display stories stored in tiddlers, using a command link, droplist, or popup display.
!!!!!Documentation
>see [[StorySaverPluginInfo]]
!!!!!Configuration
<<<
<<option chkSaveStory>> use automatic story cookie (reopens tiddlers on startup)
<<option chkStoryAllowAdd>>include 'add a story' command in droplist/popup
<<option chkStoryFold>>fold story tiddlers when opening a story (see [[CollapseTiddlersPlugin]])
<<option chkStoryClose>>close other tiddlers when opening a story
<<option chkStoryTop>>open story tiddlers at top of column
<<option chkStoryBottom>>open story tiddlers at bottom of column
<<<
!!!!!Revisions
<<<
2009.10.20 1.8.3 fix handling for 'add' item in popup menu
|please see [[StorySaverPluginInfo]] for additional revision details|
2007.10.05 1.0.0 initial release. Moved [[SetDefaultTiddlers]] inline script and rewrote as a {{{<<saveStory>>}}} macro.
<<<
!!!!!Code
***/
//{{{
version.extensions.StorySaverPlugin= {major: 1, minor: 8, revision: 3, date: new Date(2009,10,20)};
var defaults={
chkSaveStory: false,
chkStoryFold: true,
chkStoryClose: true,
chkStoryAllowAdd: true,
chkStoryTop: true,
chkStoryBottom: false
};
for (var id in defaults) if (config.options[id]===undefined)
config.options[id]=defaults[id];
// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
window.removeCookie=function(name) {
document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
}
// save or clear story cookie on exit
if (window.coreTweaks_confirmExit==undefined) {
window.coreTweaks_confirmExit=window.confirmExit;
window.confirmExit=function() {
if (config.options.chkSaveStory) { // save cookie
var links=[];
story.forEachTiddler(function(title,element){links.push('[['+title+']]');});
config.options.txtSavedStory=links.join(' ');
saveOptionCookie('txtSavedStory');
} else removeCookie('txtSavedStory');
return window.coreTweaks_confirmExit.apply(this,arguments);
}
}
//}}}
/***
''apply saved story on startup:'' //important note: the following code is actually located in [[MarkupPostBody]]. This is because it needs to supercede the core's getParameters() function, which is called BEFORE plugins are loaded, preventing the normal plugin-based hijack method from working, while code loaded into [[MarkupPostBody]] will be processed as soon as the document is read, even before the TW main() function is invoked.//
<<tiddler MarkupPostBody>>
***/
//{{{
config.macros.saveStory = {
label: 'set default tiddlers',
defaultTiddler: 'DefaultTiddlers',
prompt: 'store a list of currently displayed tiddlers in another tiddler',
askMsg: 'Enter the name of a tiddler in which to save the current story:',
tag: 'story',
excludeTag: 'excludeStory',
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var tid=params.shift()||'DefaultTiddlers';
var label=params.shift()||this.label;
var tip=params.shift()||this.prompt;
var btn=createTiddlyButton(place,label,tip,this.setTiddler,'button');
btn.setAttribute('tid',tid);
btn.setAttribute('extratags','[['+params.join(']] [[')+']]');
},
setTiddler: function() {
var cms=config.macros.saveStory; // abbrev
// get list of current open tiddlers
var tids=[];
story.forEachTiddler(function(title,element){
var t=store.getTiddler(title);
if (!t || !t.isTagged(cms.excludeTag)) tids.push('[['+title+']]');
});
// get target tiddler
var tid=this.getAttribute('tid');
if (!tid || tid=='ask') {
tid=prompt(cms.askMsg,cms.defaultTiddler);
if (!tid || !tid.length) return false; // cancelled by user
}
if(store.tiddlerExists(tid) && !confirm(config.messages.overwriteWarning.format([tid])))
return false;
tids=tids.join('\n');
var t=store.getTiddler(tid); var tags=t?t.tags:[];
var extratags=(this.getAttribute('extratags')||'').readBracketedList();
for (var i=0; i<extratags.length; i++) tags.pushUnique(extratags[i]);
tags.pushUnique(cms.tag);
store.saveTiddler(tid,tid,tids,config.options.txtUserName,new Date(),tags,t?t.fields:null);
story.displayTiddler(null,tid);
story.refreshTiddler(tid,null,true);
displayMessage(tid+' has been '+(t?'updated':'created'));
return false;
}
}
//}}}
//{{{
config.macros.openStory = {
label: 'open story: %0',
prompt: 'open the set of tiddlers listed in: %0',
popuplabel: 'stories',
popupprompt: 'view a set of tiddlers',
tag: 'story',
selectprompt: 'select a story...',
optionsprompt: 'viewing options...',
foldcmd: '[%0] fold story',
foldprompt: 'fold story tiddlers when opening a story',
closecmd: '[%0] close others',
closeprompt: 'close other tiddlers when opening a story',
topcmd: '[%0] open at top',
topprompt: 'open story tiddlers at top of column',
bottomcmd: '[%0] open at bottom',
bottomprompt: 'open story tiddlers at bottom of column',
addcmd: 'add a story...',
addprompt: 'create a new story',
excludeTag: 'excludeStory',
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if (params[0].toLowerCase()=='list') return this.createList(place,params);
else if (params[0].toLowerCase()=='popup') return this.createPopup(place,params);
else this.createButton(place,params);
},
showStory: function(tid,fold) {
var co=config.options; // abbrev
var tids=[];
var t=store.getTiddler(tid);
var tagged=store.getTaggedTiddlers(tid,'title');
if (tagged.length) // if tiddler IS a tag, use tagged tiddlers as story
for (var i=0; i<tagged.length; i++) tids.push(tagged[i].title);
else if (t) { // get tiddler list from content
if (!t.linksUpdated) t.changed();
for (var i=0; i<t.links.length; i++) {
var tid=store.getTiddler(t.links[i]);
if (tid && !tid.isTagged(this.excludeTag))
tids.push(t.links[i]);
}
}
var template=null;
if (fold||co.chkStoryFold) template='CollapsedTemplate'; // see [[CollapseTiddlersPlugin]]
if (!store.tiddlerExists('CollapsedTemplate')) template=null;
if (co.chkStoryClose) story.closeAllTiddlers();
var pos='top'; var first=tids[0];
if (!story.isEmpty() && co.chkStoryBottom) { pos='bottom'; tids=tids.reverse(); }
story.displayTiddlers(pos,tids,template);
var cmd='var t=story.getTiddler("'+first+'");if(t)window.scrollTo(0,t.offsetTop);';
var delay=config.options.chkAnimate?config.animDuration+100:0;
setTimeout(cmd,delay);
},
createButton: function(place,params) {
var tid=params[0]||'';
var label=params[1]||this.label; label=label.format([tid]);
var tip=params[2]||this.prompt; tip=tip.format([tid]);
var fold=(params[3]&&(params[3].toLowerCase()=='fold'))||config.options.chkStoryFold;
var fn=function(){config.macros.openStory.showStory(this.getAttribute('tid'),this.getAttribute('fold')); return false; };
var btn=createTiddlyButton(place,label,tip,fn,'button');
btn.setAttribute('tid',tid);
if (fold) btn.setAttribute('fold',fold);
},
createPopup: function(place,params) {
params.shift(); // discard 'popup' keyword
var label=params.shift()||this.popuplabel;
var tip=params.shift()||this.popupprompt;
var btn=createTiddlyButton(place,label,tip,this.showPopup,'button');
btn.setAttribute('filter',params.shift()||config.macros.openStory.tag);
},
showPopup: function(ev) { var e=ev||window.event;
var co=config.options; // abbrev
var cmo=config.macros.openStory; // abbrev
var indent='\xa0\xa0';
var p=Popup.create(this); if (!p) return false;
createTiddlyText(createTiddlyElement(p,'li'),cmo.selectprompt);
var stories=store.filterTiddlers('[tag['+this.getAttribute('filter')+']]');
for (var s=0; s<stories.length; s++) {
var label=indent+stories[s].title;
var tip=cmo.prompt.format([stories[s].title]);
var fn=function(){config.macros.openStory.showStory(this.getAttribute('tid'));return false;};
var btn=createTiddlyButton(createTiddlyElement(p,'li'),label,tip,fn,'button');
btn.setAttribute('tid',stories[s].title);
}
createTiddlyText(createTiddlyElement(p,'li'),cmo.optionsprompt);
if (store.tiddlerExists('CollapsedTemplate')) {
var label=indent+cmo.foldcmd.format([co.chkStoryFold?'x':'\xa0\xa0']);
var tip=cmo.foldprompt;
var fn=function(){ config.macros.option.propagateOption(
'chkStoryFold','checked',!config.options.chkStoryFold,'input'); return false; };
var btn=createTiddlyButton(createTiddlyElement(p,'li'),label,tip,fn,'button');
}
var label=indent+cmo.closecmd.format([co.chkStoryClose?'x':'\xa0\xa0']);
var tip=indent+cmo.closeprompt;
var fn=function(){ config.macros.option.propagateOption(
'chkStoryClose','checked',!config.options.chkStoryClose,'input'); return false; };
var btn=createTiddlyButton(createTiddlyElement(p,'li'),label,tip,fn,'button');
if (!co.chkStoryClose) {
var label=indent+cmo.topcmd.format([co.chkStoryTop?'x':'\xa0\xa0']);
var tip=indent+cmo.topprompt;
var fn=function(){
config.macros.option.propagateOption(
'chkStoryTop','checked',!config.options.chkStoryTop,'input');
config.macros.option.propagateOption(
'chkStoryBottom','checked',!config.options.chkStoryTop,'input');
return false;
};
var btn=createTiddlyButton(createTiddlyElement(p,'li'),label,tip,fn,'button');
var label=indent+cmo.bottomcmd.format([co.chkStoryBottom?'x':'\xa0\xa0']);
var tip=indent+cmo.botprompt;
var fn=function(){
config.macros.option.propagateOption(
'chkStoryBottom','checked',!config.options.chkStoryBottom,'input');
config.macros.option.propagateOption(
'chkStoryTop','checked',!config.options.chkStoryBottom,'input');
return false;
};
var btn=createTiddlyButton(createTiddlyElement(p,'li'),label,tip,fn,'button');
}
if (!readOnly && co.chkStoryAllowAdd) {
var label=cmo.addcmd;
var tip=cmo.addprompt;
var fn=config.macros.saveStory.setTiddler;
createTiddlyElement(createTiddlyElement(p,'li'),'hr');
var btn=createTiddlyButton(createTiddlyElement(p,'li'),label,tip,fn,'button');
}
Popup.show();
e.cancelBubble=true;if(e.stopPropagation)e.stopPropagation();
return false;
},
createList: function(place,params) {
var cmo=config.macros.openStory; // abbrev
var s=createTiddlyElement(place,'select',null,'storyListbox');
s.size=1;
s.onchange=function() {
if (this.value=='_fold') {
config.macros.option.propagateOption('chkStoryFold','checked',
!config.options.chkStoryFold,'input');
cmo.refreshList();
} else if (this.value=='_close') {
config.macros.option.propagateOption('chkStoryClose','checked',
!config.options.chkStoryClose,'input');
cmo.refreshList();
} else if (this.value=='_top') {
config.macros.option.propagateOption('chkStoryTop','checked',
!config.options.chkStoryTop,'input');
cmo.refreshList();
} else if (this.value=='_bottom') {
config.macros.option.propagateOption('chkStoryBottom','checked',
!config.options.chkStoryBottom,'input');
cmo.refreshList();
} else if (this.value=='_add')
config.macros.saveStory.setTiddler.apply(this,arguments);
else cmo.showStory(this.value);
}
params.shift(); // discard 'list' keyword
s.setAttribute('filter',params.shift()||cmo.tag);
setStylesheet('.storyListbox { width:100%; }', 'StorySaverStyles');
store.addNotification(null,this.refreshList); this.refreshList();
return;
},
refreshList: function() {
var cmo=config.macros.openStory; // abbrev
var indent='\xa0\xa0\xa0\xa0';
var lists=document.getElementsByTagName('select');
for (var i=0; i<lists.length; i++) { if (lists[i].className!='storyListbox') continue;
var here=lists[i];
var stories=store.filterTiddlers('[tag['+here.getAttribute('filter')+']]');
while (here.length) here.options[0]=null; // remove current list items
here.options[here.length]=new Option(cmo.selectprompt,'',true,true);
for (var s=0; s<stories.length; s++)
here.options[here.length]=new Option(indent+stories[s].title,stories[s].title);
if (!readOnly && config.options.chkStoryAllowAdd)
here.options[here.length]=new Option(cmo.addcmd,'_add');
here.options[here.length]=new Option(cmo.optionsprompt,'');
if (store.tiddlerExists('CollapsedTemplate')) {
var msg=cmo.foldcmd.format([config.options.chkStoryFold?'x':'\xa0\xa0']);
here.options[here.length]=new Option(indent+msg,'_fold');
}
var msg=cmo.closecmd.format([config.options.chkStoryClose?'x':'\xa0\xa0']);
here.options[here.length]=new Option(indent+msg,'_close',false,false);
if (!config.options.chkStoryClose) {
var msg=cmo.topcmd.format([config.options.chkStoryTop?'x':'\xa0\xa0']);
here.options[here.length]=new Option(indent+msg,'_top',false,false);
var msg=cmo.bottomcmd.format([config.options.chkStoryBottom?'x':'\xa0\xa0']);
here.options[here.length]=new Option(indent+msg,'_bottom',false,false);
}
}
}
}
//}}}
span.fieldHeader {font-weight: bold}
.tagged {float:right; clear:both;}
.tagging {float: right;}
/***
|Name|TiddlerTweakerPlugin|
|Source|http://www.TiddlyTools.com/#TiddlerTweakerPlugin|
|Version|2.4.5|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|select multiple tiddlers and modify author, created, modified and/or tag values|
~TiddlerTweaker is a 'power tool' for TiddlyWiki authors. Select multiple tiddlers from a listbox and 'bulk modify' the creator, author, created, modified and/or tag values of those tiddlers using a compact set of form fields. The values you enter into the fields simultaneously overwrite the existing values in all tiddlers you have selected.
!!!!!Usage
<<<
{{{<<tiddlerTweaker>>}}}
{{smallform{<<tiddlerTweaker>>}}}
By default, any tags you enter into the TiddlerTweaker will //replace// the existing tags in all the tiddlers you have selected. However, you can also use TiddlerTweaker to quickly filter specified tags from the selected tiddlers, while leaving any other tags assigned to those tiddlers unchanged:
>Any tag preceded by a '+' (plus) or '-' (minus), will be added or removed from the existing tags //instead of replacing the entire tag definition// of each tiddler (e.g., enter '-excludeLists' to remove that tag from all selected tiddlers. When using this syntax, care should be taken to ensure that //every// tag is preceded by '+' or '-', to avoid inadvertently overwriting any other existing tags on the selected tiddlers. (note: the '+' or '-' prefix on each tag value is NOT part of the tag value, and is only used by TiddlerTweaker to control how that tag value is processed)
Important Notes:
* TiddlerTweaker is a 'power user' tool that can make changes to many tiddlers at once. ''You should always have a recent backup of your document (or 'save changes' just *before* tweaking the tiddlers), just in case you accidentally 'shoot yourself in the foot'.''
* The date and author information on any tiddlers you tweak will ONLY be updated if the corresponding checkboxes have been selected. As a general rule, after using TiddlerTweaker, always ''//remember to save your document//'' when you are done, even though the tiddler timeline tab may not show any recently modified tiddlers.
* Selecting and updating all tiddlers in a document can take a while. Your browser may warn about an 'unresponsive script'. Usually, if you allow it to continue, it should complete the processing... eventually. Nonetheless, be sure to save your work before you begin tweaking lots of tiddlers, just in case something does get stuck.
<<<
!!!!!Revisions
<<<
2011.01.21 2.4.5 auto-selection: use "-" for untagged tiddlers. Also, added 'opened', 'invert'
2009.09.15 2.4.4 added 'edit' button. moved html definition to separate section
2009.09.13 2.4.3 in settiddlers(), convert backslashed chars (\n\b\s\t) in replacement text
2009.06.26 2.4.2 only add brackets around tags containing spaces
2009.06.22 2.4.1 in setFields(), add brackets around all tags shown tweaker edit field
2009.03.30 2.4.0 added 'sort by modifier'
2009.01.22 2.3.0 added support for text pattern find/replace
2008.10.27 2.2.3 in setTiddlers(), fixed Safari bug by replacing static Array.concat(...) with new Array().concat(...)
2008.09.07 2.2.2 added removeCookie() function for compatibility with [[CookieManagerPlugin]]
2008.05.12 2.2.1 replace built-in backstage tweak task with tiddler tweaker control panel (moved from BackstageTweaks)
2008.01.13 2.2.0 added 'auto-selection' links: all, changed, tags, title, text
2007.12.26 2.1.0 added support for managing 'creator' custom field (see [[CoreTweaks]])
2007.11.01 2.0.3 added config.options.txtTweakerSortBy for cookie-based persistence of list display order preference setting.
2007.09.28 2.0.2 in settiddlers() and deltiddlers(), added suspend/resume notification handling (improves performance when operating on multiple tiddlers)
2007.08.03 2.0.1 added shadow definition for [[TiddlerTweaker]] tiddler for use as parameter references with {{{<<tiddler>>, <<slider>> or <<tabs>>}}} macros.
2007.08.03 2.0.0 converted from inline script
2006.01.01 1.0.0 initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TiddlerTweakerPlugin= {major: 2, minor: 4, revision: 5, date: new Date(2011,1,21)};
// shadow tiddler
config.shadowTiddlers.TiddlerTweaker='<<tiddlerTweaker>>';
// defaults
if (config.options.txtTweakerSortBy==undefined) config.options.txtTweakerSortBy='modified';
// backstage task
if (config.tasks) { // for TW2.2b3 or above
config.tasks.tweak.tooltip='review/modify tiddler internals: dates, authors, tags, etc.';
config.tasks.tweak.content='{{smallform small groupbox{<<tiddlerTweaker>>}}}';
}
// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
window.removeCookie=function(name) {
document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
}
config.macros.tiddlerTweaker = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var span=createTiddlyElement(place,'span');
span.innerHTML=store.getTiddlerText('TiddlerTweakerPlugin##html');
this.init(span.getElementsByTagName('form')[0],config.options.txtTweakerSortBy);
},
init: function(f,sortby) { // set form controls
if (!f) return; // form might not be rendered yet...
while (f.list.options[0]) f.list.options[0]=null; // empty the list
var tids=store.getTiddlers(sortby);
if (sortby=='size') // descending order
tids.sort(function(a,b) {return a.text.length > b.text.length ? -1 : (a.text.length == b.text.length ? 0 : +1);});
var who='';
for (i=0; i<tids.length; i++) { var t=tids[i];
var label=t.title; var value=t.title;
switch (sortby) {
case 'modified':
case 'created':
var t=tids[tids.length-i-1]; // reverse order
var when=t[sortby].formatString('YY.0MM.0DD 0hh:0mm ');
label=when+t.title;
value=t.title;
break;
case 'size':
label='['+t.text.length+'] '+label;
break;
case 'modifier':
case 'creator':
if (who!=t[sortby]) {
who=t[sortby];
f.list.options[f.list.length]=new Option('by '+who+':','',false,false);
}
label='\xa0\xa0\xa0'+label; // indent
break;
}
f.list.options[f.list.length]=new Option(label,value,false,false);
}
f.title.value=f.who.value=f.creator.value=f.tags.value='';
f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value='';
f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value='';
f.stats.disabled=f.set.disabled=f.del.disabled=f.edit.disabled=f.display.disabled=true;
f.settitle.disabled=false;
config.options.txtTweakerSortBy=sortby;
f.sortby.value=sortby; // sync droplist
if (sortby!='modified') saveOptionCookie('txtTweakerSortBy');
else removeCookie('txtTweakerSortBy');
},
enablefields: function(here) { // enables/disables inputs based on #items selected
var f=here.form; var list=f.list;
var c=0; for (i=0;i<list.length;i++) if (list.options[i].selected) c++;
if (c>1) f.title.disabled=true;
if (c>1) f.settitle.checked=false;
f.set.disabled=(c==0);
f.del.disabled=(c==0);
f.edit.disabled=(c==0);
f.display.disabled=(c==0);
f.settitle.disabled=(c>1);
f.stats.disabled=(c==0);
var msg=(c==0)?'select tiddlers':(c+' tiddler'+(c!=1?'s':'')+' selected');
here.previousSibling.firstChild.firstChild.nextSibling.innerHTML=msg;
if (c) clearMessage(); else displayMessage('no tiddlers selected');
},
setfields: function(here) { // set fields from first selected tiddler
var f=here.form;
if (!here.value.length) {
f.title.value=f.who.value=f.creator.value=f.tags.value='';
f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value='';
f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value='';
return;
}
var tid=store.getTiddler(here.value); if (!tid) return;
f.title.value=tid.title;
f.who.value=tid.modifier;
f.creator.value=tid.fields['creator']||''; // custom field - might not exist
f.tags.value=tid.tags.map(function(t){return String.encodeTiddlyLink(t)}).join(' ');
var c=tid.created; var m=tid.modified;
f.cm.value=c.getMonth()+1;
f.cd.value=c.getDate();
f.cy.value=c.getFullYear();
f.ch.value=c.getHours();
f.cn.value=c.getMinutes();
f.mm.value=m.getMonth()+1;
f.md.value=m.getDate();
f.my.value=m.getFullYear();
f.mh.value=m.getHours();
f.mn.value=m.getMinutes();
},
selecttiddlers: function(here,callback) {
var f=here; while (f&&f.nodeName.toLowerCase()!='form')f=f.parentNode;
for (var t=f.list.options.length-1; t>=0; t--)
f.list.options[t].selected=callback(f.list.options[t]);
config.macros.tiddlerTweaker.enablefields(f.list);
return false;
},
settiddlers: function(here) {
var f=here.form; var list=f.list;
var tids=[];
for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert('please select at least one tiddler'); return; }
var cdate=new Date(f.cy.value,f.cm.value-1,f.cd.value,f.ch.value,f.cn.value);
var mdate=new Date(f.my.value,f.mm.value-1,f.md.value,f.mh.value,f.mn.value);
if (tids.length>1 && !confirm('Are you sure you want to update these tiddlers:\n\n'+tids.join(', '))) return;
store.suspendNotifications();
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
var title=!f.settitle.checked?tid.title:f.title.value;
var who=!f.setwho.checked?tid.modifier:f.who.value;
var text=tid.text;
if (f.replacetext.checked) {
var r=f.replacement.value.replace(/\\t/mg,'\t').unescapeLineBreaks();
text=text.replace(new RegExp(f.pattern.value,'mg'),r);
}
var tags=tid.tags;
if (f.settags.checked) {
var intags=f.tags.value.readBracketedList();
var addtags=[]; var deltags=[]; var reptags=[];
for (i=0;i<intags.length;i++) {
if (intags[i].substr(0,1)=='+')
addtags.push(intags[i].substr(1));
else if (intags[i].substr(0,1)=='-')
deltags.push(intags[i].substr(1));
else
reptags.push(intags[i]);
}
if (reptags.length)
tags=reptags;
if (addtags.length)
tags=new Array().concat(tags,addtags);
if (deltags.length)
for (i=0;i<deltags.length;i++)
{ var pos=tags.indexOf(deltags[i]); if (pos!=-1) tags.splice(pos,1); }
}
if (!f.setcdate.checked) cdate=tid.created;
if (!f.setmdate.checked) mdate=tid.modified;
store.saveTiddler(tid.title,title,text,who,mdate,tags,tid.fields);
if (f.setcreator.checked) store.setValue(tid.title,'creator',f.creator.value); // set creator
if (f.setcdate.checked) tid.assign(null,null,null,null,null,cdate); // set create date
}
store.resumeNotifications();
this.init(f,f.sortby.value);
},
displaytiddlers: function(here,edit) {
var f=here.form; var list=f.list;
var tids=[];
for (i=0; i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert('please select at least one tiddler'); return; }
story.displayTiddlers(story.findContainingTiddler(f),tids,edit?DEFAULT_EDIT_TEMPLATE:null);
},
deltiddlers: function(here) {
var f=here.form; var list=f.list;
var tids=[];
for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert('please select at least one tiddler'); return; }
if (!confirm('Are you sure you want to delete these tiddlers:\n\n'+tids.join(', '))) return;
store.suspendNotifications();
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
if (tid.tags.contains('systemConfig')) {
var msg=tid.title+' is tagged with systemConfig.'
+'\n\nRemoving this tiddler may cause unexpected results. Are you sure?';
if (!confirm(msg)) continue;
}
store.removeTiddler(tid.title);
story.closeTiddler(tid.title);
}
store.resumeNotifications();
this.init(f,f.sortby.value);
},
stats: function(here) {
var f=here.form; var list=f.list; var tids=[]; var out=''; var tot=0;
var target=f.nextSibling;
for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert('please select at least one tiddler'); return; }
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
out+='[['+tid.title+']] '+tid.text.length+'\n'; tot+=tid.text.length;
}
var avg=tot/tids.length;
out=tot+' bytes in '+tids.length+' selected tiddlers ('+avg+' bytes/tiddler)\n<<<\n'+out+'<<<\n';
removeChildren(target);
target.innerHTML="<hr><font size=-2><a href='javascript:;' style='float:right' "
+"onclick='this.parentNode.parentNode.style.display=\"none\"'>close</a></font>";
wikify(out,target);
target.style.display='block';
}
};
//}}}
/***
//{{{
!html
<style>
.tiddlerTweaker table,
.tiddlerTweaker table tr,
.tiddlerTweaker table td
{ padding:0;margin:0;border:0;white-space:nowrap; }
</style><form class='tiddlerTweaker'><!--
--><table style="width:100%"><tr valign="top"><!--
--><td style="text-align:center;width:99%;"><!--
--><font size=-2><div style="text-align:left;"><span style="float:right"><!--
--> <a href="javascript:;"
title="select all tiddlers"
onclick="return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
return opt.value.length;
});">all</a><!--
--> <a href="javascript:;"
title="select tiddlers currently displayed in the story column"
onclick="return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
return story.getTiddler(opt.value);
});">opened</a><!--
--> <a href="javascript:;"
title="select tiddlers that are new/changed since the last file save"
onclick="var lastmod=new Date(document.lastModified);
return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
var tid=store.getTiddler(opt.value);
return tid&&tid.modified>lastmod;
});
">changed</a><!--
--> <a href="javascript:;"
title="select tiddlers with at least one matching tag"
onclick="var t=prompt('Enter space-separated tags (match one or more). Use \x22-\x22 to match untagged tiddlers');
if (!t||!t.length) return false;
var tags=t.readBracketedList();
return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
var tid=store.getTiddler(opt.value);
return tid&&tags[0]=='-'?!tid.tags.length:tid.tags.containsAny(tags);
});
">tags</a><!--
--> <a href="javascript:;"
title="select tiddlers whose titles include matching text"
onclick="var t=prompt('Enter a title (or portion of a title) to match');
if (!t||!t.length) return false;
return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
return opt.value.indexOf(t)!=-1;
});
">titles</a><!--
--> <a href="javascript:;"
title="select tiddlers containing matching text"
onclick="var t=prompt('Enter tiddler text (content) to match');
if (!t||!t.length) return false;
return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
var tt=store.getTiddlerText(opt.value,'');
return tt.indexOf(t)!=-1;
});
">text</a><!--
--> <a href="javascript:;"
title="reverse selection of all list items"
onclick="return config.macros.tiddlerTweaker.selecttiddlers(this,function(opt){
return !opt.selected;
});">invert</a><!--
--></span><span>select tiddlers</span><!--
--></div><!--
--></font><select multiple name=list size="11" style="width:99.99%"
title="use click, shift-click and/or ctrl-click to select multiple tiddler titles"
onclick="config.macros.tiddlerTweaker.enablefields(this)"
onchange="config.macros.tiddlerTweaker.setfields(this)"><!--
--></select><br><!--
-->show<input type=text size=1 value="11"
onchange="this.form.list.size=this.value; this.form.list.multiple=(this.value>1);"><!--
-->by<!--
--><select name=sortby size=1
onchange="config.macros.tiddlerTweaker.init(this.form,this.value)"><!--
--><option value="title">title</option><!--
--><option value="size">size</option><!--
--><option value="modified">modified</option><!--
--><option value="created">created</option><!--
--><option value="modifier">modifier</option><!--
--></select><!--
--><input type="button" value="refresh"
onclick="config.macros.tiddlerTweaker.init(this.form,this.form.sortby.value)"<!--
--> <input type="button" name="stats" disabled value="totals..."
onclick="config.macros.tiddlerTweaker.stats(this)"><!--
--></td><td style="width:1%"><!--
--><div style="text-align:left"><font size=-2> modify values</font></div><!--
--><table style="width:100%;"><tr><!--
--><td style="padding:1px"><!--
--><input type=checkbox name=settitle unchecked
title="allow changes to tiddler title (rename tiddler)"
onclick="this.form.title.disabled=!this.checked">title<!--
--></td><td style="padding:1px"><!--
--><input type=text name=title size=35 style="width:98%" disabled><!--
--></td></tr><tr><td style="padding:1px"><!--
--><input type=checkbox name=setcreator unchecked
title="allow changes to tiddler creator"
onclick="this.form.creator.disabled=!this.checked">created by<!--
--></td><td style="padding:1px;"><!--
--><input type=text name=creator size=35 style="width:98%" disabled><!--
--></td></tr><tr><td style="padding:1px"><!--
--><input type=checkbox name=setwho unchecked
title="allow changes to tiddler author"
onclick="this.form.who.disabled=!this.checked">modified by<!--
--></td><td style="padding:1px"><!--
--><input type=text name=who size=35 style="width:98%" disabled><!--
--></td></tr><tr><td style="padding:1px"><!--
--><input type=checkbox name=setcdate unchecked
title="allow changes to created date"
onclick="var f=this.form;
f.cm.disabled=f.cd.disabled=f.cy.disabled=f.ch.disabled=f.cn.disabled=!this.checked"><!--
-->created on<!--
--></td><td style="padding:1px"><!--
--><input type=text name=cm size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--> / <input type=text name=cd size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--> / <input type=text name=cy size=4 style="width:3em;padding:0;text-align:center" disabled><!--
--> at <input type=text name=ch size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--> : <input type=text name=cn size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--></td></tr><tr><td style="padding:1px"><!--
--><input type=checkbox name=setmdate unchecked
title="allow changes to modified date"
onclick="var f=this.form;
f.mm.disabled=f.md.disabled=f.my.disabled=f.mh.disabled=f.mn.disabled=!this.checked"><!--
-->modified on<!--
--></td><td style="padding:1px"><!--
--><input type=text name=mm size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--> / <input type=text name=md size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--> / <input type=text name=my size=4 style="width:3em;padding:0;text-align:center" disabled><!--
--> at <input type=text name=mh size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--> : <input type=text name=mn size=2 style="width:2em;padding:0;text-align:center" disabled><!--
--></td></tr><tr><td style="padding:1px"><!--
--><input type=checkbox name=replacetext unchecked
title="find/replace matching text"
onclick="this.form.pattern.disabled=this.form.replacement.disabled=!this.checked">replace text<!--
--></td><td style="padding:1px"><!--
--><input type=text name=pattern size=15 value="" style="width:40%" disabled
title="enter TEXT PATTERN (regular expression)"> with<!--
--><input type=text name=replacement size=15 value="" style="width:40%" disabled
title="enter REPLACEMENT TEXT"><!--
--></td></tr><tr><td style="padding:1px"><!--
--><input type=checkbox name=settags checked
title="allow changes to tiddler tags"
onclick="this.form.tags.disabled=!this.checked">tags<!--
--></td><td style="padding:1px"><!--
--><input type=text name=tags size=35 value="" style="width:98%"
title="enter new tags or use '+tag' and '-tag' to add/remove tags from existing tags"><!--
--></td></tr></table><!--
--><div style="text-align:center"><!--
--><nobr><input type=button name=display disabled style="width:24%" value="display"
title="show selected tiddlers"
onclick="config.macros.tiddlerTweaker.displaytiddlers(this,false)"><!--
--> <input type=button name=edit disabled style="width:23%" value="edit"
title="edit selected tiddlers"
onclick="config.macros.tiddlerTweaker.displaytiddlers(this,true)"><!--
--> <input type=button name=del disabled style="width:24%" value="delete"
title="remove selected tiddlers"
onclick="config.macros.tiddlerTweaker.deltiddlers(this)"><!--
--> <input type=button name=set disabled style="width:24%" value="update"
title="update selected tiddlers"
onclick="config.macros.tiddlerTweaker.settiddlers(this)"></nobr><!--
--></div><!--
--></td></tr></table><!--
--></form><span style="display:none"><!--content replaced by tiddler "stats"--></span>
!end
//}}}
***/
<!--{{{-->
<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='tagged' macro='tags'></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Summary</span>: <span macro='view summary'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Description</span>: <span macro='view description'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Date</span>: <span macro='view datesubmitted'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Status</span>: <span macro='view status'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Priority</span>: <span macro='view priority'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Category</span>: <span macro='view category'></span></div>
<div macro="showWhenTagged bug"><span class="fieldHeader">Type</span>: <span macro='view type'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">File</span>: <span macro='view file wikified'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Revision</span>: <span macro='view revision wikified'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Date</span>: <span macro='view date'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Origin Class</span>: <span macro='view originclass'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">TA</span>: <span macro='view taresult'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">TA Class</span>: <span macro='view ta'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">DA</span>: <span macro='view daresult'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">DA Class</span>: <span macro='view da'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Origin</span>: <span macro='view origin'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Born</span>: <span macro='view born'></span></div>
<div macro="showWhenTagged commit"><span class="fieldHeader">Age</span>: <span macro='view age'></span></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->