<commit_before>/*
 * This script can be imported into any IPython notebook (assumes the web UI)
 * to allow embedding the Tree Illustrator webapp, where it can read data from
 * the surrounding notebook and save results there.
 *
 * This should be useful in pre-publication and exploratory scenarios for a
 * single user, or fairly easy collaboration using Wakari or another notebook
 * server.
 *
 * There are a few options for where the app would appear:
 *
 *   1. in a modal popup (in an IFRAME)
 *      This works best in a live (vs static HTML) notebook, where we an add a
 *      toolbar button and use IPython's built-in modal.
 *
 *   2. "inline" in a notebook cell (in an IFRAME)
 *      This makes a "static widget" in IPython parlance, so it should work
 *      even in a static HTML notebook. This allows multiple instances of Tree
 *      Illustrator to exist in different cells; is that useful?
 *
 *   3. in a new browser tab or window (esp. for static notebooks)
 *      This would need an additional bridge from the calling window to the new
 *      one. On the plus side, it should work in a live *or* static notebook.
 *
 * In any case, we'll use cross-document messaging (postMessage) to provide
 * tree data from the surrounding IPython session and to save SVG output (or
 * complete illustration JSON) from the Tree Illustrator. [1,2]
 *
 * To use this, add a code cell to your IPython notebook and embed this script
 *   %%javascript
 *   $.getScript('https://rawgit.com/opentreeoflife/f16e.../raw/48b.../ipynb-tree-illustrator.js');
 *   ti = IPythonTreeIllustrator.IllustratorWidget();
 *   ti2 = IPythonTreeIllustrator.IllustratorWidget('(a,b,(c,d));');
 *
 * [1] https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
 * [2] http://caniuse.com/#search=postMessage
 */

