<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) {
        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>special-case inject prototype for FB and amazon<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);

      this.video.classList.add('vsc-initialized');
      this.video.dataset['vscid'] = this.id;

      switch (location.hostname) {
        case 'www.amazon.com':
          // insert before parent to bypass overlay
          this.parent.parentElement.insertBefore(fragment, this.parent);
          break;

        case 'www.facebook.com':
          // set stacking context to same as parent's parent.
          // + default fallthrough
          this.parent.style.zIndex = 'auto';

        default:
          // 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);
      }
    }
  }

  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>var Latexdown={
  delay: 150,        // delay after keystroke before updating

  previewid: null,
  eventtapid: null,
  preview: null,     // filled in by Init below

  timeout: null,     // store setTimout id
  mjRunning: false,  // true when MathJax is processing
  oldText: null,     // used to check if an update is needed

  Init: function (pid,eid) {
    this.previewid=pid;
    this.eventtabid=eid;
    this.preview=document.getElementById(this.previewid);
  },
  Update: function () {
    if (this.timeout) {clearTimeout(this.timeout)}
    this.timeout = setTimeout(this.callback,this.delay);
  },
  PreviewHtml: function () {
    this.mjRunning = false;
  },
  PreviewMark: function () {
    this.mjRunning = false;
    text = this.preview.innerHTML;
    text = text.replace(/^>/mg, '>');
    this.preview.innerHTML = marked (text);
  },
  IsMarkdown: function(url){
    ext = url.split('.').pop().toLowerCase();
    if ($.inArray(ext, ['md', 'markdown', 'mdown', 'mkdn', 'mkd', 'mdtxt', 'mdtext']) == -1) {
        return false;
    }
    else{
        return true;
    }
  },
  UpdateTab: function(url , pane){
    var _this=this;
    $.get(url, function(data) {
        //this.timeout = null;
        if (_this.mjRunning) return;
        var text = data;
        if (text === _this.oldtext) return;
        _this.preview.innerHTML = _this.oldtext = text;
        _this.mjRunning = true;
        console.log(url);
        if(_this.IsMarkdown(url)  ){
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,_this.preview],["PreviewMark",_this]);
        }
        else{
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,_this.preview],["PreviewHTML",_this]);
        }
        makerbutton();
        pane.tab('show');
    });
  }
};

Latexdown.callback=MathJax.Callback(["UpdateTab",Latexdown]);
Latexdown.callback.autoReset = true;

<commit_msg>Update updatetab.js<commit_after>var Latexdown={
  delay: 50,        // delay after keystroke before updating

  previewid: null,
  bufferbid: null,
  eventtabid: null,
  preview: null,     // filled in by Init below
  buffer: null,

  timeout: null,     // store setTimout id
  mjRunning: false,  // true when MathJax is processing
  oldText: null,     // used to check if an update is needed

  Init: function (pid,bid,eid) {
    this.previewid=pid;
    this.bufferid=bid;
    this.eventtabid=eid;
    this.preview=document.getElementById(this.previewid);
    this.buffer=document.getElementById(this.bufferid);
  },
  SwapBuffers: function () {
    var buffer = this.preview, preview = this.buffer;
    this.buffer = buffer; this.preview = preview;
    buffer.style.display = "none";
    buffer.style.position = "absolute";
    preview.style.position = "";
    preview.style.display = "";
  },
  Update: function () {
    if (this.timeout) {clearTimeout(this.timeout)}
    this.timeout = setTimeout(this.callback,this.delay);
  },
  PreviewHtml: function () {
    this.mjRunning = false;
    this.SwapBuffers();
  },
  PreviewMark: function () {
    this.mjRunning = false;
    text = this.buffer.innerHTML;
    text = text.replace(/^>/mg, '>');
    this.buffer.innerHTML = marked (text);
    this.SwapBuffers();

  },
  IsMarkdown: function(url){
    ext = url.split('.').pop().toLowerCase();
    if ($.inArray(ext, ['md', 'markdown', 'mdown', 'mkdn', 'mkd', 'mdtxt', 'mdtext']) == -1) {
        return false;
    }
    else{
        return true;
    }
  },
  UpdateTab: function(url , pane){
    var _this=this;
    $.get(url, function(data) {
        //this.timeout = null;
        if (_this.mjRunning) return;
        var text = data;
        if (text === _this.oldtext) return;
        text = this.Escape(text);
        _this.buffer.innerHTML = _this.oldtext = text;
        _this.mjRunning = true;
        if(_this.IsMarkdown(url)  ){
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,_this.buffer],["PreviewMark",_this]);
        }
        else{
            MathJax.Hub.Queue(["Typeset",MathJax.Hub,_this.buffer],["PreviewHTML",_this]);
        }
        makerbutton();
        pane.tab('show');
    });
  },
  Escape: function (html, encode) {
    return html
      .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"')
     .replace(/'/g, ''');
  }
};

Latexdown.callback=MathJax.Callback(["UpdateTab",Latexdown]);
Latexdown.callback.autoReset = true;

<|endoftext|>


<commit_before>import $ from 'jquery';
import app from '../app';
import { shell } from 'electron';
import Backbone from 'backbone';
import { getPageContainer } from './selectors';
import TorExternalLinkWarning from '../views/modals/TorExternalLinkWarning';

// todo: check args and write unit test
// http://stackoverflow.com/a/21627295/632806
export function isScrolledIntoView(element) {
  let rect = element.getBoundingClientRect();
  const top = rect.top;
  const height = rect.height;
  let el = element.parentNode;

  do {
    rect = el.getBoundingClientRect();

    if (top <= rect.bottom === false) return false;

    // Check if the element is out of view due to a container scrolling
    if ((top + height) <= rect.top) return false;

    el = el.parentNode;
  } while (el !== document.body);
  // Check its within the document viewport
  return top <= document.documentElement.clientHeight;
}

// http://stackoverflow.com/a/41426040/632806
export function insertAtCursor(myField, myValue) {
  if (document.selection) {
    // IE support
    myField.focus();
    const sel = document.selection.createRange();
    sel.text = myValue;
  } else if (window.navigator.userAgent.indexOf('Edge') > -1) {
    // Microsoft Edge
    const startPos = myField.selectionStart;
    const endPos = myField.selectionEnd;

    myField.value = myField.value.substring(0, startPos) + myValue
      + myField.value.substring(endPos, myField.value.length);

    const pos = startPos + myValue.length;
    myField.focus();
    myField.setSelectionRange(pos, pos);
  } else if (myField.selectionStart || myField.selectionStart === '0') {
    // MOZILLA and others
    const startPos = myField.selectionStart;
    const endPos = myField.selectionEnd;
    myField.value = myField.value.substring(0, startPos)
      + myValue
      + myField.value.substring(endPos, myField.value.length);
  } else {
    myField.value += myValue;
  }
}

/**
 * Returns a string of text with html stripped out.
 * (https://stackoverflow.com/a/822486/632806)
 */
