/openshift/adei

To get this branch, use:
bzr branch http://darksoft.org/webbzr/openshift/adei

« back to all changes in this revision

Viewing changes to includes/sw/spinningwheel.js

  • Committer: Suren A. Chilingaryan
  • Date: 2011-01-26 02:48:39 UTC
  • mto: This revision was merged to the branch mainline in revision 212.
  • Revision ID: csa@dside.dyndns.org-20110126024839-nv6qp2ie9stmd2dn
Support of Appled devices by Toni Pirhonen

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * 
 
3
 * Find more about the Spinning Wheel function at
 
4
 * http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11
 
5
 *
 
6
 * Copyright (c) 2009 Matteo Spinelli, http://cubiq.org/
 
7
 * Released under MIT license
 
8
 * http://cubiq.org/dropbox/mit-license.txt
 
9
 * 
 
10
 * Version 1.4 - Last updated: 2009.07.09
 
11
 * 
 
12
 */
 
13
 
 
14
var SpinningWheel = {
 
15
        cellHeight: 44,
 
16
        friction: 0.003,
 
17
        slotData: [],
 
18
 
 
19
 
 
20
        /**
 
21
         *
 
22
         * Event handler
 
23
         *
 
24
         */
 
25
 
 
26
        handleEvent: function (e) {
 
27
                if (e.type == 'touchstart') {
 
28
                        this.lockScreen(e);
 
29
                        if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
 
30
                                this.tapDown(e);
 
31
                        } else if (e.currentTarget.id == 'sw-frame') {
 
32
                                this.scrollStart(e);
 
33
                        }
 
34
                } else if (e.type == 'touchmove') {
 
35
                        this.lockScreen(e);
 
36
                        
 
37
                        if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
 
38
                                this.tapCancel(e);
 
39
                        } else if (e.currentTarget.id == 'sw-frame') {
 
40
                                this.scrollMove(e);
 
41
                        }
 
42
                } else if (e.type == 'touchend') {
 
43
                        if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
 
44
                                this.tapUp(e);
 
45
                        } else if (e.currentTarget.id == 'sw-frame') {
 
46
                                this.scrollEnd(e);
 
47
                        }
 
48
                } else if (e.type == 'webkitTransitionEnd') {
 
49
                        if (e.target.id == 'sw-wrapper') {
 
50
                                this.destroy();
 
51
                        } else {
 
52
                                this.backWithinBoundaries(e);
 
53
                        }
 
54
                } else if (e.type == 'orientationchange') {
 
55
                        this.onOrientationChange(e);
 
56
                } else if (e.type == 'scroll') {
 
57
                        this.onScroll(e);
 
58
                }
 
59
        },
 
60
 
 
61
 
 
62
        /**
 
63
         *
 
64
         * Global events
 
65
         *
 
66
         */
 
67
 
 
68
        onOrientationChange: function (e) {
 
69
                window.scrollTo(0, 0);
 
70
                this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
 
71
                this.calculateSlotsWidth();
 
72
        },
 
73
        
 
74
        onScroll: function (e) {
 
75
                this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
 
76
        },
 
77
 
 
78
        lockScreen: function (e) {
 
79
                e.preventDefault();
 
80
                e.stopPropagation();
 
81
        },
 
82
 
 
83
 
 
84
        /**
 
85
         *
 
86
         * Initialization
 
87
         *
 
88
         */
 
89
 
 
90
        reset: function () {
 
91
                this.slotEl = [];
 
92
 
 
93
                this.activeSlot = null;
 
94
                
 
95
                this.swWrapper = undefined;
 
96
                this.swSlotWrapper = undefined;
 
97
                this.swSlots = undefined;
 
98
                this.swFrame = undefined;
 
99
        },
 
100
 
 
101
        calculateSlotsWidth: function () {
 
102
                var div = this.swSlots.getElementsByTagName('div');
 
103
                for (var i = 0; i < div.length; i += 1) {
 
104
                        this.slotEl[i].slotWidth = div[i].offsetWidth;
 
105
                }
 
106
        },
 
