/* appjet:version 0.1 */

/*
This code is GPL, thanks. Please clone it, but keep that in mind.
For license text and more information:
http://www.gnu.org/copyleft/gpl.html
*/

import("storage");
import("quickforms");

import("lib-user");

if (!storage.showdownlib)
{
    storage.showdownlib = wget('http://www.gibberish.com/hacks/appjet/showdown.js');
}
eval(storage.showdownlib);

if (!storage.dateformatlib)
{
    storage.dateformatlib = wget('http://stevenlevithan.com/assets/misc/date.format.js');
}
eval(storage.dateformatlib);

if (!storage.pages)
{
     storage.pages = new StorableCollection();
}

var wikiname = 'WikiJet'; 
// change that to something more appropriate if you clone pls
// also, lib-user won't use it, so don't come crying to me

// ========================
// Basic model manip
// ========================
var get_page = function(pagename, revnumber) 
{
    if (pagename == 'pagelist') return true;
    var thispage = storage.pages.filter({name: pagename});
    
    var onepage = thispage.filter({rev_number: (revnumber==null ? thispage.size() : revnumber)});
    return onepage.next();
};

var set_page = function(name, value) 
{
    var howmany = storage.pages.size();
    var lastone = get_page(name);
    var revnum = lastone ? lastone.rev_number + 1 : 1;
    
    var newpage = new StorableObject();
    newpage.name = name;
    newpage.text = value;
    newpage.date = new Date();
    newpage.user = user ? user.id : null;
    newpage.rev_number = revnum;
    storage.pages.add(newpage);
    
    return newpage;
};

// ========================
// Helpers
// ========================

var strip_tags = function(html)
{
     return html.replace(/<\/?[^>]+>/gi, '');
};