var IPythonTreeIllustrator = function(window, document, $) {

    // Are we running in a "live" notebook, or static HTML?
    var isLiveNotebook = $('body.notebook_app').length > 0;
    var isStaticNotebook = !(isLiveNotebook);

    // Define some enumerated values for callers.
    var SINGLETON = 'SINGLETON';
    var TOOLBAR_BUTTON_ID = 'ti-toolbar-button';

    // Keep track of all active instances (widgets), keyed by element ID
    var widgets = { };

    // Assign unique, serial element IDs
    var nextAvailableWidgetID = 0;

    var getNextAvailableWidgetID = function( ) {
        // creates a serial ID like 'tree-illustrator-4'
        var readyID = nextAvailableWidgetID;
        nextAvailableWidgetID = readyID + 1;
        return ('tree-illustrator-'+ readyID);
    }

    var IllustratorWidget = function(target, data) {
        if ( !(this instanceof IllustratorWidget) ) {
            console.warn("MISSING 'new' keyword for IllustratorWidget, patching this now");
            return new IllustratorWidget(data);
        }

        // Safely refer to this instance below
        var self = this;
        var elementID = getNextAvailableWidgetID();

        /* define PRIVATE members (variables and functions ) with 'var' */

        var getIframeMarkup = function() {
            // TODO: add version/SHA argument here?
            return ' \
                    ';
        }

        var showInNewWindow = function() {
            // TODO: Show the Tree Illustrator in a new browser window or tab, with a link
            // back to the calling window.
            alert('showInNewWindow(): COMING SOON');
        }

        var showInNotebookCell = function(cell) {
            // create my IFRAME element in the output of the current notebook cell

            // N.B. This ID is mostly for internal use; user probably calls this something else
            cell.append_display_data({
              'data': {
                'text/html': getIframeMarkup()
              }
            })
        }

        var showInModalPopup = function(data) {
            // Use IPython's support for a single modal popup, adapted from
            // https://github.com/minrk/ipython_extensions/blob/70ed77bd7fd36fbead09a1df41f93cab5cfdfe92/nbextensions/gist.js
            //var modal = IPython.dialog.parentModule.modal({
            var dialog = require("base/js/dialog");
            var modal = dialog.modal({
                title: "Tree Illustrator",
                body: $(getIframeMarkup()),
                buttons : {
                    //"Cancel": {},
                    "Close": {
                        class: "btn-primary",
                        click: function () {
                            // TODO: update TI header cell
                            console.log('clicked Close button (closes popup?)');
                            /*
                            var token = $(this).find('input').val();
                            localStorage[token_name] = token;
                            gist_notebook();
                            */
                        }
                    }
                },
                open : function (event, ui) {
                    // Cosmetic tweaks to the modal popup
                    var $titleArea = $('h4.modal-title:contains(Tree Illustrator)');
                    var $modalHeader = $titleArea.closest('.modal-header');
                    var $modalDialog = $modalHeader.closest('.modal-dialog');
                    $titleArea.prepend('');
                    $modalHeader.css('padding', '8px 15px');
                    $modalDialog.css({'width':'90%', 'height':'90%'}); // almost fills the window

                    // TODO: load initial data?
                    /*
                    var that = $(this);
                    // Upon ENTER, click the OK button.
                    that.find('input[type="text"]').keydown(function (event, ui) {
                        if (event.which === 13) {
                            that.find('.btn-primary').first().click();
                            return false;
                        }
                    });
                    that.find('input[type="text"]').focus().select();
                    */
                }
            });
        };

        /* TODO: define PUBLIC variables (and privileged methods) with 'self' */

        // Initialize this instance using one of the methods above

        if (!data || typeof(data) !== 'object') {
            // instance will load the "empty" illustration as usual?
            console.log("No data specified for Tree Illustrator, will use placeholders.");
        }

        if (target === SINGLETON) {
            if (isLiveNotebook) {
                // Use the modal popup support in IPython
                showInModalPopup(data);
            } else {  // it's a static HTML notebok
                // Use a new browser window or tab
                showInNewWindow(data);
            }
        } else {
            // try to embed in a specified cell
            if (target && ('append_output' in target)) {
            ///if (target && (target instanceof OutputArea)) {
                showInNotebookCell(target);
            } else {
                if (isLiveNotebook) {
                    alert("Missing notebook cell as first argument! Try 'this':"
                        + "\n  var ti = new IPythonTreeIllustrator.IllustratorWidget(this);");
                } else {
                    alert("REMINDER: Tree Illustrator can't be embedded in a cell in the static HTML notebook!");
                }
                return null;
            }
        }

        var elementSelector = ('#'+ elementID);
        self.ti_element = $(elementSelector)[0];
        self.ti_window = self.ti_element.contentWindow;

        // add this instance to the registry above
        widgets[elementID] = self;
    }

    // Do other initial setup in the noteboo
    var initNotebookUI = function( $homeCellOutputArea ) {
        if (isStaticNotebook) {
            // There's no toolbar or available cell reference; nothing we can do here
            console.warn("IPythonTreeIllustrator.initNotebookUI(): disabled in a static notebook!");
            return;
        }
        console.log("IPythonTreeIllustrator.initNotebookUI(): starting...");

        // Add a button to the shared toolbar
        if ($('#'+ TOOLBAR_BUTTON_ID).length === 0) {
            console.log("IPythonTreeIllustrator.initNotebookUI(): adding toolbar button");
            IPython.toolbar.add_buttons_group([
                {
                    'label'   : 'Launch the Tree Illustrator',
                    'icon'    : 'fa-leaf', // from http://fortawesome.github.io/Font-Awesome/icons/
                            // for prefixed names, see http://cascade.io/icon-reference.html
                    'callback': function() {
                        var ti = new IPythonTreeIllustrator.IllustratorWidget(IPythonTreeIllustrator.SINGLETON);
                    },
                    'id'      : TOOLBAR_BUTTON_ID
                },
            ]);
            // let's give it a nicer look
            $('#'+ TOOLBAR_BUTTON_ID).html(' Tree Illustrator');
        }

        // Add a "home" cell and persistent state (JSON in a TEXTAREA), if not found
        if ($homeCellOutputArea instanceof jQuery && $homeCellOutputArea.length) {
            // TODO: test for existing state and home cell
            $homeCellOutputArea.html('
VICTORY
');
        } else {
            // No jQuery provided, or it's empty
            console.warn("IPythonTreeIllustrator.initNotebookUI(): No home cell defined!");
        }
        console.log("IPythonTreeIllustrator.initNotebookUI(): done!");
    }

    /* define PUBLIC methods (that don't need private data) in its prototype */
    IllustratorWidget.prototype = {
        constructor: IllustratorWidget,

        addTree: function(data) {
            var self = this;
            // TODO
        },
        dumpCurrentIllustration: function(data) {
            var self = this;
            // TODO
        },
        dumpCurrentIllustrationSVG: function(data) {
            var self = this;
            // TODO
        }

    }

    /* expose class constructors (and static methods) for instantiation */
    return {
        // expose enumerations
        SINGLETON: SINGLETON,
        TOOLBAR_BUTTON_ID: TOOLBAR_BUTTON_ID,

        // expose static properties and methods
        initNotebookUI: initNotebookUI,
        isLiveNotebook: isLiveNotebook,
        isStaticNotebook: isStaticNotebook,

        // expose available classes
        IllustratorWidget: IllustratorWidget
    };
}(window, document, $);
<commit_msg>Load TI home-cell UI via AJAX<commit_after>/*
 * This script can be imported into any IPython notebook (assumes the web UI)
 * to allow embedding the Tree Illustrator webapp, where it can read data from
 * the surrounding notebook and save results there.
 *
 * This should be useful in pre-publication and exploratory scenarios for a
 * single user, or fairly easy collaboration using Wakari or another notebook
 * server.
 *
 * There are a few options for where the app would appear:
 *
 *   1. in a modal popup (in an IFRAME)
 *      This works best in a live (vs static HTML) notebook, where we an add a
 *      toolbar button and use IPython's built-in modal.
 *
 *   2. "inline" in a notebook cell (in an IFRAME)
 *      This makes a "static widget" in IPython parlance, so it should work
 *      even in a static HTML notebook. This allows multiple instances of Tree
 *      Illustrator to exist in different cells; is that useful?
 *
 *   3. in a new browser tab or window (esp. for static notebooks)
 *      This would need an additional bridge from the calling window to the new
 *      one. On the plus side, it should work in a live *or* static notebook.
 *
 * In any case, we'll use cross-document messaging (postMessage) to provide
 * tree data from the surrounding IPython session and to save SVG output (or
 * complete illustration JSON) from the Tree Illustrator. [1,2]
 *
 * To use this, add a code cell to your IPython notebook and embed this script
 *   %%javascript
 *   $.getScript('https://rawgit.com/opentreeoflife/f16e.../raw/48b.../ipynb-tree-illustrator.js');
 *   ti = IPythonTreeIllustrator.IllustratorWidget();
 *   ti2 = IPythonTreeIllustrator.IllustratorWidget('(a,b,(c,d));');
 *
 * [1] https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
 * [2] http://caniuse.com/#search=postMessage
 */

var IPythonTreeIllustrator = function(window, document, $) {

    // Are we running in a "live" notebook, or static HTML?
    var isLiveNotebook = $('body.notebook_app').length > 0;
    var isStaticNotebook = !(isLiveNotebook);

    // Define some enumerated values for callers.
    var SINGLETON = 'SINGLETON';
    var TOOLBAR_BUTTON_ID = 'ti-toolbar-button';

    // Keep track of all active instances (widgets), keyed by element ID
    var widgets = { };

    // Assign unique, serial element IDs
    var nextAvailableWidgetID = 0;

    var getNextAvailableWidgetID = function( ) {
        // creates a serial ID like 'tree-illustrator-4'
        var readyID = nextAvailableWidgetID;
        nextAvailableWidgetID = readyID + 1;
        return ('tree-illustrator-'+ readyID);
    }

    var IllustratorWidget = function(target, data) {
        if ( !(this instanceof IllustratorWidget) ) {
            console.warn("MISSING 'new' keyword for IllustratorWidget, patching this now");
            return new IllustratorWidget(data);
        }

        // Safely refer to this instance below
        var self = this;
        var elementID = getNextAvailableWidgetID();

        /* define PRIVATE members (variables and functions ) with 'var' */

        var getIframeMarkup = function() {
            // TODO: add version/SHA argument here?
            return ' \
                    ';
        }

        var showInNewWindow = function() {
            // TODO: Show the Tree Illustrator in a new browser window or tab, with a link
            // back to the calling window.
            alert('showInNewWindow(): COMING SOON');
        }

        var showInNotebookCell = function(cell) {
            // create my IFRAME element in the output of the current notebook cell

            // N.B. This ID is mostly for internal use; user probably calls this something else
            cell.append_display_data({
              'data': {
                'text/html': getIframeMarkup()
              }
            })
        }

        var showInModalPopup = function(data) {
            // Use IPython's support for a single modal popup, adapted from
            // https://github.com/minrk/ipython_extensions/blob/70ed77bd7fd36fbead09a1df41f93cab5cfdfe92/nbextensions/gist.js
            //var modal = IPython.dialog.parentModule.modal({
            var dialog = require("base/js/dialog");
            var modal = dialog.modal({
                title: "Tree Illustrator",
                body: $(getIframeMarkup()),
                buttons : {
                    //"Cancel": {},
                    "Close": {
                        class: "btn-primary",
                        click: function () {
                            // TODO: update TI header cell
                            console.log('clicked Close button (closes popup?)');
                            /*
                            var token = $(this).find('input').val();
                            localStorage[token_name] = token;
                            gist_notebook();
                            */
                        }
                    }
                },
                open : function (event, ui) {
                    // Cosmetic tweaks to the modal popup
                    var $titleArea = $('h4.modal-title:contains(Tree Illustrator)');
                    var $modalHeader = $titleArea.closest('.modal-header');
                    var $modalDialog = $modalHeader.closest('.modal-dialog');
                    $titleArea.prepend('');
                    $modalHeader.css('padding', '8px 15px');
                    $modalDialog.css({'width':'90%', 'height':'90%'}); // almost fills the window

                    // TODO: load initial data?
                    /*
                    var that = $(this);
                    // Upon ENTER, click the OK button.
                    that.find('input[type="text"]').keydown(function (event, ui) {
                        if (event.which === 13) {
                            that.find('.btn-primary').first().click();
                            return false;
                        }
                    });
                    that.find('input[type="text"]').focus().select();
                    */
                }
            });
        };

        /* TODO: define PUBLIC variables (and privileged methods) with 'self' */

        // Initialize this instance using one of the methods above

        if (!data || typeof(data) !== 'object') {
            // instance will load the "empty" illustration as usual?
            console.log("No data specified for Tree Illustrator, will use placeholders.");
        }

        if (target === SINGLETON) {
            if (isLiveNotebook) {
                // Use the modal popup support in IPython
                showInModalPopup(data);
            } else {  // it's a static HTML notebok
                // Use a new browser window or tab
                showInNewWindow(data);
            }
        } else {
            // try to embed in a specified cell
            if (target && ('append_output' in target)) {
            ///if (target && (target instanceof OutputArea)) {
                showInNotebookCell(target);
            } else {
                if (isLiveNotebook) {
                    alert("Missing notebook cell as first argument! Try 'this':"
                        + "\n  var ti = new IPythonTreeIllustrator.IllustratorWidget(this);");
                } else {
                    alert("REMINDER: Tree Illustrator can't be embedded in a cell in the static HTML notebook!");
                }
                return null;
            }
        }

        var elementSelector = ('#'+ elementID);
        self.ti_element = $(elementSelector)[0];
        self.ti_window = self.ti_element.contentWindow;

        // add this instance to the registry above
        widgets[elementID] = self;
    }

    // Freeze/thaw JSON data to/from a TEXTAREA
    var thawStateFromJSON = function() {
        console.warn("Now I'd thaw TI state from JSON!");
    }
    var freezeStateToJSON = function() {
        console.warn("Now I'd freeze TI state to JSON!");
    }

    var updateHomeCell = function() {
        // Refresh (or initialize) the home-cell display based on current state JSON
        var $homeCell = $('#ti-home-cell');
        console.log("Updating the Tree Illustrator home cell...");
        // TODO: Update the list of illustrations
        // TODO: Update the prefs UI
    }

    // Do other initial setup in the noteboo
    var initNotebookUI = function( $homeCellOutputArea ) {
        if (isStaticNotebook) {
            // There's no toolbar or available cell reference; nothing we can do here
            console.warn("IPythonTreeIllustrator.initNotebookUI(): disabled in a static notebook!");
            return;
        }
        console.log("IPythonTreeIllustrator.initNotebookUI(): starting...");

        // Add a button to the shared toolbar
        if ($('#'+ TOOLBAR_BUTTON_ID).length === 0) {
            console.log("IPythonTreeIllustrator.initNotebookUI(): adding toolbar button");
            IPython.toolbar.add_buttons_group([
                {
                    'label'   : 'Launch the Tree Illustrator',
                    'icon'    : 'fa-leaf', // from http://fortawesome.github.io/Font-Awesome/icons/
                            // for prefixed names, see http://cascade.io/icon-reference.html
                    'callback': function() {
                        var ti = new IPythonTreeIllustrator.IllustratorWidget(IPythonTreeIllustrator.SINGLETON);
                    },
                    'id'      : TOOLBAR_BUTTON_ID
                },
            ]);
            // let's give it a nicer look
            $('#'+ TOOLBAR_BUTTON_ID).html(' Tree Illustrator');
        }

        // Add a "home" cell and persistent state (JSON in a TEXTAREA), if not found
        if ($homeCellOutputArea instanceof jQuery && $homeCellOutputArea.length) {
            // Test for existing home cell (incl. JSON state)
            var homeCellAlreadyExists = $('#ti-home-cell, #ti-state-data').length > 1;
            if (homeCellAlreadyExists) {
                updateHomeCell();
            } else {
                // Load our template HTML into the new "home" cell
                $homeCellOutputArea.load('./ipynb-ti-home-cell.html', function( response, status, xhr ) {
                    if ( status == "error" ) {
                        var msg = "Sorry but there was an error loading the Tree Illustrator UI: ";
                        $homeCellOutputArea.text( msg + xhr.status + " " + xhr.statusText );
                        return;
                    }
                    alert("Home cell loaded!");
                    updateHomeCell();
                });
            }
        } else {
            // No jQuery provided, or it's empty
            console.warn("IPythonTreeIllustrator.initNotebookUI(): No home cell defined!");
        }
        console.log("IPythonTreeIllustrator.initNotebookUI(): done!");
    }

    /* define PUBLIC methods (that don't need private data) in its prototype */
    IllustratorWidget.prototype = {
        constructor: IllustratorWidget,

        addTree: function(data) {
            var self = this;
            // TODO
        },
        dumpCurrentIllustration: function(data) {
            var self = this;
            // TODO
        },
        dumpCurrentIllustrationSVG: function(data) {
            var self = this;
            // TODO
        }

    }

    /* expose class constructors (and static methods) for instantiation */
    return {
        // expose enumerations
        SINGLETON: SINGLETON,
        TOOLBAR_BUTTON_ID: TOOLBAR_BUTTON_ID,

        // expose static properties and methods
        initNotebookUI: initNotebookUI,
        isLiveNotebook: isLiveNotebook,
        isStaticNotebook: isStaticNotebook,

        // expose available classes
        IllustratorWidget: IllustratorWidget
    };
}(window, document, $);
<|endoftext|>


<commit_before>/*global AudioContext:false, webkitAudioContext:false*/

function buildLowshelf (context) {
  const lowshelf = context.createBiquadFilter()

  lowshelf.type = 'lowshelf'
  lowshelf.frequency.value = 750
  lowshelf.Q.value = 100
  lowshelf.gain.value = 30

  return lowshelf
}

function buildNotch (context) {
  const notch = context.createBiquadFilter()

  notch.type = 'notch'
  notch.frequency.value = 2000

  return notch
}

function buildOscillators (context, connectionNode, initialFreq = 40) {
  return oscSettings.map(settings => {
    const osc = context.createOscillator()

    osc.type = settings.type
    osc.frequency.value = initialFreq
    osc.detune.value = settings.detune

    osc.connect(connectionNode)

    return osc
  })
}

function isAudioContext (context) {
  return context instanceof AudioContext || context instanceof webkitAudioContext
}

const oscSettings = [
  {type: 'sawtooth', detune: -10},
  {type: 'sawtooth', detune: -12},
  {type: 'sawtooth', detune: 0},
  {type: 'sawtooth', detune: 15},
  {type: 'triangle', detune: 10}
]

class AdventureSynth {
  constructor (context, mainFreq = 40) {
    if (!isAudioContext(context)) {
      throw Error('Must provide AudioContext')
    }

    this.context = context
    this.mainFreq = mainFreq

    const notch = buildNotch(context)
    const lowshelf = buildLowshelf(context)

    const gain = context.createGain()
    gain.gain.setValueAtTime(0.10, context.currentTime)

    const finalGain = context.createGain()
    finalGain.gain.setValueAtTime(0.9, context.currentTime)
    var compressor = context.createDynamicsCompressor()
    compressor.threshold.value = -45
    compressor.knee.value = 40
    compressor.attack.value = 0.15
    compressor.release.value = 0.45
    compressor.ratio.value = 16
    compressor.reduction.value = -80

    notch.connect(lowshelf)
    lowshelf.connect(gain)
    gain.connect(compressor)
    compressor.connect(finalGain)

    this.oscillators = buildOscillators(context, notch, mainFreq)

    this.changeFreq = this.changeFreq.bind(this)

    this.nodes = [
      ...this.oscillators,
      notch,
      lowshelf,
      gain,
      finalGain
    ]
  }

  connect (node) {
    const finalNode = this.nodes.slice(-1)[0]
    finalNode.connect(node)

    return this
  }

  start (time) {
    const { oscillators, context } = this
    const startTime = time || context.currentTime

    oscillators.forEach(
      osc => osc.start(startTime)
    )

    return this
  }

  stop (time) {
    const { oscillators, context } = this
    const stopTime = time || context.currentTime

    oscillators.forEach(
      osc => osc.stop(stopTime)
    )

    return this
  }

  changeFreq (freq) {
    const { oscillators, context } = this

    oscillators.forEach(osc =>
      osc.frequency.setValueAtTime(freq, context.currentTime)
    )

    return this
  }

  changeGain (gain) {
    const { nodes, context } = this

    const finalNode = nodes.slice(-1)[0]
    finalNode.gain.setValueAtTime(gain, context.currentTime)

    return this
  }

}

export default AdventureSynth
<commit_msg>var -> const, break up stanza<commit_after>/*global AudioContext:false, webkitAudioContext:false*/

function buildLowshelf (context) {
  const lowshelf = context.createBiquadFilter()

  lowshelf.type = 'lowshelf'
  lowshelf.frequency.value = 750
  lowshelf.Q.value = 100
  lowshelf.gain.value = 30

  return lowshelf
}

function buildNotch (context) {
  const notch = context.createBiquadFilter()

  notch.type = 'notch'
  notch.frequency.value = 2000

  return notch
}

function buildOscillators (context, connectionNode, initialFreq = 40) {
  return oscSettings.map(settings => {
    const osc = context.createOscillator()

    osc.type = settings.type
    osc.frequency.value = initialFreq
    osc.detune.value = settings.detune

    osc.connect(connectionNode)

    return osc
  })
}

function isAudioContext (context) {
  return context instanceof AudioContext || context instanceof webkitAudioContext
}

const oscSettings = [
  {type: 'sawtooth', detune: -10},
  {type: 'sawtooth', detune: -12},
  {type: 'sawtooth', detune: 0},
  {type: 'sawtooth', detune: 15},
  {type: 'triangle', detune: 10}
]

class AdventureSynth {
  constructor (context, mainFreq = 40) {
    if (!isAudioContext(context)) {
      throw Error('Must provide AudioContext')
    }

    this.context = context
    this.mainFreq = mainFreq

    const notch = buildNotch(context)
    const lowshelf = buildLowshelf(context)

    const gain = context.createGain()
    gain.gain.setValueAtTime(0.10, context.currentTime)

    const finalGain = context.createGain()
    finalGain.gain.setValueAtTime(0.9, context.currentTime)

    const compressor = context.createDynamicsCompressor()
    compressor.threshold.value = -45
    compressor.knee.value = 40
    compressor.attack.value = 0.15
    compressor.release.value = 0.45
    compressor.ratio.value = 16
    compressor.reduction.value = -80

    notch.connect(lowshelf)
    lowshelf.connect(gain)
    gain.connect(compressor)
    compressor.connect(finalGain)

    this.oscillators = buildOscillators(context, notch, mainFreq)

    this.changeFreq = this.changeFreq.bind(this)

    this.nodes = [
      ...this.oscillators,
      notch,
      lowshelf,
      gain,
      finalGain
    ]
  }

  connect (node) {
    const finalNode = this.nodes.slice(-1)[0]
    finalNode.connect(node)

    return this
  }

  start (time) {
    const { oscillators, context } = this
    const startTime = time || context.currentTime

    oscillators.forEach(
      osc => osc.start(startTime)
    )

    return this
  }

  stop (time) {
    const { oscillators, context } = this
    const stopTime = time || context.currentTime

    oscillators.forEach(
      osc => osc.stop(stopTime)
    )

    return this
  }

  changeFreq (freq) {
    const { oscillators, context } = this

    oscillators.forEach(osc =>
      osc.frequency.setValueAtTime(freq, context.currentTime)
    )

    return this
  }

  changeGain (gain) {
    const { nodes, context } = this

    const finalNode = nodes.slice(-1)[0]
    finalNode.gain.setValueAtTime(gain, context.currentTime)

    return this
  }

}

export default AdventureSynth
<|endoftext|>


<commit_before>import {clone, pipeP, map} from 'ramda'
import {printRequest} from 'apollo-client/transport/networkInterface'

function applyWares (ctx, wares) {
  return new Promise(function (resolve, reject) {
    const queue = function (funcs, scope) {
      const next = function () {
        if (funcs.length > 0) {
          const f = funcs.shift()
          f.applyMiddleware.apply(scope, [ctx, next])
        }
        else {
          resolve(ctx)
        }
      }
      next()
    }
    queue(wares.slice())
  })
}

function executeQuery(sockets, context) {
  const {request, options} = context
  const {uri, channel} = options
  const Socket = options.Socket || require('./phoenix').Socket

  if (! uri) throw "Missing options.uri"
  if (! channel) throw "Missing options.channel"
  if (! channel.topic) throw "Missing options.channel.topic"

  if (options.logger === true) {
    options.logger = (kind, msg, data) =>
      console.log(`Apollo websocket ${kind}: ${msg}`, data)
  }

  if(! sockets[uri]) {
    sockets[uri] = {
      conn: new Socket(uri, options),
      channels: {}
    }
  }

  const socket = sockets[uri]
  let chan = socket.channels[channel.topic]

  function joinChannel(reject) {
    if (! socket.conn.isConnected()) {
      return socket.conn.connect()
    }
    if (! chan.conn) {
      chan.conn = socket.conn.channel(channel.topic, channel.params || {})
    }
    if (!chan.conn.isJoined() && !chan.conn.isJoining()) {
      chan.conn.join().receive("ok", _ => {
        const queue = chan.queue
        chan.queue = []
        map(performQuery, queue)
      }).receive('error', err => {
        chan.conn.leave()
        chan.conn = null
        reject(err)
      })
    }
  }

  function performQuery ({context, resolve, reject}) {
    const msg = context.options.channel.in_msg || "gql"
    const payload = printRequest(context.request)
    chan.conn.push(msg, payload)
      .receive("ok", resolve)
      .receive("error", reject)
      .receive("timeout", reject.bind(null, 'timeout'))
  }

  return new Promise(function (resolve, reject) {
    if(! chan) {
      chan = socket.channels[channel.topic] = {
        conn: null,
        queue: []
      }
      socket.conn.onOpen(joinChannel.bind(null, reject))
      socket.conn.onError(reject)
    }
    if (chan.conn && chan.conn.isJoined()) {
      performQuery({context, resolve, reject})
    } else {
      chan.queue.push({context, resolve, reject})
      joinChannel(reject)
    }
  })
}

const responseData = ({response}) => {
  return new Promise(function (resolve, reject) {
    if (response.error){
      reject(response)
    } else if (response.data) {
      resolve(response)
    } else {
      reject('No response')
    }
  })
}

export function createNetworkInterface(ifaceOpts) {
  const sockets = {}
  const middlewares = []
  const afterwares = []

  const use = map(item => middlewares.push(item))
  const useAfter = map(item => afterwares.push(item))

  const requestMiddleware = (request) => {
    const options = clone(ifaceOpts)
    return applyWares({request, options}, middlewares)
  }

  const responseMiddleware = (response) => {
    const options = clone(ifaceOpts)
    return applyWares({response, options}, afterwares)
  }

  const query = pipeP(requestMiddleware,
                      executeQuery.bind(null, sockets),
                      responseMiddleware,
                      responseData)

  return {query, use, useAfter}
}
<commit_msg>Small fix to execute afterware function.<commit_after>import {clone, pipeP, map} from 'ramda'
import {printRequest} from 'apollo-client/transport/networkInterface'

function applyWares (ctx, wares) {
  return new Promise(function (resolve, reject) {
    const queue = function (funcs, scope) {
      const next = function () {
        if (funcs.length > 0) {
          const f = funcs.shift()
          (f.applyMiddleware || f.applyAfterware).apply(scope, [ctx, next]);
        }
        else {
          resolve(ctx)
        }
      }
      next()
    }
    queue(wares.slice())
  })
}

function executeQuery(sockets, context) {
  const {request, options} = context
  const {uri, channel} = options
  const Socket = options.Socket || require('./phoenix').Socket

  if (! uri) throw "Missing options.uri"
  if (! channel) throw "Missing options.channel"
  if (! channel.topic) throw "Missing options.channel.topic"

  if (options.logger === true) {
    options.logger = (kind, msg, data) =>
      console.log(`Apollo websocket ${kind}: ${msg}`, data)
  }

  if(! sockets[uri]) {
    sockets[uri] = {
      conn: new Socket(uri, options),
      channels: {}
    }
  }

  const socket = sockets[uri]
  let chan = socket.channels[channel.topic]

  function joinChannel(reject) {
    if (! socket.conn.isConnected()) {
      return socket.conn.connect()
    }
    if (! chan.conn) {
      chan.conn = socket.conn.channel(channel.topic, channel.params || {})
    }
    if (!chan.conn.isJoined() && !chan.conn.isJoining()) {
      chan.conn.join().receive("ok", _ => {
        const queue = chan.queue
        chan.queue = []
        map(performQuery, queue)
      }).receive('error', err => {
        chan.conn.leave()
        chan.conn = null
        reject(err)
      })
    }
  }

  function performQuery ({context, resolve, reject}) {
    const msg = context.options.channel.in_msg || "gql"
    const payload = printRequest(context.request)
    chan.conn.push(msg, payload)
      .receive("ok", resolve)
      .receive("error", reject)
      .receive("timeout", reject.bind(null, 'timeout'))
  }

  return new Promise(function (resolve, reject) {
    if(! chan) {
      chan = socket.channels[channel.topic] = {
        conn: null,
        queue: []
      }
      socket.conn.onOpen(joinChannel.bind(null, reject))
      socket.conn.onError(reject)
    }
    if (chan.conn && chan.conn.isJoined()) {
      performQuery({context, resolve, reject})
    } else {
      chan.queue.push({context, resolve, reject})
      joinChannel(reject)
    }
  })
}

const responseData = ({response}) => {
  return new Promise(function (resolve, reject) {
    if (response.error){
      reject(response)
    } else if (response.data) {
      resolve(response)
    } else {
      reject('No response')
    }
  })
}

export function createNetworkInterface(ifaceOpts) {
  const sockets = {}
  const middlewares = []
  const afterwares = []

  const use = map(item => middlewares.push(item))
  const useAfter = map(item => afterwares.push(item))

  const requestMiddleware = (request) => {
    const options = clone(ifaceOpts)
    return applyWares({request, options}, middlewares)
  }

  const responseMiddleware = (response) => {
    const options = clone(ifaceOpts)
    return applyWares({response, options}, afterwares)
  }

  const query = pipeP(requestMiddleware,
                      executeQuery.bind(null, sockets),
                      responseMiddleware,
                      responseData)

  return {query, use, useAfter}
}
<|endoftext|>


<commit_before>import 'babel-polyfill'

import { initializeOptions } from './uri.js'
import { initializeSliders, resizeSliders } from './sliders.js'
import { initializeShapes } from './shapes.js'
import { initializeColors } from './colors.js'

function ready() {
  const options = initializeOptions()
  initializeSliders(options)
  initializeShapes(options)
  initializeColors(options)

  window.addEventListener('resize', resizeSliders)
}

if (document.readyState !== 'loading') {
  ready()
} else {
  document.addEventListener('DOMContentLoaded', ready)
}
<commit_msg>Reduced polyfills to only those needed in recent browsers.<commit_after>import 'core-js/es6/map'
import 'core-js/fn/array/includes'
import 'core-js/fn/symbol'

import { initializeOptions } from './uri.js'
import { initializeSliders, resizeSliders } from './sliders.js'
import { initializeShapes } from './shapes.js'
import { initializeColors } from './colors.js'

function ready() {
  const options = initializeOptions()
  initializeSliders(options)
  initializeShapes(options)
  initializeColors(options)

  window.addEventListener('resize', resizeSliders)
}

if (document.readyState !== 'loading') {
  ready()
} else {
  document.addEventListener('DOMContentLoaded', ready)
}
<|endoftext|>


<commit_before>export default typeof fetch=='function' ? fetch : function(url, options) {
	options = options || {};
	return new Promise( (resolve, reject) => {
		let request = new XMLHttpRequest();

		request.open(options.method || 'get', url);

		for (let i in options.headers) {
			request.setRequestHeader(i, options.headers[i]);
		}

		request.onload = () => {
			resolve(response(request));
		};

		request.onerror = () => {
			reject(Error('Network Error'));
		};

		request.send(options.body || null);

		function response(xhr) {
			let headerText = xhr.getAllResponseHeaders(),
				keys = [],
				all = [],
				headers = {},
				reg = /^\s*(.*?)\s*\:\s*([\s\S]*?)\s*$/gm,
				match, header, key;
			while ((match=reg.exec(headerText))) {
				keys.push(key = match[1].toLowerCase());
				all.push([key, match[2]]);
				header = headers[key];
				headers[key] = header ? `${header},${match[2]}` : match[2];
			}

			return {
				type: 'cors',
				ok: xhr.status/200|0 == 1,		// 200-399
				status: xhr.status,
				statusText: xhr.statusText,
				url: xhr.responseURL,
				clone: () => response(xhr),
				text: () => Promise.resolve(xhr.responseText),
				json: () => Promise.resolve(xhr.responseText).then(JSON.parse),
				xml: () => Promise.resolve(xhr.responseXML),
				blob: () => Promise.resolve(xhr.response),
				headers: {
					keys: () => keys,
					entries: () => all,
					get: n => headers[n.toLowerCase()],
					has: n => n.toLowerCase() in headers
				}
			};
		}
	});
}
<commit_msg>Proxy XHR error directly instead of creating an informationless Error instance, also saves mucho bytes<commit_after>export default typeof fetch=='function' ? fetch : function(url, options) {
	options = options || {};
	return new Promise( (resolve, reject) => {
		let request = new XMLHttpRequest();

		request.open(options.method || 'get', url);

		for (let i in options.headers) {
			request.setRequestHeader(i, options.headers[i]);
		}

		request.onload = () => {
			resolve(response(request));
		};

		request.onerror = reject;

		request.send(options.body || null);

		function response(xhr) {
			let headerText = xhr.getAllResponseHeaders(),
				keys = [],
				all = [],
				headers = {},
				reg = /^\s*(.*?)\s*\:\s*([\s\S]*?)\s*$/gm,
				match, header, key;
			while ((match=reg.exec(headerText))) {
				keys.push(key = match[1].toLowerCase());
				all.push([key, match[2]]);
				header = headers[key];
				headers[key] = header ? `${header},${match[2]}` : match[2];
			}

			return {
				type: 'cors',
				ok: xhr.status/200|0 == 1,		// 200-399
				status: xhr.status,
				statusText: xhr.statusText,
				url: xhr.responseURL,
				clone: () => response(xhr),
				text: () => Promise.resolve(xhr.responseText),
				json: () => Promise.resolve(xhr.responseText).then(JSON.parse),
				xml: () => Promise.resolve(xhr.responseXML),
				blob: () => Promise.resolve(xhr.response),
				headers: {
					keys: () => keys,
					entries: () => all,
					get: n => headers[n.toLowerCase()],
					has: n => n.toLowerCase() in headers
				}
			};
		}
	});
}
<|endoftext|>


<commit_before>// cheap way of doing AA
c.width = 32e2, c.height = 18e2, // 16:9 aspect ratio
//c.width = 192, c.height = 108; // battery saving

b = new AudioContext;
a = b.createScriptProcessor(512, t = 1, K = 1);
a.connect(b.destination);

// bass & arpeggio sequencers
// (compresses better when inlined)

// s=(notes,octave,rate,len) =>
//   31 & t * Math.pow(2, notes[(t>>rate) % len] / 12 - octave)

t = 00000;

// music
X = a.onaudioprocess = a =>
  a.outputBuffer.getChannelData(f=0).map(_ =>
    a.outputBuffer.getChannelData(0)[f++] =
    (
      (
        // kick drum
        ((
          // envelope
          K = 1e4 / (
            t & 16382
          )
        ) & 1) * 31

        // bass
        + (31 & t * Math.pow(2,
          // melody
          '7050'
        [(t>>
          // rate
          17
        ) %
          // melody length
          4
        ] / 12 -
          // octave
          4
        )) / K
      )

      // turn off above instruments after a while
      * !(t>>22)

      // hihat TODO improve/golf envelope
      + (t % 100 * t & 128) * Math.min(.2, 1e1 / ((t>>3) % 512))

      // enable hihat after t>>19
      * !!(t>>19)

      // arpeggio
      + (31 & t * Math.pow(2, (
        // melody
        ((t>>17) % 2 ? '027' : '037')
      )[(t>>(
        // rate
        13 - 3 * (t>>20) % 12
      )) %
        // melody length
        4
      ] / 12 -
        // octave
        1
      )) / K

      // enable arpeggio after t>>20
      * !!(t>>20)
    ) * (
      // fade out
      X = Math.min(Math.max(0, 1e1 - ++t / 5e5), 1)
    ) / 212
  );

// gfx
g = c.getContext`webgl`;
P = g.createProgram();

// NOTE: 2nd argument to drawArrays used to be 0, but undefined works
r = _ => g.drawArrays(g.TRIANGLE_FAN,
  // x-res, y-res, time (s), fade out
  g.uniform4f(g.getUniformLocation(P, 'a'), c.width, c.height, t / 5e5, X),
  3,
  // 1, 0, kick envelope, unused
  g.uniform4f(g.getUniformLocation(P, 'b'), 1, 1/K, t>>18, requestAnimationFrame(r))
);

// vertex shader
g.shaderSource(S=g.createShader(g.VERTEX_SHADER), require("./vertex.glsl"));
g.compileShader(S);g.attachShader(P,S);

// fragment shader
g.shaderSource(S=g.createShader(g.FRAGMENT_SHADER), require("./fragment.glsl"));
g.compileShader(S);g.attachShader(P,S);

// Log compilation errors
// if (!g.getShaderParameter(S, 35713)) { // COMPILE_STATUS = 35713
//   throw g.getShaderInfoLog(S);
// }

g.bindBuffer(g.ARRAY_BUFFER, g.createBuffer(c.parentElement.style.margin = 0));
// 1st argument to enableVertexAttribArray used to be 0, but undefined works
// 1st argument to vertexAttribPointer used to be 0, but undefined works
g.vertexAttribPointer(
  g.enableVertexAttribArray(
    g.bufferData(g.ARRAY_BUFFER, Int8Array.of(-3, 1, 1, -3, 1, 1), g.STATIC_DRAW)
  ),
2, g.BYTE, r(c.style.height = '1e2vh'), g.linkProgram(P), g.useProgram(P));
<commit_msg>Remove debug declaration<commit_after>// cheap way of doing AA
c.width = 32e2, c.height = 18e2, // 16:9 aspect ratio
//c.width = 192, c.height = 108; // battery saving

b = new AudioContext;
a = b.createScriptProcessor(512, t = 1, K = 1);
a.connect(b.destination);

// bass & arpeggio sequencers
// (compresses better when inlined)

// s=(notes,octave,rate,len) =>
//   31 & t * Math.pow(2, notes[(t>>rate) % len] / 12 - octave)

// music
X = a.onaudioprocess = a =>
  a.outputBuffer.getChannelData(f=0).map(_ =>
    a.outputBuffer.getChannelData(0)[f++] =
    (
      (
        // kick drum
        ((
          // envelope
          K = 1e4 / (
            t & 16382
          )
        ) & 1) * 31

        // bass
        + (31 & t * Math.pow(2,
          // melody
          '7050'
        [(t>>
          // rate
          17
        ) %
          // melody length
          4
        ] / 12 -
          // octave
          4
        )) / K
      )

      // turn off above instruments after a while
      * !(t>>22)

      // hihat TODO improve/golf envelope
      + (t % 100 * t & 128) * Math.min(.2, 1e1 / ((t>>3) % 512))

      // enable hihat after t>>19
      * !!(t>>19)

      // arpeggio
      + (31 & t * Math.pow(2, (
        // melody
        ((t>>17) % 2 ? '027' : '037')
      )[(t>>(
        // rate
        13 - 3 * (t>>20) % 12
      )) %
        // melody length
        4
      ] / 12 -
        // octave
        1
      )) / K

      // enable arpeggio after t>>20
      * !!(t>>20)
    ) * (
      // fade out
      X = Math.min(Math.max(0, 1e1 - ++t / 5e5), 1)
    ) / 212
  );

// gfx
g = c.getContext`webgl`;
P = g.createProgram();

// NOTE: 2nd argument to drawArrays used to be 0, but undefined works
r = _ => g.drawArrays(g.TRIANGLE_FAN,
  // x-res, y-res, time (s), fade out
  g.uniform4f(g.getUniformLocation(P, 'a'), c.width, c.height, t / 5e5, X),
  3,
  // 1, 0, kick envelope, unused
  g.uniform4f(g.getUniformLocation(P, 'b'), 1, 1/K, t>>18, requestAnimationFrame(r))
);

// vertex shader
g.shaderSource(S=g.createShader(g.VERTEX_SHADER), require("./vertex.glsl"));
g.compileShader(S);g.attachShader(P,S);

// fragment shader
g.shaderSource(S=g.createShader(g.FRAGMENT_SHADER), require("./fragment.glsl"));
g.compileShader(S);g.attachShader(P,S);

// Log compilation errors
// if (!g.getShaderParameter(S, 35713)) { // COMPILE_STATUS = 35713
//   throw g.getShaderInfoLog(S);
// }

g.bindBuffer(g.ARRAY_BUFFER, g.createBuffer(c.parentElement.style.margin = 0));
// 1st argument to enableVertexAttribArray used to be 0, but undefined works
// 1st argument to vertexAttribPointer used to be 0, but undefined works
g.vertexAttribPointer(
  g.enableVertexAttribArray(
    g.bufferData(g.ARRAY_BUFFER, Int8Array.of(-3, 1, 1, -3, 1, 1), g.STATIC_DRAW)
  ),
2, g.BYTE, r(c.style.height = '1e2vh'), g.linkProgram(P), g.useProgram(P));
<|endoftext|>


<commit_before>;(function(window, document, undefined){
  'use strict'

  var options = {
    searchInput: null,
    resultsContainer: null,
    json: [],
    searchResultTemplate: '
{title}
',
    templateMiddleware: function(){},
    noResultsText: 'No results found',
    limit: 10,
    fuzzy: false,
    exclude: []
  }

  var requiredOptions = ['searchInput','resultsContainer','json']

  var templater = require('./Templater')
  var repository = require('./Repository')
  var jsonLoader = require('./JSONLoader')
  var optionsValidator = require('./OptionsValidator')({
    required: requiredOptions
  })
  var utils = require('./utils')

  /*
    Public API
  */
  window.SimpleJekyllSearch = function SimpleJekyllSearch(_options){
    var errors = optionsValidator.validate(_options)
    if( errors.length > 0 ){
      throwError('You must specify the following required options: ' + requiredOptions)
    }

    options = utils.merge(options, _options)

    templater.setOptions({
      template: options.searchResultTemplate,
      middleware: options.templateMiddleware,
    })

    repository.setOptions({
      fuzzy: options.fuzzy,
      limit: options.limit,
    })

    if( utils.isJSON(options.json) ){
      initWithJSON(options.json)
    }else{
      initWithURL(options.json)
    }
  }

  // for backwards compatibility
  window.SimpleJekyllSearch.init = window.SimpleJekyllSearch

  function initWithJSON(json){
    repository.put(json)
    registerInput()
  }

  function initWithURL(url){
    jsonLoader.load(url, function(err,json){
      if( err ){
        throwError('failed to get JSON (' + url + ')')
      }
      initWithJSON(json)
    })
  }

  function emptyResultsContainer(){
    options.resultsContainer.innerHTML = ''
  }

  function appendToResultsContainer(text){
    options.resultsContainer.innerHTML += text
  }

  function registerInput(){

    // https://remysharp.com/2010/07/21/throttling-function-calls
    function debounce(fn, delay) {
      var timer = null;
      return function () {
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
          fn.apply(context, args);
        }, delay);
      };
    }

    options.searchInput.addEventListener('keyup', debounce(function(e){

      // whitelist the following keycodes
      var whitelist = [13,20,37,38,39,40];

      var keyCodes = whitelist.map(function(code){
        return 'e.which != ' + code;
      }).join(' && ');

      // if the key pressed isn't one of the following whitelisted codes precede
      if( eval(keyCodes) ){
        emptyResultsContainer()
        if( e.target.value.length > 0 ){
          render( repository.search(e.target.value) )
        }
      }

    }, 100)) // timeout
  }

  function render(results){
    if( results.length === 0 ){
      return appendToResultsContainer(options.noResultsText)
    }
    for (var i = 0; i < results.length; i++) {
      appendToResultsContainer( templater.compile(results[i]) )
    }
  }

  function throwError(message){ throw new Error('SimpleJekyllSearch --- '+ message) }
})(window, document);<commit_msg>added 'mod' key to whitelist<commit_after>;(function(window, document, undefined){
  'use strict'

  var options = {
    searchInput: null,
    resultsContainer: null,
    json: [],
    searchResultTemplate: '
{title}
',
    templateMiddleware: function(){},
    noResultsText: 'No results found',
    limit: 10,
    fuzzy: false,
    exclude: []
  }

  var requiredOptions = ['searchInput','resultsContainer','json']

  var templater = require('./Templater')
  var repository = require('./Repository')
  var jsonLoader = require('./JSONLoader')
  var optionsValidator = require('./OptionsValidator')({
    required: requiredOptions
  })
  var utils = require('./utils')

  /*
    Public API
  */
  window.SimpleJekyllSearch = function SimpleJekyllSearch(_options){
    var errors = optionsValidator.validate(_options)
    if( errors.length > 0 ){
      throwError('You must specify the following required options: ' + requiredOptions)
    }

    options = utils.merge(options, _options)

    templater.setOptions({
      template: options.searchResultTemplate,
      middleware: options.templateMiddleware,
    })

    repository.setOptions({
      fuzzy: options.fuzzy,
      limit: options.limit,
    })

    if( utils.isJSON(options.json) ){
      initWithJSON(options.json)
    }else{
      initWithURL(options.json)
    }
  }

  // for backwards compatibility
  window.SimpleJekyllSearch.init = window.SimpleJekyllSearch

  function initWithJSON(json){
    repository.put(json)
    registerInput()
  }

  function initWithURL(url){
    jsonLoader.load(url, function(err,json){
      if( err ){
        throwError('failed to get JSON (' + url + ')')
      }
      initWithJSON(json)
    })
  }

  function emptyResultsContainer(){
    options.resultsContainer.innerHTML = ''
  }

  function appendToResultsContainer(text){
    options.resultsContainer.innerHTML += text
  }

  function registerInput(){

    // https://remysharp.com/2010/07/21/throttling-function-calls
    function debounce(fn, delay) {
      var timer = null;
      return function () {
        var context = this, args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
          fn.apply(context, args);
        }, delay);
      };
    }

    options.searchInput.addEventListener('keyup', debounce(function(e){

      // whitelist the following keycodes
      var whitelist = [13,20,37,38,39,40,91];

      var keyCodes = whitelist.map(function(code){
        return 'e.which != ' + code;
      }).join(' && ');

      // if the key pressed isn't one of the following whitelisted codes precede
      if( eval(keyCodes) ){
        emptyResultsContainer()
        if( e.target.value.length > 0 ){
          render( repository.search(e.target.value) )
        }
      }

    }, 100)) // timeout
  }

  function render(results){
    if( results.length === 0 ){
      return appendToResultsContainer(options.noResultsText)
    }
    for (var i = 0; i < results.length; i++) {
      appendToResultsContainer( templater.compile(results[i]) )
    }
  }

  function throwError(message){ throw new Error('SimpleJekyllSearch --- '+ message) }
})(window, document);<|endoftext|>