107
 
 
108
        create: function () {
 
109
                var i, l, out, ul, div;
 
110
 
 
111
                this.reset();   // Initialize object variables
 
112
 
 
113
                // Create the Spinning Wheel main wrapper
 
114
                div = document.createElement('div');
 
115
                div.id = 'sw-wrapper';
 
116
                div.style.top = window.innerHeight + window.pageYOffset + 'px';         // Place the SW down the actual viewing screen
 
117
                div.style.webkitTransitionProperty = '-webkit-transform';
 
118
                div.innerHTML = '<div id="sw-header"><div id="sw-cancel">Cancel</' + 'div><div id="sw-done">Done</' + 'div></' + 'div><div id="sw-slots-wrapper"><div id="sw-slots"></' + 'div></' + 'div><div id="sw-frame"></' + 'div>';
 
119
 
 
120
                document.body.appendChild(div);
 
121
 
 
122
                this.swWrapper = div;                                                                                                   // The SW wrapper
 
123
                this.swSlotWrapper = document.getElementById('sw-slots-wrapper');               // Slots visible area
 
124
                this.swSlots = document.getElementById('sw-slots');                                             // Pseudo table element (inner wrapper)
 
125
                this.swFrame = document.getElementById('sw-frame');                                             // The scrolling controller
 
126
 
 
127
                // Create HTML slot elements
 
128
                for (l = 0; l < this.slotData.length; l += 1) {
 
129
                        // Create the slot
 
130
                        ul = document.createElement('ul');
 
131
                        out = '';
 
132
                        for (i in this.slotData[l].values) {
 
133
                                out += '<li>' + this.slotData[l].values[i] + '<' + '/li>';
 
134
                        }
 
135
                        ul.innerHTML = out;
 
136
 
 
137
                        div = document.createElement('div');            // Create slot container
 
138
                        div.className = this.slotData[l].style;         // Add styles to the container
 
139
                        div.appendChild(ul);
 
140
        
 
141
                        // Append the slot to the wrapper
 
142
                        this.swSlots.appendChild(div);
 
143
                        
 
144
                        ul.slotPosition = l;                    // Save the slot position inside the wrapper
 
145
                        ul.slotYPosition = 0;
 
146
                        ul.slotWidth = 0;
 
147
                        ul.slotMaxScroll = this.swSlotWrapper.clientHeight - ul.clientHeight - 86;
 
148
                        ul.style.webkitTransitionTimingFunction = 'cubic-bezier(0, 0, 0.2, 1)';         // Add default transition
 
149
                        
 
150
                        this.slotEl.push(ul);                   // Save the slot for later use
 
151
                        
 
152
                        // Place the slot to its default position (if other than 0)
 
153
                        if (this.slotData[l].defaultValue) {
 
154
                                this.scrollToValue(l, this.slotData[l].defaultValue);   
 
155
                        }
 
156
                }
 
157
                
 
158
                this.calculateSlotsWidth();
 
159
                
 
160
                // Global events
 
161
                document.addEventListener('touchstart', this, false);                   // Prevent page scrolling
 
162
                document.addEventListener('touchmove', this, false);                    // Prevent page scrolling
 
163
                window.addEventListener('orientationchange', this, true);               // Optimize SW on orientation change
 
164
                window.addEventListener('scroll', this, true);                          // Reposition SW on page scroll
 
165
 
 
166
                // Cancel/Done buttons events
 
167
                document.getElementById('sw-cancel').addEventListener('touchstart', this, false);
 
168
                document.getElementById('sw-done').addEventListener('touchstart', this, false);
 
169
 
 
170
                // Add scrolling to the slots
 
171
                this.swFrame.addEventListener('touchstart', this, false);
 
172
        },
 
173
 
 
174
        open: function () {
 
175
                this.create();
 
176
 
 
177
                this.swWrapper.style.webkitTransitionTimingFunction = 'ease-out';
 
178
                this.swWrapper.style.webkitTransitionDuration = '400ms';
 
179
                this.swWrapper.style.webkitTransform = 'translate3d(0, -260px, 0)';
 
180
        },
 
181
        
 
182
        
 
183
        /**
 
184
         *
 
185
         * Unload
 
186
         *
 
187
         */
 
