ホーム > タグ > JavaScript
JavaScript
iPhone 向けのタスク管理アプリを作ってみる
iPhone を買って約 1 ヶ月が立ちました。
AppStore には毎日のように新しいアプリが登場していて、うずうずしてきたのでアプリを作り始めました。
とはいってもネイティブアプリではなく、Safari 上で動く Web アプリです。
今回は iPhone 用の Safari にも組み込まれている SQLite を JavaScript から利用できる SQL API を使って、タスク管理(ToDo 管理とも呼びますね)アプリを作ってみようと思います。
せっかく iPhone 向けのアプリなので、インタフェースはやっぱりこだわりたいところ。
その iPhone 向けインタフェースのテンプレートが提供されているので、今回はそれを使いました。
iphone-universal - Google Code
まだ作成途中ですが、下記のリンクから試すことができます。Safari でアクセスしてください。
注:データベースを削除したい時は、
・通常の Safari の場合:「環境設定」→「セキュリティ」→「データベースを表示」→データベースを選択して「取り除く」
・iPhone 版 Safari の場合:「設定」→「Safari」→「Databases」→編集ボタン→削除
でできます。
今までできたところまで、ホーム画面はこんな感じです。

テンプレートのおかげで、あまり工数をかけずに iPhone 向けインタフェースができました。
そして SQLite を操作する JavaScript ソースが以下。
こちらのサイトを参考にしました。
- JavaScript++かも日記: 【iPhone】iPhone用 JavaScriptデータベースプログラミング入門 (目次)
- Safari 3.1 に実装された「Client-side database storage (SQL API)」とは何か? - IT戦記
// DB オブジェクト
var db;
// DB 初期化(テーブルが無ければ作成する)
function init() {
if(window.openDatabase) {
db = openDatabase('test_db', '1.0', 'Tasks');
db.transaction(
function(tx) {
tx.executeSql('SELECT COUNT(*) FROM tasks', [],
function(tx, rs) {},
function(tx, error) {
tx.executeSql('CREATE TABLE tasks (id INTEGER PRIMARY KEY, body TEXT, cmp_f BOOLEAN, priority INTEGER)', [],
function() {},
function(error) {
alert('Database does not created. : ' + error.message);
}
);
}
);
}
);
} else {
alert('Sorry, this browser does not supported.');
}
}
// タスク一覧を表示する
function show() {
var incmp = document.getElementById('incomplete_list');
var cmp = document.getElementById('completed_list');
if(!(incmp && cmp)) return;
// 現在表示しているタスクをクリアする
while(incmp.hasChildNodes()) {
incmp.removeChild(incmp.firstChild);
}
while(cmp.hasChildNodes()) {
cmp.removeChild(cmp.firstChild);
}
var ul = document.createElement('ul');
incmp.appendChild(ul);
var ul = document.createElement('ul');
cmp.appendChild(ul);
// タスク一覧を取得して表示する
db.transaction(
function(tx) {
tx.executeSql('SELECT * FROM tasks ORDER BY priority', [], function(tx, rs) {
for(var i = 0; i < rs.rows.length; i++) {
var row = rs.rows.item(i);
var list = (row.cmp_f == 't') ? cmp : incmp;
appendItem(list.firstChild, 'edit.html?id=' + row.id, 'arrow', row.body, row.priority);
}
// タスク件数が 0 件の場合
if(incmp.firstChild.childNodes.length == 0) {
appendItem(uncmp.firstChild, '#', null, '(No Item)', null);
}
if(cmp.firstChild.childNodes.length == 0) {
appendItem(cmp.firstChild, '#', null, '(No Item)', null);
}
});
},
function(error) {
alert('Task data does not get. : ' + error.message);
},
function() {}
);
}
// リストを追加する
function appendItem(prt, hrf, cl, val, pri) {
if(!prt) return;
var li = document.createElement('li');
li.className = 'pri' + pri;
prt.appendChild(li);
var a = document.createElement('a');
a.href = hrf;
if(cl) a.className = cl;
li.appendChild(a);
var t = document.createTextNode(val);
a.appendChild(t);
}
// タスクを新規作成する
function add() {
var b = document.getElementById('body');
if(!b) return;
var body = b.value;
var p = document.getElementById('priority');
if(!p) return;
var idx = p.selectedIndex;
var priority = p.options[idx].value;
db.transaction(
function(tx) {
tx.executeSql('INSERT INTO tasks VALUES(NULL, ?, ?, ?)', [body, 'f', priority]);
},
function(error) {
alert('Task does not create. : ' + error.message);
},
function() {
location.href = 'index.html';
}
);
}
// タスクを削除する
function del() {
var id = getIdValue();
if(!id) return;
if(window.confirm('Delete this task ?')) {
db.transaction(
function(tx) {
tx.executeSql('DELETE FROM tasks WHERE id = ?', [id]);
},
function(error) {
alert(error.message);
},
function() {
location.href = 'index.html';
}
);
}
}
// タスクを完了/未完了にする
function cmp(f) {
var id = getIdValue();
if(!id) return;
db.transaction(
function(tx) {
tx.executeSql('UPDATE tasks SET cmp_f = ? where id = ?', [(f) ? 't' : 'f', id]);
},
function(error) {
alert(error.message);
},
function() {
location.href = 'index.html';
}
);
}
// 1 つのタスクを表示する
function showOne() {
var id = getIdValue();
if(!id) return;
db.transaction(
function(tx) {
tx.executeSql('SELECT * FROM tasks WHERE id = ?', [id], function(tx, rs) {
if(rs.rows.length > 0) {
var row = rs.rows.item(0);
var body = document.getElementById('body');
if(!body) return;
body.value = row.body;
var p = document.getElementById('priority');
if(!p) return;
p.value = row.priority;
if(row.cmp_f == 't') {
var elm = document.getElementById('cmp');
} else {
var elm = document.getElementById('uncmp');
}
elm.style.display = 'none';
} else {
alert('Task does not exist.');
}
});
},
function(error) {
alert(error.message);
},
function() {}
);
}
// タスクを更新する
function update() {
var b = document.getElementById('body');
if(!b) return;
var body = b.value;
if(body.length > 0) {
var id = getIdValue();
if(!id) return;
var p = document.getElementById('priority');
if(!p) return;
var priority = p.value;
db.transaction(
function(tx) {
tx.executeSql('UPDATE tasks SET body = ?, priority = ? WHERE id = ?', [body, priority, id]);
},
function(error) {
alert('Task update failed. : ' + error.message);
},
function() {
location.href = 'index.html';
}
);
} else {
alert('Please input body.');
}
}
// QueryString から ID 値を取得する
function getIdValue() {
if(location.search.length > 0) {
var arr = location.search.substr(1).split('&');
for(idx in arr) {
var k = arr[idx].split('=');
if(k[0] == 'id') return k[1];
}
} else {
return null;
}
}
- Comments: 0
- Trackbacks: 2
Twitter の投稿日時のフォーマットを変更するグリモン
- 2008-05-29 (木)