<commit_before>import path from "path";
import nativeGlob from "glob";
import promisify from "util.promisify";

const glob = promisify(nativeGlob);

function globOptionsWith(compiler, globOptions) {
  return {
    cwd: compiler.context,
    ...globOptions
  };
}

function getFileDepsMap(compilation) {
  const fileDepsBy = compilation.fileDependencies.reduce(
    (acc, usedFilepath) => {
      acc[usedFilepath] = true;
      return acc;
    },
    {}
  );

  const { assets } = compilation;
  Object.keys(assets).forEach(assetRelpath => {
    const existsAt = assets[assetRelpath].existsAt;
    fileDepsBy[existsAt] = true;
  });
  return fileDepsBy;
}

async function applyAfterEmit(compiler, compilation, plugin) {
  try {
    const globOptions = globOptionsWith(compiler, plugin.globOptions);
    const fileDepsMap = getFileDepsMap(compilation);

    const files = await glob(plugin.options.pattern, globOptions);
    const unused = files.filter(
      it => !fileDepsMap[path.join(globOptions.cwd, it)]
    );

    if (unused.length !== 0) {
      throw new Error(`
UnusedFilesWebpackPlugin found some unused files:
${unused.join(`\n`)}`);
    }
  } catch (error) {
    if (plugin.options.failOnUnused && compilation.bail) {
      throw error;
    }
    const errorsList = plugin.options.failOnUnused
      ? compilation.errors
      : compilation.warnings;
    errorsList.push(error);
  }
}

