+<!doctype html>
<html>
+<head>
+<title>WebKit HTML 5 SQL Storage Notes Demo</title>
<style>
-#oddNote { background-color: White; }
-#evenNote { background-color: LightCyan; }
-#note { border: 2px dashed blue; padding: 2px; }
-</style>
+body {
+ font-family: 'Lucida Grande', 'Helvetica', sans-serif;
+}
-<script>
+.note {
+ background-color: rgb(255, 240, 70);
+ height: 250px;
+ padding: 10px;
+ position: absolute;
+ width: 200px;
+ -webkit-box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);
+}
+
+.closebutton {
+ background-image: url(deleteButton.tiff);
+ height: 30px;
+ position: absolute;
+ left: -15px;
+ top: -15px;
+ width: 30px;
+}
+
+.closebutton:active {
+ background-image: url(deleteButtonPressed.tiff);
+}
+.edit {
+ outline: none;
+}
+
+.timestamp {
+ position: absolute;
+ left: 0px;
+ right: 0px;
+ bottom: 0px;
+ font-size: 9px;
+ background-color: #db0;
+ color: white;
+ border-top: 1px solid #a80;
+ padding: 2px 4px;
+ text-align: right;
+}
+</style>
+<script>
var db;
try {
db = openDatabase("NoteTest", "1.0");
alert("Failed to open the database on disk. Are you using a WebKit nightly with this feature enabled?");
}
-function insertNote(note, timestamp)
+try {
+ db.executeSql("CREATE TABLE WebKitStickyNotes (id REAL UNIQUE, note TEXT, timestamp REAL, left TEXT, top TEXT, zindex REAL)", [], function (result) {});
+} catch(e) { }
+
+var captured = null;
+var highestZ = 0;
+var highestId = 0;
+
+function Note()
{
- db.executeSql("INSERT INTO WebKitNotes VALUES (?, ?)", [note, timestamp], function(result) {
- if (result.errorCode)
- alert("Failed to save note in database");
- else
- appendNote(note, timestamp);
- });
+ var self = this;
+
+ var note = document.createElement('div');
+ note.className = 'note';
+ note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
+ note.addEventListener('click', function() { return self.onNoteClick() }, false);
+ this.note = note;
+
+ var close = document.createElement('div');
+ close.className = 'closebutton';
+ close.addEventListener('click', function() { return self.close() }, false);
+ note.appendChild(close);
+
+ var edit = document.createElement('div');
+ edit.className = 'edit';
+ edit.setAttribute('contenteditable', true);
+ edit.addEventListener('keyup', function() { return self.onKeyUp() }, false);
+ note.appendChild(edit);
+ this.editField = edit;
+
+ var ts = document.createElement('div');
+ ts.className = 'timestamp';
+ ts.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
+ note.appendChild(ts);
+ this.lastModified = ts;
+
+ document.body.appendChild(note);
+ return this;
}
-function saveNote()
-{
- if (!db)
- return;
-
- var note = document.getElementById("note").innerText;
- var timestamp = new Date();
- timestamp = timestamp.getTime();
-
- // This CREATE TABLE might throw if the table already exists, so ignore that.
- try {
- db.executeSql("CREATE TABLE WebKitNotes (note TEXT, timestamp REAL)", [], function(result) {
- insertNote(note, timestamp);
- });
- } catch(err) {
- // The table already exists, so try an insert.
- insertNote(note, timestamp);
- }
+Note.prototype = {
+ get id()
+ {
+ if (!("_id" in this))
+ this._id = 0;
+ return this._id;
+ },
+
+ set id(x)
+ {
+ this._id = x;
+ },
+
+ get text()
+ {
+ return this.editField.innerHTML;
+ },
+
+ set text(x)
+ {
+ this.editField.innerHTML = x;
+ },
+
+ get timestamp()
+ {
+ if (!("_timestamp" in this))
+ this._timestamp = 0;
+ return this._timestamp;
+ },
+
+ set timestamp(x)
+ {
+ if (this._timestamp == x)
+ return;
+
+ this._timestamp = x;
+ var date = new Date();
+ date.setTime(parseFloat(x));
+ this.lastModified.textContent = modifiedString(date);
+ },
+
+ get left()
+ {
+ return this.note.style.left;
+ },
+
+ set left(x)
+ {
+ this.note.style.left = x;
+ },
+
+ get top()
+ {
+ return this.note.style.top;
+ },
+
+ set top(x)
+ {
+ this.note.style.top = x;
+ },
+
+ get zIndex()
+ {
+ return this.note.style.zIndex;
+ },
+
+ set zIndex(x)
+ {
+ this.note.style.zIndex = x;
+ },
+
+ close: function()
+ {
+ this.killSaveTimer();
+
+ db.executeSql("DELETE FROM WebKitStickyNotes WHERE id = ?", [this.id], function(result) {});
+ document.body.removeChild(this.note);
+ },
+
+ saveSoon: function()
+ {
+ this.cancelPendingSave();
+ var self = this;
+ this._saveTimer = setTimeout(function() { self.save() }, 200);
+ },
+
+ cancelPendingSave: function()
+ {
+ if (!("_saveTimer" in this))
+ return;
+ clearTimeout(this._saveTimer);
+ delete this._saveTimer;
+ },
+
+ save: function()
+ {
+ this.cancelPendingSave();
+
+ if ("dirty" in this) {
+ this.timestamp = new Date().getTime();
+ delete this.dirty;
+ }
+
+ db.executeSql("UPDATE WebKitStickyNotes SET note = ?, timestamp = ?, left = ?, top = ?, zindex = ? WHERE id = ?", [this.text, this.timestamp, this.left, this.top, this.zIndex, this.id], function(result) {});
+ },
+
+ saveAsNew: function()
+ {
+ this.timestamp = new Date().getTime();
+ db.executeSql("INSERT INTO WebKitStickyNotes (id, note, timestamp, left, top, zindex) VALUES (?, ?, ?, ?, ?, ?)", [this.id, this.text, this.timestamp, this.left, this.top, this.zIndex], function(result) {});
+ },
+
+ onMouseDown: function(e)
+ {
+ captured = this;
+ this.startX = e.clientX - this.note.offsetLeft;
+ this.startY = e.clientY - this.note.offsetTop;
+ this.zIndex = ++highestZ;
+
+ var self = this;
+ if (!("mouseMoveHandler" in this)) {
+ this.mouseMoveHandler = function(e) { return self.onMouseMove(e) }
+ this.mouseUpHandler = function(e) { return self.onMouseUp(e) }
+ }
+
+ document.addEventListener('mousemove', this.mouseMoveHandler, true);
+ document.addEventListener('mouseup', this.mouseUpHandler, true);
+
+ return false;
+ },
+
+ onMouseMove: function(e)
+ {
+ if (this != captured)
+ return true;
+
+ this.left = e.clientX - this.startX + 'px';
+ this.top = e.clientY - this.startY + 'px';
+ return false;
+ },
+
+ onMouseUp: function(e)
+ {
+ document.removeEventListener('mousemove', this.mouseMoveHandler, true);
+ document.removeEventListener('mouseup', this.mouseUpHandler, true);
+
+ this.save();
+ return false;
+ },
+
+ onNoteClick: function(e)
+ {
+ this.editField.focus();
+ getSelection().collapseToEnd();
+ },
+
+ onKeyUp: function()
+ {
+ this.dirty = true;
+ this.saveSoon();
+ },
}
-var added = 0;
-var noteIDs = new Array();
-noteIDs[0] = "oddNote";
-noteIDs[1] = "evenNote";
+function loaded()
+{
+ db.executeSql("SELECT id, note, timestamp, left, top, zindex FROM WebKitStickyNotes", [], function(result) {
+ if (result.errorCode) {
+ alert('Failed to retrieve notes from database');
+ return;
+ }
-function appendNote(note, timestamp)
+ for (var i = 0; i < result.rows.length; ++i) {
+ var row = result.rows.item(i);
+ var note = new Note();
+ note.id = row['id'];
+ note.text = row['note'];
+ note.timestamp = row['timestamp'];
+ note.left = row['left'];
+ note.top = row['top'];
+ note.zIndex = row['zindex'];
+
+ if (row['id'] > highestId)
+ highestId = row['id'];
+ if (row['zindex'] > highestZ)
+ highestZ = row['zindex'];
+ }
+
+ if (!result.rows.length)
+ newNote();
+ });
+}
+
+function modifiedString(date)
{
- if (!db)
- return;
-
- var previousNotes = document.getElementById("previousNotes");
-
- var date = new Date();
- date.setTime(parseFloat(timestamp));
-
- var newdiv = document.createElement('div');
- newdiv.setAttribute('id', noteIDs[added++ % 2]);
- newdiv.innerText = date + ": " + note;
-
- previousNotes.appendChild(newdiv);
+ return 'Last Modified: ' + date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
}
-function loaded()
+function newNote()
{
- if (!db)
- return;
-
- try {
- db.executeSql("SELECT note, timestamp FROM WebKitNotes ORDER BY timestamp", [], function(result) {
- if (result.errorCode) {
- alert("Failed to retrieve previous notes from database");
- return;
- }
- for (var i = 0; i < result.rows.length; ++i) {
- var row = result.rows.item(i);
- appendNote(row["note"], row["timestamp"]);
- }
- });
- } catch(err) { }
+ var note = new Note();
+ note.id = ++highestId;
+ note.timestamp = new Date().getTime();
+ note.left = Math.round(Math.random() * 400) + 'px';
+ note.top = Math.round(Math.random() * 500) + 'px';
+ note.zIndex = ++highestZ;
+ note.saveAsNew();
}
+addEventListener('load', loaded, false);
</script>
-
-<body onload="loaded()">
-<div id="note" contenteditable>Enter your note here</div>
-<input type=button onclick="saveNote()" value="Save Note">
-<div id="previousNotes"></div>
+</head>
+<body>
+<p>This page demonstrates the use of the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/section-sql.html">HTML 5 Client-side Database Storage API</a>. Any notes you create will be saved in a database on your local hard drive, and will be reloaded from that database the next time you visit this page. To try it out, use a <a href="http://nightly.webkit.org/">WebKit Nightly Build</a>.</p>
+<button onclick="newNote()">New Note</button>
</body>
-</html>
\ No newline at end of file
+</html>