<commit_before>/*globals Primus, ActiveXObject, CollectGarbage */
'use strict';

/**
 * Get an accurate type check for the given Object.
 *
 * @param {Mixed} obj The object that needs to be detected.
 * @returns {String} The object type
 * @api private
 */
function type(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

/**
 * Checks if the given object is empty. The only edge case here would be
 * objects. Most object's have a `length` attribute that indicate if there's
 * anything inside the object.
 *
 * @returns {Boolean}
 * @api private
 */
function empty(obj) {
  if (!obj) return false;

  if ('object' === type(obj)) {
    for (var key in obj) return false;
    return true;
  }

  return obj.length === 0;
}

/**
 *
 * @constructor
 * @param {String} server The server address we need to connect to.
 * @param {Object} options Pipe configuration
 * @api public
 */
function Pipe(server, options) {
  options = options || {};

  this.stream = null;                   // Reference to the connected Primus socket.
  this.pagelets = {};                   // Collection of different pagelets.
  this.styleSheets = {};                // StyleSheet cache.
  this.root = document.documentElement; // The <html> element.

  Primus.EventEmitter.call(this);

  this.configure(options);
  this.connect(server, options.primus);
}

//
// Inherit from Primus's EventEmitter.
//
Pipe.prototype = new Primus.EventEmitter();
Pipe.prototype.constructor = Pipe;

/**
 * Configure the Pipe.
 *
 * @api private
 */
Pipe.prototype.configure = function configure() {
  if (this.root.className.indexOf('no_js')) {
    this.root.className = this.root.className.replace('no_js', '');
  }
};

(function horror() {
  /**
   * Try to detect if this browser supports the onload events on the link tag.
   * It's a known cross browser bug that can affect WebKit, FireFox and Opera.
   * Internet Explorer is the only browser that supports the onload event
   * consistenly but it has other bigger issues that prevents us from using this
   * method.
   *
   * @param {Element} target
   * @api private
   */
  function detect(target) {
    if (detect.ran) return;
    detect.ran = true;

    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'data:text/css;base64,';

    link.onload = function loaded() {
      link.parentNode.removeChild(link);
      link.onload = false;
      detect.onload = true;
    };

    target.appendChild(link);
  }

  /**
   * Check if all stylesheets have been correctly injected.
   *
   * @returns {Boolean}
   * @api private
   */
  function loaded() {
    var meta, url, style;

    for (url in metaqueue) {
      meta = metaqueue[url];

      if (new Date() - meta.start > timeout) {
        meta.fn(new Error('The styleSheet has timed out'));
        delete meta.fn;
      } else {
        style = window.getComputedStyle
          ? getComputedStyle(meta.tag, null)
          : meta.tag.currentStyle;

        //
        // We assume that the CSS set the height property of the for given id
        // selector.
        //
        if (style && meta.fn && parseInt(style.height, 10) > 1) {
          meta.fn();
          delete meta.fn;
        }
      }

      if (!meta.fn) {
        meta.tag.parentNode.removeChild(meta.tag);
        delete metaqueue[url];
      }
    }

    return empty(metaqueue);
  }

  /**
   * Start polling for StyleSheet changes to detect if a StyleSheet has been
   * loaded. This is done by injecting a <meta> tag in to the page with
   * a dedicated `id` attribute that matches a selector that we've added in the
   * server side for example:
   *
   * ```css
   * #pagelet_af3f399qu { height: 45px }
   * ```
   *
   * @api private
   */
  function poll(url, root, fn) {
    var meta = document.createElement('meta');
    meta.id = 'pagelet_'+ url.split('/').pop().replace('.css').toLowerCase();
    root.appendChild(meta);

    metaqueue[url] = {
      now: +new Date(),
      tag: meta,
      fn: fn
    };

    if (loaded()) return;
    if (!poll.interval) poll.interval = setInterval(function interval() {
      if (loaded()) clearInterval(poll.interval);
    }, 20);
  }

  //
  // Internet Explorer can only have 31 style tags on a single page. One single
  // style tag is also limited to 31 @import statements so this gives us room to
  // have 961 stylesheets totally. So we should queue stylesheets.
  //
  // @see http://john.albin.net/ie-css-limits/two-style-test.html
  // @see http://support.microsoft.com/kb/262161
  //
  var styleSheets = []
    , metaqueue = {}
    , timeout = 5000;

  /**
   * Load a new stylesheet.
   *
   * @param {String} url The stylesheet url that needs to be loaded.
   * @param {Function} fn Completion callback.
   * @api private
   */
  Pipe.prototype.loadStyleSheet = function loadStyleSheet(url, fn) {
    if (url in this.styleSheets) return;

    if (document.styleSheet) {
      for (var sheet, i = 0; i < styleSheets.length; i++) {
        if (styleSheets[i].imports.length < 31) {
          sheet = i;
          break;
        }
      }

      //
      // We didn't find suitable styleSheet to add another @import statement,
      // create a new one so we can leverage that instead.
      //
      // @TODO we should probably check the amount of document.styleSheets.length
      //       to check if we're allowed to add more stylesheets.
      //
      if (sheet === undefined) {
        styleSheets.push(document.createStyleSheet());
        sheet = styleSheets.length - 1;
      }

      styleSheets[sheet].addImport(url);
      this.styleSheets[url] = styleSheets[sheet];
      return poll(url, this.root, fn);
    }

    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.type = 'text/css';
    link.href = url;

    //
    // Only add the onload/onerror listeners when we've detected that's it's
    // supported in the browser.
    //
    if (detect.onload) {
      link.onerror = function onerror() {
        link.onerror = link.onload = null;
        fn(new Error('Failed to load the stylesheet'));
      };

      link.onload = function onload() {
        link.onerror = link.onload = null;
        fn();
      };
    } else {
      poll(url, this.root, fn);

      //
      // We don't have a detect.onload, make sure we've started our feature
      // detection.
      //
      if (!detect.ran) detect(this.root);
    }

    this.styleSheets[url] = link;
    this.root.appendChild(link);
  };

  /**
   * Remove a stylesheet again.
   *
   * @param {String} url The stylesheet url that needs to be unloaded.
   * @api private
   */
  Pipe.prototype.unloadStyleSheet = function unloadStyleSheet(url) {
    if (!(url in this.styleSheets)) return;

    var styleSheet = this.styleSheets[url];

    if (!styleSheet.imports) {
      styleSheet.onload = styleSheet.onerror = null;
      styleSheet.parentNode.removeChild(styleSheet);
    } else {
      for (var i = 0, length = styleSheet.imports.length; i < length; i++) {
        if (styleSheet.imports[i].href === url) {
          styleSheet.removeImport(i);
          break;
        }
      }
    }

    delete this.styleSheets[url];
    delete metaqueue[url];
  };
}());

/**
 * A new Pagelet is flushed by the server. We should register it and update the
 * content.
 *
 * @param {String} name The name of the pagelet.
 * @param {Object} data Pagelet data.
 * @api public
 */
Pipe.prototype.arrive = function arrive(name, data) {
  this.pagelets[name] = new Pagelet(name, data);
  return this;
};

/**
 * Setup a real-time connection to the pagelet server.
 *
 * @param {String} url The server address.
 * @param {Object} options The primus configuration.
 * @api private
 */
Pipe.prototype.connect = function connect(url, options) {
  this.stream = new Primus(url, options);
};

/**
 * Representation of a single pagelet.
 *
 * @param {Pipe} pipe The pipe.
 * @param {String} name The given name of the pagelet.
 * @param {Object} data The data of the pagelet.
 */
function Pagelet(pipe, name, data) {
  Primus.EventEmitter.call(this);

  this.pipe = pipe;
  this.configure(name, data);
}

//
// Inherit from Primus's EventEmitter.
//
Pagelet.prototype = new Primus.EventEmitter();
Pagelet.prototype.constructor = Pagelet;

/**
 * Configure the Pagelet.
 *
 * @api private
 */
Pagelet.prototype.configure = function configure(name, data) {
  this.name = name;
};

/**
 * Find the element based on the attribute and value.
 *
 * @returns {Array|NodeList}
 * @api private
 */
Pagelet.prototype.$ = function $(attribute, value) {
  if (document && 'querySelectorAll' in document) {
    return document.querySelectorAll('['+ attribute +'="'+ value +'"]');
  }

  //
  // No querySelectorAll support, so we're going to do a full DOM scan.
  //
  var all = document.getElementsByTagName('*')
    , length = all.length
    , results = []
    , i = 0;

  for (; i < length; i++) {
    if (all[i].getAttribute(attribute) === value) {
      results.push(all[i]);
    }
  }

  return results;
};

/**
 * Create a sandboxed container for the pagelet to run in.
 *
 * @param {String} code The client side code that needs to be sandboxed.
 * @api private
 */
Pagelet.prototype.sandbox = function sandbox(code) {
  var script = document.getElementsByTagName('script')[0]
    , unique = this.name + (+new Date())
    , container;

  if (!this.htmlfile) {
    try {
      //
      // Internet Explorer 6/7 require a unique name attribute in order to work.
      //
      container = document.createElement('<iframe name="'+ unique +'">');
    } catch (e) {
      container = document.createElement('iframe');
      container.name = unique;
    }

    //
    // The iframe needs to be added in to the DOM before we can modify it, make
    // sure it's remains unseen.
    //
    container.style.display = 'none';
    container.style.position = 'absolute';
    script.parentNode.insertBefore(this.container, script);

    this.container = container.contentDocument || container.contentWindow.document;
    this.container.open();
  } else {
    this.container = new ActiveXObject('htmlfile');
  }

  this.container.write('<html><s'+'cript>'+ code +'</s'+'cript></html>');
  this.container.close();
};

/**
 * Prepare the JavaScript code for iframe injection and sandboxing.
 *
 * @param {String} code The client side code of the pagelet.
 * @returns {String}
 * @api private
 */
Pagelet.prototype.prepare = function prepare(code) {
  return [
    //
    // Force the same domain as our "root" script.
    //
    'document.domain="'+ document.domain +'";',
    '(function (o, h) {',

    //
    // Eliminate the browsers blocking dialogs, we're in a iframe not a browser.
    //
    'for (var i = 0; i < h.length; i++) o[h[i]] = function () {};',

    //
    // The actual client-side code that needs to be evaluated.
    //
    code,

    '})(this, ["alert", "prompt", "confirm"]);'
  ].join('\n');
};

/**
 * Does this browser support HTMLfile's. It's build upon the ActiveXObject and
 * allows us to embed a page within a page without triggering any loading
 * indicators. The added benefit is that it doesn't need to be added to the DOM
 * in order for the page and it's resources to load.
 *
 * It's detected using feature detection.
 *
 * @type {Boolean}
 * @private
 */
Pagelet.prototype.htmlfile = false;

try { Pagelet.prototype.htmlfile = !!new ActiveXObject('htmlfile'); }
catch (e) {}

/**
 * Destroy the pagelet and clean up all references so it can be re-used again in
 * the future.
 *
 * @api public
 */
Pagelet.prototype.destroy = function destroy() {
  if (!this.htmlfile) {
    this.container.parentNode.removeChild(this.container);
    this.container = null;
    return;
  }

  //
  // We need to ensure that all references to the created HTMLFile sandbox are
  // removed before we call the `CollectGarbage` method of Internet Explorer or
  // it will not be cleaned up properly.
  //
  this.container = null;
  CollectGarbage();
};
<commit_msg>[minor] Added a freelist implementation for Pagelet re-use and GC optimization<commit_after>/*globals Primus, ActiveXObject, CollectGarbage */
'use strict';

/**
 * Get an accurate type check for the given Object.
 *
 * @param {Mixed} obj The object that needs to be detected.
 * @returns {String} The object type
 * @api private
 */
function type(obj) {
  return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

/**
 * Checks if the given object is empty. The only edge case here would be
 * objects. Most object's have a `length` attribute that indicate if there's
 * anything inside the object.
 *
 * @returns {Boolean}
 * @api private
 */
function empty(obj) {
  if (!obj) return false;

  if ('object' === type(obj)) {
    for (var key in obj) return false;
    return true;
  }

  return obj.length === 0;
}

/**
 *
 * @constructor
 * @param {String} server The server address we need to connect to.
 * @param {Object} options Pipe configuration
 * @api public
 */
function Pipe(server, options) {
  options = options || {};

  this.stream = null;                   // Reference to the connected Primus socket.
  this.pagelets = {};                   // Collection of different pagelets.
  this.freelist = [];                   // Collection of unused Pagelet instances.
  this.maximum = 20;                    // Max Pagelet instances we can reuse.
  this.styleSheets = {};                // StyleSheet cache.
  this.root = document.documentElement; // The <html> element.

  Primus.EventEmitter.call(this);

  this.configure(options);
  this.connect(server, options.primus);
}

//
// Inherit from Primus's EventEmitter.
//
Pipe.prototype = new Primus.EventEmitter();
Pipe.prototype.constructor = Pipe;

/**
 * Configure the Pipe.
 *
 * @api private
 */
Pipe.prototype.configure = function configure() {
  if (this.root.className.indexOf('no_js')) {
    this.root.className = this.root.className.replace('no_js', '');
  }
};

(function horror() {
  /**
   * Try to detect if this browser supports the onload events on the link tag.
   * It's a known cross browser bug that can affect WebKit, FireFox and Opera.
   * Internet Explorer is the only browser that supports the onload event
   * consistenly but it has other bigger issues that prevents us from using this
   * method.
   *
   * @param {Element} target
   * @api private
   */
  function detect(target) {
    if (detect.ran) return;
    detect.ran = true;

    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'data:text/css;base64,';

    link.onload = function loaded() {
      link.parentNode.removeChild(link);
      link.onload = false;
      detect.onload = true;
    };

    target.appendChild(link);
  }

  /**
   * Check if all stylesheets have been correctly injected.
   *
   * @returns {Boolean}
   * @api private
   */
  function loaded() {
    var meta, url, style;

    for (url in metaqueue) {
      meta = metaqueue[url];

      if (new Date() - meta.start > timeout) {
        meta.fn(new Error('The styleSheet has timed out'));
        delete meta.fn;
      } else {
        style = window.getComputedStyle
          ? getComputedStyle(meta.tag, null)
          : meta.tag.currentStyle;

        //
        // We assume that the CSS set the height property of the for given id
        // selector.
        //
        if (style && meta.fn && parseInt(style.height, 10) > 1) {
          meta.fn();
          delete meta.fn;
        }
      }

      if (!meta.fn) {
        meta.tag.parentNode.removeChild(meta.tag);
        delete metaqueue[url];
      }
    }

    return empty(metaqueue);
  }

  /**
   * Start polling for StyleSheet changes to detect if a StyleSheet has been
   * loaded. This is done by injecting a <meta> tag in to the page with
   * a dedicated `id` attribute that matches a selector that we've added in the
   * server side for example:
   *
   * ```css
   * #pagelet_af3f399qu { height: 45px }
   * ```
   *
   * @api private
   */
  function poll(url, root, fn) {
    var meta = document.createElement('meta');
    meta.id = 'pagelet_'+ url.split('/').pop().replace('.css').toLowerCase();
    root.appendChild(meta);

    metaqueue[url] = {
      now: +new Date(),
      tag: meta,
      fn: fn
    };

    if (loaded()) return;
    if (!poll.interval) poll.interval = setInterval(function interval() {
      if (loaded()) clearInterval(poll.interval);
    }, 20);
  }

  //
  // Internet Explorer can only have 31 style tags on a single page. One single
  // style tag is also limited to 31 @import statements so this gives us room to
  // have 961 stylesheets totally. So we should queue stylesheets.
  //
  // @see http://john.albin.net/ie-css-limits/two-style-test.html
  // @see http://support.microsoft.com/kb/262161
  //
  var styleSheets = []
    , metaqueue = {}
    , timeout = 5000;

  /**
   * Load a new stylesheet.
   *
   * @param {String} url The stylesheet url that needs to be loaded.
   * @param {Function} fn Completion callback.
   * @api private
   */
  Pipe.prototype.loadStyleSheet = function loadStyleSheet(url, fn) {
    if (url in this.styleSheets) return;

    if (document.styleSheet) {
      for (var sheet, i = 0; i < styleSheets.length; i++) {
        if (styleSheets[i].imports.length < 31) {
          sheet = i;
          break;
        }
      }

      //
      // We didn't find suitable styleSheet to add another @import statement,
      // create a new one so we can leverage that instead.
      //
      // @TODO we should probably check the amount of document.styleSheets.length
      //       to check if we're allowed to add more stylesheets.
      //
      if (sheet === undefined) {
        styleSheets.push(document.createStyleSheet());
        sheet = styleSheets.length - 1;
      }

      styleSheets[sheet].addImport(url);
      this.styleSheets[url] = styleSheets[sheet];
      return poll(url, this.root, fn);
    }

    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.type = 'text/css';
    link.href = url;

    //
    // Only add the onload/onerror listeners when we've detected that's it's
    // supported in the browser.
    //
    if (detect.onload) {
      link.onerror = function onerror() {
        link.onerror = link.onload = null;
        fn(new Error('Failed to load the stylesheet'));
      };

      link.onload = function onload() {
        link.onerror = link.onload = null;
        fn();
      };
    } else {
      poll(url, this.root, fn);

      //
      // We don't have a detect.onload, make sure we've started our feature
      // detection.
      //
      if (!detect.ran) detect(this.root);
    }

    this.styleSheets[url] = link;
    this.root.appendChild(link);
  };

  /**
   * Remove a stylesheet again.
   *
   * @param {String} url The stylesheet url that needs to be unloaded.
   * @api private
   */
  Pipe.prototype.unloadStyleSheet = function unloadStyleSheet(url) {
    if (!(url in this.styleSheets)) return;

    var styleSheet = this.styleSheets[url];

    if (!styleSheet.imports) {
      styleSheet.onload = styleSheet.onerror = null;
      styleSheet.parentNode.removeChild(styleSheet);
    } else {
      for (var i = 0, length = styleSheet.imports.length; i < length; i++) {
        if (styleSheet.imports[i].href === url) {
          styleSheet.removeImport(i);
          break;
        }
      }
    }

    delete this.styleSheets[url];
    delete metaqueue[url];
  };
}());

/**
 * A new Pagelet is flushed by the server. We should register it and update the
 * content.
 *
 * @param {String} name The name of the pagelet.
 * @param {Object} data Pagelet data.
 * @api public
 */
Pipe.prototype.arrive = function arrive(name, data) {
  var pagelet = this.pagelets[name] = this.alloc();
  pagelet.configure(name, data);

  return this;
};

/**
 * Allocate a new Pagelet instance.
 *
 * @returns {Pagelet}
 */
Pipe.prototype.alloc = function alloc() {
  return this.freelist.length
    ? this.freelist.shift()
    : new Pagelet(this);
};

/**
 * Free an allocated Pagelet instance which can be re-used again to reduce
 * garbage collection.
 *
 * @param {Pagelet} pagelet The pagelet instance.
 * @api private
 */
Pipe.prototype.free = function free(pagelet) {
  if (this.freelist.length < this.maximum) this.freelist.push(pagelet);
};

/**
 * Setup a real-time connection to the pagelet server.
 *
 * @param {String} url The server address.
 * @param {Object} options The primus configuration.
 * @api private
 */
Pipe.prototype.connect = function connect(url, options) {
  this.stream = new Primus(url, options);
};

/**
 * Representation of a single pagelet.
 *
 * @constructor
 * @param {Pipe} pipe The pipe.
 * @api public
 */
function Pagelet(pipe, name, data) {
  Primus.EventEmitter.call(this);

  this.pipe = pipe;
}

//
// Inherit from Primus's EventEmitter.
//
Pagelet.prototype = new Primus.EventEmitter();
Pagelet.prototype.constructor = Pagelet;

/**
 * Configure the Pagelet.
 *
 * @param {String} name The given name of the pagelet.
 * @param {Object} data The data of the pagelet.
 * @api private
 */
Pagelet.prototype.configure = function configure(name, data) {
  this.name = name;
};

/**
 * Find the element based on the attribute and value.
 *
 * @returns {Array|NodeList}
 * @api private
 */
Pagelet.prototype.$ = function $(attribute, value) {
  if (document && 'querySelectorAll' in document) {
    return document.querySelectorAll('['+ attribute +'="'+ value +'"]');
  }

  //
  // No querySelectorAll support, so we're going to do a full DOM scan.
  //
  var all = document.getElementsByTagName('*')
    , length = all.length
    , results = []
    , i = 0;

  for (; i < length; i++) {
    if (all[i].getAttribute(attribute) === value) {
      results.push(all[i]);
    }
  }

  return results;
};

/**
 * Create a sandboxed container for the pagelet to run in.
 *
 * @param {String} code The client side code that needs to be sandboxed.
 * @api private
 */
Pagelet.prototype.sandbox = function sandbox(code) {
  var script = document.getElementsByTagName('script')[0]
    , unique = this.name + (+new Date())
    , container;

  if (!this.htmlfile) {
    try {
      //
      // Internet Explorer 6/7 require a unique name attribute in order to work.
      //
      container = document.createElement('<iframe name="'+ unique +'">');
    } catch (e) {
      container = document.createElement('iframe');
      container.name = unique;
    }

    //
    // The iframe needs to be added in to the DOM before we can modify it, make
    // sure it's remains unseen.
    //
    container.style.top = container.style.left = -10000;
    container.style.position = 'absolute';
    container.style.display = 'none';
    script.parentNode.insertBefore(this.container, script);

    this.container = container.contentDocument || container.contentWindow.document;
    this.container.open();
  } else {
    this.container = new ActiveXObject('htmlfile');
  }

  this.container.write('<html><s'+'cript>'+ code +'</s'+'cript></html>');
  this.container.close();
};

/**
 * Prepare the JavaScript code for iframe injection and sandboxing.
 *
 * @param {String} code The client side code of the pagelet.
 * @returns {String}
 * @api private
 */
Pagelet.prototype.prepare = function prepare(code) {
  return [
    //
    // Force the same domain as our "root" script.
    //
    'document.domain="'+ document.domain +'";',
    '(function (o, h) {',

    //
    // Eliminate the browsers blocking dialogs, we're in a iframe not a browser.
    //
    'for (var i = 0; i < h.length; i++) o[h[i]] = function () {};',

    //
    // The actual client-side code that needs to be evaluated.
    //
    code,

    '})(this, ["alert", "prompt", "confirm"]);'
  ].join('\n');
};

/**
 * Does this browser support HTMLfile's. It's build upon the ActiveXObject and
 * allows us to embed a page within a page without triggering any loading
 * indicators. The added benefit is that it doesn't need to be added to the DOM
 * in order for the page and it's resources to load.
 *
 * It's detected using feature detection.
 *
 * @type {Boolean}
 * @private
 */
Pagelet.prototype.htmlfile = false;

try { Pagelet.prototype.htmlfile = !!new ActiveXObject('htmlfile'); }
catch (e) {}

/**
 * Destroy the pagelet and clean up all references so it can be re-used again in
 * the future.
 *
 * @TODO remove unused CSS files
 * @api public
 */
Pagelet.prototype.destroy = function destroy() {
  //
  // Automatically schedule this Pagelet instance for re-use.
  //
  this.pipe.free(this);

  if (!this.htmlfile) {
    this.container.parentNode.removeChild(this.container);
    this.container = null;
    return;
  }

  //
  // We need to ensure that all references to the created HTMLFile sandbox are
  // removed before we call the `CollectGarbage` method of Internet Explorer or
  // it will not be cleaned up properly.
  //
  this.container = null;
  CollectGarbage();
};
<|endoftext|>


<commit_before>var tabular = require('./lib/tabular');
var customers = [
  {
    name: "Acme Ltd",
    address: {
      city: "Las Vegas",
      state: "Nevada"
    },
    orders: [
      {
        id: "SO1",
        shipments: [
          {id: "INV1"}, {id: "INV2"}
        ]
      },
      { id: "SO2", orderedAt: new Date("January 1, 2000")}
    ],
    contacts: [
      "Jim",
      "Johanna"
    ]
  }, {
    name: "ABC Co",
    address: {
      city: "Las Angeles"
    },
    orders: [
      {id: "SO3",
    shipments: [{id: "INV3"}]}
    ]
  }, {
    name: "XYZ Inc"
  }
];
var opts = {
  dot: "/",
  separator: '  ',
  dateFormatter: function(date) {return date.toISOString().substr(0,10);},
  sort: ["name", "address/state", "address/city", "-contacts"],
  classes: {
    table: "table table-striped table-bordered"
  }
}
var result = tabular.html(customers, opts);
console.log(result);
<commit_msg>Removed Play File<commit_after><|endoftext|>


<commit_before>$(document).ready(function(){

    $( ".button" ).click(function() {
        var drum = $(this).attr("id");
        var audio = new Audio("myaudio/"+drum+".mp3")
        audio.play();
    });

    $(document).keydown(function(e){
        var drum ="";
        var char = String.fromCharCode(e.which).toLowerCase();
        //console.log (e);
        switch(char){
            case "a" :
            drum="kick";
            break;

            case "s" :
            drum="snare";
            break;

            case "d":
            drum="tomtom1";
            break;

            case "f":
            drum="clap";
            break;

            case "j":
            drum="hihat";
            break;

            case "k":
            drum="cymbal";
            break;

            case "l":
            drum="tomtom2"
            break;

            case ";":
            drum="tomtom3";
            break;
        }
        if (typeof(drum) !=='undefined'){
            console.log(drum)
        $("#"+drum).click();
        }

    });
});

<commit_msg>fix semicolon<commit_after>$(document).ready(function(){

    $( ".button" ).click(function() {
        var drum = $(this).attr("id");
        var audio = new Audio("myaudio/"+drum+".mp3")
        audio.play();
    });

    $(document).keydown(function(e){
        var drum ="";
        var char = String.fromCharCode(e.which).toLowerCase();
        console.log(char);
        switch(char){
            case "a" :
            drum="kick";
            break;

            case "s" :
            drum="snare";
            break;

            case "d":
            drum="tomtom1";
            break;

            case "f":
            drum="clap";
            break;

            case "j":
            drum="hihat";
            break;

            case "k":
            drum="cymbal";
            break;

            case "l":
            drum="tomtom2"
            break;

            case "º":
            drum="tomtom3";
            break;
        }
        if (typeof(drum) !=='undefined'){
        $("#"+drum).click();
        }

    });
});

<|endoftext|>


<commit_before>
let array: number[] = [1, 2, 3.14, 42];
let theAnswer: number = array[3]; // 42
let offTheEnd: number = array[100]; // No error

let array2: Array<string> = ["an alternate", "syntax", "for arrays"];

let p: Promise<*> = fetch('');

function isNumber(x: any): x is number {
    return typeof x === "number";
}

function isString(x: any): x is string {
    return typeof x === "string";
}

function padLeft(value: string, padding: string | number) {
    if (isNumber(padding)) {
        return Array(padding + 1).join(" ") + value;
    }
    if (isString(padding)) {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

var a:string = 'a';

async function pullOne (id: string): Promise<*> {
  // comment!!
  return 1;
}

class test {
  async pullZero (id: string) {
    return 0;
  }
  async pullOne (id: string): Promise<*> {
    return 1;
  }
}
<commit_msg>Type hint for null, void<commit_after>
let array: number[] = [1, 2, 3.14, 42];
let theAnswer: number = array[3]; // 42
let offTheEnd: number = array[100]; // No error

let array2: Array<string> = ["an alternate", "syntax", "for arrays"];

let p: Promise<*> = fetch('');

function isNumber(x: any): x is number {
    return typeof x === "number";
}

function isString(x: any): x is string {
    return typeof x === "string";
}

function padLeft(value: string, padding: string | number) {
    if (isNumber(padding)) {
        return Array(padding + 1).join(" ") + value;
    }
    if (isString(padding)) {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

var a:string = 'a';

async function pullOne (id: string): Promise<*> {
  // comment!!
  return 1;
}

class test {
  async pallZero (id: string): number {
    return 0;
  }
  async pullZero (id: string): void {
    return 0;
  }
  async pullOne (id: string): Promise<*> {
    return 1;
  }
}

var foo1 = bar ? bar : null;
var foo2 = bar ? bar : void;

foo1 = null;
foo2 = void;
<|endoftext|>


<commit_before>var animate = window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function(callback) { window.setTimeout(callback, 1000/60) };

var canvas = document.createElement('canvas');
var width = 800;
var height = 500;
    canvas.width = width;
    canvas.height = height;
var context = canvas.getContext('2d');

window.onload = function() {
  document.body.appendChild(canvas);
  animate(step);
};

var step = function() {
  update();
  render();
  animate(step);
};

var update = function() {
    player.update();
    ball.update(player.paddle, computer.paddle);
};

var render = function() {
  context.fillStyle = "purple";
  context.fillRect(0, 0, width, height);
};

function Paddle(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this.x_speed = 0;
  this.y_speed = 5;
}

Paddle.prototype.render = function() {
  context.fillStyle = "yellow";
  context.fillRect(this.x, this.y, this.width, this.height);
};

function Player() {
   this.paddle = new Paddle(775, 225, 10, 50);
}

function Computer() {
  this.paddle = new Paddle(15, 225, 10, 50);
}

Player.prototype.render = function() {
  this.paddle.render();
};

Computer.prototype.render = function() {
  this.paddle.render();
};

function Ball(x, y) {
  this.x = x;
  this.y = y;
  this.x_speed = 3;
  this.y_speed = 0;
  this.radius = 5;
}

Ball.prototype.render = function() {
  context.beginPath();
  context.arc(this.x, this.y, this.radius, 2 * Math.PI, false);
  context.fillStyle = "white";
  context.fill();
};

var player = new Player();
var computer = new Computer();
var ball = new Ball(400, 250);

var render = function() {
  context.fillStyle = "purple";
  context.fillRect(0, 0, width, height);
  player.render();
  computer.render();
  ball.render();
};

//Adding movement to paddles
var keysDown = {};

window.addEventListener("keydown", function(event) {
  keysDown[event.keyCode] = true;
});

window.addEventListener("keyup", function(event) {
  delete keysDown[event.keyCode];
});

var update = function() {
  player.update();
  ball.update(player.paddle, computer.paddle);
};

Player.prototype.update = function() {
  for(var key in keysDown) {
    var value = Number(key);
    if(value == 38) { // up arrow
      this.paddle.move(0, -4);
    } else if (value == 40) { // down arrow
      this.paddle.move(0, 4);
    } else {
      this.paddle.move(0, 0);
    }
  }
};

Paddle.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  this.x_speed = x;
  this.y_speed = y;
  if(this.y < 0) { // all the way to the top
    this.y = 0;
    this.y_speed = 0;
  } else if (this.y + this.height > 500) { // all the way to the bottom
    this.y = 500 - this.height;
    this.y_speed = 0;
  }
}

//update ball
Ball.prototype.update = function(paddle1, paddle2) {
  this.x += this.x_speed;
  this.y += this.y_speed;
  var left_x = this.x - 5;
  var left_y = this.y - 5;
  var right_x = this.x + 5;
  var right_y = this.y + 5;

  if(this.y - 5 < 0) { // hitting the top wall
    this.y = 5;
    this.y_speed = -this.y_speed;
  } else if(this.y + 5 > 500) { // hitting the bottom wall
    this.y = 495;
    this.y_speed = -this.y_speed;
  }

  if(this.x < 0 || this.x > 800) { // a point was scored
    this.x_speed = 3;
    this.y_speed = 0;
    this.x = 300;
    this.y = 200;
  }
// collision detection
  if(left_x > 200) {
    if(left_x < (paddle1.x + paddle1.width) && right_x > paddle1.x && left_y < (paddle1.y + paddle1.height) && right_y > paddle1.y) {
      // hit the player's paddle
      this.x_speed = -3;
      this.y_speed += (paddle1.x_speed / 2);
      this.x += this.x_speed;
    }
  } else {
    if(left_x < (paddle2.x + paddle2.width) && right_x > paddle2.x && left_y < (paddle2.y + paddle2.height) && right_y > paddle2.y) {
      // hit the computer's paddle
      this.x_speed = 3;
      this.y_speed += (paddle2.y_speed / 2);
      this.x += this.x_speed;
    }
  }
};

<commit_msg>pong-checkpoint-5 problems<commit_after>var animate = window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  window.oRequestAnimationFrame ||
  window.msRequestAnimationFrame ||
  function(callback) { window.setTimeout(callback, 1000/60) };

var canvas = document.createElement('canvas');
var width = 800;
var height = 500;
    canvas.width = width;
    canvas.height = height;
var context = canvas.getContext('2d');

window.onload = function() {
  document.body.appendChild(canvas);
  animate(step);
};

var step = function() {
  update();
  render();
  animate(step);
};

var update = function() {
    player.update();
    computer.update();
    ball.update(player.paddle, computer.paddle);
};

var render = function() {
  context.fillStyle = "purple";
  context.fillRect(0, 0, width, height);
};

function Paddle(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this.x_speed = 0;
  this.y_speed = 5;
}

Paddle.prototype.render = function() {
  context.fillStyle = "yellow";
  context.fillRect(this.x, this.y, this.width, this.height);
};

function Player() {
   this.paddle = new Paddle(775, 225, 10, 50);
}

function Computer() {
  this.paddle = new Paddle(15, 225, 10, 50);
}

Player.prototype.render = function() {
  this.paddle.render();
};

Computer.prototype.render = function() {
  this.paddle.render();
};

function Ball(x, y) {
  this.x = x;
  this.y = y;
  this.x_speed = 3;
  this.y_speed = 0;
  this.radius = 5;
}

Ball.prototype.render = function() {
  context.beginPath();
  context.arc(this.x, this.y, this.radius, 2 * Math.PI, false);
  context.fillStyle = "white";
  context.fill();
};

var player = new Player();
var computer = new Computer();
var ball = new Ball(400, 250);

var render = function() {
  context.fillStyle = "purple";
  context.fillRect(0, 0, width, height);
  player.render();
  computer.render();
  ball.render();
};

//Adding movement to paddles
var keysDown = {};

window.addEventListener("keydown", function(event) {
  keysDown[event.keyCode] = true;
});

window.addEventListener("keyup", function(event) {
  delete keysDown[event.keyCode];
});

var update = function() {
  player.update();
  computer.update(ball);
  ball.update(player.paddle, computer.paddle);
};

Computer.prototype.update = function(ball) {
    var y_pos = ball.y;
    var diff = -((this.paddle.y + (this.paddle.height / 2)) - y_pos);
    if (diff < 0 && diff < -4) { //max speed up
        diff = -5;
    } else if (diff > 0 && diff > 4) { //max speed down
        diff =5;
    }
    this.paddle.move(diff, 0);
    if(this.paddle.y < 0) {
        this.paddle.y = 0;
    } else if (this.paddle.y + this.paddle.height > 500) {
        this.paddle.y = 500 - this.paddle.height;
    }
};

Player.prototype.update = function() {
  for(var key in keysDown) {
    var value = Number(key);
    if(value == 38) { // up arrow
      this.paddle.move(0, -4);
    } else if (value == 40) { // down arrow
      this.paddle.move(0, 4);
    } else {
      this.paddle.move(0, 0);
    }
  }
};

Paddle.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  this.x_speed = x;
  this.y_speed = y;
  if(this.y < 0) { // all the way to the top
    this.y = 0;
    this.y_speed = 0;
  } else if (this.y + this.height > 500) { // all the way to the bottom
    this.y = 500 - this.height;
    this.y_speed = 0;
  }
}

//update ball
Ball.prototype.update = function(paddle1, paddle2) {
  this.x += this.x_speed;
  this.y += this.y_speed;
  var left_x = this.x - 5;
  var left_y = this.y - 5;
  var right_x = this.x + 5;
  var right_y = this.y + 5;

  if(this.y - 5 < 0) { // hitting the top wall
    this.y = 5;
    this.y_speed = -this.y_speed;
  } else if(this.y + 5 > 500) { // hitting the bottom wall
    this.y = 495;
    this.y_speed = -this.y_speed;
  }

  if(this.x < 0 || this.x > 800) { // a point was scored
    this.x_speed = 3;
    this.y_speed = 0;
    this.x = 300;
    this.y = 200;
  }
// collision detection
  if(left_x > 200) {
    if(left_x < (paddle1.x + paddle1.width) && right_x > paddle1.x && left_y < (paddle1.y + paddle1.height) && right_y > paddle1.y) {
      // hit the player's paddle
      this.x_speed = -3;
      this.y_speed += (paddle1.x_speed / 2);
      this.x += this.x_speed;
    }
  } else {
    if(left_x < (paddle2.x + paddle2.width) && right_x > paddle2.x && left_y < (paddle2.y + paddle2.height) && right_y > paddle2.y) {
      // hit the computer's paddle
      this.x_speed = 3;
      this.y_speed += (paddle2.y_speed / 2);
      this.x += this.x_speed;
    }
  }
};

<|endoftext|>


<commit_before>/* jshint node: true, esnext: true */
'use strict';

var AWS = require('aws-sdk');
var modes = require('js-git/lib/modes');

var fs = require('fs');
var path = require('path');

var s3db = require('./s3-db');
var fsdb = require('./fs-db');
var cachedb = require('./cache-db');
var callback = require('./callback');
var queue = require('./queue');

AWS.config.update({ region : 'eu-west-1' });
if (process.env.DEBUG) {
  AWS.config.update({ logger : process.stdout });
}

var listFiles = callback(fs.readdir, fs);
var readFile = callback(fs.readFile, fs);
var stat = callback(fs.stat, fs);

var statFile = queue(async (repo, file, name) => {
  console.log('Reading file: ' + file);
  var buffer = await readFile(file);
  var hash = await repo.hash('blob', buffer);
  return { mode : modes.file, hash, name, path : file };
}, null, 5);

var uploadFile = queue(async (repo, file) => {
  console.log('Uploading file: ' + file);
  var buffer = await readFile(file);
  return await repo.saveAs('blob', buffer);
}, null, 5);

function itemsToTree(items) {
  var tree = {};
  items.forEach(({ name, mode, hash }) => tree[name] = { mode, hash });
  return tree;
}

async function statFolder(repo, folder, name) {
  var files = await listFiles(folder);
  var items = await* files.map(file => statFileOrFolder(repo, folder, file));
  var tree = itemsToTree(items);
  var hash = await repo.hash('tree', tree);
  return { mode : modes.tree, hash, name, items, path : folder };
}

async function uploadItem(repo, item) {
  var hash = await uploadDesc(repo, item);
  return { name : item.name, mode : item.mode, hash };
}

async function uploadFolder(repo, hash, items, folder) {
  var exists = await repo.inStore(hash);
  if (exists) {
    console.log('Already up to date folder: ' + folder);
    return hash;
  } else {
    console.log('Uploading folder: ' + folder);
    var actualItems = await* items.map(item => uploadItem(repo, item));
    var tree = itemsToTree(actualItems);
    return await repo.saveAs('tree', tree);
  }
}

async function statFileOrFolder(repo, folder, file) {
  var item = path.join(folder, file);
  var stats = await stat(item);
  if (stats.isFile()) {
    return await statFile(repo, item, file);
  } else if (stats.isDirectory()) {
    return await statFolder(repo, item, file);
  } else {
    throw new Error('Unrecognized stats: ' + JSON.stringify(stats));
  }
}

async function uploadDesc(repo, desc) {
  if (desc.mode === modes.file) {
    return await uploadFile(repo, desc.path);
  } else {
    return await uploadFolder(repo, desc.hash, desc.items, desc.path);
  }
}

async function upload(repo, folder) {
  var desc = await statFolder(repo, folder, 'IGNORED');
  return await uploadDesc(repo, desc);
}

async function pushOne(repo, ref, folder) {
  var ut = upload(repo, folder);
  var currentCommitHash = await repo.readRef(ref);
  var currentCommit = currentCommitHash ? await repo.loadAs('commit', currentCommitHash) : null;
  var uploadedTreeHash = await ut;
  if (currentCommit && uploadedTreeHash == currentCommit.tree) {
    console.log('Nothing changed in ' + ref);
  } else {
    console.log('Creating commit to ' + uploadedTreeHash);
    var newCommit = {
      parents : [currentCommitHash],
      author : { name : 'Mathieu', email : 'code@mais-h.eu', date : new Date() },
      committer : { name : 'Mathieu', email : 'code@mais-h.eu', date : new Date() },
      tree : uploadedTreeHash,
      message : 'Push'
    };
    var newHash = await repo.saveAs('commit', newCommit);
    console.log('Updating reference ' + ref + ' to ' + newHash);
    await repo.updateRef(ref, currentCommitHash, newHash);
  }
}

async function push(conf) {
  var fsrepo = fsdb(conf.cache);
  var s3repo = await s3db(AWS, conf.bucket, conf.key);
  var repo = cachedb(fsrepo, s3repo, ['tree', 'commit']);
  await* Object.keys(conf.folders).map(ref => pushOne(repo, ref, conf.folders[ref]));
}

push(require('./conf.json'))
  .then(() => console.log('Finished'), err => console.error('Failed', err.stack));
<commit_msg>Use lexical scoping instead of passing repo from function to function<commit_after>/* jshint node: true, esnext: true */
'use strict';

var AWS = require('aws-sdk');
var modes = require('js-git/lib/modes');

var fs = require('fs');
var path = require('path');

var s3db = require('./s3-db');
var fsdb = require('./fs-db');
var cachedb = require('./cache-db');
var callback = require('./callback');
var queue = require('./queue');

AWS.config.update({ region : 'eu-west-1' });
if (process.env.DEBUG) {
  AWS.config.update({ logger : process.stdout });
}

var listFiles = callback(fs.readdir, fs);
var readFile = callback(fs.readFile, fs);
var stat = callback(fs.stat, fs);

function itemsToTree(items) {
  var tree = {};
  items.forEach(({ name, mode, hash }) => tree[name] = { mode, hash });
  return tree;
}

async function push(conf) {
  var fsrepo = fsdb(conf.cache);
  var s3repo = await s3db(AWS, conf.bucket, conf.key);
  var repo = cachedb(fsrepo, s3repo, ['tree', 'commit']);

  var statFile = queue(async (file, name) => {
    console.log('Reading file: ' + file);
    var buffer = await readFile(file);
    var hash = await repo.hash('blob', buffer);
    return { mode : modes.file, hash, name, path : file };
  }, null, 5);

  var uploadFile = queue(async (file) => {
    console.log('Uploading file: ' + file);
    var buffer = await readFile(file);
    return await repo.saveAs('blob', buffer);
  }, null, 5);

  async function statFolder(folder, name) {
    var files = await listFiles(folder);
    var items = await* files.map(file => statFileOrFolder(folder, file));
    var tree = itemsToTree(items);
    var hash = await repo.hash('tree', tree);
    return { mode : modes.tree, hash, name, items, path : folder };
  }

  async function uploadItem(item) {
    var hash = await uploadDesc(item);
    return { name : item.name, mode : item.mode, hash };
  }

  async function uploadFolder(hash, items, folder) {
    var exists = await repo.inStore(hash);
    if (exists) {
      console.log('Already up to date folder: ' + folder);
      return hash;
    } else {
      console.log('Uploading folder: ' + folder);
      var actualItems = await* items.map(uploadItem);
      var tree = itemsToTree(actualItems);
      return await repo.saveAs('tree', tree);
    }
  }

  async function statFileOrFolder(folder, file) {
    var item = path.join(folder, file);
    var stats = await stat(item);
    if (stats.isFile()) {
      return await statFile(item, file);
    } else if (stats.isDirectory()) {
      return await statFolder(item, file);
    } else {
      throw new Error('Unrecognized stats: ' + JSON.stringify(stats));
    }
  }

  async function uploadDesc(desc) {
    if (desc.mode === modes.file) {
      return await uploadFile(desc.path);
    } else {
      return await uploadFolder(desc.hash, desc.items, desc.path);
    }
  }

  async function upload(folder) {
    var desc = await statFolder(folder, 'IGNORED');
    return await uploadDesc(desc);
  }

  async function pushOne(ref, folder) {
    var ut = upload(folder);
    var currentCommitHash = await repo.readRef(ref);
    var currentCommit = currentCommitHash ? await repo.loadAs('commit', currentCommitHash) : null;
    var uploadedTreeHash = await ut;
    if (currentCommit && uploadedTreeHash == currentCommit.tree) {
      console.log('Nothing changed in ' + ref);
    } else {
      console.log('Creating commit to ' + uploadedTreeHash);
      var newCommit = {
        parents : [currentCommitHash],
        author : { name : 'Mathieu', email : 'code@mais-h.eu', date : new Date() },
        committer : { name : 'Mathieu', email : 'code@mais-h.eu', date : new Date() },
        tree : uploadedTreeHash,
        message : 'Push'
      };
      var newHash = await repo.saveAs('commit', newCommit);
      console.log('Updating reference ' + ref + ' to ' + newHash);
      await repo.updateRef(ref, currentCommitHash, newHash);
    }
  }

  await* Object.keys(conf.folders).map(ref => pushOne(ref, conf.folders[ref]));
}

push(require('./conf.json'))
  .then(() => console.log('Finished'), err => console.error('Failed', err.stack));
<|endoftext|>


<commit_before>/**
 * push.js
 * =======
 * A compact, cross-browser solution for Javascript desktop notifications
 *
 * Credits
 * -------
 * Tsvetan Tsvetkov (ttsvetko)
 * Alex Gibson (alexgibson)
 *
 * License
 * -------
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Tyler Nickerson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

(function (global, factory) {

    'use strict';

    /* Use AMD */
    if (typeof define === 'function' && define.amd) {
        define(function () {
            return new (factory(global, global.document))();
        });
    }
    /* Use CommonJS */
    else if (typeof module !== 'undefined' && module.exports) {
        module.exports = (factory(global, global.document))();
    }
    /* Use Browser */
    else {
        global.Push = new (factory(global, global.document))();
    }

})(window !== 'undefined' ? window : self, function (w, d) {

    var Push = function () {

        var
        self = this,
        isUndefined   = function (obj) { return obj === undefined; },
        isString   = function (obj) { return obj && obj.constructor === String; },
        isFunction = function (obj) { return obj && obj.constructor === Function; },

        /* Whether Push has permission to notify */
        hasPermission = false,

        /* List of active notifications */
        notifications = [],

        /**
         * Closes a notification
         * @param {Notification} notification
         * @return {void}
         */
        close_notification = function (notification) {

            /* Safari 6+, Chrome 23+ */
            if (notification.close) {

                notification.close();

            /* Legacy webkit browsers */
            } else if (notification.cancel) {

                notification.cancel();

            /* IE9+ */
            } else if (w.external && w.external.msIsSiteMode) {

                w.external.msSiteModeClearIconOverlay();

            }

        },

        /**
         * Updates the notification count
         * @return {void}
         */
        updateCount = function () {
            self.count = notifications.length;
        },

        /**
         * Callback function for the 'create' method
         * @return {void}
         */
        create_callback = function (title, options) {

            /* Set empty settings if none are specified */
            options = options || {};

            /* Safari 6+, Chrome 23+ */
            if (w.Notification) {

                notification =  new w.Notification(
                    title,
                    {
                        icon: (isString(options.icon) || isUndefined(options.icon)) ? options.icon : options.icon.x32,
                        body: options.body,
                        tag: options.tag,
                    }
                );

            /* Legacy webkit browsers */
            } else if (w.webkitNotifications) {

                notification = win.webkitNotifications.createNotification(
                    options.icon,
                    title,
                    options.body
                );

                notification.show();

            /* Firefox Mobile */
            } else if (navigator.mozNotification) {

                notification = navigator.mozNotification.createNotification(
                    title,
                    options.body,
                    options.icon
                );

                notification.show();

            /* IE9+ */
            } else if (win.external && win.external.msIsSiteMode()) {

                //Clear any previous notifications
                w.external.msSiteModeClearIconOverlay();
                w.external.msSiteModeSetIconOverlay(((isString(options.icon) || isUndefined(options.icon)) ? options.icon : options.icon.x16), title);
                w.external.msSiteModeActivate();

                notification = {};
            }

            /* Wrapper used to close notification later on */
            wrapper = {

                close: function () {
                    close_notification(notification);
                }

            };

            /* Autoclose timeout */
            if (options.timeout) {
                setTimeout(function () {
                    wrapper.close();
                }, options.timeout);
            }

            /* Notification callbacks */
            if (isFunction(options.onShow))
                notification.addEventListener('show', options.onShow);

            if (isFunction(options.onError))
                notification.addEventListener('error', options.onError);

            if (isFunction(options.onClick))
                notification.addEventListener('click', options.onClick);

            if (isFunction(options.onClose)) {
                notification.addEventListener('close', options.onClose);
                notification.addEventListener('cancel', options.onClose);
            }

            /* Add it to the global array */
            notifications.push(notification);

            /* Update the notification count */
            updateCount();

            /* Return the wrapper so the user can call close() */
            return wrapper;
        },

        /**
         * Permission types
         * @enum {String}
         */
        Permission = {
            DEFAULT: 'default',
            GRANTED: 'granted',
            DENIED: 'denied'
        },

        Permissions = [Permission.GRANTED, Permission.DEFAULT, Permission.DENIED];

        /* Allow enums to be accessible from Push object */
        self.Permission = Permission;

        /* Number of open notifications */
        self.count = 0;

        /*****************
            Permissions
        /*****************/

        /**
         * Requests permission for desktop notifications
         * @param {Function} callback - Function to execute once permission is granted
         * @return {void}
         */
        self.Permission.request = function (onGranted, onDenied) {

            /* Return if Push not supported */
            if (!self.isSupported) { return; }

            /* Default callback */
            callback = function (result) {

                switch (result) {

                    case self.Permission.GRANTED:
                        hasPermission = true;
                        if (onGranted) onGranted();
                        break;

                    case self.Permission.DENIED:
                        hasPermission = false;
                        if (onDenied) onDenied();
                        break;

                }

            };

            /* Legacy webkit browsers */
            if (w.webkitNotifications && w.webkitNotifications.checkPermission) {
                w.webkitNotifications.requestPermission(callback);

            /* Safari 6+, Chrome 23+ */
            } else if (w.Notification && w.Notification.requestPermission) {
                w.Notification.requestPermission(callback);
            }

        };

        /**
         * Returns whether Push has been granted permission to run
         * @return {Boolean}
         */
        self.Permission.has = function () {
            return hasPermission;
        };

        /**
         * Gets the permission level
         * @return {Permission} The permission level
         */
        self.Permission.get = function () {

            var permission;

            /* Return if Push not supported */
            if (!self.isSupported) { return; }

            /* Safari 6+, Chrome 23+ */
            if (w.Notification && w.Notification.permissionLevel) {
                permission = w.Notification.permissionLevel;

            /* Legacy webkit browsers */
            } else if (w.webkitNotifications && w.webkitNotifications.checkPermission) {
                permission = Permissions[w.webkitNotifications.checkPermission()];

            /* Firefox 23+ */
            } else if (w.Notification && w.Notification.permission) {
                permission = w.Notification.permission;

            /* Firefox Mobile */
            } else if (navigator.mozNotification) {
                permission = Permissions.GRANTED;

            /* IE9+ */
            } else if (w.external && w.external.msIsSiteMode() !== undefined) {
                permission = w.external.msIsSiteMode() ? Permission.GRANTED : Permission.DEFAULT;
            }

            return permission;

        };

        /*********************
            Other Functions
        /*********************/

        /**
         * Detects whether the user's browser supports notifications
         * @return {Boolean}
         */
        self.isSupported = (function () {

             var isSupported = false;

             try {

                 isSupported =

                     /* Safari, Chrome */
                     !!(w.Notification ||

                     /* Chrome & ff-html5notifications plugin */
                     w.webkitNotifications ||

                     /* Firefox Mobile */
                     navigator.mozNotification ||

                     /* IE9+ */
                     (w.external && w.external.msIsSiteMode() !== undefined));

             } catch (e) {}

             return isSupported;

         })();

         /**
          * Creates and displays a new notification
          * @param {Array} options
          * @return {void}
          */
        self.create = function (title, options) {

            var notification,
                wrapper;

            /* Fail if the browser is not supported */
            if (!self.isSupported) {
                console.error('PushError: push.js is incompatible with self browser.');
                return;
            }

            /* Fail if no or an invalid title is provided */
            if (typeof title !== 'string') {
                throw 'PushError: Title of notification must be a string';
            }

            /* Request permission if it isn't granted */
            if (!self.Permission.has()) {
                self.Permission.request(function () {
                    return create_callback(title, options);
                });
            } else {
                return create_callback(title, options);
            }

        };

        /**
         * Closes a notification with the given tag
         * @param {String} tag - Tag of the notification to close
         * @return {void}
         */
        self.close = function (tag) {

            var i, notification;

            for (i = 0; i < notifications.length; i++) {

                notification = notifications[i];

                /* Run only if the tags match */
                if (notification.tag === tag) {

                    /* Call the notification's close() method */
                    close_notification(notification);

                    /* Remove the notification from the global array */
                    notifications.splice(i, 1);

                    /* Update the notification count */
                    updateCount();

                    /* Return after the first notification is closed */
                    return;

                }
            }

        };

        /**
         * Clears all notifications
         * @return {void}
         */
        self.clear = function () {

            var i;

            for (i = 0; i < notifications.length; i++) {
                close_notification(notifications[i]);
            }

            /* Reset the global array */
            notifications = [];

            /* Update the notification count */
            updateCount();
        };
    };

    return Push;

});
<commit_msg>Fixed issue where legacy code was being called before standard code and changed support checking location<commit_after>/**
 * push.js
 * =======
 * A compact, cross-browser solution for Javascript desktop notifications
 *
 * Credits
 * -------
 * Tsvetan Tsvetkov (ttsvetko)
 * Alex Gibson (alexgibson)
 *
 * License
 * -------
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Tyler Nickerson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

(function (global, factory) {

    'use strict';

    /* Use AMD */
    if (typeof define === 'function' && define.amd) {
        define(function () {
            return new (factory(global, global.document))();
        });
    }
    /* Use CommonJS */
    else if (typeof module !== 'undefined' && module.exports) {
        module.exports = (factory(global, global.document))();
    }
    /* Use Browser */
    else {
        global.Push = new (factory(global, global.document))();
    }

})(window !== 'undefined' ? window : self, function (w, d) {

    var Push = function () {

        var
        self = this,
        isUndefined   = function (obj) { return obj === undefined; },
        isString   = function (obj) { return obj && obj.constructor === String; },
        isFunction = function (obj) { return obj && obj.constructor === Function; },

        /* Whether Push has permission to notify */
        hasPermission = false,

        /* List of active notifications */
        notifications = [],

        /**
         * Closes a notification
         * @param {Notification} notification
         * @return {void}
         */
        close_notification = function (notification) {

            /* Safari 6+, Chrome 23+ */
            if (notification.close) {

                notification.close();

            /* Legacy webkit browsers */
            } else if (notification.cancel) {

                notification.cancel();

            /* IE9+ */
            } else if (w.external && w.external.msIsSiteMode) {

                w.external.msSiteModeClearIconOverlay();

            }

        },

        /**
         * Updates the notification count
         * @return {void}
         */
        updateCount = function () {
            self.count = notifications.length;
        },

        /**
         * Callback function for the 'create' method
         * @return {void}
         */
        create_callback = function (title, options) {

            /* Set empty settings if none are specified */
            options = options || {};

            /* Safari 6+, Chrome 23+ */
            if (w.Notification) {

                notification =  new w.Notification(
                    title,
                    {
                        icon: (isString(options.icon) || isUndefined(options.icon)) ? options.icon : options.icon.x32,
                        body: options.body,
                        tag: options.tag,
                    }
                );

            /* Legacy webkit browsers */
            } else if (w.webkitNotifications) {

                notification = win.webkitNotifications.createNotification(
                    options.icon,
                    title,
                    options.body
                );

                notification.show();

            /* Firefox Mobile */
            } else if (navigator.mozNotification) {

                notification = navigator.mozNotification.createNotification(
                    title,
                    options.body,
                    options.icon
                );

                notification.show();

            /* IE9+ */
            } else if (win.external && win.external.msIsSiteMode()) {

                //Clear any previous notifications
                w.external.msSiteModeClearIconOverlay();
                w.external.msSiteModeSetIconOverlay(((isString(options.icon) || isUndefined(options.icon)) ? options.icon : options.icon.x16), title);
                w.external.msSiteModeActivate();

                notification = {};
            }

            /* Wrapper used to close notification later on */
            wrapper = {

                close: function () {
                    close_notification(notification);
                }

            };

            /* Autoclose timeout */
            if (options.timeout) {
                setTimeout(function () {
                    wrapper.close();
                }, options.timeout);
            }

            /* Notification callbacks */
            if (isFunction(options.onShow))
                notification.addEventListener('show', options.onShow);

            if (isFunction(options.onError))
                notification.addEventListener('error', options.onError);

            if (isFunction(options.onClick))
                notification.addEventListener('click', options.onClick);

            if (isFunction(options.onClose)) {
                notification.addEventListener('close', options.onClose);
                notification.addEventListener('cancel', options.onClose);
            }

            /* Add it to the global array */
            notifications.push(notification);

            /* Update the notification count */
            updateCount();

            /* Return the wrapper so the user can call close() */
            return wrapper;
        },

        /**
         * Permission types
         * @enum {String}
         */
        Permission = {
            DEFAULT: 'default',
            GRANTED: 'granted',
            DENIED: 'denied'
        },

        Permissions = [Permission.GRANTED, Permission.DEFAULT, Permission.DENIED];

        /* Allow enums to be accessible from Push object */
        self.Permission = Permission;

        /* Number of open notifications */
        self.count = 0;

        /*****************
            Permissions
        /*****************/

        /**
         * Requests permission for desktop notifications
         * @param {Function} callback - Function to execute once permission is granted
         * @return {void}
         */
        self.Permission.request = function (onGranted, onDenied) {

            /* Return if Push not supported */
            if (!self.isSupported) {
                console.error("This browser is not supported");
                return;
            }

            /* Default callback */
            callback = function (result) {

                switch (result) {

                    case self.Permission.GRANTED:
                        hasPermission = true;
                        if (onGranted) onGranted();
                        break;

                    case self.Permission.DENIED:
                        hasPermission = false;
                        if (onDenied) onDenied();
                        break;

                }

            };

            /* Safari 6+, Chrome 23+ */
            if (w.Notification && w.Notification.requestPermission) {
                Notification.requestPermission(callback);
            }
            /* Legacy webkit browsers */
            else if (w.webkitNotifications && w.webkitNotifications.checkPermission) {
                w.webkitNotifications.requestPermission(callback);
            }

        };

        /**
         * Returns whether Push has been granted permission to run
         * @return {Boolean}
         */
        self.Permission.has = function () {
            return hasPermission;
        };

        /**
         * Gets the permission level
         * @return {Permission} The permission level
         */
        self.Permission.get = function () {

            var permission;

            /* Return if Push not supported */
            if (!self.isSupported) { return; }

            /* Safari 6+, Chrome 23+ */
            if (w.Notification && w.Notification.permissionLevel) {
                permission = w.Notification.permissionLevel;

            /* Legacy webkit browsers */
            } else if (w.webkitNotifications && w.webkitNotifications.checkPermission) {
                permission = Permissions[w.webkitNotifications.checkPermission()];

            /* Firefox 23+ */
            } else if (w.Notification && w.Notification.permission) {
                permission = w.Notification.permission;

            /* Firefox Mobile */
            } else if (navigator.mozNotification) {
                permission = Permissions.GRANTED;

            /* IE9+ */
            } else if (w.external && w.external.msIsSiteMode() !== undefined) {
                permission = w.external.msIsSiteMode() ? Permission.GRANTED : Permission.DEFAULT;
            }

            return permission;

        };

        /*********************
            Other Functions
        /*********************/

        /**
         * Detects whether the user's browser supports notifications
         * @return {Boolean}
         */
        self.isSupported = (function () {

             var isSupported = false;

             try {

                 isSupported =

                     /* Safari, Chrome */
                     !!(w.Notification ||

                     /* Chrome & ff-html5notifications plugin */
                     w.webkitNotifications ||

                     /* Firefox Mobile */
                     navigator.mozNotification ||

                     /* IE9+ */
                     (w.external && w.external.msIsSiteMode() !== undefined));

             } catch (e) {}

             return isSupported;

         })();

         /**
          * Creates and displays a new notification
          * @param {Array} options
          * @return {void}
          */
        self.create = function (title, options) {
            var notification,
                wrapper;

            /* Fail if the browser is not supported */
            if (!self.isSupported) {
                console.error('PushError: push.js is incompatible with self browser.');
                return;
            }

            /* Fail if no or an invalid title is provided */
            if (typeof title !== 'string') {
                throw 'PushError: Title of notification must be a string';
            }

            /* Request permission if it isn't granted */
            if (!self.Permission.has()) {
                self.Permission.request(function () {
                    return create_callback(title, options);
                });
            } else {
                return create_callback(title, options);
            }

        };

        /**
         * Closes a notification with the given tag
         * @param {String} tag - Tag of the notification to close
         * @return {void}
         */
        self.close = function (tag) {

            var i, notification;

            for (i = 0; i < notifications.length; i++) {

                notification = notifications[i];

                /* Run only if the tags match */
                if (notification.tag === tag) {

                    /* Call the notification's close() method */
                    close_notification(notification);

                    /* Remove the notification from the global array */
                    notifications.splice(i, 1);

                    /* Update the notification count */
                    updateCount();

                    /* Return after the first notification is closed */
                    return;

                }
            }

        };

        /**
         * Clears all notifications
         * @return {void}
         */
        self.clear = function () {

            var i;

            for (i = 0; i < notifications.length; i++) {
                close_notification(notifications[i]);
            }

            /* Reset the global array */
            notifications = [];

            /* Update the notification count */
            updateCount();
        };
    };

    return Push;

});
<|endoftext|>


