<commit_before>chrome.extension.sendMessage({}, function(response) {
  var tc = {
    settings: {
      speed: 1.0,           // default 1x
      resetSpeed: 1.0,      // default 1x
      speedStep: 0.1,       // default 0.1x
      fastSpeed: 1.8,       // default 1.8x
      rewindTime: 10,       // default 10s
      advanceTime: 10,      // default 10s
      resetKeyCode:  82,    // default: R
      slowerKeyCode: 83,    // default: S
      fasterKeyCode: 68,    // default: D
      rewindKeyCode: 90,    // default: Z
      advanceKeyCode: 88,   // default: X
      displayKeyCode: 86,   // default: V
      fastKeyCode: 71,      // default: G
      rememberSpeed: false, // default: false
      startHidden: false,   // default: false
      blacklist: `
        www.instagram.com
        twitter.com
        vine.co
        imgur.com
      `.replace(/^\s+|\s+$/gm,'')
    }
  };

  chrome.storage.sync.get(tc.settings, function(storage) {
    tc.settings.speed = Number(storage.speed);
    tc.settings.resetSpeed = Number(storage.resetSpeed);
    tc.settings.speedStep = Number(storage.speedStep);
    tc.settings.fastSpeed = Number(storage.fastSpeed);
    tc.settings.rewindTime = Number(storage.rewindTime);
    tc.settings.advanceTime = Number(storage.advanceTime);
    tc.settings.resetKeyCode = Number(storage.resetKeyCode);
    tc.settings.rewindKeyCode = Number(storage.rewindKeyCode);
    tc.settings.slowerKeyCode = Number(storage.slowerKeyCode);
    tc.settings.fasterKeyCode = Number(storage.fasterKeyCode);
    tc.settings.fastKeyCode = Number(storage.fastKeyCode);
    tc.settings.displayKeyCode = Number(storage.displayKeyCode);
    tc.settings.advanceKeyCode = Number(storage.advanceKeyCode);
    tc.settings.rememberSpeed = Boolean(storage.rememberSpeed);
    tc.settings.startHidden = Boolean(storage.startHidden);
    tc.settings.blacklist = String(storage.blacklist);

    initializeWhenReady(document);
  });

  var forEach = Array.prototype.forEach;

  function defineVideoController() {
    tc.videoController = function(target, parent) {
      this.video = target;
      this.parent = target.parentElement || parent;
      this.document = target.ownerDocument;
      this.id = Math.random().toString(36).substr(2, 9);
      if (!tc.settings.rememberSpeed) {
        tc.settings.speed = 1.0;
        tc.settings.resetSpeed = tc.settings.fastSpeed;
      }
      this.initializeControls();

      target.addEventListener('play', function(event) {
        target.playbackRate = tc.settings.speed;
      });

      target.addEventListener('ratechange', function(event) {
        if (target.readyState === 0) {
          return;
        }
        var speed = this.getSpeed();
        this.speedIndicator.textContent = speed;
        tc.settings.speed = speed;
        chrome.storage.sync.set({'speed': speed});
      }.bind(this));

      target.playbackRate = tc.settings.speed;
    };

    tc.videoController.prototype.getSpeed = function() {
      return parseFloat(this.video.playbackRate).toFixed(2);
    }

    tc.videoController.prototype.remove = function() {
      this.parentElement.removeChild(this);
    }

    tc.videoController.prototype.initializeControls = function() {
      var document = this.document;
      var speed = parseFloat(tc.settings.speed).toFixed(2),
        top = Math.max(this.video.offsetTop, 0) + "px",
        left = Math.max(this.video.offsetLeft, 0) + "px";

      var prevent = function(e) {
        e.preventDefault();
        e.stopPropagation();
      }

      var wrapper = document.createElement('div');
      wrapper.classList.add('vsc-controller');
      wrapper.dataset['vscid'] = this.id;
      wrapper.addEventListener('dblclick', prevent, true);
      wrapper.addEventListener('mousedown', prevent, true);
      wrapper.addEventListener('click', prevent, true);

      if (tc.settings.startHidden) {
        wrapper.classList.add('vsc-hidden');
      }

      var shadow = wrapper.createShadowRoot();
      var shadowTemplate = `

          ${speed}

            <button data-action="rewind" class="rw">«</button>
            <button data-action="slower">-</button>
            <button data-action="faster">+</button>
            <button data-action="advance" class="rw">»</button>
            <button data-action="close" class="hideButton">x</button>

      `;
      shadow.innerHTML = shadowTemplate;
      shadow.querySelector('.draggable').addEventListener('mousedown', (e) => {
        runAction(e.target.dataset['action'], document);
      });

      forEach.call(shadow.querySelectorAll('button'), function(button) {
        button.onclick = (e) => {
          runAction(e.target.dataset['action'], document);
        }
      });

      this.speedIndicator = shadow.querySelector('span');
      var fragment = document.createDocumentFragment();
      fragment.appendChild(wrapper);

      // Note: when triggered via a MutationRecord, it's possible that the
      // target is not the immediate parent. This appends the controller as
      // the first element of the target, which may not be the parent.
      this.parent.insertBefore(fragment, this.parent.firstChild);
      this.video.classList.add('vsc-initialized');
      this.video.dataset['vscid'] = this.id;
    }
  }

  function initializeWhenReady(document) {
    escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
    function escapeStringRegExp(str) {
      return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
    }

    var blacklisted = false;
    tc.settings.blacklist.split("\n").forEach(match => {
      match = match.replace(/^\s+|\s+$/g,'')
      if (match.length == 0) {
        return;
      }

      var regexp = new RegExp(escapeStringRegExp(match));
      if (regexp.test(location.href)) {
        blacklisted = true;
        return;
      }
    })

    if (blacklisted)
      return;

    var readyStateCheckInterval = setInterval(function() {
      if (document && document.readyState === 'complete') {
        clearInterval(readyStateCheckInterval);
        initializeNow(document);
      }
    }, 10);
  }

  function initializeNow(document) {
      if (document === window.document) {
        defineVideoController();
      } else {
        var link = document.createElement('link');
        link.href = chrome.extension.getURL('inject.css');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        document.head.appendChild(link);
      }

      document.addEventListener('keydown', function(event) {
        var keyCode = event.keyCode;

        // Ignore if following modifier is active.
        if (event.getModifierState("Alt")
            || event.getModifierState("Control")
            || event.getModifierState("Fn")
            || event.getModifierState("Meta")
            || event.getModifierState("Hyper")
            || event.getModifierState("OS")) {
          return;
        }

        // Ignore keydown event if typing in an input box
        if ((document.activeElement.nodeName === 'INPUT'
              && document.activeElement.getAttribute('type') === 'text')
            || document.activeElement.nodeName === 'TEXTAREA'
            || document.activeElement.isContentEditable) {
          return false;
        }

        if (keyCode == tc.settings.rewindKeyCode) {
          runAction('rewind', document, true)
        } else if (keyCode == tc.settings.advanceKeyCode) {
          runAction('advance', document, true)
        } else if (keyCode == tc.settings.fasterKeyCode) {
          runAction('faster', document, true)
        } else if (keyCode == tc.settings.slowerKeyCode) {
          runAction('slower', document, true)
        } else if (keyCode == tc.settings.resetKeyCode) {
          runAction('reset', document, true)
        } else if (keyCode == tc.settings.displayKeyCode) {
          runAction('display', document, true)
        } else if (keyCode == tc.settings.fastKeyCode) {
          runAction('fast', document, true);
        }

        return false;
      }, true);

      function checkForVideo(node, parent, added) {
        if (node.nodeName === 'VIDEO') {
          if (added) {
            if (!node.dataset['vscid']) {
              new tc.videoController(node, parent);
            }
          } else {
            if (node.classList.contains('vsc-initialized')) {
              let id = node.dataset['vscid'];
              let ctrl = document.querySelector(`div[data-vscid="${id}"]`)
              if (ctrl) {
                node.classList.remove('vsc-initialized');
                delete node.dataset['vscid'];
                ctrl.remove();
              }
            }
          }
        } else if (node.children != undefined) {
          for (var i = 0; i < node.children.length; i++) {
            checkForVideo(node.children[i],
                          node.children[i].parentNode || parent,
                          added);
          }
        }
      }
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
          forEach.call(mutation.addedNodes, function(node) {
            if (typeof node === "function")
              return;
            checkForVideo(node, node.parentNode || mutation.target, true);
          })
          forEach.call(mutation.removedNodes, function(node) {
            if (typeof node === "function")
              return;
            checkForVideo(node, node.parentNode || mutation.target, false);
          })
        });
      });
      observer.observe(document, { childList: true, subtree: true });

      var videoTags = document.getElementsByTagName('video');
      forEach.call(videoTags, function(video) {
        new tc.videoController(video);
      });

      var frameTags = document.getElementsByTagName('iframe');
      forEach.call(frameTags, function(frame) {
        // Ignore frames we don't have permission to access (different origin).
        try { var childDocument = frame.contentDocument } catch (e) { return }
        initializeWhenReady(childDocument);
      });
  }

  function runAction(action, document, keyboard) {
    var videoTags = document.getElementsByTagName('video');
    videoTags.forEach = Array.prototype.forEach;

    videoTags.forEach(function(v) {
      var id = v.dataset['vscid'];
      var controller = document.querySelector(`div[data-vscid="${id}"]`);

      showController(controller);

      if (!v.classList.contains('vsc-cancelled')) {
        if (action === 'rewind') {
          v.currentTime -= tc.settings.rewindTime;
        } else if (action === 'advance') {
          v.currentTime += tc.settings.advanceTime;
        } else if (action === 'faster') {
          // Maximum playback speed in Chrome is set to 16:
          // https://cs.chromium.org/chromium/src/media/blink/webmediaplayer_impl.cc?l=103
          var s = Math.min( (v.playbackRate < 0.1 ? 0.0 : v.playbackRate) + tc.settings.speedStep, 16);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'slower') {
          // Audio playback is cut at 0.05:
          // https://cs.chromium.org/chromium/src/media/filters/audio_renderer_algorithm.cc?l=49
          // Video min rate is 0.0625:
          // https://cs.chromium.org/chromium/src/media/blink/webmediaplayer_impl.cc?l=102
          var s = Math.max(v.playbackRate - tc.settings.speedStep, 0.0625);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'reset') {
          resetSpeed(v, 1.0);
        } else if (action === 'close') {
          v.classList.add('vsc-cancelled');
          controller.remove();
        } else if (action === 'display') {
          controller.classList.add('vsc-manual');
          controller.classList.toggle('vsc-hidden');
        } else if (action === 'drag') {
          handleDrag(v, controller);
        } else if (action === 'fast') {
          resetSpeed(v, tc.settings.fastSpeed);
        }
      }
    });
  }

  function resetSpeed(v, target) {
    if (v.playbackRate === target) {
      v.playbackRate = tc.settings.resetSpeed;
    } else {
      tc.settings.resetSpeed = v.playbackRate;
      chrome.storage.sync.set({'resetSpeed': v.playbackRate});
      v.playbackRate = target;
    }
  }

 function handleDrag(video, controller) {
    const parentElement = controller.parentElement,
      shadowController = controller.shadowRoot.querySelector('#controller');

    video.classList.add('vcs-dragging');
    shadowController.classList.add('dragging');

    const startDragging = (e) => {
      let style = shadowController.style;
      style.left = parseInt(style.left) + e.movementX + 'px';
      style.top  = parseInt(style.top)  + e.movementY + 'px';
    }

    const stopDragging = () => {
      parentElement.removeEventListener('mousemove', startDragging);
      parentElement.removeEventListener('mouseup', stopDragging);
      parentElement.removeEventListener('mouseleave', stopDragging);

      shadowController.classList.remove('dragging');
      video.classList.remove('vcs-dragging');
    }

    parentElement.addEventListener('mouseup',stopDragging);
    parentElement.addEventListener('mouseleave',stopDragging);
    parentElement.addEventListener('mousemove', startDragging);
  }

  var timer;
  var animation = false;
  function showController(controller) {
    controller.classList.add('vcs-show');

    if (animation)
      clearTimeout(timer);

    animation = true;
    timer = setTimeout(function() {
      controller.classList.remove('vcs-show');
      animation = false;
    }, 2000);
  }
});
<commit_msg>Not updating speed correctly #214 (#220)<commit_after>chrome.extension.sendMessage({}, function(response) {
  var tc = {
    settings: {
      speed: 1.0,           // default 1x
      resetSpeed: 1.0,      // default 1x
      speedStep: 0.1,       // default 0.1x
      fastSpeed: 1.8,       // default 1.8x
      rewindTime: 10,       // default 10s
      advanceTime: 10,      // default 10s
      resetKeyCode:  82,    // default: R
      slowerKeyCode: 83,    // default: S
      fasterKeyCode: 68,    // default: D
      rewindKeyCode: 90,    // default: Z
      advanceKeyCode: 88,   // default: X
      displayKeyCode: 86,   // default: V
      fastKeyCode: 71,      // default: G
      rememberSpeed: false, // default: false
      startHidden: false,   // default: false
      blacklist: `
        www.instagram.com
        twitter.com
        vine.co
        imgur.com
      `.replace(/^\s+|\s+$/gm,'')
    }
  };

  chrome.storage.sync.get(tc.settings, function(storage) {
    tc.settings.speed = Number(storage.speed);
    tc.settings.resetSpeed = Number(storage.resetSpeed);
    tc.settings.speedStep = Number(storage.speedStep);
    tc.settings.fastSpeed = Number(storage.fastSpeed);
    tc.settings.rewindTime = Number(storage.rewindTime);
    tc.settings.advanceTime = Number(storage.advanceTime);
    tc.settings.resetKeyCode = Number(storage.resetKeyCode);
    tc.settings.rewindKeyCode = Number(storage.rewindKeyCode);
    tc.settings.slowerKeyCode = Number(storage.slowerKeyCode);
    tc.settings.fasterKeyCode = Number(storage.fasterKeyCode);
    tc.settings.fastKeyCode = Number(storage.fastKeyCode);
    tc.settings.displayKeyCode = Number(storage.displayKeyCode);
    tc.settings.advanceKeyCode = Number(storage.advanceKeyCode);
    tc.settings.rememberSpeed = Boolean(storage.rememberSpeed);
    tc.settings.startHidden = Boolean(storage.startHidden);
    tc.settings.blacklist = String(storage.blacklist);

    initializeWhenReady(document);
  });

  var forEach = Array.prototype.forEach;

  function defineVideoController() {
    tc.videoController = function(target, parent) {
      this.video = target;
      this.parent = target.parentElement || parent;
      this.document = target.ownerDocument;
      this.id = Math.random().toString(36).substr(2, 9);
      if (!tc.settings.rememberSpeed) {
        tc.settings.speed = 1.0;
        tc.settings.resetSpeed = tc.settings.fastSpeed;
      }
      this.initializeControls();

      target.addEventListener('play', function(event) {
        target.playbackRate = tc.settings.speed;
      });

      target.addEventListener('ratechange', function(event) {
        var speed = this.getSpeed();
        this.speedIndicator.textContent = speed;
        tc.settings.speed = speed;
        chrome.storage.sync.set({'speed': speed});
      }.bind(this));

      target.playbackRate = tc.settings.speed;
    };

    tc.videoController.prototype.getSpeed = function() {
      return parseFloat(this.video.playbackRate).toFixed(2);
    }

    tc.videoController.prototype.remove = function() {
      this.parentElement.removeChild(this);
    }

    tc.videoController.prototype.initializeControls = function() {
      var document = this.document;
      var speed = parseFloat(tc.settings.speed).toFixed(2),
        top = Math.max(this.video.offsetTop, 0) + "px",
        left = Math.max(this.video.offsetLeft, 0) + "px";

      var prevent = function(e) {
        e.preventDefault();
        e.stopPropagation();
      }

      var wrapper = document.createElement('div');
      wrapper.classList.add('vsc-controller');
      wrapper.dataset['vscid'] = this.id;
      wrapper.addEventListener('dblclick', prevent, true);
      wrapper.addEventListener('mousedown', prevent, true);
      wrapper.addEventListener('click', prevent, true);

      if (tc.settings.startHidden) {
        wrapper.classList.add('vsc-hidden');
      }

      var shadow = wrapper.createShadowRoot();
      var shadowTemplate = `

          ${speed}

            <button data-action="rewind" class="rw">«</button>
            <button data-action="slower">-</button>
            <button data-action="faster">+</button>
            <button data-action="advance" class="rw">»</button>
            <button data-action="close" class="hideButton">x</button>

      `;
      shadow.innerHTML = shadowTemplate;
      shadow.querySelector('.draggable').addEventListener('mousedown', (e) => {
        runAction(e.target.dataset['action'], document);
      });

      forEach.call(shadow.querySelectorAll('button'), function(button) {
        button.onclick = (e) => {
          runAction(e.target.dataset['action'], document);
        }
      });

      this.speedIndicator = shadow.querySelector('span');
      var fragment = document.createDocumentFragment();
      fragment.appendChild(wrapper);

      // Note: when triggered via a MutationRecord, it's possible that the
      // target is not the immediate parent. This appends the controller as
      // the first element of the target, which may not be the parent.
      this.parent.insertBefore(fragment, this.parent.firstChild);
      this.video.classList.add('vsc-initialized');
      this.video.dataset['vscid'] = this.id;
    }
  }

  function initializeWhenReady(document) {
    escapeStringRegExp.matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
    function escapeStringRegExp(str) {
      return str.replace(escapeStringRegExp.matchOperatorsRe, '\\$&');
    }

    var blacklisted = false;
    tc.settings.blacklist.split("\n").forEach(match => {
      match = match.replace(/^\s+|\s+$/g,'')
      if (match.length == 0) {
        return;
      }

      var regexp = new RegExp(escapeStringRegExp(match));
      if (regexp.test(location.href)) {
        blacklisted = true;
        return;
      }
    })

    if (blacklisted)
      return;

    var readyStateCheckInterval = setInterval(function() {
      if (document && document.readyState === 'complete') {
        clearInterval(readyStateCheckInterval);
        initializeNow(document);
      }
    }, 10);
  }

  function initializeNow(document) {
      if (document === window.document) {
        defineVideoController();
      } else {
        var link = document.createElement('link');
        link.href = chrome.extension.getURL('inject.css');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        document.head.appendChild(link);
      }

      document.addEventListener('keydown', function(event) {
        var keyCode = event.keyCode;

        // Ignore if following modifier is active.
        if (event.getModifierState("Alt")
            || event.getModifierState("Control")
            || event.getModifierState("Fn")
            || event.getModifierState("Meta")
            || event.getModifierState("Hyper")
            || event.getModifierState("OS")) {
          return;
        }

        // Ignore keydown event if typing in an input box
        if ((document.activeElement.nodeName === 'INPUT'
              && document.activeElement.getAttribute('type') === 'text')
            || document.activeElement.nodeName === 'TEXTAREA'
            || document.activeElement.isContentEditable) {
          return false;
        }

        if (keyCode == tc.settings.rewindKeyCode) {
          runAction('rewind', document, true)
        } else if (keyCode == tc.settings.advanceKeyCode) {
          runAction('advance', document, true)
        } else if (keyCode == tc.settings.fasterKeyCode) {
          runAction('faster', document, true)
        } else if (keyCode == tc.settings.slowerKeyCode) {
          runAction('slower', document, true)
        } else if (keyCode == tc.settings.resetKeyCode) {
          runAction('reset', document, true)
        } else if (keyCode == tc.settings.displayKeyCode) {
          runAction('display', document, true)
        } else if (keyCode == tc.settings.fastKeyCode) {
          runAction('fast', document, true);
        }

        return false;
      }, true);

      function checkForVideo(node, parent, added) {
        if (node.nodeName === 'VIDEO') {
          if (added) {
            if (!node.dataset['vscid']) {
              new tc.videoController(node, parent);
            }
          } else {
            if (node.classList.contains('vsc-initialized')) {
              let id = node.dataset['vscid'];
              let ctrl = document.querySelector(`div[data-vscid="${id}"]`)
              if (ctrl) {
                node.classList.remove('vsc-initialized');
                delete node.dataset['vscid'];
                ctrl.remove();
              }
            }
          }
        } else if (node.children != undefined) {
          for (var i = 0; i < node.children.length; i++) {
            checkForVideo(node.children[i],
                          node.children[i].parentNode || parent,
                          added);
          }
        }
      }
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
          forEach.call(mutation.addedNodes, function(node) {
            if (typeof node === "function")
              return;
            checkForVideo(node, node.parentNode || mutation.target, true);
          })
          forEach.call(mutation.removedNodes, function(node) {
            if (typeof node === "function")
              return;
            checkForVideo(node, node.parentNode || mutation.target, false);
          })
        });
      });
      observer.observe(document, { childList: true, subtree: true });

      var videoTags = document.getElementsByTagName('video');
      forEach.call(videoTags, function(video) {
        new tc.videoController(video);
      });

      var frameTags = document.getElementsByTagName('iframe');
      forEach.call(frameTags, function(frame) {
        // Ignore frames we don't have permission to access (different origin).
        try { var childDocument = frame.contentDocument } catch (e) { return }
        initializeWhenReady(childDocument);
      });
  }

  function runAction(action, document, keyboard) {
    var videoTags = document.getElementsByTagName('video');
    videoTags.forEach = Array.prototype.forEach;

    videoTags.forEach(function(v) {
      var id = v.dataset['vscid'];
      var controller = document.querySelector(`div[data-vscid="${id}"]`);

      showController(controller);

      if (!v.classList.contains('vsc-cancelled')) {
        if (action === 'rewind') {
          v.currentTime -= tc.settings.rewindTime;
        } else if (action === 'advance') {
          v.currentTime += tc.settings.advanceTime;
        } else if (action === 'faster') {
          // Maximum playback speed in Chrome is set to 16:
          // https://cs.chromium.org/chromium/src/media/blink/webmediaplayer_impl.cc?l=103
          var s = Math.min( (v.playbackRate < 0.1 ? 0.0 : v.playbackRate) + tc.settings.speedStep, 16);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'slower') {
          // Audio playback is cut at 0.05:
          // https://cs.chromium.org/chromium/src/media/filters/audio_renderer_algorithm.cc?l=49
          // Video min rate is 0.0625:
          // https://cs.chromium.org/chromium/src/media/blink/webmediaplayer_impl.cc?l=102
          var s = Math.max(v.playbackRate - tc.settings.speedStep, 0.0625);
          v.playbackRate = Number(s.toFixed(2));
        } else if (action === 'reset') {
          resetSpeed(v, 1.0);
        } else if (action === 'close') {
          v.classList.add('vsc-cancelled');
          controller.remove();
        } else if (action === 'display') {
          controller.classList.add('vsc-manual');
          controller.classList.toggle('vsc-hidden');
        } else if (action === 'drag') {
          handleDrag(v, controller);
        } else if (action === 'fast') {
          resetSpeed(v, tc.settings.fastSpeed);
        }
      }
    });
  }

  function resetSpeed(v, target) {
    if (v.playbackRate === target) {
      v.playbackRate = tc.settings.resetSpeed;
    } else {
      tc.settings.resetSpeed = v.playbackRate;
      chrome.storage.sync.set({'resetSpeed': v.playbackRate});
      v.playbackRate = target;
    }
  }

 function handleDrag(video, controller) {
    const parentElement = controller.parentElement,
      shadowController = controller.shadowRoot.querySelector('#controller');

    video.classList.add('vcs-dragging');
    shadowController.classList.add('dragging');

    const startDragging = (e) => {
      let style = shadowController.style;
      style.left = parseInt(style.left) + e.movementX + 'px';
      style.top  = parseInt(style.top)  + e.movementY + 'px';
    }

    const stopDragging = () => {
      parentElement.removeEventListener('mousemove', startDragging);
      parentElement.removeEventListener('mouseup', stopDragging);
      parentElement.removeEventListener('mouseleave', stopDragging);

      shadowController.classList.remove('dragging');
      video.classList.remove('vcs-dragging');
    }

    parentElement.addEventListener('mouseup',stopDragging);
    parentElement.addEventListener('mouseleave',stopDragging);
    parentElement.addEventListener('mousemove', startDragging);
  }

  var timer;
  var animation = false;
  function showController(controller) {
    controller.classList.add('vcs-show');

    if (animation)
      clearTimeout(timer);

    animation = true;
    timer = setTimeout(function() {
      controller.classList.remove('vcs-show');
      animation = false;
    }, 2000);
  }
});
<|endoftext|>