export function stripHtml(text) {
  if (typeof text !== 'string') {
    throw new Error('Please provide text as a string.');
  }

  const el = document.createElement('div');
  el.innerHTML = text;
  return el.textContent || el.innerText || '';
}

export function openExternal(href) {
  if (typeof href !== 'string') {
    throw new Error('Please provide a valid href as string.');
  }

  const activeServer = app.serverConfigs.activeServer;
  const localSettings = app.localSettings;
  const warningOptedOut = app.localSettings &&
    localSettings.get('dontShowTorExternalLinkWarning');

  if (activeServer && activeServer.get('useTor') && !warningOptedOut) {
    const warningModal = new TorExternalLinkWarning({ url: href })
      .render()
      .open();

    warningModal.on('cancelClick', () => warningModal.close());
    warningModal.on('confirmClick', () => {
      shell.openExternal(href);
      warningModal.close();
    });
  } else {
    shell.openExternal(href);
  }
}

/**
 * For most cases, this handler will be able to identify an external link because
 * it will be prefaced with an "external" protocol (e.g. http, ftp). An exception
 * to this is any user based url (e.g. www.espn.com). In that case, add a
 * 'data-open-external' attribute to the url to force it to be opened externally.
 */
export function handleLinks(el) {
  $(el).on('click', 'a:not([data-bypass])', (e) => {
    const $a = $(e.target).closest('a');
    const openExternally = $a.data('openExternal') !== undefined;
    let href = $a.attr('href');

    // Anchor without href is likely being handled programmatically.
    if (!href) return;

    const link = document.createElement('a');
    link.setAttribute('href', href);

    if (link.protocol !== location.protocol || openExternally) {
      if (link.protocol === 'ob:' && !openExternally) {
        Backbone.history.navigate(href.slice(5), true);
      } else {
        openExternal(link.protocol === 'file:' ? `http://${href}` : link.href);
      }
    } else {
      if (!href.startsWith('#')) {
        href = `#${href}`;
      }

      Backbone.history.navigate(href, true);
    }

    e.preventDefault();
  });
}

/**
 * This will scroll the pageContainer to the top of the contentFrame. Use this when calling
 * scrollIntoView on a page view's root element doesn't scroll the pageContainer to zero.
 */
export function scrollPageIntoView() {
  getPageContainer()[0].scrollIntoView();
}
<commit_msg>Prevent External Link Warning in Selectize<commit_after>import $ from 'jquery';
import app from '../app';
import { shell } from 'electron';
import Backbone from 'backbone';
import { getPageContainer } from './selectors';
import TorExternalLinkWarning from '../views/modals/TorExternalLinkWarning';

// todo: check args and write unit test
// http://stackoverflow.com/a/21627295/632806
export function isScrolledIntoView(element) {
  let rect = element.getBoundingClientRect();
  const top = rect.top;
  const height = rect.height;
  let el = element.parentNode;

  do {
    rect = el.getBoundingClientRect();

    if (top <= rect.bottom === false) return false;

    // Check if the element is out of view due to a container scrolling
    if ((top + height) <= rect.top) return false;

    el = el.parentNode;
  } while (el !== document.body);
  // Check its within the document viewport
  return top <= document.documentElement.clientHeight;
}

// http://stackoverflow.com/a/41426040/632806
export function insertAtCursor(myField, myValue) {
  if (document.selection) {
    // IE support
    myField.focus();
    const sel = document.selection.createRange();
    sel.text = myValue;
  } else if (window.navigator.userAgent.indexOf('Edge') > -1) {
    // Microsoft Edge
    const startPos = myField.selectionStart;
    const endPos = myField.selectionEnd;

    myField.value = myField.value.substring(0, startPos) + myValue
      + myField.value.substring(endPos, myField.value.length);

    const pos = startPos + myValue.length;
    myField.focus();
    myField.setSelectionRange(pos, pos);
  } else if (myField.selectionStart || myField.selectionStart === '0') {
    // MOZILLA and others
    const startPos = myField.selectionStart;
    const endPos = myField.selectionEnd;
    myField.value = myField.value.substring(0, startPos)
      + myValue
      + myField.value.substring(endPos, myField.value.length);
  } else {
    myField.value += myValue;
  }
}

/**
 * Returns a string of text with html stripped out.
 * (https://stackoverflow.com/a/822486/632806)
 */
export function stripHtml(text) {
  if (typeof text !== 'string') {
    throw new Error('Please provide text as a string.');
  }

  const el = document.createElement('div');
  el.innerHTML = text;
  return el.textContent || el.innerText || '';
}

export function openExternal(href) {
  if (typeof href !== 'string') {
    throw new Error('Please provide a valid href as string.');
  }

  const activeServer = app.serverConfigs.activeServer;
  const localSettings = app.localSettings;
  const warningOptedOut = app.localSettings &&
    localSettings.get('dontShowTorExternalLinkWarning');

  if (activeServer && activeServer.get('useTor') && !warningOptedOut) {
    const warningModal = new TorExternalLinkWarning({ url: href })
      .render()
      .open();

    warningModal.on('cancelClick', () => warningModal.close());
    warningModal.on('confirmClick', () => {
      shell.openExternal(href);
      warningModal.close();
    });
  } else {
    shell.openExternal(href);
  }
}

/**
 * For most cases, this handler will be able to identify an external link because
 * it will be prefaced with an "external" protocol (e.g. http, ftp). An exception
 * to this is any user based url (e.g. www.espn.com). In that case, add a
 * 'data-open-external' attribute to the url to force it to be opened externally.
 */
export function handleLinks(el) {
  $(el).on('click', 'a:not([data-bypass])', (e) => {
    const $a = $(e.target).closest('a');
    const openExternally = $a.data('openExternal') !== undefined;
    let href = $a.attr('href');

    // Anchor without href is likely being handled programmatically.
    if (!href) return;

    // Ignore javascript:void(0) links such as in selectize's close buttons.
    if (href.startsWith('javascript')) return;

    const link = document.createElement('a');
    link.setAttribute('href', href);

    if (link.protocol !== location.protocol || openExternally) {
      if (link.protocol === 'ob:' && !openExternally) {
        Backbone.history.navigate(href.slice(5), true);
      } else {
        openExternal(link.protocol === 'file:' ? `http://${href}` : link.href);
      }
    } else {
      if (!href.startsWith('#')) {
        href = `#${href}`;
      }

      Backbone.history.navigate(href, true);
    }

    e.preventDefault();
  });
}

/**
 * This will scroll the pageContainer to the top of the contentFrame. Use this when calling
 * scrollIntoView on a page view's root element doesn't scroll the pageContainer to zero.
 */
export function scrollPageIntoView() {
  getPageContainer()[0].scrollIntoView();
}
<|endoftext|>