<commit_before>exports = module.exports = {};

const firebase = require("./firebase.js");
const send = require("./sendingHelpers.js")

const LEVEL_TO_TIME = {1: 86400, 2: 172800, 3: 604800, 4: 1209600, 5: 5259486};

// returns time difference in seconds
function getTimeDifference(date) {
  var d = new Date();
  var n = d.getTime();
  //return (n - date) / 1000;
  return 5259486; // for testing purposes: so we actually have some words to review lol
}

function findWords(userId) {
  firebase.getUserWords(userId).then(function(words) {
    var wordsToQuiz = [];
    for (var key in words) {
      if (getTimeDifference(words[key].time) >= LEVEL_TO_TIME[words[key].level]) {
        words[key].englishWord = key;
        wordsToQuiz.push(words[key]);
      };
    }
    firebase.updateUserQuizItems(userId, shuffle(wordsToQuiz)).then(function () {
      quizNext(userId);
    });
  });
};

function quizNext(userId) {
  firebase.getCurrentQuizWord(userId).then(function(word) {
    if (word) {
      send.sendTextMessage(userId, word.translation);
    } else {
      // no words left, quiz is done
      firebase.endUserQuiz(userId);
      send.sendTextMessage(userId, "That's all the words you need to review for now.");
    }
  });
}