<commit_before>'use strict';
var rsz=()=>{
    var c=document.getElementById('c').getContext('2d');
    c.canvas.width=window.innerWidth;
    c.canvas.height=window.innerHeight;
    draw('howdy fsdsdf');
};

var draw=(txt)=>{
    var c=document.getElementById('c').getContext('2d');
    c.fillStyle='rgba(200,200,200,0.99)'; // cheap fade out effect
    c.fillRect(0,0,c.canvas.width,c.canvas.height);
    c.fillStyle='rgba(20,100,131,0.7)';
    c.font='38px monospace';
    c.fillText(txt,50,50);
};

window.onload=()=>{
    var keyset=new Set();
    //var keys=[];
    window.onresize=rsz;
    window.onkeydown=window.onkeyup=(k)=>{
        if(k.type=='keydown')keyset.add(k.code);
        if(k.type=='keyup')keyset.delete(k.code);
        //keys[k.keyCode]=k.type=='keydown';
        draw(k.code+' '+k.type);
        console.log(keyset);
    };
    rsz();
};
<commit_msg>sending keycodes, now need to actually get intended characters<commit_after>'use strict';
var rsz=()=>{
    var c=document.getElementById('c').getContext('2d');
    c.canvas.width=window.innerWidth;
    c.canvas.height=window.innerHeight;
    print('howdy fsdsdf');
};

