\n@@color:#0000FF;background-color:#BBFFFF;__Underlined and cyan background__@@ dates have associated reminders, while @@font-weight:bold;background-color:#BBEEEE;color:#0000FF;bold and slightly duller cyan background@@ dates indicate a date on which the wiki was edited.\n(click on the date to pop up a list of tiddlers edited on that date).\n\nThe @@font-weight:bold;background-color:orange;color:#0000FF;orange@@ date is today, and the @@background-color:#BBBBBB;color:#0000FF;grey@@ dates are weekends.\n\nThe @@background-color:#ffaaaa;color:#0000FF;pink@@ days are holidays or important days - if you're a TiddlyWiki user, you can find them specified in the Date Plugin.
config.options.chkHttpReadOnly = false;\n
''Cinematography'' - planning your animation\n[[The Five C's of Cinematography / Joseph V. Mascelli|http://www.amazon.com/Five-Cs-Cinematography-Filming-Techniques/dp/187950541X]] - classical work on the why/when/how of camera angles, cutting/transitions, close-ups, continuity, and composition.\n\n''Character design''\n[[Cartoonist's Workbook / Robin Hall|http://www.amazon.co.uk/Cartoonists-Workbook-Robin-Hall/dp/0713682949]] - good for simple cartoon characters\n[[Creating Characters with Personality / Tom Bancroft and Glen Keane|http://www.amazon.com/Creating-Characters-Personality-Animation-Graphic/dp/0823023494]] - good book for thinking about character design\n[[Big Book of Cartooning / Bruce Blitz|http://www.amazon.com/Big-Book-Cartooning-Bruce-Blitz/dp/0762409398]] - good overall coverage (interesting section on caricature)\n[[All About Techniques in Drawing for Animation Production / Sergi Camara|http://www.amazon.com/About-Techniques-Drawing-Animation-Production/dp/0764159194]] - brief but surprisingly good coverage of storyboarding, expressions, clothing, postures, character design\n[[How to Draw and Paint Crazy Cartoon Characters / Vincent Woodcock|http://www.amazon.com/Draw-Paint-Crazy-Cartoon-Characters/dp/0764135732]] - good, techniques for character design\n[[Cartoon Cool: How to Draw New Retro-Style Characters / Christopher Hart|http://www.amazon.com/Cartoon-Cool-Draw-Retro-Style-Characters/dp/0823005879]] - good, but a tendency to drift towards more manga-like styles rather than retro in some parts of the book.\n[[Action! Cartooning / Ben Caldwell|http://www.amazon.com/Action-Cartooning-Ben-Caldwell/dp/0806987391]] - good for thinking about action poses/keyframes\n[[Cartooning for the Beginner / Christopher Hart|http://www.amazon.com/Cartooning-Beginner-Christopher-Hart-Titles/dp/0823005860]] - good basics for Disney-ish style\n[[How to Draw Cartoon Animals / Christopher Hart|http://www.amazon.com/Draw-Cartoon-Animals-Christopher-Titles/dp/0823023605]] - if you liked the previous book, here's some more how-to for specific animals\n[[How to Draw Animation / Christopher Hart|http://www.amazon.com/How-Draw-Animation-Storyboards-Christopher/dp/0823023656]] - only brief coverage of some topics, may be disappointing to people expecting more depth\n[[Cartooning: Character Design / edited by Editors Of Walter Foster, illustrated by Sherm Cohen|http://www.amazon.com/Cartooning-Character-Design-HT291-Paint/dp/156010967X/]] - over-sized book (height and width) with not a lot of pages, but gives a good summary coverage and useful tips for character design\n[[Making Faces:drawing expressions for comics and cartoons|http://www.amazon.com/Making-Faces-Drawing-Expressions-Cartoons/dp/1600610498/]] - some good examples in a variety of styles, not a lot of instructional material\n''Animation''\n[[Acting for Animators / Ed Hooks|http://www.amazon.com/Acting-Animators-Revised-Performance-Animation/dp/032500580X]] - useful for character animators\n[[Cartoon Animation / Preston Blair|http://www.amazon.com/Cartoon-Animation-Collectors-Editors-Walter/dp/1560100842]] - classical animation how-to guide\n[[The Illusion of Life / Ollie Johnston and Frank Thomas|http://www.amazon.com/Illusion-Life-Disney-Animation/dp/0786860707]] - Disney history and animation principles\n[[The Animator's Workbook / Tony White |http://www.amazon.com/Animators-Workbook-Tony-White/dp/0823002292]] - coverage of classical animation principles\n[[Character Animation in 3D / Steve Roberts|http://www.amazon.com/Character-Animation-traditional-techniques-animation/dp/0240516656]] - good basic movements coverage, also very good for 2D, lots of example 2D animation sequences\n[[Character Animation: 2D Skills for Better 3D / Steve Roberts|http://www.amazon.com/Character-Animation-Skills-Better-Effects/dp/0240520548/ref=sr_1_2?ie=UTF8&s=books&qid=1248050830&sr=1-2]] - 2nd edition of the above\n[[The Animator's Survival Kit / Richard Williams|http://www.amazon.com/Animators-Survival-Kit-Principles-Classical/dp/0571202284]] - tips and techniques for professional-looking animation\n[[Timing for Animation / Harold Whitaker and John Halas|http://www.amazon.com/Timing-Animation-Harold-Whitaker/dp/0240517148]]\n[[Digital Character Animation 3 / George Maestri|http://www.amazon.com/Digital-Character-Animation-3-digital/dp]] - good, the 3 in the title refers to 3rd edition, takes the classic animation principles and shows how they can be applied to 3D\n[[Character Animation Crash Course / Eric Goldberg|http://www.amazon.com/Character-Animation-Crash-Course-Goldberg/dp/1879505975]] - excellent work on traditional animation\n[[Get Animated!: Creating Professional Cartoon Animation on Your Home Computer / Tim Maloney|http://www.amazon.com/Get-Animated-Creating-Professional-Animation/dp/0823099210]] - good work on traditional home animation using a computer. Unfortunately, although it mentions cut-out animation in the introduction as being very productive for the home animator, it does not then cover cut-out animation in any significant depth in the rest of the book.\n[[Stop staring: Facial modeling and animation done right / Jason Osipa|http://www.amazon.com/Stop-Staring-Facial-Modeling-Animation/dp/0471789208]] - good book, mainly for 3D animators, on facial animation and rigging. Not as useful for 2D animators, although there are a few good tips in there.\n[[Guerrilla Guide to Animation: Making Animated Films Outside the Mainstream|http://www.amazon.com/Guerrilla-Guide-Animation-Animated-Mainstream/dp/0826429858/]] - some great limited animation tips\n
!General animation links\n[[AWN Toon Institute|http://www.awn.com/tooninstitute/lessonplan/lesson.htm#top]] - animation lessons\n[[Animating with "Bopsey" models|http://members.shaw.ca/ablainey/~public/schedule/htmlfiles/bopsey.html]] - cut-out characters for Flash/Anime Studio/etc. (also see [[this link|http://www.19.5degs.com/element/492.php]])\n[[Animation Meat|http://www.animationmeat.com/]] - useful animation notes and forum links\n[[Amazing Kids! Animation Lesoons|http://www.amazing-kids.org/start.html]] - links to animation lessons\n[[Cartoon Central Australia|http://members.optusnet.com.au/veemon/]] - cartoon news for Australia\n[[Preston Blair mouth shapes|http://minyos.its.rmit.edu.au/~rpyjp/a_notes/mouth_shapes_01.html]] - classic lipsynching\n\n!Cartoon Drawing\n[[Karmatoons|http://www.karmatoons.com/drawing/drawing.htm]] - drawing for classical animation\n[[Cartoonsmart|http://www.cartoonsmart.com/]] - drawing for Flash animation\n[[Cartoon Critters|http://www.cartooncritters.com/learntodraw.htm]] - step-by-step\n[[Cartoon drawing links|http://www.artistshelpingchildren.org/howtodraw.html]]\n[[Cartoon technique links|http://www.cdli.ca/CITE/cartooning.htm]]\n[[Gary Harbo|http://www.garyharbo.com/activity.html]] - monthly cartoon step-by-step\n[[Draw and color with Uncle Fred|http://www.unclefred.com/]] - ste-by-step for kids\n[[Cartoon connections|http://www.cartoonconnections.com/menupage2.htm]] - step-by-step cat and dog\n[[Cartoon Solutions|http://www.cartoonsolutions.com/]] - commercial pre-rigged characters for Flash and Toon Boom, soon for Anime Studio\n\n!''David Freedman's blog'' - personal news from an animation director\n[[King Arthur's Disasters|http://davidmaxfreedman.typepad.com/slingshot/king_arthurs_disasters/index.html]]\n[[Aaagh! It's The Mr Hell Show!|http://davidmaxfreedman.typepad.com/slingshot/aaagh_its_the_mr_hell_show/index.html]]
Classical animators (in particular the [[Nine Old Men|http://en.wikipedia.org/wiki/Nine_Old_Men]] of Disney) devised the [[12 principles of animation|http://www.frankandollie.com/PhysicalAnimation.html]], guidelines to help animators produce more character and believability in their animated characters.\n\nThese 12 principles (along with variations and extras - [[Walt Stanchfield's notes|http://www.animationmeat.com/notes/waltstanchfield/waltstanchfield.html]] gives [[28 principles of animation|http://www.animationarena.com/principles-of-animation.html]]) are extremely useful in 2D and 3D animation, and many sites and lessons give various interpretations and explanations, worthwhile but not always consistent.\n\nFor example, a paper by John Lasseter showed [[how these (except for "solid drawing") could be applied to 3D animation|http://www.siggraph.org/education/materials/HyperGraph/animation/character_animation/principles/prin_trad_anim.htm]] (see also [[this link|http://www.siggraph.org/education/materials/HyperGraph/animation/character_animation/principles/lasseter_s94.htm]] for more Lassetter notes).\n
Most televisions use rectangular pixels. Computers usually use square pixels.\n\nTo export computer animation to TV/DVD, you usually need to work on the computer using square pixels, then resize it before writing it to DVD (or broadcasting it).\n\n//Old incorrect standard, 4:3 (PAL):\n\nFor standard PAL work (in Australia), use a resolution of 768 x 576 on the computer (square pixels). \nThen resize to 720 x 576 before writing to DVD.//\n\n''New fixed standard, 4:3 (PAL):\n\nFrom the [[BBC|http://www.bbc.co.uk/commissioning/tvbranding/picturesize.shtml]], the recommendation is to work at 788 by 576 on the computer, and resize to 720 by 576 for digital processing by the TV station, of which 702 by 576 will show up on the TV screen.''\n\nThis initial size will look fine on the computer - squares will look like squares, circles like circles.\n\nAfter you have resized it, the animation will then look squashed on the computer screen (squares will look like rectangles, circles like ellipses), but when shown on TV using rectangular pixels it will look fine. Right and left dges will be cut off when displayed on the TV.\n\nSome video editors let you specify "rectangular pixels" or varying aspect ratio, and will expand the video in software, making your squashed animation still look okay on the computer screen.\n\nDon't forget to use "title safe" and "action safe" zones. Standard televisions often trim the edges off the picture (overscan) to make sure your screen is full with no blank areas and no distortion due to the shape of the picture tube. Areas inside the "title safe" and "action safe" zones stand a much better chance of being readable or viewable.\n\n//Old incorrect standard (16:9 widescreen PAL):\nFor widescreen PAL, use 1024 x 576 on the computer, then resize to 720 x 576 for showing on TV. //\n\n''New fixed standard (16:9 widescreen PAL):\nUse 1050 by 576 on the computer, then and resize to 720 by 576 for digital processing by the TV station, of which 702 by 576 will show up on the TV screen, stretched to widescreen.''\n\nYes, that's right, PAL widescreen TV cheats - it doesn't have any more information, it just uses wider pixels to display a more squashed image stretched even further. The widescreen screen ratio of width:height is 16:9 (using 1.42 pixel aspect ratio) rather the old 4:3 (1.066) setup.\n\n\nIn Australia, SD (standard definition) digital TV uses 576 lines x 720 active pixels, 50Hz interlaced (576i) - think of it as 50 frames per second, but only showing every second line of each frame (odd lines in one frame, even lines in the next). Shown in widescreen. Work on the computer at ==1024== 1050 x 576, resize to 720 x 576.\n\nHD (high definition) digital TV in Australia currently uses one of 3 formats (all widescreen):\n\n576p: 576 horizontal lines progressive x 720 active pixels, 50Hz progressive (50 full frames per second). Used by SBS, Seven, Prime. Work on the computer at ==1024== 1050 x 576, resize to 720 x 576.\n\n720p: 720 horizontal lines progressive x 1280 active pixels, 50Hz progressive. Used by the ABC. Possibly using square pixels, so you may be able to work directly at 1280 x 720.\n\n1080i: 1080 horizontal lines interlaced x 1440 active pixels (rectangular pixels) , 50Hz interlaced (only showing every second line on alternate frames, as mentioned above for SD). Used by by Nine, Ten, WIN, NBN & Southern Cross Ten, Tas Digital.\nNote that the HD specification is 1920 x 1080 (square pixels) - use this when rendering animation for HD on the computer.\nHowever, the HDV specification is 1440 x 1080 (rectangular pixels), so you will may have to resize down to this in your video editing software.\n\n[[Jeff Goldner|http://www.animationpost.co.uk/]] has [[recommended|http://www.lostmarble.com/forum/viewtopic.php?p=4609#4609]] 640 x 360 as a useful working resolution for 16:9 resolution, with higher resolutions saved for final output. [[J.Baker|http://www.flashpulse.com/]] also [[uses this resolution|http://www.lostmarble.com/forum/viewtopic.php?p=35121#35121]].\n\n[[Terrence Walker|http://www.studioartfx.com/]] has [[recommended|http://www.lostmarble.com/forum/viewtopic.php?p=34953#34953]] working in 1280x720 24p (1280p) which apparently resizes to 1920x1080 with no significant visible quality loss (partly because many modern ~HDTV-ready ~TVs do not actually display at the full HD resolution, although this is changing).\n\n[[Shawn the Touched|http://whiteradish.com/]] has [[posted|http://www.lostmarble.com/forum/viewtopic.php?p=35197#35197]] specifications for [[YouTube|http://www.youtube.com/]]. In order to avoid quality loss by conversion, post your animations using the following:\n*Video format: FLV\n*Video resolution: 320 x 240, 30 fps (note: gets upscaled to 480x360 on playback)\n*Video bitrate: 300 Kbps\n*Audio format: 22,050 Hz, 64 Kbps, mono\n\nTo avoid compression format problems, ~YouTube themselves [[recommend|http://help.youtube.com/support/youtube/bin/answer.py?hl=en&answer=55744]] the following as the safest upload:\n640x480 ~MPEG4 video with ~MP3 audio\n\n~YouTube [[recommend|http://help.youtube.com/support/youtube/bin/answer.py?hl=en&answer=132460]] the following for HD quality:\n*Resolution: 1920x1080 (1080p) or 1280 x 720\n*Format: H.264 or ~MPEG-2 codec in the form of FLV, ~MPEG-2, or ~MPEG-4 files\n* Audio: ~MP3 or AAC codec, 44.1kHz, stereo\n\n//Some sources for further information:// \nhttp://www.dba.org.au/index.asp?sectionID=15\nhttp://www.adobe.com/support/techdocs/322300.html\nhttp://www.hdv-info.org/\nhttp://en.wikipedia.org/wiki/HDV\nhttp://www.dba.org.au/index.asp?sectionID=7\nhttp://www.animationpost.co.uk/bitmaps/aspect-ratios.htm\nhttp://www.animationpost.co.uk/widescreen/ws-frame.htm\nhttp://en.wikipedia.org/wiki/Anamorphic_widescreen\nhttp://www.quantel.com/site/en.nsf/HTML/library_dfb?OpenDocument\n
!!Hand-drawn animation, paperless\n''Good software for paperless thumbnail planning, roughs, when planning for limited animation (my preferred style)''\nAlso good if you want to do your own hand-drwan animation\n\n[[Animation-ish|http://www.toonboom.com/products/animationish/]] - good for dabbling. Commercial.\n''Pro'': interface encourages experimentation/creativity, easy to use, vector-based lines, multiple levels of undo, imports/exports a variety of image formats. 3 modes: (1) 3 frames, (2) unlimited frames, (3) unlimited frames plus enables foreground (with path animation) and background layers.\n''Con'': limited tools - no shape or text tools, no option to add sound, no persistent object/line grouping (although you can multi-select), no layers, limited framerate control.\nLimited onion skins - up to 3 frames either way.\n\n[[Flip Boom Classic|http://www.toonboom.com/products/flipboom/]] - similar to the middle level of the Animation-ish (or vice versa, since I think this software came out first). Commercial.\n3.0 adds object libraries, both supplied and user-created. No sound support, no image support, no layers or persistent object grouping (you can multi-select). Only single frame onion-skin, simultaneous forward and back.\n''Update:'' New [[Flip Boom All-Star|http://www.toonboom.com/products/flipboomallstar/]] has answered most of the Classic limitations by adding ~MP3 import, image import, 16:9 resolution, background and foreground layers, and object grouping. \n//Now, all they need to do is add path animation, a group hierarchy, and pivot points to the groups, and I'll be satisfied.// <<smiley :D>>\n\n[[Pencil|http://www.pencil-animation.org/]] - freeware, open source. Bitmap and vector layers. Very basic onion skin, camera, and sound support.\n\n[[Plastic Animation Paper|http://www.plasticanimationpaper.dk/]] - great for traditional hand-drawn animation, but only does pencil and inking stages, not colouring. Base version is free.\n\n!!Limited animation\n\n[[CreaToon|http://www.creatoon.com/]] - formerly commercial, now unsupported freeware, closed source. Capable cut-out animation program (create your artwork in other software or by hand, import it). Supports object hierarchy with pivot points, object swapping (e.g. for mouth animation). Requires hardware ~OpenGL support on the video card to run effectively.\n\n[[Anime Studio|http://my.smithmicro.com/win/anime/index.html]] - my favorite software for limited animation. Commercial. Supports bones, switch layers (cel swapping), layer hierarchies, masking, various effects, sound. Import images or create vector shapes. Pro and Debut versions.\nWhat's extra in the Pro version? Scripting, 2.5D/3D features (shear, perspective, multi-plane effects, 3D objects), particles, a few extra tools (bend, noise, magnet, snap to grid), group and note layers (bone layers in Debut version can substitute for group layers), onion skin, graph mode timeline, actions, no resolution or frame number limits, multiple views, vector import, export/render single/sequential images.\n\n[[Toon Boom Studio|http://www.toonboom.com/products/toonBoomStudio/]] - excellent feature list. Commercial.
[[Anime Studio Debut|http://my.smithmicro.com/win/anime/]] is a great 2D cartoon animation program (US$49.99), formerly sold as Moho by Lost Marble.\n\nIt works like cut-out animation or shadow puppets, although it also has aspects of "flattened" 3D and clay animation (bones which can actually bend and flex, for example, a single 2D limb shape in a flat plane, similar to the way a 3D or clay figure's armature/skeleton can flex a limb shape in space).\nUnlike Flash, Anime Studio uses bones rather than pivot points, and shape tweening is the default.\nYou can use vector shapes drawn in Anime Studio, or import .ai files or character pieces in .png format.\nYou can switch between multiple alternative versions of, for example, a mouth or hand, either manually, using a script, or importing a data file created externally ([[Papagayo|hhttp://www.lostmarble.com/papagayo/index.shtml]], a free program, is provided for creating lipsynch data sychronised to a sound file).\n\nThe [[Pro version of Anime Studio|http://my.smithmicro.com/win/animepro/index.html]] (US$199.99) enables you to place 2D layers in 3D space and use a multiplane camera, as well as having scripting and 3D support and other professional features.\n\nAnime Studio is probably not the best program if you would prefer to do a flipbook/cel/frame-by-frame style of animation - there is plenty of [[other software|http://www.bakhter.com/html/2d/ink&paint_tools.html]] for that style of animation (see also [[ToonBoom's "kids" range|http://www.toonboom.com/kids/]]) . \nHowever, if you are willing to work with Anime Studio's style of animation, it is productive, fun, and capable of [[some great results|http://my.smithmicro.com/win/anime/indexa.html]] (for now, you can also still see [[the Moho gallery|http://www.lostmarble.com/moho/gallery/index.shtml]]). \n\nLots of Anime Studio help, tips, information, and finished and in-progress work at the [[Lost Marble Anime Studio|http://www.lostmarble.com/forum/]] forum, a very active and helpful forum, with lots of amateur and professional animators using Anime Studio.\nIf you are a beginner and would like a quieter forum to start in, have a look at the [[Cartoon Learning forum|http://www.cartoonlearning.com/forum/]].\n\nAnime Studio support for multiple scenes and audio manipulation has improved a lot with the introduction of the sequencer in version 6. If you still want to do this in separate specialised software, there are useful programs such as[[ video editing software|http://www.bakhter.com/html/video/video_editors.html]], [[audio editing software|http://www.bakhter.com/html/2d/sound_editors.html]], and/or (if you are exporting to Flash .swf format) SWF editing software such as [[Flash|http://www.adobe.com/products/flash/flashpro/]] or [[SWiSH Max|http://www.swishzone.com/index.php?area=products&product=max&tab=overview]] (which allows importing .swf files into scenes).\n\nIf you don't want to create your own characters, props, scenery, etc, there are sources such as [[Content Paradise|http://www.contentparadise.com/searchimageviewdetail.aspx?searchText=&sw=anime]], [[Cartoon Solutions|http://www.cartoonsolutions.com/store/catalog/Anime-Studio-Character-Packs-p-1-c-290.html]] and [[The Cartoon Boat|http://www.thecartoonboat.com/]].
!Newer tips\n\nI've moved my newer tips (a newly-started series on cut-out style animation) to a blog-based system:\nYou can find the HTML version of the tutorials at http://virtualcutout.wordpress.com/ \nand the PDF version at http://virtualcutout.posterous.com/\n(I'm still fiddling with this, so there may be minor changes).\n\n!Older tips\n\n*[[Using layered fills to fake changing shape order|http://users.monash.edu.au/~myless/catnap/misc/arm-fills-5.htm]] - written for Anime Studio 5.5\n*[[Quick look at selecting curves for filling|http://users.monash.edu.au/~myless/catnap/misc/fill-lower.html]] - written for Anime Studio 5.5\n*[[Creating and using point groups|http://users.monash.edu.au/~myless/catnap/misc/groups.html]] - written for Anime Studio 5.5\n*[[Peaking points and joining lines|http://users.monash.edu.au/~myless/catnap/misc/peak.htm]] - written for Anime Studio 5.5\n\n*[[Selecting fill colours|http://users.monash.edu.au/~myless/catnap/misc/fill_colours.htm]] - written for Moho 5\n*[[A quick look at masking|http://users.monash.edu.au/~myless/catnap/misc/masking.html]] - written for Moho 5\n*[[Bending limbs and overlapping fill gaps|http://users.monash.edu.au/~myless/catnap/misc/overlap.html]] - written for Moho 5\n*[[Selecting points to fill an "add-on" shape|http://users.monash.edu.au/~myless/catnap/misc/overlap.html]] - written for Moho 5\n*[[Using styles|http://users.monash.edu.au/~myless/catnap/misc/styles.html]] - written for Moho 5\n*[[Bone constraints|http://users.monash.edu.au/~myless/catnap/mohotips/moho4/bones.html]] - written for Moho 4\n\n*[[Older Moho tips|http://users.monash.edu.au/~myless/catnap/mohotips/index.html]] - not all of them applicable to Anime Studio
/***\n''Name:'' Calendar plugin\n''Version:'' 0.5\n''Author:'' SteveRumsby\n\n''Syntax:'' \n{{{<<calendar>>}}} or {{{<<calendar year>>}}} or {{{<<calendar year month>>}}} or {{{<<calendar thismonth>>}}}\n\n''Description:'' \nThe first form produces an full-year calendar for the current year. The second produces a full-year calendar for the given year. The third produces a single month calendar for the given month and year. The fourth form produces a single month calendar for the current month.\nWeekends and holidays are highlighted (see below for how to specify holdays).\n\n''Configuration:''\nModify this section to change the text displayed for the month and day names, to a different language for example. You can also change the format of the tiddler names linked to from each date, and the colours used.\n\n''Changes by ELS 2005.10.30:''\nconfig.macros.calendar.handler()\n^^use "tbody" element for IE compatibility^^\n^^IE returns 2005 for current year, FF returns 105... fix year adjustment accordingly^^\ncreateCalendarDays()\n^^use showDate() function (if defined) to render autostyled date with linked popup^^\ncalendar stylesheet definition\n^^use .calendar class-specific selectors, add text centering and margin settings^^\n***/\n//{{{\nconfig.macros.calendar = {};\n\nconfig.macros.calendar.monthnames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];\nconfig.macros.calendar.daynames = ["M", "T", "W", "T", "F", "S", "S"];\nconfig.macros.calendar.firstday = 0; \nconfig.macros.calendar.firstweekend = 5;\n\nconfig.macros.calendar.weekendbg = "#eeeebb";\nconfig.macros.calendar.monthbg = "#eeeeee";\nconfig.macros.calendar.holidaybg = "#ffc0c0";\n//}}}\n/***\n!Code section:\n***/\n// (you should not need to alter anything below here)//\n//{{{\nconfig.macros.calendar.tiddlerformat = "0DD/0MM/YYYY"; // This used to be changeable - for now, it isn't// <<smiley :-(>> \n\nversion.extensions.calendar = { major: 0, minor: 5, revision: 0, date: new Date(2006, 0, 11)};\nconfig.macros.calendar.monthdays = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\nconfig.macros.calendar.holidays = [ ]; // Not sure this is required anymore - use reminders instead\n//}}}\n\n// //Is the given date a holiday?\n//{{{\nfunction calendarIsHoliday(date)\n{\n var longHoliday = date.formatString("0DD/0MM/YYYY");\n var shortHoliday = date.formatString("0DD/0MM");\n\n for(var i = 0; i < config.macros.calendar.holidays.length; i++) {\n if(config.macros.calendar.holidays[i] == longHoliday || config.macros.calendar.holidays[i] == shortHoliday) {\n return true;\n }\n }\n return false;\n}\n//}}}\n\n// //The main entry point - the macro handler.\n// //Decide what sort of calendar we are creating (month or year, and which month or year)\n// // Create the main calendar container and pass that to sub-ordinate functions to create the structure.\n// ELS 2005.10.30: added creation and use of "tbody" for IE compatibility and fixup for year >1900//\n// ELS 2005.10.30: fix year calculation for IE's getYear() function (which returns '2005' instead of '105')//\n//{{{\nconfig.macros.calendar.handler = function(place,macroName,params)\n{\n var calendar = createTiddlyElement(place, "table", null, "calendar", null);\n var tbody = createTiddlyElement(calendar, "tbody", null, null, null);\n var today = new Date();\n var year = today.getYear();\n if (year<1900) year+=1900;\n if (params[0] == "thismonth")\n createCalendarOneMonth(tbody, year, today.getMonth());\n else if (params[0] == "lastmonth") {\n var month = today.getMonth()-1; if (month==-1) { month=11; year--; }\n createCalendarOneMonth(tbody, year, month);\n }\n else if (params[0] == "nextmonth") {\n var month = today.getMonth()+1; if (month>11) { month=0; year++; }\n createCalendarOneMonth(tbody, year, month);\n }\n else {\n if (params[0]) year = params[0];\n if(params[1])\n createCalendarOneMonth(tbody, year, params[1]-1);\n else\n createCalendarYear(tbody, year);\n }\n}\n//}}}\n\n//{{{\nfunction createCalendarOneMonth(calendar, year, mon)\n{\n var row = createTiddlyElement(calendar, "tr", null, "calenderMonthTitle", null);\n createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, true, year, mon);\n row = createTiddlyElement(calendar, "tr", null, "calendarDaysOfWeek", null);\n createCalendarDayHeader(row, 1);\n createCalendarDayRowsSingle(calendar, year, mon);\n}\n//}}}\n\n//{{{\nfunction createCalendarMonth(calendar, year, mon)\n{\n var row = createTiddlyElement(calendar, "tr", null, null, null);\n createCalendarMonthHeader(calendar, row, config.macros.calendar.monthnames[mon] + " " + year, false, year, mon);\n row = createTiddlyElement(calendar, "tr", null, null, null);\n createCalendarDayHeader(row, 1);\n createCalendarDayRowsSingle(calendar, year, mon);\n}\n//}}}\n\n//{{{\nfunction createCalendarYear(calendar, year)\n{\n var row;\n row = createTiddlyElement(calendar, "tr", null, null, null);\n var back = createTiddlyElement(row, "td", null, null, null);\n var backHandler = function() {\n removeChildren(calendar);\n createCalendarYear(calendar, year-1);\n };\n createTiddlyButton(back, "<", "Back", backHandler);\n back.align = "center";\n\n var yearHeader = createTiddlyElement(row, "td", null, "calendarYear", year);\n yearHeader.align = "center";\n yearHeader.setAttribute("colSpan", 19);\n\n var fwd = createTiddlyElement(row, "td", null, null, null);\n var fwdHandler = function() {\n removeChildren(calendar);\n createCalendarYear(calendar, year+1);\n };\n createTiddlyButton(fwd, ">", "Fwd", fwdHandler);\n fwd.align = "center";\n\n createCalendarMonthRow(calendar, year, 0);\n createCalendarMonthRow(calendar, year, 3);\n createCalendarMonthRow(calendar, year, 6);\n createCalendarMonthRow(calendar, year, 9);\n}\n//}}}\n\n//{{{\nfunction createCalendarMonthRow(cal, year, mon)\n{\n var row = createTiddlyElement(cal, "tr", null, null, null);\n createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon], false, year, mon);\n createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+1], false, year, mon);\n createCalendarMonthHeader(cal, row, config.macros.calendar.monthnames[mon+2], false, year, mon);\n row = createTiddlyElement(cal, "tr", null, null, null);\n createCalendarDayHeader(row, 3);\n createCalendarDayRows(cal, year, mon);\n}\n//}}}\n\n//{{{\nfunction createCalendarMonthHeader(cal, row, name, nav, year, mon)\n{\n var month;\n if(nav) {\n var back = createTiddlyElement(row, "td", null, null, null);\n var backHandler = function() {\n var newyear = year;\n var newmon = mon-1;\n if(newmon == -1) { newmon = 11; newyear = newyear-1;}\n removeChildren(cal);\n createCalendarOneMonth(cal, newyear, newmon);\n };\n createTiddlyButton(back, "<", "Back", backHandler);\n back.align = "center";\n back.style.background = config.macros.calendar.monthbg; \n month = createTiddlyElement(row, "td", null, "calendarMonthname", name)\n month.setAttribute("colSpan", 5);\n var fwd = createTiddlyElement(row, "td", null, null, null);\n var fwdHandler = function() {\n var newyear = year;\n var newmon = mon+1;\n if(newmon == 12) { newmon = 0; newyear = newyear+1;}\n removeChildren(cal);\n createCalendarOneMonth(cal, newyear, newmon);\n };\n createTiddlyButton(fwd, ">", "Fwd", fwdHandler);\n fwd.align = "center";\n fwd.style.background = config.macros.calendar.monthbg; \n } else {\n month = createTiddlyElement(row, "td", null, "calendarMonthname", name)\n month.setAttribute("colSpan", 7);\n }\n month.align = "center";\n month.style.background = config.macros.calendar.monthbg;\n}\n//}}}\n\n//{{{\nfunction createCalendarDayHeader(row, num)\n{\n var cell;\n for(var i = 0; i < num; i++) {\n for(var j = 0; j < 7; j++) {\n var d = j + config.macros.calendar.firstday;\n if(d > 6) d = d - 7;\n cell = createTiddlyElement(row, "td", null, null, config.macros.calendar.daynames[d]);\n\n if(d == config.macros.calendar.firstweekend || d == config.macros.calendar.firstweekend+1)\n cell.className = "calendarWeekend";\n }\n }\n}\n//}}}\n\n//{{{\nfunction createCalendarDays(row, col, first, max, year, mon)\n{\n var i;\n for(i = 0; i < col; i++) {\n createTiddlyElement(row, "td", null, null, null);\n }\n var day = first;\n for(i = col; i < 7; i++) {\n var d = i + config.macros.calendar.firstday;\n if(d > 6) d = d - 7;\n var daycell = createTiddlyElement(row, "td", null, null, null);\n var isaWeekend = ((d == config.macros.calendar.firstweekend || d == (config.macros.calendar.firstweekend+1))? true:false);\n\n if(day > 0 && day <= max) {\n var celldate = new Date(year, mon, day);\n // ELS 2005.10.30: use <<date>> macro's showDate() function to create popup\n if (window.showDate) {\n showDate(daycell,celldate,"popup","DD","DD-MMM-YYYY",true, isaWeekend); \n } else {\n if(isaWeekend) daycell.style.background = config.macros.calendar.weekendbg;\n var title = celldate.formatString(config.macros.calendar.tiddlerformat);\n if(calendarIsHoliday(celldate)) {\n daycell.style.background = config.macros.calendar.holidaybg;\n }\n if(window.findTiddlersWithReminders == null) {\n var link = createTiddlyLink(daycell, title, false);\n link.appendChild(document.createTextNode(day));\n } else {\n var button = createTiddlyButton(daycell, day, title, onClickCalendarDate);\n }\n }\n }\n day++;\n }\n}\n//}}}\n\n// //We've clicked on a day in a calendar - create a suitable pop-up of options.\n// //The pop-up should contain:\n// // * a link to create a new entry for that date\n// // * a link to create a new reminder for that date\n// // * an <hr>\n// // * the list of reminders for that date\n//{{{\nfunction onClickCalendarDate(e)\n{\n var button = this;\n var date = button.getAttribute("title");\n var dat = new Date(date.substr(6,4), date.substr(3,2)-1, date.substr(0, 2));\n\n date = dat.formatString(config.macros.calendar.tiddlerformat);\n var popup = createTiddlerPopup(this);\n popup.appendChild(document.createTextNode(date));\n var newReminder = function() {\n var t = store.getTiddlers(date);\n displayTiddler(null, date, 2, null, null, false, false);\n if(t) {\n document.getElementById("editorBody" + date).value += "\sn<<reminder day:" + dat.getDate() +\n " month:" + (dat.getMonth()+1) +\n " year:" + (dat.getYear()+1900) + " title: >>";\n } else {\n document.getElementById("editorBody" + date).value = "<<reminder day:" + dat.getDate() +\n " month:" + (dat.getMonth()+1) +\n " year:" + (dat.getYear()+1900) + " title: >>";\n }\n };\n var link = createTiddlyButton(popup, "New reminder", null, newReminder); \n popup.appendChild(document.createElement("hr"));\n\n var t = findTiddlersWithReminders(dat, 0, null, null);\n for(var i = 0; i < t.length; i++) {\n link = createTiddlyLink(popup, t[i].tiddler, false);\n link.appendChild(document.createTextNode(t[i].tiddler));\n }\n}\n//}}}\n\n//{{{\nfunction calendarMaxDays(year, mon)\n{\n var max = config.macros.calendar.monthdays[mon];\n if(mon == 1 && (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0)) {\n max++;\n }\n return max;\n}\n//}}}\n\n//{{{\nfunction createCalendarDayRows(cal, year, mon)\n{\n var row = createTiddlyElement(cal, "tr", null, null, null);\n\n var first1 = (new Date(year, mon, 1)).getDay() -1 - config.macros.calendar.firstday;\n if(first1 < 0) first1 = first1 + 7;\n var day1 = -first1 + 1;\n var first2 = (new Date(year, mon+1, 1)).getDay() -1 - config.macros.calendar.firstday;\n if(first2 < 0) first2 = first2 + 7;\n var day2 = -first2 + 1;\n var first3 = (new Date(year, mon+2, 1)).getDay() -1 - config.macros.calendar.firstday;\n if(first3 < 0) first3 = first3 + 7;\n var day3 = -first3 + 1;\n\n var max1 = calendarMaxDays(year, mon);\n var max2 = calendarMaxDays(year, mon+1);\n var max3 = calendarMaxDays(year, mon+2);\n\n while(day1 <= max1 || day2 <= max2 || day3 <= max3) {\n row = createTiddlyElement(cal, "tr", null, null, null);\n createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;\n createCalendarDays(row, 0, day2, max2, year, mon+1); day2 += 7;\n createCalendarDays(row, 0, day3, max3, year, mon+2); day3 += 7;\n }\n}\n//}}}\n\n//{{{\nfunction createCalendarDayRowsSingle(cal, year, mon)\n{\n var row = createTiddlyElement(cal, "tr", null, null, null);\n\n var first1 = (new Date(year, mon, 1)).getDay() -1 - config.macros.calendar.firstday;\n if(first1 < 0) first1 = first1+ 7;\n var day1 = -first1 + 1;\n var max1 = calendarMaxDays(year, mon);\n\n while(day1 <= max1) {\n row = createTiddlyElement(cal, "tr", null, null, null);\n createCalendarDays(row, 0, day1, max1, year, mon); day1 += 7;\n }\n}\n//}}}\n\n// //ELS 2005.10.30: added styles\n//{{{\nsetStylesheet(".calendar, .calendar table, .calendar th, .calendar tr, .calendar td { font-size:10pt; text-align:center; } .calendar { margin:0px !important; }", "calendarStyles");\n//}}}\n
Ever wanted to create your own comic strip, graphic novel, or gag panel?\nDefeated by all the layout and word balloon problems?\n\nThere are now a range of programs available to help, some aimed at younger children, some aimed at more sophisticated audiences.\n\n[[Comic Boom|http://www.toonboom.com/products/comicboom/]] is a relative newcomer from Toon Boom, and provides a 4-panel layout, some characters/content, and uses Toon Boom's vector drawing engine.\n\n[[Comic Book Creator|http://www.myplanetwide.com/]] comes in a standard vanilla version, but seems to be trying a whole range of theme variations, aimed at providing licensed content that will appeal to particular fan base.\n\n[[Comic Life|http://plasq.com/comiclife-win]] aims to to re-use all those digital snapshots on your hard disk by using them as the content and providing the facility to add word balloons, effects, etc.\n\n[[Toonworks Deluxe|http://www.ikesoftware.com/products/toonworks.php]] is more of a single panel creator for younger users, but does allow you to make your own cartoon faces by selecting from a range of eyes, noses, mouths, etc.\n\nOf course, there are many vector and raster programs out there to create your own artwork rather than using the content provided. Although they do not often provide features such as word balloons, they make it easy to create your own or you can import your work into the programs above. If you aren't working at a professional studio and don't need the features, learning curves, and professional prices of top-notch software like Adobe Photoshop or Adobe Illustrator, here are some programs to consider:\n\nIf you are painting in colour, [[OpenCanvas|http://www.portalgraphics.net/en/]] has a large following amongst artists.\n\nManga Studio (both [[Debut|http://my.smithmicro.com/win/manga/index.html]] and [[EX|http://my.smithmicro.com/win/mangaex/index.html]] versions) reportedly specialises more in black-and-white and half-tone style, in spite of the colour artwork on the cover (you can do colour, it just isn't the software's main area of strength).\n\n[[DrawPlus|http://www.serif.com/drawplus/drawplusx2/examples/drawings.asp]] is a very capable vector program which also includes some good paint-style brushes\n\nXara Xtreme is also a capable vector program with a very well-designed interface - [[Bob Hahn's work|http://site.xara.com/gallery/artist.asp?artist=hahn]] is a professional example of how Xara can be used for drawing cartoons.\n\n[[Inkscape|http://www.inkscape.org/]] is an open source vector program which is becoming very powerful.\n\nOf course, cartoon animation programs like [[Anime Studio|http://my.smithmicro.com/win/anime/index.html]] and [[Anime Studio Pro|http://my.smithmicro.com/win/animepro/index.html]] and [[Pencil|http://www.les-stooges.org/pascal/pencil/index.php?id=Home]] are capable of still comics as well as animated cartoons, but do not generally provide facilities such as speech balloons.\n
\nYou can talk to me in real life at:\n\nThe [[Animation SIG|http://groups.melbpc.org.au/~animate/]] of [[Melb PC|http://groups.melbpc.org.au/]] that meets at 6pm on the third Monday of every month\nThe [[Graphics SIG|http://groups.melbpc.org.au/~graphics/]] that meets immediately after it.\n\nOnline:\n\nYou can find me on twitter as [[myless|http://twitter.com/myless]]\n\nComment on my tips and tutorials blogs at:\nhttp://virtualcutout.wordpress.com/ (HTML)\nhttp://virtualcutout.posterous.com/ (PDF)\n\nI might be found lurking as ''Myles'' or ''myles'' at:\n* the [[Cartoon Learning forum|http://www.cartoonlearning.com/forum/]] (emphasis on Anime Studio and ~CreaToon)\n* the official [[Anime Studio users forum|http://www.lostmarble.com/forum/]]\n
Occasionally someone asks about DPI - e.g. what sort of DPI should my animation be created with.\n\nDPI doesn't have anything to do with on-screen display - think purely in terms of pixels.\n\nOnly in scanning and printing does DPI play a part, and it's a lot simpler than most people seem to think.\nIf you need something to be 800 pixels across on the screen, and it is 4 inches across on paper, scan at 200 dpi (i.e. 200 pixels on the screen for every inch on the paper).\nIf you want to print an 800x600 screenshot, at 200dpi it will be 4 inches by 3 inches on paper. \n\nMy rough rule of thumb: Anything less than 200 dpi will start deteriorating in quality.Anything more than 300dpi is generally wasted (unless, for example, you're scanning a small slide negative and want to print it A4 full page, then generally you want to scan at a higher resolution).\n\nScreens use pixels.\n\nI think 72 dpi was the old on-screen default resolution for Macs, which were the prevalent machines in print and design shops.\n\nThese days, Windows, Linux, and (I assume) Macs have variable resolution settings. A screen set at 800x600 will have a different on-screen dpi to one set at 1280x1024.\n\nThe native resolution of many (at least for 17") LCD screens these days is more likely to be closer to 96dpi then 72dpi, at least for Windows/Linux, but this will vary.\nWith your screen set to native resolution, draw a 72x72 pixel rectangle in a paint program, and a 96x96 pixel rectangle, and put a ruler up against the screen. Which one is closer to an inch across?\n\nSo, ignore dpi completely for on-screen work. Not 72 dpi, 96, 300, or 1200.\n\nOnly pixel dimensions.\n\nYou will need to consider your output pixel dimensions - whether you are doing 320x240 for a web-page, 640x480 multimedia on CD, DVD, TV, cinematic, etc - see also [[Animation resolutions]]\n
/***\n|''Name:''|DatePlugin|\n|''Source:''|http://www.TiddlyTools.com/#DatePlugin|\n|''Author:''|Eric Shulman - ELS Design Studios|\n|''License:''|[[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|\n|''~CoreVersion:''|2.0.10|\n\nThere are quite a few calendar generators, reminders, to-do lists, 'dated tiddlers' journals, blog-makers and GTD-like schedule managers that have been built around TW. While they all have different purposes, and vary in format, interaction, and style, in one way or another each of these plugins displays and/or uses date-based information to make finding, accessing and managing relevant tiddlers easier. This plugin provides a general approach to embedding dates and date-based links/menus within tiddler content.\n\nThis plugin display formatted dates, for the specified year, month, day using number values or mathematical expressions such as (Y+1) or (D+30). Optionally, you can create a link from the formatted output to a 'dated tiddler' for quick blogging or create a popup menu that includes the dated tiddler link plus links to changes made on that date as well as links to any pending reminders for the coming 31 days (if the RemindersPlugin is installed). This plugin also provides a public API for easily incorporating formatted date output (with or without the links/popups) into other plugins, such as calendar generators, etc.\n!!!!!Usage\n<<<\nWhen installed, this plugin defines a macro: {{{<<date [mode] [date] [format] [linkformat]>>}}}. All of the macro parameters are optional and, in it's simplest form, {{{<<date>>}}}, it is equivalent to the ~TiddlyWiki core macro, {{{<<today>>}}}.\n\nHowever, where {{{<<today>>}}} simply inserts the current date/time in a predefined format (or custom format, using {{{<<today [format]>>}}}), the {{{<<date>>}}} macro's parameters take it much further than that:\n* [mode] is either ''display'', ''link'' or ''popup''. If omitted, it defaults to ''display''. This param let's you select between simply displaying a formatted date, or creating a link to a specific 'date titled' tiddler or a popup menu containing a dated tiddler link, plus links to changes and reminders.\n* [date] lets you enter ANY date (not just today) as ''year, month, and day values or simple mathematical expressions'' using pre-defined variables, Y, M, and D for the current year, month and day, repectively. You can display the modification date of the current tiddler by using the keyword: ''tiddler'' in place of the year, month and day parameters. Use ''tiddler://name-of-tiddler//'' to display the modification date of a specific tiddler. You can also use keywords ''today'' or ''filedate'' to refer to these //dynamically changing// date/time values. \n* [format] and [linkformat] uses standard ~TiddlyWiki date formatting syntax. The default is "YYYY.0MM.0DD"\n>^^''DDD'' - day of week in full (eg, "Monday"), ''DD'' - day of month, ''0DD'' - adds leading zero^^\n>^^''MMM'' - month in full (eg, "July"), ''MM'' - month number, ''0MM'' - adds leading zero^^\n>^^''YYYY'' - full year, ''YY'' - two digit year, ''hh'' - hours, ''mm'' - minutes, ''ss'' - seconds^^\n>^^//note: use of hh, mm or ss format codes is only supported with ''tiddler'', ''today'' or ''filedate'' values//^^\n* [linkformat] - specify an alternative date format so that the title of a 'dated tiddler' link can have a format that differs from the date's displayed format\n\nIn addition to the macro syntax, DatePlugin also provides a public javascript API so that other plugins that work with dates (such as calendar generators, etc.) can quickly incorporate date formatted links or popups into their output:\n\n''{{{showDate(place, date, mode, format, linkformat, autostyle, weekend)}}}'' \n\nNote that in addition to the parameters provided by the macro interface, the javascript API also supports two optional true/false parameters:\n* [autostyle] - when true, the font/background styles of formatted dates are automatically adjusted to show the date's status: 'today' is boxed, 'changes' are bold, 'reminders' are underlined, while weekends and holidays (as well as changes and reminders) can each have a different background color to make them more visibly distinct from each other.\n* [weekend] - true indicates a weekend, false indicates a weekday. When this parameter is omitted, the plugin uses internal defaults to automatically determine when a given date falls on a weekend.\n<<<\n!!!!!Examples\n<<<\nThe current date: <<date>>\nThe current time: <<date today "0hh:0mm:0ss">>\nToday's blog: <<date link today "DDD, MMM DDth, YYYY">>\nRecent blogs/changes/reminders: <<date popup Y M D-1 "yesterday">> <<date popup today "today">> <<date popup Y M D+1 "tomorrow">>\nThe first day of next month will be a <<date Y M+1 1 "DDD">>\nThis tiddler (DatePlugin) was last updated on: <<date tiddler "DDD, MMM DDth, YYYY">>\nThe SiteUrl was last updated on: <<date tiddler:SiteUrl "DDD, MMM DDth, YYYY">>\nThis document was last saved on <<date filedate "DDD, MMM DDth, YYYY at 0hh:0mm:0ss">>\n<<date 2006 07 24 "MMM DDth, YYYY">> will be a <<date 2006 07 24 "DDD">>\n<<<\n!!!!!Installation\n<<<\nimport (or copy/paste) the following tiddlers into your document:\n''DatePlugin'' (tagged with <<tag systemConfig>>)\n<<<\n!!!!!Revision History\n<<<\n''2006.05.09 [2.2.1]'' added "todaybg" handling to set background color of current date. Also, honor excludeLists tag when getting lists of tiddlers. Based on suggestions by Mark Hulme.\n''2006.05.05 [2.2.0]'' added "linkedbg" handling to set background color when a 'dated tiddler' exists. Based on a suggestion by Mark Hulme.\n''2006.03.08 [2.1.2]'' add 'override leadtime' flag param in call to findTiddlersWithReminders(), and add "Enter a title" default text to new reminder handler. Thanks to Jeremy Sheeley for these additional tweaks.\n''2006.03.06 [2.1.0]'' hasReminders() nows uses window.reminderCacheForCalendar[] when present. If calendar cache is not present, indexReminders() now uses findTiddlersWithReminders() with a 90-day look ahead to check for reminders. Also, switched default background colors for autostyled dates: reminders are now greenish ("c0ffee") and holidays are now reddish ("ffaace").\n''2006.02.14 [2.0.5]'' when readOnly is set (by TW core), omit "new reminders..." popup menu item and, if a "dated tiddler" does not already exist, display the date as simple text instead of a link.\n''2006.02.05 [2.0.4]'' added var to variables that were unintentionally global. Avoids FireFox 1.5.0.1 crash bug when referencing global variables\n''2006.01.18 [2.0.3]'' In 1.2.x the tiddler editor's text area control was given an element ID=("tiddlerBody"+title), so that it was easy to locate this field and programmatically modify its content. With the addition of configuration templates in 2.x, the textarea no longer has an ID assigned. To find this control we now look through all the child nodes of the tiddler editor to locate a "textarea" control where attribute("edit") equals "text", and then append the new reminder to the contents of that control.\n''2006.01.11 [2.0.2]'' correct 'weekend' override detection logic in showDate()\n''2006.01.10 [2.0.1]'' allow custom-defined weekend days (default defined in config.macros.date.weekend[] array)\nadded flag param to showDate() API to override internal weekend[] array\n''2005.12.27 [2.0.0]'' Update for TW2.0\nAdded parameter handling for 'linkformat'\n''2005.12.21 [1.2.2]'' FF's date.getYear() function returns 105 (for the current year, 2005). When calculating a date value from Y M and D expressions, the plugin adds 1900 to the returned year value get the current year number. But IE's date.getYear() already returns 2005. As a result, plugin calculated date values on IE were incorrect (e.g., 3905 instead of 2005). Adding +1900 is now conditional so the values will be correct on both browsers.\n''2005.11.07 [1.2.1]'' added support for "tiddler" dynamic date parameter\n''2005.11.06 [1.2.0]'' added support for "tiddler:title" dynamic date parameter\n''2005.11.03 [1.1.2]'' when a reminder doesn't have a specified title parameter, use the title of the tiddler that contains the reminder as "fallback" text in the popup menu. Based on a suggestion from BenjaminKudria.\n''2005.11.03 [1.1.1]'' Temporarily bypass hasReminders() logic to avoid excessive overhead from generating the indexReminders() cache. While reminders can still appear in the popup menu, they just won't be indicated by auto-styling the date number that is displayed. This single change saves approx. 60% overhead (5 second delay reduced to under 2 seconds).\n''2005.11.01 [1.1.0]'' corrected logic in hasModifieds() and hasReminders() so caching of indexed modifieds and reminders is done just once, as intended. This should hopefully speed up calendar generators and other plugins that render multiple dates...\n''2005.10.31 [1.0.1]'' documentation and code cleanup\n''2005.10.31 [1.0.0]'' initial public release\n''2005.10.30 [0.9.0]'' pre-release\n<<<\n!!!!!Credits\n<<<\nThis feature was developed by EricShulman from [[ELS Design Studios|http:/www.elsdesign.com]].\n<<<\n!!!!!Code\n***/\n//{{{\nversion.extensions.date = {major: 2, minor: 2, revision: 1, date: new Date(2006,5,9)};\n//}}}\n\n//{{{\nconfig.macros.date = {\n format: "YYYY.0MM.0DD", // default date display format\n linkformat: "YYYY.0MM.0DD", // 'dated tiddler' link format\n linkedbg: "#babb1e", // "babble"\n todaybg: "#ffab1e", // "fable"\n weekendbg: "#c0c0c0", // "cocoa"\n holidaybg: "#ffaace", // "face"\n modifiedsbg: "#bbeeff", // "beef"\n remindersbg: "#c0ffee", // "coffee"\n holidays: [ "01/01", "01/26", "04/04", "04/25" ], // NewYearsDay, AustraliaDay, Pam's Birthday, AnzacDay\n weekend: [ 1,0,0,0,0,0,1 ] // [ day index values: sun=0, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6 ]\n};\n//}}}\n\n//{{{\nconfig.macros.date.handler = function(place,macroName,params)\n{\n // do we want to see a link, a popup, or just a formatted date?\n var mode="display";\n if (params[0]=="display") { mode=params[0]; params.shift(); }\n if (params[0]=="popup") { mode=params[0]; params.shift(); }\n if (params[0]=="link") { mode=params[0]; params.shift(); }\n // get the date\n var now = new Date();\n var date = now;\n if (!params[0] || params[0]=="today")\n { params.shift(); }\n else if (params[0]=="filedate")\n { date=new Date(document.lastModified); params.shift(); }\n else if (params[0]=="tiddler")\n { date=store.getTiddler(story.findContainingTiddler(place).id.substr(7)).modified; params.shift(); }\n else if (params[0].substr(0,8)=="tiddler:")\n { var t; if ((t=store.getTiddler(params[0].substr(8)))) date=t.modified; params.shift(); }\n else {\n var y = eval(params.shift().replace(/Y/ig,(now.getYear()<1900)?now.getYear()+1900:now.getYear()));\n var m = eval(params.shift().replace(/M/ig,now.getMonth()+1));\n var d = eval(params.shift().replace(/D/ig,now.getDate()+0));\n date = new Date(y,m-1,d);\n }\n // date format with optional custom override\n var format=this.format; if (params[0]) format=params.shift();\n var linkformat=this.linkformat; if (params[0]) linkformat=params.shift();\n showDate(place,date,mode,format,linkformat);\n}\n//}}}\n\n//{{{\nwindow.showDate=showDate;\nfunction showDate(place,date,mode,format,linkformat,autostyle,weekend)\n{\n if (!mode) mode="display";\n if (!format) format=config.macros.date.format;\n if (!linkformat) linkformat=config.macros.date.linkformat;\n if (!autostyle) autostyle=false;\n\n // format the date output\n var title = date.formatString(format);\n var linkto = date.formatString(linkformat);\n\n // just show the formatted output\n if (mode=="display") { place.appendChild(document.createTextNode(title)); return; }\n\n // link to a 'dated tiddler'\n var link = createTiddlyLink(place, linkto, false);\n link.appendChild(document.createTextNode(title));\n link.title = linkto;\n link.date = date;\n link.format = format;\n link.linkformat = linkformat;\n\n // if using a popup menu, replace click handler for dated tiddler link\n // with handler for popup and make link text non-italic (i.e., an 'existing link' look)\n if (mode=="popup") {\n link.onclick = onClickDatePopup;\n link.style.fontStyle="normal";\n }\n\n // format the popup link to show what kind of info it contains (for use with calendar generators)\n if (!autostyle) return;\n if (hasModifieds(date))\n { link.style.fontStyle="normal"; link.style.fontWeight="bold"; }\n if (hasReminders(date))\n { link.style.textDecoration="underline"; }\n if(isToday(date))\n { link.style.border="1px solid black"; }\n\n if( (weekend!=undefined?weekend:isWeekend(date)) && (config.macros.date.weekendbg!="") )\n { place.style.background = config.macros.date.weekendbg; }\n if(isHoliday(date)&&(config.macros.date.holidaybg!=""))\n { place.style.background = config.macros.date.holidaybg; }\n if (hasModifieds(date)&&(config.macros.date.modifiedsbg!=""))\n { place.style.background = config.macros.date.modifiedsbg; }\n if (store.tiddlerExists(linkto)&&(config.macros.date.linkedbg!=""))\n { place.style.background = config.macros.date.linkedbg; }\n if (hasReminders(date)&&(config.macros.date.remindersbg!=""))\n { place.style.background = config.macros.date.remindersbg; }\n if(isToday(date)&&(config.macros.date.todaybg!=""))\n { place.style.background = config.macros.date.todaybg; }\n}\n//}}}\n\n//{{{\nfunction isToday(date) // returns true if date is today\n { var now=new Date(); return ((now-date>=0) && (now-date<86400000)); }\n\nfunction isWeekend(date) // returns true if date is a weekend\n { return (config.macros.date.weekend[date.getDay()]); }\n\nfunction isHoliday(date) // returns true if date is a holiday\n{\n var longHoliday = date.formatString("0MM/0DD/YYYY");\n var shortHoliday = date.formatString("0MM/0DD");\n for(var i = 0; i < config.macros.date.holidays.length; i++) {\n var holiday=config.macros.date.holidays[i];\n if (holiday==longHoliday||holiday==shortHoliday) return true;\n }\n return false;\n}\n//}}}\n\n//{{{\n// Event handler for clicking on a day popup\nfunction onClickDatePopup(e)\n{\n if (!e) var e = window.event;\n var theTarget = resolveTarget(e);\n var popup = createTiddlerPopup(this);\n if(popup) {\n // always show dated tiddler link (or just date, if readOnly) at the top...\n if (!readOnly || store.tiddlerExists(this.date.formatString(this.linkformat)))\n createTiddlyLink(popup,this.date.formatString(this.linkformat),true);\n else\n createTiddlyText(popup,this.date.formatString(this.linkformat));\n addModifiedsToPopup(popup,this.date,this.format);\n addRemindersToPopup(popup,this.date,this.linkformat);\n }\n scrollToTiddlerPopup(popup,false);\n e.cancelBubble = true;\n if (e.stopPropagation) e.stopPropagation();\n return(false);\n}\n//}}}\n\n//{{{\nfunction indexModifieds() // build list of tiddlers, hash indexed by modification date\n{\n var modifieds= { };\n var tiddlers = store.getTiddlers("title","excludeLists");\n for (var t = 0; t < tiddlers.length; t++) {\n var date = tiddlers[t].modified.formatString("YYYY0MM0DD")\n if (!modifieds[date])\n modifieds[date]=new Array();\n modifieds[date].push(tiddlers[t].title);\n }\n return modifieds;\n}\nfunction hasModifieds(date) // returns true if date has modified tiddlers\n{\n if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();\n return (config.macros.date.modifieds[date.formatString("YYYY0MM0DD")]!=undefined);\n}\n\nfunction addModifiedsToPopup(popup,when,format)\n{\n if (!config.macros.date.modifieds) config.macros.date.modifieds = indexModifieds();\n var indent=String.fromCharCode(160)+String.fromCharCode(160);\n var mods = config.macros.date.modifieds[when.formatString("YYYY0MM0DD")];\n if (mods) {\n mods.sort();\n var e=createTiddlyElement(popup,"div",null,null,"changes:");\n for(var t=0; t<mods.length; t++) {\n var link=createTiddlyLink(popup,mods[t],false);\n link.appendChild(document.createTextNode(indent+mods[t]));\n createTiddlyElement(popup,"br",null,null,null);\n }\n }\n}\n//}}}\n\n//{{{\nfunction indexReminders(date,leadtime) // build list of tiddlers with reminders, hash indexed by reminder date\n{\n var reminders = { };\n if(window.findTiddlersWithReminders!=undefined) { // reminder plugin is installed\n // DEBUG var starttime=new Date();\n var t = findTiddlersWithReminders(date, [0,leadtime], null, null, 1);\n for(var i=0; i<t.length; i++) reminders[t[i].matchedDate]=true;\n // DEBUG var out="Found "+t.length+" reminders in "+((new Date())-starttime+1)+"ms\sn";\n // DEBUG out+="startdate: "+date.toLocaleDateString()+"\sn"+"leadtime: "+leadtime+" days\sn\sn";\n // DEBUG for(var i=0; i<t.length; i++) { out+=t[i].matchedDate.toLocaleDateString()+" "+t[i].params.title+"\sn"; }\n // DEBUG alert(out);\n }\n return reminders;\n}\n\nfunction hasReminders(date) // returns true if date has reminders\n{\n if (window.reminderCacheForCalendar)\n return window.reminderCacheForCalendar[date]; // use calendar cache\n if (!config.macros.date.reminders)\n config.macros.date.reminders = indexReminders(date,90); // create a 90-day leadtime reminder cache\n return (config.macros.date.reminders[date]);\n}\n\nfunction addRemindersToPopup(popup,when,format)\n{\n if(window.findTiddlersWithReminders==undefined) return; // reminder plugin not installed\n\n var indent = String.fromCharCode(160)+String.fromCharCode(160);\n var reminders=findTiddlersWithReminders(when, [0,31],null,null,1);\n var e=createTiddlyElement(popup,"div",null,null,"reminders:"+(!reminders.length?" none":""));\n for(var t=0; t<reminders.length; t++) {\n link = createTiddlyLink(popup,reminders[t].tiddler,false);\n var diff=reminders[t].diff;\n diff=(diff<1)?"Today":((diff==1)?"Tomorrow":diff+" days");\n var txt=(reminders[t].params["title"])?reminders[t].params["title"]:reminders[t].tiddler;\n link.appendChild(document.createTextNode(indent+diff+" - "+txt));\n createTiddlyElement(popup,"br",null,null,null);\n }\n if (readOnly) return; // omit "new reminder..." link\n var link = createTiddlyLink(popup,indent+"new reminder...",true); createTiddlyElement(popup,"br");\n var title = when.formatString(format);\n link.title="add a reminder to '"+title+"'";\n link.onclick = function() {\n // show tiddler editor\n story.displayTiddler(null, title, 2, null, null, false, false);\n // find body 'textarea'\n var c =document.getElementById("tiddler" + title).getElementsByTagName("*");\n for (var i=0; i<c.length; i++) if ((c[i].tagName.toLowerCase()=="textarea") && (c[i].getAttribute("edit")=="text")) break;\n // append reminder macro to tiddler content\n if (i<c.length) {\n if (store.tiddlerExists(title)) c[i].value+="\sn"; else c[i].value="";\n c[i].value += "<<reminder";\n c[i].value += " day:"+when.getDate();\n c[i].value += " month:"+(when.getMonth()+1);\n c[i].value += " year:"+when.getFullYear();\n c[i].value += ' title:"Enter a title" >>';\n }\n };\n}\n//}}}\n
[[Welcome]]\n
PAL television shows images at 50 "fields" per second, which is almost like 25 frames per second (fps), with the even-numbered lines of a frame updated on one cycle of screen refresh, and the odd-numbered lines on the next cycle.\n\nNTSC shows at roughly 60 fields per second or 30 fps (because of various technical issues the rate is more like 29.97 fps or 59.94 fields per second - there are actually a few more figures after the decimal point, but to most users the difference is not worth worrying about.).\n\nCinematic 35mm film shows at 24 fps. CDROM based multimedia often shows animations at 12 or 15 fps, and these are also often the rates used by Flash animation (earlier versions of the Flash player would only guarantee playback speed up to about that speed).\n\nOld-style television animation was often animated onto film at 24 fps, which was then converted to NTSC (a process called ''telecine'', which I guess was abbreviated from something like television-cinema or televised-cinema) by reproducing fields, ending up with some frames showing as complete frames (made up of fields from the same frame) and some frames showing interlaced frames (made up of fields from 2 different frames). If you hear "drop-frame" being discussed in this process, actual frames are not dropped, only the frame numbering drops numbers (something like the different date numbering between months, some being 30 days long and some 31).\n\nBroadcasters can generally convert 24 fps into the appropriate format, and 24 fps will possibly be used for HDTV (high-definition television).\nDVD material is usually 24 fps "behind the scenes", leaving it to the DVD player to telecine it to the appropriate format \n\nMany animators these days still recommend using 24 fps as your animation rate. It divides easily into sections of 1, 2, 3, 4, 6, 8, 12, and 24 for animation timing, and a lot of animation timing information is available which assumes a 24 fps rate. When converting for broadcast, your broadcaster or your video editing software can hopefully do some sort of telecine (some video players can also do a live telecine process).\n\nA lot of older animation also uses a style called "animating on 2s", where normal animation was only drawn on every second sheet or cel, each which was then filmed twice onto adjacent frames on the film. This is roughly equivalent to animating at 12 fps. Faster action was animated "on 1s" where every frame was drawn. \n\nAnimating everything "on 1s", which computer in-betweening does automatically, can often produce a "floaty" or over-smooth look. Computer animation can sometimes require more attention, either to keyframe settings (converting interpolation to linear or step, for example), or to the spacing of keyframes, to produce more "snap" in animation movement. Sometimes more keyframes will also be needed to produce more control over ease-in/ease-out motion than the default settings in software produce.\n\n//Sources for further information://\nhttp://www.nfsa.afc.gov.au/glossary.nsf/Pages/DVD?OpenDocument\nhttp://en.wikipedia.org/wiki/Telecine\nhttp://en.wikipedia.org/wiki/Frame_rate\nhttp://en.wikipedia.org/wiki/24p\nhttp://www.quantel.com/site/en.nsf/HTML/library_dfb?OpenDocument\nhttp://www.hdtvmagazine.com/glossary.php\nhttp://digitalcontentproducer.com/workflow/Shooting_for_DVD012606/
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: <<option txtUserName>>
I recently tried [[GraphicsMagick|http://www.graphicsmagick.org/]] (a version of [[ImageMagick|http://www.imagemagick.org/]]) for converting [[ArtRage|http://www.ambientdesign.com/]] layered .psd files into a sequence of .png files (with transparency/alpha) for import into [[Anime Studio]].\n\nCommand line: ''gm convert layers.psd alayer%02d.png''\n\nIt all worked rather well on the one test I've done so far.
[[Flat is Funny, Depth is Dramatic|http://sevencamels.blogspot.com/2009/01/flat-is-funny-depth-is-dramatic.html]] - an interesting and useful rule-of-thumb, although it won't apply to all situations.
/***\n|''Name:''|LegacyStrikeThroughPlugin|\n|''Description:''|Support for legacy (pre 2.1) strike through formatting|\n|''Version:''|1.0.1|\n|''Date:''|Jul 21, 2006|\n|''Source:''|http://www.tiddlywiki.com/#LegacyStrikeThroughPlugin|\n|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|\n|''License:''|[[BSD open source license]]|\n|''CoreVersion:''|2.1.0|\n|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|\n\n***/\n\n//{{{\n\n// Ensure that the LegacyStrikeThrough Plugin is only installed once.\nif(!version.extensions.LegacyStrikeThroughPlugin)\n {\n version.extensions.LegacyStrikeThroughPlugin = true;\n\nconfig.formatters.push(\n{\n name: "legacyStrikeByChar",\n match: "==",\n termRegExp: /(==)/mg,\n element: "strike",\n handler: config.formatterHelpers.createElementAndWikify\n});\n\n} // end of "install only once"\n//}}}\n
<<tagging links>>
[[Welcome]]\n[[Myles's notes]]\n[[Animation books]]\n[[Links]]\n\n[[Anime Studio]]\n[[Anime Studio tips]]\n[[Useful software]]\n[[Cartoon software]]\n[[Animation software]]\n\n[[SIGs]]\n[[Interesting reading]]\n\n[[Contact me]]\n\n<<calendar thismonth>>\n----\n~TiddlyWiki version:<<version>>\n----\n[[SiteMaintenance]]\n
<!--{{{-->\n<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>\n<!--}}}-->
[[Photobucket|http://www.photobucket.com/]] - good for direct link images - registration required\n[[Mediafire|http://www.mediafire.com/]] - good for large media files\n[[Twango|http://www.twango.com/]] - good for media files\n[[ImageShack|http://www.imageshack.us/]] - good for image thumbnails + links\n[[zShare|http://www.zshare.net/]] - up to 100MB of temporary storage for video, images, Flash\n[[ImagePile|http://www.imagepile.net/]] - lots of advertisements but allows hotlinking\n[[YouTube|http://www.youtube.com/]] - best-known video-sharing site, but videos may be converted/resized (converts to 320ร—240, which they display at about 425ร—318, resulting in some quality loss)\n[[Flickr|http://www.flickr.com/]] - great image sharing site, easy forums/discussion groups\n[[Revver|http://one.revver.com/revver]] - good video sharing site, quality video (480ร—392), advertisements (shares revenue with uploader)\n[[MyToons|http://www.mytoons.com/]] - a place to share cartoon animation\n[[AniBOOM|http://www.aniboom.com/]] - another place to share animation, with online animation tool [[ShapeShifter|http://www.aniboom.com/ShapeshifterMain/]]\n[[Fuzzwich|http://fuzzwich.com/]] - site to publish animations created with its own animation editor (pre-loaded content only)\n[[ComicSpace|http://www.comicspace.com/]] - for static comics\n[[ToonGum|http://www.toongum.com/]] - for Flash animation
<<tagging misc>>
After spending way too much unproductive time dabbling in 3D animation, Myles now spends way too much mostly unproductive time dabbling in 2D animation, mainly using [[Anime Studio|http://www.e-frontier.com/go/LManime]] (formerly know as Moho). \n\nMyles is deputy convener of the [[Graphics Special Interest Group (Graphics SIG)|http://groups.melbpc.org.au/~graphics/]] of [[Melbourne PC Users Group (Melb PC)|http://www.melbpc.org.au/]], and convener of the [[Animation group|http://groups.melbpc.org.au/~animate/]] (on the same night as the Graphics SIG, held before the Graphics SIG commences).
''Covers''\n[[November 2000|http://www.melbpc.org.au/pcupdate/2011/index.htm]]\n[[December 2001|http://www.melbpc.org.au/pcupdate/2112/index.htm]]\n\n''Articles - Graphics''\n[[An introduction to raytracing|http://www.melbpc.org.au/pcupdate/2011/2011article11.htm]]\n[[Spline fever|http://www.melbpc.org.au/pcupdate/2011/2011article16.htm]]\n[[Drawn out|http://www.melbpc.org.au/pcupdate/2107/2107article8.htm]]\n[[Animated conversation|http://www.melbpc.org.au/pcupdate/2107/2107article3.htm]]\n\n''Articles - Programming''\n[[Python - executable pseudocode|http://www.melbpc.org.au/pcupdate/2108/2108article9.htm]]\n[[A snake in the web: Python & CGI|http://www.melbpc.org.au/pcupdate/2112/2112article5.htm]]\n\n
!Editors\n[[SciTE folding editor|http://www.scintilla.org/SciTE.html]]\n[[SciTE forum|http://mailman.lyra.org/pipermail/scite-interest/]]\n[[SciTE scripts|http://lua-users.org/wiki/SciteScripts]]\n[[Leo outlining editor|http://leo.sourceforge.net/]]\n[[EditPlus|http://www.editplus.com/]]\n[[PSPad|http://www.pspad.com/en/]] and [[forum|http://forum.pspad.com/]]\n\n!Python\n[[Python|http://www.python.org/]]\n[[Cookbook|http://aspn.activestate.com/ASPN/Cookbook/Python]]\n[[Wiki|http://wiki.python.org/moin/FrontPage]]\n[[Style guide|http://www.python.org/doc/essays/styleguide.html]]\n[[String formatting|http://docs.python.org/lib/typesseq-strings.html]]\n[[PythonCard|http://pythoncard.sourceforge.net/]]\n[[Daily Python URL|http://www.pythonware.com/daily/]]\n[[comp.lang.python|http://groups.google.com/group/comp.lang.python?hl=en&lr=&ie=UTF-8]]\n[[comp.lang.python.announce|http://groups.google.com/group/comp.lang.python.announce?hl=en&lr=&ie=UTF-8]]\n[[Snack sound tollkit for Tcl/Tk|http://www.speech.kth.se/snack/index.html]]\n\n!Boo\n[[Home page|http://boo.codehaus.org/]]\n[[Yahoo!group|http://groups.google.com/group/boolang]]\n[[Blogdigger|http://groups.blogdigger.com/groups.jsp?id=1647]]\n\n!Lua\n[[Lua home page|http://www.lua.org/]]\n[[Lua users|http://lua-users.org/]]\n[[Wiki|http://lua-users.org/wiki/]]\n\n!Miscellaneous\n[[Rebol|http://www.rebol.com/]]\n[[UltraVNC|http://ultravnc.sourceforge.net/]]\n[[WordWeb|http://wordweb.info/]]\n
<<newReminder>>\n
/***\n|''Name:''|ReminderPlugin|\n|''Version:''|2.3.8 (Mar 9, 2006)|\n|''Source:''|http://www.geocities.com/allredfaq/reminderMacros.html|\n|''Author:''|Jeremy Sheeley(pop1280 [at] excite [dot] com)|\n|''Licence:''|[[BSD open source license]]|\n|''Macros:''|reminder, showreminders, displayTiddlersWithReminders, newReminder|\n|''TiddlyWiki:''|2.0+|\n|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|\n\n!Description\nThis plugin provides macros for tagging a date with a reminder. Use the {{{reminder}}} macro to do this. The {{{showReminders}}} and {{{displayTiddlersWithReminder}}} macros automatically search through all available tiddlers looking for upcoming reminders.\n\n!Installation\n* Create a new tiddler in your tiddlywiki titled ReminderPlugin and give it the {{{systemConfig}}} tag. The tag is important because it tells TW that this is executable code.\n* Double click this tiddler, and copy all the text from the tiddler's body.\n* Paste the text into the body of the new tiddler in your TW.\n* Save and reload your TW.\n* You can copy some examples into your TW as well. See [[Simple examples]], [[Holidays]], [[showReminders]] and [[Personal Reminders]]\n\n!Syntax:\n|>|See [[ReminderSyntax]] and [[showRemindersSyntax]]|\n\n!Revision history\n* v2.3.8 (Mar 9, 2006)\n**Bug fix: A global variable had snuck in, which was killing FF 1.5.0.1\n**Feature: You can now use TIDDLER and TIDDLERNAME in a regular reminder format\n* v2.3.6 (Mar 1, 2006)\n**Bug fix: Reminders for today weren't being matched sometimes.\n**Feature: Solidified integration with DatePlugin and CalendarPlugin\n**Feature: Recurring reminders will now return multiple hits in showReminders and the calendar.\n**Feature: Added TIDDLERNAME to the replacements for showReminders format, for plugins that need the title without brackets.\n* v2.3.5 (Feb 8, 2006)\n**Bug fix: Sped up reminders lots. Added a caching mechanism for reminders that have already been matched.\n* v2.3.4 (Feb 7, 2006)\n**Bug fix: Cleaned up code to hopefully prevent the Firefox 1.5.0.1 crash that was causing lots of plugins \nto crash Firefox. Thanks to http://www.jslint.com\n* v2.3.3 (Feb 2, 2006)\n**Feature: newReminder now has drop down lists instead of text boxes.\n**Bug fix: A trailing space in a title would trigger an infinite loop.\n**Bug fix: using tag:"birthday !reminder" would filter differently than tag:"!reminder birthday"\n* v2.3.2 (Jan 21, 2006)\n**Feature: newReminder macro, which will let you easily add a reminder to a tiddler. Thanks to Eric Shulman (http://www.elsdesign.com) for the code to do this.\n** Bug fix: offsetday was not working sometimes\n** Bug fix: when upgrading to 2.0, I included a bit to exclude tiddlers tagged with excludeSearch. I've reverted back to searching through all tiddlers\n* v2.3.1 (Jan 7, 2006)\n**Feature: 2.0 compatibility\n**Feature AlanH sent some code to make sure that showReminders prints a message if no reminders are found.\n* v2.3.0 (Jan 3, 2006)\n** Bug Fix: Using "Last Sunday (-0)" as a offsetdayofweek wasn't working.\n** Bug Fix: Daylight Savings time broke offset based reminders (for example year:2005 month:8 day:23 recurdays:7 would match Monday instead of Tuesday during DST.\n\n!Code\n***/\n//{{{\n\n//============================================================================\n//============================================================================\n// ReminderPlugin\n//============================================================================\n//============================================================================\n\nversion.extensions.ReminderPlugin = {major: 2, minor: 3, revision: 8, date: new Date(2006,3,9), source: "http://www.geocities.com/allredfaq/reminderMacros.html"};\n\n//============================================================================\n// Configuration\n// Modify this section to change the defaults for \n// leadtime and display strings\n//============================================================================\n\nconfig.macros.reminders = {};\nconfig.macros["reminder"] = {};\nconfig.macros["newReminder"] = {};\nconfig.macros["showReminders"] = {};\nconfig.macros["displayTiddlersWithReminders"] = {};\n\nconfig.macros.reminders["defaultLeadTime"] = [0,6000];\nconfig.macros.reminders["defaultReminderMessage"] = "DIFF: TITLE on DATE ANNIVERSARY";\nconfig.macros.reminders["defaultShowReminderMessage"] = "DIFF: TITLE on DATE ANNIVERSARY -- TIDDLER";\nconfig.macros.reminders["defaultAnniversaryMessage"] = "(DIFF)";\nconfig.macros.reminders["untitledReminder"] = "Untitled Reminder";\nconfig.macros.reminders["noReminderFound"] = "Couldn't find a match for TITLE in the next LEADTIMEUPPER days."\nconfig.macros.reminders["todayString"] = "Today";\nconfig.macros.reminders["tomorrowString"] = "Tomorrow";\nconfig.macros.reminders["ndaysString"] = "DIFF days";\nconfig.macros.reminders["emtpyShowRemindersString"] = "There are no upcoming events";\n\n\n//============================================================================\n// Code\n// You should not need to edit anything \n// below this. Make sure to edit this tiddler and copy \n// the code from the text box, to make sure that \n// tiddler rendering doesn't interfere with the copy \n// and paste.\n//============================================================================\n\n// This line is to preserve 1.2 compatibility\n if (!story) var story=window; \n//this object will hold the cache of reminders, so that we don't\n//recompute the same reminder over again.\nvar reminderCache = {};\n\nconfig.macros.showReminders.handler = function showReminders(place,macroName,params)\n{\n var now = new Date().getMidnight();\n var paramHash = {};\n var leadtime = [0,14];\n paramHash = getParamsForReminder(params);\n var bProvidedDate = (paramHash["year"] != null) || \n (paramHash["month"] != null) || \n (paramHash["day"] != null) || \n (paramHash["dayofweek"] != null);\n if (paramHash["leadtime"] != null)\n {\n leadtime = paramHash["leadtime"];\n if (bProvidedDate)\n {\n //If they've entered a day, we need to make \n //sure to find it. We'll reset the \n //leadtime a few lines down.\n paramHash["leadtime"] = [-10000, 10000];\n }\n }\n var matchedDate = now;\n if (bProvidedDate)\n {\n var leadTimeLowerBound = new Date().getMidnight().addDays(paramHash["leadtime"][0]);\n var leadTimeUpperBound = new Date().getMidnight().addDays(paramHash["leadtime"][1]);\n matchedDate = findDateForReminder(paramHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound); \n }\n\n var arr = findTiddlersWithReminders(matchedDate, leadtime, paramHash["tag"], paramHash["limit"]);\n var elem = createTiddlyElement(place,"span",null,null, null);\n var mess = "";\n if (arr.length == 0)\n {\n mess += config.macros.reminders.emtpyShowRemindersString; \n }\n for (var j = 0; j < arr.length; j++)\n {\n if (paramHash["format"] != null)\n {\n arr[j]["params"]["format"] = paramHash["format"];\n }\n else\n {\n arr[j]["params"]["format"] = config.macros.reminders["defaultShowReminderMessage"];\n }\n mess += getReminderMessageForDisplay(arr[j]["diff"], arr[j]["params"], arr[j]["matchedDate"], arr[j]["tiddler"]);\n mess += "\sn";\n }\n wikify(mess, elem, null, null);\n};\n\n\nconfig.macros.displayTiddlersWithReminders.handler = function displayTiddlersWithReminders(place,macroName,params)\n{\n var now = new Date().getMidnight();\n var paramHash = {};\n var leadtime = [0,14];\n paramHash = getParamsForReminder(params);\n var bProvidedDate = (paramHash["year"] != null) || \n (paramHash["month"] != null) || \n (paramHash["day"] != null) || \n (paramHash["dayofweek"] != null);\n if (paramHash["leadtime"] != null)\n {\n leadtime = paramHash["leadtime"];\n if (bProvidedDate)\n {\n //If they've entered a day, we need to make \n //sure to find it. We'll reset the leadtime \n //a few lines down.\n paramHash["leadtime"] = [-10000,10000];\n }\n }\n var matchedDate = now;\n if (bProvidedDate)\n {\n var leadTimeLowerBound = new Date().getMidnight().addDays(paramHash["leadtime"][0]);\n var leadTimeUpperBound = new Date().getMidnight().addDays(paramHash["leadtime"][1]);\n matchedDate = findDateForReminder(paramHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound); \n }\n var arr = findTiddlersWithReminders(matchedDate, leadtime, paramHash["tag"], paramHash["limit"]);\n for (var j = 0; j < arr.length; j++)\n {\n displayTiddler(null, arr[j]["tiddler"], 0, null, false, false, false);\n }\n};\n\nconfig.macros.reminder.handler = function reminder(place,macroName,params)\n{\n var dateHash = getParamsForReminder(params);\n if (dateHash["hidden"] != null)\n {\n return;\n }\n var leadTime = dateHash["leadtime"];\n if (leadTime == null)\n {\n leadTime = config.macros.reminders["defaultLeadTime"]; \n }\n var leadTimeLowerBound = new Date().getMidnight().addDays(leadTime[0]);\n var leadTimeUpperBound = new Date().getMidnight().addDays(leadTime[1]);\n var matchedDate = findDateForReminder(dateHash, new Date().getMidnight(), leadTimeLowerBound, leadTimeUpperBound);\n if (!window.story) \n {\n window.story=window; \n }\n if (!store.getTiddler) \n {\n store.getTiddler=function(title) {return this.tiddlers[title];};\n }\n var title = window.story.findContainingTiddler(place).id.substr(7);\n if (matchedDate != null)\n {\n var diff = matchedDate.getDifferenceInDays(new Date().getMidnight());\n var elem = createTiddlyElement(place,"span",null,null, null);\n var mess = getReminderMessageForDisplay(diff, dateHash, matchedDate, title);\n wikify(mess, elem, null, null);\n }\n else\n {\n createTiddlyElement(place,"span",null,null, config.macros.reminders["noReminderFound"].replace("TITLE", dateHash["title"]).replace("LEADTIMEUPPER", leadTime[1]).replace("LEADTIMELOWER", leadTime[0]).replace("TIDDLERNAME", title).replace("TIDDLER", "[[" + title + "]]") );\n }\n};\n\nconfig.macros.newReminder.handler = function newReminder(place,macroName,params)\n{\n var today=new Date().getMidnight();\n var formstring = '<html><form>Year: <select name="year"><option value="">Every year</option>';\n for (var i = 0; i < 5; i++)\n {\n formstring += '<option' + ((i == 0) ? ' selected' : '') + ' value="' + (today.getFullYear() +i) + '">' + (today.getFullYear() + i) + '</option>';\n }\n formstring += '</select>&nbsp;&nbsp;Month:<select name="month"><option value="">Every month</option>';\n for (i = 0; i < 12; i++)\n {\n formstring += '<option' + ((i == today.getMonth()) ? ' selected' : '') + ' value="' + (i+1) + '">' + config.messages.dates.months[i] + '</option>';\n }\n formstring += '</select>&nbsp;&nbsp;Day:<select name="day"><option value="">Every day</option>';\n for (i = 1; i < 32; i++)\n {\n formstring += '<option' + ((i == (today.getDate() )) ? ' selected' : '') + ' value="' + i + '">' + i + '</option>';\n }\n\nformstring += '</select>&nbsp;&nbsp;Reminder Title:<input type="text" size="40" name="title" value="please enter a title" onfocus="this.select();"><input type="button" value="ok" onclick="addReminderToTiddler(this.form)"></form></html>';\n\n var panel = config.macros.slider.createSlider(place,null,"New Reminder","Open a form to add a new reminder to this tiddler");\n wikify(formstring ,panel,null,store.getTiddler(params[1]));\n};\n\n// onclick: process input and insert reminder at 'marker'\nwindow.addReminderToTiddler = function(form) {\n if (!window.story) \n {\n window.story=window; \n }\n if (!store.getTiddler) \n {\n store.getTiddler=function(title) {return this.tiddlers[title];};\n }\n var title = window.story.findContainingTiddler(form).id.substr(7);\n var tiddler=store.getTiddler(title);\n var txt='\sn<<reminder ';\n if (form.year.value != "")\n txt += 'year:'+form.year.value + ' ';\n if (form.month.value != "")\n txt += 'month:'+form.month.value + ' ';\n if (form.day.value != "")\n txt += 'day:'+form.day.value + ' ';\n txt += 'title:"'+form.title.value+'" ';\n txt +='>>';\n tiddler.set(null,tiddler.text + txt);\n window.story.refreshTiddler(title,1,true);\n store.setDirty(true);\n};\n\nfunction hasTag(tiddlerTags, tagFilters)\n{\n //Make sure we respond well to empty tiddlerTaglists or tagFilterlists\n if (tagFilters.length==0 || tiddlerTags.length==0)\n {\n return true;\n }\n\n var bHasTag = false;\n \n /*bNoPos says: "'till now there has been no check using a positive filter"\n Imagine a filterlist consisting of 1 negative filter:\n If the filter isn't matched, we want hasTag to be true.\n Yet bHasTag is still false ('cause only positive filters cause bHasTag to change)\n \n If no positive filters are present bNoPos is true, and no negative filters are matched so we have not returned false\n Thus: hasTag returns true.\n \n If at any time a positive filter is encountered, we want at least one of the tags to match it, so we turn bNoPos to false, which\n means bHasTag must be true for hasTag to return true*/\n var bNoPos=true;\n \nfor (var t3 = 0; t3 < tagFilters.length; t3++)\n {\n for(var t2=0; t2<tiddlerTags.length; t2++)\n {\n if (tagFilters[t3].length > 1 && tagFilters[t3].charAt(0) == '!') \n {\n if (tiddlerTags[t2] == tagFilters[t3].substring(1))\n {\n //If at any time a negative filter is matched, we return false\n return false;\n }\n }\n else \n {\n if (bNoPos)\n {\n //We encountered the first positive filter\n bNoPos=false;\n }\n if (tiddlerTags[t2] == tagFilters[t3])\n {\n //A positive filter is matched. As long as no negative filter is matched, hasTag will return true\n bHasTag=true;\n }\n }\n }\n }\n return (bNoPos || bHasTag);\n};\n\n//This function searches all tiddlers for the reminder //macro. It is intended that other plugins (like //calendar) will use this function to query for \n//upcoming reminders.\n//The arguments to this function filter out reminders //based on when they will fire.\n//\n//ARGUMENTS:\n//baseDate is the date that is used as "now". \n//leadtime is a two element int array, with leadtime[0] \n// as the lower bound and leadtime[1] as the\n// upper bound. A reasonable default is [0,14]\n//tags is a space-separated list of tags to use to filter \n// tiddlers. If a tag name begins with an !, then \n// only tiddlers which do not have that tag will \n// be considered. For example "examples holidays" \n// will search for reminders in any tiddlers that \n// are tagged with examples or holidays and \n// "!examples !holidays" will search for reminders \n// in any tiddlers that are not tagged with \n// examples or holidays. Pass in null to search \n// all tiddlers.\n//limit. If limit is null, individual reminders can \n// override the leadtime specified earlier. \n// Pass in 1 in order to override that behavior.\n\nwindow.findTiddlersWithReminders = function findTiddlersWithReminders(baseDate, leadtime, tags, limit)\n{\n//function(searchRegExp,sortField,excludeTag)\n// var macroPattern = "<<([^>\s\s]+)(?:\s\s*)([^>]*)>>";\n var macroPattern = "<<(reminder)(.*)>>";\n var macroRegExp = new RegExp(macroPattern,"mg");\n var matches = store.search(macroRegExp,"title","");\n var arr = [];\n var tagsArray = null;\n if (tags != null)\n {\n tagsArray = tags.split(" ");\n }\n for(var t=matches.length-1; t>=0; t--)\n {\n if (tagsArray != null)\n {\n //If they specified tags to filter on, and this tiddler doesn't \n //match, skip it entirely.\n if ( ! hasTag(matches[t].tags, tagsArray))\n {\n continue;\n }\n }\n\n var targetText = matches[t].text;\n do {\n // Get the next formatting match\n var formatMatch = macroRegExp.exec(targetText);\n if(formatMatch && formatMatch[1] != null && formatMatch[1].toLowerCase() == "reminder")\n {\n //Find the matching date.\n \n var params = formatMatch[2] != null ? formatMatch[2].readMacroParams() : {};\n var dateHash = getParamsForReminder(params);\n if (limit != null || dateHash["leadtime"] == null)\n {\n if (leadtime == null)\n dateHash["leadtime"] = leadtime;\n else\n {\n dateHash["leadtime"] = [];\n dateHash["leadtime"][0] = leadtime[0];\n dateHash["leadtime"][1] = leadtime[1];\n }\n }\n if (dateHash["leadtime"] == null)\n dateHash["leadtime"] = config.macros.reminders["defaultLeadTime"]; \n var leadTimeLowerBound = baseDate.addDays(dateHash["leadtime"][0]);\n var leadTimeUpperBound = baseDate.addDays(dateHash["leadtime"][1]);\n var matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);\n while (matchedDate != null)\n {\n var hash = {};\n hash["diff"] = matchedDate.getDifferenceInDays(baseDate);\n hash["matchedDate"] = new Date(matchedDate.getFullYear(), matchedDate.getMonth(), matchedDate.getDate(), 0, 0);\n hash["params"] = cloneParams(dateHash);\n hash["tiddler"] = matches[t].title;\n hash["tags"] = matches[t].tags;\n arr.pushUnique(hash);\n if (dateHash["recurdays"] != null || (dateHash["year"] == null))\n {\n leadTimeLowerBound = leadTimeLowerBound.addDays(matchedDate.getDifferenceInDays(leadTimeLowerBound)+ 1);\n matchedDate = findDateForReminder(dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound);\n }\n else matchedDate = null;\n }\n }\n }while(formatMatch);\n }\n if(arr.length > 1) //Sort the array by number of days remaining.\n {\n arr.sort(function (a,b) {if(a["diff"] == b["diff"]) {return(0);} else {return (a["diff"] < b["diff"]) ? -1 : +1; } });\n }\n return arr;\n};\n\n//This function takes the reminder macro parameters and\n//generates the string that is used for display.\n//This function is not intended to be called by \n//other plugins.\n window.getReminderMessageForDisplay= function getReminderMessageForDisplay(diff, params, matchedDate, tiddlerTitle)\n{\n var anniversaryString = "";\n var reminderTitle = params["title"];\n if (reminderTitle == null)\n {\n reminderTitle = config.macros.reminders["untitledReminder"];\n }\n if (params["firstyear"] != null)\n {\n anniversaryString = config.macros.reminders["defaultAnniversaryMessage"].replace("DIFF", (matchedDate.getFullYear() - params["firstyear"]));\n }\n var mess = "";\n var diffString = "";\n if (diff == 0)\n {\n diffString = config.macros.reminders["todayString"];\n }\n else if (diff == 1)\n {\n diffString = config.macros.reminders["tomorrowString"];\n }\n else\n {\n diffString = config.macros.reminders["ndaysString"].replace("DIFF", diff);\n }\n var format = config.macros.reminders["defaultReminderMessage"];\n if (params["format"] != null)\n {\n format = params["format"];\n }\n mess = format;\n//HACK! -- Avoid replacing DD in TIDDLER with the date\n mess = mess.replace(/TIDDLER/g, "TIDELER");\n mess = matchedDate.formatStringDateOnly(mess);\n mess = mess.replace(/TIDELER/g, "TIDDLER");\n if (tiddlerTitle != null)\n {\n mess = mess.replace(/TIDDLERNAME/g, tiddlerTitle);\n mess = mess.replace(/TIDDLER/g, "[[" + tiddlerTitle + "]]");\n }\n \n mess = mess.replace("DIFF", diffString).replace("TITLE", reminderTitle).replace("DATE", matchedDate.formatString("DDD MMM DD, YYYY")).replace("ANNIVERSARY", anniversaryString);\n return mess;\n};\n\n// Parse out the macro parameters into a hashtable. This\n// handles the arguments for reminder, showReminders and \n// displayTiddlersWithReminders.\nwindow.getParamsForReminder = function getParamsForReminder(params)\n{\n var dateHash = {};\n var type = "";\n var num = 0;\n var title = "";\n for(var t=0; t<params.length; t++)\n {\n var split = params[t].split(":");\n type = split[0].toLowerCase();\n var value = split[1];\n for (var i=2; i < split.length; i++)\n {\n value += ":" + split[i];\n }\n if (type == "nolinks" || type == "limit" || type == "hidden")\n {\n num = 1;\n }\n else if (type == "leadtime")\n {\n var leads = value.split("...");\n if (leads.length == 1)\n {\n leads[1]= leads[0];\n leads[0] = 0;\n }\n leads[0] = parseInt(leads[0], 10);\n leads[1] = parseInt(leads[1], 10);\n num = leads;\n }\n else if (type == "offsetdayofweek")\n {\n if (value.substr(0,1) == "-")\n {\n dateHash["negativeOffsetDayOfWeek"] = 1;\n value = value.substr(1);\n }\n num = parseInt(value, 10);\n }\n else if (type != "title" && type != "tag" && type != "format")\n {\n num = parseInt(value, 10);\n }\n else\n {\n title = value;\n t++;\n while (title.substr(0,1) == '"' && title.substr(title.length - 1,1) != '"' && params[t] != undefined)\n {\n title += " " + params[t++];\n }\n //Trim off the leading and trailing quotes\n if (title.substr(0,1) == "\s"" && title.substr(title.length - 1,1)== "\s"")\n {\n title = title.substr(1, title.length - 2);\n t--;\n }\n num = title;\n }\n dateHash[type] = num;\n }\n //date is synonymous with day\n if (dateHash["day"] == null)\n {\n dateHash["day"] = dateHash["date"];\n }\n return dateHash;\n};\n\n//This function finds the date specified in the reminder \n//parameters. It will return null if no match can be\n//found. This function is not intended to be used by\n//other plugins.\nwindow.findDateForReminder= function findDateForReminder( dateHash, baseDate, leadTimeLowerBound, leadTimeUpperBound)\n{\n if (baseDate == null)\n {\n baseDate = new Date().getMidnight();\n }\n var hashKey = baseDate.convertToYYYYMMDDHHMM();\n for (var k in dateHash)\n {\n hashKey += "," + k + "|" + dateHash[k];\n }\n hashKey += "," + leadTimeLowerBound.convertToYYYYMMDDHHMM();\n hashKey += "," + leadTimeUpperBound.convertToYYYYMMDDHHMM();\n if (reminderCache[hashKey] == null)\n {\n //If we don't find a match in this run, then we will\n //cache that the reminder can't be matched.\n reminderCache[hashKey] = false;\n }\n else if (reminderCache[hashKey] == false)\n {\n //We've already tried this date and failed\n return null;\n }\n else\n {\n return reminderCache[hashKey];\n }\n \n var bOffsetSpecified = dateHash["offsetyear"] != null || \n dateHash["offsetmonth"] != null || \n dateHash["offsetday"] != null || \n dateHash["offsetdayofweek"] != null || \n dateHash["recurdays"] != null;\n \n // If we are matching the base date for a dayofweek offset, look for the base date a \n //little further back.\n var tmp1leadTimeLowerBound = leadTimeLowerBound; \n if ( dateHash["offsetdayofweek"] != null)\n {\n tmp1leadTimeLowerBound = leadTimeLowerBound.addDays(-6); \n }\n var matchedDate = baseDate.findMatch(dateHash, tmp1leadTimeLowerBound, leadTimeUpperBound);\n if (matchedDate != null)\n {\n var newMatchedDate = matchedDate;\n if (dateHash["recurdays"] != null)\n {\n while (newMatchedDate.getTime() < leadTimeLowerBound.getTime())\n {\n newMatchedDate = newMatchedDate.addDays(dateHash["recurdays"]);\n }\n }\n else if (dateHash["offsetyear"] != null || \n dateHash["offsetmonth"] != null || \n dateHash["offsetday"] != null || \n dateHash["offsetdayofweek"] != null)\n {\n var tmpdateHash = cloneParams(dateHash);\n tmpdateHash["year"] = dateHash["offsetyear"];\n tmpdateHash["month"] = dateHash["offsetmonth"];\n tmpdateHash["day"] = dateHash["offsetday"];\n tmpdateHash["dayofweek"] = dateHash["offsetdayofweek"];\n var tmpleadTimeLowerBound = leadTimeLowerBound;\n var tmpleadTimeUpperBound = leadTimeUpperBound;\n if (tmpdateHash["offsetdayofweek"] != null)\n {\n if (tmpdateHash["negativeOffsetDayOfWeek"] == 1)\n {\n tmpleadTimeLowerBound = matchedDate.addDays(-6);\n tmpleadTimeUpperBound = matchedDate;\n\n }\n else\n {\n tmpleadTimeLowerBound = matchedDate;\n tmpleadTimeUpperBound = matchedDate.addDays(6);\n }\n\n }\n newMatchedDate = matchedDate.findMatch(tmpdateHash, tmpleadTimeLowerBound, tmpleadTimeUpperBound);\n //The offset couldn't be matched. return null.\n if (newMatchedDate == null)\n {\n return null;\n }\n }\n if (newMatchedDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))\n {\n reminderCache[hashKey] = newMatchedDate;\n return newMatchedDate;\n }\n }\n return null;\n};\n\n//This does much the same job as findDateForReminder, but\n//this one doesn't deal with offsets or recurring \n//reminders.\nDate.prototype.findMatch = function findMatch(dateHash, leadTimeLowerBound, leadTimeUpperBound)\n{\n\n var bSpecifiedYear = (dateHash["year"] != null);\n var bSpecifiedMonth = (dateHash["month"] != null);\n var bSpecifiedDay = (dateHash["day"] != null);\n var bSpecifiedDayOfWeek = (dateHash["dayofweek"] != null);\n if (bSpecifiedYear && bSpecifiedMonth && bSpecifiedDay)\n {\n return new Date(dateHash["year"], dateHash["month"]-1, dateHash["day"], 0, 0);\n }\n var bMatchedYear = !bSpecifiedYear;\n var bMatchedMonth = !bSpecifiedMonth;\n var bMatchedDay = !bSpecifiedDay;\n var bMatchedDayOfWeek = !bSpecifiedDayOfWeek;\n if (bSpecifiedDay && bSpecifiedMonth && !bSpecifiedYear && !bSpecifiedDayOfWeek)\n {\n\n //Shortcut -- First try this year. If it's too small, try next year.\n var tmpMidnight = this.getMidnight();\n var tmpDate = new Date(this.getFullYear(), dateHash["month"]-1, dateHash["day"], 0,0);\n if (tmpDate.getTime() < leadTimeLowerBound.getTime())\n {\n tmpDate = new Date((this.getFullYear() + 1), dateHash["month"]-1, dateHash["day"], 0,0);\n }\n if ( tmpDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))\n {\n return tmpDate;\n }\n else\n {\n return null;\n }\n }\n\n var newDate = leadTimeLowerBound; \n while (newDate.isBetween(leadTimeLowerBound, leadTimeUpperBound))\n {\n var tmp = testDate(newDate, dateHash, bSpecifiedYear, bSpecifiedMonth, bSpecifiedDay, bSpecifiedDayOfWeek);\n if (tmp != null)\n return tmp;\n newDate = newDate.addDays(1);\n }\n};\n\nfunction testDate(testMe, dateHash, bSpecifiedYear, bSpecifiedMonth, bSpecifiedDay, bSpecifiedDayOfWeek)\n{\n var bMatchedYear = !bSpecifiedYear;\n var bMatchedMonth = !bSpecifiedMonth;\n var bMatchedDay = !bSpecifiedDay;\n var bMatchedDayOfWeek = !bSpecifiedDayOfWeek;\n if (bSpecifiedYear)\n {\n bMatchedYear = (dateHash["year"] == testMe.getFullYear());\n }\n if (bSpecifiedMonth)\n {\n bMatchedMonth = ((dateHash["month"] - 1) == testMe.getMonth() );\n }\n if (bSpecifiedDay)\n {\n bMatchedDay = (dateHash["day"] == testMe.getDate());\n }\n if (bSpecifiedDayOfWeek)\n {\n bMatchedDayOfWeek = (dateHash["dayofweek"] == testMe.getDay());\n }\n\n if (bMatchedYear && bMatchedMonth && bMatchedDay && bMatchedDayOfWeek)\n {\n return testMe;\n }\n};\n\n//Returns true if the date is in between two given dates\nDate.prototype.isBetween = function isBetween(lowerBound, upperBound)\n{\n return (this.getTime() >= lowerBound.getTime() && this.getTime() <= upperBound.getTime());\n}\n//Return a new date, with the time set to midnight (0000)\nDate.prototype.getMidnight = function getMidnight()\n{\n return new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0);\n};\n// Add the specified number of days to a date.\nDate.prototype.addDays = function addDays(numberOfDays)\n{\n return new Date(this.getFullYear(), this.getMonth(), this.getDate() + numberOfDays, 0, 0);\n};\n//Return the number of days between two dates.\nDate.prototype.getDifferenceInDays = function getDifferenceInDays(otherDate)\n{\n//I have to do it this way, because this way ignores daylight savings\n var tmpDate = this.addDays(0);\n if (this.getTime() > otherDate.getTime())\n {\n var i = 0;\n for (i = 0; tmpDate.getTime() > otherDate.getTime(); i++)\n {\n tmpDate = tmpDate.addDays(-1);\n }\n return i;\n }\n else\n {\n var i = 0;\n for (i = 0; tmpDate.getTime() < otherDate.getTime(); i++)\n {\n tmpDate = tmpDate.addDays(1);\n }\n return i * -1;\n }\n return 0;\n};\nfunction cloneParams(what) {\n var tmp = {};\n for (var i in what) {\n tmp[i] = what[i];\n }\n return tmp;\n}\n// Substitute date components into a string\nDate.prototype.formatStringDateOnly = function formatStringDateOnly(template)\n{\n template = template.replace("YYYY",this.getFullYear());\n template = template.replace("YY",String.zeroPad(this.getFullYear()-2000,2));\n template = template.replace("MMM",config.messages.dates.months[this.getMonth()]);\n template = template.replace("0MM",String.zeroPad(this.getMonth()+1,2));\n template = template.replace("MM",this.getMonth()+1);\n template = template.replace("DDD",config.messages.dates.days[this.getDay()]);\n template = template.replace("0DD",String.zeroPad(this.getDate(),2));\n template = template.replace("DD",this.getDate());\n return template;\n};\n\n//}}}
<<showReminders leadtime:30 format:"DIFF: TITLE on DATE ANNIVERSARY">>
The [[Graphics|http://groups.melbpc.org.au/~graphics/homepage.html]] and [[Animation|http://groups.melbpc.org.au/~animate/]] ~SIGs meet once a month in Melbourne, Australia.\n\nSome graphics topics I may be mentioning:\n<<tagging graphics>>
config.options.txtUserName = "MylesStrous";
[[WelcomeToTiddlyspot]]\n[[GettingStarted]]\n[[MainMenu]]\n[[Control panel|http://myles.tiddlyspot.com/controlpanel]]\n\n\n[[Tiddlywiki formatting details|http://www.blogjones.com/TiddlyWikiTutorial.html#HowToFormatText]]
the online notebook of a hobbyist cartoon animator
[[Myles's|MylesStrous]] TiddlyWiki
* [[Autodesk Sketchbook Pro|http://usa.autodesk.com/adsk/servlet/index?id=6848332&siteID=123112]]\n* [[Corel Painter Sketch Pad|http://www.corel.com/servlet/Satellite/au/en/Product/1231253537915]]\n* Paint software\n** [[Artrage|http://www.artrage.com/]]\n** [[Smoothdraw|http://www.smoothdraw.com/]]\n** [[OpenCanvas|http://www.portalgraphics.net/en/]]\n** [[Manga Studio|http://my.smithmicro.com/mac/manga/index.html]]\n** [[PaintTool SAI|http://www.systemax.jp/en/sai/]]\n** [[Artweaver|http://www.artweaver.de/index.php?en_version]]\n
/***\n|''Name:''|SlideShowPlugin|\n|''Description:''|Creates a slide show from a group of tiddlers|\n|''Author:''|Paulo Soares|\n|''Contributors:''|John P. Rouillard|\n|''Version:''|2.1.2|\n|''Date:''|2009-11-29|\n|''Source:''|http://www.math.ist.utl.pt/~psoares/addons.html|\n|''Documentation:''|[[SlideShowPlugin Documentation|SlideShowPluginDoc]]|\n|''License:''|[[Creative Commons Attribution-Share Alike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|\n|''~CoreVersion:''|2.5.0|\n***/\n//{{{\nif(!version.extensions.SlideShowPlugin) { //# ensure that the plugin is only installed once\nversion.extensions.SlideShowPlugin = {installed: true};\n\n(function($) {\nconfig.macros.slideShow = {maxTOCLength: 30};\n\nconfig.macros.slideShow.text = {\n label: "slide show", tooltip: "Start slide show",\n quit: {label: "x", tooltip: "Quit the slide show"},\n firstSlide: {label: "<<", tooltip: "Go to first slide"},\n previous: {label: "<", tooltip: "Go back"},\n next: {label: ">", tooltip: "Advance"},\n lastSlide: {label: ">>", tooltip: "Go to last slide"},\n goto: {label: "Go to slide:"},\n resetClock: {tooltip: "Reset the clock"},\n overlay: "overlay"\n};\n\nconfig.macros.slideShow.handler = function(place,macroName,params,wikifier,paramString){\n var args = paramString.parseParams(null,null,false);\n this.label = getParam(args,"label",this.text.label);\n this.tooltip = getParam(args,"tooltip",this.text.tooltip);\n var onclick = function(){config.macros.slideShow.onClick(place,paramString); return false;};\n createTiddlyButton(place,this.label,this.tooltip,onclick);\n return false;\n}\n\nconfig.macros.slideShow.onClick = function(place,paramString) {\n var slide, cm = config.macros.slideShow;\n var title = story.findContainingTiddler(place);\n title = title ? title.getAttribute("tiddler") : null;\n var args = paramString.parseParams(null,null,false);\n title = getParam(args,"tiddler",title);\n var argsArray = paramString.readMacroParams();\n this.clicks = ($.inArray('noClicks',argsArray) < 0);\n this.keyboard = ($.inArray('noKeyboard',argsArray) < 0);\n this.showAll = ($.inArray('showAll',argsArray) > -1);\n this.cycle = ($.inArray('cycle',argsArray) > -1);\n this.overlays = ($.inArray('noOverlays',argsArray) < 0);\n this.theme = getParam(args,"theme");\n this.tag = getParam(args,"tag");\n this.toc = getParam(args,"toc","headers");\n this.sort = getParam(args,"sort");\n this.clockFormat = getParam(args,"clockFormat",'0hh:0mm:0ss');\n this.auto = getParam(args,"auto",0);\n this.header = getParam(args,"header",title);\n this.footer = getParam(args,"footer","");\n this.clock = getParam(args,"clock");\n this.blocked = 0;\n if(this.clock){\n var startTime = new Date(0);\n this.clockCorrection=startTime.getTimezoneOffset()*60000;\n startTime = new Date();\n this.clockMultiplier = 1;\n this.clockInterval = 0;\n var clockType= parseFloat(this.clock);\n if(clockType < 0) {\n this.clockMultiplier = -1;\n this.clockInterval = -clockType*60000;\n } else if(clockType == 0){\n this.clockCorrection = 0;\n startTime = new Date(0);\n }\n this.clockStartTime=startTime.getTime();\n }\n this.slides = [];\n this.openTiddlers = [];\n $("#tiddlerDisplay > *").each(function(){cm.openTiddlers.push($(this).attr('tiddler'))});\n var count = 0;\n this.slideTOC=[];\n if(this.tag){\n var content = store.getTaggedTiddlers(this.tag,this.sort);\n $.each(content, function(){\n count++;\n cm.buildTOC(count,this.title);\n cm.slides.push(this.title);\n });\n } else {\n story.displayTiddler(null,title);\n var list = $('[tiddler='+title+']'+" .tiddlyLinkExisting");\n $.each(list,function(){\n if(!$(this).parents().hasClass("exclude")){\n slide = $(this).attr('tiddlylink');\n count++;\n cm.buildTOC(count,slide);\n cm.slides.push(slide);\n }\n });\n }\n this.nSlides = this.slides.length;\n if(this.nSlides==0) return false;\n clearMessage();\n this.toggleSlideStyles();\n if(!this.showAll){\n //Attach the key and mouse listeners\n if(this.keyboard && !$("#tiddlerDisplay").hasClass("noKeyboard")) $(document).keyup(cm.keys);\n if(this.clicks){\n $("#displayArea").mouseup(cm.clicker);\n document.oncontextmenu = function(){return false;}\n }\n if(this.clock) this.slideClock=setInterval(this.setClock, 1000);\n if(this.auto>0){\n this.autoAdvance=setInterval(cm.next, this.auto*1000);\n }\n this.showSlide(1);\n } else {\n story.closeAllTiddlers();\n story.displayTiddlers(null,this.slides);\n $(".tiddler").attr("ondblclick",null);\n $(document).keyup(cm.endSlideShow);\n }\n return false;\n}\n\nconfig.macros.slideShow.buildNavigator = function() {\n //Create the navigation bar\n var i, slidefooter = $("#controlBar")[0];\n if(!slidefooter) return;\n $(slidefooter).addClass("slideFooterOff noClicks");\n var navigator = createTiddlyElement(slidefooter,"SPAN","navigator");\n var buttonBar = createTiddlyElement(navigator,"SPAN","buttonBar");\n //Make it so that when the footer is hovered over the class will change to make it visible\n $(slidefooter).bind("mouseenter mouseleave", function(e){$(this).toggleClass("slideFooterOff");});\n //Create the control buttons for the navigation\n \n createTiddlyButton(buttonBar,this.text.firstSlide.label,this.text.firstSlide.tooltip,this.firstSlide,"button");\n createTiddlyButton(buttonBar,this.text.previous.label,this.text.previous.tooltip,this.previous,"button");\n createTiddlyButton(buttonBar,this.text.quit.label,this.text.quit.tooltip,this.endSlideShow,"button");\n createTiddlyButton(buttonBar,this.text.next.label,this.text.next.tooltip,this.next,"button");\n createTiddlyButton(buttonBar,this.text.lastSlide.label,this.text.lastSlide.tooltip,this.lastSlide,"button");\n if(this.clock){\n if(this.clock == 0){\n createTiddlyElement(navigator,"SPAN","slideClock");\n } else {\n createTiddlyButton(navigator," ",this.text.resetClock.tooltip,this.resetClock,"button","slideClock");\n }\n this.setClock();\n }\n var index = createTiddlyElement(slidefooter,"SPAN","slideCounter");\n index.onclick = this.toggleTOC;\n var toc = createTiddlyElement(slidefooter,"SPAN","toc");\n var tocLine;\n for(i=0; i<this.slideTOC.length; i++){\n $(toc).append(this.slideTOC[i][2]);\n tocLine = toc.lastChild;\n $(tocLine).addClass("tocLevel"+this.slideTOC[i][1]).css("cursor", "pointer").hover(function () {\n $(this).addClass("highlight");}, function () {\n $(this).removeClass("highlight");});\n tocLine.setAttribute("slide",this.slideTOC[i][0]);\n $(tocLine).click(config.macros.slideShow.showSlideFromTOC);\n }\n //Input box to jump to specific slide\n var tocItem = createTiddlyElement(toc,"DIV","jumpItem",null,this.text.goto.label);\n var tocJumpInput = createTiddlyElement(tocItem,"INPUT","jumpInput");\n tocJumpInput.type="text";\n $(tocJumpInput).keyup(config.macros.slideShow.jumpToSlide);\n}\n\n//Used to shorten the TOC fields\nconfig.macros.slideShow.abbreviate = function(label){\n if(label.length>this.maxTOCLength) {\n var temp = new Array();\n temp = label.split(' ');\n label = temp[0];\n for(var j=1; j<temp.length; j++){\n if((label.length+temp[j].length)<=this.maxTOCLength){\n label += " " + temp[j];\n } else {\n label += " ...";\n break;\n }\n }\n }\n return label;\n}\n\nconfig.macros.slideShow.buildTOC = function(count,title) {\n var level = 1, text;\n switch(this.toc){\n case "headers":\n var frag = wikifyStatic(store.getTiddlerText(title));\n text = frag.replace(/<div class="comment">.*<\s/div>/mg,"");\n var matches = text.match(/<h[123456]>.*?<\s/h[123456]>/mgi);\n if(matches){\n for (var j=0; j<matches.length; j++){\n level = matches[j].charAt(2);\n text = matches[j].replace(/<\s/?h[123456]>/gi,"");\n text = this.abbreviate(text);\n this.slideTOC.push([count,level,"<div>("+count+") "+text+"</div>"]);\n }\n }\n break;\n case "titles":\n text = this.abbreviate(title);\n this.slideTOC.push([count,level,"<div>("+count+") "+text+"</div>"]);\n }\n}\n\nconfig.macros.slideShow.showSlideFromTOC = function(e) {\n var cm = config.macros.slideShow;\n var slide = parseInt(e.target.getAttribute('slide'));\n $("#toc").hide();\n cm.showSlide(slide);\n return false;\n}\n\nconfig.macros.slideShow.toggleTOC = function(){\n $("#toc").toggle();\n $("#jumpInput").focus().val('');\n return false;\n}\n\nconfig.macros.slideShow.isInteger = function(s){\n for (var i = 0; i < s.length; i++){\n // Check that current character is number\n var c = s.charAt(i);\n if (((c < "0") || (c > "9"))) return false;\n }\n // All characters are numbers\n return true;\n}\n\nconfig.macros.slideShow.jumpToSlide = function(e){\n var cm = config.macros.slideShow;\n if(e.which==13){\n var input= $("#jumpInput").val();\n if(cm.isInteger(input) && input>0 && input<=cm.nSlides){\n $("#toc").hide();\n cm.showSlide(input);\n } else {$("#jumpInput").val('');}\n }\n return false;\n}\n\nconfig.macros.slideShow.toggleSlideStyles = function(){\n var contentWrapper = $('#contentWrapper');\n if(contentWrapper.hasClass("slideShowMode")){\n refreshPageTemplate();\n removeStyleSheet("SlideShowStyleSheet");\n if(this.theme) removeStyleSheet(this.theme);\n } else {\n $("#displayArea").prepend('<div id="slideBlanker" style="display:none"></div><div id="slideHeader">'+this.header+'</div><div id="slideFooter">'+this.footer+'</div><div id="controlBar"></div>');\n setStylesheet(store.getRecursiveTiddlerText("SlideShowStyleSheet"),"SlideShowStyleSheet");\n if(this.theme && store.tiddlerExists(this.theme)){setStylesheet(store.getRecursiveTiddlerText(this.theme),this.theme);}\n this.buildNavigator();\n }\n contentWrapper.toggleClass("slideShowMode");\n return false;\n}\n\nconfig.macros.slideShow.showSlide = function(n){\n if(this.cycle) {\n if(n>this.nSlides) {\n n = 1;\n } else if(n<1) {\n n = this.nSlides;\n }\n } else {\n if(n>this.nSlides || n<1) return;\n }\n story.closeAllTiddlers();\n if(this.clock=='-'){this.resetClock();}\n story.displayTiddler(null,String(this.slides[n-1]));\n $(".tiddler").attr("ondblclick",null);\n this.curSlide = n;\n $("#slideCounter").text(this.curSlide+"/"+this.nSlides);\n if(this.overlays){\n var contents = $(".viewer *");\n this.numOverlays = 1;\n while(1){\n if(contents.hasClass(this.text.overlay+this.numOverlays)){\n this.numOverlays++;\n } else {break;}\n }\n this.numOverlays--;\n this.showOverlay(0);\n }\n return false;\n}\n\nconfig.macros.slideShow.showOverlay = function(n){\n var i, set, cm = config.macros.slideShow;\n if(!cm.overlays || cm.numOverlays == 0 || n<0 || n>cm.numOverlays){return;}\n for(i=1; i<n; i++){\n set = $(".viewer "+"."+cm.text.overlay+i);\n set.removeClass("currentOverlay nextOverlay");\n set.addClass("previousOverlay");\n }\n set = $(".viewer "+"."+cm.text.overlay+n);\n set.removeClass("previousOverlay nextOverlay");\n set.addClass("currentOverlay");\n for(i=n; i<config.macros.slideShow.numOverlays; i++){\n set = $(".viewer "+"."+cm.text.overlay+(i+1));\n set.removeClass("previousOverlay currentOverlay");\n set.addClass("nextOverlay");\n }\n cm.curOverlay = n;\n}\n\nconfig.macros.slideShow.firstSlide = function(){\n config.macros.slideShow.showSlide(1);\n return false;\n}\n\nconfig.macros.slideShow.lastSlide = function(){\n config.macros.slideShow.showSlide(config.macros.slideShow.nSlides);\n return false;\n}\n\nconfig.macros.slideShow.next = function(){\n var cm = config.macros.slideShow;\n if(!cm.overlays || cm.numOverlays == 0 || cm.curOverlay == cm.numOverlays) {\n cm.showSlide(cm.curSlide+1);\n } else {\n cm.showOverlay(cm.curOverlay+1);\n }\n return false;\n}\n\nconfig.macros.slideShow.previous = function(){\n var cm = config.macros.slideShow;\n if(!cm.overlays || cm.numOverlays == 0 || cm.curOverlay == 0) {\n cm.showSlide(cm.curSlide-1);\n cm.showOverlay(cm.numOverlays);\n } else {\n cm.showOverlay(cm.curOverlay-1);\n }\n return false;\n}\n\nconfig.macros.slideShow.endSlideShow=function(){\n var cm = config.macros.slideShow;\n if(cm.autoAdvance) {clearInterval(cm.autoAdvance);}\n if(this.clock) clearInterval(this.slideClock);\n story.closeAllTiddlers();\n cm.toggleSlideStyles();\n story.displayTiddlers(null,cm.openTiddlers);\n $(document).unbind();\n document.oncontextmenu = function(){};\n return false;\n}\n\n// 'keys' code adapted from S5 which in turn was adapted from MozPoint (http://mozpoint.mozdev.org/)\nconfig.macros.slideShow.keys = function(key) {\n var cm = config.macros.slideShow;\n switch(key.which) {\n case 32: // spacebar\n if(cm.auto>0 && cm.blocked==0){\n if(cm.autoAdvance){\n clearInterval(cm.autoAdvance);\n cm.autoAdvance = null;\n } else {\n cm.autoAdvance=setInterval(cm.next, cm.auto*1000);\n }\n } else {\n if(cm.blocked==0) cm.next();\n }\n break;\n case 34: // page down\n if(cm.blocked==0) cm.showSlide(cm.curSlide+1);\n break;\n case 39: // rightkey\n if(cm.blocked==0) cm.next();\n break;\n case 40: // downkey\n if(cm.blocked==0) cm.showOverlay(cm.numOverlays);\n break;\n case 33: // page up\n if(cm.blocked==0) cm.showSlide(cm.curSlide-1);\n break;\n case 37: // leftkey\n if(cm.blocked==0) cm.previous();\n break;\n case 38: // upkey\n if(cm.blocked==0) cm.showOverlay(0);\n break;\n case 36: // home\n if(cm.blocked==0) cm.firstSlide();\n break;\n case 35: // end\n if(cm.blocked==0) cm.lastSlide();\n break;\n case 27: // escape\n cm.endSlideShow();\n break;\n case 66: // B\n $("#slideBlanker").toggle();\n cm.blocked = (cm.blocked +1)%2;\n break;\n }\n return false;\n}\n\nconfig.macros.slideShow.clicker = function(e) {\n var cm = config.macros.slideShow;\n if(cm.blocked==1 || $(e.target).attr('href') || $(e.target).parents().andSelf().hasClass('noClicks')){\n return true;\n }\n if($("#toc").is(':visible')){\n cm.toggleTOC();\n } else {\n if((!e.which && e.button == 1) || e.which == 1) {\n cm.next();\n }\n if((!e.which && e.button == 2) || e.which == 3) {\n cm.previous();\n }\n }\n return false;\n}\n\nconfig.macros.slideShow.setClock = function(){\n var cm = config.macros.slideShow;\n var actualTime = new Date();\n var newTime = actualTime.getTime() - cm.clockStartTime;\n newTime = cm.clockMultiplier*newTime+cm.clockInterval+cm.clockCorrection;\n actualTime.setTime(newTime);\n newTime = actualTime.formatString(cm.clockFormat);\n $("#slideClock").text(newTime);\n return false;\n}\n\nconfig.macros.slideShow.resetClock = function(){\n var cm = config.macros.slideShow;\n if(cm.clock == 0) return;\n var time = new Date(0);\n if(cm.clockStartTime>time){\n var startTime = new Date();\n cm.clockStartTime=startTime.getTime();\n }\n return false;\n}\n\nconfig.shadowTiddlers.SlideShowStyleSheet="/*{{{*/\sn.header, #mainMenu, #sidebar, #backstageButton, #backstageArea, .toolbar, .title, .subtitle, .tagging, .tagged, .tagClear, .comment{\sn display:none !important\sn}\sn\sn#slideBlanker{\sn position: absolute;\sn top: 0;\sn left: 0;\sn width: 100%;\sn height: 100%;\sn z-index: 90; \sn background-color: #000;\sn opacity: 0.9;\sn filter: alpha(opacity=90)\sn}\sn\sn.nextOverlay{\sn visibility: hidden\sn}\sn\sn.previousOverlay,.currentOverlay{\sn visibility: visible\sn}\sn\sn#displayArea{\sn font-size: 250%;\sn margin: 0 !important;\sn padding: 0\sn}\sn\sn#controlBar{\sn position: fixed;\sn bottom: 2px;\sn right: 2px;\sn width: 100%;\sn text-align: right\sn}\sn\sn#controlBar .button{\sn margin: 0 0.25em;\sn padding: 0 0.25em\sn}\sn\sn#slideHeader{\sn font-size: 200%;\sn font-weight: bold\sn}\sn\sn#slideFooter{\sn position: fixed;\sn bottom: 2px\sn}\sn\sn.slideFooterOff #navigator{\sn visibility: hidden\sn}\sn\sn#slideClock{\sn margin: 0 5px 0 5px\sn}\sn\sn#slideCounter{\sn cursor: pointer;\sn color: #aaa\sn}\sn\sn#toc{\sn display: none;\sn position: absolute;\sn font-size: .75em;\sn bottom: 2em;\sn right: 0;\sn background: #fff;\sn border: 1px solid #000;\sn text-align: left\sn}\sn\sn#jumpItem{\sn padding-left:0.25em\sn}\sn\sn#jumpInput{\sn margin-left: 0.25em;\sn width: 3em\sn}\sn\sn.tocLevel1{\sn font-size: .8em\sn}\sn\sn.tocLevel2{\sn margin-left: 1em;\sn font-size: .75em\sn}\sn\sn.tocLevel3{\sn margin-left: 2em;\sn font-size: .7em\sn}\sn\sn.tocLevel4{\sn margin-left: 3em;\sn font-size: .65em\sn}\sn\sn.tocLevel5{\sn margin-left: 4em;\sn font-size: .6em\sn}\sn\sn.tocLevel6{\sn margin-left: 5em;\sn font-size: .55em\sn}\sn/*}}}*/";\n\nconfig.shadowTiddlers.SlideShowPluginDoc="The documentation is available [[here|http://www.math.ist.utl.pt/~psoares/addons.html#SlideShowPluginDoc]].";\n})(jQuery)\n}\n//}}}
!Description\nThis plugin turns a set of tiddlers into a slide show. A single macro provides a flexible way to present a set of tiddlers, including:\n#a full screen presentation that hides the TiddlyWiki structure (header, sidebar, main menu);\n#a way to navigate through a set of tiddlers keeping the TiddlyWiki structure (similar to the [[NavigationMacro|http://tw.lewcid.org/#NavigationMacro]] by Saq Imtiaz);\n#a display of all the selected tiddlers ready to be printed.\n!Main features\nMost features that are usually found in presentation software are available. \n*Build a slide show from a list of tiddlers' titles or selecting a specific tag with optional sort\n*Fully customizable presentations (CSS knowledge required)\n*Auto-advancing slide show (kiosk mode) and looping (circular mode)\n*Incremental display (several overlays or layers in a slide)\n*Optional clock with 4 different modes:\n**local time\n**elapsed time of presentation\n**elapsed time for each slide\n**countdown clock for a given period\n*Screen blanking for pauses\n!Installation\nTo install this plugin copy the tiddler SlideShowPlugin to your TiddlyWiki, label it with the ''systemConfig'' tag, save the TW and refresh the browser.\nOptionally, you can also copy this documentation tiddler. If you don't, a link to the original one in my site is provided. \n!Quick start\nThe simplest way to make a slide show is to create a new tiddler with references to all the tiddlers you want to include in your presentation. Any reference or group of references to tiddlers that should be skipped must be wrapped in a {{{exclude}}} class as in the following example:\n{{{\nMissingSlide\n[[First slide|SlideShowExample-1]] {{exclude{(the tiddler SlideShowPluginDoc will not be included in the presentation)}}}\nSlideShowExample-2\nSlideShowExample-3\nSlideShowExample-4\n}}}\nAdd {{{<<slideShow>>}}} anywhere in the tiddler, close the edit form, click the ''slide show'' button and there you are. Move forward/backward in the presentation with the mouse left/right button. If you move the mouse pointer over the bottom of the browser window you will see a few navigation buttons. Click the ''x'' button or press the ''Escape'' key to exit.\n!Options\n{{{\n<<slideShow\n noClicks\n noKeyboard\n noOverlays\n cycle\n showAll\n tiddler:tiddler\n label:string\n tooltip:string\n tag:tag\n sort:field\n theme:tiddler\n toc:string\n auto:time\n clock:type\n clockFormat:string\n header:string\n footer:string\n>> \n}}}\n''noClicks''\nDisables navigation through mouse clicks on the tiddlers. This is useful when there are lots of clickable elements in the presentation.\nIf you want to keep using the mouse to move around the presentation and there are occasional elements that require mouse clicking, these can be formatted with a {{{noClicks}}} class (read more [[here|http://www.tiddlywiki.com/#CustomCssClass]]).\n\n''noKeyboard''\nDisables keyboard navigation.\n\n''noOverlays''\nDisables the incremental display of overlays.\n\n''cycle''\nThe slide shows runs in a continuous loop. \n\n''showAll''\nTo print a presentation all the selected tiddlers are opened in the specified order. Press any key to return to the default TiddlyWiki layout.\n\n''tiddler''\nUse this option to indicate the tiddler where your slide show is defined when it is not the same one that contains the button that starts the presentation.\n\n''label''\nText to replace the default text in the slide show button.\n\n''tooltip''\nText to be shown when the mouse is over the slide show button.\n\n''tag''\nUse this option to create a slide show with the tiddlers with the provided tag instead of providing a list of tiddlers.\n\n''sort''\nIf the ''tag'' option is used you can use this other option to sort the tiddlers according to a specific field. Valid values are: //created// (default), //modified//, //title// and //text//.\n\n''theme''\nA user provided tiddler containing CSS rules that define the style of the slide show. To redefine the default style, edit the SlideShowStyleSheet shadow tiddler. \n\n''toc''\nChoose the type of table of contents. Possible values: ''titles'' will enable the use of the tiddlers titles, ''headers'' (default) to choose html headers (h1, h2,...), anything else will disable the table of contents. \n\n''auto''\nThe slide show auto advances after the defined number of seconds. The slide show can be paused by pressing the ''space bar''.\n\n''clock''\nDisplays a clock near the navigation buttons at the bottom of the screen. Four types of clocks can be defined:\n#''clock:0'' -- shows the local time.\n#''clock:'+''' -- displays the elapsed time of the presentation. Click the clock display to reset the time.\n#''clock:'-''' -- displays the elapsed for each slide. Click the clock display to reset the time.\n#''clock:-20'' -- displays a countdown clock for the given (negative) number of minutes. Click the clock display to reset the time.\n''clockFormat''\nA string that defines the clock format. The default is '0hh:0mm:0ss' where the prefix '0' forces a leading zero. \n\n''header''\nA string to be used as a permanent header for the slide show. By default it's used the title of tiddler that defines the slide show.\n\n''footer''\nA string to be used as a permanent footer for the slide show. By default the footer is empty.\n!Incremental display\nA succession of overlays (or layers) can be defined in each slide by marking blocks of text with\n{{{\n{{overlay1{...some text...}}}, {{overlay2{...some text...}}}, {{overlay3{...some text...}}}, ...\n}}}\nThe default name of the classes (//overlay//) can be redefined with //config.macros.slideShow.text.overlay = "layer"//, for example. \nTo costumize the way overlays are shown you can redefine the following CSS classes\n*.previousOverlay \n*.currentOverlay \n*.nextOverlay \nin a ~StyleSheet. The default style simply hides the next overlays and shows the current and the previous ones as normal text.\n!Slide show navigation\nYou can navigate through a slide show using the keyboard or the mouse. \n!!Mouse navigation\nLeft (right) clicking on a slide jumps to the next (previous) slide or overlay. To move to the first or last slides you must use the navigation bar at the bottom of the browser's window.\n!!Keyboard\nThe following keys are defined:\n*Home - first slide\n*~PageUp - previous slide\n*~PageDown - next slide\n*End - last slide\n*Escape - exit slide show\n*Up arrow - first overlay\n*Left arrow - previous overlay/slide\n*Pause/Right arrow - next overlay/slide\n*Down arrow - last overlay\n*Spacebar - pause/resume slide show in auto advance mode, next overlay/slide otherwise\n*B - blank screen and block the slide show\n!Miscellaneous \nAny block of text marked as\n{{{\n{{comment{For my eyes only!}}}\n}}}\nwill not be displayed in the slide show.\n\nAll translatable strings are defined in //config.macros.slideShow.text// parameter. See SlideShowPluginMsgPT with the portuguese translation as an example.
//Smiley Samples:\n//<<smiley :-)>>\n//<<smiley :-(>>\n//<<smiley ;-)>>\n//<<smiley :-|>>\n//<<smiley :-D>>\n//here's how they behave inside <<smiley :-)>> some text.\n//a few more to come!\n\n// from AlanH, http://www.personal.psu.edu/staff/a/c/ach12/tiddlywiki/smiley.htm\n\nversion.extensions.smileyMacro = {major: 0, minor: 1, revision: 0, date: new Date(2005,7,20)};\n//Author: Alan Hecht\nconfig.macros.smiley = {}\nconfig.macros.smiley.handler = function(place,macroName,params)\n{\n var palette = ["transparent","#000000","#1a1507","#352e18","#464646","#666666","#a3141e","#b06b63","#cc9900","#dd9030","#dddddd","#e89d00","#edc32a","#f3cb3c","#fdf201","#fdf526","#ff3149","#ffac00","#ffbf06","#ffc846","#ffcc66","#ffd758","#ffdd01","#ffea7b","#ffed55","#ffffff"];\n var data = params;\n var imageMap = null;\n if(data[0] == ":-)" || data[0] == ":)")\n\n imageMap = "aaaaabbbbbaaaaaaaabdtyyvtdbaaaaabnyxxxxxujbaaabmyyffyffuujbaadyyyeeyeetttdabppppddyddpmmlbbwoooooooowsrlbbwwpooooowwmrlbbwwboooowwwbllbbwwwboooowbrllbacwwwbbbbbrllcaablswwwwsrrlibaaablsssrrllibaaaaabcrrlllcbaaaaaaaabbbbbaaaaa";\n else if(data[0] == ":-(" || data[0] == ":(")\n imageMap = "aaaaabbbbbaaaaaaaabdtyyvtdbaaaaabnyxxxxxujbaaabmyyyyyyyuujbaadyyyeeyeetttdabppppddyddpmmlbbwoooooooowsrlbbwwpooooowwmrlbbwwoooooowwrllbbwwwwbbbbbsrllbacwwbwwwwsbllcaablswwwwsrrlibaaablsssrrllibaaaaabcrrlllcbaaaaaaaabbbbbaaaaa";\n else if(data[0] == ";-)" || data[0] == ";)")\n imageMap = "aaaaabbbbbaaaaaaaabdtyyvtdbaaaaabnyxxxxxujbaaabmyyxxxxxuujbaadyyyxxxeetttdabppphddyddpmmlbbwoooooooowsrlbbwwpooooowwmrlbbwwboooowwwbllbbwwwboooowbrllbacwwwbbbbbrllcaablswwwwsrrlibaaablsssrrllibaaaaabcrrlllcbaaaaaaaabbbbbaaaaa";\n else if(data[0] == ":-|" || data[0] == ":|")\n imageMap = "aaaaabbbbbaaaaaaaabdtyyvtdbaaaaabnyxxxxxujbaaabmyyffyffuujbaadyyyeeyeetttdabppppddyddpmmlbbwoooooooowsrlbbwwpooooowwmrlbbwwoooooowwrllbbwwwwbbbbbsrllbacwwwwwwwsrllcaablswwwwsrrlibaaablsssrrllibaaaaabcrrlllcbaaaaaaaabbbbbaaaaa";\n else if(data[0] == ":-D" || data[0] == ":D")\n imageMap = "aaaaabbbbbaaaaaaaabdtyyvtdbaaaaabnyxxxxxujbaaabmyyeeyeeuujbaadyyyeeyeetttdabppppyyyyypmmlbbwbbbbbbbbbbblbbwbkzzzzzzzkbwbbwbfzzzzzzzfbwbbwbkzzzzzzzkbwbacwbkzzzzzkblcaablsbkzzzkblibaaablsbbbbblibaaaaabcrrlllcbaaaaaaaabbbbbaaaaa";\n else\n createTiddlyElement(place,"span",null,"errorNoSuchMacro","unknown smiley");\n if(imageMap)\n {\n var box = createTiddlyElement(place,"span",null,"smiley",String.fromCharCode(160));\n box.style.position = "relative";\n box.style.width = "15px";\n box.style.height = "15px";\n box.style.marginLeft = "1px";\n box.style.marginRight = "1px";\n box.style.paddingRight = "12px";\n box.style.verticalAlign = "top";\n\n //now divide into 15x15 grid and create each pixel\n // rows\n for(r=0; r<15; r++)\n {\n // columns\n for(c=0; c<15; c++)\n {\n //create each pixel with the correct background\n var pix = document.createElement("img");\n pix.className = "smileyPixel";\n pix.style.position = "absolute";\n pix.border = 0;\n pix.style.top = r + "px";\n pix.style.left = c + "px";\n pix.style.width = "1px";\n pix.style.height = "1px";\n pix.style.backgroundColor = palette[imageMap.charCodeAt((r*15)+c)-97];\n pix.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";\n box.appendChild(pix);\n }\n }\n }\n}\n
!Anime Studio links - 2D cartoon animation\n[[Product page|http://www.e-frontier.com/go/LManime]]\n[[Lost Marble forum|http://www.lostmarble.com/forum/]]\n[[Old Yahoo!Group|http://groups.yahoo.com/group/moho/]]\n[[Online Moho manual|http://www.lostmarble.com/moho/manual/index.html]]\n[[Papagayo (official lipsynch software for Anime Studio)|http://www.lostmarble.com/papagayo/index.shtml]]\n[[Yolo|http://yolo.sourceforge.net/]] - Papagayo clone\n[[JLipSync|http://jlipsync.sourceforge.net/]] - Magpie 0.9 clone for lipsynch\n[[CartoonLearning.com forum|http://www.cartoonlearning.com/forum/]] - Anime Studio and ~CreaToon\n\n\n!Expression links - vector/spline illustration, great features\n[[Product page|http://www.microsoft.com/products/expression/en/graphic_designer/default.mspx]]\n[[Previous version|http://www.microsoft.com/products/expression/en/graphic_designer/previous/expression3_home.aspx]] - free\n[[Studio E3 tutorials|http://www.studioe3.com/]]\n[[Yahoo!Group|http://tech.groups.yahoo.com/group/expression3/]]\n[[Microsoft forum|http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.expression.discussion&lang=en]]\n[[Expression Graphic Design Tutorials|http://www.bildsajten.com/egd/egd.htm]]\n\n!Other vector illustration programs\n[[Serif DrawPlus|http://www.serif.com/drawplus/]] - vector illustration, some great features\n[[Real-DRAW Pro|http://www.mediachance.com/realdraw/index.html]] - good vector/raster hybrid\n[[Xara Xtreme|http://www.xara.com/products/xtreme/default.asp]] - vector illustration, great interface\n[[Inkscape|http://www.inkscape.org/]] - open source vector illustration\n[[Synfig|http://www.synfig.com/]] - fledgling open source vector animation software\n\n!Video editors\n[[Sony Vegas Movie Studio|http://www.sonymediasoftware.com/products/vegasfamily.asp]]\n[[Serif MoviePlus|http://www.serif.com/movieplus/movieplus5/index.asp]]\n[[EditStudio|http://www.puremotion.com/editstudio/index.htm]]\n\n!Other software\n[[Wink (great tutorial software)|http://www.debugmode.com/wink/]] - interactive SWF output, freeware\n[[IrfanView (great image viewer/converter)|http://www.irfanview.com/]] - freeware\n[[Artweaver (paint and image manipulation)|http://www.artweaver.de/index.php?en_version]] - freeware\n[[ArtRage (great natural painting experience)|http://www.ambientdesign.com/]] - inexpensive\n[[Bakhter|http://www.bakhter.com/]] - lots of graphics links (freeware and commercial) by category\n[[Audacity (good sound editor/recorder)|http://audacity.sourceforge.net/]] - freeware\n[[Artoonix|http://www.artoonix.com/en/index.html]] - fledgling inexpensive cut-out animation software\n[[Paint.NET|http://www.getpaint.net/]] - good free image/photo manipulation, active development\n[[The GIMP|http://www.gimp.org/]] - open source image manipulation, very extensive\n[[CreaToon|http://www.creatoon.com/]] - cut-out animation software, "abandonware" (development discontinued, available free)\n\n!Useful forums\n[[TalkGraphics|http://www.talkgraphics.com/]] - Xara Xtreme forum for illustrators\n[[XaraXone|http://www.xaraxone.com/]] - great Xara Xtreme tutorials\n[[Serif|http://www.serif.com/forum/]] - support for DrawPlus, MoviePlus\n[[Artweaver|http://www.artweaver.de/forum/]] - some English sections, Some German\n[[ArtRage|http://www2.ambientdesign.com/forums/]] - artists using the software\n[[Sony Vegas Movie Studio|http://www.sonymediasoftware.com/forums/ShowTopics.asp?ForumID=12]]\n[[Real-DRAW Pro forum|http://www.mmbforums.com/phpbb/viewforum.php?f=7]]\n\n!Melb PC special interest groups\n[[Graphics SIG home page|http://groups.melbpc.org.au/~graphics/]]\n[[Graphics SIG Yahoo!Group|http://tech.groups.yahoo.com/group/melbgraphicsig/]]\n[[Animation SIG|http://groups.melbpc.org.au/~animate/]]\n\n!Spline-based 3D software\n[[Hash Animation:Master|http://www.hash.com/]] - great 3D animation software\n[[JPatch|http://www.jpatch.com/]] - useful modeling tool, limited animation\n[[HamaPatch|http://www.geocities.com/hamapatch/program/]] - useful modeling tool\n\n!Teddy 3D software and similar\n[[Teddy|http://www-ui.is.s.u-tokyo.ac.jp/~takeo/teddy/teddy.htm]] - the original\n[[SmoothTeddy|http://www-ui.is.s.u-tokyo.ac.jp/~takeo/java/smoothteddy/index.html]] - the original plus paint\n[[OpenTeddy|http://openteddy.sourceforge.net/]]\n[[TeddyPlus|http://visgraph.cs.ust.hk/fyp/teddyplus/]]\n[[MagicalSketch 2|http://www.e-frontier.com/article/articleview/1204/1/553?sbss=553]]\n[[Archipelis Designer|http://www.thebest3d.com/archipelis/index.html]] - [[original page|http://archipelis.dnsalias.com/~archipel/index.html]]\n[[Plopp|http://www.impara.de/project_painter.htm]] - [[available|http://www.amazon.de/exec/obidos/ASIN/3898353664/]]\n[[ShapeShop|http://www.unknownroad.com/shapeshop/index.html]]\n[[3D Sketch|http://ccsl.mae.cornell.edu/research/sketch/]]\n[[Moment of Inspiration|http://moi3d.com/]]\n[[Google SketchUp|http://sketchup.google.com/]] and [[full SketchUp|http://www.sketchup.com/?section=products]]\n[[Curvy 3D|http://www.curvy3d.com/]]\n\n!Slax Linux\n[[Slax home page|http://www.slax.org/]]\n[[New Slax modules|http://www.slax.org/modules.php?action=newest]]\n\n!Other links\n[[Cartoon Central Australia|http://members.optusnet.com.au/veemon/]] - cartoon news, DVD and TV\n
A free easy-to-use personal [[Wiki|http://en.wikipedia.org/wiki/Wiki]] system. It's a single HTML file you can carry about and edit on your USB drive and upload to a web site when you want to publish it. \n\nTiddlyspot provides a free host for your TiddlyWiki, including online updating directly on the Tiddlyspot site\n\n[[The main and original TiddlyWiki site|http://www.tiddlywiki.com/]]\n[[A TiddlyWiki tutorial (as a TiddlyWiki page, naturally)|http://www.blogjones.com/TiddlyWikiTutorial.html]]\n[[TiddlySpot, a free host for your TiddlyWiki|http://tiddlyspot.com/]]
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |\n| 25/10/2006 15:11:34 | YourName | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 25/10/2006 15:12:34 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 25/10/2006 15:41:56 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 25/10/2006 15:43:11 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 25/10/2006 15:45:11 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 25/10/2006 15:58:22 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 25/10/2006 15:59:0 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 25/10/2006 16:7:40 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 25/10/2006 16:8:21 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 25/10/2006 16:54:23 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 25/10/2006 16:54:33 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 10:8:2 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 10:16:45 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 10:24:38 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 11:0:12 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:1:4 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 11:17:55 | YourName | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:19:32 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:25:21 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:25:47 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:26:30 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:42:39 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:45:7 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:47:10 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:50:15 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:51:28 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 11:57:53 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 12:44:30 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 12:46:9 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 16:56:26 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 16:58:56 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 17:21:6 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 17:55:10 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 18:15:40 | YourName | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/10/2006 22:29:50 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/10/2006 22:46:46 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 27/10/2006 10:57:39 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 27/10/2006 10:58:34 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 27/10/2006 11:19:55 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 28/10/2006 15:42:42 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 28/10/2006 16:22:47 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 28/10/2006 16:32:37 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 29/10/2006 13:48:25 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 29/10/2006 14:14:56 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 31/10/2006 8:46:18 | YourName | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 31/10/2006 9:11:30 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 31/10/2006 9:15:31 | myles | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 1/11/2006 11:12:34 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 1/11/2006 11:15:30 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 1/11/2006 11:17:14 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 1/11/2006 11:18:8 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 1/11/2006 11:51:46 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 1/11/2006 11:53:6 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 1/11/2006 12:11:6 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 6/11/2006 11:42:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 6/11/2006 12:2:36 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 12:2:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 12:20:3 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 12:54:48 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 12:57:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 6/11/2006 13:12:19 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 6/11/2006 13:39:34 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 6/11/2006 15:9:22 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 15:9:45 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 15:20:14 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 15:55:53 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 15:57:31 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 16:0:27 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 16:1:43 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 16:6:10 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 6/11/2006 17:15:46 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 8/11/2006 16:14:9 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 8/11/2006 16:14:22 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 9/11/2006 12:0:40 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 9/11/2006 12:34:57 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 13/11/2006 15:35:24 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 15:35:55 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 15:41:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 15:45:1 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 15:47:28 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 15:47:58 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 13/11/2006 15:54:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 15:55:44 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 16:3:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 16:5:23 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 16:6:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 17:19:40 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 17:21:7 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 13/11/2006 17:26:7 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 14/11/2006 16:22:50 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 17:15:1 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 17:15:17 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 17:21:33 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 17:22:14 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 17:28:17 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 17:31:36 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 17:40:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 17:41:31 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 17:55:31 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 17:58:55 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:0:7 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:2:50 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:6:24 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:17:2 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 18:17:55 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:19:48 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:20:46 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:22:7 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 18:22:16 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 16/11/2006 18:23:51 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 16/11/2006 18:27:27 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 17/11/2006 14:28:52 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 17/11/2006 14:59:17 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:15:15 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:16:38 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:23:27 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:24:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:29:32 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:36:24 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 17/11/2006 15:47:10 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 17/11/2006 15:49:11 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/11/2006 10:44:41 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/11/2006 10:47:29 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 28/11/2006 12:6:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/#%5B%5BAnimation%20resolutions%5D%5D]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 4/12/2006 12:23:34 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 12/12/2006 16:33:16 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 12/12/2006 16:36:36 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 8/1/2007 9:26:52 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 8/1/2007 9:28:52 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 10/1/2007 11:7:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 11/1/2007 11:29:48 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 11/1/2007 12:39:40 | MylesStrous | [[myles.html|file:///G:/License/myles.html]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/1/2007 14:42:36 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 26/2/2007 11:27:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/2/2007 11:28:29 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/2/2007 11:35:38 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/2/2007 11:38:54 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/2/2007 11:43:37 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 26/2/2007 11:47:32 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 21/3/2007 12:29:58 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 21/3/2007 12:31:53 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/3/2007 12:34:46 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/4/2007 10:17:46 | MylesStrous | [[/|http://myles.tiddlyspot.com/#%5B%5BAnime%20Studio%20tips%5D%5D]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 18/4/2007 10:18:11 | MylesStrous | [[/|http://myles.tiddlyspot.com/#%5B%5BAnime%20Studio%20tips%5D%5D]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/4/2007 10:21:3 | MylesStrous | [[/|http://myles.tiddlyspot.com/#%5B%5BAnime%20Studio%20tips%5D%5D]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/4/2007 10:21:21 | MylesStrous | [[/|http://myles.tiddlyspot.com/#%5B%5BAnime%20Studio%20tips%5D%5D]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/5/2007 17:43:22 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 22/5/2007 17:43:44 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 17/7/2007 10:59:30 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/8/2007 14:22:36 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/8/2007 14:35:41 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 22/8/2007 14:36:46 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 22/8/2007 14:39:22 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 22/8/2007 14:52:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 23/8/2007 11:8:8 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 23/8/2007 11:25:37 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 23/8/2007 14:31:17 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 24/8/2007 15:50:9 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 24/8/2007 15:51:35 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 28/8/2007 15:48:0 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/4/2008 18:25:28 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 22/4/2008 18:25:43 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/4/2008 18:27:30 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/4/2008 18:30:7 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 19/8/2008 15:28:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 19/8/2008 15:38:54 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 19/8/2008 15:51:28 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 19/8/2008 16:7:30 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 19/8/2008 16:12:17 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 19/8/2008 16:13:12 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 4/12/2008 9:9:41 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 4/12/2008 9:10:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 4/12/2008 9:10:28 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/12/2008 14:0:12 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/12/2008 14:0:44 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/12/2008 14:2:21 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 27/4/2009 9:43:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 17/7/2009 14:6:34 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 20/7/2009 10:39:39 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 10:43:27 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 10:56:16 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 11:1:18 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 11:8:51 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 11:13:51 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 11:14:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 20/7/2009 11:19:38 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 20/7/2009 21:22:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 20/7/2009 21:23:27 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 21/7/2009 14:15:12 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 23/8/2009 13:33:29 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 23/8/2009 13:34:23 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 23/8/2009 13:35:16 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 23/8/2009 14:0:47 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 23/8/2009 14:2:5 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 23/8/2009 14:13:7 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 23/8/2009 14:35:31 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 23/8/2009 14:44:47 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 23/8/2009 20:3:20 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/9/2009 16:5:24 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 22/9/2009 22:33:40 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 22/9/2009 23:6:56 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/12/2009 10:9:4 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 18/12/2009 10:14:59 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 18/12/2009 10:37:9 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 21/1/2010 9:5:6 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:25:32 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:30:23 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:31:43 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:38:40 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:50:33 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:51:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 9:54:53 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 21/1/2010 17:16:37 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 17:36:49 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 17:40:6 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 17:41:3 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 17:46:52 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 21/1/2010 17:49:3 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 8/2/2010 20:56:52 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 8/2/2010 20:57:29 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 8/2/2010 20:58:23 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . | Ok |\n| 8/2/2010 21:2:38 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 1/4/2010 10:23:56 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |\n| 11/5/2010 10:57:53 | MylesStrous | [[/|http://myles.tiddlyspot.com/]] | [[store.cgi|http://myles.tiddlyspot.com/store.cgi]] | . | index.html | . |
/***\n|''Name:''|UploadPlugin|\n|''Description:''|Save to web a TiddlyWiki|\n|''Version:''|3.4.4|\n|''Date:''|Sep 30, 2006|\n|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|\n|''Documentation:''|http://tiddlywiki.bidix.info/#UploadDoc|\n|''Author:''|BidiX (BidiX (at) bidix (dot) info)|\n|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|\n|''~CoreVersion:''|2.0.0|\n|''Browser:''|Firefox 1.5; InternetExplorer 6.0; Safari|\n|''Include:''|config.lib.file; config.lib.log; config.lib.options; PasswordTweak|\n|''Require:''|[[UploadService|http://tiddlywiki.bidix.info/#UploadService]]|\n***/\n//{{{\nversion.extensions.UploadPlugin = {\n major: 3, minor: 4, revision: 4, \n date: new Date(2006,8,30),\n source: 'http://tiddlywiki.bidix.info/#UploadPlugin',\n documentation: 'http://tiddlywiki.bidix.info/#UploadDoc',\n author: 'BidiX (BidiX (at) bidix (dot) info',\n license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',\n coreVersion: '2.0.0',\n browser: 'Firefox 1.5; InternetExplorer 6.0; Safari'\n};\n//}}}\n\n////+++!![config.lib.file]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.file) config.lib.file= {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 0}, \n date: new Date(2006,3,9)\n};\nconfig.lib.file.dirname = function (filePath) {\n var lastpos;\n if ((lastpos = filePath.lastIndexOf("/")) != -1) {\n return filePath.substring(0, lastpos);\n } else {\n return filePath.substring(0, filePath.lastIndexOf("\s\s"));\n }\n};\nconfig.lib.file.basename = function (filePath) {\n var lastpos;\n if ((lastpos = filePath.lastIndexOf("#")) != -1) \n filePath = filePath.substring(0, lastpos);\n if ((lastpos = filePath.lastIndexOf("/")) != -1) {\n return filePath.substring(lastpos + 1);\n } else\n return filePath.substring(filePath.lastIndexOf("\s\s")+1);\n};\nwindow.basename = function() {return "@@deprecated@@";};\n//}}}\n////===\n\n////+++!![config.lib.log]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.log) config.lib.log= {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 1}, \n date: new Date(2006,8,19)\n};\nconfig.lib.Log = function(tiddlerTitle, logHeader) {\n if (version.major < 2)\n this.tiddler = store.tiddlers[tiddlerTitle];\n else\n this.tiddler = store.getTiddler(tiddlerTitle);\n if (!this.tiddler) {\n this.tiddler = new Tiddler();\n this.tiddler.title = tiddlerTitle;\n this.tiddler.text = "| !date | !user | !location |" + logHeader;\n this.tiddler.created = new Date();\n this.tiddler.modifier = config.options.txtUserName;\n this.tiddler.modified = new Date();\n if (version.major < 2)\n store.tiddlers[tiddlerTitle] = this.tiddler;\n else\n store.addTiddler(this.tiddler);\n }\n return this;\n};\n\nconfig.lib.Log.prototype.newLine = function (line) {\n var now = new Date();\n var newText = "| ";\n newText += now.getDate()+"/"+(now.getMonth()+1)+"/"+now.getFullYear() + " ";\n newText += now.getHours()+":"+now.getMinutes()+":"+now.getSeconds()+" | ";\n newText += config.options.txtUserName + " | ";\n var location = document.location.toString();\n var filename = config.lib.file.basename(location);\n if (!filename) filename = '/';\n newText += "[["+filename+"|"+location + "]] |";\n this.tiddler.text = this.tiddler.text + "\sn" + newText;\n this.addToLine(line);\n};\n\nconfig.lib.Log.prototype.addToLine = function (text) {\n this.tiddler.text = this.tiddler.text + text;\n this.tiddler.modifier = config.options.txtUserName;\n this.tiddler.modified = new Date();\n if (version.major < 2)\n store.tiddlers[this.tiddler.tittle] = this.tiddler;\n else {\n store.addTiddler(this.tiddler);\n story.refreshTiddler(this.tiddler.title);\n store.notify(this.tiddler.title, true);\n }\n if (version.major < 2)\n store.notifyAll(); \n};\n//}}}\n////===\n\n////+++!![config.lib.options]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.options) config.lib.options = {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 0}, \n date: new Date(2006,3,9)\n};\n\nconfig.lib.options.init = function (name, defaultValue) {\n if (!config.options[name]) {\n config.options[name] = defaultValue;\n saveOptionCookie(name);\n }\n};\n//}}}\n////===\n\n////+++!![PasswordTweak]\n\n//{{{\nversion.extensions.PasswordTweak = {\n major: 1, minor: 0, revision: 3, date: new Date(2006,8,30),\n type: 'tweak',\n source: 'http://tiddlywiki.bidix.info/#PasswordTweak'\n};\n//}}}\n/***\n!!config.macros.option\n***/\n//{{{\nconfig.macros.option.passwordCheckboxLabel = "Save this password on this computer";\nconfig.macros.option.passwordType = "password"; // password | text\n\nconfig.macros.option.onChangeOption = function(e)\n{\n var opt = this.getAttribute("option");\n var elementType,valueField;\n if(opt) {\n switch(opt.substr(0,3)) {\n case "txt":\n elementType = "input";\n valueField = "value";\n break;\n case "pas":\n elementType = "input";\n valueField = "value";\n break;\n case "chk":\n elementType = "input";\n valueField = "checked";\n break;\n }\n config.options[opt] = this[valueField];\n saveOptionCookie(opt);\n var nodes = document.getElementsByTagName(elementType);\n for(var t=0; t<nodes.length; t++) \n {\n var optNode = nodes[t].getAttribute("option");\n if (opt == optNode) \n nodes[t][valueField] = this[valueField];\n }\n }\n return(true);\n};\n\nconfig.macros.option.handler = function(place,macroName,params)\n{\n var opt = params[0];\n if(config.options[opt] === undefined) {\n return;}\n var c;\n switch(opt.substr(0,3)) {\n case "txt":\n c = document.createElement("input");\n c.onkeyup = this.onChangeOption;\n c.setAttribute ("option",opt);\n c.className = "txtOptionInput "+opt;\n place.appendChild(c);\n c.value = config.options[opt];\n break;\n case "pas":\n // input password\n c = document.createElement ("input");\n c.setAttribute("type",config.macros.option.passwordType);\n c.onkeyup = this.onChangeOption;\n c.setAttribute("option",opt);\n c.className = "pasOptionInput "+opt;\n place.appendChild(c);\n c.value = config.options[opt];\n // checkbox link with this password "save this password on this computer"\n c = document.createElement("input");\n c.setAttribute("type","checkbox");\n c.onclick = this.onChangeOption;\n c.setAttribute("option","chk"+opt);\n c.className = "chkOptionInput "+opt;\n place.appendChild(c);\n c.checked = config.options["chk"+opt];\n // text savePasswordCheckboxLabel\n place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));\n break;\n case "chk":\n c = document.createElement("input");\n c.setAttribute("type","checkbox");\n c.onclick = this.onChangeOption;\n c.setAttribute("option",opt);\n c.className = "chkOptionInput "+opt;\n place.appendChild(c);\n c.checked = config.options[opt];\n break;\n }\n};\n//}}}\n/***\n!! Option cookie stuff\n***/\n//{{{\nwindow.loadOptionsCookie_orig_PasswordTweak = window.loadOptionsCookie;\nwindow.loadOptionsCookie = function()\n{\n var cookies = document.cookie.split(";");\n for(var c=0; c<cookies.length; c++) {\n var p = cookies[c].indexOf("=");\n if(p != -1) {\n var name = cookies[c].substr(0,p).trim();\n var value = cookies[c].substr(p+1).trim();\n switch(name.substr(0,3)) {\n case "txt":\n config.options[name] = unescape(value);\n break;\n case "pas":\n config.options[name] = unescape(value);\n break;\n case "chk":\n config.options[name] = value == "true";\n break;\n }\n }\n }\n};\n\nwindow.saveOptionCookie_orig_PasswordTweak = window.saveOptionCookie;\nwindow.saveOptionCookie = function(name)\n{\n var c = name + "=";\n switch(name.substr(0,3)) {\n case "txt":\n c += escape(config.options[name].toString());\n break;\n case "chk":\n c += config.options[name] ? "true" : "false";\n // is there an option link with this chk ?\n if (config.options[name.substr(3)]) {\n saveOptionCookie(name.substr(3));\n }\n break;\n case "pas":\n if (config.options["chk"+name]) {\n c += escape(config.options[name].toString());\n } else {\n c += "";\n }\n break;\n }\n c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";\n document.cookie = c;\n};\n//}}}\n/***\n!! Initializations\n***/\n//{{{\n// define config.options.pasPassword\nif (!config.options.pasPassword) {\n config.options.pasPassword = 'defaultPassword';\n window.saveOptionCookie('pasPassword');\n}\n// since loadCookies is first called befor password definition\n// we need to reload cookies\nwindow.loadOptionsCookie();\n//}}}\n////===\n\n////+++!![config.macros.upload]\n\n//{{{\nconfig.macros.upload = {\n accessKey: "U",\n formName: "UploadPlugin",\n contentType: "text/html;charset=UTF-8",\n defaultStoreScript: "store.php"\n};\n\n// only this two configs need to be translated\nconfig.macros.upload.messages = {\n aboutToUpload: "About to upload TiddlyWiki to %0",\n backupFileStored: "Previous file backuped in %0",\n crossDomain: "Certainly a cross-domain isue: access to an other site isn't allowed",\n errorDownloading: "Error downloading",\n errorUploadingContent: "Error uploading content",\n fileLocked: "Files is locked: You are not allowed to Upload",\n fileNotFound: "file to upload not found",\n fileNotUploaded: "File %0 NOT uploaded",\n mainFileUploaded: "Main TiddlyWiki file uploaded to %0",\n passwordEmpty: "Unable to upload, your password is empty",\n urlParamMissing: "url param missing",\n rssFileNotUploaded: "RssFile %0 NOT uploaded",\n rssFileUploaded: "Rss File uploaded to %0"\n};\n\nconfig.macros.upload.label = {\n promptOption: "Save and Upload this TiddlyWiki with UploadOptions",\n promptParamMacro: "Save and Upload this TiddlyWiki in %0",\n saveLabel: "save to web", \n saveToDisk: "save to disk",\n uploadLabel: "upload" \n};\n\nconfig.macros.upload.handler = function(place,macroName,params){\n // parameters initialization\n var storeUrl = params[0];\n var toFilename = params[1];\n var backupDir = params[2];\n var uploadDir = params[3];\n var username = params[4];\n var password; // for security reason no password as macro parameter\n var label;\n if (document.location.toString().substr(0,4) == "http")\n label = this.label.saveLabel;\n else\n label = this.label.uploadLabel;\n var prompt;\n if (storeUrl) {\n prompt = this.label.promptParamMacro.toString().format([this.toDirUrl(storeUrl, uploadDir, username)]);\n }\n else {\n prompt = this.label.promptOption;\n }\n createTiddlyButton(place, label, prompt, \n function () {\n config.macros.upload.upload(storeUrl, toFilename, uploadDir, backupDir, username, password); \n return false;}, \n null, null, this.accessKey);\n};\nconfig.macros.upload.UploadLog = function() {\n return new config.lib.Log('UploadLog', " !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |" );\n};\nconfig.macros.upload.UploadLog.prototype = config.lib.Log.prototype;\nconfig.macros.upload.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {\n var line = " [[" + config.lib.file.basename(storeUrl) + "|" + storeUrl + "]] | ";\n line += uploadDir + " | " + toFilename + " | " + backupDir + " |";\n this.newLine(line);\n};\nconfig.macros.upload.UploadLog.prototype.endUpload = function() {\n this.addToLine(" Ok |");\n};\nconfig.macros.upload.basename = config.lib.file.basename;\nconfig.macros.upload.dirname = config.lib.file.dirname;\nconfig.macros.upload.toRootUrl = function (storeUrl, username)\n{\n return root = (this.dirname(storeUrl)?this.dirname(storeUrl):this.dirname(document.location.toString()));\n}\nconfig.macros.upload.toDirUrl = function (storeUrl, uploadDir, username)\n{\n var root = this.toRootUrl(storeUrl, username);\n if (uploadDir && uploadDir != '.')\n root = root + '/' + uploadDir;\n return root;\n}\nconfig.macros.upload.toFileUrl = function (storeUrl, toFilename, uploadDir, username)\n{\n return this.toDirUrl(storeUrl, uploadDir, username) + '/' + toFilename;\n}\nconfig.macros.upload.upload = function(storeUrl, toFilename, uploadDir, backupDir, username, password)\n{\n // parameters initialization\n storeUrl = (storeUrl ? storeUrl : config.options.txtUploadStoreUrl);\n toFilename = (toFilename ? toFilename : config.options.txtUploadFilename);\n backupDir = (backupDir ? backupDir : config.options.txtUploadBackupDir);\n uploadDir = (uploadDir ? uploadDir : config.options.txtUploadDir);\n username = (username ? username : config.options.txtUploadUserName);\n password = config.options.pasUploadPassword; // for security reason no password as macro parameter\n if (!password || password === '') {\n alert(config.macros.upload.messages.passwordEmpty);\n return;\n }\n if (storeUrl === '') {\n storeUrl = config.macros.upload.defaultStoreScript;\n }\n if (config.lib.file.dirname(storeUrl) === '') {\n storeUrl = config.lib.file.dirname(document.location.toString())+'/'+storeUrl;\n }\n if (toFilename === '') {\n toFilename = config.lib.file.basename(document.location.toString());\n }\n\n clearMessage();\n // only for forcing the message to display\n if (version.major < 2)\n store.notifyAll();\n if (!storeUrl) {\n alert(config.macros.upload.messages.urlParamMissing);\n return;\n }\n // Check that file is not locked\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n if (BidiX.GroupAuthoring.lock.isLocked() && !BidiX.GroupAuthoring.lock.isMyLock()) {\n alert(config.macros.upload.messages.fileLocked);\n return;\n }\n }\n \n var log = new this.UploadLog();\n log.startUpload(storeUrl, toFilename, uploadDir, backupDir);\n if (document.location.toString().substr(0,5) == "file:") {\n saveChanges();\n }\n var toDir = config.macros.upload.toDirUrl(storeUrl, toFilename, uploadDir, username);\n displayMessage(config.macros.upload.messages.aboutToUpload.format([toDir]), toDir);\n this.uploadChanges(storeUrl, toFilename, uploadDir, backupDir, username, password);\n if(config.options.chkGenerateAnRssFeed) {\n //var rssContent = convertUnicodeToUTF8(generateRss());\n var rssContent = generateRss();\n var rssPath = toFilename.substr(0,toFilename.lastIndexOf(".")) + ".xml";\n this.uploadContent(rssContent, storeUrl, rssPath, uploadDir, '', username, password, \n function (responseText) {\n if (responseText.substring(0,1) != '0') {\n displayMessage(config.macros.upload.messages.rssFileNotUploaded.format([rssPath]));\n }\n else {\n var toFileUrl = config.macros.upload.toFileUrl(storeUrl, rssPath, uploadDir, username);\n displayMessage(config.macros.upload.messages.rssFileUploaded.format(\n [toFileUrl]), toFileUrl);\n }\n // for debugging store.php uncomment last line\n //DEBUG alert(responseText);\n });\n }\n return;\n};\n\nconfig.macros.upload.uploadChanges = function(storeUrl, toFilename, uploadDir, backupDir, \n username, password) {\n var original;\n if (document.location.toString().substr(0,4) == "http") {\n original = this.download(storeUrl, toFilename, uploadDir, backupDir, username, password);\n return;\n }\n else {\n // standard way : Local file\n \n original = loadFile(getLocalPath(document.location.toString()));\n if(window.Components) {\n // it's a mozilla browser\n try {\n netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\n var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]\n .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);\n converter.charset = "UTF-8";\n original = converter.ConvertToUnicode(original);\n }\n catch(e) {\n }\n }\n }\n //DEBUG alert(original);\n this.uploadChangesFrom(original, storeUrl, toFilename, uploadDir, backupDir, \n username, password);\n};\n\nconfig.macros.upload.uploadChangesFrom = function(original, storeUrl, toFilename, uploadDir, backupDir, \n username, password) {\n var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it\n var endSaveArea = '</d' + 'iv>';\n // Locate the storeArea div's\n var posOpeningDiv = original.indexOf(startSaveArea);\n var posClosingDiv = original.lastIndexOf(endSaveArea);\n if((posOpeningDiv == -1) || (posClosingDiv == -1))\n {\n alert(config.messages.invalidFileError.format([document.location.toString()]));\n return;\n }\n var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + \n allTiddlersAsHtml() + "\sn\st\st" +\n original.substr(posClosingDiv);\n var newSiteTitle;\n if(version.major < 2){\n newSiteTitle = (getElementText("siteTitle") + " - " + getElementText("siteSubtitle")).htmlEncode();\n } else {\n newSiteTitle = (wikifyPlain ("SiteTitle") + " - " + wikifyPlain ("SiteSubtitle")).htmlEncode();\n }\n\n revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");\n revised = revised.replaceChunk("<!--PRE-HEAD-START--"+">","<!--PRE-HEAD-END--"+">","\sn" + store.getTiddlerText("MarkupPreHead","") + "\sn");\n revised = revised.replaceChunk("<!--POST-HEAD-START--"+">","<!--POST-HEAD-END--"+">","\sn" + store.getTiddlerText("MarkupPostHead","") + "\sn");\n revised = revised.replaceChunk("<!--PRE-BODY-START--"+">","<!--PRE-BODY-END--"+">","\sn" + store.getTiddlerText("MarkupPreBody","") + "\sn");\n revised = revised.replaceChunk("<!--POST-BODY-START--"+">","<!--POST-BODY-END--"+">","\sn" + store.getTiddlerText("MarkupPostBody","") + "\sn");\n\n var response = this.uploadContent(revised, storeUrl, toFilename, uploadDir, backupDir, \n username, password, function (responseText) {\n if (responseText.substring(0,1) != '0') {\n alert(responseText);\n displayMessage(config.macros.upload.messages.fileNotUploaded.format([getLocalPath(document.location.toString())]));\n }\n else {\n if (uploadDir !== '') {\n toFilename = uploadDir + "/" + config.macros.upload.basename(toFilename);\n } else {\n toFilename = config.macros.upload.basename(toFilename);\n }\n var toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);\n if (responseText.indexOf("destfile:") > 0) {\n var destfile = responseText.substring(responseText.indexOf("destfile:")+9, \n responseText.indexOf("\sn", responseText.indexOf("destfile:")));\n toFileUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + destfile;\n }\n else {\n toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);\n }\n displayMessage(config.macros.upload.messages.mainFileUploaded.format(\n [toFileUrl]), toFileUrl);\n if (backupDir && responseText.indexOf("backupfile:") > 0) {\n var backupFile = responseText.substring(responseText.indexOf("backupfile:")+11, \n responseText.indexOf("\sn", responseText.indexOf("backupfile:")));\n toBackupUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + backupFile;\n displayMessage(config.macros.upload.messages.backupFileStored.format(\n [toBackupUrl]), toBackupUrl);\n }\n var log = new config.macros.upload.UploadLog();\n log.endUpload();\n store.setDirty(false);\n // erase local lock\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n BidiX.GroupAuthoring.lock.eraseLock();\n // change mtime with new mtime after upload\n var mtime = responseText.substr(responseText.indexOf("mtime:")+6);\n BidiX.GroupAuthoring.lock.mtime = mtime;\n }\n \n \n }\n // for debugging store.php uncomment last line\n //DEBUG alert(responseText);\n }\n );\n};\n\nconfig.macros.upload.uploadContent = function(content, storeUrl, toFilename, uploadDir, backupDir, \n username, password, callbackFn) {\n var boundary = "---------------------------"+"AaB03x"; \n var request;\n try {\n request = new XMLHttpRequest();\n } \n catch (e) { \n request = new ActiveXObject("Msxml2.XMLHTTP"); \n }\n if (window.netscape){\n try {\n if (document.location.toString().substr(0,4) != "http") {\n netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');}\n }\n catch (e) {}\n } \n //DEBUG alert("user["+config.options.txtUploadUserName+"] password[" + config.options.pasUploadPassword + "]");\n // compose headers data\n var sheader = "";\n sheader += "--" + boundary + "\sr\snContent-disposition: form-data; name=\s"";\n sheader += config.macros.upload.formName +"\s"\sr\sn\sr\sn";\n sheader += "backupDir="+backupDir\n +";user=" + username \n +";password=" + password\n +";uploaddir=" + uploadDir;\n // add lock attributes to sheader\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n var l = BidiX.GroupAuthoring.lock.myLock;\n sheader += ";lockuser=" + l.user\n + ";mtime=" + l.mtime\n + ";locktime=" + l.locktime;\n }\n sheader += ";;\sr\sn"; \n sheader += "\sr\sn" + "--" + boundary + "\sr\sn";\n sheader += "Content-disposition: form-data; name=\s"userfile\s"; filename=\s""+toFilename+"\s"\sr\sn";\n sheader += "Content-Type: " + config.macros.upload.contentType + "\sr\sn";\n sheader += "Content-Length: " + content.length + "\sr\sn\sr\sn";\n // compose trailer data\n var strailer = new String();\n strailer = "\sr\sn--" + boundary + "--\sr\sn";\n //strailer = "--" + boundary + "--\sr\sn";\n var data;\n data = sheader + content + strailer;\n //request.open("POST", storeUrl, true, username, password);\n try {\n request.open("POST", storeUrl, true); \n }\n catch(e) {\n alert(config.macros.upload.messages.crossDomain + "\snError:" +e);\n exit;\n }\n request.onreadystatechange = function () {\n if (request.readyState == 4) {\n if (request.status == 200)\n callbackFn(request.responseText);\n else\n alert(config.macros.upload.messages.errorUploadingContent + "\snStatus: "+request.status.statusText);\n }\n };\n request.setRequestHeader("Content-Length",data.length);\n request.setRequestHeader("Content-Type","multipart/form-data; boundary="+boundary);\n request.send(data); \n};\n\n\nconfig.macros.upload.download = function(uploadUrl, uploadToFilename, uploadDir, uploadBackupDir, \n username, password) {\n var request;\n try {\n request = new XMLHttpRequest();\n } \n catch (e) { \n request = new ActiveXObject("Msxml2.XMLHTTP"); \n }\n try {\n if (uploadUrl.substr(0,4) == "http") {\n netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");\n }\n else {\n netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\n }\n } catch (e) { }\n //request.open("GET", document.location.toString(), true, username, password);\n try {\n request.open("GET", document.location.toString(), true);\n }\n catch(e) {\n alert(config.macros.upload.messages.crossDomain + "\snError:" +e);\n exit;\n }\n \n request.onreadystatechange = function () {\n if (request.readyState == 4) {\n if(request.status == 200) {\n config.macros.upload.uploadChangesFrom(request.responseText, uploadUrl, \n uploadToFilename, uploadDir, uploadBackupDir, username, password);\n }\n else\n alert(config.macros.upload.messages.errorDownloading.format(\n [document.location.toString()]) + "\snStatus: "+request.status.statusText);\n }\n };\n request.send(null);\n};\n\n//}}}\n////===\n\n////+++!![Initializations]\n\n//{{{\nconfig.lib.options.init('txtUploadStoreUrl','store.php');\nconfig.lib.options.init('txtUploadFilename','');\nconfig.lib.options.init('txtUploadDir','');\nconfig.lib.options.init('txtUploadBackupDir','');\nconfig.lib.options.init('txtUploadUserName',config.options.txtUserName);\nconfig.lib.options.init('pasUploadPassword','');\nsetStylesheet(\n ".pasOptionInput {width: 11em;}\sn"+\n ".txtOptionInput.txtUploadStoreUrl {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadFilename {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadDir {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadBackupDir {width: 25em;}\sn"+\n "",\n "UploadOptionsStyles");\nconfig.shadowTiddlers.UploadDoc = "[[Full Documentation|http://tiddlywiki.bidix.info/l#UploadDoc ]]\sn"; \nconfig.options.chkAutoSave = false; saveOptionCookie('chkAutoSave');\n\n//}}}\n////===\n\n////+++!![Core Hijacking]\n\n//{{{\nconfig.macros.saveChanges.label_orig_UploadPlugin = config.macros.saveChanges.label;\nconfig.macros.saveChanges.label = config.macros.upload.label.saveToDisk;\n\nconfig.macros.saveChanges.handler_orig_UploadPlugin = config.macros.saveChanges.handler;\n\nconfig.macros.saveChanges.handler = function(place)\n{\n if ((!readOnly) && (document.location.toString().substr(0,4) != "http"))\n createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);\n};\n\n//}}}\n////===\n
See [[notes here|http://www.virtualdub.org/blog/pivot/entry.php?id=34]]
I'm just a hobbyist, with a limited hobbyist budget, so I tend to prefer less expensive software. Surprisingly, not all less expensive software is necessary of lower quality. Some professionals prefer to use these less expensive programs even when more expensive "professional" software is available.\n\n!!Animation software\n\nPersonally, I prefer to use [[Anime Studio Pro|http://www.e-frontier.com/go/LManime]] (formerly Moho) for 2D character animation. It's very productive, fun to use, and has [[an active and supportive user community|http://www.lostmarble.com/forum/]]. Note that it is designed to do cut-out or shadow-puppet style animation (or in some ways it is closer to clay animation or 3D animation), which it does very well indeed. However, it is probably not the best choice for flipbook/cel/frame-by-frame animation.\n\n!!Drawing and painting software\n\nFor backgrounds and props try the vector/spline-based programs [[Serif DrawPlus|http://www.serif.com/drawplus/drawplusx2/]] (great feature set) and [[Xara Xtreme|http://www.xara.com/products/xtreme/]] (excellent interface).\n\nExpression3 vector editor is currently being re-developed for Windows Vista by Microsoft as [[Expression Graphic Designer|http://www.microsoft.com/products/expression/en/graphic_designer/default.mspx]] with some new features but is currently only available as part of the full (expensive) Expression suite.\nFor the moment existing users can still [[download the previous version|http://www.microsoft.com/products/expression/en/graphic_designer/previous/expression3_home.aspx]] developed by Creature House (formerly marketed as Metacreations Expression, formerly Fractal Design Expression). This non-expiring fully-featured version is labelled as a beta/trial version, and is still available for Macs and Windows (and will reportedly run on Linux under Wine). The new Expression Graphic Designer will only run under Windows (XP sp2 or Vista).\n\nFor paint software, the freeware [[Artweaver|http://www.artweaver.de/index.php?en_version]] and the inexpensive [[Artrage|http://www.ambientdesign.com/]] are worth investigating. [[Smoothdraw|http://www.smoothdraw.com/]] is a simple but effective paint program, formerly commercial now freeware. Serious artists should also check out [[the Project DogWaffle products|http://www.thebest3d.com/dogwaffle/products/index.html]] (PD Particles is good for instant plant-like art).\n\n!!Video editor\n\nFor a video editor, I like [[Sony Vegas Movie Studio|http://www.sonymediasoftware.com/products/vegasfamily.asp]], the little brother of the professional Sony Vegas. It will (with a little help) recognise the alpha channel generated by Anime Studio for compositing. Also, [[Serif MoviePlus|http://www.serif.com/movieplus/movieplus5/index.asp]], which I think was originally derived from the same codebase - it trades several features (some audio features, some transitions) for the addition of unlimited layers, which can be really handy for some animation composition.\n\n!!Sound software\n\nFor basic sound editing and recording, I use [[Audacity|http://audacity.sourceforge.net/]] - free, nice interface, capable, stable, and available for Windows, Linux, and Mac OSX.\n
* [[Serif DrawPlus|http://www.serif.com/drawplus/]]\n* [[Xara Xtreme|http://www.xara.com/uk/]]\n* [[RealDRAW Pro|http://www.mediachance.com/realdraw/index.html]]\n* [[Creature House Expression|http://www.microsoft.com/expression/expression-design/Default.aspx]]\n
\nWelcome to Myles's TiddlyWiki. <<smiley :D>>\n\n(This is my online notebook for short notes, links, lists, etc. You can track down my recent tutorials at http://virtualcutout.blogspot.com/ and [[my Twitter stream|http://twitter.com/myless]] contains short posts about animation and screen-capture software.)\n\n''Use the menu on the left to show entries of interest.''\n\n<<slider aboutCalendarSlider AboutCalendar "About the Calendar date colours" "Click here to learn what the Calendar colours mean">>\n\n!Calendar entries for the [[Animation SIG|http://groups.melbpc.org.au/~animate/]] and the [[Graphics SIG|http://groups.melbpc.org.au/~graphics/]] of [[Melb PC|http://groups.melbpc.org.au/]]\n\n<<tabs tabsClass\nReminders "Upcoming events" Reminders\nAdd "Add a new reminder" ReminderForm\n>>\n\n<<reminder year:2010 month:5 day:17 title:"Animation and Graphics ~SIGs meet" >>\n<<reminder year:2010 month:6 day:21 title:"Animation and Graphics ~SIGs meet" >>
This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://myles.tiddlyspot.com/controlpanel]] (your control panel username is //myles//).\n<<tiddler tiddlyspotControls>>\n@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the "save to web" button in the column on the right.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click "upload" and your ~TiddlyWiki will be saved back to tiddlyspot.com.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki Guides|http://tiddlywikiguides.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions.
/***\n|''Name:''|YourSearchPlugin|\n|''Version:''|2.1.0 (2006-10-12)|\n|''Source:''|http://tiddlywiki.abego-software.de/#YourSearchPlugin ([[del.icio.us|http://del.icio.us/post?url=http://tiddlywiki.abego-software.de/index.html%23YourSearchPlugin]])|\n|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|\n|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|\n|''Copyright:''|&copy; 2005-2006 [[abego Software|http://www.abego-software.de]]|\n|''~CoreVersion:''|2.1.0|\n|''Browser:''|Firefox 1.0.4+; Firefox 1.5; ~InternetExplorer 6.0|\n!About YourSearch\nYourSearch gives you a bunch of new features to simplify and speed up your daily searches in TiddlyWiki. It seamlessly integrates into the standard TiddlyWiki search: just start typing into the 'search' field and explore!\n\nFor more information see [[Help|YourSearch Help]].\n!Compatibility\nThis plugin requires TiddlyWiki 2.1. \nCheck the [[archive|http://tiddlywiki.abego-software.de/archive]] for ~YourSearchPlugins supporting older versions of TiddlyWiki.\n!Revision history\n* v2.1.0 (2006-10-12)\n** Release version with TiddlyWiki 2.1 support\n*** Support (Extended) Field search\n*** Support parenthesis in Boolean Search\n*** Support direct regular expression input\n*** Support JavaScript Expressions for filtering\n*** "new tiddler" feature (create tiddler based on search text)\n* v2.0.2 (2006-02-13)\n** Bugfix for Firefox 1.5.0.1 related to the "Show prefix" checkbox. Thanks to Ted Pavlic for reporting and to BramChen for fixing. \n** Internal\n*** Make "JSLint" conform\n* v2.0.1 (2006-02-05)\n** Support "Exact Word Match" (use '=' to prefix word)\n** Support default filter settings (when no filter flags are given in search term)\n** Rework on the "less than 3 chars search text" feature (thanks to EricShulman)\n** Better support SinglePageMode when doing "Open all tiddlers" (thanks to EricShulman)\n** Support Firefox 1.5.0.1\n** Bug: Fixed a hilite bug in "classic search mode" (thanks to EricShulman)\n* v2.0.0 (2006-01-16)\n** Add User Interface\n* v1.0.1 (2006-01-06)\n** Support TiddlyWiki 2.0\n* v1.0.0 (2005-12-28)\n** initial version\n!Source Code\n***/\n//{{{\n//============================================================================\n//============================================================================\n// YourSearchPlugin\n//============================================================================\n//============================================================================\n\n// Ensure that the Plugin is only installed once.\n//\nif (!version.extensions.YourSearchPlugin) {\n\nversion.extensions.YourSearchPlugin = {\n major: 2, minor: 1, revision: 0,\n source: "http://tiddlywiki.abego-software.de/#YourSearchPlugin",\n licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",\n copyright: "Copyright (c) abego Software GmbH, 2005-2006 (www.abego-software.de)"\n};\n\nif (!window.abego) window.abego = {};\n\n// define the Array forEach when not yet defined (e.g. by Mozilla)\nif (!Array.forEach) {\n Array.forEach = function(obj, callback, thisObj) {\n for (var i = 0,len = obj.length; i < len; i++)\n callback.call(thisObj, obj[i], i, obj);\n };\n Array.prototype.forEach = function(callback, thisObj) {\n for (var i = 0,len = this.length; i < len; i++)\n callback.call(thisObj, this[i], i, this);\n };\n}\n\nabego.toInt = function(s, defaultValue) {\n if (!s) return defaultValue;\n var n = parseInt(s);\n return (n == NaN) ? defaultValue : n;\n};\n\nabego.createEllipsis = function(place) {\n var e = createTiddlyElement(place,"span");\n e.innerHTML = "&hellip;";\n};\n\n//#concept Object\n//\nabego.shallowCopy = function(object) {\n if (!object)\n return object;\n var result = {};\n for (var n in object) \n result[n] = object[n];\n return result;\n};\n\n// Returns a shallow copy of the options, or a new, empty object if options is null/undefined.\n//\n// @param options [may be null/undefined]\n//\n//#concept Object, Options\n//#import abego.shallowCopy\n//\nabego.copyOptions = function(options) {\n return !options ? {} : abego.shallowCopy(options);\n};\n\n//#import abego.define-namespace\n// returns the number of occurances of s in the text\nabego.countStrings = function(text, s) {\n if (!s)\n return 0;\n \n var len = s.length;\n var n = 0;\n var lastIndex = 0;\n while (1) {\n var i = text.indexOf(s, lastIndex);\n if (i < 0)\n return n;\n n++;\n lastIndex = i+len;\n }\n return n;\n};// Returns the content of the first "braced" text {...}\n// Also takes care of nested braces\n//\n// Returns undefined when no braced text is found or it is not properly nested\n//\n// @param [optional] when defined and a braced text is found lastIndexRef.lastIndex will contain the index of the char following the (final) closing brace on return.\n//\nabego.getBracedText = function(text, offset,lastIndexRef) {\n if (!offset) offset = 0;\n var re = /\s{([^\s}]*)\s}/gm;\n re.lastIndex = offset;\n var m = re.exec(text);\n if (m) {\n // The matching stopped at the first closing brace.\n // But if the matched text contains opening braces \n // this is not the final closing brace.\n // Handle this case specially, find the "corresponding" closing brace\n var s = m[1];\n var nExtraOpenBrace = abego.countStrings(s,"{");\n \n if (!nExtraOpenBrace) {\n if (lastIndexRef)\n lastIndexRef.lastIndex = re.lastIndex;\n // simple case: no nested braces\n return s;\n }\n\n // special case: "nested braces"\n var len = text.length;\n for (var i = re.lastIndex; i < len && nExtraOpenBrace; i++) {\n var c = text.charAt(i);\n if (c == "{") \n nExtraOpenBrace++;\n else if (c == "}")\n nExtraOpenBrace--;\n }\n if (!nExtraOpenBrace) {\n // found the corresponding "}".\n if (lastIndexRef)\n lastIndexRef.lastIndex = i-1;\n return text.substring(m.index+1, i-1);\n }\n }\n \n // no return means: return undefined;\n};\n\n// Returns an array with those items from the array that pass the given test\n//\n// @param test an one-arg boolean function that returns true when the item should be added.\n// @param testObj [optional] the receiver for the test function (global if undefined or null)\n// @param result [optional] an array. When define the selected items are added to this array, otherwise a new array is used.\n//\n//#import Array.prototype.forEach\n//\nabego.select = function(array,test,testObj,result) {\n if (!result) result = [];\n array.forEach(function(t) {\n if (test.call(testObj,t)) \n result.push(t);\n });\n return result;\n};\n\n// Class abego.TiddlerFilterTerm =================================================================\n//\n// Used to check if a tiddler contains a given text.\n//\n// A list of fields (standard and/or extended) may be specified to restrict the search to certain fields. \n//\n// When no explicit fields are given the fields defined by defaultFields are checked, plus all extended \n// fields (when options.withExtendedFields is true).\n//\n// @param options [may be null/undefined]\n// options.fields @seeParam abego.MultiFieldRegExpTester.fields\n// options.withExtendedFields @seeParam abego.MultiFieldRegExpTester.withExtendedFields \n// options.caseSensitive [Default: false]\n// options.fullWordMatch [Default: false]\n// options.textIsRegExp [Default: false] when true the given text is already a regExp\n//\n//#import abego.MultiFieldRegExpTester\n//\nabego.TiddlerFilterTerm = function(text,options) {\n if (!options) options = {};\n\n var reText = text;\n if (!options.textIsRegExp) {\n reText = text.escapeRegExp();\n if (options.fullWordMatch) \n reText = "\s\sb"+reText+"\s\sb";\n }\n var regExp = new RegExp(reText, "m"+(options.caseSensitive ? "" : "i"));\n\n this.tester = new abego.MultiFieldRegExpTester(regExp, options.fields, options.withExtendedFields);\n}\n\nabego.TiddlerFilterTerm.prototype.test = function(tiddler) {\n return this.tester.test(tiddler);\n}\n\n//#import abego.define-namespace\n// Recognize a string like\n// "Some Title. Some content text #Tag1 #Tag2 Tag3"\n// with the tags and the text being optional.\n// Also the period at the end of the title is optional when no content text is specified)\n//\n// Returns the result in an object with properties "title" and "params",\n// with "params" following the parseParams format, containing the "tag" and "text" arguments.\n//\nabego.parseNewTiddlerCommandLine = function(s) {\n var m = /(.*?)\s.(?:\ss+|$)([^#]*)(#.*)?/.exec(s);\n if (!m) \n m = /([^#]*)()(#.*)?/.exec(s);\n if (m) {\n var r;\n if (m[3]) {\n var s2 = m[3].replace(/#/g,"");\n r = s2.parseParams("tag");\n } else\n r = [[]];\n \n // add the text parameter\n var text = m[2]?m[2].trim():"";\n r.push({name: "text", value: text});\n r[0].text = [text];\n \n return {title: m[1].trim(), params: r}; \n } else\n return {title: s.trim(),params: [[]]};\n} \n// options.defaultFields [@seeOptionDefault abego.TiddlerFilterTerm.fields] fields to check when no fields are explicitly specified in queryText.\n// options.withExtendedFields [@seeOptionDefault abego.TiddlerFilterTerm.withExtendedFields] when true and no fields are explicitly specified in queryText also the extended fields are considered (in addition to the ones in defaultFields).\n// @seeOptions abego.TiddlerFilterTerm (-fields -fullWordMatch -withExtendedFields)\n//\n//#import abego.getBracedText\n//#import abego.copyOptions\n//#import abego.TiddlerFilterTerm\n//\nabego.parseTiddlerFilterTerm = function(queryText,offset,options) {\n \n // group 1: {...} (JavaScript expression)\n // group 2: '=' (full word match (optional))\n // group 3: [!%#] (field selection short cuts)\n // group 4: fieldName ':'\n // group 5: String literal "..."\n // group 6: RegExp literal /.../\n // group 7: scheme '://' nonSpaceChars\n // group 8: word\n var re = /\ss*(?:(?:\s{([^\s}]*)\s})|(?:(=)|([#%!])|(?:(\sw+)\ss*\s:(?!\s/\s/))|(?:(?:("(?:(?:\s\s")|[^"])+")|(?:\s/((?:(?:\s\s\s/)|[^\s/])+)\s/)|(\sw+\s:\s/\s/[^\ss]+)|([^\ss\s)\s-\s"]+)))))/mg;\n var shortCuts = {'!':'title','%':'text','#':'tags'};\n \n var fieldNames = {};\n var fullWordMatch;\n re.lastIndex = offset;\n while (1) {\n var i = re.lastIndex;\n var m = re.exec(queryText);\n if (!m || m.index != i) \n throw "Word or String literal expected";\n if (m[1]) {\n var lastIndexRef = {};\n var code = abego.getBracedText(queryText,0,lastIndexRef);\n if (!code)\n throw "Invalid {...} syntax";\n var f = Function("tiddler","return ("+code+");");\n return {func: f,\n lastIndex:lastIndexRef.lastIndex,\n markRE: null};\n }\n if (m[2])\n fullWordMatch = true;\n else if (m[3]) \n fieldNames[shortCuts[m[3]]] = 1;\n else if (m[4]) \n fieldNames[m[4]] = 1;\n else {\n var textIsRegExp = m[6];\n var text = m[5] ? window.eval(m[5]) : m[6] ? m[6] : m[7] ? m[7] : m[8];\n \n var options = abego.copyOptions(options);\n options.fullWordMatch = fullWordMatch;\n options.textIsRegExp = textIsRegExp;\n\n var fields = [];\n for (var n in fieldNames)\n fields.push(n);\n if (fields.length == 0) {\n options.fields = options.defaultFields;\n } else {\n options.fields = fields;\n options.withExtendedFields = false;\n } \n var term = new abego.TiddlerFilterTerm(text,options);\n var markREText = textIsRegExp ? text : text.escapeRegExp();\n if (markREText && fullWordMatch)\n markREText = "\s\sb"+markREText+"\s\sb";\n return {func: function(tiddler) {return term.test(tiddler);},\n lastIndex:re.lastIndex,\n markRE: markREText ? "(?:"+markREText+")" : null};\n }\n }\n};\n\n// Class abego.BoolExp =================================================================\n//\n// Allows the execution/evaluation of a boolean expression, according to this syntax:\n//\n// boolExpression : unaryExpression (("AND"|"OR"|"&&"|"||")? unaryExpression)*\n// ;\n//\n// unaryExpression : ("not"|"-")? primaryExpression\n// ;\n//\n// primaryExpression : "(" boolExpression ")" \n// | Term\n// ;\n//\n// For flexibility the Term syntax is defined by a separate parse function.\n//\n// Notice that there is no precedence between "AND" and "OR" operators, i.e. they are evaluated from left to right.\n//\n// To evaluate the expression in a given context use code like this:\n//\n// var be = new abego.BoolExp(s, termParseFunc);\n// var result = be.exec(context);\n// \n// @param s the text defining the expression \n// @param parseTermFunc a Function(text,offset,options) that parses the text starting at offset for a "Term" and returns an object with properties {func: Function(context), lastIndex: ...}. func is the function to be used to evaluate the term in the given context.\n// @param options [may be null/undefined] (is also passed to the parseTermFunc)\n// options.defaultOperationIs_OR [Default: false] When true the concatenation of unaryExpressions (without an operator) is interpreted as an "OR", otherwise as an "AND".\n// options.caseSensitive [default: false]\n//\nabego.BoolExp = function(s, parseTermFunc, options) {\n this.s = s;\n var defaultOperationIs_OR = options && options.defaultOperationIs_OR;\n \n var reStart = /\ss*(?:(\s-|not)|(\s())/gi; // group 1: NOT, group2 "("\n var reCloseParenthesis = /\ss*\s)/g; // match )\n var reAndOr = /\ss*(?:(and|\s&\s&)|(or|\s|\s|))/gi; // group 1: AND, group 2: OR\n var reNonWhiteSpace = /\ss*[^\s)\ss]/g;\n \n var reNot_Parenthesis = /\ss*(\s-|not)?(\ss*\s()?/gi;\n \n var parseUnaryExpression = function(offset) {\n reNot_Parenthesis.lastIndex = offset;\n var m = reNot_Parenthesis.exec(s);\n var negate;\n var result;\n if (m && m.index == offset) {\n offset = reNot_Parenthesis.lastIndex;\n negate = m[1];\n if (m[2]) {\n // case: (...)\n var e = parseBoolExpression(offset);\n reCloseParenthesis.lastIndex = e.lastIndex;\n if (!reCloseParenthesis.exec(s))\n throw "Missing ')'";\n result = {func: e.func, lastIndex: reCloseParenthesis.lastIndex};\n }\n }\n if (!result)\n result = parseTermFunc(s,offset,options);\n\n if (negate) {\n result.func = (function(f){return function(context) {return !f(context);}})(result.func);\n // don't mark patterns that are negated\n // (This is essential since the marking may also be used to calculate "ranks". If we\n // would also count the negated matches (i.e. that should not exist) the rank may get too high)\n result.markRE = null;\n }\n return result;\n };\n\n var parseBoolExpression = function(offset) {\n var result = parseUnaryExpression(offset);\n while (1) {\n var l = result.lastIndex;\n reAndOr.lastIndex = l;\n var m = reAndOr.exec(s);\n var isOrCase;\n var nextExp;\n if (m && m.index == l) {\n isOrCase = !m[1];\n nextExp = parseUnaryExpression(reAndOr.lastIndex);\n } else {\n // no "AND" or "OR" found. \n // Maybe it is a concatenations of parseUnaryExpression without operators\n try {\n nextExp = parseUnaryExpression(l);\n } catch (e) {\n // no unary expression follows. We are done\n return result;\n }\n isOrCase = defaultOperationIs_OR;\n }\n result.func = (function(func1, func2, isOrCase) {\n return isOrCase\n ? function(context) {return func1(context) || func2(context);}\n : function(context) {return func1(context) && func2(context);};\n })(result.func,nextExp.func,isOrCase);\n result.lastIndex = nextExp.lastIndex;\n if (!result.markRE)\n result.markRE = nextExp.markRE;\n else if (nextExp.markRE) \n result.markRE = result.markRE + "|" + nextExp.markRE;\n }\n };\n \n var expr = parseBoolExpression(0);\n this.evalFunc = expr.func;\n if (expr.markRE)\n this.markRegExp = new RegExp(expr.markRE, options.caseSensitive ? "mg" : "img");\n}\n\nabego.BoolExp.prototype.exec = function() {\n return this.evalFunc.apply(this,arguments);\n};\n\nabego.BoolExp.prototype.getMarkRegExp = function() {\n return this.markRegExp;\n};\n\nabego.BoolExp.prototype.toString = function() {\n return this.s;\n};\n\n// Class abego.MultiFieldRegExpTester ==================================================================\n//\n// @param fields [optional; Default: ["title","text","tags"]] array of names of fields to be considered\n// @param withExtendedFields [optional; Default: false] when true also extended fields are considered (in addition to the ones given in 'fields')\n//\nabego.MultiFieldRegExpTester = function(re, fields, withExtendedFields) {\n this.re = re;\n this.fields = fields ? fields : ["title","text","tags"];\n this.withExtendedFields = withExtendedFields;\n}\n\n// Returns the name of the first field found that value succeeds the given test,\n// or null when no such field is found\n//\nabego.MultiFieldRegExpTester.prototype.test = function(tiddler) {\n var re = this.re;\n // Check the fields explicitly specified\n for (var i = 0; i < this.fields.length; i++) {\n var s = store.getValue(tiddler, this.fields[i]);\n if (typeof s == "string" && re.test(s))\n return this.fields[i]; \n }\n // Check the extended fields (if required)\n if (this.withExtendedFields) \n return store.forEachField(\n tiddler,\n function(tiddler, fieldName, value) {\n return typeof value == "string" && re.test(value)?fieldName:null;\n }, true);\n \n return null;\n}\n\n// Class abego.TiddlerQuery ==================================================================\n//\n//#import abego.select\n//#import abego.MultiFieldRegExpTester\n//\nabego.TiddlerQuery = function(queryText,caseSensitive,useRegExp,defaultFields,withExtendedFields) {\n if (useRegExp) {\n this.regExp = new RegExp(queryText, caseSensitive ? "mg" : "img");\n this.tester = new abego.MultiFieldRegExpTester(this.regExp, defaultFields, withExtendedFields);\n } else {\n this.expr = new abego.BoolExp(\n queryText,\n abego.parseTiddlerFilterTerm, {\n defaultFields: defaultFields,\n caseSensitive: caseSensitive,\n withExtendedFields: withExtendedFields});\n }\n \n this.getQueryText = function() {\n return queryText;\n };\n this.getUseRegExp = function() {\n return useRegExp;\n };\n this.getCaseSensitive = function() {\n return caseSensitive;\n };\n this.getDefaultFields = function() {\n return defaultFields;\n };\n this.getWithExtendedFields = function() {\n return withExtendedFields;\n };\n}\n\n// Returns true iff the query includes the given tiddler\n//\n// @param tiddler [may be null/undefined]\n//\nabego.TiddlerQuery.prototype.test = function(tiddler) {\n if (!tiddler) return false;\n if (this.regExp) {\n return this.tester.test(tiddler);\n }\n return this.expr.exec(tiddler);\n};\n\n// Returns an array with those tiddlers from the tiddlers array that match the query.\n//\nabego.TiddlerQuery.prototype.filter = function(tiddlers) {\n return abego.select(tiddlers,this.test,this);\n};\n\nabego.TiddlerQuery.prototype.getMarkRegExp = function() {\n if (this.regExp) {\n // Only use the regExp for marking when it does not match the empty string.\n return "".search(this.regExp) >= 0 ? null : this.regExp;\n }\n return this.expr.getMarkRegExp();\n};\n\nabego.TiddlerQuery.prototype.toString = function() {\n return (this.regExp ? this.regExp : this.expr).toString();\n};\n\n// Class abego.PageWiseRenderer ================================================\n//\n// Subclass or instance must implement getItemsPerPage function;\n// They should also implement onPageChanged and refresh the container of the\n// PageWiseRenderer on that event.\n//\n//#import abego.toInt\n//\nabego.PageWiseRenderer = function() {\n this.firstIndexOnPage = 0; // The index of the first item of the lastResults list displayed on the search result page\n};\n\nmerge(abego.PageWiseRenderer.prototype, {\n setItems: function(items) {\n this.items = items;\n this.setFirstIndexOnPage(0);\n },\n \n // Maximum number of pages listed in the navigation bar (before or after the current page)\n //\n getMaxPagesInNavigation: function() {\n return 10;\n },\n \n getItemsCount: function(items) {\n return this.items ? this.items.length : 0;\n },\n \n getCurrentPageIndex: function() {\n return Math.floor(this.firstIndexOnPage / this.getItemsPerPage());\n },\n \n getLastPageIndex: function() {\n return Math.floor((this.getItemsCount()-1) / this.getItemsPerPage())\n },\n \n setFirstIndexOnPage: function(index) {\n this.firstIndexOnPage = Math.min(Math.max(0, index), this.getItemsCount()-1);\n },\n \n getFirstIndexOnPage: function() {\n // Ensure that the firstIndexOnPage is really a page start. \n // This may have become violated when getItemsPerPage has changed,\n // (e.g. when switching between previewText and simple mode.)\n this.firstIndexOnPage = Math.floor(this.firstIndexOnPage / this.getItemsPerPage()) * this.getItemsPerPage();\n \n return this.firstIndexOnPage;\n },\n \n getLastIndexOnPage: function() {\n return Math.min(this.getFirstIndexOnPage()+this.getItemsPerPage()-1, this.getItemsCount()-1);\n },\n \n onPageChanged: function(pageIndex,oldPageIndex) {\n },\n \n renderPage: function(itemRenderer) {\n if (itemRenderer.beginRendering)\n itemRenderer.beginRendering(this);\n try {\n // When there are items found add them to the result page (pagewise)\n if (this.getItemsCount()) {\n // Add the items of the current page\n var lastIndex = this.getLastIndexOnPage();\n var iInPage = -1;\n for (var i=this.getFirstIndexOnPage(); i <= lastIndex; i++) {\n iInPage++;\n \n itemRenderer.render(this,this.items[i],i,iInPage);\n }\n }\n } finally {\n if (itemRenderer.endRendering)\n itemRenderer.endRendering(this);\n }\n },\n \n addPageNavigation: function(place) {\n if (!this.getItemsCount()) return;\n \n var self = this;\n var onNaviButtonClick = function(e) {\n if (!e) var e = window.event;\n \n var pageIndex = abego.toInt(this.getAttribute("page"),0);\n var oldPageIndex = self.getCurrentPageIndex();\n if (pageIndex == oldPageIndex)\n return;\n var index = pageIndex * self.getItemsPerPage();\n self.setFirstIndexOnPage(index);\n self.onPageChanged(pageIndex,oldPageIndex); \n };\n \n var button;\n var currentPageIndex = this.getCurrentPageIndex();\n var lastPageIndex = this.getLastPageIndex();\n if (currentPageIndex > 0) {\n button = createTiddlyButton(place, "Previous", "Go to previous page (Shortcut: Alt-'<')", onNaviButtonClick, "prev");\n button.setAttribute("page",(currentPageIndex-1).toString());\n button.setAttribute("accessKey","<");\n }\n \n for (var i = -this.getMaxPagesInNavigation(); i < this.getMaxPagesInNavigation(); i++) {\n var pageIndex = currentPageIndex+i;\n if (pageIndex < 0) continue;\n if (pageIndex > lastPageIndex) break;\n \n var pageNo = (i+currentPageIndex+1).toString();\n var buttonClass = pageIndex == currentPageIndex ? "currentPage" : "otherPage";\n button = createTiddlyButton(place, pageNo, "Go to page %0".format([pageNo]), onNaviButtonClick, buttonClass);\n button.setAttribute("page",(pageIndex).toString());\n }\n \n if (currentPageIndex < lastPageIndex) {\n button = createTiddlyButton(place, "Next", "Go to next page (Shortcut: Alt-'>')", onNaviButtonClick, "next");\n button.setAttribute("page",(currentPageIndex+1).toString());\n button.setAttribute("accessKey",">");\n }\n }\n});\n\n// Class abego.LimitedTextRenderer ===========================================================\n//\n// Renders a given text, ensuring that a given limit of number of characters \n// is not exceeded.\n//\n// A "markRegExp" may be specified. Substring matching this regular expression \n// ("matched strings") are rendered with the class "marked". \n//\n// if the given text is longer than the limit the matched strings are preferred \n// to be included in the rendered text (with some leading and trailing "context text"). \n// \n// Example:\n// var renderer = new abego.LimitedTextRenderer();\n//\n// var place = ... // a DOM element that should contain the rendered (limited) text\n// var s = "This is another 'Hello World' example, as saying 'Hello' is always nice. So let's say it again: >Hello!<";\n// var maxLen = 50;\n// var markRE = /hello/gi;\n// renderer.render(place,s,maxLen,markRE);\n// \n//#import abego.createEllipsis\n//\nabego.LimitedTextRenderer = function() {\n var minMatchWithContextSize = 40; \n var maxMovementForWordCorrection = 4; // When a "match" context starts or end on a word the context borders may be changed to at most this amount to include or exclude the word.\n \n \n //----------------------------------------------------------------------------\n //\n // Ranges\n //\n // Objects with a "start" and "end" property (not a specific class). \n // \n // In a corresponding "Ranges array" these objects are sorted by their start \n // and no Range object intersects/touches any other in the array.\n //\n //----------------------------------------------------------------------------\n \n // Adds the Range [startIndex,endIndex[ to the ranges, ensuring that the Ranges\n // in the array are sorted by their start and no Range object \n // intersects/touches any other in the array (i.e. possibly the new Range is \n // "merged" with existing ranges)\n //\n // @param ranges array of Range objects\n //\n var addRange = function(ranges, startIndex, endIndex) {\n var n = ranges.length;\n \n // When there are no ranges in ranges, just add it.\n if (n == 0) {\n ranges.push({start: startIndex, end: endIndex});\n return;\n }\n \n var i = 0;\n for (; i < n; i++) {\n var range = ranges[i];\n \n // find the first range that intersects or "touches" [startIndex, endIndex[\n if (range.start <= endIndex && startIndex <= range.end) {\n // Found.\n \n var r;\n // find the first range behind the new range that does not interfere\n var rIndex = i+1;\n for (; rIndex < n; rIndex++) {\n r = ranges[rIndex];\n if (r.start > endIndex || startIndex > range.end) {\n break;\n }\n }\n \n // Replace the ranges i to rIndex-1 with the union of the new range with these ranges.\n var unionStart = startIndex;\n var unionEnd = endIndex;\n for (var j = i; j < rIndex; j++) {\n r = ranges[j];\n unionStart = Math.min(unionStart, r.start);\n unionEnd = Math.max(unionEnd, r.end);\n }\n ranges.splice(i, rIndex-i, {start: unionStart, end: unionEnd});\n return; \n }\n \n // if we found a range R that is right of the new range there is no\n // intersection and we can insert the new range before R.\n if (range.start > endIndex) {\n break;\n }\n }\n \n // When we are here the new range does not interfere with any range in ranges and\n // i is the index of the first range right to it (or ranges.length, when the new range\n // becomes the right most range). \n \n ranges.splice(i, 0, {start: startIndex, end: endIndex});\n };\n \n // Returns the total size of all Ranges in ranges\n //\n var getTotalRangesSize = function(ranges) {\n var totalRangeSize = 0;\n for (var i=0; i < ranges.length; i++) {\n var range = ranges[i];\n totalRangeSize += range.end-range.start;\n }\n return totalRangeSize;\n };\n \n //----------------------------------------------------------------------------\n \n \n var isWordChar = function(c) {\n return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || c == "_";\n };\n \n // Returns the bounds of the word in s around offset as a {start: , end:} object.\n //\n // Returns null when the char at offset is not a word char.\n //\n var getWordBounds = function(s, offset) {\n // Handle the "offset is not in word" case\n if (!isWordChar(s[offset])) return null;\n \n for (var i = offset-1; i >= 0 && isWordChar(s[i]); i--) \n {/*empty*/}\n \n var startIndex = i+1;\n var n = s.length;\n for (i = offset+1; i < n && isWordChar(s[i]); i++) \n {/*empty*/}\n \n return {start: startIndex, end: i};\n };\n \n var moveToWordBorder = function(s, offset, isStartOffset) {\n var wordBounds;\n if (isStartOffset) {\n wordBounds = getWordBounds(s, offset);\n } else {\n if (offset <= 0) return offset;\n wordBounds = getWordBounds(s, offset-1);\n }\n if (!wordBounds) return offset;\n \n if (isStartOffset) {\n if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;\n if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;\n } else {\n if (wordBounds.end <= offset+maxMovementForWordCorrection) return wordBounds.end;\n if (wordBounds.start >= offset-maxMovementForWordCorrection) return wordBounds.start;\n }\n return offset;\n };\n \n \n \n // Splits s into a sequence of "matched" and "unmatched" substrings, using the \n // matchRegExp to do the matching.\n // \n // Returns an array of objects with a "text" property containing the substring text. \n // Substrings that are "matches" also contain a boolean "isMatch" property set to true.\n // \n // @param matchRegExp [may be null] when null no matching is performed and the returned \n // array just contains one item with s as its text\n // \n var getTextAndMatchArray = function(s, matchRegExp) {\n var result = [];\n if (matchRegExp) {\n var startIndex = 0;\n var n = s.length;\n var currentLen = 0;\n do {\n matchRegExp.lastIndex = startIndex;\n var match = matchRegExp.exec(s);\n if (match) {\n if (startIndex < match.index) {\n var t = s.substring(startIndex, match.index);\n result.push({text:t});\n }\n result.push({text:match[0], isMatch:true});\n startIndex = match.index + match[0].length;\n } else {\n result.push({text: s.substr(startIndex)});\n break;\n }\n } while (true);\n } else {\n result.push({text: s});\n }\n return result;\n };\n \n \n \n var getMatchedTextCount = function(textAndMatches) {\n var result = 0;\n for (var i=0; i < textAndMatches.length; i++) {\n if (textAndMatches[i].isMatch) {\n result++;\n }\n }\n return result; \n };\n \n \n \n var getContextRangeAround = function(s, startIndex, endIndex, matchCount, maxLen) {\n // Partition the available space into equal sized areas for each match and one \n // for the text start.\n // But the size should not go below a certain limit\n var size = Math.max(Math.floor(maxLen/(matchCount+1)), minMatchWithContextSize);\n \n // Substract the size of the range to get the size of the context.\n var contextSize = Math.max(size-(endIndex-startIndex), 0);\n // Two thirds of the context should be before the match, one third after.\n var contextEnd = Math.min(Math.floor(endIndex+contextSize/3), s.length);\n var contextStart = Math.max(contextEnd - size, 0);\n \n // If the contextStart/End is inside a word and the end of the word is\n // close move the pointers accordingly to make the text more readable.\n contextStart = moveToWordBorder(s, contextStart, true);\n contextEnd = moveToWordBorder(s, contextEnd, false);\n \n return {start: contextStart, end: contextEnd};\n };\n \n // Get all ranges around matched substrings with their contexts\n //\n var getMatchedTextWithContextRanges = function(textAndMatches, s, maxLen) {\n var ranges = [];\n var matchCount = getMatchedTextCount(textAndMatches);\n var pos = 0;\n for (var i=0; i < textAndMatches.length; i++) {\n var t = textAndMatches[i];\n var text = t.text;\n if (t.isMatch) {\n var range = getContextRangeAround(s, pos, pos+text.length, matchCount, maxLen);\n addRange(ranges, range.start, range.end);\n }\n pos += text.length;\n }\n return ranges;\n };\n \n var fillUpRanges = function(s, ranges, maxLen) {\n var remainingLen = maxLen - getTotalRangesSize(ranges);\n while (remainingLen > 0) {\n if (ranges.length == 0) {\n // No matches added yet. Make one large range.\n addRange(ranges, 0, moveToWordBorder(s, maxLen, false));\n return;\n } else {\n var range = ranges[0];\n var startIndex;\n var maxEndIndex;\n if (range.start == 0) {\n // The first range already starts at the beginning of the string.\n \n // When there is a second range fill to the next range start or to the maxLen.\n startIndex = range.end;\n if (ranges.length > 1) {\n maxEndIndex = ranges[1].start;\n } else {\n // Only one range. Add a range after that with the complete remaining len \n // (corrected to "beautify" the output)\n addRange(ranges, startIndex, moveToWordBorder(s, startIndex+remainingLen, false));\n return;\n }\n } else {\n // There is unused space between the start of the text and the first range.\n startIndex = 0;\n maxEndIndex = range.start;\n }\n var endIndex = Math.min(maxEndIndex, startIndex+remainingLen);\n addRange(ranges, startIndex, endIndex);\n remainingLen -= (endIndex-startIndex);\n }\n }\n };\n \n \n // Write the given ranges of s, using textAndMatches for marking portions of the text.\n //\n var writeRanges = function(place, s, textAndMatches, ranges, maxLen) {\n if (ranges.length == 0) return;\n \n // Processes the text between startIndex and endIndex of the textAndMatches\n // "writes" them (as DOM elements) at the given place, possibly as "marked" text.\n //\n // When endIndex is not the end of the full text an ellisis is appended. \n //\n var writeTextAndMatchRange = function(place, s, textAndMatches, startIndex, endIndex) {\n var t;\n var text;\n \n // find the first text item to write\n var pos = 0;\n var i = 0;\n var offset = 0;\n for (;i < textAndMatches.length; i++) {\n t = textAndMatches[i];\n text = t.text;\n if (startIndex < pos+text.length) {\n offset = startIndex - pos;\n break;\n }\n pos += text.length;\n }\n \n var remainingLen = endIndex - startIndex;\n for (; i < textAndMatches.length && remainingLen > 0; i++) {\n t = textAndMatches[i];\n text = t.text.substr(offset);\n offset = 0;\n if (text.length > remainingLen) text = text.substr(0,remainingLen);\n \n if (t.isMatch) {\n createTiddlyElement(place,"span",null,"marked",text);\n } else {\n createTiddlyText(place, text);\n }\n remainingLen -= text.length;\n }\n \n if (endIndex < s.length) {\n abego.createEllipsis(place);\n }\n };\n \n // When the first range is not at the start of the text write an ellipsis("...")\n // (Ellipses between ranges are written in the writeTextAndMatchRange method)\n if (ranges[0].start > 0) abego.createEllipsis(place);\n \n var remainingLen = maxLen;\n for (var i = 0; i < ranges.length && remainingLen > 0; i++) {\n var range = ranges[i];\n var len = Math.min(range.end - range.start, remainingLen);\n writeTextAndMatchRange(place, s, textAndMatches, range.start, range.start+len);\n remainingLen -= len;\n }\n };\n \n this.render = function(place,s,maxLen,markRegExp) {\n if (s.length < maxLen) maxLen = s.length;\n \n var textAndMatches = getTextAndMatchArray(s, markRegExp);\n \n var ranges = getMatchedTextWithContextRanges(textAndMatches, s, maxLen);\n \n // When the maxLen is not yet reached add more ranges \n // starting from the beginning until either maxLen or \n // the end of the string is reached.\n fillUpRanges(s, ranges, maxLen);\n \n writeRanges(place, s, textAndMatches, ranges, maxLen);\n };\n};\n\n\n\n(function() {\n\nfunction alertAndThrow(msg) {\n alert(msg);\n throw msg;\n};\n\nif (version.major < 2 || (version.major == 2 && version.minor < 1)) \n alertAndThrow("YourSearchPlugin requires TiddlyWiki 2.1 or newer.\sn\snCheck the archive for YourSearch plugins\snsupporting older versions of TiddlyWiki.\sn\snArchive: http://tiddlywiki.abego-software.de/archive");\n\nabego.YourSearch = {};\n\n//----------------------------------------------------------------------------\n// The Search Core\n//----------------------------------------------------------------------------\n\n// Model Variables\nvar lastResults; // Array of tiddlers that matched the last search\nvar lastQuery; // The last Search query (TiddlerQuery)\n\nvar setLastResults = function(array) {\n lastResults = array;\n};\n\nvar getLastResults = function() {\n return lastResults ? lastResults : [];\n};\n\nvar getLastResultsCount = function() {\n return lastResults ? lastResults.length : 0;\n};\n\n// Standard Ranking Weights\nvar matchInTitleWeight = 4;\nvar precisionInTitleWeight = 10;\nvar matchInTagsWeight = 2;\n\nvar getMatchCount = function(s, re) {\n var m = s.match(re);\n return m ? m.length : 0;\n};\n\nvar standardRankFunction = function(tiddler, query) { \n // Count the matches in the title and the tags\n var markRE = query.getMarkRegExp();\n if (!markRE) return 1;\n \n var matchesInTitle = tiddler.title.match(markRE);\n var nMatchesInTitle = matchesInTitle ? matchesInTitle.length : 0;\n var nMatchesInTags = getMatchCount(tiddler.getTags(), markRE);\n\n // Calculate the "precision" of the matches in the title as the ratio of\n // the length of the matches to the total length of the title.\n var lengthOfMatchesInTitle = matchesInTitle ? matchesInTitle.join("").length : 0;\n var precisionInTitle = tiddler.title.length > 0 ? lengthOfMatchesInTitle/tiddler.title.length : 0;\n \n // calculate a weighted score\n var rank= nMatchesInTitle * matchInTitleWeight \n + nMatchesInTags * matchInTagsWeight \n + precisionInTitle * precisionInTitleWeight \n + 1;\n\n return rank;\n};\n\n// @return Tiddler[]\n//\nvar findMatches = function(store, searchText,caseSensitive,useRegExp,sortField,excludeTag) {\n lastQuery = null;\n \n var candidates = store.reverseLookup("tags",excludeTag,false);\n try {\n var defaultFields = [];\n if (config.options.chkSearchInTitle) defaultFields.push("title");\n if (config.options.chkSearchInText) defaultFields.push("text");\n if (config.options.chkSearchInTags) defaultFields.push("tags");\n lastQuery = new abego.TiddlerQuery(\n searchText,caseSensitive, useRegExp,defaultFields,config.options.chkSearchExtendedFields); \n } catch (e) {\n // when an invalid query is given no tiddlers are matched\n return [];\n }\n\n var results = lastQuery.filter(candidates);\n\n // Rank the results\n var rankFunction = abego.YourSearch.getRankFunction();\n for (var i = 0; i < results.length; i++) {\n var tiddler = results[i];\n var rank = rankFunction(tiddler, lastQuery);\n // Add the rank information to the tiddler.\n // This is used during the sorting, but it may also\n // be used in the result, e.g. to display some "relevance" \n // information in the result \n tiddler.searchRank = rank; \n }\n \n // sort the result, taking care of the rank and the sortField \n if(!sortField) {\n sortField = "title";\n }\n \n var sortFunction = function (a,b) {\n var searchRankDiff = a.searchRank - b.searchRank;\n if (searchRankDiff == 0) {\n if (a[sortField] == b[sortField]) {\n return(0); \n } else {\n return (a[sortField] < b[sortField]) ? -1 : +1; \n }\n } else {\n return (searchRankDiff > 0) ? -1 : +1; \n }\n };\n results.sort(sortFunction);\n return results;\n};\n\n//----------------------------------------------------------------------------\n// The Search UI (Result page)\n//----------------------------------------------------------------------------\n\n\n// Visual appearance of the result page\nvar maxCharsInTitle = 80;\nvar maxCharsInTags = 50;\nvar maxCharsInText = 250;\nvar maxCharsInField = 50;\n\nvar itemsPerPageDefault = 25; // Default maximum number of items on one search result page\nvar itemsPerPageWithPreviewDefault = 10; // Default maximum number of items on one search result page when PreviewText is on\n\n// DOM IDs\nvar yourSearchResultID = "yourSearchResult";\nvar yourSearchResultItemsID = "yourSearchResultItems";\n\nvar lastSearchText; // The last search text, as passed to findMatches\n\nvar resultElement; // The (popup) DOM element containing the search result [may be null]\nvar searchInputField; // The "search" input field\nvar searchButton; // The "search" button\nvar lastNewTiddlerButton;\n\nvar initStylesheet = function() {\n if (version.extensions.YourSearchPlugin.styleSheetInited) \n return;\n \n version.extensions.YourSearchPlugin.styleSheetInited = true;\n setStylesheet(store.getTiddlerText("YourSearchStyleSheet"),"yourSearch");\n}\n\nvar isResultOpen = function() {\n return resultElement != null && resultElement.parentNode == document.body;\n};\n\nvar closeResult = function() {\n if (isResultOpen()) {\n document.body.removeChild(resultElement);\n }\n};\n\n// Closes the Search Result window and displays the tiddler \n// defined by the "tiddlyLink" attribute of this element\n//\nvar closeResultAndDisplayTiddler = function(e)\n{\n closeResult();\n \n var title = this.getAttribute("tiddlyLink");\n if(title) {\n var withHilite = this.getAttribute("withHilite");\n var oldHighlightHack = highlightHack;\n if (withHilite && withHilite=="true" && lastQuery) {\n highlightHack = lastQuery.getMarkRegExp();\n }\n story.displayTiddler(this,title);\n highlightHack = oldHighlightHack;\n }\n return(false);\n};\n\n// Adjusts the resultElement's size and position, relative to the search input field.\n//\nvar adjustResultPositionAndSize = function() {\n if (!searchInputField) return;\n \n var root = searchInputField;\n \n // Position the result below the root and resize it if necessary.\n var rootLeft = findPosX(root);\n var rootTop = findPosY(root);\n var rootHeight = root.offsetHeight;\n var popupLeft = rootLeft;\n var popupTop = rootTop + rootHeight;\n\n // Make sure the result is not wider than the window\n var winWidth = findWindowWidth();\n if (winWidth < resultElement.offsetWidth) {\n resultElement.style.width = (winWidth - 100)+"px";\n winWidth = findWindowWidth();\n }\n\n // Ensure that the left and right of the result are not\n // clipped by the window. Move it to the left or right, if necessary. \n var popupWidth = resultElement.offsetWidth;\n if(popupLeft + popupWidth > winWidth)\n popupLeft = winWidth - popupWidth-30;\n if (popupLeft < 0) popupLeft = 0;\n \n // Do the actual moving\n resultElement.style.left = popupLeft + "px";\n resultElement.style.top = popupTop + "px";\n resultElement.style.display = "block";\n};\n\nvar scrollVisible = function() {\n // Scroll the window to make the result page (and the search Input field) visible.\n if (resultElement) window.scrollTo(0,ensureVisible(resultElement));\n if (searchInputField) window.scrollTo(0,ensureVisible(searchInputField));\n};\n\n// Makes sure the result page has a good size and position and visible\n// (may scroll the window)\n//\nvar ensureResultIsDisplayedNicely = function() {\n adjustResultPositionAndSize();\n scrollVisible();\n};\n\n\n\nvar indexInPage; // The index (in the current page) of the tiddler currently rendered.\nvar currentTiddler; // While rendering the page the tiddler that is currently rendered.\n\nvar pager = new abego.PageWiseRenderer();\n\nvar MyItemRenderer = function(parent) {\n // Load the template how to display the items that represent a found tiddler\n this.itemHtml = store.getTiddlerText("YourSearchItemTemplate");\n if (!this.itemHtml) alertAndThrow("YourSearchItemTemplate not found");\n \n // Locate the node that shall contain the list of found tiddlers\n this.place = document.getElementById(yourSearchResultItemsID);\n if(!this.place)\n this.place = createTiddlyElement(parent,"div",yourSearchResultItemsID);\n};\n\nmerge(MyItemRenderer.prototype,{\n render: function(pager,object,index,indexOnPage) {\n // Define global variables, referenced by macros during applyHtmlMacros\n indexInPage = indexOnPage;\n currentTiddler = object;\n \n var item = createTiddlyElement(this.place,"div",null, "yourSearchItem");\n item.innerHTML = this.itemHtml;\n applyHtmlMacros(item,null);\n refreshElements(item,null);\n },\n\n endRendering: function(pager) {\n // The currentTiddler must only be defined while rendering the found tiddlers\n currentTiddler = null;\n }\n});\n\n// Refreshes the content of the result with the current search result\n// of the selected page.\n//\n// Assumes that the result is already open. \n//\nvar refreshResult = function() {\n if (!resultElement || !searchInputField) return;\n\n // Load the template for the YourSearchResult\n var html = store.getTiddlerText("YourSearchResultTemplate");\n if (!html) html = "<b>Tiddler YourSearchResultTemplate not found</b>";\n resultElement.innerHTML = html;\n\n // Expand the template macros etc.\n applyHtmlMacros(resultElement,null);\n refreshElements(resultElement,null);\n \n var itemRenderer = new MyItemRenderer(resultElement);\n pager.renderPage(itemRenderer);\n\n ensureResultIsDisplayedNicely();\n};\n\npager.getItemsPerPage = function() {\n var n = (config.options.chkPreviewText) \n ? abego.toInt(config.options.txtItemsPerPageWithPreview, itemsPerPageWithPreviewDefault) \n : abego.toInt(config.options.txtItemsPerPage, itemsPerPageDefault);\n return (n > 0) ? n : 1;\n};\n\npager.onPageChanged = function() {\n refreshResult();\n};\n\nvar showResult = function() {\n if (!resultElement) {\n resultElement = createTiddlyElement(document.body,"div",yourSearchResultID,"yourSearchResult");\n } else if (resultElement.parentNode != document.body) {\n document.body.appendChild(resultElement);\n }\n\n refreshResult();\n};\n\n\nvar reopenResultIfApplicable = function() {\n if (searchInputField == null || !config.options.chkUseYourSearch) return;\n \n if ((searchInputField.value == lastSearchText) && lastSearchText && !isResultOpen()) {\n // For speedup we check re-use the previously created resultElement, if possible.\n if (resultElement && (resultElement.parentNode != document.body)) {\n document.body.appendChild(resultElement);\n ensureResultIsDisplayedNicely();\n } else {\n showResult();\n }\n }\n};\n\n\nvar invalidateResult = function() {\n closeResult();\n resultElement = null;\n lastSearchText = null;\n};\n\n\n\n//-------------------------------------------------------------------------\n// Close the search result page when the user clicks on the document\n// (and not into the searchInputField, on the search button or in the result)\n// or presses the ESC key\n\n// Returns true if e is either self or a descendant (child, grandchild,...) of self.\n//\n// @param self DOM:Element\n// @param e DOM:Element or null\n//\nvar isDescendantOrSelf = function(self, e) {\n while (e != null) {\n if (self == e) return true;\n e = e.parentNode;\n }\n return false;\n};\n\nvar onDocumentClick = function(e) {\n if (e.target == searchInputField) return; \n if (e.target == searchButton) return; \n if (resultElement && isDescendantOrSelf(resultElement, e.target)) return; \n \n closeResult();\n};\n\nvar onDocumentKeyup = function(e) {\n // Close the search result page when the user presses "ESC"\n if (e.keyCode == 27) closeResult();\n};\naddEvent(document,"click",onDocumentClick);\naddEvent(document,"keyup",onDocumentKeyup);\n\n\n// Our Search Macro Hijack Function ==========================================\n\n// Helper\nvar myStorySearch = function(text,useCaseSensitive,useRegExp)\n{\n lastSearchText = text;\n setLastResults(findMatches(store, text,useCaseSensitive,useRegExp,"title","excludeSearch"));\n\n highlightHack = lastQuery ? lastQuery.getMarkRegExp() : null;\n pager.setItems(getLastResults());\n showResult();\n highlightHack = null;\n};\n\n\nvar myMacroSearchHandler = function(place,macroName,params,wikifier,paramString,tiddler)\n{\n initStylesheet();\n\n lastSearchText = "";\n var searchTimeout = null;\n var doSearch = function(txt)\n {\n if (config.options.chkUseYourSearch)\n myStorySearch(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);\n else\n story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);\n lastSearchText = txt.value;\n };\n var clickHandler = function(e)\n {\n doSearch(searchInputField);\n return false;\n };\n var keyHandler = function(e)\n {\n if (!e) var e = window.event;\n searchInputField = this;\n switch(e.keyCode)\n {\n case 13:\n if (e.ctrlKey && lastNewTiddlerButton && isResultOpen())\n lastNewTiddlerButton.onclick.apply(lastNewTiddlerButton,[e]);\n else\n doSearch(this);\n break;\n case 27:\n // When the result is open, close it, \n // otherwise clear the content of the input field\n if (isResultOpen()) {\n closeResult();\n } else {\n this.value = "";\n clearMessage();\n }\n break;\n }\n if (String.fromCharCode(e.keyCode) == this.accessKey || e.altKey) \n {\n reopenResultIfApplicable();\n }\n\n if(this.value.length<3 && searchTimeout) clearTimeout(searchTimeout);\n if(this.value.length > 2)\n {\n if (this.value != lastSearchText)\n {\n if (!config.options.chkUseYourSearch || config.options.chkSearchAsYouType)\n {\n if(searchTimeout)\n clearTimeout(searchTimeout);\n var txt = this;\n searchTimeout = setTimeout(function() {doSearch(txt);},500);\n }\n }\n else\n {\n if(searchTimeout)\n clearTimeout(searchTimeout);\n }\n };\n if (this.value.length == 0) \n {\n closeResult();\n }\n };\n\n\n var focusHandler = function(e)\n {\n this.select();\n clearMessage();\n reopenResultIfApplicable();\n };\n\n \n var args = paramString.parseParams("list",null,true);\n var buttonAtRight = getFlag(args, "buttonAtRight");\n var sizeTextbox = getParam(args, "sizeTextbox", this.sizeTextbox);\n \n var btn;\n if (!buttonAtRight)\n btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);\n \n var txt = createTiddlyElement(place,"input",null,null,null);\n if(params[0])\n txt.value = params[0];\n txt.onkeyup = keyHandler;\n txt.onfocus = focusHandler;\n txt.setAttribute("size",sizeTextbox);\n txt.setAttribute("accessKey",this.accessKey);\n txt.setAttribute("autocomplete","off");\n if(config.browser.isSafari)\n {\n txt.setAttribute("type","search");\n txt.setAttribute("results","5");\n }\n else\n txt.setAttribute("type","text");\n\n if (buttonAtRight)\n btn = createTiddlyButton(place,this.label,this.prompt,clickHandler);\n\n searchInputField = txt;\n searchButton = btn;\n};\n\n//----------------------------------------------------------------------------\n// Support for Macros\n//----------------------------------------------------------------------------\n\nvar openAllFoundTiddlers = function() {\n closeResult();\n var results = getLastResults();\n var n = results.length;\n if (n) {\n var titles=[];\n for(var i = 0; i<n; i++)\n titles.push(results[i].title);\n story.displayTiddlers(null,titles);\n }\n};\n\nvar createOptionWithRefresh = function(place, optionParams, wikifier,tiddler) {\n invokeMacro(place,"option",optionParams,wikifier,tiddler);\n // The option macro appended the component at the end of the place.\n var elem = place.lastChild;\n var oldOnClick = elem.onclick;\n elem.onclick = function(e) {\n var result = oldOnClick.apply(this, arguments);\n refreshResult();\n return result;\n };\n return elem;\n};\n\nvar removeTextDecoration = function(s) {\n var removeThis = ["''", "{{{", "}}}", "//", "<<<", "/***", "***/"];\n var reText = "";\n for (var i = 0; i < removeThis.length; i++) {\n if (i != 0) reText += "|";\n reText += "("+removeThis[i].escapeRegExp()+")";\n }\n return s.replace(new RegExp(reText, "mg"), "").trim();\n};\n\n\n\n// Returns the "shortcut number" of the currentTiddler. \n// I.e. When the user presses Alt-n the given tiddler is opened/display.\n//\n// @return 0-9 or -1 when no number is defined\n//\nvar getShortCutNumber = function() {\n var i = indexInPage;\n return (i >= 0 && i <= 9) \n ? (i < 9 ? (i+1) : 0)\n : -1;\n};\n\nvar limitedTextRenderer = new abego.LimitedTextRenderer();\nvar renderLimitedText = function(place, s, maxLen) {\n limitedTextRenderer.render(place,s,maxLen,lastQuery.getMarkRegExp())\n}\n\n// When any tiddler are changed reset the result.\n// \nvar oldTiddlyWikiSaveTiddler = TiddlyWiki.prototype.saveTiddler;\nTiddlyWiki.prototype.saveTiddler = function(title,newTitle,newBody,modifier,modified,tags,fields) {\n oldTiddlyWikiSaveTiddler.apply(this, arguments);\n invalidateResult();\n};\nvar oldTiddlyWikiRemoveTiddler = TiddlyWiki.prototype.removeTiddler;\nTiddlyWiki.prototype.removeTiddler = function(title) {\n oldTiddlyWikiRemoveTiddler.apply(this, arguments);\n invalidateResult();\n};\n\n//----------------------------------------------------------------------------\n// Macros\n//----------------------------------------------------------------------------\n\n// ====Macro yourSearch ================================================\n\nconfig.macros.yourSearch = {\n // Standard Properties\n label: "yourSearch",\n prompt: "Gives access to the current/last YourSearch result",\n \n handler: function(place,macroName,params,wikifier,paramString,tiddler) {\n if (params.length == 0) return;\n \n var name = params[0];\n var func = config.macros.yourSearch.funcs[name];\n if (func) func(place,macroName,params,wikifier,paramString,tiddler);\n },\n \n tests: {\n "true" : function() {return true;},\n "false" : function() {return false;},\n "found" : function() {return getLastResultsCount() > 0;},\n "previewText" : function() {return config.options.chkPreviewText;}\n },\n\n funcs: {\n itemRange: function(place) {\n if (getLastResultsCount()) {\n var lastIndex = pager.getLastIndexOnPage();\n var s = "%0 - %1".format([pager.getFirstIndexOnPage()+1,lastIndex+1]);\n createTiddlyText(place, s);\n }\n },\n \n count: function(place) {\n createTiddlyText(place, getLastResultsCount().toString());\n },\n \n query: function(place) {\n if (lastQuery) {\n createTiddlyText(place, lastQuery.toString());\n }\n },\n \n version: function(place) {\n var t = "YourSearch %0.%1.%2".format(\n [version.extensions.YourSearchPlugin.major, \n version.extensions.YourSearchPlugin.minor, \n version.extensions.YourSearchPlugin.revision]);\n var e = createTiddlyElement(place, "a");\n e.setAttribute("href", "http://tiddlywiki.abego-software.de/#YourSearchPlugin");\n e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">'+t+'<font>';\n },\n \n copyright: function(place) {\n var e = createTiddlyElement(place, "a");\n e.setAttribute("href", "http://www.abego-software.de");\n e.innerHTML = '<font color="black" face="Arial, Helvetica, sans-serif">&copy; 2005-2006 <b><font color="red">abego</font></b> Software<font>';\n },\n \n newTiddlerButton: function(place) {\n if (lastQuery) {\n var r = abego.parseNewTiddlerCommandLine(lastQuery.getQueryText());\n var btn = config.macros.newTiddler.createNewTiddlerButton(place,r.title,r.params,"new tiddler","Create a new tiddler based on search text. (Shortcut: Ctrl-Enter; Separators: '.', '#')",null,"text"); \n // Close the result before the new tiddler is created.\n var oldOnClick = btn.onclick;\n btn.onclick = function() {\n closeResult();\n oldOnClick.apply(this,arguments);\n }\n lastNewTiddlerButton = btn;\n }\n },\n \n linkButton: function(place,macroName,params,wikifier,paramString,tiddler) {\n if (params < 2) return;\n \n var tiddlyLink = params[1];\n var text = params < 3 ? tiddlyLink : params[2];\n var tooltip = params < 4 ? text : params[3];\n var accessKey = params < 5 ? null : params[4];\n \n var btn = createTiddlyButton(place,text,tooltip,closeResultAndDisplayTiddler,null,null, accessKey);\n btn.setAttribute("tiddlyLink",tiddlyLink);\n },\n \n closeButton: function(place,macroName,params,wikifier,paramString,tiddler) {\n var button = createTiddlyButton(place, "close", "Close the Search Results (Shortcut: ESC)", closeResult);\n },\n \n openAllButton: function(place,macroName,params,wikifier,paramString,tiddler) {\n var n = getLastResultsCount();\n if (n == 0) return;\n \n var title = n == 1 ? "open tiddler" : "open all %0 tiddlers".format([n]);\n var button = createTiddlyButton(place, title, "Open all found tiddlers (Shortcut: Alt-O)", openAllFoundTiddlers);\n button.setAttribute("accessKey","O");\n },\n \n naviBar: function(place,macroName,params,wikifier,paramString,tiddler) {\n pager.addPageNavigation(place);\n },\n \n "if": function(place,macroName,params,wikifier,paramString,tiddler) {\n if (params.length < 2) return;\n \n var testName = params[1];\n var negate = (testName == "not");\n if (negate) {\n if (params.length < 3) return;\n testName = params[2];\n }\n \n var test = config.macros.yourSearch.tests[testName];\n var showIt = false;\n try {\n if (test) {\n showIt = test(place,macroName,params,wikifier,paramString,tiddler) != negate;\n } else {\n // When no predefined test is specified try to evaluate it as a JavaScript expression.\n showIt = (!eval(testName)) == negate;\n }\n } catch (ex) {\n }\n \n if (!showIt) {\n place.style.display="none";\n }\n },\n \n chkPreviewText: function(place,macroName,params,wikifier,paramString,tiddler) {\n var optionParams = params.slice(1).join(" ");\n \n var elem = createOptionWithRefresh(place, "chkPreviewText", wikifier,tiddler);\n elem.setAttribute("accessKey", "P");\n elem.title = "Show text preview of found tiddlers (Shortcut: Alt-P)"; \n return elem;\n }\n }\n};\n\n\n// ====Macro foundTiddler ================================================\n\nconfig.macros.foundTiddler = {\n // Standard Properties\n label: "foundTiddler",\n prompt: "Provides information on the tiddler currently processed on the YourSearch result page",\n \n handler: function(place,macroName,params,wikifier,paramString,tiddler) {\n var name = params[0];\n var func = config.macros.foundTiddler.funcs[name];\n if (func) func(place,macroName,params,wikifier,paramString,tiddler);\n },\n \n funcs: {\n title: function(place,macroName,params,wikifier,paramString,tiddler) {\n if (!currentTiddler) return;\n \n var shortcutNumber = getShortCutNumber();\n var tooltip = shortcutNumber >= 0 \n ? "Open tiddler (Shortcut: Alt-%0)".format([shortcutNumber.toString()])\n : "Open tiddler";\n \n var btn = createTiddlyButton(place,null,tooltip,closeResultAndDisplayTiddler,null);\n btn.setAttribute("tiddlyLink",currentTiddler.title);\n btn.setAttribute("withHilite","true");\n \n renderLimitedText(btn, currentTiddler.title, maxCharsInTitle);\n \n if (shortcutNumber >= 0) {\n btn.setAttribute("accessKey",shortcutNumber.toString());\n }\n },\n \n tags: function(place,macroName,params,wikifier,paramString,tiddler) {\n if (!currentTiddler) return;\n \n renderLimitedText(place, currentTiddler.getTags(), maxCharsInTags);\n },\n \n text: function(place,macroName,params,wikifier,paramString,tiddler) {\n if (!currentTiddler) return;\n \n renderLimitedText(place, removeTextDecoration(currentTiddler.text), maxCharsInText);\n },\n \n field: function(place,macroName,params,wikifier,paramString,tiddler) {\n if (!currentTiddler) return;\n var name = params[1];\n var len = params.length > 2 ? abego.toInt(params[2],maxCharsInField) : maxCharsInField;\n var v = store.getValue(currentTiddler,name);\n if (v)\n renderLimitedText(place, removeTextDecoration(v), len);\n },\n \n // Renders the "shortcut number" of the current tiddler, to indicate to the user\n // what number to "Alt-press" to open the tiddler.\n //\n number: function(place,macroName,params,wikifier,paramString,tiddler) {\n var numberToDisplay = getShortCutNumber();\n if (numberToDisplay >= 0) {\n var text = "%0)".format([numberToDisplay.toString()]);\n createTiddlyElement(place,"span",null,"shortcutNumber",text);\n }\n }\n }\n};\n\n\n//----------------------------------------------------------------------------\n// Configuration Stuff\n//----------------------------------------------------------------------------\n\nvar opts = {chkUseYourSearch:true,\n chkPreviewText:true,\n chkSearchAsYouType:true,\n chkSearchInTitle:true,\n chkSearchInText:true,\n chkSearchInTags:true,\n chkSearchExtendedFields:true,\n txtItemsPerPage:itemsPerPageDefault,\n txtItemsPerPageWithPreview:itemsPerPageWithPreviewDefault};\nfor (var n in opts) \n if (config.options[n] == undefined) config.options[n] = opts[n];\n\n\n\n\n//----------------------------------------------------------------------------\n// Shadow Tiddlers\n//----------------------------------------------------------------------------\n\nconfig.shadowTiddlers.AdvancedOptions += "\sn<<option chkUseYourSearch>> Use 'Your Search' //([[more options|YourSearch Options]]) ([[help|YourSearch Help]])// ";\n\nconfig.shadowTiddlers["YourSearch Help"] =\n"!Field Search\snWith the Field Search you can restrict your search to certain fields of a tiddler, e.g"+\n" only search the tags or only the titles. The general form is //fieldname//'':''//textToSearch// (e."+\n"g. {{{title:intro}}}). In addition one-character shortcuts are also supported for the standard field"+\n"s {{{title}}}, {{{text}}} and {{{tags}}}:\sn|!What you want|!What you type|!Example|\sn|Search ''titles "+\n"only''|start word with ''!''|{{{!jonny}}} (shortcut for {{{title:jonny}}})|\sn|Search ''contents/text "+\n"only''|start word with ''%''|{{{%football}}} (shortcut for {{{text:football}}})|\sn|Search ''tags only"+\n"''|start word with ''#''|{{{#Plugin}}} (shortcut for {{{tags:Plugin}}})|\sn\snUsing this feature you may"+\n" also search the extended fields (\s"Metadata\s") introduced with TiddlyWiki 2.1, e.g. use {{{priority:1"+\n"}}} to find all tiddlers with the priority field set to \s"1\s".\sn\snYou may search a word in more than one"+\n" field. E.g. {{{!#Plugin}}} (or {{{title:tags:Plugin}}} in the \s"long form\s") finds tiddlers containin"+\n"g \s"Plugin\s" either in the title or in the tags (but does not look for \s"Plugin\s" in the text). \sn\sn!Boole"+\n"an Search\snThe Boolean Search is useful when searching for multiple words.\sn|!What you want|!What you "+\n"type|!Example|\sn|''All words'' must exist|List of words|{{{jonny jeremy}}} (or {{{jonny and jeremy}}}"+\n")|\sn|''At least one word'' must exist|Separate words by ''or''|{{{jonny or jeremy}}}|\sn|A word ''must "+\n"not exist''|Start word with ''-''|{{{-jonny}}} (or {{{not jonny}}})|\sn\sn''Note:'' When you specify two"+\n" words, separated with a space, YourSearch finds all tiddlers that contain both words, but not neces"+\n"sarily next to each other. If you want to find a sequence of word, e.g. '{{{John Brown}}}', you need"+\n" to put the words into quotes. I.e. you type: {{{\s"john brown\s"}}}.\sn\snUsing parenthesis you may change "+\n"the default \s"left to right\s" evaluation of the boolean search. E.g. {{{not (jonny or jeremy)}}} finds"+\n" all tiddlers that contain neither \s"jonny\s" nor \s"jeremy. In contrast to this {{{not jonny or jeremy}}"+\n"} (i.e. without parenthesis) finds all tiddlers that either don't contain \s"jonny\s" or that contain \s"j"+\n"eremy\s".\sn\sn!'Exact Word' Search\snBy default a search result all matches that 'contain' the searched tex"+\n"t. E.g. if you search for {{{Task}}} you will get all tiddlers containing 'Task', but also '~Complet"+\n"edTask', '~TaskForce' etc.\sn\snIf you only want to get the tiddlers that contain 'exactly the word' you"+\n" need to prefix it with a '='. E.g. typing '=Task' will find the tiddlers that contain the word 'Tas"+\n"k', ignoring words that just contain 'Task' as a substring.\sn\sn!~CaseSensitiveSearch and ~RegExpSearch"+\n"\snThe standard search options ~CaseSensitiveSearch and ~RegExpSearch are fully supported by YourSearc"+\n"h. However when ''~RegExpSearch'' is on Filtered and Boolean Search are disabled.\sn\snIn addition you m"+\n"ay do a \s"regular expression\s" search even with the ''~RegExpSearch'' set to false by directly enterin"+\n"g the regular expression into the search field, framed with {{{/.../}}}. \sn\snExample: {{{/m[ae][iy]er/"+\n"}}} will find all tiddlers that contain either \s"maier\s", \s"mayer\s", \s"meier\s" or \s"meyer\s".\sn\sn!~JavaScript E"+\n"xpression Filtering\snIf you are familiar with JavaScript programming and know some TiddlyWiki interna"+\n"ls you may also use JavaScript expression for the search. Just enter a JavaScript boolean expression"+\n" into the search field, framed with {{{ { ... } }}}. In the code refer to the variable tiddler and e"+\n"valuate to {{{true}}} when the given tiddler should be included in the result. \sn\snExample: {{{ { tidd"+\n"ler.modified > new Date(\s"Jul 4, 2005\s")} }}} returns all tiddler modified after July 4th, 2005.\sn\sn!Com"+\n"bined Search\snYou are free to combine the various search options. \sn\sn''Examples''\sn|!What you type|!Res"+\n"ult|\sn|{{{!jonny !jeremy -%football}}}|all tiddlers with both {{{jonny}}} and {{{jeremy}}} in its tit"+\n"les, but no {{{football}}} in content.|\sn|{{{#=Task}}}|All tiddlers tagged with 'Task' (the exact wor"+\n"d). Tags named '~CompletedTask', '~TaskForce' etc. are not considered.|\sn\sn!Access Keys\snYou are encour"+\n"aged to use the access keys (also called \s"shortcut\s" keys) for the most frequently used operations. F"+\n"or quick reference these shortcuts are also mentioned in the tooltip for the various buttons etc.\sn\sn|"+\n"!Key|!Operation|\sn|{{{Alt-F}}}|''The most important keystroke'': It moves the cursor to the search in"+\n"put field so you can directly start typing your query. Pressing {{{Alt-F}}} will also display the pr"+\n"evious search result. This way you can quickly display multiple tiddlers using \s"Press {{{Alt-F}}}. S"+\n"elect tiddler.\s" sequences.|\sn|{{{ESC}}}|Closes the [[YourSearch Result]]. When the [[YourSearch Resul"+\n"t]] is already closed and the cursor is in the search input field the field's content is cleared so "+\n"you start a new query.|\sn|{{{Alt-1}}}, {{{Alt-2}}},... |Pressing these keys opens the first, second e"+\n"tc. tiddler from the result list.|\sn|{{{Alt-O}}}|Opens all found tiddlers.|\sn|{{{Alt-P}}}|Toggles the "+\n"'Preview Text' mode.|\sn|{{{Alt-'<'}}}, {{{Alt-'>'}}}|Displays the previous or next page in the [[Your"+\n"Search Result]].|\sn|{{{Return}}}|When you have turned off the 'as you type' search mode pressing the "+\n"{{{Return}}} key actually starts the search (as does pressing the 'search' button).|\sn\sn//If some of t"+\n"hese shortcuts don't work for you check your browser if you have other extensions installed that alr"+\n"eady \s"use\s" these shortcuts.//";\n\nconfig.shadowTiddlers["YourSearch Options"] =\n"|>|!YourSearch Options|\sn|>|<<option chkUseYourSearch>> Use 'Your Search'|\sn|!|<<option chkPreviewText"+\n">> Show Text Preview|\sn|!|<<option chkSearchAsYouType>> 'Search As You Type' Mode (No RETURN required"+\n" to start search)|\sn|!|Default Search Filter:<<option chkSearchInTitle>>Title ('!') <<option chk"+\n"SearchInText>>Text ('%') <<option chkSearchInTags>>Tags ('#') <<option chkSearchExtendedFiel"+\n"ds>>Extended Fields<html><br><font size=\s"-2\s">The fields of a tiddlers that are searched when you don"+\n"'t explicitly specify a filter in the search text <br>(Explictly specify fields using one or more '!"+\n"', '%', '#' or 'fieldname:' prefix before the word/text to find).</font></html>|\sn|!|Number of items "+\n"on search result page: <<option txtItemsPerPage>>|\sn|!|Number of items on search result page with pre"+\n"view text: <<option txtItemsPerPageWithPreview>>|\sn";\n \nconfig.shadowTiddlers["YourSearchStyleSheet"] = \n"/***\sn!~YourSearchResult Stylesheet\sn***/\sn/*{{{*/\sn.yourSearchResult {\sn\stposition: absolute;\sn\stwidth: 800"+\n"px;\sn\sn\stpadding: 0.2em;\sn\stlist-style: none;\sn\stmargin: 0;\sn\sn\stbackground: #ffd;\sn\stborder: 1px solid DarkGra"+\n"y;\sn}\sn\sn/*}}}*/\sn/***\sn!!Summary Section\sn***/\sn/*{{{*/\sn.yourSearchResult .summary {\sn\stborder-bottom-width:"+\n" thin;\sn\stborder-bottom-style: solid;\sn\stborder-bottom-color: #999999;\sn\stpadding-bottom: 4px;\sn}\sn\sn.yourSea"+\n"rchRange, .yourSearchCount, .yourSearchQuery {\sn\stfont-weight: bold;\sn}\sn\sn.yourSearchResult .summary ."+\n"button {\sn\stfont-size: 10px;\sn\sn\stpadding-left: 0.3em;\sn\stpadding-right: 0.3em;\sn}\sn\sn.yourSearchResult .summa"+\n"ry .chkBoxLabel {\sn\stfont-size: 10px;\sn\sn\stpadding-right: 0.3em;\sn}\sn\sn/*}}}*/\sn/***\sn!!Items Area\sn***/\sn/*{{{*"+\n"/\sn.yourSearchResult .marked {\sn\stbackground: none;\sn\stfont-weight: bold;\sn}\sn\sn.yourSearchItem {\sn\stmargin-to"+\n"p: 2px;\sn}\sn\sn.yourSearchNumber {\sn\stcolor: #808080;\sn}\sn\sn\sn.yourSearchTags {\sn\stcolor: #008000;\sn}\sn\sn.yourSearc"+\n"hText {\sn\stcolor: #808080;\sn\stmargin-bottom: 6px;\sn}\sn\sn/*}}}*/\sn/***\sn!!Footer\sn***/\sn/*{{{*/\sn.yourSearchFoote"+\n"r {\sn\stmargin-top: 8px;\sn\stborder-top-width: thin;\sn\stborder-top-style: solid;\sn\stborder-top-color: #999999;"+\n"\sn}\sn\sn.yourSearchFooter a:hover{\sn\stbackground: none;\sn\stcolor: none;\sn}\sn/*}}}*/\sn/***\sn!!Navigation Bar\sn***/"+\n"\sn/*{{{*/\sn.yourSearchNaviBar a {\sn\stfont-size: 16px;\sn\stmargin-left: 4px;\sn\stmargin-right: 4px;\sn\stcolor: bla"+\n"ck;\sn\sttext-decoration: underline;\sn}\sn\sn.yourSearchNaviBar a:hover {\sn\stbackground-color: none;\sn}\sn\sn.yourSe"+\n"archNaviBar .prev {\sn\stfont-weight: bold;\sn\stcolor: blue;\sn}\sn\sn.yourSearchNaviBar .currentPage {\sn\stcolor: #"+\n"FF0000;\sn\stfont-weight: bold;\sn\sttext-decoration: none;\sn}\sn\sn.yourSearchNaviBar .next {\sn\stfont-weight: bold"+\n";\sn\stcolor: blue;\sn}\sn/*}}}*/\sn";\n\nconfig.shadowTiddlers["YourSearchResultTemplate"] =\n"<!--\sn{{{\sn-->\sn<span macro=\s"yourSearch if found\s">\sn<!-- The Summary Header ============================"+\n"================ -->\sn<table class=\s"summary\s" border=\s"0\s" width=\s"100%\s" cellspacing=\s"0\s" cellpadding=\s"0\s">"+\n"<tbody>\sn <tr>\sn\st<td align=\s"left\s">\sn\st\stYourSearch Result <span class=\s"yourSearchRange\s" macro=\s"yourSearc"+\n"h itemRange\s"></span>\sn\st\st&nbsp;of&nbsp;<span class=\s"yourSearchCount\s" macro=\s"yourSearch count\s"></span>\sn"+\n"\st\stfor&nbsp;<span class=\s"yourSearchQuery\s" macro=\s"yourSearch query\s"></span>\sn\st</td>\sn\st<td class=\s"yourSea"+\n"rchButtons\s" align=\s"right\s">\sn\st\st<span macro=\s"yourSearch chkPreviewText\s"></span><span class=\s"chkBoxLabel"+\n"\s">preview text</span>\sn\st\st<span macro=\s"yourSearch newTiddlerButton\s"></span>\sn\st\st<span macro=\s"yourSearch openAllButton\s"></span>\sn\st\st<span macro=\s"yourSearch lin"+\n"kButton 'YourSearch Options' options 'Configure YourSearch'\s"></span>\sn\st\st<span macro=\s"yourSearch linkB"+\n"utton 'YourSearch Help' help 'Get help how to use YourSearch'\s"></span>\sn\st\st<span macro=\s"yourSearch clo"+\n"seButton\s"></span>\sn\st</td>\sn </tr>\sn</tbody></table>\sn\sn<!-- The List of Found Tiddlers ================="+\n"=========================== -->\sn<div id=\s"yourSearchResultItems\s" itemsPerPage=\s"25\s" itemsPerPageWithPr"+\n"eview=\s"10\s"></div>\sn\sn<!-- The Footer (with the Navigation) ==========================================="+\n"= -->\sn<table class=\s"yourSearchFooter\s" border=\s"0\s" width=\s"100%\s" cellspacing=\s"0\s" cellpadding=\s"0\s"><tbody"+\n">\sn <tr>\sn\st<td align=\s"left\s">\sn\st\stResult page: <span class=\s"yourSearchNaviBar\s" macro=\s"yourSearch naviBar"+\n"\s"></span>\sn\st</td>\sn\st<td align=\s"right\s"><span macro=\s"yourSearch version\s"></span>, <span macro=\s"yourSearc"+\n"h copyright\s"></span>\sn\st</td>\sn </tr>\sn</tbody></table>\sn<!-- end of the 'tiddlers found' case ========="+\n"================================== -->\sn</span>\sn\sn\sn<!-- The \s"No tiddlers found\s" case ================="+\n"========================== -->\sn<span macro=\s"yourSearch if not found\s">\sn<table class=\s"summary\s" border="+\n"\s"0\s" width=\s"100%\s" cellspacing=\s"0\s" cellpadding=\s"0\s"><tbody>\sn <tr>\sn\st<td align=\s"left\s">\sn\st\stYourSearch Resu"+\n"lt: No tiddlers found for <span class=\s"yourSearchQuery\s" macro=\s"yourSearch query\s"></span>.\sn\st</td>\sn\st<t"+\n"d class=\s"yourSearchButtons\s" align=\s"right\s">\sn\st\st<span macro=\s"yourSearch newTiddlerButton\s"></span>\sn\st\st<span macro=\s"yourSearch linkButton 'YourSearch Options'"+\n" options 'Configure YourSearch'\s"></span>\sn\st\st<span macro=\s"yourSearch linkButton 'YourSearch Help' help"+\n" 'Get help how to use YourSearch'\s"></span>\sn\st\st<span macro=\s"yourSearch closeButton\s"></span>\sn\st</td>\sn <"+\n"/tr>\sn</tbody></table>\sn</span>\sn\sn\sn<!--\sn}}}\sn-->\sn";\n\nconfig.shadowTiddlers["YourSearchItemTemplate"] = \n"<!--\sn{{{\sn-->\sn<span class='yourSearchNumber' macro='foundTiddler number'></span>\sn<span class='yourSea"+\n"rchTitle' macro='foundTiddler title'/></span>&nbsp;-&nbsp;\sn<span class='yourSearchTags' macro='found"+\n"Tiddler field tags 50'/></span>\sn<span macro=\s"yourSearch if previewText\s"><div class='yourSearchText' macro='fo"+\n"undTiddler field text 250'/></div></span>\sn<!--\sn}}}\sn-->";\n\nconfig.shadowTiddlers["YourSearch"] = "<<tiddler [[YourSearch Help]]>>";\n\nconfig.shadowTiddlers["YourSearch Result"] = "The popup-like window displaying the result of a YourSearch query.";\n\n//----------------------------------------------------------------------------\n// Install YourSearch\n//----------------------------------------------------------------------------\n\n// Overwrite the TiddlyWiki search handler and verify after a while \n// that nobody else has overwritten it.\nconfig.macros.search.handler = myMacroSearchHandler;\n\nvar checkForOtherHijacker = function() {\n // Check that still our search handler is installed\n if (config.macros.search.handler != myMacroSearchHandler) {\n alert(\n"Message from YourSearchPlugin:\sn\sn\snAnother plugin has disabled the 'Your Search' features.\sn\sn\snYou may "+\n"disable the other plugin or change the load order of \snthe plugins (by changing the names of the tidd"+\n"lers)\snto enable the 'Your Search' features.");\n }\n};\n\nsetTimeout(checkForOtherHijacker, 5000);\n\n// === Public API =================================\n\nabego.YourSearch.getStandardRankFunction = function() {\n return standardRankFunction;\n};\n\nabego.YourSearch.getRankFunction = function() {\n return abego.YourSearch.getStandardRankFunction();\n};\n\nabego.YourSearch.getCurrentTiddler = function() {\n return currentTiddler;\n};\n\nabego.YourSearch.closeResult = function() {\n closeResult();\n}\n\n})();\n} // of "install only once"\n// Used Globals (for JSLint) ==============\n\n// ... JavaScript Core\n/*global alert,clearTimeout,confirm */\n// ... TiddlyWiki Core\n/*global Tiddler, applyHtmlMacros, clearMessage, createTiddlyElement, createTiddlyButton, createTiddlyText, ensureVisible ,findPosX, highlightHack, findPosY,findWindowWidth, invokeMacro, saveChanges, refreshElements, story */\n//}}}\n/***\n!Licence and Copyright\nCopyright (c) abego Software ~GmbH, 2005-2006 ([[www.abego-software.de|http://www.abego-software.de]])\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\nRedistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice, this\nlist of conditions and the following disclaimer in the documentation and/or other\nmaterials provided with the distribution.\n\nNeither the name of abego Software nor the names of its contributors may be\nused to endorse or promote products derived from this software without specific\nprior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY\nEXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT\nSHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR\nBUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\nANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGE.\n***/\n\n
| tiddlyspot password:|<<option pasUploadPassword>>|\n| site management:|<<upload http://myles.tiddlyspot.com/store.cgi index.html . . myles>>//(requires tiddlyspot password)//<<br>>[[control panel|http://myles.tiddlyspot.com/controlpanel]], [[download (go offline)|http://myles.tiddlyspot.com/download]]|\n| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[announcements|http://announce.tiddlyspot.com/]], [[blog|http://tiddlyspot.com/blog/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|