188
 
 
189
        destroy: function () {
 
190
                this.swWrapper.removeEventListener('webkitTransitionEnd', this, false);
 
191
 
 
192
                this.swFrame.removeEventListener('touchstart', this, false);
 
193
 
 
194
                document.getElementById('sw-cancel').removeEventListener('touchstart', this, false);
 
195
                document.getElementById('sw-done').removeEventListener('touchstart', this, false);
 
196
 
 
197
                document.removeEventListener('touchstart', this, false);
 
198
                document.removeEventListener('touchmove', this, false);
 
199
                window.removeEventListener('orientationchange', this, true);
 
200
                window.removeEventListener('scroll', this, true);
 
201
                
 
202
                this.slotData = [];
 
203
                this.cancelAction = function () {
 
204
                        return false;
 
205
                };
 
206
                
 
207
                this.cancelDone = function () {
 
208
                        return true;
 
209
                };
 
210
                
 
211
                this.reset();
 
212
                
 
213
                document.body.removeChild(document.getElementById('sw-wrapper'));
 
214
        },
 
215
        
 
216
        close: function () {
 
217
                this.swWrapper.style.webkitTransitionTimingFunction = 'ease-in';
 
218
                this.swWrapper.style.webkitTransitionDuration = '400ms';
 
219
                this.swWrapper.style.webkitTransform = 'translate3d(0, 0, 0)';
 
220
                
 
221
                this.swWrapper.addEventListener('webkitTransitionEnd', this, false);
 
222
        },
 
223
 
 
224
 
 
225
        /**
 
226
         *
 
227
         * Generic methods
 
228
         *
 
229
         */
 
230
 
 
231
        addSlot: function (values, style, defaultValue) {
 
232
                if (!style) {
 
233
                        style = '';
 
234
                }
 
235
                
 
236
                style = style.split(' ');
 
237
 
 
238
                for (var i = 0; i < style.length; i += 1) {
 
239
                        style[i] = 'sw-' + style[i];
 
240
                }
 
241
                
 
242
                style = style.join(' ');
 
243
 
 
244
                var obj = { 'values': values, 'style': style, 'defaultValue': defaultValue };
 
245
                this.slotData.push(obj);
 
246
        },
 
247
 
 
248
        getSelectedValues: function () {
 
249
                var index, count,
 
250
                    i, l,
 
251
                        keys = [], values = [];
 
252
 
 
253
                for (i in this.slotEl) {
 
254
                        // Remove any residual animation
 
255
                        this.slotEl[i].removeEventListener('webkitTransitionEnd', this, false);
 
256
                        this.slotEl[i].style.webkitTransitionDuration = '0';
 
257
 
 
258
                        if (this.slotEl[i].slotYPosition > 0) {
 
259
                                this.setPosition(i, 0);
 
260
                        } else if (this.slotEl[i].slotYPosition < this.slotEl[i].slotMaxScroll) {
 
261
                                this.setPosition(i, this.slotEl[i].slotMaxScroll);
 
262
                        }
 
263
 
 
264
                        index = -Math.round(this.slotEl[i].slotYPosition / this.cellHeight);
 
265
 
 
266
                        count = 0;
 
267
                        for (l in this.slotData[i].values) {
 
268
                                if (count == index) {
 
269
                                        keys.push(l);
 
270
                                        values.push(this.slotData[i].values[l]);
 
271
                                        break;
 
272
                                }
 
273
                                
 
274
                                count += 1;
 
275
                        }
 
276
                }
 
277
 
 
278
                return { 'keys': keys, 'values': values };
 
279
        },
 
280
 
 
281
 
 
282
        /**
 
283
         *
 
284
         * Rolling slots
 
285
         *
 
286
         */
 
287
 
 
288
        setPosition: function (slot, pos) {
 
289
                this.slotEl[slot].slotYPosition = pos;
 
290
                this.slotEl[slot].style.webkitTransform = 'translate3d(0, ' + pos + 'px, 0)';
 
291
        },
 
