//
// Please note:
//
// * This code is designed to be readable but for compactness it only includes brief comments. You can see fuller comments
// in the project repository at https://github.com/TiddlyWiki/tiddlywiki
//
// * You should never need to modify this source code directly. TiddlyWiki is carefully designed to allow deep customisation
// without changing the core code. Please consult the development group at http://groups.google.com/group/TiddlyWikiDev
//
// JSLint directives
/*global jQuery:false, version:false */
/*jslint bitwise:true, browser:true, confusion:true, eqeq:true, evil:true, forin:true, maxerr:100, plusplus:true, regexp:true, sloppy:true, sub:true, undef:true, unparam:true, vars:true, white:true */
//--
//-- Configuration repository
//--
// Miscellaneous options
var config = {
numRssItems: 20, // Number of items in the RSS feed
animDuration: 400, // Duration of UI animations in milliseconds
cascadeFast: 20, // Speed for cascade animations (higher == slower)
cascadeSlow: 60, // Speed for EasterEgg cascade animations
cascadeDepth: 5, // Depth of cascade animation
locale: "en" // W3C language tag
};
// Hashmap of alternative parsers for the wikifier
config.parsers = {};
// Adaptors
config.adaptors = {};
config.defaultAdaptor = null;
// Backstage tasks
config.tasks = {};
// Annotations
config.annotations = {};
// Custom fields to be automatically added to new tiddlers
config.defaultCustomFields = {};
// Messages
config.messages = {
messageClose: {},
dates: {},
tiddlerPopup: {}
};
// Options that can be set in the options panel and/or cookies
config.options = {
chkRegExpSearch: false,
chkCaseSensitiveSearch: false,
chkIncrementalSearch: true,
chkAnimate: true,
chkSaveBackups: true,
chkAutoSave: false,
chkGenerateAnRssFeed: false,
chkSaveEmptyTemplate: false,
chkOpenInNewWindow: true,
chkToggleLinks: false,
chkHttpReadOnly: false,
chkForceMinorUpdate: false,
chkConfirmDelete: true,
chkInsertTabs: false,
chkUsePreForStorage: true, // Whether to use <pre> format for storage
chkDisplayInstrumentation: true,
txtBackupFolder: "",
txtEditorFocus: "text",
txtMainTab: "tabTimeline",
txtMoreTab: "moreTabAll",
txtMaxEditRows: "30",
txtFileSystemCharSet: "UTF-8",
txtTheme: ""
};
config.optionsDesc = {};
config.optionsSource = {};
// Default tiddler templates
var DEFAULT_VIEW_TEMPLATE = 1;
var DEFAULT_EDIT_TEMPLATE = 2;
config.tiddlerTemplates = {
1: "ViewTemplate",
2: "EditTemplate"
};
// More messages (rather a legacy layout that should not really be like this)
config.views = {
wikified: {
tag: {}
},
editor: {
tagChooser: {}
}
};
// Backstage tasks
config.backstageTasks = ["save","importTask","tweak","plugins"];
// Extensions
config.extensions = {};
// Macros; each has a 'handler' member that is inserted later
config.macros = {
today: {},
version: {},
search: {sizeTextbox: 15},
tiddler: {},
tag: {},
tags: {},
tagging: {},
timeline: {},
allTags: {},
list: {
all: {},
missing: {},
orphans: {},
shadowed: {},
touched: {},
filter: {}
},
closeAll: {},
permaview: {},
saveChanges: {},
slider: {},
option: {},
options: {},
newTiddler: {},
newJournal: {},
tabs: {},
gradient: {},
message: {},
view: {defaultView: "text"},
edit: {},
tagChooser: {},
toolbar: {},
plugins: {},
refreshDisplay: {},
importTiddlers: {},
upgrade: {
source: "http://tiddlywiki-releases.tiddlyspace.com/upgrade",
backupExtension: "pre.core.upgrade"
},
sync: {},
annotations: {}
};
// Commands supported by the toolbar macro
config.commands = {
closeTiddler: {},
closeOthers: {},
editTiddler: {},
saveTiddler: {hideReadOnly: true},
cancelTiddler: {},
deleteTiddler: {hideReadOnly: true},
permalink: {},
references: {type: "popup"},
jump: {type: "popup"},
syncing: {type: "popup"},
fields: {type: "popup"}
};
// Control of macro parameter evaluation
config.evaluateMacroParameters = "all";
// Basic regular expressions
config.textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de\u0150\u0170]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff\u0151\u0171]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff\u0150\u0170\u0151\u0171]"
};
if(!((new RegExp("[\u0150\u0170]","g")).test("\u0150"))) {
config.textPrimitives = {
upperLetter: "[A-Z\u00c0-\u00de]",
lowerLetter: "[a-z0-9_\\-\u00df-\u00ff]",
anyLetter: "[A-Za-z0-9_\\-\u00c0-\u00de\u00df-\u00ff]",
anyLetterStrict: "[A-Za-z0-9\u00c0-\u00de\u00df-\u00ff]"
};
}
config.textPrimitives.sliceSeparator = "::";
config.textPrimitives.sectionSeparator = "##";
config.textPrimitives.urlPattern = "(?:file|http|https|mailto|ftp|irc|news|data):[^\\s'\"]+(?:/|\\b)";
config.textPrimitives.unWikiLink = "~";
config.textPrimitives.wikiLink = "(?:(?:" + config.textPrimitives.upperLetter + "+" +
config.textPrimitives.lowerLetter + "+" +
config.textPrimitives.upperLetter +
config.textPrimitives.anyLetter + "*)|(?:" +
config.textPrimitives.upperLetter + "{2,}" +
config.textPrimitives.lowerLetter + "+))";
config.textPrimitives.cssLookahead = "(?:(" + config.textPrimitives.anyLetter + "+)\\(([^\\)\\|\\n]+)(?:\\):))|(?:(" + config.textPrimitives.anyLetter + "+):([^;\\|\\n]+);)";
config.textPrimitives.cssLookaheadRegExp = new RegExp(config.textPrimitives.cssLookahead,"mg");
config.textPrimitives.brackettedLink = "\\[\\[([^\\]]+)\\]\\]";
config.textPrimitives.titledBrackettedLink = "\\[\\[([^\\[\\]\\|]+)\\|([^\\[\\]\\|]+)\\]\\]";
config.textPrimitives.tiddlerForcedLinkRegExp = new RegExp("(?:" + config.textPrimitives.titledBrackettedLink + ")|(?:" +
config.textPrimitives.brackettedLink + ")|(?:" +
config.textPrimitives.urlPattern + ")","mg");
config.textPrimitives.tiddlerAnyLinkRegExp = new RegExp("("+ config.textPrimitives.wikiLink + ")|(?:" +
config.textPrimitives.titledBrackettedLink + ")|(?:" +
config.textPrimitives.brackettedLink + ")|(?:" +
config.textPrimitives.urlPattern + ")","mg");
config.glyphs = {
currBrowser: null,
browsers: [],
codes: {}
};
//--
//-- Shadow tiddlers
//--
config.shadowTiddlers = {
StyleSheet: "",
MarkupPreHead: "",
MarkupPostHead: "",
MarkupPreBody: "",
MarkupPostBody: "",
TabTimeline: '<<timeline>>',
TabAll: '<<list all>>',
TabTags: '<<allTags excludeLists>>',
TabMoreMissing: '<<list missing>>',
TabMoreOrphans: '<<list orphans>>',
TabMoreShadowed: '<<list shadowed>>',
AdvancedOptions: '<<options>>',
PluginManager: '<<plugins>>',
SystemSettings: '',
ToolbarCommands: '|~ViewToolbar|closeTiddler closeOthers +editTiddler > fields syncing permalink references jump|\n|~EditToolbar|+saveTiddler -cancelTiddler deleteTiddler|',
WindowTitle: '<<tiddler SiteTitle>> - <<tiddler SiteSubtitle>>'
};
// Browser detection... In a very few places, there's nothing else for it but to know what browser we're using.
config.userAgent = navigator.userAgent.toLowerCase();
config.browser = {
isIE: config.userAgent.indexOf("msie") != -1 && config.userAgent.indexOf("opera") == -1,
isGecko: navigator.product == "Gecko" && config.userAgent.indexOf("WebKit") == -1,
ieVersion: /MSIE (\d.\d)/i.exec(config.userAgent), // config.browser.ieVersion[1], if it exists, will be the IE version string, eg "6.0"
isSafari: config.userAgent.indexOf("applewebkit") != -1,
isBadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150")),
firefoxDate: /gecko\/(\d{8})/i.exec(config.userAgent), // config.browser.firefoxDate[1], if it exists, will be Firefox release date as "YYYYMMDD"
isOpera: config.userAgent.indexOf("opera") != -1,
isChrome: config.userAgent.indexOf('chrome') > -1,
isLinux: config.userAgent.indexOf("linux") != -1,
isUnix: config.userAgent.indexOf("x11") != -1,
isMac: config.userAgent.indexOf("mac") != -1,
isWindows: config.userAgent.indexOf("win") != -1
};
merge(config.glyphs,{
browsers: [
function() {return config.browser.isIE;},
function() {return true;}
],
codes: {
downTriangle: ["\u25BC","\u25BE"],
downArrow: ["\u2193","\u2193"],
bentArrowLeft: ["\u2190","\u21A9"],
bentArrowRight: ["\u2192","\u21AA"]
}
});
//--
//-- Translateable strings
//--
// Strings in "double quotes" should be translated; strings in 'single quotes' should be left alone
merge(config.options,{
txtUserName: "YourName"});
merge(config.tasks,{
save: {text: "save", tooltip: "Save your changes to this TiddlyWiki"},
importTask: {text: "import", tooltip: "Import tiddlers and plugins from other TiddlyWiki files and servers", content: '<<importTiddlers>>'},
tweak: {text: "tweak", tooltip: "Tweak the appearance and behaviour of TiddlyWiki", content: '<<options>>'},
upgrade: {text: "upgrade", tooltip: "Upgrade TiddlyWiki core code", content: '<<upgrade>>'},
plugins: {text: "plugins", tooltip: "Manage installed plugins", content: '<<plugins>>'}
});
// Options that can be set in the options panel and/or cookies
merge(config.optionsDesc,{
txtUserName: "Username for signing your edits",
chkRegExpSearch: "Enable regular expressions for searches",
chkCaseSensitiveSearch: "Case-sensitive searching",
chkIncrementalSearch: "Incremental key-by-key searching",
chkAnimate: "Enable animations",
chkSaveBackups: "Keep backup file when saving changes",
chkAutoSave: "Automatically save changes",
chkGenerateAnRssFeed: "Generate an RSS feed when saving changes",
chkSaveEmptyTemplate: "Generate an empty template when saving changes",
chkOpenInNewWindow: "Open external links in a new window",
chkToggleLinks: "Clicking on links to open tiddlers causes them to close",
chkHttpReadOnly: "Hide editing features when viewed over HTTP",
chkForceMinorUpdate: "Don't update modifier username and date when editing tiddlers",
chkConfirmDelete: "Require confirmation before deleting tiddlers",
chkInsertTabs: "Use the tab key to insert tab characters instead of moving between fields",
txtBackupFolder: "Name of folder to use for backups",
txtMaxEditRows: "Maximum number of rows in edit boxes",
txtTheme: "Name of the theme to use",
txtFileSystemCharSet: "Default character set for saving changes (Firefox/Mozilla only)"});
merge(config.messages,{
customConfigError: "Problems were encountered loading plugins. See PluginManager for details",
pluginError: "Error: %0",
pluginDisabled: "Not executed because disabled via 'systemConfigDisable' tag",
pluginForced: "Executed because forced via 'systemConfigForce' tag",
pluginVersionError: "Not executed because this plugin needs a newer version of TiddlyWiki",
nothingSelected: "Nothing is selected. You must select one or more items first",
savedSnapshotError: "It appears that this TiddlyWiki has been incorrectly saved. Please see http://www.tiddlywiki.com/#Download for details",
subtitleUnknown: "(unknown)",
undefinedTiddlerToolTip: "The tiddler '%0' doesn't yet exist",
shadowedTiddlerToolTip: "The tiddler '%0' doesn't yet exist, but has a pre-defined shadow value",
tiddlerLinkTooltip: "%0 - %1, %2",
externalLinkTooltip: "External link to %0",
noTags: "There are no tagged tiddlers",
notFileUrlError: "You need to save this TiddlyWiki to a file before you can save changes",
cantSaveError: "It's not possible to save changes. Possible reasons include:\n- your browser doesn't support saving (Firefox, Internet Explorer, Safari and Opera all work if properly configured)\n- the pathname to your TiddlyWiki file contains illegal characters\n- the TiddlyWiki HTML file has been moved or renamed",
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to save backup file",
rssSaved: "RSS feed saved",
rssFailed: "Failed to save RSS feed file",
emptySaved: "Empty template saved",
emptyFailed: "Failed to save empty template file",
mainSaved: "Main TiddlyWiki file saved",
mainFailed: "Failed to save main TiddlyWiki file. Your changes have not been saved",
macroError: "Error in macro <<%0>>",
macroErrorDetails: "Error while executing macro <<%0>>:\n%1",
missingMacro: "No such macro",
overwriteWarning: "A tiddler named '%0' already exists. Choose OK to overwrite it",
unsavedChangesWarning: "WARNING! There are unsaved changes in TiddlyWiki\n\nChoose OK to save\nChoose CANCEL to discard",
confirmExit: "--------------------------------\n\nThere are unsaved changes in TiddlyWiki. If you continue you will lose those changes\n\n--------------------------------",
saveInstructions: "SaveChanges",
unsupportedTWFormat: "Unsupported TiddlyWiki format '%0'",
tiddlerSaveError: "Error when saving tiddler '%0'",
tiddlerLoadError: "Error when loading tiddler '%0'",
wrongSaveFormat: "Cannot save with storage format '%0'. Using standard format for save.",
invalidFieldName: "Invalid field name %0",
fieldCannotBeChanged: "Field '%0' cannot be changed",
loadingMissingTiddler: "Attempting to retrieve the tiddler '%0' from the '%1' server at:\n\n'%2' in the workspace '%3'",
upgradeDone: "The upgrade to version %0 is now complete\n\nClick 'OK' to reload the newly upgraded TiddlyWiki",
invalidCookie: "Invalid cookie '%0'"});
merge(config.messages.messageClose,{
text: "close",
tooltip: "close this message area"});
config.messages.backstage = {
open: {text: "backstage", tooltip: "Open the backstage area to perform authoring and editing tasks"},
close: {text: "close", tooltip: "Close the backstage area"},
prompt: "backstage: ",
decal: {
edit: {text: "edit", tooltip: "Edit the tiddler '%0'"}
}
};
config.messages.listView = {
tiddlerTooltip: "Click for the full text of this tiddler",
previewUnavailable: "(preview not available)"
};
config.messages.dates.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"];
config.messages.dates.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
config.messages.dates.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
config.messages.dates.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
// suffixes for dates, eg "1st","2nd","3rd"..."30th","31st"
config.messages.dates.daySuffixes = ["st","nd","rd","th","th","th","th","th","th","th",
"th","th","th","th","th","th","th","th","th","th",
"st","nd","rd","th","th","th","th","th","th","th",
"st"];
config.messages.dates.am = "am";
config.messages.dates.pm = "pm";
merge(config.messages.tiddlerPopup,{
});
merge(config.views.wikified.tag,{
labelNoTags: "no tags",
labelTags: "tags: ",
openTag: "Open tag '%0'",
tooltip: "Show tiddlers tagged with '%0'",
openAllText: "Open all",
openAllTooltip: "Open all of these tiddlers",
popupNone: "No other tiddlers tagged with '%0'"});
merge(config.views.wikified,{
defaultText: "The tiddler '%0' doesn't yet exist. Double-click to create it",
defaultModifier: "(missing)",
shadowModifier: "(built-in shadow tiddler)",
dateFormat: "DD MMM YYYY",
createdPrompt: "created"});
merge(config.views.editor,{
tagPrompt: "Type tags separated with spaces, [[use double square brackets]] if necessary, or add existing",
defaultText: "Type the text for '%0'"});
merge(config.views.editor.tagChooser,{
text: "tags",
tooltip: "Choose existing tags to add to this tiddler",
popupNone: "There are no tags defined",
tagTooltip: "Add the tag '%0'"});
merge(config.messages,{
sizeTemplates:
[
{unit: 1024*1024*1024, template: "%0\u00a0GB"},
{unit: 1024*1024, template: "%0\u00a0MB"},
{unit: 1024, template: "%0\u00a0KB"},
{unit: 1, template: "%0\u00a0B"}
]});
merge(config.macros.search,{
label: "search",
prompt: "Search this TiddlyWiki",
placeholder: "",
accessKey: "F",
successMsg: "%0 tiddlers found matching %1",
failureMsg: "No tiddlers found matching %0"});
merge(config.macros.tagging,{
label: "tagging: ",
labelNotTag: "not tagging",
tooltip: "List of tiddlers tagged with '%0'"});
merge(config.macros.timeline,{
dateFormat: "DD MMM YYYY"});
merge(config.macros.allTags,{
tooltip: "Show tiddlers tagged with '%0'",
noTags: "There are no tagged tiddlers"});
config.macros.list.all.prompt = "All tiddlers in alphabetical order";
config.macros.list.missing.prompt = "Tiddlers that have links to them but are not defined";
config.macros.list.orphans.prompt = "Tiddlers that are not linked to from any other tiddlers";
config.macros.list.shadowed.prompt = "Tiddlers shadowed with default contents";
config.macros.list.touched.prompt = "Tiddlers that have been modified locally";
merge(config.macros.closeAll,{
label: "close all",
prompt: "Close all displayed tiddlers (except any that are being edited)"});
merge(config.macros.permaview,{
label: "permaview",
prompt: "Link to an URL that retrieves all the currently displayed tiddlers"});
merge(config.macros.saveChanges,{
label: "save changes",
prompt: "Save all tiddlers to create a new TiddlyWiki",
accessKey: "S"});
merge(config.macros.newTiddler,{
label: "new tiddler",
prompt: "Create a new tiddler",
title: "New Tiddler",
accessKey: "N"});
merge(config.macros.newJournal,{
label: "new journal",
prompt: "Create a new tiddler from the current date and time",
accessKey: "J"});
merge(config.macros.options,{
wizardTitle: "Tweak advanced options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='false' name='chkUnknown'>Show unknown options</input>",
unknownDescription: "//(unknown)//",
listViewTemplate: {
columns: [
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
});
merge(config.macros.plugins,{
wizardTitle: "Manage plugins",
step1Title: "Currently loaded plugins",
step1Html: "<input type='hidden' name='markList'></input>", // DO NOT TRANSLATE
skippedText: "(This plugin has not been executed because it was added since startup)",
noPluginText: "There are no plugins installed",
confirmDeleteText: "Are you sure you want to delete these plugins:\n\n%0",
removeLabel: "remove systemConfig tag",
removePrompt: "Remove systemConfig tag",
deleteLabel: "delete",
deletePrompt: "Delete these tiddlers forever",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Description', field: 'Description', title: "Description", type: 'String'},
{name: 'Version', field: 'Version', title: "Version", type: 'String'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Forced', field: 'forced', title: "Forced", tag: 'systemConfigForce', type: 'TagCheckbox'},
{name: 'Disabled', field: 'disabled', title: "Disabled", tag: 'systemConfigDisable', type: 'TagCheckbox'},
{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
],
rowClasses: [
{className: 'error', field: 'error'},
{className: 'warning', field: 'warning'}
]},
listViewTemplateReadOnly: {
columns: [
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Description', field: 'Description', title: "Description", type: 'String'},
{name: 'Version', field: 'Version', title: "Version", type: 'String'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Executed', field: 'executed', title: "Loaded", type: 'Boolean', trueText: "Yes", falseText: "No"},
{name: 'Startup Time', field: 'startupTime', title: "Startup Time", type: 'String'},
{name: 'Error', field: 'error', title: "Status", type: 'Boolean', trueText: "Error", falseText: "OK"},
{name: 'Log', field: 'log', title: "Log", type: 'StringList'}
],
rowClasses: [
{className: 'error', field: 'error'},
{className: 'warning', field: 'warning'}
]}
});
merge(config.macros.toolbar,{
moreLabel: "more",
morePrompt: "Show additional commands",
lessLabel: "less",
lessPrompt: "Hide additional commands",
separator: "|"
});
merge(config.macros.refreshDisplay,{
label: "refresh",
prompt: "Redraw the entire TiddlyWiki display"
});
merge(config.macros.importTiddlers,{
readOnlyWarning: "You cannot import into a read-only TiddlyWiki file. Try opening it from a file:// URL",
wizardTitle: "Import tiddlers from another file or server",
step1Title: "Step 1: Locate the server or TiddlyWiki file",
step1Html: "Specify the type of the server: <select name='selTypes'><option value=''>Choose...</option></select><br>Enter the URL or pathname here: <input type='text' size=50 name='txtPath'><br>...or browse for a file: <input type='file' size=50 name='txtBrowse'><br><hr>...or select a pre-defined feed: <select name='selFeeds'><option value=''>Choose...</option></select>",
openLabel: "open",
openPrompt: "Open the connection to this file or server",
statusOpenHost: "Opening the host",
statusGetWorkspaceList: "Getting the list of available workspaces",
step2Title: "Step 2: Choose the workspace",
step2Html: "Enter a workspace name: <input type='text' size=50 name='txtWorkspace'><br>...or select a workspace: <select name='selWorkspace'><option value=''>Choose...</option></select>",
cancelLabel: "cancel",
cancelPrompt: "Cancel this import",
statusOpenWorkspace: "Opening the workspace",
statusGetTiddlerList: "Getting the list of available tiddlers",
errorGettingTiddlerList: "Error getting list of tiddlers, click Cancel to try again",
errorGettingTiddlerListHttp404: "Error retrieving tiddlers from url, please ensure the url exists. Click Cancel to try again.",
errorGettingTiddlerListHttp: "Error retrieving tiddlers from url, please ensure this url exists and is <a href='http://enable-cors.org/'>CORS</a> enabled",
errorGettingTiddlerListFile: "Error retrieving tiddlers from local file, please make sure the file is in the same directory as your TiddlyWiki. Click Cancel to try again.",
step3Title: "Step 3: Choose the tiddlers to import",
step3Html: "<input type='hidden' name='markList'></input><br><input type='checkbox' checked='true' name='chkSync'>Keep these tiddlers linked to this server so that you can synchronise subsequent changes</input><br><input type='checkbox' name='chkSave'>Save the details of this server in a 'systemServer' tiddler called:</input> <input type='text' size=25 name='txtSaveTiddler'>",
importLabel: "import",
importPrompt: "Import these tiddlers",
confirmOverwriteText: "Are you sure you want to overwrite these tiddlers:\n\n%0",
step4Title: "Step 4: Importing %0 tiddler(s)",
step4Html: "<input type='hidden' name='markReport'></input>", // DO NOT TRANSLATE
doneLabel: "done",
donePrompt: "Close this wizard",
statusDoingImport: "Importing tiddlers",
statusDoneImport: "All tiddlers imported",
systemServerNamePattern: "%2 on %1",
systemServerNamePatternNoWorkspace: "%1",
confirmOverwriteSaveTiddler: "The tiddler '%0' already exists. Click 'OK' to overwrite it with the details of this server, or 'Cancel' to leave it unchanged",
serverSaveTemplate: "|''Type:''|%0|\n|''URL:''|%1|\n|''Workspace:''|%2|\n\nThis tiddler was automatically created to record the details of this server",
serverSaveModifier: "(System)",
listViewTemplate: {
columns: [
{name: 'Selected', field: 'Selected', rowName: 'title', type: 'Selector'},
{name: 'Tiddler', field: 'tiddler', title: "Tiddler", type: 'Tiddler'},
{name: 'Size', field: 'size', tiddlerLink: 'size', title: "Size", type: 'Size'},
{name: 'Tags', field: 'tags', title: "Tags", type: 'Tags'}
],
rowClasses: [
]}
});
merge(config.macros.upgrade,{
wizardTitle: "Upgrade TiddlyWiki core code",
step1Title: "Update or repair this TiddlyWiki to the latest release",
step1Html: "You are about to upgrade to the latest release of the TiddlyWiki core code (from <a href='%0' class='externalLink' target='_blank'>%1</a>). Your content will be preserved across the upgrade.<br><br>Note that core upgrades have been known to interfere with older plugins. If you run into problems with the upgraded file, see <a href='http://www.tiddlywiki.org/wiki/CoreUpgrades' class='externalLink' target='_blank'>http://www.tiddlywiki.org/wiki/CoreUpgrades</a>",
errorCantUpgrade: "Unable to upgrade this TiddlyWiki. You can only perform upgrades on TiddlyWiki files stored locally",
errorNotSaved: "You must save changes before you can perform an upgrade",
step2Title: "Confirm the upgrade details",
step2Html_downgrade: "You are about to downgrade to TiddlyWiki version %0 from %1.<br><br>Downgrading to an earlier version of the core code is not recommended",
step2Html_restore: "This TiddlyWiki appears to be already using the latest version of the core code (%0).<br><br>You can continue to upgrade anyway to ensure that the core code hasn't been corrupted or damaged",
step2Html_upgrade: "You are about to upgrade to TiddlyWiki version %0 from %1",
upgradeLabel: "upgrade",
upgradePrompt: "Prepare for the upgrade process",
statusPreparingBackup: "Preparing backup",
statusSavingBackup: "Saving backup file",
errorSavingBackup: "There was a problem saving the backup file",
statusLoadingCore: "Loading core code",
errorLoadingCore: "Error loading the core code",
errorCoreFormat: "Error with the new core code",
statusSavingCore: "Saving the new core code",
statusReloadingCore: "Reloading the new core code",
startLabel: "start",
startPrompt: "Start the upgrade process",
cancelLabel: "cancel",
cancelPrompt: "Cancel the upgrade process",
step3Title: "Upgrade cancelled",
step3Html: "You have cancelled the upgrade process"
});
merge(config.macros.annotations,{
});
merge(config.commands.closeTiddler,{
text: "close",
tooltip: "Close this tiddler"});
merge(config.commands.closeOthers,{
text: "close others",
tooltip: "Close all other tiddlers"});
merge(config.commands.editTiddler,{
text: "edit",
tooltip: "Edit this tiddler",
readOnlyText: "view",
readOnlyTooltip: "View the source of this tiddler"});
merge(config.commands.saveTiddler,{
text: "done",
tooltip: "Save changes to this tiddler"});
merge(config.commands.cancelTiddler,{
text: "cancel",
tooltip: "Undo changes to this tiddler",
warning: "Are you sure you want to abandon your changes to '%0'?",
readOnlyText: "done",
readOnlyTooltip: "View this tiddler normally"});
merge(config.commands.deleteTiddler,{
text: "delete",
tooltip: "Delete this tiddler",
warning: "Are you sure you want to delete '%0'?"});
merge(config.commands.permalink,{
text: "permalink",
tooltip: "Permalink for this tiddler"});
merge(config.commands.references,{
text: "references",
tooltip: "Show tiddlers that link to this one",
popupNone: "No references"});
merge(config.commands.jump,{
text: "jump",
tooltip: "Jump to another open tiddler"});
merge(config.commands.fields,{
text: "fields",
tooltip: "Show the extended fields of this tiddler",
emptyText: "There are no extended fields for this tiddler",
listViewTemplate: {
columns: [
{name: 'Field', field: 'field', title: "Field", type: 'String'},
{name: 'Value', field: 'value', title: "Value", type: 'String'}
],
rowClasses: [
],
buttons: [
]}});
merge(config.shadowTiddlers,{
DefaultTiddlers: "[[GettingStarted]]",
MainMenu: "[[GettingStarted]]",
SiteTitle: "My TiddlyWiki",
SiteSubtitle: "a reusable non-linear personal web notebook",
SiteUrl: "",
SideBarOptions: '<<search>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options \u00bb" "Change TiddlyWiki advanced options">>',
SideBarTabs: '<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore>>',
TabMore: '<<tabs txtMoreTab "Missing" "Missing tiddlers" TabMoreMissing "Orphans" "Orphaned tiddlers" TabMoreOrphans "Shadowed" "Shadowed tiddlers" TabMoreShadowed>>'
});
merge(config.annotations,{
AdvancedOptions: "This shadow tiddler provides access to several advanced options",
ColorPalette: "These values in this shadow tiddler determine the colour scheme of the ~TiddlyWiki user interface",
DefaultTiddlers: "The tiddlers listed in this shadow tiddler will be automatically displayed when ~TiddlyWiki starts up",
EditTemplate: "The HTML template in this shadow tiddler determines how tiddlers look while they are being edited",
GettingStarted: "This shadow tiddler provides basic usage instructions",
ImportTiddlers: "This shadow tiddler provides access to importing tiddlers",
MainMenu: "This shadow tiddler is used as the contents of the main menu in the left-hand column of the screen",
MarkupPreHead: "This tiddler is inserted at the top of the <head> section of the TiddlyWiki HTML file",
MarkupPostHead: "This tiddler is inserted at the bottom of the <head> section of the TiddlyWiki HTML file",
MarkupPreBody: "This tiddler is inserted at the top of the <body> section of the TiddlyWiki HTML file",
MarkupPostBody: "This tiddler is inserted at the end of the <body> section of the TiddlyWiki HTML file immediately after the script block",
OptionsPanel: "This shadow tiddler is used as the contents of the options panel slider in the right-hand sidebar",
PageTemplate: "The HTML template in this shadow tiddler determines the overall ~TiddlyWiki layout",
PluginManager: "This shadow tiddler provides access to the plugin manager",
SideBarOptions: "This shadow tiddler is used as the contents of the option panel in the right-hand sidebar",
SideBarTabs: "This shadow tiddler is used as the contents of the tabs panel in the right-hand sidebar",
SiteSubtitle: "This shadow tiddler is used as the second part of the page title",
SiteTitle: "This shadow tiddler is used as the first part of the page title",
SiteUrl: "This shadow tiddler should be set to the full target URL for publication",
StyleSheetColors: "This shadow tiddler contains CSS definitions related to the color of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler",
StyleSheet: "This tiddler can contain custom CSS definitions",
StyleSheetLayout: "This shadow tiddler contains CSS definitions related to the layout of page elements. ''DO NOT EDIT THIS TIDDLER'', instead make your changes in the StyleSheet shadow tiddler",
StyleSheetLocale: "This shadow tiddler contains CSS definitions related to the translation locale",
StyleSheetPrint: "This shadow tiddler contains CSS definitions for printing",
SystemSettings: "This tiddler is used to store configuration options for this TiddlyWiki document",
TabAll: "This shadow tiddler contains the contents of the 'All' tab in the right-hand sidebar",
TabMore: "This shadow tiddler contains the contents of the 'More' tab in the right-hand sidebar",
TabMoreMissing: "This shadow tiddler contains the contents of the 'Missing' tab in the right-hand sidebar",
TabMoreOrphans: "This shadow tiddler contains the contents of the 'Orphans' tab in the right-hand sidebar",
TabMoreShadowed: "This shadow tiddler contains the contents of the 'Shadowed' tab in the right-hand sidebar",
TabTags: "This shadow tiddler contains the contents of the 'Tags' tab in the right-hand sidebar",
TabTimeline: "This shadow tiddler contains the contents of the 'Timeline' tab in the right-hand sidebar",
ToolbarCommands: "This shadow tiddler determines which commands are shown in tiddler toolbars",
ViewTemplate: "The HTML template in this shadow tiddler determines how tiddlers look"
});
//--
//-- Paramifiers
//--
function getParameters()
{
var p = null;
if(window.location.hash) {
p = decodeURIComponent(window.location.hash.substr(1));
if(config.browser.firefoxDate != null && config.browser.firefoxDate[1] < "20051111")
p = convertUTF8ToUnicode(p);
}
return p;
}
function invokeParamifier(params,handler)
{
if(!params || params.length == undefined || params.length <= 1)
return;
var i;
for(i=1; i<params.length; i++) {
var p = config.paramifiers[params[i].name];
if(p && p[handler] instanceof Function)
p[handler](params[i].value);
else {
var h = config.optionHandlers[params[i].name.substr(0,3)];
if(h && h.set instanceof Function)
h.set(params[i].name,params[i].value);
}
}
}
config.paramifiers = {};
config.paramifiers.start = {
oninit: function(v) {
safeMode = v.toLowerCase() == "safe";
}
};
config.paramifiers.open = {
onstart: function(v) {
if(!readOnly || store.tiddlerExists(v) || store.isShadowTiddler(v))
story.displayTiddler("bottom",v,null,false,null);
}
};
config.paramifiers.story = {
onstart: function(v) {
var list = store.getTiddlerText(v,"").parseParams("open",null,false);
invokeParamifier(list,"onstart");
}
};
config.paramifiers.search = {
onstart: function(v) {
story.search(v,false,false);
}
};
config.paramifiers.searchRegExp = {
onstart: function(v) {
story.prototype.search(v,false,true);
}
};
config.paramifiers.tag = {
onstart: function(v) {
story.displayTiddlers(null,store.filterTiddlers("[tag["+v+"]]"),null,false,null);
}
};
config.paramifiers.newTiddler = {
onstart: function(v) {
var args = v.parseParams("anon", null, null)[0];
var title = args.title ? args.title[0] : v;
var customFields = args.fields ? args.fields[0] : null;
if(!readOnly) {
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,customFields);
story.focusTiddler(title,"text");
var i,tags = args.tag || [];
for(i=0;i<tags.length;i++) {
story.setTiddlerTag(title,tags[i],+1);
}
}
}
};
config.paramifiers.newJournal = {
onstart: function(v) {
if(!readOnly) {
var now = new Date();
var title = now.formatString(v.trim());
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
story.focusTiddler(title,"text");
}
}
};
config.paramifiers.readOnly = {
onconfig: function(v) {
var p = v.toLowerCase();
readOnly = p == "yes" ? true : (p == "no" ? false : readOnly);
}
};
config.paramifiers.theme = {
onconfig: function(v) {
story.switchTheme(v);
}
};
config.paramifiers.upgrade = {
onstart: function(v) {
upgradeFrom(v);
}
};
config.paramifiers.recent= {
onstart: function(v) {
var titles=[];
var i,tiddlers=store.getTiddlers("modified","excludeLists").reverse();
for(i=0; i<v && i<tiddlers.length; i++)
titles.push(tiddlers[i].title);
story.displayTiddlers(null,titles);
}
};
config.paramifiers.filter = {
onstart: function(v) {
story.displayTiddlers(null,store.filterTiddlers(v),null,false);
}
};
//--
//-- Formatter helpers
//--
function Formatter(formatters)
{
var n;
this.formatters = [];
var pattern = [];
for(n=0; n<formatters.length; n++) {
pattern.push("(" + formatters[n].match + ")");
this.formatters.push(formatters[n]);
}
this.formatterRegExp = new RegExp(pattern.join("|"),"mg");
}
config.formatterHelpers = {
createElementAndWikify: function(w)
{
w.subWikifyTerm(createTiddlyElement(w.output,this.element),this.termRegExp);
},
inlineCssHelper: function(w)
{
var styles = [];
config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
var s,v;
if(lookaheadMatch[1]) {
s = lookaheadMatch[1].unDash();
v = lookaheadMatch[2];
} else {
s = lookaheadMatch[3].unDash();
v = lookaheadMatch[4];
}
if(s=="bgcolor")
s = "backgroundColor";
if(s=="float")
s = "cssFloat";
styles.push({style: s, value: v});
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
config.textPrimitives.cssLookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = config.textPrimitives.cssLookaheadRegExp.exec(w.source);
}
return styles;
},
applyCssHelper: function(e,styles)
{
var t;
for(t=0; t< styles.length; t++) {
try {
e.style[styles[t].style] = styles[t].value;
} catch (ex) {
}
}
},
enclosedTextHelper: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var text = lookaheadMatch[1];
if(config.browser.isIE)
text = text.replace(/\n/g,"\r");
createTiddlyElement(w.output,this.element,null,null,text);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
},
isExternalLink: function(link)
{
if(store.tiddlerExists(link) || store.isShadowTiddler(link)) {
return false;
}
var urlRegExp = new RegExp(config.textPrimitives.urlPattern,"mg");
if(urlRegExp.exec(link)) {
return true;
}
if(link.indexOf(".")!=-1 || link.indexOf("\\")!=-1 || link.indexOf("/")!=-1 || link.indexOf("#")!=-1) {
return true;
}
return false;
}
};
//--
//-- Standard formatters
//--
config.formatters = [
{
name: "table",
match: "^\\|(?:[^\\n]*)\\|(?:[fhck]?)$",
lookaheadRegExp: /^\|([^\n]*)\|([fhck]?)$/mg,
rowTermRegExp: /(\|(?:[fhck]?)$\n?)/mg,
cellRegExp: /(?:\|([^\n\|]*)\|)|(\|[fhck]?$\n?)/mg,
cellTermRegExp: /((?:\x20*)\|)/mg,
rowTypes: {"c":"caption", "h":"thead", "":"tbody", "f":"tfoot"},
handler: function(w)
{
var table = createTiddlyElement(w.output,"table",null,"twtable");
var prevColumns = [];
var currRowType = null;
var rowContainer;
var rowCount = 0;
var onmouseover = function() {jQuery(this).addClass("hoverRow");};
var onmouseout = function() {jQuery(this).removeClass("hoverRow");};
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
var nextRowType = lookaheadMatch[2];
if(nextRowType == "k") {
table.className = lookaheadMatch[1];
w.nextMatch += lookaheadMatch[0].length+1;
} else {
if(nextRowType != currRowType) {
rowContainer = createTiddlyElement(table,this.rowTypes[nextRowType]);
currRowType = nextRowType;
}
if(currRowType == "c") {
// Caption
w.nextMatch++;
if(rowContainer != table.firstChild)
table.insertBefore(rowContainer,table.firstChild);
rowContainer.setAttribute("align",rowCount == 0?"top":"bottom");
w.subWikifyTerm(rowContainer,this.rowTermRegExp);
} else {
var theRow = createTiddlyElement(rowContainer,"tr",null,rowCount%2?"oddRow":"evenRow");
theRow.onmouseover = onmouseover;
theRow.onmouseout = onmouseout;
this.rowHandler(w,theRow,prevColumns);
rowCount++;
}
}
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
},
rowHandler: function(w,e,prevColumns)
{
var col = 0;
var colSpanCount = 1;
var prevCell = null;
this.cellRegExp.lastIndex = w.nextMatch;
var cellMatch = this.cellRegExp.exec(w.source);
while(cellMatch && cellMatch.index == w.nextMatch) {
if(cellMatch[1] == "~") {
// Rowspan
var last = prevColumns[col];
if(last) {
last.rowSpanCount++;
last.element.setAttribute("rowspan",last.rowSpanCount);
last.element.setAttribute("rowSpan",last.rowSpanCount); // Needed for IE
last.element.valign = "center";
if(colSpanCount > 1) {
last.element.setAttribute("colspan",colSpanCount);
last.element.setAttribute("colSpan",colSpanCount); // Needed for IE
colSpanCount = 1;
}
}
w.nextMatch = this.cellRegExp.lastIndex-1;
} else if(cellMatch[1] == ">") {
// Colspan
colSpanCount++;
w.nextMatch = this.cellRegExp.lastIndex-1;
} else if(cellMatch[2]) {
// End of row
if(prevCell && colSpanCount > 1) {
prevCell.setAttribute("colspan",colSpanCount);
prevCell.setAttribute("colSpan",colSpanCount); // Needed for IE
}
w.nextMatch = this.cellRegExp.lastIndex;
break;
} else {
// Cell
w.nextMatch++;
var styles = config.formatterHelpers.inlineCssHelper(w);
var spaceLeft = false;
var chr = w.source.substr(w.nextMatch,1);
while(chr == " ") {
spaceLeft = true;
w.nextMatch++;
chr = w.source.substr(w.nextMatch,1);
}
var cell;
if(chr == "!") {
cell = createTiddlyElement(e,"th");
w.nextMatch++;
} else {
cell = createTiddlyElement(e,"td");
}
prevCell = cell;
prevColumns[col] = {rowSpanCount:1,element:cell};
if(colSpanCount > 1) {
cell.setAttribute("colspan",colSpanCount);
cell.setAttribute("colSpan",colSpanCount); // Needed for IE
colSpanCount = 1;
}
config.formatterHelpers.applyCssHelper(cell,styles);
w.subWikifyTerm(cell,this.cellTermRegExp);
if(w.matchText.substr(w.matchText.length-2,1) == " ") // spaceRight
cell.align = spaceLeft ? "center" : "left";
else if(spaceLeft)
cell.align = "right";
w.nextMatch--;
}
col++;
this.cellRegExp.lastIndex = w.nextMatch;
cellMatch = this.cellRegExp.exec(w.source);
}
}
},
{
name: "heading",
match: "^!{1,6}",
termRegExp: /(\n)/mg,
handler: function(w)
{
w.subWikifyTerm(createTiddlyElement(w.output,"h" + w.matchLength),this.termRegExp);
}
},
{
name: "list",
match: "^(?:[\\*#;:]+)",
lookaheadRegExp: /^(?:(?:(\*)|(#)|(;)|(:))+)/mg,
termRegExp: /(\n)/mg,
handler: function(w)
{
var stack = [w.output];
var currLevel = 0, currType = null;
var listLevel, listType, itemType, baseType;
w.nextMatch = w.matchStart;
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
while(lookaheadMatch && lookaheadMatch.index == w.nextMatch) {
if(lookaheadMatch[1]) {
listType = "ul";
itemType = "li";
} else if(lookaheadMatch[2]) {
listType = "ol";
itemType = "li";
} else if(lookaheadMatch[3]) {
listType = "dl";
itemType = "dt";
} else if(lookaheadMatch[4]) {
listType = "dl";
itemType = "dd";
}
if(!baseType)
baseType = listType;
listLevel = lookaheadMatch[0].length;
w.nextMatch += lookaheadMatch[0].length;
var t;
if(listLevel > currLevel) {
for(t=currLevel; t<listLevel; t++) {
var target = (currLevel == 0) ? stack[stack.length-1] : stack[stack.length-1].lastChild;
stack.push(createTiddlyElement(target,listType));
}
} else if(listType!=baseType && listLevel==1) {
w.nextMatch -= lookaheadMatch[0].length;
return;
} else if(listLevel < currLevel) {
for(t=currLevel; t>listLevel; t--)
stack.pop();
} else if(listLevel == currLevel && listType != currType) {
stack.pop();
stack.push(createTiddlyElement(stack[stack.length-1].lastChild,listType));
}
currLevel = listLevel;
currType = listType;
var e = createTiddlyElement(stack[stack.length-1],itemType);
w.subWikifyTerm(e,this.termRegExp);
this.lookaheadRegExp.lastIndex = w.nextMatch;
lookaheadMatch = this.lookaheadRegExp.exec(w.source);
}
}
},
{
name: "quoteByBlock",
match: "^<<<\\n",
termRegExp: /(^<<<(\n|$))/mg,
element: "blockquote",
handler: config.formatterHelpers.createElementAndWikify
},
{
name: "quoteByLine",
match: "^>+",
lookaheadRegExp: /^>+/mg,
termRegExp: /(\n)/mg,
element: "blockquote",
handler: function(w)
{
var stack = [w.output];
var currLevel = 0;
var newLevel = w.matchLength;
var t,matched;
do {
if(newLevel > currLevel) {
for(t=currLevel; t<newLevel; t++)
stack.push(createTiddlyElement(stack[stack.length-1],this.element));
} else if(newLevel < currLevel) {
for(t=currLevel; t>newLevel; t--)
stack.pop();
}
currLevel = newLevel;
w.subWikifyTerm(stack[stack.length-1],this.termRegExp);
createTiddlyElement(stack[stack.length-1],"br");
this.lookaheadRegExp.lastIndex = w.nextMatch;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
matched = lookaheadMatch && lookaheadMatch.index == w.nextMatch;
if(matched) {
newLevel = lookaheadMatch[0].length;
w.nextMatch += lookaheadMatch[0].length;
}
} while(matched);
}
},
{
name: "rule",
match: "^----+$\\n?|<hr ?/?>\\n?",
handler: function(w)
{
createTiddlyElement(w.output,"hr");
}
},
{
name: "monospacedByLine",
match: "^(?:/\\*\\{\\{\\{\\*/|\\{\\{\\{|//\\{\\{\\{|<!--\\{\\{\\{-->)\\n",
element: "pre",
handler: function(w)
{
switch(w.matchText) {
case "/*{{{*/\n": // CSS
this.lookaheadRegExp = /\/\*\{\{\{\*\/\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\*\}\}\}\*\/$\n?)/mg;
break;
case "{{{\n": // monospaced block
this.lookaheadRegExp = /^\{\{\{\n((?:^[^\n]*\n)+?)(^\f*\}\}\}$\n?)/mg;
break;
case "//{{{\n": // plugin
this.lookaheadRegExp = /^\/\/\{\{\{\n\n*((?:^[^\n]*\n)+?)(\n*^\f*\/\/\}\}\}$\n?)/mg;
break;
case "<!--{{{-->\n": //template
this.lookaheadRegExp = /<!--\{\{\{-->\n*((?:^[^\n]*\n)+?)(\n*^\f*<!--\}\}\}-->$\n?)/mg;
break;
default:
break;
}
config.formatterHelpers.enclosedTextHelper.call(this,w);
}
},
{
name: "wikifyComment",
match: "^(?:/\\*\\*\\*|<!---)\\n",
handler: function(w)
{
var termRegExp = (w.matchText == "/***\n") ? (/(^\*\*\*\/\n)/mg) : (/(^--->\n)/mg);
w.subWikifyTerm(w.output,termRegExp);
}
},
{
name: "macro",
match: "<<",
lookaheadRegExp: /<<([^>\s]+)(?:\s*)((?:[^>]|(?:>(?!>)))*)>>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart && lookaheadMatch[1]) {
w.nextMatch = this.lookaheadRegExp.lastIndex;
invokeMacro(w.output,lookaheadMatch[1],lookaheadMatch[2],w,w.tiddler);
}
}
},
{
name: "prettyLink",
match: "\\[\\[",
lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\]/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e;
var text = lookaheadMatch[1];
if(lookaheadMatch[3]) {
// Pretty bracketted link
var link = lookaheadMatch[3];
e = (!lookaheadMatch[2] && config.formatterHelpers.isExternalLink(link)) ?
createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
} else {
// Simple bracketted link
e = createTiddlyLink(w.output,text,false,null,w.isStatic,w.tiddler);
}
createTiddlyText(e,text);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "wikiLink",
match: config.textPrimitives.unWikiLink+"?"+config.textPrimitives.wikiLink,
handler: function(w)
{
if(w.matchText.substr(0,1) == config.textPrimitives.unWikiLink) {
w.outputText(w.output,w.matchStart+1,w.nextMatch);
return;
}
if(w.matchStart > 0) {
var preRegExp = new RegExp(config.textPrimitives.anyLetterStrict,"mg");
preRegExp.lastIndex = w.matchStart-1;
var preMatch = preRegExp.exec(w.source);
if(preMatch.index == w.matchStart-1) {
w.outputText(w.output,w.matchStart,w.nextMatch);
return;
}
}
if(w.autoLinkWikiWords || store.isShadowTiddler(w.matchText)) {
var link = createTiddlyLink(w.output,w.matchText,false,null,w.isStatic,w.tiddler);
w.outputText(link,w.matchStart,w.nextMatch);
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
}
},
{
name: "urlLink",
match: config.textPrimitives.urlPattern,
handler: function(w)
{
w.outputText(createExternalLink(w.output,w.matchText),w.matchStart,w.nextMatch);
}
},
{
name: "image",
match: "\\[[<>]?[Ii][Mm][Gg]\\[",
lookaheadRegExp: /\[([<]?)(>?)[Ii][Mm][Gg]\[(?:([^\|\]]+)\|)?([^\[\]\|]+)\](?:\[([^\]]*)\])?\]/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
var e = w.output;
if(lookaheadMatch[5]) {
var link = lookaheadMatch[5];
e = config.formatterHelpers.isExternalLink(link) ? createExternalLink(w.output,link) : createTiddlyLink(w.output,link,false,null,w.isStatic,w.tiddler);
jQuery(e).addClass("imageLink");
}
var img = createTiddlyElement(e,"img");
if(lookaheadMatch[1])
img.align = "left";
else if(lookaheadMatch[2])
img.align = "right";
if(lookaheadMatch[3]) {
img.title = lookaheadMatch[3];
img.setAttribute("alt",lookaheadMatch[3]);
}
img.src = lookaheadMatch[4];
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "html",
match: "<[Hh][Tt][Mm][Ll]>",
lookaheadRegExp: /<[Hh][Tt][Mm][Ll]>((?:.|\n)*?)<\/[Hh][Tt][Mm][Ll]>/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"span").innerHTML = lookaheadMatch[1];
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "commentByBlock",
match: "/%",
lookaheadRegExp: /\/%((?:.|\n)*?)%\//mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart)
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
},
{
name: "characterFormat",
match: "''|//|__|\\^\\^|~~|--(?!\\s|$)|\\{\\{\\{",
handler: function(w)
{
switch(w.matchText) {
case "''":
w.subWikifyTerm(w.output.appendChild(document.createElement("strong")),/('')/mg);
break;
case "//":
w.subWikifyTerm(createTiddlyElement(w.output,"em"),/(\/\/)/mg);
break;
case "__":
w.subWikifyTerm(createTiddlyElement(w.output,"u"),/(__)/mg);
break;
case "^^":
w.subWikifyTerm(createTiddlyElement(w.output,"sup"),/(\^\^)/mg);
break;
case "~~":
w.subWikifyTerm(createTiddlyElement(w.output,"sub"),/(~~)/mg);
break;
case "--":
w.subWikifyTerm(createTiddlyElement(w.output,"strike"),/(--)/mg);
break;
case "{{{":
var lookaheadRegExp = /\{\{\{((?:.|\n)*?)\}\}\}/mg;
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"code",null,null,lookaheadMatch[1]);
w.nextMatch = lookaheadRegExp.lastIndex;
}
break;
}
}
},
{
name: "customFormat",
match: "@@|\\{\\{",
handler: function(w)
{
switch(w.matchText) {
case "@@":
var e = createTiddlyElement(w.output,"span");
var styles = config.formatterHelpers.inlineCssHelper(w);
if(styles.length == 0)
e.className = "marked";
else
config.formatterHelpers.applyCssHelper(e,styles);
w.subWikifyTerm(e,/(@@)/mg);
break;
case "{{":
var lookaheadRegExp = /\{\{[\s]*([\w]+[\s\w]*)[\s]*\{(\n?)/mg;
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source);
if(lookaheadMatch) {
w.nextMatch = lookaheadRegExp.lastIndex;
e = createTiddlyElement(w.output,lookaheadMatch[2] == "\n" ? "div" : "span",null,lookaheadMatch[1]);
w.subWikifyTerm(e,/(\}\}\})/mg);
}
break;
}
}
},
{
name: "mdash",
match: "--",
handler: function(w)
{
createTiddlyElement(w.output,"span").innerHTML = "—";
}
},
{
name: "lineBreak",
match: "\\n|<br ?/?>",
handler: function(w)
{
createTiddlyElement(w.output,"br");
}
},
{
name: "rawText",
match: "\"{3}|<nowiki>",
lookaheadRegExp: /(?:\"{3}|<nowiki>)((?:.|\n)*?)(?:\"{3}|<\/nowiki>)/mg,
handler: function(w)
{
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
createTiddlyElement(w.output,"span",null,null,lookaheadMatch[1]);
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
},
{
name: "htmlEntitiesEncoding",
match: "(?:(?:&#?[a-zA-Z0-9]{2,8};|.)(?:&#?(?:x0*(?:3[0-6][0-9a-fA-F]|1D[c-fC-F][0-9a-fA-F]|20[d-fD-F][0-9a-fA-F]|FE2[0-9a-fA-F])|0*(?:76[89]|7[7-9][0-9]|8[0-7][0-9]|761[6-9]|76[2-7][0-9]|84[0-3][0-9]|844[0-7]|6505[6-9]|6506[0-9]|6507[0-1]));)+|&#?[a-zA-Z0-9]{2,8};)",
handler: function(w)
{
createTiddlyElement(w.output,"span").innerHTML = w.matchText;
}
}
];
//--
//-- Wikifier
//--
function getParser(tiddler,format)
{
if(tiddler) {
if(!format)
format = tiddler.fields["wikiformat"];
var i;
if(format) {
for(i in config.parsers) {
if(format == config.parsers[i].format)
return config.parsers[i];
}
} else {
for(i in config.parsers) {
if(tiddler.isTagged(config.parsers[i].formatTag))
return config.parsers[i];
}
}
}
return formatter;
}
function Wikifier(source,formatter,highlightRegExp,tiddler)
{
this.source = source;
this.output = null;
this.formatter = formatter;
this.nextMatch = 0;
this.autoLinkWikiWords = tiddler && tiddler.autoLinkWikiWords() == false ? false : true;
this.highlightRegExp = highlightRegExp;
this.highlightMatch = null;
this.isStatic = false;
if(highlightRegExp) {
highlightRegExp.lastIndex = 0;
this.highlightMatch = highlightRegExp.exec(source);
}
this.tiddler = tiddler;
}
Wikifier.prototype.wikifyPlain = function()
{
var e = createTiddlyElement(document.body,"div");
e.style.display = "none";
this.subWikify(e);
var text = jQuery(e).text();
jQuery(e).remove();
return text;
};
Wikifier.prototype.subWikify = function(output,terminator)
{
try {
if(terminator)
this.subWikifyTerm(output,new RegExp("(" + terminator + ")","mg"));
else
this.subWikifyUnterm(output);
} catch(ex) {
showException(ex);
}
};
Wikifier.prototype.subWikifyUnterm = function(output)
{
var oldOutput = this.output;
this.output = output;
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
var formatterMatch = this.formatter.formatterRegExp.exec(this.source);
while(formatterMatch) {
// Output any text before the match
if(formatterMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,formatterMatch.index);
// Set the match parameters for the handler
this.matchStart = formatterMatch.index;
this.matchLength = formatterMatch[0].length;
this.matchText = formatterMatch[0];
this.nextMatch = this.formatter.formatterRegExp.lastIndex;
var t;
for(t=1; t<formatterMatch.length; t++) {
if(formatterMatch[t]) {
this.formatter.formatters[t-1].handler(this);
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
break;
}
}
formatterMatch = this.formatter.formatterRegExp.exec(this.source);
}
if(this.nextMatch < this.source.length) {
this.outputText(this.output,this.nextMatch,this.source.length);
this.nextMatch = this.source.length;
}
this.output = oldOutput;
};
Wikifier.prototype.subWikifyTerm = function(output,terminatorRegExp)
{
var oldOutput = this.output;
this.output = output;
terminatorRegExp.lastIndex = this.nextMatch;
var terminatorMatch = terminatorRegExp.exec(this.source);
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
var formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
while(terminatorMatch || formatterMatch) {
if(terminatorMatch && (!formatterMatch || terminatorMatch.index <= formatterMatch.index)) {
if(terminatorMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,terminatorMatch.index);
this.matchText = terminatorMatch[1];
this.matchLength = terminatorMatch[1].length;
this.matchStart = terminatorMatch.index;
this.nextMatch = this.matchStart + this.matchLength;
this.output = oldOutput;
return;
}
if(formatterMatch.index > this.nextMatch)
this.outputText(this.output,this.nextMatch,formatterMatch.index);
this.matchStart = formatterMatch.index;
this.matchLength = formatterMatch[0].length;
this.matchText = formatterMatch[0];
this.nextMatch = this.formatter.formatterRegExp.lastIndex;
var t;
for(t=1; t<formatterMatch.length; t++) {
if(formatterMatch[t]) {
this.formatter.formatters[t-1].handler(this);
this.formatter.formatterRegExp.lastIndex = this.nextMatch;
break;
}
}
terminatorRegExp.lastIndex = this.nextMatch;
terminatorMatch = terminatorRegExp.exec(this.source);
formatterMatch = this.formatter.formatterRegExp.exec(terminatorMatch ? this.source.substr(0,terminatorMatch.index) : this.source);
}
if(this.nextMatch < this.source.length) {
this.outputText(this.output,this.nextMatch,this.source.length);
this.nextMatch = this.source.length;
}
this.output = oldOutput;
};
Wikifier.prototype.outputText = function(place,startPos,endPos)
{
while(this.highlightMatch && (this.highlightRegExp.lastIndex > startPos) && (this.highlightMatch.index < endPos) && (startPos < endPos)) {
if(this.highlightMatch.index > startPos) {
createTiddlyText(place,this.source.substring(startPos,this.highlightMatch.index));
startPos = this.highlightMatch.index;
}
var highlightEnd = Math.min(this.highlightRegExp.lastIndex,endPos);
createTiddlyElement(place,"span",null,"highlight",this.source.substring(startPos,highlightEnd));
startPos = highlightEnd;
if(startPos >= this.highlightRegExp.lastIndex)
this.highlightMatch = this.highlightRegExp.exec(this.source);
}
if(startPos < endPos) {
createTiddlyText(place,this.source.substring(startPos,endPos));
}
};
function wikify(source,output,highlightRegExp,tiddler)
{
if(source) {
var wikifier = new Wikifier(source,getParser(tiddler),highlightRegExp,tiddler);
var t0 = new Date();
wikifier.subWikify(output);
if(tiddler && config.options.chkDisplayInstrumentation)
displayMessage("wikify:" +tiddler.title+ " in " + (new Date()-t0) + " ms");
}
}
function wikifyStatic(source,highlightRegExp,tiddler,format)
{
var e = createTiddlyElement(document.body,"pre");
e.style.display = "none";
var html = "";
if(source && source != "") {
if(!tiddler)
tiddler = new Tiddler("temp");
var wikifier = new Wikifier(source,getParser(tiddler,format),highlightRegExp,tiddler);
wikifier.isStatic = true;
wikifier.subWikify(e);
html = e.innerHTML;
jQuery(e).remove();
}
return html;
}
function wikifyPlainText(text,limit,tiddler)
{
if(limit > 0)
text = text.substr(0,limit);
var wikifier = new Wikifier(text,formatter,null,tiddler);
return wikifier.wikifyPlain();
}
function highlightify(source,output,highlightRegExp,tiddler)
{
if(source) {
var wikifier = new Wikifier(source,formatter,highlightRegExp,tiddler);
wikifier.outputText(output,0,source.length);
}
}
//--
//-- Macro definitions
//--
function invokeMacro(place,macro,params,wikifier,tiddler)
{
try {
var m = config.macros[macro];
if(m && m.handler) {
var tiddlerElem = story.findContainingTiddler(place);
window.tiddler = tiddlerElem ? store.getTiddler(tiddlerElem.getAttribute("tiddler")) : null;
window.place = place;
var allowEval = true;
if(config.evaluateMacroParameters=="system") {
if(!tiddler || tiddler.tags.indexOf("systemAllowEval") == -1) {
allowEval = false;
}
}
m.handler(place,macro,m.noPreParse?null:params.readMacroParams(!allowEval),wikifier,params,tiddler);
} else {
createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,config.messages.missingMacro]));
}
} catch(ex) {
createTiddlyError(place,config.messages.macroError.format([macro]),config.messages.macroErrorDetails.format([macro,ex.toString()]));
}
}
config.macros.version.handler = function(place)
{
jQuery("<span/>").text(formatVersion()).appendTo(place);
};
config.macros.today.handler = function(place,macroName,params)
{
var now = new Date();
var text = params[0] ? now.formatString(params[0].trim()) : now.toLocaleString();
jQuery("<span/>").text(text).appendTo(place);
};
config.macros.list.template = "<<view title link>>";
config.macros.list.handler = function(place,macroName,params,wikifier,paramString)
{
var list = document.createElement("ul");
jQuery(list).attr({ refresh: "macro", macroName: macroName }).data("params", paramString);
place.appendChild(list);
this.refresh(list);
};
config.macros.list.refresh = function(list) {
var paramString = jQuery(list).data("params");
var params = paramString.readMacroParams();
var args = paramString.parseParams("anon", null, null)[0];
var type = args.anon ? args.anon[0] : "all";
jQuery(list).empty().addClass("list list-" + type);
var template = args.template ? store.getTiddlerText(args.template[0]) : false;
if(!template) {
template = config.macros.list.template;
}
if(this[type].prompt)
createTiddlyElement(list,"li",null,"listTitle",this[type].prompt);
var results;
if(this[type].handler)
results = this[type].handler(params);
var t;
for(t = 0; t < results.length; t++) {
var li = document.createElement("li");
list.appendChild(li);
var tiddler = results[t];
if(typeof(tiddler) == 'string') { // deal with missing etc..
tiddler = store.getTiddler(tiddler) || new Tiddler(tiddler);
}
wikify(template, li, null, tiddler);
}
if(results.length === 0 && args.emptyMessage) {
jQuery(list).addClass("emptyList");
jQuery("<li />").text(args.emptyMessage[0]).appendTo(list);
}
};
config.macros.list.all.handler = function(params)
{
return store.reverseLookup("tags","excludeLists",false,"title");
};
config.macros.list.missing.handler = function(params)
{
return store.getMissingLinks();
};
config.macros.list.orphans.handler = function(params)
{
return store.getOrphans();
};
config.macros.list.shadowed.handler = function(params)
{
return store.getShadowed();
};
config.macros.list.touched.handler = function(params)
{
return store.getTouched();
};
config.macros.list.filter.handler = function(params)
{
var filter = params[1];
var results = [];
if(filter) {
var tiddlers = store.filterTiddlers(filter);
var t;
for(t=0; t<tiddlers.length; t++)
results.push(tiddlers[t].title);
}
return results;
};
config.macros.allTags.handler = function(place,macroName,params)
{
var tags = store.getTags(params[0]);
var ul = createTiddlyElement(place,"ul");
if(tags.length == 0)
createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
var t;
for(t=0; t<tags.length; t++) {
var title = tags[t][0];
var info = getTiddlyLinkInfo(title);
var li = createTiddlyElement(ul,"li");
var btn = createTiddlyButton(li,title + " (" + tags[t][1] + ")",this.tooltip.format([title]),onClickTag,info.classes);
btn.setAttribute("tag",title);
btn.setAttribute("refresh","link");
btn.setAttribute("tiddlyLink",title);
if(params[1]) {
btn.setAttribute("sortby",params[1]);
}
}
};
var macro = config.macros.timeline;
merge(macro, {
handler: function(place,macroName,params, wikifier, paramString, tiddler) {
var container = jQuery("<div />").attr("params", paramString).
attr("macroName", macroName).appendTo(place)[0];
macro.refresh(container);
},
refresh: function(container) {
jQuery(container).attr("refresh", "macro").empty();
var paramString = jQuery(container).attr("params");
var args = paramString.parseParams("anon", null, null)[0];
var params = args.anon || [];
var field = params[0] || "modified";
var prefix = field.charAt(0);
var no_prefix_field = prefix === "-" || prefix === "+" ? field.substr(1, field.length) : field;
var dateFormat = params[2] || this.dateFormat;
var groupTemplate = macro.groupTemplate.format(no_prefix_field, dateFormat);
groupTemplate = args.groupTemplate ? store.getTiddlerText(args.groupTemplate[0]) || groupTemplate :
groupTemplate;
var itemTemplate = macro.itemTemplate;
itemTemplate = args.template ? store.getTiddlerText(args.template[0]) || itemTemplate :
itemTemplate;
var tiddlers = args.filter ? store.sortTiddlers(store.filterTiddlers(args.filter[0]), field) :
store.reverseLookup("tags", "excludeLists", false, field);
var lastGroup = "", ul;
var last = params[1] ? tiddlers.length-Math.min(tiddlers.length,parseInt(params[1],10)) : 0;
var t;
for(t=tiddlers.length-1; t>=last; t--) {
var tiddler = tiddlers[t];
var theGroup = wikifyPlainText(groupTemplate,0,tiddler);
if(typeof(ul) == "undefined" || theGroup != lastGroup) {
ul = document.createElement("ul");
jQuery(ul).addClass("timeline");
container.appendChild(ul);
createTiddlyElement(ul,"li",null,"listTitle",theGroup);
lastGroup = theGroup;
}
var item = createTiddlyElement(ul,"li",null,"listLink");
wikify(itemTemplate,item,null,tiddler);
}
},
groupTemplate: "<<view %0 date '%1'>>",
itemTemplate: "<<view title link>>"
});
config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var allowEval = true;
var stack = config.macros.tiddler.tiddlerStack;
if(stack.length > 0 && config.evaluateMacroParameters == "system") {
// included tiddler and "system" evaluation required, so check tiddler tagged appropriately
var title = stack[stack.length-1];
var pos = title.indexOf(config.textPrimitives.sectionSeparator);
if(pos != -1) {
title = title.substr(0,pos); // get the base tiddler title
}
var t = store.getTiddler(title);
if(!t || t.tags.indexOf("systemAllowEval") == -1) {
allowEval = false;
}
}
params = paramString.parseParams("name",null,allowEval,false,true);
var names = params[0]["name"];
var tiddlerName = names[0];
var className = names[1] || null;
var args = params[0]["with"];
var wrapper = createTiddlyElement(place,"span",null,className,null,{
refresh: "content", tiddler: tiddlerName
});
if(args!==undefined)
wrapper.setAttribute("args","[["+args.join("]] [[")+"]]");
this.transclude(wrapper,tiddlerName,args);
};
config.macros.tiddler.transclude = function(wrapper,tiddlerName,args)
{
var text = store.getTiddlerText(tiddlerName);
if(!text)
return;
var stack = config.macros.tiddler.tiddlerStack;
if(stack.indexOf(tiddlerName) !== -1)
return;
stack.push(tiddlerName);
try {
if(typeof args == "string")
args = args.readBracketedList();
var n = args ? Math.min(args.length,9) : 0;
var i;
for(i=0; i<n; i++) {
var placeholderRE = new RegExp("\\$" + (i + 1),"mg");
text = text.replace(placeholderRE,args[i]);
}
config.macros.tiddler.renderText(wrapper,text,tiddlerName);
} finally {
stack.pop();
}
};
config.macros.tiddler.renderText = function(place,text,tiddlerName)
{
wikify(text,place,null,store.getTiddler(tiddlerName));
};
config.macros.tiddler.tiddlerStack = [];
config.macros.tag.handler = function(place,macroName,params)
{
var btn = createTagButton(place,params[0],null,params[1],params[2]);
if(params[3]) {
btn.setAttribute('sortby',params[3]);
}
};
config.macros.tags.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,true,false,false);
var ul = createTiddlyElement(place,"ul");
var title = getParam(params,"anon","");
if(title && store.tiddlerExists(title))
tiddler = store.getTiddler(title);
var sep = getParam(params,"sep"," ");
var lingo = config.views.wikified.tag;
var label = null;
var t;
for(t=0; t<tiddler.tags.length; t++) {
var tag = store.getTiddler(tiddler.tags[t]);
if(!tag || !tag.tags.contains("excludeLists")) {
if(!label)
label = createTiddlyElement(ul,"li",null,"listTitle",lingo.labelTags.format([tiddler.title]));
createTagButton(createTiddlyElement(ul,"li"),tiddler.tags[t],tiddler.title);
if(t<tiddler.tags.length-1)
createTiddlyText(ul,sep);
}
}
if(!label)
createTiddlyElement(ul,"li",null,"listTitle",lingo.labelNoTags.format([tiddler.title]));
};
config.macros.tagging.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,true,false,false);
var ul = createTiddlyElement(place,"ul");
var title = getParam(params,"anon","");
if(title == "" && tiddler instanceof Tiddler)
title = tiddler.title;
var sep = getParam(params,"sep"," ");
ul.setAttribute("title",this.tooltip.format([title]));
var sortby = getParam(params,"sortBy",false);
var tagged = store.getTaggedTiddlers(title,sortby);
var prompt = tagged.length == 0 ? this.labelNotTag : this.label;
createTiddlyElement(ul,"li",null,"listTitle",prompt.format([title,tagged.length]));
var t;
for(t=0; t<tagged.length; t++) {
createTiddlyLink(createTiddlyElement(ul,"li"),tagged[t].title,true);
if(t<tagged.length-1)
createTiddlyText(ul,sep);
}
};
config.macros.closeAll.handler = function(place)
{
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.closeAll.onClick = function(e)
{
story.closeAllTiddlers();
return false;
};
config.macros.permaview.handler = function(place)
{
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.permaview.onClick = function(e)
{
story.permaView();
return false;
};
config.macros.saveChanges.handler = function(place,macroName,params)
{
if(!readOnly)
createTiddlyButton(place,params[0] || this.label,params[1] || this.prompt,this.onClick,null,null,this.accessKey);
};
config.macros.saveChanges.onClick = function(e)
{
saveChanges();
return false;
};
config.macros.slider.onClickSlider = function(ev)
{
var n = this.nextSibling;
var cookie = n.getAttribute("cookie");
var isOpen = n.style.display != "none";
if(config.options.chkAnimate && anim && typeof Slider == "function")
anim.startAnimating(new Slider(n,!isOpen,null,"none"));
else
n.style.display = isOpen ? "none" : "block";
config.options[cookie] = !isOpen;
saveOption(cookie);
return false;
};
config.macros.slider.createSlider = function(place,cookie,title,tooltip)
{
var c = cookie || "";
createTiddlyButton(place,title,tooltip,this.onClickSlider);
var panel = createTiddlyElement(null,"div",null,"sliderPanel");
panel.setAttribute("cookie",c);
panel.style.display = config.options[c] ? "block" : "none";
place.appendChild(panel);
return panel;
};
config.macros.slider.handler = function(place,macroName,params)
{
var panel = this.createSlider(place,params[0],params[2],params[3]);
var text = store.getTiddlerText(params[1]);
panel.setAttribute("refresh","content");
panel.setAttribute("tiddler",params[1]);
if(text)
wikify(text,panel,null,store.getTiddler(params[1]));
};
// <<gradient [[tiddler name]] vert|horiz rgb rgb rgb rgb... >>
config.macros.gradient.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var panel = wikifier ? createTiddlyElement(place,"div",null,"gradient") : place;
panel.style.position = "relative";
panel.style.overflow = "hidden";
panel.style.zIndex = "0";
if(wikifier) {
var styles = config.formatterHelpers.inlineCssHelper(wikifier);
config.formatterHelpers.applyCssHelper(panel,styles);
}
params = paramString.parseParams("color");
var locolors = [], hicolors = [];
var t;
for(t=2; t<params.length; t++) {
var c = params[t].value;
if(params[t].name == "snap") {
hicolors[hicolors.length-1] = c;
} else {
locolors.push(c);
hicolors.push(c);
}
}
drawGradient(panel,params[1].value != "vert",locolors,hicolors);
if(wikifier)
wikifier.subWikify(panel,">>");
if(document.all) {
panel.style.height = "100%";
panel.style.width = "100%";
}
};
config.macros.message.handler = function(place,macroName,params)
{
if(params[0]) {
var names = params[0].split(".");
var lookupMessage = function(root,nameIndex) {
if(root[names[nameIndex]]) {
if(nameIndex < names.length-1)
return (lookupMessage(root[names[nameIndex]],nameIndex+1));
else
return root[names[nameIndex]];
} else
return null;
};
var m = lookupMessage(config,0);
if(m == null)
m = lookupMessage(window,0);
createTiddlyText(place,m.toString().format(params.splice(1)));
}
};
config.macros.view.depth = 0;
config.macros.view.values = [];
config.macros.view.views = {
text: function(value,place,params,wikifier,paramString,tiddler) {
highlightify(value,place,highlightHack,tiddler);
},
link: function(value,place,params,wikifier,paramString,tiddler) {
createTiddlyLink(place,value,true);
},
wikified: function(value,place,params,wikifier,paramString,tiddler) {
if(config.macros.view.depth>50)
return;
if(config.macros.view.depth>0) {
if (value==config.macros.view.values[config.macros.view.depth-1]) {
return;
}
}
config.macros.view.values[config.macros.view.depth] = value;
config.macros.view.depth++;
if(params[2])
value=params[2].unescapeLineBreaks().format([value]);
wikify(value,place,highlightHack,tiddler);
config.macros.view.depth--;
config.macros.view.values[config.macros.view.depth] = null;
},
date: function(value,place,params,wikifier,paramString,tiddler) {
value = Date.convertFromYYYYMMDDHHMM(value);
createTiddlyText(place,value.formatString(params[2] || config.views.wikified.dateFormat));
}
};
config.macros.view.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if((tiddler instanceof Tiddler) && params[0]) {
var value = store.getValue(tiddler,params[0]);
if(value) {
var type = params[1] || config.macros.view.defaultView;
var handler = config.macros.view.views[type];
if(handler)
handler(value,place,params,wikifier,paramString,tiddler);
}
}
};
config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var field = params[0];
var rows = params[1] || 0;
var defVal = params[2] || '';
if((tiddler instanceof Tiddler) && field) {
story.setDirty(tiddler.title,true);
var e,v;
if(field != "text" && !rows) {
e = createTiddlyElement(null,"input",null,null,null,{
type: "text", edit: field, size: "40", autocomplete: "off"
});
e.value = store.getValue(tiddler,field) || defVal;
place.appendChild(e);
} else {
var wrapper1 = createTiddlyElement(null,"fieldset",null,"fieldsetFix");
var wrapper2 = createTiddlyElement(wrapper1,"div");
e = createTiddlyElement(wrapper2,"textarea");
e.value = v = store.getValue(tiddler,field) || defVal;
rows = rows || 10;
var lines = v.match(/\n/mg);
var maxLines = Math.max(parseInt(config.options.txtMaxEditRows,10),5);
if(lines != null && lines.length > rows)
rows = lines.length + 5;
rows = Math.min(rows,maxLines);
e.setAttribute("rows",rows);
e.setAttribute("edit",field);
place.appendChild(wrapper1);
}
if(tiddler.isReadOnly()) {
e.setAttribute("readOnly","readOnly");
jQuery(e).addClass("readOnly");
}
return e;
}
};
config.macros.tagChooser.onClick = function(ev)
{
var e = ev || window.event;
var lingo = config.views.editor.tagChooser;
var popup = Popup.create(this);
var tags = store.getTags(this.getAttribute("tags"));
if(tags.length == 0)
jQuery("<li/>").text(lingo.popupNone).appendTo(popup);
var t;
for(t=0; t<tags.length; t++) {
var tag = createTiddlyButton(createTiddlyElement(popup,"li"),tags[t][0],lingo.tagTooltip.format([tags[t][0]]),config.macros.tagChooser.onTagClick);
tag.setAttribute("tag",tags[t][0]);
tag.setAttribute("tiddler",this.getAttribute("tiddler"));
}
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
};
config.macros.tagChooser.onTagClick = function(ev)
{
var e = ev || window.event;
if(e.metaKey || e.ctrlKey) stopEvent(e); //# keep popup open on CTRL-click
var tag = this.getAttribute("tag");
var title = this.getAttribute("tiddler");
if(!readOnly)
story.setTiddlerTag(title,tag,0);
return false;
};
config.macros.tagChooser.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
if(tiddler instanceof Tiddler) {
var lingo = config.views.editor.tagChooser;
var btn = createTiddlyButton(place,lingo.text,lingo.tooltip,this.onClick);
btn.setAttribute("tiddler",tiddler.title);
btn.setAttribute("tags",params[0]);
}
};
config.macros.refreshDisplay.handler = function(place)
{
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.refreshDisplay.onClick = function(e)
{
refreshAll();
return false;
};
config.macros.annotations.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var title = tiddler ? tiddler.title : null;
var a = title ? config.annotations[title] : null;
if(!tiddler || !title || !a)
return;
var text = a.format([title]);
wikify(text,createTiddlyElement(place,"div",null,"annotation"),null,tiddler);
};
//--
//-- NewTiddler and NewJournal macros
//--
config.macros.newTiddler.createNewTiddlerButton = function(place,title,params,label,prompt,accessKey,newFocus,isJournal)
{
var tags = [];
var t;
for(t=1; t<params.length; t++) {
if((params[t].name == "anon" && t != 1) || (params[t].name == "tag"))
tags.push(params[t].value);
}
label = getParam(params,"label",label);
prompt = getParam(params,"prompt",prompt);
accessKey = getParam(params,"accessKey",accessKey);
newFocus = getParam(params,"focus",newFocus);
var customFields = getParam(params,"fields","");
if(!customFields && !store.isShadowTiddler(title))
customFields = String.encodeHashMap(config.defaultCustomFields);
var btn = createTiddlyButton(place,label,prompt,this.onClickNewTiddler,null,null,accessKey);
btn.setAttribute("newTitle",title);
btn.setAttribute("isJournal",isJournal ? "true" : "false");
if(tags.length > 0)
btn.setAttribute("params",tags.join("|"));
btn.setAttribute("newFocus",newFocus);
btn.setAttribute("newTemplate",getParam(params,"template",DEFAULT_EDIT_TEMPLATE));
if(customFields !== "")
btn.setAttribute("customFields",customFields);
var text = getParam(params,"text");
if(text !== undefined)
btn.setAttribute("newText",text);
return btn;
};
config.macros.newTiddler.onClickNewTiddler = function()
{
var title = this.getAttribute("newTitle");
if(this.getAttribute("isJournal") == "true") {
title = new Date().formatString(title.trim());
}
var params = this.getAttribute("params");
var tags = params ? params.split("|") : [];
var focus = this.getAttribute("newFocus");
var template = this.getAttribute("newTemplate");
var customFields = this.getAttribute("customFields");
if(!customFields && !store.isShadowTiddler(title))
customFields = String.encodeHashMap(config.defaultCustomFields);
story.displayTiddler(null,title,template,false,null,null);
var tiddlerElem = story.getTiddler(title);
if(customFields)
story.addCustomFields(tiddlerElem,customFields);
var text = this.getAttribute("newText");
if(typeof text == "string" && story.getTiddlerField(title,"text"))
story.getTiddlerField(title,"text").value = text.format([title]);
var t;
for(t=0;t<tags.length;t++)
story.setTiddlerTag(title,tags[t],+1);
story.focusTiddler(title,focus);
return false;
};
config.macros.newTiddler.handler = function(place,macroName,params,wikifier,paramString)
{
if(!readOnly) {
params = paramString.parseParams("anon",null,true,false,false);
var title = params[1] && params[1].name == "anon" ? params[1].value : this.title;
title = getParam(params,"title",title);
this.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"title",false);
}
};
config.macros.newJournal.handler = function(place,macroName,params,wikifier,paramString)
{
if(!readOnly) {
params = paramString.parseParams("anon",null,true,false,false);
var title = params[1] && params[1].name == "anon" ? params[1].value : config.macros.timeline.dateFormat;
title = getParam(params,"title",title);
config.macros.newTiddler.createNewTiddlerButton(place,title,params,this.label,this.prompt,this.accessKey,"text",true);
}
};
//--
//-- Search macro
//--
config.macros.search.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
params = paramString.parseParams("anon",null,false,false,false);
createTiddlyButton(place,this.label,this.prompt,this.onClick,"searchButton");
var txt = createTiddlyElement(null,"input",null,"txtOptionInput searchField");
txt.value = getParam(params,"anon","");
if(config.browser.isSafari) {
txt.setAttribute("type","search");
txt.setAttribute("results","5");
} else {
txt.setAttribute("type","text");
}
place.appendChild(txt);
txt.onkeyup = this.onKeyPress;
txt.onfocus = this.onFocus;
txt.setAttribute("size",this.sizeTextbox);
txt.setAttribute("accessKey",getParam(params,"accesskey",this.accessKey));
txt.setAttribute("autocomplete","off");
txt.setAttribute("lastSearchText","");
txt.setAttribute("placeholder",getParam(params,"placeholder",this.placeholder));
};
// Global because there's only ever one outstanding incremental search timer
config.macros.search.timeout = null;
config.macros.search.doSearch = function(txt)
{
if(txt.value.length > 0) {
story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);
txt.setAttribute("lastSearchText",txt.value);
}
};
config.macros.search.onClick = function(e)
{
config.macros.search.doSearch(this.nextSibling);
return false;
};
config.macros.search.onKeyPress = function(ev)
{
var me = config.macros.search;
var e = ev || window.event;
switch(e.keyCode) {
case 9: // Tab
return;
case 13: // Ctrl-Enter
case 10: // Ctrl-Enter on IE PC
me.doSearch(this);
break;
case 27: // Escape
this.value = "";
clearMessage();
break;
}
if(config.options.chkIncrementalSearch) {
if(this.value.length > 2) {
if(this.value != this.getAttribute("lastSearchText")) {
if(me.timeout) {
clearTimeout(me.timeout);
}
var txt = this;
me.timeout = setTimeout(function() {me.doSearch(txt);},500);
}
} else {
if(me.timeout) {
clearTimeout(me.timeout);
}
}
}
};
config.macros.search.onFocus = function(e)
{
this.select();
};
//--
//-- Tabs macro
//--
config.macros.tabs.handler = function(place,macroName,params)
{
var cookie = params[0];
var numTabs = (params.length-1)/3;
var wrapper = createTiddlyElement(null,"div",null,"tabsetWrapper " + cookie);
var tabset = createTiddlyElement(wrapper,"div",null,"tabset");
tabset.setAttribute("cookie",cookie);
var validTab = false;
var t;
for(t=0; t<numTabs; t++) {
var label = params[t*3+1];
var prompt = params[t*3+2];
var content = params[t*3+3];
var tab = createTiddlyButton(tabset,label,prompt,this.onClickTab,"tab tabUnselected");
createTiddlyElement(tab,"span",null,null," ",{style:"font-size:0pt;line-height:0px"});
tab.setAttribute("tab",label);
tab.setAttribute("content",content);
tab.title = prompt;
if(config.options[cookie] == label)
validTab = true;
}
if(!validTab)
config.options[cookie] = params[1];
place.appendChild(wrapper);
this.switchTab(tabset,config.options[cookie]);
};
config.macros.tabs.onClickTab = function(e)
{
config.macros.tabs.switchTab(this.parentNode,this.getAttribute("tab"));
return false;
};
config.macros.tabs.switchTab = function(tabset,tab)
{
var cookie = tabset.getAttribute("cookie");
var theTab = null;
var nodes = tabset.childNodes;
var t;
for(t=0; t<nodes.length; t++) {
if(nodes[t].getAttribute && nodes[t].getAttribute("tab") == tab) {
theTab = nodes[t];
theTab.className = "tab tabSelected";
} else {
nodes[t].className = "tab tabUnselected";
}
}
if(theTab) {
if(tabset.nextSibling && tabset.nextSibling.className == "tabContents")
jQuery(tabset.nextSibling).remove();
var tabContent = createTiddlyElement(null,"div",null,"tabContents");
tabset.parentNode.insertBefore(tabContent,tabset.nextSibling);
var contentTitle = theTab.getAttribute("content");
wikify(store.getTiddlerText(contentTitle),tabContent,null,store.getTiddler(contentTitle));
if(cookie) {
config.options[cookie] = tab;
saveOption(cookie);
}
}
};
//--
//-- Tiddler toolbar
//--
// Create a toolbar command button
config.macros.toolbar.createCommand = function(place,commandName,tiddler,className)
{
if(typeof commandName != "string") {
var c = null;
var t;
for(t in config.commands) {
if(config.commands[t] == commandName)
c = t;
}
commandName = c;
}
if((tiddler instanceof Tiddler) && (typeof commandName == "string")) {
var command = config.commands[commandName];
if(command.isEnabled ? command.isEnabled(tiddler) : this.isCommandEnabled(command,tiddler)) {
var text = command.getText ? command.getText(tiddler) : this.getCommandText(command,tiddler);
var tooltip = command.getTooltip ? command.getTooltip(tiddler) : this.getCommandTooltip(command,tiddler);
var cmd = command.type == "popup" ? this.onClickPopup : this.onClickCommand;
var btn = createTiddlyButton(null,text,tooltip,cmd);
btn.setAttribute("commandName",commandName);
btn.setAttribute("tiddler",tiddler.title);
jQuery(btn).addClass("command_" + commandName);
if(className)
jQuery(btn).addClass(className);
place.appendChild(btn);
}
}
};
config.macros.toolbar.isCommandEnabled = function(command,tiddler)
{
var title = tiddler.title;
var ro = tiddler.isReadOnly();
var shadow = store.isShadowTiddler(title) && !store.tiddlerExists(title);
return (!ro || (ro && !command.hideReadOnly)) && !(shadow && command.hideShadow);
};
config.macros.toolbar.getCommandText = function(command,tiddler)
{
return (tiddler.isReadOnly() && command.readOnlyText) || command.text;
};
config.macros.toolbar.getCommandTooltip = function(command,tiddler)
{
return (tiddler.isReadOnly() && command.readOnlyTooltip) || command.tooltip;
};
config.macros.toolbar.onClickCommand = function(ev)
{
var e = ev || window.event;
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
var command = config.commands[this.getAttribute("commandName")];
return command.handler(e,this,this.getAttribute("tiddler"));
};
config.macros.toolbar.onClickPopup = function(ev)
{
var e = ev || window.event;
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
var popup = Popup.create(this);
var command = config.commands[this.getAttribute("commandName")];
var title = this.getAttribute("tiddler");
popup.setAttribute("tiddler",title);
command.handlePopup(popup,title);
Popup.show();
return false;
};
// Invoke the first command encountered from a given place that is tagged with a specified class
config.macros.toolbar.invokeCommand = function(place,className,event)
{
var children = place.getElementsByTagName("a");
var t;
for(t=0; t<children.length; t++) {
var c = children[t];
if(jQuery(c).hasClass(className) && c.getAttribute && c.getAttribute("commandName")) {
if(c.onclick instanceof Function)
c.onclick.call(c,event);
break;
}
}
};
config.macros.toolbar.onClickMore = function(ev)
{
var e = this.nextSibling;
e.style.display = "inline";
this.style.display = "none";
return false;
};
config.macros.toolbar.onClickLess = function(ev)
{
var e = this.parentNode;
var m = e.previousSibling;
e.style.display = "none";
m.style.display = "inline";
return false;
};
config.macros.toolbar.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
var t;
for(t=0; t<params.length; t++) {
var btn;
var c = params[t];
switch(c) {
case "!":
createTiddlyText(place,this.separator);
break;
case "*":
createTiddlyElement(place,"br");
break;
case "<":
btn = createTiddlyButton(place,this.lessLabel,this.lessPrompt,config.macros.toolbar.onClickLess);
jQuery(btn).addClass("lessCommand");
break;
case ">":
btn = createTiddlyButton(place,this.moreLabel,this.morePrompt,config.macros.toolbar.onClickMore);
jQuery(btn).addClass("moreCommand");
var e = createTiddlyElement(place,"span",null,"moreCommand");
e.style.display = "none";
place = e;
break;
default:
var className = "";
switch(c.substr(0,1)) {
case "+":
className = "defaultCommand";
c = c.substr(1);
break;
case "-":
className = "cancelCommand";
c = c.substr(1);
break;
}
if(config.commands[c]) {
this.createCommand(place,c,tiddler,className);
} else {
this.customCommand(place,c,wikifier,tiddler);
}
break;
}
}
};
// Overrideable function to extend toolbar handler
config.macros.toolbar.customCommand = function(place,command,wikifier,tiddler)
{
};
//--
//-- Menu and toolbar commands
//--
config.commands.closeTiddler.handler = function(event,src,title)
{
if(story.isDirty(title) && !readOnly) {
if(!confirm(config.commands.cancelTiddler.warning.format([title])))
return false;
}
story.setDirty(title,false);
story.closeTiddler(title,true);
return false;
};
config.commands.closeOthers.handler = function(event,src,title)
{
story.closeAllTiddlers(title);
return false;
};
config.commands.editTiddler.handler = function(event,src,title)
{
clearMessage();
var tiddlerElem = story.getTiddler(title);
var fields = tiddlerElem.getAttribute("tiddlyFields");
story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE,false,null,fields);
var e = story.getTiddlerField(title,config.options.txtEditorFocus||"text");
if(e) {
setCaretPosition(e,0);
}
return false;
};
config.commands.saveTiddler.handler = function(event,src,title)
{
var newTitle = story.saveTiddler(title,event.shiftKey);
if(newTitle)
story.displayTiddler(null,newTitle);
return false;
};
config.commands.cancelTiddler.handler = function(event,src,title)
{
if(story.hasChanges(title) && !readOnly) {
if(!confirm(this.warning.format([title])))
return false;
}
story.setDirty(title,false);
story.displayTiddler(null,title);
return false;
};
config.commands.deleteTiddler.handler = function(event,src,title)
{
var deleteIt = true;
if(config.options.chkConfirmDelete)
deleteIt = confirm(this.warning.format([title]));
if(deleteIt) {
store.removeTiddler(title);
story.closeTiddler(title,true);
autoSaveChanges();
}
return false;
};
config.commands.permalink.handler = function(event,src,title)
{
var t = encodeURIComponent(String.encodeTiddlyLink(title));
if(window.location.hash != t)
window.location.hash = t;
return false;
};
config.commands.references.handlePopup = function(popup,title)
{
var references = store.getReferringTiddlers(title);
var c = false;
var r;
for(r=0; r<references.length; r++) {
if(references[r].title != title && !references[r].isTagged("excludeLists")) {
createTiddlyLink(createTiddlyElement(popup,"li"),references[r].title,true);
c = true;
}
}
if(!c)
createTiddlyElement(popup,"li",null,"disabled",this.popupNone);
};
config.commands.jump.handlePopup = function(popup,title)
{
story.forEachTiddler(function(title,element) {
createTiddlyLink(createTiddlyElement(popup,"li"),title,true,null,false,null,true);
});
};
config.commands.fields.handlePopup = function(popup,title)
{
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var items = [];
store.forEachField(tiddler,function(tiddler,fieldName,value){items.push({field:fieldName,value:value});},true);
items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
if(items.length > 0)
ListView.create(popup,items,this.listViewTemplate);
else
createTiddlyElement(popup,"div",null,null,this.emptyText);
};
//--
//-- Tiddler() object
//--
function Tiddler(title)
{
this.title = title;
this.text = "";
this.creator = null;
this.modifier = null;
this.created = new Date();
this.modified = this.created;
this.links = [];
this.linksUpdated = false;
this.tags = [];
this.fields = {};
return this;
}
Tiddler.prototype.getLinks = function()
{
if(this.linksUpdated==false)
this.changed();
return this.links;
};
// Returns the fields that are inherited in string field:"value" field2:"value2" format
Tiddler.prototype.getInheritedFields = function()
{
var f = {};
var i;
for(i in this.fields) {
if(i=="server.host" || i=="server.workspace" || i=="wikiformat"|| i=="server.type") {
f[i] = this.fields[i];
}
}
return String.encodeHashMap(f);
};
// Increment the changeCount of a tiddler
Tiddler.prototype.incChangeCount = function()
{
var c = this.fields['changecount'];
c = c ? parseInt(c,10) : 0;
this.fields['changecount'] = String(c+1);
};
// Clear the changeCount of a tiddler
Tiddler.prototype.clearChangeCount = function()
{
if(this.fields['changecount']) {
delete this.fields['changecount'];
}
};
Tiddler.prototype.doNotSave = function()
{
return this.fields['doNotSave'];
};
// Returns true if the tiddler has been updated since the tiddler was created or downloaded
Tiddler.prototype.isTouched = function()
{
var changecount = this.fields.changecount || 0;
return changecount > 0;
};
// Change the text and other attributes of a tiddler
Tiddler.prototype.set = function(title,text,modifier,modified,tags,created,fields,creator)
{
this.assign(title,text,modifier,modified,tags,created,fields,creator);
this.changed();
return this;
};
// Change the text and other attributes of a tiddler without triggered a tiddler.changed() call
Tiddler.prototype.assign = function(title,text,modifier,modified,tags,created,fields,creator)
{
if(title != undefined)
this.title = title;
if(text != undefined)
this.text = text;
if(modifier != undefined)
this.modifier = modifier;
if(modified != undefined)
this.modified = modified;
if(creator != undefined)
this.creator = creator;
if(created != undefined)
this.created = created;
if(fields != undefined)
this.fields = fields;
if(tags != undefined)
this.tags = (typeof tags == "string") ? tags.readBracketedList() : tags;
else if(this.tags == undefined)
this.tags = [];
return this;
};
// Get the tags for a tiddler as a string (space delimited, using [[brackets]] for tags containing spaces)
Tiddler.prototype.getTags = function()
{
return String.encodeTiddlyLinkList(this.tags);
};
// Test if a tiddler carries a tag
Tiddler.prototype.isTagged = function(tag)
{
return this.tags.indexOf(tag) != -1;
};
// Static method to convert "\n" to newlines, "\s" to "\"
Tiddler.unescapeLineBreaks = function(text)
{
return text ? text.unescapeLineBreaks() : "";
};
// Convert newlines to "\n", "\" to "\s"
Tiddler.prototype.escapeLineBreaks = function()
{
return this.text.escapeLineBreaks();
};
// Updates the secondary information (like links[] array) after a change to a tiddler
Tiddler.prototype.changed = function()
{
this.links = [];
var text = this.text;
// remove 'quoted' text before scanning tiddler source
text = text.replace(/\/%((?:.|\n)*?)%\//g,"").
replace(/\{{3}((?:.|\n)*?)\}{3}/g,"").
replace(/"""((?:.|\n)*?)"""/g,"").
replace(/<nowiki\>((?:.|\n)*?)<\/nowiki\>/g,"").
replace(/<html\>((?:.|\n)*?)<\/html\>/g,"").
replace(/<script((?:.|\n)*?)<\/script\>/g,"");
var t = this.autoLinkWikiWords() ? 0 : 1;
var tiddlerLinkRegExp = t==0 ? config.textPrimitives.tiddlerAnyLinkRegExp : config.textPrimitives.tiddlerForcedLinkRegExp;
tiddlerLinkRegExp.lastIndex = 0;
var formatMatch = tiddlerLinkRegExp.exec(text);
while(formatMatch) {
var lastIndex = tiddlerLinkRegExp.lastIndex;
if(t==0 && formatMatch[1] && formatMatch[1] != this.title) {
// wikiWordLink
if(formatMatch.index > 0) {
var preRegExp = new RegExp(config.textPrimitives.unWikiLink+"|"+config.textPrimitives.anyLetter,"mg");
preRegExp.lastIndex = formatMatch.index-1;
var preMatch = preRegExp.exec(text);
if(preMatch.index != formatMatch.index-1)
this.links.pushUnique(formatMatch[1]);
} else {
this.links.pushUnique(formatMatch[1]);
}
}
else if(formatMatch[2-t] && !config.formatterHelpers.isExternalLink(formatMatch[3-t])) // titledBrackettedLink
this.links.pushUnique(formatMatch[3-t]);
else if(formatMatch[4-t] && formatMatch[4-t] != this.title) // brackettedLink
this.links.pushUnique(formatMatch[4-t]);
tiddlerLinkRegExp.lastIndex = lastIndex;
formatMatch = tiddlerLinkRegExp.exec(text);
}
this.linksUpdated = true;
};
Tiddler.prototype.getSubtitle = function()
{
var modifier = this.modifier;
if(!modifier)
modifier = config.messages.subtitleUnknown || "";
var modified = this.modified;
if(modified)
modified = modified.toLocaleString();
else
modified = config.messages.subtitleUnknown || "";
var f = config.messages.tiddlerLinkTooltip || "%0 - %1, %2";
return f.format([this.title,modifier,modified]);
};
Tiddler.prototype.isReadOnly = function()
{
return readOnly;
};
Tiddler.prototype.autoLinkWikiWords = function()
{
return !(this.isTagged("systemConfig") || this.isTagged("excludeMissing"));
};
Tiddler.prototype.getServerType = function()
{
var serverType = null;
if(this.fields['server.type'])
serverType = this.fields['server.type'];
if(!serverType)
serverType = this.fields['wikiformat'];
if(serverType && !config.adaptors[serverType])
serverType = null;
return serverType;
};
Tiddler.prototype.getAdaptor = function()
{
var serverType = this.getServerType();
return serverType ? new config.adaptors[serverType]() : null;
};
//--
//-- TiddlyWiki instance contains TiddlerS
//--
function TiddlyWiki(params)
{
var tiddlers = {}; // Hashmap by name of tiddlers
if(params && params.config) {
this.config = config;
}
this.tiddlersUpdated = false;
this.namedNotifications = []; // Array of {name:,notify:} of notification functions
this.notificationLevel = 0;
this.slices = {}; // map tiddlerName->(map sliceName->sliceValue). Lazy.
this.clear = function() {
tiddlers = {};
this.setDirty(false);
};
this.fetchTiddler = function(title) {
var t = tiddlers[title];
return t instanceof Tiddler ? t : null;
};
this.deleteTiddler = function(title) {
delete this.slices[title];
delete tiddlers[title];
};
this.addTiddler = function(tiddler) {
delete this.slices[tiddler.title];
tiddlers[tiddler.title] = tiddler;
};
this.forEachTiddler = function(callback) {
var t;
for(t in tiddlers) {
var tiddler = tiddlers[t];
if(tiddler instanceof Tiddler)
callback.call(this,t,tiddler);
}
};
}
TiddlyWiki.prototype.setDirty = function(dirty)
{
this.dirty = dirty;
};
TiddlyWiki.prototype.isDirty = function()
{
return this.dirty;
};
TiddlyWiki.prototype.tiddlerExists = function(title)
{
var t = this.fetchTiddler(title);
return t != undefined;
};
TiddlyWiki.prototype.isShadowTiddler = function(title)
{
return config.shadowTiddlers[title] === undefined ? false : true;
};
TiddlyWiki.prototype.createTiddler = function(title)
{
var tiddler = this.fetchTiddler(title);
if(!tiddler) {
tiddler = new Tiddler(title);
this.addTiddler(tiddler);
this.setDirty(true);
}
return tiddler;
};
TiddlyWiki.prototype.getTiddler = function(title)
{
var t = this.fetchTiddler(title);
if(t != undefined)
return t;
else
return null;
};
TiddlyWiki.prototype.getShadowTiddlerText = function(title)
{
if(typeof config.shadowTiddlers[title] == "string")
return config.shadowTiddlers[title];
else
return "";
};
// Retrieve tiddler contents
TiddlyWiki.prototype.getTiddlerText = function(title,defaultText)
{
if(!title)
return defaultText;
var pos = title.indexOf(config.textPrimitives.sectionSeparator);
var section = null;
if(pos != -1) {
section = title.substr(pos + config.textPrimitives.sectionSeparator.length);
title = title.substr(0,pos);
}
pos = title.indexOf(config.textPrimitives.sliceSeparator);
if(pos != -1) {
var slice = this.getTiddlerSlice(title.substr(0,pos),title.substr(pos + config.textPrimitives.sliceSeparator.length));
if(slice)
return slice;
}
var tiddler = this.fetchTiddler(title);
var text = tiddler ? tiddler.text : null;
if(!tiddler && this.isShadowTiddler(title)) {
text = this.getShadowTiddlerText(title);
}
if(text) {
if(!section)
return text;
var re = new RegExp("(^!{1,6}[ \t]*" + section.escapeRegExp() + "[ \t]*\n)","mg");
re.lastIndex = 0;
var match = re.exec(text);
if(match) {
var t = text.substr(match.index+match[1].length);
var re2 = /^!/mg;
re2.lastIndex = 0;
match = re2.exec(t); //# search for the next heading
if(match)
t = t.substr(0,match.index-1);//# don't include final \n
return t;
}
return defaultText;
}
if(defaultText != undefined)
return defaultText;
return null;
};
TiddlyWiki.prototype.getRecursiveTiddlerText = function(title,defaultText,depth)
{
var bracketRegExp = new RegExp("(?:\\[\\[([^\\]]+)\\]\\])","mg");
var text = this.getTiddlerText(title,null);
if(text == null)
return defaultText;
var textOut = [];
var match,lastPos = 0;
do {
match = bracketRegExp.exec(text);
if(match) {
textOut.push(text.substr(lastPos,match.index-lastPos));
if(match[1]) {
if(depth <= 0)
textOut.push(match[1]);
else
textOut.push(this.getRecursiveTiddlerText(match[1],"",depth-1));
}
lastPos = match.index + match[0].length;
} else {
textOut.push(text.substr(lastPos));
}
} while(match);
return textOut.join("");
};
//TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]+)[\t\x20]*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|[\t\x20]*([^\n]+)[\t\x20]*\|$)/gm;
TiddlyWiki.prototype.slicesRE = /(?:^([\'\/]{0,2})~?([\.\w]+)\:\1[\t\x20]*([^\n]*)[\t\x20]*$)|(?:^\|([\'\/]{0,2})~?([\.\w]+)\:?\4\|[\t\x20]*([^\|\n]*)[\t\x20]*\|$)/gm;
// @internal
TiddlyWiki.prototype.calcAllSlices = function(title)
{
var slices = {};
var text = this.getTiddlerText(title,"");
this.slicesRE.lastIndex = 0;
var m = this.slicesRE.exec(text);
while(m) {
if(m[2])
slices[m[2]] = m[3];
else
slices[m[5]] = m[6];
m = this.slicesRE.exec(text);
}
return slices;
};
// Returns the slice of text of the given name
TiddlyWiki.prototype.getTiddlerSlice = function(title,sliceName)
{
var slices = this.slices[title];
if(!slices) {
slices = this.calcAllSlices(title);
this.slices[title] = slices;
}
return slices[sliceName];
};
// Build an hashmap of the specified named slices of a tiddler
TiddlyWiki.prototype.getTiddlerSlices = function(title,sliceNames)
{
var t,r = {};
for(t=0; t<sliceNames.length; t++) {
var slice = this.getTiddlerSlice(title,sliceNames[t]);
if(slice)
r[sliceNames[t]] = slice;
}
return r;
};
TiddlyWiki.prototype.suspendNotifications = function()
{
this.notificationLevel--;
};
TiddlyWiki.prototype.resumeNotifications = function()
{
this.notificationLevel++;
};
// Invoke the notification handlers for a particular tiddler
TiddlyWiki.prototype.notify = function(title,doBlanket)
{
if(!this.notificationLevel) {
var t;
for(t=0; t<this.namedNotifications.length; t++) {
var n = this.namedNotifications[t];
if((n.name == null && doBlanket) || (n.name == title))
n.notify(title);
}
}
};
// Invoke the notification handlers for all tiddlers
TiddlyWiki.prototype.notifyAll = function()
{
if(!this.notificationLevel) {
var t;
for(t=0; t<this.namedNotifications.length; t++) {
var n = this.namedNotifications[t];
if(n.name)
n.notify(n.name);
}
}
};
// Add a notification handler to a tiddler
TiddlyWiki.prototype.addNotification = function(title,fn)
{
var i;
for(i=0; i<this.namedNotifications.length; i++) {
if((this.namedNotifications[i].name == title) && (this.namedNotifications[i].notify == fn))
return this;
}
this.namedNotifications.push({name: title, notify: fn});
return this;
};
TiddlyWiki.prototype.removeTiddler = function(title)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
this.deleteTiddler(title);
this.notify(title,true);
this.setDirty(true);
}
};
// Reset the sync status of a freshly synced tiddler
TiddlyWiki.prototype.resetTiddler = function(title)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
tiddler.clearChangeCount();
this.notify(title,true);
this.setDirty(true);
}
};
TiddlyWiki.prototype.setTiddlerTag = function(title,status,tag)
{
var tiddler = this.fetchTiddler(title);
if(tiddler) {
var t = tiddler.tags.indexOf(tag);
if(t != -1)
tiddler.tags.splice(t,1);
if(status)
tiddler.tags.push(tag);
tiddler.changed();
tiddler.incChangeCount();
this.notify(title,true);
this.setDirty(true);
}
};
TiddlyWiki.prototype.addTiddlerFields = function(title,fields)
{
var tiddler = this.fetchTiddler(title);
if(!tiddler)
return;
merge(tiddler.fields,fields);
tiddler.changed();
tiddler.incChangeCount();
this.notify(title,true);
this.setDirty(true);
};
// Store tiddler in TiddlyWiki instance
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator)
{
var tiddler;
if(title instanceof Tiddler) {
tiddler = title;
title = tiddler.title;
newTitle = title;
} else {
tiddler = this.fetchTiddler(title);
if(tiddler) {
created = created || tiddler.created; // Preserve created date
creator = creator || tiddler.creator;
this.deleteTiddler(title);
} else {
created = created || modified;
tiddler = new Tiddler();
}
fields = merge(merge({},fields),config.defaultCustomFields,true);
tiddler.set(newTitle,newBody,modifier,modified,tags,created,fields,creator);
}
this.addTiddler(tiddler);
if(clearChangeCount)
tiddler.clearChangeCount();
else
tiddler.incChangeCount();
if(title != newTitle)
this.notify(title,true);
this.notify(newTitle,true);
this.setDirty(true);
return tiddler;
};
TiddlyWiki.prototype.incChangeCount = function(title)
{
var tiddler = this.fetchTiddler(title);
if(tiddler)
tiddler.incChangeCount();
};
TiddlyWiki.prototype.getLoader = function()
{
if(!this.loader)
this.loader = new TW21Loader();
return this.loader;
};
TiddlyWiki.prototype.getSaver = function()
{
if(!this.saver)
this.saver = new TW21Saver();
return this.saver;
};
// Return all tiddlers formatted as an HTML string
TiddlyWiki.prototype.allTiddlersAsHtml = function()
{
return this.getSaver().externalize(store);
};
// Load contents of a TiddlyWiki from an HTML DIV
TiddlyWiki.prototype.loadFromDiv = function(src,idPrefix,noUpdate)
{
this.idPrefix = idPrefix;
var storeElem = (typeof src == "string") ? document.getElementById(src) : src;
if(!storeElem)
return;
var tiddlers = this.getLoader().loadTiddlers(this,storeElem.childNodes);
console.log('tiddlers loaded from div', tiddlers);
this.setDirty(false);
if(!noUpdate) {
var i;
for(i = 0;i<tiddlers.length; i++)
tiddlers[i].changed();
}
jQuery(document).trigger("loadTiddlers");
};
// Load contents of a TiddlyWiki from a string
// Returns null if there's an error
TiddlyWiki.prototype.importTiddlyWiki = function(text)
{
var posDiv = locateStoreArea(text);
if(!posDiv)
return null;
var content = "<" + "html><" + "body>" + text.substring(posDiv[0],posDiv[1] + endSaveArea.length) + "<" + "/body><" + "/html>";
// Create the iframe
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var doc = iframe.document;
if(iframe.contentDocument)
doc = iframe.contentDocument; // For NS6
else if(iframe.contentWindow)
doc = iframe.contentWindow.document; // For IE5.5 and IE6
// Put the content in the iframe
doc.open();
doc.writeln(content);
doc.close();
// Load the content into a TiddlyWiki() object
var storeArea = doc.getElementById("storeArea");
this.loadFromDiv(storeArea,"store");
// Get rid of the iframe
iframe.parentNode.removeChild(iframe);
return this;
};
TiddlyWiki.prototype.updateTiddlers = function()
{
this.tiddlersUpdated = true;
this.forEachTiddler(function(title,tiddler) {
tiddler.changed();
});
};
// Return an array of tiddlers matching a search regular expression
TiddlyWiki.prototype.search = function(searchRegExp,sortField,excludeTag,match)
{
var candidates = this.reverseLookup("tags",excludeTag,!!match);
var t,results = [];
for(t=0; t<candidates.length; t++) {
if((candidates[t].title.search(searchRegExp) != -1) || (candidates[t].text.search(searchRegExp) != -1))
results.push(candidates[t]);
}
if(!sortField)
sortField = "title";
results.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
return results;
};
// Returns a list of all tags in use
// excludeTag - if present, excludes tags that are themselves tagged with excludeTag
// Returns an array of arrays where [tag][0] is the name of the tag and [tag][1] is the number of occurances
TiddlyWiki.prototype.getTags = function(excludeTag)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
var g,c;
for(g=0; g<tiddler.tags.length; g++) {
var tag = tiddler.tags[g];
var n = true;
for(c=0; c<results.length; c++) {
if(results[c][0] == tag) {
n = false;
results[c][1]++;
}
}
if(n && excludeTag) {
var t = this.fetchTiddler(tag);
if(t && t.isTagged(excludeTag))
n = false;
}
if(n)
results.push([tag,1]);
}
});
results.sort(function(a,b) {return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : (a[0].toLowerCase() == b[0].toLowerCase() ? 0 : +1);});
return results;
};
// Return an array of the tiddlers that are tagged with a given tag
TiddlyWiki.prototype.getTaggedTiddlers = function(tag,sortField)
{
return this.reverseLookup("tags",tag,true,sortField);
};
TiddlyWiki.prototype.getValueTiddlers = function(field,value,sortField)
{
return this.reverseLookup(field,value,true,sortField);
};
// Return an array of the tiddlers that link to a given tiddler
TiddlyWiki.prototype.getReferringTiddlers = function(title,unusedParameter,sortField)
{
if(!this.tiddlersUpdated)
this.updateTiddlers();
return this.reverseLookup("links",title,true,sortField);
};
// Return an array of the tiddlers that do or do not have a specified entry in the specified storage array (ie, "links" or "tags")
// lookupMatch == true to match tiddlers, false to exclude tiddlers
TiddlyWiki.prototype.reverseLookup = function(lookupField,lookupValue,lookupMatch,sortField)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
var f = !lookupMatch;
var values;
if(["links", "tags"].contains(lookupField)) {
values = tiddler[lookupField];
} else {
var accessor = TiddlyWiki.standardFieldAccess[lookupField];
if(accessor) {
values = [ accessor.get(tiddler) ];
} else {
values = tiddler.fields[lookupField] ? [tiddler.fields[lookupField]] : [];
}
}
var lookup;
for(lookup=0; lookup<values.length; lookup++) {
if(values[lookup] == lookupValue)
f = lookupMatch;
}
if(f)
results.push(tiddler);
});
if(!sortField)
sortField = "title";
return this.sortTiddlers(results,sortField);
};
// Return the tiddlers as a sorted array
TiddlyWiki.prototype.getTiddlers = function(field,excludeTag)
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(excludeTag == undefined || !tiddler.isTagged(excludeTag))
results.push(tiddler);
});
if(field)
results.sort(function(a,b) {return a[field] < b[field] ? -1 : (a[field] == b[field] ? 0 : +1);});
return results;
};
// Return array of names of tiddlers that are referred to but not defined
TiddlyWiki.prototype.getMissingLinks = function()
{
if(!this.tiddlersUpdated)
this.updateTiddlers();
var results = [];
this.forEachTiddler(function (title,tiddler) {
if(tiddler.isTagged("excludeMissing") || tiddler.isTagged("systemConfig"))
return;
var n;
for(n=0; n<tiddler.links.length;n++) {
var link = tiddler.links[n];
if(this.getTiddlerText(link,null) == null && !this.isShadowTiddler(link) && !config.macros[link])
results.pushUnique(link);
}
});
results.sort();
return results;
};
// Return an array of names of tiddlers that are defined but not referred to
TiddlyWiki.prototype.getOrphans = function()
{
var results = [];
this.forEachTiddler(function (title,tiddler) {
if(this.getReferringTiddlers(title).length == 0 && !tiddler.isTagged("excludeLists"))
results.push(title);
});
results.sort();
return results;
};
// Return an array of names of all the shadow tiddlers
TiddlyWiki.prototype.getShadowed = function()
{
var t,results = [];
for(t in config.shadowTiddlers) {
if(this.isShadowTiddler(t))
results.push(t);
}
results.sort();
return results;
};
// Return an array of tiddlers that have been touched since they were downloaded or created
TiddlyWiki.prototype.getTouched = function()
{
var results = [];
this.forEachTiddler(function(title,tiddler) {
if(tiddler.isTouched())
results.push(tiddler);
});
results.sort();
return results;
};
// Resolves a Tiddler reference or tiddler title into a Tiddler object, or null if it doesn't exist
TiddlyWiki.prototype.resolveTiddler = function(tiddler)
{
var t = (typeof tiddler == "string") ? this.getTiddler(tiddler) : tiddler;
return t instanceof Tiddler ? t : null;
};
// Sort a list of tiddlers
TiddlyWiki.prototype.sortTiddlers = function(tiddlers,field)
{
var asc = +1;
switch(field.substr(0,1)) {
case "-":
asc = -1;
field = field.substr(1);
break;
case "+":
field = field.substr(1);
break;
}
if(TiddlyWiki.standardFieldAccess[field]) {
if(field=="title") {
tiddlers.sort(function(a,b) {return a[field].toLowerCase() < b[field].toLowerCase() ? -asc : (a[field].toLowerCase() == b[field].toLowerCase() ? 0 : asc);});
} else {
tiddlers.sort(function(a,b) {return a[field] < b[field] ? -asc : (a[field] == b[field] ? 0 : asc);});
}
} else {
tiddlers.sort(function(a,b) {return a.fields[field] < b.fields[field] ? -asc : (a.fields[field] == b.fields[field] ? 0 : +asc);});
}
return tiddlers;
};
//--
//-- Filter a list of tiddlers
//--
config.filters = {
tiddler: function(results,match) {
var title = match[1]||match[4];
var tiddler = this.fetchTiddler(title);
if(tiddler) {
results.pushUnique(tiddler);
} else if(this.isShadowTiddler(title)) {
tiddler = new Tiddler();
tiddler.set(title,this.getTiddlerText(title));
results.pushUnique(tiddler);
} else {
results.pushUnique(new Tiddler(title));
}
return results;
},
tag: function(results,match) {
var m,matched = this.getTaggedTiddlers(match[3]);
for(m=0; m<matched.length; m++) {
results.pushUnique(matched[m]);
}
return results;
},
sort: function(results,match) {
return this.sortTiddlers(results,match[3]);
},
limit: function(results,match) {
return results.slice(0,parseInt(match[3],10));
},
field: function(results,match) {
var m,matched = this.getValueTiddlers(match[2],match[3]);
for (m = 0; m < matched.length; m++) {
results.pushUnique(matched[m]);
}
return results;
}
};
// Filter a list of tiddlers
TiddlyWiki.prototype.filterTiddlers = function(filter)
{
var re = /([^\s\[\]]+)|(?:\[([ \w\.\-]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;
var results = [];
if(filter) {
var match = re.exec(filter);
while(match) {
var handler = (match[1]||match[4])?'tiddler':config.filters[match[2]]?match[2]:'field';
results = config.filters[handler].call(this,results,match);
match = re.exec(filter);
}
}
return results;
};
// Returns true if path is a valid field name (path),
// i.e. a sequence of identifiers, separated by "."
TiddlyWiki.isValidFieldName = function(name)
{
var match = /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*/.exec(name);
return match && (match[0] == name);
};
// Throws an exception when name is not a valid field name.
TiddlyWiki.checkFieldName = function(name)
{
if(!TiddlyWiki.isValidFieldName(name))
throw config.messages.invalidFieldName.format([name]);
};
function StringFieldAccess(n,readOnly)
{
this.set = readOnly ?
function(t,v) {if(v != t[n]) throw config.messages.fieldCannotBeChanged.format([n]);} :
function(t,v) {if(v != t[n]) {t[n] = v; return true;}};
this.get = function(t) {return t[n];};
}
function DateFieldAccess(n)
{
this.set = function(t,v) {
var d = v instanceof Date ? v : Date.convertFromYYYYMMDDHHMM(v);
if(d != t[n]) {
t[n] = d; return true;
}
};
this.get = function(t) {return t[n].convertToYYYYMMDDHHMM();};
}
function LinksFieldAccess(n)
{
this.set = function(t,v) {
var s = (typeof v == "string") ? v.readBracketedList() : v;
if(s.toString() != t[n].toString()) {
t[n] = s; return true;
}
};
this.get = function(t) {return String.encodeTiddlyLinkList(t[n]);};
}
TiddlyWiki.standardFieldAccess = {
// The set functions return true when setting the data has changed the value.
"title": new StringFieldAccess("title",true),
// Handle the "tiddler" field name as the title
"tiddler": new StringFieldAccess("title",true),
"text": new StringFieldAccess("text"),
"modifier": new StringFieldAccess("modifier"),
"modified": new DateFieldAccess("modified"),
"creator": new StringFieldAccess("creator"),
"created": new DateFieldAccess("created"),
"tags": new LinksFieldAccess("tags")
};
TiddlyWiki.isStandardField = function(name)
{
return TiddlyWiki.standardFieldAccess[name] != undefined;
};
// Sets the value of the given field of the tiddler to the value.
// Setting an ExtendedField's value to null or undefined removes the field.
// Setting a namespace to undefined removes all fields of that namespace.
// The fieldName is case-insensitive.
// All values will be converted to a string value.
TiddlyWiki.prototype.setValue = function(tiddler,fieldName,value)
{
TiddlyWiki.checkFieldName(fieldName);
var t = this.resolveTiddler(tiddler);
if(!t)
return;
fieldName = fieldName.toLowerCase();
var isRemove = (value === undefined) || (value === null);
var accessor = TiddlyWiki.standardFieldAccess[fieldName];
if(accessor) {
if(isRemove)
// don't remove StandardFields
return;
var h = TiddlyWiki.standardFieldAccess[fieldName];
if(!h.set(t,value))
return;
} else {
var oldValue = t.fields[fieldName];
if(isRemove) {
if(oldValue !== undefined) {
// deletes a single field
delete t.fields[fieldName];
} else {
// no concrete value is defined for the fieldName
// so we guess this is a namespace path.
// delete all fields in a namespace
var re = new RegExp("^"+fieldName+"\\.");
var dirty = false;
var n;
for(n in t.fields) {
if(n.match(re)) {
delete t.fields[n];
dirty = true;
}
}
if(!dirty)
return;
}
} else {
// the "normal" set case. value is defined (not null/undefined)
// For convenience provide a nicer conversion Date->String
value = value instanceof Date ? value.convertToYYYYMMDDHHMMSSMMM() : String(value);
if(oldValue == value)
return;
t.fields[fieldName] = value;
}
}
// When we are here the tiddler/store really was changed.
this.notify(t.title,true);
if(!fieldName.match(/^temp\./))
this.setDirty(true);
};
// Returns the value of the given field of the tiddler.
// The fieldName is case-insensitive.
// Will only return String values (or undefined).
TiddlyWiki.prototype.getValue = function(tiddler,fieldName)
{
var t = this.resolveTiddler(tiddler);
if(!t)
return undefined;
if(fieldName.indexOf(config.textPrimitives.sectionSeparator) === 0 || fieldName.indexOf(config.textPrimitives.sliceSeparator) === 0) {
var sliceType = fieldName.substr(0, 2);
var sliceName = fieldName.substring(2);
return store.getTiddlerText("%0%1%2".format(t.title,sliceType,sliceName));
} else {
fieldName = fieldName.toLowerCase();
var accessor = TiddlyWiki.standardFieldAccess[fieldName];
if(accessor) {
return accessor.get(t);
}
}
return t.fields[fieldName];
};
// Calls the callback function for every field in the tiddler.
// When callback function returns a non-false value the iteration stops
// and that value is returned.
// The order of the fields is not defined.
// @param callback a function(tiddler,fieldName,value).
TiddlyWiki.prototype.forEachField = function(tiddler,callback,onlyExtendedFields)
{
var t = this.resolveTiddler(tiddler);
if(!t)
return undefined;
var n,result;
for(n in t.fields) {
result = callback(t,n,t.fields[n]);
if(result)
return result;
}
if(onlyExtendedFields)
return undefined;
for(n in TiddlyWiki.standardFieldAccess) {
if(n != "tiddler") {
// even though the "title" field can also be referenced through the name "tiddler"
// we only visit this field once.
result = callback(t,n,TiddlyWiki.standardFieldAccess[n].get(t));
if(result)
return result;
}
}
return undefined;
};
//--
//-- Story functions
//--
function Story(containerId,idPrefix)
{
this.container = containerId;
this.idPrefix = idPrefix;
this.highlightRegExp = null;
this.tiddlerId = function(title) {
title = title.replace(/_/g, "__").replace(/ /g, "_");
var id = this.idPrefix + title;
return id==this.container ? this.idPrefix + "_" + title : id;
};
this.containerId = function() {
return this.container;
};
}
Story.prototype.getTiddler = function(title)
{
return document.getElementById(this.tiddlerId(title));
};
Story.prototype.getContainer = function()
{
return document.getElementById(this.containerId());
};
Story.prototype.forEachTiddler = function(fn)
{
var place = this.getContainer();
if(!place)
return;
var e = place.firstChild;
while(e) {
var n = e.nextSibling;
var title = e.getAttribute("tiddler");
if(title) {
fn.call(this,title,e);
}
e = n;
}
};
Story.prototype.displayDefaultTiddlers = function()
{
this.displayTiddlers(null,store.filterTiddlers(store.getTiddlerText("DefaultTiddlers")),null,null,null,String.encodeHashMap(config.defaultCustomFields));
};
Story.prototype.displayTiddlers = function(srcElement,titles,template,animate,unused,customFields,toggle)
{
var t;
for(t = titles.length-1;t>=0;t--)
this.displayTiddler(srcElement,titles[t],template,animate,unused,customFields);
};
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc)
{
var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem) {
if(toggle) {
if(tiddlerElem.getAttribute("dirty") != "true")
this.closeTiddler(title,true);
} else {
this.refreshTiddler(title,template,false,customFields);
}
} else {
var place = this.getContainer();
var before = this.positionTiddler(srcElement);
tiddlerElem = this.createTiddler(place,before,title,template,customFields);
}
if(animationSrc && typeof animationSrc !== "string") {
srcElement = animationSrc;
}
if(srcElement && typeof srcElement !== "string") {
if(config.options.chkAnimate && (animate == undefined || animate == true) && anim && typeof Zoomer == "function" && typeof Scroller == "function")
anim.startAnimating(new Zoomer(title,srcElement,tiddlerElem),new Scroller(tiddlerElem));
else
window.scrollTo(0,ensureVisible(tiddlerElem));
}
return tiddlerElem;
};
Story.prototype.positionTiddler = function(srcElement)
{
var place = this.getContainer();
var before = null;
if(typeof srcElement == "string") {
switch(srcElement) {
case "top":
before = place.firstChild;
break;
case "bottom":
before = null;
break;
}
} else {
var after = this.findContainingTiddler(srcElement);
if(after == null) {
before = place.firstChild;
} else if(after.nextSibling) {
before = after.nextSibling;
if(before.nodeType != 1)
before = null;
}
}
return before;
};
Story.prototype.createTiddler = function(place,before,title,template,customFields)
{
var tiddlerElem = createTiddlyElement(null,"div",this.tiddlerId(title),"tiddler");
tiddlerElem.setAttribute("refresh","tiddler");
if(customFields)
tiddlerElem.setAttribute("tiddlyFields",customFields);
place.insertBefore(tiddlerElem,before);
var defaultText = null;
if(!store.tiddlerExists(title) && !store.isShadowTiddler(title))
defaultText = this.loadMissingTiddler(title,customFields);
this.refreshTiddler(title,template,false,customFields,defaultText);
return tiddlerElem;
};
Story.prototype.loadMissingTiddler = function(title,fields,callback)
{
var getTiddlerCallback = function(context)
{
if(context.status) {
var t = context.tiddler;
if(!t.created)
t.created = new Date();
if(!t.modified)
t.modified = t.created;
var dirty = store.isDirty();
context.tiddler = store.saveTiddler(t.title,t.title,t.text,t.modifier,t.modified,t.tags,t.fields,true,t.created,t.creator);
if(window.location.protocol != "file:")
store.setDirty(dirty);
autoSaveChanges();
} else {
story.refreshTiddler(context.title,null,true);
}
context.adaptor.close();
if(callback) {
callback(context);
}
};
var tiddler = new Tiddler(title);
tiddler.fields = typeof fields == "string" ? fields.decodeHashMap() : fields||{};
var context = {serverType:tiddler.getServerType()};
if(!context.serverType)
return "";
context.host = tiddler.fields['server.host'];
context.workspace = tiddler.fields['server.workspace'];
var adaptor = new config.adaptors[context.serverType]();
adaptor.getTiddler(title,context,null,getTiddlerCallback);
return config.messages.loadingMissingTiddler.format([title,context.serverType,context.host,context.workspace]);
};
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
if(!template)
template = DEFAULT_VIEW_TEMPLATE;
if(template == DEFAULT_VIEW_TEMPLATE || template == DEFAULT_EDIT_TEMPLATE)
template = config.tiddlerTemplates[template];
return template;
};
Story.prototype.getTemplateForTiddler = function(title,template,tiddler)
{
return store.getRecursiveTiddlerText(template,null,10);
};
Story.prototype.refreshTiddler = function(title,template,force,customFields,defaultText)
{
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem) {
if(tiddlerElem.getAttribute("dirty") == "true" && !force)
return tiddlerElem;
template = this.chooseTemplateForTiddler(title,template);
var currTemplate = tiddlerElem.getAttribute("template");
if((template != currTemplate) || force) {
var tiddler = store.getTiddler(title);
if(!tiddler) {
tiddler = new Tiddler();
if(store.isShadowTiddler(title)) {
var tags = [];
tiddler.set(title,store.getTiddlerText(title),config.views.wikified.shadowModifier,version.date,tags,version.date);
} else {
var text = template=="EditTemplate" ?
config.views.editor.defaultText.format([title]) :
config.views.wikified.defaultText.format([title]);
text = defaultText || text;
var fields = customFields ? customFields.decodeHashMap() : null;
tiddler.set(title,text,config.views.wikified.defaultModifier,version.date,[],version.date,fields);
}
}
tiddlerElem.setAttribute("tags",tiddler.tags.join(" "));
tiddlerElem.setAttribute("tiddler",title);
tiddlerElem.setAttribute("template",template);
tiddlerElem.onmouseover = this.onTiddlerMouseOver;
tiddlerElem.onmouseout = this.onTiddlerMouseOut;
tiddlerElem.ondblclick = this.onTiddlerDblClick;
tiddlerElem[window.event?"onkeydown":"onkeypress"] = this.onTiddlerKeyPress;
tiddlerElem.innerHTML = this.getTemplateForTiddler(title,template,tiddler);
applyHtmlMacros(tiddlerElem,tiddler);
if(store.getTaggedTiddlers(title).length > 0)
jQuery(tiddlerElem).addClass("isTag");
else
jQuery(tiddlerElem).removeClass("isTag");
if(store.tiddlerExists(title)) {
jQuery(tiddlerElem).removeClass("shadow");
jQuery(tiddlerElem).removeClass("missing");
} else {
jQuery(tiddlerElem).addClass(store.isShadowTiddler(title) ? "shadow" : "missing");
}
if(customFields)
this.addCustomFields(tiddlerElem,customFields);
}
}
return tiddlerElem;
};
Story.prototype.addCustomFields = function(place,customFields)
{
var fields = customFields.decodeHashMap();
var w = createTiddlyElement(place,"div",null,"customFields");
w.style.display = "none";
var t;
for(t in fields) {
var e = document.createElement("input");
e.setAttribute("type","text");
e.setAttribute("value",fields[t]);
w.appendChild(e);
e.setAttribute("edit",t);
}
};
Story.prototype.refreshAllTiddlers = function(force)
{
var e = this.getContainer().firstChild;
while(e) {
var template = e.getAttribute("template");
if(template && e.getAttribute("dirty") != "true") {
this.refreshTiddler(e.getAttribute("tiddler"),force ? null : template,true);
}
e = e.nextSibling;
}
};
Story.prototype.onTiddlerMouseOver = function(e)
{
jQuery(this).addClass("selected");
};
Story.prototype.onTiddlerMouseOut = function(e)
{
jQuery(this).removeClass("selected");
};
Story.prototype.onTiddlerDblClick = function(ev)
{
var e = ev || window.event;
var target = resolveTarget(e);
if(target && target.nodeName.toLowerCase() != "input" && target.nodeName.toLowerCase() != "textarea") {
if(document.selection && document.selection.empty)
document.selection.empty();
config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return true;
}
return false;
};
Story.prototype.onTiddlerKeyPress = function(ev)
{
var e = ev || window.event;
clearMessage();
var consume = false;
var title = this.getAttribute("tiddler");
var target = resolveTarget(e);
switch(e.keyCode) {
case 9: // Tab
var ed = story.getTiddlerField(title,"text");
if(target.tagName.toLowerCase() == "input" && ed.value==config.views.editor.defaultText.format([title])) {
// moving from input field and editor still contains default text, so select it
ed.focus();
ed.select();
consume = true;
}
if(config.options.chkInsertTabs && target.tagName.toLowerCase() == "textarea") {
replaceSelection(target,String.fromCharCode(9));
consume = true;
}
if(config.isOpera) {
target.onblur = function() {
this.focus();
this.onblur = null;
};
}
break;
case 13: // Ctrl-Enter
case 10: // Ctrl-Enter on IE PC
case 77: // Ctrl-Enter is "M" on some platforms
if(e.ctrlKey) {
blurElement(this);
config.macros.toolbar.invokeCommand(this,"defaultCommand",e);
consume = true;
}
break;
case 27: // Escape
blurElement(this);
config.macros.toolbar.invokeCommand(this,"cancelCommand",e);
consume = true;
break;
}
e.cancelBubble = consume;
if(consume) {
if(e.stopPropagation) e.stopPropagation(); // Stop Propagation
e.returnValue = true; // Cancel The Event in IE
if(e.preventDefault ) e.preventDefault(); // Cancel The Event in Moz
}
return !consume;
};
Story.prototype.getTiddlerField = function(title,field)
{
var tiddlerElem = this.getTiddler(title);
var e = null;
if(tiddlerElem) {
var t,children = tiddlerElem.getElementsByTagName("*");
for(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;
}
}
}
return e;
};
Story.prototype.focusTiddler = function(title,field)
{
var e = this.getTiddlerField(title,field);
if(e) {
e.focus();
e.select();
}
};
Story.prototype.blurTiddler = function(title)
{
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem && tiddlerElem.focus && tiddlerElem.blur) {
tiddlerElem.focus();
tiddlerElem.blur();
}
};
Story.prototype.setTiddlerField = function(title,tag,mode,field)
{
var c = this.getTiddlerField(title,field);
var tags = c.value.readBracketedList();
tags.setItem(tag,mode);
c.value = String.encodeTiddlyLinkList(tags);
};
Story.prototype.setTiddlerTag = function(title,tag,mode)
{
this.setTiddlerField(title,tag,mode,"tags");
};
Story.prototype.closeTiddler = function(title,animate,unused)
{
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem) {
clearMessage();
this.scrubTiddler(tiddlerElem);
if(config.options.chkAnimate && animate && anim && typeof Slider == "function")
anim.startAnimating(new Slider(tiddlerElem,false,null,"all"));
else {
jQuery(tiddlerElem).remove();
}
}
};
Story.prototype.scrubTiddler = function(tiddlerElem)
{
tiddlerElem.id = null;
};
Story.prototype.setDirty = function(title,dirty)
{
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem)
tiddlerElem.setAttribute("dirty",dirty ? "true" : "false");
};
Story.prototype.isDirty = function(title)
{
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem)
return tiddlerElem.getAttribute("dirty") == "true";
return null;
};
Story.prototype.areAnyDirty = function()
{
var r = false;
this.forEachTiddler(function(title,element) {
if(this.isDirty(title))
r = true;
});
return r;
};
Story.prototype.closeAllTiddlers = function(exclude)
{
clearMessage();
this.forEachTiddler(function(title,element) {
if((title != exclude) && element.getAttribute("dirty") != "true")
this.closeTiddler(title);
});
window.scrollTo(0,ensureVisible(this.container));
};
Story.prototype.isEmpty = function()
{
var place = this.getContainer();
return place && place.firstChild == null;
};
Story.prototype.search = function(text,useCaseSensitive,useRegExp)
{
this.closeAllTiddlers();
highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(),useCaseSensitive ? "mg" : "img");
var matches = store.search(highlightHack,"title","excludeSearch");
this.displayTiddlers(null,matches);
highlightHack = null;
var q = useRegExp ? "/" : "'";
if(matches.length > 0)
displayMessage(config.macros.search.successMsg.format([matches.length.toString(),q + text + q]));
else
displayMessage(config.macros.search.failureMsg.format([q + text + q]));
};
Story.prototype.findContainingTiddler = function(e)
{
while(e && !jQuery(e).hasClass("tiddler")) {
e = jQuery(e).hasClass("popup") && Popup.stack[0] ? Popup.stack[0].root : e.parentNode;
}
return e;
};
Story.prototype.gatherSaveFields = function(e,fields)
{
if(e && e.getAttribute) {
var f = e.getAttribute("edit");
if(f)
fields[f] = e.value.replace(/\r/mg,"");
if(e.hasChildNodes()) {
var t,c = e.childNodes;
for(t=0; t<c.length; t++)
this.gatherSaveFields(c[t],fields);
}
}
};
Story.prototype.hasChanges = function(title)
{
var e = this.getTiddler(title);
if(e) {
var fields = {};
this.gatherSaveFields(e,fields);
if(store.fetchTiddler(title)) {
var n;
for(n in fields) {
if(store.getValue(title,n) != fields[n]) //# tiddler changed
return true;
}
} else {
if(store.isShadowTiddler(title) && store.getShadowTiddlerText(title) == fields.text) { //# not checking for title or tags
return false;
} else { //# changed shadow or new tiddler
return true;
}
}
}
return false;
};
Story.prototype.saveTiddler = function(title,minorUpdate)
{
var tiddlerElem = this.getTiddler(title);
if(tiddlerElem) {
var fields = {};
this.gatherSaveFields(tiddlerElem,fields);
var newTitle = fields.title || title;
if(!store.tiddlerExists(newTitle)) {
newTitle = newTitle.trim();
var creator = config.options.txtUserName;
}
if(store.tiddlerExists(newTitle) && newTitle != title) {
if(!confirm(config.messages.overwriteWarning.format([newTitle.toString()])))
return null;
title = newTitle;
}
if(newTitle != title)
this.closeTiddler(newTitle,false);
tiddlerElem.id = this.tiddlerId(newTitle);
tiddlerElem.setAttribute("tiddler",newTitle);
tiddlerElem.setAttribute("template",DEFAULT_VIEW_TEMPLATE);
tiddlerElem.setAttribute("dirty","false");
if(config.options.chkForceMinorUpdate)
minorUpdate = !minorUpdate;
if(!store.tiddlerExists(newTitle))
minorUpdate = false;
var newDate = new Date();
if(store.tiddlerExists(title)) {
var t = store.fetchTiddler(title);
var extendedFields = t.fields;
creator = t.creator;
} else {
extendedFields = merge({},config.defaultCustomFields);
}
var n;
for(n in fields) {
if(!TiddlyWiki.isStandardField(n))
extendedFields[n] = fields[n];
}
var tiddler = store.saveTiddler(title,newTitle,fields.text,minorUpdate ? undefined : config.options.txtUserName,minorUpdate ? undefined : newDate,fields.tags,extendedFields,null,null,creator);
autoSaveChanges(null,[tiddler]);
return newTitle;
}
return null;
};
Story.prototype.permaView = function()
{
var links = [];
this.forEachTiddler(function(title,element) {
links.push(String.encodeTiddlyLink(title));
});
var t = encodeURIComponent(links.join(" "));
if(t == "")
t = "#";
if(window.location.hash != t)
window.location.hash = t;
};
Story.prototype.switchTheme = function(theme)
{
if(safeMode)
return;
var isAvailable = function(title) {
var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
if(s!=-1)
title = title.substr(0,s);
return store.tiddlerExists(title) || store.isShadowTiddler(title);
};
var getSlice = function(theme,slice) {
var r;
if(readOnly)
r = store.getTiddlerSlice(theme,slice+"ReadOnly") || store.getTiddlerSlice(theme,"Web"+slice);
r = r || store.getTiddlerSlice(theme,slice);
if(r && r.indexOf(config.textPrimitives.sectionSeparator)==0)
r = theme + r;
return isAvailable(r) ? r : slice;
};
var replaceNotification = function(i,name,theme,slice) {
var newName = getSlice(theme,slice);
if(name!=newName && store.namedNotifications[i].name==name) {
store.namedNotifications[i].name = newName;
return newName;
}
return name;
};
var pt = config.refresherData.pageTemplate;
var vi = DEFAULT_VIEW_TEMPLATE;
var vt = config.tiddlerTemplates[vi];
var ei = DEFAULT_EDIT_TEMPLATE;
var et = config.tiddlerTemplates[ei];
var i;
for(i=0; i<config.notifyTiddlers.length; i++) {
var name = config.notifyTiddlers[i].name;
switch(name) {
case "PageTemplate":
config.refresherData.pageTemplate = replaceNotification(i,config.refresherData.pageTemplate,theme,name);
break;
case "StyleSheet":
removeStyleSheet(config.refresherData.styleSheet);
config.refresherData.styleSheet = replaceNotification(i,config.refresherData.styleSheet,theme,name);
break;
case "ColorPalette":
config.refresherData.colorPalette = replaceNotification(i,config.refresherData.colorPalette,theme,name);
break;
default:
break;
}
}
config.tiddlerTemplates[vi] = getSlice(theme,"ViewTemplate");
config.tiddlerTemplates[ei] = getSlice(theme,"EditTemplate");
if(!startingUp) {
if(config.refresherData.pageTemplate!=pt || config.tiddlerTemplates[vi]!=vt || config.tiddlerTemplates[ei]!=et) {
refreshAll();
this.refreshAllTiddlers(true);
} else {
setStylesheet(store.getRecursiveTiddlerText(config.refresherData.styleSheet,"",10),config.refreshers.styleSheet);
}
config.options.txtTheme = theme;
saveOption("txtTheme");
}
};
//--
//-- Manager UI for groups of tiddlers
//--
config.macros.plugins.handler = function(place,macroName,params,wikifier,paramString)
{
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
listWrapper.setAttribute("refresh","macro");
listWrapper.setAttribute("macroName","plugins");
listWrapper.setAttribute("params",paramString);
this.refresh(listWrapper,paramString);
};
config.macros.plugins.refresh = function(listWrapper,params)
{
var me = config.macros.plugins;
var wizard = new Wizard(listWrapper);
var selectedRows = [];
ListView.forEachSelector(listWrapper,function(e,rowName) {
if(e.checked)
selectedRows.push(e.getAttribute("rowName"));
});
jQuery(listWrapper).empty();
params = params.parseParams("anon");
var plugins = installedPlugins.slice(0);
var t,tiddler,p;
var configTiddlers = store.getTaggedTiddlers("systemConfig");
for(t=0; t<configTiddlers.length; t++) {
tiddler = configTiddlers[t];
if(plugins.findByField("title",tiddler.title) == null) {
p = getPluginInfo(tiddler);
p.executed = false;
p.log.splice(0,0,this.skippedText);
plugins.push(p);
}
}
for(t=0; t<plugins.length; t++) {
p = plugins[t];
p.size = p.tiddler.text ? p.tiddler.text.length : 0;
p.forced = p.tiddler.isTagged("systemConfigForce");
p.disabled = p.tiddler.isTagged("systemConfigDisable");
p.Selected = selectedRows.indexOf(plugins[t].title) != -1;
}
if(plugins.length == 0) {
createTiddlyElement(listWrapper,"em",null,null,this.noPluginText);
wizard.setButtons([]);
} else {
var template = readOnly ? this.listViewTemplateReadOnly : this.listViewTemplate;
var listView = ListView.create(listWrapper,plugins,template,this.onSelectCommand);
wizard.setValue("listView",listView);
if(!readOnly) {
wizard.setButtons([
{caption: me.removeLabel, tooltip: me.removePrompt, onClick: me.doRemoveTag},
{caption: me.deleteLabel, tooltip: me.deletePrompt, onClick: me.doDelete}
]);
}
}
};
config.macros.plugins.doRemoveTag = function(e)
{
var wizard = new Wizard(this);
var listView = wizard.getValue("listView");
var rowNames = ListView.getSelectedRows(listView);
if(rowNames.length == 0) {
alert(config.messages.nothingSelected);
} else {
var t;
for(t=0; t<rowNames.length; t++) {
store.setTiddlerTag(rowNames[t],false,"systemConfig");
}
autoSaveChanges();
}
};
config.macros.plugins.doDelete = function(e)
{
var wizard = new Wizard(this);
var listView = wizard.getValue("listView");
var rowNames = ListView.getSelectedRows(listView);
if(rowNames.length == 0) {
alert(config.messages.nothingSelected);
} else {
if(confirm(config.macros.plugins.confirmDeleteText.format([rowNames.join(", ")]))) {
var t;
for(t=0; t<rowNames.length; t++) {
store.removeTiddler(rowNames[t]);
story.closeTiddler(rowNames[t],true);
}
}
autoSaveChanges();
}
};
//--
//-- Message area
//--
function getMessageDiv()
{
var msgArea = document.getElementById("messageArea");
if(!msgArea)
return null;
if(!msgArea.hasChildNodes())
createTiddlyButton(createTiddlyElement(msgArea,"div",null,"messageToolbar"),
config.messages.messageClose.text,
config.messages.messageClose.tooltip,
clearMessage);
msgArea.style.display = "block";
return createTiddlyElement(msgArea,"div");
}
function displayMessage(text,linkText)
{
var e = getMessageDiv();
if(!e) {
alert(text);
return;
}
if(linkText) {
var link = createTiddlyElement(e,"a",null,null,text);
link.href = linkText;
link.target = "_blank";
} else {
e.appendChild(document.createTextNode(text));
}
}
function clearMessage()
{
var msgArea = document.getElementById("messageArea");
if(msgArea) {
jQuery(msgArea).empty();
msgArea.style.display = "none";
}
return false;
}
//--
//-- Refresh mechanism
//--
config.notifyTiddlers = [
{name: "SystemSettings", notify: onSystemSettingsChange},
{name: "StyleSheetLayout", notify: refreshStyles},
{name: "StyleSheetColors", notify: refreshStyles},
{name: "StyleSheet", notify: refreshStyles},
{name: "StyleSheetPrint", notify: refreshStyles},
{name: "PageTemplate", notify: refreshPageTemplate},
{name: "SiteTitle", notify: refreshPageTitle},
{name: "SiteSubtitle", notify: refreshPageTitle},
{name: "WindowTitle", notify: refreshPageTitle},
{name: "ColorPalette", notify: refreshColorPalette},
{name: null, notify: refreshDisplay}
];
config.refreshers = {
link: function(e,changeList)
{
var title = e.getAttribute("tiddlyLink");
refreshTiddlyLink(e,title);
return true;
},
tiddler: function(e,changeList)
{
var title = e.getAttribute("tiddler");
var template = e.getAttribute("template");
if(changeList && (changeList.indexOf && changeList.indexOf(title) != -1) && !story.isDirty(title))
story.refreshTiddler(title,template,true);
else
refreshElements(e,changeList);
return true;
},
content: function(e,changeList)
{
var title = e.getAttribute("tiddler");
var force = e.getAttribute("force");
var args = e.getAttribute("args");
if(force != null || changeList == null || (changeList.indexOf && changeList.indexOf(title) != -1)) {
jQuery(e).empty();
config.macros.tiddler.transclude(e,title,args);
return true;
} else
return false;
},
macro: function(e,changeList)
{
var macro = e.getAttribute("macroName");
var params = e.getAttribute("params");
if(macro)
macro = config.macros[macro];
if(macro && macro.refresh)
macro.refresh(e,params);
return true;
}
};
config.refresherData = {
styleSheet: "StyleSheet",
defaultStyleSheet: "StyleSheet",
pageTemplate: "PageTemplate",
defaultPageTemplate: "PageTemplate",
colorPalette: "ColorPalette",
defaultColorPalette: "ColorPalette"
};
function refreshElements(root,changeList)
{
var c,nodes = root.childNodes;
for(c=0; c<nodes.length; c++) {
var e = nodes[c], type = null;
if(e.getAttribute && (e.tagName ? e.tagName != "IFRAME" : true))
type = e.getAttribute("refresh");
var refresher = config.refreshers[type];
var refreshed = false;
if(refresher != undefined)
refreshed = refresher(e,changeList);
if(e.hasChildNodes() && !refreshed)
refreshElements(e,changeList);
}
}
function applyHtmlMacros(root,tiddler)
{
var e = root.firstChild;
while(e) {
var nextChild = e.nextSibling;
if(e.getAttribute) {
var macro = e.getAttribute("macro");
if(macro) {
e.removeAttribute("macro");
var params = "";
var p = macro.indexOf(" ");
if(p != -1) {
params = macro.substr(p+1);
macro = macro.substr(0,p);
}
invokeMacro(e,macro,params,null,tiddler);
}
}
if(e.hasChildNodes())
applyHtmlMacros(e,tiddler);
e = nextChild;
}
}
function refreshPageTemplate(title)
{
var stash = jQuery("<div/>").appendTo("body").hide()[0];
var display = story.getContainer();
var nodes,t;
if(display) {
nodes = display.childNodes;
for(t=nodes.length-1; t>=0; t--)
stash.appendChild(nodes[t]);
}
var wrapper = document.getElementById("contentWrapper");
var isAvailable = function(title) {
var s = title ? title.indexOf(config.textPrimitives.sectionSeparator) : -1;
if(s!=-1)
title = title.substr(0,s);
return store.tiddlerExists(title) || store.isShadowTiddler(title);
};
if(!title || !isAvailable(title))
title = config.refresherData.pageTemplate;
if(!isAvailable(title))
title = config.refresherData.defaultPageTemplate; //# this one is always avaialable
wrapper.innerHTML = store.getRecursiveTiddlerText(title,null,10);
applyHtmlMacros(wrapper);
refreshElements(wrapper);
display = story.getContainer();
jQuery(display).empty();
if(!display)
display = createTiddlyElement(wrapper,"div",story.containerId());
nodes = stash.childNodes;
for(t=nodes.length-1; t>=0; t--)
display.appendChild(nodes[t]);
jQuery(stash).remove();
}
function refreshDisplay(hint)
{
if(typeof hint == "string")
hint = [hint];
var e = document.getElementById("contentWrapper");
refreshElements(e,hint);
if(backstage.isPanelVisible()) {
e = document.getElementById("backstage");
refreshElements(e,hint);
}
}
function refreshPageTitle()
{
document.title = getPageTitle();
}
function getPageTitle()
{
return wikifyPlainText(store.getTiddlerText("WindowTitle",""),null,tiddler);
}
function refreshStyles(title,doc)
{
setStylesheet(title == null ? "" : store.getRecursiveTiddlerText(title,"",10),title,doc || document);
}
function refreshColorPalette(title)
{
if(!startingUp)
refreshAll();
}
function refreshAll()
{
refreshPageTemplate();
refreshDisplay();
refreshStyles("StyleSheetLayout");
refreshStyles("StyleSheetColors");
refreshStyles(config.refresherData.styleSheet);
refreshStyles("StyleSheetPrint");
}
//--
//-- Option handling
//--
config.optionHandlers = {
'txt': {
get: function(name) {return encodeCookie(config.options[name].toString());},
set: function(name,value) {config.options[name] = decodeCookie(value);}
},
'chk': {
get: function(name) {return config.options[name] ? 'true' : 'false';},
set: function(name,value) {config.options[name] = value == 'true';}
}
};
function setOption(name,value)
{
var optType = name.substr(0,3);
if(config.optionHandlers[optType] && config.optionHandlers[optType].set)
config.optionHandlers[optType].set(name,value);
}
// Gets the value of an option as a string. Most code should just read from config.options.* directly
function getOption(name)
{
var optType = name.substr(0,3);
return config.optionHandlers[optType] && config.optionHandlers[optType].get ? config.optionHandlers[optType].get(name) : null;
}
function loadOptions()
{
if(safeMode)
return;
loadCookies();
loadSystemSettings();
}
// @Deprecated; retained for backwards compatibility
var loadOptionsCookie = loadOptions;
function getCookies()
{
var cookieList = document.cookie.split(';');
var i,cookies = {};
for(i=0; i<cookieList.length; i++) {
var p = cookieList[i].indexOf('=');
if(p != -1) {
var name = cookieList[i].substr(0,p).trim();
var value = cookieList[i].substr(p+1).trim();
cookies[name] = value;
}
}
return cookies;
}
function loadCookies()
{
var i,cookies = getCookies();
if(cookies['TiddlyWiki']) {
cookies = cookies['TiddlyWiki'].decodeHashMap();
}
for(i in cookies) {
if(config.optionsSource[i] != 'setting') {
setOption(i,cookies[i]);
}
}
}
function loadSystemSettings()
{
var key,settings = store.calcAllSlices('SystemSettings');
config.optionsSource = {};
for(key in settings) {
setOption(key,settings[key]);
config.optionsSource[key] = 'setting';
}
}
function onSystemSettingsChange()
{
if(!startingUp) {
loadSystemSettings();
}
}
function saveOption(name)
{
if(safeMode)
return;
if(name.match(/[()\s]/g, '_')) {
alert(config.messages.invalidCookie.format([name]));
return;
}
saveCookie(name);
if(config.optionsSource[name] == 'setting') {
saveSystemSetting(name,true);
}
}
// @Deprecated; retained for backwards compatibility
var saveOptionCookie = saveOption;
function removeCookie(name)
{
document.cookie = name + '=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
function saveCookie(name)
{
var key,cookies = {};
for(key in config.options) {
var value = getOption(key);
value = value == null ? 'false' : value;
cookies[key] = value;
}
document.cookie = 'TiddlyWiki=' + String.encodeHashMap(cookies) + '; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/';
cookies = getCookies();
var c;
for(c in cookies) {
var optType = c.substr(0,3);
if(config.optionHandlers[optType])
removeCookie(c);
}
}
var systemSettingSave;
function commitSystemSettings(storeWasDirty)
{
if(systemSettingSave) {
window.clearTimeout(systemSettingSave);
}
systemSettingSave = window.setTimeout(function() {
var tiddler = store.getTiddler('SystemSettings');
autoSaveChanges(null,[tiddler]);
}, 1000);
}
function saveSystemSetting(name,saveFile)
{
var title = 'SystemSettings';
var slice = store.getTiddlerSlice(title,name);
if(readOnly || slice === getOption(name)) {
return; //# don't save if read-only or the option hasn't changed
}
var slices = store.calcAllSlices(title);
var key;
for(key in config.optionsSource) {
var value = getOption(key) || '';
if(slices[key] !== value) {
slices[key] = value;
}
}
var text = [];
for(key in slices) {
text.push('%0: %1'.format([key,slices[key]]));
}
text = text.sort().join('\n');
var storeWasDirty = store.isDirty();
var tiddler = store.getTiddler(title);
if(tiddler) {
tiddler.text = text;
tiddler = store.saveTiddler(tiddler);
} else {
tiddler = store.saveTiddler(title,title,text,'System',new Date(),['excludeLists'],config.defaultCustomFields);
}
if(saveFile) {
commitSystemSettings(storeWasDirty);
}
}
function encodeCookie(s)
{
return escape(convertUnicodeToHtmlEntities(s));
}
function decodeCookie(s)
{
s = unescape(s);
var re = /&#[0-9]{1,5};/g;
return s.replace(re,function($0) {return String.fromCharCode(eval($0.replace(/[&#;]/g,'')));});
}
config.macros.option.genericCreate = function(place,type,opt,className,desc)
{
var typeInfo = config.macros.option.types[type];
var c = document.createElement(typeInfo.elementType);
if(typeInfo.typeValue)
c.setAttribute('type',typeInfo.typeValue);
c[typeInfo.eventName] = typeInfo.onChange;
c.setAttribute('option',opt);
c.className = className || typeInfo.className;
if(config.optionsDesc[opt])
c.setAttribute('title',config.optionsDesc[opt]);
place.appendChild(c);
if(desc != 'no')
createTiddlyText(place,config.optionsDesc[opt] || opt);
c[typeInfo.valueField] = config.options[opt];
return c;
};
config.macros.option.genericOnChange = function(e)
{
var opt = this.getAttribute('option');
if(opt) {
var optType = opt.substr(0,3);
var handler = config.macros.option.types[optType];
if(handler.elementType && handler.valueField)
config.macros.option.propagateOption(opt,handler.valueField,this[handler.valueField],handler.elementType,this);
}
return true;
};
config.macros.option.types = {
'txt': {
elementType: 'input',
valueField: 'value',
eventName: 'onchange',
className: 'txtOptionInput',
create: config.macros.option.genericCreate,
onChange: config.macros.option.genericOnChange
},
'chk': {
elementType: 'input',
valueField: 'checked',
eventName: 'onclick',
className: 'chkOptionInput',
typeValue: 'checkbox',
create: config.macros.option.genericCreate,
onChange: config.macros.option.genericOnChange
}
};
config.macros.option.propagateOption = function(opt,valueField,value,elementType,elem)
{
config.options[opt] = value;
saveOption(opt);
var t,nodes = document.getElementsByTagName(elementType);
for(t=0; t<nodes.length; t++) {
var optNode = nodes[t].getAttribute('option');
if(opt == optNode && nodes[t]!=elem)
nodes[t][valueField] = value;
}
};
config.macros.option.handler = function(place,macroName,params,wikifier,paramString)
{
params = paramString.parseParams('anon',null,true,false,false);
var opt = (params[1] && params[1].name == 'anon') ? params[1].value : getParam(params,'name',null);
var className = (params[2] && params[2].name == 'anon') ? params[2].value : getParam(params,'class',null);
var desc = getParam(params,'desc','no');
var type = opt.substr(0,3);
var h = config.macros.option.types[type];
if(h && h.create)
h.create(place,type,opt,className,desc);
};
config.macros.options.handler = function(place,macroName,params,wikifier,paramString)
{
params = paramString.parseParams('anon',null,true,false,false);
var showUnknown = getParam(params,'showUnknown','no');
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement('markList');
var chkUnknown = wizard.getElement('chkUnknown');
chkUnknown.checked = showUnknown == 'yes';
chkUnknown.onchange = this.onChangeUnknown;
var listWrapper = document.createElement('div');
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue('listWrapper',listWrapper);
this.refreshOptions(listWrapper,showUnknown == 'yes');
};
config.macros.options.refreshOptions = function(listWrapper,showUnknown)
{
var n,opts = [];
for(n in config.options) {
var opt = {};
opt.option = '';
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
if(!opt.lowlight || showUnknown)
opts.push(opt);
}
opts.sort(function(a,b) {return a.name.substr(3) < b.name.substr(3) ? -1 : (a.name.substr(3) == b.name.substr(3) ? 0 : +1);});
ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n<opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if(h && h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,null,'no');
}
}
};
config.macros.options.onChangeUnknown = function(e)
{
var wizard = new Wizard(this);
var listWrapper = wizard.getValue('listWrapper');
jQuery(listWrapper).empty();
config.macros.options.refreshOptions(listWrapper,this.checked);
return false;
};
// If there are unsaved changes, force the user to confirm before exitting
function confirmExit()
{
hadConfirmExit = true;
if((store && store.isDirty && store.isDirty()) || (story && story.areAnyDirty && story.areAnyDirty()))
return config.messages.confirmExit;
}
// Give the user a chance to save changes before exitting
function checkUnsavedChanges()
{
if(store && store.isDirty && store.isDirty() && window.hadConfirmExit === false) {
if(confirm(config.messages.unsavedChangesWarning))
saveChanges();
}
}
function saveChanges() {
// expects to be implemented elsewhere
}
function autoSaveChanges(onlyIfDirty,tiddlers)
{
if(config.options.chkAutoSave)
saveChanges(onlyIfDirty,tiddlers);
}
//--
//-- Filesystem utilities
//--
function convertUTF8ToUnicode(u)
{
return config.browser.isOpera || !window.netscape ? manualConvertUTF8ToUnicode(u) : mozConvertUTF8ToUnicode(u);
}
function manualConvertUTF8ToUnicode(utf)
{
var uni = utf;
var src = 0;
var dst = 0;
var b1, b2, b3;
var c;
while(src < utf.length) {
b1 = utf.charCodeAt(src++);
if(b1 < 0x80) {
dst++;
} else if(b1 < 0xE0) {
b2 = utf.charCodeAt(src++);
c = String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
uni = uni.substring(0,dst++).concat(c,utf.substr(src));
} else {
b2 = utf.charCodeAt(src++);
b3 = utf.charCodeAt(src++);
c = String.fromCharCode(((b1 & 0xF) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
uni = uni.substring(0,dst++).concat(c,utf.substr(src));
}
}
return uni;
}
function mozConvertUTF8ToUnicode(u)
{
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
} catch(ex) {
return manualConvertUTF8ToUnicode(u);
} // fallback
var s = converter.ConvertToUnicode(u);
var fin = converter.Finish();
return fin.length > 0 ? s+fin : s;
}
function convertUnicodeToFileFormat(s)
{
return config.browser.isOpera || !window.netscape ? (config.browser.isIE ? convertUnicodeToHtmlEntities(s) : s) : mozConvertUnicodeToUTF8(s);
}
function convertUnicodeToHtmlEntities(s)
{
var re = /[^\u0000-\u007F]/g;
return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
}
function convertUnicodeToUTF8(s)
{
// return convertUnicodeToFileFormat to allow plugin migration
return convertUnicodeToFileFormat(s);
}
function manualConvertUnicodeToUTF8(s)
{
return unescape(encodeURIComponent(s));
}
function mozConvertUnicodeToUTF8(s)
{
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
} catch(ex) {
return manualConvertUnicodeToUTF8(s);
} // fallback
var u = converter.ConvertFromUnicode(s);
var fin = converter.Finish();
return fin.length > 0 ? u + fin : u;
}
function convertUriToUTF8(uri,charSet)
{
if(window.netscape == undefined || charSet == undefined || charSet == "")
return uri;
try {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var converter = Components.classes["@mozilla.org/intl/utf8converterservice;1"].getService(Components.interfaces.nsIUTF8ConverterService);
} catch(ex) {
return uri;
}
return converter.convertURISpecToUTF8(uri,charSet);
}
//--
//-- Server adaptor base class
//--
function AdaptorBase()
{
this.host = null;
this.store = null;
return this;
}
AdaptorBase.prototype.close = function()
{
return true;
};
AdaptorBase.prototype.fullHostName = function(host)
{
if(!host)
return '';
host = host.trim();
if(!host.match(/:\/\//))
host = 'http://' + host;
if(host.substr(host.length-1) == '/')
host = host.substr(0,host.length-1);
return host;
};
AdaptorBase.minHostName = function(host)
{
return host;
};
AdaptorBase.prototype.setContext = function(context,userParams,callback)
{
if(!context) context = {};
context.userParams = userParams;
if(callback) context.callback = callback;
context.adaptor = this;
if(!context.host)
context.host = this.host;
context.host = this.fullHostName(context.host);
if(!context.workspace)
context.workspace = this.workspace;
return context;
};
// Open the specified host
AdaptorBase.prototype.openHost = function(host,context,userParams,callback)
{
this.host = host;
context = this.setContext(context,userParams,callback);
context.status = true;
if(callback)
window.setTimeout(function() {context.callback(context,userParams);},10);
return true;
};
// Open the specified workspace
AdaptorBase.prototype.openWorkspace = function(workspace,context,userParams,callback)
{
this.workspace = workspace;
context = this.setContext(context,userParams,callback);
context.status = true;
if(callback)
window.setTimeout(function() {callback(context,userParams);},10);
return true;
};
//--
//-- HTTP request code
//--
function ajaxReq(args)
{
if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
return jQuery.ajax(args);
}
function httpReq(type,url,callback,params,headers,data,contentType,username,password,allowCache)
{
var httpSuccess = function(xhr) {
try {
// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
return (!xhr.status && location.protocol === "file:") ||
(xhr.status >= 200 && xhr.status < 300) ||
xhr.status === 304 || xhr.status === 1223;
} catch(e) {}
return false;
};
var options = {
type:type,
url:url,
processData:false,
data:data,
cache:!!allowCache,
beforeSend: function(xhr) {
var i;
for(i in headers)
xhr.setRequestHeader(i,headers[i]);
xhr.setRequestHeader("X-Requested-With", "TiddlyWiki " + formatVersion());
}
};
if(callback) {
options.complete = function(xhr,textStatus) {
if(httpSuccess(xhr))
callback(true,params,xhr.responseText,url,xhr);
else
callback(false,params,null,url,xhr);
};
}
if(contentType)
options.contentType = contentType;
if(username)
options.username = username;
if(password)
options.password = password;
if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
return jQuery.ajax(options);
}
//--
//-- TiddlyWiki-specific utility functions
//--
// Returns TiddlyWiki version string
function formatVersion(v)
{
v = v || version;
return v.major + "." + v.minor + "." + v.revision +
(v.alpha ? " (alpha " + v.alpha + ")" : "") +
(v.beta ? " (beta " + v.beta + ")" : "");
}
function compareVersions(v1,v2)
{
var x1,x2,i,a = ["major","minor","revision"];
for(i = 0; i<a.length; i++) {
x1 = v1[a[i]] || 0;
x2 = v2[a[i]] || 0;
if(x1<x2)
return 1;
if(x1>x2)
return -1;
}
x1 = v1.beta || 9999;
x2 = v2.beta || 9999;
if(x1<x2)
return 1;
return x1 > x2 ? -1 : 0;
}
function merge(dst,src,preserveExisting)
{
var i;
for(i in src) {
if(!preserveExisting || dst[i] === undefined)
dst[i] = src[i];
}
return dst;
}
// Resolve the target object of an event
function resolveTarget(e)
{
var obj;
if(e.target)
obj = e.target;
else if(e.srcElement)
obj = e.srcElement;
if(obj.nodeType == 3) // defeat Safari bug
obj = obj.parentNode;
return obj;
}
// Returns a string containing the description of an exception, optionally prepended by a message
function exceptionText(e,message)
{
var s = e.description || e.toString();
return message ? "%0:\n%1".format([message,s]) : s;
}
// Displays an alert of an exception description with optional message
function showException(e,message)
{
alert(exceptionText(e,message));
}
function alertAndThrow(m)
{
alert(m);
throw(m);
}
function glyph(name)
{
var g = config.glyphs;
var b = g.currBrowser;
if(b == null) {
b = 0;
while(b < g.browsers.length-1 && !g.browsers[b]())
b++;
g.currBrowser = b;
}
if(!g.codes[name])
return "";
return g.codes[name][b];
}
function createTiddlyText(parent,text)
{
return parent.appendChild(document.createTextNode(text));
}
function createTiddlyCheckbox(parent,caption,checked,onChange)
{
var cb = document.createElement("input");
cb.setAttribute("type","checkbox");
cb.onclick = onChange;
parent.appendChild(cb);
cb.checked = checked;
cb.className = "chkOptionInput";
if(caption)
wikify(caption,parent);
return cb;
}
function createTiddlyElement(parent,element,id,className,text,attribs)
{
var n,e = document.createElement(element);
if(className != null)
e.className = className;
if(id != null)
e.setAttribute("id",id);
if(text != null)
e.appendChild(document.createTextNode(text));
if(attribs) {
for(n in attribs) {
e.setAttribute(n,attribs[n]);
}
}
if(parent != null)
parent.appendChild(e);
return e;
}
function createTiddlyButton(parent,text,tooltip,action,className,id,accessKey,attribs)
{
var i,btn = document.createElement("a");
btn.setAttribute("href","javascript:;");
if(action) {
btn.onclick = action;
}
if(tooltip)
btn.setAttribute("title",tooltip);
if(text)
btn.appendChild(document.createTextNode(text));
btn.className = className || "button";
if(id)
btn.id = id;
if(attribs) {
for(i in attribs) {
btn.setAttribute(i,attribs[i]);
}
}
if(parent)
parent.appendChild(btn);
if(accessKey)
btn.setAttribute("accessKey",accessKey);
return btn;
}
function createExternalLink(place,url,label)
{
var link = document.createElement("a");
link.className = "externalLink";
link.href = url;
var f = config.messages.externalLinkTooltip;
link.title = f ? f.format([url]) : url;
if(config.options.chkOpenInNewWindow)
link.target = "_blank";
place.appendChild(link);
if(label)
createTiddlyText(link, label);
return link;
}
function getTiddlyLinkInfo(title,currClasses)
{
var classes = currClasses ? currClasses.split(" ") : [];
classes.pushUnique("tiddlyLink");
var tiddler = store.fetchTiddler(title);
var subTitle;
if(tiddler) {
subTitle = tiddler.getSubtitle();
classes.pushUnique("tiddlyLinkExisting");
classes.remove("tiddlyLinkNonExisting");
classes.remove("shadow");
} else {
var f;
classes.remove("tiddlyLinkExisting");
classes.pushUnique("tiddlyLinkNonExisting");
if(store.isShadowTiddler(title)) {
f = config.messages.shadowedTiddlerToolTip;
classes.pushUnique("shadow");
} else {
f = config.messages.undefinedTiddlerToolTip;
classes.remove("shadow");
}
subTitle = f ? f.format([title]) : "";
}
if(typeof config.annotations[title]=="string")
subTitle = config.annotations[title];
return {classes: classes.join(" "),subTitle: subTitle};
}
// Event handler for clicking on a tiddly link
function onClickTiddlerLink(ev)
{
var e = ev || window.event;
var target = resolveTarget(e);
var link = target;
var title = null;
var fields = null;
var noToggle = null;
do {
title = link.getAttribute("tiddlyLink");
fields = link.getAttribute("tiddlyFields");
noToggle = link.getAttribute("noToggle");
link = link.parentNode;
} while(title == null && link != null);
if(!store.isShadowTiddler(title)) {
var f = fields ? fields.decodeHashMap() : {};
fields = String.encodeHashMap(merge(f,config.defaultCustomFields,true));
}
if(title) {
var toggling = e.metaKey || e.ctrlKey;
if(config.options.chkToggleLinks)
toggling = !toggling;
if(noToggle)
toggling = false;
if(store.getTiddler(title))
fields = null;
story.displayTiddler(target,title,null,true,null,fields,toggling);
}
clearMessage();
return false;
}
function createTiddlyLink(place,title,includeText,className,isStatic,linkedFromTiddler,noToggle)
{
var title = jQuery.trim(title);
var text = includeText ? title : null;
var i = getTiddlyLinkInfo(title,className);
var btn = isStatic ? createExternalLink(place,store.getTiddlerText("SiteUrl",null) + "#" + title) : createTiddlyButton(place,text,i.subTitle,onClickTiddlerLink,i.classes);
if(isStatic)
btn.className += ' ' + className;
btn.setAttribute("refresh","link");
btn.setAttribute("tiddlyLink",title);
if(noToggle)
btn.setAttribute("noToggle","true");
if(linkedFromTiddler) {
var fields = linkedFromTiddler.getInheritedFields();
if(fields)
btn.setAttribute("tiddlyFields",fields);
}
return btn;
}
function refreshTiddlyLink(e,title)
{
var i = getTiddlyLinkInfo(title,e.className);
e.className = i.classes;
e.title = i.subTitle;
}
function createTiddlyDropDown(place,onchange,options,defaultValue)
{
var sel = createTiddlyElement(place,"select");
sel.onchange = onchange;
var t;
for(t=0; t<options.length; t++) {
var e = createTiddlyElement(sel,"option",null,null,options[t].caption);
e.value = options[t].name;
if(options[t].name == defaultValue)
e.selected = true;
}
return sel;
}
//--
//-- TiddlyWiki-specific popup utility functions
//--
// Event handler for 'open all' on a tiddler popup
function onClickTagOpenAll(ev)
{
var tiddlers = store.getTaggedTiddlers(this.getAttribute("tag"));
var sortby = this.getAttribute("sortby");
if(sortby&&sortby.length) {
store.sortTiddlers(tiddlers,sortby);
}
story.displayTiddlers(this,tiddlers);
return false;
}
// Event handler for clicking on a tiddler tag
function onClickTag(ev)
{
var e = ev || window.event;
var popup = Popup.create(this);
jQuery(popup).addClass("taggedTiddlerList");
var tag = this.getAttribute("tag");
var title = this.getAttribute("tiddler");
if(popup && tag) {
var tagged = tag.indexOf("[")==-1 ? store.getTaggedTiddlers(tag) : store.filterTiddlers(tag);
var sortby = this.getAttribute("sortby");
if(sortby&&sortby.length) {
store.sortTiddlers(tagged,sortby);
}
var titles = [];
var r;
for(r=0;r<tagged.length;r++) {
if(tagged[r].title != title)
titles.push(tagged[r].title);
}
var lingo = config.views.wikified.tag;
if(titles.length > 0) {
var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText.format([tag]),lingo.openAllTooltip,onClickTagOpenAll);
openAll.setAttribute("tag",tag);
openAll.setAttribute("sortby",sortby);
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
for(r=0; r<titles.length; r++) {
createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
}
} else {
createTiddlyElement(popup,"li",null,"disabled",lingo.popupNone.format([tag]));
}
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
var h = createTiddlyLink(createTiddlyElement(popup,"li"),tag,false);
createTiddlyText(h,lingo.openTag.format([tag]));
}
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
}
// Create a button for a tag with a popup listing all the tiddlers that it tags
function createTagButton(place,tag,excludeTiddler,title,tooltip)
{
var btn = createTiddlyButton(place,title||tag,(tooltip||config.views.wikified.tag.tooltip).format([tag]),onClickTag);
btn.setAttribute("tag",tag);
if(excludeTiddler)
btn.setAttribute("tiddler",excludeTiddler);
return btn;
}
function onClickTiddlyPopup(ev)
{
var e = ev || window.event;
var tiddler = this.tiddler;
if(tiddler.text) {
var popup = Popup.create(this,"div","popupTiddler");
wikify(tiddler.text,popup,null,tiddler);
Popup.show();
}
if(e) e.cancelBubble = true;
if(e && e.stopPropagation) e.stopPropagation();
return false;
}
function createTiddlyPopup(place,caption,tooltip,tiddler)
{
if(tiddler.text) {
createTiddlyLink(place,caption,true);
var btn = createTiddlyButton(place,glyph("downArrow"),tooltip,onClickTiddlyPopup,"tiddlerPopupButton");
btn.tiddler = tiddler;
} else {
createTiddlyText(place,caption);
}
}
function onClickError(ev)
{
var e = ev || window.event;
var popup = Popup.create(this);
var lines = this.getAttribute("errorText").split("\n");
var t;
for(t=0; t<lines.length; t++)
createTiddlyElement(popup,"li",null,null,lines[t]);
Popup.show();
e.cancelBubble = true;
if(e.stopPropagation) e.stopPropagation();
return false;
}
function createTiddlyError(place,title,text)
{
var btn = createTiddlyButton(place,title,null,onClickError,"errorButton");
if(text) btn.setAttribute("errorText",text);
}
//-
//- Animation engine
//-
function Animator()
{
this.running = 0; // Incremented at start of each animation, decremented afterwards. If zero, the interval timer is disabled
this.timerID = 0; // ID of the timer used for animating
this.animations = []; // List of animations in progress
return this;
}
// Start animation engine
Animator.prototype.startAnimating = function() //# Variable number of arguments
{
var t;
for(t=0; t<arguments.length; t++)
this.animations.push(arguments[t]);
if(this.running == 0) {
var me = this;
this.timerID = window.setInterval(function() {me.doAnimate(me);},10);
}
this.running += arguments.length;
};
// Perform an animation engine tick, calling each of the known animation modules
Animator.prototype.doAnimate = function(me)
{
var a = 0;
while(a < me.animations.length) {
var animation = me.animations[a];
if(animation.tick()) {
a++;
} else {
me.animations.splice(a,1);
if(--me.running == 0)
window.clearInterval(me.timerID);
}
}
};
Animator.slowInSlowOut = function(progress)
{
return(1-((Math.cos(progress * Math.PI)+1)/2));
};
//--
//-- Morpher animation
//--
// Animate a set of properties of an element
function Morpher(element,duration,properties,callback)
{
this.element = element;
this.duration = duration;
this.properties = properties;
this.startTime = new Date();
this.endTime = Number(this.startTime) + duration;
this.callback = callback;
this.tick();
return this;
}
Morpher.prototype.assignStyle = function(element,style,value)
{
switch(style) {
case "-tw-vertScroll":
window.scrollTo(findScrollX(),value);
break;
case "-tw-horizScroll":
window.scrollTo(value,findScrollY());
break;
default:
element.style[style] = value;
break;
}
};
Morpher.prototype.stop = function()
{
var t;
for(t=0; t<this.properties.length; t++) {
var p = this.properties[t];
if(p.atEnd !== undefined) {
this.assignStyle(this.element,p.style,p.atEnd);
}
}
if(this.callback)
this.callback(this.element,this.properties);
};
Morpher.prototype.tick = function()
{
var currTime = Number(new Date());
var t,progress = Animator.slowInSlowOut(Math.min(1,(currTime-this.startTime)/this.duration));
for(t=0; t<this.properties.length; t++) {
var p = this.properties[t];
if(p.start !== undefined && p.end !== undefined) {
var template = p.template || "%0";
switch(p.format) {
case undefined:
case "style":
var v = p.start + (p.end-p.start) * progress;
this.assignStyle(this.element,p.style,template.format([v]));
break;
case "color":
break;
}
}
}
if(currTime >= this.endTime) {
this.stop();
return false;
}
return true;
};
//--
//-- Zoomer animation
//--
function Zoomer(text,startElement,targetElement,unused)
{
var e = createTiddlyElement(document.body,"div",null,"zoomer");
createTiddlyElement(e,"div",null,null,text);
var winWidth = findWindowWidth();
var winHeight = findWindowHeight();
var p = [
{style: 'left', start: findPosX(startElement), end: findPosX(targetElement), template: '%0px'},
{style: 'top', start: findPosY(startElement), end: findPosY(targetElement), template: '%0px'},
{style: 'width', start: Math.min(startElement.scrollWidth,winWidth), end: Math.min(targetElement.scrollWidth,winWidth), template: '%0px', atEnd: 'auto'},
{style: 'height', start: Math.min(startElement.scrollHeight,winHeight), end: Math.min(targetElement.scrollHeight,winHeight), template: '%0px', atEnd: 'auto'},
{style: 'fontSize', start: 8, end: 24, template: '%0pt'}
];
var c = function(element,properties) {jQuery(element).remove();};
return new Morpher(e,config.animDuration,p,c);
}
//--
//-- Scroller animation
//--
function Scroller(targetElement)
{
var p = [{style: '-tw-vertScroll', start: findScrollY(), end: ensureVisible(targetElement)}];
return new Morpher(targetElement,config.animDuration,p);
}
//--
//-- Slider animation
//--
// deleteMode - "none", "all" [delete target element and it's children], [only] "children" [but not the target element]
function Slider(element,opening,unused,deleteMode)
{
element.style.overflow = 'hidden';
if(opening)
element.style.height = '0px'; // Resolves a Firefox flashing bug
element.style.display = 'block';
var height = element.scrollHeight;
var p = [];
var c = null;
if(opening) {
p.push({style: 'height', start: 0, end: height, template: '%0px', atEnd: 'auto'});
p.push({style: 'opacity', start: 0, end: 1, template: '%0'});
p.push({style: 'filter', start: 0, end: 100, template: 'alpha(opacity:%0)'});
} else {
p.push({style: 'height', start: height, end: 0, template: '%0px'});
p.push({style: 'display', atEnd: 'none'});
p.push({style: 'opacity', start: 1, end: 0, template: '%0'});
p.push({style: 'filter', start: 100, end: 0, template: 'alpha(opacity:%0)'});
switch(deleteMode) {
case "all":
c = function(element,properties) {jQuery(element).remove();};
break;
case "children":
c = function(element,properties) {jQuery(element).empty();};
break;
}
}
return new Morpher(element,config.animDuration,p,c);
}
//--
//-- Popup menu
//--
var Popup = {
stack: [] // Array of objects with members root: and popup:
};
Popup.create = function(root,elem,className)
{
var stackPosition = this.find(root,"popup");
Popup.remove(stackPosition+1);
var popup = createTiddlyElement(document.body,elem || "ol","popup",className || "popup");
popup.stackPosition = stackPosition;
Popup.stack.push({root: root, popup: popup});
return popup;
};
Popup.onDocumentClick = function(ev)
{
var e = ev || window.event;
if(e.eventPhase == undefined)
Popup.remove();
else if(e.eventPhase == Event.BUBBLING_PHASE || e.eventPhase == Event.AT_TARGET)
Popup.remove();
return true;
};
Popup.show = function(valign,halign,offset)
{
var curr = Popup.stack[Popup.stack.length-1];
this.place(curr.root,curr.popup,valign,halign,offset);
jQuery(curr.root).addClass("highlight");
if(config.options.chkAnimate && anim && typeof Scroller == "function")
anim.startAnimating(new Scroller(curr.popup));
else
window.scrollTo(0,ensureVisible(curr.popup));
};
Popup.place = function(root,popup,valign,halign,offset)
{
if(!offset)
offset = {x:0,y:0};
if(popup.stackPosition >= 0 && !valign && !halign) {
offset.x = offset.x + root.offsetWidth;
} else {
offset.x = (halign == "right") ? offset.x + root.offsetWidth : offset.x;
offset.y = (valign == "top") ? offset.y : offset.y + root.offsetHeight;
}
var rootLeft = findPosX(root);
var rootTop = findPosY(root);
var popupLeft = rootLeft + offset.x;
var popupTop = rootTop + offset.y;
var winWidth = findWindowWidth();
if(popup.offsetWidth > winWidth*0.75)
popup.style.width = winWidth*0.75 + "px";
var popupWidth = popup.offsetWidth;
var scrollWidth = winWidth - document.body.offsetWidth;
if(popupLeft + popupWidth > winWidth - scrollWidth - 1) {
if(halign == "right")
popupLeft = popupLeft - root.offsetWidth - popupWidth;
else
popupLeft = winWidth - popupWidth - scrollWidth - 1;
}
popup.style.left = popupLeft + "px";
popup.style.top = popupTop + "px";
popup.style.display = "block";
};
Popup.find = function(e)
{
var t,pos = -1;
for(t=this.stack.length-1; t>=0; t--) {
if(isDescendant(e,this.stack[t].popup))
pos = t;
}
return pos;
};
Popup.remove = function(pos)
{
if(!pos) pos = 0;
if(Popup.stack.length > pos) {
Popup.removeFrom(pos);
}
};
Popup.removeFrom = function(from)
{
var t;
for(t=Popup.stack.length-1; t>=from; t--) {
var p = Popup.stack[t];
jQuery(p.root).removeClass("highlight");
jQuery(p.popup).remove();
}
Popup.stack = Popup.stack.slice(0,from);
};
//--
//-- Wizard support
//--
function Wizard(elem)
{
if(elem) {
this.formElem = findRelated(elem,"wizard","className");
this.bodyElem = findRelated(this.formElem.firstChild,"wizardBody","className","nextSibling");
this.footElem = findRelated(this.formElem.firstChild,"wizardFooter","className","nextSibling");
} else {
this.formElem = null;
this.bodyElem = null;
this.footElem = null;
}
}
Wizard.prototype.setValue = function(name,value)
{
jQuery(this.formElem).data(name, value);
};
Wizard.prototype.getValue = function(name)
{
return this.formElem ? jQuery(this.formElem).data(name) : null;
};
Wizard.prototype.createWizard = function(place,title)
{
this.formElem = createTiddlyElement(place,"form",null,"wizard");
createTiddlyElement(this.formElem,"h1",null,null,title);
this.bodyElem = createTiddlyElement(this.formElem,"div",null,"wizardBody");
this.footElem = createTiddlyElement(this.formElem,"div",null,"wizardFooter");
return this.formElem;
};
Wizard.prototype.clear = function()
{
jQuery(this.bodyElem).empty();
};
Wizard.prototype.setButtons = function(buttonInfo,status)
{
jQuery(this.footElem).empty();
var t;
for(t=0; t<buttonInfo.length; t++) {
createTiddlyButton(this.footElem,buttonInfo[t].caption,buttonInfo[t].tooltip,buttonInfo[t].onClick);
insertSpacer(this.footElem);
}
if(typeof status == "string") {
createTiddlyElement(this.footElem,"span",null,"status",status);
}
};
Wizard.prototype.addStep = function(stepTitle,html)
{
jQuery(this.bodyElem).empty();
var w = createTiddlyElement(this.bodyElem,"div");
createTiddlyElement(w,"h2",null,null,stepTitle);
var step = createTiddlyElement(w,"div",null,"wizardStep");
step.innerHTML = html;
applyHtmlMacros(step,tiddler);
};
Wizard.prototype.getElement = function(name)
{
return this.formElem.elements[name];
};
//--
//-- ListView gadget
//--
var ListView = {};
// Create a listview
ListView.create = function(place,listObject,listTemplate,callback,className)
{
var table = createTiddlyElement(place,"table",null,className || "listView twtable");
var thead = createTiddlyElement(table,"thead");
var t,r = createTiddlyElement(thead,"tr");
for(t=0; t<listTemplate.columns.length; t++) {
var columnTemplate = listTemplate.columns[t];
var c = createTiddlyElement(r,"th");
var colType = ListView.columnTypes[columnTemplate.type];
if(colType && colType.createHeader) {
colType.createHeader(c,columnTemplate,t);
if(columnTemplate.className)
jQuery(c).addClass(columnTemplate.className);
}
}
var rc,tbody = createTiddlyElement(table,"tbody");
for(rc=0; rc<listObject.length; rc++) {
var rowObject = listObject[rc];
r = createTiddlyElement(tbody,"tr");
for(c=0; c<listTemplate.rowClasses.length; c++) {
if(rowObject[listTemplate.rowClasses[c].field])
jQuery(r).addClass(listTemplate.rowClasses[c].className);
}
rowObject.rowElement = r;
rowObject.colElements = {};
var cc;
for(cc=0; cc<listTemplate.columns.length; cc++) {
c = createTiddlyElement(r,"td");
columnTemplate = listTemplate.columns[cc];
var field = columnTemplate.field;
colType = ListView.columnTypes[columnTemplate.type];
if(colType && colType.createItem) {
colType.createItem(c,rowObject,field,columnTemplate,cc,rc);
if(columnTemplate.className)
jQuery(c).addClass(columnTemplate.className);
}
rowObject.colElements[field] = c;
}
}
if(callback && listTemplate.actions)
createTiddlyDropDown(place,ListView.getCommandHandler(callback),listTemplate.actions);
if(callback && listTemplate.buttons) {
for(t=0; t<listTemplate.buttons.length; t++) {
var a = listTemplate.buttons[t];
if(a && a.name != "")
createTiddlyButton(place,a.caption,null,ListView.getCommandHandler(callback,a.name,a.allowEmptySelection));
}
}
return table;
};
ListView.getCommandHandler = function(callback,name,allowEmptySelection)
{
return function(e) {
var view = findRelated(this,"TABLE",null,"previousSibling");
var tiddlers = [];
ListView.forEachSelector(view,function(e,rowName) {
if(e.checked)
tiddlers.push(rowName);
});
if(tiddlers.length == 0 && !allowEmptySelection) {
alert(config.messages.nothingSelected);
} else {
if(this.nodeName.toLowerCase() == "select") {
callback(view,this.value,tiddlers);
this.selectedIndex = 0;
} else {
callback(view,name,tiddlers);
}
}
};
};
// Invoke a callback for each selector checkbox in the listview
ListView.forEachSelector = function(view,callback)
{
var checkboxes = view.getElementsByTagName("input");
var t,hadOne = false;
for(t=0; t<checkboxes.length; t++) {
var cb = checkboxes[t];
if(cb.getAttribute("type") == "checkbox") {
var rn = cb.getAttribute("rowName");
if(rn) {
callback(cb,rn);
hadOne = true;
}
}
}
return hadOne;
};
ListView.getSelectedRows = function(view)
{
var rowNames = [];
ListView.forEachSelector(view,function(e,rowName) {
if(e.checked)
rowNames.push(rowName);
});
return rowNames;
};
ListView.columnTypes = {};
ListView.columnTypes.String = {
createHeader: function(place,columnTemplate,col)
{
createTiddlyText(place,columnTemplate.title);
},
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined)
createTiddlyText(place,v);
}
};
ListView.columnTypes.WikiText = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined)
wikify(v,place,null,null);
}
};
ListView.columnTypes.Tiddler = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined && v.title)
createTiddlyPopup(place,v.title,config.messages.listView.tiddlerTooltip,v);
}
};
ListView.columnTypes.Size = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var msg = config.messages.sizeTemplates;
var v = listObject[field];
if(v != undefined) {
var t = 0;
while(t<msg.length-1 && v<msg[t].unit)
t++;
createTiddlyText(place,msg[t].template.format([Math.round(v/msg[t].unit)]));
}
}
};
ListView.columnTypes.Link = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
var c = columnTemplate.text;
if(v != undefined)
createExternalLink(place,v,c || v);
}
};
ListView.columnTypes.Date = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined)
createTiddlyText(place,v.formatString(columnTemplate.dateFormat));
}
};
ListView.columnTypes.StringList = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined) {
var t;
for(t=0; t<v.length; t++) {
createTiddlyText(place,v[t]);
createTiddlyElement(place,"br");
}
}
}
};
ListView.columnTypes.Selector = {
createHeader: function(place,columnTemplate,col)
{
createTiddlyCheckbox(place,null,false,this.onHeaderChange);
},
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var e = createTiddlyCheckbox(place,null,listObject[field],null);
e.setAttribute("rowName",listObject[columnTemplate.rowName]);
},
onHeaderChange: function(e)
{
var state = this.checked;
var view = findRelated(this,"TABLE");
if(!view)
return;
ListView.forEachSelector(view,function(e,rowName) {
e.checked = state;
});
}
};
ListView.columnTypes.Tags = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var tags = listObject[field];
createTiddlyText(place,String.encodeTiddlyLinkList(tags));
}
};
ListView.columnTypes.Boolean = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
if(listObject[field] == true)
createTiddlyText(place,columnTemplate.trueText);
if(listObject[field] == false)
createTiddlyText(place,columnTemplate.falseText);
}
};
ListView.columnTypes.TagCheckbox = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var e = createTiddlyCheckbox(place,null,listObject[field],this.onChange);
e.setAttribute("tiddler",listObject.title);
e.setAttribute("tag",columnTemplate.tag);
},
onChange : function(e)
{
var tag = this.getAttribute("tag");
var tiddler = this.getAttribute("tiddler");
store.setTiddlerTag(tiddler,this.checked,tag);
}
};
ListView.columnTypes.TiddlerLink = {
createHeader: ListView.columnTypes.String.createHeader,
createItem: function(place,listObject,field,columnTemplate,col,row)
{
var v = listObject[field];
if(v != undefined) {
var link = createTiddlyLink(place,listObject[columnTemplate.tiddlerLink],false,null);
createTiddlyText(link,listObject[field]);
}
}
};
//--
//-- Augmented methods for the JavaScript Array() object
//--
// Add indexOf function if browser does not support it
if(!Array.indexOf) {
Array.prototype.indexOf = function(item,from)
{
if(!from)
from = 0;
var i;
for(i=from; i<this.length; i++) {
if(this[i] === item)
return i;
}
return -1;
};}
// Find an entry in a given field of the members of an array
Array.prototype.findByField = function(field,value)
{
var t;
for(t=0; t<this.length; t++) {
if(this[t][field] === value)
return t;
}
return null;
};
// Return whether an entry exists in an array
Array.prototype.contains = function(item)
{
return this.indexOf(item) != -1;
};
// Adds, removes or toggles a particular value within an array
// value - value to add
// mode - +1 to add value, -1 to remove value, 0 to toggle it
Array.prototype.setItem = function(value,mode)
{
var p = this.indexOf(value);
if(mode == 0)
mode = (p == -1) ? +1 : -1;
if(mode == +1) {
if(p == -1)
this.push(value);
} else if(mode == -1) {
if(p != -1)
this.splice(p,1);
}
};
// Return whether one of a list of values exists in an array
Array.prototype.containsAny = function(items)
{
var i;
for(i=0; i<items.length; i++) {
if(this.indexOf(items[i]) != -1)
return true;
}
return false;
};
// Return whether all of a list of values exists in an array
Array.prototype.containsAll = function(items)
{
var i;
for(i = 0; i<items.length; i++) {
if(this.indexOf(items[i]) == -1)
return false;
}
return true;
};
// Push a new value into an array only if it is not already present in the array. If the optional unique parameter is false, it reverts to a normal push
Array.prototype.pushUnique = function(item,unique)
{
if(unique === false) {
this.push(item);
} else {
if(this.indexOf(item) == -1)
this.push(item);
}
};
Array.prototype.remove = function(item)
{
var p = this.indexOf(item);
if(p != -1)
this.splice(p,1);
};
if(!Array.prototype.map) {
Array.prototype.map = function(fn,thisObj)
{
var scope = thisObj || window;
var i,j,a = [];
for(i=0, j=this.length; i < j; ++i) {
a.push(fn.call(scope,this[i],i,this));
}
return a;
};}
//--
//-- Augmented methods for the JavaScript String() object
//--
// Get characters from the right end of a string
String.prototype.right = function(n)
{
return n < this.length ? this.slice(this.length-n) : this;
};
// Trim whitespace from both ends of a string
String.prototype.trim = function()
{
return this.replace(/^\s*|\s*$/g,"");
};
// Convert a string from a CSS style property name to a JavaScript style name ("background-color" -> "backgroundColor")
String.prototype.unDash = function()
{
var t,s = this.split("-");
if(s.length > 1) {
for(t=1; t<s.length; t++)
s[t] = s[t].substr(0,1).toUpperCase() + s[t].substr(1);
}
return s.join("");
};
// Substitute substrings from an array into a format string that includes '%1'-type specifiers
String.prototype.format = function(s)
{
var substrings = s && s.constructor == Array ? s : arguments;
var subRegExp = /(?:%(\d+))/mg;
var currPos = 0;
var match,r = [];
do {
match = subRegExp.exec(this);
if(match && match[1]) {
if(match.index > currPos)
r.push(this.substring(currPos,match.index));
r.push(substrings[parseInt(match[1],10)]);
currPos = subRegExp.lastIndex;
}
} while(match);
if(currPos < this.length)
r.push(this.substring(currPos,this.length));
return r.join("");
};
// Escape any special RegExp characters with that character preceded by a backslash
String.prototype.escapeRegExp = function()
{
var s = "\\^$*+?()=!|,{}[].";
var t,c = this;
for(t=0; t<s.length; t++)
c = c.replace(new RegExp("\\" + s.substr(t,1),"g"),"\\" + s.substr(t,1));
return c;
};
// Convert "\" to "\s", newlines to "\n" (and remove carriage returns)
String.prototype.escapeLineBreaks = function()
{
return this.replace(/\\/mg,"\\s").replace(/\n/mg,"\\n").replace(/\r/mg,"");
};
// Convert "\n" to newlines, "\b" to " ", "\s" to "\" (and remove carriage returns)
String.prototype.unescapeLineBreaks = function()
{
return this.replace(/\\n/mg,"\n").replace(/\\b/mg," ").replace(/\\s/mg,"\\").replace(/\r/mg,"");
};
// Convert & to "&", < to "<", > to ">" and " to """
String.prototype.htmlEncode = function()
{
return this.replace(/&/mg,"&").replace(/</mg,"<").replace(/>/mg,">").replace(/\"/mg,""");
};
// Convert "&" to &, "<" to <, ">" to > and """ to "
String.prototype.htmlDecode = function()
{
return this.replace(/</mg,"<").replace(/>/mg,">").replace(/"/mg,"\"").replace(/&/mg,"&");
};
// Parse a space-separated string of name:value parameters
// The result is an array of objects:
// result[0] = object with a member for each parameter name, value of that member being an array of values
// result[1..n] = one object for each parameter, with 'name' and 'value' members
String.prototype.parseParams = function(defaultName,defaultValue,allowEval,noNames,cascadeDefaults)
{
var parseToken = function(match,p) {
var n;
if(match[p]) // Double quoted
n = match[p];
else if(match[p+1]) // Single quoted
n = match[p+1];
else if(match[p+2]) // Double-square-bracket quoted
n = match[p+2];
else if(match[p+3]) // Double-brace quoted
try {
n = match[p+3];
if(allowEval && config.evaluateMacroParameters != "none") {
if(config.evaluateMacroParameters == "restricted") {
if(window.restrictedEval) {
n = window.restrictedEval(n);
}
} else {
n = window.eval(n);
}
}
} catch(ex) {
throw "Unable to evaluate {{" + match[p+3] + "}}: " + exceptionText(ex);
}
else if(match[p+4]) // Unquoted
n = match[p+4];
else if(match[p+5]) // empty quote
n = "";
return n;
};
var r = [{}];
var dblQuote = "(?:\"((?:(?:\\\\\")|[^\"])+)\")";
var sngQuote = "(?:'((?:(?:\\\\\')|[^'])+)')";
var dblSquare = "(?:\\[\\[((?:\\s|\\S)*?)\\]\\])";
var dblBrace = "(?:\\{\\{((?:\\s|\\S)*?)\\}\\})";
var unQuoted = noNames ? "([^\"'\\s]\\S*)" : "([^\"':\\s][^\\s:]*)";
var emptyQuote = "((?:\"\")|(?:''))";
var skipSpace = "(?:\\s*)";
var token = "(?:" + dblQuote + "|" + sngQuote + "|" + dblSquare + "|" + dblBrace + "|" + unQuoted + "|" + emptyQuote + ")";
var re = noNames ? new RegExp(token,"mg") : new RegExp(skipSpace + token + skipSpace + "(?:(\\:)" + skipSpace + token + ")?","mg");
var match;
do {
match = re.exec(this);
if(match) {
var n = parseToken(match,1);
if(noNames) {
r.push({name:"",value:n});
} else {
var v = parseToken(match,8);
if(v == null && defaultName) {
v = n;
n = defaultName;
} else if(v == null && defaultValue) {
v = defaultValue;
}
r.push({name:n,value:v});
if(cascadeDefaults) {
defaultName = n;
defaultValue = v;
}
}
}
} while(match);
// Summarise parameters into first element
var t;
for(t=1; t<r.length; t++) {
if(r[0][r[t].name])
r[0][r[t].name].push(r[t].value);
else
r[0][r[t].name] = [r[t].value];
}
return r;
};
// Process a string list of macro parameters into an array. Parameters can be quoted with "", '',
// [[]], {{ }} or left unquoted (and therefore space-separated). Double-braces {{}} results in
// an *evaluated* parameter: e.g. {{config.options.txtUserName}} results in the current user's name.
String.prototype.readMacroParams = function(notAllowEval)
{
var p = this.parseParams("list",null,!notAllowEval,true);
var t,n = [];
for(t=1; t<p.length; t++)
n.push(p[t].value);
return n;
};
// Process a string list of unique tiddler names into an array. Tiddler names that have spaces in them must be [[bracketed]]
String.prototype.readBracketedList = function(unique)
{
var p = this.parseParams("list",null,false,true);
var t,n = [];
for(t=1; t<p.length; t++) {
if(p[t].value)
n.pushUnique(p[t].value,unique);
}
return n;
};
// Returns array with start and end index of chunk between given start and end marker, or undefined.
String.prototype.getChunkRange = function(start,end)
{
var s = this.indexOf(start);
if(s != -1) {
s += start.length;
var e = this.indexOf(end,s);
if(e != -1)
return [s,e];
}
};
// Replace a chunk of a string given start and end markers
String.prototype.replaceChunk = function(start,end,sub)
{
var r = this.getChunkRange(start,end);
return r ? this.substring(0,r[0]) + sub + this.substring(r[1]) : this;
};
// Returns a chunk of a string between start and end markers, or undefined
String.prototype.getChunk = function(start,end)
{
var r = this.getChunkRange(start,end);
if(r)
return this.substring(r[0],r[1]);
};
// Static method to bracket a string with double square brackets if it contains a space
String.encodeTiddlyLink = function(title)
{
return title.indexOf(" ") == -1 ? title : "[[" + title + "]]";
};
// Static method to encodeTiddlyLink for every item in an array and join them with spaces
String.encodeTiddlyLinkList = function(list)
{
if(list) {
var t,results = [];
for(t=0; t<list.length; t++)
results.push(String.encodeTiddlyLink(list[t]));
return results.join(" ");
} else {
return "";
}
};
// Convert a string as a sequence of name:"value" pairs into a hashmap
String.prototype.decodeHashMap = function()
{
var fields = this.parseParams("anon","",false);
var t,r = {};
for(t=1; t<fields.length; t++)
r[fields[t].name] = fields[t].value;
return r;
};
// Static method to encode a hashmap into a name:"value"... string
String.encodeHashMap = function(hashmap)
{
var t,r = [];
for(t in hashmap)
r.push(t + ':"' + hashmap[t] + '"');
return r.join(" ");
};
// Static method to left-pad a string with 0s to a certain width
String.zeroPad = function(n,d)
{
var s = n.toString();
if(s.length < d)
s = "000000000000000000000000000".substr(0,d-s.length) + s;
return s;
};
String.prototype.startsWith = function(prefix)
{
return !prefix || this.substring(0,prefix.length) == prefix;
};
// Returns the first value of the given named parameter.
function getParam(params,name,defaultValue)
{
if(!params)
return defaultValue;
var p = params[0][name];
return p ? p[0] : defaultValue;
}
// Returns the first value of the given boolean named parameter.
function getFlag(params,name,defaultValue)
{
return !!getParam(params,name,defaultValue);
}
//--
//-- Augmented methods for the JavaScript Date() object
//--
// Substitute date components into a string
Date.prototype.formatString = function(template)
{
var t = template.replace(/0hh12/g,String.zeroPad(this.getHours12(),2));
t = t.replace(/hh12/g,this.getHours12());
t = t.replace(/0hh/g,String.zeroPad(this.getHours(),2));
t = t.replace(/hh/g,this.getHours());
t = t.replace(/mmm/g,config.messages.dates.shortMonths[this.getMonth()]);
t = t.replace(/0mm/g,String.zeroPad(this.getMinutes(),2));
t = t.replace(/mm/g,this.getMinutes());
t = t.replace(/0ss/g,String.zeroPad(this.getSeconds(),2));
t = t.replace(/ss/g,this.getSeconds());
t = t.replace(/[ap]m/g,this.getAmPm().toLowerCase());
t = t.replace(/[AP]M/g,this.getAmPm().toUpperCase());
t = t.replace(/wYYYY/g,this.getYearForWeekNo());
t = t.replace(/wYY/g,String.zeroPad(this.getYearForWeekNo()-2000,2));
t = t.replace(/YYYY/g,this.getFullYear());
t = t.replace(/YY/g,String.zeroPad(this.getFullYear()-2000,2));
t = t.replace(/MMM/g,config.messages.dates.months[this.getMonth()]);
t = t.replace(/0MM/g,String.zeroPad(this.getMonth()+1,2));
t = t.replace(/MM/g,this.getMonth()+1);
t = t.replace(/0WW/g,String.zeroPad(this.getWeek(),2));
t = t.replace(/WW/g,this.getWeek());
t = t.replace(/DDD/g,config.messages.dates.days[this.getDay()]);
t = t.replace(/ddd/g,config.messages.dates.shortDays[this.getDay()]);
t = t.replace(/0DD/g,String.zeroPad(this.getDate(),2));
t = t.replace(/DDth/g,this.getDate()+this.daySuffix());
t = t.replace(/DD/g,this.getDate());
var tz = this.getTimezoneOffset();
var atz = Math.abs(tz);
t = t.replace(/TZD/g,(tz < 0 ? '+' : '-') + String.zeroPad(Math.floor(atz / 60),2) + ':' + String.zeroPad(atz % 60,2));
t = t.replace(/\\/g,"");
return t;
};
Date.prototype.getWeek = function()
{
var dt = new Date(this.getTime());
var d = dt.getDay();
if(d==0) d=7;// JavaScript Sun=0, ISO Sun=7
dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week to calculate weekNo
var n = Math.floor((dt.getTime()-new Date(dt.getFullYear(),0,1)+3600000)/86400000);
return Math.floor(n/7)+1;
};
Date.prototype.getYearForWeekNo = function()
{
var dt = new Date(this.getTime());
var d = dt.getDay();
if(d==0) d=7;// JavaScript Sun=0, ISO Sun=7
dt.setTime(dt.getTime()+(4-d)*86400000);// shift day to Thurs of same week
return dt.getFullYear();
};
Date.prototype.getHours12 = function()
{
var h = this.getHours();
return h > 12 ? h-12 : ( h > 0 ? h : 12 );
};
Date.prototype.getAmPm = function()
{
return this.getHours() >= 12 ? config.messages.dates.pm : config.messages.dates.am;
};
Date.prototype.daySuffix = function()
{
return config.messages.dates.daySuffixes[this.getDate()-1];
};
// Convert a date to local YYYYMMDDHHMM string format
Date.prototype.convertToLocalYYYYMMDDHHMM = function()
{
return this.getFullYear() + String.zeroPad(this.getMonth()+1,2) + String.zeroPad(this.getDate(),2) + String.zeroPad(this.getHours(),2) + String.zeroPad(this.getMinutes(),2);
};
// Convert a date to UTC YYYYMMDDHHMM string format
Date.prototype.convertToYYYYMMDDHHMM = function()
{
return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2);
};
// Convert a date to UTC YYYYMMDD.HHMMSSMMM string format
Date.prototype.convertToYYYYMMDDHHMMSSMMM = function()
{
return this.getUTCFullYear() + String.zeroPad(this.getUTCMonth()+1,2) + String.zeroPad(this.getUTCDate(),2) + "." + String.zeroPad(this.getUTCHours(),2) + String.zeroPad(this.getUTCMinutes(),2) + String.zeroPad(this.getUTCSeconds(),2) + String.zeroPad(this.getUTCMilliseconds(),3) +"0";
};
// Static method to create a date from a UTC YYYYMMDDHHMM format string
Date.convertFromYYYYMMDDHHMM = function(d)
{
d = d?d.replace(/[^0-9]/g, ""):"";
return Date.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,12));
};
// Static method to create a date from a UTC YYYYMMDDHHMMSS format string
Date.convertFromYYYYMMDDHHMMSS = function(d)
{
d = d?d.replace(/[^0-9]/g, ""):"";
return Date.convertFromYYYYMMDDHHMMSSMMM(d.substr(0,14));
};
// Static method to create a date from a UTC YYYYMMDDHHMMSSMMM format string
Date.convertFromYYYYMMDDHHMMSSMMM = function(d)
{
d = d ? d.replace(/[^0-9]/g, "") : "";
return new Date(Date.UTC(parseInt(d.substr(0,4),10),
parseInt(d.substr(4,2),10)-1,
parseInt(d.substr(6,2),10),
parseInt(d.substr(8,2)||"00",10),
parseInt(d.substr(10,2)||"00",10),
parseInt(d.substr(12,2)||"00",10),
parseInt(d.substr(14,3)||"000",10)));
};
//--
//-- RGB colour object
//--
// Construct an RGB colour object from a '#rrggbb', '#rgb' or 'rgb(n,n,n)' string or from separate r,g,b values
function RGB(r,g,b)
{
this.r = 0;
this.g = 0;
this.b = 0;
if(typeof r == "string") {
if(r.substr(0,1) == "#") {
if(r.length == 7) {
this.r = parseInt(r.substr(1,2),16)/255;
this.g = parseInt(r.substr(3,2),16)/255;
this.b = parseInt(r.substr(5,2),16)/255;
} else {
this.r = parseInt(r.substr(1,1),16)/15;
this.g = parseInt(r.substr(2,1),16)/15;
this.b = parseInt(r.substr(3,1),16)/15;
}
} else {
var rgbPattern = /rgb\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/;
var c = r.match(rgbPattern);
if(c) {
this.r = parseInt(c[1],10)/255;
this.g = parseInt(c[2],10)/255;
this.b = parseInt(c[3],10)/255;
}
}
} else {
this.r = r;
this.g = g;
this.b = b;
}
return this;
}
// Mixes this colour with another in a specified proportion
// c = other colour to mix
// f = 0..1 where 0 is this colour and 1 is the new colour
// Returns an RGB object
RGB.prototype.mix = function(c,f)
{
return new RGB(this.r + (c.r-this.r) * f,this.g + (c.g-this.g) * f,this.b + (c.b-this.b) * f);
};
// Return an rgb colour as a #rrggbb format hex string
RGB.prototype.toString = function()
{
var clamp = function(x,min,max) {
return x < min ? min : (x > max ? max : x);
};
return "#" +
("0" + Math.floor(clamp(this.r,0,1) * 255).toString(16)).right(2) +
("0" + Math.floor(clamp(this.g,0,1) * 255).toString(16)).right(2) +
("0" + Math.floor(clamp(this.b,0,1) * 255).toString(16)).right(2);
};
//--
//-- DOM utilities - many derived from www.quirksmode.org
//--
function drawGradient(place,horiz,locolors,hicolors)
{
if(!hicolors)
hicolors = locolors;
var t;
for(t=0; t<= 100; t+=2) {
var bar = document.createElement("div");
place.appendChild(bar);
bar.style.position = "absolute";
bar.style.left = horiz ? t + "%" : 0;
bar.style.top = horiz ? 0 : t + "%";
bar.style.width = horiz ? (101-t) + "%" : "100%";
bar.style.height = horiz ? "100%" : (101-t) + "%";
bar.style.zIndex = -1;
var p = t/100*(locolors.length-1);
var hc = hicolors[Math.floor(p)];
if(typeof hc == "string")
hc = new RGB(hc);
var lc = locolors[Math.ceil(p)];
if(typeof lc == "string")
lc = new RGB(lc);
bar.style.backgroundColor = hc.mix(lc,p-Math.floor(p)).toString();
}
}
function addEvent(obj,type,fn)
{
if(obj.attachEvent) {
obj["e"+type+fn] = fn;
obj[type+fn] = function(){obj["e"+type+fn](window.event);};
obj.attachEvent("on"+type,obj[type+fn]);
} else {
obj.addEventListener(type,fn,false);
}
}
function removeEvent(obj,type,fn)
{
if(obj.detachEvent) {
obj.detachEvent("on"+type,obj[type+fn]);
obj[type+fn] = null;
} else {
obj.removeEventListener(type,fn,false);
}
}
// Find the closest relative with a given property value (property defaults to tagName, relative defaults to parentNode)
function findRelated(e,value,name,relative)
{
name = name || "tagName";
relative = relative || "parentNode";
if(name == "className") {
while(e && !jQuery(e).hasClass(value)) {
e = e[relative];
}
} else {
while(e && e[name] != value) {
e = e[relative];
}
}
return e;
}
// Get the scroll position for window.scrollTo necessary to scroll a given element into view
function ensureVisible(e)
{
var posTop = findPosY(e);
var posBot = posTop + e.offsetHeight;
var winTop = findScrollY();
var winHeight = findWindowHeight();
var winBot = winTop + winHeight;
if(posTop < winTop) {
return posTop;
} else if(posBot > winBot) {
if(e.offsetHeight < winHeight)
return posTop - (winHeight - e.offsetHeight);
else
return posTop;
} else {
return winTop;
}
}
// Get the current width of the display window
function findWindowWidth()
{
return window.innerWidth || document.documentElement.clientWidth;
}
// Get the current height of the display window
function findWindowHeight()
{
return window.innerHeight || document.documentElement.clientHeight;
}
// Get the current height of the document
function findDocHeight() {
var D = document;
return Math.max(
Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
Math.max(D.body.clientHeight, D.documentElement.clientHeight)
);
}
// Get the current horizontal page scroll position
function findScrollX()
{
return window.scrollX || document.documentElement.scrollLeft;
}
// Get the current vertical page scroll position
function findScrollY()
{
return window.scrollY || document.documentElement.scrollTop;
}
function findPosX(obj)
{
var curleft = 0;
while(obj.offsetParent) {
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
return curleft;
}
function findPosY(obj)
{
var curtop = 0;
while(obj.offsetParent) {
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
return curtop;
}
// Blur a particular element
function blurElement(e)
{
if(e && e.focus && e.blur) {
e.focus();
e.blur();
}
}
// Create a non-breaking space
function insertSpacer(place)
{
var e = document.createTextNode(String.fromCharCode(160));
if(place)
place.appendChild(e);
return e;
}
// Replace the current selection of a textarea or text input and scroll it into view
function replaceSelection(e,text)
{
if(e.setSelectionRange) {
var oldpos = e.selectionStart;
var isRange = e.selectionEnd > e.selectionStart;
e.value = e.value.substr(0,e.selectionStart) + text + e.value.substr(e.selectionEnd);
e.setSelectionRange(isRange ? oldpos : oldpos + text.length,oldpos + text.length);
var linecount = e.value.split("\n").length;
var thisline = e.value.substr(0,e.selectionStart).split("\n").length-1;
e.scrollTop = Math.floor((thisline - e.rows / 2) * e.scrollHeight / linecount);
} else if(document.selection) {
var range = document.selection.createRange();
if(range.parentElement() == e) {
var isCollapsed = range.text == "";
range.text = text;
if(!isCollapsed) {
range.moveStart("character", -text.length);
range.select();
}
}
}
}
// Set the caret position in a text area
function setCaretPosition(e,pos)
{
if(e.selectionStart || e.selectionStart == '0') {
e.selectionStart = pos;
e.selectionEnd = pos;
e.focus();
} else if(document.selection) {
// IE support
e.focus ();
var sel = document.selection.createRange();
sel.moveStart('character', -e.value.length);
sel.moveStart('character',pos);
sel.moveEnd('character',0);
sel.select();
}
}
// Returns the text of the given (text) node, possibly merging subsequent text nodes
function getNodeText(e)
{
var t = "";
while(e && e.nodeName == "#text") {
t += e.nodeValue;
e = e.nextSibling;
}
return t;
}
// Returns true if the element e has a given ancestor element
function isDescendant(e,ancestor)
{
while(e) {
if(e === ancestor)
return true;
e = e.parentNode;
}
return false;
}
// deprecate the following...
// Prevent an event from bubbling
function stopEvent(e)
{
var ev = e || window.event;
ev.cancelBubble = true;
if(ev.stopPropagation) ev.stopPropagation();
return false;
}
// Remove any event handlers or non-primitve custom attributes
function scrubNode(e)
{
if(!config.browser.isIE)
return;
var att = e.attributes;
if(att) {
var t;
for(t=0; t<att.length; t++) {
var n = att[t].name;
if(n !== "style" && (typeof e[n] === "function" || (typeof e[n] === "object" && e[n] != null))) {
try {
e[n] = null;
} catch(ex) {
}
}
}
}
var c = e.firstChild;
while(c) {
scrubNode(c);
c = c.nextSibling;
}
}
function setStylesheet(s,id,doc)
{
jQuery.twStylesheet(s,{id:id,doc:doc});
}
function removeStyleSheet(id)
{
jQuery.twStylesheet.remove({id:id});
}
//--
//-- LoaderBase and SaverBase
//--
function LoaderBase() {}
LoaderBase.prototype.loadTiddler = function(store,node,tiddlers)
{
var title = this.getTitle(store,node);
if(safeMode && store.isShadowTiddler(title))
return;
if(title) {
var tiddler = store.createTiddler(title);
this.internalizeTiddler(store,tiddler,title,node);
tiddlers.push(tiddler);
}
};
LoaderBase.prototype.loadTiddlers = function(store,nodes)
{
var t,tiddlers = [];
for(t = 0; t < nodes.length; t++) {
try {
this.loadTiddler(store,nodes[t],tiddlers);
} catch(ex) {
showException(ex,config.messages.tiddlerLoadError.format([this.getTitle(store,nodes[t])]));
}
}
return tiddlers;
};
function SaverBase() {}
SaverBase.prototype.externalize = function(store)
{
var results = [];
var t,tiddlers = store.getTiddlers("title");
for(t = 0; t < tiddlers.length; t++) {
if(!tiddlers[t].doNotSave())
results.push(this.externalizeTiddler(store, tiddlers[t]));
}
return results.join("\n");
};
//--
//-- TW21Loader (inherits from LoaderBase)
//--
function TW21Loader() {}
TW21Loader.prototype = new LoaderBase();
TW21Loader.prototype.getTitle = function(store,node)
{
var title = null;
if(node.getAttribute) {
title = node.getAttribute("title");
if(!title)
title = node.getAttribute("tiddler");
}
if(!title && node.id) {
var lenPrefix = store.idPrefix.length;
if(node.id.substr(0,lenPrefix) == store.idPrefix)
title = node.id.substr(lenPrefix);
}
return title;
};
TW21Loader.prototype.internalizeTiddler = function(store,tiddler,title,node)
{
var e = node.firstChild;
var text = null;
if(node.getAttribute("tiddler")) {
text = getNodeText(e).unescapeLineBreaks();
} else {
while(e.nodeName!="PRE" && e.nodeName!="pre") {
e = e.nextSibling;
}
text = e.innerHTML.replace(/\r/mg,"").htmlDecode();
}
var creator = node.getAttribute("creator");
var modifier = node.getAttribute("modifier");
var c = node.getAttribute("created");
var m = node.getAttribute("modified");
var created = c ? Date.convertFromYYYYMMDDHHMMSS(c) : version.date;
var modified = m ? Date.convertFromYYYYMMDDHHMMSS(m) : created;
var tags = node.getAttribute("tags");
var fields = {};
var i,attrs = node.attributes;
for(i = attrs.length-1; i >= 0; i--) {
var name = attrs[i].name;
if(attrs[i].specified && !TiddlyWiki.isStandardField(name)) {
fields[name] = attrs[i].value.unescapeLineBreaks();
}
}
tiddler.assign(title,text,modifier,modified,tags,created,fields,creator);
return tiddler;
};
//--
//-- TW21Saver (inherits from SaverBase)
//--
function TW21Saver() {}
TW21Saver.prototype = new SaverBase();
TW21Saver.prototype.externalizeTiddler = function(store,tiddler)
{
try {
var extendedAttributes = "";
var usePre = config.options.chkUsePreForStorage;
store.forEachField(tiddler,
function(tiddler,fieldName,value) {
// don't store stuff from the temp namespace
if(typeof value != "string")
value = "";
if(!fieldName.match(/^temp\./))
extendedAttributes += ' %0="%1"'.format([fieldName,value.escapeLineBreaks().htmlEncode()]);
},true);
var created = tiddler.created;
var modified = tiddler.modified;
var attributes = tiddler.creator ? ' creator="' + tiddler.creator.htmlEncode() + '"' : "";
attributes += tiddler.modifier ? ' modifier="' + tiddler.modifier.htmlEncode() + '"' : "";
attributes += (usePre && created == version.date) ? "" :' created="' + created.convertToYYYYMMDDHHMM() + '"';
attributes += (usePre && modified == created) ? "" : ' modified="' + modified.convertToYYYYMMDDHHMM() +'"';
var tags = tiddler.getTags();
if(!usePre || tags)
attributes += ' tags="' + tags.htmlEncode() + '"';
return ('<div %0="%1"%2%3>%4</'+'div>').format([
usePre ? "title" : "tiddler",
tiddler.title.htmlEncode(),
attributes,
extendedAttributes,
usePre ? "\n<pre>" + tiddler.text.htmlEncode() + "</pre>\n" : tiddler.text.escapeLineBreaks().htmlEncode()
]);
} catch (ex) {
throw exceptionText(ex,config.messages.tiddlerSaveError.format([tiddler.title]));
}
};
/***
|''Name''|TiddlyWebAdaptor|
|''Description''|adaptor for interacting with TiddlyWeb|
|''Author:''|FND|
|''Contributors''|Chris Dent, Martin Budden|
|''Version''|1.4.10|
|''Status''|stable|
|''Source''|http://svn.tiddlywiki.org/Trunk/association/adaptors/TiddlyWebAdaptor.js|
|''CodeRepository''|http://svn.tiddlywiki.org/Trunk/association/|
|''License''|[[BSD|http://www.opensource.org/licenses/bsd-license.php]]|
|''CoreVersion''|2.5|
|''Keywords''|serverSide TiddlyWeb|
!Notes
This plugin includes [[jQuery JSON|http://code.google.com/p/jquery-json/]].
!To Do
* createWorkspace
* document custom/optional context attributes (e.g. filters, query, revision) and tiddler fields (e.g. server.title, origin)
!Code
***/
//{{{
(function($) {
var adaptor = config.adaptors.tiddlyweb = function() {};
adaptor.prototype = new AdaptorBase();
adaptor.serverType = "tiddlyweb";
adaptor.serverLabel = "TiddlyWeb";
adaptor.mimeType = "application/json";
adaptor.parsingErrorMessage = "Error parsing result from server";
adaptor.noBagErrorMessage = "no bag specified for tiddler";
adaptor.locationIDErrorMessage = "no bag or recipe specified for tiddler"; // TODO: rename
// retrieve current status (requires TiddlyWeb status plugin)
adaptor.prototype.getStatus = function(context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/status";
var uri = uriTemplate.format([context.host]);
var req = httpReq("GET", uri, adaptor.getStatusCallback, context,
null, null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getStatusCallback = function(status, context, responseText, uri, xhr) {
context.status = responseText ? status : false;
try {
context.statusText = xhr.statusText;
} catch(exc) { // offline (Firefox)
context.status = false;
context.statusText = null;
}
context.httpStatus = xhr.status;
if(context.status) {
context.serverStatus = $.evalJSON(responseText); // XXX: error handling!?
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// retrieve a list of workspaces
adaptor.prototype.getWorkspaceList = function(context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.workspaces = [];
var uriTemplate = "%0/recipes"; // XXX: bags?
var uri = uriTemplate.format([context.host]);
var req = httpReq("GET", uri, adaptor.getWorkspaceListCallback,
context, { accept: adaptor.mimeType }, null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getWorkspaceListCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
try {
var workspaces = $.evalJSON(responseText);
} catch(ex) {
context.status = false; // XXX: correct?
context.statusText = exceptionText(ex, adaptor.parsingErrorMessage);
if(context.callback) {
context.callback(context, context.userParams);
}
return;
}
context.workspaces = workspaces.map(function(itm) { return { title: itm }; });
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// retrieve a list of tiddlers
adaptor.prototype.getTiddlerList = function(context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/%1/%2/tiddlers%3";
var params = context.filters ? "?" + context.filters : "";
if(context.format) {
params = context.format + params;
}
var workspace = adaptor.resolveWorkspace(context.workspace);
var uri = uriTemplate.format([context.host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name), params]);
var req = httpReq("GET", uri, adaptor.getTiddlerListCallback,
context, merge({ accept: adaptor.mimeType }, context.headers), null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerListCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
context.tiddlers = [];
try {
var tiddlers = $.evalJSON(responseText); //# NB: not actual tiddler instances
} catch(ex) {
context.status = false; // XXX: correct?
context.statusText = exceptionText(ex, adaptor.parsingErrorMessage);
if(context.callback) {
context.callback(context, context.userParams);
}
return;
}
for(var i = 0; i < tiddlers.length; i++) {
var tiddler = adaptor.toTiddler(tiddlers[i], context.host);
context.tiddlers.push(tiddler);
}
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// perform global search
adaptor.prototype.getSearchResults = function(context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/search?q=%1%2";
var filterString = context.filters ? ";" + context.filters : "";
var uri = uriTemplate.format([context.host, context.query, filterString]); // XXX: parameters need escaping?
var req = httpReq("GET", uri, adaptor.getSearchResultsCallback,
context, { accept: adaptor.mimeType }, null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getSearchResultsCallback = function(status, context, responseText, uri, xhr) {
adaptor.getTiddlerListCallback(status, context, responseText, uri, xhr); // XXX: use apply?
};
// retrieve a particular tiddler's revisions
adaptor.prototype.getTiddlerRevisionList = function(title, limit, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/%1/%2/tiddlers/%3/revisions";
var workspace = adaptor.resolveWorkspace(context.workspace);
var uri = uriTemplate.format([context.host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name), adaptor.normalizeTitle(title)]);
var req = httpReq("GET", uri, adaptor.getTiddlerRevisionListCallback,
context, merge({ accept: adaptor.mimeType }, context.headers), null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerRevisionListCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
context.revisions = [];
try {
var tiddlers = $.evalJSON(responseText); //# NB: not actual tiddler instances
} catch(ex) {
context.status = false; // XXX: correct?
context.statusText = exceptionText(ex, adaptor.parsingErrorMessage);
if(context.callback) {
context.callback(context, context.userParams);
}
return;
}
for(var i = 0; i < tiddlers.length; i++) {
var tiddler = adaptor.toTiddler(tiddlers[i], context.host);
context.revisions.push(tiddler);
}
var sortField = "server.page.revision";
context.revisions.sort(function(a, b) {
return a.fields[sortField] < b.fields[sortField] ? 1 :
(a.fields[sortField] == b.fields[sortField] ? 0 : -1);
});
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// retrieve an individual tiddler revision -- XXX: breaks with standard arguments list -- XXX: convenience function; simply use getTiddler?
adaptor.prototype.getTiddlerRevision = function(title, revision, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.revision = revision;
return this.getTiddler(title, context, userParams, callback);
};
// retrieve an individual tiddler
adaptor.prototype.getTiddler = function(title, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = title;
if(context.revision) {
var uriTemplate = "%0/%1/%2/tiddlers/%3/revisions/%4";
} else {
uriTemplate = "%0/%1/%2/tiddlers/%3";
}
if(!context.tiddler) {
context.tiddler = new Tiddler(title);
}
context.tiddler.fields["server.type"] = adaptor.serverType;
context.tiddler.fields["server.host"] = AdaptorBase.minHostName(context.host);
context.tiddler.fields["server.workspace"] = context.workspace;
var workspace = adaptor.resolveWorkspace(context.workspace);
var uri = uriTemplate.format([context.host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name), adaptor.normalizeTitle(title),
context.revision]);
var req = httpReq("GET", uri, adaptor.getTiddlerCallback, context,
merge({ accept: adaptor.mimeType }, context.headers), null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
try {
var tid = $.evalJSON(responseText);
} catch(ex) {
context.status = false;
context.statusText = exceptionText(ex, adaptor.parsingErrorMessage);
if(context.callback) {
context.callback(context, context.userParams);
}
return;
}
var tiddler = adaptor.toTiddler(tid, context.host);
tiddler.title = context.tiddler.title;
tiddler.fields["server.etag"] = xhr.getResponseHeader("Etag");
// normally we'd assign context.tiddler = tiddler here - but we can't do
// that because of IE, which triggers getTiddler in putTiddlerCallback,
// and since ServerSideSavingPlugin foolishly relies on persistent
// object references, we need to merge the data into the existing object
$.extend(context.tiddler, tiddler);
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// retrieve tiddler chronicle (all revisions)
adaptor.prototype.getTiddlerChronicle = function(title, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = title;
var uriTemplate = "%0/%1/%2/tiddlers/%3/revisions?fat=1";
var workspace = adaptor.resolveWorkspace(context.workspace);
var uri = uriTemplate.format([context.host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name), adaptor.normalizeTitle(title)]);
var req = httpReq("GET", uri, adaptor.getTiddlerChronicleCallback,
context, { accept: adaptor.mimeType }, null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerChronicleCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(status) {
context.responseText = responseText;
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// store an individual tiddler
adaptor.prototype.putTiddler = function(tiddler, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = tiddler.title;
context.tiddler = tiddler;
context.host = context.host || this.fullHostName(tiddler.fields["server.host"]);
var uriTemplate = "%0/%1/%2/tiddlers/%3";
try {
context.workspace = context.workspace || tiddler.fields["server.workspace"];
var workspace = adaptor.resolveWorkspace(context.workspace);
} catch(ex) {
return adaptor.locationIDErrorMessage;
}
var uri = uriTemplate.format([context.host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name),
adaptor.normalizeTitle(tiddler.title)]);
var etag = adaptor.generateETag(workspace, tiddler);
var headers = etag ? { "If-Match": etag } : null;
var payload = {
type: tiddler.fields["server.content-type"] || null,
text: tiddler.text,
tags: tiddler.tags,
fields: $.extend({}, tiddler.fields)
};
delete payload.fields.changecount;
$.each(payload.fields, function(key, value) {
if(key.indexOf("server.") == 0) {
delete payload.fields[key];
}
});
payload = $.toJSON(payload);
var req = httpReq("PUT", uri, adaptor.putTiddlerCallback,
context, headers, payload, adaptor.mimeType, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.putTiddlerCallback = function(status, context, responseText, uri, xhr) {
context.status = [204, 1223].contains(xhr.status);
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(context.status) {
var loc = xhr.getResponseHeader("Location");
var etag = xhr.getResponseHeader("Etag");
if(loc && etag) {
var bag = loc.split("/bags/").pop().split("/")[0];
context.tiddler.fields["server.bag"] = bag;
context.tiddler.fields["server.workspace"] = "bags/" + bag;
var rev = etag.split("/").pop().split(/;|:/)[0];
context.tiddler.fields["server.page.revision"] = rev;
context.tiddler.fields["server.etag"] = etag;
if(context.callback) {
context.callback(context, context.userParams);
}
} else { // IE
context.adaptor.getTiddler(context.tiddler.title, context,
context.userParams, context.callback);
}
} else if(context.callback) {
context.callback(context, context.userParams);
}
};
// store a tiddler chronicle
adaptor.prototype.putTiddlerChronicle = function(revisions, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = revisions[0].title;
var headers = null;
var uriTemplate = "%0/%1/%2/tiddlers/%3/revisions";
var host = context.host || this.fullHostName(tiddler.fields["server.host"]);
var workspace = adaptor.resolveWorkspace(context.workspace);
var uri = uriTemplate.format([host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name),
adaptor.normalizeTitle(context.title)]);
if(workspace.type == "bag") { // generate ETag
var etag = [adaptor.normalizeTitle(workspace.name),
adaptor.normalizeTitle(context.title), 0].join("/"); //# zero-revision prevents overwriting existing contents
headers = { "If-Match": '"' + etag + '"' };
}
var payload = $.toJSON(revisions);
var req = httpReq("POST", uri, adaptor.putTiddlerChronicleCallback,
context, headers, payload, adaptor.mimeType, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.putTiddlerChronicleCallback = function(status, context, responseText, uri, xhr) {
context.status = [204, 1223].contains(xhr.status);
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(context.callback) {
context.callback(context, context.userParams);
}
};
// store a collection of tiddlers (import TiddlyWiki HTML store)
adaptor.prototype.putTiddlerStore = function(store, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
var uriTemplate = "%0/%1/%2/tiddlers";
var host = context.host;
var workspace = adaptor.resolveWorkspace(context.workspace);
var uri = uriTemplate.format([host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name)]);
var req = httpReq("POST", uri, adaptor.putTiddlerStoreCallback,
context, null, store, "text/x-tiddlywiki", null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.putTiddlerStoreCallback = function(status, context, responseText, uri, xhr) {
context.status = [204, 1223].contains(xhr.status);
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(context.callback) {
context.callback(context, context.userParams);
}
};
// rename an individual tiddler or move it to a different workspace -- TODO: make {from|to}.title optional
adaptor.prototype.moveTiddler = function(from, to, context, userParams, callback) { // XXX: rename parameters (old/new)?
var self = this;
var newTiddler = store.getTiddler(from.title) || store.getTiddler(to.title); //# local rename might already have occurred
var oldTiddler = $.extend(true, {}, newTiddler); //# required for eventual deletion
oldTiddler.title = from.title; //# required for original tiddler's ETag
var _getTiddlerChronicle = function(title, context, userParams, callback) {
return self.getTiddlerChronicle(title, context, userParams, callback);
};
var _putTiddlerChronicle = function(context, userParams) {
if(!context.status) {
return callback(context, userParams);
}
var revisions = $.evalJSON(context.responseText); // XXX: error handling?
// change current title while retaining previous location
for(var i = 0; i < revisions.length; i++) {
delete revisions[i].revision;
if(!revisions[i].fields.origin) { // NB: origin = "<workspace>/<title>"
revisions[i].fields.origin = ["bags", revisions[i].bag, revisions[i].title].join("/");
}
revisions[i].title = to.title;
}
// add new revision
var rev = $.extend({}, revisions[0]);
$.each(newTiddler, function(i, item) {
if(!$.isFunction(item)) {
rev[i] = item;
}
});
rev.title = to.title;
rev.created = rev.created.convertToYYYYMMDDHHMM();
rev.modified = new Date().convertToYYYYMMDDHHMM();
delete rev.fields.changecount;
revisions.unshift(rev);
if(to.workspace) {
context.workspace = to.workspace;
} else if(context.workspace.substring(0, 4) != "bags") { // NB: target workspace must be a bag
context.workspace = "bags/" + rev.bag;
}
var subCallback = function(context, userParams) {
if(!context.status) {
return callback(context, userParams);
}
context.adaptor.getTiddler(newTiddler.title, context, userParams, _deleteTiddler);
};
return self.putTiddlerChronicle(revisions, context, context.userParams, subCallback);
};
var _deleteTiddler = function(context, userParams) {
if(!context.status) {
return callback(context, userParams);
}
$.extend(true, newTiddler, context.tiddler);
context.callback = null;
return self.deleteTiddler(oldTiddler, context, context.userParams, callback);
};
callback = callback || function() {};
context = this.setContext(context, userParams);
context.host = context.host || oldTiddler.fields["server.host"];
context.workspace = from.workspace || oldTiddler.fields["server.workspace"];
return _getTiddlerChronicle(from.title, context, userParams, _putTiddlerChronicle);
};
// delete an individual tiddler
adaptor.prototype.deleteTiddler = function(tiddler, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = tiddler.title; // XXX: not required!?
var uriTemplate = "%0/bags/%1/tiddlers/%2";
var host = context.host || this.fullHostName(tiddler.fields["server.host"]);
var bag = tiddler.fields["server.bag"];
if(!bag) {
return adaptor.noBagErrorMessage;
}
var uri = uriTemplate.format([host, adaptor.normalizeTitle(bag),
adaptor.normalizeTitle(tiddler.title)]);
var etag = adaptor.generateETag({ type: "bag", name: bag }, tiddler);
var headers = etag ? { "If-Match": etag } : null;
var req = httpReq("DELETE", uri, adaptor.deleteTiddlerCallback, context, headers,
null, null, null, null, true);
return typeof req == "string" ? req : true;
};
adaptor.deleteTiddlerCallback = function(status, context, responseText, uri, xhr) {
context.status = [204, 1223].contains(xhr.status);
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
if(context.callback) {
context.callback(context, context.userParams);
}
};
// compare two revisions of a tiddler (requires TiddlyWeb differ plugin)
adaptor.prototype.getTiddlerDiff = function(title, context, userParams, callback) {
context = this.setContext(context, userParams, callback);
context.title = title;
var tiddler = store.getTiddler(title);
try {
var workspace = adaptor.resolveWorkspace(tiddler.fields["server.workspace"]);
} catch(ex) {
return adaptor.locationIDErrorMessage;
}
var tiddlerRef = [workspace.type + "s", workspace.name, tiddler.title].join("/");
var rev1 = context.rev1 ? [tiddlerRef, context.rev1].join("/") : tiddlerRef;
var rev2 = context.rev2 ? [tiddlerRef, context.rev2].join("/") : null;
var uriTemplate = "%0/diff?rev1=%1";
if(rev2) {
uriTemplate += "&rev2=%2";
}
if(context.format) {
uriTemplate += "&format=%3";
}
var host = context.host || this.fullHostName(tiddler.fields["server.host"]);
var uri = uriTemplate.format([host, adaptor.normalizeTitle(rev1),
adaptor.normalizeTitle(rev2), context.format]);
if(rev2) {
var req = httpReq("GET", uri, adaptor.getTiddlerDiffCallback, context, null,
null, null, null, null, true);
} else {
var payload = {
title: tiddler.title,
text: tiddler.text,
modifier: tiddler.modifier,
tags: tiddler.tags,
fields: $.extend({}, tiddler.fields)
}; // XXX: missing attributes!?
payload = $.toJSON(payload);
req = httpReq("POST", uri, adaptor.getTiddlerDiffCallback, context,
null, payload, adaptor.mimeType, null, null, true);
}
return typeof req == "string" ? req : true;
};
adaptor.getTiddlerDiffCallback = function(status, context, responseText, uri, xhr) {
context.status = status;
context.statusText = xhr.statusText;
context.httpStatus = xhr.status;
context.uri = uri;
if(status) {
context.diff = responseText;
}
if(context.callback) {
context.callback(context, context.userParams);
}
};
// generate tiddler information
adaptor.prototype.generateTiddlerInfo = function(tiddler) {
var info = {};
var uriTemplate = "%0/%1/%2/tiddlers/%3";
var host = this.host || tiddler.fields["server.host"]; // XXX: this.host obsolete?
host = this.fullHostName(host);
var workspace = adaptor.resolveWorkspace(tiddler.fields["server.workspace"]);
info.uri = uriTemplate.format([host, workspace.type + "s",
adaptor.normalizeTitle(workspace.name),
adaptor.normalizeTitle(tiddler.title)]);
return info;
};
// create Tiddler instance from TiddlyWeb tiddler JSON
adaptor.toTiddler = function(json, host) {
var created = Date.convertFromYYYYMMDDHHMM(json.created);
var modified = Date.convertFromYYYYMMDDHHMM(json.modified);
var fields = json.fields;
fields["server.type"] = adaptor.serverType;
fields["server.host"] = AdaptorBase.minHostName(host);
fields["server.bag"] = json.bag;
fields["server.title"] = json.title;
if(json.recipe) {
fields["server.recipe"] = json.recipe;
}
if(json.type && json.type != "None") {
fields["server.content-type"] = json.type;
}
fields["server.permissions"] = json.permissions.join(", ");
fields["server.page.revision"] = json.revision;
fields["server.workspace"] = "bags/" + json.bag;
var tiddler = new Tiddler(json.title);
tiddler.assign(tiddler.title, json.text, json.modifier, modified, json.tags,
created, json.fields, json.creator);
return tiddler;
};
adaptor.resolveWorkspace = function(workspace) {
var components = workspace.split("/");
return {
type: components[0] == "bags" ? "bag" : "recipe",
name: components[1] || components[0]
};
};
adaptor.generateETag = function(workspace, tiddler) {
var revision = tiddler.fields["server.page.revision"];
var etag = revision == "false" ? null : tiddler.fields["server.etag"];
if(!etag && workspace.type == "bag") {
if(typeof revision == "undefined") {
revision = "0";
} else if(revision == "false") {
return null;
}
etag = [adaptor.normalizeTitle(workspace.name),
adaptor.normalizeTitle(tiddler.title), revision].join("/");
etag = '"' + etag + '"';
}
return etag;
};
adaptor.normalizeTitle = function(title) {
return encodeURIComponent(title);
};
})(jQuery);
/*
* jQuery JSON Plugin
* version: 1.3
* source: http://code.google.com/p/jquery-json/
* license: MIT (http://www.opensource.org/licenses/mit-license.php)
*/
(function($){function toIntegersAtLease(n)
{return n<10?'0'+n:n;}
Date.prototype.toJSON=function(date)
{return this.getUTCFullYear()+'-'+
toIntegersAtLease(this.getUTCMonth())+'-'+
toIntegersAtLease(this.getUTCDate());};var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};$.quoteString=function(string)
{if(escapeable.test(string))
{return'"'+string.replace(escapeable,function(a)
{var c=meta[a];if(typeof c==='string'){return c;}
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
return'"'+string+'"';};$.toJSON=function(o,compact)
{var type=typeof(o);if(type=="undefined")
return"undefined";else if(type=="number"||type=="boolean")
return o+"";else if(o===null)
return"null";if(type=="string")
{return $.quoteString(o);}
if(type=="object"&&typeof o.toJSON=="function")
return o.toJSON(compact);if(type!="function"&&typeof(o.length)=="number")
{var ret=[];for(var i=0;i<o.length;i++){ret.push($.toJSON(o[i],compact));}
if(compact)
return"["+ret.join(",")+"]";else
return"["+ret.join(", ")+"]";}
if(type=="function"){throw new TypeError("Unable to convert object of type 'function' to json.");}
var ret=[];for(var k in o){var name;type=typeof(k);if(type=="number")
name='"'+k+'"';else if(type=="string")
name=$.quoteString(k);else
continue;var val=$.toJSON(o[k],compact);if(typeof(val)!="string"){continue;}
if(compact)
ret.push(name+":"+val);else
ret.push(name+": "+val);}
return"{"+ret.join(", ")+"}";};$.compactJSON=function(o)
{return $.toJSON(o,true);};$.evalJSON=function(src)
{return eval("("+src+")");};$.secureEvalJSON=function(src)
{var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,'@');filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']');filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered))
return eval("("+src+")");else
throw new SyntaxError("Error parsing JSON, source is not valid.");};})(jQuery);
//}}}
/***
|''Name''|ServerSideSavingPlugin|
|''Description''|server-side saving|
|''Author''|FND|
|''Version''|0.6.5|
|''Status''|stable|
|''Source''|http://svn.tiddlywiki.org/Trunk/association/plugins/ServerSideSavingPlugin.js|
|''License''|[[BSD|http://www.opensource.org/licenses/bsd-license.php]]|
|''CoreVersion''|2.5.3|
|''Keywords''|serverSide|
!Notes
This plugin relies on a dedicated adaptor to be present.
The specific nature of this plugin depends on the respective server.
!Revision History
!!v0.1 (2008-11-24)
* initial release
!!v0.2 (2008-12-01)
* added support for local saving
!!v0.3 (2008-12-03)
* added Save to Web macro for manual synchronization
!!v0.4 (2009-01-15)
* removed ServerConfig dependency by detecting server type from the respective tiddlers
!!v0.5 (2009-08-25)
* raised CoreVersion to 2.5.3 to take advantage of core fixes
!!v0.6 (2010-04-21)
* added notification about cross-domain restrictions to ImportTiddlers
!To Do
* conflict detection/resolution
* rename to ServerLinkPlugin?
* document deletion/renaming convention
!Code
***/
//{{{
(function($) {
readOnly = false; //# enable editing over HTTP
var plugin = config.extensions.ServerSideSavingPlugin = {};
plugin.locale = {
saved: "%0 saved successfully",
saveError: "Error saving %0: %1",
saveConflict: "Error saving %0: edit conflict",
deleted: "Removed %0",
deleteError: "Error removing %0: %1",
deleteLocalError: "Error removing %0 locally",
removedNotice: "This tiddler has been deleted.",
connectionError: "connection could not be established",
hostError: "Unable to import from this location due to cross-domain restrictions."
};
plugin.sync = function(tiddlers) {
tiddlers = tiddlers && tiddlers[0] ? tiddlers : store.getTiddlers();
$.each(tiddlers, function(i, tiddler) {
var changecount = parseInt(tiddler.fields.changecount, 10);
if(tiddler.fields.deleted === "true" && changecount === 1) {
plugin.removeTiddler(tiddler);
} else if(tiddler.isTouched() && !tiddler.doNotSave() &&
tiddler.getServerType() && tiddler.fields["server.host"]) { // XXX: server.host could be empty string
delete tiddler.fields.deleted;
plugin.saveTiddler(tiddler);
}
});
};
plugin.saveTiddler = function(tiddler) {
try {
var adaptor = this.getTiddlerServerAdaptor(tiddler);
} catch(ex) {
return false;
}
var context = {
tiddler: tiddler,
changecount: tiddler.fields.changecount,
workspace: tiddler.fields["server.workspace"]
};
var serverTitle = tiddler.fields["server.title"]; // indicates renames
if(!serverTitle) {
tiddler.fields["server.title"] = tiddler.title;
} else if(tiddler.title != serverTitle) {
return adaptor.moveTiddler({ title: serverTitle },
{ title: tiddler.title }, context, null, this.saveTiddlerCallback);
}
var req = adaptor.putTiddler(tiddler, context, {}, this.saveTiddlerCallback);
return req ? tiddler : false;
};
plugin.saveTiddlerCallback = function(context, userParams) {
var tiddler = context.tiddler;
if(context.status) {
if(tiddler.fields.changecount == context.changecount) { //# check for changes since save was triggered
tiddler.clearChangeCount();
} else if(tiddler.fields.changecount > 0) {
tiddler.fields.changecount -= context.changecount;
}
plugin.reportSuccess("saved", tiddler);
store.setDirty(false);
} else {
if(context.httpStatus == 412) {
plugin.reportFailure("saveConflict", tiddler);
} else {
plugin.reportFailure("saveError", tiddler, context);
}
}
};
plugin.removeTiddler = function(tiddler) {
try {
var adaptor = this.getTiddlerServerAdaptor(tiddler);
} catch(ex) {
return false;
}
var context = {
host: tiddler.fields["server.host"],
workspace: tiddler.fields["server.workspace"],
tiddler: tiddler
};
var req = adaptor.deleteTiddler(tiddler, context, {}, this.removeTiddlerCallback);
return req ? tiddler : false;
};
plugin.removeTiddlerCallback = function(context, userParams) {
var tiddler = context.tiddler;
if(context.status) {
if(tiddler.fields.deleted === "true") {
store.deleteTiddler(tiddler.title);
} else {
plugin.reportFailure("deleteLocalError", tiddler);
}
plugin.reportSuccess("deleted", tiddler);
store.setDirty(false);
} else {
plugin.reportFailure("deleteError", tiddler, context);
}
};
plugin.getTiddlerServerAdaptor = function(tiddler) { // XXX: rename?
var type = tiddler.fields["server.type"] || config.defaultCustomFields["server.type"];
return new config.adaptors[type]();
};
plugin.reportSuccess = function(msg, tiddler) {
displayMessage(plugin.locale[msg].format([tiddler.title]));
};
plugin.reportFailure = function(msg, tiddler, context) {
var desc = (context && context.httpStatus) ? context.statusText :
plugin.locale.connectionError;
displayMessage(plugin.locale[msg].format([tiddler.title, desc]));
};
config.macros.saveToWeb = { // XXX: hijack existing sync macro?
locale: { // TODO: merge with plugin.locale?
btnLabel: "save to web",
btnTooltip: "synchronize changes",
btnAccessKey: null
},
handler: function(place, macroName, params, wikifier, paramString, tiddler) {
createTiddlyButton(place, this.locale.btnLabel, this.locale.btnTooltip,
plugin.sync, null, null, this.locale.btnAccessKey);
}
};
window.saveChanges = function(onlyIfDirty, tiddlers) {
plugin.sync(tiddlers);
};
// override removeTiddler to flag tiddler as deleted -- XXX: use hijack to preserve compatibility?
TiddlyWiki.prototype.removeTiddler = function(title) { // XXX: should override deleteTiddler instance method?
var tiddler = this.fetchTiddler(title);
if(tiddler) {
tiddler.tags = ["excludeLists", "excludeSearch", "excludeMissing"];
tiddler.text = plugin.locale.removedNotice;
tiddler.fields.deleted = "true"; // XXX: rename to removed/tiddlerRemoved?
tiddler.fields.changecount = "1";
this.notify(title, true);
this.setDirty(true);
}
};
// hijack ImportTiddlers wizard to handle cross-domain restrictions
var _onOpen = config.macros.importTiddlers.onOpen;
config.macros.importTiddlers.onOpen = function(ev) {
var btn = $(resolveTarget(ev));
var url = btn.closest(".wizard").find("input[name=txtPath]").val();
if(window.location.protocol != "file:" && url.indexOf("://") != -1) {
var host = url.split("/")[2];
var macro = config.macros.importTiddlers;
if(host != window.location.host) {
btn.text(macro.cancelLabel).attr("title", macro.cancelPrompt);
btn[0].onclick = macro.onCancel;
$('<span class="status" />').text(plugin.locale.hostError).insertAfter(btn);
return false;
}
}
return _onOpen.apply(this, arguments);
};
})(jQuery);
//}}}
//--
//-- Backstage
//--
// Backstage tasks
config.tasks.save.action = saveChanges;
var backstage = {
area: null,
toolbar: null,
button: null,
showButton: null,
hideButton: null,
cloak: null,
panel: null,
panelBody: null,
panelFooter: null,
currTabName: null,
currTabElem: null,
content: null,
init: function() {
var cmb = config.messages.backstage;
this.area = document.getElementById("backstageArea");
this.toolbar = jQuery("#backstageToolbar").empty()[0];
this.button = jQuery("#backstageButton").empty()[0];
this.button.style.display = "block";
var t = cmb.open.text + " " + glyph("bentArrowLeft");
this.showButton = createTiddlyButton(this.button,t,cmb.open.tooltip,
function(e) {backstage.show(); return false;},null,"backstageShow");
t = glyph("bentArrowRight") + " " + cmb.close.text;
this.hideButton = createTiddlyButton(this.button,t,cmb.close.tooltip,
function(e) {backstage.hide(); return false;},null,"backstageHide");
this.cloak = document.getElementById("backstageCloak");
this.panel = document.getElementById("backstagePanel");
this.panelFooter = createTiddlyElement(this.panel,"div",null,"backstagePanelFooter");
this.panelBody = createTiddlyElement(this.panel,"div",null,"backstagePanelBody");
this.cloak.onmousedown = function(e) {backstage.switchTab(null);};
createTiddlyText(this.toolbar,cmb.prompt);
for(t=0; t<config.backstageTasks.length; t++) {
var taskName = config.backstageTasks[t];
console.log('taskName', taskName);
var task = config.tasks[taskName];
var handler = task.action ? this.onClickCommand : this.onClickTab;
var text = task.text + (task.action ? "" : glyph("downTriangle"));
var btn = createTiddlyButton(this.toolbar,text,task.tooltip,handler,"backstageTab");
jQuery(btn).addClass(task.action ? "backstageAction" : "backstageTask");
btn.setAttribute("task", taskName);
}
this.content = document.getElementById("contentWrapper");
if(config.options.chkBackstage)
this.show();
else
this.hide();
},
isVisible: function() {
return this.area ? this.area.style.display == "block" : false;
},
show: function() {
this.area.style.display = "block";
if(anim && config.options.chkAnimate) {
backstage.toolbar.style.left = findWindowWidth() + "px";
var p = [{style: "left", start: findWindowWidth(), end: 0, template: "%0px"}];
anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p));
} else {
backstage.area.style.left = "0px";
}
jQuery(this.showButton).hide();
jQuery(this.hideButton).show();
config.options.chkBackstage = true;
saveOption("chkBackstage");
jQuery(this.content).addClass("backstageVisible");
},
hide: function() {
if(this.currTabElem) {
this.switchTab(null);
} else {
backstage.toolbar.style.left = "0px";
if(anim && config.options.chkAnimate) {
var p = [{style: "left", start: 0, end: findWindowWidth(), template: "%0px"}];
var c = function(element,properties) {backstage.area.style.display = "none";};
anim.startAnimating(new Morpher(backstage.toolbar,config.animDuration,p,c));
} else {
this.area.style.display = "none";
}
this.showButton.style.display = "block";
this.hideButton.style.display = "none";
config.options.chkBackstage = false;
saveOption("chkBackstage");
jQuery(this.content).removeClass("backstageVisible");
}
},
onClickCommand: function(e) {
var task = config.tasks[this.getAttribute("task")];
if(task.action) {
backstage.switchTab(null);
task.action();
}
return false;
},
onClickTab: function(e) {
backstage.switchTab(this.getAttribute("task"));
return false;
},
// Switch to a given tab, or none if null is passed
switchTab: function(tabName) {
var tabElem = null;
var e = this.toolbar.firstChild;
while(e) {
if(e.getAttribute && e.getAttribute("task") == tabName)
tabElem = e;
e = e.nextSibling;
}
if(tabName == backstage.currTabName) {
backstage.hidePanel();
return;
}
if(backstage.currTabElem) {
jQuery(this.currTabElem).removeClass("backstageSelTab");
}
if(tabElem && tabName) {
backstage.preparePanel();
jQuery(tabElem).addClass("backstageSelTab");
var task = config.tasks[tabName];
wikify(task.content,backstage.panelBody,null,null);
backstage.showPanel();
} else if(backstage.currTabElem) {
backstage.hidePanel();
}
backstage.currTabName = tabName;
backstage.currTabElem = tabElem;
},
isPanelVisible: function() {
return backstage.panel ? backstage.panel.style.display == "block" : false;
},
preparePanel: function() {
backstage.cloak.style.height = findDocHeight() + "px";
backstage.cloak.style.display = "block";
jQuery(backstage.panelBody).empty();
return backstage.panelBody;
},
showPanel: function() {
backstage.panel.style.display = "block";
if(anim && config.options.chkAnimate) {
backstage.panel.style.top = (-backstage.panel.offsetHeight) + "px";
var p = [{style: "top", start: -backstage.panel.offsetHeight, end: 0, template: "%0px"}];
anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p),new Scroller(backstage.panel,false));
} else {
backstage.panel.style.top = "0px";
}
return backstage.panelBody;
},
hidePanel: function() {
if(backstage.currTabElem)
jQuery(backstage.currTabElem).removeClass("backstageSelTab");
backstage.currTabElem = null;
backstage.currTabName = null;
if(anim && config.options.chkAnimate) {
var p = [
{style: "top", start: 0, end: -(backstage.panel.offsetHeight), template: "%0px"},
{style: "display", atEnd: "none"}
];
var c = function(element,properties) {backstage.cloak.style.display = "none";};
anim.startAnimating(new Morpher(backstage.panel,config.animDuration,p,c));
} else {
jQuery([backstage.panel,backstage.cloak]).hide();
}
}
};
config.macros.backstage = {};
config.macros.backstage.handler = function(place,macroName,params)
{
var backstageTask = config.tasks[params[0]];
if(backstageTask)
createTiddlyButton(place,backstageTask.text,backstageTask.tooltip,function(e) {backstage.switchTab(params[0]); return false;});
};
//--
//-- Main
//--
var params = null; // Command line parameters
var store = null; // TiddlyWiki storage
var story = null; // Main story
var formatter = null; // Default formatters for the wikifier
var anim = typeof Animator == "function" ? new Animator() : null; // Animation engine
var readOnly = false; // Whether we're in readonly mode
var highlightHack = null; // Embarrassing hack department...
var hadConfirmExit = false; // Don't warn more than once
var safeMode = false; // Disable all plugins and cookies
var showBackstage; // Whether to include the backstage area
var installedPlugins = []; // Information filled in when plugins are executed
var startingUp = false; // Whether we're in the process of starting up
var pluginInfo,tiddler; // Used to pass information to plugins in loadPlugins()
jQuery(document).ready(function() {
jQuery("#contentWrapper").addClass("loading").text("Loading Space...");
var total_tiddlers = -1;
ajaxReq({ dataType: "text", url: "/tiddlers", success: function(text) {
total_tiddlers = text.split("\n").length;
}
});
ajaxReq({ dataType: "json", url: "/status", success: function(status) {
var host = window.location.protocol + "//" + window.location.hostname;
var workspace = "recipes/" + status["space"]["recipe"];
config.defaultCustomFields = {
"server.workspace": workspace,
"server.type": "tiddlyweb",
"server.host": host
};
var defaults = config.defaultCustomFields;
var filter = "?select=tag:excludeLists&type:!text/css&select=type:!text/html&select=type:!image/png&select=type:!image/jpg&select=type:!image/gif&select=type:!image/jpeg";
var time = 0, start = 0, total = 50;
var lazy_load_content = function(title) {
window.setTimeout(function() {
if(time < 0) {
return;
}
ajaxReq({
dataType: "json",
data: {
"fat": "1"
},
url: host + "/tiddlers?sort=-modified&limit=" + start + "," + total,
success: function(tids) {
for(var i = 0; i < tids.length; i++) {
var tid = config.adaptors.tiddlyweb.toTiddler(tids[i], host);
store.addTiddler(tid);
store.notify(tid.title);
story.refreshTiddler(tid.title, null, true);
}
if(tids.length === 0) {
time = -1;
} else {
if(total_tiddlers > -1) {
var pc = 100 - Math.floor(((total_tiddlers - store.getTiddlers().length) / total_tiddlers) * 100);
displayMessage(pc + "% of document loaded.");
}
start += total;
time += 100;
lazy_load_content(title);
}
},
});
}, time);
}
var success = function(json) {
store = new TiddlyWiki({config:config});
invokeParamifier(params,"oninit");
story = new Story("tiddlerDisplay","tiddler");
jQuery("#contentWrapper").removeClass("loading");
for(var i = 0; i < json.length; i++) {
var title = json[i].title;
console.log('loading in success', title);
if(["ServerSideSavingPlugin",
"TiddlySpaceInit", "TiddlyWebAdaptor", "LoadMissingTiddlersPlugin"].indexOf(title) === -1) {
var tid = config.adaptors.tiddlyweb.toTiddler(json[i], host);
store.addTiddler(tid);
}
}
//lazy_load_content();
main();
};
ajaxReq({
dataType: "json",
data: {
"fat": "1"
},
url: host + "/" + workspace + "/tiddlers" + filter,
success: function(tiddlers) {
success(tiddlers);
},
error: function() {
jQuery("#contentWrapper").addClass("error").text("Error occurred loading your tiddlers");
}
});
}});
});
// Starting up
function main()
{
console.log('reached tiddlywiki main');
var t10,t9,t8,t7,t6,t5,t4,t3,t2,t1,t0 = new Date();
startingUp = true;
var doc = jQuery(document);
jQuery.noConflict();
window.onbeforeunload = function(e) {if(window.confirmExit) return confirmExit();};
params = getParameters();
if(params)
params = params.parseParams("open",null,false);
addEvent(document,"click",Popup.onDocumentClick);
var s;
for(s=0; s<config.notifyTiddlers.length; s++)
store.addNotification(config.notifyTiddlers[s].name,config.notifyTiddlers[s].notify);
t1 = new Date();
console.log('loading shadows');
loadShadowTiddlers();
doc.trigger("loadShadows");
t2 = new Date();
console.log('loading from div');
store.loadFromDiv("storeArea","store",true);
console.log('loaded from div');
console.log('store.getTiddlers()', store.getTiddlers());
doc.trigger("loadTiddlers");
loadOptions();
t3 = new Date();
invokeParamifier(params,"onload");
t4 = new Date();
readOnly = (window.location.protocol == "file:") ? false : config.options.chkHttpReadOnly;
console.log('loading plugins');
var pluginProblem = loadPlugins("systemConfig");
console.log('loaded plugins');
doc.trigger("loadPlugins");
t5 = new Date();
formatter = new Formatter(config.formatters);
invokeParamifier(params,"onconfig");
story.switchTheme(config.options.txtTheme);
showBackstage = showBackstage !== undefined ? showBackstage : !readOnly;
t6 = new Date();
var m;
for(m in config.macros) {
if(config.macros[m].init)
config.macros[m].init();
}
t7 = new Date();
store.notifyAll();
t8 = new Date();
restart();
console.log('restarted, time to refresh');
refreshDisplay();
console.log('refreshed');
t9 = new Date();
if(pluginProblem) {
story.displayTiddler(null,"PluginManager");
displayMessage(config.messages.customConfigError);
}
if(showBackstage)
backstage.init();
t10 = new Date();
if(config.options.chkDisplayInstrumentation) {
displayMessage("LoadShadows " + (t2-t1) + " ms");
displayMessage("LoadFromDiv " + (t3-t2) + " ms");
displayMessage("LoadPlugins " + (t5-t4) + " ms");
displayMessage("Macro init " + (t7-t6) + " ms");
displayMessage("Notify " + (t8-t7) + " ms");
displayMessage("Restart " + (t9-t8) + " ms");
displayMessage("Total: " + (t10-t0) + " ms");
}
startingUp = false;
doc.trigger("startup");
}
// Called on unload. All functions called conditionally since they themselves may have been unloaded.
function unload()
{
if(window.checkUnsavedChanges)
checkUnsavedChanges();
if(window.scrubNodes)
scrubNodes(document.body);
}
// Restarting
function restart()
{
invokeParamifier(params,"onstart");
if(story.isEmpty()) {
story.displayDefaultTiddlers();
}
window.scrollTo(0,0);
}
function loadShadowTiddlers()
{
var shadows = new TiddlyWiki();
shadows.loadFromDiv("shadowArea","shadows",true);
shadows.forEachTiddler(function(title,tiddler){config.shadowTiddlers[title] = tiddler.text;});
}
function loadPlugins(tag)
{
if(safeMode)
return false;
var tiddlers = store.getTaggedTiddlers(tag);
tiddlers.sort(function(a,b) {return a.title < b.title ? -1 : (a.title == b.title ? 0 : 1);});
var toLoad = [];
var nLoaded = 0;
var map = {};
var nPlugins = tiddlers.length;
installedPlugins = [];
var i;
for(i=0; i<nPlugins; i++) {
var p = getPluginInfo(tiddlers[i]);
installedPlugins[i] = p;
var n = p.Name || p.title;
if(n)
map[n] = p;
n = p.Source;
if(n)
map[n] = p;
}
var visit = function(p) {
if(!p || p.done)
return;
p.done = 1;
var reqs = p.Requires;
if(reqs) {
reqs = reqs.readBracketedList();
var i;
for(i=0; i<reqs.length; i++)
visit(map[reqs[i]]);
}
toLoad.push(p);
};
for(i=0; i<nPlugins; i++)
visit(installedPlugins[i]);
for(i=0; i<toLoad.length; i++) {
p = toLoad[i];
pluginInfo = p;
tiddler = p.tiddler;
console.log('executing', tiddler.title);
if(isPluginExecutable(p)) {
if(isPluginEnabled(p)) {
p.executed = true;
var startTime = new Date();
try {
if(tiddler.text)
window.eval(tiddler.text);
nLoaded++;
} catch(ex) {
p.log.push(config.messages.pluginError.format([exceptionText(ex)]));
p.error = true;
if(!console.tiddlywiki) {
console.log("error evaluating " + tiddler.title, ex);
}
}
pluginInfo.startupTime = String((new Date()) - startTime) + "ms";
} else {
nPlugins--;
}
} else {
p.warning = true;
}
}
return nLoaded != nPlugins;
}
function getPluginInfo(tiddler)
{
var p = store.getTiddlerSlices(tiddler.title,["Name","Description","Version","Requires","CoreVersion","Date","Source","Author","License","Browsers"]);
p.tiddler = tiddler;
p.title = tiddler.title;
p.log = [];
return p;
}
// Check that a particular plugin is valid for execution
function isPluginExecutable(plugin)
{
if(plugin.tiddler.isTagged("systemConfigForce")) {
plugin.log.push(config.messages.pluginForced);
return true;
}
if(plugin["CoreVersion"]) {
var coreVersion = plugin["CoreVersion"].split(".");
var w = parseInt(coreVersion[0],10) - version.major;
if(w == 0 && coreVersion[1])
w = parseInt(coreVersion[1],10) - version.minor;
if(w == 0 && coreVersion[2])
w = parseInt(coreVersion[2],10) - version.revision;
if(w > 0) {
plugin.log.push(config.messages.pluginVersionError);
return false;
}
}
return true;
}
function isPluginEnabled(plugin)
{
if(plugin.tiddler.isTagged("systemConfigDisable")) {
plugin.log.push(config.messages.pluginDisabled);
return false;
}
return true;
}