exports.beginQuiz = function(userId) {
  firebase.beginUserQuiz(userId);
  send.sendTextMessage(userId, "Great, let's start the quiz! \n\n I'll send you some words, and you translate them to English");
  findWords(userId);
};

exports.evaluateAnswer = function evaluateAnswer(userId, currentGuess, timeOfGuess) {
  firebase.getCurrentQuizWord(userId).then(function(currentWord) {
    removeWordFromQuizList(userId, currentWord.englishWord)
    currentWord.time = timeOfGuess;
    var returnMessage = null;
    if (currentWord.englishWord.toLowerCase() === currentGuess.toLowerCase()) {
      // TODO: If level is 5 we don't want to increase it any more, otherwise we do
      currentWord.level = currentWord.level + 1;
      returnMessage = "Correct! Here is your next word.";
    }
    else {
      // Wrong guess, back to level 1 loser!!!
      currentWord.level =  1;
      returnMessage = "That is incorrect. The correct answer is " + currentWord.englishWord.toLowerCase() + ".";
    }
    send.sendTextMessage(userId, returnMessage);
    updateWord(userId, currentWord);
    removeWordFromQuizList(userId, currentWord).then(function() {
      quizNext(userId);
    });
  })
}

function updateWord(senderID, wordEntry) {
  firebase.addWord(senderID, wordEntry.englishWord, wordEntry);
}

