summaryrefslogtreecommitdiff
path: root/js/spinningwheel.js
diff options
context:
space:
mode:
authorAlib <aliboba@free.fr>2010-03-15 17:56:22 +0100
committerAlib <aliboba@free.fr>2010-03-15 17:56:22 +0100
commit438912c0c4a4075a5cd74cf9689451c9df4bbe34 (patch)
tree334e03bbba01b002a34d3cbf81ee7e600ec7bff6 /js/spinningwheel.js
parenta8012963c23e7e5eaf487ee9e192ae5c4d4ccff2 (diff)
downloadistreamdev-438912c0c4a4075a5cd74cf9689451c9df4bbe34.tar.gz
istreamdev-438912c0c4a4075a5cd74cf9689451c9df4bbe34.tar.bz2
New Jquery branch. Initial development version for istreamdev 1.0
Diffstat (limited to 'js/spinningwheel.js')
-rw-r--r--js/spinningwheel.js492
1 files changed, 492 insertions, 0 deletions
diff --git a/js/spinningwheel.js b/js/spinningwheel.js
new file mode 100644
index 0000000..79f8ab6
--- /dev/null
+++ b/js/spinningwheel.js
@@ -0,0 +1,492 @@
+/**
+ *
+ * Find more about the Spinning Wheel function at
+ * http://cubiq.org/spinning-wheel-on-webkit-for-iphone-ipod-touch/11
+ *
+ * Copyright (c) 2009 Matteo Spinelli, http://cubiq.org/
+ * Released under MIT license
+ * http://cubiq.org/dropbox/mit-license.txt
+ *
+ * Version 1.4 - Last updated: 2009.07.09
+ *
+ */
+
+var SpinningWheel = {
+ cellHeight: 44,
+ friction: 0.003,
+ slotData: [],
+
+
+ /**
+ *
+ * Event handler
+ *
+ */
+
+ handleEvent: function (e) {
+ if (e.type == 'touchstart') {
+ this.lockScreen(e);
+ if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
+ this.tapDown(e);
+ } else if (e.currentTarget.id == 'sw-frame') {
+ this.scrollStart(e);
+ }
+ } else if (e.type == 'touchmove') {
+ this.lockScreen(e);
+
+ if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
+ this.tapCancel(e);
+ } else if (e.currentTarget.id == 'sw-frame') {
+ this.scrollMove(e);
+ }
+ } else if (e.type == 'touchend') {
+ if (e.currentTarget.id == 'sw-cancel' || e.currentTarget.id == 'sw-done') {
+ this.tapUp(e);
+ } else if (e.currentTarget.id == 'sw-frame') {
+ this.scrollEnd(e);
+ }
+ } else if (e.type == 'webkitTransitionEnd') {
+ if (e.target.id == 'sw-wrapper') {
+ this.destroy();
+ } else {
+ this.backWithinBoundaries(e);
+ }
+ } else if (e.type == 'orientationchange') {
+ this.onOrientationChange(e);
+ } else if (e.type == 'scroll') {
+ this.onScroll(e);
+ }
+ },
+
+
+ /**
+ *
+ * Global events
+ *
+ */
+
+ onOrientationChange: function (e) {
+ window.scrollTo(0, 0);
+ this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
+ this.calculateSlotsWidth();
+ },
+
+ onScroll: function (e) {
+ this.swWrapper.style.top = window.innerHeight + window.pageYOffset + 'px';
+ },
+
+ lockScreen: function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ },
+
+
+ /**
+ *
+ * Initialization
+ *
+ */
+
+ reset: function () {
+ this.slotEl = [];
+
+ this.activeSlot = null;
+
+ this.swWrapper = undefined;
+ this.swSlotWrapper = undefined;
+ this.swSlots = undefined;
+ this.swFrame = undefined;
+ },
+
+ calculateSlotsWidth: function () {
+ var div = this.swSlots.getElementsByTagName('div');
+ for (var i = 0; i < div.length; i += 1) {
+ this.slotEl[i].slotWidth = div[i].offsetWidth;
+ }
+ },
+
+ create: function () {
+ var i, l, out, ul, div;
+
+ this.reset(); // Initialize object variables
+
+ // Create the Spinning Wheel main wrapper
+ div = document.createElement('div');
+ div.id = 'sw-wrapper';
+ div.style.top = window.innerHeight + window.pageYOffset + 'px'; // Place the SW down the actual viewing screen
+ div.style.webkitTransitionProperty = '-webkit-transform';
+ 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>';
+
+ document.body.appendChild(div);
+
+ this.swWrapper = div; // The SW wrapper
+ this.swSlotWrapper = document.getElementById('sw-slots-wrapper'); // Slots visible area
+ this.swSlots = document.getElementById('sw-slots'); // Pseudo table element (inner wrapper)
+ this.swFrame = document.getElementById('sw-frame'); // The scrolling controller
+
+ // Create HTML slot elements
+ for (l = 0; l < this.slotData.length; l += 1) {
+ // Create the slot
+ ul = document.createElement('ul');
+ out = '';
+ for (i in this.slotData[l].values) {
+ out += '<li>' + this.slotData[l].values[i] + '<' + '/li>';
+ }
+ ul.innerHTML = out;
+
+ div = document.createElement('div'); // Create slot container
+ div.className = this.slotData[l].style; // Add styles to the container
+ div.appendChild(ul);
+
+ // Append the slot to the wrapper
+ this.swSlots.appendChild(div);
+
+ ul.slotPosition = l; // Save the slot position inside the wrapper
+ ul.slotYPosition = 0;
+ ul.slotWidth = 0;
+ ul.slotMaxScroll = this.swSlotWrapper.clientHeight - ul.clientHeight - 86;
+ ul.style.webkitTransitionTimingFunction = 'cubic-bezier(0, 0, 0.2, 1)'; // Add default transition
+
+ this.slotEl.push(ul); // Save the slot for later use
+
+ // Place the slot to its default position (if other than 0)
+ if (this.slotData[l].defaultValue) {
+ this.scrollToValue(l, this.slotData[l].defaultValue);
+ }
+ }
+
+ this.calculateSlotsWidth();
+
+ // Global events
+ document.addEventListener('touchstart', this, false); // Prevent page scrolling
+ document.addEventListener('touchmove', this, false); // Prevent page scrolling
+ window.addEventListener('orientationchange', this, true); // Optimize SW on orientation change
+ window.addEventListener('scroll', this, true); // Reposition SW on page scroll
+
+ // Cancel/Done buttons events
+ document.getElementById('sw-cancel').addEventListener('touchstart', this, false);
+ document.getElementById('sw-done').addEventListener('touchstart', this, false);
+
+ // Add scrolling to the slots
+ this.swFrame.addEventListener('touchstart', this, false);
+ },
+
+ open: function () {
+ this.create();
+
+ this.swWrapper.style.webkitTransitionTimingFunction = 'ease-out';
+ this.swWrapper.style.webkitTransitionDuration = '400ms';
+ this.swWrapper.style.webkitTransform = 'translate3d(0, -260px, 0)';
+ },
+
+
+ /**
+ *
+ * Unload
+ *
+ */
+
+ destroy: function () {
+ this.swWrapper.removeEventListener('webkitTransitionEnd', this, false);
+
+ this.swFrame.removeEventListener('touchstart', this, false);
+
+ document.getElementById('sw-cancel').removeEventListener('touchstart', this, false);
+ document.getElementById('sw-done').removeEventListener('touchstart', this, false);
+
+ document.removeEventListener('touchstart', this, false);
+ document.removeEventListener('touchmove', this, false);
+ window.removeEventListener('orientationchange', this, true);
+ window.removeEventListener('scroll', this, true);
+
+ this.slotData = [];
+ this.cancelAction = function () {
+ return false;
+ };
+
+ this.cancelDone = function () {
+ return true;
+ };
+
+ this.reset();
+
+ document.body.removeChild(document.getElementById('sw-wrapper'));
+ },
+
+ close: function () {
+ this.swWrapper.style.webkitTransitionTimingFunction = 'ease-in';
+ this.swWrapper.style.webkitTransitionDuration = '400ms';
+ this.swWrapper.style.webkitTransform = 'translate3d(0, 0, 0)';
+
+ this.swWrapper.addEventListener('webkitTransitionEnd', this, false);
+ },
+
+
+ /**
+ *
+ * Generic methods
+ *
+ */
+
+ addSlot: function (values, style, defaultValue) {
+ if (!style) {
+ style = '';
+ }
+
+ style = style.split(' ');
+
+ for (var i = 0; i < style.length; i += 1) {
+ style[i] = 'sw-' + style[i];
+ }
+
+ style = style.join(' ');
+
+ var obj = { 'values': values, 'style': style, 'defaultValue': defaultValue };
+ this.slotData.push(obj);
+ },
+
+ getSelectedValues: function () {
+ var index, count,
+ i, l,
+ keys = [], values = [];
+
+ for (i in this.slotEl) {
+ // Remove any residual animation
+ this.slotEl[i].removeEventListener('webkitTransitionEnd', this, false);
+ this.slotEl[i].style.webkitTransitionDuration = '0';
+
+ if (this.slotEl[i].slotYPosition > 0) {
+ this.setPosition(i, 0);
+ } else if (this.slotEl[i].slotYPosition < this.slotEl[i].slotMaxScroll) {
+ this.setPosition(i, this.slotEl[i].slotMaxScroll);
+ }
+
+ index = -Math.round(this.slotEl[i].slotYPosition / this.cellHeight);
+
+ count = 0;
+ for (l in this.slotData[i].values) {
+ if (count == index) {
+ keys.push(l);
+ values.push(this.slotData[i].values[l]);
+ break;
+ }
+
+ count += 1;
+ }
+ }
+
+ return { 'keys': keys, 'values': values };
+ },
+
+
+ /**
+ *
+ * Rolling slots
+ *
+ */
+
+ setPosition: function (slot, pos) {
+ this.slotEl[slot].slotYPosition = pos;
+ this.slotEl[slot].style.webkitTransform = 'translate3d(0, ' + pos + 'px, 0)';
+ },
+
+ scrollStart: function (e) {
+ // Find the clicked slot
+ var xPos = e.targetTouches[0].clientX - this.swSlots.offsetLeft; // Clicked position minus left offset (should be 11px)
+
+ // Find tapped slot
+ var slot = 0;
+ for (var i = 0; i < this.slotEl.length; i += 1) {
+ slot += this.slotEl[i].slotWidth;
+
+ if (xPos < slot) {
+ this.activeSlot = i;
+ break;
+ }
+ }
+
+ // If slot is readonly do nothing
+ if (this.slotData[this.activeSlot].style.match('readonly')) {
+ this.swFrame.removeEventListener('touchmove', this, false);
+ this.swFrame.removeEventListener('touchend', this, false);
+ return false;
+ }
+
+ this.slotEl[this.activeSlot].removeEventListener('webkitTransitionEnd', this, false); // Remove transition event (if any)
+ this.slotEl[this.activeSlot].style.webkitTransitionDuration = '0'; // Remove any residual transition
+
+ // Stop and hold slot position
+ var theTransform = window.getComputedStyle(this.slotEl[this.activeSlot]).webkitTransform;
+ theTransform = new WebKitCSSMatrix(theTransform).m42;
+ if (theTransform != this.slotEl[this.activeSlot].slotYPosition) {
+ this.setPosition(this.activeSlot, theTransform);
+ }
+
+ this.startY = e.targetTouches[0].clientY;
+ this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
+ this.scrollStartTime = e.timeStamp;
+
+ this.swFrame.addEventListener('touchmove', this, false);
+ this.swFrame.addEventListener('touchend', this, false);
+
+ return true;
+ },
+
+ scrollMove: function (e) {
+ var topDelta = e.targetTouches[0].clientY - this.startY;
+
+ if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
+ topDelta /= 2;
+ }
+
+ this.setPosition(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition + topDelta);
+ this.startY = e.targetTouches[0].clientY;
+
+ // Prevent slingshot effect
+ if (e.timeStamp - this.scrollStartTime > 80) {
+ this.scrollStartY = this.slotEl[this.activeSlot].slotYPosition;
+ this.scrollStartTime = e.timeStamp;
+ }
+ },
+
+ scrollEnd: function (e) {
+ this.swFrame.removeEventListener('touchmove', this, false);
+ this.swFrame.removeEventListener('touchend', this, false);
+
+ // If we are outside of the boundaries, let's go back to the sheepfold
+ if (this.slotEl[this.activeSlot].slotYPosition > 0 || this.slotEl[this.activeSlot].slotYPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
+ this.scrollTo(this.activeSlot, this.slotEl[this.activeSlot].slotYPosition > 0 ? 0 : this.slotEl[this.activeSlot].slotMaxScroll);
+ return false;
+ }
+
+ // Lame formula to calculate a fake deceleration
+ var scrollDistance = this.slotEl[this.activeSlot].slotYPosition - this.scrollStartY;
+
+ // The drag session was too short
+ if (scrollDistance < this.cellHeight / 1.5 && scrollDistance > -this.cellHeight / 1.5) {
+ if (this.slotEl[this.activeSlot].slotYPosition % this.cellHeight) {
+ this.scrollTo(this.activeSlot, Math.round(this.slotEl[this.activeSlot].slotYPosition / this.cellHeight) * this.cellHeight, '100ms');
+ }
+
+ return false;
+ }
+
+ var scrollDuration = e.timeStamp - this.scrollStartTime;
+
+ var newDuration = (2 * scrollDistance / scrollDuration) / this.friction;
+ var newScrollDistance = (this.friction / 2) * (newDuration * newDuration);
+
+ if (newDuration < 0) {
+ newDuration = -newDuration;
+ newScrollDistance = -newScrollDistance;
+ }
+
+ var newPosition = this.slotEl[this.activeSlot].slotYPosition + newScrollDistance;
+
+ if (newPosition > 0) {
+ // Prevent the slot to be dragged outside the visible area (top margin)
+ newPosition /= 2;
+ newDuration /= 3;
+
+ if (newPosition > this.swSlotWrapper.clientHeight / 4) {
+ newPosition = this.swSlotWrapper.clientHeight / 4;
+ }
+ } else if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll) {
+ // Prevent the slot to be dragged outside the visible area (bottom margin)
+ newPosition = (newPosition - this.slotEl[this.activeSlot].slotMaxScroll) / 2 + this.slotEl[this.activeSlot].slotMaxScroll;
+ newDuration /= 3;
+
+ if (newPosition < this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4) {
+ newPosition = this.slotEl[this.activeSlot].slotMaxScroll - this.swSlotWrapper.clientHeight / 4;
+ }
+ } else {
+ newPosition = Math.round(newPosition / this.cellHeight) * this.cellHeight;
+ }
+
+ this.scrollTo(this.activeSlot, Math.round(newPosition), Math.round(newDuration) + 'ms');
+
+ return true;
+ },
+
+ scrollTo: function (slotNum, dest, runtime) {
+ this.slotEl[slotNum].style.webkitTransitionDuration = runtime ? runtime : '100ms';
+ this.setPosition(slotNum, dest ? dest : 0);
+
+ // If we are outside of the boundaries go back to the sheepfold
+ if (this.slotEl[slotNum].slotYPosition > 0 || this.slotEl[slotNum].slotYPosition < this.slotEl[slotNum].slotMaxScroll) {
+ this.slotEl[slotNum].addEventListener('webkitTransitionEnd', this, false);
+ }
+ },
+
+ scrollToValue: function (slot, value) {
+ var yPos, count, i;
+
+ this.slotEl[slot].removeEventListener('webkitTransitionEnd', this, false);
+ this.slotEl[slot].style.webkitTransitionDuration = '0';
+
+ count = 0;
+ for (i in this.slotData[slot].values) {
+ if (i == value) {
+ yPos = count * this.cellHeight;
+ this.setPosition(slot, yPos);
+ break;
+ }
+
+ count -= 1;
+ }
+ },
+
+ backWithinBoundaries: function (e) {
+ e.target.removeEventListener('webkitTransitionEnd', this, false);
+
+ this.scrollTo(e.target.slotPosition, e.target.slotYPosition > 0 ? 0 : e.target.slotMaxScroll, '150ms');
+ return false;
+ },
+
+
+ /**
+ *
+ * Buttons
+ *
+ */
+
+ tapDown: function (e) {
+ e.currentTarget.addEventListener('touchmove', this, false);
+ e.currentTarget.addEventListener('touchend', this, false);
+ e.currentTarget.className = 'sw-pressed';
+ },
+
+ tapCancel: function (e) {
+ e.currentTarget.removeEventListener('touchmove', this, false);
+ e.currentTarget.removeEventListener('touchend', this, false);
+ e.currentTarget.className = '';
+ },
+
+ tapUp: function (e) {
+ this.tapCancel(e);
+
+ if (e.currentTarget.id == 'sw-cancel') {
+ this.cancelAction();
+ } else {
+ this.doneAction();
+ }
+
+ this.close();
+ },
+
+ setCancelAction: function (action) {
+ this.cancelAction = action;
+ },
+
+ setDoneAction: function (action) {
+ this.doneAction = action;
+ },
+
+ cancelAction: function () {
+ return false;
+ },
+
+ cancelDone: function () {
+ return true;
+ }
+}; \ No newline at end of file