<commit_before>define(function(require) {

  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var EntriesView = require('views/entries');
  var EditorView = require('views/editor');
  var docs = require('collections/docs');
  var cache = require('utils/cache');
  var settings = require('models/settings');

  var noDocs = function() {
    return docs.length < 2 && cache.openDoc.isEmpty();
  };

  var AppView = Backbone.View.extend({

    el: 'body',

    initialize: function() {
      this.editor = new EditorView();
      this.entries = new EntriesView();

      this.$menuButton = $('#menu-button');

      if (cache.isMobile) {

        cache.loading.done(_.bind(function() {
          //only one doc and it is empty
          if ( noDocs() ) {
            this.$menuButton.fadeOut();
          } else {
            this.aside('visible');
          }
        }, this));

      } else {

        this.aside('visible');

        setTimeout(_.bind(function() {
          this.aside('hidden');
        }, this), 3000);

      }

      docs.on('fetch', this.editor.render, this.editor);

      docs.on('change:title', function() {
          setTimeout(_.bind(function() {
            if (cache.isMobile) {
              this.$menuButton[noDocs() ? 'fadeOut' : 'fadeIn']();
            } else {
              this.aside('hidden');
            }
          }, this), 1000);
      }, this);

      var showAside = function() {
        if (!cache.isMobile) this.aside('visible');
      };
      docs.on('add', showAside, this);
      docs.on('fetch', showAside, this);

    },

    aside: (function() {
      var $aside = $('body');
      return function(state) {
        if (!state) {
          $aside.toggleClass('visible');
        } else if (state === 'visible') {
          $aside.addClass('visible');
        } else if (state === 'hidden') {
          //hide sidebar when 3 or more docs and the open doc is not empty
          if (docs.length > 2 && !cache.openDoc.isEmpty()) {
            $aside.removeClass('visible');
          }
        }
      };
    })(),

    events: {
      'click #add': 'newDoc',
      'click #aside': 'toggleAside',
      'click #menu-button': 'toggleAside',
      'keydown': 'handleKey',
      'keyup': 'hideAside'
    },

    newDoc: function(e) {
      e.preventDefault();

      if (!cache.openDoc.isEmpty()) {
        docs.addNew();
      } else {
        this.editor.focus();
      }

    },

    toggleAside: function(e) {
      if (cache.isMobile) {
        e.stopImmediatePropagation();
        this.$menuButton.fadeIn();
        this.aside();
      }
    },

    handleKey: function(e) {
      if (e.which === 9) { //tab
        e.preventDefault();
      } else if (e[cache.modKey.name]) {
        this.aside('visible');
        if (e.which === 78) { //n
          this.newDoc(e);
        } else if (e.which === 38) { //up
          this.openPreviousDoc();
          return false;
        } else if (e.which === 40) { //down
          this.openNextDoc();
          return false;
        }
      }
    },

    hideAside: function(e) {
      if (e.which === (cache.modKey.code)) this.aside('hidden');
    },

    openPreviousDoc: function() {
      var previousDoc = docs.at(docs.indexOf(cache.openDoc) - 1);
      if (previousDoc) {
        settings.save('openDocId', previousDoc.id);
      }
    },

    openNextDoc: function() {
      var nextDoc = docs.at(docs.indexOf(cache.openDoc) + 1);
      if (nextDoc) {
        settings.save('openDocId', nextDoc.id);
      }
    }
  });

  return AppView;
});
<commit_msg>never hide or fade menu button because it just scrolls up, @jorin-vogel please review<commit_after>define(function(require) {

  var $ = require('jquery');
  var _ = require('underscore');
  var Backbone = require('backbone');
  var EntriesView = require('views/entries');
  var EditorView = require('views/editor');
  var docs = require('collections/docs');
  var cache = require('utils/cache');
  var settings = require('models/settings');

  var noDocs = function() {
    return docs.length < 2 && cache.openDoc.isEmpty();
  };

  var AppView = Backbone.View.extend({

    el: 'body',

    initialize: function() {
      this.editor = new EditorView();
      this.entries = new EntriesView();

      if (cache.isMobile) {

        cache.loading.done(_.bind(function() {
          //only one doc and it is empty
          if ( noDocs() ) {
          } else {
            this.aside('visible');
          }
        }, this));

      } else {

        this.aside('visible');

        setTimeout(_.bind(function() {
          this.aside('hidden');
        }, this), 3000);

      }

      docs.on('fetch', this.editor.render, this.editor);

      docs.on('change:title', function() {
          setTimeout(_.bind(function() {
            if (cache.isMobile) {
            } else {
              this.aside('hidden');
            }
          }, this), 1000);
      }, this);

      var showAside = function() {
        if (!cache.isMobile) this.aside('visible');
      };
      docs.on('add', showAside, this);
      docs.on('fetch', showAside, this);

    },

    aside: (function() {
      var $aside = $('body');
      return function(state) {
        if (!state) {
          $aside.toggleClass('visible');
        } else if (state === 'visible') {
          $aside.addClass('visible');
        } else if (state === 'hidden') {
          //hide sidebar when 3 or more docs and the open doc is not empty
          if (docs.length > 2 && !cache.openDoc.isEmpty()) {
            $aside.removeClass('visible');
          }
        }
      };
    })(),

    events: {
      'click #add': 'newDoc',
      'click #aside': 'toggleAside',
      'click #menu-button': 'toggleAside',
      'keydown': 'handleKey',
      'keyup': 'hideAside'
    },

    newDoc: function(e) {
      e.preventDefault();

      if (!cache.openDoc.isEmpty()) {
        docs.addNew();
      } else {
        this.editor.focus();
      }

    },

    toggleAside: function(e) {
      if (cache.isMobile) {
        e.stopImmediatePropagation();
        this.aside();
      }
    },

    handleKey: function(e) {
      if (e.which === 9) { //tab
        e.preventDefault();
      } else if (e[cache.modKey.name]) {
        this.aside('visible');
        if (e.which === 78) { //n
          this.newDoc(e);
        } else if (e.which === 38) { //up
          this.openPreviousDoc();
          return false;
        } else if (e.which === 40) { //down
          this.openNextDoc();
          return false;
        }
      }
    },

    hideAside: function(e) {
      if (e.which === (cache.modKey.code)) this.aside('hidden');
    },

    openPreviousDoc: function() {
      var previousDoc = docs.at(docs.indexOf(cache.openDoc) - 1);
      if (previousDoc) {
        settings.save('openDocId', previousDoc.id);
      }
    },

    openNextDoc: function() {
      var nextDoc = docs.at(docs.indexOf(cache.openDoc) + 1);
      if (nextDoc) {
        settings.save('openDocId', nextDoc.id);
      }
    }
  });

  return AppView;
});
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { useEffect, useRef, useState, useMemo } from 'react';
import styled from 'styled-components';
import { v4 as uuidv4 } from 'uuid';

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import { Section } from '../../../common';
import { UnitsProvider } from '../../../../../units';
import { PAGE_RATIO, TEXT_SET_SIZE } from '../../../../../constants';
import PillGroup from '../../shared/pillGroup';
import { PANE_PADDING } from '../../shared';
import useRovingTabIndex from '../../../../../utils/useRovingTabIndex';
import { getTextSets } from './utils';
import TextSet from './textSet';

const TextSetContainer = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  row-gap: 12px;
  column-gap: 12px;
  margin-top: 28px;
`;

/* Undo the -1.5em set by the Pane */
const CategoryWrapper = styled.div`
  margin-left: -${PANE_PADDING};
  margin-right: -${PANE_PADDING};
`;

const CATEGORIES = {
  contact: __('Contact', 'web-stories'),
  editorial: __('Editorial', 'web-stories'),
  list: __('List', 'web-stories'),
  cover: __('Cover', 'web-stories'),
  section_header: __('Header', 'web-stories'),
  step: __('Steps', 'web-stories'),
  table: __('Table', 'web-stories'),
  quote: __('Quote', 'web-stories'),
};

function TextSets() {
  const [textSets, setTextSets] = useState([]);
  const [selectedCat, setSelectedCat] = useState(null);
  const [filteredTextSets, setFilteredTextSets] = useState([]);
  const [renderedTextSets, setRenderedTextSets] = useState([]);
  const ref = useRef();
  const isLoadingRef = useRef(false);
  const loadingTimeoutRef = useRef(-1);

  const allTextSets = useMemo(() => Object.values(textSets).flat(), [textSets]);
  const categories = useMemo(
    () =>
      Object.keys(textSets).map((cat) => ({
        id: cat,
        label: CATEGORIES[cat] ?? cat,
      })),
    [textSets]
  );

  useEffect(() => {
    getTextSets().then(setTextSets);
  }, []);

  useEffect(() => {
    window.clearTimeout(loadingTimeoutRef.current);
    isLoadingRef.current = false;

    setFilteredTextSets(selectedCat ? textSets[selectedCat] : allTextSets);
    setRenderedTextSets([]);
  }, [selectedCat, textSets, allTextSets]);

  useEffect(() => {
    if (isLoadingRef.current) {
      return;
    }

    if (renderedTextSets.length < filteredTextSets.length) {
      isLoadingRef.current = true;

      loadingTimeoutRef.current = window.setTimeout(() => {
        isLoadingRef.current = false;
        setRenderedTextSets([
          ...renderedTextSets,
          filteredTextSets[renderedTextSets.length],
        ]);
      }, 10);
    }
  }, [renderedTextSets, filteredTextSets]);

  useEffect(() => {
    window.clearTimeout(loadingTimeoutRef.current);
    isLoadingRef.current = false;
  }, []);

  useRovingTabIndex({ ref });

  const sectionId = `section-${uuidv4()}`;
  const title = __('Text Sets', 'web-stories');
  return (

      <CategoryWrapper>
        <PillGroup
          items={categories}
          selectedItemId={selectedCat}
          selectItem={setSelectedCat}
          deselectItem={() => setSelectedCat(null)}
        />
      </CategoryWrapper>
      <TextSetContainer ref={ref} role="list" aria-labelledby={sectionId}>
        <UnitsProvider
          pageSize={{
            width: TEXT_SET_SIZE,
            height: TEXT_SET_SIZE / PAGE_RATIO,
          }}
        >
          {renderedTextSets.map(
            (elements) =>
              elements.length > 0 && (
                <TextSet key={elements[0].id} elements={elements} />
              )
          )}
        </UnitsProvider>
      </TextSetContainer>

  );
}

export default TextSets;
<commit_msg>Removed unneeded units provider<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { useEffect, useRef, useState, useMemo } from 'react';
import styled from 'styled-components';
import { v4 as uuidv4 } from 'uuid';

/**
 * WordPress dependencies
 */
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import { Section } from '../../../common';
import PillGroup from '../../shared/pillGroup';
import { PANE_PADDING } from '../../shared';
import useRovingTabIndex from '../../../../../utils/useRovingTabIndex';
import { getTextSets } from './utils';
import TextSet from './textSet';

const TextSetContainer = styled.div`
  display: grid;
  grid-template-columns: 1fr 1fr;
  row-gap: 12px;
  column-gap: 12px;
  margin-top: 28px;
`;

/* Undo the -1.5em set by the Pane */
const CategoryWrapper = styled.div`
  margin-left: -${PANE_PADDING};
  margin-right: -${PANE_PADDING};
`;

const CATEGORIES = {
  contact: __('Contact', 'web-stories'),
  editorial: __('Editorial', 'web-stories'),
  list: __('List', 'web-stories'),
  cover: __('Cover', 'web-stories'),
  section_header: __('Header', 'web-stories'),
  step: __('Steps', 'web-stories'),
  table: __('Table', 'web-stories'),
  quote: __('Quote', 'web-stories'),
};

function TextSets() {
  const [textSets, setTextSets] = useState([]);
  const [selectedCat, setSelectedCat] = useState(null);
  const [filteredTextSets, setFilteredTextSets] = useState([]);
  const [renderedTextSets, setRenderedTextSets] = useState([]);
  const ref = useRef();
  const isLoadingRef = useRef(false);
  const loadingTimeoutRef = useRef(-1);

  const allTextSets = useMemo(() => Object.values(textSets).flat(), [textSets]);
  const categories = useMemo(
    () =>
      Object.keys(textSets).map((cat) => ({
        id: cat,
        label: CATEGORIES[cat] ?? cat,
      })),
    [textSets]
  );

  useEffect(() => {
    getTextSets().then(setTextSets);
  }, []);

  useEffect(() => {
    window.clearTimeout(loadingTimeoutRef.current);
    isLoadingRef.current = false;

    setFilteredTextSets(selectedCat ? textSets[selectedCat] : allTextSets);
    setRenderedTextSets([]);
  }, [selectedCat, textSets, allTextSets]);

  useEffect(() => {
    if (isLoadingRef.current) {
      return;
    }

    if (renderedTextSets.length < filteredTextSets.length) {
      isLoadingRef.current = true;

      loadingTimeoutRef.current = window.setTimeout(() => {
        isLoadingRef.current = false;
        setRenderedTextSets([
          ...renderedTextSets,
          filteredTextSets[renderedTextSets.length],
        ]);
      }, 10);
    }
  }, [renderedTextSets, filteredTextSets]);

  useEffect(() => {
    window.clearTimeout(loadingTimeoutRef.current);
    isLoadingRef.current = false;
  }, []);

  useRovingTabIndex({ ref });

  const sectionId = `section-${uuidv4()}`;
  const title = __('Text Sets', 'web-stories');
  return (

      <CategoryWrapper>
        <PillGroup
          items={categories}
          selectedItemId={selectedCat}
          selectItem={setSelectedCat}
          deselectItem={() => setSelectedCat(null)}
        />
      </CategoryWrapper>
      <TextSetContainer ref={ref} role="list" aria-labelledby={sectionId}>
        {renderedTextSets.map(
          (elements) =>
            elements.length > 0 && (
              <TextSet key={elements[0].id} elements={elements} />
            )
        )}
      </TextSetContainer>

  );
}

export default TextSets;
<|endoftext|>


<commit_before>/* global angular:false */
'use strict';

/**
 * Define our ndslabs module here. All other files will
 * use the single-argument notation for angular.module()
 */
angular.module('ndslabs', [ 'navbar', 'footer', 'ndslabs-services', 'ndslabs-filters', 'ndslabs-api', 'ngWizard', 'ngGrid', 'ngAlert',
    'ngRoute', 'ngResource', 'ngCookies', 'ngAnimate', 'ngMessages', 'ui.bootstrap', 'ui.pwgen', 'frapontillo.gage' ])

/**
 * If true, display verbose debug data as JSON
 */
.constant('DEBUG', false)

/**
 *TODO: Whether or not to use mock data (false if talking to live etcd)
 */
//.constant('MOCKDATA', false)

/**
 * Make lodash available for injection into controllers
 */
.constant('_', window._)

/**
 * The route to our Login View
 */
.constant('LoginRoute', '/login')

/**
 * The route to our Express Setup View
 */
.constant('ExpressRoute', '/express')

/**
 * The route to our Expert Setup View
 */
.constant('ExpertRoute', '/home')

/**
 * The version/revision of this GUI
 */
.constant('BuildVersion', '0.1alpha')
.constant('BuildDate', new Date())

/**
 * Hostname / Port for communicating with etcd
 *
 * This must be the external IP and nodePort (when running in k8)
 *
 * TODO: We assume this is running on the same machine as the apiserver.
 */
.constant('ApiHost', '141.142.208.127')
.constant('ApiPort', '30001')

/**
 * Logic for communicating with etcd (powered by swagger-js-codegen)
 * @param {string} ApiHost - the hostname defined above
 * @param {string} ApiPort - the port defined above
 * @param {Object} ApiServer - the REST API client generated by swagger; see '/app/shared/NdsLabsRestApi.js'
 */
.factory('NdsLabsApi', [ 'ApiHost', 'ApiPort', 'ApiServer', function(ApiHost, ApiPort, ApiServer) {
  // TODO: Investigate options / caching
  // XXX: Caching may not be possible due to the unique token sent with every request
  return new ApiServer('http://' + ApiHost + ':' + ApiPort);
}])

/**
 * A shared store for our AuthInfo, done as a provider so that we
 * can easily inject it into the .config() block below
 */
.provider('AuthInfo', function() {
  this.authInfo = {
    namespace: '',
    password: '',
    saveCookie: false,
    project: null,
    token: null
  };

  this.$get = function() {
    var authInfo = this.authInfo;
    return {
      get: function() { return authInfo; }
    }
  };
})

/**
 * Configure routes / HTTP for our app using the services defined above
 */
.config([ '$routeProvider', '$httpProvider', 'AuthInfoProvider', 'LoginRoute', 'ExpressRoute', 'ExpertRoute',
    function($routeProvider, $httpProvider, authInfo, LoginRoute, ExpressRoute, ExpertRoute) {
  // Setup default behaviors for encountering errors
  $httpProvider.interceptors.push(['$rootScope', '$cookies', '$q', '$location', '$log', '_', 'DEBUG', 'ApiHost', 'ApiPort', 'AuthInfo',
      function (scope, $cookies, $q, $location, $log, _, DEBUG, ApiHost, ApiPort, AuthInfo) {
    return {
      // Attach our auth token to each outgoing request (to the api server)
      'request': function(config) {
        // If this is a request for our API server
        if (_.includes(config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug('Request:');
          }

          // If this was *not* an attempt to authenticate
          if (!_.includes(config.url, '/authenticate')) {
            // We need to attach our token to this request
            config.headers['Authorization'] = 'Bearer ' + $cookies.get('token');
          }
        }
        return config;
      },
      'requestError': function(rejection) {
        if (_.includes(rejection.config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug('Request Rejection:');
            console.debug(rejection);
          }
        }
        return $q.reject(rejection);
      },
      'response': function(response) {
        // If this is a response from our API server
        if (_.includes(response.config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug("Response:")
            console.debug(response);
          }

          // If this was in response to an /authenticate or /refresh_token request
          if ((_.includes(response.config.url, '/authenticate') && response.config.method === 'POST')
              || (_.includes(response.config.url, '/refresh_token') && response.config.method === 'GET')) {
            // This response should contain a new token, so save it as a cookie
            $cookies.put('token', response.data.token);
          }
        }

        return response;
      },
      'responseError': function(rejection) {
        // If this is a response from our API server
        if (_.includes(rejection.config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug("Response Rejection:");
            console.debug(rejection);
          }

          // Read out the HTTP error code
          var status = rejection.status;

          // Handle HTTP 401: Not Authorized - User needs to provide credentials
          if (status == 401) {
            // TODO: If we want to intercept the route to redirect them after a successful login
            //window.location = "/account/login?redirectUrl=" + Base64.encode(document.URL);

            // Purge current session data
            authInfo.authInfo.token = null;
            $cookies.remove('token');
            $cookies.remove('namespace');

            // Route to Login Page to prompt for credentials
            if ($location.path() !== LoginRoute) {
              $location.path(LoginRoute);
            }
            return $q.reject(rejection);
          }
        }

        // otherwise
        return $q.reject(rejection);
      }
    };
  }]);

  // Setup routes to our different pages
  $routeProvider.when(ExpertRoute, {
    controller: 'ExpertSetupController',
    templateUrl: '/app/expert/expertSetup.html'
  })
  .when(LoginRoute, {
    controller: 'LoginController',
    templateUrl: '/app/login/login.html'
  })
  /*
  .when(ExpressRoute, {
    controller: 'ExpressSetupController',
    templateUrl: '/app/express/expressSetup.html'
  })
  */
  .otherwise({ redirectTo: LoginRoute });
}])

/**
 * Once configured, run this section of code to finish bootstrapping our app
 */
.run([ '$rootScope', '$location', '$log', '$interval', '$cookies', 'AuthInfo', 'LoginRoute', 'ExpertRoute', 'NdsLabsApi',
    function($rootScope, $location, $log, $interval, $cookies, authInfo, LoginRoute, ExpertRoute, NdsLabsApi) {

  // Grab saved auth data from cookies and attempt to use the leftover session
  var token = $cookies.get('token');
  var namespace = $cookies.get('namespace');
  if (token && namespace) {
    // Pull our token / namespace from cookies
    authInfo.get().token = token;
    authInfo.get().namespace = namespace;
  }

  var HomeRoute = ExpertRoute;

  // Make _ bindable in partial views
  // TODO: Investigate performance concerns here...
  $rootScope._ = window._;

  // When user changes routes, check that they are still authed
  $rootScope.$on( "$routeChangeStart", function(event, next, current) {
    // Define the logic for ending a user's session in the browser
    var terminateSession = function() {
      // Purge current session data
      authInfo.get().token = null;
      $cookies.remove('token');
      $cookies.remove('namespace');

      $interval.cancel(checkTokenInterval);
      checkTokenInterval = null;

      // user needs to log in, redirect to /login
      if (next.templateUrl !== "/app/login/login.html") {
        $location.path(LoginRoute);
      }
    };

    // Each minute, check that our token is still valid
    var checkTokenInterval = null;
    var checkToken = function() {
      NdsLabsApi.getCheck_token().then(function() { $log.debug('Token is still valid.'); }, function() {
        $log.error('Token expired, redirecting to login.');
        $interval.cancel(checkTokenInterval);
        checkTokenInterval = null;
        terminateSession();
      });
    };

    // Check if the token is still valid on route changes
    var token = $cookies.get('token');
    if (token) {
      authInfo.get().token = token;
      authInfo.get().namespace = $cookies.get('namespace');
      NdsLabsApi.getRefresh_token().then(function() {
        $log.debug('Token refreshed: ' + authInfo.get().token);

        // Start our token check interval
        checkTokenInterval = $interval(checkToken, 60000);

        // Reroute to /home if necessary
        if (next.templateUrl !== '/app/expert/expertSetup.html') {
          $location.path(HomeRoute);
        }
      }, function() {
        $log.debug('Failed to refresh token!');

        // TODO: Allow login page to reroute user to destination?
        // XXX: This would matter more if there are multiple views
        //authInfo.returnRoute = next.$$route.originalPath;

        terminateSession();
      });
    } else {
      terminateSession();
    }
  });
}]);
<commit_msg>Back out this hard-cody-ness<commit_after>/* global angular:false */
'use strict';

/**
 * Define our ndslabs module here. All other files will
 * use the single-argument notation for angular.module()
 */
angular.module('ndslabs', [ 'navbar', 'footer', 'ndslabs-services', 'ndslabs-filters', 'ndslabs-api', 'ngWizard', 'ngGrid', 'ngAlert',
    'ngRoute', 'ngResource', 'ngCookies', 'ngAnimate', 'ngMessages', 'ui.bootstrap', 'ui.pwgen', 'frapontillo.gage' ])

/**
 * If true, display verbose debug data as JSON
 */
.constant('DEBUG', false)

/**
 *TODO: Whether or not to use mock data (false if talking to live etcd)
 */
//.constant('MOCKDATA', false)

/**
 * Make lodash available for injection into controllers
 */
.constant('_', window._)

/**
 * The route to our Login View
 */
.constant('LoginRoute', '/login')

/**
 * The route to our Express Setup View
 */
.constant('ExpressRoute', '/express')

/**
 * The route to our Expert Setup View
 */
.constant('ExpertRoute', '/home')

/**
 * The version/revision of this GUI
 */
.constant('BuildVersion', '0.1alpha')
.constant('BuildDate', new Date())

/**
 * Hostname / Port for communicating with etcd
 *
 * This must be the external IP and nodePort (when running in k8)
 *
 * TODO: We assume this is running on the same machine as the apiserver.
 */
.constant('ApiHost', '')
.constant('ApiPort', '')

/**
 * Logic for communicating with etcd (powered by swagger-js-codegen)
 * @param {string} ApiHost - the hostname defined above
 * @param {string} ApiPort - the port defined above
 * @param {Object} ApiServer - the REST API client generated by swagger; see '/app/shared/NdsLabsRestApi.js'
 */
.factory('NdsLabsApi', [ 'ApiHost', 'ApiPort', 'ApiServer', function(ApiHost, ApiPort, ApiServer) {
  // TODO: Investigate options / caching
  // XXX: Caching may not be possible due to the unique token sent with every request
  return new ApiServer('http://' + ApiHost + ':' + ApiPort);
}])

/**
 * A shared store for our AuthInfo, done as a provider so that we
 * can easily inject it into the .config() block below
 */
.provider('AuthInfo', function() {
  this.authInfo = {
    namespace: '',
    password: '',
    saveCookie: false,
    project: null,
    token: null
  };

  this.$get = function() {
    var authInfo = this.authInfo;
    return {
      get: function() { return authInfo; }
    }
  };
})

/**
 * Configure routes / HTTP for our app using the services defined above
 */
.config([ '$routeProvider', '$httpProvider', 'AuthInfoProvider', 'LoginRoute', 'ExpressRoute', 'ExpertRoute',
    function($routeProvider, $httpProvider, authInfo, LoginRoute, ExpressRoute, ExpertRoute) {
  // Setup default behaviors for encountering errors
  $httpProvider.interceptors.push(['$rootScope', '$cookies', '$q', '$location', '$log', '_', 'DEBUG', 'ApiHost', 'ApiPort', 'AuthInfo',
      function (scope, $cookies, $q, $location, $log, _, DEBUG, ApiHost, ApiPort, AuthInfo) {
    return {
      // Attach our auth token to each outgoing request (to the api server)
      'request': function(config) {
        // If this is a request for our API server
        if (_.includes(config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug('Request:');
          }

          // If this was *not* an attempt to authenticate
          if (!_.includes(config.url, '/authenticate')) {
            // We need to attach our token to this request
            config.headers['Authorization'] = 'Bearer ' + $cookies.get('token');
          }
        }
        return config;
      },
      'requestError': function(rejection) {
        if (_.includes(rejection.config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug('Request Rejection:');
            console.debug(rejection);
          }
        }
        return $q.reject(rejection);
      },
      'response': function(response) {
        // If this is a response from our API server
        if (_.includes(response.config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug("Response:")
            console.debug(response);
          }

          // If this was in response to an /authenticate or /refresh_token request
          if ((_.includes(response.config.url, '/authenticate') && response.config.method === 'POST')
              || (_.includes(response.config.url, '/refresh_token') && response.config.method === 'GET')) {
            // This response should contain a new token, so save it as a cookie
            $cookies.put('token', response.data.token);
          }
        }

        return response;
      },
      'responseError': function(rejection) {
        // If this is a response from our API server
        if (_.includes(rejection.config.url, ApiHost + ":" + ApiPort)) {
          if (DEBUG) {
            $log.debug("Response Rejection:");
            console.debug(rejection);
          }

          // Read out the HTTP error code
          var status = rejection.status;

          // Handle HTTP 401: Not Authorized - User needs to provide credentials
          if (status == 401) {
            // TODO: If we want to intercept the route to redirect them after a successful login
            //window.location = "/account/login?redirectUrl=" + Base64.encode(document.URL);

            // Purge current session data
            authInfo.authInfo.token = null;
            $cookies.remove('token');
            $cookies.remove('namespace');

            // Route to Login Page to prompt for credentials
            if ($location.path() !== LoginRoute) {
              $location.path(LoginRoute);
            }
            return $q.reject(rejection);
          }
        }

        // otherwise
        return $q.reject(rejection);
      }
    };
  }]);

  // Setup routes to our different pages
  $routeProvider.when(ExpertRoute, {
    controller: 'ExpertSetupController',
    templateUrl: '/app/expert/expertSetup.html'
  })
  .when(LoginRoute, {
    controller: 'LoginController',
    templateUrl: '/app/login/login.html'
  })
  /*
  .when(ExpressRoute, {
    controller: 'ExpressSetupController',
    templateUrl: '/app/express/expressSetup.html'
  })
  */
  .otherwise({ redirectTo: LoginRoute });
}])

/**
 * Once configured, run this section of code to finish bootstrapping our app
 */
.run([ '$rootScope', '$location', '$log', '$interval', '$cookies', 'AuthInfo', 'LoginRoute', 'ExpertRoute', 'NdsLabsApi',
    function($rootScope, $location, $log, $interval, $cookies, authInfo, LoginRoute, ExpertRoute, NdsLabsApi) {

  // Grab saved auth data from cookies and attempt to use the leftover session
  var token = $cookies.get('token');
  var namespace = $cookies.get('namespace');
  if (token && namespace) {
    // Pull our token / namespace from cookies
    authInfo.get().token = token;
    authInfo.get().namespace = namespace;
  }

  var HomeRoute = ExpertRoute;

  // Make _ bindable in partial views
  // TODO: Investigate performance concerns here...
  $rootScope._ = window._;

  // When user changes routes, check that they are still authed
  $rootScope.$on( "$routeChangeStart", function(event, next, current) {
    // Define the logic for ending a user's session in the browser
    var terminateSession = function() {
      // Purge current session data
      authInfo.get().token = null;
      $cookies.remove('token');
      $cookies.remove('namespace');

      $interval.cancel(checkTokenInterval);
      checkTokenInterval = null;

      // user needs to log in, redirect to /login
      if (next.templateUrl !== "/app/login/login.html") {
        $location.path(LoginRoute);
      }
    };

    // Each minute, check that our token is still valid
    var checkTokenInterval = null;
    var checkToken = function() {
      NdsLabsApi.getCheck_token().then(function() { $log.debug('Token is still valid.'); }, function() {
        $log.error('Token expired, redirecting to login.');
        $interval.cancel(checkTokenInterval);
        checkTokenInterval = null;
        terminateSession();
      });
    };

    // Check if the token is still valid on route changes
    var token = $cookies.get('token');
    if (token) {
      authInfo.get().token = token;
      authInfo.get().namespace = $cookies.get('namespace');
      NdsLabsApi.getRefresh_token().then(function() {
        $log.debug('Token refreshed: ' + authInfo.get().token);

        // Start our token check interval
        checkTokenInterval = $interval(checkToken, 60000);

        // Reroute to /home if necessary
        if (next.templateUrl !== '/app/expert/expertSetup.html') {
          $location.path(HomeRoute);
        }
      }, function() {
        $log.debug('Failed to refresh token!');

        // TODO: Allow login page to reroute user to destination?
        // XXX: This would matter more if there are multiple views
        //authInfo.returnRoute = next.$$route.originalPath;

        terminateSession();
      });
    } else {
      terminateSession();
    }
  });
}]);
<|endoftext|>


<commit_before>/*
var ball;

function setup() {
  createCanvas(1024, 600);
  ball = new Ball(60);
}

function draw() {
  background(255,0,0, 50);
  ball.update();
  ball.render();
}
*/

var n = 1000;
var minRad = 10;
var maxRad = 570;
var nfAng;//0.005;
var nfTime = 0.002;
var fillColor = 255;
var acc = 0.07;
var nfAcc;

function setup() {
    createCanvas(1024, 1024);
    background(0);
    noFill();

    nfAng = createSlider(5,100,1);
    nfAcc = createSlider(0,10000,1);
}

function draw()
{
  stroke(fillColor, 25);
  translate(width/2, height/2);
  rotate(radians(frameCount/frameRate));
  beginShape();
  for (var i=0; i<n; i++) {
    var ang = map(i, 0, n, 0, 6.283);
    var rad = map(noise(i*(nfAng.value() / 1000), frameCount*nfTime), 0, 1, minRad, maxRad);
    var x = rad * cos(ang);
    var y = rad * sin(ang);
    curveVertex(x, y);
  }
  endShape(CLOSE);
  nfAng+= nfAcc.value() * 0.000001;
    if (fillColor > 0) {
        fillColor -= acc;
        acc *= 1.0009;
    } else {
        console.log("Finished");
        noLoop();
    }
}
<commit_msg>Update sketch.js<commit_after>/*
var ball;

function setup() {
  createCanvas(1024, 600);
  ball = new Ball(60);
}

function draw() {
  background(255,0,0, 50);
  ball.update();
  ball.render();
}
*/

var n = 1000;
var minRad = 10;
var maxRad = 570;
var nfAng = 0.005;
var nfTime = 0.002;
var fillColor = 255;
var acc = 0.07;
var nfAcc;

function setup() {
    createCanvas(1024, 1024);
    background(0);
    noFill();

    //nfAng = createSlider(5,100,1);
    nfAcc = createSlider(0,10000,1);
}

function draw()
{
  stroke(fillColor, 25);
  translate(width/2, height/2);
  rotate(radians(frameCount/frameRate));
  beginShape();
  for (var i=0; i<n; i++) {
    var ang = map(i, 0, n, 0, 6.283);
    var rad = map(noise(i*(nfAng), frameCount*nfTime), 0, 1, minRad, maxRad);
    var x = rad * cos(ang);
    var y = rad * sin(ang);
    curveVertex(x, y);
  }
  endShape(CLOSE);
  nfAng+= nfAcc.value() * 0.000001;
    if (fillColor > 0) {
        fillColor -= acc;
        acc *= 1.0009;
    } else {
        console.log("Finished");
        noLoop();
    }
}
<|endoftext|>


<commit_before>'use strict';

var bump = require('gulp-bump');
var config = require('../config');

module.exports = function (gulp) {
  gulp.task('bump:patch', function () {
    return gulp.src(config.manifests)
      .pipe(bump({type: 'patch'}))
      .pipe(gulp.destination('./'));
  });

  gulp.task('bump:minor', function () {
    return gulp.src(config.manifests)
      .pipe(bump({type: 'minor'}))
      .pipe(gulp.destination('./'));
  });

  gulp.task('bump:major', function () {
    return gulp.src(config.manifests)
      .pipe(bump({type: 'major'}))
      .pipe(gulp.destination('./'));
  });
};
<commit_msg>fix gulp bump<commit_after>'use strict';

var bump = require('gulp-bump');
var config = require('../config');

module.exports = function (gulp) {
  gulp.task('bump:patch', function () {
    return gulp.src(config.manifests)
      .pipe(bump({type: 'patch'}))
      .pipe(gulp.dest('./'));
  });

  gulp.task('bump:minor', function () {
    return gulp.src(config.manifests)
      .pipe(bump({type: 'minor'}))
      .pipe(gulp.dest('./'));
  });

  gulp.task('bump:major', function () {
    return gulp.src(config.manifests)
      .pipe(bump({type: 'major'}))
      .pipe(gulp.dest('./'));
  });
};
<|endoftext|>


<commit_before>const path = require('path');
const KarmaServer = require('karma').Server;
const globalConfig = require('../global-config');

module.exports = (gulp, options, done) => {

    const files = options.files;
    files.push({ pattern: '**/*.js.map', included: false, watched: false });

    const frameworks = ['mocha', 'chai', 'sinon', 'source-map-support', 'browserify'].concat(options.frameworks || []);
    const plugins = ['karma-mocha-reporter', 'karma-mocha', 'karma-sinon', 'karma-chai', 'karma-electron', 'karma-sauce-launcher', 'karma-browserstack-launcher', 'karma-firefox-launcher', 'karma-safari-launcher', 'karma-chrome-launcher', 'karma-browserify', 'karma-source-map-support'].concat(options.plugins || []);

    const proxies = options.proxies || '';
    const urlRoot = options.proxies ? '/karma/' : '/'; // if  proxy, move karma to another url
    const preprocessors = options.preprocessors || '';
    let karmaConfig = {
        // logLevel: 'LOG_DEBUG', //keep for debugging
        browserConsoleLogOptions: {
            level: 'log',
            terminal: true
        },
        basePath: process.cwd(),
        files,
        frameworks,
        plugins,
        preprocessors,
        port: 9876, //fix port, don't change
        urlRoot,
        proxies,
        browserify: {
            debug: true, // include inline source maps
            transform: ['require-globify'],
            plugin: [['sourcemapify', { 'root': '/' }]]
        },
    };

    const browserStackCustomLaunchers = {
        bs_safari_mac: {
            base: 'BrowserStack',
            browser: 'Safari',
            browser_version: '10.0',
            os: 'OS X',
            os_version: 'Sierra'
        },
        bs_chrome_win: {
            base: 'BrowserStack',
            browser: 'Chrome',
            browser_version: '57.0',
            os: 'Windows',
            os_version: '10'
        },
        bs_firefox_win: {
            base: 'BrowserStack',
            browser: 'Firefox',
            browser_version: '52.0',
            os: 'Windows',
            os_version: '10'
        },
        bs_edge_win: {
            base: 'BrowserStack',
            browser: 'Edge',
            browser_version: '14.0',
            os: 'Windows',
            os_version: '10'
        },
        bs_ie11_win: {
            base: 'BrowserStack',
            browser: 'IE',
            browser_version: '11.0',
            os: 'Windows',
            os_version: '10'
        }
    }

    if (options.browserStack) {
        karmaConfig.browserStack = options.credentials || {
                username: process.env.BROWSERSTACK_USERNAME,
                accessKey: process.env.BROWSERSTACK_ACCESS_KEY
            };
        karmaConfig.customLaunchers = options.customLaunchers ? options.customLaunchers : browserStackCustomLaunchers;
        karmaConfig.browsers = options.browsers ? options.browsers : ['bs_edge_win', 'bs_safari_mac', 'bs_firefox_win', 'bs_ie11_win', 'bs_chrome_win'];
        karmaConfig.reporters = options.reports || ['mocha'];
        karmaConfig.concurrency = options.concurrency || karmaConfig.browsers.length;
        karmaConfig.concurrency = 1; //keep temporary
    } else {
        karmaConfig.browsers = options.browsers || ['Electron'];
        karmaConfig.reporters = options.reports || ['mocha'];
        karmaConfig.singleRun = ! globalConfig.devmode || options.singleRun;
        karmaConfig.singleRun = true; //keep temporary
    }
    karmaConfig.client = {
        captureConsole: true,
        mocha: {
            reporter: 'html',
            timeout: 10000
        }
    };

    const server = new KarmaServer(karmaConfig, () => {
        done();
        process.exit();
    });
    server.start();
};<commit_msg>remove karma-sauce-launcher<commit_after>const path = require('path');
const KarmaServer = require('karma').Server;
const globalConfig = require('../global-config');

module.exports = (gulp, options, done) => {

    const files = options.files;
    files.push({ pattern: '**/*.js.map', included: false, watched: false });

    const frameworks = ['mocha', 'chai', 'sinon', 'source-map-support', 'browserify'].concat(options.frameworks || []);
    const plugins = ['karma-mocha-reporter', 'karma-mocha', 'karma-sinon', 'karma-chai', 'karma-electron', 'karma-browserstack-launcher', 'karma-firefox-launcher', 'karma-safari-launcher', 'karma-chrome-launcher', 'karma-browserify', 'karma-source-map-support'].concat(options.plugins || []);

    const proxies = options.proxies || '';
    const urlRoot = options.proxies ? '/karma/' : '/'; // if  proxy, move karma to another url
    const preprocessors = options.preprocessors || '';
    let karmaConfig = {
        // logLevel: 'LOG_DEBUG', //keep for debugging
        browserConsoleLogOptions: {
            level: 'log',
            terminal: true
        },
        basePath: process.cwd(),
        files,
        frameworks,
        plugins,
        preprocessors,
        port: 9876, //fix port, don't change
        urlRoot,
        proxies,
        browserify: {
            debug: true, // include inline source maps
            transform: ['require-globify'],
            plugin: [['sourcemapify', { 'root': '/' }]]
        },
    };

    const browserStackCustomLaunchers = {
        bs_safari_mac: {
            base: 'BrowserStack',
            browser: 'Safari',
            browser_version: '10.0',
            os: 'OS X',
            os_version: 'Sierra'
        },
        bs_chrome_win: {
            base: 'BrowserStack',
            browser: 'Chrome',
            browser_version: '57.0',
            os: 'Windows',
            os_version: '10'
        },
        bs_firefox_win: {
            base: 'BrowserStack',
            browser: 'Firefox',
            browser_version: '52.0',
            os: 'Windows',
            os_version: '10'
        },
        bs_edge_win: {
            base: 'BrowserStack',
            browser: 'Edge',
            browser_version: '14.0',
            os: 'Windows',
            os_version: '10'
        },
        bs_ie11_win: {
            base: 'BrowserStack',
            browser: 'IE',
            browser_version: '11.0',
            os: 'Windows',
            os_version: '10'
        }
    }

    if (options.browserStack) {
        karmaConfig.browserStack = options.credentials || {
                username: process.env.BROWSERSTACK_USERNAME,
                accessKey: process.env.BROWSERSTACK_ACCESS_KEY
            };
        karmaConfig.customLaunchers = options.customLaunchers ? options.customLaunchers : browserStackCustomLaunchers;
        karmaConfig.browsers = options.browsers ? options.browsers : ['bs_edge_win', 'bs_safari_mac', 'bs_firefox_win', 'bs_ie11_win', 'bs_chrome_win'];
        karmaConfig.reporters = options.reports || ['mocha'];
        karmaConfig.concurrency = options.concurrency || karmaConfig.browsers.length;
        karmaConfig.concurrency = 1; //keep temporary
    } else {
        karmaConfig.browsers = options.browsers || ['Electron'];
        karmaConfig.reporters = options.reports || ['mocha'];
        karmaConfig.singleRun = ! globalConfig.devmode || options.singleRun;
        karmaConfig.singleRun = true; //keep temporary
    }
    karmaConfig.client = {
        captureConsole: true,
        mocha: {
            reporter: 'html',
            timeout: 10000
        }
    };

    const server = new KarmaServer(karmaConfig, () => {
        done();
        process.exit();
    });
    server.start();
};<|endoftext|>