function removeWordFromQuizList(userId, currentWord) {
 return firebase.getCurrentQuizWords(userId).then(function(words) {
   words = words.splice(1);
   firebase.updateUserQuizItems(userId, words);
 });
}

// From StackOverflow https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
} <commit_msg>🛣🍍 Updated with Glitch<commit_after>exports = module.exports = {};

const firebase = require("./firebase.js");
const send = require("./sendingHelpers.js")

const LEVEL_TO_TIME = {1: 86400, 2: 172800, 3: 604800, 4: 1209600, 5: 5259486};

// returns time difference in seconds
function getTimeDifference(date) {
  var d = new Date();
  var n = d.getTime();
  //return (n - date) / 1000;
  return 5259486; // for testing purposes: so we actually have some words to review lol
}

function findWords(userId) {
  firebase.getUserWords(userId).then(function(words) {
    var wordsToQuiz = [];
    for (var key in words) {
      if (getTimeDifference(words[key].time) >= LEVEL_TO_TIME[words[key].level]) {
        words[key].englishWord = key;
        wordsToQuiz.push(words[key]);
      };
    }
    firebase.updateUserQuizItems(userId, shuffle(wordsToQuiz)).then(function () {
      quizNext(userId);
    });
  });
};

function quizNext(userId) {
  firebase.getCurrentQuizWord(userId).then(function(word) {
    if (word) {
      send.sendTextMessage(userId, word.translation);
    } else {
      // no words left, quiz is done
      firebase.endUserQuiz(userId);
      send.sendTextMessage(userId, "That's all the words you need to review for now.");
    }
  });
}