var decode=(k)=>{
    // given a Set of KeyboardEvent, get the typed character.
};

var cursor={x:50,y:75};
var print=(ks)=>{
    var c=document.getElementById('c').getContext('2d');
    c.fillStyle='rgba(200,200,200,0.99)'; // cheap fade out effect
    c.fillRect(0,0,c.canvas.width,c.canvas.height);
    c.fillStyle='rgba(20,100,131,0.7)';
    c.font='28px monospace';
    c.fillText(ks,cursor.x,cursor.y);
};

window.onload=()=>{
    var keyset=new Set();
    window.onresize=rsz;
    window.onkeydown=window.onkeyup=(k)=>{
        if(k.type=='keydown'){
            var kc=k.keyCode;
            console.log(kc);
            keyset.add(k);
            print(decode(k));
        }
        if(k.type=='keyup')keyset.delete(k);
    };
    rsz();
};
<|endoftext|>


<commit_before>import { Interval } from "luxon";
import PropTypes from "prop-types";
import React, { useMemo } from "react";
import styled from "styled-components";

import { activityStatus } from "@agir/activity/common/helpers";
import { dateFromISOString, displayHumanDate } from "@agir/lib/utils/time";
import { getGenderedWord } from "@agir/lib/utils/display";
import { routeConfig } from "@agir/front/app/routes.config";
import style from "@agir/front/genericComponents/_variables.scss";