これは何?
Twitter に表示される投稿日時(3 hours ago など)を yyyy/mm/dd hh:mi 形式に変更する Greasemonkey です。
きっかけは自分のログを振り返っている時に、「17 時間前」などと表示されても直感的にいつだったのかが分かりにくかったからです。
対応しているページは、Friends Timeline, Replies, Archive, Public Timeline, Favorites です。Permalink, Direct Messages には対応していません。
使い方
下記リンクからインストールしてください。 Firefox/Greasemonkey または Safari/Greasekit が必要です。
Twitter Time Converter – Userscripts.org
技術的なところ
Twitter のページの HTML に、ISO 8601 形式の日付が abbr タグの title 属性に入っているのですが、その ISO 8601 形式の日付が JavaScript の Date.parse で解釈できないようなので、仕方なく手動で parse しました。
parse については、以下のライブラリを参考にさせていただきました。感謝。
[JSAN] Date.W3CDTF - ISO-8601日時フォーマット対応JavaScriptクラス
関連リンク
ソース
// ==UserScript==
// @name Twitter Time Converter
// @namespace http://www.sukechan.net/
// @description The format of posted date is change.
// @include http://twitter.com/*
// @version 1.0
// ==/UserScript==
(function() {
var f = function() {
var x = document.evaluate('//a[@class="entry-date"]/abbr', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < x.snapshotLength; i++) {
var item = x.snapshotItem(i);
var ds = parseDate(item.getAttribute("title"));
if (ds) item.parentNode.innerHTML = ds;
}
}
function parseDate(s) {
var arr = s.split(/[^0-9]/);
if (arr.length == 8 ) {
for (var j = 0; j < arr.length; j++) arr[j] = arr[j] - 0;
var msec = Date.UTC(arr[0], arr[1] - 1, arr[2], arr[3], arr[4], arr[5]);
if (s.indexOf("+") < 0) arr[6] *= -1;
msec -= (arr[6] * 60 + arr[7]) * 60 * 1000;
var dt = new Date(msec);
return dt.getFullYear() + "/" + padZero(dt.getMonth() + 1) + "/" + padZero(dt.getDate()) + " " + padZero(dt.getHours()) + ":" + padZero(dt.getMinutes());
} else {
return;
}
}
function padZero(s) {
return ("0" + s).slice(-2);
}
f();
addFilter(f);
function addFilter(filter, i) {
i = i || 4;
if (window.AutoPagerize && window.AutoPagerize.addFilter) {
window.AutoPagerize.addFilter(filter);
}
else if (i > 1) {
setTimeout(arguments.callee, 1000, filter, i -1);
}
}
})();
- Comments: 0
- Trackbacks: 0
Safari 3.1 の Web インスペクタがすごい
- 2008-03-20 (木)
- Safari
先日リリースされた Safari 3.1 の新機能の 1 つに、「開発」メニューがあります。その中の Web インスペクタがとても便利なので紹介します。
環境設定の「詳細」にある「メニューバーに”開発”メニューを表示」にチェックを入れると、開発メニューが表示されます。以前のバージョンでは隠し機能であった「Debug」メニューになります。