export class UnusedFilesWebpackPlugin {
  constructor(options = {}) {
    this.options = {
      pattern: `**/*.*`,
      ...options,
      failOnUnused: options.failOnUnused === true
    };

    this.globOptions = {
      ignore: `node_modules/**/*`,
      ...options.globOptions
    };
  }

  apply(compiler) {
    compiler.plugin(`after-emit`, (compilation, done) =>
      applyAfterEmit(compiler, compilation, this).then(done, done)
    );
  }
}

export default UnusedFilesWebpackPlugin;
<commit_msg>feat(index.js): support multiple patterns<commit_after>import path from "path";
import warning from "warning";
import nativeGlobAll from "glob-all";
import promisify from "util.promisify";

const globAll = promisify(nativeGlobAll);

function globOptionsWith(compiler, globOptions) {
  return {
    cwd: compiler.context,
    ...globOptions
  };
}

function getFileDepsMap(compilation) {
  const fileDepsBy = compilation.fileDependencies.reduce(
    (acc, usedFilepath) => {
      acc[usedFilepath] = true;
      return acc;
    },
    {}
  );

  const { assets } = compilation;
  Object.keys(assets).forEach(assetRelpath => {
    const existsAt = assets[assetRelpath].existsAt;
    fileDepsBy[existsAt] = true;
  });
  return fileDepsBy;
}

