node.js でカレンダー作成

Node.js で簡単にスケジュール／カレンダーが作れないかと調べたところ
以下のサイトで本当に簡単にできた

[Creating Online Event Calendar with Node.js and dhtmlxScheduler](http://www.dhtmlx.com/blog/using-dhtmlxscheduler-with-node-js/)

しかし、少し情報が古い+カスタマイズしたので、ここで記録のために手順を残す

変更点は、
* body-parser を別途インストールし、関連するソース(app.use(bodyParser...)の箇所)を新しい書き方にする
* MongoDBに接続するURLにスキーマ mongodb:// を追加する
* index.htmlで初期表示するカレンダーを当月にする
* Month View で週を日曜日始まりに変更(index.htmlの `scheduler.config.start_on_monday = false;` と書いた箇所。これがなければ月曜日始まりになる)
* 日本語ロケールに変更 (index.html で locale_jp.js をロード)

## 必要なもの
* Node.js
* MongoDB
* [dhtmlxScheduler.zip](http://dhtmlx.com/x/download/regular/dhtmlxScheduler.zip)

Node.js と MongoDB のインストールと起動については省略する

```
mkdir scheduler-node
cd scheduler-node
npm install express
npm install mongoskin
npm install body-parser
```

`scheduler-node` ディレクトリの下に `public` ディレクトリを作り、
そこにdhtmlxScheduler.zipの中にある `codebase` ディレクトリを展開する

```
  scheduler-node/
      |
      +-node_modules/    ← npm install で作成されたディレクトリ
      +-public/          ← 作成
           |
           +- codebase/  ← dhtmlxScheduler.zip の中にあるディレクトリ
```

scheduler-node/public ディレクトリの下に以下の index.html を作成する

```scheduler-node/public/index.html
<!doctype html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <script src="codebase/dhtmlxscheduler.js" type="text/javascript" charset="utf-8"></script>
    <link rel="stylesheet" href="codebase/dhtmlxscheduler.css" type="text/css" media="screen" title="no title" charset="utf-8">
    <script src="codebase/locale/locale_jp.js" type="text/javascript"></script>
    <style type="text/css" media="screen">
        html, body{
            margin:0px;
            padding:0px;
            height:100%;
            overflow:hidden;
        }   
    </style>
</head>


<script type="text/javascript" charset="utf-8">
    function init() {
        scheduler.config.start_on_monday = false;
        scheduler.init('scheduler_here',new Date(),"month");

        scheduler.templates.xml_date = function(value){ return new Date(value); };
        scheduler.load("/data", "json");

        scheduler.config.xml_date="%Y-%m-%d %H:%i";

        var dp = new dataProcessor("/data");
        dp.init(scheduler);
        dp.setTransactionMode("POST", false);
    }
</script>

<body onload="init();">
    <div id="scheduler_here" class="dhx_cal_container" style='width:100%; height:100%;'>
        <div class="dhx_cal_navline">
            <div class="dhx_cal_prev_button">&nbsp;</div>
            <div class="dhx_cal_next_button">&nbsp;</div>
            <div class="dhx_cal_today_button"></div>
            <div class="dhx_cal_date"></div>
            <div class="dhx_cal_tab" name="day_tab" style="right:204px;"></div>
            <div class="dhx_cal_tab" name="week_tab" style="right:140px;"></div>            <div class="dhx_cal_tab" name="month_tab" style="right:76px;"></div>        </div>
        <div class="dhx_cal_header">
        </div>
        <div class="dhx_cal_data">
        </div>
    </div>
</body>
```

scheduler-node の下に app.js を作成する

```scheduler-node/app.js
var express = require('express');
var bodyParser = require('body-parser');
var path = require('path');

//connect to the mongoDB
var db = require('mongoskin').db("mongodb://localhost/testdb", { w: 0});
    db.bind('event');

//create express app, use public folder for static files
var app = express();

//is necessary for parsing POST request
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.use(express.static(path.join(__dirname, 'public')));

app.get('/init', function(req, res){
    db.event.insert({ 
        text:"My test event A", 
        start_date: new Date(2013,8,1),
        end_date:   new Date(2013,8,5)
    });
    db.event.insert({ 
        text:"One more test event", 
        start_date: new Date(2013,8,3),
        end_date:   new Date(2013,8,8),
        color: "#DD8616"
    });

    /*... skipping similar code for other test events...*/

    res.send("Test events were added to the database")
});


app.get('/data', function(req, res){
    db.event.find().toArray(function(err, data){
        //set id property for all records
        for (var i = 0; i < data.length; i++)
            data[i].id = data[i]._id;

        //output response
        res.send(data);
    });
});

app.post('/data', function(req, res){
    var data = req.body;

    //get operation type
    var mode = data["!nativeeditor_status"];
    //get id of record
    var sid = data.id;
    var tid = sid;

    //remove properties which we do not want to save in DB
    delete data.id;
    delete data.gr_id;
    delete data["!nativeeditor_status"];


    //output confirmation response
    function update_response(err, result){
        if (err)
            mode = "error";
        else if (mode == "inserted")
            tid = data._id;

        res.setHeader("Content-Type","text/xml");
        res.send("<data><action type='"+mode+"' sid='"+sid+"' tid='"+tid+"'/></data>");
    }

    //run db operation
    if (mode == "updated")
        db.event.updateById( sid, data, update_response);
    else if (mode == "inserted")
        db.event.insert(data, update_response);
    else if (mode == "deleted")
        db.event.removeById( sid, update_response);
    else
        res.send("Not supported operation");
});

app.listen(3000);
```

app.js を起動する

```
node app.js
```

`http://localhost:3000` にアクセスする