開発メニューから「Web インスペクタを表示」を選択します。HTML や JavaScript のソースを閲覧できます。
要素を選択すると、その要素がページ上でハイライト表示され、非常に分かりやすいです。
この Web インスペクタ以外にも Safari 3.1 にはさまざまな機能が追加されています。みなさんもぜひ使ってみてください。
- Comments: 0
- Trackbacks (Close): 0
Twitter に Google Maps へのリンクを追加するグリモン
- 2008-03-17 (月)

これは何?
Twitter の発言で、L:住所 と表示されているロケーション情報に、Google Maps へのリンクを追加する Greasemonkey です。
使い方
下記リンクからインストールしてください。例によって Greasemonkey が必要です。(Safari の場合は GreaseKit)
インストール後、Twitter の L:住所 という部分が Google Maps へのリンクになります。
twittergooglemapslink.user.js (version 1.0)
Userscripts.org で管理することにしました。
Twitter Google Maps Link – Userscripts.org
その他、技術的なこと
既に同じグリモンがあるかもしれませんが、見つからなかったので作りました。
今回、初めて XPath を使いました。最初 DOM でやろうと思ったんですが、処理が煩雑になるし XPath の方が早いらしいので。以下のリンクを参考にしました。
- Introduction to using XPath in JavaScript - MDC
- JavaScript-XPath をリリースしました!さあ、あなたも XPath を使おう!(解説付き) - IT戦記
// ==UserScript==
// @name Twitter Google Maps Link
// @namespace http://www.sukechan.net/
// @description Location is convert into the link to Google Maps.
// @include http://twitter.com/*
// @version 1.0
// ==/UserScript==
(function() {
var x = document.evaluate('//*[@class="entry-title entry-content"] | //div[@class="desc"]/descendant::p[1]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < x.snapshotLength; i++) {
var idx = x.snapshotItem(i).textContent.indexOf("L:")
if (idx >= 0) {
loc = x.snapshotItem(i).textContent.substr(idx + 2).split(/\s+/)[0].replace(/[\n\r\s]/g, "");
x.snapshotItem(i).innerHTML = x.snapshotItem(i).innerHTML.replace(loc, "" + loc + "");
}
}
})();
- Comments: 0
- Trackbacks (Close): 0
相手に Follow されてるかどうかを表示するグリモン
- 2008-03-15 (土)

これは何?
Twitter で、指定したユーザに Follow されてるかどうかを表示する Greasemonkey スクリプトです。
以前作ったこれを Greasemonkey にしました。
使い方
下記リンクからインストールしてください。要 Greasemonkey。
インストール後、Twitter のユーザページにアクセスすると、「Does follow do you?」というボタンが追加されます。そのボタンをクリックすると、あなたがそのユーザから follow されてるかどうかを表示します。
twitterfollowercheck.user.js (version 1.0)
その他
グリモンは初めて作りました。
Firefox, Safari(GreaseKit) で動くと思います。
コードも晒しておきます。つっこみ大歓迎。
// ==UserScript==
// @name Twitter Follower Check
// @namespace http://www.sukechan.net/
// @include http://twitter.com/*
// @version 1.0
// ==/UserScript==
(function(){
var num = 1;
var screenName = location.href.substr(19);
// Followers Request
function getFollowers() {
addElm.disabled = true;
addElm.value = "Loading...";
var httpObj = createRequest();
if (httpObj) {
httpObj.open("GET", "http://twitter.com/statuses/followers.json?page=" + num, true);
httpObj.send(null);
httpObj.onreadystatechange = function() {
if (httpObj.readyState == 4) {
if (httpObj.status == 200) {
var jsonData = eval(httpObj.responseText);
if (jsonData.length > 0) {
for (var i = 0; i < jsonData.length; i++) {
if (screenName == jsonData[i].screen_name) {
addElm.value = "You are followed.";
return;
}
}
getFollowers(num++);
} else {
addElm.value = "You are not followed.";
}
} else {
addElm.value = "error. status: " + httpObj.status;
}
}
}
}
}
// XMLHttpRequest
function createRequest() {
var xmlHttp = null;
if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xmlHttp = new ActiveXObject("MSXML2.XMLHTTP");
} catch (e) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}
// add element
var elm = document.getElementById("follow-control");
if (elm) {
var addElm = document.createElement("input");
addElm.setAttribute("class", "follow-button");
addElm.setAttribute("type", "button");
addElm.setAttribute("value", "Does follow do you?");
addElm.addEventListener("click", function() {getFollowers()}, false);
addElm.style.width = "150px";
addElm.style.marginTop = "3px";
elm.appendChild(addElm);
}
})();
- Comments: 0
- Trackbacks (Close): 0
Home > Tags > JavaScript
- Search
- Feeds
- Meta
- あわせて読みたい
- Others...