async function applyAfterEmit(compiler, compilation, plugin) {
  try {
    const globOptions = globOptionsWith(compiler, plugin.globOptions);
    const fileDepsMap = getFileDepsMap(compilation);

    const files = await globAll(
      plugin.options.patterns || plugin.options.pattern,
      globOptions
    );
    const unused = files.filter(
      it => !fileDepsMap[path.join(globOptions.cwd, it)]
    );

    if (unused.length !== 0) {
      throw new Error(`
UnusedFilesWebpackPlugin found some unused files:
${unused.join(`\n`)}`);
    }
  } catch (error) {
    if (plugin.options.failOnUnused && compilation.bail) {
      throw error;
    }
    const errorsList = plugin.options.failOnUnused
      ? compilation.errors
      : compilation.warnings;
    errorsList.push(error);
  }
}

export class UnusedFilesWebpackPlugin {
  constructor(options = {}) {
    warning(
      !options.pattern,
      `
"options.pattern" is deprecated and will be removed in v4.0.0.
Use "options.patterns" instead, which supports array of patterns and exclude pattern.
See https://www.npmjs.com/package/glob-all#notes
`
    );
    this.options = {
      ...options,
      patterns: options.patterns || options.pattern || [`**/*.*`],
      failOnUnused: options.failOnUnused === true
    };

    this.globOptions = {
      ignore: `node_modules/**/*`,
      ...options.globOptions
    };
  }

  apply(compiler) {
    compiler.plugin(`after-emit`, (compilation, done) =>
      applyAfterEmit(compiler, compilation, this).then(done, done)
    );
  }
}

export default UnusedFilesWebpackPlugin;
<|endoftext|>


<commit_before>export t from 'tcomb';
export PureComponent from './PureComponent';
export Query from './Query';
export App from './App';
export CookieSerializer from './CookieSerializer';
export isReactComponent from './isReactComponent';
export listener from './decorators/listener';
export props from './decorators/props';
export pure from './decorators/pure';
export queries from './decorators/queries';
export skinnable from './decorators/skinnable';
export autobound from './decorators/autobound';
export util from './util';
<commit_msg>do not export optional `lib/util`<commit_after>export t from 'tcomb';
export PureComponent from './PureComponent';
export Query from './Query';
export App from './App';
export CookieSerializer from './CookieSerializer';
export isReactComponent from './isReactComponent';
export listener from './decorators/listener';
export props from './decorators/props';
export pure from './decorators/pure';
export queries from './decorators/queries';
export skinnable from './decorators/skinnable';
export autobound from './decorators/autobound';
<|endoftext|>


<commit_before>const url = require('url')
const image = require('./image')

module.exports = (req, res) => {
  let {pathname, query} = url.parse(req.url, true)

  if (pathname === '/favicon.ico') {
    return ''
  }
  if (pathname === '/') {
    pathname = Math.random().toString()
  } else {
    res.setHeader('Cache-Control', 'max-age=2592000, public')
  }
  if (query.type === 'svg') {
    res.setHeader('Content-Type', 'svg+xml')
  } else {
    res.setHeader('Content-Type', 'image/png')
  }

  if (query.type === 'svg') {
    return image.generateSVG(pathname)
  }

  return image.generatePNG(pathname, query.size)
}
<commit_msg>Accept type by extension<commit_after>const url = require('url')
const image = require('./image')
const svgExt = /\.svg$/;
const pngExt = /\.png$/;

module.exports = (req, res) => {
  let {pathname, query} = url.parse(req.url, true)

  if (pathname === '/favicon.ico') {
    return ''
  }
  if (pathname === '/') {
    pathname = Math.random().toString()
  } else {
    res.setHeader('Cache-Control', 'max-age=2592000, public')
  }
  if (query.type === 'svg' || svgExt.test(pathname)) {
    res.setHeader('Content-Type', 'svg+xml')
    return image.generateSVG(pathname.replace(svgExt, ''));
  }
  res.setHeader('Content-Type', 'image/png')
  return image.generatePNG(pathname.replace(pngExt, ''), query.size)
}
<|endoftext|>


<commit_before>import express from 'express';
import Promise from 'bluebird';

global.Promise = Promise;

let app = express();
let router = express.Router();

// Hello World
router.get('/', (req, res) => {
    res.send('Hello World!');
});

const PORT = process.env.PORT || 3000;

app.use(router);
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));<commit_msg>Configure dotenv<commit_after>import dotenv from 'dotenv';
import express from 'express';
import Promise from 'bluebird';

dotenv.config();
global.Promise = Promise;

let app = express();
let router = express.Router();

// Hello World
router.get('/', (req, res) => {
    res.send('Hello World!');
});

const PORT = process.env.PORT || 3000;

app.use(router);
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));<|endoftext|>


<commit_before>const {scrollPosIntoView, posAtCoords, coordsAtPos} = require("./dompos")
const {draw, redraw} = require("./draw")
const {initInput, finishUpdateFromDOM, dispatchKeyDown, dispatchKeyPress} = require("./input")
const {SelectionReader, selectionToDOM} = require("./selection")
const {viewDecorations, addDummy} = require("./decoration")

;({Decoration: exports.Decoration, DecorationSet: exports.DecorationSet} = require("./decoration"))

