Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
conversations about websites
<<newTiddler label:"new bookmark" prompt:"create a new bookmark" template:bookmarkEditTemplate title:"New Bookmark" tag:"bookmark">>
[[Home]]
[[Trails]]
[[Bookmarks]]
<<overlay launchLabel:"Trail Player">>
[[About]]
<<search>>
<<closeAll>>
AdvancedOptions
<<slider chkTiddlerTabs SideBarTabs "tiddlers ยป" "manage tiddlers">>
/*
Theme - http://www.colourlovers.com/palette/60028/Night_Sky
#001848 dark blue
#301860 dark violet
#483078 violet
#604878 pink-violet
#906090 violet-pink
*/
@font-face {
font-family: "punkass";
src: url("images/punkass_.ttf");
}
.header { text-align: center; font-size: 100%; padding: 0.7em 0; width: 100%; color: white; }
.siteTitle { font-family: punkass, ariel, helvetica, sans serif; text-shadow: black 1px 1px 0; font-size: 5em; }
.siteSubtitle { font-weight: bold; font-family: times new roman, serif; }
.clearance { clear: both; height: 1px; }
.title, h1, h2, h3, h4, h5, h6 { color: [[ColorPalette::PrimaryMid]]; }
#scrumptiousWrapper { text-align: center; }
#scrumptiousMain { margin: 0 auto; width: 960px; }
#mainMenu { width: 150px; position: static; margin: 0; padding: 0; float: left; }
#dontCollapse { width: 1px; overflow: hidden; }
#displayArea { width: 540px; margin: 0 30px; padding: 0; text-align: left; float: left; position: static; }
#sidebar { width: 210px; font-size: 1em; float: left; position: static; margin: 0; padding: 0; text-align: left; }
#sidebar, #mainMenu { margin-top: 1em; }
h1,h2,h3,h4,h5,h6 { border: 0; }
.tabsetWrapper { font-size: 80%; overflow: hidden;}
.trailViewTiddler, .trailEditTiddler, .trailLink a {
background-color: #88f;
filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#9999ff,EndColorStr=#ddddff);
background-image: -webkit-gradient(linear, left top, left bottom, from(#99f), to(#ddf));
background-image: -moz-linear-gradient(top, #c8d, #edf);
border: 1px solid #99f;
}
.trailTiddler table { border-collapse: collapse; }
.trailTiddler td { padding: 0.6em 0; }
.trailViewTiddler td { padding: 1em 0; }
.trailTiddler td input { display: inline; }
.trailTiddler td.bookmarkLinkCell { text-align: right; }
.trailTiddler .bookmarkLink { cursor: move; margin-right: 5px; }
.trailEditTiddler .bookmarkNote { width: 250px; height: 2em; }
.bookmarkTiddler, .bookmarkEditTiddler, .bookmarkLink {
background-color: #77f;
filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#9999ff,EndColorStr=#ddddff);
background-image: -webkit-gradient(linear, left top, left bottom, from(#99f), to(#ddf));
background-image: -moz-linear-gradient(top, #77f, #eef);
border: 1px solid #77f;
}
.dataTiddler {
padding: 0 10px 5px 20px;
-moz-border-radius: 8px; -webkit-border-radius: 8px;
}
.dataCategory { font-size: 0.6em; color: black; font-style: italic; font-weight: normal; }
.trailViewTiddler .title td { padding: 0; vertical-align: bottom; }
.trailViewTiddler .title td.playCell { padding-bottom: 3px; }
.playTrail { border: 1px solid [[ColorPalette::PrimaryDarker]]; background: [[ColorPalette::PrimaryPale]]; font-size: 0.5em; color: black; padding: 1px; margin-left: 4px; font-weight: normal; cursor: pointer; }
.toolbar { padding-top: 5px; }
.toolbar a:hover { font-weight: bold; }
.dataTiddler .button, .selected .toolbar a { color: [[ColorPalette::PrimaryDarker]]; border: 0; background: inherit; }
.dataTiddler .button:hover, .selected .dataTiddler .toolbar a:hover { color: white; }
.dataTiddler .tagged .button { color: #505; }
.dataTiddler .comments textarea { width: 90%; }
.dataTiddler .comments .addComment { display: block; float: left; border: 1px solid [[ColorPalette::PrimaryMid]]; padding: 2px; background: #99f; color: black; }
.dataTiddler .comments .addComment:hover { color: white; }
.dataTiddler .comments h1 { color: #000; text-decoration: underline; font-size: 1em; font-weight: normal; }
.dataTiddler .comment .heading { background: #99f; }
.dataTiddler .tag a { color: [[ColorPalette::PrimaryMid]]; }
.dataTiddler .url { font-size: 1.2em; color: #20c; }
.dataTiddler .description { font-size: 1.2em; margin-top: 0.3em; }
.trailViewTiddler .bookmarks { margin-top: 1.5em; }
/* .normalTiddler { border-top: 1px solid #99f; border-bottom: 1px solid #99f; } */
.normalTiddler, .tagTiddler { border-left: 5px solid #add; padding-left: 15px; }
.bookmarkLink { padding: 5px; color: black; margin-right: 10px; }
.bookmarkLink:hover { background-color: #88f; color: white; background-image: none; }
.bookmarkLink { color: black; }
.scrumptiousTagging { font-size: normal; margin-top: 1em; }
.scrumptiousTags { font-size: normal; margin: 1em 0; line-height: 3em; }
.bookmarkLink {
-moz-border-radius: 5px; -webkit-border-radius: 5px;
}
.tag, .tagg, .tagName { background: [[ColorPalette::PrimaryPale]]; }
.tag:hover { background: [[ColorPalette::PrimaryMid]]; }
.tag:hover a { color: white; }
.tagsView { margin-bottom: 1em; }
.tagg, .tagName { padding: 0.2em; }
.tagCloud { line-height: 2em; }
.tag { padding: 0.5em; margin: 0.5em 0.5em 0.5em 0; }
.taggedTiddlers { margin: 1em 0 2em; }
.taggingIntro { margin-bottom: 2em; }
.tagCategory { font-size: 0.8em; font-style: italic; }
.clearance { height: 1px; clear: both; }
table { border: 1px; }
<div class="dataTiddler trailTiddler trailViewTiddler">
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title'><table><tr><td><span macro='view title'></span></td><td class="playCell"><span class="playTrail" macro="playTrail" /></td></tr></table></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='scrumptiousTags' macro='scrumptiousTags'></div>
<div class='description' macro='view Description wikified'></div>
<div class='bookmarksView' macro='bookmarksView'></div>
<div class='comments' macro="comments expandHeading:true heading:'Comments (%c):'"></div>
<div class='tagClear'></div>
</div>
<div class="dataTiddler trailTiddler trailEditTiddler">
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title'><span macro='view title'></span> <span class="dataCategory">trail</span></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<h2>Trail Tags</h2>
<div class='tagSuggest' macro='tagSuggest prefix:user_'></div>
<div class='editor' macro='edit tags' style='display:none;></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<div class='editor' macro='edit text' style="display: none;"></div>
<div class='editTrail' macro='editTrail'></div>
<div class="clearance"> </div>
</div>
<div class="dataTiddler bookmarkTiddler">
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title'><span macro='view title'></span> <span class="dataCategory">bookmark</span></div>
<div class='bookmarkURL' macro='bookmarkURL'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='scrumptiousTags' macro='scrumptiousTags'></div>
<div class='description' macro='view Description wikified'></div>
<div class='comments' macro="comments expandHeading:true heading:'Comments (%c):'"></div>
<div class='tagClear'></div>
</div>
<div class="bookmarkEditTiddler dataTiddler">
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title'><span macro='view title'></span> <span class="dataCategory">bookmark</span></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text' style="display: none;"></div>
<div class='editBookmark' macro='editBookmark'></div>
<h2>Bookmark Tags</h2>
<div class='tagSuggest' macro='tagSuggest filterTag:bookmark prefix:user_'></div>
<div class='editor' macro='edit tags' style='display:none;></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser excludeLists'></span></div>
<div class="clearance"> </div>
</div>
<div class="tagTiddler">
<div class='clearance'> </div>
<div class='scrumptiousTagging' macro='scrumptiousTagging'></div>
<div class='clearance'> </div>
</div>
<div class="normalTiddler">
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='scrumptiousTagging' macro='scrumptiousTagging'></div>
<div class='clearance'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
</div>
[[Home]]
[[News]]
[[user_tech]]
[[Reddit]]
[[Digg]]
[[Bookmarks]]
[[About]]
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryDark]]'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div id="scrumptiousWrapper">
<div id="scrumptiousMain">
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id="dontCollapse"> </div>
<div id='tiddlerDisplay'></div>
</div>
<div id='sidebar' refresh='content' force='true' tiddler='SideBar'></div>
</div>
</div>
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #b8b
PrimaryLight: #604878
PrimaryMid: #483078
PrimaryDark: #301860
PrimaryDarker: #001848
SecondaryPale: #bdf
SecondaryLight: #8df
SecondaryMid: #39c
SecondaryDark: #059
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
PrimarySourceURL - http://www.colourlovers.com/palette/328418/rosso_rosso
|~ViewToolbar|+editTiddler closeTiddler > closeOthers fields syncing permalink references jump deleteTiddler|
|~EditToolbar|+saveTiddler -cancelTiddler|
/*
(function(){
// http://paulirish.com/2009/fighting-the-font-face-fout/
// if firefox 3.5+, hide content till load (or 3 seconds) to prevent FOUT
var d = document, e = d.documentElement, s = d.createElement('style');
if (e.style.MozTransform === ''){ // gecko 1.9.1 inference
s.textContent = 'body{visibility:hidden}';
e.firstChild.appendChild(s);
function f(){ s.parentNode && s.parentNode.removeChild(s); }
addEventListener('load',f,false);
setTimeout(f,2000);
}
})();
*/
if (version.extensions.TrailPlayer.syncFromFragmentID()) {
version.extensions.overlay.toggle();
}
config.options.chkAutoSave = true;
config.options.chkTiddlerTabs = false;
(function($) {
/******************************************************************
* Bookmark
******************************************************************/
var editBookmark = config.macros.editBookmark = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
$("<div>URL: </div>").appendTo(place);
$("<input class='bookmarkURL'/>")
.val(store.getTiddlerText(tiddler.title+"::url")||"")
.keyup(function() {
editBookmark.updateText(this);
})
.appendTo(place);
$("<div>Description (optional): </div>").appendTo(place);
$("<input class='bookmarkDescription'/>")
.val(store.getTiddlerText(tiddler.title+"##Description")||"")
.keyup(function() {
editBookmark.updateText(this);
})
.appendTo(place);
},
updateText: function(src) {
var $bookmarkTiddler = $(src).parents(".bookmarkEditTiddler");
var text = "url: " + $bookmarkTiddler.find(".bookmarkURL").val();
var description = $bookmarkTiddler.find(".bookmarkDescription").val();
if ($.trim(description).length) text+="\n!Description\n"+description;
$bookmarkTiddler.find("textArea").val(text);
}
}
config.macros.bookmarkURL = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var url = store.getTiddlerText(tiddler.title+"::url");
$("<a class='url'/>").appendTo(place).attr("href", url).html(url);
}
}
config.macros.bookmarkDescription = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
$("<div class='description'/>")
.appendTo(place)
.html(store.getTiddlerText(tiddler.title+"##Description"));
}
}
/******************************************************************
* Trail
******************************************************************/
var editTrail = config.macros.editTrail = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
$("<div>Description (optional): </div>").appendTo(place);
$("<input class='trailDescription'/>")
.val(store.getTiddlerText(tiddler.title+"##Description")||"")
.keyup(function() {
editTrail.updateText(this);
})
.appendTo(place);
renderBookmarks(place, tiddler, true);
},
updateText: function(src) {
var $trailTiddler = $(src).parents(".trailEditTiddler");
console.log($trailTiddler);
var text = "";
var description = $trailTiddler.find(".trailDescription").val();
if ($.trim(description).length) text+="\n!Description\n"+description;
var $bookmarkLinks = $trailTiddler.find(".bookmarkLink");
if ($bookmarkLinks.length) {
text += "\n!Bookmarks\n";
$bookmarkLinks.each(function(i, bookmarkLink) {
text += "[[" + $(bookmarkLink).html() + "]]";
var note = $(bookmarkLink).parents("tr").find(".bookmarkNote").val();
console.log(note);
if (note.length) text+=" " + note;
text += "\n";
});
}
$trailTiddler.find(".editor").find("textArea").val(text);
}
}
config.macros.bookmarksView = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
renderBookmarks(place, tiddler, false);
}
}
function renderBookmarks(place, tiddler, editable) {
var trail = version.extensions.TrailPlugin.extractTrail(tiddler);
$("<h2>Bookmarks on this Trail</h2>").appendTo(place);
var $bookmarkTable = $("<table/>").appendTo(place);
var $bookmarkList = $("<tbody/>").appendTo($bookmarkTable);
$.each(trail.bookmarks, function(i, bookmark) {
var $bookmarkItem = $("<tr/>").appendTo($bookmarkList);
$bookmarkLinkCell = $("<td class='bookmarkLinkCell' />").appendTo($bookmarkItem);
var $bookmarkLink;
if (editable) {
$bookmarkLink = $("<span/>").appendTo($bookmarkLinkCell);
} else {
$bookmarkLink = $(createTiddlyLink($bookmarkLinkCell.get(0), bookmark.title, true));
console.log($bookmarkLink)
}
$bookmarkLink.addClass("bookmarkLink").html(bookmark.title)
var $noteCell = $("<td/>").appendTo($bookmarkItem);
var $note;
if (editable) {
var $note = $("<textarea type='text' class='bookmarkNote'/>")
.val(bookmark.note)
.keyup(function() {
editTrail.updateText(this);
})
} else {
$note = $("<span>"+bookmark.note+"</span>")
// this is a "hidden feature" where the user can click on bookmark text
// and when they launch the player, it
.click(function() {
tiddler.recentBookmark = bookmark;
});
}
$note.appendTo($noteCell)
});
if (editable) {
$bookmarkTable.tableDnD({
onDrop: function(table, row) {
editTrail.updateText(row);
},
dragHandle: ".bookmarkLink"
});
}
}
/******************************************************************
* General Utility
******************************************************************/
function log() { if (console && console.log) console.log.apply(console, arguments); }
_getValue = store.getValue;
store.getValue = function(tiddler, field, value) {
tiddler = store.resolveTiddler(tiddler);
var sectionContent = store.getTiddlerText(tiddler.title+"##"+field);
if (sectionContent) return sectionContent;
var sliceContent = store.getTiddlerText(tiddler.title+"::"+field);
if (sliceContent) return sliceContent;
return _getValue.apply(this, arguments);
}
})(jQuery);
(function($) {
config.macros.scrumptiousTagTitle = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
$("<div class='tagTitle'/>").html(tiddler.title.substr(5)).appendTo(place);
}
};
config.macros.scrumptiousTagging = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var tagName = tiddler.title;
if (tagName.substr(0,5)!="user_") return;
tagName = tagName.substr(5);
var $tagging = $("<div/>").appendTo(place);
showTiddlersInCategory("bookmark", "user_"+tagName, "bookmarks", $tagging);
showTiddlersInCategory("trail", "user_"+tagName, "trails", $tagging);
}
};
function showTiddlersInCategory(categoryTag, userTag, pluralLabel, $tagging) {
console.log("-----", userTag,store.getTaggedTiddlers(userTag));
var tiddlersInCategory = _.filter(store.getTaggedTiddlers(userTag), function(tiddler) {
return tiddler.isTagged(categoryTag);
});
if (!tiddlersInCategory.length) return;
// TODO show tag as tiddlylink (for styling consistency) ie use createTiddlyLink here
$tagging.append("<div class='taggingIntro'><span class='tag'>" + userTag.substr(5) + "</span> <span class='tagCategory'>" + pluralLabel + " with this tag</span></div>");
var $taggedTiddler = $("<div class='taggedTiddlers' />").appendTo($tagging);
$.each(tiddlersInCategory, function(i,tiddler) {
$link = $("<span />").appendTo($taggedTiddler);
var link = createTiddlyLink($link.get(0), tiddler.title, true);
link.className = categoryTag+"Link";
$("<span> </span>").appendTo($taggedTiddler);
});
}
config.macros.scrumptiousTags = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
$.each(tiddler.tags, function(i, tag) {
if (tag.substr(0,5)=="user_") {
var $tag = $("<span class='tag' />").appendTo(place);
var link = createTiddlyLink($tag.get(0), tag, true);
link.innerHTML = tag.substr(5);
$("<span> </span>").appendTo(place);
}
});
}
};
var _chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler;
Story.prototype.chooseTemplateForTiddler = function(title,template) {
//console.log("choose", arguments);
if (title.substr(0,5)=="user_") return "userTagViewTemplate";
//console.log("returning");
return _chooseTemplateForTiddler.apply(this, arguments);
};
})(jQuery);
(function($) {
config.macros.playTrail = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var title = params[0] || "Play This Trail!";
$("<span>"+title+"</span>")
.click(function() {
version.extensions.overlay.toggle();
$.switchTrailPlayer(tiddler.title, tiddler.recentBookmark.title);
// recentBookmark is a "hidden feature" - see renderBookmarks()
})
.appendTo(place);
}
}
})(jQuery);
/**
* TableDnD plug-in for JQuery, allows you to drag and drop table rows
* You can set up various options to control how the system will work
* Copyright (c) Denis Howlett <denish@isocra.com>
* Licensed like jQuery, see http://docs.jquery.com/License.
*
* Configuration options:
*
* onDragStyle
* This is the style that is assigned to the row during drag. There are limitations to the styles that can be
* associated with a row (such as you can't assign a border--well you can, but it won't be
* displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
* a map (as used in the jQuery css(...) function).
* onDropStyle
* This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
* to what you can do. Also this replaces the original style, so again consider using onDragClass which
* is simply added and then removed on drop.
* onDragClass
* This class is added for the duration of the drag and then removed when the row is dropped. It is more
* flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
* is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
* stylesheet.
* onDrop
* Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
* and the row that was dropped. You can work out the new order of the rows by using
* table.rows.
* onDragStart
* Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
* table and the row which the user has started to drag.
* onAllowDrop
* Pass a function that will be called as a row is over another row. If the function returns true, allow
* dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
* the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
* scrollAmount
* This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
* window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
* FF3 beta
* dragHandle
* This is the name of a class that you assign to one or more cells in each row that is draggable. If you
* specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
* will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
* the whole row is draggable.
*
* Other ways to control behaviour:
*
* Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
* that you don't want to be draggable.
*
* Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
* <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
* an ID as must all the rows.
*
* Other methods:
*
* $("...").tableDnDUpdate()
* Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
* This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
* The table maintains the original configuration (so you don't have to specify it again).
*
* $("...").tableDnDSerialize()
* Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
* called from anywhere and isn't dependent on the currentTable being set up correctly before calling
*
* Known problems:
* - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
*
* Version 0.2: 2008-02-20 First public version
* Version 0.3: 2008-02-07 Added onDragStart option
* Made the scroll amount configurable (default is 5 as before)
* Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
* Added onAllowDrop to control dropping
* Fixed a bug which meant that you couldn't set the scroll amount in both directions
* Added serialize method
* Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
* draggable
* Improved the serialize method to use a default (and settable) regular expression.
* Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
*/
jQuery.tableDnD = {
/** Keep hold of the current table being dragged */
currentTable : null,
/** Keep hold of the current drag object if any */
dragObject: null,
/** The current mouse offset */
mouseOffset: null,
/** Remember the old value of Y so that we don't do too much processing */
oldY: 0,
/** Actually build the structure */
build: function(options) {
// Set up the defaults if any
this.each(function() {
// This is bound to each matching table, set up the defaults and override with user options
this.tableDnDConfig = jQuery.extend({
onDragStyle: null,
onDropStyle: null,
// Add in the default class for whileDragging
onDragClass: "tDnD_whileDrag",
onDrop: null,
onDragStart: null,
scrollAmount: 5,
serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
serializeParamName: null, // If you want to specify another parameter name instead of the table ID
dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
}, options || {});
// Now make the rows draggable
jQuery.tableDnD.makeDraggable(this);
});
// Now we need to capture the mouse up and mouse move event
// We can use bind so that we don't interfere with other event handlers
jQuery(document)
.bind('mousemove', jQuery.tableDnD.mousemove)
.bind('mouseup', jQuery.tableDnD.mouseup);
// Don't break the chain
return this;
},
/** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
makeDraggable: function(table) {
var config = table.tableDnDConfig;
// mahemoff - patched this so dragHandle is *child* of a TD, not a TD itself
// also, dragHandle is now a jQuery selector, not a class name
if (table.tableDnDConfig.dragHandle) {
// We only need to add the event to the specified cells
jQuery(table.tableDnDConfig.dragHandle, table).each(function() {
// The cell is bound to "this"
var cell = this.parentNode;
console.log("cell", cell);
jQuery(cell).mousedown(function(ev) {
jQuery.tableDnD.dragObject = this.parentNode;
jQuery.tableDnD.currentTable = table;
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
if (config.onDragStart) {
// Call the onDrop method if there is one
config.onDragStart(table, this);
}
return false;
});
})
} else {
// mahemoff - I patched this but am no longer using it (and it's probably wrong anyway)
// using the "true" branch above
// For backwards compatibility, we add the event to the whole row
var rows = jQuery("tr", table); // get all the rows as a wrapped set
rows.each(function() {
// Iterate through each row, the row is bound to "this"
var row = jQuery(this);
if (! row.hasClass("nodrag")) {
row.mousedown(function(ev) {
var $target = jQuery(ev.target);
if (! $target.hasClass("bookmarkLink")) return false;
row = $target.parents("tr").get(0);
console.log("row", row);
jQuery.tableDnD.dragObject = row;
jQuery.tableDnD.currentTable = table;
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(row, ev);
if (config.onDragStart) {
// Call the onDrop method if there is one
config.onDragStart(table, row);
}
return true;
}).css("cursor", "move"); // Store the tableDnD object
}
});
}
},
updateTables: function() {
this.each(function() {
// this is now bound to each matching table
if (this.tableDnDConfig) {
jQuery.tableDnD.makeDraggable(this);
}
})
},
/** Get the mouse coordinates from the event (allowing for browser differences) */
mouseCoords: function(ev){
if(ev.pageX || ev.pageY){
return {x:ev.pageX, y:ev.pageY};
}
return {
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y:ev.clientY + document.body.scrollTop - document.body.clientTop
};
},
/** Given a target element and a mouse event, get the mouse offset from that element.
To do this we need the element's position and the mouse position */
getMouseOffset: function(target, ev) {
ev = ev || window.event;
var docPos = this.getPosition(target);
var mousePos = this.mouseCoords(ev);
return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
},
/** Get the position of an element by going up the DOM tree and adding up all the offsets */
getPosition: function(e){
var left = 0;
var top = 0;
/** Safari fix -- thanks to Luis Chato for this! */
if (e.offsetHeight == 0) {
/** Safari 2 doesn't correctly grab the offsetTop of a table row
this is detailed here:
http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
note that firefox will return a text node as a first child, so designing a more thorough
solution may need to take that into account, for now this seems to work in firefox, safari, ie */
e = e.firstChild; // a table cell
}
while (e.offsetParent){
left += e.offsetLeft;
top += e.offsetTop;
e = e.offsetParent;
}
left += e.offsetLeft;
top += e.offsetTop;
return {x:left, y:top};
},
mousemove: function(ev) {
if (jQuery.tableDnD.dragObject == null) {
return;
}
var dragObj = jQuery(jQuery.tableDnD.dragObject);
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
var mousePos = jQuery.tableDnD.mouseCoords(ev);
var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
//auto scroll the window
var yOffset = window.pageYOffset;
if (document.all) {
// Windows version
//yOffset=document.body.scrollTop;
if (typeof document.compatMode != 'undefined' &&
document.compatMode != 'BackCompat') {
yOffset = document.documentElement.scrollTop;
}
else if (typeof document.body != 'undefined') {
yOffset=document.body.scrollTop;
}
}
if (mousePos.y-yOffset < config.scrollAmount) {
window.scrollBy(0, -config.scrollAmount);
} else {
var windowHeight = window.innerHeight ? window.innerHeight
: document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
window.scrollBy(0, config.scrollAmount);
}
}
if (y != jQuery.tableDnD.oldY) {
// work out if we're going up or down...
var movingDown = y > jQuery.tableDnD.oldY;
// update the old value
jQuery.tableDnD.oldY = y;
// update the style to show we're dragging
if (config.onDragClass) {
dragObj.addClass(config.onDragClass);
} else {
dragObj.css(config.onDragStyle);
}
// If we're over a row then move the dragged row to there so that the user sees the
// effect dynamically
var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
if (currentRow) {
// TODO worry about what happens when there are multiple TBODIES
if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
} else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
}
}
}
return false;
},
/** We're only worried about the y position really, because we can only move rows up and down */
findDropTargetRow: function(draggedRow, y) {
var rows = jQuery.tableDnD.currentTable.rows;
for (var i=0; i<rows.length; i++) {
var row = rows[i];
var rowY = this.getPosition(row).y;
var rowHeight = parseInt(row.offsetHeight)/2;
if (row.offsetHeight == 0) {
rowY = this.getPosition(row.firstChild).y;
rowHeight = parseInt(row.firstChild.offsetHeight)/2;
}
// Because we always have to insert before, we need to offset the height a bit
if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
// that's the row we're over
// If it's the same as the current row, ignore it
if (row == draggedRow) {return null;}
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
if (config.onAllowDrop) {
if (config.onAllowDrop(draggedRow, row)) {
return row;
} else {
return null;
}
} else {
// If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
var nodrop = jQuery(row).hasClass("nodrop");
if (! nodrop) {
return row;
} else {
return null;
}
}
return row;
}
}
return null;
},
mouseup: function(e) {
if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
var droppedRow = jQuery.tableDnD.dragObject;
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
// If we have a dragObject, then we need to release it,
// The row will already have been moved to the right place so we just reset stuff
if (config.onDragClass) {
jQuery(droppedRow).removeClass(config.onDragClass);
} else {
jQuery(droppedRow).css(config.onDropStyle);
}
jQuery.tableDnD.dragObject = null;
if (config.onDrop) {
// Call the onDrop method if there is one
config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
}
jQuery.tableDnD.currentTable = null; // let go of the table too
}
},
serialize: function() {
if (jQuery.tableDnD.currentTable) {
return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
} else {
return "Error: No Table id set, you need to set an id on your table and every row";
}
},
serializeTable: function(table) {
var result = "";
var tableId = table.id;
var rows = table.rows;
for (var i=0; i<rows.length; i++) {
if (result.length > 0) result += "&";
var rowId = rows[i].id;
if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
}
result += tableId + '[]=' + rowId;
}
return result;
},
serializeTables: function() {
var result = "";
this.each(function() {
// this is now bound to each matching table
result += jQuery.tableDnD.serializeTable(this);
});
return result;
}
}
jQuery.fn.extend(
{
tableDnD : jQuery.tableDnD.build,
tableDnDUpdate : jQuery.tableDnD.updateTables,
tableDnDSerialize: jQuery.tableDnD.serializeTables
}
);
Welcome!
TODO show recently added bookmarks and trails
<<list filter [tag[bookmark]] [sort[modified]] >>
Welcome to Scrumptious. Here you can store bookmarks and build up "trails" of websites for others to follow. You can leave a description for each bookmark and trail, and there are also user comments attached to them. More info at [[scrumptious.tv|http://scrumptious.tv]].
Scrumptious was built by [[Michael Mahemoff|http://mahemoff.com]] at [[Osmosoft|http://osmosoft.com]] and is free for you to use under the [[permissive MIT License|http://creativecommons.org/licenses/MIT/]].
!Bookmarks
<<tagCloud filterTag:bookmark quantity:20 prefix:"user_">>
!Trails
<<tagCloud filterTag:trail quantity:20 prefix:"user_">>
Scrumptious is a bookmarking service focused on collaboration and flexibility. Its similar to services like Delicious, but has its own unique take, and has a certain aesthetic that will be familiar to anyone who's used TiddlyWiki.
!Features for Users
* Tagging, descriptions, and nested comments for each bookmark.
* Supports not just bookmarks, but trails. Trails are lists of bookmarks, each with an optional annotation. Like bookmarks, trails hve tags, descriptions, and nested comments.
* Trail Player - Scrumptious can flip back and forth between regular mode and Trail Player mode. Through the magic of "iFrames"™, the player lets you easily navigate through a trail - hit the next button to jump to the next site in the trail. Each site you see is accompanied by notes from the
* Tag clouds. Shiny.
* Searching and browsing.
!Features for Installers and Extenders
* Works in standalone mode, on a local file:// URL; running on a server as published, static, HTML content; or as a multi-user, interactive, web application.
* Completely hackable. All the interaction is coded in client-side Javascript as Scrumptious is a TiddlyWiki vertical, making it easy to customise. You can even customise simple things like the [[SiteTitle]], [[DefaultTiddlers]], and [[ColorPalette]] without even knowing Javascript. And you can also introduce TiddlyWiki plugins from elsewhere.
* Open source, under the permissive [[MIT License]], which means you can happily install it on your own server, and modify it to your heart's content.
* Compatible with all major browsers (note: IE6 will be supported, but still needs work).
!Philosophy: Surfing with Intent
Scrumptious shares a similar philosophy to the rest of the TiddlyVerse. I envsion two styles of Scrumptious instance:
* Task-focused: When we use the web, we're often "Surfing With Intent" (Paul Downey's term). We're hunting for specific information and collecting a bunch of resources along the way. Scrumptious is ideal for gathering those resources. e.g. a house-hunting family (or individual) would create a space with all the interesting homes they've spotted on the web.
* Individual: You can also use Scrumptious as a repository of your own bookmarks. In the context of TiddlyWiki hosting environments like the in-progress TiddlySpace, others will be able to "follow" your bookmarks this way. You could, for example, build up a collection of bookmarks in your team by aggregating everyone's bookmarks (perhaps filtering on a particular tag).
!Limitations
At this stage, the expectation is that each Scrumptious instance will include at most a few hundred bookmarks. This is consistent with the philosophy expressed above. It's always trivial to create a new space for a new set of bookmarks, whether in file:// or collaboritive, online, mode (once we've built http://tiddlyspace.com !)
!TODO
* "Play this trail" (with hidden feature - if you click on a bookmark in the trail tiddler first, trail starts on that bookmark)
* add/remove bookmarks in trail
* Bookmarklet
* Comments shown on trail and can submit
* Better "list of trails/bookmarks" support - order by name, date, etc
* Also list of comments - esp. recent comments
* "This bookmark appears in these trails"
* Delicious/Instapaper sync.
* IE-compatible
!More Info
See also http://scrumptious.tv.
/*
BetterListPlugin
*/
/***
|Name|BetterListPlugin|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
***/
(function($) {
version.extensions.BetterListPlugin = {installed:true};
var macro = config.macros.betterList = {
init: function() {
// var stylesheet = store.getTiddlerText(tiddler.title + "##StyleSheet");
// config.shadowTiddlers["StyleSheetBetterListPlugin"] = stylesheet;
// store.addNotification("StyleSheetBetterListPlugin", refreshStyles);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var format, macroParams = paramString.parseParams();
var matches = paramString.match(/^(.*)(format:)(\S+)(.*)$/);
console.log("par", paramString);
if (matches) {
console.log("matches", matches);
format = matches[3];
paramString = matches[1]+matches[4];
console.log("for", format);
}
console.log("pam", paramString);
params = paramString.split(/\s+/);
var type = params[0] || "all";
$(place).addClass("betterList");
if (!config.macros.list[type].handler) return;
var tiddlers = config.macros.list[type].handler(params);
if (tiddlers.length) {
if (format=="raw") {
$.each(tiddlers, function(i, tiddler) { $(place).append(tiddler.title+"\n"); })
} else {
var $list = $("<ul/>").appendTo(place);
$.each(tiddlers, function(i, tiddler) {
var $li = $("<li/>").appendTo($list);
createTiddlyLink($li.get(0), tiddler.title || tiddler, true);
});
}
} else {
$("<div/>").text(getParam(macroParams, "emptyMessage") || "no matches").appendTo(place);
}
}
}
})(jQuery);
/***
!StyleSheet
!(end of StyleSheet)
***/
/*}}}*/
/***
|Name|TrailPlugin|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
!TrailTemplate
<h3 class="tiddlerLink" tiddlerTitle="<%= trail.name %>"><%= trail.name %></h3>
<strong><%= trail.description %></strong>
<dl class="trail">
<% for (var i=0; i<trail.bookmarks.length; i++) { bookmark = trail.bookmarks[i]; %>
<div class="bookmark">
<dt><a href="<%= bookmark.url %>"><%= bookmark.name %></a></dt>
<dd>
<%= bookmark.note %>
<span class="tiddlerLink" tiddlerTitle="<%= bookmark.title %>">#</span>
</dd>
</div>
<% } %>
</dl>
!StyleSheet
.bookmark { margin: 20px 0; }
.trail dt { margin: 25px 0; padding: 5px; font-weight: normal; background: #008; color: white; display: inline; }
.trail dt a { color: white; }
.trail dd { margin-left: 0; color: #008; padding: 5px 0; display: inline; }
.trail dd .tiddlerLink { padding: 8px 8px 8px 2px; opacity: 0.5; cursor: pointer; font-size: 0.7em; font-style: italic; }
!Javascript
{{{
***/
(function($) {
var plugin = version.extensions.TrailPlugin = {installed:true};
var macro = config.macros.trail = {
init: function() {
var stylesheet = store.getTiddlerText("TrailPlugin##StyleSheet");
config.shadowTiddlers["StyleSheetTrailPlugin"] = stylesheet;
store.addNotification("StyleSheetTrailPlugin", refreshStyles);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
tiddler = store.getTiddler(paramString);
var trail = version.extensions.TrailPlugin.extractTrail(tiddler);
$(place).append(tmpl("TrailPlugin##TrailTemplate", {trail:trail }));
$(".tiddlerLink").click(function() {
story.displayTiddler("top", this.getAttribute("tiddlerTitle"));
});
}
};
config.macros.trails = {
handler: function(place) {
// $.each(store.getTaggedTiddlers("trail"), function(i, trailTiddler) {
$.each(plugin.findTrails(["todo"]), function(i, trailTiddler) {
invokeMacro($("<div/>").appendTo(place).get(0), "trail", trailTiddler.title);
});
}
};
// If "tags" argument is a list (with at least one element), will only return trails matching at least
// one of those tags. Implementation of filtering is slower than it could be.
version.extensions.TrailPlugin.findTrails = function(userTags) {
if (!userTags) userTags = [];
if (typeof(userTags)=="string") userTags = [userTags];
var tags = _.map(userTags, function(userTag) { return "user_"+userTag.replace(/^user_/, "") });
var allTrails = store.getTaggedTiddlers("trail");
if (!tags.length) return allTrails;
return _.select(allTrails, function(trail) {
return _.any(trail.tags, function(tag) { return tags.indexOf(tag)!=-1; });
});
}
version.extensions.TrailPlugin.extractTrail = function(trailTiddler) {
trailTiddler = store.getTiddler(trailTiddler.title) || trailTiddler;
var trail = {
description: store.getTiddlerText(trailTiddler.title+"##Description"),
name: trailTiddler.title,
bookmarks: []
};
var bookmarkSpec = /\[\[(.*?)\]\](.*)/g;
var bookmarkMatch;
while (bookmarkMatch = bookmarkSpec.exec(store.getTiddlerText(trailTiddler.title+"##Bookmarks"))) {
var bookmarkTitle = bookmarkMatch[1];
trail.bookmarks.push({
title: bookmarkTitle,
url: slice(bookmarkTitle, "url"),
name: slice(bookmarkTitle, "name") || bookmarkTitle,
note: $.trim(bookmarkMatch[2]),
description: section(bookmarkTitle, "Description"),
});
};
return trail;
}
var cache = {};
function tmpl(str, data){
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
var fn = !/\W/.test(str) ?
cache[str] = cache[str] ||
// tmpl(document.getElementById(str).innerHTML) :
tmpl(store.getTiddlerText(str)) :
// Generate a reusable function that will serve as a template
// generator (and which will be cached).
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
store.getTiddlerText(str)
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
// Provide some basic currying to the user
return data ? fn( data ) : fn;
};
version.extensions.microtemplate = tmpl;
function log() { if (window.console) console.log.apply(console, arguments); }
function slice(tiddler, key) { return store.getTiddlerText(tiddler+"::"+key); }
function section(tiddler, key) { return store.getTiddlerText(tiddler+"##"+key); }
})(jQuery);
// underscore
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.7";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!a.concat&&!a.substr&&!a.apply&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};
b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;
a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;
b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=
function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();
/*}}}*/
(function($) {
$.modal = {}
$.modal.show = function(message, options) {
$.fn.attach = function(html) { return this.append(html).children(":last"); };
var defaults = {
dialogWidth: 400,
dialogHeight: 300,
close: function() { return $(this); }
}
var settings = $.extend(defaults, options);
var CUSHION_LENGTH = 20000;
FADE_DURATION = 500;
var mask = $("<div/>")
.animate({opacity: 0.85}, FADE_DURATION)
.css({
position: "absolute",
top: -CUSHION_LENGTH,
left: -CUSHION_LENGTH,
background: "#999",
zIndex: 9999998,
height: 2*CUSHION_LENGTH+$(window).height(),
width: 2*CUSHION_LENGTH+$(window).width(),
opacity: 0.01
})
.click(function(ev) {
$.modal.close(ev);
})
.appendTo($(document.body));
var dialog = $("<div class='modal'/>")
.fadeIn(FADE_DURATION)
.css({
position: "absolute",
background: "#fff",
border: "1px solid #666",
// "-moz-border-radius": 5,
// "-webkit-border-radius": 5,
width: settings.dialogWidth,
height: settings.dialogHeight,
zIndex: 9999999,
top: $(window.body).scrollTop()+$(window).height()/2-settings.dialogHeight/2,
left: $(window).width()/2-settings.dialogWidth/2
})
.attach("<div/>")
.css({
margin: "10px"
})
.append(message)
.end()
.attach("<div/>")
.css({
position: "absolute",
top: "-1.2em",
right: "4px",
cursor: "pointer"
})
.html("X")
.click(function(ev) {
$.modal.close(ev);
})
.end()
.data("close", settings.close)
.appendTo($(document.body));
$(window.body).keyup(function(ev) {
if (ev.charCode==27 || ev.keyCode==27) $.modal.close(ev);
});
$.modal.close = function(ev) {
if (ev) ev.stopPropagation();
var close = dialog.data("close");
dialog.slideUp(FADE_DURATION, function() { dialog.remove(); });
mask.fadeOut(FADE_DURATION, function() {
mask.remove();
if (close) close.apply(dialog);
});
}
return dialog;
}
})(jQuery);
/***
|Name|portal|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
!code
{{{
***/
(function($) {
version.extensions.overlay = {installed:true};
var $overlay, allStylesFrag;
var macro = config.macros.overlay = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var macroParams = paramString.parseParams();
createTiddlyButton(place,getParam(macroParams,"launchLabel")||"launch",null,version.extensions.overlay.toggle);
}
};
function makeOverlay() {
if (! $("#overlay").length) {
var overlays = store.getTaggedTiddlers("overlay");
if (!overlays.length) return;
// var content = store.getTiddlerText(getParam(macroParams, "contentTiddler"));
overlay = overlays[0];
$overlay = $("<div id='overlay'/>").hide().html(store.getTiddlerText(overlay.title+"##Content")).appendTo("body");
call(config[overlay.title].onLoad);
// $("#closeOverlay").click(version.extensions.overlay.toggle);
}
}
version.extensions.overlay.toggle = function() {
var overlayTiddlerTitle = store.getTaggedTiddlers("overlay")[0].title;
if ($overlay.css("display")=="none") {
scroll(0,0);
$.twStylesheet(store.getTiddlerText(overlayTiddlerTitle+"##StyleSheet"),
{id: "overlayStyleSheet"});
overlayBaseStylesheet = $("style:last")[0];
call(config[overlayTiddlerTitle].onOpen);
$overlay.fadeIn(1000, function() {
allStylesFrag = document.createDocumentFragment();
$("style").each(function() {
if (this!=overlayBaseStylesheet) allStylesFrag.appendChild(this);
});
// $.twStylesheet("#copyright,#saveTest,#backstage,#backstageCloak,#backstageArea,#backstageButton,#storeArea,#shadowArea,#contentWrapper { display: none; }", {id:"main"});
$.twStylesheet("#copyright,#saveTest,#backstage,#backstageCloak,#backstageArea,#backstageButton,#storeArea,#shadowArea,#contentWrapper { height: 0; overflow: hidden; }", {id:"main"});
$("#backstageArea,#backstageButton").hide();
});
} else {
$.twStylesheet.remove({id:"main"});
$(allStylesFrag).children().each(function() {
$("head").append(this);
});
$("#backstageArea,#backstageButton").show();
call(config[overlayTiddlerTitle].onClose);
$("#overlay").fadeOut(function() {
$.twStylesheet.remove({id:"overlayStyleSheet"});
$(this).hide();
});
}
}
function call(fn) { return fn ? fn() : false; }
makeOverlay();
})(jQuery);
/*}}}*/
/*
* IFrame Loader Plugin for JQuery
* - Notifies your event handler when iframe has finished loading
* - Your event handler receives loading duration (as well as iframe)
* - Optionally calls your timeout handler
*
* http://project.ajaxpatterns.org/jquery-iframe
*
* The MIT License
*
* Copyright (c) 2009, Michael Mahemoff
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
(function($) {
var timer;
$.fn.src = function(url, onLoad, options) {
setIFrames($(this), onLoad, options, function() {
this.src = url;
});
return $(this);
}
$.fn.squirt = function(content, onLoad, options) {
setIFrames($(this), onLoad, options, function() {
// var doc = this.contentDocument || this.contentWindow.document;
doc = this.document;
if(this.contentDocument)
doc = this.contentDocument; // For NS6
else if(this.contentWindow)
doc = this.contentWindow.document; // For IE5.5 and IE6
doc.open();
doc.writeln(content);
doc.close();
});
return this;
}
function setIFrames(iframes, onLoad, options, iFrameSetter) {
iframes.each(function() {
if (this.tagName=="IFRAME") setIFrame(this, onLoad, options, iFrameSetter);
});
}
function setIFrame(iframe, onLoad, options, iFrameSetter) {
var iframe;
iframe.onload = null;
if (timer) clearTimeout(timer);
var defaults = {
timeoutDuration: 0,
timeout: null
};
var opts = $.extend(defaults, options);
if (opts.timeout && !opts.timeoutDuration) opts.timeoutDuration = 60000;
opts.frameactive = true;
var startTime = (new Date()).getTime();
if (opts.timeout) {
var timer = setTimeout(function() {
opts.frameactive=false;
iframe.onload=null;
if (opts.timeout) opts.timeout(iframe, opts.timeout);
}, opts.timeoutDuration);
};
var onloadHandler = function() {
var duration=(new Date()).getTime()-startTime;
if (timer) clearTimeout(timer);
if (onLoad && opts.frameactive) onLoad.apply(iframe,[duration]);
opts.frameactive=false;
}
iFrameSetter.apply(iframe);
iframe.onload = onloadHandler;
opts.completeReadyStateChanges=0;
iframe.onreadystatechange = function() { // IE ftw
if (++(opts.completeReadyStateChanges)==3) onloadHandler();
}
return iframe;
};
})(jQuery);
/***
|Name|TrailPlayer|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
!Content
<div id="topBar">
<div id="playControls">
<!-- TRAIL INFO -->
<select id="trail"></select>
<div id="infoLink"> </div>
<!-- FLOATING LEFT -->
<div id="nav">
<span id="start" class="navigator"> </span>
<span id="prev" class="navigator"> </span>
<span id="next" class="navigator"> </span>
<span id="end" class="navigator"> </span>
</div>
<select id="bookmark"></select>
<div id="progressHolder">
<img id="progress" src="images/progress.gif" />
<img id="reload" src="images/reload.gif" />
</div>
</div>
<!-- FLOATING RIGHT -->
<a id="close" class="clickster">
<div class="icon">X</div>
<div class="prompt">close</div>
</a>
<div id="hide" class="clickster">
<div class="icon">↑</div>
<div class="prompt">hide</div>
</div>
<div id="noteControl" class="clickster showing">
<div title="note" class="icon"> </div>
<div class="prompt">note</div>
<div id="commentStat" class="stat"><span id="commentCount">--</span> comments from users</div>
</div>
</div>
<div id="miniBar">
<div id="controls">
<span id="restore">↓trail</span>
<span id="miniPrev" class="navigator">«</span>
<span id="miniNext" class="navigator">»</span>
<!-- <a id="miniLink">#</a> -->
</div>
</div>
<div id="note">
<div id="noteHide">↑ hide note and comments ↑</div>
<div class="existingNote">
<h5><a href="#"></a></h5>
<div class="content"></div>
<div class="absentContent">[no note]</div>
<h4>comments</h4>
<div id="comments"></div>
</div>
</div>
<div id="resourceView">
<div id="initialBacking">---</div>
</div>
!StyleSheet
#overlay { background: black; font-family: sans-serif; overflow: hidden; }
.pseudoLink, a, a:visited { text-decoration: none; color: #00b; cursor: pointer; }
li { list-style-type: none; }
#topBar { position: absolute; top: 0; left: 0; width: 100%; height: 46px;
z-index: 90000;
border-bottom: 2px solid #666;
line-height: 1; overflow: hidden;
font-family: sans-serif;
background-color: #a8dae5;
background-image: -webkit-gradient(linear, left top, left bottom, from(#d3ffff), to(#58dae5));
background-image: -moz-linear-gradient(top, #d3ffff, #58dae5);
filter:progid:DXImageTransform.Microsoft.Gradient (GradientType=0, StartColorStr='#d3ffff', EndColorStr='#58dae5'); }
#infoLink { margin-left: 5px; }
#infoLink { background-image: url(images/info16.png); background-position: 5px 0; background-repeat: no-repeat; width:25px; float: left; height: 36px; margin-top: 2px; cursor: pointer; }
.modal { font-family: Gill Sans, sans-serif; }
#info button {
cursor: pointer;
border: 1px solid black;
background: #ff9;
margin: 5px 0 10px; -moz-border-radius: 5px; -webkit-border-radius: 5px;
}
#info button:hover { background: #dd7; }
#info .selected { background: #fed; }
#info #options { margin: 10px 10px 0 0; font-size: x-small; font-family: serif; text-align: right; }
#info .note { background: #000; color: #fff; padding: 10px; font-size: small;
-moz-border-radius: 5px; -webkit-border-radius: 5px;
margin: 5px 10px 10px; }
#info li { padding: 5px; cursor: pointer; }
#info li:hover { background: #e9e9ff; }
.infoURL { font-size: small; color: #00b; }
#playControls { margin: 12px 0 0 20px; float: left; }
#nav { float: left; margin: -3px 10px 0 50px; }
#nav span { float: left; cursor: pointer; width: 38px; height: 30px; background-position: 0 0; background-repeat: no-repeat; }
#next { background-image: url("images/next.png"); }
#next:hover { background-image: url("images/next-hovered.png"); }
#prev { background-image: url("images/prev.png"); }
#prev:hover { background-image: url("images/prev-hovered.png"); }
#end { background-image: url("images/end.png"); }
#end:hover { background-image: url("images/end-hovered.png"); }
#start { background-image: url("images/start.png"); }
#start:hover { background-image: url("images/start-hovered.png"); }
select { width: 100px; float: left; text-align: center; }
select#bookmark { width: 200px; }
// #progress { float: left; width: 16px; height: 16px; padding-left: 10px; visibility: hidden; }
#progressHolder { float: left; width: 16px; height: 16px; padding-left: 10px; cursor: pointer; }
#noteExpand { position: absolute; top: 28px; height: 16px; opacity: 0; }
#noteHide { text-align: center; width: 100%; color: #06b; cursor: pointer; }
#note { background: #000; }
#note { position: absolute; top: 48px; right: 5px; width: 200px;
border: 1px solid #444; border-top-width: 0;
opacity: 0.9; filter:alpha(opacity=90);
-moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px;
padding: 2px 10px 10px; z-index: 999992; color: #fff; font-size: 12px; }
#note h4 { margin: 5px 0; text-align: right; font-style: italic; color: #333; cursor: pointer; }
#note h5 a, #note h5 a:visited { color: #fff; margin: 10px 0; font-weight: bold; }
#note h5 a:hover { text-decoration: underline; }
.absentContent { opacity: 0.2; }
#noteControl .icon { background-image: url("images/note.png"); background-repeat: no-repeat; background-position: 9px 0; }
* html #noteControl .icon { background-image: url("images/note.gif"); }
#noteControl.showing, #noteControl.showing div { background-color: #5bbac7; color: #333; }
.stat { color: #777; text-align: right; padding-right: 5px; }
#miniBar { position: absolute; top: 0; right: 0;
text-align: right; z-index: 1; }
#miniBar #restore { font-size: small; }
#miniBar #controls { background: #a8dae5; opacity: 0.9; padding: 4px;
border: 2px solid #38626b; border-top-width: 0; border-right-width: 0; }
#miniBar #controls, #miniBar a { color: #000; }
#miniBar #controls span:hover { color: #0000dd; }
#miniBar #controls span { margin: 0 4px; cursor: pointer; }
.clickster { width: 40px; height: 100%; float: right; text-align: center; }
.clickster { font-size: 14px; float: right; padding: 2px 1px; cursor: pointer; width: 44px; height: 46px; text-align: center; float: right; }
.clickster:hover,a.clickster:hover { background: #5081b2; }
.clickster div, .clickster a { text-align: center; font-size: small; color: #000; }
.clickster div.icon { font-size: 20px; margin: 5px 0 2px; }
* html #resourceView { top: 48px; }
body { overflow: hidden; }
#resourceView { position: absolute; width: 100%; height: 100%; top: 0; left: 0; margin-top: 48px; }
#resourceView iframe { position: relative; top: 0; left: 0; width: 100%; height: 100%; z-index: 2; }
!InfoTemplate
<div id="info">
<h3><%= trail.name %></h3>
<p><%= trail.description %></p>
<ol id="bookmarks" class="xoxo">
<% jQuery.each(trail.bookmarks, function(i, bookmark) { %>
<% var selectedString = (i==trail.selectedIndex ? " selected" : ""); %>
<li class="bookmarkItem<%= selectedString %>">
<a class="bookmark" href="<%= bookmark.url %>" target="bookmarkView" title="<%= bookmark.name %>"><%= bookmark.name %></a>
<div class="infoURL"><%= bookmark.url %></div>
<% if (jQuery.trim(bookmark.note).length) { %>
<div class="note"><%= bookmark.note %></div>
<% } %>
</li>
<% }); %>
</ol>
</div>
!Javascript
{{{
***/
(function($) {
var player = version.extensions.TrailPlayer = {installed:true};
config.TrailPlayer = {
onLoad: function() {
$("#noteHide,#noteControl").click(toggleNote);
$("#bookmark").change(switchBookmark);
wireNavButtons();
$("#progressHolder").click(function() { loadIframe(); });
$("#infoLink").click(showInfo);
$("#hide").click(function() { toggleTopBar(false); });
$("#restore").click(function() { toggleTopBar(true); });
$("#close").click(version.extensions.overlay.toggle);
// if (!store.getTaggedTiddlers("trail").length) return false;
// player.syncFromFragmentID();
// return $("#trail option").length;
populateDropdowns();
},
onOpen: function() {
// player.syncToFragmentID();
populateDropdowns();
},
onClose: function() {
if (!this.closedBefore) {
this.closedBefore = true;
story.closeAllTiddlers();
}
story.displayTiddler("top", getCurrentTrail().name);
story.displayTiddler("top", getCurrentBookmark().name);
player.clearFragmentID();
},
};
function populateDropdowns() {
$("#trail").empty();
$.each(store.getTaggedTiddlers("trail"), function(i,trailTiddler) {
$("<option>"+trailTiddler.title+"</option>")
.val(trailTiddler.title)
.appendTo($("#trail"));
});
$("#trail").change(function() {
switchTrail();
});
}
version.extensions.TrailPlayer.syncFromFragmentID = function() {
var matches = document.location.href.match(/#\[trail\[(.*?)(\/(.*)\])?\]$/);
if (matches) {
var trailTitle=decodeURI(matches[1]), bookmarkTitle = decodeURI(matches[3]);
$.switchTrailPlayer(trailTitle, bookmarkTitle);
/*
$("#trail option[value="+trailTitle+"]").attr("selected", true);
var bookmarkIndex = _.pluck(getCurrentTrail().bookmarks, "name").indexOf(bookmarkTitle);
switchTrail(bookmarkIndex==-1 ? 0 : bookmarkIndex);
*/
return true;
} else {
switchTrail();
return false;
}
};
version.extensions.TrailPlayer.syncToFragmentID = function() {
document.location.hash = "#[trail[" + encodeURI(getCurrentTrail().name) + "/" + encodeURI(getCurrentBookmark().name)+"]]";
};
version.extensions.TrailPlayer.clearFragmentID = function() {
document.location.hash = "";
}
var navButtonCalculators = {
next: function(siteIndex) { return siteIndex+1; },
prev: function(siteIndex) { return siteIndex-1; },
start: function(siteIndex) { return 0; },
end: function(siteIndex) { return $("#bookmark option").length-1; }
};
navButtonCalculators.miniNext = navButtonCalculators.next;
navButtonCalculators.miniPrev = navButtonCalculators.prev;
function wireNavButtons() {
$(".navigator").click(function() {
// var bookmarkIndex = $("#bookmark option:selected").prevAll().length;
var bookmarkIndex = getCurrentBookmarkIndex();
var newIndex = navButtonCalculators[this.id](parseInt(bookmarkIndex, 10));
if (newIndex!=bookmarkIndex && newIndex>=0 && newIndex<$("#bookmark option").length) {
$("#bookmark option").get(newIndex).selected = true;
switchBookmark();
}
});
}
function showInfo() {
var modalOptions = {
dialogWidth: 600,
dialogHeight: 600
};
var trail = getCurrentTrail();
trail.selectedIndex = getCurrentBookmarkIndex();
var infoMessage = version.extensions.microtemplate("TrailPlayer##InfoTemplate", {
trail: trail
});
$.modal.show($("<div/>").html(infoMessage), modalOptions);
$("#info ol").click(function(ev) {
var firstButton = (ev.which===1 || ev.button===0);
if (!firstButton) return;
var $target = $(ev.target).closest("li");
if ($target.length) {
$(".modal").data("close", null);
$.modal.close();
$("#bookmark option").get($target.prevAll().length).selected = true;
switchBookmark();
}
});
}
function toggleNote() {
var willBeHidden = $("#note").isDisplayed();
if (willBeHidden) {
$("#noteControl").removeClass("showing");
} else {
$("#noteControl").addClass("showing");
$("#noteHide").css("opacity", 1);
}
$("#note").slideToggle();
}
function toggleTopBar(shouldShow) {
var DELAY=300;
if (shouldShow) {
$("#note").css("opacity",0).animate({opacity:1}, DELAY);
$("#topBar").slideDown(DELAY);
$("#resourceView").animate({"marginTop": 48}, DELAY, function() {
// $("#miniBar").removeClass("available");
$("#miniBar").css({zIndex: -1, opacity: 0});
});
} else {
$("#note").css("opacity",1).animate({opacity:0}, DELAY);
$("#topBar").slideUp(DELAY);
$("#resourceView").animate({"marginTop": 0}, DELAY, function() {
$("#miniBar").addClass("available").css("zIndex", 999999).blink(). hover(function() {
$(this).css("opacity", 1);
}, function() {
$(this).css("opacity", 0.05);
});
});
}
}
function switchTrail(bookmarkIndex) {
if (!$("#trail option").length) return;
$("#bookmark").empty();
var trailTiddler = store.getTiddler($("#trail").val());
var trail = version.extensions.TrailPlugin.extractTrail(trailTiddler);
$.each(trail.bookmarks, function(i,bookmark) {
$("<option/>").html(bookmark.name).val(bookmark.name).appendTo($("#bookmark"));
});
if (trail.bookmarks.length) $("#bookmark option").get(bookmarkIndex||0).selected = true;
switchBookmark();
}
function switchBookmark() {
// $('#resourceView').empty().create("<iframe/>").src(url, function() { $("#progress").visible(false); });
// var bookmark = $("#bookmark").val();
// $("#progress").visible();
// $('#resourceView').empty().attach("<iframe/>").src(getCurrentBookmark().url,
var bookmark = getCurrentBookmark();
$("#note .content").showIf(!isWhitespace(bookmark.note));
$("#note .absentContent").showIf(isWhitespace(bookmark.note));
$("#note .content").html(bookmark.note);
// $.trim(bookmark.note).length
// $("#resourceView iframe").src(bookmark.url, function() { $("#progress").hidden(); });
loadIframe();
if ($("#overlay").isDisplayed()) player.syncToFragmentID();
}
function loadIframe(url) {
// if (!url) url = $("#bookmark
var url = getCurrentBookmark().url;
showProgress();
$("#resourceView").html($("<iframe/>"));
$("#resourceView iframe").src(url, function() { hideProgress(); });
}
function getCurrentTrail() {
var trailTiddler = store.getTiddler($("#trail").val());
return version.extensions.TrailPlugin.extractTrail(trailTiddler);
}
function getCurrentBookmark() {
// var bookmarkIndex = $("#bookmark").val();
return getCurrentTrail().bookmarks[getCurrentBookmarkIndex()];
}
function getCurrentBookmarkIndex() { // TODO inline
// return $("#bookmark").val();
return $("#bookmark option:selected").prevAll().length;
}
function log() {
if (console && console.log) console.log.apply(console, arguments);
}
$.fn.attach = function(html) { return this.append(html).children(":last"); };
$.fn.visible = function() { return this.css("visibility", "visible"); };
$.fn.hidden = function() { return this.css("visibility", "hidden"); };
function showProgress() { $("#progress").show(); $("#reload").hide(); }
function hideProgress() { $("#progress").hide(); $("#reload").show(); }
$.fn.showIf = function(shouldShow, options) {
var settings = $.extend({
showEffect: function() { $(this).show(); },
hideEffect: function() { $(this).hide(); }
}, options);
shouldShow ? settings.showEffect.apply(this) : settings.hideEffect.apply(this);
return $(this);
};
$.fn.isDisplayed = function() { return $(this).css("display")!="none"; };
$.fn.slideToggle = function() { return $(this)[$(this).isDisplayed() ? "slideUp":"slideDown"](200); };
$.fn.toggleClass = function(test, klass) { test ? this.addClass(klass):this.removeClass(klass); };
$.fn.blink = function() {
for (var i=0; i<2; i++) $(this).fadeTo(200, 1).fadeTo(i==2?1000:200, 0.05);
return $(this);
};
function isWhitespace(s) { return (/^[ \t\n]*$/).test(s); }
$.switchTrailPlayer = function(trailTitle, bookmarkTitle) {
$("#trail option[value="+trailTitle+"]").attr("selected", true);
var bookmarkIndex = _.pluck(getCurrentTrail().bookmarks, "name").indexOf(bookmarkTitle);
switchTrail(bookmarkIndex==-1 ? 0 : bookmarkIndex);
}
// http://lanitdev.wordpress.com/2009/09/22/jquery-custom-selector-for-selecting-elements-by-exact-text-textequals/
})(jQuery);
/*}}}*/
/***
|Name|TagCloud|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
!StyleSheet
.tagCloud .tagg { margin-right: 8px; cursor: pointer; line-height: 2em; }
.tagCloud .tag0 { font-size: 0.8em; }
.tagCloud .tag1 { font-size: 1em; }
.tagCloud .tag2 { font-size: 1.15em; }
.tagCloud .tag3 { font-size: 1.3em; }
.tagCloud .tag4 { font-size: 1.6em; }
.tagCloud .clearance { height: 1px; clear: left; }
!Javascript
{{{
***/
(function($) {
version.extensions.tagCloud = {installed:true};
version.extensions.tagCloud.getTagRecords = function(options) {
var settings = $.extend({
filterTag: null,
excludeTags: ["systemConfig", "systemTheme", "excludeSearch", "excludeLists"],
prefix: null
}, options);
var tiddlers = settings.filterTag ? store.getTaggedTiddlers(settings.filterTag) : store.getTiddlers();
var tagRecords = [];
_.each(tiddlers, function(tiddler) {
_.each(tiddler.tags, function(tag) {
if ( (!settings.excludeTags || settings.excludeTags.indexOf(tag)==-1)
&& (!settings.prefix || tag.substr(0,settings.prefix.length)==settings.prefix) ) {
var tagRecord =
_.detect(tagRecords, function(tagRecord) { return tagRecord.tag==tag; })
|| tagRecords.push({ tag: tag, count: 0 });
tagRecord.count++;
}
});
});
if (options.quantity) {
tagRecords.sort(function(a,b) { return a.count > b.count ? 1 : -1; });
tagRecords = tagRecords.slice(0,options.quantity);
}
tagRecords.sort(function(a,b) { return a.tag > b.tag ? 1 : -1; });
return tagRecords;
};
version.extensions.tagCloud.getTags = function(filterTag, excludeTags) {
return _.pluck(version.extensions.tagSuggest.getTagRecords(filterTag, excludeTags), "tag");
}
version.extensions.tagCloud.clicked = function(tag) {
story.displayTiddler("top", tag);
}
var macro = config.macros.tagCloud = {
init: function() {
var stylesheet = store.getTiddlerText("TagCloud##StyleSheet");
config.shadowTiddlers["StyleSheetTagCloud"] = stylesheet;
store.addNotification("StyleSheetTagCloud", refreshStyles);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var p=getParam;
var params = paramString.parseParams();
var filterTag = p(params, "filterTag");
var excludeTags = (p(params, "excludeTags") || "").split(",");
var prefix = p(params, "prefix");
var quantity = p(params, "quantity");
var format = p(params, "format") || "%t";
var tagRecords = version.extensions.tagCloud.getTagRecords({
filterTag: filterTag,
excludeTags: excludeTags,
prefix: prefix,
quantity: quantity
});
var orderedTagRecords = _.clone(tagRecords).sort(function(a,b) { return a.count > b.count ? 1 : -1; });
var buckets = [];
var bucketAmount = 5;
$.each(orderedTagRecords, function(i, tagRecord) {
tagRecord.bucket = Math.round((i/orderedTagRecords.length)*bucketAmount);
});
b=buckets;
t=tagRecords;
var $tagCloud = $("<div class='tagCloud'/>").appendTo(place);
$.each(tagRecords, function(i, tagRecord) {
var message = format.replace("%t", tagRecord.tag.replace(prefix,"")).replace("%c", tagRecord.count); // TODO only if prefix at start
$("<a/>").html(message)
.addClass("tagg").addClass("tag"+tagRecord.bucket)
.data("tag", tagRecord.tag)
.appendTo($tagCloud);
$tagCloud.append(" ");
});
$("<div class='clearance'>").appendTo($tagCloud);
$(place).click(function(ev) {
var $tag = $(ev.target);
if ($tag.hasClass("tagg")) version.extensions.tagCloud.clicked($tag.data("tag"));
})
}
};
// TODO delete and test
function _map(list, fn) {
var mapped = [];
$.each(list, function(i, item) { mapped.push(fn(item)); });
return mapped;
}
// TODO delete and test
function _clone(list, fn) {
return _map(list, function(item) { return item; });
}
function log() {
console.log.apply(console, arguments);
}
// underscore
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.7";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!a.concat&&!a.substr&&!a.apply&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};
b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;
a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;
b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=
function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();
})(jQuery);
/*}}}*/
/***
|Name|TagSuggest|
|Description||
|Source||
|Documentation||
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
!StyleSheet
.ac_results {
padding: 0px;
border: 1px solid WindowFrame;
background-color: Window;
overflow: hidden;
}
.ac_results ul {
width: 100%;
list-style-position: outside;
list-style: none;
padding: 0;
margin: 0;
}
.ac_results iframe {
display:none;
position:absolute;
top:0;
left:0;
z-index:-1;
filter:mask();
width:3000px;
height:3000px;
}
.ac_results li {
margin: 0px;
padding: 2px 5px;
cursor: pointer;
display: block;
width: 100%;
font: menu;
font-size: 12px;
overflow: hidden;
}
.ac_loading {
background : Window url('./indicator.gif') right center no-repeat;
}
.ac_over {
background-color: Highlight;
color: HighlightText;
}
.tagSuggest { margin-top: 2px; }
.tagView { float: left; padding: 5px 10px 5px 0; }
.tagView div { float: left; }
.tagSuggest button { margin-left: 5px; }
.tagDelete { cursor: pointer; color: #00b; margin: auto 5px; }
.clearance { clear: both; height: 1px; }
!Javascript
{{{
***/
(function($) {
version.extensions.tagSuggest = {installed:true};
version.extensions.tagHelper = {installed:true};
version.extensions.tagHelper.getTagRecords = function(options) {
var settings = $.extend({
filterTag: null,
excludeTags: ["systemConfig", "systemTheme", "excludeSearch", "excludeLists"],
prefix: null
}, options);
var tiddlers = settings.filterTag ? store.getTaggedTiddlers(settings.filterTag) : store.getTiddlers();
var tagRecords = [];
_.each(tiddlers, function(tiddler) {
_.each(tiddler.tags, function(tag) {
if ( (!settings.excludeTags || settings.excludeTags.indexOf(tag)==-1)
&& (!settings.prefix || tag.substr(0,settings.prefix.length)==settings.prefix) ) {
var tagRecord =
_.detect(tagRecords, function(tagRecord) { return tagRecord.tag==tag; })
|| tagRecords.push({ tag: tag, count: 0 });
tagRecord.count++;
}
});
});
if (options.quantity) {
tagRecords.sort(function(a,b) { return a.count > b.count ? 1 : -1; });
tagRecords = tagRecords.slice(0,options.quantity);
}
tagRecords.sort(function(a,b) { return a.tag > b.tag ? 1 : -1; });
return tagRecords;
};
version.extensions.tagHelper.getTags = function(options) {
return _.pluck(version.extensions.tagHelper.getTagRecords(options), "tag");
};
version.extensions.tagSuggest.enhanceEditTemplate = function() {
var template = config.shadowTiddlers.EditTemplate;
if ((/tagSuggest/g).test(template)) return; // already enhanced
var tagsDiv = "<div class='editor' macro='edit tags'></div>";
var tagsDivHidden = "<div class='editor' macro='edit tags' style='display:block;></div>";
var tagSuggestDiv = "<div class='tagSuggest' macro='tagSuggest'></div>";
config.shadowTiddlers.EditTemplate = template.replace(tagsDiv,tagSuggestDiv+"\n"+tagsDivHidden);
};
var macro = config.macros.tagSuggest = {
init: function() {
var stylesheet = store.getTiddlerText("TagSuggest##StyleSheet");
config.shadowTiddlers["StyleSheetTagSuggest"] = stylesheet;
store.addNotification("StyleSheetTagSuggest", refreshStyles);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var $tagsContainer = $("<div/>")
.appendTo(place)
.data("tiddler", tiddler)
.data("params", paramString.parseParams());
var prefix = getParam(paramString.parseParams(), "prefix") || "";
var unprefixedTags = _.select(tiddler.tags, function(tag) {
return tag.indexOf(prefix)===0;
});
unprefixedTags = _.map(unprefixedTags, function(tag) {
return tag.replace(prefix, "");
});
$tagsContainer.data("tags", unprefixedTags);
var $tagInput = $("<input class='tagInput'/>")
.keydown(function(ev) {
if (ev.keyCode==13) {
if ($(".ac_results").css("display")=="none") addTag(ev.target.value, $tagsContainer);
return false;
}
})
.appendTo($tagsContainer);
var $add = $("<button>add tag</button>").appendTo($tagsContainer)
.click(function() {
addTag($(this).siblings(".tagInput").val(), $tagsContainer);
});
$tags = $("<div class='tagsView'/>").appendTo($tagsContainer);
$("<div class='.clearance'> </div>").appendTo($tagsContainer);
updateTags($tagsContainer);
makeAutoComplete($tagsContainer);
}
};
function addTag(tagName, $tagsContainer) {
var tags = $tagsContainer.data("tags");
if (tags.indexOf(tagName)==-1) {
$tagsContainer.data("tags").push(tagName);
updateTags($tagsContainer);
}
$(".tagInput", $tagsContainer).val("");
}
function updateTags($tagsContainer) {
$tagsContainer.data("tags").sort();
var $tagsView = $(".tagsView", $tagsContainer).empty();
$.each($tagsContainer.data("tags"), (function(i, tag) {
var $tag = $("<div class='tagView'>").appendTo($tagsView);
$("<div class='tagName'/>").text(tag).appendTo($tag);
$("<div class='tagDelete'/>").text("x").appendTo($tag).click(function() {
$tagsContainer.data("tags").remove(tag);
updateTags($tagsContainer);
});
}));
var $realTags = $tagsContainer.parents(".tiddler").find("[edit=tags]");
console.log("TC", $tagsContainer.length, $tagsContainer, "RT", $realTags.length, "sel", $realTags.selector);
if (!$realTags.length) return;
var prefix = getParam($tagsContainer.data("params"), "prefix") || "";
var unprefixedRealTags = _.select($realTags.val().split(/ +/), function(tag) {
return tag.indexOf(prefix)!=0;
});
chosenPrefixedTags = $tagsContainer.data("tags").map(function(tag) {
return prefix+tag;
});
$realTags.val((unprefixedRealTags.concat(chosenPrefixedTags)).sort().join(" "));
// makeAutoComplete($tagsContainer);
};
function makeAutoComplete($tagsContainer) {
var params = $tagsContainer.data("params"),
chosenTags = $tagsContainer.data("tags")
prefix = getParam(params, "prefix");
var candidateTags = version.extensions.tagHelper.getTags({
filterTag: getParam(params, "filterTag"),
excludeTags: (getParam(params, "excludeTags") || "").split(","),
prefix: prefix
});
console.log("cand 2", candidateTags, "chosen", chosenTags);
candidateTags = _.select(candidateTags, function(tag) {
return chosenTags.indexOf(tag)==-1;
});
console.log("cand after", candidateTags);
if (prefix) candidateTags = _.map(candidateTags, function(tag) {
return tag.replace(prefix, "");
});
$(".tagInput", $tagsContainer).autocompleteArray(candidateTags, {
onItemSelect: function(li) {
addTag(li.innerHTML, $tagsContainer);
}
});
}
// http://project.mahemoff.com/autocomplete
// adapted from http://www.pengoworks.com/workshop/jquery/autocomplete.htm
jQuery.autocomplete = function(input, options) {
// Create a link to self
var me = this;
// Create jQuery object for input element
var $input = $(input).attr("autocomplete", "off");
// Apply inputClass if necessary
if (options.inputClass) $input.addClass(options.inputClass);
// Create results
var results = document.createElement("div");
// Create jQuery object for results
var $results = $(results);
$results.hide().addClass(options.resultsClass).css("position", "absolute");
if( options.width > 0 ) $results.css("width", options.width);
// Add to body element
$("body").append(results);
input.autocompleter = me;
var timeout = null;
var prev = "";
var active = -1;
var cache = {};
var keyb = false;
var hasFocus = false;
var lastKeyPressCode = null;
// flush cache
function flushCache(){
cache = {};
cache.data = {};
cache.length = 0;
};
// flush cache
flushCache();
// if there is a data array supplied
if( options.data != null ){
var sFirstChar = "", stMatchSets = {}, row = [];
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
if( typeof options.url != "string" ) options.cacheLength = 1;
// loop through the array and create a lookup structure
for( var i=0; i < options.data.length; i++ ){
// if row is a string, make an array otherwise just reference the array
row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);
// if the length is zero, don't add to list
if( row[0].length > 0 ){
// get the first character
sFirstChar = row[0].substring(0, 1).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
// if the match is a string
stMatchSets[sFirstChar].push(row);
}
}
// add the data items to the cache
for( var k in stMatchSets ){
// increase the cache size
options.cacheLength++;
// add to the cache
addToCache(k, stMatchSets[k]);
}
}
$input
.keydown(function(e) {
// track last key pressed
lastKeyPressCode = e.keyCode;
switch(e.keyCode) {
case 38: // up
e.preventDefault();
moveSelect(-1);
break;
case 40: // down
e.preventDefault();
moveSelect(1);
break;
case 9: // tab
case 13: // return
if( selectCurrent() ){
e.preventDefault();
}
break;
case 32: // space
if( selectCurrent() ){
e.preventDefault();
}
break;
default:
active = -1;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(function(){onChange();}, options.delay);
break;
}
})
.focus(function(){
// track whether the field has focus, we shouldn't process any results if the field no longer has focus
hasFocus = true;
})
.blur(function() {
// track whether the field has focus
hasFocus = false;
hideResults();
});
hideResultsNow();
function onChange() {
// ignore if the following keys are pressed: [del] [shift] [capslock]
if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
var v = $input.val().replace(/^.*?(\S+)$/, "$1");
if (v == prev) return;
prev = v;
if (v.length >= options.minChars) {
$input.addClass(options.loadingClass);
requestData(v);
} else {
$input.removeClass(options.loadingClass);
$results.hide();
}
};
function moveSelect(step) {
var lis = $("li", results);
if (!lis) return;
active += step;
if (active < 0) {
active = 0;
} else if (active >= lis.size()) {
active = lis.size() - 1;
}
lis.removeClass("ac_over");
$(lis[active]).addClass("ac_over");
// Weird behaviour in IE
// if (lis[active] && lis[active].scrollIntoView) {
// lis[active].scrollIntoView(false);
// }
};
function selectCurrent() {
var li = $("li.ac_over", results)[0];
if (!li) {
var $li = $("li", results);
if (options.selectOnly) {
if ($li.length == 1) li = $li[0];
} else if (options.selectFirst) {
li = $li[0];
}
}
if (li) {
selectItem(li);
return true;
} else {
return false;
}
};
function selectItem(li) {
if (!li) {
li = document.createElement("li");
li.extra = [];
li.selectValue = "";
}
var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
input.lastSelected = v;
prev = v;
$results.html("");
$input.val($input.val().replace(/(\S+)$/, v+" "));
hideResultsNow();
if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li); }, 1);
};
// selects a portion of the input string
function createSelection(start, end){
// get a reference to the input element
var field = $input.get(0);
if( field.createTextRange ){
var selRange = field.createTextRange();
selRange.collapse(true);
selRange.moveStart("character", start);
selRange.moveEnd("character", end);
selRange.select();
} else if( field.setSelectionRange ){
field.setSelectionRange(start, end);
} else {
if( field.selectionStart ){
field.selectionStart = start;
field.selectionEnd = end;
}
}
field.focus();
};
// fills in the input box w/the first match (assumed to be the best match)
function autoFill(sValue){
// if the last user key pressed was backspace, don't autofill
if( lastKeyPressCode != 8 ){
// fill in the value (keep the case the user has typed)
$input.val($input.val() + sValue.substring(prev.length));
// select the portion of the value not typed by the user (so the next character will erase)
createSelection(prev.length, sValue.length);
}
};
function showResults() {
// get the position of the input field right now (in case the DOM is shifted)
var pos = findPos(input);
// either use the specified width, or autocalculate based on form element
var iWidth = (options.width > 0) ? options.width : $input.width();
// reposition
$results.css({
width: parseInt(iWidth) + "px",
top: (pos.y + input.offsetHeight) + "px",
left: pos.x + "px"
}).show();
};
function hideResults() {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(hideResultsNow, 200);
};
function hideResultsNow() {
if (timeout) clearTimeout(timeout);
$input.removeClass(options.loadingClass);
if ($results.is(":visible")) {
$results.hide();
}
if (options.mustMatch) {
var v = $input.val();
if (v != input.lastSelected) {
selectItem(null);
}
}
};
function receiveData(q, data) {
if (data) {
$input.removeClass(options.loadingClass);
results.innerHTML = "";
// if the field no longer has focus or if there are no matches, do not display the drop down
if( !hasFocus || data.length == 0 ) return hideResultsNow();
if ($.browser.msie) {
// we put a styled iframe behind the calendar so HTML SELECT elements don't show through
$results.append(document.createElement('iframe'));
}
results.appendChild(dataToDom(data));
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
showResults();
} else {
hideResultsNow();
}
};
function parseData(data) {
if (!data) return null;
var parsed = [];
var rows = data.split(options.lineSeparator);
for (var i=0; i < rows.length; i++) {
var row = $.trim(rows[i]);
if (row) {
parsed[parsed.length] = row.split(options.cellSeparator);
}
}
return parsed;
};
function dataToDom(data) {
var ul = document.createElement("ul");
var num = data.length;
// limited results to a max number
if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
for (var i=0; i < num; i++) {
var row = data[i];
if (!row) continue;
var li = document.createElement("li");
if (options.formatItem) {
li.innerHTML = options.formatItem(row, i, num);
li.selectValue = row[0];
} else {
li.innerHTML = row[0];
li.selectValue = row[0];
}
var extra = null;
if (row.length > 1) {
extra = [];
for (var j=1; j < row.length; j++) {
extra[extra.length] = row[j];
}
}
li.extra = extra;
ul.appendChild(li);
$(li).hover(
function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
function() { $(this).removeClass("ac_over"); }
).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });
}
return ul;
};
function requestData(q) {
if (!options.matchCase) q = q.toLowerCase();
var data = options.cacheLength ? loadFromCache(q) : null;
// recieve the cached data
if (data) {
receiveData(q, data);
// if an AJAX url has been supplied, try loading the data now
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
$.get(makeUrl(q), function(data) {
data = parseData(data);
addToCache(q, data);
receiveData(q, data);
});
// if there's been no data found, remove the loading class
} else {
$input.removeClass(options.loadingClass);
}
};
function makeUrl(q) {
var url = options.url + "?q=" + encodeURI(q);
for (var i in options.extraParams) {
url += "&" + i + "=" + encodeURI(options.extraParams[i]);
}
return url;
};
function loadFromCache(q) {
if (!q) return null;
if (cache.data[q]) return cache.data[q];
if (options.matchSubset) {
for (var i = q.length - 1; i >= options.minChars; i--) {
var qs = q.substr(0, i);
var c = cache.data[qs];
if (c) {
var csub = [];
for (var j = 0; j < c.length; j++) {
var x = c[j];
var x0 = x[0];
if (matchSubset(x0, q)) {
csub[csub.length] = x;
}
}
return csub;
}
}
}
return null;
};
function matchSubset(s, sub) {
if (!options.matchCase) s = s.toLowerCase();
var i = s.indexOf(sub);
if (i == -1) return false;
return i == 0 || options.matchContains;
};
this.flushCache = function() {
flushCache();
};
this.setExtraParams = function(p) {
options.extraParams = p;
};
this.findValue = function(){
var q = $input.val();
if (!options.matchCase) q = q.toLowerCase();
var data = options.cacheLength ? loadFromCache(q) : null;
if (data) {
findValueCallback(q, data);
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
$.get(makeUrl(q), function(data) {
data = parseData(data)
addToCache(q, data);
findValueCallback(q, data);
});
} else {
// no matches
findValueCallback(q, null);
}
}
function findValueCallback(q, data){
if (data) $input.removeClass(options.loadingClass);
var num = (data) ? data.length : 0;
var li = null;
for (var i=0; i < num; i++) {
var row = data[i];
if( row[0].toLowerCase() == q.toLowerCase() ){
li = document.createElement("li");
if (options.formatItem) {
li.innerHTML = options.formatItem(row, i, num);
li.selectValue = row[0];
} else {
li.innerHTML = row[0];
li.selectValue = row[0];
}
var extra = null;
if( row.length > 1 ){
extra = [];
for (var j=1; j < row.length; j++) {
extra[extra.length] = row[j];
}
}
li.extra = extra;
}
}
if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
}
function addToCache(q, data) {
if (!data || !q || !options.cacheLength) return;
if (!cache.length || cache.length > options.cacheLength) {
flushCache();
cache.length++;
} else if (!cache[q]) {
cache.length++;
}
cache.data[q] = data;
};
function findPos(obj) {
var curleft = obj.offsetLeft || 0;
var curtop = obj.offsetTop || 0;
while (obj = obj.offsetParent) {
curleft += obj.offsetLeft
curtop += obj.offsetTop
}
return {x:curleft,y:curtop};
}
}
jQuery.fn.autocomplete = function(url, options, data) {
// Make sure options exists
options = options || {};
// Set url as option
options.url = url;
// set some bulk local data
options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;
// Set default values for required options
options.inputClass = options.inputClass || "ac_input";
options.resultsClass = options.resultsClass || "ac_results";
options.lineSeparator = options.lineSeparator || "\n";
options.cellSeparator = options.cellSeparator || "|";
options.minChars = options.minChars || 1;
options.delay = options.delay || 400;
options.matchCase = options.matchCase || 0;
options.matchSubset = options.matchSubset || 1;
options.matchContains = options.matchContains || 0;
options.cacheLength = options.cacheLength || 1;
options.mustMatch = options.mustMatch || 0;
options.extraParams = options.extraParams || {};
options.loadingClass = options.loadingClass || "ac_loading";
options.selectFirst = options.selectFirst || false;
options.selectOnly = options.selectOnly || false;
options.maxItemsToShow = options.maxItemsToShow || -1;
options.autoFill = options.autoFill || false;
options.width = parseInt(options.width, 10) || 0;
this.each(function() {
var input = this;
new jQuery.autocomplete(input, options);
});
// Don't break the chain
return this;
}
jQuery.fn.autocompleteArray = function(data, options) {
return this.autocomplete(null, options, data);
}
jQuery.fn.indexOf = function(e){
for( var i=0; i<this.length; i++ ){
if( this[i] == e ) return i;
}
return -1;
};
// underscore
(function(){var j=this,n=j._,i=function(a){this._wrapped=a},m=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;var k=Array.prototype.slice,o=Array.prototype.unshift,p=Object.prototype.toString,q=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;b.VERSION="0.5.7";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(b.isArray(a)||b.isArguments(a))for(var e=0,f=a.length;e<f;e++)c.call(d,
a[e],e,a);else{var g=b.keys(a);f=g.length;for(e=0;e<f;e++)c.call(d,a[g[e]],g[e],a)}}catch(h){if(h!=m)throw h;}return a};b.map=function(a,c,d){if(a&&b.isFunction(a.map))return a.map(c,d);var e=[];b.each(a,function(f,g,h){e.push(c.call(d,f,g,h))});return e};b.reduce=function(a,c,d,e){if(a&&b.isFunction(a.reduce))return a.reduce(b.bind(d,e),c);b.each(a,function(f,g,h){c=d.call(e,c,f,g,h)});return c};b.reduceRight=function(a,c,d,e){if(a&&b.isFunction(a.reduceRight))return a.reduceRight(b.bind(d,e),c);
var f=b.clone(b.toArray(a)).reverse();b.each(f,function(g,h){c=d.call(e,c,g,h,a)});return c};b.detect=function(a,c,d){var e;b.each(a,function(f,g,h){if(c.call(d,f,g,h)){e=f;b.breakLoop()}});return e};b.select=function(a,c,d){if(a&&b.isFunction(a.filter))return a.filter(c,d);var e=[];b.each(a,function(f,g,h){c.call(d,f,g,h)&&e.push(f)});return e};b.reject=function(a,c,d){var e=[];b.each(a,function(f,g,h){!c.call(d,f,g,h)&&e.push(f)});return e};b.all=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.every))return a.every(c,
d);var e=true;b.each(a,function(f,g,h){(e=e&&c.call(d,f,g,h))||b.breakLoop()});return e};b.any=function(a,c,d){c=c||b.identity;if(a&&b.isFunction(a.some))return a.some(c,d);var e=false;b.each(a,function(f,g,h){if(e=c.call(d,f,g,h))b.breakLoop()});return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=
function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g<e.computed&&(e={value:f,computed:g})});return e.value};b.sortBy=function(a,c,d){return b.pluck(b.map(a,
function(e,f,g){return{value:e,criteria:c.call(d,e,f,g)}}).sort(function(e,f){e=e.criteria;f=f.criteria;return e<f?-1:e>f?1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(a.toArray)return a.toArray();if(b.isArray(a))return a;if(b.isArguments(a))return k.call(a);return b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=function(a,c,d){return c&&!d?k.call(a,
0,c):a[0]};b.rest=function(a,c,d){return k.call(a,b.isUndefined(c)||d?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d));c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c===true?b.last(d)!=e:!b.include(d,
e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e<c;e++)d[e]=b.pluck(a,String(e));return d};b.indexOf=function(a,c){if(a.indexOf)return a.indexOf(c);for(var d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,c){if(a.lastIndexOf)return a.lastIndexOf(c);for(var d=
a.length;d--;)if(a[d]===c)return d;return-1};b.range=function(a,c,d){var e=b.toArray(arguments),f=e.length<=1;a=f?0:e[0];c=f?e[0]:e[1];d=e[2]||1;e=Math.ceil((c-a)/d);if(e<=0)return[];e=new Array(e);f=a;for(var g=0;;f+=d){if((d>0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||j,d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);b.each(c,function(d){a[d]=b.bind(a[d],a)});
return a};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=function(a){if(b.isArray(a))return b.range(0,a.length);
var c=[];for(var d in a)q.call(a,d)&&c.push(d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=function(a){return b.select(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.tap=function(a,c){c(a);return a};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a;if(d!=typeof c)return false;if(a==c)return true;if(!a&&c||a&&!c)return false;
if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return true;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return b.keys(a).length==
0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return!!(a&&a.concat&&a.unshift)};b.isArguments=function(a){return a&&b.isNumber(a.length)&&!a.concat&&!a.substr&&!a.apply&&!r.call(a,"length")};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a===+a||p.call(a)==="[object Number]"};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};
b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=n;return this};b.identity=function(a){return a};b.breakLoop=function(){throw m;};var s=0;b.uniqueId=function(a){var c=s++;return a?a+c:c};b.templateSettings={start:"<%",end:"%>",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings;
a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").replace(new RegExp("'(?=[^"+d.end[0]+"]*"+d.end+")","g"),"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;
b.methods=b.functions;var l=function(a,c){return c?b(a).chain():a};b.each(b.functions(b),function(a){var c=b[a];i.prototype[a]=function(){var d=b.toArray(arguments);o.call(d,this._wrapped);return l(c.apply(b,d),this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=Array.prototype[a];i.prototype[a]=function(){c.apply(this._wrapped,arguments);return l(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){var c=Array.prototype[a];i.prototype[a]=
function(){return l(c.apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})();
})(jQuery);
/*}}}*/
/*
TiddlyWiki Comments Plugin - Online demo at http://tiddlyguv.org/CommentsPlugin.html
TODO:
- Support Cascade comment delete when the top-level tiddler is deleted
- Support more than one < <comments> > per tiddler. This will probably entail creating an invisible root tiddler to
hold all the comments for a macro together. The user will need to provide an ID for this tiddler.
- Don't use global "macro" var (use "macro" param a la jquery)
*/
/***
|Name|CommentsPlugin|
|Description|Macro for nested comments, where each comment is a separate tiddler.|
|Source|http://tiddlyguv.org/CommentsPlugin.html#CommentsPlugin|
|Documentation|http://tiddlyguv.org/CommentsPlugin.html#CommentsPluginInfo|
|Version|0.1|
|Author|Michael Mahemoff, Osmosoft|
|''License:''|[[BSD open source license]]|
|~CoreVersion|2.2|
***/
/*{{{*/
if(!version.extensions.CommentsPlugin) {
version.extensions.CommentsPlugin = {installed:true};
(function(plugin) {
var cmacro = config.macros.comments = {
init: function() {
var stylesheet = store.getTiddlerText(tiddler.title + "##StyleSheet");
if (stylesheet) { // check necessary because it happens more than once for some reason
config.shadowTiddlers["StyleSheetCommentsPlugin"] = stylesheet;
store.addNotification("StyleSheetCommentsPlugin", refreshStyles);
}
if (!version.extensions.CommentsPlugin.retainViewTemplate) cmacro.enhanceViewTemplate();
},
enhanceViewTemplate: function() {
var template = config.shadowTiddlers.ViewTemplate;
if ((/commentBreadcrumb/g).test(template)) return; // already enhanced
var TITLE_DIV = "<div class='title' macro='view title'></div>";
var commentsDiv = "<div class='commentBreadcrumb' macro='commentBreadcrumb'></div>";
config.shadowTiddlers.ViewTemplate = template.replace(TITLE_DIV,commentsDiv+"\n"+TITLE_DIV);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var macroParams = paramString.parseParams();
var tiddlerParam = getParam(macroParams, "tiddler");
tiddler = tiddlerParam ? store.getTiddler(tiddlerParam) : tiddler;
if (!tiddler || !store.getTiddler(tiddler.title)) return;
cmacro.buildCommentsArea(tiddler, place, macroParams);
// cmacro.refreshCommentsFromRoot(story.getTiddler(tiddler.title).commentsEl, tiddler, macroParams);
cmacro.refreshCommentsFromRoot(place.commentsEl, tiddler, macroParams);
},
buildCommentsArea: function(rootTiddler, place, macroParams) {
var commentsArea = createTiddlyElement(place, "div", null, "comments");
var headingEl, heading = getParam(macroParams, "heading");
if (heading) heading = heading.replace("%c",
config.macros.comments.findCommentsFromRoot(rootTiddler).length);
if (heading) headingEl = createTiddlyElement(commentsArea, "h1", null, null, heading);
var comments = createTiddlyElement(commentsArea, "div", null, "");
place.commentsEl = comments;
var newCommentArea;
if (cmacro.editable(macroParams)) {
newCommentArea = createTiddlyElement(commentsArea, "div", null, "newCommentArea", "New comment:");
cmacro.forceLoginIfRequired(params, newCommentArea, function() {
var newCommentEl = cmacro.makeTextArea(newCommentArea, macroParams);
// var addComment = createTiddlyElement(newCommentArea, "button", null, "addComment button", "Add Comment");
var addComment = createTiddlyButton(newCommentArea, "Add Comment", null, function() {
var comment = cmacro.createComment(newCommentEl.value, rootTiddler, macroParams);
newCommentEl.value = "";
cmacro.refreshCommentsFromRoot(comments, rootTiddler, macroParams);
}, "addComment button");
});
}
var expandHeading = getParam(macroParams, "expandHeading");
if (heading && expandHeading && expandHeading.toLowerCase()=="true") {
var $expandPrompt, expandableElements;
var $expandables = jQuery([place.commentsEl,newCommentArea]).hide();
jQuery(headingEl)
.append($expandPrompt = "<span id='expandPrompt'> »</span>")
.addClass("expander").click(function() {
$expandables.toggle();
});
}
},
makeTextArea: function(container, macroParams) {
var textArea = createTiddlyElement(container, "textarea");
textArea.rows = getParam(macroParams, "textRows") || 4;
textArea.cols = getParam(macroParams, "textCols") || 20;
textArea.value = getParam(macroParams, "text") || "";
return textArea;
},
refreshCommentsFromRoot: function(rootCommentsEl, rootTiddler, macroParams) {
cmacro.treeifyComments(rootTiddler);
cmacro.refreshComments(rootCommentsEl, rootTiddler, macroParams);
},
refreshComments: function(daddyCommentsEl, tiddler, macroParams) {
var commentsEl;
if (tiddler.fields.daddy) {
var commentEl = cmacro.buildCommentEl(daddyCommentsEl, tiddler, macroParams);
daddyCommentsEl.appendChild(commentEl);
commentsEl = commentEl.commentsEl;
} else { // root element
removeChildren(daddyCommentsEl);
// refreshedEl = story.getTiddler(tiddler.title);
commentsEl = daddyCommentsEl;
}
for (var child = tiddler.firstChild; child; child = child.next) {
cmacro.refreshComments(commentsEl, child, macroParams);
}
},
// This has become more complex due to "confused comments" - multiple comments
// pointing back to the same daddy (which implies they all think they're the first
// child) or a single "2nd-last" sibling (which implies they all think they're the
// last sibling). This happens in the typical "atomic transaction 101" scenario -
// user A opens wiki, user B opens wiki, one of the users submits a comment,
// the other user submits a comment.
//
// Normally, each comment says "make my daddy's first child be me", or "make my prev
// sibling's next sibling be me". That's how the tree gets built. But to deal
// with confused comments, we now have to check if daddy/prev is already pointing
// to something. If so, we will have to walk through the list to find the right place
// for the new item.
//
// We begin by sorting by date; if we can assume we are walking through the comments by date,
// the confused comments will appear in the right order.
treeifyComments: function(rootTiddler) {
// First, clear the tree data
// We sort the comments to ensure "confused" comments
var comments = cmacro.findCommentsFromRoot(rootTiddler).sort(function(a,b) {
return a.modified > b.modified;
});
var nodes=comments.concat(rootTiddler);
for (var i=0; i<nodes.length; i++) {
delete nodes[i]["firstChild"];
delete nodes[i]["next"];
}
// Now walk through each comment
cmacro.forEach(comments, function(comment) {
var prev = comment.fields.prev;
var daddy = comment.fields.daddy;
if (prev) {
var prevTiddler = store.getTiddler(prev);
if (prevTiddler.next) {
for (var lastChild=prevTiddler.next; lastChild.next; lastChild=lastChild.next)
;
lastChild.next = comment;
// } else {
} else {
prevTiddler.next = comment;
}
} else {
var daddyTiddler = store.getTiddler(daddy);
if (daddyTiddler.firstChild) {
for (var lastChild=daddyTiddler.firstChild; lastChild.next; lastChild=lastChild.next)
;
lastChild.next = comment;
} else {
daddyTiddler.firstChild = comment;
}
}
});
for (var i=0; i<comments.length; i++) {
var c=comments.sort()[i];
}
},
logComments: function(comments) {
for (var i=0; i<comments.length; i++) {
var comment = comments[i];
}
},
findCommentsFromRoot: function(rootTiddler) {
var comments = [];
store.forEachTiddler(function(title,tiddler) {
if (tiddler.fields.root==rootTiddler.title) comments.push(tiddler);
});
return comments;
},
findChildren: function(daddyTiddler) {
var comments = [];
store.forEachTiddler(function(title,tiddler) {
if (tiddler.fields.daddy==daddyTiddler.title) comments.push(tiddler);
});
return comments;
},
buildCommentEl: function(daddyCommentsEl, comment, macroParams) {
// COMMENT ELEMENT
var commentEl = document.createElement("div");
commentEl.className = "comment";
// HEADING <- METAINFO AND DELETE
var headingEl = createTiddlyElement(commentEl, "div", null, "heading");
var metaInfoEl = createTiddlyElement(headingEl, "div", null, "commentTitle", comment.modifier + '@' + comment.modified.formatString(getParam(macroParams,"dateFormat") || "DDD, MMM DDth, YYYY hh12:0mm:0ss am"));
metaInfoEl.onclick = function() {
// story.closeAllTiddlers();
story.displayTiddler("top", comment.title, null, true);
// document.location.hash = "#" + comment.title;
};
var deleteEl = createTiddlyElement(headingEl, "div", null, "deleteComment", "X");
deleteEl.onclick = function() {
if (true || confirm("Delete this comment and all of its replies?")) {
cmacro.deleteTiddlerAndDescendents(comment);
commentEl.parentNode.removeChild(commentEl);
}
};
// TEXT
commentEl.text = createTiddlyElement(commentEl, "div", null, "commentText");
wikify(comment.text, commentEl.text);
// REPLY LINK
if (cmacro.editable(macroParams)) {
var replyLinkZone = createTiddlyElement(commentEl, "div", null, "replyLinkZone");
var replyLink = createTiddlyElement(replyLinkZone, "span", null, "replyLink", "reply to this comment");
replyLink.onclick = function() { cmacro.openReplyLink(comment, commentEl, replyLink, macroParams); };
}
// var clearance = createTiddlyElement(commentEl, "clearance", null, "clearance");
// clearance.innerHTML = " ";
// COMMENTS AREA
commentEl.commentsEl = createTiddlyElement(commentEl, "div", null, "comments");
// RETURN
return commentEl;
},
openReplyLink: function(commentTiddler, commentEl, replyLink, macroParams) {
if (commentEl.replyEl) {
commentEl.replyEl.style.display = "block";
return;
}
commentEl.replyEl = document.createElement("div");
commentEl.replyEl.className = "reply";
replyLink.style.display = "none";
var newReplyHeading = createTiddlyElement(commentEl.replyEl, "div", null, "newReply");
createTiddlyElement(newReplyHeading, "div", null, "newReplyLabel", "New Reply:");
var closeNewReply = createTiddlyElement(newReplyHeading, "div", null, "closeNewReply", "close");
closeNewReply.onclick = function() {
commentEl.replyEl.style.display = "none";
replyLink.style.display = "block";
};
cmacro.forceLoginIfRequired(params, commentEl.replyEl, function() {
var replyText = cmacro.makeTextArea(commentEl.replyEl, macroParams);
var submitReply = createTiddlyButton(commentEl.replyEl, "Reply", null, function() {
var newComment = cmacro.createComment(replyText.value, commentTiddler, macroParams);
replyText.value = "";
closeNewReply.onclick();
cmacro.refreshComments(commentEl.commentsEl, newComment, macroParams);
});
});
commentEl.insertBefore(commentEl.replyEl, commentEl.commentsEl);
},
createComment: function(text, daddy, macroParams) {
var rootTitle = daddy.fields.root ? daddy.fields.root : daddy.title;
// second case is the situation where daddy *is* root
var newComment = cmacro.createCommentTiddler(macroParams, rootTitle);
var fieldsParam = getParam(macroParams, "fields") || "";
var fields = fieldsParam.decodeHashMap();
var inheritedFields = (getParam(macroParams, "inheritedFields") || "").split(",");
cmacro.forEach(inheritedFields, function(field) {
if (field!="") fields[field] = daddy.fields[field];
});
var tagsParam = getParam(macroParams, "tags") || "comment";
var now = new Date();
newComment.set(null, text, config.options.txtUserName, now, tagsParam.split(","), now, fields);
var youngestSibling = cmacro.findYoungestChild(daddy)
if (youngestSibling) newComment.fields.prev = youngestSibling.title;
newComment.fields.daddy = daddy.title;
newComment.fields.root = rootTitle;
cmacro.saveTiddler(newComment.title);
autoSaveChanges(false);
return newComment;
},
findYoungestChild: function(daddy) {
var siblingCount = 0;
var elderSiblings = cmacro.mapize(cmacro.selectTiddlers(function(tiddler) {
isChild = (tiddler.fields.daddy==daddy.title);
if (isChild) siblingCount++;
return isChild;
}));
if (!siblingCount) return null;
// Find the only sibling that doesn't have a prev pointing at it
var youngestSiblings = cmacro.clone(elderSiblings) // as a starting point
cmacro.forEachMap(elderSiblings, function(tiddler) {
delete youngestSiblings[tiddler.fields.prev];
});
for (title in youngestSiblings) { return youngestSiblings[title]; }
},
// The recursive delete is run by a separate function (nested inside
// this one, for encapsulation purposes).
deleteTiddlerAndDescendents: function(tiddler) {
function deleteRecursively(tiddler) {
for (var child = tiddler.firstChild; child; child = child.next) {
deleteRecursively(child);
}
store.removeTiddler(tiddler.title);
}
cmacro.treeifyComments(store.getTiddler(tiddler.fields.root));
// save some info prior to deleting
var prev = tiddler.fields.prev;
var next = tiddler.next;
deleteRecursively(tiddler);
// used saved info
if (next) {
next.fields.prev = prev;
cmacro.saveTiddler(next.title);
}
autoSaveChanges(false);
},
forEach: function(list, visitor) { for (var i=0; i<list.length; i++) visitor(list[i]); },
forEachMap: function(map, visitor) { for (var key in map) visitor(map[key]); },
select: function(list, selector) {
var selection = [];
cmacro.forEach(list, function(currentItem) {
if (selector(currentItem)) { selection.push(currentItem); }
});
return selection;
},
selectTiddlers: function(selector) {
var tiddlers = [];
store.forEachTiddler(function(title, tiddler) {
var wanted = selector(tiddler);
if (wanted) tiddlers.push(tiddler);
});
return tiddlers;
},
map: function(list, mapper) {
var mapped = [];
cmacro.forEach(list, function(currentItem) { mapped.push(mapper(currentItem)); });
return mapped;
},
remove: function(list, unwantedItem) {
return cmacro.select(list,
function(currentItem) { return currentItem!=unwantedItem; });
},
mapize: function(tiddlerList) {
var map = {};
cmacro.forEach(tiddlerList, function(tiddler) { map[tiddler.title] = tiddler; });
return map;
},
clone: function(map) { return merge({}, map); },
editable: function(params) {
var editable = getParam(params, "editable");
return (!editable || editable!="false");
},
needsLogin: function(params) {
var loginCheck = getParam(params, "loginCheck");
return loginCheck && !window[loginCheck]();
},
forceLoginIfRequired: function(params, loginPromptContainer, authenticatedBlock) {
if (cmacro.needsLogin(params)) wikify("<<"+getParam(macroParams, "loginPrompt")+">>", loginPromptContainer);
else authenticatedBlock();
},
mergeReadOnly: function(first, second) {
var merged = {};
for (var field in first) { merged[field] = first[field]; }
for (var field in second) { merged[field] = second[field]; }
return merged;
},
// callers may replace this with their own ID generation algorithm
createCommentTiddler: function(macroParams, rootTitle) {
// var titleFormat = getParam(macroParams, "titleFormat") || "%root%Comment";
var prefix = rootTitle+"Comment"; // was "_comment"
if (!store.createGuidTiddler) return store.createTiddler(prefix+((new Date()).getTime()));
return store.createGuidTiddler(prefix);
},
saveTiddler: function(tiddler) {
var tiddler = (typeof(tiddler)=="string") ? store.getTiddler(tiddler) : tiddler;
store.saveTiddler(tiddler.title, tiddler.title, tiddler.text, tiddler.modifier, tiddler.modified, tiddler.tags, cmacro.mergeReadOnly(config.defaultCustomFields, tiddler.fields), false, tiddler.created)
},
log: function() { if (console && console.firebug) console.log.apply(console, arguments); },
assert: function() { if (console && console.firebug) console.assert.apply(console, arguments); },
copyFields: function(fromTiddler, toTiddler, field1, field2, fieldN) {
for (var i=2; i<arguments.length; i++) {
fieldKey = arguments[i];
if (fromTiddler.fields[fieldKey]) toTiddler.fields[fieldKey] = fromTiddler.fields[fieldKey];
}
}
}
config.macros.commentsCount = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var count = 0;
if (tiddler && store.getTiddler(tiddler.title)) {
var rootTiddler = paramString.length ? paramString : tiddler.title;
count = config.macros.comments.findCommentsFromRoot(store.getTiddler(rootTiddler)).length;
}
createTiddlyText(place, count);
}
},
config.macros.commentBreadcrumb = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if (!tiddler.fields.root) return;
var rootLink = createTiddlyElement(place, "span", null, null);
createTiddlyLink(rootLink, tiddler.fields.root, true);
var rootIsParent = tiddler.fields.daddy==tiddler.fields.root;
var rootIsGrandparent = (store.getTiddler(tiddler.fields.daddy)).fields.daddy==tiddler.fields.root;
if (!rootIsParent) {
if (!rootIsGrandparent) createTiddlyElement(place, "span", null, null, " > ... ");
createTiddlyElement(place, "span", null, null, " > ");
var daddyLink = createTiddlyElement(place, "span", null, null);
createTiddlyLink(daddyLink, tiddler.fields.daddy, true);
}
createTiddlyElement(place, "span", null, null, " > ");
// place.appendChild(createTiddlyLink(tiddler.fields.root));
}
}
config.macros.tiddlyWebComments = {};
config.macros.tiddlyWebComments.handler =
function(place,macroName,params,wikifier,paramString,tiddler) {
paramString = "fields:'server.workspace:bags/comments' inheritedFields:'server.host,server.type'";
config.macros.comments.handler(place,macroName,params,wikifier, paramString,tiddler);
};
function log() { if (console && console.firebug) console.log.apply(console, arguments); }
})(version.extensions.CommentsPlugin);
/***
!StyleSheet
.comments h1 { margin-bottom: 0; padding-bottom: 0; }
.comments { padding: 0; }
.comment .comments { margin-left: 1em; }
.comment { padding: 0; margin: 1em 0 0; }
.comment .comment { margin 0; }
.comment .toolbar .button { border: 0; color: #9a4; }
.comment .heading { background: [[ColorPalette::PrimaryPale]]; color: [[ColorPalette::PrimaryDark]]; border-bottom: 1px solid [[ColorPalette::PrimaryLight]]; border-right: 1px solid [[ColorPalette::PrimaryLight]]; padding: 0.5em; height: 1.3em; }
.commentTitle { float: left; }
.commentTitle:hover { text-decoration: underline; cursor: pointer; }
.commentText { clear: both; padding: 1em 1em; }
.deleteComment { float: right; cursor: pointer; text-decoration:underline; color:[[ColorPalette::SecondaryDark]]; padding-right: 0.3em; }
.comment .reply { margin-left: 1em; }
.comment .replyLink { color:[[ColorPalette::SecondaryDark]]; font-style: italic;
cursor: pointer; text-decoration: underline; margin: 0 1em; }
.comment .created { }
.comment .newReply { color:[[ColorPalette::SecondaryDark]]; margin-top: 1em; }
.newReplyLabel { float: left; }
.closeNewReply { cursor: pointer; float: right; text-decoration: underline; }
.comments textarea { width: 100%; padding: 0.3em; margin-bottom: 0.6em; }
.newCommentArea { margin-top: 0.5em; }
.clearance { clear: both; }
.expander { cursor: pointer; }
!(end of StyleSheet)
***/
config.macros.comments.init();
} // end of 'install only once'
/*}}}*/
// function log() { if (console && console.firebug) console.log.apply(console, arguments); }
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Documentation|http://www.TiddlyTools.com/#TaggedTemplateTweakInfo|
|Version|1.5.1|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.chooseTemplateForTiddler()|
|Description|use alternative ViewTemplate/EditTemplate for tiddler's tagged with specific tag values|
This tweak extends story.chooseTemplateForTiddler() so that ''whenever a tiddler is marked with a specific tag value, it can be viewed and/or edited using alternatives to the standard tiddler templates.''
!!!!!Documentation
>see [[TaggedTemplateTweakInfo]]
!!!!!Revisions
<<<
2009.01.06 [1.5.1] reversed logic so that title-as-prefix takes precedence over tag-matched prefix
2008.12.18 [1.5.0] added handling for using tiddler //title// as prefix (e.g., {{{SomeTiddlerViewTemplate}}})
| please see [[TaggedTemplateTweakInfo]] for previous revision details |
2007.06.11 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TaggedTemplateTweak= {major: 1, minor: 5, revision: 1, date: new Date(2009,1,6)};
Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
// get default template from core
var coreTemplate=this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);
// if the tiddler doesn't exist yet, return core result
var tiddler=store.getTiddler(title); if (!tiddler) return coreTemplate;
// split core template into theme prefix and template name
var theme="";
var template=coreTemplate;
var parts=template.split(config.textPrimitives.sectionSeparator);
if (parts[1]) { theme=parts[0]; template=parts[1]; }
else theme=config.options.txtTheme||""; // fallback if theme is not specified
theme+=config.textPrimitives.sectionSeparator;
// look for template whose prefix matches the *title* of this tiddler
if (!store.getTaggedTiddlers(title).length) { // if tiddler is not a tag
if (store.getTiddlerText(theme+title+template)) { return theme+title+template; } // theme##TitleTemplate
if (store.getTiddlerText(title+template)) { return title+template; } // TitleTemplate
}
// look for template whose prefix matches a *tag* on this tiddler (if any)
for (i=0; i<tiddler.tags.length; i++) {
var t=tiddler.tags[i]+template; // add tag prefix to template
var c=t.substr(0,1).toUpperCase()+t.substr(1); // capitalized for WikiWord title
if (store.getTiddlerText(theme+t)) { return theme+t; } // theme##tagTemplate
if (store.getTiddlerText(theme+c)) { return theme+c; } // theme##TagTemplate
if (store.getTiddlerText(t)) { return t; } // tagTemplate
if (store.getTiddlerText(c)) { return c; } // TagTemplate
}
// no matching tag OR title prefix... return core result
return coreTemplate;
}
//}}}
/***
|Name|SinglePageModePlugin|
|Source|http://www.TiddlyTools.com/#SinglePageModePlugin|
|Documentation|http://www.TiddlyTools.com/#SinglePageModePluginInfo|
|Version|2.9.5|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides|Story.prototype.displayTiddler(), Story.prototype.displayTiddlers()|
|Options|##Configuration|
|Description|Show tiddlers one at a time with automatic permalink, or always open tiddlers at top/bottom of page.|
This plugin allows you to configure TiddlyWiki to navigate more like a traditional multipage web site with only one tiddler displayed at a time.
!!!!!Documentation
>see [[SinglePageModePluginInfo]]
!!!!!Configuration
<<<
<<option chkSinglePageMode>> Display one tiddler at a time
><<option chkSinglePagePermalink>> Automatically permalink current tiddler
><<option chkSinglePageKeepFoldedTiddlers>> Don't close tiddlers that are folded
><<option chkSinglePageKeepEditedTiddlers>> Don't close tiddlers that are being edited
<<option chkTopOfPageMode>> Open tiddlers at the top of the page
<<option chkBottomOfPageMode>> Open tiddlers at the bottom of the page
<<option chkSinglePageAutoScroll>> Automatically scroll tiddler into view (if needed)
Notes:
* The "display one tiddler at a time" option can also be //temporarily// set/reset by including a 'paramifier' in the document URL: {{{#SPM:true}}} or {{{#SPM:false}}}.
* If more than one display mode is selected, 'one at a time' display takes precedence over both 'top' and 'bottom' settings, and if 'one at a time' setting is not used, 'top of page' takes precedence over 'bottom of page'.
* When using Apple's Safari browser, automatically setting the permalink causes an error and is disabled.
<<<
!!!!!Revisions
<<<
2008.06.12 [2.9.5] corrected 'scroll to top of page' logic in auto-scroll handling
| Please see [[SinglePageModePluginInfo]] for previous revision details |
2005.08.15 [1.0.0] Initial Release. Support for BACK/FORWARD buttons adapted from code developed by Clint Checketts.
<<<
!!!!!Code
***/
//{{{
version.extensions.SinglePageModePlugin= {major: 2, minor: 9, revision: 5, date: new Date(2008,6,12)};
//}}}
//{{{
config.paramifiers.SPM = { onstart: function(v) {
config.options.chkSinglePageMode=eval(v);
if (config.options.chkSinglePageMode && config.options.chkSinglePagePermalink && !config.browser.isSafari) {
config.lastURL = window.location.hash;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
} };
//}}}
//{{{
if (config.options.chkSinglePageMode==undefined)
config.options.chkSinglePageMode=false;
if (config.options.chkSinglePagePermalink==undefined)
config.options.chkSinglePagePermalink=true;
if (config.options.chkSinglePageKeepFoldedTiddlers==undefined)
config.options.chkSinglePageKeepFoldedTiddlers=false;
if (config.options.chkSinglePageKeepEditedTiddlers==undefined)
config.options.chkSinglePageKeepEditedTiddlers=false;
if (config.options.chkTopOfPageMode==undefined)
config.options.chkTopOfPageMode=false;
if (config.options.chkBottomOfPageMode==undefined)
config.options.chkBottomOfPageMode=false;
if (config.options.chkSinglePageAutoScroll==undefined)
config.options.chkSinglePageAutoScroll=true;
//}}}
//{{{
config.SPMTimer = 0;
config.lastURL = window.location.hash;
function checkLastURL()
{
if (!config.options.chkSinglePageMode)
{ window.clearInterval(config.SPMTimer); config.SPMTimer=0; return; }
if (config.lastURL == window.location.hash) return; // no change in hash
var tids=decodeURIComponent(window.location.hash.substr(1)).readBracketedList();
if (tids.length==1) // permalink (single tiddler in URL)
story.displayTiddler(null,tids[0]);
else { // restore permaview or default view
config.lastURL = window.location.hash;
if (!tids.length) tids=store.getTiddlerText("DefaultTiddlers").readBracketedList();
story.closeAllTiddlers();
story.displayTiddlers(null,tids);
}
}
if (Story.prototype.SPM_coreDisplayTiddler==undefined)
Story.prototype.SPM_coreDisplayTiddler=Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,slowly)
{
var title=(tiddler instanceof Tiddler)?tiddler.title:tiddler;
var tiddlerElem=document.getElementById(story.idPrefix+title); // ==null unless tiddler is already displayed
var opt=config.options;
var single=opt.chkSinglePageMode && !startingUp;
var top=opt.chkTopOfPageMode && !startingUp;
var bottom=opt.chkBottomOfPageMode && !startingUp;
if (single) {
story.forEachTiddler(function(tid,elem) {
// skip current tiddler and, optionally, tiddlers that are folded.
if ( tid==title
|| (opt.chkSinglePageKeepFoldedTiddlers && elem.getAttribute("folded")=="true"))
return;
// if a tiddler is being edited, ask before closing
if (elem.getAttribute("dirty")=="true") {
if (opt.chkSinglePageKeepEditedTiddlers) return;
// if tiddler to be displayed is already shown, then leave active tiddler editor as is
// (occurs when switching between view and edit modes)
if (tiddlerElem) return;
// otherwise, ask for permission
var msg="'"+tid+"' is currently being edited.\n\n";
msg+="Press OK to save and close this tiddler\nor press Cancel to leave it opened";
if (!confirm(msg)) return; else story.saveTiddler(tid);
}
story.closeTiddler(tid);
});
}
else if (top)
arguments[0]=null;
else if (bottom)
arguments[0]="bottom";
if (single && opt.chkSinglePagePermalink && !config.browser.isSafari) {
window.location.hash = encodeURIComponent(String.encodeTiddlyLink(title));
config.lastURL = window.location.hash;
document.title = wikifyPlain("SiteTitle") + " - " + title;
if (!config.SPMTimer) config.SPMTimer=window.setInterval(function() {checkLastURL();},1000);
}
if (tiddlerElem && tiddlerElem.getAttribute("dirty")=="true") { // editing... move tiddler without re-rendering
var isTopTiddler=(tiddlerElem.previousSibling==null);
if (!isTopTiddler && (single || top))
tiddlerElem.parentNode.insertBefore(tiddlerElem,tiddlerElem.parentNode.firstChild);
else if (bottom)
tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
else this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
} else
this.SPM_coreDisplayTiddler.apply(this,arguments); // let CORE render tiddler
var tiddlerElem=document.getElementById(story.idPrefix+title);
if (tiddlerElem&&opt.chkSinglePageAutoScroll) {
// scroll to top of page or top of tiddler
var isTopTiddler=(tiddlerElem.previousSibling==null);
var yPos=isTopTiddler?0:ensureVisible(tiddlerElem);
// if animating, defer scroll until 200ms after animation completes
var delay=opt.chkAnimate?config.animDuration+200:0;
setTimeout("window.scrollTo(0,"+yPos+")",delay);
}
}
if (Story.prototype.SPM_coreDisplayTiddlers==undefined)
Story.prototype.SPM_coreDisplayTiddlers=Story.prototype.displayTiddlers;
Story.prototype.displayTiddlers = function() {
// suspend single/top/bottom modes when showing multiple tiddlers
var opt=config.options;
var saveSPM=opt.chkSinglePageMode; opt.chkSinglePageMode=false;
var saveTPM=opt.chkTopOfPageMode; opt.chkTopOfPageMode=false;
var saveBPM=opt.chkBottomOfPageMode; opt.chkBottomOfPageMode=false;
this.SPM_coreDisplayTiddlers.apply(this,arguments);
opt.chkBottomOfPageMode=saveBPM;
opt.chkTopOfPageMode=saveTPM;
opt.chkSinglePageMode=saveSPM;
}
//}}}
!Description
A list of tech news sites for you.
!Bookmarks
[[Slashdot]]
[[Digg]]
[[Reddit]]
[[Hacker News]]
url: http://Slashdot.org
!Description
A Geek's Geek Site
url: http://reddit.com
!Description
Vote up or down.
url: http://news.ycombinator.com
!Description
A list of fun news sites for you.
!Bookmarks
[[YouTube]] Lots of videos.
[[YTMND]] Fun single-serving videos :D
url: http://YouTube.com
!Description
Videos for all!
url: http://ytmnd.com
name: You're The Man Now Dog!