| <!doctype html> |
| <html manifest="StickyNotes.manifest"> |
| <head> |
| <title>WebKit HTML 5 SQL Storage Notes Demo</title> |
| <style> |
| body { |
| font-family: 'Lucida Grande', 'Helvetica', sans-serif; |
| } |
| |
| .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); |
| } |
| |
| .note:hover .closebutton { |
| display: block; |
| } |
| |
| .closebutton { |
| display: none; |
| background-image: url(deleteButton.png); |
| height: 30px; |
| position: absolute; |
| left: -15px; |
| top: -15px; |
| width: 30px; |
| } |
| |
| .closebutton:active { |
| background-image: url(deleteButtonPressed.png); |
| } |
| |
| .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 = null; |
| |
| try { |
| if (window.openDatabase) { |
| db = openDatabase("NoteTest", "1.0", "HTML5 Database API example", 200000); |
| if (!db) |
| alert("Failed to open the database on disk. This is probably because the version was bad or there is not enough space left in this domain's quota"); |
| } else |
| alert("Couldn't open the database. Please try with a WebKit nightly with this feature enabled"); |
| } catch(err) { |
| db = null; |
| alert("Couldn't open the database. Please try with a WebKit nightly with this feature enabled"); |
| } |
| |
| var captured = null; |
| var highestZ = 0; |
| var highestId = 0; |
| |
| function Note() |
| { |
| 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(event) { return self.close(event) }, 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; |
| } |
| |
| 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(event) |
| { |
| this.cancelPendingSave(); |
| |
| var note = this; |
| db.transaction(function(tx) |
| { |
| tx.executeSql("DELETE FROM WebKitStickyNotes WHERE id = ?", [note.id]); |
| }); |
| |
| var duration = event.shiftKey ? 2 : .25; |
| this.note.style.webkitTransition = '-webkit-transform ' + duration + 's ease-in, opacity ' + duration + 's ease-in'; |
| this.note.offsetTop; // Force style recalc |
| this.note.style.webkitTransformOrigin = "0 0"; |
| this.note.style.webkitTransform = 'skew(30deg, 0deg) scale(0)'; |
| this.note.style.opacity = '0'; |
| |
| var self = this; |
| setTimeout(function() { document.body.removeChild(self.note) }, duration * 1000); |
| }, |
| |
| 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; |
| } |
| |
| var note = this; |
| db.transaction(function (tx) |
| { |
| tx.executeSql("UPDATE WebKitStickyNotes SET note = ?, timestamp = ?, left = ?, top = ?, zindex = ? WHERE id = ?", [note.text, note.timestamp, note.left, note.top, note.zIndex, note.id]); |
| }); |
| }, |
| |
| saveAsNew: function() |
| { |
| this.timestamp = new Date().getTime(); |
| |
| var note = this; |
| db.transaction(function (tx) |
| { |
| tx.executeSql("INSERT INTO WebKitStickyNotes (id, note, timestamp, left, top, zindex) VALUES (?, ?, ?, ?, ?, ?)", [note.id, note.text, note.timestamp, note.left, note.top, note.zIndex]); |
| }); |
| }, |
| |
| 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(); |
| }, |
| } |
| |
| function loaded() |
| { |
| db.transaction(function(tx) { |
| tx.executeSql("SELECT COUNT(*) FROM WebkitStickyNotes", [], function(result) { |
| loadNotes(); |
| }, function(tx, error) { |
| tx.executeSql("CREATE TABLE WebKitStickyNotes (id REAL UNIQUE, note TEXT, timestamp REAL, left TEXT, top TEXT, zindex REAL)", [], function(result) { |
| loadNotes(); |
| }); |
| }); |
| }); |
| } |
| |
| function loadNotes() |
| { |
| db.transaction(function(tx) { |
| tx.executeSql("SELECT id, note, timestamp, left, top, zindex FROM WebKitStickyNotes", [], function(tx, result) { |
| 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(tx, error) { |
| alert('Failed to retrieve notes from database - ' + error.message); |
| return; |
| }); |
| }); |
| } |
| |
| function modifiedString(date) |
| { |
| return 'Last Modified: ' + date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds(); |
| } |
| |
| function newNote() |
| { |
| 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(); |
| } |
| |
| if (db != null) |
| addEventListener('load', loaded, false); |
| </script> |
| </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 id="newNoteButton" onclick="newNote()">New Note</button> |
| <script> |
| document.getElementById("newNoteButton").disabled = !db; |
| </script> |
| </body> |
| </html> |