292
        
 
293
        scrollStart: function (e) {
 
294
                // Find the clicked slot
 
295
                var xPos = e.targetTouches[0].clientX - this.swSlots.offsetLeft;        // Clicked position minus left offset (should be 11px)
 
296
 
 
297
                // Find tapped slot
 
298
                var slot = 0;
 
299
                for (var i = 0; i < this.slotEl.length; i += 1) {
 
300
                        slot += this.slotEl[i].slotWidth;
 
301
                        
 
302
                        if (xPos < slot) {
 
303
                                this.activeSlot = i;
 
304
                                break;
 
305
                        }
 
306
                }
 
307
 
 
308
                // If slot is readonly do nothing
 
309
                if (this.slotData[this.activeSlot].style.match('readonly')) {
 
310
                        this.swFrame.removeEventListener('touchmove', this, false);
 
311
                        this.swFrame.removeEventListener('touchend', this, false);
 
312
                        return false;
 
313
                }
 
314
 
 
315
                this.slotEl[this.activeSlot].removeEventListener('webkitTransitionEnd', this, false);   // Remove transition event (if any)
 
316
                this.slotEl[this.activeSlot].style.webkitTransitionDuration = '0';              // Remove any residual transition
 
317
                
 
318
                // Stop and hold slot position
 
319
                var theTransform = window.getComputedStyle(this.slotEl[this.activeSlot]).webkitTransform;
 
320
                theTransform = new WebKitCSSMatrix(theTransform).m42;
 
321
                if (theTransform != this.slotEl[this.activeSlot].slotYPosition) {
 
322
                        this.setPosition(this.activeSlot, theTransform);
 
323
                }
 
324
                
 
325
                this.startY = e.targetTouches[0].clientY;
 
326
                this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
 
327
                this.scrollStartTime = e.timeStamp;
 
328
 
 
329
                this.swFrame.addEventListener('touchmove', this, false);
 
330
                this.swFrame.addEventListener('touchend', this, false);
 
331
                
 
332
                return true;
 
333
        },
 
334
 
 
335
        scrollMove: function (e) {
 
336
                var topDelta = e.targetTouches[0].clientY - this.startY;
 
337
 
 
338
                if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
 
339
                        topDelta /= 2;
 
340
                }
 
341
                
 
342
                this.setPosition(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition + topDelta);
 
343
                this.startY = e.targetTouches[0].clientY;
 
344
 
 
345
                // Prevent slingshot effect
 
346
                if (e.timeStamp - this.scrollStartTime > 80) {
 
347
                        this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
 
348
                        this.scrollStartTime = e.timeStamp;
 
349
                }
 
350
        },
 
351
        
 
352
        scrollEnd: function (e) {
 
353
                this.swFrame.removeEventListener('touchmove', this, false);
 
354
                this.swFrame.removeEventListener('touchend', this, false);
 
355
 
 
356
                // If we are outside of the boundaries, let's go back to the sheepfold
 
357
                if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
 
358
                        this.scrollTo(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition > 0 ? 0 : this.slotEl[this.activeSlot].slotMaxScroll);
 
359
                        return false;
 
360
                }
 
361
 
 
362
                // Lame formula to calculate a fake deceleration
 
363
                var scrollDistance = this.slotEl[this.activeSlot].slotYPosition - this.scrollStartY;
 
364
 
 
365
                // The drag session was too short
 
366
                if (scrollDistance < this.cellHeight / 1.5 && scrollDistance > -this.cellHeight / 1.5) {
 
367
                        if (this.slotEl[this.activeSlot].slotYPosition % this.cellHeight) {
 
368
                                this.scrollTo(this.activeSlot, Math.round(this.slotEl[this.activeSlot].slotYPosition / this.cellHeight) * this.cellHeight, '100ms');
 
369
                        }
 
370
 
 
371
                        return false;
 
372
                }
 
373
 
 
374
                var scrollDuration = e.timeStamp - this.scrollStartTime;
 
375
 
 
376
                var newDuration = (2 * scrollDistance / scrollDuration) / this.friction;
 
377
                var newScrollDistance = (this.friction / 2) * (newDuration * newDuration);
 
378
                
 
379
                if (newDuration < 0) {
 
380
                        newDuration = -newDuration;
 
381
                        newScrollDistance = -newScrollDistance;
 
382
                }
 
383
 
 
384
                var newPosition = this.slotEl[this.activeSlot].slotYPosition + newScrollDistance;
 