// ::- An editor view manages the DOM structure that represents an
// editor. Its state and behavior are determined by its
// [props](#view.EditorProps).
class EditorView {
  // :: (?union<dom.Node, (dom.Node)>, EditorProps)
  // Create a view. `place` may be a DOM node that the editor should
  // be appended to, or a function that will place it into the
  // document. If it is `null`, the editor will not be added to the
  // document.
  constructor(place, props) {
    // :: EditorProps
    // The view's current [props](#view.EditorProps).
    this.props = props
    // :: EditorState
    // The view's current [state](#state.EditorState).
    this.state = this.drawnState = props.state
    this.dirty = null

    // :: dom.Node
    // The editable DOM node containing the document. (You probably
    // should not be directly interfering with its child nodes.)
    this.content = document.createElement("div")
    this.content.setAttribute("pm-container", "true")
    this.content.classList.add("ProseMirror-content")

    this.wrapper = document.createElement("div")
    this.wrapper.appendChild(this.content)

    this._root = null

    this.updateDOMForProps()

    if (place && place.appendChild) place.appendChild(this.wrapper)
    else if (place) place(this.wrapper)

    draw(this, this.state.doc, this.drawnDecorations = viewDecorations(this))
    this.content.contentEditable = true

    this.lastSelectedNode = null
    this.selectionReader = new SelectionReader(this)
    initInput(this)
  }

  // :: (EditorProps)
  // Update the view's props. Will immediately cause an update to
  // the view's DOM.
  update(props) {
    this.props = props
    this.updateState(props.state)
    this.updateDOMForProps()
  }

  // :: (EditorState)
  // Update the editor's `state` prop, without touching any of the
  // other props.
  updateState(state) {
    this.state = state

    if (this.inDOMChange) {
      if (state.view.inDOMChange != this.inDOMChange.id)
        setTimeout(() => finishUpdateFromDOM(this), 0)
      return
    } else if (state.view.inDOMChange != null) {
      setTimeout(() => this.props.onAction({type: "endDOMChange"}), 0)
      return
    }

    let redrawn = false
    let docChange = !state.doc.eq(this.drawnState.doc)

    let decorations = viewDecorations(this)
    if (docChange || this.dirty || !decorations.sameOutput(this.drawnDecorations)) {
      this.redraw(state.doc, decorations)
      redrawn = true
    }

    if (redrawn || !state.selection.eq(this.drawnState.selection))
      selectionToDOM(this, state.selection)

    // FIXME somehow schedule this relative to ui/update so that it
    // doesn't cause extra layout
    if (state.view.scrollToSelection)
      scrollPosIntoView(this, state.selection.head == null ? state.selection.from : state.selection.from)

    // Make sure we don't use an outdated range on drop event
    if (this.dragging && docChange) this.dragging.move = false
    this.drawnState = state
  }

  redraw(doc, decorations) {
    let oldDecorations = this.drawnDecorations, oldDoc = this.drawnState.doc
    this.drawnDecorations = decorations

    if (this.dirty) {
      let $start = oldDoc.resolve(this.dirty.from), same = $start.sharedDepth(this.dirty.to)
      this.dirty = null
      if (same == 0)
        return draw(this, doc, decorations)
      oldDecorations = addDummy(decorations, doc, $start.before(same), $start.after(same))
    }
    redraw(this, oldDoc, doc, oldDecorations, decorations)
  }

  updateDOMForProps() {
    let spellcheck = !!this.someProp("spellcheck")
    if (spellcheck != this.content.spellcheck) this.content.spellcheck = spellcheck
    let label = this.someProp("label", f => f(this.state)) || ""
    if (this.content.getAttribute("aria-label") != label) this.content.setAttribute("aria-label", label)
    let className = "ProseMirror"
    this.someProp("class", f => { let cls = f(this.state); if (cls) className += " " + cls })
    if (this.wrapper.className != className) this.wrapper.className = className
  }

  // :: () → bool
  // Query whether the view has focus.
  hasFocus() {
    if (this.content.ownerDocument.activeElement != this.content) return false
    let sel = this.root.getSelection()
    return sel.rangeCount && this.content.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode)
  }

  // :: (string, (prop: *) → *) → *
  // Goes over the values of a prop, first those provided directly,
  // then those from plugins (in order), and calls `f` every time a
  // non-undefined value is found. When `f` returns a truthy value,
  // that is immediately returned. When `f` isn't provided, it is
  // treated as the identity function (the prop value is returned
  // directly).
  someProp(propName, f) {
    let prop = this.props && this.props[propName], value
    if (prop && (value = f ? f(prop) : prop)) return value
    let plugins = this.state.plugins
    if (plugins) for (let i = 0; i < plugins.length; i++) {
      let prop = plugins[i].props[propName]
      if (prop != null && (value = f ? f(prop) : prop)) return value
    }
  }

  // :: ()
  // Focus the editor.
  focus() {
    selectionToDOM(this, this.state.selection, true)
    this.content.focus()
  }

  // :: union<dom.Document, dom.DocumentFragment>
  // Get the document root in which the editor exists. This will
  // usually be the top-level `document`, but might be a shadow DOM
  // root if the editor is inside a shadow DOM.
  get root() {
    let cached = this._root
    if (cached == null) for (let search = this.wrapper.parentNode; search; search = search.parentNode) {
      if (search.nodeType == 9 || (search.nodeType == 11 && search.host))
        return this._root = search
    }
    return cached || document
  }

  // :: ({left: number, top: number}) → ?{pos: number, inside: number}
  // Given a pair of coordinates, return the document position that
  // corresponds to them. May return null if the given coordinates
  // aren't inside of the visible editor. When an object is returned,
  // its `pos` property is the position nearest to the coordinates,
  // and its `inside` property holds the position before the inner
  // node that the click happened inside of, or -1 if the click was at
  // the top level.
  posAtCoords(coords) { return posAtCoords(this, coords) }

  // :: (number) → {left: number, right: number, top: number, bottom: number}
  // Returns the screen rectangle at a given document position. `left`
  // and `right` will be the same number, as this returns a flat
  // cursor-ish rectangle.
  coordsAtPos(pos) { return coordsAtPos(this, pos) }

  dispatchKeyDown(event) {
    return dispatchKeyDown(this, event)
  }

  dispatchKeyPress(event) {
    return dispatchKeyPress(this, event)
  }
}
exports.EditorView = EditorView

// EditorProps:: interface
//
// The configuration object that can be passed to an editor view. It
// supports the following properties (only `state` and `onAction` are
// required).
//
// The various event-handling functions may all return `true` to
// indicate that they handled the given event. The view will then take
// care to call `preventDefault` on the event, except with
// `handleDOMEvent, where the handler itself is responsible for that.
//
// Except for `state` and `onAction`, these may also be present on the
// `props` property of plugins. How a prop is resolved depends on the
// prop. Handler functions are called one at a time, starting with the
// plugins (in order of appearance), and finally looking at the base
// props, until one of them returns true. For some props, the first
// plugin that yields a value gets precedence. For `class`, all the
// classes returned are combined.
//
//   state:: EditorState
//   The state of the editor.
//
//   onAction:: (action: Action)
//   The callback over which to send actions (state updates) produced
//   by the view. You'll usually want to make sure this ends up
//   calling the view's [`update`](#view.EditorView.update) method
//   with a new state that has the action
//   [applied](#state.EditorState.applyAction).
//
//   handleDOMEvent:: ?(view: EditorView, event: dom.Event) → bool
//   Called before the view handles a DOM event. This is a kind of
//   catch-all override hook. Contrary to the other event handling
//   props, when returning true from this one, you are responsible for
//   calling `preventDefault` yourself (or not, if you want to allow
//   the default behavior).
//
//   handleKeyDown:: ?(view: EditorView, event: dom.KeyboardEvent) → bool
//   Called when the editor receives a `keydown` event.
//
//   handleKeyPress:: ?(view: EditorView, event: dom.KeyboardEvent) → bool
//   Handler for `keypress` events.
//
//   handleTextInput:: ?(view: EditorView, from: number, to: number, text: string) → bool
//   Whenever the user directly input text, this handler is called
//   before the input is applied. If it returns `true`, the default
//   effect of actually inserting the text is suppressed.
//
//   handleClickOn:: ?(view: EditorView, pos: number, node: Node, nodePos: number, event: dom.MouseEvent) → bool
//   Called for each node around a click, from the inside out.
//
//   handleClick:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when the editor is clicked, after `handleClickOn` handlers
//   have been called.
//
//   handleDoubleClickOn:: ?(view: EditorView, pos: number, node: Node, nodePos: number, event: dom.MouseEvent) → bool
//   Called for each node around a double click.
//
//   handleDoubleClick:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when the editor is double-clicked, after `handleDoubleClickOn`.
//
//   handleTripleClickOn:: ?(view: EditorView, pos: number, node: Node, nodePos: number, event: dom.MouseEvent) → bool
//   Called for each node around a triple click.
//
//   handleTripleClick:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when the editor is triple-clicked, after `handleTripleClickOn`.
//
//   handleContextMenu:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when a context menu event is fired in the editor.
//
//   onFocus:: ?(view: EditorView, event: dom.Event)
//   Called when the editor is focused.
//
//   onBlur:: ?(view: EditorView, event: dom.Event)
//   Called when the editor loses focus.
//
//   onUnmountDOM:: ?(view: EditorView, dom.Node)
//   Called when a display update throws away a DOM node that was part
//   of the previous document view. Can be useful when your node
//   representations need to be cleaned up somehow. Note that this is
//   only called with the top of the unmounted tree, not with every
//   node in it.
//
//   domParser:: ?DOMParser
//   The [parser](#model.DOMParser) to use when reading editor changes
//   from the DOM. Defaults to calling
//   [`DOMParser.fromSchema`](#model.DOMParser^fromSchema) on the
//   editor's schema.
//
//   clipboardParser:: ?DOMParser
//   The [parser](#model.DOMParser) to use when reading content from
//   the clipboard. When not given, the value of the
//   [`domParser`](#view.EditorProps.domParser) prop is used.
//
//   transformPasted:: ?(Slice) → Slice
//   Can be used to transform pasted content before it is applied to
//   the document.
//
//   domSerializer:: ?DOMSerializer
//   The [serializer](#model.DOMSerializer) to use when drawing the
//   document to the display. If not given, the result of
//   [`DOMSerializer.fromSchema`](#model.DOMSerializer^fromSchema)
//   will be used.
//
//   clipboardSerializer:: ?DOMSerializer
//   The DOM serializer to use when putting content onto the
//   clipboard. When not given, the value of the
//   [`domSerializer`](#view.EditorProps.domSerializer) prop is used.
//
//   decorations:: ?DecorationSet
//   A set of [document decorations](#view.Decoration) to add to the
//   view.
//
//   spellcheck:: ?bool
//   Controls whether the DOM spellcheck attribute is enabled on the
//   editable content. Defaults to false.
//
//   class:: ?(state: EditorState) → ?string
//   Controls the CSS class name of the editor DOM node. Any classes
//   returned from this will be added to the default `ProseMirror`
//   class.
//
//   label:: ?(state: EditorState) → ?string
//   Can be used to set an `aria-label` attribute on the editable
//   content node.
//
//   scrollThreshold:: ?number
//   Determines the distance (in pixels) between the cursor and the
//   end of the visible viewport at which point, when scrolling the
//   cursor into view, scrolling takes place. Defaults to 0.
//
//   scrollMargin:: ?number
//   Determines the extra space (in pixels) that is left above or
//   below the cursor when it is scrolled into view. Defaults to 5.
<commit_msg>Fix doc comment for decorations prop<commit_after>const {scrollPosIntoView, posAtCoords, coordsAtPos} = require("./dompos")
const {draw, redraw} = require("./draw")
const {initInput, finishUpdateFromDOM, dispatchKeyDown, dispatchKeyPress} = require("./input")
const {SelectionReader, selectionToDOM} = require("./selection")
const {viewDecorations, addDummy} = require("./decoration")

;({Decoration: exports.Decoration, DecorationSet: exports.DecorationSet} = require("./decoration"))

