Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
Simple minded, single threaded chat or forum. To add a new entry, click ''new chat entry'' in the menu on the right.
[[README]]
[[News]]
[[Download]]
Change the ''Issue'' tag of bug reports or wish list entries to ''Done'' to move them to the list of Fixed bugs.
!Mercurial repository
{{{
hg clone http://odds.aminche.com/hg/poker-eval-webservice
}}}
!Tarball
http://odds.aminche.com/poker-eval-webservice_1.0.0-1.tar.gz
!Debian source package
{{{
dget http://odds.aminche.com/poker-eval-webservice_1.0.0-1.dsc
}}}
Powered by [[tiddlyforge|http://garden.dachary.org/tiddlyforge.html]] Copyright Loic Dachary mailto:loic@dachary.org License : [[GNU GPLv3 or Later|http://gnu.org/licenses/gpl.txt]]
/***
|''Name:''|GenerateRssByTagPlugin|
|''Description:''|Only tiddlers with a specific tag are inluded in the RSSFeed. If no tiddlers are selected then works as before. (see ticket #270: http://trac.tiddlywiki.org/tiddlywiki/ticket/270). <br>RssTag: <<option txtRssTag>>|
|''Version:''|1.0.2|
|''Date:''|Apr 20, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#GenerateRssByTagPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''[[License]]:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.GenerateRssByTagPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date("Apr 20, 2007"),
source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0 (Beta 5)'
};
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.generateRssByTag = function()
{
var s = [];
var d = new Date();
var u = store.getTiddlerText("SiteUrl");
// Assemble the header
s.push("<" + "?xml version=\"1.0\"" + " encoding='UTF-8' " + "?" + ">");
s.push("<rss version=\"2.0\">");
s.push("<channel>");
s.push("<title" + ">" + wikifyPlain("SiteTitle").htmlEncode() + "</title" + ">");
if(u)
s.push("<link>" + u.htmlEncode() + "</link>");
s.push("<description>" + wikifyPlain("SiteSubtitle").htmlEncode() + "</description>");
s.push("<language>en-us</language>");
s.push("<copyright>Copyright " + d.getFullYear() + " " + config.options.txtUserName.htmlEncode() + "</copyright>");
s.push("<pubDate>" + d.toGMTString() + "</pubDate>");
s.push("<lastBuildDate>" + d.toGMTString() + "</lastBuildDate>");
s.push("<docs>http://blogs.law.harvard.edu/tech/rss</docs>");
s.push("<generator>TiddlyWiki " + version.major + "." + version.minor + "." + version.revision + "</generator>");
// The body
var tiddlers;
if (config.options.txtRssTag && store.getTaggedTiddlers(config.options.txtRssTag).length > 0)
tiddlers = store.getTaggedTiddlers(config.options.txtRssTag,"modified");
else
tiddlers = store.getTiddlers("modified","[[excludeLists]]");
var n = config.numRssItems > tiddlers.length ? 0 : tiddlers.length-config.numRssItems;
for (var t=tiddlers.length-1; t>=n; t--)
s.push(tiddlers[t].saveToRss(u));
// And footer
s.push("</channel>");
s.push("</rss>");
// Save it all
return s.join("\n");
};
//
// Initializations
//
bidix.generateRss = generateRss; // backup core version
generateRss = bidix.generateRssByTag; // install new one
config.options.txtRssTag = "toRSS"; // default RssTag. use <<option txtRssTag>> to overwritte
merge(config.optionsDesc,{txtRssTag: "Only tiddlers with this tag will be included in the RSS Feed."});
//}}}
/***
| Name|HideWhenPlugin|
| Description|Allows conditional inclusion/exclusion in templates|
| Version|3.0 ($Rev: 1845 $)|
| Date|$Date: 2007-03-16 15:19:22 +1000 (Fri, 16 Mar 2007) $|
| Source|http://mptw.tiddlyspot.com/#HideWhenPlugin|
| Author|Simon Baird <simon.baird@gmail.com>|
| License|http://mptw.tiddlyspot.com/#TheBSDLicense|
For use in ViewTemplate and EditTemplate. Example usage:
{{{<div macro="showWhenTagged Task">[[TaskToolbar]]</div>}}}
{{{<div macro="showWhen tiddler.modifier == 'BartSimpson'"><img src="bart.gif"/></div>}}}
***/
//{{{
window.removeElementWhen = function(test,place) {
if (test) {
removeChildren(place);
place.parentNode.removeChild(place);
}
};
merge(config.macros,{
hideWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( eval(paramString), place);
}},
showWhen: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !eval(paramString), place);
}},
hideWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAll(params), place);
}},
showWhenTagged: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAll(params), place);
}},
hideWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAny(params), place);
}},
showWhenTaggedAny: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAny(params), place);
}},
hideWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( tiddler.tags.containsAll(params), place);
}},
showWhenTaggedAll: { handler: function (place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !tiddler.tags.containsAll(params), place);
}},
hideWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0]), place);
}},
showWhenExists: { handler: function(place,macroName,params,wikifier,paramString,tiddler) {
removeElementWhen( !(store.tiddlerExists(params[0]) || store.isShadowTiddler(params[0])), place);
}}
});
//}}}
http://www.hawksworx.com/playground/TeamTasks/ which helps replace the manually maintained issue list. I'm unsure if this really is necessary given that a tiddler is a good tracker entry for most purposes.
#
# ssh root@odds.aminche.com -p 3822
#
# tiddlywiki_cp '/home/www/index.html#Invitations' /root/.ssh/authorized_keys
#
# To get root access to the machine add your public ssh key here, with your email.
# One of the persons listed below will open the door for you. If it does not happen
# fast enough, send them an email. And be prepared to answer similar emails from
# newcomers in the future.
#
# Loic Dachary loic@dachary.org
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEApKfP/I/WoSeX7MAFGpdntC56oHk0wJNn+qy0cud321yTaZRReJGQyqhoBk/sXsnqF4mOpXTf3+Ao1hQIiFazR5Fbmvk7DR0nKj8sGPV0iJB1F0+eyL+w7mgPyD8VDCw+ygCWG4ghMfeGKRAJQweO4v7rFx61UeYDxNk3roOgeuM= loic@inspiron.dachary.org
To add a new entry, click ''new bug'' in the menu on the right.
/***
|''Name:''|LegacyStrikeThroughPlugin|
|''Description:''|Support for legacy (pre 2.1) strike through formatting|
|''Version:''|1.0.2|
|''Date:''|Jul 21, 2006|
|''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|
|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|
|''License:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.0|
***/
//{{{
// Ensure that the LegacyStrikeThrough Plugin is only installed once.
if(!version.extensions.LegacyStrikeThroughPlugin) {
version.extensions.LegacyStrikeThroughPlugin = {installed:true};
config.formatters.push(
{
name: "legacyStrikeByChar",
match: "==",
termRegExp: /(==)/mg,
element: "strike",
handler: config.formatterHelpers.createElementAndWikify
});
} //# end of "install only once"
//}}}
The license(s) covering all the copyrightable materials in this directory.
[[README]]
[[News]]
[[Download]]
[[Browse Sources|http://odds.aminche.com/hg/poker-eval-webservice/file/tip/]]
[[Bug tracker & wishes|Issue]]
[[Fixed bugs|Done]]
[[Chat & Forum|Chat]]
[[Invitations]]
[[License]]
[[Wiki RSS|index.xml]]
[[Sources RSS|http://odds.aminche.com/hg/poker-eval-webservice/rss-log]]
[[Appliance RSS|http://odds.aminche.com/hg/odds.aminche.com/rss-log]]
[[Meta|Meta]]
<!-- software appliance meta data information. the associated micro format is loosely described at http://garden.dachary.org/#meta -->
<appliance state="tiddlywiki" url="http://garden.dachary.org/tiddlyforge.html" name="tiddlyforge" domain="dachary.org" realm="dachary" author="loic@dachary.org">
</appliance>
<<rssReader noDesc "http://odds.aminche.com/hg/poker-eval-webservice/rss-log">>
<<rssReader noDesc "http://odds.aminche.com/hg/odds.aminche.com/rss-log">>
!Next Generation In Poker Stats Software
!Background and Rationale
<html>
<p>The poker-eval library, which was floating around since the early 1990s,
has been often used by new players to begin to understand the
statistical situations that occur in hand lineups. Indeed, in a
geek-home-game that I once frequented in Boston, it was very popular,
upon an all-in situation on the flop, for someone to pull out a laptop
and run hcmp2/pokenum to *prove* he was right that he got his money in
as a favorite.</p>
<p> Certainly, using hcmp2 or pokenum has helped many new players
understand why certain hands that are likely not “best” are
still strong holdings and want to be played aggressively even against a
likely “better” hand when there are cards to come. For example, pokenum
taught many of us that semi-bluffing all-in (when stacks are medium-deep)
with middle pair and a flush draw against a likely weak top pair is an
excellent play:</p>
<pre>
$ pokenum -h ac qc - 7d 5d -- 7c 3d qd
Holdem Hi: 990 enumerated boards containing 7c Qd 3d
cards win %win lose %lose tie %tie EV
Ac Qc 494 49.90 496 50.10 0 0.00 0.499
7d 5d 496 50.10 494 49.90 0 0.00 0.501
</pre>
<p>
We quickly see that the Hero (holding 5d7d) is a slight favorite, thus
when factoring in even a little bit of fold equity, it's clear that the
Hero should put the money in even if Hero "knows" the Villain has TPTK.
</p>
<p>
Indeed, I even wrote a rudimentary Perl script at one point to wrap
pokenum and calculate the win/loss percentage and EV based on the idea
that the Villain has a set N% of the time, top pair N%, etc.</p>
<p>
These sort of calculations are very helpful to the new player, and
everyone goes through a period in their poker education when do a lot of
these to get a feel for such situations.</p>
<p>However, I rarely do these sorts of things anymore, because I basically
know the math well and can get a rough estimate by sight when presented
with a situation and make the correct playing decision. I think this is
common for most players.</p>
<p>Indeed, non-FLOSS geeks often used <a
href="http://twodimes.net">twodimes.net</a>, which is a simplistic web
interface to pokenum, and is similar to the work proppy did on this
project. However, once people get reasonably good at poker, these types
of calculations become less and less interesting, as they have for me.</p>
<p>There are, however, calculations of interest to even the experienced
player, but they are tougher to describe. I believe that such software
could be very helpful to players in an ongoing way, and it would be
particularly useful if an inventive AJAX interface (as well as a CLI, of
course) were designed to allow such calculations.</p>
</html>
!Problem Specification
<html>
<p>The calculations that are complicated enough that experienced players
would seek to do would, most typically, involve a multi-street situation
against a somewhat knowledgeable opponent who will act differently based
on various complications. This is best explained by stating a common
statistic question that a somewhat experienced player might ask:</p>
<p style="margin-right: '5%'; margin-left: '5%'">
In NL HE, I often raise with AK in early position and receive one
caller who has position. Assuming that I and my opponent have stacks
of 100 BBs, it would be useful to figure out what my EV is in
continuation betting the flop when I flop only Ace-high.
Let's assume that on the flop, when I bet 3/4ths the pot, my opponent
acts as follows:
<ol type="a">
<li>my opponent will call 60% of the time on the flop with
middle pair with a non-overcard kicker, </li>
<li> that he will call 65% of the time with middle pair and
overcard, and raise 10% of the time in that situation.</li>
<li> slow play a set, a flopped straight, or a flopped flush,
or any better hand, by just calling with a plan to raise
the turn</li>
<li> always raise with any top pair on a board that has a
straight draw and a flush draw, and raise 80% of the time
with top pair in other situations, and call the rest of
the time.</li>
</ol>
</p>
<p style="margin-right: '5%'; margin-left: '5%'">
For simplicity, we assume that I am done with the hand (check-fold) if
I still have only ace high on the turn, and I will bet half the pot on
the turn and river when I turn an Ace or King, and always fold when
raised, and assume my opponent will raise if he makes a set or better
on the turn or river.
</p>
<p>This is a relatively basic simulation, from a poker player's point of
view. In other words, this is in the <strong>bottom</strong> of
complexity for the kinds of statistic questions poker players want to ask.
It's easy to see how we could increase or make more complex the situation,
by adding additional callers preflop, or by making our opponent more
sophisticated, or by adding in more options for what happens on the turn
or the river.</p>
</html>
!Thoughts on Software API for Defining Action
<html>
<p>The most difficult part of creating software for this purpose is the
interface for defining the situation. A first step, therefore, would be
to create a series of objects (presumably in Python, so it could use the
rest of poker-engine and poker-network) that make it extremely easy to
program these types of actions by players in a hand together. The API
needs the following things:
<ol type="a">
<li> A SimulationActor class, that allows to create an object that
has actions based on what betting round it is.</li>
<li> Board/Hand evaluation macros, that allow you to quantify a
situation like “I want this player call 65% of the time with
middle pair and overcard, and raise 10% of the time in that
situation.” or “call 75% of a time with a straight draw without
a flush draw possible, and raise 25% of the time”. </li>
<li> Hand range operators for evaluating starting hands. </li>
</ol>
</p>
<p>
So, we might set up a situation this way (forgive the pseudo-code-ness):
<pre>
def villainFlopPlan(hand, flop):
if flop.haveTopPair(hand) and flop.haveOvercard(hand)
and flop.haveNoStraightFlushDraw(flop):
return { call : 65%, raise : { 'half-pot', 10% },
fold : rest}
# flushDraw(flop) might return things like
# "backdoor-s;oneCard-h"
elsif (not flop.flushDraw().find('oneCard')) and
flop.haveOpenEndedStraightDraw(hand):
return { call : 75%, raise { 'pot' : 25% } }
return { fold : 100% }
def villainHandSelection(startingHand):
if startingHand.suitedConnector(min = '45', max = 'AK'):
return { call : 100% }
elsif startingHand.tenPointers(hand = startingHand, min = 'QT'):
return { call : 90% }
elsif startingHand.pairs(min = '55', max = 'TT'):
return { call : 100% }
# We don't care about hands he folds or reraises with
# for this simulation
return { fold : 100% }
def heroHandSelection(startingHand):
if startingHand.anySuits('AK'):
return { raise: {'pot' : 100% }}
return { fold : 100% }
def heroFlopPlan(hand, flop):
return { bet: '3/4-pot' }
sim = new Simulation('nl-he-100-200')
villain = new SimulationActor(stack = 1000)
villain.actionPlan('preflop', villainHandSelection)
villain.actionPlan('flop', villainFlopPlan)
hero = new SimulationActor(stack = 1000)
hero.actionPlan('preflop', heroHandSelection)
hero.actionPlan(flop, heroFlopPlan)
sim.run({ actors : [ hero, villain ])
# Returns EV for each player
</pre>
</p>
</html>
!Next Steps After API
<html>
<p>
The work above is not terribly difficult, although there is a lot of
grunt work involved. The hard part after that is making that kind of
system accessible to non-programmers. This is the area where my
expertise falls down; I can't imagine what a GUI would look like to make
programming those objects easy to a non-programmer.</p>
<p>But, that's where this becomes really interesting for some good UI
designer out there. I imagine that a site that featured this sort of
system, if the interface were strong, would be quite popular and could
even support itself with advertising.</p>
<p>Some ideas I had is a sort of domain-specific language for defining
actions, which maybe would make it too much like programming. Another
idea is some sort of line-based action rules like:
<pre>
HAND: { (ACTION, PERCENT), (ACTION, PERCENT) ... }
</pre>
which would give an easy interface to a subset of what the API can
actually do.
</p>
</html>
!Other Applications
<html>
<p>
Obviously, some the API I've suggested would be useful in implementing
bot players too. I know some pokersource hackers are working on bot API
infrastructure, and I would encourage them to think about how this kind
of API could be used both for bots and simulation programming. I think
the domains overlap enough that we should be general enough to support
both.
</p>
<p>
This document is Copyright © 2008, Bradley M. Kuhn. I license it
under the disjunction of three licenses: GPLv3-or-later and
FDLv1.1-or-later and CC-By-SA-2.0-only. Email me if you are confused
about licensing details.</p>
</html>
This is a bug / wish list example entry that should be removed. It's only here to show how it looks like.
<!--{{{-->
<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>
<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 id="footer" class="footer" refresh='content' force='true' tiddler='Footer'></div>
</div>
<!--}}}-->
!Introduction
poker-eval-webservice is a JSON webservice for poker odds evaluation with a [[jQuery|http://jquery.com/]] (javascript) client form
!jQuery client
http://odds.aminche.com/poker-eval/
!Testing
http://odds.aminche.com/poker-eval/test.html
!Webservice usage
http://odds-webservice.aminche.com/
<html>
<a href='http://odds-webservice.aminche.com/?&game="holdem"&pockets=[["Ks","Kd"],["As","Ad"]]&jsoncallback=jsonpcallback
'>http://odds-webservice.aminche.com/?&game="holdem"&pockets=[["Ks","Kd"],["As","Ad"]]&jsoncallback=jsonpcallback
</a>
<a href='http://odds-webservice.aminche.com/?&game="holdem"&pockets=[["Ks","Kd"],["As","Ad"]]&board=["Ac","Kc"]&jsoncallback=jsonpcallback'>http://odds-webservice.aminche.com/?&game="holdem"&pockets=[["Ks","Kd"],["As","Ad"]]&board=["Ac","Kc"]&jsoncallback=jsonpcallback
</a>
<a
href='http://odds-webservice.aminche.com/?&game="holdem"&pockets=[["Ks","Kd"],["As","Ad"]]&board=["Ac","Kc"]&dead=["Ah"]&jsoncallback=jsonpcallback'>
http://odds-webservice.aminche.com/?&game="holdem"&pockets=[["Ks","Kd"],["As","Ad"]]&board=["Ac","Kc"]&dead=["Ah"]&jsoncallback=jsonpcallback
</a>
</html>
/***
|''Name:''|RSSReaderPlugin|
|''Description:''|This plugin provides a RSSReader for TiddlyWiki|
|''Version:''|0.3.0|
|''Date:''|Aug 24, 2006|
|''Source:''|http://tiddlywiki.bidix.info/#RSSReaderPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#RSSReaderPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''Credit:''|BramChen for RssNewsMacro|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.0.0|
|''Browser:''|Firefox 1.5; InternetExplorer 6.0; Safari|
|''Include:''|none|
|''Require:''|none|
***/
//{{{
version.extensions.RSSReaderPlugin = {
major: 0, minor: 3, revision: 0,
date: new Date("Aug 24, 2006"),
author: "BidiX",
credit: "BramChen for RssNewsMacro",
source: "http://TiddlyWiki.bidix.info/#RSSReaderPlugin",
documentation : "http://TiddlyWiki.bidix.info/#RSSReaderPluginDoc",
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
coreVersion: '2.0.0',
browser: 'Firefox 1.5; InternetExplorer 6.0; Safari'
};
config.macros.rssReader = {
dateFormat: "DDD, DD MMM YYYY",
itemStyle: "display: block;border: 1px solid black;padding: 5px;margin: 5px;", //useed '@@'+itemStyle+itemText+'@@'
msg:{
permissionDenied: "Permission to read preferences was denied.",
noRSSFeed: "No RSS Feed at this address %0",
urlNotAccessible: " Access to %0 is not allowed"
},
cache: [], // url => request
desc: "noDesc",
// feedURL: "",
place:"",
handler: function(place,macroName,params,wikifier,paramString,tiddler){
var desc = params[0];
var feedURL = params[1];
// var toFilter = (params[2] ? params[2] : false);
var toFilter = false;
var filterString;
if (params[2] != undefined) {
toFilter = true;
if (params[2].match(/\w+/))
filterString = params[2];
else
filterString = tiddler.title;
}
var place = createTiddlyElement(place, "div", "RSSReader");
wikify("^^<<rssFeedUpdate "+feedURL+" [[" + tiddler.title + "]]>>^^\n",place);
if (this.cache[feedURL]) {
this.processResponse(this.cache[feedURL], feedURL, place, desc, toFilter, filterString);
}
else {
this.asyncGet(feedURL, place, desc, toFilter, filterString);
}
},
asyncGet: function (feedURL, place, desc, toFilter, filterString){
var xmlhttp;
try {xmlhttp=new XMLHttpRequest();}
catch (e) {
try {xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");}
catch (e) {
try {xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}
catch (e) { displayMessage(e.description?e.description:e.toString());}
}
}
if (!xmlhttp){
return;
}
if (window.netscape){
try {
if (document.location.protocol.indexOf("http") == -1) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
}
}
catch (e) { displayMessage(e.description?e.description:e.toString()); }
}
xmlhttp.onreadystatechange=function (){
if (xmlhttp.readyState==4) {
if (xmlhttp.status==200 || xmlhttp.status===0) {
config.macros.rssReader.processResponse(xmlhttp, feedURL, place, desc, toFilter, filterString);
}
else {
displayMessage("Problem retrieving XML data:" + xmlhttp.statusText);
}
}
};
try {
xmlhttp.open("GET",feedURL,true);
if (config.browser.isIE) {
xmlhttp.send();
}
else {
xmlhttp.send(null);
}
}
catch (e) {
wikify(e.toString()+this.urlNotAccessible.format([feedURL]), place);
}
},
processResponse: function(xmlhttp, feedURL, place, desc, toFilter, filterString){
if (window.netscape){
try {
if (document.location.protocol.indexOf("http") == -1) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
}
}
catch (e) { displayMessage(e.description?e.description:e.toString()); }
}
if (xmlhttp.responseXML){
this.cache[feedURL] = xmlhttp;
this.genRssNews(xmlhttp.responseXML, place, feedURL, desc, toFilter, filterString);
}
else {
var dom = (new DOMParser()).parseFromString(xmlhttp.responseText, "text/xml");
if (dom) {
this.cache[feedURL] = xmlhttp;
this.genRssNews(dom, place, feedURL, desc, toFilter, filterString);
}
else {
wikify("<html>"+xmlhttp.responseText+"</html>", place);
displayMessage(this.msg.noRSSFeed.format([feedURL]));
}
}
},
genRssNews: function(xml, place, feedURL, desc, toFilter, filterString){
// Channel
var chanelNode = xml.getElementsByTagName('channel').item(0);
var chanelTitleElement = (chanelNode ? chanelNode.getElementsByTagName('title').item(0) : null);
var chanelTitle = "";
if ((chanelTitleElement) && (chanelTitleElement.firstChild)) chanelTitle = chanelTitleElement.firstChild.nodeValue;
var chanelLinkElement = (chanelNode ? chanelNode.getElementsByTagName('link').item(0) : null);
var chanelLink = "";
if (chanelLinkElement) chanelLink = chanelLinkElement.firstChild.nodeValue;
var titleTxt = "!![["+chanelTitle+"|"+chanelLink+"]]\n";
var title = createTiddlyElement(place,"div",null,"ChanelTitle",null);
wikify(titleTxt,title);
// ItemList
var itemList = xml.getElementsByTagName('item');
var article = createTiddlyElement(place,"ul",null,null,null);
var lastDate;
var re;
if (toFilter)
re = new RegExp(filterString.escapeRegExp());
for (var i=0; i<itemList.length; i++){
var titleElm = itemList[i].getElementsByTagName('title').item(0);
var titleText = (titleElm ? titleElm.firstChild.nodeValue : '');
if (toFilter && ! titleText.match(re)) {
continue;
}
var descText = '';
var isWikitext = false;
var descElem = itemList[i].getElementsByTagName('wikitext').item(0);
if (descElem){
try{
isWikitext = true;
descText = "\n"+descElem.firstChild.nodeValue;}
catch(e){}
}
else {
descElem = itemList[i].getElementsByTagName('encoded').item(0);
if (descElem){
try{descText = descElem.firstChild.nodeValue;}
catch(e){}
descText = "<html>"+descText+"</html>";
}
else {
descElem = itemList[i].getElementsByTagName('description').item(0);
if (descElem){
try{descText = descElem.firstChild.nodeValue;}
catch(e){}
descText = descText.replace(/<br \/>/g,'\n');
if (desc == "asHtml")
descText = "<html>"+descText+"</html>";
}
}
}
var linkElm = itemList[i].getElementsByTagName("link").item(0);
var linkURL = linkElm.firstChild.nodeValue;
var pubElm = itemList[i].getElementsByTagName('pubDate').item(0);
var pubDate;
if (!pubElm) {
pubElm = itemList[i].getElementsByTagName('date').item(0); // for del.icio.us
if (pubElm) {
pubDate = pubElm.firstChild.nodeValue;
pubDate = this.formatDateString(this.dateFormat, pubDate);
}
else {
pubDate = '0';
}
}
else {
pubDate = (pubElm ? pubElm.firstChild.nodeValue : 0);
pubDate = this.formatString(this.dateFormat, pubDate);
}
titleText = titleText.replace(/\[|\]/g,'');
var rssText = '** '+'[[' + titleText + '|' + linkURL + ']]' + '\n' ;
if ((desc != "noDesc") && descText){
if (version.extensions.nestedSliders){
rssText = rssText.replace(/\n/g,' ');
descText = '+++[...]\n'
+(isWikitext ? '\n<<rssFeedImportTiddler '+ feedURL + ' [['+titleText+']]>>':'')
+'@@'+this.itemStyle+descText+'\n@@\n'
+'===';
}
rssText = rssText + descText + '\n\n';
}
var story;
if ((lastDate != pubDate) && ( pubDate != '0')) {
story = createTiddlyElement(article,"li",null,"RSSItem",pubDate);
lastDate = pubDate;
}
else {
lastDate = pubDate;
}
story = createTiddlyElement(article,"div",null,"RSSItem",null);
wikify(rssText,story);
}
},
formatString: function(template, theDate){
var dateString = new Date(theDate);
template = template.replace(/hh|mm|ss/g,'');
return dateString.formatString(template);
},
formatDateString: function(template, theDate){
var dateString = new Date(theDate.substr(0,4), theDate.substr(5,2) - 1, theDate.substr(8,2)
/*, theDate.substr(11,2), theDate.substr(14,2), theDate.substr(17,2)*/
);
return dateString.formatString(template);
}
};
//}}}
//{{{
config.macros.rssFeedUpdate = {
label: "Update",
prompt: "Clear the cache and redisplay this RssFeed",
handler: function(place,macroName,params) {
var feedURL = params[0];
var tiddlerTitle = params[1];
createTiddlyButton(place, this.label, this.prompt,
function () {
if (config.macros.rssReader.cache[feedURL]) {
config.macros.rssReader.cache[feedURL] = null;
//story.refreshTiddler(tiddlerTitle,null, true);
}
story.refreshTiddler(tiddlerTitle,null, true);
return false;});
}
};
//}}}
//{{{
config.macros.rssFeedImportTiddler = {
label: "Import",
prompt: "Import this tiddler in this TiddlyWiki",
askReplaceMsg: "Tiddler already exists, replace it ?",
handler: function(place,macroName,params) {
var feedUrl = params[0];
var tiddlerTitle = params[1];
createTiddlyButton(place, this.label, this.prompt,
function () {
if (feedUrl && config.macros.rssReader.cache[feedUrl]) {
var tiddler = config.macros.rssFeedImportTiddler.parseRssNews(config.macros.rssReader.cache[feedUrl].responseXML, tiddlerTitle);
if (tiddler && (! store.getTiddler(tiddlerTitle) || confirm(config.macros.rssFeedImportTiddler.askReplaceMsg))) {
store.addTiddler(tiddler);
store.notify(tiddler.title, true);
store.setDirty(true);
}
}
return false;});
},
// parse a RssFeed for retrieving a Tiddler with title
parseRssNews: function(xml, title) {
// ItemList
if (document.location.protocol.indexOf("http") == -1) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
}
var itemList = xml.getElementsByTagName('item');
for (var i=0; i<itemList.length; i++){
var titleElm = itemList[i].getElementsByTagName('title').item(0);
var titleText = titleElm.firstChild.nodeValue;
if (titleText == title) {
// <tiddlywiki:title>
// titleText
titleText = titleText.htmlDecode();
// <tiddlywiki:wikitext>
var elem = itemList[i].getElementsByTagName('wikitext').item(0);
var text = elem ? elem.firstChild.nodeValue.htmlDecode() : "";
// <tiddlywiki:modifier>
elem = itemList[i].getElementsByTagName('modifier').item(0);
var modifier = elem ? elem.firstChild.nodeValue : "";
// <tiddlywiki:modified>
elem = itemList[i].getElementsByTagName('modified').item(0);
var modified = elem ? Date.convertFromYYYYMMDDHHMM(elem.firstChild.nodeValue) : "";
// <tiddlywiki:created>
elem = itemList[i].getElementsByTagName('created').item(0);
var created = elem ? Date.convertFromYYYYMMDDHHMM(elem.firstChild.nodeValue) : "";
// <tiddlywiki:links>
// Links ?
// <tiddlywiki:tags>
elem = itemList[i].getElementsByTagName('tags').item(0);
var tags = elem ? elem.firstChild.nodeValue.htmlDecode() : "";
var tiddler = new Tiddler();
tiddler.assign(titleText,text,modifier,modified,tags,created);
return tiddler;
}
}
// not found
alert("Tiddler \[[" + title +"]] notFound.");
return null;
}
};
//}}}
<<search>><<closeAll>><<permaview>><<newTiddler>><<newTiddler label:"new bug" prompt:"Create a new bug entry" title:"Bug Title" tag:toRSS tag:Issue>><<newTiddler label:"new chat entry" prompt:"Create a new chat entry" title:"Message title" tag:toRSS tag:Chat>><<saveChanges>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
/***
| Name|TagglyTaggingPlugin|
| Description|tagglyTagging macro is a replacement for the builtin tagging macro in your ViewTemplate|
| Version|3.1 ($Rev: 2341 $)|
| Date|$Date: 2007-07-05 10:02:27 +1000 (Thu, 05 Jul 2007) $|
| 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
***/
//{{{
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':",
excerpts: "excerpts",
contents: "contents",
sliders: "sliders",
noexcerpts: "title only"
},
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",
contents: "Click to show entire tiddler contents",
sliders: "Click to show tiddler contents in sliders",
noexcerpts: "Click to show entire title only"
}
},
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","contents","sliders"]
},
valuePrefix: "taggly.",
excludeTags: ["excludeLists","excludeTagging"],
excerptSize: 50,
excerptMarker: "/%"+"%/"
},
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) {
if (!store.tiddlerExists(title))
// create it silently
store.saveTiddler(title,title,config.views.editor.defaultText.format([title]),config.options.txtUserName,new Date(),null);
// 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);
// a little usability enhancement. actually it doesn't work right for grouped or sitemap
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) {
switch(this.getTagglyOpt(title,"listMode")) {
case "group": return this.createTagglyListGrouped(place,title); break;
case "normal": return this.createTagglyListNormal(place,title,false); break;
case "commas": return this.createTagglyListNormal(place,title,true); break;
case "sitemap":return this.createTagglyListSiteMap(place,title); break;
}
},
getTaggingCount: function(title) {
// thanks to Doug Edmunds
if (this.config.showTaggingCounts) {
var tagCount = store.getTaggedTiddlers(title).length;
if (tagCount > 0)
return " ("+tagCount+")";
}
return "";
},
getExcerpt: function(inTiddlerTitle,title,indent) {
if (!indent)
indent = 1;
if (this.getTagglyOpt(inTiddlerTitle,"excerpts") == "excerpts") {
var t = store.getTiddler(title);
if (t) {
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 (this.getTagglyOpt(inTiddlerTitle,"excerpts") == "contents") {
var t = store.getTiddler(title);
if (t) {
return "\n{{contents indent"+indent+"{\n" + t.text + "\n}}}";
}
}
else if (this.getTagglyOpt(inTiddlerTitle,"excerpts") == "sliders") {
var t = store.getTiddler(title);
if (t) {
return "<slider slide>\n{{contents{\n" + t.text + "\n}}}\n</slider>";
}
}
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) {
var list = store.getTaggedTiddlers(title,this.getTagglyOpt(title,"sortBy"));
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) {
var sortBy = this.getTagglyOpt(title,"sortBy");
var sortOrder = this.getTagglyOpt(title,"sortOrder");
var list = store.getTaggedTiddlers(title,sortBy);
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) {
var list = store.getTaggedTiddlers(title,sortBy);
if (sortOrder == "desc")
list.reverse();
var indent = "";
for (var j=0;j<depth;j++)
indent += "*"
var childOutput = "";
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);
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) {
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"));
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 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);
refreshContainer.setAttribute("title",tiddler.title);
this.refresh(refreshContainer);
},
refresh: function(place) {
var title = place.getAttribute("title");
removeChildren(place);
if (store.getTaggedTiddlers(title).length > 0) {
var lingo = config.taggly.lingo;
config.taggly.createListControl(place,title,"hideState");
if (config.taggly.getTagglyOpt(title,"hideState") == "show") {
createTiddlyElement(place,"span",null,"tagglyLabel",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);
}
}
}
}
},
// 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]]; }",
"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; }",
"/*}}}*/",
""].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
***/
//{{{
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[1] + " "+"\u00BB",lookaheadMatch[1],this.onClickSlider,"button sliderButton");
var panel = createTiddlyElement(w.output,"div",null,"sliderPanel");
panel.style.display = "none";
wikify(lookaheadMatch[2],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;
}
});
//}}}
<!--{{{-->
<div class='toolbar' macro='toolbar closeTiddler closeOthers +editTiddler > fields syncing permalink references jump'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class="tagglyTagging" macro="tagglyTagging"></div>
<div class='tagged' macro='tags'></div>
<div macro="hideWhen tiddler.tags.containsAny(['css','html','pre','systemConfig']) && !tiddler.text.match('{{'+'{')">
<div class='viewer' macro='view text wikified'></div>
</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 class='tagClear'></div>
<!--}}}-->
/***
|''Name:''|WebDavPlugin|
|''Description:''|Allows a TiddlyWiki to be saved to a WebDav server|
|''Author:''|Saq Imtiaz ( lewcid@gmail.com )|
|''Co-author:''|Loic Dachary|
|''Source:''|http://tw.lewcid.org/#WebDavPlugin|
|''Code Repository:''|http://tw.lewcid.org/svn/plugins|
|''Version:''|1.0|
|''Date:''|17/11/2007|
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''~CoreVersion:''|2.2.3|
***/
// /%
//!BEGIN-PLUGIN-CODE
DAV = {
run : function(type,u,data,cb,params,headers){
var callback = function(status,params,responseText,url,xhr) {
url = url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1);
if(params.length){
params.shift().apply(this,[status,params,responseText,url,xhr]);
}
};
params = params||[];
params.unshift(cb);
var r = doHttp.apply(this,[type,u,data,null,null,null,callback,params,headers]);
if (typeof r == "string")
alert(r);
return r;
},
get : function(url,cb,params){
return DAV.run("GET",url,null,cb,params,null);
},
put : function(url,cb,params,data) {
return DAV.run("PUT",url,data,cb,params,null);
},
move : function(url,cb,params,destination) {
return DAV.run("MOVE",url,null,cb,params,{"Destination":destination,"Overwrite":"T"});
},
copy : function(url,cb,params,destination) {
return DAV.run("COPY",url,null,cb,params,{"Destination":destination,"Overwrite":"T","Depth":0});
},
propfind : function(url,cb,params,prop,depth){ // !!!
var xml = '<?xml version="1.0" encoding="UTF-8" ?>' +
'<D:propfind xmlns:D="DAV:">' +
'<D:prop>'+
'<D:'+prop+'/>'+
'</D:prop>'+
'</D:propfind>';
return DAV.run("PROPFIND",url,xml,cb,params,{"Content-type":"text/xml","Depth":depth?depth:0});
},
makeDir : function(url,cb,params){
return DAV.run("MKCOL",url,null,cb,params,null);
},
options : function(url,cb,params){
return DAV.run("OPTIONS",url,null,cb,params,null);
},
safeput : function(url,cb,params,data){
firstcb = function(status,p,responseText,u,xhr){
if(status)
DAV.move(u,cb,p,u.replace("-davsavingtemp",""));
else
cb.apply(firstcb,arguments);
};
return DAV.put(url+"-davsavingtemp",firstcb,params,data);
}
};
config.DavSaver = {
defaultFileName : 'index.html',
messages : {
startSaveMessage : 'saving to server...',
endSaveMessage : 'TiddlyWiki successfuly saved to server',
overwriteNewerPrompt : 'The remote file has changed since you last loaded or saved it.\nDo you wish to overwrite it?',
getModDateError : "Could not get last modified date",
raceError: "Save failed because the remote file is newer than the one you are trying to save"
},
errors:{
raceconflict : 'The save failed because your file is out of date',
getlastmodified : 'The save was aborted because the last modified date of the file could not be retrieved',
davenabled : 'The server does not support WebDav',
saverss : 'There was an error saving the rss file, the save was aborted',
saveempty: 'There was an error saving the empty file, the save was aborted',
savemain : 'Save failed! There was an error saving the main TW file',
savebackup: 'Save failed! There was an error creating a backup file',
makebackupdir: 'Save failed! The backup directory could not be created'
},
timestamp: new Date().toGMTString(),
orig_saveChanges: saveChanges,
saver : null
};
DavSaver = function(){
this.Q = [];
this.reset = function(){
config.DavSaver.saver = null;
};
this.runQ = function(){
if(this.Q.length){
this.Q.shift().apply(this,arguments);
}
else
this.reset();
};
this.posDiv = null;
this.original = null;
this.getOriginalPath = function(){
var p = document.location.toString();
p = convertUriToUTF8(p,config.options.txtFileSystemCharSet);
var argPos = p.indexOf("?");
if(argPos != -1)
p = p.substr(0,argPos);
var hashPos = p.indexOf("#");
if(hashPos != -1)
p = p.substr(0,hashPos);
if (p.charAt(p.length-1) == "/")
p = p + config.DavSaver.defaultFileName;
return p;
};
this.originalPath = this.getOriginalPath();
this.backupPath = null;
this.emptyPath = null;
this.rssPath = null;
this.throwError = function(er){
clearMessage();
this.reset();
alert(config.DavSaver.errors[er]); //clear message, reset and delete
};
};
DavSaver.prototype.getOriginal = function(){
var callback = function(status,params,original,url,xhr) {
var that = params[0];
if(status){
var p = that.posDiv = locateStoreArea(original);
if(!p || (p[0] == -1) || (p[1] == -1)) {
alert(config.messages.invalidFileError.format([url]));
return;
}
that.original = original;
that.runQ();
}
else
that.throwError('getOriginal');
};
DAV.get(this.originalPath,callback,[this]);
};
DavSaver.prototype.checkRace = function(){
var callback = function(status,params,responseText,url,xhr){
var that = params[0];
if(status){
var lastmod = responseText.match(/<(\w*?):getlastmodified>(.*?)<\/\1:getlastmodified>/)[2];
if(Date.parse(lastmod) > Date.parse(config.DavSaver.timestamp)){
if (confirm(config.DavSaver.messages.overwriteNewerPrompt))
that.runQ();
else
that.throwError('raceconflict');
}
else
that.runQ();
}
else
that.throwError('getlastmodified');
};
DAV.propfind(this.originalPath,callback,[this],"getlastmodified",0);
};
DavSaver.prototype.isDavEnabled = function(){
var callback = function(status,params,responseText,url,xhr){
that = params[0];
if(status && !! xhr.getResponseHeader("DAV")){
that.runQ();
}
else
that.throwError('davenabled');
};
DAV.options(this.originalPath,callback,[this]);
};
DavSaver.prototype.saveRss = function(){
var callback = function(status,params,responseText,url,xhr){
var that = params[0];
if(status){
displayMessage(config.messages.rssSaved,that.rssPath);
that.runQ();
}
else
that.throwError('saverss');
};
var u = this.originalPath;
var rssPath = this.rssPath = u.substr(0,u.lastIndexOf(".")) + ".xml";
DAV.safeput(rssPath,callback,[this],convertUnicodeToUTF8(generateRss()));
};
DavSaver.prototype.saveEmpty = function(){
var callback = function(status,params,responseText,url,xhr){
var that = params[0];
if(status){
displayMessage(config.messages.emptySaved,that.emptyPath);
that.runQ();
}
else
that.throwError('saveempty');
};
var u = this.originalPath;
var emptyPath,p;
if((p = u.lastIndexOf("/")) != -1)
emptyPath = u.substr(0,p) + "/empty.html";
else
emptyPath = u + ".empty.html";
this.emptyPath = emptyPath;
var empty = this.original.substr(0,this.posDiv[0] + startSaveArea.length) + this.original.substr(this.posDiv[1]);
DAV.safeput(emptyPath,callback,[this],empty);
};
DavSaver.prototype.saveMain = function(){
var callback = function(status,params,responseText,url,xhr){
var that = params[0];
if(status){
config.DavSaver.timestamp = xhr.getResponseHeader('Date');
displayMessage(config.messages.mainSaved,that.originalPath);
store.setDirty(false);
that.runQ();
}
else
that.throwError('savemain');
};
var revised = updateOriginal(this.original,this.posDiv);
DAV.safeput(this.originalPath,callback,[this],revised);
};
DavSaver.prototype.saveBackup = function(){
var callback = function(status,params,responseText,url,xhr){
var that = params[0];
if(status){
clearMessage();
displayMessage(config.messages.backupSaved,that.backupPath);
that.runQ();
}
else
that.throwError('savebackup');
};
var backupPath = this.backupPath = getBackupPath(this.originalPath);
DAV.copy(this.originalPath,callback,[this],this.backupPath);
};
DavSaver.prototype.makeBackupDir = function(){
var callback = function(status,params,responseText,url,xhr){
var that = params[0];
if(status)
that.runQ();
else
that.throwError('makebackupdir');
};
var u = getBackupPath(this.originalPath);
var backupDirPath = u.substr(0,u.lastIndexOf("/"));
DAV.makeDir(backupDirPath,callback,[this]);
};
DavSaver.prototype.save = function(onlyIfDirty,tiddlers){
if(onlyIfDirty && !store.isDirty())
return;
clearMessage();
displayMessage(config.DavSaver.messages.startSaveMessage);
var Q = this.Q =[];
Q.push(this.isDavEnabled);
Q.push(this.getOriginal);
Q.push(this.checkRace);
if (config.options.chkSaveBackups){
if (config.options.txtBackupFolder!='')
Q.push(this.makeBackupDir);
Q.push(this.saveBackup);
}
Q.push(this.saveMain);
if (config.options.chkGenerateAnRssFeed)
Q.push(this.saveRss);
if (config.options.chkSaveEmptyTemplate)
Q.push(this.saveEmpty);
//Q.push(this.reset);
this.runQ();
};
window.saveChanges = function(onlyIfDirty,tiddlers)
{
var c = config.DavSaver;
if (document.location.protocol.toString() == "http:"){
var saver = c.saver = new DavSaver();
saver.save(onlyIfDirty,tiddlers);
}
else
return c.orig_saveChanges(onlyIfDirty,tiddlers);
};
//!END-PLUGIN-CODE
// %/
To add a message to the chat, click new journal and add the ''Chat'' tag to the new entry.
Loic Dachary mailto:loic@dachary.org