exports.beginQuiz = function(userId) {
  firebase.beginUserQuiz(userId);
  send.sendTextMessage(userId, "Great, let's start the quiz! \n\n I'll send you some words, and you translate them to English");
  findWords(userId);
};

exports.evaluateAnswer = function evaluateAnswer(userId, currentGuess, timeOfGuess) {
  firebase.getCurrentQuizWord(userId).then(function(currentWord) {
    removeWordFromQuizList(userId, currentWord.englishWord)
    currentWord.time = timeOfGuess;
    var returnMessage = null;
    if (currentWord.englishWord.toLowerCase() === currentGuess.text.toLowerCase()) {
      // TODO: If level is 5 we don't want to increase it any more, otherwise we do
      currentWord.level = currentWord.level + 1;
      returnMessage = "Correct! Here is your next word.";
    }
    else {
      // Wrong guess, back to level 1 loser!!!
      currentWord.level =  1;
      returnMessage = "That is incorrect. The correct answer is " + currentWord.englishWord.toLowerCase() + ".";
    }
    send.sendTextMessage(userId, returnMessage);
    updateWord(userId, currentWord);
    removeWordFromQuizList(userId, currentWord).then(function() {
      quizNext(userId);
    });
  })
}

function updateWord(senderID, wordEntry) {
  firebase.addWord(senderID, wordEntry.englishWord, wordEntry);
}

