途中だけどうpっておく

簡単なURLとUser名だけで、好きなページにwiki形式でメモするツールがほしかったので作った。

TODO

  • authorを認証にする
  • parseが微妙に失敗したりするので直す
  • 文字コード不明なページ上で処理すると文字化けする
  • 長い文字列の場合はgetの都合に合わせて適当に分割する(ここ、途中で眠くなった)
  • なんか2回ずつリクエスト飛ばすのが気持ち悪い
  • とりあえず・・・とりあえず・・・ 

appengine側

#!/usr/bin/env python2.5
# -*- coding: utf-8 -*-

import cgi, codecs
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db

class Data(db.Model):
    author = db.StringProperty(required = True)
    url = db.StringProperty(required = True)
    content = db.StringProperty(multiline = True)
    date = db.DateTimeProperty(auto_now_add = True)
    
class Display(webapp.RequestHandler):
    def get(self):
        if self.request.get('url') and self.request.get('author'):
            datas = db.GqlQuery("SELECT * FROM Data WHERE url = '%s' AND author = '%s' ORDER BY date DESC LIMIT 1"
                                % (self.request.get('url'), self.request.get('author')))
            for data in datas:
                if data.content:
                    self.response.out.write('jWiki.display({"result":"%s","error":null,"id":"0"})'
                                            % data.content.encode('utf-8'))
        else:
            self.response.out.write('jWiki.display({"result":null,"error":"parameter error","id":"0"})'
                                    % data.content.encode('utf-8'))
            

class Save(webapp.RequestHandler):
    def get(self):
        if self.request.get('content') and self.request.get('url') and self.request.get('author'):
            data = Data(content = self.request.get('content'),
                        url = self.request.get('url'),
                        author = self.request.get('author'))
            data.put()
   
            datas = db.GqlQuery("SELECT * FROM Data WHERE url = '%s' ORDER BY date DESC LIMIT 1"
                                % (self.request.get('url'), self.request.get('author')))
            for data in datas:
                self.response.out.write('jWiki.save({"result":"success content=%s url=%s","error":null,"id":"1"})'
                                        %(guess_charset(data.content), data.url))
                return True
            self.response.out.write('jWiki.save({"result":null,"error":"cannot save your data","id":"1"})')
        else:
            self.response.out.write('jWiki.save({"result":null,"error":"parameter error","id":"1"})')
            
            

application = webapp.WSGIApplication(
    [('/jWiki/display', Display), ('/jWiki/save', Save)],
    debug = True)

def main():
    run_wsgi_app(application)

if __name__ == '__main__':
    main()


def guess_charset(data):
    f = lambda d, enc: d.decode(enc) and enc

    try: return f(data, 'utf-8')
    except: pass
    try: return f(data, 'shift-jis')
    except: pass
    try: return f(data, 'euc-jp')
    except: pass
    try: return f(data, 'iso2022-jp')
    except: pass
    return None

js側

/************************************************************
 * NAME		: jWiki.js
 * AUTHOR	: muddydixon@gmail.com
 * VERSION	: 0.0.0
 * DESCRIPTION	: どこででもwikiを書いておけるようにする付箋ツールのようなもの
 * DATE		: 2009/10/09	create
 */
