Rubber-stamped by Tim Hatcher.
[WebKit-https.git] / WebKitSite / misc / DatabaseExample.html
1 <!doctype html>
2 <html>
3 <head>
4 <title>WebKit HTML 5 SQL Storage Notes Demo</title>
5 <style>
6 body {
7     font-family: 'Lucida Grande', 'Helvetica', sans-serif;
8 }
9
10 .note {
11     background-color: rgb(255, 240, 70);
12     height: 250px;
13     padding: 10px;
14     position: absolute;
15     width: 200px;
16     -webkit-box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5);
17 }
18
19 /* FIXME: this empty rule works around a bug in our css handling */
20 .note:hover { }
21 .note:hover .closebutton {
22     display: block;
23
24
25 .closebutton {
26     display: none;
27     background-image: url(deleteButton.png);
28     height: 30px;
29     position: absolute;
30     left: -15px;
31     top: -15px;
32     width: 30px;
33 }
34
35 .closebutton:active {
36     background-image: url(deleteButtonPressed.png);
37 }
38
39 .edit {
40     outline: none;
41 }
42
43 .timestamp {
44     position: absolute;
45     left: 0px;
46     right: 0px;
47     bottom: 0px;
48     font-size: 9px;
49     background-color: #db0;
50     color: white;
51     border-top: 1px solid #a80;
52     padding: 2px 4px;
53     text-align: right;
54 }
55 </style>
56 <script>
57 var db;
58 try {
59     db = openDatabase("NoteTest", "1.0");
60 } catch(err) { }
61
62 if (!db) {
63     alert("Failed to open the database on disk.  Are you using a WebKit nightly with this feature enabled?");
64 }
65
66 var captured = null;
67 var highestZ = 0;
68 var highestId = 0;
69
70 function Note()
71 {
72     var self = this;
73
74     var note = document.createElement('div');
75     note.className = 'note';
76     note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
77     note.addEventListener('click', function() { return self.onNoteClick() }, false);
78     this.note = note;
79
80     var close = document.createElement('div');
81     close.className = 'closebutton';
82     close.addEventListener('click', function(event) { return self.close(event) }, false);
83     note.appendChild(close);
84
85     var edit = document.createElement('div');
86     edit.className = 'edit';
87     edit.setAttribute('contenteditable', true);
88     edit.addEventListener('keyup', function() { return self.onKeyUp() }, false);
89     note.appendChild(edit);
90     this.editField = edit;
91
92     var ts = document.createElement('div');
93     ts.className = 'timestamp';
94     ts.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
95     note.appendChild(ts);
96     this.lastModified = ts;
97
98     document.body.appendChild(note);
99     return this;
100 }
101
102 Note.prototype = {
103     get id()
104     {
105         if (!("_id" in this))
106             this._id = 0;
107         return this._id;
108     },
109
110     set id(x)
111     {
112         this._id = x;
113     },
114
115     get text()
116     {
117         return this.editField.innerHTML;
118     },
119
120     set text(x)
121     {
122         this.editField.innerHTML = x;
123     },
124
125     get timestamp()
126     {
127         if (!("_timestamp" in this))
128             this._timestamp = 0;
129         return this._timestamp;
130     },
131
132     set timestamp(x)
133     {
134         if (this._timestamp == x)
135             return;
136
137         this._timestamp = x;
138         var date = new Date();
139         date.setTime(parseFloat(x));
140         this.lastModified.textContent = modifiedString(date);
141     },
142
143     get left()
144     {
145         return this.note.style.left;
146     },
147
148     set left(x)
149     {
150         this.note.style.left = x;
151     },
152
153     get top()
154     {
155         return this.note.style.top;
156     },
157
158     set top(x)
159     {
160         this.note.style.top = x;
161     },
162
163     get zIndex()
164     {
165         return this.note.style.zIndex;
166     },
167
168     set zIndex(x)
169     {
170         this.note.style.zIndex = x;
171     },
172
173     close: function(event)
174     {
175         this.cancelPendingSave();
176
177         db.executeSql("DELETE FROM WebKitStickyNotes WHERE id = ?", [this.id], function(result) {});
178
179         var duration = event.shiftKey ? 2 : .25;
180         this.note.style.webkitTransition = '-webkit-transform ' + duration + 's ease-in, opacity ' + duration + 's ease-in';
181         this.note.offsetTop; // Force style recalc
182         this.note.style.webkitTransformOrigin = "0 0";
183         this.note.style.webkitTransform = 'skew(30deg, 0deg) scale(0)';
184         this.note.style.opacity = '0';
185
186         var self = this;
187         setTimeout(function() { document.body.removeChild(self.note) }, duration * 1000);
188     },
189
190     saveSoon: function()
191     {
192         this.cancelPendingSave();
193         var self = this;
194         this._saveTimer = setTimeout(function() { self.save() }, 200);
195     },
196
197     cancelPendingSave: function()
198     {
199         if (!("_saveTimer" in this))
200             return;
201         clearTimeout(this._saveTimer);
202         delete this._saveTimer;
203     },
204
205     save: function()
206     {
207         this.cancelPendingSave();
208
209         if ("dirty" in this) {
210             this.timestamp = new Date().getTime();
211             delete this.dirty;
212         }
213
214         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) {});
215     },
216
217     saveAsNew: function()
218     {
219         this.timestamp = new Date().getTime();
220         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) {});
221     },
222
223     onMouseDown: function(e)
224     {
225         captured = this;
226         this.startX = e.clientX - this.note.offsetLeft;
227         this.startY = e.clientY - this.note.offsetTop;
228         this.zIndex = ++highestZ;
229
230         var self = this;
231         if (!("mouseMoveHandler" in this)) {
232             this.mouseMoveHandler = function(e) { return self.onMouseMove(e) }
233             this.mouseUpHandler = function(e) { return self.onMouseUp(e) }
234         }
235
236         document.addEventListener('mousemove', this.mouseMoveHandler, true);
237         document.addEventListener('mouseup', this.mouseUpHandler, true);
238
239         return false;
240     },
241
242     onMouseMove: function(e)
243     {
244         if (this != captured)
245             return true;
246
247         this.left = e.clientX - this.startX + 'px';
248         this.top = e.clientY - this.startY + 'px';
249         return false;
250     },
251
252     onMouseUp: function(e)
253     {
254         document.removeEventListener('mousemove', this.mouseMoveHandler, true);
255         document.removeEventListener('mouseup', this.mouseUpHandler, true);
256
257         this.save();
258         return false;
259     },
260
261     onNoteClick: function(e)
262     {
263         this.editField.focus();
264         getSelection().collapseToEnd();
265     },
266
267     onKeyUp: function()
268     {
269         this.dirty = true;
270         this.saveSoon();
271     },
272 }
273
274 function loaded()
275 {
276     try {
277         db.executeSql("SELECT COUNT(*) FROM WebKitStickyNotes", [], function(result) { loadNotes(); });
278     } catch(err) {
279         db.executeSql("CREATE TABLE WebKitStickyNotes (id REAL UNIQUE, note TEXT, timestamp REAL, left TEXT, top TEXT, zindex REAL)", [], function(result) { loadNotes(); });
280     }
281 }
282
283 function loadNotes()
284 {
285     db.executeSql("SELECT id, note, timestamp, left, top, zindex FROM WebKitStickyNotes", [], function(result) {
286         if (result.errorCode) {
287             alert('Failed to retrieve notes from database');
288             return;
289         }
290
291         for (var i = 0; i < result.rows.length; ++i) {
292             var row = result.rows.item(i);
293             var note = new Note();
294             note.id = row['id'];
295             note.text = row['note'];
296             note.timestamp = row['timestamp'];
297             note.left = row['left'];
298             note.top = row['top'];
299             note.zIndex = row['zindex'];
300
301             if (row['id'] > highestId)
302                 highestId = row['id'];
303             if (row['zindex'] > highestZ)
304                 highestZ = row['zindex'];
305         }
306
307         if (!result.rows.length)
308             newNote();
309     });
310 }
311
312 function modifiedString(date)
313 {
314     return 'Last Modified: ' + date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
315 }
316
317 function newNote()
318 {
319     var note = new Note();
320     note.id = ++highestId;
321     note.timestamp = new Date().getTime();
322     note.left = Math.round(Math.random() * 400) + 'px';
323     note.top = Math.round(Math.random() * 500) + 'px';
324     note.zIndex = ++highestZ;
325     note.saveAsNew();
326 }
327
328 addEventListener('load', loaded, false);
329 </script>
330 </head>
331 <body>
332 <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>
333 <button onclick="newNote()">New Note</button>
334 </body>
335 </html>