// ::- An editor view manages the DOM structure that represents an
// editor. Its state and behavior are determined by its
// [props](#view.EditorProps).
class EditorView {
  // :: (?union<dom.Node, (dom.Node)>, EditorProps)
  // Create a view. `place` may be a DOM node that the editor should
  // be appended to, or a function that will place it into the
  // document. If it is `null`, the editor will not be added to the
  // document.
  constructor(place, props) {
    // :: EditorProps
    // The view's current [props](#view.EditorProps).
    this.props = props
    // :: EditorState
    // The view's current [state](#state.EditorState).
    this.state = this.drawnState = props.state
    this.dirty = null

    // :: dom.Node
    // The editable DOM node containing the document. (You probably
    // should not be directly interfering with its child nodes.)
    this.content = document.createElement("div")
    this.content.setAttribute("pm-container", "true")
    this.content.classList.add("ProseMirror-content")

    this.wrapper = document.createElement("div")
    this.wrapper.appendChild(this.content)

    this._root = null

    this.updateDOMForProps()

    if (place && place.appendChild) place.appendChild(this.wrapper)
    else if (place) place(this.wrapper)

    draw(this, this.state.doc, this.drawnDecorations = viewDecorations(this))
    this.content.contentEditable = true

    this.lastSelectedNode = null
    this.selectionReader = new SelectionReader(this)
    initInput(this)
  }

  // :: (EditorProps)
  // Update the view's props. Will immediately cause an update to
  // the view's DOM.
  update(props) {
    this.props = props
    this.updateState(props.state)
    this.updateDOMForProps()
  }

  // :: (EditorState)
  // Update the editor's `state` prop, without touching any of the
  // other props.
  updateState(state) {
    this.state = state

    if (this.inDOMChange) {
      if (state.view.inDOMChange != this.inDOMChange.id)
        setTimeout(() => finishUpdateFromDOM(this), 0)
      return
    } else if (state.view.inDOMChange != null) {
      setTimeout(() => this.props.onAction({type: "endDOMChange"}), 0)
      return
    }

    let redrawn = false
    let docChange = !state.doc.eq(this.drawnState.doc)

    let decorations = viewDecorations(this)
    if (docChange || this.dirty || !decorations.sameOutput(this.drawnDecorations)) {
      this.redraw(state.doc, decorations)
      redrawn = true
    }

    if (redrawn || !state.selection.eq(this.drawnState.selection))
      selectionToDOM(this, state.selection)

    // FIXME somehow schedule this relative to ui/update so that it
    // doesn't cause extra layout
    if (state.view.scrollToSelection)
      scrollPosIntoView(this, state.selection.head == null ? state.selection.from : state.selection.from)

    // Make sure we don't use an outdated range on drop event
    if (this.dragging && docChange) this.dragging.move = false
    this.drawnState = state
  }

  redraw(doc, decorations) {
    let oldDecorations = this.drawnDecorations, oldDoc = this.drawnState.doc
    this.drawnDecorations = decorations

    if (this.dirty) {
      let $start = oldDoc.resolve(this.dirty.from), same = $start.sharedDepth(this.dirty.to)
      this.dirty = null
      if (same == 0)
        return draw(this, doc, decorations)
      oldDecorations = addDummy(decorations, doc, $start.before(same), $start.after(same))
    }
    redraw(this, oldDoc, doc, oldDecorations, decorations)
  }

  updateDOMForProps() {
    let spellcheck = !!this.someProp("spellcheck")
    if (spellcheck != this.content.spellcheck) this.content.spellcheck = spellcheck
    let label = this.someProp("label", f => f(this.state)) || ""
    if (this.content.getAttribute("aria-label") != label) this.content.setAttribute("aria-label", label)
    let className = "ProseMirror"
    this.someProp("class", f => { let cls = f(this.state); if (cls) className += " " + cls })
    if (this.wrapper.className != className) this.wrapper.className = className
  }

  // :: () → bool
  // Query whether the view has focus.
  hasFocus() {
    if (this.content.ownerDocument.activeElement != this.content) return false
    let sel = this.root.getSelection()
    return sel.rangeCount && this.content.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode)
  }

  // :: (string, (prop: *) → *) → *
  // Goes over the values of a prop, first those provided directly,
  // then those from plugins (in order), and calls `f` every time a
  // non-undefined value is found. When `f` returns a truthy value,
  // that is immediately returned. When `f` isn't provided, it is
  // treated as the identity function (the prop value is returned
  // directly).
  someProp(propName, f) {
    let prop = this.props && this.props[propName], value
    if (prop && (value = f ? f(prop) : prop)) return value
    let plugins = this.state.plugins
    if (plugins) for (let i = 0; i < plugins.length; i++) {
      let prop = plugins[i].props[propName]
      if (prop != null && (value = f ? f(prop) : prop)) return value
    }
  }

  // :: ()
  // Focus the editor.
  focus() {
    selectionToDOM(this, this.state.selection, true)
    this.content.focus()
  }

  // :: union<dom.Document, dom.DocumentFragment>
  // Get the document root in which the editor exists. This will
  // usually be the top-level `document`, but might be a shadow DOM
  // root if the editor is inside a shadow DOM.
  get root() {
    let cached = this._root
    if (cached == null) for (let search = this.wrapper.parentNode; search; search = search.parentNode) {
      if (search.nodeType == 9 || (search.nodeType == 11 && search.host))
        return this._root = search
    }
    return cached || document
  }

  // :: ({left: number, top: number}) → ?{pos: number, inside: number}
  // Given a pair of coordinates, return the document position that
  // corresponds to them. May return null if the given coordinates
  // aren't inside of the visible editor. When an object is returned,
  // its `pos` property is the position nearest to the coordinates,
  // and its `inside` property holds the position before the inner
  // node that the click happened inside of, or -1 if the click was at
  // the top level.
  posAtCoords(coords) { return posAtCoords(this, coords) }

  // :: (number) → {left: number, right: number, top: number, bottom: number}
  // Returns the screen rectangle at a given document position. `left`
  // and `right` will be the same number, as this returns a flat
  // cursor-ish rectangle.
  coordsAtPos(pos) { return coordsAtPos(this, pos) }

  dispatchKeyDown(event) {
    return dispatchKeyDown(this, event)
  }

  dispatchKeyPress(event) {
    return dispatchKeyPress(this, event)
  }
}
exports.EditorView = EditorView

// EditorProps:: interface
//
// The configuration object that can be passed to an editor view. It
// supports the following properties (only `state` and `onAction` are
// required).
//
// The various event-handling functions may all return `true` to
// indicate that they handled the given event. The view will then take
// care to call `preventDefault` on the event, except with
// `handleDOMEvent, where the handler itself is responsible for that.
//
// Except for `state` and `onAction`, these may also be present on the
// `props` property of plugins. How a prop is resolved depends on the
// prop. Handler functions are called one at a time, starting with the
// plugins (in order of appearance), and finally looking at the base
// props, until one of them returns true. For some props, the first
// plugin that yields a value gets precedence. For `class`, all the
// classes returned are combined.
//
//   state:: EditorState
//   The state of the editor.
//
//   onAction:: (action: Action)
//   The callback over which to send actions (state updates) produced
//   by the view. You'll usually want to make sure this ends up
//   calling the view's [`update`](#view.EditorView.update) method
//   with a new state that has the action
//   [applied](#state.EditorState.applyAction).
//
//   handleDOMEvent:: ?(view: EditorView, event: dom.Event) → bool
//   Called before the view handles a DOM event. This is a kind of
//   catch-all override hook. Contrary to the other event handling
//   props, when returning true from this one, you are responsible for
//   calling `preventDefault` yourself (or not, if you want to allow
//   the default behavior).
//
//   handleKeyDown:: ?(view: EditorView, event: dom.KeyboardEvent) → bool
//   Called when the editor receives a `keydown` event.
//
//   handleKeyPress:: ?(view: EditorView, event: dom.KeyboardEvent) → bool
//   Handler for `keypress` events.
//
//   handleTextInput:: ?(view: EditorView, from: number, to: number, text: string) → bool
//   Whenever the user directly input text, this handler is called
//   before the input is applied. If it returns `true`, the default
//   effect of actually inserting the text is suppressed.
//
//   handleClickOn:: ?(view: EditorView, pos: number, node: Node, nodePos: number, event: dom.MouseEvent) → bool
//   Called for each node around a click, from the inside out.
//
//   handleClick:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when the editor is clicked, after `handleClickOn` handlers
//   have been called.
//
//   handleDoubleClickOn:: ?(view: EditorView, pos: number, node: Node, nodePos: number, event: dom.MouseEvent) → bool
//   Called for each node around a double click.
//
//   handleDoubleClick:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when the editor is double-clicked, after `handleDoubleClickOn`.
//
//   handleTripleClickOn:: ?(view: EditorView, pos: number, node: Node, nodePos: number, event: dom.MouseEvent) → bool
//   Called for each node around a triple click.
//
//   handleTripleClick:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when the editor is triple-clicked, after `handleTripleClickOn`.
//
//   handleContextMenu:: ?(view: EditorView, pos: number, event: dom.MouseEvent) → bool
//   Called when a context menu event is fired in the editor.
//
//   onFocus:: ?(view: EditorView, event: dom.Event)
//   Called when the editor is focused.
//
//   onBlur:: ?(view: EditorView, event: dom.Event)
//   Called when the editor loses focus.
//
//   onUnmountDOM:: ?(view: EditorView, dom.Node)
//   Called when a display update throws away a DOM node that was part
//   of the previous document view. Can be useful when your node
//   representations need to be cleaned up somehow. Note that this is
//   only called with the top of the unmounted tree, not with every
//   node in it.
//
//   domParser:: ?DOMParser
//   The [parser](#model.DOMParser) to use when reading editor changes
//   from the DOM. Defaults to calling
//   [`DOMParser.fromSchema`](#model.DOMParser^fromSchema) on the
//   editor's schema.
//
//   clipboardParser:: ?DOMParser
//   The [parser](#model.DOMParser) to use when reading content from
//   the clipboard. When not given, the value of the
//   [`domParser`](#view.EditorProps.domParser) prop is used.
//
//   transformPasted:: ?(Slice) → Slice
//   Can be used to transform pasted content before it is applied to
//   the document.
//
//   domSerializer:: ?DOMSerializer
//   The [serializer](#model.DOMSerializer) to use when drawing the
//   document to the display. If not given, the result of
//   [`DOMSerializer.fromSchema`](#model.DOMSerializer^fromSchema)
//   will be used.
//
//   clipboardSerializer:: ?DOMSerializer
//   The DOM serializer to use when putting content onto the
//   clipboard. When not given, the value of the
//   [`domSerializer`](#view.EditorProps.domSerializer) prop is used.
//
//   decorations:: (EditorState) → ?DecorationSet
//   A set of [document decorations](#view.Decoration) to add to the
//   view.
//
//   spellcheck:: ?bool
//   Controls whether the DOM spellcheck attribute is enabled on the
//   editable content. Defaults to false.
//
//   class:: ?(state: EditorState) → ?string
//   Controls the CSS class name of the editor DOM node. Any classes
//   returned from this will be added to the default `ProseMirror`
//   class.
//
//   label:: ?(state: EditorState) → ?string
//   Can be used to set an `aria-label` attribute on the editable
//   content node.
//
//   scrollThreshold:: ?number
//   Determines the distance (in pixels) between the cursor and the
//   end of the visible viewport at which point, when scrolling the
//   cursor into view, scrolling takes place. Defaults to 0.
//
//   scrollMargin:: ?number
//   Determines the extra space (in pixels) that is left above or
//   below the cursor when it is scrolled into view. Defaults to 5.
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'
import debounce from 'lodash/debounce'
import CodeMirror from 'codemirror'