(function(){
    /************************************************************
     * 設定
     */
    if(!window.jWiki) window.jWiki;
    var cfg = {
	baseurl : 'http://localhost',
	codeckey : '--*--'
    };
    var codec = new RegExp(cfg.codeckey);
    /************************************************************
     * 共通関数
     */
    var showLoading = function(){
    };
    var hideLoading = function(){
    };
    var translate = function(data){
	var O = function(){this.initialize.apply(this, arguments)};
	O.prototype = {
	    initialize: function(parent, elm, data){
		this.elm = elm;
		this.data = data;
		this.parent = parent;
	    },
	    push: function(child){
		if(!this.child) this.child = [];
		this.child.push(child);
	    },
	    makeText: function(){
		if(this.data){
		    return '<'+this.elm+'>'+this.data+'</'+this.elm+'>';
		}else{
		    var tmp = [];
		    if(this.child){
			for(var i = 0, l = this.child.length; i < l; i++){
			    tmp.push(this.child[i].makeText());
			}
			return '<'+this.elm+'>'+tmp.join('')+'</'+this.elm+'>';
		    }
		    return '';
		}
	    }
	};
	
	var rv = new O(null, 'div', false);
	var p = rv;
	var idt = 0;
	window.RV = rv;
	
	for(var i = 0, l = data.length; i < l; i++){

	    var match = data[i].match(/^(\s+)(\*|.\.)\s+(.+)$/);

	    if(!match){
		if(data[i].match(/^[\s]*$/)){
		    if(p.parent){
			p = p.parent;
			idt = 0;
		    }
		}else{
		    p.push(new O(p, 'p', data[i]));
		}
	    }else{
		if(match[2]){
		    var elm = (match[2] === '*' ? 'ul' : 'ol');
		    if(idt < match[1].length){
			idt = match[1].length;
			var o = new O(p, elm, false);
			p.push(o);
			p = o;
		    }else if(idt > match[1].length){
			idt = match[1].length;
 			p = p.parent;
		    }else if(p.elm !== elm){
			p = p.parent;
			var o = new O(p, elm, false);
			p.push(o);
			p = o;
		    }
		    p.push(new O(p, 'li', match[3]));
		}
	    }
	}
	return rv.makeText();
    };
    var execute = function(action, data){
	var sendRequest = function(action, param){
	    alert(param);
	    //データが大きい場合は分割して個別にidをつけてリクエストを飛ばす必要がある
	    var s = document.createElement('scr'+'ipt');
	    s.setAttribute('type', 'text/javascript');
	    s.setAttribute('src', cfg.baseurl+'/jWiki/'+action+(param.length > 0 ? '?'+param.join('&'): '')+'&id='+i);
	    document.body.appendChild(s);
	}
	
	var param = [];
	if(data.content && typeof data.content === 'string'){
	    var content = data.content;
	    delete data.content;
	    var match = content.match(/.{10}/g);
	    for(var i = 0, l = match.length; i < l; i++){
		param = [];
		for(var i in data){
		    if(data.hasOwnProperty(i)){
			param.push(i+'='+data[i]);
		    }
		}
		alert(match[i]);
		param.push('content='+match[i]);
		
		sendRequest(action, param);
	    }
	}else{
	    for(var i in data){
		if(data.hasOwnProperty(i)){
		    param.push(i+'='+data[i]);
		}
	    }
	    sendRequest(action, param);
	}
    };

    var showJWiki = function(){
	document.getElementById('jwiki_head').style.display = '';
	document.getElementById('jwiki_body').style.display = '';
	document.getElementById('jwiki_foot').style.display = '';
	document.getElementById('jwiki_close_foot').style.display = 'none';
	document.getElementById('jwiki').className = '';
    };
    var hideJWiki = function(){
	document.getElementById('jwiki_head').style.display = 'none';
	document.getElementById('jwiki_body').style.display = 'none';
	document.getElementById('jwiki_foot').style.display = 'none';
	document.getElementById('jwiki_close_foot').style.display = '';
	document.getElementById('jwiki').className = 'close';
    };

    Object.prototype.show = function(){
	if(this.style.display) this.style.display = '';};
    Object.prototype.hide = function(){
	alert(this);
	if(this.style.display) this.style.display = 'none';};
    Observe = function(elm, ev, fn, cap){
	if(elm.addEventListener){
	    elm.addEventListener(ev, fn, cap);
	}else if(elm.attachEvent){
	    elm.attachEvent('on'+ev, fn);
	}
    };

    var jWikiPane = document.createElement('div');
    jWikiPane.id = 'jwiki';
    jWikiPane.innerHTML =
	'<div id="jwiki_head"></div>'+
	'<div id="jwiki_body"><div id="jwiki_body_display"></div><textarea id="jwiki_body_text"></textarea></div>'+
	'<div id="jwiki_foot"><span id="jwiki_close" class="jwiki_button">[x]</span><br clear="both"/></div>'+
    	'<div id="jwiki_close_foot"><span id="jwiki_open" class="jwiki_button">[open]</span><br clear="both"/></div>';

    
    /************************************************************
     * 初期化
     */
    var initialize = function(){
	if(document.getElementById('jWiki')) return false;
	
	var css = document.createElement('link');
	css.setAttribute('type', 'text/css');
	css.setAttribute('rel', 'stylesheet');
	css.setAttribute('href', cfg.baseurl+'/css/jWiki.css');

	var h = document.getElementsByTagName('head')[0];
	h.appendChild(css);
	
	document.body.appendChild(jWikiPane);
	document.getElementById('jwiki_close_foot').style.display = 'none';
	document.getElementById('jwiki_body_text').style.display = 'none';

	Observe(document.getElementById('jwiki_body_display'), 'click', edit, false);
	Observe(document.getElementById('jwiki_body_text'), 'blur', save, false);
	Observe(document.getElementById('jwiki_close'), 'click', hideJWiki, false);
	Observe(document.getElementById('jwiki_open'), 'click', showJWiki, false);
	execute('display', {url: location.href, author: 'muddydixon'});
    };
    /************************************************************
     * 各種イベントハンドラ
     */
    var edit = function(){
	document.getElementById('jwiki_body_display').style.display = 'none';
	document.getElementById('jwiki_body_text').style.display = '';
	document.getElementById('jwiki_body_text').focus();
    }
    var save = function(){
	document.getElementById('jwiki_body_display').style.display = '';
	document.getElementById('jwiki_body_text').style.display = 'none';
	var text = document.getElementById('jwiki_body_text').value.replace(/\n/g, cfg.codeckey);
 	execute('save', {author:'muddydixon', url:location.href, content:text});
	jWiki.display({"result":text, "error":null, "id": -1});
    }
    /************************************************************
     * イベントコールバック
     */
    jWiki = {
	display	: function(data){
	    if(data.error){
		return false;
	    }
// 	    var text = decodeURI(data.result).split(cfg.codeckey);
	    var text = data.result.split(cfg.codeckey);
	    document.getElementById('jwiki_body_text').value = text.join('\n');
	    document.getElementById('jwiki_body_display').innerHTML = translate(text);
	},
	save	: function(data){
	    if(data.error){
		return false;
	    }
	}
    }
    /************************************************************
     * 実行
     */
    initialize();
})();