From 7fb1af8dbc5bec1e7cea5a181edf41825ce121b8 Mon Sep 17 00:00:00 2001
From: jkleiser <jon.kleiser@usit.uio.no>
Date: Mon, 15 Feb 2016 10:46:40 +0100
Subject: [PATCH 1/2] Added carousel example

---
 examples/carousel-svg/README.md  |  15 +
 examples/carousel-svg/build.js   | 566 +++++++++++++++++++++++++++++++
 examples/carousel-svg/index.html |  74 ++++
 examples/carousel-svg/script.js  |  74 ++++
 4 files changed, 729 insertions(+)
 create mode 100644 examples/carousel-svg/README.md
 create mode 100644 examples/carousel-svg/build.js
 create mode 100644 examples/carousel-svg/index.html
 create mode 100644 examples/carousel-svg/script.js

diff --git a/examples/carousel-svg/README.md b/examples/carousel-svg/README.md
new file mode 100644
index 0000000..d94939a
--- /dev/null
+++ b/examples/carousel-svg/README.md
@@ -0,0 +1,15 @@
+I built the build.js using npm and browserify. 
+
+In my local copy of the snabbdom project root I did these preparations:
+```
+npm install --save-dev babelify
+npm install --save-dev babel-preset-es2015
+echo '{ "presets": ["es2015"] }' > .babelrc
+```
+
+I then built like this:
+```
+browserify examples/carousel-svg/script.js -t babelify -o examples/carousel-svg/build.js
+```
+
+-- *jk*
diff --git a/examples/carousel-svg/build.js b/examples/carousel-svg/build.js
new file mode 100644
index 0000000..3b1d598
--- /dev/null
+++ b/examples/carousel-svg/build.js
@@ -0,0 +1,566 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+'use strict';
+
+var VNode = require('./vnode');
+var is = require('./is');
+
+function addNS(data, children) {
+  data.ns = 'http://www.w3.org/2000/svg';
+  if (children !== undefined) {
+    for (var i = 0; i < children.length; ++i) {
+      addNS(children[i].data, children[i].children);
+    }
+  }
+}
+
+module.exports = function h(sel, b, c) {
+  var data = {},
+      children,
+      text,
+      i;
+  if (arguments.length === 3) {
+    data = b;
+    if (is.array(c)) {
+      children = c;
+    } else if (is.primitive(c)) {
+      text = c;
+    }
+  } else if (arguments.length === 2) {
+    if (is.array(b)) {
+      children = b;
+    } else if (is.primitive(b)) {
+      text = b;
+    } else {
+      data = b;
+    }
+  }
+  if (is.array(children)) {
+    for (i = 0; i < children.length; ++i) {
+      if (is.primitive(children[i])) children[i] = VNode(undefined, undefined, undefined, children[i]);
+    }
+  }
+  if (sel[0] === 's' && sel[1] === 'v' && sel[2] === 'g') {
+    addNS(data, children);
+  }
+  return VNode(sel, data, children, text, undefined);
+};
+
+},{"./is":2,"./vnode":7}],2:[function(require,module,exports){
+'use strict';
+
+module.exports = {
+  array: Array.isArray,
+  primitive: function primitive(s) {
+    return typeof s === 'string' || typeof s === 'number';
+  }
+};
+
+},{}],3:[function(require,module,exports){
+"use strict";
+
+var booleanAttrs = ["allowfullscreen", "async", "autofocus", "autoplay", "checked", "compact", "controls", "declare", "default", "defaultchecked", "defaultmuted", "defaultselected", "defer", "disabled", "draggable", "enabled", "formnovalidate", "hidden", "indeterminate", "inert", "ismap", "itemscope", "loop", "multiple", "muted", "nohref", "noresize", "noshade", "novalidate", "nowrap", "open", "pauseonexit", "readonly", "required", "reversed", "scoped", "seamless", "selected", "sortable", "spellcheck", "translate", "truespeed", "typemustmatch", "visible"];
+
+var booleanAttrsDict = {};
+for (var i = 0, len = booleanAttrs.length; i < len; i++) {
+  booleanAttrsDict[booleanAttrs[i]] = true;
+}
+
+function updateAttrs(oldVnode, vnode) {
+  var key,
+      cur,
+      old,
+      elm = vnode.elm,
+      oldAttrs = oldVnode.data.attrs || {},
+      attrs = vnode.data.attrs || {};
+
+  // update modified attributes, add new attributes
+  for (key in attrs) {
+    cur = attrs[key];
+    old = oldAttrs[key];
+    if (old !== cur) {
+      // TODO: add support to namespaced attributes (setAttributeNS)
+      if (!cur && booleanAttrsDict[key]) elm.removeAttribute(key);else elm.setAttribute(key, cur);
+    }
+  }
+  //remove removed attributes
+  // use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value)
+  // the other option is to remove all attributes with value == undefined
+  for (key in oldAttrs) {
+    if (!(key in attrs)) {
+      elm.removeAttribute(key);
+    }
+  }
+}
+
+module.exports = { create: updateAttrs, update: updateAttrs };
+
+},{}],4:[function(require,module,exports){
+'use strict';
+
+var is = require('../is');
+
+function arrInvoker(arr) {
+  return function () {
+    // Special case when length is two, for performance
+    arr.length === 2 ? arr[0](arr[1]) : arr[0].apply(undefined, arr.slice(1));
+  };
+}
+
+function fnInvoker(o) {
+  return function (ev) {
+    o.fn(ev);
+  };
+}
+
+function updateEventListeners(oldVnode, vnode) {
+  var name,
+      cur,
+      old,
+      elm = vnode.elm,
+      oldOn = oldVnode.data.on || {},
+      on = vnode.data.on;
+  if (!on) return;
+  for (name in on) {
+    cur = on[name];
+    old = oldOn[name];
+    if (old === undefined) {
+      if (is.array(cur)) {
+        elm.addEventListener(name, arrInvoker(cur));
+      } else {
+        cur = { fn: cur };
+        on[name] = cur;
+        elm.addEventListener(name, fnInvoker(cur));
+      }
+    } else if (is.array(old)) {
+      // Deliberately modify old array since it's captured in closure created with `arrInvoker`
+      old.length = cur.length;
+      for (var i = 0; i < old.length; ++i) {
+        old[i] = cur[i];
+      }on[name] = old;
+    } else {
+      old.fn = cur;
+      on[name] = old;
+    }
+  }
+}
+
+module.exports = { create: updateEventListeners, update: updateEventListeners };
+
+},{"../is":2}],5:[function(require,module,exports){
+'use strict';
+
+var raf = window && window.requestAnimationFrame || setTimeout;
+var nextFrame = function nextFrame(fn) {
+  raf(function () {
+    raf(fn);
+  });
+};
+
+function setNextFrame(obj, prop, val) {
+  nextFrame(function () {
+    obj[prop] = val;
+  });
+}
+
+function updateStyle(oldVnode, vnode) {
+  var cur,
+      name,
+      elm = vnode.elm,
+      oldStyle = oldVnode.data.style || {},
+      style = vnode.data.style || {},
+      oldHasDel = 'delayed' in oldStyle;
+  for (name in oldStyle) {
+    if (!style[name]) {
+      elm.style[name] = '';
+    }
+  }
+  for (name in style) {
+    cur = style[name];
+    if (name === 'delayed') {
+      for (name in style.delayed) {
+        cur = style.delayed[name];
+        if (!oldHasDel || cur !== oldStyle.delayed[name]) {
+          setNextFrame(elm.style, name, cur);
+        }
+      }
+    } else if (name !== 'remove' && cur !== oldStyle[name]) {
+      elm.style[name] = cur;
+    }
+  }
+}
+
+function applyDestroyStyle(vnode) {
+  var style,
+      name,
+      elm = vnode.elm,
+      s = vnode.data.style;
+  if (!s || !(style = s.destroy)) return;
+  for (name in style) {
+    elm.style[name] = style[name];
+  }
+}
+
+function applyRemoveStyle(vnode, rm) {
+  var s = vnode.data.style;
+  if (!s || !s.remove) {
+    rm();
+    return;
+  }
+  var name,
+      elm = vnode.elm,
+      idx,
+      i = 0,
+      maxDur = 0,
+      compStyle,
+      style = s.remove,
+      amount = 0,
+      applied = [];
+  for (name in style) {
+    applied.push(name);
+    elm.style[name] = style[name];
+  }
+  compStyle = getComputedStyle(elm);
+  var props = compStyle['transition-property'].split(', ');
+  for (; i < props.length; ++i) {
+    if (applied.indexOf(props[i]) !== -1) amount++;
+  }
+  elm.addEventListener('transitionend', function (ev) {
+    if (ev.target === elm) --amount;
+    if (amount === 0) rm();
+  });
+}
+
+module.exports = { create: updateStyle, update: updateStyle, destroy: applyDestroyStyle, remove: applyRemoveStyle };
+
+},{}],6:[function(require,module,exports){
+// jshint newcap: false
+/* global require, module, document, Element */
+'use strict';
+
+var VNode = require('./vnode');
+var is = require('./is');
+
+function isUndef(s) {
+  return s === undefined;
+}
+function isDef(s) {
+  return s !== undefined;
+}
+
+function emptyNodeAt(elm) {
+  return VNode(elm.tagName, {}, [], undefined, elm);
+}
+
+var emptyNode = VNode('', {}, [], undefined, undefined);
+
+function sameVnode(vnode1, vnode2) {
+  return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
+}
+
+function createKeyToOldIdx(children, beginIdx, endIdx) {
+  var i,
+      map = {},
+      key;
+  for (i = beginIdx; i <= endIdx; ++i) {
+    key = children[i].key;
+    if (isDef(key)) map[key] = i;
+  }
+  return map;
+}
+
+function createRmCb(childElm, listeners) {
+  return function () {
+    if (--listeners === 0) childElm.parentElement.removeChild(childElm);
+  };
+}
+
+var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];
+
+function init(modules) {
+  var i,
+      j,
+      cbs = {};
+  for (i = 0; i < hooks.length; ++i) {
+    cbs[hooks[i]] = [];
+    for (j = 0; j < modules.length; ++j) {
+      if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]);
+    }
+  }
+
+  function createElm(vnode, insertedVnodeQueue) {
+    var i,
+        data = vnode.data;
+    if (isDef(data)) {
+      if (isDef(i = data.hook) && isDef(i = i.init)) i(vnode);
+      if (isDef(i = data.vnode)) vnode = i;
+    }
+    var elm,
+        children = vnode.children,
+        sel = vnode.sel;
+    if (isDef(sel)) {
+      // Parse selector
+      var hashIdx = sel.indexOf('#');
+      var dotIdx = sel.indexOf('.', hashIdx);
+      var hash = hashIdx > 0 ? hashIdx : sel.length;
+      var dot = dotIdx > 0 ? dotIdx : sel.length;
+      var tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
+      elm = vnode.elm = isDef(data) && isDef(i = data.ns) ? document.createElementNS(i, tag) : document.createElement(tag);
+      if (hash < dot) elm.id = sel.slice(hash + 1, dot);
+      if (dotIdx > 0) elm.className = sel.slice(dot + 1).replace(/\./g, ' ');
+      if (is.array(children)) {
+        for (i = 0; i < children.length; ++i) {
+          elm.appendChild(createElm(children[i], insertedVnodeQueue));
+        }
+      } else if (is.primitive(vnode.text)) {
+        elm.appendChild(document.createTextNode(vnode.text));
+      }
+      for (i = 0; i < cbs.create.length; ++i) {
+        cbs.create[i](emptyNode, vnode);
+      }i = vnode.data.hook; // Reuse variable
+      if (isDef(i)) {
+        if (i.create) i.create(emptyNode, vnode);
+        if (i.insert) insertedVnodeQueue.push(vnode);
+      }
+    } else {
+      elm = vnode.elm = document.createTextNode(vnode.text);
+    }
+    return vnode.elm;
+  }
+
+  function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
+    for (; startIdx <= endIdx; ++startIdx) {
+      parentElm.insertBefore(createElm(vnodes[startIdx], insertedVnodeQueue), before);
+    }
+  }
+
+  function invokeDestroyHook(vnode) {
+    var i = vnode.data,
+        j;
+    if (isDef(i)) {
+      if (isDef(i = i.hook) && isDef(i = i.destroy)) i(vnode);
+      for (i = 0; i < cbs.destroy.length; ++i) {
+        cbs.destroy[i](vnode);
+      }if (isDef(i = vnode.children)) {
+        for (j = 0; j < vnode.children.length; ++j) {
+          invokeDestroyHook(vnode.children[j]);
+        }
+      }
+    }
+  }
+
+  function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
+    for (; startIdx <= endIdx; ++startIdx) {
+      var i,
+          listeners,
+          rm,
+          ch = vnodes[startIdx];
+      if (isDef(ch)) {
+        if (isDef(ch.sel)) {
+          invokeDestroyHook(ch);
+          listeners = cbs.remove.length + 1;
+          rm = createRmCb(ch.elm, listeners);
+          for (i = 0; i < cbs.remove.length; ++i) {
+            cbs.remove[i](ch, rm);
+          }if (isDef(i = ch.data) && isDef(i = i.hook) && isDef(i = i.remove)) {
+            i(ch, rm);
+          } else {
+            rm();
+          }
+        } else {
+          // Text node
+          parentElm.removeChild(ch.elm);
+        }
+      }
+    }
+  }
+
+  function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
+    var oldStartIdx = 0,
+        newStartIdx = 0;
+    var oldEndIdx = oldCh.length - 1;
+    var oldStartVnode = oldCh[0];
+    var oldEndVnode = oldCh[oldEndIdx];
+    var newEndIdx = newCh.length - 1;
+    var newStartVnode = newCh[0];
+    var newEndVnode = newCh[newEndIdx];
+    var oldKeyToIdx, idxInOld, elmToMove, before;
+
+    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
+      if (isUndef(oldStartVnode)) {
+        oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
+      } else if (isUndef(oldEndVnode)) {
+          oldEndVnode = oldCh[--oldEndIdx];
+        } else if (sameVnode(oldStartVnode, newStartVnode)) {
+          patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
+          oldStartVnode = oldCh[++oldStartIdx];
+          newStartVnode = newCh[++newStartIdx];
+        } else if (sameVnode(oldEndVnode, newEndVnode)) {
+          patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
+          oldEndVnode = oldCh[--oldEndIdx];
+          newEndVnode = newCh[--newEndIdx];
+        } else if (sameVnode(oldStartVnode, newEndVnode)) {
+          // Vnode moved right
+          patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
+          parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling);
+          oldStartVnode = oldCh[++oldStartIdx];
+          newEndVnode = newCh[--newEndIdx];
+        } else if (sameVnode(oldEndVnode, newStartVnode)) {
+          // Vnode moved left
+          patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
+          parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
+          oldEndVnode = oldCh[--oldEndIdx];
+          newStartVnode = newCh[++newStartIdx];
+        } else {
+          if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
+          idxInOld = oldKeyToIdx[newStartVnode.key];
+          if (isUndef(idxInOld)) {
+            // New element
+            parentElm.insertBefore(createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
+            newStartVnode = newCh[++newStartIdx];
+          } else {
+            elmToMove = oldCh[idxInOld];
+            patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
+            oldCh[idxInOld] = undefined;
+            parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm);
+            newStartVnode = newCh[++newStartIdx];
+          }
+        }
+    }
+    if (oldStartIdx > oldEndIdx) {
+      before = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
+      addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
+    } else if (newStartIdx > newEndIdx) {
+      removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
+    }
+  }
+
+  function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
+    var i, hook;
+    if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
+      i(oldVnode, vnode);
+    }
+    if (isDef(i = oldVnode.data) && isDef(i = i.vnode)) oldVnode = i;
+    if (isDef(i = vnode.data) && isDef(i = i.vnode)) vnode = i;
+    var elm = vnode.elm = oldVnode.elm,
+        oldCh = oldVnode.children,
+        ch = vnode.children;
+    if (oldVnode === vnode) return;
+    if (isDef(vnode.data)) {
+      for (i = 0; i < cbs.update.length; ++i) {
+        cbs.update[i](oldVnode, vnode);
+      }i = vnode.data.hook;
+      if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
+    }
+    if (isUndef(vnode.text)) {
+      if (isDef(oldCh) && isDef(ch)) {
+        if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue);
+      } else if (isDef(ch)) {
+        addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
+      } else if (isDef(oldCh)) {
+        removeVnodes(elm, oldCh, 0, oldCh.length - 1);
+      }
+    } else if (oldVnode.text !== vnode.text) {
+      elm.textContent = vnode.text;
+    }
+    if (isDef(hook) && isDef(i = hook.postpatch)) {
+      i(oldVnode, vnode);
+    }
+  }
+
+  return function (oldVnode, vnode) {
+    var i;
+    var insertedVnodeQueue = [];
+    for (i = 0; i < cbs.pre.length; ++i) {
+      cbs.pre[i]();
+    }if (oldVnode instanceof Element) {
+      if (oldVnode.parentElement !== null) {
+        createElm(vnode, insertedVnodeQueue);
+        oldVnode.parentElement.replaceChild(vnode.elm, oldVnode);
+      } else {
+        oldVnode = emptyNodeAt(oldVnode);
+        patchVnode(oldVnode, vnode, insertedVnodeQueue);
+      }
+    } else {
+      patchVnode(oldVnode, vnode, insertedVnodeQueue);
+    }
+    for (i = 0; i < insertedVnodeQueue.length; ++i) {
+      insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
+    }
+    for (i = 0; i < cbs.post.length; ++i) {
+      cbs.post[i]();
+    }return vnode;
+  };
+}
+
+module.exports = { init: init };
+
+},{"./is":2,"./vnode":7}],7:[function(require,module,exports){
+"use strict";
+
+module.exports = function (sel, data, children, text, elm) {
+  var key = data === undefined ? undefined : data.key;
+  return { sel: sel, data: data, children: children,
+    text: text, elm: elm, key: key };
+};
+
+},{}],8:[function(require,module,exports){
+'use strict';
+
+var snabbdom = require('../../snabbdom.js');
+var patch = snabbdom.init([require('../../modules/attributes'), require('../../modules/style'), require('../../modules/eventlisteners')]);
+var h = require('../../h.js');
+
+var vnode;
+
+var data = {
+	degRotation: 0
+};
+
+function gRotation() {
+	//console.log("gRotation: %s", data.degRotation);
+	return "rotate(" + data.degRotation + "deg)";
+}
+
+function triangleClick(id) {
+	console.log("triangleClick: %s", id);
+	render();
+}
+
+function handleRotate(degs) {
+	data.degRotation += degs;
+	console.log("handleRotate: %s, %s", degs, data.degRotation);
+	render();
+}
+
+function handleReset(degs) {
+	data.degRotation = degs;
+	console.log("handleReset: %s", degs);
+	render();
+}
+
+function render() {
+	vnode = patch(vnode, view(data));
+}
+
+var hTriangle = function hTriangle(id, degRotation) {
+	return h("polygon#" + id, {
+		attrs: {
+			points: "-50,-88 0,-175 50,-88",
+			transform: "rotate(" + degRotation + ")",
+			"stroke-width": 3
+		},
+		on: { click: [triangleClick, id] }
+	});
+};
+
+var view = function view(data) {
+	return h("div.view", [h("h1", "Snabbdom SVG Carousel"), h("svg", { attrs: { width: 380, height: 380, viewBox: [-190, -190, 380, 380] } }, [h("g#carousel", { style: { "-webkit-transform": gRotation(), transform: gRotation() } }, [hTriangle("yellow", 0), hTriangle("green", 60), hTriangle("magenta", 120), hTriangle("red", 180), hTriangle("cyan", 240), hTriangle("blue", 300)])]), h("button", { on: { click: [handleRotate, 60] } }, "Rotate Clockwise"), h("button", { on: { click: [handleRotate, -60] } }, "Rotate Anticlockwise"), h("button", { on: { click: [handleReset, 0] } }, "Reset")]);
+};
+
+window.addEventListener("DOMContentLoaded", function () {
+	var container = document.getElementById("container");
+	vnode = patch(container, view(data));
+	render();
+});
+
+},{"../../h.js":1,"../../modules/attributes":3,"../../modules/eventlisteners":4,"../../modules/style":5,"../../snabbdom.js":6}]},{},[8]);
diff --git a/examples/carousel-svg/index.html b/examples/carousel-svg/index.html
new file mode 100644
index 0000000..3b81d87
--- /dev/null
+++ b/examples/carousel-svg/index.html
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+	<meta charset="utf-8">
+	<title>Carousel</title>
+	<script type="text/javascript" src="build.js"></script>
+	<style type="text/css">
+div.view {
+	margin: 10px;
+}
+h1 {
+	font-size: 24px;
+	color: #505000;
+}
+svg {
+	display: block;
+	margin-bottom: 10px;
+	border: 1px solid gray;
+}
+g#carousel {
+	-webkit-transition: -webkit-transform 1s ease;
+	transition: transform 1s ease;
+}
+polygon {
+	stroke: #808000;
+	transition: fill 0.5s linear;
+}
+polygon#yellow {
+	fill: rgba(255,255,0,0.4);
+}
+polygon#yellow:hover, polygon#yellow:active {
+	fill: yellow;
+}
+polygon#green {
+	fill: rgba(0,128,0,0.4);
+}
+polygon#green:hover, polygon#green:active {
+	fill: green;
+}
+polygon#magenta {
+	fill: rgba(255,0,255,0.4);
+}
+polygon#magenta:hover, polygon#magenta:active {
+	fill: magenta;
+}
+polygon#red {
+	fill: rgba(255,0,0,0.4);
+}
+polygon#red:hover, polygon#red:active {
+	fill: red;
+}
+polygon#cyan {
+	fill: rgba(0,255,255,0.4);
+}
+polygon#cyan:hover, polygon#cyan:active {
+	fill: cyan;
+}
+polygon#blue {
+	fill: rgba(0,0,255,0.4);
+}
+polygon#blue:hover, polygon#blue:active {
+	fill: blue;
+}
+button {
+	font-size: 15px;
+	margin: 0 0.7em 0.7em 0;
+}
+	</style>
+</head>
+<body>
+	<div id="container"></div>
+</body>
+</html>
diff --git a/examples/carousel-svg/script.js b/examples/carousel-svg/script.js
new file mode 100644
index 0000000..d5b7106
--- /dev/null
+++ b/examples/carousel-svg/script.js
@@ -0,0 +1,74 @@
+var snabbdom = require('../../snabbdom.js');
+var patch = snabbdom.init([
+	require('../../modules/attributes'),
+	require('../../modules/style'),
+	require('../../modules/eventlisteners')
+]);
+var h = require('../../h.js');
+
+var vnode;
+
+var data = {
+	degRotation: 0
+};
+
+function gRotation() {
+	//console.log("gRotation: %s", data.degRotation);
+	return "rotate(" + data.degRotation + "deg)";
+}
+
+function triangleClick(id) {
+	console.log("triangleClick: %s", id);
+	render();
+}
+
+function handleRotate(degs) {
+	data.degRotation += degs;
+	console.log("handleRotate: %s, %s", degs, data.degRotation);
+	render();
+}
+
+function handleReset(degs) {
+	data.degRotation = degs;
+	console.log("handleReset: %s", degs);
+	render();
+}
+
+function render() {
+	vnode = patch(vnode, view(data));
+}
+
+const hTriangle = (id, degRotation) =>
+	h("polygon#" + id, {
+		attrs: {
+			points: "-50,-88 0,-175 50,-88",
+			transform: "rotate(" + degRotation + ")",
+			"stroke-width": 3
+		},
+		on: {click: [triangleClick, id]}
+	});
+
+const view = (data) =>
+	h("div.view", [
+		h("h1", "Snabbdom SVG Carousel"),
+		h("svg", {attrs: {width: 380, height: 380, viewBox: [-190, -190, 380, 380]}}, [
+			h("g#carousel",
+				{style: {"-webkit-transform": gRotation(), transform: gRotation()}}, [
+				hTriangle("yellow", 0),
+				hTriangle("green", 60),
+				hTriangle("magenta", 120),
+				hTriangle("red", 180),
+				hTriangle("cyan", 240),
+				hTriangle("blue", 300)
+			])
+		]),
+		h("button", {on: {click: [handleRotate, 60]}}, "Rotate Clockwise"),
+		h("button", {on: {click: [handleRotate, -60]}}, "Rotate Anticlockwise"),
+		h("button", {on: {click: [handleReset, 0]}}, "Reset")
+	]);
+
+window.addEventListener("DOMContentLoaded", () => {
+	var container = document.getElementById("container");
+	vnode = patch(container, view(data));
+	render();
+});

From 71cc36e5326a151ede88307e1978f02716922939 Mon Sep 17 00:00:00 2001
From: jkleiser <jon.kleiser@usit.uio.no>
Date: Mon, 15 Feb 2016 11:05:48 +0100
Subject: [PATCH 2/2] Added a couple of lines to carousel README.md

---
 examples/carousel-svg/README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/examples/carousel-svg/README.md b/examples/carousel-svg/README.md
index d94939a..a5ca07b 100644
--- a/examples/carousel-svg/README.md
+++ b/examples/carousel-svg/README.md
@@ -1,3 +1,7 @@
+This carousel example uses `style transform` and `transition` to rotate a group of SVG triangles.
+
+Also, the color of each triangle changes when you hover or click/tap it.
+
 I built the build.js using npm and browserify. 
 
 In my local copy of the snabbdom project root I did these preparations: