2008-03-28 Stephanie Lewis <slewis@apple.com>
[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 .note:hover .closebutton {
20     display: block;
21
22
23 .closebutton {
24     display: none;
25     background-image: url(deleteButton.png);
26     height: 30px;
27     position: absolute;
28     left: -15px;
29     top: -15px;
30     width: 30px;
31 }
32
33 .closebutton:active {
34     background-image: url(deleteButtonPressed.png);
35 }
36
37 .edit {
38     outline: none;
39 }
40
41 .timestamp {
42     position: absolute;
43     left: 0px;
44     right: 0px;
45     bottom: 0px;
46     font-size: 9px;
47     background-color: #db0;
48     color: white;
49     border-top: 1px solid #a80;
50     padding: 2px 4px;
51     text-align: right;
52 }
53 </style>
54 <script>
55 var db;
56
57 try {
58     if (window.openDatabase) {
59         db = openDatabase("NoteTest", "1.0", "HTML5 Database API example", 200000);
60         if (!db)
61             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");
62     } else
63         alert("Couldn't open the database.  Please try with a WebKit nightly with this feature enabled");
64 } catch(err) { }
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         var note = this;
178         db.transaction(function(tx)
179         {
180             tx.executeSql("DELETE FROM WebKitStickyNotes WHERE id = ?", [note.id]);
181         });
182         
183         var duration = event.shiftKey ? 2 : .25;
184         this.note.style.webkitTransition = '-webkit-transform ' + duration + 's ease-in, opacity ' + duration + 's ease-in';
185         this.note.offsetTop; // Force style recalc
186         this.note.style.webkitTransformOrigin = "0 0";
187         this.note.style.webkitTransform = 'skew(30deg, 0deg) scale(0)';
188         this.note.style.opacity = '0';
189
190         var self = this;
191         setTimeout(function() { document.body.removeChild(self.note) }, duration * 1000);
192     },
193
194     saveSoon: function()
195     {
196         this.cancelPendingSave();
197         var self = this;
198         this._saveTimer = setTimeout(function() { self.save() }, 200);
199     },
200
201     cancelPendingSave: function()
202     {
203         if (!("_saveTimer" in this))
204             return;
205         clearTimeout(this._saveTimer);
206         delete this._saveTimer;
207     },
208
209     save: function()
210     {
211         this.cancelPendingSave();
212
213         if ("dirty" in this) {
214             this.timestamp = new Date().getTime();
215             delete this.dirty;
216         }
217
218         var note = this;
219         db.transaction(function (tx)
220         {
221             tx.executeSql("UPDATE WebKitStickyNotes SET note = ?, timestamp = ?, left = ?, top = ?, zindex = ? WHERE id = ?", [note.text, note.timestamp, note.left, note.top, note.zIndex, note.id]);
222         });
223     },
224
225     saveAsNew: function()
226     {
227         this.timestamp = new Date().getTime();
228         
229         var note = this;
230         db.transaction(function (tx) 
231         {
232             tx.executeSql("INSERT INTO WebKitStickyNotes (id, note, timestamp, left, top, zindex) VALUES (?, ?, ?, ?, ?, ?)", [note.id, note.text, note.timestamp, note.left, note.top, note.zIndex]);
233         }); 
234     },
235
236     onMouseDown: function(e)
237     {
238         captured = this;
239         this.startX = e.clientX - this.note.offsetLeft;
240         this.startY = e.clientY - this.note.offsetTop;
241         this.zIndex = ++highestZ;
242
243         var self = this;
244         if (!("mouseMoveHandler" in this)) {
245             this.mouseMoveHandler = function(e) { return self.onMouseMove(e) }
246             this.mouseUpHandler = function(e) { return self.onMouseUp(e) }
247         }
248
249         document.addEventListener('mousemove', this.mouseMoveHandler, true);
250         document.addEventListener('mouseup', this.mouseUpHandler, true);
251
252         return false;
253     },
254
255     onMouseMove: function(e)
256     {
257         if (this != captured)
258             return true;
259
260         this.left = e.clientX - this.startX + 'px';
261         this.top = e.clientY - this.startY + 'px';
262         return false;
263     },
264
265     onMouseUp: function(e)
266     {
267         document.removeEventListener('mousemove', this.mouseMoveHandler, true);
268         document.removeEventListener('mouseup', this.mouseUpHandler, true);
269
270         this.save();
271         return false;
272     },
273
274     onNoteClick: function(e)
275     {
276         this.editField.focus();
277         getSelection().collapseToEnd();
278     },
279
280     onKeyUp: function()
281     {
282         this.dirty = true;
283         this.saveSoon();
284     },
285 }
286
287 function loaded()
288 {
289     db.transaction(function(tx) {
290         tx.executeSql("SELECT COUNT(*) FROM WebkitStickyNotes", [], function(result) {
291             loadNotes();
292         }, function(tx, error) {
293             tx.executeSql("CREATE TABLE WebKitStickyNotes (id REAL UNIQUE, note TEXT, timestamp REAL, left TEXT, top TEXT, zindex REAL)", [], function(result) { 
294                 loadNotes(); 
295             });
296         });
297     });
298 }
299
300 function loadNotes()
301 {
302     db.transaction(function(tx) {
303         tx.executeSql("SELECT id, note, timestamp, left, top, zindex FROM WebKitStickyNotes", [], function(tx, result) {
304             for (var i = 0; i < result.rows.length; ++i) {
305                 var row = result.rows.item(i);
306                 var note = new Note();
307                 note.id = row['id'];
308                 note.text = row['note'];
309                 note.timestamp = row['timestamp'];
310                 note.left = row['left'];
311                 note.top = row['top'];
312                 note.zIndex = row['zindex'];
313
314                 if (row['id'] > highestId)
315                     highestId = row['id'];
316                 if (row['zindex'] > highestZ)
317                     highestZ = row['zindex'];
318             }
319
320             if (!result.rows.length)
321                 newNote();
322         }, function(tx, error) {
323             alert('Failed to retrieve notes from database - ' + error.message);
324             return;
325         });
326     });
327 }
328
329 function modifiedString(date)
330 {
331     return 'Last Modified: ' + date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds();
332 }
333
334 function newNote()
335 {
336     var note = new Note();
337     note.id = ++highestId;
338     note.timestamp = new Date().getTime();
339     note.left = Math.round(Math.random() * 400) + 'px';
340     note.top = Math.round(Math.random() * 500) + 'px';
341     note.zIndex = ++highestZ;
342     note.saveAsNew();
343 }
344
345 addEventListener('load', loaded, false);
346 </script>
347 </head>
348 <body>
349 <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>
350 <button onclick="newNote()">New Note</button>
351 </body>
352 </html>