function removeWordFromQuizList(userId, currentWord) {
 return firebase.getCurrentQuizWords(userId).then(function(words) {
   words = words.splice(1);
   firebase.updateUserQuizItems(userId, words);
 });
}

// From StackOverflow https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
} <|endoftext|>


<commit_before>"use strict";

class gsuiOscillator extends HTMLElement {
	constructor() {
		const children = GSUI.getTemplate( "gsui-oscillator" ),
			waves = [
				children[ 1 ].firstChild,
				children[ 1 ].lastChild,
			];

		super();
		this._children = children;
		this._waves = waves;
		this._elSelect = children[ 4 ];
		this._timeidType = null;
		this._sliders = Object.freeze( {
			detune: this._initSlider( 5, "detune" ),
			pan: this._initSlider( 6, "pan" ),
			gain: this._initSlider( 7, "gain" ),
		} );
		this._selectWaves = {
			sine: true,
			triangle: true,
			sawtooth: true,
			square: true,
		};
		this._dispatch = GSUI.dispatchEvent.bind( null, this, "gsuiOscillator" );
		Object.seal( this );

		waves[ 0 ].frequency =
		waves[ 1 ].frequency = 1;
		this._elSelect.onchange = this._onchangeSelect.bind( this );
		this._elSelect.onkeydown = this._onkeydownSelect.bind( this );
		children[ 2 ].onclick = this._onclickPrevNext.bind( this, -1 );
		children[ 3 ].onclick = this._onclickPrevNext.bind( this, 1 );
		children[ 8 ].onclick = () => this._dispatch( "remove" );
	}