import Card from "@agir/front/genericComponents/Card";
import { Column, Row } from "@agir/front/genericComponents/grid";
import EventCard from "@agir/front/genericComponents/EventCard";
import FeatherIcon from "@agir/front/genericComponents/FeatherIcon";
import Link from "@agir/front/app/Link";

import CONFIG from "./activity.config.json";

const CHANGED_DATA_LABEL = {
  name: "nom",
  start_time: "date",
  end_time: "date",
  contact_name: "contact",
  contact_email: "contact",
  contact_phone: "contact",
  location_name: "lieu",
  location_address1: "lieu",
  location_address2: "lieu",
  location_city: "lieu",
  location_zip: "lieu",
  location_country: "lieu",
  description: "présentation",
  facebook: "lien facebook",
};

const StyledParagraph = styled.p`
  margin-bottom: 0;
`;

const LowMarginCard = styled(Card)`
  @media only screen and (min-width: ${style.collapse}px) {
    padding: 0;
    border: none;
  }
  p {
    & > strong,
    & > a {
      font-weight: 600;
      text-decoration: none;
    }
  }
`;

const EventCardContainer = styled.div`
  margin-top: 1rem;

  @media only screen and (min-width: ${style.collapse}px) {
    padding-left: 2.5rem;
    margin-bottom: 1.5rem;
  }

  & ${Card} {
    box-shadow: none;
    border: 1px solid ${style.black100};
  }
`;

const ActivityCardContainer = React.memo((props) => {
  const { status, type, timestamp, event, children } = props;

  const date = useMemo(
    () =>
      displayHumanDate(dateFromISOString(timestamp))
        .split("")
        .map((ch, i) => (i ? ch : ch.toUpperCase()))
        .join(""),
    [timestamp]
  );

  const iconName = useMemo(() => CONFIG[type].icon || "info", [type]);

  const eventSchedule = useMemo(
    () => event && Interval.fromISO(`${event.startTime}/${event.endTime}`),
    [event]
  );

  if (!CONFIG[type]) {
    return null;
  }

  return (
    <LowMarginCard isUnread={status === activityStatus.STATUS_UNDISPLAYED}>
      <Row gutter="8" align="flex-start">
        <Column width="1rem" collapse={0} style={{ paddingTop: "2px" }}>
          <FeatherIcon name={iconName} color={style.black500} />
        </Column>
        <Column collapse={0} grow style={{ fontSize: "15px" }}>

            {date}

        </Column>
      </Row>
      {CONFIG[type].hasEvent && (
        <EventCardContainer>
          <EventCard {...event} schedule={eventSchedule} />
        </EventCardContainer>
      )}
    </LowMarginCard>
  );
});
ActivityCardContainer.displayName = "ActivityCardContainer";
ActivityCardContainer.propTypes = {
  status: PropTypes.oneOf(Object.values(activityStatus)),
  type: PropTypes.string.isRequired,
  event: PropTypes.object, // see event card PropTypes
  timestamp: PropTypes.string.isRequired,
  children: PropTypes.node,
};

