Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
@ Commands are system commands used to configure the game or your character. They are heavily restricted and controlled.
[[Account Commands]] are available to all players, but the rest are restricted to particular users or statuses.
Syntax:
*@password <old password>=<new password>
This changes your password.
Turns a player into a small nasty toad.
This essentially kills the player's character. They may no longer login to the system with this character. Any objects they are carrying return to their respective homes. Any Location, Objects or Actions that they have created become the property of the Wizard who turned them into a toad.
This really should be used as a last resort for obnoxious players. However sometimes there is a need to apply justice.
Syntax:
*@wall <message>
This sends a message to all users currently connected. This is used by the Game Wizards for global notifications. Unlike [[Chat]] there is no way to ignore this message.
Syntax:
*Accept
*Accept from <character>
Used on it's own, it acknowledges a [[Give]] transaction from another player. The items offered are then transferred to the recipient.
When used with the keyword "from" it can be used to set permissions. The named character is allowed to [[Give]] the player items. To revoke this permission, use the [[Reject]] command.
Given items which would put the recipient overweight are not automatically accepted even if the sender has permission to transfer items. In this case the recipient must issue an accept command.
An Account allows you to play various Alternate characters. You register your email address and all characters on that account may then access the same resources, homes and workshops. A verification step takes place during this process to prevent abuse.
These commands control your account information.
All players start at the [[Adventurers Guild]]. Until you can find yourself a home within the game, any [[Home Potion]] will return you here.
The Adventurer's Guild is where all new players start the game. Before you may leave to go adventuring you must have created your basic character.
Syntax:
*aim <target>
Aims a readied missile weapon at <target>. For each turn spent aiming after the first, your chance to hit increases by 1, up to a maximum of +5. Performing any offensive action other than fire (including aiming at a different target) erases any accrued bonuses. You (and no one else) will receive a notification of bonuses accrued, once per turn. If you fire without aiming, the to-hit roll is made at -3.
The need to aim is a moderately significant disadvantage for ranged weapons. However, they also have offsetting advantages. Aiming increases your chance to hit, so ranged weapons may be the most effective form of attack against well-defended opponents. And, many opponents will be using [[Parry]] as their defence mode; parrying is ineffective against many ranged weapons. Finally, a player who is being [[guarded|Guard]] by another cannot be attacked with melee weapons until something (bad) happens to the guarding player: ranged weapons may be the only feasible method of neutralizing, say, a potent magic user who is giving you and your allies great trouble.
An Alchemist can make potions from raw ingredients. Obviously you will need a workshop to be able to brew up a potion as well as the necessary ingredients. You will also need to know the relevant recipe of how to make a potion.
As with any [[Artisan Skill|Artisan Skills]] the higher skill level you gain the better chance of successfully making a potion will be. Some recipes are easier than others. But then again, the more you practice making a particular potion, the easier it becomes.
Your Alignment is a measure of how Good or Evil you are. Everybody starts at a neutral level, and your actions will determine which way you swing.
The use of your Alignment can be useful when trying to negotiate with NPCs and such.
You are good at balancing your weapons. You get a bonus when fighting with two weapons.
Armour protects you from taking damage. Although armour is placed on individual parts of the body, the amount of protection it delivers is only applicable to those actual areas. Hits to other areas of the body are not protected.
There are 4 areas for armour. Legs, Torso, Arms and Head.
An Armourer can craft [[Armour]].
Artisan Skills allow you to make items within the game. Each skill has it's own set of levels and rules. But all of them require a workshop to allow you to practice your craft. You will probably also need particular tools and equipment to make your items.
Artisans use the [[Craft]] command to make items. You will need to know the recipes of the item you wish to make first. Many locations where you learn the skills also list a book, scroll or teacher where you can learn some of the recipes. But of course you could also try experimenting to see what you can craft.
Syntax:
*assess <player name> <skill or spell>
If you are a Master or Grand Master of a Skill or Spell you may assess the abilities of other Players with this command. The other player may then be able to [[Practice]] their skill to prove their ability and increase their level.
A Master may only assess another Master. But a Grand Master can assess any level.
Syntax:
*attack [<target>]
*kill [<target>]
Initiates a melee attack against <target> or a previous [[targetted|Target]] character. If you have a readied weapon, damage is figured from that weapon's stats. If you do not have a readied weapon, damage is figured as bare-hand fighting or [[Brawling]].
Attack and Kill are the same thing.
/***
|''Name:''|AutoRefreshPlugin|
|''Version:''|1.0.1 (2007-01-20)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#AutoRefreshPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[AutoRefreshPlugin Documentation|http://tiddlywiki.abego-software.de/#%5B%5BAutoRefreshPlugin%20Documentation%5D%5D]]|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''~CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.5.0.9 or better; Internet Explorer 6.0|
A tiddler containing the {{{<<autoRefresh...>>}}} macro is automatically refreshed (re-painted) whenever a tiddler changes.
!Syntax
{{{
<<autoRefresh [observeTiddler: tiddler ...]>>
}}}
|{{{observeTiddler}}}|(optional) when specified the refresh will only happen when one of the tiddlers specified is changed.|
!Source Code
***/
//{{{
if (!window.abego) window.abego = {};
// autoRefresh Macro =============================================================
//
(function() {
var REFRESHER_NAME = "abego_onEveryChange";
var tiddlersToRefresh = {}; // A set holding the names of tiddlers to be refreshed
var onEveryChangeRefresher = function(e,changeList) {
var tiddlerElem = story.findContainingTiddler(e);
if (!tiddlerElem) return false;
var title = tiddlerElem.getAttribute("tiddler");
if (!title) return false;
// if "observeTiddler" are specified we only refresh if one of the given
// tiddlers has changed.
var observedTiddlers = e.getAttribute("observedTiddlers");
if (observedTiddlers) {
var a = observedTiddlers.readBracketedList();
if (!changeList || !a.containsAny(changeList))
return;
}
// Refresh the tiddler asynchronously.
// This way we can avoid repeated refreshes (e.g. when a tiddler is renamed)
tiddlersToRefresh[title] = true;
setTimeout(function() {
// Refresh all tiddlers in tiddlersToRefresh
for(var title in tiddlersToRefresh)
story.refreshTiddler(title,null,true);
// We have refreshed all pending tiddlers. Clear the set.
tiddlersToRefresh = {};
}, 0);
return true;
}
config.refreshers[REFRESHER_NAME] = onEveryChangeRefresher;
config.macros.autoRefresh = {};
config.macros.autoRefresh.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
params = paramString.parseParams("observeTiddler",null,true,false,true); // allowEval, cascadeDefaults, names allowed
var e = createTiddlyElement(place,"span");
e.setAttribute("refresh",REFRESHER_NAME);
var observedTiddlers = params[0]["observeTiddler"];
if (observedTiddlers && observedTiddlers.length) {
var s = "[["+observedTiddlers.join("]] [[")+"]]";
e.setAttribute("observedTiddlers",s);
}
};
})();
//}}}
You are trained in the use of weapons from the Axes & Maces group, having been instructed in effective manoeuvrers and practised enough to execute them reliably. Additional levels increase your chances to use the weapons offensively and defensively. Impromptu weapons used as a club should be considered part of the Axes & Maces group.
Axes, Maces and Mauls are considered to be under the same class of weapons.
You may open a Bank Account here to keep your monies safe. Any money Deposited in the Bank can earn interest. So it may be worthwhile investing if you have sufficient funds. You may also rent a Deposit Box to store equipment. Fees for this are based on how much you are storing. If you go into debt then you are unable to withdraw any items until the debt is paid in full, so have a care with what you store.
Syntax:
*block
Block sets your defence mode to Block: you attempt to avoid attacks by warding them off with your shield. You must have an equipped shield in order to block.
You are trained in the use of weapons from the Bows group, having been instructed in effective techniques and practiced enough to execute them reliably. Familiarty with the skill lets you use weapons from the group at the base chance. Additional levels increase your chances to hit.
Bowyer allows you to craft [[Bows]] and [[Crossbows]], as well as Arrows and Bolts.
You are braver than most in the face of danger - You gain a bonus chance when in combat and a resistance to fear.
You have developed your ability to fight unarmed. Most likely, the training was informal, though you can choose to define the skill as having been learned from a teacher or school. The Brawling skill essentially increases the amount of damage you do in unarmed combat.
Syntax:
*brew [<n>] <potion>
Allows you to make a known potion from your stock of ingredients. You can specify a number of potions to make or if left blank only a single potion. Success is not guaranteed though. The higher your skill level the more likely it is to succeed, the more complicated the potion, the less likely. Don't try brewing an extremely complicated potion when you're only a Novice.
All of this work can only be carried out on an official Alchemists Workbench.
Brew is an [[Alchemist's|Alchemist]] alias for [[Craft]].
"With great power, comes great responsibility"
A Builder is somebody who can add-to and modify the actual game. As you can imagine this can have a huge effect on balance, game play and in fact everything. As a Builder you have the ability to access part of the database, depending on your level of access you can see some or all of the database.
To be a Builder you need to be familar with TinyMUCK development practices.
You can make the game bigger and better by building more features into the game, from inside the game.
This is extremely difficult and could upset the balance of the game without great care. Because of this, Building is strictly limited.
For a full list of Building Commands and instructions Please see the TinyMUCK Development documentation.
Syntax:
*buy <object>
*buy <object> from <player>
This allows you to pay for items within the game. You can either buy directly from a shop or from a player who has set up a [[Merchant]] position.
See also [[Sell]] and [[List]].
A carpenter makes things out of wood. For example axe handles, boxes, chests, furniture and work benches.
Syntax:
*cast <spell>
*cast <spell> at|on|vs <object>
*cast <spell> at|on|vs <character>
*cast <spell> at|on|vs <character>'s <object>
Casts a spell. You can only cast a spell that you have learned. You must have the required components and [[mana|Mana]] available to be able to cast it. Every spell that you cast can lose you [[Fatigue]] points. Additional experience and levels in the spell can reduce this.
When targetting another character, or other character's equipment, they must be present in the same location.
The Central Market is right outside the [[Adventurers Guild]]. Here is one of the few main locations where you can find players who are not playing, but have set themselves up as shops to [[Buy]] and [[Sell]] goods using a [[Merchant Bag]].
Character Points are used to increase your [[Stats|Statistics]], [[Skills]] and [[Spells]].
Your initial starting Character Points are determined by your rolled stats. They are your maximum possible points, minus your rolled stats. These points can then be used to learn new Skills, Spells, or to improve your existing ones.
!!!Increasing Stats
During Character Creation, you may increase your stats for 2 [[Character Points]], or for 3 [[Character Points]] if this would take you above the normal racial maximum starting values. Once your Character has been established, then to increase a base statistic above initial starting levels will require significantly more, it will take 3 points to increase a statistic or 3+(double the number of points above max) to increase it above racial maximum. E.g. If your max is 18 then to raise to 19 would cost 5 points, to raise it further to 20 would cost 7 points, another point to 21, 9 points etc.
Once you enter the game fully, Stats may only be increased whilst in the [[Adventurers Guild]].
!!!Increasing Skills and Spells
The cost to increase a [[skill|Skills]] or [[spell|Spells]] is determined by this table:
| !Level | !Cost | !Description |
| 1 | 1 | Novice |
| 2 | 3 | Apprentice |
| 3 | 6 | Craftsman |
| 4 | 10 | Journeyman |
| 5 | 15 | Master |
| 6 | 21 | Grand Master |
| - | +n | (Total of other Skill Levels)/[[Intelligence]] |
It is essentially, the cost of the level, plus the cost of all preceding levels. There is also extra to pay for the total number of Other skills that you have learned.
Some [[Racial|Race]] types get bonuses for learning certain skills.
!!!Increasing Skills and Stats after gameplay starts.
It is possible to start playing without any skills, because you can earn [[Experience Points|Experience]] during play, which may be traded in for [[Character Points]] to improve your character later.
General Skills may only be increased whilst in the Adventurers Guild, Artisan skills in the workshop of the appropriate skill. Spells and Magic levels may be increased in the Magii Guild.
The character that you play has a set of attributes and skills.
!Character Generation
When you connect to the game, you are prompted to enter your character name. If this is a new character never seen before then you are automatically taken through the character creation process. Once your character has been made, you can then enter a tutorial section, or if you are already familiar with how ~MUDs work then you can proceed straight to the game.
!!Name
The first choice that you face for your character is a name. Choose one single name that is fitting for your character and not derogatory. Only letters are valid, so no using numbers, weird apostrophes or spaces. The managment reserve the right to ban insulting names.
!!Race
The next choice that a player has for their character is [[Race]]. Players may choose to be:
<<list filter [tag[Race]]>>The [[race|Race]] chosen will automatically determine the overall [[Size]] of the character. This in turn has an effect on what equipment and weapons they can use. Obviously Small Armour cannot be worn by Ogres.
The race also influences the starting [[Primary Stats]] that they have and any racial bonuses.
<<forEachTiddler
where
'tiddler.tags.contains("Race")'
sortBy
'tiddler.title'
write
'"\n|[["+tiddler.title+
"]] | " + store.getTiddlerSlice(tiddler.title,"Size") +
" | " + store.getTiddlerSlice(tiddler.title,"Strength") +
" | " + store.getTiddlerSlice(tiddler.title,"Dexterity") +
" | " + store.getTiddlerSlice(tiddler.title,"Constitution") +
" | " + store.getTiddlerSlice(tiddler.title,"Intelligence") +
" |" + store.getTiddlerSlice(tiddler.title,"Special") +
" |"'
begin
'"|![[Race]] | ![[Size]] | ![[Strength]] | ![[Dexterity]] | ![[Constitution]] | ![[Intelligence]] | !Special Ability |"'
>>
!!Sex
You are then prompted to select your Sex. You can choose:
*Female
*Male
*Neuter
It is typical that if your name ends in a vowel (a, e, i, o, or u) then your character is female. If it ends with a consonant then you are male. But this is just a guideline. There is no real distinction in terms of stats or skills between the sexes, but it may have an effect when negotiating with some NPCs.
!!Character Points
Once you have determined your basic stats, you are then allocated a number of [[Character Points]]. These are then used to spend on either improving your stats or buying [[Skills]]. However there is no hurry as you can acquire [[Character Points]] as you play, by expending [[Experience Points|Experience]].
At the start, the number of points available to you is your maximum stats value minus your starting stats. For example a Human has 3D6 in all 4 stats, which is 18*4=72. If you start with stats of 14 for all stats (14*4=56) you will have 16 points available to use.
<<tiddler [[Character Points##Increasing Stats]]>>
!!Age
The age that you choose modifies your stats and skills.
*Young - You have the power of youth. You may only choose up to 4 points of skills. Some of these may have been automatically added due to your [[Race]] or [[Traits]] that you may have already chosen. In addition, a point will be added to both [[Strength]] and [[Dexterity]].
*Middle-aged - You are now in the prime of your life.
*Old - Your youth is now just a faded memory. A point will be automatically deducted from [[Strength]], [[Dexterity]] and [[Constitution]]. However, you have learned a lot in life, 1 point will be added to your [[Intelligence]] and you will get a small bonus when earning [[Experience Points|Experience]].
These extra points earned do not count towards the calculation of [[CPs|Character Points]], they could also put you a point above racial maximum without penalty.
!!Traits
You can add optional personality [[Traits]] to your character. The first Trait you add is for free. After that it will cost a [[CP|Character Points]] per trait already taken. 1 CP for the next trait, 2 CP for the next, 3 CP after that etc. Traits are random, a few hinder your character is some way, but most enhance it.
!!Skills
Finally any remaining Character Points may be spent in learning [[Skills]].
!!Class
Unlike many ~MUDs that have typical Dungeon and Dragon based classes (Fighter, Wizard, Rogue etc.), TinyFantasy is a classless game. Any character may learn any skill. However be aware though that as you start specialising in some skills, it makes it harder to level up other skills. Not impossible, just it will take more Character Points to level up.
!!Levels
Again, like other ~MUDs, there is no concept of Level (other than your skill levels). So does this mean that you can start the game obtain a whole load of powerful equipment and start kicking ass? Well sort of. All of your [[Equipment]] has a number of characteristics, extra points for your [[Skills]] and [[Statistics]]. But all of the points that you wear affects how much [[Experience]] that you earn. So the higher "level" equipment you have, the less XP you will gain. And of course, there is no way you can boost up your skills unless you play the game, so it all balances out in the end.
!!Alternates and Accounts
If you wish, you may register an [[Account]] at the [[Bank]]. This will allow all characters on your account access to the same monies and equipment, and it will also allow your same account characters to enter any homes or workshops that you own. You are NOT allowed to play two characters on the same account at the same time. If you attempt to log in multiple account characters at the same time both will be disconnected from the game. There is also a restriction on accounts being accessed from the same IP addresses at the same time. If you are playing from a locations which could have multiple players (such as a University or Business) then please contact the administrators with a [[Gripe]] message to discuss reasons.
Syntax:
*chat <message>
*chat [on|off]
Chat is the global channel within the game to send messages to everybody connected. It can be somewhat distracting, so you can turn it on and off should you need so.
All also: [[Say]], [[Pose]] and [[Whisper]].
You are able to take raw ingredients and conjure them into a taste sensation. Or bake a wholesome loaf of bread to stave off hunger.
Syntax:
*close <object>
Allows you to close a door or an openable [[Containers]]. Some doors and containers cannot be closed.
See also [[Lock]], [[Unlock]] and [[Open]].
/***
|Name:|CloseOnCancelPlugin|
|Description:|Closes the tiddler if you click new tiddler then cancel. Default behaviour is to leave it open|
|Version:|3.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#CloseOnCancelPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
merge(config.commands.cancelTiddler,{
handler_mptw_orig_closeUnsaved: config.commands.cancelTiddler.handler,
handler: function(event,src,title) {
this.handler_mptw_orig_closeUnsaved(event,src,title);
if (!story.isDirty(title) && !store.tiddlerExists(title) && !store.isShadowTiddler(title))
story.closeTiddler(title,true);
return false;
}
});
//}}}
Everybody needs clothes. Well sort of. Some clothes don't offer you protection, but they can keep you warm. A nice warm scarf around your neck to keep out the cold etc. Might be useful if you travel into cold lands.
A measurement of both weight and size. All items in the game have a weight given in Cobs to represent how unwieldy it is.
Name: MptwBlue
Background: #fff
Foreground: #000
PrimaryPale: #dcf
PrimaryLight: #66c
PrimaryMid: #214
PrimaryDark: #102
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
Combat is obviously one of the most difficult mechanics of the game to understand. As with any game that involves multiple players the way the system works has to be fair and unbiased. Though if you have the skills and equipment to fight then you stand a much better chance of winning. So if you want to take on a fully battle ready warrior with your trusty Faerie armed only with a butter knife then expect to have a really tough fight on your hands.
There are safe areas within the game patrolled by the [[Watch]]. In these areas combat is seriously frowned upon and instigating a fight will call in the [[Watch]] to deal with the aggressor. Outside of the patrolled areas, well you literally take your life in your hands. Make sure you can deal with whatever you can find.
A fight is initiated when one character hits another. This may not necessarily be started by a player, but it could be an aggressive NPC.
!!Mechanics
When a fight sequence is initiated an automated program takes over. Fights go in a timed sequence with an average wait between actions based on your [[initiative|Initiative]]. This value is modified based on other factors such as:
*[[Dexterity]] - A high dexterity can decrease the fight cycle, whereas a low dexterity increases it.
*[[Fatigue]] - The more tired you become the slower you get
*[[Equipment]] - Large heavy equipment being carried can obviously slow you down
*[[Magic]] - Spells can be cast offensively or defensively to increase or decrease the targets speed. Magic items can also add to your base stats and modify your initiative directly.
*[[Damage]] - The more wounded you are, the harder it is to fight.
*Number of attackers. Having more people attacking you tends to focus the mind. As such your initiative improves if more people have you as their target.
Skills such as Fencing or Martial Arts can also aid your initiative if you are using the appropriate weapons.
!!Weapons
[[Weapons]] are used to inflict damage on opponents. All weapons have a minimum strength to use (for blunt and bladed weapons anyway) and having a particular skill in the category of that weapon increase your chance of hitting.
!!Unarmed Combat
If you do not have any weapon available then you will be using your fists. Damage for unarmed combat is based on your strength. The Skills [[Brawling]] and [[Martial Arts]] will greatly influence your ability to hit and cause damage.
!!Armour
[[Armour]] is used to prevent you from taking damage. Obviously the better armour you have the less damage you will take. Most armour is made by size and will only fit players of that size. A good Armourer should be able to reasonably modify armour to make it fit somebody smaller or larger. For a fee of course.
!!Offensive Modes and Commands
Offensive
*[[Attack]] [<target>]
*[[Target]] <target>
*[[Wait]]
Melee Weapon Commands
*[[Disarm]] [<target>]
*[[Feint]] [<target>]
Ranged Weapon Commands
*[[Aim]] [<target>]
*[[Fire]] [<target>]
*[[Throw]] [<target>]
!!Defensive Modes and commands
*[[Dodge]]
*[[Block]] (requires a shield)
*[[Parry]] (requires a weapon)
*[[Guard]] <target>
*[[Unguard]]
*[[No Defence]]
*[[Wimp]] <value>
*[[Flee]] [<direction>]
These skills are used exclusively for fighting.
*combine [<n>] <resource>, [<n>] <resource>,...
You can try combining various ingredients to see if you can make a viable potion. Beware though, if it backfires you could be hurt. Sometimes seriously so.
When combining all of the ingredients simply list them out in full separated by commas. Preface the resource with a number to indicate quantities. The more ingredients you have or the better quality ingredients, then usually the better the overall potion will turn out. Once you have found the end result, then in future you may just [[Brew]] that potion.
Combine is an [[Alchemist's|Alchemist]] alias for [[Craft]].
Commands are typed in the console to enable your character to perform actions. There are common commands like [[Look]] and [[Get]] as well as some more esoteric ones that occur in specific situations. Other commands are more [[social|Social Commands]] allowing you to chat together or venture forth as a [[group|Group Commands]] to explore the lands.
All non-[[@|@ Commands]] can yield a help description for that particular command my using:
* <command> help
Some commands are [[Account Commands]] used to maintain your account and [[Building Commands]] used within the game to allow you to build new locations, objects and commands. These are reserved for advanced [[World Builders|Building]].
Commands are not case sensitive. So "Look" and "look" perform the same action.
Below is a list of the main Commands.
You are intuitive when it comes to people. You gain a bonus when learning the [[Healer]] skill and an additional amount when you attempt to [[Heal]] people.
PageTemplate
|>|>|SiteTitle - SiteSubtitle|
|MainMenu|DefaultTiddlers<br><br><br><br>ViewTemplate<br><br>EditTemplate|SideBarOptions|
|~|~|OptionsPanel|
|~|~|AdvancedOptions|
|~|~|<<tiddler Configuration.SideBarTabs>>|
''StyleSheet:'' StyleSheetColors - StyleSheetLayout - StyleSheetPrint
InterfaceOptions
SiteUrl
Extensive StartupParameters to control the startup beahviour of ~TiddlyWiki through specially crafted ~URLs
Syntax:
* consider <target>
Allows you to evaluate whether it is worth risking combat against the target.
This determines how resistant to damage the character is. Either damage taken from combat or from poisons. It also determines your overall level of fitness and gives your [[Fatigue]] levels.
Normally you would be carrying all your items separately. However carrying them inside another item makes things easier to handle. As such there is a bonus applied to keeping your items inside other things. This is where a Container comes in handy. Containers may be any size, from small bags and pouches, to small or large chests, or even horse carts.
Each Container object has a maximum size of object that you can put in it as well as a weight limit. However items that are inside the bag will "weigh" 25% less than they do outside to represent the ease of carrying.
For example a small Gem bag would have a maximum object size of 5 and maximum weight limit of 20. Given the 25% weight bonus you could keep 25 gems of 1 Cob size inside it yet only effectively be carrying 20 Cobs of weight. But you couldn't put a 10 Cob knife in the pouch.
Containers can be put inside containers. So you can have a pouch, inside a bag, inside a rucksack, inside a cart. Obviously you cannot see the contents inside the inner containers.
You are a coward, adept at escaping danger - You gain an increased chance when attempting to [[Flee]] and you can set a [[Wimp]] value of 3/4 your max [[Hit Points]].
Syntax:
*craft [<item>]
*craft [<n>] <resource>, [<n>] <resource>,...
Allows you to use your [[Artisan Skill|Artisan Skills]] to make an item. You must know the recipe for the item you wish to make and have the requisite resources in your inventory, or immediately to hand.
Or you could also trying experimenting with various reousrces to see if you can make anything useful. Simply list all the resources you wish to use, separated by commas. Preface the resource with a number if you wish to use multiples. A word of warning about experimenting though, some of the more dangerous skills such as [[Alchemist]] or [[Sorcerer]] can be very dangerous if you try experimenting too much. Sometimes things go boom.
Crafting can only occur where there is a craft bench or forge for the particular skill. Except [[Chef]], they can pretty much cook anywhere providing they have a pot, pan or skillet and a fire.
You are trained in the use of weapons from the Crossbows group, having been instructed in effective techniques and practised enough to execute them reliably. Familiarity with the skill lets you use weapons from the group at the base chance. Additional levels increase your chances to hit.
Syntax:
* [[cast|Cast]] clw
* [[cast|Cast]] clw at|on <target>
When cast this spell will cure 1D4 plus your Spell level [[Hit Points]]. If the <target> is omitted then the spell is cast against yourself.
Base Mana Cost: 5
/***
|''Name:''|DataTiddlerPlugin|
|''Version:''|1.0.7 (2012-04-19)|
|''Summary:''|Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).|
|''Source:''|http://tiddlywiki.abego-software.de/#DataTiddlerPlugin|
|''Twitter:''|[[@abego|https://twitter.com/#!/abego]]|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''License:''|[[BSD open source license|http://www.abego-software.de/legal/apl-v10.html]]|
!Description
Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).
Such tiddler data can be used in various applications. E.g. you may create tables that collect data from various tiddlers.
''//Example: "Table with all December Expenses"//''
{{{
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
}}}
//(This assumes that expenses are stored in tiddlers tagged with "expense".)//
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
For other examples see DataTiddlerExamples.
''Access and Modify Tiddler Data''
You can "attach" data to every tiddler by assigning a JavaScript value (such as a string, boolean, number, or even arrays and compound objects) to named fields.
These values can be accessed and modified through the following Tiddler methods:
|!Method|!Example|!Description|
|{{{data(field)}}}|{{{t.data("age")}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{data(field,defaultValue)}}}|{{{t.data("isVIP",false)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{data()}}}|{{{t.data()}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{setData(field,value)}}}|{{{t.setData("age",42)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{setData(field,value,defaultValue)}}}|{{{t.setData("isVIP",flag,false)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
Alternatively you may use the following functions to access and modify the data. In this case the tiddler argument is either a tiddler or the name of a tiddler.
|!Method|!Description|
|{{{DataTiddler.getData(tiddler,field)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{DataTiddler.getData(tiddler,field,defaultValue)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{DataTiddler.getDataObject(tiddler)}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{DataTiddler.setData(tiddler,field,value)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{DataTiddler.setData(tiddler,field,value,defaultValue)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
//(For details on the various functions see the detailed comments in the source code.)//
''Data Representation in a Tiddler''
The data of a tiddler is stored as plain text in the tiddler's content/text, inside a "data" section that is framed by a {{{<data>...</data>}}} block. Inside the data section the information is stored in the [[JSON format|http://www.crockford.com/JSON/index.html]].
//''Data Section Example:''//
{{{
<data>{"isVIP":true,"user":"John Brown","age":34}</data>
}}}
The data section is not displayed when viewing the tiddler (see also "The showData Macro").
Beside the data section a tiddler may have all kind of other content.
Typically you will not access the data section text directly but use the methods given above. Nevertheless you may retrieve the text of the data section's content through the {{{DataTiddler.getDataText(tiddler)}}} function.
''Saving Changes''
The "setData" methods respect the "ForceMinorUpdate" and "AutoSave" configuration values. I.e. when "ForceMinorUpdate" is true changing a value using setData will not affect the "modifier" and "modified" attributes. With "AutoSave" set to true every setData will directly save the changes after a setData.
''Notifications''
No notifications are sent when a tiddler's data value is changed through the "setData" methods.
''Escape Data Section''
In case that you want to use the text {{{<data>}}} or {{{</data>}}} in a tiddler text you must prefix the text with a tilde ('~'). Otherwise it may be wrongly considered as the data section. The tiddler text {{{~<data>}}} is displayed as {{{<data>}}}.
''The showData Macro''
By default the data of a tiddler (that is stored in the {{{<data>...</data>}}} section of the tiddler) is not displayed. If you want to display this data you may used the {{{<<showData ...>>}}} macro:
''Syntax:''
|>|{{{<<}}}''showData '' [''JSON''] [//tiddlerName//] {{{>>}}}|
|''JSON''|By default the data is rendered as a table with a "Name" and "Value" column. When defining ''JSON'' the data is rendered in JSON format|
|//tiddlerName//|Defines the tiddler holding the data to be displayed. When no tiddler is given the tiddler containing the showData macro is used. When the tiddler name contains spaces you must quote the name (or use the {{{[[...]]}}} syntax.)|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
!Source Code
***/
/***
This plugin's source code is compressed (and hidden).
Use this [[link|http://tiddlywiki.abego-software.de/archive/DataTiddlerPlugin/1.0.7/DataTiddlerPlugin-1.0.7-src.js]] to get the readable source code.
***/
///%
if(!version.extensions.DataTiddlerPlugin){version.extensions.DataTiddlerPlugin={major:1,minor:0,revision:7,date:new Date(2012,3,19),type:"plugin",source:"http://tiddlywiki.abego-software.de/#DataTiddlerPlugin"};if(!window.story){window.story=window}if(!TiddlyWiki.prototype.getTiddler){TiddlyWiki.prototype.getTiddler=function(b){var a=this.tiddlers[b];return(a!==undefined&&a instanceof Tiddler)?a:null}}function DataTiddler(){}DataTiddler={stringify:null,parse:null};window.DataTiddler=DataTiddler;DataTiddler.getData=function(c,d,a){var b=(typeof c=="string")?store.getTiddler(c):c;if(!(b instanceof Tiddler)){throw"Tiddler expected. Got "+c}return DataTiddler.getTiddlerDataValue(b,d,a)};DataTiddler.setData=function(c,e,d,a){var b=(typeof c=="string")?store.getTiddler(c):c;if(!(b instanceof Tiddler)){throw"Tiddler expected. Got "+c+"("+b+")"}DataTiddler.setTiddlerDataValue(b,e,d,a)};DataTiddler.getDataObject=function(b){var a=(typeof b=="string")?store.getTiddler(b):b;if(!(a instanceof Tiddler)){throw"Tiddler expected. Got "+b}return DataTiddler.getTiddlerDataObject(a)};DataTiddler.getDataText=function(b){var a=(typeof b=="string")?store.getTiddler(b):b;if(!(a instanceof Tiddler)){throw"Tiddler expected. Got "+b}return DataTiddler.readDataSectionText(a)};DataTiddler.extendJSONError=function(a){if(a.name=="JSONError"){a.toString=function(){return a.name+": "+a.message+" ("+a.text+")"}}return a};DataTiddler.getTiddlerDataObject=function(a){if(a.dataObject===undefined){var b=DataTiddler.readData(a);a.dataObject=(b)?b:{}}return a.dataObject};DataTiddler.getTiddlerDataValue=function(b,d,a){var c=DataTiddler.getTiddlerDataObject(b)[d];return(c===undefined)?a:c};DataTiddler.setTiddlerDataValue=function(c,f,e,a){var d=DataTiddler.getTiddlerDataObject(c);var b=d[f];if(e==a){if(b!==undefined){delete d[f];DataTiddler.save(c)}return}d[f]=e;DataTiddler.save(c)};DataTiddler.readDataSectionText=function(a){var b=DataTiddler.getDataTiddlerMatches(a);if(b===null||!b[2]){return null}return b[2]};DataTiddler.readData=function(b){var c=DataTiddler.readDataSectionText(b);try{return c?DataTiddler.parse(c):null}catch(a){throw DataTiddler.extendJSONError(a)}};DataTiddler.getDataTextOfTiddler=function(a){var b=DataTiddler.getTiddlerDataObject(a);return DataTiddler.stringify(b)};DataTiddler.indexOfNonEscapedText=function(c,a,d){var b=c.indexOf(a,d);while((b>0)&&(c[b-1]=="~")){b=c.indexOf(a,b+1)}return b};DataTiddler.getDataSectionInfo=function(e){var a="<data>{";var f="}</data>";var d=DataTiddler.indexOfNonEscapedText(e,a,0);if(d<0){return null}var c=e.indexOf(f,d);if(c<0){return null}var b;while((b=e.indexOf(f,c+1))>=0){c=b}return{prefixEnd:d,dataStart:d+(a.length)-1,dataEnd:c,suffixStart:c+(f.length)}};DataTiddler.getDataTiddlerMatches=function(a){var f=a.text;var e=DataTiddler.getDataSectionInfo(f);if(!e){return null}var c=f.substr(0,e.prefixEnd);var b=f.substr(e.dataStart,e.dataEnd-e.dataStart+1);var d=f.substr(e.suffixStart);return[f,c,b,d]};DataTiddler.save=function(a){var e=DataTiddler.getDataTiddlerMatches(a);var d;var f;if(e===null){d=a.text;f=""}else{d=e[1];f=e[3]}var b=DataTiddler.getDataTextOfTiddler(a);var c=(b!==null)?d+"<data>"+b+"</data>"+f:d+f;if(c!=a.text){a.isDataTiddlerChange=true;a.set(a.title,c,config.options.txtUserName,config.options.chkForceMinorUpdate?undefined:new Date(),a.tags);delete a.isDataTiddlerChange;store.dirty=true;if(config.options.chkAutoSave){saveChanges()}}};DataTiddler.MyTiddlerChangedFunction=function(){if(this.dataObject&&!this.isDataTiddlerChange){delete this.dataObject}DataTiddler.originalTiddlerChangedFunction.apply(this,arguments)};config.formatters.push({name:"data-escape",match:"~<\\/?data>",handler:function(a){a.outputText(a.output,a.matchStart+1,a.nextMatch)}});config.formatters.push({name:"data",match:"<data>",handler:function(a){var b=DataTiddler.getDataSectionInfo(a.source);if(b&&b.prefixEnd==a.matchStart){a.nextMatch=b.suffixStart}else{a.outputText(a.output,a.matchStart,a.nextMatch)}}});DataTiddler.originalTiddlerChangedFunction=Tiddler.prototype.changed;Tiddler.prototype.changed=DataTiddler.MyTiddlerChangedFunction;Tiddler.prototype.data=function(b,a){return(b)?DataTiddler.getTiddlerDataValue(this,b,a):DataTiddler.getTiddlerDataObject(this)};Tiddler.prototype.setData=function(c,b,a){DataTiddler.setTiddlerDataValue(this,c,b,a)};config.macros.showData={label:"showData",prompt:"Display the values stored in the data section of the tiddler"};config.macros.showData.handler=function(a,g,h){var c=0;var d=false;if((c<h.length)&&h[c]=="JSON"){c++;d=true}var b=story.findContainingTiddler(a).getAttribute("tiddler");if(c<h.length){b=h[c];c++}try{if(d){this.renderDataInJSONFormat(a,b)}else{this.renderDataAsTable(a,b)}}catch(f){this.createErrorElement(a,f)}};config.macros.showData.renderDataInJSONFormat=function(a,b){var c=DataTiddler.getDataText(b);if(c){createTiddlyElement(a,"pre",null,null,c)}};config.macros.showData.renderDataAsTable=function(a,b){var f="|!Name|!Value|\n";var e=DataTiddler.getDataObject(b);if(e){for(var c in e){var d=e[c];f+="|"+c+"|"+DataTiddler.stringify(d)+"|\n"}}wikify(f,a)};config.macros.showData.createErrorElement=function(a,b){var c=(b.description)?b.description:b.toString();return createTiddlyElement(a,"span",null,"showDataError","<<showData ...>>: "+c)};setStylesheet(".showDataError{color: #ffffff;background-color: #880000;}","showData")}var JSON={copyright:"(c)2005 JSON.org",license:"http://www.crockford.com/JSON/license.html",stringify:function(c){var b=[];function f(a){b[b.length]=a}function d(a){var j,h=undefined,e,g;switch(typeof a){case"object":if(a){if(a instanceof Array){f("[");e=b.length;for(h=0;h<a.length;h+=1){g=a[h];if(typeof g!="undefined"&&typeof g!="function"){if(e<b.length){f(",")}d(g)}}f("]");return}else{if(typeof a.toString!="undefined"){f("{");e=b.length;for(h in a){g=a[h];if(a.hasOwnProperty(h)&&typeof g!="undefined"&&typeof g!="function"){if(e<b.length){f(",")}d(h);f(":");d(g)}}return f("}")}}}f("null");return;case"number":f(isFinite(a)?+a:"null");return;case"string":e=a.length;f('"');for(h=0;h<e;h+=1){j=a.charAt(h);if(j>=" "){if(j=="\\"||j=='"'){f("\\")}f(j)}else{switch(j){case"\b":f("\\b");break;case"\f":f("\\f");break;case"\n":f("\\n");break;case"\r":f("\\r");break;case"\t":f("\\t");break;default:j=j.charCodeAt();f("\\u00"+Math.floor(j/16).toString(16)+(j%16).toString(16))}}}f('"');return;case"boolean":f(String(a));return;default:f("null");return}}d(c);return b.join("")},parse:function(text){var p=/^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,token=undefined,operator=undefined;function error(m,t){throw {name:"JSONError",message:m,text:t||operator||token}}function next(b){if(b&&b!=operator){error("Expected '"+b+"'")}if(text){var t=p.exec(text);if(t){if(t[2]){token=null;operator=t[2]}else{operator=null;try{token=eval(t[1])}catch(e){error("Bad token",t[1])}}text=text.substring(t[0].length)}else{error("Unrecognized token",text)}}else{token=operator=undefined}}function val(){var k,o;switch(operator){case"{":next("{");o={};if(operator!="}"){for(;;){if(operator||typeof token!="string"){error("Missing key")}k=token;next();next(":");o[k]=val();if(operator!=","){break}next(",")}}next("}");return o;case"[":next("[");o=[];if(operator!="]"){for(;;){o.push(val());if(operator!=","){break}next(",")}}next("]");return o;default:if(operator!==null){error("Missing value")}k=token;next();return k}}next();return val()}};DataTiddler.format="JSON";DataTiddler.stringify=JSON.stringify;DataTiddler.parse=JSON.parse;
//%/
Dexterity determines how nimble or agile the character is. Having a high dexterity allows you to dodge blows or lessen the impact of them. It is also used to determine how well you are able to manipulate tools or objects. Having a high dexterity is useful to pick locks or pockets.
Dexterity also determines exactly how many individual items you can carry. Items that are equipped or worn do not count to this limit, neither do items that are inside [[containers|Containers]]. So for example 2 gems inside a gem pouch, inside a rucksack only counts as one item even though you technically have 4 individual items. Or none at all if you are wearing the rucksack. But you will still have to observe the [[weight|Weight]] limits of the items you carry.
Syntax:
*disarm [<character>]
This signals your intent to try to disarm your opponent. A successful attack would cause the target to drop their weapon.
Syntax:
*dodge
*dodge [<left|right>]
Dodge sets your defence mode to Dodge: you attempt to avoid attacks by moving out of their way. Your Dodge skill is equal to your current STR + current DEX divided by 2 plus 2. You may declare Dodge at any time. Dodging is the only defense mode available if you have no shield or weapon that can parry. It may also be your best choice if you are unskilled with your weapon or shield.
You can also dodge to the left or right. If you manage to dodge to the opposite side of your opponent, then it's possible to get an additional attack in whilst they're recovering from your movement.
Syntax:
*drink [<potion>]
*drink [<object>]
You will take a sup of whatever it was you selected. If the item you selected only has 1 use, then that item is consumed. Otherwise the quantity of the item will be diminished. For example a Potion only has a single use. Whereas drinking from a Fountain is unlikely to drain it all.
Syntax:
*drop [<quantity>] <object>
This drops the named object or quantity of items onto the ground. When you drop an object that contains additional objects (such as a bag or rucksack) then everything is dropped. Other players may [[pick up|Get]] the dropped item. Use the keyword "all" to drop all items of the type specified. You cannot perform a "drop all" and drop everything.
Example:
*drop 1 groat
*drop all seashells
*drop Dagger
You can drop a [[wielded|Wield]] weapon or shield, but you cannot drop [[equipped|Equip]] armour.
If you drop a container (such as a [[Bag|Containers]]) then you will also be dropping the contents of the bag at the same time.
!!Description
[>img[./images/Dwarf.jpg]]Dwarves are a short, Muscular race, that the males and females look alike as both have beards, and in dwarfs beards mean a lot. Dwarves live deep beneath the mountains and have mined themselves vast strongholds beneath theses mountain peaks. Dwarves are immensely strong and resilient they have broad hands and feet. Dwarves are known to be stubborn and un-forgetting. Dwarves respect the following things; Age, Wealth and Skill.
Dwarves favour axes, maces and mauls as a weapon. Like the Elves, they take pride in their work and their axes show immense craftsmanship. Dwarves have a hate of Goblins and Ogres who have raided their strong holds many a time and took away their wealth. Dwarves also distrust Elves due to an ancient war that was fought between them.
!!Stats
|Size|Small |
|Strength|2D6+3|
|~|Min 5, Avg 10, Max 15 |
|Dexterity|3D6|
|~|Min 3, Avg 10, Max 18 |
|Constitution|3D6+1|
|~|Min 4, Avg 11, Max 19 |
|Intelligence|3D6|
|~|Min 3, Avg 10, Max 18 |
|Special|Bonuses when learning [[Endurance]], [[Axes & Maces]], [[Armourer]], [[Miner]] and [[Smith]] skills. |
You have sharp vision, allowing you to see further and spot hidden details. You get a bonus when [[Searching|Search]], as well as automatically learning the [[Perception]] skill.
Syntax:
*eat <food>
Eating keeps your strength up. Some foods keep you going for longer. Others are just a quick snack.
!!Description
[>img[./images/Elf.jpg]]Elves are a tall, slim and regal built race. They are seen as a noble race, and tend to be beautiful or handsome in appearance. Elves are a paled skinned race. Elves are strong and agile in comparison with humans and are often seen as more intelligent and wiser. Elves have a longer life span than humans do. Elves build refined weapons that are seen as master craftsmanship by other races. They favour using Bows over Swords.
Although they value Nature and gentle ways, they do understand that animals are useful.
!!Stats
|Size|Medium |
|Strength|2D6+3|
|~|Min 5, Avg 10, Max 15 |
|Dexterity|3D6+1|
|~|Min 4, Avg 11, Max 19 |
|Constitution|2D6+3|
|~|Min 5, Avg 10, Max 15 |
|Intelligence|3D6+1|
|~|Min 4, Avg 11, Max 19 |
|Special|Bonuses when learning [[Bows]], [[Bowyer]], [[Hunter]], [[Farmer]] and [[Lumberjack]] skills |
However there are some downsides. They value purity, so [[Half-Elves|Half-Elf]] are seen as outcasts. They tolerate [[Humans|Human]] and rather admire the [[Faerie]] for their skills, but they despise [[Dwarves|Dwarf]] and fear [[Ogres|Ogre]].
You have trained yourself to endure tiredness. Each skill level adds 3 to your total [[Fatigue]] level.
Syntax:
*equip <item>
*wield <item>
*wear <item>
This command allows you to put on an item, for example a helmet or ring. Any items must be located in your inventory. Equipped items (except for weapons and shields) cannot be [[dropped|Drop]].
Certain items can only be equipped in a particular order. For example, you cannot put on a Ring whist wearing Gloves.
The alias "wield" can also be used for Weapons, and "wear" for clothes. But Equip works for all items.
See also [[Remove]]
Items that you can carry or wear are considered Equipment. Everything has a [[weight|Weight]] which is a combination of the actual weight of an item and it's size.
The number of individual items you can carry is limited by your [[dexterity|Dexterity]], but items that are worn are not counted against this limit.
Items can be placed on:
* Head (Helmets or hats)
* Neck (Necklace or Amulets)
* Body
* Back (Cloak or Backpack)
* Arms (Vambraces or Bracelets)
* Hands (Gloves)
* Fingers (Rings)
* Waist (Belts)
* Legs (Greaves)
* Feet (Boots or Shoes)
Some belts may also have pouches attached to them. When worn like this they are considered Equipment and do not count against your carrying limit. You can also equip weapons and light sources to show that you are using them, however these do count as carried items. For example a sword in one hand and a lantern in the other.
Items that are in pouches or bags are not counted against the dexterity limit, but they do add to the weight that you are carrying.
Equipped items can also have abilities which enhance your [[Statistics]] when you wear them. So a Belt of Strength could increase your [[Strength]] by a point or three. Each piece of equipment is different. However increase by equipment also has a negative effect on how much [[Experience]] you earn as you play so you must find a balance between getting better and how fast you earn XP.
Syntax:
*Examine <object>
Allows you to look closely at an item to find out more information about it. Skills such as [[Perception]] can improve your ability to find additional information. However bear in mind, that most items are pretty standard boring items and don't have any special properties.
#displayArea {background-color: #ffccff; }
#mainMenu {border: 1px solid #ffff88; }
#commandPanel {background-color: #008800; }
As you play you earn Experience points (XP). These are either given as Experience for using particular skills, or as general rewards for achieving goals, quests or killing monsters.
Experience Points are then used to buy [[Character Points]], which in turn, are used to increase your skill level. Approximately every 1,000 points of Experience points may be traded in for 1 [[Character Point|Character Points]] which can then be used to improve your skills and abilities, however the required amount of XP needed does increase each time you trade in the points and can also be affected by your [[Intelligence]].
[[Artisan Skills]] however can earn experience for the skill directly. A successful use of your skill will reward a certain number of points to the skill, and an additional 10% to your character experience. So in effect you are earning 110% points for Artisan skills. Artisan Skills will automatically increase as you earn the points necessary for the upgrade, however you may spend your normal experience points against them to improve as well.
The experience points earned for [[Artisan Skills]] are not linear. A Master who makes items that are trivial, although they would be of good quality, would earn less experience than a Novice making the same item. Whereas a Novice trying to make complicated items would likely fail thereby yielding a minimal experience point gain, and a Master would succeed.
How much Experience you earn is very must dependent on how you play the game. It depends on your [[Intelligence]] as well as how you [[equip|Equipment]] yourself.
/***
|Name|ExportTiddlersPlugin|
|Source|http://www.TiddlyTools.com/#ExportTiddlersPlugin|
|Documentation|http://www.TiddlyTools.com/#ExportTiddlersPluginInfo|
|Version|2.9.6|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|interactively select/export tiddlers to a separate file|
!!!!!Documentation
>see [[ExportTiddlersPluginInfo]]
!!!!!Inline control panel (live):
><<exportTiddlers inline>>
!!!!!Revisions
<<<
2011.02.14 2.9.6 fix OSX error: use picker.file.path
2010.02.25 2.9.5 added merge checkbox option and improved 'merge' status message
|please see [[ExportTiddlersPluginInfo]] for additional revision details|
2005.10.09 0.0.0 development started
<<<
!!!!!Code
***/
//{{{
// version
version.extensions.ExportTiddlersPlugin= {major: 2, minor: 9, revision: 6, date: new Date(2011,2,14)};
// default shadow definition
config.shadowTiddlers.ExportTiddlers='<<exportTiddlers inline>>';
// add 'export' backstage task (following built-in import task)
if (config.tasks) { // TW2.2 or above
config.tasks.exportTask = {
text:'export',
tooltip:'Export selected tiddlers to another file',
content:'<<exportTiddlers inline>>'
}
config.backstageTasks.splice(config.backstageTasks.indexOf('importTask')+1,0,'exportTask');
}
config.macros.exportTiddlers = {
$: function(id) { return document.getElementById(id); }, // abbreviation
label: 'export tiddlers',
prompt: 'Copy selected tiddlers to an export document',
okmsg: '%0 tiddler%1 written to %2',
failmsg: 'An error occurred while creating %1',
overwriteprompt: '%0\ncontains %1 tiddler%2 that will be discarded or replaced',
mergestatus: '%0 tiddler%1 added, %2 tiddler%3 updated, %4 tiddler%5 unchanged',
statusmsg: '%0 tiddler%1 - %2 selected for export',
newdefault: 'export.html',
datetimefmt: '0MM/0DD/YYYY 0hh:0mm:0ss', // for 'filter date/time' edit fields
type_TW: "tw", type_PS: "ps", type_TX: "tx", type_CS: "cs", type_NF: "nf", // file type tokens
type_map: { // maps type param to token values
tiddlywiki:"tw", tw:"tw", wiki: "tw",
purestore: "ps", ps:"ps", store:"ps",
plaintext: "tx", tx:"tx", text: "tx",
comma: "cs", cs:"cs", csv: "cs",
newsfeed: "nf", nf:"nf", xml: "nf", rss:"nf"
},
handler: function(place,macroName,params) {
if (params[0]!='inline')
{ createTiddlyButton(place,this.label,this.prompt,this.togglePanel); return; }
var panel=this.createPanel(place);
panel.style.position='static';
panel.style.display='block';
},
createPanel: function(place) {
var panel=this.$('exportPanel');
if (panel) { panel.parentNode.removeChild(panel); }
setStylesheet(store.getTiddlerText('ExportTiddlersPlugin##css',''),'exportTiddlers');
panel=createTiddlyElement(place,'span','exportPanel',null,null)
panel.innerHTML=store.getTiddlerText('ExportTiddlersPlugin##html','');
this.initFilter();
this.refreshList(0);
var fn=this.$('exportFilename');
if (window.location.protocol=='file:' && !fn.value.length) {
// get new target path/filename
var newPath=getLocalPath(window.location.href);
var slashpos=newPath.lastIndexOf('/'); if (slashpos==-1) slashpos=newPath.lastIndexOf('\\');
if (slashpos!=-1) newPath=newPath.substr(0,slashpos+1); // trim filename
fn.value=newPath+this.newdefault;
}
return panel;
},
togglePanel: function(e) { var e=e||window.event;
var cme=config.macros.exportTiddlers; // abbrev
var parent=resolveTarget(e).parentNode;
var panel=cme.$('exportPanel');
if (panel==undefined || panel.parentNode!=parent)
panel=cme.createPanel(parent);
var isOpen=panel.style.display=='block';
if(config.options.chkAnimate)
anim.startAnimating(new Slider(panel,!isOpen,e.shiftKey || e.altKey,'none'));
else
panel.style.display=isOpen?'none':'block' ;
if (panel.style.display!='none') {
cme.refreshList(0);
cme.$('exportFilename').focus();
cme.$('exportFilename').select();
}
e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); return(false);
},
process: function(which) { // process panel control interactions
var theList=this.$('exportList'); if (!theList) return false;
var count = 0;
var total = store.getTiddlers('title').length;
switch (which.id) {
case 'exportFilter':
count=this.filterExportList();
var panel=this.$('exportFilterPanel');
if (count==-1) { panel.style.display='block'; break; }
this.$('exportStart').disabled=(count==0);
this.$('exportDelete').disabled=(count==0);
this.displayStatus(count,total);
if (count==0) { alert('No tiddlers were selected'); panel.style.display='block'; }
break;
case 'exportStart':
this.go();
break;
case 'exportDelete':
this.deleteTiddlers();
break;
case 'exportHideFilter':
case 'exportToggleFilter':
var panel=this.$('exportFilterPanel')
panel.style.display=(panel.style.display=='block')?'none':'block';
break;
case 'exportSelectChanges':
var lastmod=new Date(document.lastModified);
for (var t = 0; t < theList.options.length; t++) {
if (theList.options[t].value=='') continue;
var tiddler=store.getTiddler(theList.options[t].value); if (!tiddler) continue;
theList.options[t].selected=(tiddler.modified>lastmod);
count += (tiddler.modified>lastmod)?1:0;
}
this.$('exportStart').disabled=(count==0);
this.$('exportDelete').disabled=(count==0);
this.displayStatus(count,total);
if (count==0) alert('There are no unsaved changes');
break;
case 'exportSelectAll':
for (var t = 0; t < theList.options.length; t++) {
if (theList.options[t].value=='') continue;
theList.options[t].selected=true;
count += 1;
}
this.$('exportStart').disabled=(count==0);
this.$('exportDelete').disabled=(count==0);
this.displayStatus(count,count);
break;
case 'exportSelectOpened':
for (var t=0; t<theList.options.length; t++) theList.options[t].selected=false;
var tiddlerDisplay=this.$('tiddlerDisplay');
for (var t=0; t<tiddlerDisplay.childNodes.length;t++) {
var tiddler=tiddlerDisplay.childNodes[t].id.substr(7);
for (var i=0; i<theList.options.length; i++) {
if (theList.options[i].value!=tiddler) continue;
theList.options[i].selected=true; count++; break;
}
}
this.$('exportStart').disabled=(count==0);
this.$('exportDelete').disabled=(count==0);
this.displayStatus(count,total);
if (count==0) alert('There are no tiddlers currently opened');
break;
case 'exportSelectRelated':
// recursively build list of related tiddlers
function getRelatedTiddlers(tid,tids) {
var t=store.getTiddler(tid); if (!t || tids.contains(tid)) return tids;
tids.push(t.title);
if (!t.linksUpdated) t.changed();
for (var i=0; i<t.links.length; i++)
if (t.links[i]!=tid) tids=getRelatedTiddlers(t.links[i],tids);
return tids;
}
// for all currently selected tiddlers, gather up the related tiddlers (including self) and select them as well
var tids=[];
for (var i=0; i<theList.options.length; i++)
if (theList.options[i].selected) tids=getRelatedTiddlers(theList.options[i].value,tids);
// select related tiddlers (includes original selected tiddlers)
for (var i=0; i<theList.options.length; i++)
theList.options[i].selected=tids.contains(theList.options[i].value);
this.displayStatus(tids.length,total);
break;
case 'exportListSmaller': // decrease current listbox size
var min=5;
theList.size-=(theList.size>min)?1:0;
break;
case 'exportListLarger': // increase current listbox size
var max=(theList.options.length>25)?theList.options.length:25;
theList.size+=(theList.size<max)?1:0;
break;
case 'exportClose':
this.$('exportPanel').style.display='none';
break;
}
return false;
},
displayStatus: function(count,total) {
var txt=this.statusmsg.format([total,total!=1?'s':'',!count?'none':count==total?'all':count]);
clearMessage(); displayMessage(txt);
return txt;
},
refreshList: function(selectedIndex) {
var theList = this.$('exportList'); if (!theList) return;
// get the sort order
var sort;
if (!selectedIndex) selectedIndex=0;
if (selectedIndex==0) sort='modified';
if (selectedIndex==1) sort='title';
if (selectedIndex==2) sort='modified';
if (selectedIndex==3) sort='modifier';
if (selectedIndex==4) sort='tags';
// unselect headings and count number of tiddlers actually selected
var count=0;
for (var t=5; t < theList.options.length; t++) {
if (!theList.options[t].selected) continue;
if (theList.options[t].value!='')
count++;
else { // if heading is selected, deselect it, and then select and count all in section
theList.options[t].selected=false;
for ( t++; t<theList.options.length && theList.options[t].value!=''; t++) {
theList.options[t].selected=true;
count++;
}
}
}
// disable 'export' and 'delete' buttons if no tiddlers selected
this.$('exportStart').disabled=(count==0);
this.$('exportDelete').disabled=(count==0);
// show selection count
var tiddlers = store.getTiddlers('title');
if (theList.options.length) this.displayStatus(count,tiddlers.length);
// if a [command] item, reload list... otherwise, no further refresh needed
if (selectedIndex>4) return;
// clear current list contents
while (theList.length > 0) { theList.options[0] = null; }
// add heading and control items to list
var i=0;
var indent=String.fromCharCode(160)+String.fromCharCode(160);
theList.options[i++]=
new Option(tiddlers.length+' tiddlers in document', '',false,false);
theList.options[i++]=
new Option(((sort=='title' )?'>':indent)+' [by title]', '',false,false);
theList.options[i++]=
new Option(((sort=='modified')?'>':indent)+' [by date]', '',false,false);
theList.options[i++]=
new Option(((sort=='modifier')?'>':indent)+' [by author]', '',false,false);
theList.options[i++]=
new Option(((sort=='tags' )?'>':indent)+' [by tags]', '',false,false);
// output the tiddler list
switch(sort) {
case 'title':
for(var t = 0; t < tiddlers.length; t++)
theList.options[i++] = new Option(tiddlers[t].title,tiddlers[t].title,false,false);
break;
case 'modifier':
case 'modified':
var tiddlers = store.getTiddlers(sort);
// sort descending for newest date first
tiddlers.sort(function (a,b) {if(a[sort] == b[sort]) return(0); else return (a[sort] > b[sort]) ? -1 : +1; });
var lastSection = '';
for(var t = 0; t < tiddlers.length; t++) {
var tiddler = tiddlers[t];
var theSection = '';
if (sort=='modified') theSection=tiddler.modified.toLocaleDateString();
if (sort=='modifier') theSection=tiddler.modifier;
if (theSection != lastSection) {
theList.options[i++] = new Option(theSection,'',false,false);
lastSection = theSection;
}
theList.options[i++] = new Option(indent+indent+tiddler.title,tiddler.title,false,false);
}
break;
case 'tags':
var theTitles = {}; // all tiddler titles, hash indexed by tag value
var theTags = new Array();
for(var t=0; t<tiddlers.length; t++) {
var title=tiddlers[t].title;
var tags=tiddlers[t].tags;
if (!tags || !tags.length) {
if (theTitles['untagged']==undefined) { theTags.push('untagged'); theTitles['untagged']=new Array(); }
theTitles['untagged'].push(title);
}
else for(var s=0; s<tags.length; s++) {
if (theTitles[tags[s]]==undefined) { theTags.push(tags[s]); theTitles[tags[s]]=new Array(); }
theTitles[tags[s]].push(title);
}
}
theTags.sort();
for(var tagindex=0; tagindex<theTags.length; tagindex++) {
var theTag=theTags[tagindex];
theList.options[i++]=new Option(theTag,'',false,false);
for(var t=0; t<theTitles[theTag].length; t++)
theList.options[i++]=new Option(indent+indent+theTitles[theTag][t],theTitles[theTag][t],false,false);
}
break;
}
theList.selectedIndex=selectedIndex; // select current control item
this.$('exportStart').disabled=true;
this.$('exportDelete').disabled=true;
this.displayStatus(0,tiddlers.length);
},
askForFilename: function(here) {
var msg=here.title; // use tooltip as dialog box message
var path=getLocalPath(document.location.href);
var slashpos=path.lastIndexOf('/'); if (slashpos==-1) slashpos=path.lastIndexOf('\\');
if (slashpos!=-1) path = path.substr(0,slashpos+1); // remove filename from path, leave the trailing slash
var filetype=this.$('exportFormat').value.toLowerCase();
var defext='html';
if (filetype==this.type_TX) defext='txt';
if (filetype==this.type_CS) defext='csv';
if (filetype==this.type_NF) defext='xml';
var file=this.newdefault.replace(/html$/,defext);
var result='';
if(window.Components) { // moz
try {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var nsIFilePicker = window.Components.interfaces.nsIFilePicker;
var picker = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
picker.init(window, msg, nsIFilePicker.modeSave);
var thispath = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
thispath.initWithPath(path);
picker.displayDirectory=thispath;
picker.defaultExtension=defext;
picker.defaultString=file;
picker.appendFilters(nsIFilePicker.filterAll|nsIFilePicker.filterText|nsIFilePicker.filterHTML);
if (picker.show()!=nsIFilePicker.returnCancel) var result=picker.file.path;
}
catch(e) { alert('error during local file access: '+e.toString()) }
}
else { // IE
try { // XPSP2 IE only
var s = new ActiveXObject('UserAccounts.CommonDialog');
s.Filter='All files|*.*|Text files|*.txt|HTML files|*.htm;*.html|XML files|*.xml|';
s.FilterIndex=defext=='txt'?2:'html'?3:'xml'?4:1;
s.InitialDir=path;
s.FileName=file;
if (s.showOpen()) var result=s.FileName;
}
catch(e) { // fallback
var result=prompt(msg,path+file);
}
}
return result;
},
initFilter: function() {
this.$('exportFilterStart').checked=false; this.$('exportStartDate').value='';
this.$('exportFilterEnd').checked=false; this.$('exportEndDate').value='';
this.$('exportFilterTags').checked=false; this.$('exportTags').value='';
this.$('exportFilterText').checked=false; this.$('exportText').value='';
this.showFilterFields();
},
showFilterFields: function(which) {
var show=this.$('exportFilterStart').checked;
this.$('exportFilterStartBy').style.display=show?'block':'none';
this.$('exportStartDate').style.display=show?'block':'none';
var val=this.$('exportFilterStartBy').value;
this.$('exportStartDate').value
=this.getFilterDate(val,'exportStartDate').formatString(this.datetimefmt);
if (which && (which.id=='exportFilterStartBy') && (val=='other'))
this.$('exportStartDate').focus();
var show=this.$('exportFilterEnd').checked;
this.$('exportFilterEndBy').style.display=show?'block':'none';
this.$('exportEndDate').style.display=show?'block':'none';
var val=this.$('exportFilterEndBy').value;
this.$('exportEndDate').value
=this.getFilterDate(val,'exportEndDate').formatString(this.datetimefmt);
if (which && (which.id=='exportFilterEndBy') && (val=='other'))
this.$('exportEndDate').focus();
var show=this.$('exportFilterTags').checked;
this.$('exportTags').style.display=show?'block':'none';
var show=this.$('exportFilterText').checked;
this.$('exportText').style.display=show?'block':'none';
},
getFilterDate: function(val,id) {
var result=0;
switch (val) {
case 'file':
result=new Date(document.lastModified);
break;
case 'other':
result=new Date(this.$(id).value);
break;
default: // today=0, yesterday=1, one week=7, two weeks=14, a month=31
var now=new Date(); var tz=now.getTimezoneOffset()*60000; now-=tz;
var oneday=86400000;
if (id=='exportStartDate')
result=new Date((Math.floor(now/oneday)-val)*oneday+tz);
else
result=new Date((Math.floor(now/oneday)-val+1)*oneday+tz-1);
break;
}
return result;
},
filterExportList: function() {
var theList = this.$('exportList'); if (!theList) return -1;
var filterStart=this.$('exportFilterStart').checked;
var val=this.$('exportFilterStartBy').value;
var startDate=config.macros.exportTiddlers.getFilterDate(val,'exportStartDate');
var filterEnd=this.$('exportFilterEnd').checked;
var val=this.$('exportFilterEndBy').value;
var endDate=config.macros.exportTiddlers.getFilterDate(val,'exportEndDate');
var filterTags=this.$('exportFilterTags').checked;
var tags=this.$('exportTags').value;
var filterText=this.$('exportFilterText').checked;
var text=this.$('exportText').value;
if (!(filterStart||filterEnd||filterTags||filterText)) {
alert('Please set the selection filter');
this.$('exportFilterPanel').style.display='block';
return -1;
}
if (filterStart&&filterEnd&&(startDate>endDate)) {
var msg='starting date/time:\n'
msg+=startDate.toLocaleString()+'\n';
msg+='is later than ending date/time:\n'
msg+=endDate.toLocaleString()
alert(msg);
return -1;
}
// if filter by tags, get list of matching tiddlers
// use getMatchingTiddlers() (if MatchTagsPlugin is installed) for full boolean expressions
// otherwise use getTaggedTiddlers() for simple tag matching
if (filterTags) {
var fn=store.getMatchingTiddlers||store.getTaggedTiddlers;
var t=fn.apply(store,[tags]);
var tagged=[];
for (var i=0; i<t.length; i++) tagged.push(t[i].title);
}
// scan list and select tiddlers that match all applicable criteria
var total=0;
var count=0;
for (var i=0; i<theList.options.length; i++) {
// get item, skip non-tiddler list items (section headings)
var opt=theList.options[i]; if (opt.value=='') continue;
// get tiddler, skip missing tiddlers (this should NOT happen)
var tiddler=store.getTiddler(opt.value); if (!tiddler) continue;
var sel=true;
if ( (filterStart && tiddler.modified<startDate)
|| (filterEnd && tiddler.modified>endDate)
|| (filterTags && !tagged.contains(tiddler.title))
|| (filterText && (tiddler.text.indexOf(text)==-1) && (tiddler.title.indexOf(text)==-1)))
sel=false;
opt.selected=sel;
count+=sel?1:0;
total++;
}
return count;
},
deleteTiddlers: function() {
var list=this.$('exportList'); if (!list) return;
var tids=[];
for (i=0;i<list.length;i++)
if (list.options[i].selected && list.options[i].value.length)
tids.push(list.options[i].value);
if (!confirm('Are you sure you want to delete these tiddlers:\n\n'+tids.join(', '))) return;
store.suspendNotifications();
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
var msg="'"+tid.title+"' is tagged with 'systemConfig'.\n\n";
msg+='Removing this tiddler may cause unexpected results. Are you sure?'
if (tid.tags.contains('systemConfig') && !confirm(msg)) continue;
store.removeTiddler(tid.title);
story.closeTiddler(tid.title);
}
store.resumeNotifications();
alert(tids.length+' tiddlers deleted');
this.refreshList(0); // reload listbox
store.notifyAll(); // update page display
},
go: function() {
if (window.location.protocol!='file:') // make sure we are local
{ displayMessage(config.messages.notFileUrlError); return; }
// get selected tidders, target filename, target type, and notes
var list=this.$('exportList'); if (!list) return;
var tids=[]; for (var i=0; i<list.options.length; i++) {
var opt=list.options[i]; if (!opt.selected||!opt.value.length) continue;
var tid=store.getTiddler(opt.value); if (!tid) continue;
tids.push(tid);
}
if (!tids.length) return; // no tiddlers selected
var target=this.$('exportFilename').value.trim();
if (!target.length) {
displayMessage('A local target path/filename is required',target);
return;
}
var merge=this.$('exportMerge').checked;
var filetype=this.$('exportFormat').value.toLowerCase();
var notes=this.$('exportNotes').value.replace(/\n/g,'<br>');
var total={val:0};
var out=this.assembleFile(target,filetype,tids,notes,total,merge);
if (!total.val) return; // cancelled file overwrite
var link='file:///'+target.replace(/\\/g,'/');
var samefile=link==decodeURIComponent(window.location.href);
var p=getLocalPath(document.location.href);
if (samefile) {
if (config.options.chkSaveBackups) { var t=loadOriginal(p);if(t)saveBackup(p,t); }
if (config.options.chkGenerateAnRssFeed && saveRss instanceof Function) saveRss(p);
}
var ok=saveFile(target,out);
displayMessage((ok?this.okmsg:this.failmsg).format([total.val,total.val!=1?'s':'',target]),link);
},
plainTextHeader:
'Source:\n\t%0\n'
+'Title:\n\t%1\n'
+'Subtitle:\n\t%2\n'
+'Created:\n\t%3 by %4\n'
+'Application:\n\tTiddlyWiki %5 / %6 %7\n\n',
plainTextTiddler:
'- - - - - - - - - - - - - - -\n'
+'| title: %0\n'
+'| created: %1\n'
+'| modified: %2\n'
+'| edited by: %3\n'
+'| tags: %4\n'
+'- - - - - - - - - - - - - - -\n'
+'%5\n',
plainTextFooter:
'',
newsFeedHeader:
'<'+'?xml version="1.0"?'+'>\n'
+'<rss version="2.0">\n'
+'<channel>\n'
+'<title>%1</title>\n'
+'<link>%0</link>\n'
+'<description>%2</description>\n'
+'<language>en-us</language>\n'
+'<copyright>Copyright '+(new Date().getFullYear())+' %4</copyright>\n'
+'<pubDate>%3</pubDate>\n'
+'<lastBuildDate>%3</lastBuildDate>\n'
+'<docs>http://blogs.law.harvard.edu/tech/rss</docs>\n'
+'<generator>TiddlyWiki %5 / %6 %7</generator>\n',
newsFeedTiddler:
'\n%0\n',
newsFeedFooter:
'</channel></rss>',
pureStoreHeader:
'<html><body>'
+'<style type="text/css">'
+' #storeArea {display:block;margin:1em;}'
+' #storeArea div {padding:0.5em;margin:1em;border:2px solid black;height:10em;overflow:auto;}'
+' #pureStoreHeading {width:100%;text-align:left;background-color:#eeeeee;padding:1em;}'
+'</style>'
+'<div id="pureStoreHeading">'
+' TiddlyWiki "PureStore" export file<br>'
+' Source'+': <b>%0</b><br>'
+' Title: <b>%1</b><br>'
+' Subtitle: <b>%2</b><br>'
+' Created: <b>%3</b> by <b>%4</b><br>'
+' TiddlyWiki %5 / %6 %7<br>'
+' Notes:<hr><pre>%8</pre>'
+'</div>'
+'<div id="storeArea">',
pureStoreTiddler:
'%0\n%1',
pureStoreFooter:
'</div><!--POST-BODY-START-->\n<!--POST-BODY-END--></body></html>',
assembleFile: function(target,filetype,tids,notes,total,merge) {
var revised='';
var now = new Date().toLocaleString();
var src=convertUnicodeToUTF8(document.location.href);
var title = convertUnicodeToUTF8(wikifyPlain('SiteTitle').htmlEncode());
var subtitle = convertUnicodeToUTF8(wikifyPlain('SiteSubtitle').htmlEncode());
var user = convertUnicodeToUTF8(config.options.txtUserName.htmlEncode());
var twver = version.major+'.'+version.minor+'.'+version.revision;
var v=version.extensions.ExportTiddlersPlugin; var pver = v.major+'.'+v.minor+'.'+v.revision;
var headerargs=[src,title,subtitle,now,user,twver,'ExportTiddlersPlugin',pver,notes];
switch (filetype) {
case this.type_TX: // plain text
var header=this.plainTextHeader.format(headerargs);
var footer=this.plainTextFooter;
break;
case this.type_CS: // comma-separated
var fields={};
for (var i=0; i<tids.length; i++) for (var f in tids[i].fields) fields[f]=f;
var names=['title','created','modified','modifier','tags','text'];
for (var f in fields) names.push(f);
var header=names.join(',')+'\n';
var footer='';
break;
case this.type_NF: // news feed (XML)
headerargs[0]=store.getTiddlerText('SiteUrl','');
var header=this.newsFeedHeader.format(headerargs);
var footer=this.newsFeedFooter;
break;
case this.type_PS: // PureStore (no code)
var header=this.pureStoreHeader.format(headerargs);
var footer=this.pureStoreFooter;
break;
case this.type_TW: // full TiddlyWiki
default:
var currPath=getLocalPath(window.location.href);
var original=loadFile(currPath);
if (!original) { displayMessage(config.messages.cantSaveError); return; }
var posDiv = locateStoreArea(original);
if (!posDiv) { displayMessage(config.messages.invalidFileError.format([currPath])); return; }
var header = original.substr(0,posDiv[0]+startSaveArea.length)+'\n';
var footer = '\n'+original.substr(posDiv[1]);
break;
}
var out=this.getData(target,filetype,tids,fields,merge);
var revised = header+convertUnicodeToUTF8(out.join('\n'))+footer;
// if full TW, insert page title and language attr, and reset all MARKUP blocks...
if (filetype==this.type_TW) {
var newSiteTitle=convertUnicodeToUTF8(getPageTitle()).htmlEncode();
revised=revised.replaceChunk('<title'+'>','</title'+'>',' ' + newSiteTitle + ' ');
revised=updateLanguageAttribute(revised);
var titles=[]; for (var i=0; i<tids.length; i++) titles.push(tids[i].title);
revised=updateMarkupBlock(revised,'PRE-HEAD',
titles.contains('MarkupPreHead')? 'MarkupPreHead' :null);
revised=updateMarkupBlock(revised,'POST-HEAD',
titles.contains('MarkupPostHead')?'MarkupPostHead':null);
revised=updateMarkupBlock(revised,'PRE-BODY',
titles.contains('MarkupPreBody')? 'MarkupPreBody' :null);
revised=updateMarkupBlock(revised,'POST-SCRIPT',
titles.contains('MarkupPostBody')?'MarkupPostBody':null);
}
total.val=out.length;
return revised;
},
getData: function(target,filetype,tids,fields,merge) {
// output selected tiddlers and gather list of titles (for use with merge)
var out=[]; var titles=[];
var url=store.getTiddlerText('SiteUrl','');
for (var i=0; i<tids.length; i++) {
out.push(this.formatItem(store,filetype,tids[i],url,fields));
titles.push(tids[i].title);
}
// if TW or PureStore format, ask to merge with existing tiddlers (if any)
if (filetype==this.type_TW || filetype==this.type_PS) {
var txt=loadFile(target);
if (txt && txt.length) {
var remoteStore=new TiddlyWiki();
if (version.major+version.minor*.1+version.revision*.01<2.52) txt=convertUTF8ToUnicode(txt);
if (remoteStore.importTiddlyWiki(txt)) {
var existing=remoteStore.getTiddlers('title');
var msg=this.overwriteprompt.format([target,existing.length,existing.length!=1?'s':'']);
if (merge) {
var added=titles.length; var updated=0; var kept=0;
for (var i=0; i<existing.length; i++)
if (titles.contains(existing[i].title)) {
added--; updated++;
} else {
out.push(this.formatItem(remoteStore,filetype,existing[i],url));
kept++;
}
displayMessage(this.mergestatus.format(
[added,added!=1?'s':'',updated,updated!=1?'s':'',kept,kept!=1?'s':'',]));
}
else if (!confirm(msg)) out=[]; // empty the list = don't write file
}
}
}
return out;
},
formatItem: function(s,f,t,u,fields) {
if (f==this.type_TW)
var r=s.getSaver().externalizeTiddler(s,t);
if (f==this.type_PS)
var r=this.pureStoreTiddler.format([t.title,s.getSaver().externalizeTiddler(s,t)]);
if (f==this.type_NF)
var r=this.newsFeedTiddler.format([t.saveToRss(u)]);
if (f==this.type_TX)
var r=this.plainTextTiddler.format([t.title, t.created.toLocaleString(), t.modified.toLocaleString(),
t.modifier, String.encodeTiddlyLinkList(t.tags), t.text]);
if (f==this.type_CS) {
function toCSV(t) { return '"'+t.replace(/"/g,'""')+'"'; } // always encode CSV
var out=[ toCSV(t.title), toCSV(t.created.toLocaleString()), toCSV(t.modified.toLocaleString()),
toCSV(t.modifier), toCSV(String.encodeTiddlyLinkList(t.tags)), toCSV(t.text) ];
for (var f in fields) out.push(toCSV(t.fields[f]||''));
var r=out.join(',');
}
return r||"";
}
}
//}}}
/***
!!!Control panel CSS
//{{{
!css
#exportPanel {
display: none; position:absolute; z-index:12; width:35em; right:105%; top:6em;
background-color: #eee; color:#000; font-size: 8pt; line-height:110%;
border:1px solid black; border-bottom-width: 3px; border-right-width: 3px;
padding: 0.5em; margin:0em; -moz-border-radius:1em;-webkit-border-radius:1em;
}
#exportPanel a, #exportPanel td a { color:#009; display:inline; margin:0px; padding:1px; }
#exportPanel table {
width:100%; border:0px; padding:0px; margin:0px;
font-size:8pt; line-height:110%; background:transparent;
}
#exportPanel tr { border:0px;padding:0px;margin:0px; background:transparent; }
#exportPanel td { color:#000; border:0px;padding:0px;margin:0px; background:transparent; }
#exportPanel select { width:98%;margin:0px;font-size:8pt;line-height:110%;}
#exportPanel input { width:98%;padding:0px;margin:0px;font-size:8pt;line-height:110%; }
#exportPanel textarea { width:98%;padding:0px;margin:0px;overflow:auto;font-size:8pt; }
#exportPanel .box {
border:1px solid black; padding:3px; margin-bottom:5px;
background:#f8f8f8; -moz-border-radius:5px;-webkit-border-radius:5px; }
#exportPanel .topline { border-top:2px solid black; padding-top:3px; margin-bottom:5px; }
#exportPanel .rad { width:auto;border:0 }
#exportPanel .chk { width:auto;border:0 }
#exportPanel .btn { width:auto; }
#exportPanel .btn1 { width:98%; }
#exportPanel .btn2 { width:48%; }
#exportPanel .btn3 { width:32%; }
#exportPanel .btn4 { width:24%; }
#exportPanel .btn5 { width:19%; }
!end
//}}}
!!!Control panel HTML
//{{{
!html
<!-- target path/file -->
<div>
<div style="float:right;padding-right:.5em">
<input type="checkbox" style="width:auto" id="exportMerge" CHECKED
title="combine selected tiddlers with existing tiddlers (if any) in export file"> merge
</div>
export to:<br>
<input type="text" id="exportFilename" size=40 style="width:93%"><input
type="button" id="exportBrowse" value="..." title="select or enter a local folder/file..." style="width:5%"
onclick="var fn=config.macros.exportTiddlers.askForFilename(this); if (fn.length) this.previousSibling.value=fn; ">
</div>
<!-- output format -->
<div>
format:
<select id="exportFormat" size=1>
<option value="TW">TiddlyWiki HTML document (includes core code)</option>
<option value="PS">TiddlyWiki "PureStore" HTML file (tiddler data only)</option>
<option value="TX">TiddlyWiki plain text TXT file (tiddler source listing)</option>
<option value="CS">Comma-Separated Value (CSV) data file</option>
<option value="NF">RSS NewsFeed XML file</option>
</select>
</div>
<!-- notes -->
<div>
notes:<br>
<textarea id="exportNotes" rows=3 cols=40 style="height:4em;margin-bottom:5px;" onfocus="this.select()"></textarea>
</div>
<!-- list of tiddlers -->
<table><tr align="left"><td>
select:
<a href="JavaScript:;" id="exportSelectAll"
onclick="return config.macros.exportTiddlers.process(this)" title="select all tiddlers">
all </a>
<a href="JavaScript:;" id="exportSelectChanges"
onclick="return config.macros.exportTiddlers.process(this)" title="select tiddlers changed since last save">
changes </a>
<a href="JavaScript:;" id="exportSelectOpened"
onclick="return config.macros.exportTiddlers.process(this)" title="select tiddlers currently being displayed">
opened </a>
<a href="JavaScript:;" id="exportSelectRelated"
onclick="return config.macros.exportTiddlers.process(this)" title="select tiddlers related to the currently selected tiddlers">
related </a>
<a href="JavaScript:;" id="exportToggleFilter"
onclick="return config.macros.exportTiddlers.process(this)" title="show/hide selection filter">
filter </a>
</td><td align="right">
<a href="JavaScript:;" id="exportListSmaller"
onclick="return config.macros.exportTiddlers.process(this)" title="reduce list size">
– </a>
<a href="JavaScript:;" id="exportListLarger"
onclick="return config.macros.exportTiddlers.process(this)" title="increase list size">
+ </a>
</td></tr></table>
<select id="exportList" multiple size="10" style="margin-bottom:5px;"
onchange="config.macros.exportTiddlers.refreshList(this.selectedIndex)">
</select><br>
<!-- selection filter -->
<div id="exportFilterPanel" style="display:none">
<table><tr align="left"><td>
selection filter
</td><td align="right">
<a href="JavaScript:;" id="exportHideFilter"
onclick="return config.macros.exportTiddlers.process(this)" title="hide selection filter">hide</a>
</td></tr></table>
<div class="box">
<input type="checkbox" class="chk" id="exportFilterStart" value="1"
onclick="config.macros.exportTiddlers.showFilterFields(this)"> starting date/time<br>
<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">
<select size=1 id="exportFilterStartBy"
onchange="config.macros.exportTiddlers.showFilterFields(this);">
<option value="0">today</option>
<option value="1">yesterday</option>
<option value="7">a week ago</option>
<option value="30">a month ago</option>
<option value="file">file date</option>
<option value="other">other (mm/dd/yyyy hh:mm)</option>
</select>
</td><td width="50%">
<input type="text" id="exportStartDate" onfocus="this.select()"
onchange="config.macros.exportTiddlers.$('exportFilterStartBy').value='other';">
</td></tr></table>
<input type="checkbox" class="chk" id="exportFilterEnd" value="1"
onclick="config.macros.exportTiddlers.showFilterFields(this)"> ending date/time<br>
<table cellpadding="0" cellspacing="0"><tr valign="center"><td width="50%">
<select size=1 id="exportFilterEndBy"
onchange="config.macros.exportTiddlers.showFilterFields(this);">
<option value="0">today</option>
<option value="1">yesterday</option>
<option value="7">a week ago</option>
<option value="30">a month ago</option>
<option value="file">file date</option>
<option value="other">other (mm/dd/yyyy hh:mm)</option>
</select>
</td><td width="50%">
<input type="text" id="exportEndDate" onfocus="this.select()"
onchange="config.macros.exportTiddlers.$('exportFilterEndBy').value='other';">
</td></tr></table>
<input type="checkbox" class="chk" id=exportFilterTags value="1"
onclick="config.macros.exportTiddlers.showFilterFields(this)"> match tags<br>
<input type="text" id="exportTags" onfocus="this.select()">
<input type="checkbox" class="chk" id=exportFilterText value="1"
onclick="config.macros.exportTiddlers.showFilterFields(this)"> match titles/tiddler text<br>
<input type="text" id="exportText" onfocus="this.select()">
</div> <!--box-->
</div> <!--panel-->
<!-- action buttons -->
<div style="text-align:center">
<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"
id="exportFilter" value="apply filter">
<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"
id="exportStart" value="export tiddlers">
<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"
id="exportDelete" value="delete tiddlers">
<input type=button class="btn4" onclick="config.macros.exportTiddlers.process(this)"
id="exportClose" value="close">
</div><!--center-->
!end
//}}}
***/
/***
|Name:|ExtentTagButtonPlugin|
|Description:|Adds a New tiddler button in the tag drop down|
|Version:|3.2a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#ExtendTagButtonPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
window.onClickTag_mptw_orig = window.onClickTag;
window.onClickTag = function(e) {
window.onClickTag_mptw_orig.apply(this,arguments);
var tag = this.getAttribute("tag");
var title = this.getAttribute("tiddler");
// Thanks Saq, you're a genius :)
var popup = Popup.stack[Popup.stack.length-1].popup;
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
wikify("<<newTiddler label:'New tiddler' tag:'"+tag+"'>>",createTiddlyElement(popup,"li"));
return false;
}
//}}}
!!Description
Faeries are creatures that inhabit the most secluded and peaceful areas of the world. Faeries are a peaceful race they hate evil and ugliness. They try to avoid conflict and they will usually flee if challenged. When the situation is desperate a Faerie will defend themselves by using their abilities to blind or confuse the attacker using their magical powers. Faeries look a lot like small humans apart from they have pointy ears like elves, they are nature's embodiment of physical beauty. Faeries are ever young they are charming graceful and intelligent.
Despite popular myths, they do not have wings.
!!Stats
|Size|Small |
|Strength|2D6|
|~|Min 2, Avg 7, Max 12 |
|Dexterity|3D6+1|
|~|Min 4, Avg 11, Max 19 |
|Constitution|2D6|
|~|Min 2, Avg 7, Max 12 |
|Intelligence|3D6+3|
|~|Min 6, Avg 13, Max 21 |
|Special|Bonuses when learning [[Hunter]], [[Farmer]], [[Alchemist]], [[Healer]], [[Mage]], [[Sorcerer]] and all [[Spells]]. Penalty when learning [[Toughness]], [[Endurance]] and all [[Combat Skills]]. |
Plants and how to grow them are your main skill. Not only grow them, but nurture them and even find them out in the wild.
You take great care of your weapons and equipment and they are less likely to wear out and break.
Fatigue is how tired the character is. The more tired you are, the harder it is to perform actions. Fatigue is computed based on your [[Strength]], [[Constitution]] and your [[Endurance skill|Endurance]]. Based on the formula (STR + 2*Constitution) + 3*Endurance.
Every time you move or perform an action in [[Combat]], you use a point of Fatigue. When your Fatigue level reaches zero, every point below that is an additional negative to whatever action you are trying to perform.
Characters normally recover Fatigue at the rate of 1 point per minute, every 30 seconds when [[sitting|Sit]] on the ground, or every 20 seconds when sitting on an object.
Syntax:
*feint <target>
*feint [<left|right>]
Changes your attack mode to feinting. Feinting is a tactical manoeuvrer that can increase your chance to hit a well-defended opponent. Instead of attacking, you fake an attack in attempt to draw your opponent off guard. If the feint succeeds (rolled as a contest of skills of your weapon skill vs your opponent's weapon skill or shield skill — whichever is highest), the amount by which you made your feint is applied as a modifier to your next attack roll. Upon executing a successful feint, your attack mode automatically switches to attack. Your next attack is made with the feint modifier, and then your attack mode automatically switches back to feint.
Feinting is useful against an opponent whom you are having great difficulty hitting. If your normal chance to hit is reasonable then feinting is counter-productive: turns are spent feinting which otherwise would be used for attacks.
Fencing weapons are characterised by being designed for a highly trained style of fighting that makes extensive use of the point of the weapon (as opposed to the edge) in a way that allows the user to keep them 'on point'. That is, whereas the user of a broadsword might take large 'round' swings when striking or parrying, with the result that the weapon must be brought back in line before it can be used again, the fencer's weapon remains pointed at the opponent at all times.
Fencing allows the user to act faster in [[combat|Combat]] partly because Fencing Weapons are usually lighter and smaller than normal weapons. This gives the character a better [[initiative|Initiative]]. It also gives an increased chance to [[parry|Parry]] a blow.
The Fencing skill is learned like any other skill, but has a prerequisite of [[Swords]] 2.
/***
|''Name:''|FieldsEditorPlugin|
|''Description:''|//create//, //edit//, //view// and //delete// commands in toolbar <<toolbar fields>>.|
|''Version:''|1.0.2|
|''Date:''|Dec 21,2007|
|''Source:''|http://visualtw.ouvaton.org/VisualTW.html|
|''Author:''|Pascal Collin|
|''License:''|[[BSD open source license|License]]|
|''~CoreVersion:''|2.2.0|
|''Browser:''|Firefox 2.0; InternetExplorer 6.0, others|
!Demo:
On [[homepage|http://visualtw.ouvaton.org/VisualTW.html]], see [[FieldEditor example]]
!Installation:
*import this tiddler from [[homepage|http://visualtw.ouvaton.org/VisualTW.html]] (tagged as systemConfig)
*save and reload
*optionnaly : add the following css text in your StyleSheet : {{{#popup tr.fieldTableRow td {padding:1px 3px 1px 3px;}}}}
!Code
***/
//{{{
config.commands.fields.handlePopup = function(popup,title) {
var tiddler = store.fetchTiddler(title);
if(!tiddler)
return;
var fields = {};
store.forEachField(tiddler,function(tiddler,fieldName,value) {fields[fieldName] = value;},true);
var items = [];
for(var t in fields) {
var editCommand = "<<untiddledCall editFieldDialog "+escape(title)+" "+escape(t)+">>";
var deleteCommand = "<<untiddledCall deleteField "+escape(title)+" "+escape(t)+">>";
var renameCommand = "<<untiddledCall renameField "+escape(title)+" "+escape(t)+">>";
items.push({field: t,value: fields[t], actions: editCommand+renameCommand+deleteCommand});
}
items.sort(function(a,b) {return a.field < b.field ? -1 : (a.field == b.field ? 0 : +1);});
var createNewCommand = "<<untiddledCall createField "+escape(title)+">>";
items.push({field : "", value : "", actions:createNewCommand });
if(items.length > 0)
ListView.create(popup,items,this.listViewTemplate);
else
createTiddlyElement(popup,"div",null,null,this.emptyText);
}
config.commands.fields.listViewTemplate = {
columns: [
{name: 'Field', field: 'field', title: "Field", type: 'String'},
{name: 'Actions', field: 'actions', title: "Actions", type: 'WikiText'},
{name: 'Value', field: 'value', title: "Value", type: 'WikiText'}
],
rowClasses: [
{className: 'fieldTableRow', field: 'actions'}
],
buttons: [ //can't use button for selected then delete, because click on checkbox will hide the popup
]
}
config.macros.untiddledCall = { // when called from listview, tiddler is unset, so we need to pass tiddler as parameter
handler : function(place,macroName,params,wikifier,paramString) {
var macroName = params.shift();
if (macroName) var macro = config.macros[macroName];
var title = params.shift();
if (title) var tiddler = store.getTiddler(unescape(title));
if (macro) macro.handler(place,macroName,params,wikifier,paramString,tiddler);
}
}
config.macros.deleteField = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly && params[0]) {
fieldName = unescape(params[0]);
var btn = createTiddlyButton(place,"delete", "delete "+fieldName,this.onClickDeleteField);
btn.setAttribute("title",tiddler.title);
btn.setAttribute("fieldName", fieldName);
}
},
onClickDeleteField : function() {
var title=this.getAttribute("title");
var fieldName=this.getAttribute("fieldName");
var tiddler = store.getTiddler(title);
if (tiddler && fieldName && confirm("delete field " + fieldName+" from " + title +" tiddler ?")) {
delete tiddler.fields[fieldName];
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
return false;
}
}
config.macros.createField = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly) {
var btn = createTiddlyButton(place,"create new", "create a new field",this.onClickCreateField);
btn.setAttribute("title",tiddler.title);
}
},
onClickCreateField : function() {
var title=this.getAttribute("title");
var tiddler = store.getTiddler(title);
if (tiddler) {
var fieldName = prompt("Field name","");
if (store.getValue(tiddler,fieldName)) {
window.alert("This field already exists.");
}
else if (fieldName) {
var v = prompt("Field value","");
tiddler.fields[fieldName]=v;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
}
return false;
}
}
config.macros.editFieldDialog = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly && params[0]) {
fieldName = unescape(params[0]);
var btn = createTiddlyButton(place,"edit", "edit this field",this.onClickEditFieldDialog);
btn.setAttribute("title",tiddler.title);
btn.setAttribute("fieldName", fieldName);
}
},
onClickEditFieldDialog : function() {
var title=this.getAttribute("title");
var tiddler = store.getTiddler(title);
var fieldName=this.getAttribute("fieldName");
if (tiddler && fieldName) {
var value = tiddler.fields[fieldName];
value = value ? value : "";
var lines = value.match(/\n/mg);
lines = lines ? true : false;
if (!lines || confirm("This field contains more than one line. Only the first line will be kept if you edit it here. Proceed ?")) {
var v = prompt("Field value",value);
tiddler.fields[fieldName]=v;
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
}
return false;
}
}
config.macros.renameField = {
handler : function(place,macroName,params,wikifier,paramString,tiddler) {
if(!readOnly && params[0]) {
fieldName = unescape(params[0]);
var btn = createTiddlyButton(place,"rename", "rename "+fieldName,this.onClickRenameField);
btn.setAttribute("title",tiddler.title);
btn.setAttribute("fieldName", fieldName);
}
},
onClickRenameField : function() {
var title=this.getAttribute("title");
var fieldName=this.getAttribute("fieldName");
var tiddler = store.getTiddler(title);
if (tiddler && fieldName) {
var newName = prompt("Rename " + fieldName + " as ?", fieldName);
if (newName) {
tiddler.fields[newName]=tiddler.fields[fieldName];
delete tiddler.fields[fieldName];
store.saveTiddler(tiddler.title,tiddler.title,tiddler.text,tiddler.modifier,tiddler.modified,tiddler.tags,tiddler.fields);
story.refreshTiddler(title,"ViewTemplate",true);
}
}
return false;
}
}
config.shadowTiddlers.StyleSheetFieldsEditor = "/*{{{*/\n";
config.shadowTiddlers.StyleSheetFieldsEditor += ".fieldTableRow td {padding : 1px 3px}\n";
config.shadowTiddlers.StyleSheetFieldsEditor += ".fieldTableRow .button {border:0; padding : 0 0.2em}\n";
config.shadowTiddlers.StyleSheetFieldsEditor +="/*}}}*/";
store.addNotification("StyleSheetFieldsEditor", refreshStyles);
//}}}
Syntax:
*fire [<target>]
Fires a readied missile weapon at <target>. If you fire after aiming, any bonuses accrued by aiming are applied to the attack, regardless of your skill with the weapon. If you fire after aiming, your offensive action then reverts to aim; if you fire while not aiming, your offensive action remains fire and you will continue to fire once per turn, providing you have enough ammunition.
If you run out of ammo then your action switches to [[Wait]].
You are able to charm the trout from the rivers and the bass from the seas.
Syntax:
*flee [<direction>]
If you feel that combat may not be going your way you can flee the skirmish, the default direction being the way you came in. You can flee in a specified direction, but usually this is harder to do so. You certainly cannot flee in a direction that is [[Guarded|Guard]].
See also [[Wimp]].
Syntax:
* follow <character>
Whenever the named character moves, you will immediately follow them. This is especially useful when you have a [[Group]] of adventurers and wish to move together, but you may follow any player or [[NPC|NPCs]]. Although you should be careful, some people don't like being followed.
You will need to [[Eat]] and [[Drink]] during your game. Whilst you probably won't die of starvation, you are likely to get tired more easily and eating food definitely helps you heal properly in the long term.
There are numerous shops where you can buy (and sell) food and you can even learn to [[Cook]] your own food with the [[Chef]] skill.
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description
Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.
''Syntax:''
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]] is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].
!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features:
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen)
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features:
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs:
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features:
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// ForEachTiddlerPlugin
//============================================================================
//============================================================================
// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {
if (!window.abego) window.abego = {};
version.extensions.ForEachTiddlerPlugin = {
major: 1, minor: 0, revision: 8,
date: new Date(2007,3,12),
source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};
// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
TiddlyWiki.prototype.forEachTiddler = function(callback) {
for(var t in this.tiddlers) {
callback.call(this,t,this.tiddlers[t]);
}
};
}
//============================================================================
// forEachTiddler Macro
//============================================================================
version.extensions.forEachTiddler = {
major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
config.macros.forEachTiddler = {
// Standard Properties
label: "forEachTiddler",
prompt: "Perform actions on a (sorted) selection of tiddlers",
// actions
actions: {
addToList: {},
write: {}
}
};
// ---------------------------------------------------------------------------
// The forEachTiddler Macro Handler
// ---------------------------------------------------------------------------
config.macros.forEachTiddler.getContainingTiddler = function(e) {
while(e && !hasClass(e,"tiddler"))
e = e.parentNode;
var title = e ? e.getAttribute("tiddler") : null;
return title ? store.getTiddler(title) : null;
};
config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);
if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the "in" clause
var tiddlyWikiPath = undefined;
if ((i < params.length) && params[i] == "in") {
i++;
if (i >= params.length) {
this.handleError(place, "TiddlyWiki path expected behind 'in'.");
return;
}
tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the where clause
var whereClause ="true";
if ((i < params.length) && params[i] == "where") {
i++;
whereClause = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the sort stuff
var sortClause = null;
var sortAscending = true;
if ((i < params.length) && params[i] == "sortBy") {
i++;
if (i >= params.length) {
this.handleError(place, "sortClause missing behind 'sortBy'.");
return;
}
sortClause = this.paramEncode(params[i]);
i++;
if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
sortAscending = params[i] == "ascending";
i++;
}
}
// Parse the script
var scriptText = null;
if ((i < params.length) && params[i] == "script") {
i++;
scriptText = this.paramEncode((i < params.length) ? params[i] : "");
i++;
}
// Parse the action.
// When we are already at the end use the default action
var actionName = "addToList";
if (i < params.length) {
if (!config.macros.forEachTiddler.actions[params[i]]) {
this.handleError(place, "Unknown action '"+params[i]+"'.");
return;
} else {
actionName = params[i];
i++;
}
}
// Get the action parameter
// (the parsing is done inside the individual action implementation.)
var actionParameter = params.slice(i);
// --- Processing ------------------------------------------
try {
this.performMacro({
place: place,
inTiddler: tiddler,
whereClause: whereClause,
sortClause: sortClause,
sortAscending: sortAscending,
actionName: actionName,
actionParameter: actionParameter,
scriptText: scriptText,
tiddlyWikiPath: tiddlyWikiPath});
} catch (e) {
this.handleError(place, e);
}
};
// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {
var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);
var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
context["tiddlyWiki"] = tiddlyWiki;
// Get the tiddlers, as defined by the whereClause
var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
context["tiddlers"] = tiddlers;
// Sort the tiddlers, when sorting is required.
if (parameter.sortClause) {
this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
}
return {tiddlers: tiddlers, context: context};
};
// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
return this.getTiddlersAndContext(parameter).tiddlers;
};
// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
// The following properties are supported:
//
// place
// whereClause
// sortClause
// sortAscending
// actionName
// actionParameter
// scriptText
// tiddlyWikiPath
//
// All properties are optional.
// For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
var tiddlersAndContext = this.getTiddlersAndContext(parameter);
// Perform the action
var actionName = parameter.actionName ? parameter.actionName : "addToList";
var action = config.macros.forEachTiddler.actions[actionName];
if (!action) {
this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
return;
}
var actionHandler = action.handler;
actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};
// ---------------------------------------------------------------------------
// The actions
// ---------------------------------------------------------------------------
// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
return;
}
// Perform the action.
var list = document.createElement("ul");
place.appendChild(list);
for (var i = 0; i < tiddlers.length; i++) {
var tiddler = tiddlers[i];
var listItem = document.createElement("li");
list.appendChild(listItem);
createTiddlyLink(listItem, tiddler.title, true);
}
};
abego.parseNamedParameter = function(name, parameter, i) {
var beginExpression = null;
if ((i < parameter.length) && parameter[i] == name) {
i++;
if (i >= parameter.length) {
throw "Missing text behind '%0'".format([name]);
}
return config.macros.forEachTiddler.paramEncode(parameter[i]);
}
return null;
}
// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
// Parse the parameter
var p = 0;
if (p >= parameter.length) {
this.handleError(place, "Missing expression behind 'write'.");
return;
}
var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
// Parse the "begin" option
var beginExpression = abego.parseNamedParameter("begin", parameter, p);
if (beginExpression !== null)
p += 2;
var endExpression = abego.parseNamedParameter("end", parameter, p);
if (endExpression !== null)
p += 2;
var noneExpression = abego.parseNamedParameter("none", parameter, p);
if (noneExpression !== null)
p += 2;
// Parse the "toFile" option
var filename = null;
var lineSeparator = undefined;
if ((p < parameter.length) && parameter[p] == "toFile") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
return;
}
filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
p++;
if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
p++;
if (p >= parameter.length) {
this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
return;
}
lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
p++;
}
}
// Check for extra parameters
if (parameter.length > p) {
config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
return;
}
// Perform the action.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
var count = tiddlers.length;
var text = "";
if (count > 0 && beginExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
for (var i = 0; i < count; i++) {
var tiddler = tiddlers[i];
text += func(tiddler, context, count, i);
}
if (count > 0 && endExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);
if (count == 0 && noneExpression)
text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
if (filename) {
if (lineSeparator !== undefined) {
lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
text = text.replace(/\n/mg,lineSeparator);
}
saveFile(filename, convertUnicodeToUTF8(text));
} else {
var wrapper = createTiddlyElement(place, "span");
wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
}
};
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
return {
place : placeParam,
whereClause : whereClauseParam,
sortClause : sortClauseParam,
sortAscending : sortAscendingParam,
script : scriptText,
actionName : actionNameParam,
actionParameter : actionParameterParam,
tiddlyWikiPath : tiddlyWikiPathParam,
inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
};
};
// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
if (!idPrefix) {
idPrefix = "store";
}
var lenPrefix = idPrefix.length;
// Read the content of the given file
var content = loadFile(this.getLocalPath(path));
if(content === null) {
throw "TiddlyWiki '"+path+"' not found.";
}
var tiddlyWiki = new TiddlyWiki();
// Starting with TW 2.2 there is a helper function to import the tiddlers
if (tiddlyWiki.importTiddlyWiki) {
if (!tiddlyWiki.importTiddlyWiki(content))
throw "File '"+path+"' is not a TiddlyWiki.";
tiddlyWiki.dirty = false;
return tiddlyWiki;
}
// The legacy code, for TW < 2.2
// Locate the storeArea div's
var posOpeningDiv = content.indexOf(startSaveArea);
var posClosingDiv = content.lastIndexOf(endSaveArea);
if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
throw "File '"+path+"' is not a TiddlyWiki.";
}
var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
// Create a "div" element that contains the storage text
var myStorageDiv = document.createElement("div");
myStorageDiv.innerHTML = storageText;
myStorageDiv.normalize();
// Create all tiddlers in a new TiddlyWiki
// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
var store = myStorageDiv.childNodes;
for(var t = 0; t < store.length; t++) {
var e = store[t];
var title = null;
if(e.getAttribute)
title = e.getAttribute("tiddler");
if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
title = e.id.substr(lenPrefix);
if(title && title !== "") {
var tiddler = tiddlyWiki.createTiddler(title);
tiddler.loadFromDiv(e,title);
}
}
tiddlyWiki.dirty = false;
return tiddlyWiki;
};
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
//
// (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
var script = context["script"];
var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
var fullText = (script ? script+";" : "")+functionText+";theFunction;";
return eval(fullText);
};
// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
var result = [];
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
tiddlyWiki.forEachTiddler(function(title,tiddler) {
if (func(tiddler, context, undefined, undefined)) {
result.push(tiddler);
}
});
return result;
};
// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
var message = "Extra parameter behind '"+actionName+"':";
for (var i = firstUnusedIndex; i < parameter.length; i++) {
message += " "+parameter[i];
}
this.handleError(place, message);
};
// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? -1
: +1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
var result =
(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue)
? 0
: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
? +1
: -1;
return result;
};
// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
// To avoid evaluating the sortClause whenever two items are compared
// we pre-calculate the sortValue for every item in the array and store it in a
// temporary property ("forEachTiddlerSortValue") of the tiddlers.
var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
var count = tiddlers.length;
var i;
for (i = 0; i < count; i++) {
var tiddler = tiddlers[i];
tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
}
// Do the sorting
tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);
// Delete the temporary property that holds the sortValue.
for (i = 0; i < tiddlers.length; i++) {
delete tiddlers[i].forEachTiddlerSortValue;
}
};
// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
displayMessage(message);
};
// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
var message ="<<"+macroName;
for (var i = 0; i < params.length; i++) {
message += " "+params[i];
}
message += ">>";
displayMessage(message);
};
// Internal.
//
// Creates an element that holds an error message
//
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
var message = (exception.description) ? exception.description : exception.toString();
return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};
// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
if (place) {
this.createErrorElement(place, exception);
} else {
throw exception;
}
};
// Internal.
//
// Encodes the given string.
//
// Replaces
// "$))" to ">>"
// "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
var reGTGT = new RegExp("\\$\\)\\)","mg");
var reGT = new RegExp("\\$\\)","mg");
return s.replace(reGTGT, ">>").replace(reGT, ">");
};
// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
//
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
// Remove any location part of the URL
var hashPos = originalPath.indexOf("#");
if(hashPos != -1)
originalPath = originalPath.substr(0,hashPos);
// Convert to a native file format assuming
// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
var localPath;
if(originalPath.charAt(9) == ":") // pc local file
localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(7));
else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
localPath = unescape(originalPath.substr(5));
else // pc network file
localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");
return localPath;
};
// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
"forEachTiddler");
//============================================================================
// End of forEachTiddler Macro
//============================================================================
//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
var n = prefix.length;
return (this.length >= n) && (this.slice(0, n) == prefix);
};
//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
var n = suffix.length;
return (this.length >= n) && (this.right(n) == suffix);
};
//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
return this.indexOf(substring) >= 0;
};
//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
for (var i = 0; i < this.length; i++) {
if (this[i] == item) {
return i;
}
}
return -1;
};
//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false.
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
return (this.indexOf(item) >= 0);
};
//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
for(var i = 0; i < items.length; i++) {
if (this.contains(items[i])) {
return true;
}
}
return false;
};
//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
//
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null]
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
for(var i = 0; i < items.length; i++) {
if (!this.contains(items[i])) {
return false;
}
}
return true;
};
} // of "install only once"
// Used Globals (for JSLint) ==============
// ... DOM
/*global document */
// ... TiddlyWiki Core
/*global convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink,
displayMessage, endSaveArea, hasClass, loadFile, saveFile,
startSaveArea, store, wikify */
//}}}
The idea of TinyFantasy is to have fun.
There are no set goals other than those that you set for yourself. But that does not mean that the game is boring. There are numerous quests and features within the game for you to explore and use. So whether you want to build your characters stats, become the richest or most famous, or even learn a trade or skill, there are so many ways to achieve your goals.
You are not limited to just the existing map and locations. You can also build and add components to the game, so if you wish to build a house or castle or even add quests for other players to partake in then that is also possible.
However you may also take part in the game even when you are not logged in. When you logout your character essentially becomes hidden. All of your equipment, inventory spell effects etc. stay with you. However if you have purchased a [[Merchant]] bag then you turn into a stone statue where you can sell (or buy) items. Basically becoming your own little trading stand. When you re-connect you will get a short message regarding your time away.
There are, however, a number of [[Rules]] that we ask all players to abide by. Don't abuse the game and have fun.
Syntax:
*get <object>
*get <object> from <container>
*get all from <container>
This picks up any item from the current location and places it in your inventory. Items are subject to [[Weight]], so the more you carry the more encumbered you become. It is possible to carry so much that you cannot move.
Objects may be kept inside other objects such as [[Bags or Chests|Containers]]. The second option allows you to remove them. The <container> may be in your location or in your [[Inventory]] either works. See also [[Put]].
Syntax:
*give [<quantity>] <object> to <character>
This will send a request to transfer a quantity of the name objects to the intended character. If no quantity is specified for objects which have multiples then only a single item is given. Use the keyword "all" to give all of the objects.
Example:
*give Dagger to Greg
*give 10 groats to Greg
The transaction is not necessarily immediate. If the recipient has already set permissions to receive items from you then the items are passed. Otherwise a message is sent to the intended recipient, asking whether they wish to accept the items. If they [[Accept]] then the items are transferred. If they are [[Rejected|Reject]] then they are not.
Syntax:
*go <direction>
*move <direction>
*<direction>
Moves the player in the specified direction. The word 'go' may be omitted. 'move' is an alias for 'go'.
North, South, East and West are all possible movement directions. As are Up, and Down. These can also be abbreviated or joined, such as "nw" for ~North-West. There may also be subtle directions such as 'Enter' or 'Leave'.
It is also possible to have additional hidden movement directions. "Climb" could indicate climb a tree or rock. The most obvious exits from the current location are displayed, but feel free to find other moves.
Syntax:
*gripe <message>
Sends <message> to the system maintainer. Gripes are logged for later reference; also, if the system maintainer is connected he will receive the gripe real-time when the gripe is made.
An Adventurers Group allows you to combine your forces when attacking a dungeon or location. You can only belong to one adventurer group. When in a group it is especially useful to [[Guard]] some of the weaker members to protect them from immediate harm. These are usually the [[Healers|Healer]] or [[Magic]] users of the group. When in a group all experience earned is shared between the group members equally.
Syntax:
* group - Shows information about your current group
* group create - Creates a new group with you as the leader
* group accept - Accepts an invite from another player
* group leave - Leave the current group
* group invite <name> - Invites the named player to join the group
* group say <message> - Sends a message to all members of your group
The following commands can only be performed by the group leader
* group name <name> - Names the current group
* group disband - Removes all group members and deletes the group
* group promote <name> - Makes the named player the new group leader
* group kick <name> - Removes the named player from the group
* group follow <name> - Causes all members of the group to follow the named player
May also be abbrevaited to "grp" as well. e.g. grp create, grp say Hi.
Resources and equipment from corpses killed by the Group would still still need to be shared. As with normal [[Combat]], the corpse would be locked to whoever did the killing blow.
See also [[Follow]] and [[Guard]].
Players may form groups in order to explore, hunt, role-play or otherwise enjoy the game cooperatively. Whenever a monster is slain by a player, the rest of the group will receive experience points and the credit needed for quests. Players also gain experience points for [[healing|Healer]] other wounded players.
Syntax:
*guard <target>
*guard ally <player>
*guard ally group
The Guard command lets you guard another player, an object, or an exit.
When you are guarding a player, he or she may not be attacked with melee weapons or in unarmed combat. However, they may still be targeted with ranged weapons or spells.
Because a guarded player may not be attacked, the system will not allow you to guard someone who is already guarding something or someone... otherwise, players would be able to create unrealistic, unattackable 'guard loops' (Antar is guarding Nim, so Nim can't be attacked, and Nim is guarding Antar, so Antar can't be attacked).
Guarded players can still use combat actions, or spells. Fighters, it would be a good idea to Guard any magic users in your party.
When you are guarding an exit, it cannot be used until you stop guarding it. Likewise, When you are guarding a thing, it cannot be picked up until you stop guarding it.
The exception to this is if you specify the "ally" option. If this command parameter is used then the named ally is allowed to use the object or exit. Or if you are in a group then all of your allies will be allowed to use the object. This is a very good option if the group is being pursued by a monster. A valiant warrior could hold the door whilst the rest of the group flee to safety.
The command [[Unguard]] causes you to stop guarding whatever you were guarding before. Although this will automatically happen should you or the character you are guarding become separated, or in the case of an exit, you leave the room.
!!Description
!!Stats
|Size|Medium |
|Strength|3D6 |
|Dexterity|3D6 |
|Constitution|2D6+3 |
|Intelligence|3D6 |
|Special|Bonuses when learning [[Farmer]], and [[Endurance]] skills |
!!Description
!!Stats
|Size|Large |
|Strength|3D6+3|
|~|Min 6, Avg 13, Max 21 |
|Dexterity|2D6|
|~|Min 2, Avg 7, Max 12 |
|Constitution|3D6+3|
|~|Min 6, Avg 13, Max 21 |
|Intelligence|2D6+3|
|~|Min 5, Avg 10, Max 15 |
|Special|Bonuses when learning [[Toughness]] and [[Endurance]] skills. [[Toughness]] automatically starts at level 2. |
Syntax:
* heal
* heal <character>
You can heal yourself or another player and increase their [[Hit Points]] (upto their maximum, of course) once per player per time frame for 1D4+your [[Healer]] skill. If you omit the <character> name then you heal yourself.
|!Skill level| !Time |
| 1 | 60 mins |
| 2 | 30 mins |
| 3 | 20 mins |
| 4 | 15 mins |
| 5 | 12 mins |
| 6 | 10 mins |
You are a trained healer, and can use techniques and equipment to heal wounded players. Once per player per hour you may attempt to [[Heal]] 1D4 + your Healer skill of damage to a player.
Healers may also learn [[Healing Spells]] at a lower cost of [[Character Points]].
Healing Spells are a class of spell that can either directly heal a character or enhance their natural healing rate.
Syntax:
*help
*help <topic>
This provides basic in-game help on a variety of topics.
Full help for all game commands is available using the following syntax:
*<command> help
The game systems have been extensively re-written from the Original TinyMUCK command set, so commands like [[Get]] no longer function like the orginal commands. The are still there, but they only work in the restricted [[Wizard Zone]].
/***
|Name:|HideWhenPlugin|
|Description:|Allows conditional inclusion/exclusion in templates|
|Version:|3.2a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#HideWhenPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
For use in ViewTemplate and EditTemplate. Example usage:
{{{<div macro="showWhenTagged Task">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}
Warning: the showWhen and hideWhen macros will blindly eval paramString.
This could be used to execute harmful javascript from a tiddler.
(TODO: Make some effort to sanitize paramString. Perhaps disallow the equals sign?)
***/
//{{{
window.hideWhenLastTest = false;
window.removeElementWhen = function(test,place) {
window.hideWhenLastTest = test;
if (test) {
jQuery(place).empty()
place.parentNode.removeChild(place);
}
};
merge(config.macros,{
hideWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( eval(paramString), place );
}},
showWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !eval(paramString), place );
}},
hideWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAll(params), place );
}},
showWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAll(params), place );
}},
hideWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAny(params), place );
}},
showWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAny(params), place );
}},
hideWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAll(params), place );
}},
showWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAll(params), place );
}},
hideWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0]), place );
}},
showWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !(store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0])), place );
}},
hideWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.title == params[0], place );
}},
showWhenTitleIs: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.title != params[0], place );
}},
'else': { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !window.hideWhenLastTest, place );
}}
});
//}}}
The initial Hit Point values are your [[Constitution]] plus [[Toughness]] skill multiplied by two. During combat (or possibly other activities) Hit Points can be reduced. Once a Character's HP reduces to zero then they lose consciousness at this time. Another hit after this means that you will likely be dead.
To simulate losing consciousness, a player will be disconnected from the game. There is normally a 30 second lock out during this period, but this amount varies depending on your connection speed.
Hit Points, like [[Mana]] points, are recovered at the rate of 1 point per hour, but this healing rate may be modified by Potions or Objects, or you may be healed by players with the [[Healer]] skill or [[Healing Spells]].
[[Armour]], both magical and physical, can protect you from being hurt by absorbing [[Damage]].
Death is permanent... except when it isn't. Normally when your character dies, thats the end. Except it may be possible to preserve your character through the use of magic spells or objects. Such objects can transport your body to a place of safety. If you don't have such a safety net then your character is permanently lost.
Syntax:
*hold <object>
This allows you to hold an item ready to use. For example you can hold a lantern to use it to light a dark room, or hold a [[Merchant Bag]] before disconnecting from the game.
Where does it all go? You can eat more than most before you get full.
Syntax:
*home
*drink <Home Potion>
Using a [[Home Potion]] will magically teleport you to the [[Adventurers' Guild]]. It may be used at anytime, even if you are in combat.
The [[Home Potion]] must be in your immediate inventory, it may not be inside a Bag or Pouch.
Home Potions are made by [[Alchemists|Alchemist]] using a closely guarded secret. They may be purchased by Adventurers in their shop, or, if you learn the [[Alchemist]] skill then you may also learn how to make them yourself.
A Home Potion is a specific type of potion which will magically transport your character back to their home location. In case you haven't yet made a home within the game, it will take you back to the [[Adventurers Guild]].
Home Potions may be used at any time, even whilst in [[Combat]], and are usually used as a last minute save.
As with all Potions, they may be invoked by the [[Drink]] command. Or in the specific case of the Home Potion, by the simple command [[Home]].
Of course if you don't have any with you... tough.
There is also a Magical Spell which can be cast on your body so that if you lose consciousness during combat then you will automatically be transported [[Home]].
@@A word of warning though: The Home Potion is balanced to your body. If you are carrying more items than your bodily strength can manage then some items may get left behind.@@
!!Description
[>img[./images/Human.jpg]]Humans are the most numerous of the species. They are the most adaptable, flexible, and ambitious people among the common races. They are diverse in their tastes, morals, customs, and habits. They can be hardy or fine, light‑skinned or dark, showy or austere, primitive or civilized, devout or impious, humans run the gamut. They typically stand from 5 feet to a little over 6 feet tall and weigh from 125 to 250 pounds. With men noticeably taller and heavier then women. Thanks to their penchant for migration and conquest, and to their short generations, humans are more physically diverse than other common races, with skin shades that run from nearly black to very pale, hair from black to blond, and facial hair (for men) from sparse to thick.
!!Stats
|Size|Medium |
|Strength|3D6|
|~|Min 3, Avg 10, Max 18 |
|Dexterity|3D6|
|~|Min 3, Avg 10, Max 18 |
|Constitution|3D6|
|~|Min 3, Avg 10, Max 18 |
|Intelligence|3D6|
|~|Min 3, Avg 10, Max 18 |
|Special|None|
You are knowledgable in the needs and abilities of animals as well as where they thrive. As such you can track them, catch and kill them. You also know how best to butcher them preserving hides, furs and bones etc.
You are also familiar with how to tame and train animals both wild and domestic, and can train them to perform tasks or tricks within their capabilities. This ranges from simply training a riding horse, to a training up a fully battle ready warhorse or even more exotic animals such as Hawks, Wolves or Bears. Obviously some animals are more easier to train than others.
You were raised in dodgy circumstances. You have a better understanding of many [[illegal actvities|Thieves Skills]]. You automatically gain [[Thieves Cant]] level 1.
Creates a small sphere of light that hovers over the recipient for a short period of time. It will illuminate the immediate surroundings.
| !Mana | !Difficulty |
| 5 | 1 |
<<include "reference.html">>
Your Initiative is how fast you are in [[Combat]], it is based on [[Intelligence]] and [[Dexterity]]. In this instance having a lower score is better as is decreases the time you take to perform actions and makes you quicker in [[Combat]].
Base score is (300 - [[Fatigue]]*2) / ([[Intelligence]] + [[Dexterity]] + the skill level of the weapon you are using^2 + [[Martial Arts]] skill*2)
For example a Master Swordsman with [[Intelligence]] and [[Dexterity]] of 15, and a [[Fatigue]] of 25 would have an initiative of (300 - 50) / (15 + 15 + 25) = 4.5
This means that they could make one attack every 4.5 seconds.
Your initiative is also modified depending on how much [[Weight]] you are carrying. If you are encumbered then your initiative is much longer than normal.
/***
|Name:|InstantTimestampPlugin|
|Description:|A handy way to insert timestamps in your tiddler content|
|Version:|1.0.10a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#InstantTimestampPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Usage
If you enter {ts} in your tiddler content (without the spaces) it will be replaced with a timestamp when you save the tiddler. Full list of formats:
* {ts} or {t} -> timestamp
* {ds} or {d} -> datestamp
* !ts or !t at start of line -> !!timestamp
* !ds or !d at start of line -> !!datestamp
(I added the extra ! since that's how I like it. Remove it from translations below if required)
!!Notes
* Change the timeFormat and dateFormat below to suit your preference.
* See also http://mptw2.tiddlyspot.com/#AutoCorrectPlugin
* You could invent other translations and add them to the translations array below.
***/
//{{{
config.InstantTimestamp = {
// adjust to suit
timeFormat: 'DD/0MM/YY 0hh:0mm',
dateFormat: 'DD/0MM/YY',
translations: [
[/^!ts?$/img, "'!!{{ts{'+now.formatString(config.InstantTimestamp.timeFormat)+'}}}'"],
[/^!ds?$/img, "'!!{{ds{'+now.formatString(config.InstantTimestamp.dateFormat)+'}}}'"],
// thanks Adapted Cat
[/\{ts?\}(?!\}\})/ig,"'{{ts{'+now.formatString(config.InstantTimestamp.timeFormat)+'}}}'"],
[/\{ds?\}(?!\}\})/ig,"'{{ds{'+now.formatString(config.InstantTimestamp.dateFormat)+'}}}'"]
],
excludeTags: [
"noAutoCorrect",
"noTimestamp",
"html",
"CSS",
"css",
"systemConfig",
"systemConfigDisabled",
"zsystemConfig",
"Plugins",
"Plugin",
"plugins",
"plugin",
"javascript",
"code",
"systemTheme",
"systemPalette"
],
excludeTiddlers: [
"StyleSheet",
"StyleSheetLayout",
"StyleSheetColors",
"StyleSheetPrint"
// more?
]
};
TiddlyWiki.prototype.saveTiddler_mptw_instanttimestamp = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created) {
tags = tags ? tags : []; // just in case tags is null
tags = (typeof(tags) == "string") ? tags.readBracketedList() : tags;
var conf = config.InstantTimestamp;
if ( !tags.containsAny(conf.excludeTags) && !conf.excludeTiddlers.contains(newTitle) ) {
var now = new Date();
var trans = conf.translations;
for (var i=0;i<trans.length;i++) {
newBody = newBody.replace(trans[i][0], eval(trans[i][1]));
}
}
// TODO: use apply() instead of naming all args?
return this.saveTiddler_mptw_instanttimestamp(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created);
}
// you can override these in StyleSheet
setStylesheet(".ts,.ds { font-style:italic; }","instantTimestampStyles");
//}}}
Question: What's the difference between Intelligence and Stupidity?
Answer: Intelligence has limits.
Intelligence indicates how smart a character is. It allows you to earn [[Experience]] Points at a greater rate and is also a prime requisite for [[Magic]] ability.
Syntax:
*inventory [<object>] [full]
*inv [<object>] [full]
Gives a list of all the items the character is carrying. You can also get the contents of a container if it is unlocked and you are carrying it, with this command. (See also "[[Look]] in container")
The additional parameter "full" will give a breakdown of the weights and additional information.
You can eat almost anything without becoming ill.
A Jeweller can make Rings, Necklaces, and other more interesting items.
You are trained in the use of weapons from the Knives group, having been instructed in effective manoeuvrers and practised enough to execute them reliably. Familiarity with the skill lets you use weapons from the group at the base chance. Additional levels increase your chances to use the weapons offensively and defensively. Just for clarity Daggers are classed under the Knives skill.
Syntax:
*learn <skill name>
*learn <spell name>
You can learn Skills from a teacher. You will need to find out where they are and ask them if they would be willing to teach you. Spells hpowever may be learnt from either a teacher or from spell books.
Learning a skill or spell will cost 1 [[Character Point|Character Points]]. Once a skill/spell has been learned you may [[Practice]] it up to and including level 4. To progress beyond that point you may only practice in the presence of a Master or Grand Master.
Able to make shoes, belts, bags and pouches. As well as Leather Armour and Leather Helms.
/***
|Name:|LessBackupsPlugin|
|Description:|Intelligently limit the number of backup files you create|
|Version:|3.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#LessBackupsPlugin|
|Author:|Simon Baird|
|Email:|simon.baird@gmail.com|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Description
You end up with just backup one per year, per month, per weekday, per hour, minute, and second. So total number won't exceed about 200 or so. Can be reduced by commenting out the seconds/minutes/hours line from modes array
!!Notes
Works in IE and Firefox only. Algorithm by Daniel Baird. IE specific code by by Saq Imtiaz.
***/
//{{{
var MINS = 60 * 1000;
var HOURS = 60 * MINS;
var DAYS = 24 * HOURS;
if (!config.lessBackups) {
config.lessBackups = {
// comment out the ones you don't want or set config.lessBackups.modes in your 'tweaks' plugin
modes: [
//["YYYY", 365*DAYS], // one per year for ever
//["MMM", 31*DAYS], // one per month
//["ddd", 7*DAYS], // one per weekday
//["d0DD", 1*DAYS], // one per day of month
//["h0hh", 24*HOURS], // one per hour
//["m0mm", 1*HOURS], // one per minute
//["s0ss", 1*MINS], // one per second
["latest",0] // always keep last version. (leave this).
]
};
}
window.getSpecialBackupPath = function(backupPath) {
var now = new Date();
var modes = config.lessBackups.modes;
for (var i=0;i<modes.length;i++) {
// the filename we will try
var specialBackupPath = backupPath.replace(/(\.)([0-9]+\.[0-9]+)(\.html)$/,
'$1'+now.formatString(modes[i][0]).toLowerCase()+'$3')
// open the file
try {
if (config.browser.isIE) {
var fsobject = new ActiveXObject("Scripting.FileSystemObject")
var fileExists = fsobject.FileExists(specialBackupPath);
if (fileExists) {
var fileObject = fsobject.GetFile(specialBackupPath);
var modDate = new Date(fileObject.DateLastModified).valueOf();
}
}
else {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(specialBackupPath);
var fileExists = file.exists();
if (fileExists) {
var modDate = file.lastModifiedTime;
}
}
}
catch(e) {
// give up
return backupPath;
}
// expiry is used to tell if it's an 'old' one. Eg, if the month is June and there is a
// June file on disk that's more than an month old then it must be stale so overwrite
// note that "latest" should be always written because the expiration period is zero (see above)
var expiry = new Date(modDate + modes[i][1]);
if (!fileExists || now > expiry)
return specialBackupPath;
}
}
// hijack the core function
window.getBackupPath_mptw_orig = window.getBackupPath;
window.getBackupPath = function(localPath) {
return getSpecialBackupPath(getBackupPath_mptw_orig(localPath));
}
//}}}
Syntax:
*list
*list all
*list <player>
This allows you to see what is available from the various shops and [[Merchant Statues|Merchant]]. From there you can [[Buy]] or [[Sell]] as appropriate.
There are many fixed locations within the game. You will automatically start all new [[Characters]] at the [[Adventurers Guild]]. The majority of the main locations are based around the [[Central Market]].
Syntax:
*lock <object>
This allows you to lock closed doors and [[Containers]], providing they are lockable in the first place. You would need to be [[holding|Hold]] the appropriate key for the lock. You cannot [[Lock]] an [[Open]] door or [[Container|Containers]].
See also [[Unlock]], [[Open]] and [[Close]].
Syntax:
*look
*l (abbreviation)
*look <object or location>
*look [in <container>]
Gives you a description of your current location or an object of note. Looking at a container only shows you the description of the container. For example; "A small wooden chest". To see the contents of the container you have to "look in chest".
Syntax:
*loot <corpse>
When a monster (or indeed a NPC or player) is killed, they leave behind a corpse. After a killing, the corpse is locked for 10 minutes so that the one who made the killing can divest the corpse of any goodies it may contain. The corpse itself may be carried/taken away. If you have the necessary skill you could cook the corpse and turn it into food.
Corpses will rot over time. Eating rotten food is not advisable.
You have an intuitive knowledge of rare lore and esoterica. You are able to identify things far more easily.
You know the ins and outs of the forest and are able to cut down the finest woods necessary for producing usable timber. The knowledge of this skill also helps you when learning the [[Carpenter]] trade.
The Mage skill enhances your [[magical|Magic]] capabilities.
Your Mage skill determines how well you can [[Cast]] spells, as well as how many Spells you are able to handle. The number of Spells you can learn is limited to ([[Intelligence]] + [[Mage]]^2) /3.
Each Spell has a base difficulty level. However as the caster gets more experienced in casting the spell, it gets easier and with better results. Like [[Skills]], [[Spells]] also can be improved with Character Points.
Once you learn Mage 4 you may then progress to becoming a [[Sorcerer]] and be able to make your own magic items.
Any player may use magic items. Any player may learn magic [[spells|Spells]]. But only a true [[Mage]] may become great at it.
You understand the mechanics of [[Magic]] better than most. The [[Mana]] cost of any spells you cast is reduced.
Your overall amount of [[Mana]] that you have is increased.
[[Home|TinyFantasy]]
[[Game]]
[[Characters]]
[[Commands]]
You really have a knack for making things. You gain a bonus when learning any [[Artisan Skills]].
Mana is the character's [[Magic]] ability. Each [[casting|Cast]] of a spell uses up some of your Mana. The initial Mana of any given character is based on the ability to memorise and understand spells, as well as that physical endurance ability which the spell takes on the caster. As such the default starting value is ([[Intelligence]]*2 + [[Constitution]])/3 + ([[Mage]] skill + [[Sorcerer]] skill)*2.
Mana is displayed as the maximum amount available and the current amount available to the character. When casting a [[spell|Spells]] a character uses up Mana and [[Fatigue]] in equal amounts depending on the difficulty of the spell, and the casters [[Magical|Mage]] ability.
Mana used may be recovered at the character's recovery rate. The base amount for this is 1 point per hour, but this may be modified by magical items and Potions.
You have been trained in mysterious arts which allow you to fight more effectively in [[combat|Combat]]. Martial Arts skills gives you a faster response time which improves your [[initiative|Initiative]] and allows you to inflict more damage with your bare hands or weapons.
It also allows you to be able to "roll with the blow" and take less damage. Each time you are hit 1D6 is rolled. If the result is less than or equal to your Martial Arts skill, then that amount is subtracted from the damage done.
There are however some limitations to being able to use this skill. Firstly it requires an [[Intelligence]] of 10 (reflecting the tremendous self discipline required) and a [[Dexterity]] of 10 to be able to learn it.
Secondly the more armour you are wearing then the less effective it becomes. You cannot perform Martial Arts successfully in full Plate Armour. For every 2 points or armour worn, 1 point is subtracted from your Martial Arts skill.
Syntax:
*merchant
*merchant buy [<number>] <object> for <price>
*merchant sell [<number>] <object> for <price>
*merchant remove <n>
If you disconnect from the game holding a [[Merchant Bag]] then your character will immediately be turned into a stone statue for all to see offering your wares up for sale, or allowing you to buy items.
The Merchant command allows you to set buy or sell prices for items. Another player may [[Buy]] or [[Sell]] those items. Only items that are in the Bag are sold, and only coins in the bag are used to pay for items you want to purchase. When you run out of stock or you have no money left to pay for items that you want, then your statue will crumble to the ground and no longer be visible. When you re-connect all the items you bought, or the money from items you sold will be in the [[Merchant Bag]].
The Merchant command only works on the bag that you are currently holding. You can have multiple bags and switch between them as necessary. But only one of them can be used in Merchant Mode.
Merchant Statues are only allowed in select areas of the game, and even then, only in limited numbers.
The 'merchant' command on it's own will show what you have current setup for sale or purchase. There is a limit on how many items you can sell or purchase and the maximum size of those items, based on the size of your bag.
The command 'merchant remove n' will remove that numbered entry from the Merchant list.
Example:
*sell 10 Small Silver Daggers for 15
This will sell a maximum of 10 Small Silver Daggers for 15 coins each. If you only have 6 daggers in your [[Merchant Bag]] then only 6 will be available for sale.
*buy 1000 Herb Flowers for 1
If you only have 15 gold in your bag then only 15 will be listed. If you sell a Silver Dagger then it will show 25 flowers wanted.
By carefully balancing your buy and sell entries you should be able to end up with a decent profit and sales whilst you are away.
Be aware about [[weight|Weight]] limits though. If you offer to buy a large number of items and you successfully purchase them whilst away, then when you return you may suddenly find yourself so overburdened that you cannot move.
If you disconnect from the game whilst [[holding|Hold]] a Merchant Bag then you will immediately be turned into a Stone Statue. Merchant Bags come in various sizes ranging from Tiny up to Huge.
[[Merchant]] positions are only allowed in select areas within the game. This makes it easier for people to find the locations and make sales.
To use a Merchant Bag, simply [[Hold]] the bag before you [[Quit]] the game.
You have a knowledge of ores and of how to dig and shore up mine shafts. Knowledge of this is vital to extracting valuable metals from the ground and how to refine it to produce useable materials.
Name: MptwBlue
Background: #fff
Foreground: #000
PrimaryPale: #cdf
PrimaryLight: #57c
PrimaryMid: #114
PrimaryDark: #012
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
//{{{
// Pretty sure this is incomplete and experimental
// TODO: Fix it or remove it.
(function($){
merge(config.macros,{
mptwCollapse: {
handler: function(place,macroName,params) {
createTiddlyButton(place, params[0] == '+' ? '\u25AD' : '\u25AC', 'collapse/uncollapse', function(){
$(story.findContainingTiddler(place)).toggleClass('collapsed');
});
}
}
});
/* this doesn't work unless you have a modified ViewTempate */
config.shadowTiddlers["MptwCollapsePluginStyles"] = ""
+".collapsed .uncollapsedView { display:none; }"
+".collapsedView { display:none; }"
+".collapsed .collapsedView { display:block; }"
+".tiddler.collapsed { padding-bottom:1em; }"
+".tiddler.collapsed .title { font-size:100%; }"
;
store.addNotification("MptwCollapsePluginStyles",refreshStyles);
})(jQuery);
//}}}
/***
|Name:|MptwConfigPlugin|
|Description:|Miscellaneous tweaks used by MPTW|
|Version:|1.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#MptwConfigPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#MptwConfigPlugin|
!!Note: instead of editing this you should put overrides in MptwUserConfigPlugin
***/
//{{{
var originalReadOnly = readOnly;
var originalShowBackstage = showBackstage;
config.options.chkHttpReadOnly = false; // means web visitors can experiment with your site by clicking edit
readOnly = false; // needed because the above doesn't work any more post 2.1 (??)
showBackstage = true; // show backstage for same reason
config.options.chkInsertTabs = true; // tab inserts a tab when editing a tiddler
config.views.wikified.defaultText = ""; // don't need message when a tiddler doesn't exist
config.views.editor.defaultText = ""; // don't need message when creating a new tiddler
config.options.chkSaveBackups = true; // do save backups
config.options.txtBackupFolder = 'twbackup'; // put backups in a backups folder
config.options.chkAutoSave = (window.location.protocol == "file:"); // do autosave if we're in local file
config.mptwVersion = "2.7.3";
config.macros.mptwVersion={handler:function(place){wikify(config.mptwVersion,place);}};
if (config.options.txtTheme == '')
config.options.txtTheme = 'MptwTheme';
// add to default GettingStarted
config.shadowTiddlers.GettingStarted += "\n\nSee also [[MPTW]].";
// add select theme and palette controls in default OptionsPanel
config.shadowTiddlers.OptionsPanel = config.shadowTiddlers.OptionsPanel.replace(/(\n\-\-\-\-\nAlso see \[\[AdvancedOptions\]\])/, "{{select{<<selectTheme>>\n<<selectPalette>>}}}$1");
// these are used by ViewTemplate
config.mptwDateFormat = 'DD/MM/YY';
config.mptwJournalFormat = 'Journal DD/MM/YY';
//}}}
Name: MptwBlue
Background: #fff
Foreground: #000
PrimaryPale: #dcf
PrimaryLight: #66c
PrimaryMid: #214
PrimaryDark: #102
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
Name: MptwSmoke
Background: #fff
Foreground: #000
PrimaryPale: #aaa
PrimaryLight: #777
PrimaryMid: #111
PrimaryDark: #000
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
|Name|~MptwStandard|
|Description|Mptw Theme with the default ~TiddlyWiki PageLayout and Styles|
|ViewTemplate|MptwTheme##ViewTemplate|
|EditTemplate|MptwTheme##EditTemplate|
|Name|MptwTheme|
|Description|Mptw Theme including custom PageLayout|
|PageTemplate|##PageTemplate|
|ViewTemplate|##ViewTemplate|
|EditTemplate|##EditTemplate|
|StyleSheet|##StyleSheet|
http://mptw.tiddlyspot.com/#MptwTheme ($Rev: 1829 $)
!PageTemplate
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<!-- horizontal MainMenu -->
<div id='topMenu' refresh='content' tiddler='MainMenu'></div>
<!-- original MainMenu menu -->
<!-- <div id='mainMenu' refresh='content' tiddler='MainMenu'></div> -->
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
!ViewTemplate
<!--{{{-->
<div class="uncollapsedView">
[[MptwTheme##ViewTemplateToolbar]]
<div class="tagglyTagged" macro="tags"></div>
<div class='titleContainer'>
<span class='title' macro='view title'></span>
<span macro="miniTag"></span>
</div>
<div class='subtitle'>
(updated <span macro='view modified date {{config.mptwDateFormat?config.mptwDateFormat:"MM/0DD/YY"}}'></span>
by <span macro='view modifier link'></span>)
<!--
(<span macro='message views.wikified.createdPrompt'></span>
<span macro='view created date {{config.mptwDateFormat?config.mptwDateFormat:"MM/0DD/YY"}}'></span>)
-->
</div>
<div macro="showWhen tiddler.tags.containsAny(['css','html','pre','systemConfig']) && !tiddler.text.match('{{'+'{')">
<div class='viewer'><pre macro='view text'></pre></div>
</div>
<div macro="else">
<div class='viewer' macro='view text wikified'></div>
</div>
<div class="tagglyTagging" macro="tagglyTagging"></div>
</div>
<div class="collapsedView">
<span class='toolbar'>
<span macro='toolbar closeTiddler'></span>
<span macro='mptwCollapse +'></span>
</span>
<span class='title' macro='view title'></span>
</div>
<!--}}}-->
!ViewTemplateToolbar
<!--{{{-->
<div class='toolbar'>
<span macro="showWhenTagged systemConfig">
<span macro="toggleTag systemConfigDisable . '[[disable|systemConfigDisable]]'"></span>
</span>
<span macro="showWhenTagged systemTheme"><span macro="applyTheme"></span></span>
<span macro="showWhenTagged systemPalette"><span macro="applyPalette"></span></span>
<span macro="showWhen tiddler.tags.contains('css') || tiddler.title == 'StyleSheet'"><span macro="refreshAll"></span></span>
<span style="padding:1em;"></span>
<span macro='toolbar closeTiddler closeOthers +editTiddler deleteTiddler > fields syncing permalink references jump'></span>
<!--span macro='mptwCollapse -'></span-->
</div>
<!--}}}-->
!EditTemplate
<!--{{{-->
<div class="toolbar" macro="toolbar +saveTiddler saveCloseTiddler closeOthers -cancelTiddler cancelCloseTiddler deleteTiddler"></div>
<div class="title" macro="view title"></div>
<div class="editLabel">Title</div><div class="editor" macro="edit title"></div>
<div macro='annotations'></div>
<div class="editLabel">Content</div><div class="editor" macro="edit text"></div>
<div class="editLabel">Tags</div><div class="editor" macro="edit tags"></div>
<div class="editorFooter"><span macro="message views.editor.tagPrompt"></span><span macro="tagChooser"></span></div>
<!--}}}-->
!StyleSheet
/*{{{*/
/* a contrasting background so I can see where one tiddler ends and the other begins */
body {
background: [[ColorPalette::TertiaryLight]];
}
/* sexy colours and font for the header */
.headerForeground {
color: [[ColorPalette::PrimaryPale]];
}
.headerShadow, .headerShadow a {
color: [[ColorPalette::PrimaryMid]];
}
/* separate the top menu parts */
.headerForeground, .headerShadow {
padding: 1em 1em 0;
}
.headerForeground, .headerShadow {
font-family: 'Trebuchet MS', sans-serif;
font-weight:bold;
}
.headerForeground .siteSubtitle {
color: [[ColorPalette::PrimaryLight]];
}
.headerShadow .siteSubtitle {
color: [[ColorPalette::PrimaryMid]];
}
/* make shadow go and down right instead of up and left */
.headerShadow {
left: 1px;
top: 1px;
}
/* prefer monospace for editing */
.editor textarea, .editor input {
font-family: 'Consolas', monospace;
background-color:[[ColorPalette::TertiaryPale]];
}
/* sexy tiddler titles */
.title {
font-size: 250%;
color: [[ColorPalette::PrimaryLight]];
font-family: 'Trebuchet MS', sans-serif;
}
/* more subtle tiddler subtitle */
.subtitle {
padding:0px;
margin:0px;
padding-left:1em;
font-size: 90%;
color: [[ColorPalette::TertiaryMid]];
}
.subtitle .tiddlyLink {
color: [[ColorPalette::TertiaryMid]];
}
/* a little bit of extra whitespace */
.viewer {
padding-bottom:3px;
}
/* don't want any background color for headings */
h1,h2,h3,h4,h5,h6 {
background-color: transparent;
color: [[ColorPalette::Foreground]];
}
/* give tiddlers 3d style border and explicit background */
.tiddler {
background: [[ColorPalette::Background]];
border-right: 2px [[ColorPalette::TertiaryMid]] solid;
border-bottom: 2px [[ColorPalette::TertiaryMid]] solid;
margin-bottom: 1em;
padding:1em 2em 2em 1.5em;
}
/* make options slider look nicer */
#sidebarOptions .sliderPanel {
border:solid 1px [[ColorPalette::PrimaryLight]];
}
/* the borders look wrong with the body background */
#sidebar .button {
border-style: none;
}
/* this means you can put line breaks in SidebarOptions for readability */
#sidebarOptions br {
display:none;
}
/* undo the above in OptionsPanel */
#sidebarOptions .sliderPanel br {
display:inline;
}
/* horizontal main menu stuff */
#displayArea {
margin: 1em 15.7em 0em 1em; /* use the freed up space */
}
#topMenu br {
display: none;
}
#topMenu {
background: [[ColorPalette::PrimaryMid]];
color:[[ColorPalette::PrimaryPale]];
}
#topMenu {
padding:2px;
}
#topMenu .button, #topMenu .tiddlyLink, #topMenu a {
margin-left: 0.5em;
margin-right: 0.5em;
padding-left: 3px;
padding-right: 3px;
color: [[ColorPalette::PrimaryPale]];
font-size: 115%;
}
#topMenu .button:hover, #topMenu .tiddlyLink:hover {
background: [[ColorPalette::PrimaryDark]];
}
/* make 2.2 act like 2.1 with the invisible buttons */
.toolbar {
visibility:hidden;
}
.selected .toolbar {
visibility:visible;
}
/* experimental. this is a little borked in IE7 with the button
* borders but worth it I think for the extra screen realestate */
.toolbar { float:right; }
/* fix for TaggerPlugin. from sb56637. improved by FND */
.popup li .tagger a {
display:inline;
}
/* makes theme selector look a little better */
#sidebarOptions .sliderPanel .select .button {
padding:0.5em;
display:block;
}
#sidebarOptions .sliderPanel .select br {
display:none;
}
/* make it print a little cleaner */
@media print {
#topMenu {
display: none ! important;
}
/* not sure if we need all the importants */
.tiddler {
border-style: none ! important;
margin:0px ! important;
padding:0px ! important;
padding-bottom:2em ! important;
}
.tagglyTagging .button, .tagglyTagging .hidebutton {
display: none ! important;
}
.headerShadow {
visibility: hidden ! important;
}
.tagglyTagged .quickopentag, .tagged .quickopentag {
border-style: none ! important;
}
.quickopentag a.button, .miniTag {
display: none ! important;
}
}
/* get user styles specified in StyleSheet */
[[StyleSheet]]
/*}}}*/
/***
|Description:|A place to put your config tweaks so they aren't overwritten when you upgrade MPTW|
See http://www.tiddlywiki.org/wiki/Configuration_Options for other options you can set. In some cases where there are clashes with other plugins it might help to rename this to zzMptwUserConfigPlugin so it gets executed last.
***/
//{{{
// example: set your preferred date format
//config.mptwDateFormat = 'MM/0DD/YY';
//config.mptwJournalFormat = 'Journal MM/0DD/YY';
// example: set the theme you want to start with
//config.options.txtTheme = 'MptwRoundTheme';
// example: switch off autosave, switch on backups and set a backup folder
//config.options.chkSaveBackups = true;
//config.options.chkAutoSave = false;
//config.options.txtBackupFolder = 'backups';
// uncomment to disable 'new means new' functionality for the new journal macro
//config.newMeansNewForJournalsToo = false;
//}}}
NPCs are ~Non-Player Characters and are part of the game's infrastructure. They may own shops or run markets, and taverns etc. where you can [[Buy]] or [[Sell]] resources and items. They can also impart wisdom for those who speak to them, or can assign you quests. They are powered by a minimal AI system which is triggered by keywords. But they can also remember some interactions. They can be fought and killed, like players and monsters. But if this happens then the shops that they run are closed down and some quests may be terminated. Also you'd get a pretty hefty negative [[Experience]] penalty. But then again, most shop owners have insurance in the form of magical protection, so it may not be worth attacking them in the first place.
Most of them are static and remain in one place, but a few are mobile and can wander about. A few are veritably hostile and may just try and take your head off if they don't like you.
/***
|Name:|NewHerePlugin|
|Description:|Creates the new here and new journal macros|
|Version:|3.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#NewHerePlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
merge(config.macros, {
newHere: {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
wikify("<<newTiddler "+paramString+" tag:[["+tiddler.title+"]]>>",place,null,tiddler);
}
},
newJournalHere: {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
wikify("<<newJournal "+paramString+" tag:[["+tiddler.title+"]]>>",place,null,tiddler);
}
}
});
//}}}
/***
|Name:|NewMeansNewPlugin|
|Description:|If 'New Tiddler' already exists then create 'New Tiddler (1)' and so on|
|Version:|1.1.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/empty.html#NewMeansNewPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Note: I think this should be in the core
***/
//{{{
// change this or set config.newMeansNewForJournalsToo it in MptwUuserConfigPlugin
if (config.newMeansNewForJournalsToo == undefined) config.newMeansNewForJournalsToo = true;
String.prototype.getNextFreeName = function() {
numberRegExp = / \(([0-9]+)\)$/;
var match = numberRegExp.exec(this);
if (match) {
var num = parseInt(match[1]) + 1;
return this.replace(numberRegExp," ("+num+")");
}
else {
return this + " (1)";
}
}
config.macros.newTiddler.checkForUnsaved = function(newName) {
var r = false;
story.forEachTiddler(function(title,element) {
if (title == newName)
r = true;
});
return r;
}
config.macros.newTiddler.getName = function(newName) {
while (store.getTiddler(newName) || config.macros.newTiddler.checkForUnsaved(newName))
newName = newName.getNextFreeName();
return newName;
}
config.macros.newTiddler.onClickNewTiddler = function()
{
var title = this.getAttribute("newTitle");
if(this.getAttribute("isJournal") == "true") {
title = new Date().formatString(title.trim());
}
// ---- these three lines should be the only difference between this and the core onClickNewTiddler
if (config.newMeansNewForJournalsToo || this.getAttribute("isJournal") != "true")
title = config.macros.newTiddler.getName(title);
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").value = text.format([title]);
for(var t=0;t<tags.length;t++)
story.setTiddlerTag(title,tags[t],+1);
story.focusTiddler(title,focus);
return false;
};
//}}}
Syntax:
*nodef
*nodefence
You're not bothering to defend yourself and going for all out attack. This gives you an additional bonus to hit your opponent. But of course, it may also let them hit you.
!!Description
[>img[./images/Ogre.jpg]]Ogres are HUGE in size, dwarfing almost every other race. Their bulky frames are built to withstand nearly anything: ogres can resist even the most extreme heat or cold, and damage that might cripple an ordinary human would only show as scratches on this extremely sturdy race. Due to their incredibly massive builds ogres are not exactly light on their feet; in fact they are very clumsy and not well suited to any activities requiring dexterity. Ogres are also the stupidest race and this single-mindedness tends to aid them in combat as their battle focus is an innate skill waiting only to be trained. Ogres also have innate skill in dishing out enhanced damage and flattening foes via bash, once they have received proper instruction on these abilities.
!!Stats
|Size|Large |
|Strength|4D6|
|~|Min 4, Avg 14, Max 24 |
|Dexterity|2D6|
|~|Min 2, Avg 7, Max 12 |
|Constitution|4D6|
|~|Min 4, Avg 14, Max 24 |
|Intelligence|2D6|
|~|Min 2, Avg 7, Max 12 |
|Special|Bonuses when learning [[Toughness]], [[Endurance]] and [[Brawling]] skills. [[Toughness]] automatically starts at level 3. Penalty when learning [[Magic]]. |
Syntax:
*open <object>
Allows you to open a door or an openable [[Container|Containers]]. Only an open door can allow you to pass and you can only put items into or take them out of open [[Containers]].
See also [[Lock]], [[Unlock]] and [[Close]]
Syntax:
*parry
Parry sets your defence mode to Parry: you attempt to avoid attacks by deflecting them with your weapon. You must have an equipped weapon in order to parry. Players who have the [[Fencing]] skill and are using fencing weapons receive an additional bonus when parrying. Not all weapons may be used to parry.
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.10 (2011-05-23)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts.
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features"). E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.
''Syntax:''
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any tiddler content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.
Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations".
Wrap every citation with a part and a proper name.
''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.//
in //Proc. ICSM//, 1998.</part>
<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.//
Thesis, Uni Stuttgart, 2002.</part>
<part DUC99>Ducasse, St�fane et al: //A Language Independent Approach for Detecting Duplicated Code.//
in //Proc. ICSM//, 1999.</part>
}}}
You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.
Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.
''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|
<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}
Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".
BTW: The same approach can be used to create bullet lists with items that contain more than one line.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.
With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.
''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline
Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab
Timeline Timeline SideBarTabs/Timeline
All 'All tiddlers' SideBarTabs/All
Tags 'All tags' SideBarTabs/Tags
More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab
Missing 'Missing tiddlers' SideBarTabs/Missing
Orphans 'Orphaned tiddlers' SideBarTabs/Orphans
Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}
Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.
E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler
sortBy 'tiddler.modified' descending
write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature
''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}
Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Revision history<html><a name="Revisions"/></html>
* v1.0.10 (2011-05-23)
** Adapt to TW 2.6.2 default behaviour when existing tiddlers are opened (don't select text) and fixed Firefox 4 issue. Thanks to dave for reporting the issue.
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivi�re for reporting the issue.
** Support "./partName" syntax inside {{{<<tabs ...>>}}} macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to Jos� Luis Gonz�lez Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Code<html><a name="Code"/></html>
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
//{{{
//============================================================================
// PartTiddlerPlugin
// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {
version.extensions.PartTiddlerPlugin = {
major: 1, minor: 0, revision: 10,
date: new Date(2011, 4, 23),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};
if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");
//============================================================================
// Common Helpers
// Looks for the next newline, starting at the index-th char of text.
//
// If there are only whitespaces between index and the newline
// the index behind the newline is returned,
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
var re = /(\n|[^\s])/g;
re.lastIndex = index;
var result = re.exec(text);
return (result && text.charAt(result.index) == '\n')
? result.index+1
: index;
}
//============================================================================
// Constants
var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";
//============================================================================
// Plugin Specific Helpers
// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
var params = paramText.readMacroParams();
if (params.length == 0 || params[0].length == 0) return null;
var name = params[0];
var paramsIndex = 1;
var hidden = false;
if (paramsIndex < params.length) {
hidden = params[paramsIndex] == "hidden";
paramsIndex++;
}
return {
partName: name,
isHidden: hidden
};
}
// Returns the match to the next (end or start) part tag in the text,
// starting the search at startIndex.
//
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
var re = new RegExp(partEndOrStartTagRE);
re.lastIndex = startIndex;
var match = re.exec(text);
return match;
}
//============================================================================
// Formatter
// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
if (!tagMatch) return false;
if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;
// Parse the start tag parameters
var arguments = parseStartTagParams(tagMatch[3]);
if (!arguments) return false;
// Continue processing
var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
if (endMatch && endMatch[1]) {
if (!arguments.isHidden) {
w.nextMatch = startTagEndIndex;
w.subWikify(w.output,partEndTagREString);
}
w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
return true;
}
return false;
}
config.formatters.push( {
name: "part",
match: "<part\\s+[^>]+>",
handler: function(w) {
if (!handlePartSection(w)) {
w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
}
}
} )
//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers
// as tiddlers.
var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)
// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
var i = 0;
while (true) {
var tagMatch = findNextPartEndOrStartTagMatch(text, i);
if (!tagMatch) return null;
if (tagMatch[2]) {
// Is start tag
// Check the name
var arguments = parseStartTagParams(tagMatch[3]);
if (arguments && arguments.partName == partName) {
return tagMatch;
}
}
i = tagMatch.index+tagMatch[0].length;
}
}
// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler
// object, using fullName as the Tiddler's title.
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from
// the parentTiddler.
//
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
var text = parentTiddler.text;
var startTag = findPartStartTagByName(text, partName);
if (!startTag) return null;
var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);
if (indexOfEndTag >= 0) {
var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
var partTiddler = new Tiddler();
partTiddler.set(
fullName,
partTiddlerText,
parentTiddler.modifier,
parentTiddler.modified,
parentTiddler.tags,
parentTiddler.created);
partTiddler.abegoIsPartTiddler = true;
return partTiddler;
}
return null;
}
// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
var oldFetchTiddler = store.fetchTiddler ;
store.fetchTiddler = function(title) {
var result = oldFetchTiddler.apply(this, arguments);
if (!result && title) {
var i = title.lastIndexOf('/');
if (i > 0) {
var parentName = title.substring(0, i);
var partName = title.substring(i+1);
var parent = (parentName == ".")
? store.resolveTiddler(currentParent)
: oldFetchTiddler.apply(this, [parentName]);
if (parent) {
return getPart(parent, partName, parent.title+"/"+partName);
}
}
}
return result;
};
};
// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag.
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
var oldRestartFunc = restart;
window.restart = function() {
hijackFetchTiddler();
oldRestartFunc.apply(this,arguments);
};
} else
hijackFetchTiddler();
// The user must not edit a readOnly/partTiddler
//
config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
// Tiddler.isReadOnly was introduced with TW 2.0.6.
// For older version we explicitly check the global readOnly flag
if (config.commands.editTiddler.oldIsReadOnlyFunction) {
if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
} else {
if (readOnly) return true;
}
return this.abegoIsPartTiddler;
}
config.commands.editTiddler.handler_PartTiddlerPlugin = config.commands.editTiddler.handler;
config.commands.editTiddler.handler = function(event,src,title)
{
var t = store.getTiddler(title);
// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
// or the tiddler is not readOnly
if(!t || !t.abegoIsPartTiddler)
{
return config.commands.editTiddler.handler_PartTiddlerPlugin(event,src,title);
}
return false;
}
// To allow the "./partName" syntax in macros we need to hijack
// the invokeMacro to define the "currentParent" while it is running.
//
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
var oldCurrentParent = currentParent;
if (tiddler) currentParent = tiddler;
try {
oldInvokeMacro.apply(this, arguments);
} finally {
currentParent = oldCurrentParent;
}
}
window.invokeMacro = myInvokeMacro;
// To correctly support the "./partName" syntax while refreshing we need to hijack
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
//
(function() {
var oldTiddlerRefresher= config.refreshers.tiddler;
config.refreshers.tiddler = function(e,changeList) {
var oldCurrentParent = currentParent;
try {
currentParent = e.getAttribute("tiddler");
return oldTiddlerRefresher.apply(this,arguments);
} finally {
currentParent = oldCurrentParent;
}
};
})();
// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
var extendRelativeNames = function(e, title) {
var nodes = e.getElementsByTagName("a");
for(var i=0; i<nodes.length; i++) {
var node = nodes[i];
var s = node.getAttribute("content");
if (s && s.indexOf("./") == 0)
node.setAttribute("content",title+s.substr(1));
}
};
var oldHandler = config.macros.tabs.handler;
config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var result = oldHandler.apply(this,arguments);
if (tiddler)
extendRelativeNames(place, tiddler.title);
return result;
};
})();
// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
var tiddlerElem = null;
if (tiddler) {
tiddlerElem = document.getElementById(story.idPrefix + tiddler);
}
if (!tiddlerElem && evt) {
var target = resolveTarget(evt);
tiddlerElem = story.findContainingTiddler(target);
}
if (!tiddlerElem) return;
var children = tiddlerElem.getElementsByTagName("a");
for (var i = 0; i < children.length; i++) {
var child = children[i];
var name = child.getAttribute("name");
if (name == anchorName) {
var y = findPosY(child);
window.scrollTo(0,y);
return;
}
}
}
} // of "install only once"
//}}}
/***
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2011 ([[www.abego-software.de|http://www.abego-software.de]])
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
Improves your chance of spotting something. This skill affects your ability to [[Search]] for items as well as [[Examining|Examine]] something.
You've learned the nefarious art of removing money from other peoples pockets. A quick dip or a huge handful, either way you don't want to get caught. Especially not by the [[Watch]].
You are trained in the use of weapons from the Polearms group, having been instructed in effective manoeuvrers and practised enough to execute them reliably. Familiarity with the skill lets you use weapons from the group at the base chance. Additional levels increase your chances to use the weapons offensively and defensively.
Syntax:
* : <message>
*pose <message>
Poses a message to everyone in the room. This is used for actions. Ie: if your name was Igor, and you typed ': falls down.', everyone would see "Igor falls down.".
Also see: [[Chat]], [[Say]] and [[Whisper]].
Potions are crafted by an [[Alchemist]]. There are many potions, not all of them are listed here. They are also of varying quality as they are dependant upon the level of the Alchemist who made them.
Syntax:
*practice <skill name>
*practice <spell name>
Once learned a [[skill|Skills]] may be practiced upto level 4 no matter where you are. However to become a Master or Grand Master, you must practice with them to ensure that you have the proper knowledge.
If you have already acheived mastery then you may enable this by [[assessing|Assess]] other players.
Train is an alias for Practice and may be used instead if you wish.
/***
|Name:|PrettyDatesPlugin|
|Description:|Provides a new date format ('pppp') that displays times such as '2 days ago'|
|Version:|1.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#PrettyDatesPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Notes
* If you want to you can rename this plugin. :) Some suggestions: LastUpdatedPlugin, RelativeDatesPlugin, SmartDatesPlugin, SexyDatesPlugin.
* Inspired by http://ejohn.org/files/pretty.js
***/
//{{{
Date.prototype.prettyDate = function() {
var diff = (((new Date()).getTime() - this.getTime()) / 1000);
var day_diff = Math.floor(diff / 86400);
if (isNaN(day_diff)) return "";
else if (diff < 0) return "in the future";
else if (diff < 60) return "just now";
else if (diff < 120) return "1 minute ago";
else if (diff < 3600) return Math.floor(diff/60) + " minutes ago";
else if (diff < 7200) return "1 hour ago";
else if (diff < 86400) return Math.floor(diff/3600) + " hours ago";
else if (day_diff == 1) return "Yesterday";
else if (day_diff < 7) return day_diff + " days ago";
else if (day_diff < 14) return "a week ago";
else if (day_diff < 31) return Math.ceil(day_diff/7) + " weeks ago";
else if (day_diff < 62) return "a month ago";
else if (day_diff < 365) return "about " + Math.ceil(day_diff/31) + " months ago";
else if (day_diff < 730) return "a year ago";
else return Math.ceil(day_diff/365) + " years ago";
}
Date.prototype.formatString_orig_mptw = Date.prototype.formatString;
Date.prototype.formatString = function(template) {
return this.formatString_orig_mptw(template).replace(/pppp/,this.prettyDate());
}
// for MPTW. otherwise edit your ViewTemplate as required.
config.mptwDateFormat = 'pppp (DD/MM/YY)';
// config.mptwDateFormat = 'pppp';
//}}}
Primary Stats are based on the physical build of the character. [[Secondary Stats]] are calculated at a base level from the primary ones. It is extremely hard to raise the Primary Stats after character creation. However they may be raised temporarily by additional equipment or spells.
<<forEachTiddler
where
'tiddler.tags.contains("Race")'
sortBy
'tiddler.title'
write
'"|[["+tiddler.title+
"]] | " + store.getTiddlerSlice(tiddler.title,"Strength") +
" | " + store.getTiddlerSlice(tiddler.title,"Dexterity") +
" | " + store.getTiddlerSlice(tiddler.title,"Constitution") +
" | " + store.getTiddlerSlice(tiddler.title,"Intelligence") +
" |\n" '
begin
'"|![[Race]] | ![[Strength]] | ![[Dexterity]] | ![[Constitution]] | ![[Intelligence]] |\n"'
>>
Syntax:
* [[cast|Cast]] purge
* [[cast|Cast]] purge on|at|vs <target>
Cures the target of Poison effects.
Base Mana Cost: 5
Syntax:
*Purse
Tells you how much money you have available.
You purse is where you store your money. It is assumed to be tucked inside your clothing or firmly fastened to your belt or somewhere safe on your person.
Coins that you pick up or when you sell something are automatically added to your purse. When you buy something the money is taken from your purse.
Only the most skilled of pickpockets could ever get anywhere near it and it is extremely likely you'll notice if they do.
Syntax:
*put <object> in|on <container>
Attempts to put the named <object> into the [[<container>|Containers]]. The objects must be of the required size. All objects have a [[weight|Cobs]] which reflects how much it weighs and how large it is. All containers have a maximum weight limit, and a maximum size of an object which may be placed inside it. You cannot put a Great Sword into a small Gem Pouch.
/***
|Name:|QuickOpenTagPlugin|
|Description:|Changes tag links to make it easier to open tags as tiddlers|
|Version:|3.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#QuickOpenTagPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
***/
//{{{
config.quickOpenTag = {
dropdownChar: (document.all ? "\u25bc" : "\u25be"), // the little one doesn't work in IE?
createTagButton: function(place,tag,excludeTiddler) {
// little hack so we can do this: <<tag PrettyTagName|RealTagName>>
var splitTag = tag.split("|");
var pretty = tag;
if (splitTag.length == 2) {
tag = splitTag[1];
pretty = splitTag[0];
}
var sp = createTiddlyElement(place,"span",null,"quickopentag");
createTiddlyText(createTiddlyLink(sp,tag,false),pretty);
var theTag = createTiddlyButton(sp,config.quickOpenTag.dropdownChar,
config.views.wikified.tag.tooltip.format([tag]),onClickTag);
theTag.setAttribute("tag",tag);
if (excludeTiddler)
theTag.setAttribute("tiddler",excludeTiddler);
return(theTag);
},
miniTagHandler: function(place,macroName,params,wikifier,paramString,tiddler) {
var tagged = store.getTaggedTiddlers(tiddler.title);
if (tagged.length > 0) {
var theTag = createTiddlyButton(place,config.quickOpenTag.dropdownChar,
config.views.wikified.tag.tooltip.format([tiddler.title]),onClickTag);
theTag.setAttribute("tag",tiddler.title);
theTag.className = "miniTag";
}
},
allTagsHandler: function(place,macroName,params) {
var tags = store.getTags(params[0]);
var filter = params[1]; // new feature
var ul = createTiddlyElement(place,"ul");
if(tags.length == 0)
createTiddlyElement(ul,"li",null,"listTitle",this.noTags);
for(var t=0; t<tags.length; t++) {
var title = tags[t][0];
if (!filter || (title.match(new RegExp('^'+filter)))) {
var info = getTiddlyLinkInfo(title);
var theListItem =createTiddlyElement(ul,"li");
var theLink = createTiddlyLink(theListItem,tags[t][0],true);
var theCount = " (" + tags[t][1] + ")";
theLink.appendChild(document.createTextNode(theCount));
var theDropDownBtn = createTiddlyButton(theListItem," " +
config.quickOpenTag.dropdownChar,this.tooltip.format([tags[t][0]]),onClickTag);
theDropDownBtn.setAttribute("tag",tags[t][0]);
}
}
},
// todo fix these up a bit
styles: [
"/*{{{*/",
"/* created by QuickOpenTagPlugin */",
".tagglyTagged .quickopentag, .tagged .quickopentag ",
" { margin-right:1.2em; border:1px solid #eee; padding:2px; padding-right:0px; padding-left:1px; }",
".quickopentag .tiddlyLink { padding:2px; padding-left:3px; }",
".quickopentag a.button { padding:1px; padding-left:2px; padding-right:2px;}",
"/* extra specificity to make it work right */",
"#displayArea .viewer .quickopentag a.button, ",
"#displayArea .viewer .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink, ",
"#mainMenu .quickopentag a.tiddyLink ",
" { border:0px solid black; }",
"#displayArea .viewer .quickopentag a.button, ",
"#mainMenu .quickopentag a.button ",
" { margin-left:0px; padding-left:2px; }",
"#displayArea .viewer .quickopentag a.tiddlyLink, ",
"#mainMenu .quickopentag a.tiddlyLink ",
" { margin-right:0px; padding-right:0px; padding-left:0px; margin-left:0px; }",
"a.miniTag {font-size:150%;} ",
"#mainMenu .quickopentag a.button ",
" /* looks better in right justified main menus */",
" { margin-left:0px; padding-left:2px; margin-right:0px; padding-right:0px; }",
"#topMenu .quickopentag { padding:0px; margin:0px; border:0px; }",
"#topMenu .quickopentag .tiddlyLink { padding-right:1px; margin-right:0px; }",
"#topMenu .quickopentag .button { padding-left:1px; margin-left:0px; border:0px; }",
"/*}}}*/",
""].join("\n"),
init: function() {
// we fully replace these builtins. can't hijack them easily
window.createTagButton = this.createTagButton;
config.macros.allTags.handler = this.allTagsHandler;
config.macros.miniTag = { handler: this.miniTagHandler };
config.shadowTiddlers["QuickOpenTagStyles"] = this.styles;
store.addNotification("QuickOpenTagStyles",refreshStyles);
}
}
config.quickOpenTag.init();
//}}}
Syntax:
*QUIT
This command must be entered in CAPITAL LETTERS. It logs out your character and disconnect you from the game. If you disconnect whilst in [[Combat]] then it is very likely that you will die.
There are many Player Races in the Game. Others are too rare or unique to be given to players.
!Known Races
<<forEachTiddler
where
'tiddler.tags.contains("Race")'
sortBy
'tiddler.title'
write
'"|[["+tiddler.title+
"]] | " + store.getTiddlerSlice(tiddler.title,"Size") +
" | " + store.getTiddlerSlice(tiddler.title,"Strength") +
" | " + store.getTiddlerSlice(tiddler.title,"Dexterity") +
" | " + store.getTiddlerSlice(tiddler.title,"Constitution") +
" | " + store.getTiddlerSlice(tiddler.title,"Intelligence") +
" |" + store.getTiddlerSlice(tiddler.title,"Special") +
" |\n" '
begin
'"|![[Race]] | ![[Size]] | ![[Strength]] | ![[Dexterity]] | ![[Constitution]] | ![[Intelligence]] | !Special Ability |\n"'
>>
Races that have Bonuses for specific [[Skills]] automatically start at level 1 and do not need to learn that skill.
Syntax:
*Reject
*Reject all
*Reject from <character>
Refuses offered items from a [[Give]] command. The "all" option means that you'll never accept items given to you from anybody. If a character is named then that character may never give you items. This cancels an [[Accept]] from a character.
By default "Reject all" is automatically enabled when you first play.
Syntax:
*remove <item>
This command allows you to take off an equipped/worn item, for example a helmet or ring. It places the item in your normal inventory.
Certain items can only be removed in a particular order. For example, you cannot remove a Ring whist wearing Gloves.
/***
|Name:|RenameTagsPlugin|
|Description:|Allows you to easily rename or delete tags across multiple tiddlers|
|Version:|3.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#RenameTagsPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License|http://mptw.tiddlyspot.com/#TheBSDLicense|
Rename a tag and you will be prompted to rename it in all its tagged tiddlers.
***/
//{{{
config.renameTags = {
prompts: {
rename: "Rename the tag '%0' to '%1' in %2 tidder%3?",
remove: "Remove the tag '%0' from %1 tidder%2?"
},
removeTag: function(tag,tiddlers) {
store.suspendNotifications();
for (var i=0;i<tiddlers.length;i++) {
store.setTiddlerTag(tiddlers[i].title,false,tag);
}
store.resumeNotifications();
store.notifyAll();
},
renameTag: function(oldTag,newTag,tiddlers) {
store.suspendNotifications();
for (var i=0;i<tiddlers.length;i++) {
store.setTiddlerTag(tiddlers[i].title,false,oldTag); // remove old
store.setTiddlerTag(tiddlers[i].title,true,newTag); // add new
}
store.resumeNotifications();
store.notifyAll();
},
storeMethods: {
saveTiddler_orig_renameTags: TiddlyWiki.prototype.saveTiddler,
saveTiddler: function(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator) {
if (title != newTitle) {
var tagged = this.getTaggedTiddlers(title);
if (tagged.length > 0) {
// then we are renaming a tag
if (confirm(config.renameTags.prompts.rename.format([title,newTitle,tagged.length,tagged.length>1?"s":""])))
config.renameTags.renameTag(title,newTitle,tagged);
if (!this.tiddlerExists(title) && newBody == "")
// dont create unwanted tiddler
return null;
}
}
return this.saveTiddler_orig_renameTags(title,newTitle,newBody,modifier,modified,tags,fields,clearChangeCount,created,creator);
},
removeTiddler_orig_renameTags: TiddlyWiki.prototype.removeTiddler,
removeTiddler: function(title) {
var tagged = this.getTaggedTiddlers(title);
if (tagged.length > 0)
if (confirm(config.renameTags.prompts.remove.format([title,tagged.length,tagged.length>1?"s":""])))
config.renameTags.removeTag(title,tagged);
return this.removeTiddler_orig_renameTags(title);
}
},
init: function() {
merge(TiddlyWiki.prototype,this.storeMethods);
}
}
config.renameTags.init();
//}}}
You can go longer without drink and nourishment and your [[Fatigue]] level is increased. Additional levels of [[Endurance]] only makes this duration longer.
See ‘help rules’ in game and the subsection of helpfiles there. Those helpfiles will always be the most current rules in effect and take precedence over any information on the website. For all matters, covered here or not, use common sense. In case of disputes, the interpretation and ruling of the administrative staff is final and binding.
There are a numer of binding rules that all players should abide by. They are:
!!Player Killing
There are no specific laws against player killing. Just make absolutely sure that you have a sensible, In Character reason for doing so. Also realise that the NPCs here should be treated no worse or better than the players. Unlike most muds, the NPCs do not exist just to be slaughtered and looted. Expect the NPCs to react IC as well. If you try to take down a tribal nation single-handedly, expect them to react accordingly. They are fully capable of ganging up, killing you, calling for help, and getting more reinforcements than can easily be beaten, or whatever their intelligence level and resources permit. Mindless pointless slaughter of PC’s and NPCs is strongly discouraged, unless you really are playing a crazed lunatic... but consider that society will probably deal with you quite harshly and quickly in such a case.
!!Powergaming
A special note must be made for power gaming. This is the unrealistic overuse of a skill in hopes of improving it. Some degree of practice and use is normal, but this should not be abused and must be tempered by the bounds of reason and realistic behaviour. Sure, you might practice.. but not many people would practice for 2 days straight without stopping. Also, and this is very important… do NOT set automatic triggers to repeatedly perform a skill over and over… that is highly illegal. In any case, code is implemented to prevent most skills from going up unnaturally fast. If you practice too hard, it won’t help you overmuch and will only succeed in drawing the attention of the system admins to your behaviour. Repeated abusers of code limitations and chronic power gamers will be asked to modify their behaviour and will then be paid closer attention to in the logs, those who fail to mend their ways will have the abused skills penalised or removed altogether.
!!Multiplaying
Possessing and playing more than one living character per player is allowed in the game, but you cannot play them at the same time. There are numerous reasons for this, not the least of which is the tendency for IC information to get mixed up between multiple active characters and for one character to either act upon, or refrain from taking a certain course of action based upon knowledge gained while playing another character. It also prevents you from [[Grouping|Group]] up to abuse the game system. Neither Characters from the same [[Account]] or from the same IP Address may connect at the same time.
Anybody can search. But to truly be able to find things you really need to know how to Rummage.
!!Description
[>img[./images/Saurian.jpg]]Saurians are derived from lizard breeding stock. Although they claim that they are descended from Dragons of old there is absolutely no evidence of this. But because they still believe this claim, they believe that they are the superior race. As such they look down on all of the other races, much as the other races look down on them. They live in a Clan structure where only the strongest may lead. As such they are very warrior and fighting orientated. The finer things in life completely pass them by, unless it's a nice shiny sword to kill things with.
Their scaley skin gives them a natural [[Toughness]]. But they have one severe drawback - they cannot stand the cold. As such will not venture into mountainous regions or into Tundra. They are however great fighters and practice regularly. They have claws when gives them a natural bare handed weapon, but this does mean that they have great trouble attempting the fine work necessary for crafting and making objects.
They're ambivalent to [[Magic]] more preferring the practical approach. But the have been a few notable Warrior Mages from their race.
!!Stats
|Size|Medium |
|Strength|3D6|
|~|Min 3, Avg 10, Max 18 |
|Dexterity|2D6|
|~|Min 3, Avg 10, Max 18 |
|Constitution|3D6+1|
|~|Min 4, Avg 11, Max 19 |
|Intelligence|3D6|
|~|Min 3, Avg 10, Max 18 |
|Special|Bonuses when learning [[Toughness]] and all [[Combat Skills]], but have penalties for all [[Artisan Skills]], except [[Smith]] and [[Armourer]]. |
/***
|Name:|SaveCloseTiddlerPlugin|
|Description:|Provides two extra toolbar commands, saveCloseTiddler and cancelCloseTiddler|
|Version:|3.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#SaveCloseTiddlerPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
To use these add them to the commands in ToolbarCommands under EditToolbar,
or in the MptwTheme tiddler under EditTemplate.
***/
//{{{
merge(config.commands,{
saveCloseTiddler: {
text: 'done/close',
tooltip: 'Save changes to this tiddler and close it',
handler: function(ev,src,title) {
var closeTitle = title;
var newTitle = story.saveTiddler(title,ev.shiftKey);
if (newTitle)
closeTitle = newTitle;
return config.commands.closeTiddler.handler(ev,src,closeTitle);
}
},
cancelCloseTiddler: {
text: 'cancel/close',
tooltip: 'Undo changes to this tiddler and close it',
handler: function(ev,src,title) {
// the same as closeTiddler now actually
return config.commands.closeTiddler.handler(ev,src,title);
}
}
});
//}}}
Syntax:
*"<message>
*say <message>
Says <message> out loud to everyone in the room. If your name is Igor, and you typed '"Hello everyone.', you will see 'You say, "Hello everyone."' and everyone else in the room will see 'Igor says, "Hello everyone."'
Also see: [[Chat]], [[Pose]] and [[Whisper]], as well as [[Group]] say.
Whilst there is no "Score" as such, this command will give you a quick overview of your stats, general health as well as a small summary of what you are carrying. This is an alias for the simple [[Stats]] command.
Syntax:
*search [<location or object>] [for <object>]
Searching on it's own gives a small chance of success of finding any of the things for that object or location. Searching for specific things gives a greater chance of success. The additional arguments are optional. If you do not specify a location then "here" is assumed.
Example:
*search
*search here
*search wall
*search for mushrooms
*search rock for writing
There is a law of diminishing returns. The more you search a location for the same object the less likely you will find it. Each time your chance halves (rounding down). For example if you only have a 10% chance of finding something and fail. The next chance would be 5%. Followed by 2%, 1%, and finally never. Skills such as [[Perception]] or spells such as [[Locate Object]] can increase the chance of finding something.
Secondary stats are initially calculated based on the [[Primary Stats]]. Both Primary and Secondary stats may be temporarily modified by equipment or spells. If a Primary Stat is adjusted, then any Secondary Stat based on the initial values will also change automatically.
/***
|Name:|SelectThemePlugin|
|Description:|Lets you easily switch theme and palette|
|Version:|1.0.1a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#SelectThemePlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!Notes
* Borrows largely from ThemeSwitcherPlugin by Martin Budden http://www.martinswiki.com/#ThemeSwitcherPlugin
* Theme is cookie based. But set a default by setting config.options.txtTheme in MptwConfigPlugin (for example)
* Palette is not cookie based. It actually overwrites your ColorPalette tiddler when you select a palette, so beware.
!Usage
* {{{<<selectTheme>>}}} makes a dropdown selector
* {{{<<selectPalette>>}}} makes a dropdown selector
* {{{<<applyTheme>>}}} applies the current tiddler as a theme
* {{{<<applyPalette>>}}} applies the current tiddler as a palette
* {{{<<applyTheme TiddlerName>>}}} applies TiddlerName as a theme
* {{{<<applyPalette TiddlerName>>}}} applies TiddlerName as a palette
***/
//{{{
config.macros.selectTheme = {
label: {
selectTheme:"select theme",
selectPalette:"select palette"
},
prompt: {
selectTheme:"Select the current theme",
selectPalette:"Select the current palette"
},
tags: {
selectTheme:'systemTheme',
selectPalette:'systemPalette'
}
};
config.macros.selectTheme.handler = function(place,macroName)
{
var btn = createTiddlyButton(place,this.label[macroName],this.prompt[macroName],this.onClick);
// want to handle palettes and themes with same code. use mode attribute to distinguish
btn.setAttribute('mode',macroName);
};
config.macros.selectTheme.onClick = function(ev)
{
var e = ev ? ev : window.event;
var popup = Popup.create(this);
var mode = this.getAttribute('mode');
var tiddlers = store.getTaggedTiddlers(config.macros.selectTheme.tags[mode]);
// for default
if (mode == "selectPalette") {
var btn = createTiddlyButton(createTiddlyElement(popup,'li'),"(default)","default color palette",config.macros.selectTheme.onClickTheme);
btn.setAttribute('theme',"(default)");
btn.setAttribute('mode',mode);
}
for(var i=0; i<tiddlers.length; i++) {
var t = tiddlers[i].title;
var name = store.getTiddlerSlice(t,'Name');
var desc = store.getTiddlerSlice(t,'Description');
var btn = createTiddlyButton(createTiddlyElement(popup,'li'), name?name:t, desc?desc:config.macros.selectTheme.label['mode'], config.macros.selectTheme.onClickTheme);
btn.setAttribute('theme',t);
btn.setAttribute('mode',mode);
}
Popup.show();
return stopEvent(e);
};
config.macros.selectTheme.onClickTheme = function(ev)
{
var mode = this.getAttribute('mode');
var theme = this.getAttribute('theme');
if (mode == 'selectTheme')
story.switchTheme(theme);
else // selectPalette
config.macros.selectTheme.updatePalette(theme);
return false;
};
config.macros.selectTheme.updatePalette = function(title)
{
if (title != "") {
store.deleteTiddler("ColorPalette");
if (title != "(default)")
store.saveTiddler("ColorPalette","ColorPalette",store.getTiddlerText(title),
config.options.txtUserName,undefined,"");
refreshAll();
if(config.options.chkAutoSave)
saveChanges(true);
}
};
config.macros.applyTheme = {
label: "apply",
prompt: "apply this theme or palette" // i'm lazy
};
config.macros.applyTheme.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var useTiddler = params[0] ? params[0] : tiddler.title;
var btn = createTiddlyButton(place,this.label,this.prompt,config.macros.selectTheme.onClickTheme);
btn.setAttribute('theme',useTiddler);
btn.setAttribute('mode',macroName=="applyTheme"?"selectTheme":"selectPalette"); // a bit untidy here
}
config.macros.selectPalette = config.macros.selectTheme;
config.macros.applyPalette = config.macros.applyTheme;
config.macros.refreshAll = { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
createTiddlyButton(place,"refresh","refresh layout and styles",function() { refreshAll(); });
}};
//}}}
Syntax:
*sell <object>
*sell <object> to <player>
This allows you to sell objects directly to a shop or to a player's [[Merchant Statue|Merchant]].
Sell also [[Buy]] and [[List]]
/***
|''Name''|SharedTiddlersPlugin|
|''Description''|Introduces a possibility to use tiddlers from other ~TiddlyWikis (with or without importing them)|
|''Documentation''|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPluginInfo|
|''Type''|plugin|
|''Version''|2.4.0"'|
|''~CoreVersion''|2.2.6|
|''Requires''|UpToDateFiltersPlugin|
|''Requirements note''|[[UpToDateFiltersPlugin|http://yakovl.bplaced.net/TW/ExtraFilters.html#UpToDateFiltersPlugin]] is necessary only for TW below v2.6.2|
|''Source''|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPlugin|
|''Author''|Yakov Litvin|
|''Forked from''|[[IncludePlugin|http://tiddlywiki.abego-software.de/#IncludePlugin]], by Udo Borkowski|
|''Contact''|see [[docs|SharedTiddlersPluginInfo]]|
|''Copyright''|Yakov Litvin, 2013|
|''Licence''|[[BSD-like open source license|http://yakovl.bplaced.net/TW/STP/STP.html#%5B%5BYakov%20Litvin%20Public%20Licence%5D%5D]] |
|>| In this tiddler, the code is minified and hidden; the full code can be found [[here|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPluginCode]]. |
''Config:''
***/
//{{{
config.options.STP_hijackPrettyLink = true;
config.options.STP_hijackImageFormatter = false;
//}}}
// /%
config.filters.all = function(results,match) {
if(match[3] == "with included") { // brings included tiddlers as well
var m,matched = this.reverseLookup();
for(m = 0; m < matched.length; m++)
results.pushUnique(matched[m]);
} else
this.forEachTiddler(function(tName,tiddler){
results.pushUnique(tiddler);
});
return results;
};
config.filters.includedFrom = function(results,match) {
var url = twWeb.getStoreUrlById(match[3]);
if(!url)
return [];
for(var i = 0; i < results.length; i++)
if(results[i].getIncludeURL() != url)
results.splice(i--,1);
return results;
};
config.filters.external = function(results,match) {
for(var i = 0; i < results.length; i++)
if(!results[i].getIncludeURL())
results.splice(i--,1);
return results;
};
config.filters.internal = function(results,match) {
for(var i = 0; i < results.length; i++)
if(results[i].getIncludeURL())
results.splice(i--,1);
return results;
};
(function(){
//==============================================================================
// install only once stuff
// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};
// Install only once (don't install if abego.IncludePlugin is installed as well)
if (abego.TiddlyWikiIncluder)
return alert("Warning: abego.TiddlyWikiIncluder already exists, so probably two copies of SharedTiddlersPlugin and/or IncludePlugin are installed and activated. It is highly recommended to deactivate all but one copy. You can find those by searching 'abego.TiddlyWikiIncluder'.");
// abego.TiddlyWikiIncluder is defined near the end of the code
// Define the API namespace:
window.sharedTiddlersAPI = {};
//==============================================================================
// Helpers
// This is used instead of displayMessage, because the latter sometimes doesn't show the messages
var displayAndLogMessage = function(text,linkText) {
displayMessage(text,linkText);
console.log(text);
};
var invokeLater = function(func, delay, priority) {
return setTimeout(func,delay);
};
//------------------------------------------------------------------------------
// url helpers
var isRelativeURL = function(url) {
// as Unix filesystem root is "/", urls starting with it are not considered as relative
return (url.search(/^(?:((http(s)?)|(file)):)|(.\:\\)|(\\\\)|(\/)/) != 0);
};
var getPathFromURL = function(url) {
return (url.lastIndexOf("/") > -1) ?
url.substr(0, url.lastIndexOf("/") + 1) : "";
};
var resolveUrlFrom = function(urlToResolve, sourceUrl) {
return (isRelativeURL(urlToResolve) && sourceUrl) ?
getPathFromURL(sourceUrl) + urlToResolve : urlToResolve;
};
// limitedly turns URI (URL) reference into an absolute URI (URL) and windows paths into URL
var stp_resolveURL = function(url) {
if (url.search(/^((http(s)?)|(file)):/) != 0) {
// no protocol prefix..
if (isRelativeURL(url))
url = resolveUrlFrom(url, document.location.toString());
else
// "url" is an "absolute" path to a local file. Prefix it with file://
url = "file://" + url;
// replace every \ by a /, to cover Windows style pathes
url = url.replace(/\\/mg,"/");
}
return url;
};
//------------------------------------------------------------------------------
// file/tw loading functions
// an evolution of the deprecated loadRemoteFile function with TW 2.7.0 codes
var stp_loadRemoteFile = function(url,callback,params) {
if(version.major < 2 || version.major == 2 && version.minor < 7) {
var httpSuccess = function(xhr) {
try {
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:"GET",
url:url,
processData:false,
data:undefined, // cut off?
cache:false,
beforeSend: function(xhr) {;},
complete: function(xhr,textStatus) {
if(httpSuccess(xhr))
callback(true,params,xhr.responseText,url,xhr);
else
callback(false,params,null,url,xhr);
}
};
try {
if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
} catch (ex) {}
return jQuery.ajax(options);
} else
return httpReq("GET",url,callback,params);
};
// Asynchronously load the given (local or remote) file.
//
// @param url
// value: either an URL or a local file path to a file
//
// Examples:
// * http://www.abego-software.de/index.html
// * file:///C:/abegoWebSite-Copy/index.html
// * C:\abegoWebSite-Copy\index.html (for Windows machines)
//
// Notice: backslashes in JavaScript string constants must be escaped,
// i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
// when "hardcoded" in JavaScript source code.
//
// @param callback
// value: function(content,url,params,errorMessage)
// called at the end of the operation.
// On success content holds the content of the loaded file.
// On error content is undefined and errorMessage holds an error message.
// params is the params passed into stp_LoadFile.
//
// @param params
// passed through to the callback function
//
var stp_LoadFile = function(url,callback,params) {
var onLoad = function(status,params,responseText,url,xhr) {
return status
? callback(responseText, url, params)
: callback(undefined, url, params, "Error loading %0".format([url]));
};
// Make sure the URL is a real URL, with protocol prefix etc.
url = stp_resolveURL(url);
stp_loadRemoteFile(url,onLoad,params);
};
// Asynchronously load the given (local or remote) TiddlyWiki store.
//
// @param url
// value: either an URL or a local file path to a TiddlyWiki file (absolute or relative)
//
// Examples:
// * http://www.abego-software.de/index.html
// * file:///C:/abegoWebSite-Copy/index.html
// * include/beta.html
// * C:\abegoWebSite-Copy\index.html (for Windows machines)
//
// Notice: backslashes in JavaScript string constants must be escaped,
// i.e. the last example must be written as: "C:\\abegoWebSite-Copy\\index.html"
// when "hardcoded" in JavaScript source code.
//
// @param callbackWithStore
// value: function(theStore,url,params,errorMessage)
// called at the end of the operation.
// On success theStore holds the loaded store (a TiddlyWiki object).
// On error theStore is undefined and errorMessage holds an error message.
// params is the params passed into stp_loadTiddlyWikiStore
//
// @param params
// passed through to the callbackWithStore
//
// @param progress [optional]
// value: function(message, sender, state, url, params)
// called in various situations during the operation,
// typically used to show "the progress" of the operation.
// sender: the constant "stp_loadTiddlyWikiStore"
// state: one of these: "Started", "Processing", "Done", "Failed"
// "Processing" means the data has been received and in now processed.
//
var stp_loadTiddlyWikiStore = function(url,callbackWithStore,params,progress) {
var sendProgress = function(message, state) {
if (progress)
progress(message,"stp_loadTiddlyWikiStore",state,url,params);
};
var sendError = function(message) {
sendProgress("Error when loading %0".format([url]),"Failed");
callbackWithStore(undefined, url, params, message);
return message;
};
var sendStore = function(store) {
sendProgress("Loaded %0".format([url]),"Done");
callbackWithStore(store, url, params);
return null;
};
var callback = function(content,theURL,params,errorMessage) {
if (content === undefined) {
sendError(errorMessage);
return;
};
sendProgress("Processing %0".format([url]),"Processing");
var invalidFileErrorMsg = "The file '%0' does not appear to be a valid TiddlyWiki file";
try {
// Load the content from the "content" string into a TiddlyWiki() object
var importStore = new TiddlyWiki();
var errorText = importStore.importTiddlyWiki(content)? null :
"Problem with importing TiddlyWiki, probable reason is: "+
invalidFileErrorMsg.format([url]);
if(errorText)
sendError(errorText);
else
sendStore(importStore);
} catch (ex) {
sendError(exceptionText(ex));
};
};
sendProgress("Start loading %0".format([url]),"Started");
stp_LoadFile(url,callback,params);
};
//------------------------------------------------------------------------------
// plugin installation helpers
var getArbitraryPluginInfo = function(tiddler) {
// getPluginInfo can't be used (for included tiddlers) because of the ugly slice handling of the core
// (it uses {store} where it could handle the tiddler directly)
var pInfo = {};
var slices = {};
var requiredInfo = ["Name","Description","Version","Requires","CoreVersion","Date","Source", "Author","License","Browsers"];
store.slicesRE.lastIndex = 0;
var match = store.slicesRE.exec(tiddler.text);
while(match) {
if(match[2])
slices[match[2]] = match[3];
else
slices[match[5]] = match[6];
match = store.slicesRE.exec(tiddler.text);
}
for(var i = 0; i < requiredInfo.length; i++)
if(slices[requiredInfo[i]])
pInfo[requiredInfo[i]] = slices[requiredInfo[i]];
pInfo.tiddler = tiddler;
pInfo.title = tiddler.title;
pInfo.log = [];
return pInfo;
}
var checkPluginInstalled = function(pluginName) {
for(var i = 0; i < installedPlugins.length; i++)
if(installedPlugins[i].title == pluginName || installedPlugins[i].Name == pluginName)
return true;
return false;
}
var installPlugin = function(tiddler,force) {
var pluginName = getArbitraryPluginInfo(tiddler).Name || tiddler.title;
// check if such a plugin was installed previously, return if so
if(!force) // two layers to improve the speed in the "force == true" case
if(checkPluginInstalled(pluginName))
return;
// get the plugin info
var pluginInfo = getArbitraryPluginInfo(tiddler);
if(tiddler.getIncludeURL())
pluginInfo.log.push("included from "+tiddler.getIncludeURL());
// install the plugin
pluginInfo.executed = true;
var startTime = new Date();
try {
window.eval(tiddler.text);
} catch(ex) {
pluginInfo.log.push(config.messages.pluginError.format([exceptionText(ex)]));
pluginInfo.error = true;
console.log("error evaluating " + tiddler.title, ex);
story.displayTiddler(null,"PluginManager");
displayMessage(config.messages.customConfigError);
}
pluginInfo.startupTime = String((new Date()) - startTime) + "ms";
// register the plugin
installedPlugins.push(pluginInfo);
}
//==============================================================================
// Shared Tiddlers Plugin
// Constants
var WAITING = "waiting";
var LOADING = "loading";
var ANI_DURATION_HIDE_STATE = 1000;
var REFRESH_PRIORITY = -200;
var ANIMATION_PRIORITY = -100;
var UPDATE_STATE_PRIORITY = -300;
// --------------------------------------------------
// Variables
var useInclude; // this variable can be used to control include enabling by other things than cookies
var includedStores = {}; // url(String) -> TiddlyWiki or String; if not (yet) loaded a status or an error string
var pendingOnLoadURLs = []; // [] of String: a list of urls that should be passed with the next "notifyListeners"
var refreshTiddlyWikiTimerID; // for delayed refresh
var listeners = [];
var progress;
// rules pointing which stores tiddlers with conflicting names should be fetched from
function FetchPreferences() {
var prefs = {};
this.add = function(tiddlerName,sourceUrl,substitute,substituteShadow) {
if(!substitute && !substituteShadow)
return;
if(prefs[tiddlerName] == undefined)
prefs[tiddlerName] = {urlsSubs: [], urlsSubsSh: []};
if(substitute)
prefs[tiddlerName].urlsSubs.pushUnique(sourceUrl);
if(substituteShadow)
prefs[tiddlerName].urlsSubsSh.pushUnique(sourceUrl);
};
/* this.getPrefs = function(tiddlerName) {
return jQuery.extend(true, {}, prefs[tiddlerName]);
};
*/ this.getSubsUrl = function(tiddlerName) {
var pref = prefs[tiddlerName];
if(pref == null)
return null;
if(pref.urlsSubs.length == 0)
return null;
return pref.urlsSubs[0];
};
this.getSubsShUrl = function(tiddlerName) {
var pref = prefs[tiddlerName];
if(pref == null)
return null;
if(pref.urlsSubsSh.length == 0)
return null;
// check if there's a tiddler with both preferences
var i, j;
for(i = 0; i < pref.urlsSubs.length; i++)
for(j = 0; i < pref.urlsSubsSh.length; j++)
if(pref.urlsSubs[i] == pref.urlsSubsSh[j])
return pref.urlsSubs[i];
return pref.urlsSubsSh[0];
};
this.containSubsUrl = function(tName,url) {
var pref = prefs[tName];
if(pref == null)
return false;
return pref.urlsSubs.contains(url);
};
this.containSubsShUrl = function(tName,url) {
var pref = prefs[tName];
if(pref == null)
return false;
return pref.urlsSubsSh.contains(url);
};
this.removeByUrl = function(url) { // removes all priorities of tiddlers, included from the url
var tName, pref, i;
for(tName in prefs) {
pref = prefs[tName]
for(i = 0; i < pref.urlsSubs.length; i++)
if(pref.urlsSubs[i] == url)
pref.urlsSubs.splice(i--,1);
for(i = 0; i < pref.urlsSubsSh.length; i++)
if(pref.urlsSubsSh[i] == url)
pref.urlsSubsSh.splice(i--,1);
// if (pref.urlsSubs.length == 0 && pref.urlsSubsSh.length == 0), not nec. to delete pref
}
};
};
var fetchPreferences = new FetchPreferences();
function Conflicts() {
// hashmaps by tiddler name of potentially conflicting tiddlers with that name;
// each element is a hashmap by url of tiddlers with such names
var pConfs = {}, // doesn't contain info about tiddlers in the main store
pSConfs = {}; // for tiddlers that may conflict when substituting a shadow
this.init = function() {
// notify of name conflicts in the whole set of initial and included tiddlers?
if(config.options.chkWarnOnSharedTiddlersConflicts == undefined) config.options.chkWarnOnSharedTiddlersConflicts = true;
// use alert() for notifications?
if(config.options.chkAlertOnSharedTiddlersConflicts == undefined) config.options.chkAlertOnSharedTiddlersConflicts = false;
};
// check whether a new tiddler (not included yet) may cause a conflict (now or in the future)
this.checkNew = function(tName, url, subs, subsSh) {
// use to add when a tiddler with such name is already present
var addData = function(confsMap, tidParams) {
var includeData = function(tidParams) { // better to create a separate class
return { subs: tidParams.substitute, warned: false };
};
var getSubsPriority = function(data) {
return data.subs;
}
var conf = confsMap[tidParams.title];
if(!conf) {
conf = confsMap[tidParams.title] = {};
conf[tidParams.url] = includeData(tidParams);
} else {
var confPart = conf[tidParams.url];
if(!confPart)
conf[tidParams.url] = includeData(tidParams);
else {
if(!getSubsPriority(confPart) && tidParams.substitute)
conf[tidParams.url] = includeData(tidParams);
// better to set subs to true and warned to false
}
}
}
// check if there's a tiddler with such a name
var existingTid = forEachLoadedStore(function(theStore, storeUrl) { // in included stores
if(storeUrl != url)
return theStore.fetchTiddler(tName);
}) || window.sharedTiddlersAPI.orig_fetchTiddler(tName); // or in the main one
// if the tiddler is added to the main store after exactly one tiddler is included, no conflict is
// detected by this algorithm; also, it doesn't account deleting the tiddler from the main store
if(!existingTid)
return;
var mainStoreId = "main store",
existingTidUrl = existingTid.getIncludeURL();
// check conflicts among all included tiddlers (important when no shadow with such name exists)
if(!pConfs[tName])
addData(pConfs, {
title: tName,
url: existingTidUrl || mainStoreId,
substitute: existingTidUrl ? fetchPreferences.containSubsUrl(tName,existingTidUrl) : undefined
});
addData(pConfs, { title: tName, url: url, substitute: subs });
// check conflicts among tiddlers that substitute a shadow
if(subsSh) {
var existingTidSubsSh = forEachLoadedStore(function(theStore, storeUrl) {
if(storeUrl != url && fetchPreferences.containSubsShUrl(tName,storeUrl))
return theStore.fetchTiddler(tName);
}) || window.sharedTiddlersAPI.orig_fetchTiddler(tName);
// same problems
if(!existingTidSubsSh)
return;
var existingTidSubsShUrl = existingTidSubsSh.getIncludeURL();
// conflicts among substituting tiddlers are important when a shadow with such name exists
if(!pSConfs[tName])
addData(pSConfs, {
title: tName,
url: existingTidSubsShUrl || mainStoreId,
substitute: existingTidSubsShUrl ? fetchPreferences.containSubsShUrl(tName,existingTidSubsShUrl) : undefined
});
addData(pSConfs, { title: tName, url: url, substitute: subs });
}
};
this.markNodeUnloaded = function(url) {
var t;
for(t in pConfs)
if(pConfs[t][url])
delete pConfs[t][url];
for(t in pSConfs)
if(pSConfs[t][url])
delete pSConfs[t][url];
};
this.notify = function() {
var msgAndLog = config.options.chkWarnOnSharedTiddlersConflicts;
var doAlert = config.options.chkAlertOnSharedTiddlersConflicts;
if(!doAlert && !msgAndLog)
return; // no conflict is marked as "warned" - this is by intent
var tName, tUrl;
var checkOrDisplayConflicts = function(map,msgAndLog,markWarned) {
var msg = ""; // message to return (for alerting etc)
var addM = function(m) { // pushes all notification messages
msg += ("\n"+m);
if(msgAndLog)
displayAndLogMessage(m);
};
var tName, tSources, tUrl, subs, nOfConflicting, newPresent,
subsMsg = " (with the subsitute priority)";
// mark conflicts among tiddlers with the substitute priority
// find out where necessary, notify
for(tName in map) {
tSources = map[tName];
// find out which priority is of interest
subs = false;
for(tUrl in tSources)
if(tSources[tUrl].subs)
subs = true;
// if there's only one tiddler with "substitute", there's no conflict
nOfConflicting = 0;
for(tUrl in tSources)
if(tSources[tUrl].subs == subs)
nOfConflicting++;
if(nOfConflicting < 2)
break;
// find out if new conflicting tiddlers are present
newPresent = false;
for(tUrl in tSources)
if(tSources[tUrl].subs == subs && !tSources[tUrl].warned)
newPresent = true;
// start notification
if(newPresent) {
addM("* "+tName+" in:");
for(tUrl in tSources)
if(tSources[tUrl].subs == subs && !tSources[tUrl].warned) {
addM("** "+tUrl);
if(markWarned)
tSources[tUrl].warned = true;
}
if(subs)
addM(subsMsg);
}
}
return msg;
};
var msg = "";
if(checkOrDisplayConflicts(pConfs,false,false)) { // new conflicts present
msg += "New conflicts:";
if(msgAndLog) displayAndLogMessage(msg);
msg += checkOrDisplayConflicts(pConfs,msgAndLog,true);
};
if(checkOrDisplayConflicts(pSConfs,false,false)) { // new conflicts among tiddlers substituting shadows present
if(msg) msg += "\n";
var m = "New conflicts among tiddlers competing for substituting shadows:";
msg += m;
if(msgAndLog) displayAndLogMessage(m);
msg += checkOrDisplayConflicts(pSConfs,msgAndLog,true);
};
if(doAlert && msg)
alert(msg);
};
// this.state = // return current state as a string (for includeState)
};
conflicts = new Conflicts();
conflicts.init();
// --------------------------------------------------
// Helper functions
var isIncludeEnabled = function() {
if (useInclude === undefined)
useInclude = config.options.chkUseInclude === undefined || config.options.chkUseInclude;
return useInclude;
};
var getMissingIncludeMsg = function(url) {
return "No include specified for %0".format([url])
};
// Called after one or more included TiddlyWikis are loaded
//
var notifyListeners = function() {
var urls = pendingOnLoadURLs;
pendingOnLoadURLs = [];
if (urls.length)
for (var i = 0; i < listeners.length; i++)
listeners[i](urls);
};
var idleCount; // Reset to 0 when the system is "not idle", incremented inside refreshTiddlyWiki
var refreshTiddlyWiki = function() {
// To avoid to much refreshing/flickering don't refresh immediately
// but wait until the system was idle for a certain time.
if (refreshTiddlyWikiTimerID !== undefined) clearInterval(refreshTiddlyWikiTimerID);
idleCount = 0;
var sendDone = function() {
twWeb.sendProgress("","","Done");
};
refreshTiddlyWikiTimerID = setInterval(function() {
idleCount++;
if (idleCount <= 10)
return;
clearInterval(refreshTiddlyWikiTimerID);
refreshTiddlyWikiTimerID = undefined;
twWeb.sendProgress("Refreshing...","","");
refreshDisplay();
invokeLater(sendDone,0,REFRESH_PRIORITY);
},1);
};
// Calls callback for every loaded store and returns the first non-false/null.. value returned by callback.
//
// @param callback
// value: function(store, url)
//
var forEachLoadedStore = function(callback) {
var result;
for(var url in includedStores) {
var theStore = twWeb.getStore(url);
if (theStore && (result = callback(theStore, url)))
return result;
}
};
// hijack fetchTiddler so that it works with tiddlers from included stores as well
var attachToStore = function() {
if (!window.store)
return invokeLater(attachToStore,100);
var orig_fetchTiddler = store.fetchTiddler;
window.sharedTiddlersAPI.orig_fetchTiddler = orig_fetchTiddler;
// reserve access to the original method to be able to fetch tiddlers from main store,
// including substituted ones
store.fetchTiddler = function(title) {
var t, subsUrl = fetchPreferences.getSubsUrl(title), subsShUrl;
// first, look for the tiddler in the main store, unless there's nothing to substitute with
if(!subsUrl) {
t = orig_fetchTiddler.apply(this,arguments);
if(t) return t;
} else
return includedStores[subsUrl].fetchTiddler(title);
// then, look for shadowed tiddlers in main store and external ones to substitute those
if(config.shadowTiddlers[title] !== undefined) {
if(subsShUrl = fetchPreferences.getSubsShUrl(title))
return includedStores[subsShUrl].fetchTiddler(title);
else
return undefined;
};
// Don't look for the "New Tiddler" tiddler in the included TiddlyWikis,
// since returning such a tiddler (that is readonly) will make it impossible
// in the Main TiddlyWiki to create new tiddlers via standart "new tiddler" button.
if (title == config.macros.newTiddler.title) return undefined;
// finally, look for an external one without "substitute" preference
return forEachLoadedStore(function(theStore, url) {
return theStore.fetchTiddler(title);
});
};
// refresh TiddlyWiki to reflect the new included Tiddlers (if we have any).
if(twWeb.getIncludedStoresUrls().length)
refreshTiddlyWiki();
};
var includeFromIncludeList = function() {
if (!window.store)
return invokeLater(includeFromIncludeList,100);
var includeListText = store.getTiddlerText("IncludeList");
if (includeListText)
wikify(includeListText,document.createElement("div"),undefined,store.fetchTiddler("IncludeList"));
};
var getFunctionUsingForReallyEachTiddler = function(func) {
var wrapper = function() {
var orig_forEachTiddler = store.forEachTiddler;
var forEachTiddlerWithIncludes = function(callback) {
var done = {};
var callbackWrapper = function(title, tiddler) {
// ensure every title is only processed once
if(done[title])
return;
// do and set done for appropriate tiddlers
if(!fetchPreferences.getSubsUrl(title)||
(fetchPreferences.getSubsUrl(title) == tiddler.getIncludeURL())) {
done[title] = 1;
callback.apply(this,arguments);
};
};
// first, forEachTiddler over the original tiddlers
orig_forEachTiddler.call(store, callbackWrapper);
// add nonsubstituted shadowTiddler titles to done
// (to avoid an included store hide a shadow tiddler)
for (var n in config.shadowTiddlers)
if(!fetchPreferences.getSubsShUrl(n))
done[n] = 1;
// add the "New Tiddler" tiddler to done
// to avoid an included store (with such tiddler) prevent creating new tiddlers
done[config.macros.newTiddler.title] = 1;
// forEachTiddler over every included store
forEachLoadedStore(function(theStore, url) {
theStore.forEachTiddler(callbackWrapper);
});
};
store.forEachTiddler = forEachTiddlerWithIncludes;
try {
return func.apply(this,arguments);
} finally {
store.forEachTiddler = orig_forEachTiddler;
};
};
return wrapper;
};
var useForReallyEachTiddler = function(object,property) {
return object[property] = getFunctionUsingForReallyEachTiddler(object[property]);
};
//================================================================================
// config.extensions.SharedTiddlersPlugin (the "includer" engine)
config.extensions.SharedTiddlersPlugin = {
// function config.extensions.SharedTiddlersPlugin.getFunctionUsingForReallyEachTiddler(func)
//
// Returns a function that behaves as func, but every call to store.forEachTiddler will actually
// be a call to forReallyEachTiddler (see below), i.e. iterate over the tiddlers of the main store
// and of the included TiddlyWikis
//
// @return the patched function
//
getFunctionUsingForReallyEachTiddler: getFunctionUsingForReallyEachTiddler,
// function config.extensions.SharedTiddlersPlugin.useForReallyEachTiddler(object,property)
//
// Patches the function hold in the given property of the object in such a way that every call
// to store.forEachTiddler will actually be a call to forReallyEachTiddler (see below), i.e.
// iterate over the tiddlers of the main store and of the included TiddlyWikis
//
// @param object
// @param property the name of the property of the object containing the function to be patched.
// @return the patched function
//
useForReallyEachTiddler: useForReallyEachTiddler,
// Add a listener function to the TiddlyWikiIncluder.
//
// @param listener function(urls)
// urls: [] of Strings, containing the urls of the TiddlyWiki just included
// (see url@config.extensions.SharedTiddlersPlugin.include)
// called whenever one or more TiddlyWiki store are successfully included.
//
addListener: function(listener) {
listeners.push(listener);
}
};
// -------------------------------------------------------------------------------
// TiddlyWikiIncluder initialization code
config.extensions.SharedTiddlersPlugin.addListener(refreshTiddlyWiki);
config.shadowTiddlers.AdvancedOptions +=
("\n~IncludePlugin settings:"+
"\n<<option chkUseInclude>> Include ~TiddlyWikis"+
"\n<<option chkAlertOnSharedTiddlersConflicts>> Alert on tiddler name conflicts"+
"\n<<option chkWarnOnSharedTiddlersConflicts>> Display messages and write log in the browser console on conflicts"+
"\nIncludeList | IncludeState | ImportIncluded | [[help|http://yakovl.bplaced.net/TW/SharedTiddlersPlugin.html/#SharedTiddlersPluginInfo]]"+
"\n^^(Reload this ~TiddlyWiki to make changes become effective)^^");
config.shadowTiddlers.IncludeState = "<<includeState>>";
// add the "importer" engine
config.shadowTiddlers.ImportIncluded =
"| the url of the document to import from | <<option txtStoreUrl>> |\n"+
"| the filter of tiddlers to import | <<option txtFilterTiddlersToImport>> |\n"+
"| the importing mode | <<option txtImportMode>> |\n"+
"<html><a href='javascript:;' onclick='\n"+
" var storeUrl = config.options.txtStoreUrl,\n"+
" params = {\n"+
" filterLine: config.options.txtFilterTiddlersToImport,\n"+
" importMode: config.options.txtImportMode,\n"+
" noRefresh: true,\n"+
" };\n"+
" if(!storeUrl)\n"+
" return displayMessage(\"please specify the url to import from\");\n"+
" if(!params.filterLine)\n"+
" return displayMessage(\"please specify the filter of tiddlers to import\");\n"+
" if(!sharedTiddlersAPI.getStore(storeUrl))\n"+
" displayMessage(\"warning: no store was loaded from \"+storeUrl+\" previously, trying now\");\n"+
" twWeb.include(storeUrl,params);\n"+
"'>import (without saving)<a/></html>";
//================================================================================
// Tiddler extension/modification
Tiddler.prototype.isIncluded = function() {
return this.includeURL != undefined;
};
Tiddler.prototype.getIncludeURL = function() {
return this.includeURL;
};
Tiddler.prototype.setIncludeURL = function(url) {
this.includeURL = url;
};
Tiddler.prototype.deleteIncludeURL = function() {
delete this.includeURL;
};
// make included tiddlers readonly
config.extensions.SharedTiddlersPlugin.orig_Tiddler_isReadOnly = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
return config.extensions.SharedTiddlersPlugin.orig_Tiddler_isReadOnly.apply(this,arguments) || this.isIncluded();
}
//================================================================================
// TiddlyWiki modifications
// In some TiddlyWiki functions the "forEachTiddler" should work on all tiddlers, also those from
// included store. (E.g. TiddlyWiki.prototype.getTags)
//
// But not for all (e.g. TiddlyWiki.prototype.getTiddlers is used for saving, but only the "own" tiddlers should be saved)
//
// Therefore explicitly list the functions that should be "wrapped" to use the "forReallyEachTiddler".
//
var tiddlyWikiFunctionsUsingForReallyEachTiddler = {
getMissingLinks: 1, getOrphans: 1,
getTags: 1, reverseLookup: 1, updateTiddlers: 1};
for (var n in tiddlyWikiFunctionsUsingForReallyEachTiddler)
useForReallyEachTiddler(TiddlyWiki.prototype,n);
//================================================================================
// Web of TiddlyWikis
function IncludingMemorizer() { // the structure to store info about already handled include macros
var used = {};
this.isUsed = function(line) { return used[line]? true : false; };
this.setUsed = function(line) { used[line] = 1; };
this.getUsed = function() { return jQuery.extend(true, {}, used) };
this.markUnused = function(usedMap) {
for(var u in usedMap)
used[u] = undefined; // don't delete to keep the order of inclusion (for reloading)
};
};
function TwWeb() {
var nodes = {}; // hashmap by node name of nodes' metadata
var nodeConflicts = {}; // hashmap by node name of arrays of conflicts
var nodeWaitingTasks = {}; // hashmap by node name of arrays of Waiting Tasks
var nodeDescription = function(url) { // PoG: can be turned into a separated "class"
return { url : url };
};
var selfNodeName;
var self = this;
this.includeUsages = new IncludingMemorizer();
// ----- Nodes desctiptions part ---------------------------------------------------------------------
// set/get the name of "main" (including) node
this.setSelfNodeName = function(name) {
selfNodeName = name;
};
this.getSelfNodeName = function() {
return selfNodeName;
};
// set/get/delete node description
this.setNodeDesc = function(nodeName, desc) {
var existing = this.getNodeDesc(nodeName);
// currently, doesn't change metadata on conflict
if(existing)
this.addConflict(nodeName, desc);
else
nodes[nodeName] = desc;
this.callWaitingTasks(nodeName);
};
this.getNodeDesc = function(nodeName) {
return nodes[nodeName];
};
this.deleteNodeDesc = function(nodeName) {
nodes[nodeName] = null;
};
// "API" method to be used in the macro
this.addNodeDesc = function(nodeName, url) {
this.setNodeDesc(nodeName, nodeDescription(url));
};
// returns "better" description if one is "strictly better" than the other or "even" and null otherwise
this.compareNodeDesc = function(desc1, desc2) {
// looks like here's a mistake: probably ": desc2" (?)
return (desc1.url == desc2.url)? desc1 : null;
};
this.getNodeUrl = function(nodeName) {
var desc = this.getNodeDesc(nodeName);
return desc? desc.url : null;
};
// @param nodeId
// url or "node: nodeName" id of the node
// @return url of the node (if it is defined) or null
//
this.getStoreUrlById = function(nodeId) {
var node = self.checkNodeNotation(nodeId),
url = node ? self.getNodeUrl(node) : nodeId;
return url;
}
this.setNodeNotation = function(nodeName) {
return "node: " + nodeName;
};
this.checkNodeNotation = function(urlParam) {
var nodeCalcRE = /node: (.*)/,
nodeCalcMatch = nodeCalcRE.exec(urlParam);
return nodeCalcMatch ? nodeCalcMatch[1] : null;
};
this.addConflict = function(nodeName, nodeDesciption) {
var betterDesc = this.compareNodeDesc(nodeDesciption,this.getNodeDesc(nodeName));
if(betterDesc) {
this.deleteNodeDesc(nodeName);
this.setNodeDesc(nodeName, betterDesc);
return;
}
if(nodeConflicts[nodeName])
nodeConflicts[nodeName].push(nodeDesciption);
else
nodeConflicts[nodeName] = [ nodeDesciption ];
alert( "Warning: more than one description of the "+nodeName+" node was pushed. "+
"The earlier version is kept." );
};
// ----- Waiting tasks part --------------------------------------------------------------------------
this.setWaitingTask = function(nodeName, waitingTaskFunc, waitingTaskSelf) {
var waitingTask = { action: waitingTaskFunc, self: waitingTaskSelf };
if(nodeWaitingTasks[nodeName])
nodeWaitingTasks[nodeName].push(waitingTask);
else
nodeWaitingTasks[nodeName] = [ waitingTask ];
if(this.getNodeUrl(nodeName))
this.callWaitingTasks(nodeName);
};
this.callWaitingTasks = function(nodeName) {
var toDo = nodeWaitingTasks[nodeName];
if(toDo)
for(var i = 0; i < toDo.length; i++)
toDo[i].action.call(toDo[i].self);
nodeWaitingTasks[nodeName] = null; // remove called Waiting Tasks
};
// ----- Including stuff part ------------------------------------------------------------------------
// ---- helpers ----
this.setProgressFunction = function(func) {
progress = func;
};
/* this.getProgressFunction = function() { // is not in use
return progress;
};
*/ this.sendProgress = function(message, sender, state) {
if (progress)
progress.apply(this,arguments);
};
// Returns true when there are "pending" includes, i.e. TiddlyWiki that are not yet loaded.
// A TiddlyWiki that failed loading is not pending.
//
this.hasPendingIncludes = function() {
var state;
for(var url in includedStores) {
state = this.getState(url);
if (state == WAITING || state == LOADING)
return true;
};
return false;
};
// Called when an included TiddlyWiki could not be loaded.
// By default an error message is displayed.
//
this.onError = function(url, errorMessage) {
displayAndLogMessage("Error when including '%0':\n%1".format([url, errorMessage]));
};
// import a tiddler from an included store
//
// @param tiddler - a tiddler to import
// @param mode: undefined or 1 - import anyway (other modes, like "don't substitute" will be supported)
// @return the result object:
// .status means: -1 = error, 0 = tiddler is imported, >0 - reserved for other situations
// .errorText is not empty on error
// .tiddler is the tiddler with /the title/ which is in the store after "it is over" (may be null)
// .from is the url line equal to the includeURL of the imported tiddler
//
this.importIncluded = function(tiddler,mode) {
if(mode == undefined)
mode = 1;
else
mode = parseInt(mode);
var t, tInMain, result = { tiddler: null };
var doImport = function(t) {
store.addTiddler(t);
store.setDirty(true);
result.status = 0;
result.tiddler = t;
result.from = url;
};
if(tiddler instanceof Tiddler) {
// see return value, this is for the case when the import is not done
result.tiddler = window.sharedTiddlersAPI.orig_fetchTiddler(tiddler.title);
t = jQuery.extend(true, new Tiddler(), tiddler);
} else {
result.status = -1;
result.errorText = "not a Tiddler instance";
return result;
};
var url;
if(url = t.getIncludeURL())
t.deleteIncludeURL();
switch(mode) {
case 4: { // import only newer and on confirm
tInMain = window.sharedTiddlersAPI.orig_fetchTiddler(t.title);
if(!tInMain || tInMain.modified < t.modified)
if(confirm("Up-to-date "+t.title+" from "+url+" is availabe, import?"))
doImport(t);
break
}
case 2: { // import only newer versions/unexisting tiddlers
tInMain = window.sharedTiddlersAPI.orig_fetchTiddler(t.title);
if(!tInMain || tInMain.modified < t.modified)
doImport(t);
break
}
case 3: { // import on confirm
if(confirm(t.title+" from "+url+" is availabe, import?"))
doImport(t);
break
}
case 1: { // import anyway
doImport(t);
break
}
default: {
result.status = -1;
result.errorText = "unknown import mode";
}
};
return result;
};
this.importAndLog = function(tiddler,mode) {
var name = tiddler.title,
result = twWeb.importIncluded(tiddler,mode);
// this.importIncluded is not used to be able to pass the method to the API
switch(result.status) {
case 0: console.log("imported: "+name+" from "+result.from);
break
case -1: console.log("error importing "+name+": "+result.errorText);
break
}
};
// ---- main ----
// Includes the (local or remote) TiddlyWiki store with the given url.
//
// @param url see url@stp_loadTiddlyWikiStore
// @param includeParams a set of the following params for including:
// filterLine a filter expression defining a set of tiddlers to include
// substituting points whether an included tiddler should sustitute
// one in the main document if there's a conflict of names
// substituteShadows points whether shadowed tiddlers of the main document with names equal to
// those of included ones should be substituted (in cases of conflicts);
// works only with (substituting == true)
// delayMilliSeconds addition delay of loading
// noRefresh
// importMode
// evalTiddlers
// wikifyTiddlers
//
this.include = function(urlOrNodeParam, includeParams) {
if (!isIncludeEnabled())
return;
var self = this;
var includeOrHandleUrl = function(url) {
var loadStoreCallback = function(theStore,urlInCallback,params,errorMessage) {
if(theStore === undefined) {
includedStores[url] = errorMessage;
self.onError(url, errorMessage);
return;
} else
includedStores[url] = theStore;
// keep orig_store not to load tw-documents multiple times
// because of multiple include macros:
includedStores[url].orig_store = new TiddlyWiki();
includedStores[url].forEachTiddler(function(tName,tiddler){
tiddler.setIncludeURL(url);
includedStores[url].orig_store.addTiddler(tiddler);
});
includedStores[url].clear();
// include, create fetchPreferences and notify of conflicts; or import
addFromLoadedStore();
};
var addFromLoadedStore = function() {
// uses url and includeParams exploiting closure
var substituting = includeParams.substitute,
substituteShadows = includeParams.substituteShadows,
importMode = includeParams.importMode;
if(twWeb.getStore(url) === null)
return invokeLater(addFromLoadedStore, 100); // 100 milliseconds
// new tiddlers should be added *when the store is loaded*
// add tiddlers to included stores and check new conflicts (among non-subs. tids)
var i, t,
new_tiddlers = includedStores[url].orig_store.filterTiddlers(includeParams.filterLine);
for(i = 0; i < new_tiddlers.length; i++) {
t = jQuery.extend(true, new Tiddler(), new_tiddlers[i]); //copy (by value)
if(includedStores[url].orig_store.fetchTiddler(t.title)) {
// ignore empty tiddlers created by the "tiddler" filter
if(importMode)
twWeb.importAndLog(t,importMode);
else {
// check for upcoming conflicts first
conflicts.checkNew(t.title, url, substituting, substituteShadows);
// then include
includedStores[url].addTiddler(t);
};
if(includeParams.evalTiddlers)
installPlugin(t);
if(includeParams.wikifyTiddlers)
wikify(t.text,document.createElement("div"),undefined,t);
};
};
// add items to fetchPreferences if have to
if(!importMode && (substituting || substituteShadows))
for(i = 0; i < new_tiddlers.length; i++)
fetchPreferences.add(new_tiddlers[i].title,url,substituting, substituteShadows);
conflicts.notify();
// "recalc" slices
store.slices = {};
// refresh things to get included stylesheets, PageTemplate and ViewTemplate applied
if(!includeParams.noRefresh) {
refreshAll();
story.refreshAllTiddlers();
}
pendingOnLoadURLs.push(url);
invokeLater(notifyListeners);
};
var loadStore = function() {
includedStores[url] = LOADING;
stp_loadTiddlyWikiStore(url,loadStoreCallback,null,progress);
// {includeParams:includeParams} can be used instead of null so that
// loadStoreCallback will have access to the includeParams
};
var urlIsNew = !(includedStores[url]);
if(urlIsNew) {
includedStores[url] = WAITING;
if (includeParams.delayMilliSeconds)
invokeLater(loadStore, includeParams.delayMilliSeconds);
else
loadStore();
} else
addFromLoadedStore();
};
var nodeName = this.checkNodeNotation(urlOrNodeParam);
if(nodeName) {
if(nodeName == this.getSelfNodeName()) // don't include from the main (self) TiddlyWiki
return;
this.setWaitingTask(nodeName,function(){
var url = this.getNodeUrl(nodeName);
includeOrHandleUrl(url);
},this);
} else
includeOrHandleUrl(urlOrNodeParam);
};
// ----- Methods for dealing with included stuff -----------------------------------------------------
// @return [] of Strings, the URLs of the includes
//
this.getIncludedStoresUrls = function() { // in a form of an array
var includes = [];
for(var url in includedStores)
includes.push(url);
return includes;
};
// @return the (TiddlyWiki) store with the given URL or "node: nodeName", or null if not (yet) loaded.
//
this.getStore = function(nodeId) {
var url = self.getStoreUrlById(nodeId)
if(!url)
return null;
var s = includedStores[url];
if(s && s instanceof TiddlyWiki)
return s;
return null;
};
// @return a state/error text of the store with the given URL, or null when the store is already loaded
//
this.getState = function(nodeId) {
var url = self.getStoreUrlById(nodeId)
if(!url)
return "the node "+self.checkNodeNotation(nodeId)+" is not described yet, the address is unknown";
var s = includedStores[url];
if (!s)
return getMissingIncludeMsg(url);
return typeof s == "string" ? s : null;
};
// reload one included store or all of them
// previous inclusions are done again, by default only those without eval and import
// important: reload doesn't work correctly with <<include>> usages with multiple urls
//
// @param reloadParams an object containing some of these configuration properties:
// urlOrNodeList an array of TWs' IDs (url or "node: nodeName") to reload;
// if undefined, all TWs are reloaded
// eval points whether to redo inclusions with the "eval" parameter (undefined == false)
// import same for the "import" parameter, but undefined/null -> true
// wikify same for the "wikify" parameter
this.reload = function(reloadParams) {
// determine a TW(s) to reload (undefined => all)
var i, twsToReload = reloadParams.urlOrNodeParam;
// turn "IDs" into actual urls; filter out node names for which urls are not defined
// so waiting tasks are not doubled
if(twsToReload)
for(i = 0; i < twsToReload.length; i++){
nodeName = this.checkNodeNotation(twsToReload[i]);
twsToReload[i] = nodeName ? this.getNodeUrl(nodeName) : twsToReload[i];
if(!twsToReload[i])
twsToReload.splice(i--,1);
}
if(reloadParams.import === undefined || reloadParams.import === null)
reloadParams.import = true;
if(reloadParams.wikify === undefined || reloadParams.wikify === null)
reloadParams.wikify = true;
// collect macro expressions to reload
// may the order of including be important?
var usage, usages = this.includeUsages.getUsed(),
paramString, params, pParams, urlParam, nodeName,
dontReload, i;
for(usage in usages) {
// parsing copied from config.macros.include.handler (to get macro params)
paramString = usage.substring(10,usage.length - 2);
params = paramString.readMacroParams();
pParams = paramString.parseParams("url",null,true,false,true);
urlParam = pParams[0]["url"][0];
nodeName = this.checkNodeNotation(urlParam);
urlParam = nodeName ? this.getNodeUrl(nodeName) : urlParam;
// keep only inclusions that contain IDs corresponding to urls from twsToReload
dontReload = true;
if(twsToReload) {
for(i = 0; i < twsToReload.length; i++)
if(urlParam == twsToReload[i])
dontReload = false;
} else
dontReload = false;
// exclude inclusions with import and eval, if necessary
if(!reloadParams.import && getParam(pParams,"import",undefined) ||
!reloadParams.eval && params.contains('eval') ||
!reloadParams.wikify && params.contains('wikify'))
dontReload = true;
if(dontReload) {
delete usages[usage];
continue;
}
// clean stuff: unload included store, clear priorities, remove conflicts;
// deleting "used" marks is outside this loop
includedStores[urlParam] = undefined;
fetchPreferences.removeByUrl(urlParam);
conflicts.markNodeUnloaded(urlParam);
}
// delete "used" marks
this.includeUsages.markUnused(usages);
// include again
for(usage in usages)
wikify(usage,document.createElement("div"),undefined,null);
};
};
twWeb = new TwWeb();
//================================================================================
// Default Progress Handling for config.extensions.SharedTiddlersPlugin
var showAnimated = function(e, showing, duration) {
// if (!anim || !abego.ShowAnimation) {
e.style.display = showing ? "block" : "none";
return;
// }
// anim.startAnimating(new abego.ShowAnimation(e,showing,duration));
};
config.extensions.SharedTiddlersPlugin.getDefaultProgressFunction = function() {
setStylesheet(
".includeProgressState{\n"+
"background-color:#FFCC00;\n"+
"position:absolute;\n"+
"right:0.2em;\n"+
"top:0.2em;\n"+
"width:7em;\n"+
"padding-left:0.2em;\n"+
"padding-right:0.2em\n"+
"}\n",
"stp_Include");
var createStateElem = function() {
var e = document.createElement("div");
e.className = "includeProgressState";
e.style.display = "none";
document.body.appendChild(e);
return e;
};
var stateElem = createStateElem();
var showState = function(message) {
removeChildren(stateElem);
createTiddlyText(stateElem,message);
showAnimated(stateElem,true,0);
};
var hideState = function() {
// hide the state the next idle time
invokeLater(function() {
showAnimated(stateElem,false,ANI_DURATION_HIDE_STATE);
},100,ANIMATION_PRIORITY);
};
var myProgressFunction = function(message, sender, state, url, params) {
if (state == "Done" || state == "Failed") {
hideState();
return;
}
if (sender == "stp_loadTiddlyWikiStore") {
idleCount = 0;
if (state == "Processing")
showState("Including...");
} else {
showState(message);
}
};
return myProgressFunction;
};
twWeb.setProgressFunction(config.extensions.SharedTiddlersPlugin.getDefaultProgressFunction());
//================================================================================
// The "describeNode" macro
//
// Syntax: <<describeNode nodeName {nodeUrl|self}>>
//
config.macros.describeNode = {};
config.macros.describeNode.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var macroTWcode = wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch);
createTiddlyText(createTiddlyElement(place,"code"),macroTWcode);
// node description duplicates are handled when adding, so no "handle only once" here
var includeURL = tiddler.getIncludeURL(),
nodeName = params[0],
urlParam = params[1],
self = (urlParam == "self");
if(self) {
var oldSelf = twWeb.getSelfNodeName();
if(oldSelf && (oldSelf != nodeName))
return alert("The \'"+oldSelf+"\' alias is already assigned as the name of the current "+
"TiddlyWiki; the new attempt to assign \'"+nodeName+"\' is ignored.");
twWeb.setSelfNodeName(nodeName);
return;
}
var url = resolveUrlFrom(urlParam, includeURL);
url = stp_resolveURL(url); // if no includeURL
twWeb.addNodeDesc(nodeName,url);
};
//================================================================================
// The "include" macro
//
// Syntax: <<include [url:]url [filters:filterLine] [substitute]
// [substituteShadows] [hide:hideFlag] [delay:delayDuration]>>
//
config.macros.include = {};
config.macros.include.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var macroTWcode = wikifier.source.substring(wikifier.matchStart, wikifier.nextMatch),
pParams = paramString.parseParams("url",null,true,false,true); // allowEval, cascadeDefaults, names allowed
var hide = getFlag(pParams, "hide", false) || params.contains('hide');
if (!hide)
createTiddlyText(createTiddlyElement(place,"code"),macroTWcode);
if (twWeb.includeUsages.isUsed(macroTWcode))
return;
twWeb.includeUsages.setUsed(macroTWcode);
var urls = pParams[0]["url"],
includeParams = {
delayMilliSeconds: parseInt(getParam(pParams,"delay","0")),
filterLine: getParam(pParams,"filters","[all[-]]"),
substitute: params.contains('substitute'),
substituteShadows: params.contains('substituteShadows'),
noRefresh: params.contains('noRefresh'),
importMode: getParam(pParams,"import",undefined),
evalTiddlers: params.contains('eval'),
wikifyTiddlers: params.contains('wikify')
};
var checkUrlAndInclude = function(url) {
if(url == 'hide' || url == 'substituteShadows' || url == 'substitute' || url == 'eval' ||
url == 'wikify' || url == 'noRefresh')
return;
twWeb.include(url,includeParams);
};
for (var i = 0; urls && i < urls.length; i++)
checkUrlAndInclude(urls[i]);
};
//================================================================================
// The "reloadIncluded" macro
//
// Syntax: <<reloadIncluded [urls:urlsJSON] [reloadParams:otherReloadParamsJSON]
// [label:labelText] [tooltip:tooltipText] [class:className]>>
// (for reloadParams, see twWeb.reload)
//
config.macros.reloadIncluded = {};
config.macros.reloadIncluded.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
// parse params
var pParams = paramString.parseParams("url",null,true,false,true),
label = getParam(pParams,"label","refresh"),
tooltip = getParam(pParams,"tooltip",undefined),
elClass = getParam(pParams,"class"," "), // " " overwrites the default "button" class
urlsText = getParam(pParams,"urls",undefined),
otherReloadParamsText = getParam(pParams,"reloadParams","{}"),
reloadParams = JSON && JSON.parse(otherReloadParamsText) || jQuery.parseJSON(otherReloadParamsText);
reloadParams.urlOrNodeList = !urlsText ? undefined :
(JSON && JSON.parse(urlsText) || jQuery.parseJSON(urlsText));
if(!tooltip) {
if(reloadParams.urlOrNodeList) {
tooltip = "refresh '"+reloadParams.urlOrNodeList[0]+"'";
for(var i = 1; i < reloadParams.urlOrNodeList.length; i++)
tooltip += ", '"+reloadParams.urlOrNodeList[i]+"'";
tooltip += (i > 0) ? " nodes" : " node";
} else
tooltip = "refresh all included nodes";
}
// create button, add handler
createTiddlyButton(place,label,tooltip,function(){
var returnHere = function() {
if(twWeb.hasPendingIncludes()) {
invokeLater(returnHere,100);
return;
} // wait until all the stores are loaded and the page is refreshed
var t = tiddler.title, te = DEFAULT_VIEW_TEMPLATE;
story.displayTiddler(this,t,story.chooseTemplateForTiddler(t,te));
}
twWeb.reload(reloadParams);
invokeLater(returnHere,100); // wait a bit for the nodes to unload
},elClass);
};
//================================================================================
// The "includeState" macro
//
// Syntax: <<includeState>>
//
config.macros.includeState = {};
config.macros.includeState.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var getFullState = function () {
var s = "";
var includes = twWeb.getIncludedStoresUrls();
if (!includes.length)
return "{{noIncludes{\nNo TiddlyWiki is included or including is disabled (see AdvancedOptions)\n}}}\n";
s += "|!Address|!State|\n";
for (var i = 0; i < includes.length; i++) {
var inc = includes[i];
s += "|{{{"+inc+"}}}|";
var t = twWeb.getState(inc);
s += t ? "{{{"+t+"}}}" : "included";
s += "|\n"
}
s += "|includeState|k\n";
return s;
};
var updateState = function(){
removeChildren(div);
wikify(getFullState(),div);
if (twWeb.hasPendingIncludes())
invokeLater(updateState,500,UPDATE_STATE_PRIORITY);
};
var div = createTiddlyElement(place,"div");
invokeLater(updateState,0,UPDATE_STATE_PRIORITY);
};
//================================================================================
// Change standart formatters
var getFormatterIndex = function(formatterName) {
for(var i = 0; i < config.formatters.length; i++)
if(config.formatters[i].name == formatterName)
return i;
return null;
}
//--------------------------------------------------------------------------------
// Change the prettyLink formatter so that it
// * recognizes [[text|target]]@nodeName and [[target]]@nodeName syntax
// * takes into account includeURL of the tiddler and propagates inclusion
if(config.options.STP_hijackPrettyLink) {
var prettyLinkFormatterIndex = getFormatterIndex("prettyLink");
config.extensions.SharedTiddlersPlugin.orig_prettyLinkFormatter = config.formatters[prettyLinkFormatterIndex];
config.formatters[prettyLinkFormatterIndex] = {
name: "prettyLink",
match: "\\[\\[",
lookaheadRegExp: /\[\[(.*?)(?:\|(~)?(.*?))?\]\](?:(?:@(\w+))(?:@([\w\s\:]+)@)?)?/mg,
handler: function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source);
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
// call the formatter
config.extensions.SharedTiddlersPlugin.orig_prettyLinkFormatter.handler(w);
// call the include.handler, if necessary
var node = lookaheadMatch[4]? lookaheadMatch[4] : undefined,
includeURL = w.tiddler? w.tiddler.getIncludeURL() : null,
urlParam = node? twWeb.setNodeNotation(node) : (includeURL? includeURL : undefined),
target = lookaheadMatch[3]? lookaheadMatch[3] : lookaheadMatch[1],
paramString = '"'+urlParam+'" filters:"[['+target+']]" hide noRefresh ';
if(lookaheadMatch[5])
paramString += lookaheadMatch[5];
if(urlParam)
config.macros.include.handler(w.output,"include",
paramString.readMacroParams(true),w,paramString,w.tiddler);
// move nextMatch according to this.lookaheadRegExp, not original prettyLink
w.nextMatch = this.lookaheadRegExp.lastIndex;
}
}
};
}
//--------------------------------------------------------------------------------
// Change the image formatter so that it
// * takes into account includeURL of the tiddler and recalcs relative urls
if(config.options.STP_hijackImageFormatter) {
var imageFormatterIndex = getFormatterIndex("image");
config.extensions.SharedTiddlersPlugin.orig_imageFormatterHandler = config.formatters[imageFormatterIndex].handler;
config.formatters[imageFormatterIndex].handler = function(w) {
var lastChildBeforeHandling = w.output.lastChild;
config.extensions.SharedTiddlersPlugin.orig_imageFormatterHandler.apply(this,arguments);
if(w.output.lastChild != lastChildBeforeHandling) {
var img = w.output.lastChild,
includeURL = w.tiddler ? w.tiddler.getIncludeURL() : "",
imgSrc = jQuery(img).attr("src");
// take includeURL into account:
img.src = resolveUrlFrom(imgSrc, includeURL);
}
}
}
//================================================================================
// Add inline-management tools by hijacking .edit.handler
config.extensions.SharedTiddlersPlugin.orig_editHandler = config.macros.edit.handler;
config.macros.edit.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var includeUrl = tiddler.getIncludeURL();
if(params[0] == "text" && includeUrl){ // only for "text", not other fields
var e = createTiddlyElement(null,"div");
e.className = "manageIncludedPanel";
createTiddlyText(e,"manage the included tiddler: ");
// go to the source
var sourceUrl = includeUrl + "#[["+tiddler.title+"]]";
createExternalLink(e, sourceUrl, "open in the source TiddlyWiki");
// view the link to the source
createTiddlyText(e," (");
createTiddlyButton(e,"view link","view the link to the source",function(e){
var popup = Popup.create(this);
createTiddlyText(popup,sourceUrl);
Popup.show();
var ev = e || window.event;
ev.cancelBubble = true;
if(ev.stopPropagation)
ev.stopPropagation();
return false;
}," ");
createTiddlyText(e,")");
// import
if(!readOnly) {
createTiddlyText(e," | ");
createTiddlyButton(e,"import","import this tiddler",function(){
twWeb.importAndLog(tiddler,1);
}," ");
}
// reload
createTiddlyText(e," | ");
config.macros.reloadIncluded.handler(e,"",null,null,'urls:\'["'+includeUrl+'"]\'',tiddler);
// other actions
// if the read only mode is not set, display all tools otherwise ...
place.appendChild(e);
}
return config.extensions.SharedTiddlersPlugin.orig_editHandler(place,macroName,params,wikifier,paramString,tiddler);
};
//================================================================================
// Perform plugin startup tasks
// add this for the "install only once" check (which also prevents conflicts with abego.IncludePlugin)
// (this is also deprecated API for backward compability)
abego.TiddlyWikiIncluder = {
getIncludes: twWeb.getIncludedStoresUrls,
getState: twWeb.getState,
getStore: twWeb.getStore
};
attachToStore();
invokeLater(includeFromIncludeList,100);
// add several more methods to the "API namespace"
window.sharedTiddlersAPI.getIncludes = twWeb.getIncludedStoresUrls;
window.sharedTiddlersAPI.getState = twWeb.getState;
window.sharedTiddlersAPI.getStore = twWeb.getStore;
window.sharedTiddlersAPI.importAndLog = twWeb.importAndLog;
// iterates over all tiddlers of "the store" and all tiddlers of included (and loaded) stores
//
window.sharedTiddlersAPI.forReallyEachTiddler = function(callback) {
var caller = function() {
store.forEachTiddler(callback);
};
getFunctionUsingForReallyEachTiddler(caller).call(store);
};
})();
//%/ //
|''Name''|SharedTiddlersPluginInfo|
|''Source''|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPluginInfo|
|''Description''|Documentation for ~SharedTiddlersPlugin|
|''Plugin source''|http://yakovl.bplaced.net/TW/STP/STP.html#SharedTiddlersPlugin|
|''Version''|2.4.0|
|''Author''|Yakov Litvin|
|''License''|[[BSD-like open source license|http://yakovl.bplaced.net/TW/STP/STP.html#%5B%5BYakov%20Litvin%20Public%20Licence%5D%5D]]|
|''Contact''|Yakov.A.Litvin at gmail dot com (please read docs, esp. "Installation, configuring and troubleshooting" section before sending an email)|
<<tabs txtSharedTiddlersPluginInfoTab
"Installation, configuring and troubleshooting" "" [[SharedTiddlersPluginInfo##Installation, configuring and troubleshooting]]
"Functionality and examples" "" [[SharedTiddlersPluginInfo##Functionality and examples]]
"Detailed reference" "" [[SharedTiddlersPluginInfo##Detailed reference]]
"More details on possible applications" "" [[SharedTiddlersPluginInfo##More details on possible applications]]
"API changes and coding with SharedTiddlersPlugin" "" [[SharedTiddlersPluginInfo##API changes and coding with SharedTiddlersPlugin]]
{{"Making other plugins \"include-aware\""}} "" [[SharedTiddlersPluginInfo##Making other plugins include-aware]]
"Versions history and quality issues" "" [[SharedTiddlersPluginInfo##Versions history and quality issues]]
>>/%
!Functionality and examples
<html><h1>Including and include macro</h1>
<h2>Simple including</h2>
</html>Including is done via the {{{include}}} macro. For instance, a ~TiddlyWiki you'd like to include from is named {{{references.html}}} and is put in the same folder that another one, {{{main.html}}}, you'd like to include to. To include all the tiddlers from {{{references.html}}}, put the following macro in {{{main.html}}}:
{{{
<<include "./references.html">>
}}}
Where exactly should you write the macro? Usually, {{{include}}} macros are written in IncludeList: all the macros in there are wikified at startup, so the secondary TW is included each time the main one is opened. Alternatively, you can put {{{include}}} macro in an arbitrary tiddler, in that case including will be done when the tiddler is displayed (wikified). If you want to avoid it being visible, write it as
{{{
<<include "./references.html" hide>>
}}}
<html><h2>Partial including</h2>
</html>There are various reasons why you may prefer to include only a subset of tiddlers from another TW. For instance, you want to include only tiddlers tagged {{{ToDo}}} to your GTD thing and don't want the timeline to get crowded by other tiddlers from outside. For that case {{{include}}} macro supports core filter engine:
{{{
<<include "./projects/myProject.html" filters:"[tag[ToDo]] [[Summary]]">>
}}}
For more details about filters, see the "Detailed reference" part of the docs.
<html><h2>Substituting tiddlers in main TiddlyWiki</h2>
</html>By default, if the including and an included TW both have a tiddler named "A", the tiddler from the main ~TiddlyWiki will be displayed/fetched in all the situations; even if in the main TW only a shadowed tiddler "A" is present. As sometimes this is not a desirable behaviour, there's {{{substitute}}} and {{{substituteShadows}}} options in the {{{include}}} macro:
{{{
<<include "./commons.html" filters:"[[PageTemplate]] [[ViewTemplate]]" substituteShadows>>
}}}
will bring PageTemplate and ViewTemplate instead of the shadowed ones; and
{{{
<<include "./commons.html" filters:"[[PageTemplate]] [[ViewTemplate]]" substituteShadows substitute>>
}}}
will do so even if custom PageTemplate or ViewTemplate are created. Note: included tiddlers are not saved in the including TW, unless the {{{import}}} option is used (see below).
<html><h2>Import and autoimport of included tiddlers</h2>
</html>For some tiddlers, you'd probably prefer importing instead of just including -- for this case a "manual" tool ImportIncluded and autoimport via the {{{include}}} macro are present:
{{{
<<include "./local_repositories/STP.html" filters:"[[SharedTiddlersPlugin]]" import:4>>
}}}
where {{{4}}} is a number of the import mode. For details, see the "Detailed reference" part.
<html><h2>Launching (wikifying) macros from included tiddlers</h2>
</html>This feature was designed primarily for centralized system of customization of ~TiddlyWikis. Imagine a TW called "commons.html" which has different parts of interface (like PageTemplate or ToolbarCommands), some evaluated transclusions etc. The idea is do changes to that TW so that they are applied in all your ~TWs. You can include all the elements in other ~TWs but the problem is you still have to make changes in all the ~IncludeLists. The solution is brought by introducing the {{{wikify}}} parameter to the {{{include}}} macro. Consider this: IncludeList in each of your ~TWs contains just
{{{
<<include "./commons.html" filters:"[[IncludeHub]]" wikify>>
}}}
and all the changes are done in the {{{IncludeHub}}} tiddler in your commons.html like these:
{{{
<<include "./commons.html" filters:"[[PageTemplate]]" substituteShadows>>
<<include "./local_repositories/STP.html" filters:"[[SharedTiddlersPlugin]]" import:4>>
// you can remove, change or add macros in here so that they are applied in each of your TWs
}}}
The only issue is as long as you use relative addresses, you have to place all your ~TWs in the same folder as "commons.html" (how to pass around this limitation, see below).
<html><h2>Launching plugins from included TiddlyWikis</h2>
</html>Plugin launching is done on startup, so just including external plugins is "too late" and they won't actually work. To launch a plugin from an included tiddler, use {{{eval}}} parameter of the {{{include}}} macro:
{{{
<<include "./commons.html" filters:"[[RearrangeTiddlersPlugin]]" eval>>
}}}
For many plugins, most likely, you will use both {{{improt:4}}} and {{{eval}}} parameters.
<html><h2>Reloading included stuff</h2>
</html>Sometimes it's desirable to reload included tiddlers without reloading the whole TW document. For this case, the {{{reloadIncluded}}} macro is introduced. It generates a link/button which reloads tiddlers from certain ~TWs on click:
{{{
<<reloadIncluded urls:'["node: node1", "included test nodes/node 2.html"]' reloadParams:'{"import":false}'>>
}}}
Also, this link can be found in the toolbar of included tiddlers (in the edit mode).
<html><h2>Avoiding excessive refreshing</h2>
</html>After including, the {{{include}}} macro refreshes your TW -- this is done for things like ~StyleSheets or ~ViewTemplate to get applied. However, multiple refreshing on startup can take a second or more, so it can be desirable to avoid refreshing. It is recommended to use the corresponding {{{noRefresh}}} parameter with all {{{include}}} macros that don't need it (usually those including content tiddlers, but not elements of themes, like ~ViewTemplate):
{{{
<<include "./references/holy writ.html" filters:"[[contents]] [tag[chapter]] [tag[verse]]" noRefresh>>
<<include "./references/Feuerbach.html" filters:"[[list of publications]] [tag[publication]]" noRefresh>>
<<include "./commons.html" filters:"[tag[theme parts]]" substitute substituteShadows>>
}}}
<html><h1>TwWeb</h1>
</html>Sometimes it is desirable to rename or move ~TiddlyWikis, and such changes can break many links/includes. Also, using relative paths in {{{include}}} macros can cause errors in the path calculation when the macro itself is contained in an tiddler included from "path/A.html", where "path" is not ".". To avoid these pesky issues and to enable further "merging" of ~TWs, ~TwWeb system was introduced.
<html><h2>Describing a node</h2>
</html>The idea is to describe "nodes" -- ~TWs with certain addresses and then use node names instead of addresses. The {{{describeNode}}} does the first part of the job:
{{{
<<describeNode "commons" "./commons.html">>
}}}
where the first argument is the name of the node and the second one is its address. It is recommended to give your nodes names without spaces (for compability with future features). Note that you can use {{{include}}} macro with {{{wikify}}} parameter to take node descriptions from another TW. This means that you can create a central TW with all node descriptions, include it, and use in all other ~TWs node names instead of addresses. After that, renaming a TW should be backed up by only changing the description in the central node.
<html><h2>Support of node names</h2>
</html>Node names are supported in the following mechanisms:
# {{{include}}} macro can get a special form of the url parameter:{{jD{
{{{
<<include "node: node_name" ...>>
<<include "node: commons" filters:"[tag[theme parts]]" substitute substituteShadows>>
}}}
}}} If the macro is wikified before the node was described, it "waits" for the description to appear.
# ImportIncluded allows this "form of url" too
# the {{{includedFrom}}} filter supports the {{{[includedFrom[node: nodeName]]}}} notation
# extended prettyLinks now work in the following syntaxes:
## {{{[[link text|target tiddler]]@nodeName}}} -- which includes the {{{target tiddler}}} and creates an ordinary {{{[[link text|target tiddler]]}}} prettyLink
## {{{[[link text|target tiddler]]@nodeName@options line@}}} which does the same but uses options from the {{{options line}}} as well: for instance, to substitute an existing tiddler {{{target tiddler}}}, just add the options line {{{substitute}}}.
** if the included TW is large so you're delayed waiting when the link get enabled, it can be an option to include the node with an empty filter ({{{filters:""}}}) using IncludeList (may be with the delay option, too)
* @@font-size:smaller;In the future versions of STP, some other engines may be supported: for instance, {{{<<tiddler [[tiddler name]]@nodeName>>}}}.@@
<html><h2>Describing self</h2>
</html>To avoid different side effects of transitive inclusion (like including a tiddler by TW "A" from the same TW "A" with substituting which makes it impossible to edit the tiddler) you can mark one nodeName as "self":
{{{
<<describeNode nodeName self>>
}}}
This prevents from including from {{{nodeName}}} at all; such self-description should be put into the IncludeList.
<html><h2>Propagation</h2>
</html>As included tiddlers can contain links, images, transclusions etc, STP takes care of those so that they are not broken (to some extent). See details in the "Detailed reference" section.
<html><h2>Inline management of included tiddlers</h2>
</html>In the edit mode an included (but not imported) tiddler has an additional panel with a link to its "original" in the included TW, a link which imports it and a refresh link that reloads the included TW. This is added by default, no changes to EditTemplate are needed.
!Detailed reference
<html><h2>IncludeList tiddler</h2>
</html>Special tiddler IncludeList is used to wikify macros within it on the startup of ~TiddlyWiki: it's implied that you write {{{<<include>>}}} and {{{<<describeNode>>}}} macros that you need to lauch (each time) there. For instance, if you write
{{{
<<include "./common TW elements.html" filters:"[[PageTemplate]]" substituteShadows>>
}}}
in there, the including will be done each time the TW is opened.
<html><h2>The "include" macro</h2>
</html>The {{{<<include>>}}} macro has the following syntax (@@font-size:smaller;[...] parts are optional@@):
{{{
<<include [url:]url [filters:filterLine] [substitute] [substituteShadows]
[import:importMode] [eval] [wikify] [noRefresh] [hide[:hideFlag]] [delay:delayDuration]>>
}}}
|The parameters of the {{{<<include>>}}} macro|c
| name | demanded? | value | effect | comments |h
| ''url'' | demanded | a url line in quotes | defines the path to the ~TiddlyWiki from which stuff is included | by "url line" a relative path, an absolute local file path, or a "file:" URL is meant |
| ''filters'' | optional | a line of TW filters in quotes | defines the set of tiddlers included from the TW;<br>if omitted, means "include all" | see "Filters" below |
| ''substitute'' | optional | none | adds "substitute" priority to the included tiddlers | see "Name conflicts and priorities" below |
| ''substituteShadows'' | optional | none | adds "substituteShadows" priority to the included tiddlers |~|
| ''import'' | optional | an id of an import mode to use | tiddlers are imported instead of including | see "Import tools" below |
| ''eval'' | optional | none | treats tiddlers as plugins | see "Evaluating tiddlers" below |
| ''wikify'' | optional | none | wikifies tiddlers after loading/importing | can be used to wikify {{{include}}} and other macros;<br>may also be useful with evaluated transclusions |
| ''noRefresh'' | optional | none | prevents refreshing of TW after tiddlers are loaded | refreshing is used to apply included style sheets, elements of interface etc, but multiple refreshing can slow down the startup time; to avoid such effects, this option is to be used in macros that include only content (not parts of the interface) |
| ''hide'' | optional | boulean | by default the macro is displayed as written; when {{{hideFlag}}} is {{{true}}}, it's hidden | can be useful when the macro is used outside ~IncludeList |
| ''delay'' | optional | number (integer) | defines the number of milliseconds (thousands of a second) to wait before actually including the ~TiddlyWiki ||
Some examples of how the ''url'' line can be written:
{{{
<<include "C:\TiddlyWiki\shared\TiddlyWikiHelp.html" hide:true>>
<<include "file://C:/Private/journals/2006-12.html" delay:8000>>
<<include "../tests/STP/sample33.html" hide>>
<<include "tests/main_sample.html">>
}}}
__Note__: some browsers forbid including from outside the folder where the including TW is placed. See details in the "Installation, configuring and troubleshooting" section.
<html><h2>Filters</h2>
</html>See main documentation [[at tiddlywiki.org|http://tiddlywiki.org/#Filters]]. Note: when the {{{tiddler}}} filter is used, if tiddler with {{{tiddlerName}}} doesn't exist, the filter creates a new empty tiddler and adds it to the set. To avoid problems like blank ~PageTemplate etc, tiddlers which don't exist are not included (or imported) even if the {{{tiddler}}} filter would cause otherwise.
STP introduces 3 new filters:
* {{{all}}} (mostly for inner purposes) which brings all the tiddlers of a store. Usage:
** {{{[all[with included]]}}} fetches ordinary and included tiddlers (with regard to "substituting")
** {{{[all[-]]}}} (with any non-empty argument other than "with included") fetches only ordinary tiddlers (without regard to "substituting")
** Example: {{{[all[-]] [sort[modified]]}}}.
* {{{includedFrom}}} which retains only tiddlers from a certain included store. Examples: {{{[tag[ToDo]] [includedFrom[project.html]], [tag[ToDo]] [includedFrom[node: project]]}}}.
* {{{internal}}} which filters out included tiddlers. Example: {{{[tag[ToDo]] [internal[-]]}}}.
* {{{external}}} which leaves only included tiddlers. Example: {{{[all[with included]] [external[-]]}}}.
Example of usage with the {{{include}}} macro:
{{{
<<include "./biology.html" filters:"[tag[Mammals]] [[Mammals]]">>
}}}
<html><h2>Name conflicts and priorities</h2>
</html>Sometimes there are tiddlers with a common name in the main and an included or multiple included ~TiddlyWikis -- this may occur "accidentally" or because one needs to substitute a tiddler in the main ~TiddlyWiki by an included one (like shared ~PageTemplate). In such cases the following rules work:
* each tiddler in each store has or doesn't have some priorities which is defined when the tiddler is included
* shadowed tiddlers are substituted only by tiddlers with the "substituteShadows" priority
* tiddlers inside the main ~TiddlyWiki substitute those in included ones, unless those have "substitute" priority, in which case it is otherwise
* ({{{sh ambiguity}}}) if there are multiple tiddlers with "substituteShadows" and the shadow presents, there's no specific rule that defines which one would be fetched instead of the shadow; if the shadow with such name doesn't exist, those are treated as included tiddlers without "substituteShadows"
* ({{{s ambiguity}}}) if there are multiple tiddlers with "substitute", the same thing applies: a "random" one is chosen (this actually depends on the order of the handling of {{{<<include>>}}} macros)
* ({{{ambiguity}}}) the same is true for tiddlers included with no added priorities
For tracking conflicts, a notification system is present:
* notifications appear if at least one of these parameters is set to {{{true}}} (see the "tweak" panel in the sidebar):
** {{{chkWarnOnSharedTiddlersConflicts}}} -- displays messages (this is usually interrupted by refreshing engines) and writes them to the browser console
** {{{chkAlertOnSharedTiddlersConflicts}}} -- alerts (in contrast to displaying messages, you won't lose this ones, but you have to click "ok" after each one, so this is to be used when setting up)
* for some reason (probably some page-refreshing called from out of the plugin) notifications are not displayed each time conflicts appear, or are removed too fast (when displaying fails logs and alerts can help)
* notifications are displayed in each case: {{{sh ambiguity}}}, {{{s ambiguity}}}, {{{ambiguity}}} and when there's a tiddler T in the main store and one tiddler with the same name in an included store without the "substitute" priority (in the last two cases the tiddler in the main store is also described as "conflicting" for the ease of understanding what's going on, despite the fact it is fetched for sure, not any included one)
* it is implied that shadows are not added after {{{<<include>>}}} macros are handled (otherwise some notifications will appear as in the {{{ambiguity}}} case while after a shadow is added it can become the {{{sh ambiguity}}})
<html><h2>The "reloadIncluded" macro and reloading</h2>
</html>The {{{reloadIncluded}}} macro creates a "link" which causes reloading of the corresponding ~TWs: the content is unloaded and then previously wikified {{{include}}} macros are handled again. Syntax:
{{{
<<reloadIncluded [urls:urlsJSON] [reloadParams:otherReloadParamsJSON]
[label:labelText] [tooltip:tooltipText] [class:className]>>
}}}
The {{{label}}}, {{{tooltip}}} and {{{class}}} parameters are sefl-explaining; it's worth mentioning the {{{button}}} class which is used in macros like {{{tag}}} and {{{slider}}}. The other two parameters should be in the JSON format, like this (note the double quotes, esp. around {{{import}}}):
{{{
urls:'["node: node1", "included test nodes/node 2.html"]' reloadParams:'{"import":false}'
}}}
The {{{urls}}} defines the ~TWs to reload (if not present, means "reload all"). The {{{reloadParams}}} part defines whether to wikify again {{{include}}} macros with the {{{import}}}/{{{wikify}}}/{{{eval}}} params, by default only {{{eval}}} is set to false to avoid repeated evaluation of shared plugins (and in this example {{{import}}} is set to {{{false}}} as well).
<html><h2>The "includeState" macro</h2>
</html>The {{{<<includeState>>}}} macro can be used to see the progress of inclusion of ~TiddlyWikis (e.g. "included" for those already loaded, or a text of an error for others etc). There's a shadow IncludeState where you can see it in work.
<html><h2>Import tools</h2>
</html>As some plugins and other things should present inside each tw-document rather than be "shared" (like [[NestedSlidersPlugin|http://www.TiddlyTools.com/#NestedSlidersPlugin]], without which some content will be "spoiled"), tools to import/autoimport tiddlers from other ~TiddlyWikis are introduced.
Importing can be done in different __import modes__, currently the following are available:
* {{{1}}} means "import anyway, even if a tiddler with such name is present" (useful, for instance, as "silent mode for plugins")
* {{{2}}} means "skip import if a version of this tiddler in the main store has 'modified' date no less than that of importing one"
* {{{3}}} means "import on confirm of the user"
* {{{4}}} combines the requirements of the modes {{{2}}} and {{{3}}} for import to happen (useful as "totally controlled" import, say for autoupdating plugins from a central TW)
The results of import are currently only logged in a browser's console.
There are 2 ways to import:
* autoimport via the {{{include}}} macro (with the {{{import:importMode}}} argument, import is done instead of including)
* manual import via the tool in the ImportIncluded shadow (using a url, a filter and a mode; the default is the {{{1}}} mode)
Don't forget to save your TW if this is needed (STP sets the usual TW reminder of unsaved changes, but only for browsers which support it)
An example:
{{{
<<include "./commons/plugins.html" filter:"[tag[commonInPlacePlugins]]" import:4>>
}}}
<html><h2>Evaluating tiddlers</h2>
</html>The {{{eval}}} parameter of the {{{<<include>>}}} macro allows to use plugins included from other ~TWs. For instance,
{{{
<<include "commonPlugins.html" filters:"[[CollapseTiddlersPlugin]] [[RearrangeTiddlersPlugin]]" eval>>
}}}
will load two plugins and evaluate them as ~JavaScript (they can be without {{{systemConfig}}} tag). In current implementation, some differences from the conventional way to launch plugins are present: plugins are not registered (no entries in the backstage), no dedicated reaction on errors; also, there's no check preventing some plugin to launch twice or more. Note: if evaluated, tiddlers still will be included or imported.
<html><h2>"include" macros outside the IncludeList and transitive inclusion</h2>
</html>While {{{<<include>>}}} macros inside the ~IncludeList tiddler are handled at the startup, one can also place them in other tiddlers so that new parts are included when the tiddlers are displayed (the ''hide'' option can be useful here). This can save some time at startup; {{{<<include>>}}} macros outside ~IncludeList also provide the possibility of transitive inclusion (see below). However, sometimes opening a tiddler with an {{{<<include>>}}} macro can take several (mili)seconds.
When ~TiddlyWiki B is included to ~TiddlyWiki A, even if ~IncludeList is included with substitution, this won't bring tiddlers included to B in A, because the startup action is already done (see {{{wikifyIncluded}}} macro, though). However, it's possible to get "transitive inclusion" in the following meaning: add some tiddler with {{{<<include>>}}} in B (for instance, a tiddler "Fruits" with the follwing content:
{{{
All fruits: <<include "./food.html" filters:"[tag[Fruit]]" hide>>
<<tag [[Fruit]]>>
}}}
where "./food.html" is a third ~TiddlyWiki), then if you include this tiddler to A, you will get all the "Fruit"-tagged tiddlers included in A and the tiddler "Fruits" will be displayed as in B (*if* A and B are in the same directory -- otherwise the "./food.html" path will be incorrect in A).
<html><h2>Including remote TiddlyWikis</h2>
</html>Although the documentation of the ~IncludePlugin sais that it's possible to include some remote ~TiddlyWikis into a local one, in modern browsers this seemingly no longer works (tested in IP 1.0.1 and STP 1.3 + Opera 12.0-16.0 and ~FireFox 13.0.1). On the other hand, when running a ~TiddlyWiki on a webserver, one can include ~TWs from the same domain, but can't include any TW from a different website (because of so-called "~Cross-Site Scripting" security issue). @@font-size:smaller;Including remote ~TiddlyWikis into a local one may be reintroduced in the future, but this requires changing the load engine which means some extensive search. Any feedback or ideas regarding this issue are particularly appreciated.@@
<html><h2>Making secondary TiddlyWikis smaller</h2>
</html>In some cases included ~TWs can be supposed not to get changed (like archived pieces of journal or news) and be desired to be smaller (especially when placed at a web-site) at the same time. In such cases included ~TiddlyWikis can be reduced to the [[PureStore|http://yakovl.bplaced.net/TW/STP/STP.html#PureStore]] format.
<html><h2>The TwWeb and the describeNode macro</h2>
</html>The {{{describeNode}}} macro addes a description of a node (a ~TiddlyWiki) to the ~TwWeb. Minimal description consists of the name of the node and its address:
{{{
<<describeNode "nodeName" "path to the node/file.html">>
}}}
Once a node is described, its name can be used instead of its url. This is supported in the {{{include}}} macro:
{{{
<<include "node: nodeName" ...>>
}}}
If such macro is wikified before the node was described, it "waits" for the description to appear. This syntax also can be used in the ImportIncluded.
Also, this is supported in the extended form of prettyLinks:
{{{
[[link text|target tiddler]]@nodeName
}}}
where {{{nodeName}}} contains alphanumeric symbols (latin letters, numbers and {{{_}}} symbols), is equivalent to
{{{
<<include "node: nodeName" filters:"[[target tiddler]]" hide noRefresh>>
[[link text|target tiddler]]
}}}
(except no linebreak is added) and
{{{
[[link text|target tiddler]]@nodeName@param string@
}}}
is equivalent to
{{{
<<include "node: nodeName" filters:"[[target tiddler]]" hide noRefresh param string>>
[[link text|target tiddler]]
}}}
where {{{param string}}} is copied "as is" (to get {{{import:4}}} option of the macro, write param string {{{import:4}}} etc). There's a limitation: {{{param string}}} is only recognized when consists of alphanumeric symbols, space symbols and {{{:}}} (so evaluated parameters are not allowed).
There's another function of the {{{describeNode}}} macro: when used like this
{{{
<<describeNode nodeName self>>
}}}
it sets the name {{{nodeName}}} as the name of the main ~TiddlyWiki. Any including from the node {{{nodeName}}} (which may be caused by an included tiddler) won't work. This only works once meaning that the following markup
{{{
<<describeNode nodeName1 self>>
<<describeNode nodeName2 self>>
}}}
will result in setting {{{nodeName1}}} as the name of the main TW and the second macro will alert about itseft but do nothing else.
----
By ~TwWeb a system that handles all the nodes stuff is meant (not to confuse with ~TiddlyWeb). ~TwWeb is a part of STP.
<html><h2>Propagation</h2>
</html>STP prevents some parts of included tiddlers to get broken because they are in the context of the including TW, not the included one. What currently STP does:
* pretty links without {{{@nodeName}}} in included tiddlers, when displayed, cause including of corresponding tiddlers (so links are not broken). This is another part of the hijacking the "prettyLink" formatter and is optional (see "Installation, configuring and troubleshooting" section)
* image formatter ({{{[img[path][link]]}}} syntax) now takes care of relative links ({{{path}}} part is recalced from the including path). This is also optional, "Installation, configuring and troubleshooting" section.
What is not currently implemented:
* no macros are taken care of -- even a transclusion won't work if the corresponding tiddler is not explicitly included (and, of course, more complex macros like {{{tag}}} or {{{list}}})
* name conflicts are not handled: pretty links just cause including, and if there's a tiddler with the same name inside including TW, the priority system applies (deciding which one will be displayed on click)
* (auto)import doesn't account any of these things, including pretty links
Within these docs, such mechanisms are called "propagation" (of links, images etc).
<html><h2>Inline management of included tiddlers</h2>
</html>In the edit mode an included tiddler has an additional panel to manage it. This is implemented by hijacking the {{{edit}}} macro, no changes to EditTemplate are needed. The panel has {{{manageIncludedPanel}}} that can be used for style settings.
!Installation, configuring and troubleshooting
<html><h2>Installation</h2>
</html>First, put the plugin inside your ~TiddlyWiki. This is done as always: import the SharedTiddlersPlugin tiddler (using ImportTiddlers or another engine) or copy it (don't forget the {{{systemConfig}}} tag). You'll need to restart your ~TiddlyWiki (refresh the page) to get it working -- then you may check if it's installed properly in the PluginManager. You can also import or copy this documentation to have it at hand when needed.
Second, add {{{<<include>>}}} macros to IncludeList and wherever you need them. As IncludeList is wikified when you view it, you don't need to restart the "main" ~TiddlyWiki, despite the fact that IncludeList is processed on startup. Add links, transclusions and other "connections" with included tiddlers, if necessary. Search, timeline, {{{<<tag>>}}} and other macros can be used as well.
Now you're ready to go. Remember that included tiddlers are readonly in the "main" ~TiddlyWiki, so you have to go to "original" ~TiddlyWikis to edit them.
<html><h2>Settings</h2>
</html>You may need to change one of these settings:
* <<option chkUseInclude>> {{{chkUseInclude}}} option -- uncheck this and reload your ~TiddlyWiki to stop all inclusions
* <<option chkWarnOnSharedTiddlersConflicts>> {{{chkWarnOnSharedTiddlersConflicts}}} option -- defines whether notifications of conflicts should be displayed and logged
* <<option chkAlertOnSharedTiddlersConflicts>> {{{chkAlertOnSharedTiddlersConflicts}}} option -- defines whether alerting shoud be used for notifying about conflicts
* extending prettyLinks is optional, too; because the hijacking is done at startup, the option is hardcoded: to change it, go to SharedTiddlersPlugin and in the {{{config.options.STP_hijackPrettyLink = true;}}} set {{{false}}} instead
* such is extending of the image formatter ({{{[img[path][link]]}}}): it's governed by the {{{config.options.STP_hijackImageFormatter = ...}}} line in SharedTiddlersPlugin
These along with links to IncludeList, IncludeState and these documentation in the web can be found in AdvancedOptions.
<html><h2>Troubleshooting</h2>
</html>If something goes wrong, you should check:
* core version of your TW: it must be no less than the ~CoreVersion value in the [[SharedTiddlersPlugin]]'s header. You can check it using the {{{<<version>>}}} macro. Also, if [[UpToDateFiltersPlugin|http://yakovl.bplaced.net/TW/ExtraFilters.html#UpToDateFiltersPlugin]] is not installed, it must be 2.6.2 or greater
* the PluginManager -- ~SharedTiddlersPlugin should be present there, be not Disabled, have no error log (if all of these is true, than the plugin is installed correctly); also, only one copy of ~SharedTiddlersPlugin or ~IncludePlugin should be installed
* if the plugin is installed correctly, inspect the {{{chkUseInclude}}} option -- should be checked for the plugin to work
* if the problem is not because of {{{chkUseInclude}}}, go to the IncludeState and see if any problems are reported there
** in some browsers there are additional security settings which can forbid loading other ~TWs, see [[Known Problems with Importing in TiddlyWiki|http://tiddlywiki.com/#%5B%5BTiddlyWiki%20Browser%20Compatibility%5D%5D]] section at tiddlywiki.com (it is about importing, but applies to "including" as well). Problems with ~FireFox seem to be solved by either updating to STP v1.5.0+ or TW v2.7.0+
* some browsers forbid including from outside the folder where the including TW is placed. According to tests, addresses containing ".." (container folder) work in Opera (12.15), Safari (5.0.3), IE (10.0) and don't work in ~FireFox (20.0), Chrome (26.0). In ~FireFox, this can be "fixed" by changing a security option: to do this, in the address bar type {{{about:config}}}, press enter, search for the {{{security.fileuri.strict_origin_policy}}} option and set it to {{{false}}} (fortunately, this works even in Android).
If the problem remains, you can search or post your issue in the [[community newsgroup|http://groups.google.com/group/tiddlywiki]] and if it's not solved, you can also notify the maintainer by email (don't forget to add the link to the thread).
!More details on possible applications
In general, ~SharedTiddlersPlugin can help with the following:
* reducing sizes of ~TiddlyWikis which can speed up saving TW and loading from the web, when used as a web-page/site
** examples: old journal/news tiddlers can "archived" into a separate TW monthly so that your diary/blog/site remains approximately the same size but old entries are easily accessible
* using common content in several ~TiddlyWikis
** like some glossary, or help texts about ~TiddlyWiki or some plugins (like ~SharedTiddlersPlugin which has this extensive documentation)
** in this case it's usually easy to avoid name conflicts -- just try to make sure that in the shared document tiddler names are specific enough
* using common interface
** ~PageTemplate, style sheets and other parts of ~TiddlyWiki theme can be shared (don't forget "substitute"/"substituteShadows" priorities, depending on what is present in a document you include those into)
*** all ~PageTemplate, ~StyleSheet, ~ViewTemplate, ~EditTemplate and ~ToolbarCommands are applied as expected; however, it is done by extra refreshing which causes already displayed messages to disappear (on handling of the {{{<<include>>}}} macro)
** included elements can also be used as parts of a custom theme (instead of substituting the default ones); style sheets can be included in the StyleSheet tiddler as well (by adding lines like {{{[[IncludedStyleSheet]]}}} into it)
** evaluated transclusions can be shared as well (and other stuff which is rendered as it shows, like {{{<html>}}}-insertions)
** finally, included plugins can be evaluated (see the "Detailed reference" part of this docs)
*** keep in mind that for some plugins it's more suitable to import them rather than include and evaluate
* aggregating content from other ~TWs
** ~SharedTiddlersPlugin can be used to get all tiddlers with "~ToDo" tag from all ~TiddlyWikis in a separate TW and other things like that
** usually this wouldn't require extra efforts, but if you expect name conflicts and need to get access to all the tiddlers (including different ones with the same name), check "API changes and coding with ~SharedTiddlersPlugin" section for details
** @@font-size:smaller;[currently unavailable, see "Detailed reference" section for details]@@ it's also can be useful to aggregate tiddlers from web (from online ~TiddlyWikis, like http://www.tiddlywiki.com/index.html), which can bring you news from tiddly world or just from people you know who use ~TiddlyWiki; but remember the cross-site restriction
* finally, ~SharedTiddlersPlugin can be used to "merge" content of several ~TWs
** for instance, if you have a TW dedicated to science you're doing and a TW created for learning (at university or anyhow else), there definitely can appear some topics that belong to both wikis; it's rather weird thing to create new ~TiddlyWikis for each such topic, so you can keep tiddlers in one of them and include them to another for smoother workflow
** however, because included tiddlers are read-only, "default" workflow is not absolutely smooth as you'll need to go to the wiki containing tiddlers "originally" to change them
!API changes and coding with SharedTiddlersPlugin
<html><h2>Accessing stores</h2>
</html>Each included ~TiddlyWiki is represented as a {{{TiddlyWiki}}} object, like the main one is. While for main ~TiddlyWiki it's the global {{{store}}} instance of {{{TiddlyWiki}}}, included ones can be accessed as following:
{{{
var includedStore = window.sharedTiddlersAPI.getStore(tiddyWikiURL);
}}}
@@font-size:smaller;For backward compability, there's still {{{abego.TiddlyWikiIncluder.getStore(tiddyWikiURL)}}} method which is not recomended to use (see below).@@
Those included stores have only those tiddlers which are included (taking account of the ''filter'' parameters of {{{<<include>>}}} macros). Each of them has an additional property,
{{{
var fullStore = includedStore.orig_store;
}}}
which is another {{{TiddlyWiki}}} that contains ''all'' the tiddlers of the included TW document.
The list of urls of all included ~TiddlyWikis can be accessed using the
{{{
window.sharedTiddlersAPI.getIncludes()
}}}
method (it returns an array of strings – urls). Currently, this array contains urls exactly as they were written in {{{<<include>>}}} macros and, for instance, two macros with "example included.html" and "example%20included.html" urls create two different included stores.
@@font-size:smaller;For backward compability, there's also {{{abego.TiddlyWikiIncluder.getIncludes()}}} method which is not recomended to use (see below).@@
To get the state of a {{{store}}} (loaded, or some error occured when loading, etc), the
{{{
window.sharedTiddlersAPI.getState(tiddyWikiURL)
}}}
method can be used. It returns {{{null}}} when the store is already loaded or a a state/error text otherwise.
@@font-size:smaller;For backward compability, there's also {{{abego.TiddlyWikiIncluder.getState(tiddyWikiURL)}}} method which is not recomended to use (see below).@@
<html><h2>Accessing tiddlers</h2>
</html>As {{{TiddlyWiki}}} instances, both {{{includedStore}}} and {{{fullStore}}} in these examples have corresponding methods, like {{{fetchTiddler}}} etc.
Normally, however, one wouldn't use these direct methods. Instead, there's hijacked
{{{
store.fetchTiddler(title)
}}}
which fetches a tiddler from the set of "original" and "included" ones taking into account the "substituting". There's also
{{{
window.sharedTiddlersAPI.forReallyEachTiddler(callback)
}}}
method which does the same thing as {{{store.forEachTiddler}}}, but "iterates" all the tiddlers taking into account the "substituting". Main {{{store}}} methods {{{reverseLookup}}}, {{{updateTiddlers}}}, {{{getTags}}}, {{{getMissingLinks}}}, {{{getOrphans}}} are hijacked in the same way. Finally, already existing functions that use {{{store.forEachTiddler}}} can be modified so that it works as {{{sharedTiddlersAPI.forReallyEachTiddler}}} (see the "Making other plugins "include-aware"" section of these docs).
All original tiddlers from the main store can be accessed via the
{{{
window.sharedTiddlersAPI.orig_fetchTiddler(title)
}}}
and the original (not hijacked)
{{{
store.forEachTiddler(callback)
}}}
Notes:
* normally, the {{{window.}}} prefix can be omitted
* methods of {{{abego.TiddlyWikiIncluder}}} are retained so that users of ~IncludePlugin don't need to change their scripts if moving to ~SharedTiddlersPlugin. However, it is recomended that those methods are substituted with those from the {{{sharedTiddlersAPI}}} "namespace".
<html><h2>Included tiddlers</h2>
</html>With SharedTiddlersPlugin, tiddlers get a new property, the url of the store they were included from. It can be got by the {{{getIncludeURL()}}} method of the {{{Tiddler}}} object: it returns the url if the tiddler was included and {{{null}}} otherwise (if it's from the main {{{store}}}).
<html><h2>Importing included tiddlers</h2>
</html>To easily import included tiddlers, the following function can be used:
{{{
window.sharedTiddlersAPI.importAndLog(tiddler,mode)
}}}
It strips {{{includeURL}}} from the {{{tiddler}}}, imports it in the corresponding {{{mode}}} and logs the results in the browser console. The {{{mode}}} argument is optional, mode numbers can be used either as {{{1}}} or {{{"1"}}}; for the list of modes see "Import tools" in "Detailed reference".
<html><h2>Examples</h2>
</html>(require "example included.html" ~TiddlyWiki included):
<html><a href="javascript:;" onclick='
url = "example included.html";
var twStore = sharedTiddlersAPI.getStore(url);
if(!twStore) {
displayMessage("The store is not loaded");
return;
}
displayMessage("Tiddlers:");
twStore.forEachTiddler(function(title,tiddler) {
usedTiddler = store.fetchTiddler(title);
if(usedTiddler.getIncludeURL() == url)
displayMessage("- "+title);
});
'>Show all tiddlers included from the "example included.html" store (with regard to priorities)</a></html>
{{{
<html><a href="javascript:;" onclick='
url = "example included.html";
var twStore = sharedTiddlersAPI.getStore(url);
if(!twStore) {
displayMessage("The store is not loaded");
return;
}
displayMessage("Tiddlers:");
twStore.forEachTiddler(function(title,tiddler) {
usedTiddler = store.fetchTiddler(title);
if(usedTiddler.getIncludeURL() == url)
displayMessage("- "+title);
});
'>Show all tiddlers included from the "example included.html" store (with regard to priorities)</a></html>
}}}
<html><a href="javascript:;" onclick='
var twStore = sharedTiddlersAPI.getStore("example included.html");
if(!twStore) {
displayMessage("The store is not loaded");
return;
}
displayMessage("Tiddlers:");
twStore.forEachTiddler(function(title,tiddler) {
displayMessage("- "+title);
});
'>Show all tiddlers included from the "example included.html" store, without regard to priorities</a></html>
{{{
<html><a href="javascript:;" onclick='
var twStore = sharedTiddlersAPI.getStore("example included.html");
if(!twStore) {
displayMessage("The store is not loaded");
return;
}
displayMessage("Tiddlers:");
twStore.forEachTiddler(function(title,tiddler) {
displayMessage("- "+title);
});
'>Show all tiddlers included from the "example included.html" store, without regard to priorities</a></html>
}}}
<html><a href="javascript:;" onclick='
var includedStore = sharedTiddlersAPI.getStore("example included.html");
if(!includedStore) {
displayMessage("The store is not loaded");
return;
}
var realStore = includedStore.orig_store;
var list = [];
realStore.forEachTiddler(function(title,tiddler) {
if(tiddler.modified > new Date("July 1, 2007"))
list.push(title);
});
if(list.length > 0) {
displayMessage("Tiddlers:");
for(var i = 0; i < list.length; i++)
displayMessage("- "+list[i]);
} else
displayMessage("No tiddlers changed after July 1st, 2007 were found");
'>Show all tiddlers in the "example included.html" store changed after July 1st, 2007</a></html>
{{{
<html><a href="javascript:;" onclick='
var includedStore = sharedTiddlersAPI.getStore("example included.html");
if(!includedStore) {
displayMessage("The store is not loaded");
return;
}
var realStore = includedStore.orig_store;
var list = [];
realStore.forEachTiddler(function(title,tiddler) {
if(tiddler.modified > new Date("July 1, 2007"))
list.push(title);
});
if(list.length > 0) {
displayMessage("Tiddlers:");
for(var i = 0; i < list.length; i++)
displayMessage("- "+list[i]);
} else
displayMessage("No tiddlers changed after July 1st, 2007 were found");
'>Show all tiddlers in the "example included.html" store changed after July 1st, 2007</a></html>
}}}
<html><a href="javascript:;" onclick='
displayMessage("Tiddlers:");
store.forEachTiddler(function(title,tiddler) {
displayMessage("- "+title);
});
'>Show all tiddlers in the main store (including substituted ones)</a></html>
{{{
<html><a href="javascript:;" onclick='
displayMessage("Tiddlers:");
store.forEachTiddler(function(title,tiddler) {
displayMessage("- "+title);
});
'>Show all tiddlers in the main store (including substituted ones)</a></html>
}}}
[[Check an included tiddler|Example of a tiddler in the included tw-document]], <html><a href="javascript:;" onclick='
var mode = 4,
tiddler = store.fetchTiddler("Example of a tiddler in the included tw-document");
// the tiddler is supposed to be included already
sharedTiddlersAPI.importAndLog(tiddler,mode);
'>import it</a></html>, then check it again.
{{{
<html><a href="javascript:;" onclick='
var mode = 4,
tiddler = store.fetchTiddler("Example of a tiddler in the included tw-document");
// the tiddler is supposed to be included already
sharedTiddlersAPI.importAndLog(tiddler,mode);
'>import it</a></html>
}}}
See also ImportIncluded tiddler for another example of "importing" included tiddlers.
!Making other plugins include-aware
To make external tiddlers usable, ~SharedTiddlersPlugin basically does 3 things:
* hijackes the {{{fetchTiddler}}} method of the main {{{store}}} object
* "recalcs" slices by deleting them
* makes some core functions see {{{store.forEachTiddler}}} as thought as it were hijacked (so that it iterates all the tiddlers that {{{fetchTiddler}}} gets)
If you want a plugin to act with regard to including and it uses {{{store.forEachTiddler}}}, you need to do this "partial hijacking" of it. This can be done in several ways, but probably the best one is the following:
* for ~SomePlugin using functions {{{func1}}}, {{{func2}}}, ..., {{{funcN}}} and objects with methods {{{obj1.met1}}}, ... {{{objM.metL}}} utilizing {{{store.forEachTiddler}}} form a piece of "hijacking code" that will make them "include-aware" – lines like:{{justDiv{
{{{
func1 = config.extensions.SharedTiddlersPlugin.getFunctionUsingForReallyEachTiddler(func1);
}}}
}}}For objects, there's an analogue which is used like this:{{justDiv{
{{{
config.extensions.SharedTiddlersPlugin.useForReallyEachTiddler(obj1,met1);
}}}
}}}
** current implementation makes "double hijacking" to work as an ordinary one, so even if {{{func1}}} calls {{{func2}}} and both are hijacked, {{{func2}}} will work correctly; but in some such cases it's not necessary to hijack all the functions and methods
** be careful with functions which modify ~TiddlyWiki store: by hijacking everything you can get external tiddlers saved in the main TW
* make sure that ~SharedTiddlersPlugin tiddler contains the "Name" slice. In the end of ~SomePlugin add this code (put the "hijacking code" where pointed):{{justDiv{
{{{
var isSharedTiddlersPluginEnabled = function() {
for(var i = 0; i < installedPlugins.length; i++)
if(installedPlugins[i].Name == "SharedTiddlersPlugin" && isPluginEnabled(installedPlugins[i]))
return true;
return false;
};
var makeInclusionAware = function() {
var now = false;
if(config.extensions.SharedTiddlersPlugin)
if(config.extensions.SharedTiddlersPlugin.useForReallyEachTiddler)
now = true;
if(now) {
// "hijacking code" goes here
} else
setTimeout(makeInclusionAware,100);
};
if(isSharedTiddlersPluginEnabled())
makeInclusionAware();
}}}
}}}
* restart to apply this "patch"
<html><h2>Examples</h2>
</html>Previously, other abego plugins were made "include-aware" right in the ~IncludePlugin/~SharedTiddlersPlugin code:
{{{
//================================================================================
// Make IntelliTagger "Include-aware"
var patchIntelliTagger = function() {
if (abego.IntelliTagger)
useForReallyEachTiddler(abego.IntelliTagger,"assistTagging");
};
//================================================================================
// Make ForEachTiddler "Include-aware"
var patchForEachTiddler = function() {
if (config.macros.forEachTiddler)
// Note: this will not patch the TiddlyWiki passed to findTiddlers but the "global" store
// This is by intent
useForReallyEachTiddler(config.macros.forEachTiddler,"findTiddlers");
};
...
invokeLater(patchIntelliTagger,100);
invokeLater(patchForEachTiddler,100);
}}}
This pieces of code are removed from ~SharedTiddlersPlugin; to make ~ForEachTiddler "see" included tiddlers, add the following to the end of the ~ForEachTiddlerPlugin code:
{{{
var isSharedTiddlersPluginEnabled = function() {
for(var i = 0; i < installedPlugins.length; i++)
if(installedPlugins[i].Name == "SharedTiddlersPlugin" && isPluginEnabled(installedPlugins[i]))
return true;
return false;
};
var makeInclusionAware = function() {
var now = false;
if(config.extensions.SharedTiddlersPlugin)
if(config.extensions.SharedTiddlersPlugin.useForReallyEachTiddler)
now = true;
if(now) {
// the place for hijacks
config.extensions.SharedTiddlersPlugin.useForReallyEachTiddler(config.macros.forEachTiddler,"findTiddlers");
} else
setTimeout(makeInclusionAware,100);
};
if(isSharedTiddlersPluginEnabled())
makeInclusionAware();
}}}
Then (after restarting), you can try its work by creating a table of included tiddlers:
{{{
<<forEachTiddler
where
'tiddler.includeURL'
write
'"|[[" +tiddler.title+"]]|"+tiddler.includeURL+"|\n"'
begin
'"| name | came from |h\n"'
>>
}}}
!Versions history and quality issues
<html><h2>Version history</h2></html>
* 2.4.0 (10.09.2013)
** introduced the {{{reloadIncluded}}} macro
** filters: added {{{external}}}, added {{{node: nodeName}}} syntax support to {{{includedFrom}}}, fixed a bug in {{{includedFrom}}} and {{{internal}}}
** fixed {{{hijackImageFormatter}}} for cases when the image is not inside a tiddler
** some code refactoring, including "internal" interfaces
** some minor quality issues in the conflict notification system are introduced (like doubling messages in rare cases), to be fixed in the next versions
** {{{wikifyIncluded}}} macro is ''completely removed'' (no notifications are now provided)
* 2.3.0 (15.06.2013)
** implemented new modes of import (2, 3)
** ImportIncluded now supports modes, "node: nodeName" syntax and got several fixes
** propagation of including is introduced for pretty links and images
** managing included tiddlers panel got the "import" button (link)
* 2.2.0 (30.05.2013)
** implemented the {{{<<describeNode nodeName self>>}}} syntax and ban of including from the node marked self
** added a panel for managing inluded tiddlers inside their edit mode (now contains a link to the tiddler in its "source" ~TiddlyWiki)
* 2.1.0 (13.05.2013)
** introduced the support of {{{[[text|tiddler]]@node}}} links
** removed functionality from the {{{wikifyIncluded}}} macro (only a notification remains)
** extended the {{{all}}} filter so that {{{[all[with included]]}}} filter brings all tiddlers accounting included ones
** fixed 'a.fetchTiddler is not a function' bug which appeared in FF, in TW 2.6.5, when a TW from outside the current folder (../some path) was tried to include (now {{{.getStore}}} method works correctly in such cases as well)
* 2.0.0 (1.05.2013)
** introduced ~TwWeb, {{{describeNode}}} macro, added support of node names to the {{{include}}} macro
** added {{{wikify}}} parameter to the {{{include}}} macro; deprecated {{{wikifyIncluded}}} macro and added a notification that pops when it is used
** some minor fixes and improvements
** major documentation update, added simplified "Functionality and examples" section, changed the structure of sections
* the 2.x.x series are dedicated to content managing and interaction, to ~TwWeb
* 1.6.1 (8.03.2013)
** added {{{noRefresh}}} parameter to the {{{include}}} macro
** added {{{internal}}} filter
** fixed a bug preventing ImportIncluded engine to work
** documentation improvements, added description of the {{{window.sharedTiddlersAPI.importAndLog(tiddler,mode)}}} method
* 1.6.0 (13.02.2013)
** added evaluation of tiddlers via the {{{include}}} macro with the new {{{eval}}} parameter
** added filters {{{all}}} and {{{includedFrom}}}; now in TW 2.6.1 or elder STP requires [[UpToDateFiltersPlugin|http://yakovl.bplaced.net/TW/ExtraFilters.html#UpToDateFiltersPlugin]]
** {{{allTiddlersAreLoaded}}} parameter is no longer added to stores, no need to check it (but old {{{if(allTiddlersAreLoaded)}}} code pieces to get all tiddlers should work)
** {{{hide}}} parameter of the {{{include}}} macro now can be used without trailing {{{:true}}}
** some fixes and code improvements, better API documentation
* 1.5.0 (11.01.2013)
** solved problems in ~FireFox, now including works in TW 2.6.0
** added autoimport via the {{{include}}} macro
** added import modes and setting the {{{store}}} as {{{dirty}}} on import
** added "protection" from including non-existing tiddlers (caused by the {{{tiddler}}} filter)
** some fixes, code improvments
* 1.4.4 (6.12.2012)
** slices are now "recalced", substituted ~ToolbarCommands is now applied as expected
** the {{{include}}} macro is now launched only once (per session) with each used exact "wording", so no loop because of refreshing is now possible
** substituted ~ViewTemplate is now refreshed on including
** {{{wikifyIncluded}}} now doesn't make infinite loops, control parameters are added
** making some abego plugins "include-aware" is removed from the code; howTo make any plugin "include-aware" is added to docs
** some minor fixes/enhancements, documentation improvments, added minified version of the plugin
* 1.4.3 (21.11.2012)
** alerting on conflicts controled by {{{chkAlertOnSharedTiddlersConflicts}}} parameter is introduced
** many improvments of code and update of documentation (including corrections in the "Name conflicts and priorities" section) are done
* 1.4.2 (wasn't published)
** introduced the {{{wikifyIncluded}}} macro
** introduced the "importing" engine (ImportIncluded)
* 1.4.1 (wasn't published)
** included ~PageTemplate is now applied
** the issue with the "orphans" list is fixed, the issue with the "missing" list is partially fixed
** {{{window.sharedTiddlersAPI.orig_forEachTiddler}}} is removed as the {{{store.forEachTiddler}}} is not actually hijacked
** documentation is updated
* 1.4.0 (29.09.2012)
** removed the {{{abego}}} namespace where possible, added {{{window.sharedTiddlersAPI}}} object and moved all (but some deprecated) API there, reduced the scope of some internal functions
* 1.3.0 (wasn't published)
** notifications of conflicts were added
** theme is refreshed when an inclusion is done so that included style sheets and templates are applied right away
* 1.2.0 (wasn't published)
** substitute and substituteShadows possibilities were added
* 1.1.0 (wasn't published)
** filtering was added
* forked from [[IncludePlugin|http://tiddlywiki.abego-software.de/#IncludePlugin]] v1.0.1, by Udo Borkowski
<html><h2>Current quality issues</h2></html>
* some tiddlers in the main store, referenced in included tiddlers, appear in the "missing" list
!%/<<tiddler {{setStylesheet('div[tiddler="SharedTiddlersPluginInfo"] .tabContents { background-color:'+store.getTiddlerText('ColorPalette::Background')+'; }', 'STPInfoCSS');'';}}>>
/%
!info
|Name|ShowTiddlerStatistics|
|Source|http://www.TiddlyTools.com/#ShowTiddlerStatistics|
|Version|1.0.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|transclusion |
|Description|display document summary / tiddler stats (newest, oldest, largest, smallest, etc.)|
Usage
<<<
{{{
<<tiddler ShowTiddlerStatistics>>
}}}
!end
!show
{{nowrap{
$1
}}}
!end
!summary
{{fine{There are ''%0 tiddlers'', including: ''%1 systemConfig'' + ''%2 Plugins'' (%3 total bytes)}}}
!end
!table
|borderless|k
| last change: |[[%0]] {{fine{(updated %1)}}}|
| newest: |[[%2]] {{fine{(created %3)}}}|
| oldest: |[[%4]] {{fine{(created %5)}}}|
| smallest: |[[%6]] {{fine{(%7 bytes)}}}|
| largest: |[[%8]] {{fine{(%9 bytes)}}}|
!end
%/<<tiddler ShowTiddlerStatistics##show with: {{
var tiddlers=store.getTiddlers("modified","excludeLists");
var last=tiddlers[tiddlers.length-1];
var total=oldest=newest=smallest=largest=0;
for (var i=0; i<tiddlers.length; i++) {
total+=tiddlers[i].text.length;
if (!oldest || tiddlers[i].created<oldest)
{ var oldest=tiddlers[i].created; var oldtid=tiddlers[i]; }
if (!newest || tiddlers[i].created>newest)
{ var newest=tiddlers[i].created; var newtid=tiddlers[i]; }
if (!smallest || tiddlers[i].text.length<smallest)
{ var smallest=tiddlers[i].text.length; var smalltid=tiddlers[i]; }
if (!largest || tiddlers[i].text.length>largest)
{ var largest=tiddlers[i].text.length; var largetid=tiddlers[i]; }
}
var out=store.getTiddlerText("ShowTiddlerStatistics##summary").format([
tiddlers.length,
store.getTaggedTiddlers("systemConfig").length,
store.getTaggedTiddlers("Plugins").length,
total]);
out+='\n'+store.getTiddlerText("ShowTiddlerStatistics##table").format([
last.title, last.modified.formatString("MMM DDth YYYY, 0hh:0mm"),
newtid.title, newtid.created.formatString("MMM DDth YYYY, 0hh:0mm"),
oldtid.title, oldtid.created.formatString("MMM DDth YYYY, 0hh:0mm"),
smalltid.title, smalltid.text.length,
largetid.title, largetid.text.length]);
out;
}}>>
<<search>><<closeAll>><<permaview>><<newTiddler "New Entry" label:"new entry" focus:title>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
Syntax:
*sit
*sit [<object>]
Allows you to sit down. If you don't specify an object then it is assumed that you are sitting on the floor. Sitting allows you to recover [[Fatigue]] points faster. When sitting on the floor you recover them twice as fast, when sitting on a chair, stool or other item, then you recover them three times as fast.
Performing an action from the sitting position is slower. For example initing combat means that the first action takes three times as long. But you can move in a direction immediately from sitting.
See also: [[Stand]]
An online Multi User Dungeon
Size determines the physical size of the Character. This has major implications on what equipment they may wear and also other aspects within the game. For example hole in the rock may only accommodate a small stature person, or a tree branch or ledge may only be accessible to large characters.
Size may never be changed as it is based on the [[race|Race]] chosen.
<<forEachTiddler
where
'tiddler.tags.contains("Race")'
sortBy
'tiddler.title'
write
'"|[["+tiddler.title+
"]]| " + store.getTiddlerSlice(tiddler.title,"Size") +
" |\n" '
begin
'"|! [[Race]] | ![[Size]] |\n"'
>>
Skills are abilities which your character may learn. Some are useful for [[Combat]], others are useful for crafting items, or just the overall abilities of your Character.
Skill ratings range from 1 (Novice) through to 6 (Grand Master). <<tiddler [[Character Points##Increasing Skills and Spells]]>>
Using skills essentially comes down to a dice roll. You roll 1D6 and if you score your skill or under, then you get the rolled number as a bonus to whatever action you are taking. If you don't have a skill level then it doesn't stop you trying your hand at the task, but you'll never get a bonus and you may risk trouble.
!!Learning a skill
You need to find a teacher to be able to [[Learn]] a skill. Initially learning a skill will cost 1 [[Character Point|Character Points]] (plus the addendum for the amount of skills you have).
!!Practicing a skill
Once learned, providing you have enough ~CPs you can keep on [[practicing|Practice]] upto and including level 4 no matter where you are. However in order to be able to become a Master or Grand Master you must be able to prove your skill to others. This is done by having a Master or Grand Master [[Assess]] your skill as you practice.
You can use the [[Skills|Skills (Command)]] command to get a list of all of your currently known skills and levels. Some Skills, such as [[Artisan Skills]], earn experience points directly when they are used. When they reach the required experience point level, then that experience is automatically converted into [[Character Points]] just for that skill. The amount of experience nesessary to earn these ~CPs increases depending on the skill level.
Syntax:
*skills [full]
This will give you a list of all of the [[Skills]] and Levels that you have learned. The Full skill listing will also show what [[Character Points]] and, in the case or [[Artisan Skills]], what [[Experience Points|Experience]] you have earned for them.
When levelling up your skills you first use the skill allocated [[Character Points]], before you use your general points.
A Smith can manufacture metal [[Weapons]] such as [[Axes|Axes & Maces]], [[Mauls|Axes & Maces]] and [[Swords]].
These commands allow you to interact with other players within the game, and in some cases with [[NPCs]].
A Sorcerer is somebody who can craft magical items. With it they can imbue magical effects into equipment which, when worn, may give additional bonuses to the wearer.
For example a Ring of True Seeing may add to a player's [[Perception]] skill, or a Sword of Sharpness may deliver additional damage.
You may not learn Sorcerer until you have achieved Journeyman status in [[Mage]] (level 4), and your Sorcery skill may not exceed your Mage skill.
Spells are [[cast|Cast]] either against a target or yourself. If no explicit target is given then it is assumed that yourself or the current location you are in is the target.
The chances of a spell working is dependant on your [[Mage]] skill and the natural defence of the target.
Syntax:
*stand
You stop [[Sitting|Sit]].
You stats say everything about who you are as a character. Large or Small, Fast or Slow, Intelligent or Slow-witted. Your [[Primary Stats]] describe your character. Your [[Secondary Stats]] are calculated based off your Primaries and your [[Skills]].
You can see your current Statistics using the [[Stats]] command.
Syntax:
*stats [all]
*stats <character>
Displays the character's statistics and character sheet. Initial usage only gives an overview of yours stats. Using "all" gives all skills, spells and any affecting spells or features.
Requesting the stats of another character gives an approximate overview of how their stats look, but not their actual values, nor will you be able to see their skills.
[[Score]] displays the same as basic Stats command, but also additional information regarding how much money you have, items in your inventory and how burdened you are.
A life spent growing up on the streets has left you wise to many common tricks - You gain an additional chance to avoid any kind of [[Thieves Skills]] used against you.
Strength determines how physically strong the character is. Stronger characters can carry more [[weight|Weight]], inflict additional damage on their opponents and have better chances at strength based tests such as trying to move a rock.
You are trained in the use of weapons from the Swords group, having been instructed in effective manoeuvrers and practised enough to execute them reliably. Familiarity with the skill lets you use weapons from the group at the base chance. Additional levels increase your chances to use the weapons offensively and defensively.
Once you have obtained the level of Swords 2, you may then start specialising in additional skills such as [[Fencing]].
Swords is a [[Skill|Skills]] as well as a class of [[Weapon|Weapons]].
/***
|''Name:''|SyncFileTiddlerPlugin|
|''Summary:''|Automatically synchronizes the text of a tiddler with the content of an associated text file.|
|''Version:''|1.0.0 (2012-04-16)|
|''Source:''|http://tiddlywiki.abego-software.de/#SyncFileTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|© 2012 [[abego Software|http://www.abego-software.de]]|
!About SyncFileTiddler
SyncFileTiddler automatically synchronizes the text of a tiddler with the content of an associated text file.
The tiddlers to be synched are specified in a table in the tiddler SyncFileTiddlers. A table entry looks like this:
{{{
|SomeTiddler|path/To/File.txt|readonly|
}}}
This will be rendered as:
|SomeTiddler|path/To/File.txt|readonly|
When the "readonly" parameter is missing the tiddler is editable. Changes to the tiddler's text will be written to the associated file.
When "readonly" is specified the tiddler is not editable in the TiddlyWiki, but changes in the associated file will also change the tiddler's text accordingly.
The file is regularily read ("polled") to verify if its text has changed.
The tags of the tiddler can only be specified in the TiddlyWiki. The file will not affect the tags.
!Revision history
* SyncFileTiddlerPlugin 1.0.0 (2012-04-16)
** initial version
!Source Code
***/
//{{{
var abego = abego || {};
(function(namespace) {
var entries = {};
var errorMsg = function(s) {
throw "SyncFileTiddler: " + s;
};
var save = function(filepath, s) {
var b = saveFile(filepath, s);
if (!b) {
throw errorMsg("file could not be saved: " + filepath);
}
return s;
};
var createTiddler = function(title, text) {
var t = store.createTiddler(title);
t.text = text;
return text;
};
var getTiddlerText = function(title) {
return store.getTiddlerText(title, null);
};
var setTiddlerText = function(title, text) {
var t = store.fetchTiddler(title);
store.saveTiddler(title, title, text, "SyncFileTiddler ("
+ config.options.txtUserName + ")", new Date(), t.fields.tags);
return text;
};
/**
* FIXME: also implement the "delete" case, i.e. delete the "Slave" when the
* "Master" is missing (need a deleteFile method first). (? What about
* losing tags when deleting the tiddler after a file delete?)
*
* @param oldFileContent
* [nullable] when not null it holds the content of the file as
* it was loaded "some time ago". Before the method overwrites
* the file it checks if the file still has the oldFileContent.
* If the file's current content differs from the oldFileContent,
* the file was modified "from the outside". Then we have a sync
* conflict and the method fails.
* @param fileIsMaster
* when true the file is the Master, i.e. the tiddler should get
* the file's content. When false the file should get the
* tiddler's content. When undefined the function should try to
* find out what is the Master.
* @return [nullable] the text/content of the Tiddler/File, or null if
* neither Tiddler nor File exists
*/
var syncTiddlerAndFile = function(tiddler, filepath, oldFileContent,
fileIsMaster) {
var tiddlerContent = getTiddlerText(tiddler);
var fileContent = loadFile(filepath);
if (fileContent) {
fileContent = fileContent.replace(/\r\n/g,'\n');
if (tiddlerContent) {
// both file and tiddler exist.
if (fileContent == tiddlerContent) {
// both file and tiddler are equal
return fileContent;
} else {
// file and tiddler differ.
if (fileIsMaster === undefined) {
// No Master is explicitly defined.
throw errorMsg("tiddler and file have different content (delete the 'old' one to fix this):\n"
+ tiddler + "\n" + filepath);
} else if (fileIsMaster) {
// the file is the master, i.e. the tiddler gets the
// file's content
return setTiddlerText(tiddler, fileContent);
} else {
// the tiddler is the master, i.e. the file gets the
// tiddler's text
// But first check if the file was not modified in the
// meantime
if (oldFileContent && oldFileContent != fileContent) {
throw errorMsg("The file was modified externally: "
+ filepath);
}
return save(filepath, tiddlerContent);
}
}
} else {
// file exists, but no tiddler.
// Create a new tiddler with the fileContent
return createTiddler(tiddler, fileContent);
}
} else {
if (tiddlerContent) {
// tiddler exists, but no file.
// create a file with the tiddler's content
return save(filepath, tiddlerContent);
} else {
// Neither tiddler nor file exists.
// do nothing
return null;
}
}
};
var syncEntry = function(entry, fileIsMaster, ignoreOldFileContent) {
if (entry.readonly && !fileIsMaster) {
return false;
}
entry.oldFileContent = syncTiddlerAndFile(entry.tiddler,
entry.filepath, ignoreOldFileContent ? null
: entry.oldFileContent, fileIsMaster);
return true;
};
var saveAsFile = function(tiddler) {
var e = entries[tiddler];
if (e) {
var s = getTiddlerText(tiddler);
save(e.filepath, s);
e.oldFileContent = s;
}
};
// When a tiddler is saved and it is a "SyncFileTiddler" the corresponding
// file is updated.
var oldSaveTiddler = TiddlyWiki.prototype.saveTiddler;
TiddlyWiki.prototype.saveTiddler = function(title, newTitle) {
var result = oldSaveTiddler.apply(this, arguments);
try {
// "Sync" the tiddler with its file, with the tiddler being the
// Master.
// (Will do nothing if the tiddler is not a "SyncFileTiddler")
SyncFileTiddler.syncTiddler(newTitle, false);
} catch (e) {
if (confirm("The file to sync with was modified externally.\n\n"
+ "Press OK to overwrite anyway.\n"
+ "Press Cancel to keep file unchanged.")) {
saveAsFile(newTitle);
} else if (confirm("Use the file's content as the tiddler text?")) {
syncEntry(entries[newTitle], true);
}
}
return result;
};
// The tiddler must be readonly when its "sync" entry is readonly
var oldIsReadOnly = Tiddler.prototype.isReadOnly;
Tiddler.prototype.isReadOnly = function() {
if (oldIsReadOnly.apply(this, arguments)) {
return true;
}
var e = entries[this.title];
return e && e.readonly;
}
var pauseBetweenFilePolls = 2000;
var polling = false;
var pollFileChanges = function() {
if (polling) {
SyncFileTiddler.syncAll(true);
// schedule the next time to poll for file changes
setTimeout(pollFileChanges, pauseBetweenFilePolls);
}
};
var SyncFileTiddler = {
/**
* The tiddler is registered to be "synched" with the file.
*
* If a tiddler is added more than once the last file specified is used.
*
* @param tiddler
* title of the tiddler to register
* @param filepath
* path to the file to associate with the tiddler.
*/
add : function(tiddler, filepath, readonly) {
if (!filepath.startsWith("/")) {
// a path relative to the document
var docPath = document.URL;
if (!docPath.startsWith("file://"))
return;
var p = docPath.substring(7, docPath.lastIndexOf("/") + 1);
filepath = p + filepath;
}
entries[tiddler] = {
tiddler : tiddler,
filepath : filepath,
readonly : readonly,
oldFileContent : syncTiddlerAndFile(tiddler, filepath, null,
true),
};
},
/**
* "Syncs" the tiddler with its file.
*
* Will do nothing when the tiddler is not a "FileSyncTiddler" (i.e. was
* not registered using add)
*/
syncTiddler : function(tiddler, fileIsMaster) {
var e = entries[tiddler];
if (e) {
syncEntry(e, fileIsMaster);
autoSaveChanges(true);
return true;
} else {
return false;
}
},
syncAll : function(fileIsMaster) {
for ( var e in entries) {
syncEntry(entries[e], fileIsMaster);
}
autoSaveChanges(true);
},
setPolling : function(b) {
if (b == polling)
return;
polling = b;
if (polling) {
// start polling
pollFileChanges();
}
},
isPolling : function() {
return polling;
},
setPauseBetweenFilePolls : function(millis) {
pauseBetweenFilePolls = millis;
},
getPauseBetweenFilePolls : function() {
return pauseBetweenFilePolls;
},
addSyncFileTiddlers : function() {
var s = store.getTiddlerText("SyncFileTiddlers");
if (!s)
return;
var lines = s.split("\n");
for ( var i = 0; i < lines.length; i++) {
var line = lines[i];
var a = line.split('\|');
if (a.length >= 3) {
var tiddler = a[1];
var filepath = a[2];
var readonly = a.length >= 4 && a[3]=="readonly";
SyncFileTiddler.add(tiddler, filepath,readonly);
}
}
}
};
namespace.SyncFileTiddler = SyncFileTiddler;
})(abego);
setTimeout(function() {
abego.SyncFileTiddler.addSyncFileTiddlers();
abego.SyncFileTiddler.setPolling(true);
},1);
//}}}
/***
|Name:|TagglyTaggingPlugin|
|Description:|tagglyTagging macro is a replacement for the builtin tagging macro in your ViewTemplate|
|Version:|3.3.2a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#TagglyTaggingPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!Notes
See http://mptw.tiddlyspot.com/#TagglyTagging
***/
//{{{
merge(String.prototype,{
parseTagExpr: function(debug) {
if (this.trim() == "")
return "(true)";
var anyLogicOp = /(!|&&|\|\||\(|\))/g;
var singleLogicOp = /^(!|&&|\|\||\(|\))$/;
var spaced = this.
// because square brackets in templates are no good
// this means you can use [(With Spaces)] instead of [[With Spaces]]
replace(/\[\(/g," [[").
replace(/\)\]/g,"]] ").
// space things out so we can use readBracketedList. tricky eh?
replace(anyLogicOp," $1 ");
var expr = "";
var tokens = spaced.readBracketedList(false); // false means don't uniq the list. nice one JR!
for (var i=0;i<tokens.length;i++)
if (tokens[i].match(singleLogicOp))
expr += tokens[i];
else
expr += "tiddler.tags.contains('%0')".format([tokens[i].replace(/'/,"\\'")]); // fix single quote bug. still have round bracket bug i think
if (debug)
alert(expr);
return '('+expr+')';
}
});
merge(TiddlyWiki.prototype,{
getTiddlersByTagExpr: function(tagExpr,sortField) {
var result = [];
var expr = tagExpr.parseTagExpr();
store.forEachTiddler(function(title,tiddler) {
if (eval(expr))
result.push(tiddler);
});
if(!sortField)
sortField = "title";
result.sort(function(a,b) {return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);});
return result;
}
});
config.taggly = {
// for translations
lingo: {
labels: {
asc: "\u2191", // down arrow
desc: "\u2193", // up arrow
title: "title",
modified: "modified",
created: "created",
show: "+",
hide: "-",
normal: "normal",
group: "group",
commas: "commas",
sitemap: "sitemap",
numCols: "cols\u00b1", // plus minus sign
label: "Tagged as '%0':",
exprLabel: "Matching tag expression '%0':",
excerpts: "excerpts",
descr: "descr",
slices: "slices",
contents: "contents",
sliders: "sliders",
noexcerpts: "title only",
noneFound: "(none)"
},
tooltips: {
title: "Click to sort by title",
modified: "Click to sort by modified date",
created: "Click to sort by created date",
show: "Click to show tagging list",
hide: "Click to hide tagging list",
normal: "Click to show a normal ungrouped list",
group: "Click to show list grouped by tag",
sitemap: "Click to show a sitemap style list",
commas: "Click to show a comma separated list",
numCols: "Click to change number of columns",
excerpts: "Click to show excerpts",
descr: "Click to show the description slice",
slices: "Click to show all slices",
contents: "Click to show entire tiddler contents",
sliders: "Click to show tiddler contents in sliders",
noexcerpts: "Click to show entire title only"
},
tooDeepMessage: "* //sitemap too deep...//"
},
config: {
showTaggingCounts: true,
listOpts: {
// the first one will be the default
sortBy: ["title","modified","created"],
sortOrder: ["asc","desc"],
hideState: ["show","hide"],
listMode: ["normal","group","sitemap","commas"],
numCols: ["1","2","3","4","5","6"],
excerpts: ["noexcerpts","excerpts","descr","slices","contents","sliders"]
},
valuePrefix: "taggly.",
excludeTags: ["excludeLists","excludeTagging"],
excerptSize: 50,
excerptMarker: "/%"+"%/",
siteMapDepthLimit: 25
},
getTagglyOpt: function(title,opt) {
var val = store.getValue(title,this.config.valuePrefix+opt);
return val ? val : this.config.listOpts[opt][0];
},
setTagglyOpt: function(title,opt,value) {
// create it silently if it doesn't exist
if (!store.tiddlerExists(title)) {
store.saveTiddler(title,title,config.views.editor.defaultText.format([title]),config.options.txtUserName,new Date(),"");
// <<tagglyTagging expr:"...">> creates a tiddler to store its display settings
// Make those tiddlers less noticeable by tagging as excludeSearch and excludeLists
// Because we don't want to hide real tags, check that they aren't actually tags before doing so
// Also tag them as tagglyExpression for manageability
// (contributed by RA)
if (!store.getTaggedTiddlers(title).length) {
store.setTiddlerTag(title,true,"excludeSearch");
store.setTiddlerTag(title,true,"excludeLists");
store.setTiddlerTag(title,true,"tagglyExpression");
}
}
// if value is default then remove it to save space
return store.setValue(title, this.config.valuePrefix+opt, value == this.config.listOpts[opt][0] ? null : value);
},
getNextValue: function(title,opt) {
var current = this.getTagglyOpt(title,opt);
var pos = this.config.listOpts[opt].indexOf(current);
// supposed to automagically don't let cols cycle up past the number of items
// currently broken in some situations, eg when using an expression
// lets fix it later when we rewrite for jquery
// the columns thing should be jquery table manipulation probably
var limit = (opt == "numCols" ? store.getTaggedTiddlers(title).length : this.config.listOpts[opt].length);
var newPos = (pos + 1) % limit;
return this.config.listOpts[opt][newPos];
},
toggleTagglyOpt: function(title,opt) {
var newVal = this.getNextValue(title,opt);
this.setTagglyOpt(title,opt,newVal);
},
createListControl: function(place,title,type) {
var lingo = config.taggly.lingo;
var label;
var tooltip;
var onclick;
if ((type == "title" || type == "modified" || type == "created")) {
// "special" controls. a little tricky. derived from sortOrder and sortBy
label = lingo.labels[type];
tooltip = lingo.tooltips[type];
if (this.getTagglyOpt(title,"sortBy") == type) {
label += lingo.labels[this.getTagglyOpt(title,"sortOrder")];
onclick = function() {
config.taggly.toggleTagglyOpt(title,"sortOrder");
return false;
}
}
else {
onclick = function() {
config.taggly.setTagglyOpt(title,"sortBy",type);
config.taggly.setTagglyOpt(title,"sortOrder",config.taggly.config.listOpts.sortOrder[0]);
return false;
}
}
}
else {
// "regular" controls, nice and simple
label = lingo.labels[type == "numCols" ? type : this.getNextValue(title,type)];
tooltip = lingo.tooltips[type == "numCols" ? type : this.getNextValue(title,type)];
onclick = function() {
config.taggly.toggleTagglyOpt(title,type);
return false;
}
}
// hide button because commas don't have columns
if (!(this.getTagglyOpt(title,"listMode") == "commas" && type == "numCols"))
createTiddlyButton(place,label,tooltip,onclick,type == "hideState" ? "hidebutton" : "button");
},
makeColumns: function(orig,numCols) {
var listSize = orig.length;
var colSize = listSize/numCols;
var remainder = listSize % numCols;
var upperColsize = colSize;
var lowerColsize = colSize;
if (colSize != Math.floor(colSize)) {
// it's not an exact fit so..
upperColsize = Math.floor(colSize) + 1;
lowerColsize = Math.floor(colSize);
}
var output = [];
var c = 0;
for (var j=0;j<numCols;j++) {
var singleCol = [];
var thisSize = j < remainder ? upperColsize : lowerColsize;
for (var i=0;i<thisSize;i++)
singleCol.push(orig[c++]);
output.push(singleCol);
}
return output;
},
drawTable: function(place,columns,theClass) {
var newTable = createTiddlyElement(place,"table",null,theClass);
var newTbody = createTiddlyElement(newTable,"tbody");
var newTr = createTiddlyElement(newTbody,"tr");
for (var j=0;j<columns.length;j++) {
var colOutput = "";
for (var i=0;i<columns[j].length;i++)
colOutput += columns[j][i];
var newTd = createTiddlyElement(newTr,"td",null,"tagglyTagging"); // todo should not need this class
wikify(colOutput,newTd);
}
return newTable;
},
createTagglyList: function(place,title,isTagExpr) {
switch(this.getTagglyOpt(title,"listMode")) {
case "group": return this.createTagglyListGrouped(place,title,isTagExpr); break;
case "normal": return this.createTagglyListNormal(place,title,false,isTagExpr); break;
case "commas": return this.createTagglyListNormal(place,title,true,isTagExpr); break;
case "sitemap":return this.createTagglyListSiteMap(place,title,isTagExpr); break;
}
},
getTaggingCount: function(title,isTagExpr) {
// thanks to Doug Edmunds
if (this.config.showTaggingCounts) {
var tagCount = config.taggly.getTiddlers(title,'title',isTagExpr).length;
if (tagCount > 0)
return " ("+tagCount+")";
}
return "";
},
getTiddlers: function(titleOrExpr,sortBy,isTagExpr) {
return isTagExpr ? store.getTiddlersByTagExpr(titleOrExpr,sortBy) : store.getTaggedTiddlers(titleOrExpr,sortBy);
},
getExcerpt: function(inTiddlerTitle,title,indent) {
if (!indent)
indent = 1;
var displayMode = this.getTagglyOpt(inTiddlerTitle,"excerpts");
var t = store.getTiddler(title);
if (t && displayMode == "excerpts") {
var text = t.text.replace(/\n/," ");
var marker = text.indexOf(this.config.excerptMarker);
if (marker != -1) {
return " {{excerpt{<nowiki>" + text.substr(0,marker) + "</nowiki>}}}";
}
else if (text.length < this.config.excerptSize) {
return " {{excerpt{<nowiki>" + t.text + "</nowiki>}}}";
}
else {
return " {{excerpt{<nowiki>" + t.text.substr(0,this.config.excerptSize) + "..." + "</nowiki>}}}";
}
}
else if (t && displayMode == "contents") {
return "\n{{contents indent"+indent+"{\n" + t.text + "\n}}}";
}
else if (t && displayMode == "sliders") {
return "<slider slide>\n{{contents{\n" + t.text + "\n}}}\n</slider>";
}
else if (t && displayMode == "descr") {
var descr = store.getTiddlerSlice(title,'Description');
return descr ? " {{excerpt{" + descr + "}}}" : "";
}
else if (t && displayMode == "slices") {
var result = "";
var slices = store.calcAllSlices(title);
for (var s in slices)
result += "|%0|<nowiki>%1</nowiki>|\n".format([s,slices[s]]);
return result ? "\n{{excerpt excerptIndent{\n" + result + "}}}" : "";
}
return "";
},
notHidden: function(t,inTiddler) {
if (typeof t == "string")
t = store.getTiddler(t);
return (!t || !t.tags.containsAny(this.config.excludeTags) ||
(inTiddler && this.config.excludeTags.contains(inTiddler)));
},
// this is for normal and commas mode
createTagglyListNormal: function(place,title,useCommas,isTagExpr) {
var list = config.taggly.getTiddlers(title,this.getTagglyOpt(title,"sortBy"),isTagExpr);
if (this.getTagglyOpt(title,"sortOrder") == "desc")
list = list.reverse();
var output = [];
var first = true;
for (var i=0;i<list.length;i++) {
if (this.notHidden(list[i],title)) {
var countString = this.getTaggingCount(list[i].title);
var excerpt = this.getExcerpt(title,list[i].title);
if (useCommas)
output.push((first ? "" : ", ") + "[[" + list[i].title + "]]" + countString + excerpt);
else
output.push("*[[" + list[i].title + "]]" + countString + excerpt + "\n");
first = false;
}
}
return this.drawTable(place,
this.makeColumns(output,useCommas ? 1 : parseInt(this.getTagglyOpt(title,"numCols"))),
useCommas ? "commas" : "normal");
},
// this is for the "grouped" mode
createTagglyListGrouped: function(place,title,isTagExpr) {
var sortBy = this.getTagglyOpt(title,"sortBy");
var sortOrder = this.getTagglyOpt(title,"sortOrder");
var list = config.taggly.getTiddlers(title,sortBy,isTagExpr);
if (sortOrder == "desc")
list = list.reverse();
var leftOvers = []
for (var i=0;i<list.length;i++)
leftOvers.push(list[i].title);
var allTagsHolder = {};
for (var i=0;i<list.length;i++) {
for (var j=0;j<list[i].tags.length;j++) {
if (list[i].tags[j] != title) { // not this tiddler
if (this.notHidden(list[i].tags[j],title)) {
if (!allTagsHolder[list[i].tags[j]])
allTagsHolder[list[i].tags[j]] = "";
if (this.notHidden(list[i],title)) {
allTagsHolder[list[i].tags[j]] += "**[["+list[i].title+"]]"
+ this.getTaggingCount(list[i].title) + this.getExcerpt(title,list[i].title) + "\n";
leftOvers.setItem(list[i].title,-1); // remove from leftovers. at the end it will contain the leftovers
}
}
}
}
}
var allTags = [];
for (var t in allTagsHolder)
allTags.push(t);
var sortHelper = function(a,b) {
if (a == b) return 0;
if (a < b) return -1;
return 1;
};
allTags.sort(function(a,b) {
var tidA = store.getTiddler(a);
var tidB = store.getTiddler(b);
if (sortBy == "title") return sortHelper(a,b);
else if (!tidA && !tidB) return 0;
else if (!tidA) return -1;
else if (!tidB) return +1;
else return sortHelper(tidA[sortBy],tidB[sortBy]);
});
var leftOverOutput = "";
for (var i=0;i<leftOvers.length;i++)
if (this.notHidden(leftOvers[i],title))
leftOverOutput += "*[["+leftOvers[i]+"]]" + this.getTaggingCount(leftOvers[i]) + this.getExcerpt(title,leftOvers[i]) + "\n";
var output = [];
if (sortOrder == "desc")
allTags.reverse();
else if (leftOverOutput != "")
// leftovers first...
output.push(leftOverOutput);
for (var i=0;i<allTags.length;i++)
if (allTagsHolder[allTags[i]] != "")
output.push("*[["+allTags[i]+"]]" + this.getTaggingCount(allTags[i]) + this.getExcerpt(title,allTags[i]) + "\n" + allTagsHolder[allTags[i]]);
if (sortOrder == "desc" && leftOverOutput != "")
// leftovers last...
output.push(leftOverOutput);
return this.drawTable(place,
this.makeColumns(output,parseInt(this.getTagglyOpt(title,"numCols"))),
"grouped");
},
// used to build site map
treeTraverse: function(title,depth,sortBy,sortOrder,isTagExpr) {
var list = config.taggly.getTiddlers(title,sortBy,isTagExpr);
if (sortOrder == "desc")
list.reverse();
var indent = "";
for (var j=0;j<depth;j++)
indent += "*"
var childOutput = "";
if (depth > this.config.siteMapDepthLimit)
childOutput += indent + this.lingo.tooDeepMessage + "\n";
else
for (var i=0;i<list.length;i++)
if (list[i].title != title)
if (this.notHidden(list[i].title,this.config.inTiddler))
childOutput += this.treeTraverse(list[i].title,depth+1,sortBy,sortOrder,false);
if (depth == 0)
return childOutput;
else
return indent + "[["+title+"]]" + this.getTaggingCount(title) + this.getExcerpt(this.config.inTiddler,title,depth) + "\n" + childOutput;
},
// this if for the site map mode
createTagglyListSiteMap: function(place,title,isTagExpr) {
this.config.inTiddler = title; // nasty. should pass it in to traverse probably
var output = this.treeTraverse(title,0,this.getTagglyOpt(title,"sortBy"),this.getTagglyOpt(title,"sortOrder"),isTagExpr);
return this.drawTable(place,
this.makeColumns(output.split(/(?=^\*\[)/m),parseInt(this.getTagglyOpt(title,"numCols"))), // regexp magic
"sitemap"
);
},
macros: {
tagglyTagging: {
handler: function (place,macroName,params,wikifier,paramString,tiddler) {
var parsedParams = paramString.parseParams("tag",null,true);
var refreshContainer = createTiddlyElement(place,"div");
// do some refresh magic to make it keep the list fresh - thanks Saq
refreshContainer.setAttribute("refresh","macro");
refreshContainer.setAttribute("macroName",macroName);
var tag = getParam(parsedParams,"tag");
var expr = getParam(parsedParams,"expr");
if (expr) {
refreshContainer.setAttribute("isTagExpr","true");
refreshContainer.setAttribute("title",expr);
refreshContainer.setAttribute("showEmpty","true");
}
else {
refreshContainer.setAttribute("isTagExpr","false");
if (tag) {
refreshContainer.setAttribute("title",tag);
refreshContainer.setAttribute("showEmpty","true");
}
else {
refreshContainer.setAttribute("title",tiddler.title);
refreshContainer.setAttribute("showEmpty","false");
}
}
this.refresh(refreshContainer);
},
refresh: function(place) {
var title = place.getAttribute("title");
var isTagExpr = place.getAttribute("isTagExpr") == "true";
var showEmpty = place.getAttribute("showEmpty") == "true";
jQuery(place).empty()
addClass(place,"tagglyTagging");
var countFound = config.taggly.getTiddlers(title,'title',isTagExpr).length
if (countFound > 0 || showEmpty) {
var lingo = config.taggly.lingo;
config.taggly.createListControl(place,title,"hideState");
if (config.taggly.getTagglyOpt(title,"hideState") == "show") {
createTiddlyElement(place,"span",null,"tagglyLabel",
isTagExpr ? lingo.labels.exprLabel.format([title]) : lingo.labels.label.format([title]));
config.taggly.createListControl(place,title,"title");
config.taggly.createListControl(place,title,"modified");
config.taggly.createListControl(place,title,"created");
config.taggly.createListControl(place,title,"listMode");
config.taggly.createListControl(place,title,"excerpts");
config.taggly.createListControl(place,title,"numCols");
config.taggly.createTagglyList(place,title,isTagExpr);
if (countFound == 0 && showEmpty)
createTiddlyElement(place,"div",null,"tagglyNoneFound",lingo.labels.noneFound);
}
}
}
}
},
// todo fix these up a bit
styles: [
"/*{{{*/",
"/* created by TagglyTaggingPlugin */",
".tagglyTagging { padding-top:0.5em; }",
".tagglyTagging li.listTitle { display:none; }",
".tagglyTagging ul {",
" margin-top:0px; padding-top:0.5em; padding-left:2em;",
" margin-bottom:0px; padding-bottom:0px;",
"}",
".tagglyTagging { vertical-align: top; margin:0px; padding:0px; }",
".tagglyTagging table { margin:0px; padding:0px; }",
".tagglyTagging .button { visibility:hidden; margin-left:3px; margin-right:3px; }",
".tagglyTagging .button, .tagglyTagging .hidebutton {",
" color:[[ColorPalette::TertiaryLight]]; font-size:90%;",
" border:0px; padding-left:0.3em;padding-right:0.3em;",
"}",
".tagglyTagging .button:hover, .hidebutton:hover, ",
".tagglyTagging .button:active, .hidebutton:active {",
" border:0px; background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]];",
"}",
".selected .tagglyTagging .button { visibility:visible; }",
".tagglyTagging .hidebutton { color:[[ColorPalette::Background]]; }",
".selected .tagglyTagging .hidebutton { color:[[ColorPalette::TertiaryLight]] }",
".tagglyLabel { color:[[ColorPalette::TertiaryMid]]; font-size:90%; }",
".tagglyTagging ul {padding-top:0px; padding-bottom:0.5em; margin-left:1em; }",
".tagglyTagging ul ul {list-style-type:disc; margin-left:-1em;}",
".tagglyTagging ul ul li {margin-left:0.5em; }",
".editLabel { font-size:90%; padding-top:0.5em; }",
".tagglyTagging .commas { padding-left:1.8em; }",
"/* not technically tagglytagging but will put them here anyway */",
".tagglyTagged li.listTitle { display:none; }",
".tagglyTagged li { display: inline; font-size:90%; }",
".tagglyTagged ul { margin:0px; padding:0px; }",
".excerpt { color:[[ColorPalette::TertiaryDark]]; }",
".excerptIndent { margin-left:4em; }",
"div.tagglyTagging table,",
"div.tagglyTagging table tr,",
"td.tagglyTagging",
" {border-style:none!important; }",
".tagglyTagging .contents { border-bottom:2px solid [[ColorPalette::TertiaryPale]]; padding:0 1em 1em 0.5em;",
" margin-bottom:0.5em; }",
".tagglyTagging .indent1 { margin-left:3em; }",
".tagglyTagging .indent2 { margin-left:4em; }",
".tagglyTagging .indent3 { margin-left:5em; }",
".tagglyTagging .indent4 { margin-left:6em; }",
".tagglyTagging .indent5 { margin-left:7em; }",
".tagglyTagging .indent6 { margin-left:8em; }",
".tagglyTagging .indent7 { margin-left:9em; }",
".tagglyTagging .indent8 { margin-left:10em; }",
".tagglyTagging .indent9 { margin-left:11em; }",
".tagglyTagging .indent10 { margin-left:12em; }",
".tagglyNoneFound { margin-left:2em; color:[[ColorPalette::TertiaryMid]]; font-size:90%; font-style:italic; }",
"/*}}}*/",
""].join("\n"),
init: function() {
merge(config.macros,this.macros);
config.shadowTiddlers["TagglyTaggingStyles"] = this.styles;
store.addNotification("TagglyTaggingStyles",refreshStyles);
}
};
config.taggly.init();
//}}}
/***
InlineSlidersPlugin
By Saq Imtiaz
http://tw.lewcid.org/sandbox/#InlineSlidersPlugin
// syntax adjusted to not clash with NestedSlidersPlugin
// added + syntax to start open instead of closed
***/
//{{{
config.formatters.unshift( {
name: "inlinesliders",
// match: "\\+\\+\\+\\+|\\<slider",
match: "\\<slider",
// lookaheadRegExp: /(?:\+\+\+\+|<slider) (.*?)(?:>?)\n((?:.|\n)*?)\n(?:====|<\/slider>)/mg,
lookaheadRegExp: /(?:<slider)(\+?) (.*?)(?:>)\n((?:.|\n)*?)\n(?:<\/slider>)/mg,
handler: function(w) {
this.lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = this.lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart ) {
var btn = createTiddlyButton(w.output,lookaheadMatch[2] + " "+"\u00BB",lookaheadMatch[2],this.onClickSlider,"button sliderButton");
var panel = createTiddlyElement(w.output,"div",null,"sliderPanel");
panel.style.display = (lookaheadMatch[1] == '+' ? "block" : "none");
wikify(lookaheadMatch[3],panel);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
},
onClickSlider : function(e) {
if(!e) var e = window.event;
var n = this.nextSibling;
n.style.display = (n.style.display=="none") ? "block" : "none";
return false;
}
});
//}}}
Can make Clothes, Cloaks and Hats.
Syntax:
*target <character>
Your principle target to attack will be specified. If you do not have a target specified then it will automatically be set to the first character that attacks you. Having an initial target during combat may just give you an edge.
[[Target]] and [[Wait]] are not offensive commands and will not initiate combat. Depending on how [[perceptive|Perception]] your target may be, depends on whether they notice your intent.
You know the silent hand signal language of thieves.
Syntax:
*cant <message>
You may send hidden hand signals to other people present who may know the language. Normally these are unseen by other players, but depending on the length of your message and your skill, other players may see you waving your hands about and realise that you may be in knowledge of illicit skills.
It is dangerous to use Thief Skills in the open. If you are caught then you will automatically start a Combat round. Having said that, a judicious bit of lock picking never really hurt anybody. Did it?
However the Law of the Land prevails. You may not learn Thieves Skills when you first start the game. Perhaps you can try your luck in applying for the Thieves Guild... if you can find them.
Syntax:
*throw [<target>]]
Causes you to throw a readied throwing weapon at your target. Unlike [[Fire]], you do not have to spend time aiming with thrown weapons. After you throw a weapon, it is unreadied and will be in the room, rather than in your inventory. Your readied weapon must be one which can be thrown.
You are trained to throw balanced weapons accurately and forcefully. This means that you can use weapons from the Thrown Weapons group at no minuses.
/***
|Name|TiddlerTweakerPlugin|
|Source|http://www.TiddlyTools.com/#TiddlerTweakerPlugin|
|Version|2.4.0|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|select multiple tiddlers and modify author, created, modified and/or tag values|
~TiddlerTweaker is a tool for TiddlyWiki authors. It allows you to select multiple tiddlers from a listbox, either by direct interaction or automatically matching specific criteria. You can then modify the creator, author, created, modified and/or tag values of those tiddlers using a compact set of form fields. The values you enter into the fields simultantously overwrite the existing values in all tiddlers you have selected.
!!!!!Usage
<<<
{{{<<tiddlerTweaker>>}}}
{{smallform{<<tiddlerTweaker>>}}}
By default, any tags you enter into the TiddlerTweaker will //replace// the existing tags in all the tiddlers you have selected. However, you can also use TiddlerTweaker to quickly filter specified tags from the selected tiddlers, while leaving any other tags assigned to those tiddlers unchanged:
>Any tag preceded by a "+" (plus) or "-" (minus), will be added or removed from the existing tags //instead of replacing the entire tag definition// of each tiddler (e.g., enter "-excludeLists" to remove that tag from all selected tiddlers. When using this syntax, care should be taken to ensure that //every// tag is preceded by "+" or "-", to avoid inadvertently overwriting any other existing tags on the selected tiddlers. (note: the "+" or "-" prefix on each tag value is NOT part of the tag value, and is only used by TiddlerTweaker to control how that tag value is processed)
Important Notes:
* Inasmuch as TiddlerTweaker is a 'power user' tool that can perform 'batch' functions (operating on many tiddlers at once), you should always have a recent backup of your document (or "save changes" just *before* tweaking the tiddlers), just in case you "shoot yourself in the foot".
* By design, TiddlerTweaker does NOT update the 'modified' date of tiddlers simply by making changes to the tiddler's values. A tiddler's dates are ONLY updated when the corresponding 'created' and/or 'modified' checkboxes are selected and you enter new values for those dates. As a general rule, after using TiddlerTweaker, always ''//remember to save your document//'' when you are done, even though the tiddler timeline tab may not show any recently modified tiddlers.
* Because you may be changing the values on many tiddlers simultaneously, selecting and updating all tiddlers in a document operation may take a while and your browser might warn about an "unresponsive script"... you should give it a whole bunch of time to 'continue'... it should complete the processing... eventually.
<<<
!!!!!Revisions
<<<
2009.03.30 [2.4.0] added 'sort by modifier'
2009.01.22 [2.3.0] added support for text pattern find/replace
2008.10.27 [2.2.3] in setTiddlers(), fixed Safari bug by replacing static Array.concat(...) with new Array().concat(...)
2008.09.07 [2.2.2] added removeCookie() function for compatibility with [[CookieManagerPlugin]]
2008.05.12 [2.2.1] replace built-in backstage "tweak" task with tiddler tweaker control panel (moved from BackstageTweaks)
2008.01.13 [2.2.0] added "auto-selection" links: all, changed, tags, title, text
2007.12.26 [2.1.0] added support for managing 'creator' custom field (see [[CoreTweaks]])
2007.11.01 [2.0.3] added config.options.txtTweakerSortBy for cookie-based persistence of list display order preference setting.
2007.09.28 [2.0.2] in settiddlers() and deltiddlers(), added suspend/resume notification handling (improves performance when operating on multiple tiddlers)
2007.08.03 [2.0.1] added shadow definition for [[TiddlerTweaker]] tiddler for use as parameter references with {{{<<tiddler>>, <<slider>> or <<tabs>>}}} macros.
2007.08.03 [2.0.0] converted from inline script
2006.01.01 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TiddlerTweakerPlugin= {major: 2, minor: 4, revision: 0, date: new Date(2009,1,22)};
// shadow tiddler
config.shadowTiddlers.TiddlerTweaker="<<tiddlerTweaker>>";
/// backstage task
if (config.tasks) { // for TW2.2b3 or above
config.tasks.tweak.tooltip="review/modify tiddler internals: dates, authors, tags, etc.";
config.tasks.tweak.content="{{smallform small groupbox{<<tiddlerTweaker>>}}}";
}
if (config.options.txtTweakerSortBy==undefined) config.options.txtTweakerSortBy="modified";
// if removeCookie() function is not defined by TW core, define it here.
if (window.removeCookie===undefined) {
window.removeCookie=function(name) {
document.cookie = name+'=; expires=Thu, 01-Jan-1970 00:00:01 UTC; path=/;';
}
}
config.macros.tiddlerTweaker = {
html: '<form style="display:inline"><!--\
--><table style="padding:0;margin:0;border:0;width:100%"><tr valign="top" style="padding:0;margin:0;border:0"><!--\
--><td style="text-align:center;white-space:nowrap;width:99%;padding:0;margin:0;border:0"><!--\
--><font size=-2><div style="text-align:left;"><span style="float:right"><!--\
--> <a href="javascript:;" \
title="select all tiddlers"\
onclick="\
var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
for (var t=0; t<f.list.options.length; t++)\
if (f.list.options[t].value.length) f.list.options[t].selected=true;\
config.macros.tiddlerTweaker.selecttiddlers(f.list);\
return false">all</a><!--\
--> <a href="javascript:;" \
title="select tiddlers that are new/changed since the last file save"\
onclick="\
var lastmod=new Date(document.lastModified);\
var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
for (var t=0; t<f.list.options.length; t++) {\
var tid=store.getTiddler(f.list.options[t].value);\
f.list.options[t].selected=tid&&tid.modified>lastmod;\
}\
config.macros.tiddlerTweaker.selecttiddlers(f.list);\
return false">changed</a><!--\
--> <a href="javascript:;" \
title="select tiddlers with at least one matching tag"\
onclick="\
var t=prompt(\'Enter space-separated tags (match ONE)\');\
if (!t||!t.length) return false;\
var tags=t.readBracketedList();\
var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
for (var t=0; t<f.list.options.length; t++) {\
f.list.options[t].selected=false;\
var tid=store.getTiddler(f.list.options[t].value);\
if (tid&&tid.tags.containsAny(tags)) f.list.options[t].selected=true;\
}\
config.macros.tiddlerTweaker.selecttiddlers(f.list);\
return false">tags</a><!--\
--> <a href="javascript:;" \
title="select tiddlers whose titles include matching text"\
onclick="\
var txt=prompt(\'Enter a title (or portion of a title) to match\');\
if (!txt||!txt.length) return false;\
var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
for (var t=0; t<f.list.options.length; t++) {\
f.list.options[t].selected=f.list.options[t].value.indexOf(txt)!=-1;\
}\
config.macros.tiddlerTweaker.selecttiddlers(f.list);\
return false">titles</a><!--\
--> <a href="javascript:;" \
title="select tiddlers containing matching text"\
onclick="\
var txt=prompt(\'Enter tiddler text (content) to match\');\
if (!txt||!txt.length) return false;\
var f=this; while (f&&f.nodeName.toLowerCase()!=\'form\')f=f.parentNode;\
for (var t=0; t<f.list.options.length; t++) {\
var tt=store.getTiddlerText(f.list.options[t].value,\'\');\
f.list.options[t].selected=(tt.indexOf(txt)!=-1);\
}\
config.macros.tiddlerTweaker.selecttiddlers(f.list);\
return false">text</a> <!--\
--></span><span>select tiddlers</span><!--\
--></div><!--\
--></font><select multiple name=list size="11" style="width:99.99%" \
title="use click, shift-click and/or ctrl-click to select multiple tiddler titles" \
onclick="config.macros.tiddlerTweaker.selecttiddlers(this)" \
onchange="config.macros.tiddlerTweaker.setfields(this)"><!--\
--></select><br><!--\
-->show<input type=text size=1 value="11" \
onchange="this.form.list.size=this.value; this.form.list.multiple=(this.value>1);"><!--\
-->by<!--\
--><select name=sortby size=1 \
onchange="config.macros.tiddlerTweaker.init(this.form,this.value)"><!--\
--><option value="title">title</option><!--\
--><option value="size">size</option><!--\
--><option value="modified">modified</option><!--\
--><option value="created">created</option><!--\
--><option value="modifier">modifier</option><!--\
--></select><!--\
--><input type="button" value="refresh" \
onclick="config.macros.tiddlerTweaker.init(this.form,this.form.sortby.value)"<!--\
--> <input type="button" name="stats" disabled value="totals..." \
onclick="config.macros.tiddlerTweaker.stats(this)"><!--\
--></td><td style="white-space:nowrap;padding:0;margin:0;border:0;width:1%"><!--\
--><div style="text-align:left"><font size=-2> modify values</font></div><!--\
--><table border=0 style="width:100%;padding:0;margin:0;border:0;"><tr style="padding:0;border:0;"><!--\
--><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=settitle unchecked \
title="allow changes to tiddler title (rename tiddler)" \
onclick="this.form.title.disabled=!this.checked">title<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=title size=35 style="width:98%" disabled><!--\
--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=setcreator unchecked \
title="allow changes to tiddler creator" \
onclick="this.form.creator.disabled=!this.checked">created by<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=creator size=35 style="width:98%" disabled><!--\
--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=setwho unchecked \
title="allow changes to tiddler author" \
onclick="this.form.who.disabled=!this.checked">modified by<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=who size=35 style="width:98%" disabled><!--\
--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=setcdate unchecked \
title="allow changes to created date" \
onclick="var f=this.form; f.cm.disabled=f.cd.disabled=f.cy.disabled=f.ch.disabled=f.cn.disabled=!this.checked"><!--\
-->created on<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=cm size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--> / <input type=text name=cd size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--> / <input type=text name=cy size=4 style="width:3em;padding:0;text-align:center" disabled><!--\
--> at <input type=text name=ch size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--> : <input type=text name=cn size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=setmdate unchecked \
title="allow changes to modified date" \
onclick="var f=this.form; f.mm.disabled=f.md.disabled=f.my.disabled=f.mh.disabled=f.mn.disabled=!this.checked"><!--\
-->modified on<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=mm size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--> / <input type=text name=md size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--> / <input type=text name=my size=4 style="width:3em;padding:0;text-align:center" disabled><!--\
--> at <input type=text name=mh size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--> : <input type=text name=mn size=2 style="width:2em;padding:0;text-align:center" disabled><!--\
--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=replacetext unchecked\
title="find/replace matching text" \
onclick="this.form.pattern.disabled=this.form.replacement.disabled=!this.checked">replace text<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=pattern size=15 value="" style="width:40%" disabled \
title="enter TEXT PATTERN (regular expression)"> with <!--\
--><input type=text name=replacement size=15 value="" style="width:40%" disabled \
title="enter REPLACEMENT TEXT"><!--\
--></td></tr><tr style="padding:0;border:0;"><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=checkbox name=settags checked \
title="allow changes to tiddler tags" \
onclick="this.form.tags.disabled=!this.checked">tags<!--\
--></td><td style="padding:1px;border:0;white-space:nowrap"><!--\
--><input type=text name=tags size=35 value="" style="width:98%" \
title="enter new tags or use \'+tag\' and \'-tag\' to add/remove tags from existing tags"><!--\
--></td></tr></table><!--\
--><div style="text-align:center"><!--\
--><nobr><input type=button name=display disabled style="width:32%" value="display tiddlers" \
onclick="config.macros.tiddlerTweaker.displaytiddlers(this)"><!--\
--> <input type=button name=del disabled style="width:32%" value="delete tiddlers" \
onclick="config.macros.tiddlerTweaker.deltiddlers(this)"><!--\
--> <input type=button name=set disabled style="width:32%" value="update tiddlers" \
onclick="config.macros.tiddlerTweaker.settiddlers(this)"></nobr><!--\
--></div><!--\
--></td></tr></table><!--\
--></form><span style="display:none"><!--content replaced by tiddler "stats"--></span>\
',
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var span=createTiddlyElement(place,"span");
span.innerHTML=this.html;
this.init(span.firstChild,config.options.txtTweakerSortBy);
},
init: function(f,sortby) { // initialize form controls
if (!f) return; // form might not be rendered yet...
while (f.list.options[0]) f.list.options[0]=null; // empty current list content
var tids=store.getTiddlers(sortby);
if (sortby=='size') // descending order
tids.sort(function(a,b) {return a.text.length > b.text.length ? -1 : (a.text.length == b.text.length ? 0 : +1);});
var who='';
for (i=0; i<tids.length; i++) { var t=tids[i];
var label=t.title; var value=t.title;
switch (sortby) {
case 'modified':
case 'created':
var t=tids[tids.length-i-1]; // reverse order
var when=t[sortby].formatString('YY.0MM.0DD 0hh:0mm ');
label=when+t.title;
value=t.title;
break;
case 'size':
label='['+t.text.length+'] '+label;
break;
case 'modifier':
case 'creator':
if (who!=t[sortby]) {
who=t[sortby];
f.list.options[f.list.length]=new Option('by '+who+':','',false,false);
}
label='\xa0\xa0\xa0'+label; // indent
break;
}
f.list.options[f.list.length]=new Option(label,value,false,false);
}
f.title.value=f.who.value=f.creator.value=f.tags.value="";
f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value="";
f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value="";
f.stats.disabled=f.set.disabled=f.del.disabled=f.display.disabled=true;
f.settitle.disabled=false;
config.options.txtTweakerSortBy=sortby; // remember current setting
f.sortby.value=sortby; // sync droplist selection with current setting
if (sortby!="modified") // non-default preference... save cookie
saveOptionCookie("txtTweakerSortBy");
else removeCookie("txtTweakerSortBy"); // default preference... clear cookie
},
selecttiddlers: function(here) { // enable/disable tweaker fields based on number of items selected
// count how many tiddlers are selected
var f=here.form; var list=f.list;
var c=0; for (i=0;i<list.length;i++) if (list.options[i].selected) c++;
if (c>1) f.title.disabled=true;
if (c>1) f.settitle.checked=false;
f.set.disabled=(c==0);
f.del.disabled=(c==0);
f.display.disabled=(c==0);
f.settitle.disabled=(c>1);
f.stats.disabled=(c==0);
var msg=(c==0)?'select tiddlers':(c+' tiddler'+(c!=1?'s':'')+' selected');
here.previousSibling.firstChild.firstChild.nextSibling.innerHTML=msg;
if (c) clearMessage(); else displayMessage("no tiddlers selected");
},
setfields: function(here) { // set tweaker edit fields from first selected tiddler
var f=here.form;
if (!here.value.length) {
f.title.value=f.who.value=f.creator.value=f.tags.value="";
f.cm.value=f.cd.value=f.cy.value=f.ch.value=f.cn.value="";
f.mm.value=f.md.value=f.my.value=f.mh.value=f.mn.value="";
return;
}
var tid=store.getTiddler(here.value); if (!tid) return;
f.title.value=tid.title;
f.who.value=tid.modifier;
f.creator.value=tid.fields['creator']||''; // custom field - might not exist
f.tags.value=tid.tags.join(' ');
var c=tid.created; var m=tid.modified;
f.cm.value=c.getMonth()+1;
f.cd.value=c.getDate();
f.cy.value=c.getFullYear();
f.ch.value=c.getHours();
f.cn.value=c.getMinutes();
f.mm.value=m.getMonth()+1;
f.md.value=m.getDate();
f.my.value=m.getFullYear();
f.mh.value=m.getHours();
f.mn.value=m.getMinutes();
},
settiddlers: function(here) {
var f=here.form; var list=f.list;
var tids=[];
for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert("please select at least one tiddler"); return; }
var cdate=new Date(f.cy.value,f.cm.value-1,f.cd.value,f.ch.value,f.cn.value);
var mdate=new Date(f.my.value,f.mm.value-1,f.md.value,f.mh.value,f.mn.value);
if (tids.length>1 && !confirm("Are you sure you want to update these tiddlers:\n\n"+tids.join(', '))) return;
store.suspendNotifications();
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
var title=!f.settitle.checked?tid.title:f.title.value;
var who=!f.setwho.checked?tid.modifier:f.who.value;
var text=tid.text;
if (f.replacetext.checked) text=text.replace(new RegExp(f.pattern.value,'mg'),f.replacement.value);
var tags=tid.tags;
if (f.settags.checked) {
var intags=f.tags.value.readBracketedList();
var addtags=[]; var deltags=[]; var reptags=[];
for (i=0;i<intags.length;i++) {
if (intags[i].substr(0,1)=='+')
addtags.push(intags[i].substr(1));
else if (intags[i].substr(0,1)=='-')
deltags.push(intags[i].substr(1));
else
reptags.push(intags[i]);
}
if (reptags.length)
tags=reptags;
if (addtags.length)
tags=new Array().concat(tags,addtags);
if (deltags.length)
for (i=0;i<deltags.length;i++)
{ var pos=tags.indexOf(deltags[i]); if (pos!=-1) tags.splice(pos,1); }
}
if (!f.setcdate.checked) cdate=tid.created;
if (!f.setmdate.checked) mdate=tid.modified;
store.saveTiddler(tid.title,title,text,who,mdate,tags,tid.fields);
if (f.setcreator.checked) store.setValue(tid.title,'creator',f.creator.value); // set creator
if (f.setcdate.checked) tid.assign(null,null,null,null,null,cdate); // set create date
}
store.resumeNotifications();
this.init(f,f.sortby.value);
},
displaytiddlers: function(here) {
var f=here.form; var list=f.list;
var tids=[];
for (i=0; i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert("please select at least one tiddler"); return; }
story.displayTiddlers(story.findContainingTiddler(f),tids)
},
deltiddlers: function(here) {
var f=here.form; var list=f.list;
var tids=[];
for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert("please select at least one tiddler"); return; }
if (!confirm("Are you sure you want to delete these tiddlers:\n\n"+tids.join(', '))) return;
store.suspendNotifications();
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
if (tid.tags.contains("systemConfig"))
if (!confirm("'"+tid.title+"' is tagged with 'systemConfig'.\n\nRemoving this tiddler may cause unexpected results. Are you sure?"))
continue;
store.removeTiddler(tid.title);
story.closeTiddler(tid.title);
}
store.resumeNotifications();
this.init(f,f.sortby.value);
},
stats: function(here) {
var f=here.form; var list=f.list; var tids=[]; var out=''; var tot=0;
var target=f.nextSibling;
for (i=0;i<list.length;i++) if (list.options[i].selected) tids.push(list.options[i].value);
if (!tids.length) { alert("please select at least one tiddler"); return; }
for (t=0;t<tids.length;t++) {
var tid=store.getTiddler(tids[t]); if (!tid) continue;
out+='[['+tid.title+']] '+tid.text.length+'\n'; tot+=tid.text.length;
}
var avg=tot/tids.length;
out=tot+' bytes in '+tids.length+' selected tiddlers ('+avg+' bytes/tiddler)\n<<<\n'+out+'<<<\n';
removeChildren(target);
target.innerHTML="<hr><font size=-2><a href='javascript:;' style='float:right' "
+"onclick='this.parentNode.parentNode.style.display=\"none\"'>close</a></font>";
wikify(out,target);
target.style.display="block";
}
};
//}}}
TinyFantasy is an online, text-based, multi-user role-playing [[game|Game]].
Remember the old black screens with descriptions like "You are in a dark room. A match box stands beside you on the table. What do you do?" type of thing? Yes, exactly like that.
You connect via telnet or a dedicated MUD client (such as TinyFugue) to a game connection which gives you a text user interface. From there you type in [[Commands]] and see the results. When you connect for the first time, you will be prompted to create your own [[Character|Characters]] with which you will interact with the world and other players. Remember this is a multi-user game so you will be able to talk to other players, like yourself, within the game.
If you are already familiar with how ~MUDs work then you can skip the tutorial part of the initial period which teaches you how to move and interact with the game.
TinyFantasy is based on a long-term project dating back to early 1991. It is based on the TinyMUCK codebase. Initially written for TinyMUCK 2.1, this version has been modified to take advantage of further development and other works and is now based on the ~FuzzBall 6.17 MUCK code.
TinyFugue, aka "tf", is a flexible, screen-oriented MUD client, for use with any type of text MUD.
It is available for most platforms at http://tinyfugue.sourceforge.net/
TinyMUCK or, more broadly, a MUCK, is a type of user-extendable online text-based role-playing game, designed for role playing and social interaction. Backronyms like "~Multi-User Chat/Created/Computer/Character/Carnal Kingdom" and "~Multi-User Construction Kit" are sometimes cited, but are not the actual origin of the term; "muck" is simply a play on the term MUD.
https://en.wikipedia.org/wiki/TinyMUCK
It has a very distinctive programming system. The <html><b><a href="TinyMUCK.html">Development Documentation</a></b></html> is in a separate page.
/***
|Name:|ToggleTagPlugin|
|Description:|Makes a checkbox which toggles a tag in a tiddler|
|Version:|3.1.0a|
|Date:|27-Jun-2011|
|Source:|http://mptw.tiddlyspot.com/#ToggleTagPlugin|
|Author:|Simon Baird <simon.baird@gmail.com>|
|License:|http://mptw.tiddlyspot.com/#TheBSDLicense|
!!Usage
{{{<<toggleTag }}}//{{{TagName TiddlerName LabelText}}}//{{{>>}}}
* TagName - the tag to be toggled, default value "checked"
* TiddlerName - the tiddler to toggle the tag in, default value the current tiddler
* LabelText - the text (gets wikified) to put next to the check box, default value is '{{{[[TagName]]}}}' or '{{{[[TagName]] [[TiddlerName]]}}}'
(If a parameter is '.' then the default will be used)
* TouchMod flag - if non empty then touch the tiddlers mod date. Note, can set config.toggleTagAlwaysTouchModDate to always touch mod date
!!Examples
|Code|Description|Example|h
|{{{<<toggleTag>>}}}|Toggles the default tag (checked) in this tiddler|<<toggleTag>>|
|{{{<<toggleTag TagName>>}}}|Toggles the TagName tag in this tiddler|<<toggleTag TagName>>|
|{{{<<toggleTag TagName TiddlerName>>}}}|Toggles the TagName tag in the TiddlerName tiddler|<<toggleTag TagName TiddlerName>>|
|{{{<<toggleTag TagName TiddlerName 'click me'>>}}}|Same but with custom label|<<toggleTag TagName TiddlerName 'click me'>>|
|{{{<<toggleTag . . 'click me'>>}}}|dot means use default value|<<toggleTag . . 'click me'>>|
!!Notes
* If TiddlerName doesn't exist it will be silently created
* Set label to '-' to specify no label
* See also http://mgtd-alpha.tiddlyspot.com/#ToggleTag2
!!Known issues
* Doesn't smoothly handle the case where you toggle a tag in a tiddler that is current open for editing
* Should convert to use named params
***/
//{{{
if (config.toggleTagAlwaysTouchModDate == undefined) config.toggleTagAlwaysTouchModDate = false;
merge(config.macros,{
toggleTag: {
createIfRequired: true,
shortLabel: "[[%0]]",
longLabel: "[[%0]] [[%1]]",
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var tiddlerTitle = tiddler ? tiddler.title : '';
var tag = (params[0] && params[0] != '.') ? params[0] : "checked";
var title = (params[1] && params[1] != '.') ? params[1] : tiddlerTitle;
var defaultLabel = (title == tiddlerTitle ? this.shortLabel : this.longLabel);
var label = (params[2] && params[2] != '.') ? params[2] : defaultLabel;
var touchMod = (params[3] && params[3] != '.') ? params[3] : "";
label = (label == '-' ? '' : label); // dash means no label
var theTiddler = (title == tiddlerTitle ? tiddler : store.getTiddler(title));
var cb = createTiddlyCheckbox(place, label.format([tag,title]), theTiddler && theTiddler.isTagged(tag), function(e) {
if (!store.tiddlerExists(title)) {
if (config.macros.toggleTag.createIfRequired) {
var content = store.getTiddlerText(title); // just in case it's a shadow
store.saveTiddler(title,title,content?content:"",config.options.txtUserName,new Date(),null);
}
else
return false;
}
if ((touchMod != "" || config.toggleTagAlwaysTouchModDate) && theTiddler)
theTiddler.modified = new Date();
store.setTiddlerTag(title,this.checked,tag);
return true;
});
}
}
});
//}}}
Toughness means you can take a beating. Each level of Toughness taken increases your [[hit points|Hit Points]] by 2.
A trait is a personality quirk of your [[Character|Characters]] that may only be selected when creating your Character for the first time.
Traits are random personality quirks that your character may have. You can only gain Traits when creating your character for the first time and they are assigned purely on a random basis. Once one is allocated to you it can never be removed.
The current list of Traits are:
<<forEachTiddler
where
'tiddler.tags.contains("Trait")'
sortBy
'tiddler.title'
write
'"|!" + tiddler.title + "|<"+"<tiddler [[" + tiddler.title + "]]>" + "> |\n"'
begin
''
>>
Causes you to stop guarding a player, exit or object that you were previously [[Guarding|Guard]].
Syntax:
*unlock <object>
This allows you to unlock an already locked door or [[Container|Containers]]. You would need to be [[holding|Hold]] the appropriate key for the lock.
See also [[Lock]], [[Open]] and [[Close]].
Syntax:
*value <object>
Some Shop keepers will appraise items to give you a value that is applicable to them. If they aren't interested then they may not give you a value, or evaluate it extremely low.
See also [[Buy]], [[Sell]], and [[List]].
Syntax:
*wait
[[Wait]] and [[Target]] are readiness manoeuvres. Wait initiates an event loop with no designated target and with your offensive action set to 'wait'. You are not doing anything, but you are ready. [[Target]] does the same thing, but also designates a target. Section 1.7.10 below, Responding to Combat Events, discusses the reasons why you might want to use these commands.
The Watch are the Guardians of the Law. Where they patrol, citizens feel safe. However there are parts where they never patrol... or even know about.
Of course if you're willing to put in the work, then maybe you could apply to join the Watch.
Weapons come in many types depending on the need. Some weapons such as [[Axes]] are also used as tools to practice skills, as well as to protect yourself.
An alias for [[Equip]].
Certain items can only be worn in a particular order. For example, you cannot put on a Ring whist wearing Gloves.
Every object that you carry has a weight (measured in [[Cobs]]). This "weight" also represents that awkwardness of carrying something.
The more you carry, the more encumbered you become. Eventually you become so encumbered that your movement is severely restricted. It's even possible to become so loaded down with goods that you physically cannot move. Your only option is to [[drop|Drop]] something.
|!Limit |!Consequence |![[Initiative]] |
| Up to STR*10 |Unencumbered |Normal |
| Up to STR*20 |Incur an extra [[Fatigue]] cost everytime you move or fight |Twice |
| Up to STR*30 |Incur triple [[Fatigue]] costs |Thrice |
| Over STR*30 |Overloaded. Unable to move or fight |-- |
[[Containers]] can help you organise your equipment a bit better.
Syntax:
*whisper <player> <message>
Whispers the message to the named person, if they are in the same room as you. No one else can see the message. Wizards can use the form 'whisper <player> <message>' to whisper to players in other rooms. Characters in the same room know that you're whispering to the otehr character, but not what is said.
Also see: [[Chat]], [[Say]] and [[Pose]].
Syntax:
* who
This command allows you to see which characters are connected to the game. In some areas it may also tell you exactly what they are doing or where they are located.
You grew up in the wilds. As such you have an inate ability to understand Nature and automatically gain [[Hunter]] and [[Farmer]] skills.
Syntax:
*wimp <n>
*wimp max
*wimp min
This sets a value for which you will automatically attempt to [[Flee]] the battle. When your [[Hit Points]] drops to this level then you will always try to [[Flee]] the combat. A Wimp value of 0 (default) means that you will fight to the death. You cannot set your Wimp value to be greater than 1/2 of your maximum [[Hit Points]]. The value "max" will set your wimp value to it's highest level, whilst a "min" value will set it to 10% of your maximum [[Hit Points]].
A Wizard is a player who has special privileges within the game. They can manipulate the database and modify settings, as well as individual players.
These commands are reserved for the system wizards. Normal mortals will be denied access to them.
The Wizard Zone is a restricted areas of the system which houses all of the scripts and commands necessary for the functioning of the game world. It is restricted to the System Wizards. You will probably never see it.