	// .........................................................................
	connectedCallback() {
		if ( this._children ) {
			this.classList.add( "gsuiOscillator" );
			this.setAttribute( "draggable", "true" );
			this.append( ...this._children );
			this._children = null;
		}
	}
	static get observedAttributes() {
		return [ "order", "type", "detune", "gain", "pan" ];
	}
	attributeChangedCallback( prop, prev, val ) {
		if ( prev !== val ) {
			const num = +val;

			switch ( prop ) {
				case "order": this._changeOrder( num ); break;
				case "type": this._changeType( val ); break;
				case "detune":
				case "gain":
				case "pan":
					this._changeProp( prop, num );
					break;
			}
		}
	}

	// .........................................................................
	addWaves( arr ) {
		const opts = [];

		arr.sort();
		arr.forEach( w => {
			if ( !this._selectWaves[ w ] ) {
				const opt = document.createElement( "option" );

				this._selectWaves[ w ] = true;
				opt.value = w;
				opt.className = "gsuiOscillator-waveOpt";
				opt.textContent = w;
				opts.push( opt );
			}
		} );
		Element.prototype.append.apply( this._elSelect, opts );
	}
	updateWave( prop, val ) {
		const [ w0, w1 ] = this._waves,
			gain = prop === "gain" ? val : +this.getAttribute( "gain" ),
			pan = prop === "pan" ? val : +this.getAttribute( "pan" );

		w0.type =
		w1.type = prop === "type" ? val : this.getAttribute( "type" );
		w0.amplitude = Math.min( gain * ( pan < 0 ? 1 : 1 - pan ), .95 );
		w1.amplitude = Math.min( gain * ( pan > 0 ? 1 : 1 + pan ), .95 );
		w0.draw();
		w1.draw();
	}

	// .........................................................................
	_changeOrder( n ) {
		this.dataset.order = n;
	}
	_changeType( type ) {
		this._elSelect.value = type;
	}
	_changeProp( prop, val ) {
		const [ sli, span ] = this._sliders[ prop ];

		sli.setValue( val );
		span.textContent = prop === "detune" ? val : val.toFixed( 2 );
	}

	// .........................................................................
	_initSlider( childNum, prop ) {
		const root = this._children[ childNum ],
			slider = root.firstChild.firstChild,
			elValue = root.lastChild;

		slider.oninput = this._oninputSlider.bind( this, prop );
		slider.onchange = val => this._dispatch( "change", prop, val );
		return Object.freeze( [ slider, elValue ] );
	}

	// events:
	// .........................................................................
	_onclickPrevNext( dir ) {
		const sel = this._elSelect,
			currOpt = sel.querySelector( `option[value="${ sel.value }"]` ),
			opt = dir < 0
				? currOpt.previousElementSibling
				: currOpt.nextElementSibling;

		if ( opt ) {
			sel.value = opt.value;
			this._onchangeSelect();
		}
	}
	_onchangeSelect() {
		const type = this._elSelect.value;

		clearTimeout( this._timeidType );
		this.updateWave( "type", type );
		this._dispatch( "liveChange", "type", type );
		this._timeidType = setTimeout( () => {
			if ( type !== this.getAttribute( "type" ) ) {
				this._dispatch( "change", "type", type );
			}
		}, 700 );
	}
	_onkeydownSelect( e ) {
		if ( e.key.length === 1 ) {
			e.preventDefault();
		}
	}
	_oninputSlider( prop, val ) {
		let val2 = val;

		if ( prop === "gain" ) {
			this.updateWave( "gain", val );
			val2 = val.toFixed( 2 );
		} else if ( prop === "pan" ) {
			this.updateWave( "pan", val );
			val2 = val.toFixed( 2 );
		}
		this._sliders[ prop ][ 1 ].textContent = val2;
		this._dispatch( "liveChange", prop, +val2 );
	}
}

customElements.define( "gsui-oscillator", gsuiOscillator );

Object.freeze( gsuiOscillator );
<commit_msg>gsuiOscillator: sync the attributes<commit_after>"use strict";

class gsuiOscillator extends HTMLElement {
	constructor() {
		const children = GSUI.getTemplate( "gsui-oscillator" ),
			waves = [
				children[ 1 ].firstChild,
				children[ 1 ].lastChild,
			];

		super();
		this._children = children;
		this._waves = waves;
		this._elSelect = children[ 4 ];
		this._timeidType = null;
		this._sliders = Object.freeze( {
			detune: this._initSlider( 5, "detune" ),
			pan: this._initSlider( 6, "pan" ),
			gain: this._initSlider( 7, "gain" ),
		} );
		this._selectWaves = {
			sine: true,
			triangle: true,
			sawtooth: true,
			square: true,
		};
		this._dispatch = GSUI.dispatchEvent.bind( null, this, "gsuiOscillator" );
		Object.seal( this );

		waves[ 0 ].frequency =
		waves[ 1 ].frequency = 1;
		this._elSelect.onchange = this._onchangeSelect.bind( this );
		this._elSelect.onkeydown = this._onkeydownSelect.bind( this );
		children[ 2 ].onclick = this._onclickPrevNext.bind( this, -1 );
		children[ 3 ].onclick = this._onclickPrevNext.bind( this, 1 );
		children[ 8 ].onclick = () => this._dispatch( "remove" );
	}

	// .........................................................................
	connectedCallback() {
		if ( this._children ) {
			this.classList.add( "gsuiOscillator" );
			this.setAttribute( "draggable", "true" );
			this.append( ...this._children );
			this._children = null;
			GSUI.recallAttributes( this, {
				order: 0,
				type: "sine",
				detune: 0,
				gain: 1,
				pan: 0,
			} );
			this.updateWave();
		}
	}
	static get observedAttributes() {
		return [ "order", "type", "detune", "gain", "pan" ];
	}
	attributeChangedCallback( prop, prev, val ) {
		if ( !this._children && prev !== val ) {
			const num = +val;

			switch ( prop ) {
				case "order": this._changeOrder( num ); break;
				case "type": this._changeType( val ); break;
				case "detune":
				case "gain":
				case "pan":
					this._changeProp( prop, num );
					break;
			}
		}
	}

	// .........................................................................
	addWaves( arr ) {
		const opts = [];

		arr.sort();
		arr.forEach( w => {
			if ( !this._selectWaves[ w ] ) {
				const opt = document.createElement( "option" );

				this._selectWaves[ w ] = true;
				opt.value = w;
				opt.className = "gsuiOscillator-waveOpt";
				opt.textContent = w;
				opts.push( opt );
			}
		} );
		Element.prototype.append.apply( this._elSelect, opts );
	}
	updateWave( prop, val ) {
		const [ w0, w1 ] = this._waves,
			gain = prop === "gain" ? val : +this.getAttribute( "gain" ),
			pan = prop === "pan" ? val : +this.getAttribute( "pan" );

		w0.type =
		w1.type = prop === "type" ? val : this.getAttribute( "type" );
		w0.amplitude = Math.min( gain * ( pan < 0 ? 1 : 1 - pan ), .95 );
		w1.amplitude = Math.min( gain * ( pan > 0 ? 1 : 1 + pan ), .95 );
		w0.draw();
		w1.draw();
	}

	// .........................................................................
	_changeOrder( n ) {
		this.dataset.order = n;
	}
	_changeType( type ) {
		this._elSelect.value = type;
	}
	_changeProp( prop, val ) {
		const [ sli, span ] = this._sliders[ prop ];

		sli.setValue( val );
		span.textContent = prop === "detune" ? val : val.toFixed( 2 );
	}

	// .........................................................................
	_initSlider( childNum, prop ) {
		const root = this._children[ childNum ],
			slider = root.firstChild.firstChild,
			elValue = root.lastChild;

		slider.oninput = this._oninputSlider.bind( this, prop );
		slider.onchange = this._onchangeSlider.bind( this, prop );
		return Object.freeze( [ slider, elValue ] );
	}