const ReferralUpdateActivityText = (props) => {
  const {
    individual,
    meta: { totalReferrals },
    routes,
  } = props;
  if (totalReferrals < 5) {
    return (
      <ActivityCardContainer {...props}>
        Grâce à vous, {individual || "quelqu'un"} a parrainé la candidature de
        Jean-Luc Mélenchon.

        Merci beaucoup, continuez à partager ! 👍
      </ActivityCardContainer>
    );
  }
  if (totalReferrals === 5) {
    return (
      <ActivityCardContainer {...props}>
        5 personnes ont parrainé la candidature de Jean-Luc Mélenchon grâce à
        vous ! La campagne de signature continue, invitez vos amis à partager
        leur lien personnalisé à leur tour !
      </ActivityCardContainer>
    );
  }
  if (totalReferrals < 10) {
    return (
      <ActivityCardContainer {...props}>
        Encore un ! {individual} a parrainé la candidature de Jean-Luc
        Mélenchon.

        C'est super, vous avez fait signer {totalReferrals} personnes !
        Continuez comme ça ! 😀
      </ActivityCardContainer>
    );
  }
  if (totalReferrals === 10) {
    return (
      <ActivityCardContainer {...props}>
        Vous avez permis la signature de 10 personnes ! Quel est votre secret ?!

        Si vous n'y aviez pas encore songé, il est peut-être temps de{" "}

          créer une équipe de soutien dans votre ville
        {" "}
        ;)
      </ActivityCardContainer>
    );
  }
  if (totalReferrals === 20) {
    return (
      <ActivityCardContainer {...props}>
        Grâce à vous, 20 personnes ont parrainé la candidature de Jean-Luc
        Mélenchon !

        Beau travail ! Prochaine étape :{" "}
        organiser un événement en ligne pour
        récolter encore plus de signatures !
      </ActivityCardContainer>
    );
  }
  return (
    <ActivityCardContainer {...props}>
      Et de {totalReferrals} ! {individual} a parrainé la candidature de
      Jean-Luc Mélenchon. Génial ! 😍
    </ActivityCardContainer>
  );
};
ReferralUpdateActivityText.propTypes = {
  individual: PropTypes.node,
  meta: PropTypes.shape({
    totalReferrals: PropTypes.number.isRequired,
  }).isRequired,
  routes: PropTypes.shape({
    createEvent: PropTypes.string,
    createGroup: PropTypes.string,
  }),
};

const ActivityCard = (props) => {
  const { type, meta, event, supportGroup, individual } = props;

  const { Event, SupportGroup, Individual } = useMemo(
    () => ({
      Event: event && (
        <Link
          to={
            routeConfig.eventDetails
              ? routeConfig.eventDetails.getLink({ eventPk: event.id })
              : event.routes.details
          }
        >
          {event.name}
        </Link>
      ),
      SupportGroup: supportGroup && (
        {supportGroup.name}
      ),
      Individual: individual && {individual.firstName},
    }),
    [event, supportGroup, individual]
  );
  const changedDataLabel = useMemo(() => {
    let changedDataLabel = "";
    if (meta && Array.isArray(meta.changed_data)) {
      const labels = [];
      meta.changed_data.forEach((field) => {
        if (
          !!CHANGED_DATA_LABEL[field] &&
          !labels.includes(CHANGED_DATA_LABEL[field])
        ) {
          labels.push(CHANGED_DATA_LABEL[field]);
        }
      });
      changedDataLabel = labels
        .map((label, i) => {
          if (i === 0) {
            return ` de ${label}`;
          }
          if (i === labels.length - 1) {
            return ` et de ${label}`;
          }
          return `, de ${label}`;
        })
        .join("");
    }
    return changedDataLabel;
  }, [meta]);

  if (!CONFIG[type]) {
    return null;
  }

  switch (type) {
    case "waiting-payment":
      return (
        <ActivityCardContainer {...props}>
          Vous n'avez pas encore réglé votre place pour l'événément {Event}
        </ActivityCardContainer>
      );
    case "group-invitation":
      return (
        <ActivityCardContainer {...props}>
          Vous avez été invité⋅e à rejoindre {SupportGroup}
        </ActivityCardContainer>
      );
    case "new-member":
      return (
        <ActivityCardContainer {...props}>
          {Individual || "Quelqu'un"} a rejoint {SupportGroup}. Prenez le temps
          de l’accueillir !
        </ActivityCardContainer>
      );
    case "waiting-location-group":
      return (
        <ActivityCardContainer {...props}>
          Précisez la localisation de {SupportGroup}
        </ActivityCardContainer>
      );
    case "group-coorganization-invite":
      return (
        <ActivityCardContainer {...props}>
          {Individual || "Quelqu'un"} a proposé à {SupportGroup} de co-organiser{" "}
          {Event}
        </ActivityCardContainer>
      );
    case "waiting-location-event":
      return (
        <ActivityCardContainer {...props}>
          Précisez la localisation de votre événement : {Event}
        </ActivityCardContainer>
      );
    case "group-coorganization-accepted":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup} a accepté de co-organiser votre événement {Event}
        </ActivityCardContainer>
      );
    case "group-info-update":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup} a été mis à jour
        </ActivityCardContainer>
      );
    case "accepted-invitation-member":
      return (
        <ActivityCardContainer {...props}>
          {Individual || "Quelqu'un"} a rejoint {SupportGroup} en acceptant une
          invitation.
        </ActivityCardContainer>
      );
    case "new-attendee":
      return (
        <ActivityCardContainer {...props}>
          {(individual && individual.firstName) || "Quelqu'un"}{" "}
          s'est {getGenderedWord(individual && individual.gender, "inscrit·e")}{" "}
          à votre événement {Event}
        </ActivityCardContainer>
      );
    case "event-update": {
      return (
        <ActivityCardContainer {...props}>
          Mise à jour : l'événement {Event} auquel vous participez a changé
          {changedDataLabel}.
        </ActivityCardContainer>
      );
    }
    case "new-event-mygroups":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup || Individual || "Quelqu'un"} a publié un nouvel
          événement
        </ActivityCardContainer>
      );
    case "new-report":
      return (
        <ActivityCardContainer {...props}>
          Le compte-rendu de l'événement {Event} a été ajouté par les
          organisateurs
        </ActivityCardContainer>
      );
    case "new-event-aroundme":
      return (
        <ActivityCardContainer {...props}>
          Un nouvel événement a été publié près de chez vous par{" "}
          {SupportGroup || Individual || "quelqu'un"}
        </ActivityCardContainer>
      );
    case "group-coorganization-info":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup} a rejoint l'organisation de l'événement {Event}
        </ActivityCardContainer>
      );
    case "cancelled-event":
      return (
        <ActivityCardContainer {...props}>
          L'événement {Event} a été annulé.
        </ActivityCardContainer>
      );
    case "referral-accepted":
      return <ReferralUpdateActivityText {...props} individual={Individual} />;
    case "transferred-group-member":
      return (
        <ActivityCardContainer {...props}>
          Vous avez été transféré·e de « {meta && meta.oldGroup}
           » et avez rejoint {SupportGroup}.

          Votre nouvelle équipe vous attend !
        </ActivityCardContainer>
      );
    case "new-members-through-transfer":
      return (
        <ActivityCardContainer {...props}>
          {meta && meta.transferredMemberships} membre
          {meta && meta.transferredMemberships > 0 ? "s" : ""} ont rejoint{" "}
          {supportGroup.name} suite à
          un transfert depuis «
          {meta && meta.oldGroup} ».
        </ActivityCardContainer>
      );
    default:
      return null;
  }
};
ActivityCard.propTypes = {
  type: PropTypes.string,
  event: PropTypes.object,
  supportGroup: PropTypes.object,
  individual: PropTypes.object,
  routes: PropTypes.object,
  meta: PropTypes.object,
};

export default ActivityCard;
<commit_msg>feat: update ActivityCard link color<commit_after>import { Interval } from "luxon";
import PropTypes from "prop-types";
import React, { useMemo } from "react";
import styled from "styled-components";

import { activityStatus } from "@agir/activity/common/helpers";
import { dateFromISOString, displayHumanDate } from "@agir/lib/utils/time";
import { getGenderedWord } from "@agir/lib/utils/display";
import { routeConfig } from "@agir/front/app/routes.config";
import style from "@agir/front/genericComponents/_variables.scss";

