<commit_before>/*
 * Copyright 2013 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

/**
 * Implements `document.register`
 * @module CustomElements
*/

/**
 * Polyfilled extensions to the `document` object.
 * @class Document
*/

(function() {

/**
 * Registers a custom tag name with the document.
 *
 * When a registered element is created, a `readyCallback` method is called
 * in the scope of the element. The `readyCallback` method can be specified on
 * either `inOptions.prototype` or `inOptions.lifecycle` with the latter taking
 * precedence.
 *
 * @method register
 * @param {String} inName The tag name to register. Must include a dash ('-'),
 *    for example 'x-component'.
 * @param {Object} inOptions
 *    @param {String} [inOptions.extends]
 *      (_off spec_) Tag name of an element to extend (or blank for a new
 *      element). This parameter is not part of the specification, but instead
 *      is a hint for the polyfill because the extendee is difficult to infer.
 *      Remember that the input prototype must chain to the extended element's
 *      prototype (or HTMLElement.prototype) regardless of the value of
 *      `extends`.
 *    @param {Object} inOptions.prototype The prototype to use for the new
 *      element. The prototype must inherit from HTMLElement.
 *    @param {Object} [inOptions.lifecycle]
 *      Callbacks that fire at important phases in the life of the custom
 *      element.
 *
 * @example
 *      FancyButton = document.register("fancy-button", {
 *        extends: 'button',
 *        prototype: Object.create(HTMLButtonElement.prototype, {
 *          readyCallback: {
 *            value: function() {
 *              console.log("a fancy-button was created",
 *            }
 *          }
 *        })
 *      });
 * @return {Function} Constructor for the newly registered type.
 */
function register(inName, inOptions) {
  //console.warn('document.register("' + inName + '", ', inOptions, ')');
  // construct a defintion out of options
  // TODO(sjmiles): probably should clone inOptions instead of mutating it
  var definition = inOptions || {};
  if (!inName) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Name argument must not be empty');
  }
  // record name
  definition.name = inName;
  // must have a prototype, default to an extension of HTMLElement
  // TODO(sjmiles): probably should throw if no prototype, check spec
  if (!definition.prototype) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Options missing required prototype property');
  }
  // ensure a lifecycle object so we don't have to null test it
  definition.lifecycle = definition.lifecycle || {};
  // build a list of ancestral custom elements (for native base detection)
  // TODO(sjmiles): we used to need to store this, but current code only
  // uses it in 'resolveTagName': it should probably be inlined
  definition.ancestry = ancestry(definition.extends);
  // extensions of native specializations of HTMLElement require localName
  // to remain native, and use secondary 'is' specifier for extension type
  resolveTagName(definition);
  // some platforms require modifications to the user-supplied prototype
  // chain
  resolvePrototypeChain(definition);
  // 7.1.5: Register the DEFINITION with DOCUMENT
  registerDefinition(inName, definition);
  // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
  // 7.1.8. Return the output of the previous step.
  definition.ctor = generateConstructor(definition);
  definition.ctor.prototype = definition.prototype;
  // blanket upgrade (?)
  document.upgradeElements();
  return definition.ctor;
}

function ancestry(inExtends) {
  var extendee = registry[inExtends];
  if (extendee) {
    return ancestry(extendee.extends).concat([extendee]);
  }
  return [];
}

function resolveTagName(inDefinition) {
  // if we are explicitly extending something, that thing is our
  // baseTag, unless it represents a custom component
  var baseTag = inDefinition.extends;
  // if our ancestry includes custom components, we only have a
  // baseTag if one of them does
  for (var i=0, a; (a=inDefinition.ancestry[i]); i++) {
    baseTag = a.is && a.tag;
  }
  // our tag is our baseTag, if it exists, and otherwise just our name
  inDefinition.tag = baseTag || inDefinition.name;
  if (baseTag) {
    // if there is a base tag, use secondary 'is' specifier
    inDefinition.is = inDefinition.name;
  }
}

function resolvePrototypeChain(inDefinition) {
  // TODO(sjmiles): contains ShadowDOM polyfill pollution
  // if we don't support __proto__ we need to locate the native level
  // prototype for precise mixing in
  // we also need native prototype so we can replace it with a wrapper
  // prototype if necessary
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)
        || !Object.__proto__) {
    if (inDefinition.is) {
      // for non-trivial extensions, work out both prototypes
      var inst = domCreateElement(inDefinition.tag);
      var wrapperNative = Object.getPrototypeOf(inst);
      if (window.WrapperElement) {
        inst = unwrap(inst);
      }
      var native = Object.getPrototypeOf(inst);
    } else {
      // otherwise, use the defaults
      native = HTMLElement.prototype;
      if (window.WrapperElement) {
        wrapperNative = WrapperHTMLUnknownElement.prototype;
      }
    }
  }
  // cache this in case of mixin
  inDefinition.native = native;
  // under ShadowDOM polyfill we need to use Wrapper prototype chain instead
  // of native DOM
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)) {
    if (Object.__proto__) {
      // search and replace 'native' protoype with 'wrapperNative'
      var p = inDefinition.prototype, pp;
      while (true) {
        pp = Object.getPrototypeOf(p);
        if (pp === native || pp == HTMLUnknownElement.prototype) {
          break;
        }
        p = pp;
      }
      p.__proto__ = wrapperNative;
      //console.log(native, wrapperNative, p);
    } else {
      // TODO(sjmiles): support IE
    }
  }
}

// SECTION 4

function instantiate(inDefinition) {
  // 4.a.1. Create a new object that implements PROTOTYPE
  // 4.a.2. Let ELEMENT by this new object
  //
  // the custom element instantiation algorithm must also ensure that the
  // output is a valid DOM element with the proper wrapper in place.
  //
  return upgrade(domCreateElement(inDefinition.tag), inDefinition);
}

function upgrade(inElement, inDefinition) {
  // make 'element' implement inDefinition.prototype
  implement(inElement, inDefinition);
  // some definitions specify an 'is' attribute
  if (inDefinition.is) {
    inElement.setAttribute('is', inDefinition.is);
  }
  // flag as upgraded
  inElement.__upgraded__ = true;
  // invoke lifecycle.created callbacks
  created(inElement, inDefinition);
  // OUTPUT
  return inElement;
}

function implement(inElement, inDefinition) {
  // prototype swizzling is best
  if (Object.__proto__) {
    inElement.__proto__ = inDefinition.prototype;
  }
  // TODO(sjmiles): below here is feature detected, but really it's
  // just for IE
  else {
    // where above we can re-acquire inPrototype via
    // getPrototypeOf(Element), we cannot do so when
    // we use mixin, so we install a magic reference
    if (!Object.__proto__) {
      inElement.__proto__ = inDefinition.prototype;
    }
    customMixin(inElement, inDefinition.prototype, inDefinition.native);
  }
}

if (!console.group) {
  console.group = function(m) {console.log("[group] " + m);};
  console.groupEnd = function() {console.log("[end]");};
}

function customMixin(inTarget, inSrc, inNative) {
  //console.group(inTarget.localName);
  // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
  // any property. This set should be precalculated. We also need to
  // consider this for supporting 'super'.
  var used = {};
  // start with inSrc
  var p = inSrc;
  // sometimes the default is HTMLUnknownElement.prototype instead of
  // HTMLElement.prototype, so we add a test
  // the idea is to avoid mixing in native prototypes, so adding
  // the second test is WLOG
  while (p !== inNative && p !== HTMLUnknownElement.prototype) {
    //console.group('proto');
    var keys = Object.getOwnPropertyNames(p);
    for (var i=0, k; k=keys[i]; i++) {
      if (!used[k]) {
        //console.log(k);
        Object.defineProperty(inTarget, k,
            Object.getOwnPropertyDescriptor(p, k));
        used[k] = 1;
      }
    }
    //console.groupEnd();
    p = Object.getPrototypeOf(p);
  }
  //console.groupEnd();
}

function created(inElement, inDefinition) {
  var readyCallback = inDefinition.lifecycle.readyCallback ||
      inElement.readyCallback;
  if (readyCallback) {
    readyCallback.call(inElement);
  }
}

var registry = {};
var registrySlctr = '';

function registerDefinition(inName, inDefinition) {
  registry[inName] = inDefinition;
  registrySlctr += (registrySlctr ? ',' : '');
  if (inDefinition.extends) {
    registrySlctr += inDefinition.tag + '[is=' + inDefinition.is + '],';
  }
  registrySlctr += inName;
}

function generateConstructor(inDefinition) {
  return function() {
    return instantiate(inDefinition);
  };
}

function createElement(inTag) {
  var definition = registry[inTag];
  if (definition) {
    return new definition.ctor();
  }
  return domCreateElement(inTag);
}

/**
 * Upgrade an element to a custom element. Upgrading an element
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElement
 * @param {Element} inElement The element to upgrade.
 * @return {Element} The upgraded element.
 */
function upgradeElement(inElement) {
  if (!inElement.__upgraded__) {
    var type = inElement.getAttribute('is') || inElement.localName;
    var definition = registry[type];
    return definition && upgrade(inElement, definition);
  }
}

/**
 * Upgrade all elements under `inRoot` that match selector `inSlctr`.
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElements
 * @param {Node} inRoot The root of the DOM subtree in which elements are to
 *  be upgraded.
 * @param {String} [inSlctr] An optional selector for matching specific
 * elements, otherwise all register element types are upgraded.
 */
function upgradeElements(inRoot, inSlctr) {
  var slctr = inSlctr || registrySlctr;
  if (slctr) {
    var root = inRoot || document;
    forEach(root.querySelectorAll(slctr), upgradeElement);
  }
}

// utilities

var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);

// copy all properties from inProps (et al) to inObj
function mixin(inObj/*, inProps, inMoreProps, ...*/) {
  var obj = inObj || {};
  for (var i = 1; i < arguments.length; i++) {
    var p = arguments[i];
    // TODO(sjmiles): on IE we are using mixin
    // to generate custom element instances, as we have
    // no way to alter element prototypes after creation
    // (nor a way to create an element with a custom prototype)
    // however, prototype sources (inSource) are ultimately
    // chained to a native prototype (HTMLElement or inheritor)
    // and trying to copy HTMLElement properties to itself throwss
    // in IE
    // we don't actually want to copy those properties anyway, but I
    // can't find a way to determine if a prototype is a native
    // or custom inheritor of HTMLElement
    // ad hoc solution is to simply stop at the first exception
    // an alternative exists if we have a tagName hint: then we can
    // work out where the native objects are in the prototype chain
    try {
      for (var n in p) {
        copyProperty(n, p, obj);
      }
    } catch(x) {
      //console.log(x);
    }
  }
  return obj;
}

// copy property inName from inSource object to inTarget object
function copyProperty(inName, inSource, inTarget) {
  var pd = getPropertyDescriptor(inSource, inName);
  Object.defineProperty(inTarget, inName, pd);
}

