13 Mar 2012 cdent   » (Master)

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

    Latest blog entries     Older blog entries

    New Advogato Features

    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!