import Card from "@agir/front/genericComponents/Card";
import { Column, Row } from "@agir/front/genericComponents/grid";
import EventCard from "@agir/front/genericComponents/EventCard";
import FeatherIcon from "@agir/front/genericComponents/FeatherIcon";
import Link from "@agir/front/app/Link";

import CONFIG from "./activity.config.json";

const CHANGED_DATA_LABEL = {
  name: "nom",
  start_time: "date",
  end_time: "date",
  contact_name: "contact",
  contact_email: "contact",
  contact_phone: "contact",
  location_name: "lieu",
  location_address1: "lieu",
  location_address2: "lieu",
  location_city: "lieu",
  location_zip: "lieu",
  location_country: "lieu",
  description: "présentation",
  facebook: "lien facebook",
};

const StyledParagraph = styled.p`
  margin-bottom: 0;
`;

const LowMarginCard = styled(Card)`
  @media only screen and (min-width: ${style.collapse}px) {
    padding: 0;
    border: none;
  }
  p {
    & > strong,
    & > a {
      font-weight: 600;
      text-decoration: none;
    }

    & > a {
      color: ${style.primary500};
    }
  }
`;

const EventCardContainer = styled.div`
  margin-top: 1rem;

  @media only screen and (min-width: ${style.collapse}px) {
    padding-left: 2.5rem;
    margin-bottom: 1.5rem;
  }

  & ${Card} {
    box-shadow: none;
    border: 1px solid ${style.black100};
  }
`;

const ActivityCardContainer = React.memo((props) => {
  const { status, type, timestamp, event, children } = props;

  const date = useMemo(
    () =>
      displayHumanDate(dateFromISOString(timestamp))
        .split("")
        .map((ch, i) => (i ? ch : ch.toUpperCase()))
        .join(""),
    [timestamp]
  );

  const iconName = useMemo(() => CONFIG[type].icon || "info", [type]);

  const eventSchedule = useMemo(
    () => event && Interval.fromISO(`${event.startTime}/${event.endTime}`),
    [event]
  );

  if (!CONFIG[type]) {
    return null;
  }

  return (
    <LowMarginCard isUnread={status === activityStatus.STATUS_UNDISPLAYED}>
      <Row gutter="8" align="flex-start">
        <Column width="1rem" collapse={0} style={{ paddingTop: "2px" }}>
          <FeatherIcon name={iconName} color={style.black500} />
        </Column>
        <Column collapse={0} grow style={{ fontSize: "15px" }}>

            {date}

        </Column>
      </Row>
      {CONFIG[type].hasEvent && (
        <EventCardContainer>
          <EventCard {...event} schedule={eventSchedule} />
        </EventCardContainer>
      )}
    </LowMarginCard>
  );
});
ActivityCardContainer.displayName = "ActivityCardContainer";
ActivityCardContainer.propTypes = {
  status: PropTypes.oneOf(Object.values(activityStatus)),
  type: PropTypes.string.isRequired,
  event: PropTypes.object, // see event card PropTypes
  timestamp: PropTypes.string.isRequired,
  children: PropTypes.node,
};

const ReferralUpdateActivityText = (props) => {
  const {
    individual,
    meta: { totalReferrals },
    routes,
  } = props;
  if (totalReferrals < 5) {
    return (
      <ActivityCardContainer {...props}>
        Grâce à vous, {individual || "quelqu'un"} a parrainé la candidature de
        Jean-Luc Mélenchon.

        Merci beaucoup, continuez à partager ! 👍
      </ActivityCardContainer>
    );
  }
  if (totalReferrals === 5) {
    return (
      <ActivityCardContainer {...props}>
        5 personnes ont parrainé la candidature de Jean-Luc Mélenchon grâce à
        vous ! La campagne de signature continue, invitez vos amis à partager
        leur lien personnalisé à leur tour !
      </ActivityCardContainer>
    );
  }
  if (totalReferrals < 10) {
    return (
      <ActivityCardContainer {...props}>
        Encore un ! {individual} a parrainé la candidature de Jean-Luc
        Mélenchon.

        C'est super, vous avez fait signer {totalReferrals} personnes !
        Continuez comme ça ! 😀
      </ActivityCardContainer>
    );
  }
  if (totalReferrals === 10) {
    return (
      <ActivityCardContainer {...props}>
        Vous avez permis la signature de 10 personnes ! Quel est votre secret ?!

        Si vous n'y aviez pas encore songé, il est peut-être temps de{" "}

          créer une équipe de soutien dans votre ville
        {" "}
        ;)
      </ActivityCardContainer>
    );
  }
  if (totalReferrals === 20) {
    return (
      <ActivityCardContainer {...props}>
        Grâce à vous, 20 personnes ont parrainé la candidature de Jean-Luc
        Mélenchon !

        Beau travail ! Prochaine étape :{" "}
        organiser un événement en ligne pour
        récolter encore plus de signatures !
      </ActivityCardContainer>
    );
  }
  return (
    <ActivityCardContainer {...props}>
      Et de {totalReferrals} ! {individual} a parrainé la candidature de
      Jean-Luc Mélenchon. Génial ! 😍
    </ActivityCardContainer>
  );
};
ReferralUpdateActivityText.propTypes = {
  individual: PropTypes.node,
  meta: PropTypes.shape({
    totalReferrals: PropTypes.number.isRequired,
  }).isRequired,
  routes: PropTypes.shape({
    createEvent: PropTypes.string,
    createGroup: PropTypes.string,
  }),
};

