diff --git a/web/deck-of-cards b/web/deck-of-cards
new file mode 160000
index 0000000..8d3be56
--- /dev/null
+++ b/web/deck-of-cards
@@ -0,0 +1 @@
+Subproject commit 8d3be56c56950afe787b6217bc3993ebeab62e9c
diff --git a/web/img/c1.png b/web/img/c1.png
new file mode 100644
index 0000000..303204c
Binary files /dev/null and b/web/img/c1.png differ
diff --git a/web/img/c2.png b/web/img/c2.png
new file mode 100644
index 0000000..80b926a
Binary files /dev/null and b/web/img/c2.png differ
diff --git a/web/img/c3.png b/web/img/c3.png
new file mode 100644
index 0000000..d699735
Binary files /dev/null and b/web/img/c3.png differ
diff --git a/web/img/c4.png b/web/img/c4.png
new file mode 100644
index 0000000..31cf67d
Binary files /dev/null and b/web/img/c4.png differ
diff --git a/web/img/prehension.png b/web/img/prehension.png
new file mode 100644
index 0000000..3c6cd5d
Binary files /dev/null and b/web/img/prehension.png differ
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..c4a3258
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,53 @@
+
+
+
+
+ superject
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/scripts/drag.js b/web/scripts/drag.js
new file mode 100644
index 0000000..eed6eec
--- /dev/null
+++ b/web/scripts/drag.js
@@ -0,0 +1,34 @@
+
+// Make the DIV element draggable:
+dragElement(document.getElementById("card"));
+
+function dragElement(elmnt) {
+ var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
+ elmnt.onmousedown = dragMouseDown;
+ }
+
+ function dragMouseDown(e) {
+ e = e || window.event;
+ e.preventDefault();
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ document.onmouseup = closeDragElement;
+ document.onmousemove = elementDrag;
+ }
+
+ function elementDrag(e) {
+ e = e || window.event;
+ e.preventDefault();
+ pos1 = pos3 - e.clientX;
+ pos2 = pos4 - e.clientY;
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
+ elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
+ }
+
+ function closeDragElement() {
+ document.onmouseup = null;
+ document.onmousemove = null;
+ }
+}
diff --git a/web/scripts/gradientify.min.js b/web/scripts/gradientify.min.js
new file mode 100644
index 0000000..f0e92aa
--- /dev/null
+++ b/web/scripts/gradientify.min.js
@@ -0,0 +1,14 @@
+"use strict";function _slicedToArray(a,b){return _arrayWithHoles(a)||_iterableToArrayLimit(a,b)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function _iterableToArrayLimit(a,b){if(Symbol.iterator in Object(a)||"[object Arguments]"===Object.prototype.toString.call(a)){var c=[],d=!0,e=!1,f=void 0;try{for(var g,h=a[Symbol.iterator]();!(d=(g=h.next()).done)&&(c.push(g.value),!(b&&c.length===b));d=!0);}catch(a){e=!0,f=a}finally{try{d||null==h["return"]||h["return"]()}finally{if(e)throw f}}return c}}function _arrayWithHoles(a){if(Array.isArray(a))return a}function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function _defineProperties(a,b){for(var c,d=0;d100,n=void 0===this.states[this.activeState].loop||this.states[this.activeState].loop;(null===this.previousTimeStamp||o)&&(this.previousTimeStamp=t),this.progress=this.progress+(t-this.previousTimeStamp),e=(this.progress/this.activetransitionSpeed*100).toFixed(2),this.previousTimeStamp=t,this.refreshColorsAndPos(e),e<100?this.animation=requestAnimationFrame(this.animateColors.bind(this)):this.channelsIndex1&&(i-=1),i<1/6?t+6*(e-t)*i:i<.5?e:i<2/3?t+(e-t)*(2/3-i)*6:t}function r(t,e,i,s){var o,r,a,h,c;return 0===e?o=r=a=i:(h=i<.5?i*(1+e):i+e-i*e,c=2*i-h,o=n(c,h,t+1/3),r=n(c,h,t),a=n(c,h,t-1/3)),[Math.round(255*o),Math.round(255*r),Math.round(255*a),s]}var a,h={hexa:/^#(?:[0-9a-fA-F]{3}){1,2}$/,rgba:/^rgba\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3}), ?(.?\d{1,3})\)$/,rgb:/^rgb\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)$/,hsla:/^hsla\((\d{1,3}), ?(\d{1,3})%, ?(\d{1,3})%, ?(.?\d{1,3})\)$/,hsl:/^hsl\((\d{1,3}), ?(\d{1,3})%, ?(\d{1,3})%\)$/};e.exports=function(t){switch(s(t)){default:this.triggerError("colorType");case"hexa":return o(t);case"rgba":return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10),parseFloat(a[4])];case"rgb":return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10),1];case"hsla":return r(parseInt(a[1],10)/360,parseInt(a[2],10)/100,parseInt(a[3],10)/100,parseFloat(a[4]));case"hsl":return r(parseInt(a[1],10)/360,parseInt(a[2],10)/100,parseInt(a[3],10)/100,1)}}},{}],8:[function(t,e,i){"use strict";e.exports=function(){this.onResize("removeListeners"),this.onScroll("removeListeners"),this.clear()}},{}],9:[function(t,e,i){"use strict";e.exports=function(){function t(t,e){e=e||{bubbles:!1,cancelable:!1,detail:void 0};var i=document.createEvent("CustomEvent");return i.initCustomEvent(t,e.bubbles,e.cancelable,e.detail),i}"function"!=typeof window.CustomEvent&&(t.prototype=window.Event.prototype,window.CustomEvent=t)}},{}],10:[function(t,e,i){"use strict";e.exports=function(t){return"string"==typeof t?t:"object"==typeof t&&t.color?t.color:void this.triggerError("gradient.color")}},{}],11:[function(t,e,i){"use strict";e.exports=function(t,e){var i=0,s=[];for(i;i<4;i++)s.push(e[i]-t[i]);return s}},{}],12:[function(t,e,i){"use strict";e.exports=function(t,e){return"object"==typeof t&&t.pos?t.pos:parseFloat(e?(1/(this.gradientLength-1)*e).toFixed(2):0)}},{}],13:[function(t,e,i){"use strict";e.exports=function(t,e){return e-t}},{}],14:[function(t,e,i){"use strict";e.exports=function(){var t,e,i=[];for(t=0;t=128?"light":"dark"}},{}],19:[function(t,e,i){"use strict";e.exports=function(){var t=this.setDirection(),e=document.querySelector(this.elToSetClassOn).classList,i=0;for(this.context.clearRect(0,0,this.x1,this.y1),this.image&&this.context.drawImage(this.imageNode,this.imagePosition.x,this.imagePosition.y,this.imagePosition.width,this.imagePosition.height),i;iwindow.innerWidth||e.top>window.innerHeight),t.isCanvasInWindowView){if(!t.isPaused||t.firstScrollInit){if(t.image&&!t.isImgLoaded)return;t.isPausedBecauseNotInView=!1,t.play("isPlayedBecauseInView"),t.firstScrollInit=!1}}else!t.image&&t.firstScrollInit&&(t.refreshColorsAndPos(),t.firstScrollInit=!1),t.isPaused||t.isPausedBecauseNotInView||(t.isPausedBecauseNotInView=!0,t.pause("isPausedBecauseNotInView"))},this.scrollDebounceThreshold)}},{}],24:[function(t,e,i){"use strict";e.exports=function(t){var e="isPlayedBecauseInView"===t;e||(this.isPaused=!1),this.isCleared=!1,this.animating||(this.animation=requestAnimationFrame(this.animateColors.bind(this)),this.animating=!0)}},{}],25:[function(t,e,i){"use strict";e.exports=function(){function t(){function t(t){var i,s=e[t+"1"],o=e["x"===t?"imgOriginalWidth":"imgOriginalHeight"],n="x"===t?e.image.position[0]:e.image.position[1];switch(n){case"center":i=o>s?-(o-s)/2:(s-o)/2,e.imagePosition[t]=i,e.imagePosition["x"===t?"width":"height"]=o;break;case"top":e.imagePosition.y=0,e.imagePosition.height=o;break;case"bottom":e.imagePosition.y=s-o,e.imagePosition.height=o;break;case"right":e.imagePosition.x=s-o,e.imagePosition.width=o;break;case"left":e.imagePosition.x=0,e.imagePosition.width=o}if(e.image.stretchMode)switch(n="x"===t?e.image.stretchMode[0]:e.image.stretchMode[1]){case"none":break;case"stretch":e.imagePosition[t]=0,e.imagePosition["x"===t?"width":"height"]=s;break;case"stretch-if-bigger":if(os)break;e.imagePosition[t]=0,e.imagePosition["x"===t?"width":"height"]=s}}var i,s;for(i=0;i<2;i++)s=i?"y":"x",t(s)}var e=this;return this.imagePosition||(this.imagePosition={x:0,y:0,width:0,height:0}),this.image.blendingMode&&(this.context.globalCompositeOperation=this.image.blendingMode),this.imageNode?void t():(this.imageNode=new Image,this.imageNode.onerror=function(){throw new Error("Granim: The image source is invalid.")},this.imageNode.onload=function(){e.imgOriginalWidth=e.imageNode.width,e.imgOriginalHeight=e.imageNode.height,t(),e.refreshColorsAndPos(),e.isPausedWhenNotInView&&!e.isCanvasInWindowView||(e.animation=requestAnimationFrame(e.animateColors.bind(e))),e.isImgLoaded=!0},void(this.imageNode.src=this.image.source))}},{}],26:[function(t,e,i){"use strict";e.exports=function(t){var e,i,s,o,n=this;for(s=0;s=0&&(n.currentColors[s][o]=e);i=parseFloat((n.activeColorsPos[s]+n.activeColorsPosDiff[s]/100*t).toFixed(4)),i<=1&&i>=0&&(n.currentColorsPos[s]=i)}this.makeGradient()}},{}],27:[function(t,e,i){"use strict";e.exports=function(){var t,e,i,s,o=this;return this.channels[this.activeState]||(this.channels[this.activeState]=[]),void 0!==this.channels[this.activeState][this.channelsIndex]?(this.activeColors=this.channels[this.activeState][this.channelsIndex].colors,this.activeColorsDiff=this.channels[this.activeState][this.channelsIndex].colorsDiff,this.activeColorsPos=this.channels[this.activeState][this.channelsIndex].colorsPos,void(this.activeColorsPosDiff=this.channels[this.activeState][this.channelsIndex].colorsPosDiff)):(this.channels[this.activeState].push([{}]),this.channels[this.activeState][this.channelsIndex].colors=[],this.channels[this.activeState][this.channelsIndex].colorsDiff=[],this.channels[this.activeState][this.channelsIndex].colorsPos=[],this.channels[this.activeState][this.channelsIndex].colorsPosDiff=[],this.activeColors=[],this.activeColorsDiff=[],this.activeColorsPos=[],this.activeColorsPosDiff=[],this.states[this.activeState].gradients[this.channelsIndex].forEach(function(n,r){var a=o.getColorPos(n,r),n=o.getColor(n),h=o.convertColorToRgba(n),c=o.channels[o.activeState];c[o.channelsIndex].colors.push(h),o.activeColors.push(h),c[o.channelsIndex].colorsPos.push(a),o.activeColorsPos.push(a),o.isCurrentColorsSet||(o.currentColors.push(o.convertColorToRgba(n)),o.currentColorsPos.push(a)),o.channelsIndex===o.states[o.activeState].gradients.length-1?(t=o.getColorDiff(c[o.channelsIndex].colors[r],c[0].colors[r]),e=o.getColorPosDiff(c[o.channelsIndex].colorsPos[r],c[0].colorsPos[r])):(i=o.convertColorToRgba(o.getColor(o.states[o.activeState].gradients[o.channelsIndex+1][r])),s=o.getColorPos(o.states[o.activeState].gradients[o.channelsIndex+1][r],r),t=o.getColorDiff(c[o.channelsIndex].colors[r],i),e=o.getColorPosDiff(c[o.channelsIndex].colorsPos[r],s)),c[o.channelsIndex].colorsDiff.push(t),o.activeColorsDiff.push(t),c[o.channelsIndex].colorsPosDiff.push(e),o.activeColorsPosDiff.push(e)}),this.activetransitionSpeed=this.states[this.activeState].transitionSpeed||5e3,void(this.isCurrentColorsSet=!0))}},{}],28:[function(t,e,i){"use strict";function s(t,e){return t.indexOf("%")>-1?e/100*parseInt(t.split("%")[0],10):parseInt(t.split("px")[0],10)}e.exports=function(){var t=this.context;switch(this.direction){case"diagonal":return t.createLinearGradient(0,0,this.x1,this.y1);case"left-right":return t.createLinearGradient(0,0,this.x1,0);case"top-bottom":return t.createLinearGradient(this.x1/2,0,this.x1/2,this.y1);case"radial":return t.createRadialGradient(this.x1/2,this.y1/2,this.x1/2,this.x1/2,this.y1/2,0);case"custom":return t.createLinearGradient(s(this.customDirection.x0,this.x1),s(this.customDirection.y0,this.y1),s(this.customDirection.x1,this.x1),s(this.customDirection.y1,this.y1))}}},{}],29:[function(t,e,i){"use strict";e.exports=function(){this.getDimensions(),this.canvas.setAttribute("width",this.x1),this.canvas.setAttribute("height",this.y1),this.image&&this.prepareImage(),this.refreshColorsAndPos()}},{}],30:[function(t,e,i){"use strict";e.exports=function(t){var e="https://sarcadass.github.io/granim.js/api.html";throw new Error('Granim: Input error on "'+t+'" option.\nCheck the API '+e+".")}},{}],31:[function(t,e,i){"use strict";function s(t){for(var e,i=!0,s=0;i&&s0}),(!n||o.length>2||!o[0]||o[1]||!/^-?\d+\.?\d*$/.test(o[0]))&&(i=!1)}s++}return i}e.exports=function(t){var e=["left","center","right"],i=["top","center","bottom"],o=["none","stretch","stretch-if-smaller","stretch-if-bigger"],n=["multiply","screen","normal","overlay","darken","lighten","lighter","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],r=["diagonal","left-right","top-bottom","radial","custom"];switch(t){case"image":Array.isArray(this.image.position)&&2===this.image.position.length&&e.indexOf(this.image.position[0])!==-1&&i.indexOf(this.image.position[1])!==-1||this.triggerError("image.position"),this.image.stretchMode&&(Array.isArray(this.image.stretchMode)&&2===this.image.stretchMode.length&&o.indexOf(this.image.stretchMode[0])!==-1&&o.indexOf(this.image.stretchMode[1])!==-1||this.triggerError("image.stretchMode"));break;case"blendingMode":n.indexOf(this.image.blendingMode)===-1&&(this.clear(),this.triggerError("blendingMode"));break;case"direction":r.indexOf(this.direction)===-1?this.triggerError("direction"):"custom"===this.direction&&(s([this.customDirection.x0,this.customDirection.x1,this.customDirection.y0,this.customDirection.y1])||this.triggerError("customDirection"))}}},{}],32:[function(t,e,i){window.Granim=t("./lib/Granim.js")},{"./lib/Granim.js":1}]},{},[32]);
\ No newline at end of file
diff --git a/web/shuffle.html b/web/shuffle.html
new file mode 100644
index 0000000..448de99
--- /dev/null
+++ b/web/shuffle.html
@@ -0,0 +1,98 @@
+
+
+
+
+ superject
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/shuffle/deck.js b/web/shuffle/deck.js
new file mode 100644
index 0000000..45c3548
--- /dev/null
+++ b/web/shuffle/deck.js
@@ -0,0 +1,980 @@
+'use strict';
+
+var Deck = (function () {
+ 'use strict';
+
+ var ticking;
+ var animations = [];
+
+ function animationFrames(delay, duration) {
+ var now = Date.now();
+
+ // calculate animation start/end times
+ var start = now + delay;
+ var end = start + duration;
+
+ var animation = {
+ start: start,
+ end: end
+ };
+
+ // add animation
+ animations.push(animation);
+
+ if (!ticking) {
+ // start ticking
+ ticking = true;
+ requestAnimationFrame(tick);
+ }
+ var self = {
+ start: function start(cb) {
+ // add start callback (just one)
+ animation.startcb = cb;
+ return self;
+ },
+ progress: function progress(cb) {
+ // add progress callback (just one)
+ animation.progresscb = cb;
+ return self;
+ },
+ end: function end(cb) {
+ // add end callback (just one)
+ animation.endcb = cb;
+ return self;
+ }
+ };
+ return self;
+ }
+
+ function tick() {
+ var now = Date.now();
+
+ if (!animations.length) {
+ // stop ticking
+ ticking = false;
+ return;
+ }
+
+ for (var i = 0, animation; i < animations.length; i++) {
+ animation = animations[i];
+ if (now < animation.start) {
+ // animation not yet started..
+ continue;
+ }
+ if (!animation.started) {
+ // animation starts
+ animation.started = true;
+ animation.startcb && animation.startcb();
+ }
+ // animation progress
+ var t = (now - animation.start) / (animation.end - animation.start);
+ animation.progresscb && animation.progresscb(t < 1 ? t : 1);
+ if (now > animation.end) {
+ // animation ended
+ animation.endcb && animation.endcb();
+ animations.splice(i--, 1);
+ continue;
+ }
+ }
+ requestAnimationFrame(tick);
+ }
+
+ // fallback
+ window.requestAnimationFrame || (window.requestAnimationFrame = function (cb) {
+ setTimeout(cb, 0);
+ });
+
+ var style = document.createElement('p').style;
+ var memoized = {};
+
+ function prefix(param) {
+ if (typeof memoized[param] !== 'undefined') {
+ return memoized[param];
+ }
+
+ if (typeof style[param] !== 'undefined') {
+ memoized[param] = param;
+ return param;
+ }
+
+ var camelCase = param[0].toUpperCase() + param.slice(1);
+ var prefixes = ['webkit', 'moz', 'Moz', 'ms', 'o'];
+ var test;
+
+ for (var i = 0, len = prefixes.length; i < len; i++) {
+ test = prefixes[i] + camelCase;
+ if (typeof style[test] !== 'undefined') {
+ memoized[param] = test;
+ return test;
+ }
+ }
+ }
+
+ var has3d;
+
+ function translate(a, b, c) {
+ typeof has3d !== 'undefined' || (has3d = check3d());
+
+ c = c || 0;
+
+ if (has3d) {
+ return 'translate3d(' + a + ', ' + b + ', ' + c + ')';
+ } else {
+ return 'translate(' + a + ', ' + b + ')';
+ }
+ }
+
+ function check3d() {
+ // I admit, this line is stealed from the great Velocity.js!
+ // http://julian.com/research/velocity/
+ var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
+
+ if (!isMobile) {
+ return false;
+ }
+
+ var transform = prefix('transform');
+ var $p = document.createElement('p');
+
+ document.body.appendChild($p);
+ $p.style[transform] = 'translate3d(1px,1px,1px)';
+
+ has3d = $p.style[transform];
+ has3d = has3d != null && has3d.length && has3d !== 'none';
+
+ document.body.removeChild($p);
+
+ return has3d;
+ }
+
+ function createElement(type) {
+ return document.createElement(type);
+ }
+
+ var maxZ = 52;
+
+ function _card(i) {
+ var transform = prefix('transform');
+
+ // calculate rank/suit, etc..
+ var rank = i % 13 + 1;
+ var suit = i / 13 | 0;
+ var z = (52 - i) / 4;
+
+ // create elements
+ var $el = createElement('div');
+ var $face = createElement('div');
+ var $back = createElement('div');
+
+ // states
+ var isDraggable = false;
+ var isFlippable = false;
+
+ // self = card
+ var self = { i: i, rank: rank, suit: suit, pos: i, $el: $el, mount: mount, unmount: unmount, setSide: setSide };
+
+ var modules = Deck.modules;
+ var module;
+
+ // add classes
+ $face.classList.add('face');
+ $back.classList.add('back');
+
+ // add default transform
+ $el.style[transform] = translate(-z + 'px', -z + 'px');
+
+ // add default values
+ self.x = -z;
+ self.y = -z;
+ self.z = z;
+ self.rot = 0;
+
+ // set default side to back
+ self.setSide('back');
+
+ // add drag/click listeners
+ addListener($el, 'mousedown', onMousedown);
+ addListener($el, 'touchstart', onMousedown);
+
+ // load modules
+ for (module in modules) {
+ addModule(modules[module]);
+ }
+
+ self.animateTo = function (params) {
+ var delay = params.delay;
+ var duration = params.duration;
+ var _params$x = params.x;
+ var x = _params$x === undefined ? self.x : _params$x;
+ var _params$y = params.y;
+ var y = _params$y === undefined ? self.y : _params$y;
+ var _params$rot = params.rot;
+ var rot = _params$rot === undefined ? self.rot : _params$rot;
+ var ease$$ = params.ease;
+ var onStart = params.onStart;
+ var onProgress = params.onProgress;
+ var onComplete = params.onComplete;
+
+ var startX, startY, startRot;
+ var diffX, diffY, diffRot;
+
+ animationFrames(delay, duration).start(function () {
+ startX = self.x || 0;
+ startY = self.y || 0;
+ startRot = self.rot || 0;
+ onStart && onStart();
+ }).progress(function (t) {
+ var et = ease[ease$$ || 'cubicInOut'](t);
+
+ diffX = x - startX;
+ diffY = y - startY;
+ diffRot = rot - startRot;
+
+ onProgress && onProgress(t, et);
+
+ self.x = startX + diffX * et;
+ self.y = startY + diffY * et;
+ self.rot = startRot + diffRot * et;
+
+ $el.style[transform] = translate(self.x + 'px', self.y + 'px') + (diffRot ? 'rotate(' + self.rot + 'deg)' : '');
+ }).end(function () {
+ onComplete && onComplete();
+ });
+ };
+
+ // set rank & suit
+ self.setRankSuit = function (rank, suit) {
+ var suitName = SuitName(suit);
+ $el.setAttribute('class', 'card ' + suitName + ' rank' + rank);
+ };
+
+ self.setRankSuit(rank, suit);
+
+ self.enableDragging = function () {
+ // this activates dragging
+ if (isDraggable) {
+ // already is draggable, do nothing
+ return;
+ }
+ isDraggable = true;
+ $el.style.cursor = 'move';
+ };
+
+ self.enableFlipping = function () {
+ if (isFlippable) {
+ // already is flippable, do nothing
+ return;
+ }
+ isFlippable = true;
+ };
+
+ self.disableFlipping = function () {
+ if (!isFlippable) {
+ // already disabled flipping, do nothing
+ return;
+ }
+ isFlippable = false;
+ };
+
+ self.disableDragging = function () {
+ if (!isDraggable) {
+ // already disabled dragging, do nothing
+ return;
+ }
+ isDraggable = false;
+ $el.style.cursor = '';
+ };
+
+ return self;
+
+ function addModule(module) {
+ // add card module
+ module.card && module.card(self);
+ }
+
+ function onMousedown(e) {
+ var startPos = {};
+ var pos = {};
+ var starttime = Date.now();
+
+ e.preventDefault();
+
+ // get start coordinates and start listening window events
+ if (e.type === 'mousedown') {
+ startPos.x = pos.x = e.clientX;
+ startPos.y = pos.y = e.clientY;
+ addListener(window, 'mousemove', onMousemove);
+ addListener(window, 'mouseup', onMouseup);
+ } else {
+ startPos.x = pos.x = e.touches[0].clientX;
+ startPos.y = pos.y = e.touches[0].clientY;
+ addListener(window, 'touchmove', onMousemove);
+ addListener(window, 'touchend', onMouseup);
+ }
+
+ if (!isDraggable) {
+ // is not draggable, do nothing
+ return;
+ }
+
+ // move card
+ $el.style[transform] = translate(self.x + 'px', self.y + 'px') + (self.rot ? ' rotate(' + self.rot + 'deg)' : '');
+ $el.style.zIndex = maxZ++;
+
+ function onMousemove(e) {
+ if (!isDraggable) {
+ // is not draggable, do nothing
+ return;
+ }
+ if (e.type === 'mousemove') {
+ pos.x = e.clientX;
+ pos.y = e.clientY;
+ } else {
+ pos.x = e.touches[0].clientX;
+ pos.y = e.touches[0].clientY;
+ }
+
+ // move card
+ $el.style[transform] = translate(Math.round(self.x + pos.x - startPos.x) + 'px', Math.round(self.y + pos.y - startPos.y) + 'px') + (self.rot ? ' rotate(' + self.rot + 'deg)' : '');
+ }
+
+ function onMouseup(e) {
+ if (isFlippable && Date.now() - starttime < 200) {
+ // flip sides
+ self.setSide(self.side === 'front' ? 'back' : 'front');
+ }
+ if (e.type === 'mouseup') {
+ removeListener(window, 'mousemove', onMousemove);
+ removeListener(window, 'mouseup', onMouseup);
+ } else {
+ removeListener(window, 'touchmove', onMousemove);
+ removeListener(window, 'touchend', onMouseup);
+ }
+ if (!isDraggable) {
+ // is not draggable, do nothing
+ return;
+ }
+
+ // set current position
+ self.x = self.x + pos.x - startPos.x;
+ self.y = self.y + pos.y - startPos.y;
+ }
+ }
+
+ function mount(target) {
+ // mount card to target (deck)
+ target.appendChild($el);
+
+ self.$root = target;
+ }
+
+ function unmount() {
+ // unmount from root (deck)
+ self.$root && self.$root.removeChild($el);
+ self.$root = null;
+ }
+
+ function setSide(newSide) {
+ // flip sides
+ if (newSide === 'front') {
+ if (self.side === 'back') {
+ $el.removeChild($back);
+ }
+ self.side = 'front';
+ $el.appendChild($face);
+ self.setRankSuit(self.rank, self.suit);
+ } else {
+ if (self.side === 'front') {
+ $el.removeChild($face);
+ }
+ self.side = 'back';
+ $el.appendChild($back);
+ $el.setAttribute('class', 'card');
+ }
+ }
+ }
+
+ function SuitName(suit) {
+ // return suit name from suit value
+ return suit === 0 ? 'spades' : suit === 1 ? 'hearts' : suit === 2 ? 'clubs' : suit === 3 ? 'diamonds' : 'joker';
+ }
+
+ function addListener(target, name, listener) {
+ target.addEventListener(name, listener);
+ }
+
+ function removeListener(target, name, listener) {
+ target.removeEventListener(name, listener);
+ }
+
+ var ease = {
+ linear: function linear(t) {
+ return t;
+ },
+ quadIn: function quadIn(t) {
+ return t * t;
+ },
+ quadOut: function quadOut(t) {
+ return t * (2 - t);
+ },
+ quadInOut: function quadInOut(t) {
+ return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
+ },
+ cubicIn: function cubicIn(t) {
+ return t * t * t;
+ },
+ cubicOut: function cubicOut(t) {
+ return --t * t * t + 1;
+ },
+ cubicInOut: function cubicInOut(t) {
+ return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
+ },
+ quartIn: function quartIn(t) {
+ return t * t * t * t;
+ },
+ quartOut: function quartOut(t) {
+ return 1 - --t * t * t * t;
+ },
+ quartInOut: function quartInOut(t) {
+ return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
+ },
+ quintIn: function quintIn(t) {
+ return t * t * t * t * t;
+ },
+ quintOut: function quintOut(t) {
+ return 1 + --t * t * t * t * t;
+ },
+ quintInOut: function quintInOut(t) {
+ return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
+ }
+ };
+
+ var flip = {
+ deck: function deck(_deck) {
+ _deck.flip = _deck.queued(flip);
+
+ function flip(next, side) {
+ var flipped = _deck.cards.filter(function (card) {
+ return card.side === 'front';
+ }).length / _deck.cards.length;
+
+ _deck.cards.forEach(function (card, i) {
+ card.setSide(side ? side : flipped > 0.5 ? 'back' : 'front');
+ });
+ next();
+ }
+ }
+ };
+
+ var sort = {
+ deck: function deck(_deck2) {
+ _deck2.sort = _deck2.queued(sort);
+
+ function sort(next, reverse) {
+ var cards = _deck2.cards;
+
+ cards.sort(function (a, b) {
+ if (reverse) {
+ return a.i - b.i;
+ } else {
+ return b.i - a.i;
+ }
+ });
+
+ cards.forEach(function (card, i) {
+ card.sort(i, cards.length, function (i) {
+ if (i === cards.length - 1) {
+ next();
+ }
+ }, reverse);
+ });
+ }
+ },
+ card: function card(_card2) {
+ var $el = _card2.$el;
+
+ _card2.sort = function (i, len, cb, reverse) {
+ var z = i / 4;
+ var delay = i * 10;
+
+ _card2.animateTo({
+ delay: delay,
+ duration: 400,
+
+ x: -z,
+ y: -150,
+ rot: 0,
+
+ onComplete: function onComplete() {
+ $el.style.zIndex = i;
+ }
+ });
+
+ _card2.animateTo({
+ delay: delay + 500,
+ duration: 400,
+
+ x: -z,
+ y: -z,
+ rot: 0,
+
+ onComplete: function onComplete() {
+ cb(i);
+ }
+ });
+ };
+ }
+ };
+
+ function plusminus(value) {
+ var plusminus = Math.round(Math.random()) ? -1 : 1;
+
+ return plusminus * value;
+ }
+
+ function fisherYates(array) {
+ var rnd, temp;
+
+ for (var i = array.length - 1; i; i--) {
+ rnd = Math.random() * i | 0;
+ temp = array[i];
+ array[i] = array[rnd];
+ array[rnd] = temp;
+ }
+
+ return array;
+ }
+
+ function fontSize() {
+ return window.getComputedStyle(document.body).getPropertyValue('font-size').slice(0, -2);
+ }
+
+ var ____fontSize;
+
+ var shuffle = {
+ deck: function deck(_deck3) {
+ _deck3.shuffle = _deck3.queued(shuffle);
+
+ function shuffle(next) {
+ var cards = _deck3.cards;
+
+ ____fontSize = fontSize();
+
+ fisherYates(cards);
+
+ cards.forEach(function (card, i) {
+ card.pos = i;
+
+ card.shuffle(function (i) {
+ if (i === cards.length - 1) {
+ next();
+ }
+ });
+ });
+ return;
+ }
+ },
+
+ card: function card(_card3) {
+ var $el = _card3.$el;
+
+ _card3.shuffle = function (cb) {
+ var i = _card3.pos;
+ var z = i / 4;
+ var delay = i * 2;
+
+ _card3.animateTo({
+ delay: delay,
+ duration: 200,
+
+ x: plusminus(Math.random() * 40 + 20) * ____fontSize / 16,
+ y: -z,
+ rot: 0
+ });
+ _card3.animateTo({
+ delay: 200 + delay,
+ duration: 200,
+
+ x: -z,
+ y: -z,
+ rot: 0,
+
+ onStart: function onStart() {
+ $el.style.zIndex = i;
+ },
+
+ onComplete: function onComplete() {
+ cb(i);
+ }
+ });
+ };
+ }
+ };
+
+ var __fontSize;
+
+ var poker = {
+ deck: function deck(_deck4) {
+ _deck4.poker = _deck4.queued(poker);
+
+ function poker(next) {
+ var cards = _deck4.cards;
+ var len = cards.length;
+
+ __fontSize = fontSize();
+
+ cards.slice(-5).reverse().forEach(function (card, i) {
+ card.poker(i, len, function (i) {
+ card.setSide('front');
+ if (i === 4) {
+ next();
+ }
+ });
+ });
+ }
+ },
+ card: function card(_card4) {
+ var $el = _card4.$el;
+
+ _card4.poker = function (i, len, cb) {
+ var delay = i * 250;
+
+ _card4.animateTo({
+ delay: delay,
+ duration: 250,
+
+ x: Math.round((i - 2.05) * 70 * __fontSize / 16),
+ y: Math.round(-110 * __fontSize / 16),
+ rot: 0,
+
+ onStart: function onStart() {
+ $el.style.zIndex = len - 1 + i;
+ },
+ onComplete: function onComplete() {
+ cb(i);
+ }
+ });
+ };
+ }
+ };
+
+ var intro = {
+ deck: function deck(_deck5) {
+ _deck5.intro = _deck5.queued(intro);
+
+ function intro(next) {
+ var cards = _deck5.cards;
+
+ cards.forEach(function (card, i) {
+ card.setSide('front');
+ card.intro(i, function (i) {
+ animationFrames(250, 0).start(function () {
+ card.setSide('back');
+ });
+ if (i === cards.length - 1) {
+ next();
+ }
+ });
+ });
+ }
+ },
+ card: function card(_card5) {
+ var transform = prefix('transform');
+
+ var $el = _card5.$el;
+
+ _card5.intro = function (i, cb) {
+ var delay = 500 + i * 10;
+ var z = i / 4;
+
+ $el.style[transform] = translate(-z + 'px', '-250px');
+ $el.style.opacity = 0;
+
+ _card5.x = -z;
+ _card5.y = -250 - z;
+ _card5.rot = 0;
+
+ _card5.animateTo({
+ delay: delay,
+ duration: 1000,
+
+ x: -z,
+ y: -z,
+
+ onStart: function onStart() {
+ $el.style.zIndex = i;
+ },
+ onProgress: function onProgress(t) {
+ $el.style.opacity = t;
+ },
+ onComplete: function onComplete() {
+ $el.style.opacity = '';
+ cb && cb(i);
+ }
+ });
+ };
+ }
+ };
+
+ var _fontSize;
+
+ var fan = {
+ deck: function deck(_deck6) {
+ _deck6.fan = _deck6.queued(fan);
+
+ function fan(next) {
+ var cards = _deck6.cards;
+ var len = cards.length;
+
+ _fontSize = fontSize();
+
+ cards.forEach(function (card, i) {
+ card.fan(i, len, function (i) {
+ if (i === cards.length - 1) {
+ next();
+ }
+ });
+ });
+ }
+ },
+ card: function card(_card6) {
+ var $el = _card6.$el;
+
+ _card6.fan = function (i, len, cb) {
+ var z = i / 4;
+ var delay = i * 10;
+ var rot = i / (len - 1) * 260 - 130;
+
+ _card6.animateTo({
+ delay: delay,
+ duration: 300,
+
+ x: -z,
+ y: -z,
+ rot: 0
+ });
+ _card6.animateTo({
+ delay: 300 + delay,
+ duration: 300,
+
+ x: Math.cos(deg2rad(rot - 90)) * 55 * _fontSize / 16,
+ y: Math.sin(deg2rad(rot - 90)) * 55 * _fontSize / 16,
+ rot: rot,
+
+ onStart: function onStart() {
+ $el.style.zIndex = i;
+ },
+
+ onComplete: function onComplete() {
+ cb(i);
+ }
+ });
+ };
+ }
+ };
+
+ function deg2rad(degrees) {
+ return degrees * Math.PI / 180;
+ }
+
+ var ___fontSize;
+
+ var bysuit = {
+ deck: function deck(_deck7) {
+ _deck7.bysuit = _deck7.queued(bysuit);
+
+ function bysuit(next) {
+ var cards = _deck7.cards;
+
+ ___fontSize = fontSize();
+
+ cards.forEach(function (card) {
+ card.bysuit(function (i) {
+ if (i === cards.length - 1) {
+ next();
+ }
+ });
+ });
+ }
+ },
+ card: function card(_card7) {
+ var rank = _card7.rank;
+ var suit = _card7.suit;
+
+ _card7.bysuit = function (cb) {
+ var i = _card7.i;
+ var delay = i * 10;
+
+ _card7.animateTo({
+ delay: delay,
+ duration: 400,
+
+ x: -Math.round((6.75 - rank) * 8 * ___fontSize / 16),
+ y: -Math.round((1.5 - suit) * 92 * ___fontSize / 16),
+ rot: 0,
+
+ onComplete: function onComplete() {
+ cb(i);
+ }
+ });
+ };
+ }
+ };
+
+ function queue(target) {
+ var array = Array.prototype;
+
+ var queueing = [];
+
+ target.queue = queue;
+ target.queued = queued;
+
+ return target;
+
+ function queued(action) {
+ return function () {
+ var self = this;
+ var args = arguments;
+
+ queue(function (next) {
+ action.apply(self, array.concat.apply(next, args));
+ });
+ };
+ }
+
+ function queue(action) {
+ if (!action) {
+ return;
+ }
+
+ queueing.push(action);
+
+ if (queueing.length === 1) {
+ next();
+ }
+ }
+ function next() {
+ queueing[0](function (err) {
+ if (err) {
+ throw err;
+ }
+
+ queueing = queueing.slice(1);
+
+ if (queueing.length) {
+ next();
+ }
+ });
+ }
+ }
+
+ function observable(target) {
+ target || (target = {});
+ var listeners = {};
+
+ target.on = on;
+ target.one = one;
+ target.off = off;
+ target.trigger = trigger;
+
+ return target;
+
+ function on(name, cb, ctx) {
+ listeners[name] || (listeners[name] = []);
+ listeners[name].push({ cb: cb, ctx: ctx });
+ }
+
+ function one(name, cb, ctx) {
+ listeners[name] || (listeners[name] = []);
+ listeners[name].push({
+ cb: cb, ctx: ctx, once: true
+ });
+ }
+
+ function trigger(name) {
+ var self = this;
+ var args = Array.prototype.slice(arguments, 1);
+
+ var currentListeners = listeners[name] || [];
+
+ currentListeners.filter(function (listener) {
+ listener.cb.apply(self, args);
+
+ return !listener.once;
+ });
+ }
+
+ function off(name, cb) {
+ if (!name) {
+ listeners = {};
+ return;
+ }
+
+ if (!cb) {
+ listeners[name] = [];
+ return;
+ }
+
+ listeners[name] = listeners[name].filter(function (listener) {
+ return listener.cb !== cb;
+ });
+ }
+ }
+
+ function Deck(jokers) {
+ // init cards array
+ var cards = new Array(jokers ? 55 : 52);
+
+ var $el = createElement('div');
+ var self = observable({ mount: mount, unmount: unmount, cards: cards, $el: $el });
+ var $root;
+
+ var modules = Deck.modules;
+ var module;
+
+ // make queueable
+ queue(self);
+
+ // load modules
+ for (module in modules) {
+ addModule(modules[module]);
+ }
+
+ // add class
+ $el.classList.add('deck');
+
+ var card;
+
+ // create cards
+ for (var i = cards.length; i; i--) {
+ card = cards[i - 1] = _card(i - 1);
+ card.setSide('back');
+ card.mount($el);
+ }
+
+ return self;
+
+ function mount(root) {
+ // mount deck to root
+ $root = root;
+ $root.appendChild($el);
+ }
+
+ function unmount() {
+ // unmount deck from root
+ $root.removeChild($el);
+ }
+
+ function addModule(module) {
+ module.deck && module.deck(self);
+ }
+ }
+ Deck.animationFrames = animationFrames;
+ Deck.ease = ease;
+ Deck.modules = { bysuit: bysuit, fan: fan, intro: intro, poker: poker, shuffle: shuffle, sort: sort, flip: flip };
+ Deck.Card = _card;
+ Deck.prefix = prefix;
+ Deck.translate = translate;
+
+ return Deck;
+})();
diff --git a/web/shuffle/example.css b/web/shuffle/example.css
new file mode 100644
index 0000000..a89aa8e
--- /dev/null
+++ b/web/shuffle/example.css
@@ -0,0 +1,391 @@
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+html,
+body {
+ height: 100%;
+}
+body {
+ background-color: #45a173;
+ color: #333;
+ font-family: 'Open Sans', sans-serif;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ overflow: hidden;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ text-size-adjust: 100%;
+}
+#container {
+ position: fixed;
+ top: calc(50% + 1.5rem);
+ left: 50%;
+ z-index: 1;
+ -webkit-transform: translate3d(-50%, -50%, 0);
+ -moz-transform: translate3d(-50%, -50%, 0);
+ -o-transform: translate3d(-50%, -50%, 0);
+ -ms-transform: translate3d(-50%, -50%, 0);
+ transform: translate3d(-50%, -50%, 0);
+}
+#topbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ background-color: #334d41;
+ padding: 0.25rem;
+ text-align: center;
+}
+#topbar button {
+ background: none;
+ border: 1px solid #fff;
+ outline: none;
+ margin: 0.1rem;
+ padding: 0.4rem;
+ font: inherit;
+ font-size: 0.75rem;
+ line-height: 1;
+ color: #fff;
+ cursor: pointer;
+}
+#topbar button:hover {
+ background-color: #fff;
+ color: #444;
+}
+.message {
+ position: fixed;
+ top: 2.5rem;
+ left: 0;
+ width: 100%;
+ padding: 1rem 0.5rem;
+ font-size: 0.5rem;
+ text-align: center;
+}
+.card {
+ position: absolute;
+ display: inline-block;
+ left: -1.9375rem;
+ top: -2.75rem;
+ width: 3.875rem;
+ height: 5.5rem;
+ background-color: #fff;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15);
+ box-shadow: 0 1px 1px rgba(0,0,0,0.15);
+ cursor: default;
+ will-change: transform;
+}
+.card:before,
+.card:after {
+ position: absolute;
+ font-size: 0.7rem;
+ text-align: center;
+ line-height: 0.7rem;
+ font-family: 'Ubuntu Condensed', sans-serif;
+ white-space: pre-line;
+ width: 0.55rem;
+ letter-spacing: -0.1rem;
+}
+.card:before {
+ top: 0.15rem;
+ left: 0;
+}
+.card:after {
+ bottom: 0.1rem;
+ right: 0;
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.card .face {
+ height: 100%;
+ background-position: 50% 50%;
+ -webkit-background-size: 100% 100%;
+ -moz-background-size: 100% 100%;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+}
+.card .back {
+ position: absolute;
+ background-image: url("faces/back.png");
+ background-position: 50% 50%;
+ -webkit-background-size: 100% 100%;
+ -moz-background-size: 100% 100%;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+}
+.card.spades,
+.card.clubs,
+.card.joker {
+ color: #000;
+}
+.card.hearts,
+.card.diamonds,
+.card.joker.rank3 {
+ color: #d40000;
+}
+.card.joker.rank1:before,
+.card.joker.rank2:before,
+.card.joker.rank3:before,
+.card.joker.rank1:after,
+.card.joker.rank2:after,
+.card.joker.rank3:after {
+ content: "J\a O\a K\a E\a R";
+ letter-spacing: 0;
+ font-size: 0.4rem;
+ line-height: 0.4rem;
+ padding: 0.15rem 0.05rem;
+ width: 0.5rem;
+}
+.card.rank1:before,
+.card.rank1:after {
+ content: "A";
+}
+.card.rank2:before,
+.card.rank2:after {
+ content: "2";
+}
+.card.rank3:before,
+.card.rank3:after {
+ content: "3";
+}
+.card.rank4:before,
+.card.rank4:after {
+ content: "4";
+}
+.card.rank5:before,
+.card.rank5:after {
+ content: "5";
+}
+.card.rank6:before,
+.card.rank6:after {
+ content: "6";
+}
+.card.rank7:before,
+.card.rank7:after {
+ content: "7";
+}
+.card.rank8:before,
+.card.rank8:after {
+ content: "8";
+}
+.card.rank9:before,
+.card.rank9:after {
+ content: "9";
+}
+.card.rank10:before,
+.card.rank10:after {
+ content: "10";
+}
+.card.rank11:before,
+.card.rank11:after {
+ content: "J";
+}
+.card.rank12:before,
+.card.rank12:after {
+ content: "Q";
+}
+.card.rank13:before,
+.card.rank13:after {
+ content: "K";
+}
+.card.spades.rank1 .face {
+ background-image: url("faces/0_1.svg");
+}
+.card.spades.rank2 .face {
+ background-image: url("faces/c1.png");
+}
+.card.spades.rank3 .face {
+ background-image: url("faces/c2.png");
+}
+.card.spades.rank4 .face {
+ background-image: url("faces/c3.png");
+}
+.card.spades.rank5 .face {
+ background-image: url("faces/c4.png");
+}
+.card.spades.rank6 .face {
+ background-image: url("faces/c4.png");
+}
+.card.spades.rank7 .face {
+ background-image: url("faces/c4.png");
+}
+.card.spades.rank8 .face {
+ background-image: url("faces/c1.png");
+}
+.card.spades.rank9 .face {
+ background-image: url("faces/c3.png");
+}
+.card.spades.rank10 .face {
+ background-image: url("faces/c3.png");
+}
+.card.spades.rank11 .face {
+ background-image: url("faces/c3.png");
+}
+.card.spades.rank12 .face {
+ background-image: url("faces/0_12.svg");
+}
+.card.spades.rank13 .face {
+ background-image: url("faces/0_13.svg");
+}
+.card.hearts.rank1 .face {
+ background-image: url("faces/1_1.svg");
+}
+.card.hearts.rank2 .face {
+ background-image: url("faces/1_2.svg");
+}
+.card.hearts.rank3 .face {
+ background-image: url("faces/1_3.svg");
+}
+.card.hearts.rank4 .face {
+ background-image: url("faces/1_4.svg");
+}
+.card.hearts.rank5 .face {
+ background-image: url("faces/1_5.svg");
+}
+.card.hearts.rank6 .face {
+ background-image: url("faces/1_6.svg");
+}
+.card.hearts.rank7 .face {
+ background-image: url("faces/1_7.svg");
+}
+.card.hearts.rank8 .face {
+ background-image: url("faces/1_8.svg");
+}
+.card.hearts.rank9 .face {
+ background-image: url("faces/1_9.svg");
+}
+.card.hearts.rank10 .face {
+ background-image: url("faces/1_10.svg");
+}
+.card.hearts.rank11 .face {
+ background-image: url("faces/1_11.svg");
+}
+.card.hearts.rank12 .face {
+ background-image: url("faces/1_12.svg");
+}
+.card.hearts.rank13 .face {
+ background-image: url("faces/1_13.svg");
+}
+.card.clubs.rank1 .face {
+ background-image: url("faces/2_1.svg");
+}
+.card.clubs.rank2 .face {
+ background-image: url("faces/2_2.svg");
+}
+.card.clubs.rank3 .face {
+ background-image: url("faces/2_3.svg");
+}
+.card.clubs.rank4 .face {
+ background-image: url("faces/2_4.svg");
+}
+.card.clubs.rank5 .face {
+ background-image: url("faces/2_5.svg");
+}
+.card.clubs.rank6 .face {
+ background-image: url("faces/2_6.svg");
+}
+.card.clubs.rank7 .face {
+ background-image: url("faces/2_7.svg");
+}
+.card.clubs.rank8 .face {
+ background-image: url("faces/2_8.svg");
+}
+.card.clubs.rank9 .face {
+ background-image: url("faces/2_9.svg");
+}
+.card.clubs.rank10 .face {
+ background-image: url("faces/2_10.svg");
+}
+.card.clubs.rank11 .face {
+ background-image: url("faces/2_11.svg");
+}
+.card.clubs.rank12 .face {
+ background-image: url("faces/2_12.svg");
+}
+.card.clubs.rank13 .face {
+ background-image: url("faces/2_13.svg");
+}
+.card.diamonds.rank1 .face {
+ background-image: url("faces/3_1.svg");
+}
+.card.diamonds.rank2 .face {
+ background-image: url("faces/3_2.svg");
+}
+.card.diamonds.rank3 .face {
+ background-image: url("faces/3_3.svg");
+}
+.card.diamonds.rank4 .face {
+ background-image: url("faces/3_4.svg");
+}
+.card.diamonds.rank5 .face {
+ background-image: url("faces/3_5.svg");
+}
+.card.diamonds.rank6 .face {
+ background-image: url("faces/3_6.svg");
+}
+.card.diamonds.rank7 .face {
+ background-image: url("faces/3_7.svg");
+}
+.card.diamonds.rank8 .face {
+ background-image: url("faces/3_8.svg");
+}
+.card.diamonds.rank9 .face {
+ background-image: url("faces/3_9.svg");
+}
+.card.diamonds.rank10 .face {
+ background-image: url("faces/3_10.svg");
+}
+.card.diamonds.rank11 .face {
+ background-image: url("faces/3_11.svg");
+}
+.card.diamonds.rank12 .face {
+ background-image: url("faces/3_12.svg");
+}
+.card.diamonds.rank13 .face {
+ background-image: url("faces/3_13.svg");
+}
+.card.joker.rank1 .face {
+ background-image: url("faces/4_1.svg");
+}
+.card.joker.rank2 .face {
+ background-image: url("faces/4_2.svg");
+}
+.card.joker.rank3 .face {
+ background-image: url("faces/4_3.svg");
+}
+@media (max-width: 540px) {
+ #topbar {
+ text-align: left;
+ }
+ .gh-ribbon {
+ -webkit-transform: scale(0.5);
+ -moz-transform: scale(0.5);
+ -o-transform: scale(0.5);
+ -ms-transform: scale(0.5);
+ transform: scale(0.5);
+ -webkit-transform-origin: 100% 0;
+ -moz-transform-origin: 100% 0;
+ -o-transform-origin: 100% 0;
+ -ms-transform-origin: 100% 0;
+ transform-origin: 100% 0;
+ }
+}
+@media (min-width: 640px) {
+ html {
+ font-size: 125%;
+ }
+}
diff --git a/web/shuffle/example.js b/web/shuffle/example.js
new file mode 100644
index 0000000..638b160
--- /dev/null
+++ b/web/shuffle/example.js
@@ -0,0 +1,232 @@
+
+/* global Deck */
+
+var prefix = Deck.prefix
+
+var transform = prefix('transform')
+
+var translate = Deck.translate
+
+var $container = document.getElementById('container')
+var $topbar = document.getElementById('topbar')
+
+var $sort = document.createElement('button')
+var $shuffle = document.createElement('button')
+var $bysuit = document.createElement('button')
+var $fan = document.createElement('button')
+var $poker = document.createElement('button')
+var $flip = document.createElement('button')
+
+$shuffle.textContent = 'Shuffle'
+$sort.textContent = 'Sort'
+$bysuit.textContent = 'By suit'
+$fan.textContent = 'Fan'
+$poker.textContent = 'Poker'
+$flip.textContent = 'Flip'
+
+$topbar.appendChild($flip)
+$topbar.appendChild($shuffle)
+$topbar.appendChild($bysuit)
+$topbar.appendChild($fan)
+$topbar.appendChild($poker)
+$topbar.appendChild($sort)
+
+var deck = Deck()
+
+// easter eggs start
+
+var acesClicked = []
+var kingsClicked = []
+
+deck.cards.forEach(function (card, i) {
+ card.enableDragging()
+ card.enableFlipping()
+
+ card.$el.addEventListener('mousedown', onTouch)
+ card.$el.addEventListener('touchstart', onTouch)
+
+ function onTouch () {
+ var card
+
+ if (i % 13 === 0) {
+ acesClicked[i] = true
+ if (acesClicked.filter(function (ace) {
+ return ace
+ }).length === 4) {
+ document.body.removeChild($topbar)
+ deck.$el.style.display = 'none'
+ setTimeout(function () {
+ startWinning()
+ }, 250)
+ }
+ } else if (i % 13 === 12) {
+ if (!kingsClicked) {
+ return
+ }
+ kingsClicked[i] = true
+ if (kingsClicked.filter(function (king) {
+ return king
+ }).length === 4) {
+ for (var j = 0; j < 3; j++) {
+ card = Deck.Card(52 + j)
+ card.mount(deck.$el)
+ card.$el.style[transform] = 'scale(0)'
+ card.setSide('front')
+ card.enableDragging()
+ card.enableFlipping()
+ deck.cards.push(card)
+ }
+ deck.sort(true)
+ kingsClicked = false
+ }
+ } else {
+ acesClicked = []
+ if (kingsClicked) {
+ kingsClicked = []
+ }
+ }
+ }
+})
+
+function startWinning () {
+ var $winningDeck = document.createElement('div')
+ $winningDeck.classList.add('deck')
+
+ $winningDeck.style[transform] = translate(Math.random() * window.innerWidth - window.innerWidth / 2 + 'px', Math.random() * window.innerHeight - window.innerHeight / 2 + 'px')
+
+ $container.appendChild($winningDeck)
+
+ var side = Math.floor(Math.random() * 2) ? 'front' : 'back'
+
+ for (var i = 0; i < 55; i++) {
+ addWinningCard($winningDeck, i, side)
+ }
+
+ setTimeout(startWinning, Math.round(Math.random() * 1000))
+}
+
+function addWinningCard ($deck, i, side) {
+ var card = Deck.Card(54 - i)
+ var delay = (55 - i) * 20
+ var animationFrames = Deck.animationFrames
+ var ease = Deck.ease
+
+ card.enableFlipping()
+
+ if (side === 'front') {
+ card.setSide('front')
+ } else {
+ card.setSide('back')
+ }
+
+ card.mount($deck)
+ card.$el.style.display = 'none'
+
+ var xStart = 0
+ var yStart = 0
+ var xDiff = -500
+ var yDiff = 500
+
+ animationFrames(delay, 1000)
+ .start(function () {
+ card.x = 0
+ card.y = 0
+ card.$el.style.display = ''
+ })
+ .progress(function (t) {
+ var tx = t
+ var ty = ease.cubicIn(t)
+ card.x = xStart + xDiff * tx
+ card.y = yStart + yDiff * ty
+ card.$el.style[transform] = translate(card.x + 'px', card.y + 'px')
+ })
+ .end(function () {
+ card.unmount()
+ })
+}
+
+// easter eggs end
+
+$shuffle.addEventListener('click', function () {
+ deck.shuffle()
+ deck.shuffle()
+})
+$sort.addEventListener('click', function () {
+ deck.sort()
+})
+$bysuit.addEventListener('click', function () {
+ deck.sort(true) // sort reversed
+ deck.bysuit()
+})
+$fan.addEventListener('click', function () {
+ deck.fan()
+})
+$flip.addEventListener('click', function () {
+ deck.flip()
+})
+$poker.addEventListener('click', function () {
+ deck.queue(function (next) {
+ deck.cards.forEach(function (card, i) {
+ setTimeout(function () {
+ card.setSide('back')
+ }, i * 7.5)
+ })
+ next()
+ })
+ deck.shuffle()
+ deck.shuffle()
+ deck.poker()
+})
+
+deck.mount($container)
+
+deck.intro()
+deck.sort()
+
+// secret message..
+
+var randomDelay = 10000 + 30000 * Math.random()
+
+setTimeout(function () {
+ printMessage('Psst..I want to share a secret with you...')
+}, randomDelay)
+
+setTimeout(function () {
+ printMessage('...try clicking all kings and nothing in between...')
+}, randomDelay + 5000)
+
+setTimeout(function () {
+ printMessage('...have fun ;)')
+}, randomDelay + 10000)
+
+function printMessage (text) {
+ var animationFrames = Deck.animationFrames
+ var ease = Deck.ease
+ var $message = document.createElement('p')
+ $message.classList.add('message')
+ $message.textContent = text
+
+ document.body.appendChild($message)
+
+ $message.style[transform] = translate(window.innerWidth + 'px', 0)
+
+ var diffX = window.innerWidth
+
+ animationFrames(1000, 700)
+ .progress(function (t) {
+ t = ease.cubicInOut(t)
+ $message.style[transform] = translate((diffX - diffX * t) + 'px', 0)
+ })
+
+ animationFrames(6000, 700)
+ .start(function () {
+ diffX = window.innerWidth
+ })
+ .progress(function (t) {
+ t = ease.cubicInOut(t)
+ $message.style[transform] = translate((-diffX * t) + 'px', 0)
+ })
+ .end(function () {
+ document.body.removeChild($message)
+ })
+}
diff --git a/web/shuffle/example_without_will_change.css b/web/shuffle/example_without_will_change.css
new file mode 100644
index 0000000..8894f65
--- /dev/null
+++ b/web/shuffle/example_without_will_change.css
@@ -0,0 +1,389 @@
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+html,
+body {
+ height: 100%;
+}
+body {
+ background-color: #45a173;
+ color: #333;
+ font-family: 'Open Sans', sans-serif;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ overflow: hidden;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ text-size-adjust: 100%;
+}
+#container {
+ position: fixed;
+ top: calc(50% + 1.5rem);
+ left: 50%;
+ -webkit-transform: translate3d(-50%, -50%, 0);
+ -moz-transform: translate3d(-50%, -50%, 0);
+ -o-transform: translate3d(-50%, -50%, 0);
+ -ms-transform: translate3d(-50%, -50%, 0);
+ transform: translate3d(-50%, -50%, 0);
+}
+#topbar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ background-color: #334d41;
+ padding: 0.25rem;
+ text-align: center;
+}
+#topbar button {
+ background: none;
+ border: 1px solid #fff;
+ outline: none;
+ margin: 0.1rem;
+ padding: 0.4rem;
+ font: inherit;
+ font-size: 0.75rem;
+ line-height: 1;
+ color: #fff;
+ cursor: pointer;
+}
+#topbar button:hover {
+ background-color: #fff;
+ color: #444;
+}
+.message {
+ position: fixed;
+ top: 2.5rem;
+ left: 0;
+ width: 100%;
+ padding: 1rem 0.5rem;
+ font-size: 0.5rem;
+ text-align: center;
+}
+.card {
+ position: absolute;
+ display: inline-block;
+ left: -1.9375rem;
+ top: -2.75rem;
+ width: 3.875rem;
+ height: 5.5rem;
+ background-color: #fff;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.15);
+ box-shadow: 0 1px 1px rgba(0,0,0,0.15);
+ cursor: default;
+}
+.card:before,
+.card:after {
+ position: absolute;
+ font-size: 0.7rem;
+ text-align: center;
+ line-height: 0.7rem;
+ font-family: 'Ubuntu Condensed', sans-serif;
+ white-space: pre-line;
+ width: 0.55rem;
+ letter-spacing: -0.1rem;
+}
+.card:before {
+ top: 0.15rem;
+ left: 0;
+}
+.card:after {
+ bottom: 0.1rem;
+ right: 0;
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+.card .face {
+ height: 100%;
+ background-position: 50% 50%;
+ -webkit-background-size: 100% 100%;
+ -moz-background-size: 100% 100%;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+}
+.card .back {
+ position: absolute;
+ background-image: url("faces/back.png");
+ background-position: 50% 50%;
+ -webkit-background-size: 100% 100%;
+ -moz-background-size: 100% 100%;
+ background-size: 100% 100%;
+ background-repeat: no-repeat;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+}
+.card.spades,
+.card.clubs,
+.card.joker {
+ color: #000;
+}
+.card.hearts,
+.card.diamonds,
+.card.joker.rank3 {
+ color: #d40000;
+}
+.card.joker.rank1:before,
+.card.joker.rank2:before,
+.card.joker.rank3:before,
+.card.joker.rank1:after,
+.card.joker.rank2:after,
+.card.joker.rank3:after {
+ content: "J\a O\a K\a E\a R";
+ letter-spacing: 0;
+ font-size: 0.4rem;
+ line-height: 0.4rem;
+ padding: 0.15rem 0.05rem;
+ width: 0.5rem;
+}
+.card.rank1:before,
+.card.rank1:after {
+ content: "A";
+}
+.card.rank2:before,
+.card.rank2:after {
+ content: "2";
+}
+.card.rank3:before,
+.card.rank3:after {
+ content: "3";
+}
+.card.rank4:before,
+.card.rank4:after {
+ content: "4";
+}
+.card.rank5:before,
+.card.rank5:after {
+ content: "5";
+}
+.card.rank6:before,
+.card.rank6:after {
+ content: "6";
+}
+.card.rank7:before,
+.card.rank7:after {
+ content: "7";
+}
+.card.rank8:before,
+.card.rank8:after {
+ content: "8";
+}
+.card.rank9:before,
+.card.rank9:after {
+ content: "9";
+}
+.card.rank10:before,
+.card.rank10:after {
+ content: "10";
+}
+.card.rank11:before,
+.card.rank11:after {
+ content: "J";
+}
+.card.rank12:before,
+.card.rank12:after {
+ content: "Q";
+}
+.card.rank13:before,
+.card.rank13:after {
+ content: "K";
+}
+.card.spades.rank1 .face {
+ background-image: url("faces/0_1.svg");
+}
+.card.spades.rank2 .face {
+ background-image: url("faces/0_2.svg");
+}
+.card.spades.rank3 .face {
+ background-image: url("faces/0_3.svg");
+}
+.card.spades.rank4 .face {
+ background-image: url("faces/0_4.svg");
+}
+.card.spades.rank5 .face {
+ background-image: url("faces/0_5.svg");
+}
+.card.spades.rank6 .face {
+ background-image: url("faces/0_6.svg");
+}
+.card.spades.rank7 .face {
+ background-image: url("faces/0_7.svg");
+}
+.card.spades.rank8 .face {
+ background-image: url("faces/0_8.svg");
+}
+.card.spades.rank9 .face {
+ background-image: url("faces/0_9.svg");
+}
+.card.spades.rank10 .face {
+ background-image: url("faces/0_10.svg");
+}
+.card.spades.rank11 .face {
+ background-image: url("faces/0_11.svg");
+}
+.card.spades.rank12 .face {
+ background-image: url("faces/0_12.svg");
+}
+.card.spades.rank13 .face {
+ background-image: url("faces/0_13.svg");
+}
+.card.hearts.rank1 .face {
+ background-image: url("faces/1_1.svg");
+}
+.card.hearts.rank2 .face {
+ background-image: url("faces/1_2.svg");
+}
+.card.hearts.rank3 .face {
+ background-image: url("faces/1_3.svg");
+}
+.card.hearts.rank4 .face {
+ background-image: url("faces/1_4.svg");
+}
+.card.hearts.rank5 .face {
+ background-image: url("faces/1_5.svg");
+}
+.card.hearts.rank6 .face {
+ background-image: url("faces/1_6.svg");
+}
+.card.hearts.rank7 .face {
+ background-image: url("faces/1_7.svg");
+}
+.card.hearts.rank8 .face {
+ background-image: url("faces/1_8.svg");
+}
+.card.hearts.rank9 .face {
+ background-image: url("faces/1_9.svg");
+}
+.card.hearts.rank10 .face {
+ background-image: url("faces/1_10.svg");
+}
+.card.hearts.rank11 .face {
+ background-image: url("faces/1_11.svg");
+}
+.card.hearts.rank12 .face {
+ background-image: url("faces/1_12.svg");
+}
+.card.hearts.rank13 .face {
+ background-image: url("faces/1_13.svg");
+}
+.card.clubs.rank1 .face {
+ background-image: url("faces/2_1.svg");
+}
+.card.clubs.rank2 .face {
+ background-image: url("faces/2_2.svg");
+}
+.card.clubs.rank3 .face {
+ background-image: url("faces/2_3.svg");
+}
+.card.clubs.rank4 .face {
+ background-image: url("faces/2_4.svg");
+}
+.card.clubs.rank5 .face {
+ background-image: url("faces/2_5.svg");
+}
+.card.clubs.rank6 .face {
+ background-image: url("faces/2_6.svg");
+}
+.card.clubs.rank7 .face {
+ background-image: url("faces/2_7.svg");
+}
+.card.clubs.rank8 .face {
+ background-image: url("faces/2_8.svg");
+}
+.card.clubs.rank9 .face {
+ background-image: url("faces/2_9.svg");
+}
+.card.clubs.rank10 .face {
+ background-image: url("faces/2_10.svg");
+}
+.card.clubs.rank11 .face {
+ background-image: url("faces/2_11.svg");
+}
+.card.clubs.rank12 .face {
+ background-image: url("faces/2_12.svg");
+}
+.card.clubs.rank13 .face {
+ background-image: url("faces/2_13.svg");
+}
+.card.diamonds.rank1 .face {
+ background-image: url("faces/3_1.svg");
+}
+.card.diamonds.rank2 .face {
+ background-image: url("faces/3_2.svg");
+}
+.card.diamonds.rank3 .face {
+ background-image: url("faces/3_3.svg");
+}
+.card.diamonds.rank4 .face {
+ background-image: url("faces/3_4.svg");
+}
+.card.diamonds.rank5 .face {
+ background-image: url("faces/3_5.svg");
+}
+.card.diamonds.rank6 .face {
+ background-image: url("faces/3_6.svg");
+}
+.card.diamonds.rank7 .face {
+ background-image: url("faces/3_7.svg");
+}
+.card.diamonds.rank8 .face {
+ background-image: url("faces/3_8.svg");
+}
+.card.diamonds.rank9 .face {
+ background-image: url("faces/3_9.svg");
+}
+.card.diamonds.rank10 .face {
+ background-image: url("faces/3_10.svg");
+}
+.card.diamonds.rank11 .face {
+ background-image: url("faces/3_11.svg");
+}
+.card.diamonds.rank12 .face {
+ background-image: url("faces/3_12.svg");
+}
+.card.diamonds.rank13 .face {
+ background-image: url("faces/3_13.svg");
+}
+.card.joker.rank1 .face {
+ background-image: url("faces/4_1.svg");
+}
+.card.joker.rank2 .face {
+ background-image: url("faces/4_2.svg");
+}
+.card.joker.rank3 .face {
+ background-image: url("faces/4_3.svg");
+}
+@media (max-width: 540px) {
+ #topbar {
+ text-align: left;
+ }
+ .gh-ribbon {
+ -webkit-transform: scale(0.5);
+ -moz-transform: scale(0.5);
+ -o-transform: scale(0.5);
+ -ms-transform: scale(0.5);
+ transform: scale(0.5);
+ -webkit-transform-origin: 100% 0;
+ -moz-transform-origin: 100% 0;
+ -o-transform-origin: 100% 0;
+ -ms-transform-origin: 100% 0;
+ transform-origin: 100% 0;
+ }
+}
+@media (min-width: 640px) {
+ html {
+ font-size: 125%;
+ }
+}
diff --git a/web/shuffle/faces/0_1.svg b/web/shuffle/faces/0_1.svg
new file mode 100644
index 0000000..683e2b7
--- /dev/null
+++ b/web/shuffle/faces/0_1.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_10.svg b/web/shuffle/faces/0_10.svg
new file mode 100644
index 0000000..02c13d8
--- /dev/null
+++ b/web/shuffle/faces/0_10.svg
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_11.svg b/web/shuffle/faces/0_11.svg
new file mode 100644
index 0000000..885e931
--- /dev/null
+++ b/web/shuffle/faces/0_11.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_12.svg b/web/shuffle/faces/0_12.svg
new file mode 100644
index 0000000..c400c26
--- /dev/null
+++ b/web/shuffle/faces/0_12.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_13.svg b/web/shuffle/faces/0_13.svg
new file mode 100644
index 0000000..59e950d
--- /dev/null
+++ b/web/shuffle/faces/0_13.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_2.svg b/web/shuffle/faces/0_2.svg
new file mode 100644
index 0000000..bb08a86
--- /dev/null
+++ b/web/shuffle/faces/0_2.svg
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_3.svg b/web/shuffle/faces/0_3.svg
new file mode 100644
index 0000000..91b0f83
--- /dev/null
+++ b/web/shuffle/faces/0_3.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_4.svg b/web/shuffle/faces/0_4.svg
new file mode 100644
index 0000000..44179d7
--- /dev/null
+++ b/web/shuffle/faces/0_4.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_5.svg b/web/shuffle/faces/0_5.svg
new file mode 100644
index 0000000..e5e60a3
--- /dev/null
+++ b/web/shuffle/faces/0_5.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_6.svg b/web/shuffle/faces/0_6.svg
new file mode 100644
index 0000000..ca73a46
--- /dev/null
+++ b/web/shuffle/faces/0_6.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_7.svg b/web/shuffle/faces/0_7.svg
new file mode 100644
index 0000000..521d1bc
--- /dev/null
+++ b/web/shuffle/faces/0_7.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_8.svg b/web/shuffle/faces/0_8.svg
new file mode 100644
index 0000000..195574c
--- /dev/null
+++ b/web/shuffle/faces/0_8.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/0_9.svg b/web/shuffle/faces/0_9.svg
new file mode 100644
index 0000000..b6a1752
--- /dev/null
+++ b/web/shuffle/faces/0_9.svg
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_1.svg b/web/shuffle/faces/1_1.svg
new file mode 100644
index 0000000..8a204e4
--- /dev/null
+++ b/web/shuffle/faces/1_1.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_10.svg b/web/shuffle/faces/1_10.svg
new file mode 100644
index 0000000..1dd0471
--- /dev/null
+++ b/web/shuffle/faces/1_10.svg
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_11.svg b/web/shuffle/faces/1_11.svg
new file mode 100644
index 0000000..fb6b71d
--- /dev/null
+++ b/web/shuffle/faces/1_11.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_12.svg b/web/shuffle/faces/1_12.svg
new file mode 100644
index 0000000..413e91c
--- /dev/null
+++ b/web/shuffle/faces/1_12.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_13.svg b/web/shuffle/faces/1_13.svg
new file mode 100644
index 0000000..0b5e6cd
--- /dev/null
+++ b/web/shuffle/faces/1_13.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_2.svg b/web/shuffle/faces/1_2.svg
new file mode 100644
index 0000000..834b8bf
--- /dev/null
+++ b/web/shuffle/faces/1_2.svg
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_3.svg b/web/shuffle/faces/1_3.svg
new file mode 100644
index 0000000..badce9d
--- /dev/null
+++ b/web/shuffle/faces/1_3.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_4.svg b/web/shuffle/faces/1_4.svg
new file mode 100644
index 0000000..963895f
--- /dev/null
+++ b/web/shuffle/faces/1_4.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_5.svg b/web/shuffle/faces/1_5.svg
new file mode 100644
index 0000000..2c95079
--- /dev/null
+++ b/web/shuffle/faces/1_5.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_6.svg b/web/shuffle/faces/1_6.svg
new file mode 100644
index 0000000..101655c
--- /dev/null
+++ b/web/shuffle/faces/1_6.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_7.svg b/web/shuffle/faces/1_7.svg
new file mode 100644
index 0000000..fdf9757
--- /dev/null
+++ b/web/shuffle/faces/1_7.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_8.svg b/web/shuffle/faces/1_8.svg
new file mode 100644
index 0000000..9bc7c5e
--- /dev/null
+++ b/web/shuffle/faces/1_8.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/1_9.svg b/web/shuffle/faces/1_9.svg
new file mode 100644
index 0000000..8ac41f2
--- /dev/null
+++ b/web/shuffle/faces/1_9.svg
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_1.svg b/web/shuffle/faces/2_1.svg
new file mode 100644
index 0000000..9737a17
--- /dev/null
+++ b/web/shuffle/faces/2_1.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_10.svg b/web/shuffle/faces/2_10.svg
new file mode 100644
index 0000000..2b88894
--- /dev/null
+++ b/web/shuffle/faces/2_10.svg
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_11.svg b/web/shuffle/faces/2_11.svg
new file mode 100644
index 0000000..0c7970d
--- /dev/null
+++ b/web/shuffle/faces/2_11.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_12.svg b/web/shuffle/faces/2_12.svg
new file mode 100644
index 0000000..8c81fab
--- /dev/null
+++ b/web/shuffle/faces/2_12.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_13.svg b/web/shuffle/faces/2_13.svg
new file mode 100644
index 0000000..242547a
--- /dev/null
+++ b/web/shuffle/faces/2_13.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_2.svg b/web/shuffle/faces/2_2.svg
new file mode 100644
index 0000000..5949b60
--- /dev/null
+++ b/web/shuffle/faces/2_2.svg
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_3.svg b/web/shuffle/faces/2_3.svg
new file mode 100644
index 0000000..daab3f3
--- /dev/null
+++ b/web/shuffle/faces/2_3.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_4.svg b/web/shuffle/faces/2_4.svg
new file mode 100644
index 0000000..662ac5a
--- /dev/null
+++ b/web/shuffle/faces/2_4.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_5.svg b/web/shuffle/faces/2_5.svg
new file mode 100644
index 0000000..2827e2c
--- /dev/null
+++ b/web/shuffle/faces/2_5.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_6.svg b/web/shuffle/faces/2_6.svg
new file mode 100644
index 0000000..710fb39
--- /dev/null
+++ b/web/shuffle/faces/2_6.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_7.svg b/web/shuffle/faces/2_7.svg
new file mode 100644
index 0000000..05e3da8
--- /dev/null
+++ b/web/shuffle/faces/2_7.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_8.svg b/web/shuffle/faces/2_8.svg
new file mode 100644
index 0000000..a3a32cf
--- /dev/null
+++ b/web/shuffle/faces/2_8.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/2_9.svg b/web/shuffle/faces/2_9.svg
new file mode 100644
index 0000000..c6a6700
--- /dev/null
+++ b/web/shuffle/faces/2_9.svg
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_1.svg b/web/shuffle/faces/3_1.svg
new file mode 100644
index 0000000..ad8e2ee
--- /dev/null
+++ b/web/shuffle/faces/3_1.svg
@@ -0,0 +1,16 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_10.svg b/web/shuffle/faces/3_10.svg
new file mode 100644
index 0000000..3ab9367
--- /dev/null
+++ b/web/shuffle/faces/3_10.svg
@@ -0,0 +1,25 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_11.svg b/web/shuffle/faces/3_11.svg
new file mode 100644
index 0000000..07642de
--- /dev/null
+++ b/web/shuffle/faces/3_11.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_12.svg b/web/shuffle/faces/3_12.svg
new file mode 100644
index 0000000..6cbf548
--- /dev/null
+++ b/web/shuffle/faces/3_12.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_13.svg b/web/shuffle/faces/3_13.svg
new file mode 100644
index 0000000..a186816
--- /dev/null
+++ b/web/shuffle/faces/3_13.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_2.svg b/web/shuffle/faces/3_2.svg
new file mode 100644
index 0000000..38511a8
--- /dev/null
+++ b/web/shuffle/faces/3_2.svg
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_3.svg b/web/shuffle/faces/3_3.svg
new file mode 100644
index 0000000..ab2773e
--- /dev/null
+++ b/web/shuffle/faces/3_3.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_4.svg b/web/shuffle/faces/3_4.svg
new file mode 100644
index 0000000..2b48eb0
--- /dev/null
+++ b/web/shuffle/faces/3_4.svg
@@ -0,0 +1,19 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_5.svg b/web/shuffle/faces/3_5.svg
new file mode 100644
index 0000000..a5f9650
--- /dev/null
+++ b/web/shuffle/faces/3_5.svg
@@ -0,0 +1,20 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_6.svg b/web/shuffle/faces/3_6.svg
new file mode 100644
index 0000000..17f54ec
--- /dev/null
+++ b/web/shuffle/faces/3_6.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_7.svg b/web/shuffle/faces/3_7.svg
new file mode 100644
index 0000000..9554ec9
--- /dev/null
+++ b/web/shuffle/faces/3_7.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_8.svg b/web/shuffle/faces/3_8.svg
new file mode 100644
index 0000000..6d8cef8
--- /dev/null
+++ b/web/shuffle/faces/3_8.svg
@@ -0,0 +1,23 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/3_9.svg b/web/shuffle/faces/3_9.svg
new file mode 100644
index 0000000..8226e7c
--- /dev/null
+++ b/web/shuffle/faces/3_9.svg
@@ -0,0 +1,24 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/4_1.svg b/web/shuffle/faces/4_1.svg
new file mode 100644
index 0000000..f1becdc
--- /dev/null
+++ b/web/shuffle/faces/4_1.svg
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/4_2.svg b/web/shuffle/faces/4_2.svg
new file mode 100644
index 0000000..c0d4e84
--- /dev/null
+++ b/web/shuffle/faces/4_2.svg
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/4_3.svg b/web/shuffle/faces/4_3.svg
new file mode 100644
index 0000000..e221d79
--- /dev/null
+++ b/web/shuffle/faces/4_3.svg
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/web/shuffle/faces/back.png b/web/shuffle/faces/back.png
new file mode 100644
index 0000000..1bc29a4
Binary files /dev/null and b/web/shuffle/faces/back.png differ
diff --git a/web/shuffle/faces/c1.png b/web/shuffle/faces/c1.png
new file mode 100644
index 0000000..303204c
Binary files /dev/null and b/web/shuffle/faces/c1.png differ
diff --git a/web/shuffle/faces/c2.png b/web/shuffle/faces/c2.png
new file mode 100644
index 0000000..80b926a
Binary files /dev/null and b/web/shuffle/faces/c2.png differ
diff --git a/web/shuffle/faces/c3.png b/web/shuffle/faces/c3.png
new file mode 100644
index 0000000..d699735
Binary files /dev/null and b/web/shuffle/faces/c3.png differ
diff --git a/web/shuffle/faces/c4.png b/web/shuffle/faces/c4.png
new file mode 100644
index 0000000..31cf67d
Binary files /dev/null and b/web/shuffle/faces/c4.png differ
diff --git a/web/shuffle/index-example.html b/web/shuffle/index-example.html
new file mode 100644
index 0000000..2da88a0
--- /dev/null
+++ b/web/shuffle/index-example.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ HTML5 Deck of Cards
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/shuffle/index.html b/web/shuffle/index.html
new file mode 100644
index 0000000..c586ecc
--- /dev/null
+++ b/web/shuffle/index.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+ HTML5 Deck of Cards
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/shuffle/without_will_change.html b/web/shuffle/without_will_change.html
new file mode 100644
index 0000000..edf3809
--- /dev/null
+++ b/web/shuffle/without_will_change.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ HTML5 Deck of Cards
+
+
+
+
+
+
+
+
+
+