// get property descriptor for inName on inObject, even if
// inName exists on some link in inObject's prototype chain
function getPropertyDescriptor(inObject, inName) {
  if (inObject) {
    var pd = Object.getOwnPropertyDescriptor(inObject, inName);
    return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
  }
}

function watchDOM(inRoot) {
  var mo = window.MutationObserver || window.WebKitMutationObserver;
  if (mo) {
    var observer = new mo(function(mutations) {
      mutations.forEach(function(mx) {
        if (mx.type == 'childList') {
          mx.addedNodes.forEach(upgradeElement);
        }
      })
    });
    observer.observe(inRoot, {childList: true});
    return observer;
  }
}

// capture native createElement before we override it
var domCreateElement = document.createElement.bind(document);

// exports

document.register = document.webkitRegister || document.register;

if (!document.register || flags.register !== 'native') {
  document.register = register;
  document.createElement = createElement; // override
  document.upgradeElement = upgradeElement;
  document.upgradeElements = upgradeElements;
  document.watchDOM = watchDOM;
} else {
  var nop = function() {};
  document.upgradeElement = nop;
  document.upgradeElements = nop;
  document.watchDOM = nop;
}

// TODO(sjmiles): temporary, control scope better
window.mixin = mixin;

})();
<commit_msg>the "addedNodes" mutation entries are actually "nodes who have had nodes added"<commit_after>/*
 * Copyright 2013 The Toolkitchen Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style
 * license that can be found in the LICENSE file.
 */

/**
 * Implements `document.register`
 * @module CustomElements
*/

/**
 * Polyfilled extensions to the `document` object.
 * @class Document
*/

(function() {

/**
 * Registers a custom tag name with the document.
 *
 * When a registered element is created, a `readyCallback` method is called
 * in the scope of the element. The `readyCallback` method can be specified on
 * either `inOptions.prototype` or `inOptions.lifecycle` with the latter taking
 * precedence.
 *
 * @method register
 * @param {String} inName The tag name to register. Must include a dash ('-'),
 *    for example 'x-component'.
 * @param {Object} inOptions
 *    @param {String} [inOptions.extends]
 *      (_off spec_) Tag name of an element to extend (or blank for a new
 *      element). This parameter is not part of the specification, but instead
 *      is a hint for the polyfill because the extendee is difficult to infer.
 *      Remember that the input prototype must chain to the extended element's
 *      prototype (or HTMLElement.prototype) regardless of the value of
 *      `extends`.
 *    @param {Object} inOptions.prototype The prototype to use for the new
 *      element. The prototype must inherit from HTMLElement.
 *    @param {Object} [inOptions.lifecycle]
 *      Callbacks that fire at important phases in the life of the custom
 *      element.
 *
 * @example
 *      FancyButton = document.register("fancy-button", {
 *        extends: 'button',
 *        prototype: Object.create(HTMLButtonElement.prototype, {
 *          readyCallback: {
 *            value: function() {
 *              console.log("a fancy-button was created",
 *            }
 *          }
 *        })
 *      });
 * @return {Function} Constructor for the newly registered type.
 */
function register(inName, inOptions) {
  //console.warn('document.register("' + inName + '", ', inOptions, ')');
  // construct a defintion out of options
  // TODO(sjmiles): probably should clone inOptions instead of mutating it
  var definition = inOptions || {};
  if (!inName) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Name argument must not be empty');
  }
  // record name
  definition.name = inName;
  // must have a prototype, default to an extension of HTMLElement
  // TODO(sjmiles): probably should throw if no prototype, check spec
  if (!definition.prototype) {
    // TODO(sjmiles): replace with more appropriate error (Erik can probably
    // offer guidance)
    throw new Error('Options missing required prototype property');
  }
  // ensure a lifecycle object so we don't have to null test it
  definition.lifecycle = definition.lifecycle || {};
  // build a list of ancestral custom elements (for native base detection)
  // TODO(sjmiles): we used to need to store this, but current code only
  // uses it in 'resolveTagName': it should probably be inlined
  definition.ancestry = ancestry(definition.extends);
  // extensions of native specializations of HTMLElement require localName
  // to remain native, and use secondary 'is' specifier for extension type
  resolveTagName(definition);
  // some platforms require modifications to the user-supplied prototype
  // chain
  resolvePrototypeChain(definition);
  // 7.1.5: Register the DEFINITION with DOCUMENT
  registerDefinition(inName, definition);
  // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
  // 7.1.8. Return the output of the previous step.
  definition.ctor = generateConstructor(definition);
  definition.ctor.prototype = definition.prototype;
  // blanket upgrade (?)
  document.upgradeElements();
  return definition.ctor;
}

function ancestry(inExtends) {
  var extendee = registry[inExtends];
  if (extendee) {
    return ancestry(extendee.extends).concat([extendee]);
  }
  return [];
}

function resolveTagName(inDefinition) {
  // if we are explicitly extending something, that thing is our
  // baseTag, unless it represents a custom component
  var baseTag = inDefinition.extends;
  // if our ancestry includes custom components, we only have a
  // baseTag if one of them does
  for (var i=0, a; (a=inDefinition.ancestry[i]); i++) {
    baseTag = a.is && a.tag;
  }
  // our tag is our baseTag, if it exists, and otherwise just our name
  inDefinition.tag = baseTag || inDefinition.name;
  if (baseTag) {
    // if there is a base tag, use secondary 'is' specifier
    inDefinition.is = inDefinition.name;
  }
}

function resolvePrototypeChain(inDefinition) {
  // TODO(sjmiles): contains ShadowDOM polyfill pollution
  // if we don't support __proto__ we need to locate the native level
  // prototype for precise mixing in
  // we also need native prototype so we can replace it with a wrapper
  // prototype if necessary
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)
        || !Object.__proto__) {
    if (inDefinition.is) {
      // for non-trivial extensions, work out both prototypes
      var inst = domCreateElement(inDefinition.tag);
      var wrapperNative = Object.getPrototypeOf(inst);
      if (window.WrapperElement) {
        inst = unwrap(inst);
      }
      var native = Object.getPrototypeOf(inst);
    } else {
      // otherwise, use the defaults
      native = HTMLElement.prototype;
      if (window.WrapperElement) {
        wrapperNative = WrapperHTMLUnknownElement.prototype;
      }
    }
  }
  // cache this in case of mixin
  inDefinition.native = native;
  // under ShadowDOM polyfill we need to use Wrapper prototype chain instead
  // of native DOM
  if (window.WrapperElement
        && !(inDefinition.prototype instanceof WrapperElement)) {
    if (Object.__proto__) {
      // search and replace 'native' protoype with 'wrapperNative'
      var p = inDefinition.prototype, pp;
      while (true) {
        pp = Object.getPrototypeOf(p);
        if (pp === native || pp == HTMLUnknownElement.prototype) {
          break;
        }
        p = pp;
      }
      p.__proto__ = wrapperNative;
      //console.log(native, wrapperNative, p);
    } else {
      // TODO(sjmiles): support IE
    }
  }
}

// SECTION 4

function instantiate(inDefinition) {
  // 4.a.1. Create a new object that implements PROTOTYPE
  // 4.a.2. Let ELEMENT by this new object
  //
  // the custom element instantiation algorithm must also ensure that the
  // output is a valid DOM element with the proper wrapper in place.
  //
  return upgrade(domCreateElement(inDefinition.tag), inDefinition);
}

function upgrade(inElement, inDefinition) {
  // make 'element' implement inDefinition.prototype
  implement(inElement, inDefinition);
  // some definitions specify an 'is' attribute
  if (inDefinition.is) {
    inElement.setAttribute('is', inDefinition.is);
  }
  // flag as upgraded
  inElement.__upgraded__ = true;
  // invoke lifecycle.created callbacks
  created(inElement, inDefinition);
  // OUTPUT
  return inElement;
}

function implement(inElement, inDefinition) {
  // prototype swizzling is best
  if (Object.__proto__) {
    inElement.__proto__ = inDefinition.prototype;
  }
  // TODO(sjmiles): below here is feature detected, but really it's
  // just for IE
  else {
    // where above we can re-acquire inPrototype via
    // getPrototypeOf(Element), we cannot do so when
    // we use mixin, so we install a magic reference
    if (!Object.__proto__) {
      inElement.__proto__ = inDefinition.prototype;
    }
    customMixin(inElement, inDefinition.prototype, inDefinition.native);
  }
}

if (!console.group) {
  console.group = function(m) {console.log("[group] " + m);};
  console.groupEnd = function() {console.log("[end]");};
}

function customMixin(inTarget, inSrc, inNative) {
  //console.group(inTarget.localName);
  // TODO(sjmiles): 'used' allows us to only copy the 'youngest' version of
  // any property. This set should be precalculated. We also need to
  // consider this for supporting 'super'.
  var used = {};
  // start with inSrc
  var p = inSrc;
  // sometimes the default is HTMLUnknownElement.prototype instead of
  // HTMLElement.prototype, so we add a test
  // the idea is to avoid mixing in native prototypes, so adding
  // the second test is WLOG
  while (p !== inNative && p !== HTMLUnknownElement.prototype) {
    //console.group('proto');
    var keys = Object.getOwnPropertyNames(p);
    for (var i=0, k; k=keys[i]; i++) {
      if (!used[k]) {
        //console.log(k);
        Object.defineProperty(inTarget, k,
            Object.getOwnPropertyDescriptor(p, k));
        used[k] = 1;
      }
    }
    //console.groupEnd();
    p = Object.getPrototypeOf(p);
  }
  //console.groupEnd();
}

function created(inElement, inDefinition) {
  var readyCallback = inDefinition.lifecycle.readyCallback ||
      inElement.readyCallback;
  if (readyCallback) {
    readyCallback.call(inElement);
  }
}

var registry = {};
var registrySlctr = '';

function registerDefinition(inName, inDefinition) {
  registry[inName] = inDefinition;
  registrySlctr += (registrySlctr ? ',' : '');
  if (inDefinition.extends) {
    registrySlctr += inDefinition.tag + '[is=' + inDefinition.is + '],';
  }
  registrySlctr += inName;
}

function generateConstructor(inDefinition) {
  return function() {
    return instantiate(inDefinition);
  };
}

function createElement(inTag) {
  var definition = registry[inTag];
  if (definition) {
    return new definition.ctor();
  }
  return domCreateElement(inTag);
}

/**
 * Upgrade an element to a custom element. Upgrading an element
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElement
 * @param {Element} inElement The element to upgrade.
 * @return {Element} The upgraded element.
 */
function upgradeElement(inElement) {
  if (!inElement.__upgraded__) {
    var type = inElement.getAttribute('is') || inElement.localName;
    var definition = registry[type];
    return definition && upgrade(inElement, definition);
  }
}

/**
 * Upgrade all elements under `inRoot` that match selector `inSlctr`.
 * causes the custom prototype to be applied, an `is` attribute to be attached
 * (as needed), and invocation of the `readyCallback`.
 * `upgradeElement` does nothing is the element is already upgraded, or
 * if it matches no registered custom tag name.
 *
 * @method ugpradeElements
 * @param {Node} inRoot The root of the DOM subtree in which elements are to
 *  be upgraded.
 * @param {String} [inSlctr] An optional selector for matching specific
 * elements, otherwise all register element types are upgraded.
 */