const ActivityCard = (props) => {
  const { type, meta, event, supportGroup, individual } = props;

  const { Event, SupportGroup, Individual } = useMemo(
    () => ({
      Event: event && (
        <Link
          to={
            routeConfig.eventDetails
              ? routeConfig.eventDetails.getLink({ eventPk: event.id })
              : event.routes.details
          }
        >
          {event.name}
        </Link>
      ),
      SupportGroup: supportGroup && (
        {supportGroup.name}
      ),
      Individual: individual && {individual.firstName},
    }),
    [event, supportGroup, individual]
  );
  const changedDataLabel = useMemo(() => {
    let changedDataLabel = "";
    if (meta && Array.isArray(meta.changed_data)) {
      const labels = [];
      meta.changed_data.forEach((field) => {
        if (
          !!CHANGED_DATA_LABEL[field] &&
          !labels.includes(CHANGED_DATA_LABEL[field])
        ) {
          labels.push(CHANGED_DATA_LABEL[field]);
        }
      });
      changedDataLabel = labels
        .map((label, i) => {
          if (i === 0) {
            return ` de ${label}`;
          }
          if (i === labels.length - 1) {
            return ` et de ${label}`;
          }
          return `, de ${label}`;
        })
        .join("");
    }
    return changedDataLabel;
  }, [meta]);

  if (!CONFIG[type]) {
    return null;
  }

  switch (type) {
    case "waiting-payment":
      return (
        <ActivityCardContainer {...props}>
          Vous n'avez pas encore réglé votre place pour l'événément {Event}
        </ActivityCardContainer>
      );
    case "group-invitation":
      return (
        <ActivityCardContainer {...props}>
          Vous avez été invité⋅e à rejoindre {SupportGroup}
        </ActivityCardContainer>
      );
    case "new-member":
      return (
        <ActivityCardContainer {...props}>
          {Individual || "Quelqu'un"} a rejoint {SupportGroup}. Prenez le temps
          de l’accueillir !
        </ActivityCardContainer>
      );
    case "waiting-location-group":
      return (
        <ActivityCardContainer {...props}>
          Précisez la localisation de {SupportGroup}
        </ActivityCardContainer>
      );
    case "group-coorganization-invite":
      return (
        <ActivityCardContainer {...props}>
          {Individual || "Quelqu'un"} a proposé à {SupportGroup} de co-organiser{" "}
          {Event}
        </ActivityCardContainer>
      );
    case "waiting-location-event":
      return (
        <ActivityCardContainer {...props}>
          Précisez la localisation de votre événement : {Event}
        </ActivityCardContainer>
      );
    case "group-coorganization-accepted":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup} a accepté de co-organiser votre événement {Event}
        </ActivityCardContainer>
      );
    case "group-info-update":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup} a été mis à jour
        </ActivityCardContainer>
      );
    case "accepted-invitation-member":
      return (
        <ActivityCardContainer {...props}>
          {Individual || "Quelqu'un"} a rejoint {SupportGroup} en acceptant une
          invitation.
        </ActivityCardContainer>
      );
    case "new-attendee":
      return (
        <ActivityCardContainer {...props}>
          {(individual && individual.firstName) || "Quelqu'un"}{" "}
          s'est {getGenderedWord(individual && individual.gender, "inscrit·e")}{" "}
          à votre événement {Event}
        </ActivityCardContainer>
      );
    case "event-update": {
      return (
        <ActivityCardContainer {...props}>
          Mise à jour : l'événement {Event} auquel vous participez a changé
          {changedDataLabel}.
        </ActivityCardContainer>
      );
    }
    case "new-event-mygroups":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup || Individual || "Quelqu'un"} a publié un nouvel
          événement
        </ActivityCardContainer>
      );
    case "new-report":
      return (
        <ActivityCardContainer {...props}>
          Le compte-rendu de l'événement {Event} a été ajouté par les
          organisateurs
        </ActivityCardContainer>
      );
    case "new-event-aroundme":
      return (
        <ActivityCardContainer {...props}>
          Un nouvel événement a été publié près de chez vous par{" "}
          {SupportGroup || Individual || "quelqu'un"}
        </ActivityCardContainer>
      );
    case "group-coorganization-info":
      return (
        <ActivityCardContainer {...props}>
          {SupportGroup} a rejoint l'organisation de l'événement {Event}
        </ActivityCardContainer>
      );
    case "cancelled-event":
      return (
        <ActivityCardContainer {...props}>
          L'événement {Event} a été annulé.
        </ActivityCardContainer>
      );
    case "referral-accepted":
      return <ReferralUpdateActivityText {...props} individual={Individual} />;
    case "transferred-group-member":
      return (
        <ActivityCardContainer {...props}>
          Vous avez été transféré·e de « {meta && meta.oldGroup}
           » et avez rejoint {SupportGroup}.

          Votre nouvelle équipe vous attend !
        </ActivityCardContainer>
      );
    case "new-members-through-transfer":
      return (
        <ActivityCardContainer {...props}>
          {meta && meta.transferredMemberships} membre
          {meta && meta.transferredMemberships > 0 ? "s" : ""} ont rejoint{" "}
          {supportGroup.name} suite à
          un transfert depuis «
          {meta && meta.oldGroup} ».
        </ActivityCardContainer>
      );
    default:
      return null;
  }
};
ActivityCard.propTypes = {
  type: PropTypes.string,
  event: PropTypes.object,
  supportGroup: PropTypes.object,
  individual: PropTypes.object,
  routes: PropTypes.object,
  meta: PropTypes.object,
};

export default ActivityCard;
<|endoftext|>


<commit_before>require('bonescript');

outputPin = bone.P8_3;
inputPin = bone.P8_5;
outputPin2 = bone.P8_7;
inputPin2 = bone.P8_9;
ledPin = bone.USR3;
ledPin2 = bone.USR2;
mydelay = 100;
mydelay2 = 33;
blink2Loop = 0;

setup = function() {
    console.log('Please connect ' + inputPin.key + ' to ' + outputPin.key +
        ' with a 1kohm resistor');
    console.log('Please connect ' + inputPin2.key + ' to ' + outputPin2.key +
        ' with a 1kohm resistor');
    pinMode(inputPin, INPUT);
    pinMode(outputPin, OUTPUT);
    digitalWrite(outputPin, LOW);
    pinMode(ledPin, OUTPUT);
    pinMode(inputPin2, INPUT);
    pinMode(outputPin2, OUTPUT);
    digitalWrite(outputPin2, LOW);
    pinMode(ledPin2, OUTPUT);
    attachInterrupt(inputPin, function(x) {
        digitalWrite(ledPin, x.value);
    }, CHANGE);
    attachInterrupt(inputPin2, function(x) {
        digitalWrite(ledPin2, x.value);
    }, CHANGE);
    var blink2 = function() {
        digitalWrite(outputPin2, HIGH);
        delay(mydelay2);
        digitalWrite(outputPin2, LOW);
        delay(mydelay2);
    };
    function toggleBlink2() {
        if(blink2Loop) {
            console.log('Disabling toggle2');
            removeLoop(blink2Loop);
            blink2Loop = 0;
        } else {
            console.log('Enabling toggle2');
            blink2Loop = addLoop(blink2);
        }
        setTimeout(toggleBlink2, 10000);
    };
    setTimeout(toggleBlink2, 10000);
    blink2Loop = addLoop(blink2);
};

loop = function() {
    digitalWrite(outputPin, HIGH);
    delay(mydelay);
    digitalWrite(outputPin, LOW);
    delay(mydelay);
};
<commit_msg>input2.js: updated style<commit_after>var b = require('bonescript');

var outputPin = "P8_13";
var inputPin = "P8_14";
var outputPin2 = "P8_15";
var inputPin2 = "P8_16";
var ledPin = "USR3";
var ledPin2 = "USR2";
var mydelay = 100;
var mydelay2 = 33;
var toggleState = b.LOW;
var toggleState2 = b.LOW;

console.log('Please connect ' + inputPin + ' to ' + outputPin +
    ' with a 1kohm resistor');
console.log('Please connect ' + inputPin2 + ' to ' + outputPin2 +
    ' with a 1kohm resistor');
b.pinMode(inputPin, b.INPUT);
b.pinMode(outputPin, b.OUTPUT);
b.pinMode(ledPin, b.OUTPUT);
b.pinMode(inputPin2, b.INPUT);
b.pinMode(outputPin2, b.OUTPUT);
b.pinMode(ledPin2, b.OUTPUT);
b.digitalWrite(outputPin, b.LOW);
b.digitalWrite(outputPin2, b.LOW);
b.attachInterrupt(inputPin, inputHandler, b.CHANGE);
b.attachInterrupt(inputPin2, inputHandler2, b.CHANGE);
toggle();
toggle2();

function inputHandler(x) {
    b.digitalWrite(ledPin, x.value);
}

function inputHandler2(x) {
    b.digitalWrite(ledPin2, x.value);
}

function toggle() {
    toggleState = (toggleState == b.LOW) ? b.HIGH : b.LOW;
    b.digitalWrite(outputPin, toggleState);
    setTimeout(toggle, mydelay);
}

function toggle2() {
    toggleState2 = (toggleState2 == b.LOW) ? b.HIGH : b.LOW;
    b.digitalWrite(outputPin2, toggleState2);
    setTimeout(toggle2, mydelay2);
}
<|endoftext|>


<commit_before>MeteorApp = {};

MeteorApp.Card = new Mongo.Collection("Card");
MeteorApp.Action = new Mongo.Collection("Action");

Meteor.methods({
    moveCreature: function(id, position) {
        var card = MeteorApp.Card.findOne({_id: id});
        card.x = position.x;
        card.y = position.y;

        MeteorApp.Card.update(id, card);

        MeteorApp.Action.insert({
            type: 'Backend:creatureMoved',
            params: {
                id: id,
                position: position
            }
        });
    },

    removeCreature: function(id) {
        MeteorApp.Card.remove(id);

        MeteorApp.Action.insert({
            type: 'Backend:creatureRemoved',
            params: {
                id: id
            }
        });
    }
});
<commit_msg>refactor<commit_after>MeteorApp = {};

