const scrollTolerance = 5;
const activeClass = 'active';

export default function ButtonHandler(el, tapHandler, holdHandler, scrollX, scrollY) {
  var startX, startY, boundaries, active;

  if (typeof tapHandler !== 'function')
    throw new Error('ButtonHandler 2nd argument must be a function!');

  if (holdHandler && typeof holdHandler !== 'function')
    throw new Error('ButtonHandler 3rd argument must be a function!');

  function onTouchStart(e) {
    let touch = e.changedTouches[0];
    let boundingRect = el.getBoundingClientRect();
    startX = touch.clientX;
    startY = touch.clientY;
    boundaries = {
      minX: boundingRect.left,
      maxX: boundingRect.right,
      minY: boundingRect.top,
      maxY: boundingRect.bottom
    };
    active = true;
    setTimeout(() => {
      if (active) el.classList.add(activeClass);
    }, 200);
  }

  function onTouchMove(e) {
    // if going out of bounds, no way to reenable the button
    if (active) {
      var touch = e.changedTouches[0];
      active = isActive(touch);
      if (!active) el.classList.remove(activeClass);
    }
  }

  function onTouchEnd(e) {
    e.preventDefault();
    if (active) {
      el.classList.add(activeClass);
      tapHandler(e);
      active = false;
      setTimeout(() => el.classList.remove(activeClass), 80);
    }
  }

  function onTouchCancel() {
    active = false;
    el.classList.remove(activeClass);
  }

  function onHold(e) {
    e.preventDefault();
    e.stopPropagation();
    if (holdHandler) {
      holdHandler();
      active = false;
      el.classList.remove(activeClass);
    }
  }

  function isActive(touch) {
    var x = touch.clientX,
      y = touch.clientY,
      b = boundaries,
      d = 0;
    if (scrollX) d = Math.abs(x - startX);
    if (scrollY) d = Math.abs(y - startY);
    return x < b.maxX && x > b.minX && y < b.maxY && y > b.minY && d < scrollTolerance;
  }

  el.addEventListener('touchstart', onTouchStart, false);
  el.addEventListener('touchmove', onTouchMove, false);
  el.addEventListener('touchend', onTouchEnd, false);
  el.addEventListener('touchcancel', onTouchCancel, false);
  el.addEventListener('contextmenu', onHold, false);

  return function unbind() {
    el.removeEventListener('touchstart', onTouchStart, false);
    el.removeEventListener('touchmove', onTouchMove, false);
    el.removeEventListener('touchend', onTouchEnd, false);
    el.removeEventListener('touchcancel', onTouchCancel, false);
    el.removeEventListener('contextmenu', onHold, false);
  };
}

@@ -1,12 +1,38 @@
+const holdDuration = 800;%0A
 const scroll
@@ -77,16 +77,93 @@
 tive';%0A%0A
+function hasContextMenu() %7B%0A  return window.cordova.platformId !== 'ios';%0A%7D%0A%0A
 export d
@@ -281,16 +281,31 @@
 , active
+, holdTimeoutID
 ;%0A%0A  if 
@@ -967,16 +967,93 @@
 , 200);%0A
+    if (!hasContextMenu()) holdTimeoutID = setTimeout(onHold, holdDuration);%0A
   %7D%0A%0A  f
@@ -1241,24 +1241,71 @@
 if (!active)
+ %7B%0A        clearTimeout(holdTimeoutID);%0A       
  el.classLis
@@ -1319,32 +1319,40 @@
 e(activeClass);%0A
+      %7D%0A
     %7D%0A  %7D%0A%0A  fun
@@ -1411,24 +1411,59 @@
  (active) %7B%0A
+      clearTimeout(holdTimeoutID);%0A
       el.cla
@@ -1628,24 +1628,57 @@
 hCancel() %7B%0A
+    clearTimeout(holdTimeoutID);%0A
     active =
@@ -1741,20 +1741,27 @@
 ction on
-Hold
+ContextMenu
 (e) %7B%0A  
@@ -1803,24 +1803,98 @@
 pagation();%0A
+    if (holdTimeoutID === undefined) onHold();%0A  %7D%0A%0A  function onHold() %7B%0A
     if (hold
@@ -2552,36 +2552,43 @@
 contextmenu', on
-Hold
+ContextMenu
 , false);%0A%0A  ret
@@ -2903,20 +2903,27 @@
 enu', on
-Hold
+ContextMenu
 , false)

Add support for touch hold on iOS