function upgradeElements(inRoot, inSlctr) {
  var slctr = inSlctr || registrySlctr;
  if (slctr) {
    var root = inRoot || document;
    forEach(root.querySelectorAll(slctr), upgradeElement);
  }
}

// utilities

var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);

// copy all properties from inProps (et al) to inObj
function mixin(inObj/*, inProps, inMoreProps, ...*/) {
  var obj = inObj || {};
  for (var i = 1; i < arguments.length; i++) {
    var p = arguments[i];
    // TODO(sjmiles): on IE we are using mixin
    // to generate custom element instances, as we have
    // no way to alter element prototypes after creation
    // (nor a way to create an element with a custom prototype)
    // however, prototype sources (inSource) are ultimately
    // chained to a native prototype (HTMLElement or inheritor)
    // and trying to copy HTMLElement properties to itself throwss
    // in IE
    // we don't actually want to copy those properties anyway, but I
    // can't find a way to determine if a prototype is a native
    // or custom inheritor of HTMLElement
    // ad hoc solution is to simply stop at the first exception
    // an alternative exists if we have a tagName hint: then we can
    // work out where the native objects are in the prototype chain
    try {
      for (var n in p) {
        copyProperty(n, p, obj);
      }
    } catch(x) {
      //console.log(x);
    }
  }
  return obj;
}

// copy property inName from inSource object to inTarget object
function copyProperty(inName, inSource, inTarget) {
  var pd = getPropertyDescriptor(inSource, inName);
  Object.defineProperty(inTarget, inName, pd);
}

// get property descriptor for inName on inObject, even if
// inName exists on some link in inObject's prototype chain
function getPropertyDescriptor(inObject, inName) {
  if (inObject) {
    var pd = Object.getOwnPropertyDescriptor(inObject, inName);
    return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
  }
}

function watchDOM(inRoot) {
  var mo = window.MutationObserver || window.WebKitMutationObserver;
  if (mo) {
    var observer = new mo(function(mutations) {
      mutations.forEach(function(mx) {
        if (mx.type == 'childList') {
          forEach(mx.addedNodes, upgradeElements);
        }
      })
    });
    observer.observe(inRoot, {childList: true});
    return observer;
  }
}

// capture native createElement before we override it
var domCreateElement = document.createElement.bind(document);

// exports

document.register = document.webkitRegister || document.register;

if (!document.register || flags.register !== 'native') {
  document.register = register;
  document.createElement = createElement; // override
  document.upgradeElement = upgradeElement;
  document.upgradeElements = upgradeElements;
  document.watchDOM = watchDOM;
} else {
  var nop = function() {};
  document.upgradeElement = nop;
  document.upgradeElements = nop;
  document.watchDOM = nop;
}

// TODO(sjmiles): temporary, control scope better
window.mixin = mixin;

})();
<|endoftext|>


<commit_before>/*
 * ext-imagelib.js
 *
 * Licensed under the MIT License
 *
 * Copyright(c) 2010 Alexis Deveria
 *
 */