MeteorApp.Card = new Mongo.Collection("Card");
MeteorApp.Action = new Mongo.Collection("Action");

Meteor.methods({
    moveCard: function(id, position) {
        var card = MeteorApp.Card.findOne({_id: id});
        card.x = position.x;
        card.y = position.y;

        MeteorApp.Card.update(id, card);

        MeteorApp.Action.insert({
            type: 'Backend:cardMoved',
            params: {
                id: id,
                position: position
            }
        });
    },

    removeCard: function(id) {
        MeteorApp.Card.remove(id);

        MeteorApp.Action.insert({
            type: 'Backend:cardRemoved',
            params: {
                id: id
            }
        });
    }
});
<|endoftext|>


<commit_before>var n2kMappings = require("./n2kMappings.js").mappings;
var signalkSchema = require("signalk-schema");
var through = require('through');
var debug = require('debug')('signalk:n2k-signalk')

var toDelta = function(n2k) {
  var theMappings = n2kMappings[n2k.pgn];
  var result = {
    updates: [{
      source: {
        label: '',
        type: 'NMEA2000',
        pgn: Number(n2k.pgn),
        src: n2k.src.toString()
      },
      timestamp: n2k.timestamp,
      values: toValuesArray(theMappings, n2k)
    }]
  };
  if (typeof theMappings != 'undefined') {
    theMappings.forEach(function(mapping) {
      if (typeof mapping.context === 'function') {
        result.context = mapping.context(n2k);
      }
    });
  }
  return result;
}

function getValue(n2k, theMapping) {
  if (typeof theMapping.source != 'undefined') {
    var stringValue = n2k.fields[theMapping.source];
    var numberValue = Number(stringValue);
    return isNaN(numberValue) ? stringValue : numberValue;
  } else {
    return theMapping.value(n2k);
  }
}

var toValuesArray = function(theMappings, n2k) {
  if (typeof theMappings != 'undefined') {
    return theMappings
      .filter(function(theMapping) {
        try {
          return typeof theMapping.filter === 'undefined' || theMapping.filter(n2k);
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
          return false;
        }
      })
      .map(function(theMapping) {
        try {
          if (typeof theMapping.node != 'undefined') {
            return {
              path: theMapping.node,
              value: getValue(n2k, theMapping)
            }
          }
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
        }
      })
      .filter(function(x) {
        return x != undefined;
      });
  }
  return [];
}

var addToTree = function(pathValue, source, tree) {
  var result = {};
  var temp = tree;
  var parts = msg.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    temp[parts[i]] = {};
    temp = temp[parts[i]];
  }
  temp[parts[parts.length - 1]] = msg;
  return result;
}

function addAsNested(pathValue, source, timestamp, result) {
  var temp = result;
  var parts = pathValue.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    if (typeof temp[parts[i]] === 'undefined') {
      temp[parts[i]] = {};
    }
    temp = temp[parts[i]];
  };

  //mapping produced an object like {latitude:...,longitude:...}
  if (typeof pathValue.value === 'object') {
    temp[parts[parts.length - 1]] = pathValue.value;
    temp[parts[parts.length - 1]].source = source;
    temp[parts[parts.length - 1]].timestamp = timestamp + '';
  } else {
    temp[parts[parts.length - 1]] = {
      value: pathValue.value,
      source: source,
      timestamp: timestamp + ''
    };
  }
}

exports.toDelta = toDelta;
exports.toNested = function(n2k) {
  var delta = toDelta(n2k);
  if (!delta.context) {
    delta.context = "vessels." + signalkSchema.fakeMmsiId;
  }
  return signalkSchema.deltaToFull(delta).vessels[delta.context.split('.')[1]];
}

exports.toDeltaTransformer = function(options) {
  var stream = through(function(data) {
    if (options.debug) {
      console.log(data);
    }
    stream.queue(exports.toDelta(data));
  });
  return stream;
}

exports.toNestedTransformer = function(options) {
  var stream = through(function(data) {
    if (options.debug) {
      console.log(data);
    }
    var nested = exports.toNested(data);
    if (Object.getOwnPropertyNames(nested).length > 0) {
      stream.queue(nested);
    }
  });
  return stream;
}
<commit_msg>Fix missing cog in 129026<commit_after>var n2kMappings = require("./n2kMappings.js").mappings;
var signalkSchema = require("signalk-schema");
var through = require('through');
var debug = require('debug')('signalk:n2k-signalk')

var toDelta = function(n2k) {
  var theMappings = n2kMappings[n2k.pgn];
  var result = {
    updates: [{
      source: {
        label: '',
        type: 'NMEA2000',
        pgn: Number(n2k.pgn),
        src: n2k.src.toString()
      },
      timestamp: n2k.timestamp,
      values: toValuesArray(theMappings, n2k)
    }]
  };
  if (typeof theMappings != 'undefined') {
    theMappings.forEach(function(mapping) {
      if (typeof mapping.context === 'function') {
        result.context = mapping.context(n2k);
      }
    });
  }
  return result;
}

function getValue(n2k, theMapping) {
  if (typeof theMapping.source != 'undefined') {
    var stringValue = n2k.fields[theMapping.source];
    var numberValue = Number(stringValue);
    return isNaN(numberValue) ? stringValue : numberValue;
  } else {
    return theMapping.value(n2k);
  }
}

var toValuesArray = function(theMappings, n2k) {
  if (typeof theMappings != 'undefined') {
    return theMappings
      .filter(function(theMapping) {
        try {
          return typeof theMapping.filter === 'undefined' || theMapping.filter(n2k);
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
          return false;
        }
      })
      .map(function(theMapping) {
        try {
          if (typeof theMapping.node != 'undefined') {
            return {
              path: theMapping.node,
              value: getValue(n2k, theMapping)
            }
          }
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
        }
      })
      .filter(function(x) {
        return x != undefined && !(x.value == null);
      });
  }
  return [];
}

var addToTree = function(pathValue, source, tree) {
  var result = {};
  var temp = tree;
  var parts = msg.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    temp[parts[i]] = {};
    temp = temp[parts[i]];
  }
  temp[parts[parts.length - 1]] = msg;
  return result;
}

function addAsNested(pathValue, source, timestamp, result) {
  var temp = result;
  var parts = pathValue.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    if (typeof temp[parts[i]] === 'undefined') {
      temp[parts[i]] = {};
    }
    temp = temp[parts[i]];
  };

  //mapping produced an object like {latitude:...,longitude:...}
  if (typeof pathValue.value === 'object') {
    temp[parts[parts.length - 1]] = pathValue.value;
    temp[parts[parts.length - 1]].source = source;
    temp[parts[parts.length - 1]].timestamp = timestamp + '';
  } else {
    temp[parts[parts.length - 1]] = {
      value: pathValue.value,
      source: source,
      timestamp: timestamp + ''
    };
  }
}

exports.toDelta = toDelta;
exports.toNested = function(n2k) {
  var delta = toDelta(n2k);
  if (!delta.context) {
    delta.context = "vessels." + signalkSchema.fakeMmsiId;
  }
  return signalkSchema.deltaToFull(delta).vessels[delta.context.split('.')[1]];
}

exports.toDeltaTransformer = function(options) {
  var stream = through(function(data) {
    if (options.debug) {
      console.log(data);
    }
    stream.queue(exports.toDelta(data));
  });
  return stream;
}

exports.toNestedTransformer = function(options) {
  var stream = through(function(data) {
    if (options.debug) {
      console.log(data);
    }
    var nested = exports.toNested(data);
    if (Object.getOwnPropertyNames(nested).length > 0) {
      stream.queue(nested);
    }
  });
  return stream;
}
<|endoftext|>