var do_template = function(vars_obj,tmp_str,printit)
{
     var sub_each = function(replacee, propname)
     {
         return vars_obj[propname];
     }
     if (printit != null)
         print(raw(tmp_str.replace(/#\{([^\}]*)\}/g, sub_each)));
     else
         return tmp_str.replace(/#\{([^\}]*)\}/g, sub_each);
};
     
var normalize_name = function(name)
{
     var norm = name.replace(/^[\s]+/, '')
         .replace(/[\s]+$/, '')
         .replace(/[\s]+/g, '_')
         .replace(/\W/g, '')
         .split('_')
    ;
    for (var i=0; i < norm.length; i++)
    {
        norm[i] = norm[i].substr(0,1).toUpperCase() + norm[i].substr(1);
    }
    return norm.join('_');
};

var wikify = function(pagesrc)
{
     pagesrc = strip_tags(pagesrc);
     
     converter = new Showdown.converter();
     pagesrc = converter.makeHtml(pagesrc);
     
     var write_link = function(replacee, linktxt)
     {
         var linkee = normalize_name(linktxt);
         if (get_page(linkee))
         {
            return '<a href="/' + linkee + '">' + linktxt + '</a>';
         }
         else
         {
             return '<a href="/_edit/' + linkee + '">' + linktxt + '</a>';
         }
     };
     return pagesrc.replace(/\[\[(.*?)\]\]/g, write_link);
};

var render = function(title, filling, ctrls, header)
{
    if (header != null)
        page.head.write(header);
    
    print(H1(wikiname + "" + title));
    if (user != null) print(H4('Logged in as ' + user.uname));
     
    print(DIV({id: 'body'}, raw(filling)));
     
    print(raw('<p>'));
     
    ctrls.unshift(link('/', 'Home'));
    if (!user)
    {
        ctrls.push(link('/lib-user/login', 'Login'));
        ctrls.push(link('/lib-user/register', 'Register'));
    }
    else
    {
        ctrls.push(link('/lib-user/logout', 'Logout'));
    }
     
    print(DIV({id: 'ctrls'}, raw(ctrls.join(' - '))));
    // assumes that entries in ctrls are already htmlstrings
    /*
    print(link("/_edit/" + name, "Edit page"));
    print(link("/_history/" + name, "Page history"));
    */
};

var page_url = function(pagename)
{
    return "http://" + 
        (appjet.mainDomain == 'appjet.net' ? (appjet.appName + ".") : '') +
        appjet.mainDomain + "/" + 
        normalize_name(pagename);
};

// ========================
// URL actions
// ========================

var pagelist = function() 
{
    var pagecode = '<ul>';
    
    var obj = null;
    var pagestubs = storage.pages.filter({rev_number: 1});
    for (var ct = 0; ct < pagestubs.size(); ct++)
    {
        obj = pagestubs.next();
        pagecode += '<li>' +
            '<a href="/' + normalize_name(obj.name) + '">' + 
            obj.name.replace(/_/g, ' ') + 
            '</a></li>'
        ;
    }
    //print(raw());
    pagecode += "</ul>\n";
    
    render('Page List', pagecode, [link('/', 'Home page')]);
};

var recent = function()
{
    var pagecode = '<table><thead><tr><th>Page</th><th>Revised at</th><th>Revision</th></tr></thead><tbody>';
    
    var obj = null;
    var revs = storage.pages.sort(function(a,b){return a.date < b.date;});
    for (var ct = 0; ct < 25; ct++)
    {
        obj = revs.next();
        if (typeof obj == 'undefined') break;
        pagecode +=
            '<tr><td><a href="/' + normalize_name(obj.name) + '">' + 
            obj.name.replace(/_/g, ' ') + 
            '</a></td><td><small>' + obj.date + "</small></td>" +
            '<td><a href="/' + normalize_name(obj.name) + '/rev/' + obj.rev_number + '">' +
            obj.rev_number + '</a></td></tr>'
        ;
    }
    pagecode += "</tbody></table>\n";
    
    render('Recent Changes', pagecode, [link('/', 'Home page')], 
        '<link rel="alternate" type="application/rss+xml" title="' + wikiname + ' RSS Feed" href="http://' + 
        (appjet.mainDomain == 'appjet.net' ? (appjet.appName + ".") : '') +
        appjet.mainDomain + '/recent.xml" />'
    );
};

var recent_rss = function()
{
    page.setMode("plain");
    response.setContentType('application/rss+xml');
    
    var thetime = new Date();
    
    print(raw("""<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
        <channel>"""));
            print(raw("<title>"+ wikiname +": Recent Changes</title>"));
            print(raw("<link>http://"+ (appjet.mainDomain=='appjet.net' ? appjet.appName + '.' : '') + appjet.mainDomain +"</link>"));
            print(raw("""<description>Track the most recent changes to the wiki in this feed.</description>
            <language>en</language>
            <generator>WikiJet-0.3</generator>"""));
            print(raw("<lastBuildDate>"+ 
                thetime.toGMTString() +
                "</lastBuildDate>"
            ));
            
            var chgs = storage.pages.sort(function(a,b){return a.date < b.date;});
            for (var ct = 0; ct < 25; ct++)
            {
                var obj = chgs.next();
                var objuser = getStorable(obj.user);
                print(raw("<item>"));
                print(raw("<title>" + obj.name.replace(/_/g, ' ') + "</title>"));
                print(raw("<link>" + page_url(obj.name) + "</link>"));
                print(raw("<description>" + wikify(obj.text) + "</description>"));
                print(raw("<pubDate>" + obj.date.toGMTString() + "</pubDate>"));
                if (typeof objuser != 'undefined')
                    print(raw("<dc:creator>" + objuser.uname  + "</dc:creator>"));
                print(raw("</item>"));
            }
        print(raw("""
        </channel>
</rss>
"""));
                
};

var history = function(name)
{
    nname = normalize_name(name);
    
    var pagecode = "<UL>\n";
    
    var rev = null;
    var pagehistory = storage.pages.filter({name: nname})
        .sort(function(a,b){return a.rev_number < b.rev_number;});
    
    pagecode += "<table><thead><tr>" +
        "<th>Revision</th><th>Date</th><th>User</th>" +
        "</tr></thead><tbody>";
    
    for (var ct = 0; ct < pagehistory.size(); ct++)
    {
        rev = pagehistory.next();
        historyuser = getStorable(rev.user);
        pagecode += "<tr>";
        pagecode += "<td>" + link('/' + nname + 
            '/rev/' + rev.rev_number, 
            rev.rev_number) + "</td>";
        pagecode += "<td><small>" + rev.date + "</small></td>";
        pagecode += "<td>" + (historyuser ? historyuser.uname : '') + "</td>";
        pagecode += '</tr>\n';        
    }
    pagecode += "</tbody></table>\n";
    
    render('Revision history of \''+name+'\'', 
        pagecode, 
        [link('/'+nname, 'Back to \''+name+'\'')]
    );
};

var revision = function(name, revnum)
{
    nname = normalize_name(name);
    wpage = get_page(nname, revnum);
    
    if (wpage) 
    {
        render(name + ', revision ' + revnum + ' (' + wpage.date + ')',
            wikify(wpage.text),
            [
                link("/_edit/" + nname + '/rev/' + revnum, "Edit page with this revision"),
                link("/_history/" + name, "Page history"),
                link("/pagelist", "All pages")
            ]
        );
    }
    else 
    {
        render(name + " not found. ",
            link("/_edit/" + nname, "Create it"),
            []
        );
    }
};

var template_test = function()
{
    page.setMode("plain");
    response.setContentType('application/rss+xml');
    
    var thetime = new Date();
    
    var headvars = {
      wikiname: wikiname,
      topurl: (appjet.mainDomain=='appjet.net' ? appjet.appName + '.' : '') + appjet.mainDomain,
      gmtnow: thetime.toGMTString()
    };
    
    var thehead = """<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
        <channel>
            <title>#{wikiname}: Recent Changes</title>
            <link>http://#{topurl}</link>
            <description>Track the most recent changes to the wiki in this feed.</description>
            <language>en</language>
            <generator>WikiJet-0.3</generator>
            <lastBuildDate>#{gmtnow}</lastBuildDate>
    """;
    
    do_template(headvars, thehead, 'printit');
    
    var chgs = storage.pages.sort(function(a,b){return a.date < b.date;});
    var chgtmp = """<item>
      <title>#{objtitle}</title>
      <link>#{objurl}</link>
      <description>#{objdesc}</description>
      <pubDate>#{objgmt}</pubdate>
      #{objcreatortag}
    </item>""";
    var chgcollect = ""; var chgvars = {};
    
    for (var ct = 0; ct < 25; ct++)
    {
        var obj = chgs.next();
        var objuser = getStorable(obj.user);
        chgvars = {
          objtitle: obj.name.replace(/_/g, ' '),
          objurl: page_url(obj.name),
          objdesc: wikify(obj.text),
          objgmt: obj.date.toGMTString(),
          objcreatortag: typeof objuser != 'undefined' ? "<dc:creator>" + objuser.uname  + "</dc:creator>" : ''
        };
        
        chgcollect += do_template(chgvars, chgtmp) + "\n";
    }
    
    print(raw(chgcollect + "</channel>\n</rss>"));
};

var view = function(name) 
{
    if (name == '') name = 'Home';
    nname = normalize_name(name);
    
    wpage = get_page(nname);
    if (wpage) 
    {
        var pagetitle = nname.replace(/_/g, ' ');
        var pagecode = wikify(wpage.text);
    }
    else 
    {
        var pagecode = name + " not found. ";
        pagecode += link("/_edit/" + nname, "Create it");
        var pagetitle = nname + ' doesn\'t exist yet';
    }
    
    if (wpage)
    {
        var themenu = [
            link("/recent", "Recent changes"),
            link("/pagelist", "All pages")
        ];
        if (user)
        {
            themenu.unshift(link("/_edit/" + nname, "Edit"));
            themenu.unshift(link("/_history/" + nname, "Page history"));
        }
    }    
    render(pagetitle, pagecode, themenu);
};

var edit = function(name, revnumber) 
{
    nname = normalize_name(name);
    var thisurl = "/_edit/" + nname;
    if (revnumber) thisurl += "/rev/" + revnumber;
    
    if (request.isPost) 
    {
        if (user)
        {
            set_page(nname, request.params.body);
        }
        response.redirect("/" + nname);
    }
    else     
    {
        if (user)
        {
            var pagebody = "";
            var thepage = get_page(nname, revnumber);
            if (thepage)
            {
                var body = thepage.text;
                if (revnumber)
                {
                    pagebody = "<p>This revision is dated " + thepage.date +
                        ". Saving this edit will revert the page to this older version! " +
                        "(<a href='/_edit/" + nname + "'>Edit current version instead</a>)</p>" +
                        pagebody;
                }
            }
            else
            {
                body = "";
            }
            var form = new QuickForm({action: "/_edit/" + nname, method: "post"});
    
            form.addInputTextArea("body", {
                label: "Body",
                value: body
            });
            form.addSubmit("submit","Save");
            
            pagebody += form.toHTML();
            
            render("Edit '" + (thepage ? thepage.name.replace(/_/g, ' ') : nname) + "'",
                pagebody,
                []
            );
        }
        else
        {
            response.redirect("/lib-user/login?and=" + thisurl);
        }
    }
};

// =======================================
// I say it's duck season, and I say FIRE
// =======================================
var pdispatch = function(urls) 
{
    for (var i = 0; i < urls.length; i += 2) 
    {
        var p = urls[i];
        var f = urls[i+1];
        
        var result = new RegExp(p).exec(request.path);
        if (result) 
        {
            var args = result.slice(1, result.length);
            //printp(args);
            f.apply(null, args);
            break;
        }
        //printp(result);
    }
};

urls = [
    "^/pagelist$", pagelist,
    "^/recent$", recent,
    "^/recent.xml$", recent_rss,
    "^/([A-Za-z0-9_]*)$", view,
    "/_history/([A-Za-z0-9_]*)$", history,
    "/_edit/([A-Za-z0-9_]*)$", edit,
    "/_edit/([A-Za-z0-9_]*?)/rev/([0-9]*)", edit,
    "/([A-Za-z0-9_]*?)/rev/([0-9]*)$", revision,
    "/template/", template_test
];
    
pdispatch(urls);


/* appjet:css */

body {
    margin: 10px;
}

h1 {
    margin: 0px 0px 10px;
    border-bottom: 1px solid #88a;
    font-family: Sylfaen,Georgia,serif;
    font-weight: normal;
    padding-bottom: 5px;
}

div#body {
    margin: 0px 0px 20px;
}

div#ctrls {
    border-top: 1px solid #88a;
    padding-top: 5px;
}









© Copyright 2007-2008 AppJet Inc.