import 'codemirror/addon/selection/active-line'
import 'codemirror/addon/edit/closetag'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/search/match-highlighter'
import 'codemirror/addon/search/search'
import 'codemirror/addon/search/searchcursor'
import 'codemirror/addon/search/jump-to-line'
import 'codemirror/addon/dialog/dialog'
import 'codemirror/addon/scroll/annotatescrollbar'
import 'codemirror/addon/search/matchesonscrollbar'
import 'codemirror/mode/xml/xml'

import { fsReadFile, fsWriteFile } from 'helpers/fs'
import { setPreview } from 'actions/preview'

import './styles.scss'

@connect(state => {
  const { settings } = state
  return {
    mjmlEngine: settings.getIn(['mjml', 'engine'], 'auto'),
    wrapLines: settings.getIn(['editor', 'wrapLines'], true),
  }
}, {
  setPreview,
}, null, { withRef: true })
class FileEditor extends Component {

  state = {
    isLoading: true,
  }

  componentDidMount () {
    this.initEditor()
    this.loadContent()
  }

  componentWillReceiveProps (nextProps) {
    if (this.props.mjmlEngine !== nextProps.mjmlEngine) {
      this.handleChange()
    }
  }

  componentDidUpdate (prevProps) {
    if (prevProps.fileName !== this.props.fileName) {
      this.loadContent()
    }
    if (prevProps.wrapLines !== this.props.wrapLines) {
      this._codeMirror.setOption('lineWrapping', this.props.wrapLines)
    }
  }

  componentWillUnmount () {
    if (this._codeMirror) {
      this._codeMirror.toTextArea()
      this._codeMirror = null
    }
  }

  async loadContent () {
    const { fileName } = this.props
    const { isLoading } = this.state

    if (!isLoading) {
      this.setState({ isLoading: true })
    }

    try {
      const content = await fsReadFile(fileName, { encoding: 'utf8' })
      if (!this._codeMirror) { return }
      this._codeMirror.setValue(content)
      this.setState({ isLoading: false })
    } catch (e) {} // eslint-disable-line

  }

  initEditor () {

    const {
      wrapLines,
    } = this.props

    if (this._codeMirror) {
      this._codeMirror.toTextArea()
      this._codeMirror = null
    }
    this._codeMirror = CodeMirror.fromTextArea(this._textarea, {
      indentUnit: 2,
      tabSize: 2,
      indentWithTabs: false,
      mode: 'xml',
      lineNumbers: true,
      theme: 'one-dark',
      autoCloseTags: true,
      foldGutter: true,
      gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
      styleActiveLine: {
        nonEmpty: true,
      },
      highlightSelectionMatches: {
        wordsOnly: true,
      },
      lineWrapping: wrapLines,
    })
    this._codeMirror.on('change', this.handleChange)
  }

  handleChange = debounce(async () => {
    const {
      setPreview,
      fileName,
      mjmlEngine,
    } = this.props
    const mjml = this._codeMirror.getValue()
    if (mjmlEngine === 'auto') {
      setPreview(fileName, mjml)
      this.debounceWrite(fileName, mjml)
    } else {
      await fsWriteFile(fileName, mjml)
      setPreview(fileName, mjml)
    }
  }, 200)

  debounceWrite = debounce((fileName, mjml) => {
    fsWriteFile(fileName, mjml)
  }, 500)

  refresh = () => {
    this._codeMirror && this._codeMirror.refresh()
  }

  focus = () => {
    this._codeMirror && this._codeMirror.focus()
  }

  render () {

    const {
      disablePointer,
    } = this.props

    const {
      isLoading,
    } = this.state

    return (

        {isLoading && (

            {'...'}

        )}
        <textarea ref={r => this._textarea = r} />

    )
  }

}

export default FileEditor
<commit_msg>Clear Codemirror history when switching file<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'
import debounce from 'lodash/debounce'
import CodeMirror from 'codemirror'

import 'codemirror/addon/selection/active-line'
import 'codemirror/addon/edit/closetag'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/search/match-highlighter'
import 'codemirror/addon/search/search'
import 'codemirror/addon/search/searchcursor'
import 'codemirror/addon/search/jump-to-line'
import 'codemirror/addon/dialog/dialog'
import 'codemirror/addon/scroll/annotatescrollbar'
import 'codemirror/addon/search/matchesonscrollbar'
import 'codemirror/mode/xml/xml'

import { fsReadFile, fsWriteFile } from 'helpers/fs'
import { setPreview } from 'actions/preview'

import './styles.scss'

@connect(state => {
  const { settings } = state
  return {
    mjmlEngine: settings.getIn(['mjml', 'engine'], 'auto'),
    wrapLines: settings.getIn(['editor', 'wrapLines'], true),
  }
}, {
  setPreview,
}, null, { withRef: true })
class FileEditor extends Component {

  state = {
    isLoading: true,
  }

  componentDidMount () {
    this.initEditor()
    this.loadContent()
  }

  componentWillReceiveProps (nextProps) {
    if (this.props.mjmlEngine !== nextProps.mjmlEngine) {
      this.handleChange()
    }
  }

  componentDidUpdate (prevProps) {
    if (prevProps.fileName !== this.props.fileName) {
      this.loadContent()
    }
    if (prevProps.wrapLines !== this.props.wrapLines) {
      this._codeMirror.setOption('lineWrapping', this.props.wrapLines)
    }
  }

  componentWillUnmount () {
    if (this._codeMirror) {
      this._codeMirror.toTextArea()
      this._codeMirror = null
    }
  }

  async loadContent () {
    const { fileName } = this.props
    const { isLoading } = this.state

    if (!isLoading) {
      this.setState({ isLoading: true })
    }

    try {
      const content = await fsReadFile(fileName, { encoding: 'utf8' })
      if (!this._codeMirror) { return }
      this._codeMirror.setValue(content)
      this._codeMirror.clearHistory()
      this.setState({ isLoading: false })
    } catch (e) {} // eslint-disable-line

  }

  initEditor () {

    const {
      wrapLines,
    } = this.props

    if (this._codeMirror) {
      this._codeMirror.toTextArea()
      this._codeMirror = null
    }
    this._codeMirror = CodeMirror.fromTextArea(this._textarea, {
      indentUnit: 2,
      tabSize: 2,
      indentWithTabs: false,
      mode: 'xml',
      lineNumbers: true,
      theme: 'one-dark',
      autoCloseTags: true,
      foldGutter: true,
      gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
      styleActiveLine: {
        nonEmpty: true,
      },
      highlightSelectionMatches: {
        wordsOnly: true,
      },
      lineWrapping: wrapLines,
    })
    this._codeMirror.on('change', this.handleChange)
  }

  handleChange = debounce(async () => {
    const {
      setPreview,
      fileName,
      mjmlEngine,
    } = this.props
    const mjml = this._codeMirror.getValue()
    if (mjmlEngine === 'auto') {
      setPreview(fileName, mjml)
      this.debounceWrite(fileName, mjml)
    } else {
      await fsWriteFile(fileName, mjml)
      setPreview(fileName, mjml)
    }
  }, 200)

  debounceWrite = debounce((fileName, mjml) => {
    fsWriteFile(fileName, mjml)
  }, 500)

  refresh = () => {
    this._codeMirror && this._codeMirror.refresh()
  }

  focus = () => {
    this._codeMirror && this._codeMirror.focus()
  }

  render () {

    const {
      disablePointer,
    } = this.props

    const {
      isLoading,
    } = this.state

    return (

        {isLoading && (

            {'...'}

        )}
        <textarea ref={r => this._textarea = r} />

    )
  }

}

export default FileEditor
<|endoftext|>


<commit_before>//Get the required shit together
const config = require("./config.json");
const Discord = require("discord.js");
const client = new Discord.Client();
const MSS = require("./functions/");
const fs = require("fs");
var command = [];
var reaction = [];

//Login to Discord
client.login(config.API.discord);

//Include all files in the commands directory for commands
fs.readdir("./commands/", function(err, items) {
	items.forEach(function(item) {
		var file = item.replace(/['"]+/g, "");
		if (file.endsWith(".js")) {
			file = file.replace(".js", "")
			command[file] = require("./commands/" + file);
		}
	})
});

//Include all files in the commands directory for reactions
fs.readdir("./reactions/", function(err, items) {
	items.forEach(function(item) {
		var file = item.replace(/['"]+/g, "");
		if (file.endsWith(".js")) {
			file = file.replace(".js", "")
			reaction[file] = require("./reactions/" + file);
		}
	})
});

//List all avaliable commands
console.dir(command);
console.dir(reaction);

client.on("ready", function() {
	console.log("Successfully connected to Discord!");
	client.user.setGame(config.MSS.prefix + "help | " + config.MSS.version);
});

client.on("message", function(message) {
	if (message.author.bot) return;
	if (!message.guild) return MSS.msg.rich(message, "Error", "You are not allowed to send commands via Direct Messaging. If you are trying to invite this bot, please goto: http://moustacheminer.com/r/invite-bot", "#FF0000");

	if (!message.content.startsWith(config.MSS.prefix)) return false;
	let input = message.content.replace (/\n/g, "").split(" ");
	input[0] = input[0].substring(config.MSS.prefix.length);

	if (command[input[0]]) {
		command[input[0]](message);
	}
});

client.on("messageReactionAdd", function(messageReaction, user) {
	//Not on other's messages
	if(!(messageReaction.message.author.id === client.user.id)) return;
	//Not if the author is a bot
	if (user.bot) return;

	//Get decimal codepoint of emoji
	var input = messageReaction.emoji.name.codePointAt().toString();

	console.log(input);

	if (reaction[input]) {
		reaction[input](messageReaction, user);
	}
});
<commit_msg>Moved react commands<commit_after>//Get the required shit together
const config = require("./config.json");
const Discord = require("discord.js");
const client = new Discord.Client();
const MSS = require("./functions/");
const fs = require("fs");
var command = [];
var reaction = [];

//Login to Discord
client.login(config.API.discord);

//Include all files in the commands directory for commands
fs.readdir("./commands/", function(err, items) {
	items.forEach(function(item) {
		var file = item.replace(/['"]+/g, "");
		if (file.endsWith(".js")) {
			file = file.replace(".js", "")
			command[file] = require("./commands/" + file);
		}
	});
	//List all avaliable commands
	console.dir(command);
});

//Include all files in the commands directory for reactions
fs.readdir("./reactions/", function(err, items) {
	items.forEach(function(item) {
		var file = item.replace(/['"]+/g, "");
		if (file.endsWith(".js")) {
			file = file.replace(".js", "")
			reaction[file] = require("./reactions/" + file);
		}
	});
	//List all avalible reactions
	console.dir(reaction);
});

client.on("ready", function() {
	console.log("Successfully connected to Discord!");
	client.user.setGame(config.MSS.prefix + "help | " + config.MSS.version);
});

client.on("message", function(message) {
	if (message.author.bot) return;
	if (!message.guild) return MSS.msg.rich(message, "Error", "You are not allowed to send commands via Direct Messaging. If you are trying to invite this bot, please goto: http://moustacheminer.com/r/invite-bot", "#FF0000");

	if (!message.content.startsWith(config.MSS.prefix)) return false;
	let input = message.content.replace (/\n/g, "").split(" ");
	input[0] = input[0].substring(config.MSS.prefix.length);

	if (command[input[0]]) {
		command[input[0]](message);
	}
});

client.on("messageReactionAdd", function(messageReaction, user) {
	//Not on other's messages
	if(!(messageReaction.message.author.id === client.user.id)) return;
	//Not if the author is a bot
	if (user.bot) return;

	//Get decimal codepoint of emoji
	var input = messageReaction.emoji.name.codePointAt().toString();

	console.log(input);

	if (reaction[input]) {
		reaction[input](messageReaction, user);
	}
});
<|endoftext|>