	// events:
	// .........................................................................
	_onclickPrevNext( dir ) {
		const sel = this._elSelect,
			currOpt = sel.querySelector( `option[value="${ sel.value }"]` ),
			opt = dir < 0
				? currOpt.previousElementSibling
				: currOpt.nextElementSibling;

		if ( opt ) {
			sel.value = opt.value;
			this._onchangeSelect();
		}
	}
	_onchangeSelect() {
		const type = this._elSelect.value;

		clearTimeout( this._timeidType );
		this.updateWave( "type", type );
		this._dispatch( "liveChange", "type", type );
		this._timeidType = setTimeout( () => {
			if ( type !== this.getAttribute( "type" ) ) {
				this.setAttribute( "type", type );
				this._dispatch( "change", "type", type );
			}
		}, 700 );
	}
	_onkeydownSelect( e ) {
		if ( e.key.length === 1 ) {
			e.preventDefault();
		}
	}
	_onchangeSlider( prop, val ) {
		this.setAttribute( prop, val );
		this._dispatch( "change", prop, val );
	}
	_oninputSlider( prop, val ) {
		let val2 = val;

		if ( prop === "gain" ) {
			this.updateWave( "gain", val );
			val2 = val.toFixed( 2 );
		} else if ( prop === "pan" ) {
			this.updateWave( "pan", val );
			val2 = val.toFixed( 2 );
		}
		this._sliders[ prop ][ 1 ].textContent = val2;
		this._dispatch( "liveChange", prop, +val2 );
	}
}

customElements.define( "gsui-oscillator", gsuiOscillator );

Object.freeze( gsuiOscillator );
<|endoftext|>


<commit_before>import Articles from "../../../collections/articles.coffee"
import Backbone from "backbone"
import sinon from "sinon"
import request from "superagent"
let routes = require("rewire")("../routes.js")

describe("Routes", () => {
  let req = {}
  let res = {}
  const published_at = new Date().toISOString()
  let results = {
    articles: [
      {
        id: "1",
        published_at,
        sections: [
          {
            type: "social_embed",
            url: "https://instagram.com/image",
          },
        ],
      },
      {
        id: "2",
        published_at,
        sections: [
          {
            type: "text",
            body: "Body Text",
          },
        ],
      },
      {
        id: "3",
        published_at,
        sections: [
          {
            type: "text",
            body: "Body Text 2",
          },
        ],
      },
    ],
  }

  beforeEach(() => {
    sinon.stub(Backbone, "sync")
    request.get = sinon.stub().returns({
      end: cb => {
        cb(null, {
          body: {
            html: '
',
          },
        })
      },
    })

    routes.__set__("sd", {
      ARTSY_EDITORIAL_CHANNEL: "foo",
    })
    routes.__set__("request", request)
    routes.__set__("positronql", sinon.stub().returns(Promise.resolve(results)))

    req = {}
    res = {
      render: sinon.stub(),
      set: sinon.stub(),
    }
  })

  afterEach(() => {
    Backbone.sync.restore()
  })

  describe("#partnerUpdates", () => {
    it("renders articles", () => {
      routes.partnerUpdates(req, res)
      Backbone.sync.args[0][2].success({
        total: 16088,
        count: 2,
        results: results.articles,
      })
      res.render.args[0][0].should.containEql("partner_updates")
      res.render.args[0][1].articles.length.should.eql(3)
    })
  })

  describe("#news", () => {
    it("renders the rss feed if #findArticlesWithEmbeds rejects", async () => {
      request.get = sinon.stub().returns({
        end: cb => {
          cb(new Error())
        },
      })
      routes.__set__("request", request)
      await routes.news(req, res)
      res.render.args[0][0].should.containEql("news")
      res.render.args[0][1].articles.length.should.eql(3)
    })

    it("renders the rss feed for news without unpublished video articles", async () => {
      const articlesWithVideo = [
        ...results.articles,
        {
          id: "4",
          published_at,
          layout: "video",
          media: {
            published: false,
          },
        },
      ]

      routes.__set__(
        "positronql",
        sinon.stub().returns(
          Promise.resolve({
            articles: articlesWithVideo,
          })
        )
      )

      const findArticlesWithEmbedsStub = sinon.stub()

      routes.__set__(
        "findArticlesWithEmbeds",
        findArticlesWithEmbedsStub.returns(
          Promise.resolve(new Articles(results.articles))
        )
      )

      await routes.news(req, res)

      // This ensures that articles are filtered between the positron call and the #findArticlesWithEmbeds call
      findArticlesWithEmbedsStub.firstCall.args[0].length.should.eql(3)

      res.render.args[0][0].should.containEql("news")
      res.render.args[0][1].articles.length.should.eql(3)
    })
  })

  describe("#findSocialEmbeds", () => {
    let article = {
      sections: [{}, undefined],
    }

    it("filters out undefined sections from an article", async () => {
      const sections = await routes.findSocialEmbeds(article)
      sections.length.should.eql(1)
    })
  })

  describe("#maybeFetchSocialEmbed", () => {
    let section = {}

    it("Returns the section if not social_embed", async () => {
      section.type = "text"
      const newSection = await routes.maybeFetchSocialEmbed(section)
      newSection.type.should.eql("text")
    })

    it("Returns fetched oembed data if social_embed", async () => {
      section = {
        type: "social_embed",
        url: "https://twitter.com/artsy/status/978997552061272064",
      }

      const newSection = await routes.maybeFetchSocialEmbed(section)
      request.get.args[0][0].should.eql(
        "https://publish.twitter.com/oembed?url=https://twitter.com/artsy/status/978997552061272064"
      )
      newSection.url.should.eql('
')
      newSection.type.should.eql("social_embed")
    })

    it("Fetches from instagram", async () => {
      section = {
        type: "social_embed",
        url: "https://www.instagram.com/p/Bh-Az5_gaVB/?taken-by=artsy",
      }

      await routes.maybeFetchSocialEmbed(section)
      request.get.args[0][0].should.eql(
        "https://api.instagram.com/oembed?url=https://www.instagram.com/p/Bh-Az5_gaVB/?taken-by=artsy"
      )
    })

    it("This case is about fetching the undefined data", async () => {
      section = undefined
      const newSection = await routes.maybeFetchSocialEmbed(section)
      const result = newSection === undefined
      result.should.be.true()
    })
  })
})
<commit_msg>Silence console.error during test run<commit_after>import Articles from "../../../collections/articles.coffee"
import Backbone from "backbone"
import sinon from "sinon"
import request from "superagent"
let routes = require("rewire")("../routes.js")

describe("Routes", () => {
  let req = {}
  let res = {}
  const published_at = new Date().toISOString()
  let results = {
    articles: [
      {
        id: "1",
        published_at,
        sections: [
          {
            type: "social_embed",
            url: "https://instagram.com/image",
          },
        ],
      },
      {
        id: "2",
        published_at,
        sections: [
          {
            type: "text",
            body: "Body Text",
          },
        ],
      },
      {
        id: "3",
        published_at,
        sections: [
          {
            type: "text",
            body: "Body Text 2",
          },
        ],
      },
    ],
  }

  beforeEach(() => {
    sinon.stub(Backbone, "sync")
    request.get = sinon.stub().returns({
      end: cb => {
        cb(null, {
          body: {
            html: '
',
          },
        })
      },
    })

    routes.__set__("sd", {
      ARTSY_EDITORIAL_CHANNEL: "foo",
    })
    routes.__set__("request", request)
    routes.__set__("positronql", sinon.stub().returns(Promise.resolve(results)))

    req = {}
    res = {
      render: sinon.stub(),
      set: sinon.stub(),
    }
  })

  afterEach(() => {
    Backbone.sync.restore()
  })

  describe("#partnerUpdates", () => {
    it("renders articles", () => {
      routes.partnerUpdates(req, res)
      Backbone.sync.args[0][2].success({
        total: 16088,
        count: 2,
        results: results.articles,
      })
      res.render.args[0][0].should.containEql("partner_updates")
      res.render.args[0][1].articles.length.should.eql(3)
    })
  })

  describe("#news", () => {
    let originalConsoleError = console.error

    beforeEach(() => {
      console.error = () => {}
    })

    afterEach(() => {
      console.error = originalConsoleError
    })

    it("renders the rss feed if #findArticlesWithEmbeds rejects", async () => {
      request.get = sinon.stub().returns({
        end: cb => {
          cb(new Error())
        },
      })
      routes.__set__("request", request)
      await routes.news(req, res)
      res.render.args[0][0].should.containEql("news")
      res.render.args[0][1].articles.length.should.eql(3)
    })

    it("renders the rss feed for news without unpublished video articles", async () => {
      const articlesWithVideo = [
        ...results.articles,
        {
          id: "4",
          published_at,
          layout: "video",
          media: {
            published: false,
          },
        },
      ]

      routes.__set__(
        "positronql",
        sinon.stub().returns(
          Promise.resolve({
            articles: articlesWithVideo,
          })
        )
      )

      const findArticlesWithEmbedsStub = sinon.stub()

      routes.__set__(
        "findArticlesWithEmbeds",
        findArticlesWithEmbedsStub.returns(
          Promise.resolve(new Articles(results.articles))
        )
      )

      await routes.news(req, res)

      // This ensures that articles are filtered between the positron call and the #findArticlesWithEmbeds call
      findArticlesWithEmbedsStub.firstCall.args[0].length.should.eql(3)

      res.render.args[0][0].should.containEql("news")
      res.render.args[0][1].articles.length.should.eql(3)
    })
  })

  describe("#findSocialEmbeds", () => {
    let article = {
      sections: [{}, undefined],
    }

    it("filters out undefined sections from an article", async () => {
      const sections = await routes.findSocialEmbeds(article)
      sections.length.should.eql(1)
    })
  })

  describe("#maybeFetchSocialEmbed", () => {
    let section = {}

    it("Returns the section if not social_embed", async () => {
      section.type = "text"
      const newSection = await routes.maybeFetchSocialEmbed(section)
      newSection.type.should.eql("text")
    })

    it("Returns fetched oembed data if social_embed", async () => {
      section = {
        type: "social_embed",
        url: "https://twitter.com/artsy/status/978997552061272064",
      }

      const newSection = await routes.maybeFetchSocialEmbed(section)
      request.get.args[0][0].should.eql(
        "https://publish.twitter.com/oembed?url=https://twitter.com/artsy/status/978997552061272064"
      )
      newSection.url.should.eql('
')
      newSection.type.should.eql("social_embed")
    })

    it("Fetches from instagram", async () => {
      section = {
        type: "social_embed",
        url: "https://www.instagram.com/p/Bh-Az5_gaVB/?taken-by=artsy",
      }

      await routes.maybeFetchSocialEmbed(section)
      request.get.args[0][0].should.eql(
        "https://api.instagram.com/oembed?url=https://www.instagram.com/p/Bh-Az5_gaVB/?taken-by=artsy"
      )
    })

    it("This case is about fetching the undefined data", async () => {
      section = undefined
      const newSection = await routes.maybeFetchSocialEmbed(section)
      const result = newSection === undefined
      result.should.be.true()
    })
  })
})
<|endoftext|>


<commit_before>function setColors(reading) {
	if(reading < 150) {
		document.body.style.color="black";
		document.body.style.background = "white";
	}
	else if(reading < 325) {
		document.body.style.color="black";
		document.body.style.background = "white";
	}
	else if(reading < 440) {
		document.body.style.color="black";
		document.body.style.background = "white";
	}
	else if(reading < 570) {
		document.body.style.color="black";
		document.body.style.background = "white";
	}
	else {
		document.body.style.color="black";
		document.body.style.background = "white";
	}
}<commit_msg>removed setColors file added function to index.html<commit_after><|endoftext|>