385
 
 
386
                if (newPosition > 0) {
 
387
                        // Prevent the slot to be dragged outside the visible area (top margin)
 
388
                        newPosition /= 2;
 
389
                        newDuration /= 3;
 
390
 
 
391
                        if (newPosition > this.swSlotWrapper.clientHeight / 4) {
 
392
                                newPosition = this.swSlotWrapper.clientHeight / 4;
 
393
                        }
 
394
                } else if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
 
395
                        // Prevent the slot to be dragged outside the visible area (bottom margin)
 
396
                        newPosition = (newPosition - this.slotEl[this.activeSlot].slotMaxScroll) / 2 + this.slotEl[this.activeSlot].slotMaxScroll;
 
397
                        newDuration /= 3;
 
398
                        
 
399
                        if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4) {
 
400
                                newPosition = this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4;
 
401
                        }
 
402
                } else {
 
403
                        newPosition = Math.round(newPosition / this.cellHeight) * this.cellHeight;
 
404
                }
 
405
 
 
406
                this.scrollTo(this.activeSlot, Math.round(newPosition), Math.round(newDuration) + 'ms');
 
407
 
 
408
                return true;
 
409
        },
 
410
 
 
411
        scrollTo: function (slotNum, dest, runtime) {
 
412
                this.slotEl[slotNum].style.webkitTransitionDuration = runtime ? runtime : '100ms';
 
413
                this.setPosition(slotNum, dest ? dest : 0);
 
414
 
 
415
                // If we are outside of the boundaries go back to the sheepfold
 
416
                if (this.slotEl[slotNum].slotYPosition > 0 || this.slotEl[slotNum].slotYPosition < this.slotEl[slotNum].slotMaxScroll) {
 
417
                        this.slotEl[slotNum].addEventListener('webkitTransitionEnd', this, false);
 
418
                }
 
419
        },
 
420
        
 
421
        scrollToValue: function (slot, value) {
 
422
                var yPos, count, i;
 
423
 
 
424
                this.slotEl[slot].removeEventListener('webkitTransitionEnd', this, false);
 
425
                this.slotEl[slot].style.webkitTransitionDuration = '0';
 
426
                
 
427
                count = 0;
 
428
                for (i in this.slotData[slot].values) {
 
429
                        if (i == value) {
 
430
                                yPos = count * this.cellHeight;
 
431
                                this.setPosition(slot, yPos);
 
432
                                break;
 
433
                        }
 
434
                        
 
435
                        count -= 1;
 
436
                }
 
437
        },
 
438
        
 
439
        backWithinBoundaries: function (e) {
 
440
                e.target.removeEventListener('webkitTransitionEnd', this, false);
 
441
 
 
442
                this.scrollTo(e.target.slotPosition, e.target.slotYPosition > 0 ? 0 : e.target.slotMaxScroll, '150ms');
 
443
                return false;
 
444
        },
 
445
 
 
446
 
 
447
        /**
 
448
         *
 
449
         * Buttons
 
450
         *
 
451
         */
 
452
 
 
453
        tapDown: function (e) {
 
454
                e.currentTarget.addEventListener('touchmove', this, false);
 
455
                e.currentTarget.addEventListener('touchend', this, false);
 
456
                e.currentTarget.className = 'sw-pressed';
 
457
        },
 
458
 
 
459
        tapCancel: function (e) {
 
460
                e.currentTarget.removeEventListener('touchmove', this, false);
 
461
                e.currentTarget.removeEventListener('touchend', this, false);
 
462
                e.currentTarget.className = '';
 
463
        },
 
464
        
 
465
        tapUp: function (e) {
 
466
                this.tapCancel(e);
 
467
 
 
468
                if (e.currentTarget.id == 'sw-cancel') {
 
469
                        this.cancelAction();
 
470
                } else {
 
471
                        this.doneAction();
 
472
                }
 
473
                
 
474
                this.close();
 
475
        },
 
476
 
 
477
        setCancelAction: function (action) {
 
478
                this.cancelAction = action;
 
479
        },
 
480
 
 
481
        setDoneAction: function (action) {
 
482
                this.doneAction = action;
 
483
        },
 
484
        
 
485
        cancelAction: function () {
 
486
                return false;
 
487
        },
 
488
 
 
489
        cancelDone: function () {
 
490
                return true;
 
491
        }
 
492
};
 
 
b'\\ No newline at end of file'