svgEditor.addExtension("imagelib", function() {

	var uiStrings = svgEditor.uiStrings;

	$.extend(uiStrings, {
		imagelib: {
			select_lib: 'Select an image library',
			show_list: 'Show library list',
			import_single: 'Import single',
			import_multi: 'Import multiple',
			open: 'Open as new document'
		}
	});

	var img_libs = [{
			name: 'Demo library (local)',
			url: 'extensions/imagelib/index.html',
			description: 'Demonstration library for SVG-edit on this server'
		},
		{
			name: 'IAN Symbol Libraries',
			url: 'http://ian.umces.edu/symbols/catalog/svgedit/album_chooser.php',
			description: 'Free library of illustrations'
		}
	];

	function closeBrowser() {
		$('#imgbrowse_holder').hide();
	}

	function importImage(url) {
		var newImage = svgCanvas.addSvgElementFromJson({
			"element": "image",
			"attr": {
				"x": 0,
				"y": 0,
				"width": 0,
				"height": 0,
				"id": svgCanvas.getNextId(),
				"style": "pointer-events:inherit"
			}
		});
		svgCanvas.clearSelection();
		svgCanvas.addToSelection([newImage]);
		svgCanvas.setImageURL(url);
	}

	var mode = 's';
	var multi_arr = [];
	var cur_meta;
	var tranfer_stopped = false;
	var pending = {};

	 window.addEventListener("message", function(evt) {
		// Receive postMessage data
		var response = evt.data;

		if(!response || typeof response !== "string") { // Todo: Should namespace postMessage API for this extension and filter out here
			// Do nothing
			return;
		}
        try { // This block can be removed if embedAPI moves away from a string to an object (if IE9 support not needed)
            var res = JSON.parse(response);
            if (res.namespace) { // Part of embedAPI communications
                return;
            }
        }
        catch (e) {}

		var char1 = response.charAt(0);

		var svg_str;
		var img_str;

		if(char1 != "{" && tranfer_stopped) {
			tranfer_stopped = false;
			return;
		}

		if(char1 == '|') {
			var secondpos = response.indexOf('|', 1);
			var id = response.substr(1, secondpos-1);
			response = response.substr(secondpos+1);
			char1 = response.charAt(0);

		}

		// Hide possible transfer dialog box
		$('#dialog_box').hide();

		switch (char1) {
			case '{':
				// Metadata
				tranfer_stopped = false;
				var cur_meta = JSON.parse(response);

				pending[cur_meta.id] = cur_meta;

				var name = (cur_meta.name || 'file');

				var message = uiStrings.notification.retrieving.replace('%s', name);

				if(mode != 'm') {
					$.process_cancel(message, function() {
						tranfer_stopped = true;
						// Should a message be sent back to the frame?

						$('#dialog_box').hide();
					});
				} else {
					var entry = $('
' + message + '
').data('id', cur_meta.id);
					preview.append(entry);
					cur_meta.entry = entry;
				}

				return;
			case '<':
				svg_str = true;
				break;
			case 'd':
				if(response.indexOf('data:image/svg+xml') === 0) {
					var pre = 'data:image/svg+xml;base64,';
					var src = response.substring(pre.length);
					response = svgedit.utilities.decode64(src);
					svg_str = true;
					break;
				} else if(response.indexOf('data:image/') === 0) {
					img_str = true;
					break;
				}
				// Else fall through
			default:
				// TODO: See if there's a way to base64 encode the binary data stream
// 				var str = 'data:;base64,' + svgedit.utilities.encode64(response, true);

				// Assume it's raw image data
// 				importImage(str);

				// Don't give warning as postMessage may have been used by something else
				if(mode !== 'm') {
					closeBrowser();
				} else {
					pending[id].entry.remove();
				}
// 				$.alert('Unexpected data was returned: ' + response, function() {
// 					if(mode !== 'm') {
// 						closeBrowser();
// 					} else {
// 						pending[id].entry.remove();
// 					}
// 				});
				return;
		}

		switch (mode) {
			case 's':
				// Import one
				if(svg_str) {
					svgCanvas.importSvgString(response);
				} else if(img_str) {
					importImage(response);
				}
				closeBrowser();
				break;
			case 'm':
				// Import multiple
				multi_arr.push([(svg_str ? 'svg' : 'img'), response]);
				var cur_meta = pending[id];
				if(svg_str) {
					if(cur_meta && cur_meta.name) {
						var title = cur_meta.name;
					}  else {
						// Try to find a title
						var xml = new DOMParser().parseFromString(response, 'text/xml').documentElement;
						var title = $(xml).children('title').first().text() || '(SVG #' + response.length + ')';
					}
					if(cur_meta) {
						preview.children().each(function() {
							if($(this).data('id') == id) {
								if(cur_meta.preview_url) {
									$(this).html('' + title);
								} else {
									$(this).text(title);
								}
								submit.removeAttr('disabled');
							}
						});
					} else {
						preview.append('
'+title+'
');
						submit.removeAttr('disabled');
					}
				} else {
					if(cur_meta && cur_meta.preview_url) {
						var title = cur_meta.name || '';
					}
					if(cur_meta && cur_meta.preview_url) {
						var entry = '' + title;
					} else {
						var entry = '';
					}

					if(cur_meta) {
						preview.children().each(function() {
							if($(this).data('id') == id) {
								$(this).html(entry);
								submit.removeAttr('disabled');
							}
						});
					} else {
						preview.append($('
').append(entry));
						submit.removeAttr('disabled');
					}

				}
				break;
			case 'o':
				// Open
				if(!svg_str) break;
				svgEditor.openPrep(function(ok) {
					if(!ok) return;
					svgCanvas.clear();
					svgCanvas.setSvgString(response);
					// updateCanvas();
				});
				closeBrowser();
				break;
		}
	}, true);

	var preview, submit;

	function toggleMulti(show) {

		$('#lib_framewrap, #imglib_opts').css({right: (show ? 200 : 10)});
		if(!preview) {
			preview = $('
').css({
				position: 'absolute',
				top: 45,
				right: 10,
				width: 180,
				bottom: 45,
				background: '#fff',
				overflow: 'auto'
			}).insertAfter('#lib_framewrap');

			submit = $('<button disabled>Import selected</button>')
				.appendTo('#imgbrowse')
				.on("click touchend", function() {
				$.each(multi_arr, function(i) {
					var type = this[0];
					var data = this[1];
					if(type == 'svg') {
						svgCanvas.importSvgString(data);
					} else {
						importImage(data);
					}
					svgCanvas.moveSelectedElements(i*20, i*20, false);
				});
				preview.empty();
				multi_arr = [];
				$('#imgbrowse_holder').hide();
			}).css({
				position: 'absolute',
				bottom: 10,
				right: -10
			});

		}

		preview.toggle(show);
		submit.toggle(show);
	}

	function showBrowser() {

		var browser = $('#imgbrowse');
		if(!browser.length) {
			$('

\

').insertAfter('#svg_docprops');
			browser = $('#imgbrowse');

			var all_libs = uiStrings.imagelib.select_lib;

			var lib_opts = $('
').appendTo(browser);
			var frame = $('').prependTo(browser).hide().wrap('
');

			var header = $('
').prependTo(browser).text(all_libs).css({
				position: 'absolute',
				top: 0,
				left: 0,
				width: '100%'
			});

			var cancel = $('<button>' + uiStrings.common.cancel + '</button>')
				.appendTo(browser)
				.on("click touchend", function() {
				$('#imgbrowse_holder').hide();
			}).css({
				position: 'absolute',
				top: 5,
				right: -10
			});

			var leftBlock = $('').css({position:'absolute',top:5,left:10}).appendTo(browser);

			var back = $('<button hidden>' + uiStrings.imagelib.show_list + '</button>')
				.appendTo(leftBlock)
				.on("click touchend", function() {
				frame.attr('src', 'about:blank').hide();
				lib_opts.show();
				header.text(all_libs);
				back.hide();
			}).css({
				'margin-right': 5
			}).hide();

			var type = $('<select><option value=s>' +
			uiStrings.imagelib.import_single + '</option><option value=m>' +
			uiStrings.imagelib.import_multi + '</option><option value=o>' +
			uiStrings.imagelib.open + '</option></select>').appendTo(leftBlock).change(function() {
				mode = $(this).val();
				switch (mode) {
					case 's':
					case 'o':
						toggleMulti(false);
						break;

					case 'm':
						// Import multiple
						toggleMulti(true);
				}
			}).css({
				'margin-top': 10
			});

			cancel.prepend($.getSvgIcon('cancel', true));
			back.prepend($.getSvgIcon('tool_imagelib', true));

			$.each(img_libs, function(i, opts) {
				$('
')
					.appendTo(lib_opts)
					.text(opts.name)
					.on("click touchend", function() {
					frame.attr('src', opts.url).show();
					header.text(opts.name);
					lib_opts.hide();
					back.show();
				}).append('' + opts.description + '');
			});

		} else {
			$('#imgbrowse_holder').show();
		}
	}

	return {
		svgicons: "extensions/ext-imagelib.xml",
		buttons: [{
			id: "tool_imagelib",
			type: "app_menu", // _flyout
			position: 4,
			title: "Image library",
			events: {
				"mouseup": showBrowser
			}
		}],
		callback: function() {

			$('


<commit_before>import React, {Component} from "react";
import Controls from "./Controls";
import Grid from "./Grid";
import {
    getColor,
    getNeighborsCoord,
    getCellCoordinates,
    getCenterCoordinates,
    getCellCoordinatesToDraw
        } from "../utils/helpers";
import PropTypes from "prop-types";

class App extends Component {
    state = {
        gridWidth: "501px",
        gridHeight: "381px"
    }

    static propTypes = {
        gridWidth: PropTypes.string,
        gridHeight: PropTypes.string,
        onUpdate: PropTypes.func,
        handleReset: PropTypes.func,
        handleStart: PropTypes.func
    }

    //length, height string - canvas length and height in px;
    handleUpdateCanvas = (length, height) => {
        var canvas = document.getElementById("game-grid");
        var context = canvas.getContext("2d");

        for (let x = 0.5; x < length; x += 10) {
            context.moveTo(x, 0);
            context.lineTo(x, height);
        }

        for (let y = 0.5; y < height; y += 10) {
            context.moveTo(0, y);
            context.lineTo(length, y);
        }

        context.strokeStyle = "#ddd";
        context.stroke();
    }

    handleStart = () => {
        console.log( getCenterCoordinates() );
    }

    handleReset = () => {
        let canvas = document.getElementById("game-grid");
        let ctx = canvas.getContext("2d");

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        this.handleUpdateCanvas(this.state.gridWidth, this.state.gridHeight);
    }

    render() {
        let width= this.state.gridWidth;
        let height = this.state.gridHeight;

        return(

                <Controls handleReset={this.handleReset}
                          handleStart={this.handleStart}/>
                <Grid gridWidth={width}
                      gridHeight={height}
                      onUpdate={this.handleUpdateCanvas}/>

        );
    }
}

export default App;
<commit_msg>Implement game start<commit_after>import React, {Component} from "react";
import Controls from "./Controls";
import Grid from "./Grid";
import {
    getColor,
    getNeighborsCoord,
    getCellCoordinates,
    getCenterCoordinates,
    getCellCoordinatesToDraw
        } from "../utils/helpers";
import PropTypes from "prop-types";

class App extends Component {
    state = {
        gridWidth: "501px",
        gridHeight: "381px"
    }

    static propTypes = {
        gridWidth: PropTypes.string,
        gridHeight: PropTypes.string,
        onUpdate: PropTypes.func,
        handleReset: PropTypes.func,
        handleStart: PropTypes.func
    }

    //length, height string - canvas length and height in px;
    handleUpdateCanvas = (length, height) => {
        var canvas = document.getElementById("game-grid");
        var context = canvas.getContext("2d");

        for (let x = 0.5; x < length; x += 10) {
            context.moveTo(x, 0);
            context.lineTo(x, height);
        }

        for (let y = 0.5; y < height; y += 10) {
            context.moveTo(0, y);
            context.lineTo(length, y);
        }

        context.strokeStyle = "#ddd";
        context.stroke();
    }

    handleStart = () => {

        let centerCoordinates = getCenterCoordinates();
        let canvas = document.getElementById("game-grid");
        let ctx = canvas.getContext("2d");

        for (let coordinates of centerCoordinates) {
            console.log(getColor(ctx, coordinates) );
        }
    }

    handleReset = () => {
        let canvas = document.getElementById("game-grid");
        let ctx = canvas.getContext("2d");

        ctx.clearRect(0, 0, canvas.width, canvas.height);
        this.handleUpdateCanvas(this.state.gridWidth, this.state.gridHeight);
    }

    render() {
        let width= this.state.gridWidth;
        let height = this.state.gridHeight;

        return(

                <Controls handleReset={this.handleReset}
                          handleStart={this.handleStart}/>
                <Grid gridWidth={width}
                      gridHeight={height}
                      onUpdate={this.handleUpdateCanvas}/>

        );
    }
}

export default App;
<|endoftext|>


<commit_before>import React from 'react';

import PlayerSearchBox from './PlayerSearchBox.js'
import ProfileCard from './ProfileCard.js'
import StatCard from './StatCard.js'
import SimilarPlayersCard from './SimilarPlayersCard.js'
import LeagueComparisonCard from './LeagueComparisonCard.js'
import lastSeasonData from '../2016-2017-data.js'

/* Import Style */
import '../style/components/App.css';

/* Import images */
import curryImg from '../img/curry.png';
import irvingImg from '../img/irving.png';
import wallImg from '../img/wall.png';
import westbrookImg from '../img/westbrook.png';

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      'nbaData': lastSeasonData.playerstatsentry,
      'currentPlayer': {
        'playerName': 'Giannis Antetokounmpo',
        'img': irvingImg,
        'team': 'Minnesota Timberwolves',
        'position': 'Point Guard',
        'ppg': 21.0,
        'apg': 8.4,
        'rpg': 2.1,
      },
      'similarPlayersList': [
        {
          'playerName': 'Kentavious Caldwell-Pope',
          'img': curryImg,
          'ppg': 22.3,
          'apg': 6.6,
          'rpg': 2.1
        },
        {
          'playerName': 'John Wall',
          'img': wallImg,
          'ppg': 20.3,
          'apg': 6.6,
          'rpg': 5.1
        },
        {
          'playerName': 'Russell Westbrook',
          'img': westbrookImg,
          'ppg': 32.0,
          'apg': 10.0,
          'rpg': 11.8
        }
      ],
      'leagueStats': {
        'ppg': {
          'leagueLeaderName': 'Russell Westbrook',
          'leagueLeaderValue': 32,
          'leagueAverageValue': 5.4
        },
        'apg': {
          'leagueLeaderName': 'James Harden',
          'leagueLeaderValue': 12.8,
          'leagueAverageValue': 2.4
        },
        'rpg': {
          'leagueLeaderName': 'Hassan Whiteside',
          'leagueLeaderValue': 15,
          'leagueAverageValue': 6.4
        }
      }
    }
  }

  /* Fetch data & trigger rest of the app */
  componentDidMount() {
    const nbaDataUrl = 'https://api.mysportsfeeds.com/v1.1/pull/nba/'
                       + '2016-2017-regular/cumulative_player_stats.json?'
                       + 'playerstats=PTS/G,AST/G,REB/G';
    fetch(nbaDataUrl, {
       method: 'get',
       headers: {
         'Authorization': 'Basic '+btoa('iNaesu:mysportsfeeds'),
         'Content-Type': 'application/x-www-form-urlencoded'
       }
    })
    .then(response => {
      return response.json();
    })
    .then(nbaData => {
      this.setState({
        'nbaData': nbaData.cumulativeplayerstats.playerstatsentry
      });
    })
    .then(() => {
      const data = this.state.nbaData;

      const pointsLeader = getLeagueLeader(data, 'PtsPerGame');
      const assistsLeader = getLeagueLeader(data, 'AstPerGame');
      const reboundsLeader = getLeagueLeader(data, 'RebPerGame');

      this.setState({
        'leagueStats': {
          'ppg': {
            'leagueLeaderName': pointsLeader.name,
            'leagueLeaderValue': pointsLeader.value,
            'leagueAverageValue': getLeagueAverage(data, 'PtsPerGame')
          },
          'apg': {
            'leagueLeaderName': assistsLeader.name,
            'leagueLeaderValue': assistsLeader.value,
            'leagueAverageValue': getLeagueAverage(data,'AstPerGame')
          },
          'rpg': {
            'leagueLeaderName': reboundsLeader.name,
            'leagueLeaderValue': reboundsLeader.value,
            'leagueAverageValue': getLeagueAverage(data,'RebPerGame')
          }
        }
      });
    });
  }

  render() {
    return (

NBA Player Dashboard

            <PlayerSearchBox placeholderText='Player Search' />

              <ProfileCard
                playerName={this.state.currentPlayer.playerName}
                img={this.state.currentPlayer.img}
                team={this.state.currentPlayer.team}
                position={this.state.currentPlayer.position}
              />

                <StatCard
                  id='ppg-card'
                  statName={['Points', 'Per Game']}
                  value={this.state.currentPlayer.ppg}
                  leagueLeaderValue={
                    this.state.leagueStats.ppg.leagueLeaderValue
                  }
                />
                <StatCard
                  id='apg-card'
                  statName={['Assists', 'Per Game']}
                  value={this.state.currentPlayer.apg}
                  leagueLeaderValue={
                    this.state.leagueStats.apg.leagueLeaderValue
                  }
                />
                <StatCard
                  id='rpg-card'
                  statName={['Rebounds', 'Per Game']}
                  value={this.state.currentPlayer.rpg}
                  leagueLeaderValue={
                    this.state.leagueStats.rpg.leagueLeaderValue
                  }
                />

            <SimilarPlayersCard
              similarPlayer0={this.state.similarPlayersList[0].playerName}
              img0={this.state.similarPlayersList[0].img}
              ppg0={this.state.similarPlayersList[0].ppg}
              apg0={this.state.similarPlayersList[0].apg}
              rpg0={this.state.similarPlayersList[0].rpg}

              similarPlayer1={this.state.similarPlayersList[1].playerName}
              img1={this.state.similarPlayersList[1].img}
              ppg1={this.state.similarPlayersList[1].ppg}
              apg1={this.state.similarPlayersList[1].apg}
              rpg1={this.state.similarPlayersList[1].rpg}

              similarPlayer2={this.state.similarPlayersList[2].playerName}
              img2={this.state.similarPlayersList[2].img}
              ppg2={this.state.similarPlayersList[2].ppg}
              apg2={this.state.similarPlayersList[2].apg}
              rpg2={this.state.similarPlayersList[2].rpg}
            />

            <LeagueComparisonCard
              playerName={this.state.currentPlayer.playerName}

              ppgLeaderName={this.state.leagueStats.ppg.leagueLeaderName}
              leaderPpg={this.state.leagueStats.ppg.leagueLeaderValue}
              playerPpg={this.state.currentPlayer.ppg}
              averagePpg={this.state.leagueStats.ppg.leagueAverageValue}

              apgLeaderName={this.state.leagueStats.apg.leagueLeaderName}
              leaderApg={this.state.leagueStats.apg.leagueLeaderValue}
              playerApg={this.state.currentPlayer.apg}
              averageApg={this.state.leagueStats.apg.leagueAverageValue}

              rpgLeaderName={this.state.leagueStats.rpg.leagueLeaderName}
              leaderRpg={this.state.leagueStats.rpg.leagueLeaderValue}
              playerRpg={this.state.currentPlayer.rpg}
              averageRpg={this.state.leagueStats.rpg.leagueAverageValue}
            />

    );
  }
}

function errorMessage(message) {
  return 'Error: ' + message;
}

/**
 * Get the player name and stat value of the league leader of a given stat.
 * @param {Object} nbaData - Data fetched from sports API
 * @param {string} statDesc - Description of the stat (must match sports API)
 */
function getLeagueLeader(nbaData, statDesc) {
  if (!Array.isArray(nbaData)) {
    console.log(errorMessage('nbaData is not an array'));
  }
  if (typeof(statDesc) !== 'string') {
    console.log(errorMessage('statDesc is not a string'));
  }

  let leagueLeader = {
    'name': '',
    'value': 0
  };

  nbaData.forEach((datum, idx) => {
    if (!datum.hasOwnProperty('player')) {
      console.log(errorMessage('nbaData[' + idx + '] | No player property'));
    }
    if (!datum.hasOwnProperty('stats')) {
      console.log(errorMessage('nbaData[' + idx + '] | No stats property'));
    }

    const value = parseFloat(datum.stats[statDesc]['#text']);
    if (value > leagueLeader.value) {
      leagueLeader.value = value;
      leagueLeader.name = datum.player.FirstName + ' ' + datum.player.LastName;
    }
  });

  return leagueLeader;
}

/**
 * Get the leavue average value of a given stat.
 * @param {Object} nbaData - Data fetched from sports API
 * @param {string} statDesc - Description of the stat (must match sports API)
 */
function getLeagueAverage(nbaData, statDesc) {
  return 6.8;
}
<commit_msg>WIP - Calculate league averages (need to verify and clean up function)<commit_after>import React from 'react';

import PlayerSearchBox from './PlayerSearchBox.js'
import ProfileCard from './ProfileCard.js'
import StatCard from './StatCard.js'
import SimilarPlayersCard from './SimilarPlayersCard.js'
import LeagueComparisonCard from './LeagueComparisonCard.js'
import lastSeasonData from '../2016-2017-data.js'

/* Import Style */
import '../style/components/App.css';

/* Import images */
import curryImg from '../img/curry.png';
import irvingImg from '../img/irving.png';
import wallImg from '../img/wall.png';
import westbrookImg from '../img/westbrook.png';

export default class App extends React.Component {
  constructor() {
    super();
    this.state = {
      'nbaData': lastSeasonData.playerstatsentry,
      'currentPlayer': {
        'playerName': 'Giannis Antetokounmpo',
        'img': irvingImg,
        'team': 'Minnesota Timberwolves',
        'position': 'Point Guard',
        'ppg': 21.0,
        'apg': 8.4,
        'rpg': 2.1,
      },
      'similarPlayersList': [
        {
          'playerName': 'Kentavious Caldwell-Pope',
          'img': curryImg,
          'ppg': 22.3,
          'apg': 6.6,
          'rpg': 2.1
        },
        {
          'playerName': 'John Wall',
          'img': wallImg,
          'ppg': 20.3,
          'apg': 6.6,
          'rpg': 5.1
        },
        {
          'playerName': 'Russell Westbrook',
          'img': westbrookImg,
          'ppg': 32.0,
          'apg': 10.0,
          'rpg': 11.8
        }
      ],
      'leagueStats': {
        'ppg': {
          'leagueLeaderName': 'Russell Westbrook',
          'leagueLeaderValue': 32,
          'leagueAverageValue': 5.4
        },
        'apg': {
          'leagueLeaderName': 'James Harden',
          'leagueLeaderValue': 12.8,
          'leagueAverageValue': 2.4
        },
        'rpg': {
          'leagueLeaderName': 'Hassan Whiteside',
          'leagueLeaderValue': 15,
          'leagueAverageValue': 6.4
        }
      }
    }
  }

  /* Fetch data & trigger rest of the app */
  componentDidMount() {
    const nbaDataUrl = 'https://api.mysportsfeeds.com/v1.1/pull/nba/'
                       + '2016-2017-regular/cumulative_player_stats.json?'
                       + 'playerstats=PTS/G,AST/G,REB/G';
    fetch(nbaDataUrl, {
       method: 'get',
       headers: {
         'Authorization': 'Basic '+btoa('iNaesu:mysportsfeeds'),
         'Content-Type': 'application/x-www-form-urlencoded'
       }
    })
    .then(response => {
      return response.json();
    })
    .then(nbaData => {
      this.setState({
        'nbaData': nbaData.cumulativeplayerstats.playerstatsentry
      });
    })
    .then(() => {
      const data = this.state.nbaData;

      const pointsLeader = getLeagueLeader(data, 'PtsPerGame');
      const assistsLeader = getLeagueLeader(data, 'AstPerGame');
      const reboundsLeader = getLeagueLeader(data, 'RebPerGame');

      this.setState({
        'leagueStats': {
          'ppg': {
            'leagueLeaderName': pointsLeader.name,
            'leagueLeaderValue': pointsLeader.value,
            'leagueAverageValue': getLeagueAverage(data, 'PtsPerGame')
          },
          'apg': {
            'leagueLeaderName': assistsLeader.name,
            'leagueLeaderValue': assistsLeader.value,
            'leagueAverageValue': getLeagueAverage(data,'AstPerGame')
          },
          'rpg': {
            'leagueLeaderName': reboundsLeader.name,
            'leagueLeaderValue': reboundsLeader.value,
            'leagueAverageValue': getLeagueAverage(data,'RebPerGame')
          }
        }
      });
    });
  }

  render() {
    return (

NBA Player Dashboard

            <PlayerSearchBox placeholderText='Player Search' />

              <ProfileCard
                playerName={this.state.currentPlayer.playerName}
                img={this.state.currentPlayer.img}
                team={this.state.currentPlayer.team}
                position={this.state.currentPlayer.position}
              />

                <StatCard
                  id='ppg-card'
                  statName={['Points', 'Per Game']}
                  value={this.state.currentPlayer.ppg}
                  leagueLeaderValue={
                    this.state.leagueStats.ppg.leagueLeaderValue
                  }
                />
                <StatCard
                  id='apg-card'
                  statName={['Assists', 'Per Game']}
                  value={this.state.currentPlayer.apg}
                  leagueLeaderValue={
                    this.state.leagueStats.apg.leagueLeaderValue
                  }
                />
                <StatCard
                  id='rpg-card'
                  statName={['Rebounds', 'Per Game']}
                  value={this.state.currentPlayer.rpg}
                  leagueLeaderValue={
                    this.state.leagueStats.rpg.leagueLeaderValue
                  }
                />

            <SimilarPlayersCard
              similarPlayer0={this.state.similarPlayersList[0].playerName}
              img0={this.state.similarPlayersList[0].img}
              ppg0={this.state.similarPlayersList[0].ppg}
              apg0={this.state.similarPlayersList[0].apg}
              rpg0={this.state.similarPlayersList[0].rpg}

              similarPlayer1={this.state.similarPlayersList[1].playerName}
              img1={this.state.similarPlayersList[1].img}
              ppg1={this.state.similarPlayersList[1].ppg}
              apg1={this.state.similarPlayersList[1].apg}
              rpg1={this.state.similarPlayersList[1].rpg}

              similarPlayer2={this.state.similarPlayersList[2].playerName}
              img2={this.state.similarPlayersList[2].img}
              ppg2={this.state.similarPlayersList[2].ppg}
              apg2={this.state.similarPlayersList[2].apg}
              rpg2={this.state.similarPlayersList[2].rpg}
            />

            <LeagueComparisonCard
              playerName={this.state.currentPlayer.playerName}

              ppgLeaderName={this.state.leagueStats.ppg.leagueLeaderName}
              leaderPpg={this.state.leagueStats.ppg.leagueLeaderValue}
              playerPpg={this.state.currentPlayer.ppg}
              averagePpg={this.state.leagueStats.ppg.leagueAverageValue}

              apgLeaderName={this.state.leagueStats.apg.leagueLeaderName}
              leaderApg={this.state.leagueStats.apg.leagueLeaderValue}
              playerApg={this.state.currentPlayer.apg}
              averageApg={this.state.leagueStats.apg.leagueAverageValue}

              rpgLeaderName={this.state.leagueStats.rpg.leagueLeaderName}
              leaderRpg={this.state.leagueStats.rpg.leagueLeaderValue}
              playerRpg={this.state.currentPlayer.rpg}
              averageRpg={this.state.leagueStats.rpg.leagueAverageValue}
            />

    );
  }
}

function errorMessage(message) {
  return 'Error: ' + message;
}

/**
 * Get the player name and stat value of the league leader of a given stat.
 * @param {Object} nbaData - Data fetched from sports API
 * @param {string} statDesc - Description of the stat (must match sports API)
 */
function getLeagueLeader(nbaData, statDesc) {
  if (!Array.isArray(nbaData)) {
    console.log(errorMessage('nbaData is not an array'));
  }
  if (typeof(statDesc) !== 'string') {
    console.log(errorMessage('statDesc is not a string'));
  }

  let leagueLeader = {
    'name': '',
    'value': 0
  };

  nbaData.forEach((datum, idx) => {
    const value = parseFloat(datum.stats[statDesc]['#text']);
    if (value > leagueLeader.value) {
      leagueLeader.value = value;
      leagueLeader.name = datum.player.FirstName + ' ' + datum.player.LastName;
    }
  });

  return leagueLeader;
}

/**
 * Get the leavue average value of a given stat.
 * @param {Object} nbaData - Data fetched from sports API
 * @param {string} statDesc - Description of the stat (must match sports API)
 */
function getLeagueAverage(nbaData, statDesc) {
  if (!Array.isArray(nbaData)) {
    console.log(errorMessage('nbaData is not an array'));
  }
  if (typeof(statDesc) !== 'string') {
    console.log(errorMessage('statDesc is not a string'));
  }

  const total = nbaData.reduce((currentTotal, datum) => {
    return (currentTotal + parseFloat(datum.stats[statDesc]['#text']));
  }, 0);

  return total / nbaData.length;
}
<|endoftext|>


<commit_before>/**
 * @module src/DownloadTokens
 *
 * Component that display SourceDocument
 * Accepts the following properties:
 *  - language: Ethnologue code of the language
 *  - version: Version of the language
 *  - revision: Autogenerated for each updation of this same source
*/

import React, { Component } from 'react';
import './App.css';
import Header from './Header';
import Footer from './Footer';
import { FormControl } from 'react-bootstrap';
import SourceLanguages from './SourceLanguages';
import $ from 'jquery';
import GlobalURL from './GlobalURL';
import TargetLanguages from './TargetLanguages';
import Checkbox from './Checkbox';

const booksName1 = [
  'GEN', 'EXO', 'LEV', 'NUM', 'DEU', 'JOS', 'JDG', 'RUT', '1SA', '2SA', '1KI', '2KI', '1CH', '2CH', 'EZR', 'NEH', 'EST', 'JOB',
  'PSA', 'PRO', 'ECC', 'SNG', 'ISA', 'JER', 'LAM', 'EZK', 'DAN', 'HOS', 'JOL', 'AMO', 'OBA', 'JON', 'MIC', 'NAM', 'HAB', 'ZEP',
  'HAG', 'ZEC', 'MAL', 'MAT', 'MRK', 'LUK', 'JHN', 'ACT', 'ROM', '1CO', '2CO', 'GAL', 'EPH', 'PHP', 'COL', '1TH', '2TH', '1TI',
  '2TI', 'TIT', 'PHM', 'HEB', 'JAS', '1PE', '2PE', '1JN', '2JN', '3JN', 'JUD', 'REV'
];

const booksName2 = [
  'GEN', 'EXO', 'LEV', 'NUM', 'DEU', 'JOS', 'JDG', 'RUT', '1SA', '2SA', '1KI', '2KI', '1CH', '2CH', 'EZR', 'NEH', 'EST', 'JOB',
  'PSA', 'PRO', 'ECC', 'SNG', 'ISA', 'JER', 'LAM', 'EZK', 'DAN', 'HOS', 'JOL', 'AMO', 'OBA', 'JON', 'MIC', 'NAM', 'HAB', 'ZEP',
  'HAG', 'ZEC', 'MAL', 'MAT', 'MRK', 'LUK', 'JHN', 'ACT', 'ROM', '1CO', '2CO', 'GAL', 'EPH', 'PHP', 'COL', '1TH', '2TH', '1TI',
  '2TI', 'TIT', 'PHM', 'HEB', 'JAS', '1PE', '2PE', '1JN', '2JN', '3JN', 'JUD', 'REV'
];

class DownloadTokens extends Component {
  constructor(props) {
    super(props);

    this.state = {
      sourcelang:'tam',
      version: '',
      revision: '',
      targetlang:'mal',
      books: [],
      nbooks: [],
      uploaded:'uploadingStatus',
      message: ''
    }
      // Upload file specific callback handlers
      this.onSelect = this.onSelect.bind(this);
      this.downloadTokenWords = this.downloadTokenWords.bind(this);
      this.parseJSONToXLS = this.parseJSONToXLS.bind(this);
  }

  componentWillMount = () => {
    this.selectedCheckboxes1 = new Set();
    this.selectedCheckboxes2 = new Set();
  }

  toggleCheckbox1 = label => {
    if (this.selectedCheckboxes1.has(label)) {
      this.selectedCheckboxes1.delete(label);
    } else {
      this.selectedCheckboxes1.add(label);
    }
  }

  toggleCheckbox2 = label => {
    if (this.selectedCheckboxes2.has(label)) {
      this.selectedCheckboxes2.delete(label);
    } else {
      this.selectedCheckboxes2.add(label);
    }
  }

  createCheckbox1 = label => (
    <Checkbox
            label={label}
            handleCheckboxChange={this.toggleCheckbox1}
            key={label}
    />
  )

  createCheckbox2 = label => (
    <Checkbox
            label={label}
            handleCheckboxChange={this.toggleCheckbox2}
            key={label}
    />
  )

  createCheckboxes1 = () => (
    booksName1.map(this.createCheckbox1)
  )

  createCheckboxes2 = () => (
    booksName2.map(this.createCheckbox2)
  )

  onSelect(e) {
    this.setState({
      [e.target.name]: e.target.value });
  }

// For Downloads Token words
  downloadTokenWords(e){
    e.preventDefault();
    global.books = [];
    global.nbooks= [];
    for (const books of this.selectedCheckboxes1) {
      global.books = Array.from(this.selectedCheckboxes1);
    }
    for (const nbooks of this.selectedCheckboxes2) {
      global.nbooks = Array.from(this.selectedCheckboxes2);
    }

    var _this = this
    var data = {
        "sourcelang": this.state.sourcelang, "version": this.state.version, "revision": this.state.revision , "targetlang": this.state.targetlang, "nbooks":global.nbooks, "books": global.books
    }
    let accessToken = JSON.parse(window.localStorage.getItem('access_token'))
    $.ajax({
      url: GlobalURL["hostURL"]+"/v1/getbookwiseautotokens",
      contentType: "application/json; charset=utf-8",
      data : JSON.stringify(data),
      method : "POST",
      headers: {
                "Authorization": "bearer " + JSON.stringify(accessToken['access_token']).slice(1,-1),
      },
       beforeSend: function () {
          $(".modal").show();
      },
      complete: function () {
          $(".modal").hide();
      },
      success: function (result) {
        result = JSON.parse(result)
        if (result.success !== false){
         _this.parseJSONToXLS(result);
        }
        else {
          _this.setState({message: result.message, uploaded: 'failure'})
        }
      },
      error: function (error) {
       _this.setState({message: error.message, uploaded: 'failure'})
      }
    });
  }

  // for parse JSON to XLS
  parseJSONToXLS(jsonData) {
  var array = [];
  var str = '';
  array = typeof jsonData !== 'object' ? JSON.parse(jsonData) : jsonData;
      for (var i = 0; i < array.length; i++) {
        str += array[i] + '\n';
      }
      str += '\r\n';
      var a = document.createElement('a');
      var blob = new Blob([ new Uint8Array([0xEF, 0xBB, 0xBF]), str], {'type':'application/vnd.ms-excel;charset=utf-8'});
      a.href = window.URL.createObjectURL(blob);
      a.download = this.state.sourcelang + this.state.version + 'Tokens.xls';
      a.click();
  }

  render() {
    return(

          <form className="col-md-12 uploader" encType="multipart/form-data">

Download Tokens

                {this.state.message}

                {this.state.message}

                <lable className="control-label Concord2">  Source Language  </lable>
                    <FormControl value={this.state.sourcelang} onChange={this.onSelect} name="sourcelang" componentClass="select" placeholder="select">
                      {SourceLanguages.map((sourcelang, i) => <option  key={i} value={sourcelang.code}>{sourcelang.value}</option>)}
                    </FormControl>
                 <lable className="control-lable Concord2">  Version  </lable>
                    <input value={this.state.version} onChange={this.onSelect} name="version" type="text"  placeholder="version" className="form-control"/>
                <lable className="control-lable Concord2">  Revision  </lable>
                    <input value={this.state.revision} onChange={this.onSelect} name="revision" type="text" placeholder="revision" className="form-control"/>
                <lable className="control-label Concord2">  Target Language  </lable>
                    <FormControl value={this.state.targetlang} onChange={this.onSelect} name="targetlang" componentClass="select" placeholder="select">
                      {TargetLanguages.map((targetlang, i) => <option  key={i} value={targetlang.code}>{targetlang.value}</option>)}
                    </FormControl>

                      Include Books
                      <form className="myCheck1" >
                        {this.createCheckboxes1()}
                      </form>

                      Exclude Books
                      <form className="myCheck2">
                        {this.createCheckboxes2()}
                      </form>

                  <button id="btnGet" type="button" className="btn btn-success ConcordButton" onClick={this.downloadTokenWords}> Download Token & Concordances</button>

            </form>

      );
    }
}

export default DownloadTokens;<commit_msg>Button name changes done<commit_after>/**
 * @module src/DownloadTokens
 *
 * Component that display SourceDocument
 * Accepts the following properties:
 *  - language: Ethnologue code of the language
 *  - version: Version of the language
 *  - revision: Autogenerated for each updation of this same source
*/

import React, { Component } from 'react';
import './App.css';
import Header from './Header';
import Footer from './Footer';
import { FormControl } from 'react-bootstrap';
import SourceLanguages from './SourceLanguages';
import $ from 'jquery';
import GlobalURL from './GlobalURL';
import TargetLanguages from './TargetLanguages';
import Checkbox from './Checkbox';

const booksName1 = [
  'GEN', 'EXO', 'LEV', 'NUM', 'DEU', 'JOS', 'JDG', 'RUT', '1SA', '2SA', '1KI', '2KI', '1CH', '2CH', 'EZR', 'NEH', 'EST', 'JOB',
  'PSA', 'PRO', 'ECC', 'SNG', 'ISA', 'JER', 'LAM', 'EZK', 'DAN', 'HOS', 'JOL', 'AMO', 'OBA', 'JON', 'MIC', 'NAM', 'HAB', 'ZEP',
  'HAG', 'ZEC', 'MAL', 'MAT', 'MRK', 'LUK', 'JHN', 'ACT', 'ROM', '1CO', '2CO', 'GAL', 'EPH', 'PHP', 'COL', '1TH', '2TH', '1TI',
  '2TI', 'TIT', 'PHM', 'HEB', 'JAS', '1PE', '2PE', '1JN', '2JN', '3JN', 'JUD', 'REV'
];

const booksName2 = [
  'GEN', 'EXO', 'LEV', 'NUM', 'DEU', 'JOS', 'JDG', 'RUT', '1SA', '2SA', '1KI', '2KI', '1CH', '2CH', 'EZR', 'NEH', 'EST', 'JOB',
  'PSA', 'PRO', 'ECC', 'SNG', 'ISA', 'JER', 'LAM', 'EZK', 'DAN', 'HOS', 'JOL', 'AMO', 'OBA', 'JON', 'MIC', 'NAM', 'HAB', 'ZEP',
  'HAG', 'ZEC', 'MAL', 'MAT', 'MRK', 'LUK', 'JHN', 'ACT', 'ROM', '1CO', '2CO', 'GAL', 'EPH', 'PHP', 'COL', '1TH', '2TH', '1TI',
  '2TI', 'TIT', 'PHM', 'HEB', 'JAS', '1PE', '2PE', '1JN', '2JN', '3JN', 'JUD', 'REV'
];

class DownloadTokens extends Component {
  constructor(props) {
    super(props);

    this.state = {
      sourcelang:'tam',
      version: '',
      revision: '',
      targetlang:'mal',
      books: [],
      nbooks: [],
      uploaded:'uploadingStatus',
      message: ''
    }
      // Upload file specific callback handlers
      this.onSelect = this.onSelect.bind(this);
      this.downloadTokenWords = this.downloadTokenWords.bind(this);
      this.parseJSONToXLS = this.parseJSONToXLS.bind(this);
  }

  componentWillMount = () => {
    this.selectedCheckboxes1 = new Set();
    this.selectedCheckboxes2 = new Set();
  }

  toggleCheckbox1 = label => {
    if (this.selectedCheckboxes1.has(label)) {
      this.selectedCheckboxes1.delete(label);
    } else {
      this.selectedCheckboxes1.add(label);
    }
  }

  toggleCheckbox2 = label => {
    if (this.selectedCheckboxes2.has(label)) {
      this.selectedCheckboxes2.delete(label);
    } else {
      this.selectedCheckboxes2.add(label);
    }
  }

  createCheckbox1 = label => (
    <Checkbox
            label={label}
            handleCheckboxChange={this.toggleCheckbox1}
            key={label}
    />
  )

  createCheckbox2 = label => (
    <Checkbox
            label={label}
            handleCheckboxChange={this.toggleCheckbox2}
            key={label}
    />
  )

  createCheckboxes1 = () => (
    booksName1.map(this.createCheckbox1)
  )

  createCheckboxes2 = () => (
    booksName2.map(this.createCheckbox2)
  )

  onSelect(e) {
    this.setState({
      [e.target.name]: e.target.value });
  }

// For Downloads Token words
  downloadTokenWords(e){
    e.preventDefault();
    global.books = [];
    global.nbooks= [];
    for (const books of this.selectedCheckboxes1) {
      global.books = Array.from(this.selectedCheckboxes1);
    }
    for (const nbooks of this.selectedCheckboxes2) {
      global.nbooks = Array.from(this.selectedCheckboxes2);
    }

    var _this = this
    var data = {
        "sourcelang": this.state.sourcelang, "version": this.state.version, "revision": this.state.revision , "targetlang": this.state.targetlang, "nbooks":global.nbooks, "books": global.books
    }
    let accessToken = JSON.parse(window.localStorage.getItem('access_token'))
    $.ajax({
      url: GlobalURL["hostURL"]+"/v1/getbookwiseautotokens",
      contentType: "application/json; charset=utf-8",
      data : JSON.stringify(data),
      method : "POST",
      headers: {
                "Authorization": "bearer " + JSON.stringify(accessToken['access_token']).slice(1,-1),
      },
       beforeSend: function () {
          $(".modal").show();
      },
      complete: function () {
          $(".modal").hide();
      },
      success: function (result) {
        result = JSON.parse(result)
        if (result.success !== false){
         _this.parseJSONToXLS(result);
        }
        else {
          _this.setState({message: result.message, uploaded: 'failure'})
        }
      },
      error: function (error) {
       _this.setState({message: error.message, uploaded: 'failure'})
      }
    });
  }

  // for parse JSON to XLS
  parseJSONToXLS(jsonData) {
  var array = [];
  var str = '';
  array = typeof jsonData !== 'object' ? JSON.parse(jsonData) : jsonData;
      for (var i = 0; i < array.length; i++) {
        str += array[i] + '\n';
      }
      str += '\r\n';
      var a = document.createElement('a');
      var blob = new Blob([ new Uint8Array([0xEF, 0xBB, 0xBF]), str], {'type':'application/vnd.ms-excel;charset=utf-8'});
      a.href = window.URL.createObjectURL(blob);
      a.download = this.state.sourcelang + this.state.version + 'Tokens.xls';
      a.click();
  }

  render() {
    return(

          <form className="col-md-12 uploader" encType="multipart/form-data">

Download Tokens

                {this.state.message}

                {this.state.message}

                <lable className="control-label Concord2">  Source Language  </lable>
                    <FormControl value={this.state.sourcelang} onChange={this.onSelect} name="sourcelang" componentClass="select" placeholder="select">
                      {SourceLanguages.map((sourcelang, i) => <option  key={i} value={sourcelang.code}>{sourcelang.value}</option>)}
                    </FormControl>
                 <lable className="control-lable Concord2">  Version  </lable>
                    <input value={this.state.version} onChange={this.onSelect} name="version" type="text"  placeholder="version" className="form-control"/>
                <lable className="control-lable Concord2">  Revision  </lable>
                    <input value={this.state.revision} onChange={this.onSelect} name="revision" type="text" placeholder="revision" className="form-control"/>
                <lable className="control-label Concord2">  Target Language  </lable>
                    <FormControl value={this.state.targetlang} onChange={this.onSelect} name="targetlang" componentClass="select" placeholder="select">
                      {TargetLanguages.map((targetlang, i) => <option  key={i} value={targetlang.code}>{targetlang.value}</option>)}
                    </FormControl>

                      Include Books
                      <form className="myCheck1" >
                        {this.createCheckboxes1()}
                      </form>

                      Exclude Books
                      <form className="myCheck2">
                        {this.createCheckboxes2()}
                      </form>

                  <button id="btnGet" type="button" className="btn btn-success ConcordButton" onClick={this.downloadTokenWords}> Download Token</button>

            </form>

      );
    }
}

export default DownloadTokens;<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import styled, { css } from 'styled-components'
import { Box, Flex, Container, cx, theme } from '@hackclub/design-system'
import Icon from '@hackclub/icons'
import Flag from 'components/Flag'
import { Link } from 'gatsby'
import ScrollLock from 'react-scrolllock'

const rgbaBgColor = (props, opacity) =>
  `rgba(
    ${props.bgColor[0]},
    ${props.bgColor[1]},
    ${props.bgColor[2]},
    ${opacity}
  )`

const Root = styled(Box.withComponent('header'))`
  position: absolute;
  top: 0;
  width: 100%;
  z-index: 1000;
  ${props =>
    (props.scrolled || props.toggled || props.fixed) &&
    css`
      position: fixed;
      background-color: ${rgbaBgColor(props, 0.96875)};
      border-bottom: 1px solid rgba(48, 48, 48, 0.125);
      @supports (-webkit-backdrop-filter: none) or (backdrop-filter: none) {
        background-color: ${props.transparent
          ? 'transparent'
          : rgbaBgColor(props, 0.75)};
        ${props.dark
          ? css`
              -webkit-backdrop-filter: saturate(90%) blur(20px);
            `
          : css`
              -webkit-backdrop-filter: saturate(180%) blur(20px);
            `};
      }
      ${theme.mediaQueries.reduceTransparency} {
        -webkit-backdrop-filter: auto !important;
      }
    `} @media print {
    display: none;
  }
`

const Content = styled(Container)`
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  padding-left: ${theme.space[3]}px;
  ${theme.mediaQueries.md} {
    padding: 0 ${theme.space[4]}px;
  }
`

const hoverColor = name =>
  ({
    white: 'smoke',
    smoke: 'muted',
    muted: 'slate',
    slate: 'black',
    black: 'slate',
    primary: 'error'
  }[name] || 'black')

const NavBar = styled(Box.withComponent('nav'))`
  display: none;
  a {
    color: ${props => cx(props.color)};
    margin-left: ${theme.space[2]}px;
    padding: ${theme.space[2]}px;
    text-decoration: none;
  }
  ${props =>
    props.isMobile
      ? css`
          display: ${props.toggled ? 'flex' : 'none'};
          flex-direction: column;
          overflow-y: auto;
          text-align: left;
          height: 100vh;
          a {
            padding: 0;
            margin: 0 auto;
            height: 64px;
            line-height: 64px;
            font-weight: bold;
            width: 100%;
            max-width: 18rem;
            &:not(:last-child) {
              border-bottom: 1px solid rgba(48, 48, 48, 0.125);
            }
            @media screen and (max-width: 22em) {
              max-width: 16rem;
            }
          }
        `
      : css`
          @media (min-width: 56em) {
            display: flex;
            position: absolute;
            left: 50%;
            transform: translateX(-50%);
          }
          a {
            font-size: ${theme.fontSizes[1]}px;
            text-transform: uppercase;
            &:hover {
              color: ${theme.cx(hoverColor(props.color))};
            }
          }
        `};
`

const Navigation = props => (
  <NavBar role="navigation" {...props}>
    <Link to="/start/" children="Clubs" />
    <Link to="/workshops/" children="Workshops" />
    <a
      href="https://hackathons.hackclub.com/"
      children="Hackathons"
      target="_blank"
      rel="noopener noreferrer"
    />
    <Link to="/bank/" children="Bank" />
    <Link to="/donate/" children="Donate" />
  </NavBar>
)

const ToggleContainer = styled(Flex)`
  align-items: center;
  justify-content: center;
  min-width: 64px;
  min-height: 44px;
  cursor: pointer;
  user-select: none;
  margin-left: auto;
`

const Toggle = styled(Icon)`
  display: block;
  ${theme.mediaQueries.md} {
    display: none;
  }
`

class Header extends Component {
  state = {
    scrolled: false,
    toggled: false
  }

  static defaultProps = {
    dark: false,
    color: 'white'
  }

  static propTypes = {
    color: PropTypes.string,
    bgColor: PropTypes.arrayOf(PropTypes.number),
    dark: PropTypes.bool,
    transparent: PropTypes.bool,
    fixed: PropTypes.bool
  }

  componentDidMount() {
    this.bindScroll(true)
    if (typeof window !== 'undefined') {
      const mobileQuery = window.matchMedia('(max-width: 48em)')
      mobileQuery.addListener(() => {
        this.setState({ toggled: false })
      })
    }
  }

  componentWillUnmount = () => {
    this.bindScroll(false)
  }

  bindScroll = add => {
    if (typeof window !== 'undefined') {
      window[add ? 'addEventListener' : 'removeEventListener'](
        'scroll',
        this.onScroll
      )
    }
  }

  onScroll = () => {
    const newState = window.scrollY >= 16
    const { scrolled: oldState } = this.state

    if (newState !== oldState) {
      this.setState({
        scrolled: newState
      })
    }
  }

  handleToggleMenu = () => {
    this.setState(state => ({
      toggled: !state.toggled
    }))
  }

  render() {
    const { color, dark, fixed, bgColor, ...props } = this.props
    const { scrolled, toggled } = this.state
    const baseColor = dark
      ? color || 'white'
      : color === 'white'
      ? 'black'
      : color
    const toggleColor = dark
      ? color || 'snow'
      : toggled || (color === 'white' && scrolled)
      ? 'slate'
      : color

    return (
      <Root
        {...props}
        fixed={fixed}
        scrolled={scrolled}
        toggled={toggled}
        dark={dark}
        bgColor={bgColor || (dark ? [32, 34, 36] : [255, 255, 255])}
      >
        <Content>
          <Flag scrolled={scrolled || fixed} />
          <Navigation color={baseColor} dark={dark} />
          <ToggleContainer color={toggleColor} onClick={this.handleToggleMenu}>
            <Toggle glyph={toggled ? 'view-close' : 'menu'} toggled={toggled} />
          </ToggleContainer>
        </Content>
        <Navigation isMobile toggled={toggled} color={baseColor} dark={dark} />
        {toggled && <ScrollLock />}
      </Root>
    )
  }
}

export default Header
<commit_msg>Revert "Possibly fix #508"<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import styled, { css } from 'styled-components'
import { Box, Flex, Container, cx, theme } from '@hackclub/design-system'
import Icon from '@hackclub/icons'
import Flag from 'components/Flag'
import { Link } from 'gatsby'
import ScrollLock from 'react-scrolllock'

const rgbaBgColor = (props, opacity) =>
  `rgba(
    ${props.bgColor[0]},
    ${props.bgColor[1]},
    ${props.bgColor[2]},
    ${opacity}
  )`

const Root = styled(Box.withComponent('header'))`
  position: absolute;
  top: 0;
  width: 100%;
  z-index: 1000;
  ${props =>
    (props.scrolled || props.toggled || props.fixed) &&
    css`
      position: fixed;
      background-color: ${rgbaBgColor(props, 0.96875)};
      border-bottom: 1px solid rgba(48, 48, 48, 0.125);
      @supports (-webkit-backdrop-filter: none) or (backdrop-filter: none) {
        background-color: ${props.transparent
          ? 'transparent'
          : rgbaBgColor(props, 0.75)};
        ${props.dark
          ? css`
              -webkit-backdrop-filter: saturate(90%) blur(20px);
            `
          : css`
              -webkit-backdrop-filter: saturate(180%) blur(20px);
            `};
      }
      ${theme.mediaQueries.reduceTransparency} {
        -webkit-backdrop-filter: auto !important;
      }
    `} @media print {
    display: none;
  }
`

const Content = styled(Container)`
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  padding-left: ${theme.space[3]}px;
  ${theme.mediaQueries.md} {
    padding: 0 ${theme.space[4]}px;
  }
`

const hoverColor = name =>
  ({
    white: 'smoke',
    smoke: 'muted',
    muted: 'slate',
    slate: 'black',
    black: 'slate',
    primary: 'error'
  }[name] || 'black')

const NavBar = styled(Box.withComponent('nav'))`
  display: none;
  a {
    color: ${props => cx(props.color)};
    margin-left: ${theme.space[2]}px;
    padding: ${theme.space[2]}px;
    text-decoration: none;
  }
  ${props =>
    props.isMobile
      ? css`
          display: ${props.toggled ? 'flex' : 'none'};
          flex-direction: column;
          overflow-y: auto;
          text-align: left;
          height: 100vh;
          a {
            padding: 0;
            margin: 0 auto;
            height: 64px;
            line-height: 64px;
            font-weight: bold;
            width: 100%;
            max-width: 18rem;
            &:not(:last-child) {
              border-bottom: 1px solid rgba(48, 48, 48, 0.125);
            }
            @media screen and (max-width: 22em) {
              max-width: 16rem;
            }
          }
        `
      : css`
          @media (min-width: 56em) {
            display: flex;
            position: absolute;
            left: 50%;
            transform: translateX(-50%);
          }
          a {
            font-size: ${theme.fontSizes[1]}px;
            text-transform: uppercase;
            &:hover {
              color: ${theme.cx(hoverColor(props.color))};
            }
          }
        `};
`

const Navigation = props => (
  <NavBar role="navigation" {...props}>
    <Link to="/start/" children="Clubs" />
    <Link to="/workshops/" children="Workshops" />
    <a
      href="https://hackathons.hackclub.com/"
      children="Hackathons"
      target="_blank"
      rel="noopener noreferrer"
    />
    <Link to="/bank/" children="Bank" />
    <Link to="/donate/" children="Donate" />
  </NavBar>
)

const ToggleContainer = styled(Flex)`
  align-items: center;
  justify-content: center;
  min-width: 64px;
  min-height: 44px;
  cursor: pointer;
  user-select: none;
  margin-left: auto;
`

const Toggle = styled(Icon)`
  display: block;
  ${theme.mediaQueries.md} {
    display: none;
  }
`

class Header extends Component {
  state = {
    scrolled: false,
    toggled: false
  }

  static defaultProps = {
    dark: false,
    color: 'white'
  }

  static propTypes = {
    color: PropTypes.string,
    bgColor: PropTypes.arrayOf(PropTypes.number),
    dark: PropTypes.bool,
    transparent: PropTypes.bool,
    fixed: PropTypes.bool
  }

  componentDidMount() {
    this.bindScroll(true)
    if (typeof window !== 'undefined') {
      const mobileQuery = window.matchMedia('(max-width: 48em)')
      mobileQuery.addListener(() => {
        this.setState({ toggled: false })
      })
    }
  }

  componentWillUnmount = () => {
    this.bindScroll(false)
  }

  bindScroll = add => {
    if (typeof window !== 'undefined') {
      window[add ? 'addEventListener' : 'removeEventListener'](
        'scroll',
        this.onScroll
      )
    }
  }

  onScroll = () => {
    const newState = window.scrollY >= 16
    const { scrolled: oldState } = this.state

    if (newState !== oldState) {
      this.setState({
        scrolled: newState
      })
    }
  }

  handleToggleMenu = () => {
    this.setState(state => ({
      toggled: !state.toggled
    }))
  }

  render() {
    const { color, dark, fixed, bgColor, ...props } = this.props
    const { scrolled, toggled } = this.state
    const baseColor = dark
      ? color || 'white'
      : color === 'white' && scrolled
      ? 'black'
      : color
    const toggleColor = dark
      ? color || 'snow'
      : toggled || (color === 'white' && scrolled)
      ? 'slate'
      : color

    return (
      <Root
        {...props}
        fixed={fixed}
        scrolled={scrolled}
        toggled={toggled}
        dark={dark}
        bgColor={bgColor || (dark ? [32, 34, 36] : [255, 255, 255])}
      >
        <Content>
          <Flag scrolled={scrolled || fixed} />
          <Navigation color={baseColor} dark={dark} />
          <ToggleContainer color={toggleColor} onClick={this.handleToggleMenu}>
            <Toggle glyph={toggled ? 'view-close' : 'menu'} toggled={toggled} />
          </ToggleContainer>
        </Content>
        <Navigation isMobile toggled={toggled} color={baseColor} dark={dark} />
        {toggled && <ScrollLock />}
      </Root>
    )
  }
}

export default Header
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import styled, {keyframes} from 'styled-components';

import pod from '../img/pod.svg';

import {times, STATION} from '../constants';

// moves from departure to arrival
const Wrapper = styled.div`
  transition: transform ${times.podAnimation}ms;
  position: absolute;
  left: calc(50% - 3em/2);
  top: calc(50% - 3em/2);
`;

function isCenter(stop) {
  return stop['@id'] === STATION.URI;
}

export default class Pod extends Component {
  constructor(props) {
    super(props);
    const {origin} = props;
    if (isCenter(origin)) {
      this.state = {
        position: {
          transform: `translateX(0) translateY(0) scale(-1, 1)`,
        }
      }
    } else {
      this.state = {
        position: {
          transform: `translateX(0) translateY(0) scale(0.8)`
        }
      };
    }
  }

  componentDidMount() {
    // eslint-disable-next-line no-unused-vars
    const {origin, destination} = this.props;

    let position = '';

    if (isCenter(origin)) {
      position = `translateX(-50vw) translateY(-50vh) scale(-1, 1)`;
    } else {
      this.setState({
        position: {
          transform: `translateX(-50vw) translateY(50vh) scale(1)`
        }
      });
      position = `translateX(0) translateY(0) scale(0.8)`;
    }

    setTimeout(() => {
      this.setState({
        position: {
          transform: position
        }
      });
    },10); // meh, idk why this won't work with rAF
  }

  render() {
    const random = Math.random();

    const buzz = keyframes`
      0%, 100% {
        transform:
          scale(1)
          translateX(${random}vh)
          translateY(${random}vw);
      }

      50% {
        transform:
          scale(${random * (1.4 - 0.6) + 0.6})
          translateX(${random}vh)
          translateY(${random}vw);
      }
    `;

    const Img = styled.img`
      width: 3em;
      height: 3em;
      animation: ${buzz} ${props => .5 + props.random}s infinite;
    `;

    return (
      <Wrapper style={this.state.position}>

      </Wrapper>
    );
  }
}
<commit_msg>rAF works<commit_after>import React, {Component} from 'react';
import styled, {keyframes} from 'styled-components';

import pod from '../img/pod.svg';

import {times, STATION} from '../constants';

// moves from departure to arrival
const Wrapper = styled.div`
  transition: transform ${times.podAnimation}ms;
  position: absolute;
  left: calc(50% - 3em/2);
  top: calc(50% - 3em/2);
`;

function isCenter(stop) {
  return stop['@id'] === STATION.URI;
}

export default class Pod extends Component {
  constructor(props) {
    super(props);
    const {origin} = props;
    if (isCenter(origin)) {
      this.state = {
        position: {
          transform: `translateX(0) translateY(0) scale(-1, 1)`,
        }
      }
    } else {
      this.state = {
        position: {
          transform: `translateX(0) translateY(0) scale(0.8)`
        }
      };
    }
  }

  componentDidMount() {
    // eslint-disable-next-line no-unused-vars
    const {origin, destination} = this.props;

    let position = '';

    if (isCenter(origin)) {
      position = `translateX(-50vw) translateY(-50vh) scale(-1, 1)`;
    } else {
      this.setState({
        position: {
          transform: `translateX(-50vw) translateY(50vh) scale(1)`
        }
      });
      position = `translateX(0) translateY(0) scale(0.8)`;
    }

    requestAnimationFrame(() => {
      this.setState({
        position: {
          transform: position
        }
      });
    });
  }

  render() {
    const random = Math.random();

    const buzz = keyframes`
      0%, 100% {
        transform:
          scale(1)
          translateX(${random}vh)
          translateY(${random}vw);
      }

      50% {
        transform:
          scale(${random * (1.4 - 0.6) + 0.6})
          translateX(${random}vh)
          translateY(${random}vw);
      }
    `;

    const Img = styled.img`
      width: 3em;
      height: 3em;
      animation: ${buzz} ${props => .5 + props.random}s infinite;
    `;

    return (
      <Wrapper style={this.state.position}>

      </Wrapper>
    );
  }
}
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import players from '../resources/players'
import Players from './Players'
import Drafts from './Drafts'

// class Counter extends Component {
//   constructor(props) {
//     super(props)
//     this.state = {counter: 0}
//     this.interval = setInterval(() => this.tick(), 1000)
//   }
//
//   tick() {
//     this.setState({
//       counter: this.state.counter + this.props.increment
//     })
//   }
//
//   componentWillUnmount() {
//     clearInterval(this.interval)
//   }
//
//   render() {
//     return (
//

//         Counter ({this.props.increment}): {this.state.counter}
//

//     )
//   }
// }

export class App extends Component {
  render() {
    return (

# Draft pod generator for Magic: The Gathering

        For instructions

        <Players players={players} />

        <Drafts players={players} />

    )
  }
}
<commit_msg>remove comments<commit_after>import React, {Component} from 'react'
import players from '../resources/players'
import Players from './Players'
import Drafts from './Drafts'

export class App extends Component {
  render() {
    return (

# Draft pod generator for Magic: The Gathering

        For instructions

        <Players players={players} />

        <Drafts players={players} />

    )
  }
}
<|endoftext|>