20120312
The basic idea is that the downloaded TiddlyWiki should be "lite", meaning not having extraneous stuff in it.
20120312
zzzeek : Pycon 2012 : Hand Coded Applications with SQLAlchemy
Here's the slides from my Pycon 2012 talk, "Hand Coded Applications with SQLAlchemy".zzzeek : Pycon 2012 : Hand Coded Applications with SQLAlchemy
20120311
twp.mysql2 to twp.mysql3 transition today, but without complete success. On my main dev machine, I've upgraded to mysql 5.5. I did that to ensure that the new code solves the problems of working with 5.5. However, on 5.5 the old code doesn't work so I can't do a test migration on that machine. I have a few others to choose from.mysql3 branch of the TiddlySpace code should be merged to master.Syndicated 2012-03-11 15:33:46 (Updated 2012-03-14 21:40:57) from cdent
20120309
render and fat on JSON tiddler and tiddlers are now mutually independent. This now makes it possible to get JSON that has a render field but doesn't have a text field.Syndicated 2012-03-09 17:26:08 (Updated 2012-03-09 18:19:55) from cdent
Scheduled Maintenance 14 March, 2012
Syndicated 2012-03-09 17:02:55 (Updated 2012-03-09 17:02:05) from cdent
20120308
setInterval refresh. It turns out that my pages are viewed quite a lot and if the page is left open it bounces on the search that updates the sidebar. Since one of my goals is to minimize traffic while maximizing use, switching to websockets is more efficient. However the fact that my own pages have so much JavaScript making them go makes me feel dirty.mysql3) of the TiddlySpace code that uses twp.mysql3 and used it in a dev env. Seems to work. Need to determine when the best time for a migration might be.Syndicated 2012-03-08 14:23:44 (Updated 2012-03-08 16:40:22) from cdent
edit.js
$(function() {
var Set = function() {}
Set.prototype.add = function(o) { this[o] = true; }
Set.prototype.remove = function(o) { delete this[o]; }
var adler32 = function(a){for(var b=65521,c=1,d=0,e=0,f;f=a.charCodeAt(e++);d=(d+c)%b)c=(c+f)%b;return(d h1').text());
});
$('#save').bind('click', function() {
saveEdit();
});
$('#saver').bind('click', function() {
saveEdit(function() {
var title = encodeURIComponent($('#editor > h1').text());
startHash = adler32($('input[name=tags]').val()
+ $('textarea[name=text]').val());
window.location.href = '/' + title;
});
});
$('#delete').bind('click', function() {
var title = encodeURIComponent($('#editor > h1').text());
if (currentBag) {
var confirmation = confirm('Are you sure you want to delete ' + title + '?');
if (confirmation) {
$('input[name=tags]').val('');
$('textarea[name=text]').val('');
$('#editor > h1').text('');
startHash = adler32('');
deleteTiddler(title);
}
} else {
displayMessage('Tiddler never saved to server.');
}
});
/*
* Fade in an announcement text message.
*/
function displayMessage(message, extra) {
var content = $('').text(message);
$('#message').empty();
$('#message').append(content)
if (extra) {
$('#message').append(extra);
}
$('#message').fadeIn();
}
/*
* Display an icon indicating privacy status of tiddler.
*/
function setIcon(privatep) {
$('.privacyicon').remove();
var img = $('
').attr({
src: host + (privatep ? privateIcon : publicIcon),
'class': 'privacyicon'});
if (!currentBag) {
img.css('cursor', 'pointer')
.click(function() {
var target = privatep ? 'public' : 'private';
if (confirm('Switch to '
+ (privatep ? 'public' : 'private') + '?')) {
currentBag = space + '_' + target;
setIcon(!privatep);
}
});
}
$('#type').prepend(img);
}
/*
* Send a DELETE for the tiddler named by title.
*/
function deleteTiddler(title) {
if (title && currentBag) {
$(window).unbind('hashchange');
window.location.hash = '';
$(window).bind('hashchange', checkHash);
var uri = host + 'bags/'
+ encodeURIComponent(currentBag)
+ '/tiddlers/'
+ encodeURIComponent(title);
$.ajax({
url: uri,
type: 'DELETE',
success: changes
});
} else {
displayMessage('Nothing to delete.');
}
}
/*
* Inform a non-member that they may not edit.
*/
function guestPage() {
$('button, input, .inputs').attr('disabled', 'disabled');
$('#message').text('You are not a member of this space, so cannot edit. ');
var link = $('')
.attr('href', host)
.text('Visit the space.');
$('#message').append(link).fadeIn();
}
/*
* Save the text and tags to the title in currentBag.
*/
function saveEdit(callback) {
callback = callback || changes;
var title = $('#editor > h1').text();
if (title) {
var text = $('textarea[name=text]').val()
_processText(title, text, callback);
} else {
displayMessage('There is nothing to save');
}
}
/*
* Search for '.extraclude' in page and do an
* extraclusion if found. Multiples possible.
*/
function _processText(title, text, callback) {
var newTiddlers = {}
, match;
while (match = extracludeRE.exec(text)) {
var subtitle = match[1]
, subtext = match[2].replace(/^\s*/, '').replace(/\s*$/, '')
, tiddler = {
text: subtext,
type: currentFields.type
};
newTiddlers[subtitle] = tiddler;
}
var countTiddlers = Object.keys(newTiddlers).length;
var countSuccess = 0;
var postExtra = function(data, status, xhr) {
countSuccess++;
if (countSuccess >= countTiddlers) {
text = text.replace(extracludeRE, '>');
_saveEdit(title, text, callback)
}
};
var postExtraFail = function(xhr, status, errorThrown) {
displayMessage('Extraclude failed' + status);
}
if (countTiddlers) {
var subtitle;
for (subtitle in newTiddlers) {
_putTiddler(subtitle, newTiddlers[subtitle], postExtra,
postExtraFail)
}
} else {
_saveEdit(title, text, callback)
}
}
/*
* PUT a tiddler that was extracluded.
*/
function _putTiddler(title, tiddlerData, successCall, errorCall) {
var jsonText = JSON.stringify(tiddlerData);
if (!currentBag) {
currentBag = space + '_public';
}
$.ajax({
url: host + 'bags/' + encodeURIComponent(currentBag)
+ '/tiddlers/' + encodeURIComponent(title),
type: 'PUT',
data: jsonText,
contentType: 'application/json',
success: successCall,
error: errorCall
});
}
function _saveEdit(title, text, callback) {
var tags = readTagView()
, tiddler = {};
tiddler.text = text;
tiddler.tags = tags;
tiddler.type = currentFields.type;
delete currentFields.type;
tiddler.fields = currentFields;
// update content based on radio buttons
var matchedType = $('[name=type]:checked').val();
if (matchedType !== 'other') {
if (matchedType === 'default') {
delete tiddler.type;
} else {
tiddler.type = matchedType;
}
}
var jsonText = JSON.stringify(tiddler);
if (!currentBag) {
currentBag = space + '_public';
}
$.ajax({
beforeSend: function(xhr) {
if (tiddler.fields['server.etag']) {
xhr.setRequestHeader('If-Match',
tiddler.fields['server.etag']);
}
},
url: host + 'bags/' + encodeURIComponent(currentBag)
+ '/tiddlers/' + encodeURIComponent(title),
type: "PUT",
contentType: 'application/json',
data: jsonText,
success: callback,
statusCode: {
412: function() {
displayMessage('Edit Conflict');
}
}
});
}
/*
* Read the current tags from the input into an array.
*/
function readTagView(tagString) {
var tags = [];
tagString = tagString || $('input[name=tags]').val();
var matches = tagString.match(/([^ \]\[]+)|(?:\[\[([^\]]+)\]\])/g) || [];
$.each(matches, function(index, value) {
tags.push(value.replace(/[\]\[]+/g, ''));
});
return tags;
}
/*
* Write updated tags into the tag view. If a non-false second
* argument is passed, it is assumed to be a tag that is being
* added or removed.
*/
function updateTagView(tags, changedTag) {
var outputTags = [];
if (changedTag) {
var tagIndex = tags.indexOf(changedTag);
if (tagIndex == -1) {
tags.push(changedTag);
} else {
tags.splice(tags.indexOf(changedTag), 1);
}
}
$.each(tags, function(index, tag) {
if (tag.match(/ /)) {
outputTags.push('[[' + tag + ']]');
} else {
outputTags.push(tag);
}
});
$('#editor input').val(outputTags.join(' '))
}
/*
* Display the most recently used tags.
*/
function updateTags(tags) {
$('#tags').empty();
tags = Object.keys(tags);
tags = tags.sort();
$.each(tags, function(index, tag) {
var taglink = $('')
.text(tag)
.addClass('taglink')
.bind('click', function() {
updateTagView(readTagView(), tag);
});
$('#tags').append(taglink);
});
}
function updateContentType(tiddlerType) {
$('[name=type]').prop('checked', false);
var matchedType = $('[name=type]')
.filter('[value="' + tiddlerType + '"]');
if (matchedType.length) {
matchedType.prop('checked', true)
} else if (tiddlerType) {
$('[name=type]').filter('[value=other]').prop('checked', true);
} else {
$('[name=type]').filter('[value="default"]').prop('checked', true);
}
}
/*
* Callback after tiddler is GET from server, filling in forms,
* preparing for edit.
*/
function establishEdit(tiddler, status, xhr) {
currentBag = tiddler.bag;
$('textarea[name=text]').val(tiddler.text);
var tagList = [];
currentFields = tiddler.fields;
currentFields['type'] = tiddler.type
// update the content type buttons
updateContentType(tiddler.type);
currentFields['server.etag'] = xhr.getResponseHeader('etag');
updateTagView(tiddler.tags, null);
if (currentBag.split(/_/)[0] !== space) {
$('button, input, .inputs').attr('disabled', 'disabled');
displayMessage('Edit permission denied. Choose another tiddler.');
return;
}
startHash = adler32($('input[name=tags]').val()
+ $('textarea[name=text]').val());
if (currentBag.match(/_(private|public)$/)) {
setIcon(currentBag.match(/_private$/));
}
}
/*
* Get the named tiddler to do an edit.
*/
function startEdit(tiddlerTitle, freshTags, freshType) {
$('#message').fadeOut('slow');
$('button, input, .inputs').removeAttr('disabled');
$('#editor > h1').text(tiddlerTitle);
$.ajax({
dataType: 'json',
headers: {'Cache-Control': 'max-age=0'},
url: host + encodeURIComponent(tiddlerTitle),
success: establishEdit,
statusCode: {
404: function() {
$('[name=type]')
.filter('[value="default"]')
.prop('checked', true);
$('textarea[name=text]').val('');
setIcon(false);
updateContentType(freshType);
updateTagView(readTagView(freshTags), null);
}
}
});
}
function emptyEdit() {
$('button, input, .inputs').attr('disabled', 'disabled');
var titler = $('')
.attr('placeholder', 'Or enter a new title')
.bind('blur change', editNew);
displayMessage('Select a tiddler to edit from the right.', titler);
}
function editNew() {
var newTitle = $(this).val();
if (newTitle) {
startEdit(newTitle);
}
}
/*
* Check the href anchor to see if we've been told what to edit.
*/
function checkHash() {
var hash = window.location.hash;
if (hash) {
hash = hash.replace(/^#/, '');
var title, tagString, type, args;
args = hash.split('/');
if (args.length == 4) {
args[2] = args.slice(2).join('/');
args.pop();
}
$.each(args, function(index, arg) {
args[index] = decodeURIComponent(arg);
});
title = args[0] || emptyEdit();
tagString = args[1] || '';
type = args[2] || '';
startEdit(title, tagString, type);
} else {
emptyEdit();
}
}
/*
* Display the recent changes.
*/
function displayChanges(tiddlers) {
$.each(tiddlers, function(index, tiddler) {
if (!tiddler.type
|| tiddler.type.match(/^text/)) {
$.each(tiddler.tags, function(index, tag) {
recentTags.add(tag);
})
var penSpan = $('').text('\u270E')
.bind('click', function() {
var title = $(this).parent().attr('data-tiddler-title');
$(window).unbind('hashchange');
window.location.hash = title;
$(window).bind('hashchange', checkHash);
startEdit(title);
});
var tiddlerLink = $('').attr({
href: '/' + encodeURIComponent(tiddler.title),
target: '_blank'})
.text(tiddler.title)
var list = $('').attr('data-tiddler-title',
tiddler.title).append(tiddlerLink).prepend(penSpan);
$('#recents > ul').append(list);
}
});
updateTags(recentTags);
}
/*
* Get the 20 most recently changed tiddlers in the public and private
* bag of the space, callback to displayChanges.
*/
function changes() {
$('#recents > ul').empty();
$.ajax({
dataType: 'json',
headers: {'Cache-Control': 'max-age=0'},
url: host + 'search?q=bag:' + encodeURIComponent(space)
+ '_public%20OR%20bag:' + encodeURIComponent(space)
+ '_private',
success: displayChanges
});
checkHash();
}
/*
* Start up, establishing if the current user has the power to edit.
*/
function init() {
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader("X-ControlView", "false");
}
});
var recipe = tiddlyweb.status.space.recipe;
if (recipe.match(/_private$/)) {
changes();
}else {
guestPage();
}
}
init();
});
Syndicated 2012-02-13 22:19:22 (Updated 2012-07-05 20:37:29) from cdent
edit.js
$(function() {
var Set = function() {}
Set.prototype.add = function(o) { this[o] = true; }
Set.prototype.remove = function(o) { delete this[o]; }
var adler32 = function(a){for(var b=65521,c=1,d=0,e=0,f;f=a.charCodeAt(e++);d=(d+c)%b)c=(c+f)%b;return(d h1').text());
});
$('#save').bind('click', function() {
saveEdit();
});
$('#saver').bind('click', function() {
saveEdit(function() {
var title = encodeURIComponent($('#editor > h1').text());
startHash = adler32($('input[name=tags]').val()
+ $('textarea[name=text]').val());
window.location.href = '/' + title;
});
});
$('#delete').bind('click', function() {
var title = decodeURIComponent(window.location.hash.replace(/^#/, ''));
if (currentBag) {
var confirmation = confirm('Are you sure you want to delete ' + title + '?');
if (confirmation) {
$('input[name=tags]').val('');
$('textarea[name=text]').val('');
$('#editor > h1').text('');
startHash = adler32('');
deleteTiddler(title);
}
} else {
displayMessage('Tiddler never saved to server.');
}
});
/*
* Fade in an announcement text message.
*/
function displayMessage(message) {
$('#message').text(message).fadeIn();
}
/*
* Display an icon indicating privacy status of tiddler.
*/
function setIcon(privatep) {
$('.privacyicon').remove();
var img = $('
').attr({
src: host + (privatep ? privateIcon : publicIcon),
'class': 'privacyicon'});
if (!currentBag) {
img.css('cursor', 'pointer')
.click(function() {
var target = privatep ? 'public' : 'private';
if (confirm('Switch to '
+ (privatep ? 'public' : 'private') + '?')) {
currentBag = space + '_' + target;
setIcon(!privatep);
}
});
}
$('#type').prepend(img);
}
/*
* Send a DELETE for the tiddler named by title.
*/
function deleteTiddler(title) {
if (title && currentBag) {
window.location.hash = '';
var uri = host + 'bags/'
+ encodeURIComponent(currentBag)
+ '/tiddlers/'
+ encodeURIComponent(title);
$.ajax({
url: uri,
type: 'DELETE',
success: changes
});
} else {
displayMessage('Nothing to delete.');
}
}
/*
* Inform a non-member that they may not edit.
*/
function guestPage() {
$('button, input, .inputs').attr('disabled', 'disabled');
$('#message').text('You are not a member of this space, so cannot edit. ');
var link = $('')
.attr('href', host)
.text('Visit the space.');
$('#message').append(link).fadeIn();
}
/*
* Save the text and tags to the title in currentBag.
*/
function saveEdit(callback) {
callback = callback || changes;
var title = $('#editor > h1').text();
if (title) {
var text = $('textarea[name=text]').val()
, tags = readTagView()
, tiddler = {};
tiddler.text = text;
tiddler.tags = tags;
tiddler.type = currentFields.type;
delete currentFields.type;
tiddler.fields = currentFields;
// update content based on radio buttons
var matchedType = $('[name=type]:checked').val();
if (matchedType !== 'other') {
if (matchedType === 'default') {
delete tiddler.type;
} else {
tiddler.type = matchedType;
}
}
var jsonText = JSON.stringify(tiddler);
if (!currentBag) {
currentBag = space + '_public';
}
$.ajax({
beforeSend: function(xhr) {
if (tiddler.fields['server.etag']) {
xhr.setRequestHeader('If-Match',
tiddler.fields['server.etag']);
}
},
url: host + 'bags/' + encodeURIComponent(currentBag)
+ '/tiddlers/' + encodeURIComponent(title),
type: "PUT",
contentType: 'application/json',
data: jsonText,
success: callback,
statusCode: {
412: function() {
displayMessage('Edit Conflict');
}
}
});
} else {
displayMessage('There is nothing to save');
}
}
/*
* Read the current tags from the input into an array.
*/
function readTagView(tagString) {
var tags = [];
tagString = tagString || $('input[name=tags]').val();
var matches = tagString.match(/([^ \]\[]+)|(?:\[\[([^\]]+)\]\])/g) || [];
$.each(matches, function(index, value) {
tags.push(value.replace(/[\]\[]+/g, ''));
});
return tags;
}
/*
* Write updated tags into the tag view. If a non-false second
* argument is passed, it is assumed to be a tag that is being
* added or removed.
*/
function updateTagView(tags, changedTag) {
var outputTags = [];
if (changedTag) {
var tagIndex = tags.indexOf(changedTag);
if (tagIndex == -1) {
tags.push(changedTag);
} else {
tags.splice(tags.indexOf(changedTag), 1);
}
}
$.each(tags, function(index, tag) {
if (tag.match(/ /)) {
outputTags.push('[[' + tag + ']]');
} else {
outputTags.push(tag);
}
});
$('#editor input').val(outputTags.join(' '))
}
/*
* Display the most recently used tags.
*/
function updateTags(tags) {
$('#tags').empty();
tags = Object.keys(tags);
tags = tags.sort();
$.each(tags, function(index, tag) {
var taglink = $('')
.text(tag)
.addClass('taglink')
.bind('click', function() {
updateTagView(readTagView(), tag);
});
$('#tags').append(taglink);
});
}
function updateContentType(tiddlerType) {
$('[name=type]').prop('checked', false);
var matchedType = $('[name=type]')
.filter('[value="' + tiddlerType + '"]');
if (matchedType.length) {
matchedType.prop('checked', true)
} else if (tiddlerType) {
$('[name=type]').filter('[value=other]').prop('checked', true);
} else {
$('[name=type]').filter('[value="default"]').prop('checked', true);
}
}
/*
* Callback after tiddler is GET from server, filling in forms,
* preparing for edit.
*/
function establishEdit(tiddler, status, xhr) {
currentBag = tiddler.bag;
$('textarea[name=text]').val(tiddler.text);
var tagList = [];
currentFields = tiddler.fields;
currentFields['type'] = tiddler.type
// update the content type buttons
updateContentType(tiddler.type);
currentFields['server.etag'] = xhr.getResponseHeader('etag');
updateTagView(tiddler.tags, null);
startHash = adler32($('input[name=tags]').val()
+ $('textarea[name=text]').val());
if (currentBag.match(/_(private|public)$/)) {
setIcon(currentBag.match(/_private$/));
}
}
/*
* Get the named tiddler to do an edit.
*/
function startEdit(tiddlerTitle, freshTags, freshType) {
$('#message').fadeOut('slow');
$('button, input, .inputs').removeAttr('disabled');
window.location.hash = tiddlerTitle;
$('#editor > h1').text(tiddlerTitle);
$.ajax({
dataType: 'json',
headers: {'Cache-Control': 'max-age=0'},
url: host + encodeURIComponent(tiddlerTitle),
success: establishEdit,
statusCode: {
404: function() {
$('[name=type]')
.filter('[value="default"]')
.prop('checked', true);
setIcon(false);
updateContentType(freshType);
updateTagView(readTagView(freshTags), null);
}
}
});
}
function emptyEdit() {
$('button, input, .inputs').attr('disabled', 'disabled');
displayMessage('Select a tiddler to edit');
}
/*
* Check the href anchor to see if we've been told what to edit.
*/
function checkHash() {
var hash = window.location.href.split('#')[1] || '';
if (hash) {
var title, tagString, type, args;
args = hash.split('/', 3);
$.each(args, function(index, arg) {
args[index] = decodeURIComponent(arg);
});
title = args[0] || emptyEdit();
tagString = args[1] || '';
type = args[2] || '';
startEdit(title, tagString, type);
} else {
emptyEdit();
}
}
/*
* Display the recent changes.
*/
function displayChanges(tiddlers) {
$.each(tiddlers, function(index, tiddler) {
if (!tiddler.type
|| tiddler.type.match(/^text/)) {
$.each(tiddler.tags, function(index, tag) {
recentTags.add(tag);
})
var penSpan = $('').text('\u270E')
.bind('click', function() {
startEdit($(this).parent().attr('data-tiddler-title'));
});
var tiddlerLink = $('').attr({
href: '/' + encodeURIComponent(tiddler.title),
target: '_blank'})
.text(tiddler.title)
var list = $('').attr('data-tiddler-title',
tiddler.title).append(tiddlerLink).prepend(penSpan);
$('#recents > ul').append(list);
}
});
updateTags(recentTags);
}
/*
* Get the 20 most recently changed tiddlers in the public and private
* bag of the space, callback to displayChanges.
*/
function changes() {
$('#recents > ul').empty();
$.ajax({
dataType: 'json',
headers: {'Cache-Control': 'max-age=0'},
url: host + 'search?q=bag:' + encodeURIComponent(space)
+ '_public%20OR%20bag:' + encodeURIComponent(space)
+ '_private',
success: displayChanges
});
checkHash();
}
/*
* Start up, establishing if the current user has the power to edit.
*/
function init() {
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader("X-ControlView", "false");
}
});
var url = '/status'
, genHost = false;
if (window.location.href.match(/^file:/)) {
// for dev
url = 'http://cdent.tiddlyspace.com/status';
genHost = true;
}
$.ajax({
dataType: 'json',
url: url,
success: function(data) {
space = data.space.name;
host = '/';
if (genHost) {
host = data.server_host.scheme + '://'
+ space + '.' + data.server_host.host + '/';
}
if (data.username === 'GUEST') {
guestPage();
} else {
$.ajax({
url: host + 'spaces/' + space + '/members',
success: changes,
error: guestPage,
});
}
}
});
}
init();
});
Syndicated 2012-02-13 22:19:22 (Updated 2012-03-08 14:35:16) from cdent
plate
tiddlyweb.log)40x.Syndicated 2011-01-25 14:13:10 (Updated 2012-05-07 16:55:23) from cdent
New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.
Keep up with the latest Advogato features by reading the Advogato status blog.
If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!