<commit_before>var term,
    protocol,
    socketURL,
    socket,
    pid;

var terminalContainer = document.getElementById('terminal-container'),
    actionElements = {
      findNext: document.querySelector('#find-next'),
      findPrevious: document.querySelector('#find-previous')
    },
    optionElements = {
      cursorBlink: document.querySelector('#option-cursor-blink'),
      cursorStyle: document.querySelector('#option-cursor-style'),
      scrollback: document.querySelector('#option-scrollback'),
      tabstopwidth: document.querySelector('#option-tabstopwidth'),
      bellStyle: document.querySelector('#option-bell-style')
    },
    colsElement = document.getElementById('cols'),
    rowsElement = document.getElementById('rows');

function setTerminalSize() {
  var cols = parseInt(colsElement.value, 10);
  var rows = parseInt(rowsElement.value, 10);
  var viewportElement = document.querySelector('.xterm-viewport');
  var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
  var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px';
  var height = (rows * term.charMeasure.height).toString() + 'px';

  terminalContainer.style.width = width;
  terminalContainer.style.height = height;
  term.resize(cols, rows);
}

colsElement.addEventListener('change', setTerminalSize);
rowsElement.addEventListener('change', setTerminalSize);

actionElements.findNext.addEventListener('keypress', function (e) {
  if (e.key === "Enter") {
    e.preventDefault();
    term.findNext(actionElements.findNext.value);
  }
});
actionElements.findPrevious.addEventListener('keypress', function (e) {
  if (e.key === "Enter") {
    e.preventDefault();
    term.findPrevious(actionElements.findPrevious.value);
  }
});

optionElements.cursorBlink.addEventListener('change', function () {
  term.setOption('cursorBlink', optionElements.cursorBlink.checked);
});
optionElements.cursorStyle.addEventListener('change', function () {
  term.setOption('cursorStyle', optionElements.cursorStyle.value);
});
optionElements.bellStyle.addEventListener('change', function () {
  term.setOption('bellStyle', optionElements.bellStyle.value);
});
optionElements.scrollback.addEventListener('change', function () {
  term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
});
optionElements.tabstopwidth.addEventListener('change', function () {
  term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
});

createTerminal();

function createTerminal() {
  // Clean terminal
  while (terminalContainer.children.length) {
    terminalContainer.removeChild(terminalContainer.children[0]);
  }
  term = new Terminal({
    cursorBlink: optionElements.cursorBlink.checked,
    scrollback: parseInt(optionElements.scrollback.value, 10),
    tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
  });
  term.on('resize', function (size) {
    if (!pid) {
      return;
    }
    var cols = size.cols,
        rows = size.rows,
        url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;

    fetch(url, {method: 'POST'});
  });
  protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
  socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';

  term.open(terminalContainer);
  term.fit();

  // fit is called within a setTimeout, cols and rows need this.
  setTimeout(() => {
    colsElement.value = term.cols;
    rowsElement.value = term.rows;

    // Set terminal size again to set the specific dimensions on the demo
    setTerminalSize();

    fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {

      res.text().then(function (pid) {
        window.pid = pid;
        socketURL += pid;
        socket = new WebSocket(socketURL);
        socket.binaryType = "arraybuffer";
        socket.onopen = runRealTerminal;
        socket.onclose = runFakeTerminal;
        socket.onerror = runFakeTerminal;

        socket.addEventListener("message", handleWSMessage);
      });
    });
  }, 0);
}

//----------------------------------------------------------------------
// UI STUFF

function _show_file_info(xfer) {
    var file_info = xfer.get_details();

    document.getElementById("name").textContent = file_info.name;
    document.getElementById("size").textContent = file_info.size;
    document.getElementById("mtime").textContent = file_info.mtime;
    document.getElementById("files_remaining").textContent = file_info.files_remaining;
    document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining;

    document.getElementById("mode").textContent = "0" + file_info.mode.toString(8);

    document.getElementById("zm_file").style.display = "";
}
function _hide_file_info() {
    document.getElementById("zm_file").style.display = "none";
}

function _download(xfer, buffer) {
    return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name);
}

function _update_progress(xfer) {
    document.getElementById("zm_progress").style.display = "";

    var total_in = xfer.get_offset();

    document.getElementById("bytes_received").textContent = total_in;

    var percent_received = 100 * total_in / xfer.get_details().size;
    document.getElementById("percent_received").textContent = percent_received;
}

var start_form = document.getElementById("zm_start");

// END UI STUFF
//----------------------------------------------------------------------

function _handle_receive_session(zsession) {
    zsession.on("offer", function(xfer) {
        _show_file_info(xfer);

        var offer_form = document.getElementById("zm_offer");
        offer_form.style.display = "";
        offer_form.onsubmit = function(e) {
            offer_form.style.display = "none";

            //START
            if (offer_form.zmaccept.value) {
                var FILE_BUFFER = [];
                xfer.on("input", (payload) => {
                    _update_progress(xfer);
                    FILE_BUFFER.push.apply(FILE_BUFFER, payload);
                });
                xfer.accept().then( () => {
                    _download(xfer, FILE_BUFFER);
                    _hide_file_info();
                } );
            }
            else {
                _hide_file_info();
                xfer.skip();
            }
            //END
        };
    } );

    zsession.start();
}

function _handle_send_session(zsession) {
    var choose_form = document.getElementById("zm_choose");
    choose_form.style.display = "";
    choose_form.onsubmit = function(e) {
        choose_form.style.display = "none";

        var file_el = document.getElementById("zm_files");
        var files_obj = file_el.files;

        Zmodem.Browser.send_files(
            zsession,
            files_obj,
            {
                on_offer_response(obj, xfer) {
                    console.log("offer", xfer ? "accepted" : "skipped");
                },
                on_file_complete(obj) {
                    console.log("COMPLETE", obj);
                },
            }
        ).then(
            zsession.close.bind(zsession),
            console.error.bind(console)
        );
    };
}

var text_encoder = new TextEncoder();
var zsentry = new Zmodem.Sentry( {
    to_terminal(octets) {
        term.write(
            String.fromCharCode.apply(String, octets)
        );
    },

    sender(octets) {
        socket.send( new Uint8Array(octets) );
    },

    on_retract() {
        start_form.style.display = "none";
        start_form.onsubmit = null;
    },

    on_detect(detection) {
        start_form.style.display = "";
        start_form.onsubmit = function(e) {
            start_form.style.display = "none";

            //Aborted
            if (!e.currentTarget.zmstart.value) {
                return;
            }

            var zsession = detection.confirm();

            //HERE
            if (zsession.type === "receive") {
                _handle_receive_session(zsession);
            }
            else {
                _handle_send_session(zsession);
            }
        };
    },
} );

function handleWSMessage(evt) {

    //For some reason the first message from the server is text.
    if (typeof evt.data === "string") {
        term.write(evt.data);
    }
    else {
        zsentry.consume(evt.data);
    }
}

//var text_encoder = new TextEncoder();

function runRealTerminal() {
  //term.attach(socket);

    term.on("data", (d) => {
        //socket.send( text_encoder.encode(d) );
        socket.send(d);
    });

  term._initialized = true;
}

function runFakeTerminal() {
  if (term._initialized) {
    return;
  }

  term._initialized = true;

  var shellprompt = '$ ';

  term.prompt = function () {
    term.write('\r\n' + shellprompt);
  };

  term.writeln('Welcome to xterm.js');
  term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
  term.writeln('Type some keys and commands to play around.');
  term.writeln('');
  term.prompt();

  term.on('key', function (key, ev) {
    var printable = (
      !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
    );

    if (ev.keyCode == 13) {
      term.prompt();
    } else if (ev.keyCode == 8) {
     // Do not delete the prompt
      if (term.x > 2) {
        term.write('\b \b');
      }
    } else if (printable) {
      term.write(key);
    }
  });

  term.on('paste', function (data, ev) {
    term.write(data);
  });
}
<commit_msg>demo: hide progress after receive<commit_after>var term,
    protocol,
    socketURL,
    socket,
    pid;

var terminalContainer = document.getElementById('terminal-container'),
    actionElements = {
      findNext: document.querySelector('#find-next'),
      findPrevious: document.querySelector('#find-previous')
    },
    optionElements = {
      cursorBlink: document.querySelector('#option-cursor-blink'),
      cursorStyle: document.querySelector('#option-cursor-style'),
      scrollback: document.querySelector('#option-scrollback'),
      tabstopwidth: document.querySelector('#option-tabstopwidth'),
      bellStyle: document.querySelector('#option-bell-style')
    },
    colsElement = document.getElementById('cols'),
    rowsElement = document.getElementById('rows');

function setTerminalSize() {
  var cols = parseInt(colsElement.value, 10);
  var rows = parseInt(rowsElement.value, 10);
  var viewportElement = document.querySelector('.xterm-viewport');
  var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
  var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px';
  var height = (rows * term.charMeasure.height).toString() + 'px';

  terminalContainer.style.width = width;
  terminalContainer.style.height = height;
  term.resize(cols, rows);
}

colsElement.addEventListener('change', setTerminalSize);
rowsElement.addEventListener('change', setTerminalSize);

actionElements.findNext.addEventListener('keypress', function (e) {
  if (e.key === "Enter") {
    e.preventDefault();
    term.findNext(actionElements.findNext.value);
  }
});
actionElements.findPrevious.addEventListener('keypress', function (e) {
  if (e.key === "Enter") {
    e.preventDefault();
    term.findPrevious(actionElements.findPrevious.value);
  }
});

optionElements.cursorBlink.addEventListener('change', function () {
  term.setOption('cursorBlink', optionElements.cursorBlink.checked);
});
optionElements.cursorStyle.addEventListener('change', function () {
  term.setOption('cursorStyle', optionElements.cursorStyle.value);
});
optionElements.bellStyle.addEventListener('change', function () {
  term.setOption('bellStyle', optionElements.bellStyle.value);
});
optionElements.scrollback.addEventListener('change', function () {
  term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
});
optionElements.tabstopwidth.addEventListener('change', function () {
  term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
});

createTerminal();

function createTerminal() {
  // Clean terminal
  while (terminalContainer.children.length) {
    terminalContainer.removeChild(terminalContainer.children[0]);
  }
  term = new Terminal({
    cursorBlink: optionElements.cursorBlink.checked,
    scrollback: parseInt(optionElements.scrollback.value, 10),
    tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
  });
  term.on('resize', function (size) {
    if (!pid) {
      return;
    }
    var cols = size.cols,
        rows = size.rows,
        url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;

    fetch(url, {method: 'POST'});
  });
  protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
  socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';

  term.open(terminalContainer);
  term.fit();

  // fit is called within a setTimeout, cols and rows need this.
  setTimeout(() => {
    colsElement.value = term.cols;
    rowsElement.value = term.rows;

    // Set terminal size again to set the specific dimensions on the demo
    setTerminalSize();

    fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {

      res.text().then(function (pid) {
        window.pid = pid;
        socketURL += pid;
        socket = new WebSocket(socketURL);
        socket.binaryType = "arraybuffer";
        socket.onopen = runRealTerminal;
        socket.onclose = runFakeTerminal;
        socket.onerror = runFakeTerminal;

        socket.addEventListener("message", handleWSMessage);
      });
    });
  }, 0);
}

//----------------------------------------------------------------------
// UI STUFF

function _show_file_info(xfer) {
    var file_info = xfer.get_details();

    document.getElementById("name").textContent = file_info.name;
    document.getElementById("size").textContent = file_info.size;
    document.getElementById("mtime").textContent = file_info.mtime;
    document.getElementById("files_remaining").textContent = file_info.files_remaining;
    document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining;

    document.getElementById("mode").textContent = "0" + file_info.mode.toString(8);

    document.getElementById("zm_file").style.display = "";
}
function _hide_file_info() {
    document.getElementById("zm_file").style.display = "none";
}

function _download(xfer, buffer) {
    return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name);
}

function _update_progress(xfer) {
    document.getElementById("zm_progress").style.display = "";

    var total_in = xfer.get_offset();

    document.getElementById("bytes_received").textContent = total_in;

    var percent_received = 100 * total_in / xfer.get_details().size;
    document.getElementById("percent_received").textContent = percent_received;
}

function _hide_progress() {
    document.getElementById("zm_progress").style.display = "none";
}

var start_form = document.getElementById("zm_start");

// END UI STUFF
//----------------------------------------------------------------------

function _handle_receive_session(zsession) {
    zsession.on("offer", function(xfer) {
        _show_file_info(xfer);

        var offer_form = document.getElementById("zm_offer");
        offer_form.style.display = "";
        offer_form.onsubmit = function(e) {
            offer_form.style.display = "none";

            //START
            if (offer_form.zmaccept.value) {
                var FILE_BUFFER = [];
                xfer.on("input", (payload) => {
                    _update_progress(xfer);
                    FILE_BUFFER.push.apply(FILE_BUFFER, payload);
                });
                xfer.accept().then( () => {
                    _download(xfer, FILE_BUFFER);
                    _hide_file_info();
                    _hide_progress();
                } );
            }
            else {
                _hide_file_info();
                xfer.skip();
            }
            //END
        };
    } );

    zsession.start();
}

function _handle_send_session(zsession) {
    var choose_form = document.getElementById("zm_choose");
    choose_form.style.display = "";
    choose_form.onsubmit = function(e) {
        choose_form.style.display = "none";

        var file_el = document.getElementById("zm_files");
        var files_obj = file_el.files;

        Zmodem.Browser.send_files(
            zsession,
            files_obj,
            {
                on_offer_response(obj, xfer) {
                    console.log("offer", xfer ? "accepted" : "skipped");
                },
                on_file_complete(obj) {
                    console.log("COMPLETE", obj);
                },
            }
        ).then(
            zsession.close.bind(zsession),
            console.error.bind(console)
        );
    };
}

var text_encoder = new TextEncoder();
var zsentry = new Zmodem.Sentry( {
    to_terminal(octets) {
        term.write(
            String.fromCharCode.apply(String, octets)
        );
    },

    sender(octets) {
        socket.send( new Uint8Array(octets) );
    },

    on_retract() {
        start_form.style.display = "none";
        start_form.onsubmit = null;
    },

    on_detect(detection) {
        start_form.style.display = "";
        start_form.onsubmit = function(e) {
            start_form.style.display = "none";

            //Aborted
            if (!e.currentTarget.zmstart.value) {
                return;
            }

            var zsession = detection.confirm();

            //HERE
            if (zsession.type === "receive") {
                _handle_receive_session(zsession);
            }
            else {
                _handle_send_session(zsession);
            }
        };
    },
} );

function handleWSMessage(evt) {

    //For some reason the first message from the server is text.
    if (typeof evt.data === "string") {
        term.write(evt.data);
    }
    else {
        zsentry.consume(evt.data);
    }
}

//var text_encoder = new TextEncoder();

function runRealTerminal() {
  //term.attach(socket);

    term.on("data", (d) => {
        //socket.send( text_encoder.encode(d) );
        socket.send(d);
    });

  term._initialized = true;
}

function runFakeTerminal() {
  if (term._initialized) {
    return;
  }

  term._initialized = true;

  var shellprompt = '$ ';

  term.prompt = function () {
    term.write('\r\n' + shellprompt);
  };

  term.writeln('Welcome to xterm.js');
  term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
  term.writeln('Type some keys and commands to play around.');
  term.writeln('');
  term.prompt();

  term.on('key', function (key, ev) {
    var printable = (
      !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
    );

    if (ev.keyCode == 13) {
      term.prompt();
    } else if (ev.keyCode == 8) {
     // Do not delete the prompt
      if (term.x > 2) {
        term.write('\b \b');
      }
    } else if (printable) {
      term.write(key);
    }
  });

  term.on('paste', function (data, ev) {
    term.write(data);
  });
}
<|endoftext|>


<commit_before>'use babel';

import fs from 'fs-extra';
import temp from 'temp';
import { vouch } from 'atom-build-spec-helpers';
import { provideBuilder } from '../lib/make';

describe('make', () => {
  let directory;
  const builder = provideBuilder();

  beforeEach(() => {
    waitsForPromise(() => {
      return vouch(temp.mkdir, 'atom-build-make-spec-')
        .then((dir) => vouch(fs.realpath, dir))
        .then((dir) => (directory = `${dir}/`));
    });
  });

  afterEach(() => {
    fs.removeSync(directory);
  });

  describe('when makefile exists', () => {
    it('should be eligible', () => {
      fs.writeFileSync(directory + 'Makefile', fs.readFileSync(__dirname + '/Makefile'));
      expect(builder.isEligable(directory)).toBe(true);
    });

    it('should give default target', () => {
      fs.writeFileSync(directory + 'Makefile', fs.readFileSync(__dirname + '/Makefile'));
      waitsForPromise(() => {
        return Promise.resolve(builder.settings(directory)).then((settings) => {
          expect(settings.length).toBe(1);
          const s = settings[0];
          expect(s.name).toBe('GNU Make: default');
          expect(s.exec).toBe('make');
          expect(s.args).toBe(undefined);
          expect(s.sh).toBe(false);
        });
      });
    });
  });
});
<commit_msg>Added specs for not eligible<commit_after>'use babel';

import fs from 'fs-extra';
import temp from 'temp';
import { vouch } from 'atom-build-spec-helpers';
import { provideBuilder } from '../lib/make';

describe('make', () => {
  let directory;
  const builder = provideBuilder();

  beforeEach(() => {
    waitsForPromise(() => {
      return vouch(temp.mkdir, 'atom-build-make-spec-')
        .then((dir) => vouch(fs.realpath, dir))
        .then((dir) => (directory = `${dir}/`));
    });
  });

  afterEach(() => {
    fs.removeSync(directory);
  });

  describe('when makefile exists', () => {
    it('should be eligible', () => {
      fs.writeFileSync(directory + 'Makefile', fs.readFileSync(__dirname + '/Makefile'));
      expect(builder.isEligable(directory)).toBe(true);
    });

    it('should give default target', () => {
      fs.writeFileSync(directory + 'Makefile', fs.readFileSync(__dirname + '/Makefile'));
      waitsForPromise(() => {
        return Promise.resolve(builder.settings(directory)).then((settings) => {
          expect(settings.length).toBe(1);
          const s = settings[0];
          expect(s.name).toBe('GNU Make: default');
          expect(s.exec).toBe('make');
          expect(s.args).toBe(undefined);
          expect(s.sh).toBe(false);
        });
      });
    });
  });

  describe('when makefile does not exist', () => {
    it('should not be eligible', () => {
      expect(builder.isEligable(directory)).toBe(false);
    });
  });
});
<|endoftext|>


<commit_before>/* global HTMLElement */
var ANode = require('./a-node');
var components = require('./component').components;
var debug = require('../utils/debug');
var re = require('./a-register-element');
var THREE = require('../../lib/three');

var isNode = re.isNode;
var log = debug('core:a-entity');
var error = debug('core:a-entity:error');
var registerElement = re.registerElement;

/**
 * Entity element definition.
 * Entities represent all elements that are part of the scene, and always have
 * a position, rotation, and scale.
 * In the entity-component system, entities are just a container of components.
 *
 * For convenience of inheriting components, the scene element inherits from
 * this prototype. When necessary, it differentiates itself by setting
 * `this.isScene`.
 *
 * @namespace Entity
 * @member {object} components - entity's currently initialized components.
 * @member {object} object3D - three.js object.
 * @member {array} states
 */
var proto = {
  defaults: {
    value: {
      position: '',
      rotation: '',
      scale: '',
      visible: ''
    }
  },

  createdCallback: {
    value: function () {
      this.isEntity = true;
      this.states = [];
      this.components = {};
      this.object3D = new THREE.Mesh();
    }
  },

  attributeChangedCallback: {
    value: function (attr, oldVal, newVal) {
      this.setEntityAttribute(attr, oldVal, newVal);
    }
  },

  attachedCallback: {
    value: function () {
      this.addToParent();
      if (!this.isScene) {
        this.load();
      }
    }
  },

  /**
   * Tell parent to remove this element's object3D from its object3D.
   * Do not call on scene element because that will cause a call to
   * document.body.remove().
   */
  detachedCallback: {
    value: function () {
      if (!this.parentEl || this.isScene) { return; }
      this.removeComponents();
      this.parentEl.remove(this);
    }
  },

  applyMixin: {
    value: function (attr) {
      var attrValue = this.getAttribute(attr);
      if (!attr) {
        this.updateComponents();
        return;
      }
      this.updateComponent(attr, attrValue);
    }
  },

  /**
   * @returns {array} Direct children that are entities.
   */
  getChildEntities: {
    value: function () {
      var children = this.children;
      var childEntities = [];

      for (var i = 0; i < this.children.length; i++) {
        var child = children[i];
        if (child.tagName === this.tagName) {
          childEntities.push(child);
        }
      }

      return childEntities;
    }
  },

  mapStateMixins: {
    value: function (state, op) {
      var mixins = this.getAttribute('mixin');
      var mixinIds;
      if (!mixins) { return; }
      mixinIds = mixins.split(' ');
      mixinIds.forEach(function (id) {
        var mixinId = id + '-' + state;
        op(mixinId);
      });
      this.updateComponents();
    }
  },

  updateStateMixins: {
    value: function (newMixins, oldMixins) {
      var self = this;
      oldMixins = oldMixins || '';
      var newMixinsIds = newMixins.split(' ');
      var oldMixinsIds = oldMixins ? oldMixins.split(' ') : [];
      // The list of mixins that might have been removed on update
      var diff = oldMixinsIds.filter(function (i) { return newMixinsIds.indexOf(i) < 0; });
      // Remove the mixins that are gone on update
      diff.forEach(function (mixinId) {
        var forEach = Array.prototype.forEach;
        // State Mixins
        var stateMixinsEls = document.querySelectorAll('[id^=' + mixinId + '-]');
        var stateMixinIds = [];
        forEach.call(stateMixinsEls, function (el) { stateMixinIds.push(el.id); });
        stateMixinIds.forEach(self.unregisterMixin.bind(self));
      });
      this.states.forEach(function (state) {
        newMixinsIds.forEach(function (id) {
          var mixinId = id + '-' + state;
          self.registerMixin(mixinId);
        });
      });
    }
  },

  add: {
    value: function (el) {
      if (!el.object3D) {
        error("Trying to add an object3D that doesn't exist");
      }
      this.object3D.add(el.object3D);
    }
  },

  addToParent: {
    value: function () {
      var self = this;
      var parent = this.parentEl = this.parentNode;
      var attachedToParent = this.attachedToParent;
      if (!parent || attachedToParent) { return; }
      if (isNode(parent)) {
        attach();
        return;
      }
      // If the parent isn't an `ANode` but eventually it will be
      // when a templated element is created, we want to attach
      // this element to the parent then.
      parent.addEventListener('nodeready', attach);
      function attach () {
        // To prevent an object to attach itself multiple times to the parent.
        self.attachedToParent = true;
        if (parent.add) {
          parent.add(self);
        }
      }
    }
  },

  load: {
    value: function () {
      if (this.hasLoaded) { return; }
      this.object3D.el = this;

      // Attach to parent object3D.
      this.addToParent();

      if (this.isScene) {
        ANode.prototype.load.call(this, this.updateComponents.bind(this));
      } else {
        ANode.prototype.load.call(this, this.updateComponents.bind(this),
                                  function (el) { return el.isEntity; });
      }
    },
    writable: window.debug
  },

  remove: {
    value: function (el) {
      this.object3D.remove(el.object3D);
    }
  },

  /**
   * Check if a component is defined for an entity, including defaults and mixins.
   *
   * @param {string} name - Component name.
   */
  isComponentDefined: {
    value: function (name) {
      // If the defaults contain the component
      var inDefaults = this.defaults[name];
      // If the element contains the component
      var inAttribute = this.hasAttribute(name);
      if (inDefaults !== undefined || inAttribute) { return true; }
      return this.isComponentMixedIn(name);
    }
  },

  isComponentMixedIn: {
    value: function (name) {
      var i;
      var inMixin = false;
      var mixinEls = this.mixinEls;
     // If any of the mixins contains the component
      for (i = 0; i < mixinEls.length; ++i) {
        inMixin = mixinEls[i].hasAttribute(name);
        if (inMixin) { break; }
      }
      return inMixin;
    }
  },

  initComponent: {
    value: function (name, isDependency) {
      var isComponentDefined;
      // If it's not a component name or
      // If the component is already initialized
      if (!components[name] || this.components[name]) { return; }
      isComponentDefined = this.isComponentDefined(name);
      // If the component is not defined for the element
      if (!isComponentDefined && !isDependency) { return; }
      this.initComponentDependencies(name);
      // If a component it's a dependency of another but it's not defined
      // on the attribute, mixins or entity defaults
      // we have to add it.
      if (isDependency && !isComponentDefined) {
        this.setAttribute(name, '');
      } else {
        this.components[name] = new components[name].Component(this);
      }
      log('Component initialized: %s', name);
    }
  },

  initComponentDependencies: {
    value: function (name) {
      var self = this;
      var component = components[name];
      var dependencies;
      // If the component doesn't exist
      if (!component) { return; }
      dependencies = components[name].dependencies;
      if (!dependencies) { return; }
      dependencies.forEach(function (component) {
        self.initComponent(component, true);
      });
    }
  },

  removeComponents: {
    value: function () {
      var self = this;
      var entityComponents = Object.keys(this.components);
      entityComponents.forEach(removeComponent);
      function removeComponent (name) {
        self.components[name].remove();
        delete self.components[name];
      }
    }
  },

  updateComponents: {
    value: function () {
      var self = this;
      var entityComponents = Object.keys(components);
      // Updates components
      entityComponents.forEach(updateComponent);
      function updateComponent (name) {
        var elValue = self.getAttribute(name);
        self.updateComponent(name, elValue);
      }
    }
  },

  /**
   * Initialize, update, or remove a single component.
   *
   * When initializing, we set the component on `this.components`.
   *
   * @param {string} name - Component name.
   * @param {object} newData - The new attributes assigned to the component
   */
  updateComponent: {
    value: function (name, newData) {
      var component = this.components[name];
      var isDefault = name in this.defaults;
      var isMixedIn = this.isComponentMixedIn(name);
      if (component) {
        // Attribute was removed. Remove component.
        // 1. If the component is not defined in the defaults,
        // mixins or element attribute
        // 2. If the new data is null, it's not a default
        // component and the component it's not defined via
        // mixins
        if (!this.isComponentDefined(name) ||
            newData === null && !isDefault && !isMixedIn) {
          component.remove();
          delete this.components[name];
          return;
        }
        if (typeof newData === 'string') {
          newData = component.parse(newData);
        }
        // Component already initialized. Update component.
        component.updateAttributes(newData);
        return;
      }
      // Component not yet initialized. Initialize component.
      this.initComponent(name);
    }
  },

  /**
   * If `attr` is a component name and `componentAttr` is not defined, removeAttribute removes
   * the entire component from the entity.
   *
   * If `attr` is a component name and `componentAttr` is defined, removeAttribute removes a
   * single attribute from the component.
   *
   * @param {string} attr - Attribute name, which could also be a component name.
   * @param {string} componentAttr - Component attribute name.
   */
  removeAttribute: {
    value: function (attr, componentAttr) {
      var component = components[attr];
      if (component) {
        if (componentAttr) {
          this.setAttribute(attr, componentAttr, undefined);
        } else {
          this.setEntityAttribute(attr, undefined, null);
        }
      }
      HTMLElement.prototype.removeAttribute.call(this, attr);
    }
  },

  /**
   * Deals with updates on entity-specific attributes (i.e., components and mixins).
   *
   * @param {string} attr
   * @param {string} oldVal
   * @param {string|object} newVal
   */
  setEntityAttribute: {
    value: function (attr, oldVal, newVal) {
      var component = components[attr];
      oldVal = oldVal || this.getAttribute(attr);
      // When creating entities programatically and setting attributes, it is not part
      // of the scene until it is inserted into the DOM. This does not apply to scenes as
      // scenes depend on its child entities to load.
      if (!this.hasLoaded && !this.isScene) { return; }
      if (attr === 'mixin') {
        this.updateStateMixins(newVal, oldVal);
        this.updateComponents();
        return;
      }
      if (component) { this.updateComponent(attr, newVal); }
    }
  },

  /**
   * If attribute is a component, setAttribute will apply the value to the
   * existing component data, not replace it. Examples:
   *
   * Examples:
   *
   * setAttribute('id', 'my-element');
   * setAttribute('material', { color: 'crimson' });
   * setAttribute('material', 'color', 'crimson');
   *
   * @param {string} attr - Attribute name. setAttribute will initialize or update
   *        a component if the name corresponds to a registered component.
   * @param {string|object} value - If a string, setAttribute will update the attribute or.
   *        component. If an object, the value will be mixed into the component.
   * @param {string} componentAttrValue - If defined, `value` will act as the attribute
   *        name and setAttribute will only set a single component attribute.
   */
  setAttribute: {
    value: function (attr, value, componentAttrValue) {
      var self = this;
      var component = components[attr];
      var partialComponentData;
      value = value === undefined ? '' : value;
      var valueStr = value;
      var oldValue;
      if (component) {
        if (typeof value === 'string' && componentAttrValue !== undefined) {
          // Update currently-defined component data with the new attribute value.
          partialComponentData = self.getAttribute(attr) || {};
          partialComponentData[value] = componentAttrValue;
          value = partialComponentData;
        }
        valueStr = component.stringify(value);
      }

      oldValue = this.getAttribute(attr);
      ANode.prototype.setAttribute.call(self, attr, valueStr);
      self.setEntityAttribute(attr, oldValue, value);
    },
    writable: window.debug
  },

  /**
   * If `attr` is a component, returns JUST the component data specified in the HTML
   * by parsing the style-like string into an object. Like a partial version of
   * `getComputedAttribute` as returned component data does not include applied mixins or
   * defaults.
   *
   * If `attr` is not a component, fall back to HTML getAttribute.
   *
   * @param {string} attr
   * @returns {object|string} Object if component, else string.
   */
  getAttribute: {
    value: function (attr) {
      var component = components[attr];
      var value = HTMLElement.prototype.getAttribute.call(this, attr);
      if (!component || typeof value !== 'string') { return value; }
      return component.parse(value);
    },
    writable: window.debug
  },

  /**
   * If `attr` is a component, returns ALL component data including applied mixins and
   * defaults.
   *
   * If `attr` is not a component, fall back to HTML getAttribute.
   *
   * @param {string} attr
   * @returns {object|string} Object if component, else string.
   */
  getComputedAttribute: {
    value: function (attr) {
      var component = this.components[attr];
      if (component) { return component.getData(); }
      return HTMLElement.prototype.getAttribute.call(this, attr);
    }
  },

  addState: {
    value: function (state) {
      if (this.is(state)) { return; }
      this.states.push(state);
      this.mapStateMixins(state, this.registerMixin.bind(this));
      this.emit('stateadded', {state: state});
    }
  },

  removeState: {
    value: function (state) {
      var stateIndex = this.is(state);
      if (stateIndex === false) { return; }
      this.states.splice(stateIndex, 1);
      this.mapStateMixins(state, this.unregisterMixin.bind(this));
      this.emit('stateremoved', {state: state});
    }
  },

  /**
   * Checks if the element is in a given state. e.g. el.is('alive');
   * @type {string} state - Name of the state we want to check
   */
  is: {
    value: function (state) {
      var is = false;
      this.states.forEach(function (elState, index) {
        if (elState === state) { is = index; }
      });
      return is;
    }
  }
};

module.exports = registerElement('a-entity', {
  prototype: Object.create(ANode.prototype, proto)
});
<commit_msg>use instanceof to check for child entities<commit_after>/* global HTMLElement */
var ANode = require('./a-node');
var components = require('./component').components;
var debug = require('../utils/debug');
var re = require('./a-register-element');
var THREE = require('../../lib/three');

var isNode = re.isNode;
var log = debug('core:a-entity');
var error = debug('core:a-entity:error');
var registerElement = re.registerElement;

var AEntity;

/**
 * Entity element definition.
 * Entities represent all elements that are part of the scene, and always have
 * a position, rotation, and scale.
 * In the entity-component system, entities are just a container of components.
 *
 * For convenience of inheriting components, the scene element inherits from
 * this prototype. When necessary, it differentiates itself by setting
 * `this.isScene`.
 *
 * @namespace Entity
 * @member {object} components - entity's currently initialized components.
 * @member {object} object3D - three.js object.
 * @member {array} states
 */
var proto = Object.create(ANode.prototype, {
  defaults: {
    value: {
      position: '',
      rotation: '',
      scale: '',
      visible: ''
    }
  },

  createdCallback: {
    value: function () {
      this.isEntity = true;
      this.states = [];
      this.components = {};
      this.object3D = new THREE.Mesh();
    }
  },

  attributeChangedCallback: {
    value: function (attr, oldVal, newVal) {
      this.setEntityAttribute(attr, oldVal, newVal);
    }
  },

  attachedCallback: {
    value: function () {
      this.addToParent();
      if (!this.isScene) {
        this.load();
      }
    }
  },

  /**
   * Tell parent to remove this element's object3D from its object3D.
   * Do not call on scene element because that will cause a call to
   * document.body.remove().
   */
  detachedCallback: {
    value: function () {
      if (!this.parentEl || this.isScene) { return; }
      this.removeComponents();
      this.parentEl.remove(this);
    }
  },

  applyMixin: {
    value: function (attr) {
      var attrValue = this.getAttribute(attr);
      if (!attr) {
        this.updateComponents();
        return;
      }
      this.updateComponent(attr, attrValue);
    }
  },

  mapStateMixins: {
    value: function (state, op) {
      var mixins = this.getAttribute('mixin');
      var mixinIds;
      if (!mixins) { return; }
      mixinIds = mixins.split(' ');
      mixinIds.forEach(function (id) {
        var mixinId = id + '-' + state;
        op(mixinId);
      });
      this.updateComponents();
    }
  },

  updateStateMixins: {
    value: function (newMixins, oldMixins) {
      var self = this;
      oldMixins = oldMixins || '';
      var newMixinsIds = newMixins.split(' ');
      var oldMixinsIds = oldMixins ? oldMixins.split(' ') : [];
      // The list of mixins that might have been removed on update
      var diff = oldMixinsIds.filter(function (i) { return newMixinsIds.indexOf(i) < 0; });
      // Remove the mixins that are gone on update
      diff.forEach(function (mixinId) {
        var forEach = Array.prototype.forEach;
        // State Mixins
        var stateMixinsEls = document.querySelectorAll('[id^=' + mixinId + '-]');
        var stateMixinIds = [];
        forEach.call(stateMixinsEls, function (el) { stateMixinIds.push(el.id); });
        stateMixinIds.forEach(self.unregisterMixin.bind(self));
      });
      this.states.forEach(function (state) {
        newMixinsIds.forEach(function (id) {
          var mixinId = id + '-' + state;
          self.registerMixin(mixinId);
        });
      });
    }
  },

  add: {
    value: function (el) {
      if (!el.object3D) {
        error("Trying to add an object3D that doesn't exist");
      }
      this.object3D.add(el.object3D);
    }
  },

  addToParent: {
    value: function () {
      var self = this;
      var parent = this.parentEl = this.parentNode;
      var attachedToParent = this.attachedToParent;
      if (!parent || attachedToParent) { return; }
      if (isNode(parent)) {
        attach();
        return;
      }
      // If the parent isn't an `ANode` but eventually it will be
      // when a templated element is created, we want to attach
      // this element to the parent then.
      parent.addEventListener('nodeready', attach);
      function attach () {
        // To prevent an object to attach itself multiple times to the parent.
        self.attachedToParent = true;
        if (parent.add) {
          parent.add(self);
        }
      }
    }
  },

  load: {
    value: function () {
      if (this.hasLoaded) { return; }
      this.object3D.el = this;

      // Attach to parent object3D.
      this.addToParent();

      if (this.isScene) {
        ANode.prototype.load.call(this, this.updateComponents.bind(this));
      } else {
        ANode.prototype.load.call(this, this.updateComponents.bind(this),
                                  function (el) { return el.isEntity; });
      }
    },
    writable: window.debug
  },

  remove: {
    value: function (el) {
      this.object3D.remove(el.object3D);
    }
  },

  /**
   * @returns {array} Direct children that are entities.
   */
  getChildEntities: {
    value: function () {
      var children = this.children;
      var childEntities = [];

      for (var i = 0; i < this.children.length; i++) {
        var child = children[i];
        if (child instanceof AEntity) {
          childEntities.push(child);
        }
      }

      return childEntities;
    }
  },

  /**
   * Check if a component is defined for an entity, including defaults and mixins.
   *
   * @param {string} name - Component name.
   */
  isComponentDefined: {
    value: function (name) {
      // If the defaults contain the component
      var inDefaults = this.defaults[name];
      // If the element contains the component
      var inAttribute = this.hasAttribute(name);
      if (inDefaults !== undefined || inAttribute) { return true; }
      return this.isComponentMixedIn(name);
    }
  },

  isComponentMixedIn: {
    value: function (name) {
      var i;
      var inMixin = false;
      var mixinEls = this.mixinEls;
     // If any of the mixins contains the component
      for (i = 0; i < mixinEls.length; ++i) {
        inMixin = mixinEls[i].hasAttribute(name);
        if (inMixin) { break; }
      }
      return inMixin;
    }
  },

  initComponent: {
    value: function (name, isDependency) {
      var isComponentDefined;
      // If it's not a component name or
      // If the component is already initialized
      if (!components[name] || this.components[name]) { return; }
      isComponentDefined = this.isComponentDefined(name);
      // If the component is not defined for the element
      if (!isComponentDefined && !isDependency) { return; }
      this.initComponentDependencies(name);
      // If a component it's a dependency of another but it's not defined
      // on the attribute, mixins or entity defaults
      // we have to add it.
      if (isDependency && !isComponentDefined) {
        this.setAttribute(name, '');
      } else {
        this.components[name] = new components[name].Component(this);
      }
      log('Component initialized: %s', name);
    }
  },

  initComponentDependencies: {
    value: function (name) {
      var self = this;
      var component = components[name];
      var dependencies;
      // If the component doesn't exist
      if (!component) { return; }
      dependencies = components[name].dependencies;
      if (!dependencies) { return; }
      dependencies.forEach(function (component) {
        self.initComponent(component, true);
      });
    }
  },

  removeComponents: {
    value: function () {
      var self = this;
      var entityComponents = Object.keys(this.components);
      entityComponents.forEach(removeComponent);
      function removeComponent (name) {
        self.components[name].remove();
        delete self.components[name];
      }
    }
  },

  updateComponents: {
    value: function () {
      var self = this;
      var entityComponents = Object.keys(components);
      // Updates components
      entityComponents.forEach(updateComponent);
      function updateComponent (name) {
        var elValue = self.getAttribute(name);
        self.updateComponent(name, elValue);
      }
    }
  },

  /**
   * Initialize, update, or remove a single component.
   *
   * When initializing, we set the component on `this.components`.
   *
   * @param {string} name - Component name.
   * @param {object} newData - The new attributes assigned to the component
   */
  updateComponent: {
    value: function (name, newData) {
      var component = this.components[name];
      var isDefault = name in this.defaults;
      var isMixedIn = this.isComponentMixedIn(name);
      if (component) {
        // Attribute was removed. Remove component.
        // 1. If the component is not defined in the defaults,
        // mixins or element attribute
        // 2. If the new data is null, it's not a default
        // component and the component it's not defined via
        // mixins
        if (!this.isComponentDefined(name) ||
            newData === null && !isDefault && !isMixedIn) {
          component.remove();
          delete this.components[name];
          return;
        }
        if (typeof newData === 'string') {
          newData = component.parse(newData);
        }
        // Component already initialized. Update component.
        component.updateAttributes(newData);
        return;
      }
      // Component not yet initialized. Initialize component.
      this.initComponent(name);
    }
  },

  /**
   * If `attr` is a component name and `componentAttr` is not defined, removeAttribute removes
   * the entire component from the entity.
   *
   * If `attr` is a component name and `componentAttr` is defined, removeAttribute removes a
   * single attribute from the component.
   *
   * @param {string} attr - Attribute name, which could also be a component name.
   * @param {string} componentAttr - Component attribute name.
   */
  removeAttribute: {
    value: function (attr, componentAttr) {
      var component = components[attr];
      if (component) {
        if (componentAttr) {
          this.setAttribute(attr, componentAttr, undefined);
        } else {
          this.setEntityAttribute(attr, undefined, null);
        }
      }
      HTMLElement.prototype.removeAttribute.call(this, attr);
    }
  },

  /**
   * Deals with updates on entity-specific attributes (i.e., components and mixins).
   *
   * @param {string} attr
   * @param {string} oldVal
   * @param {string|object} newVal
   */
  setEntityAttribute: {
    value: function (attr, oldVal, newVal) {
      var component = components[attr];
      oldVal = oldVal || this.getAttribute(attr);
      // When creating entities programatically and setting attributes, it is not part
      // of the scene until it is inserted into the DOM. This does not apply to scenes as
      // scenes depend on its child entities to load.
      if (!this.hasLoaded && !this.isScene) { return; }
      if (attr === 'mixin') {
        this.updateStateMixins(newVal, oldVal);
        this.updateComponents();
        return;
      }
      if (component) { this.updateComponent(attr, newVal); }
    }
  },

  /**
   * If attribute is a component, setAttribute will apply the value to the
   * existing component data, not replace it. Examples:
   *
   * Examples:
   *
   * setAttribute('id', 'my-element');
   * setAttribute('material', { color: 'crimson' });
   * setAttribute('material', 'color', 'crimson');
   *
   * @param {string} attr - Attribute name. setAttribute will initialize or update
   *        a component if the name corresponds to a registered component.
   * @param {string|object} value - If a string, setAttribute will update the attribute or.
   *        component. If an object, the value will be mixed into the component.
   * @param {string} componentAttrValue - If defined, `value` will act as the attribute
   *        name and setAttribute will only set a single component attribute.
   */
  setAttribute: {
    value: function (attr, value, componentAttrValue) {
      var self = this;
      var component = components[attr];
      var partialComponentData;
      value = value === undefined ? '' : value;
      var valueStr = value;
      var oldValue;
      if (component) {
        if (typeof value === 'string' && componentAttrValue !== undefined) {
          // Update currently-defined component data with the new attribute value.
          partialComponentData = self.getAttribute(attr) || {};
          partialComponentData[value] = componentAttrValue;
          value = partialComponentData;
        }
        valueStr = component.stringify(value);
      }

      oldValue = this.getAttribute(attr);
      ANode.prototype.setAttribute.call(self, attr, valueStr);
      self.setEntityAttribute(attr, oldValue, value);
    },
    writable: window.debug
  },

  /**
   * If `attr` is a component, returns JUST the component data specified in the HTML
   * by parsing the style-like string into an object. Like a partial version of
   * `getComputedAttribute` as returned component data does not include applied mixins or
   * defaults.
   *
   * If `attr` is not a component, fall back to HTML getAttribute.
   *
   * @param {string} attr
   * @returns {object|string} Object if component, else string.
   */
  getAttribute: {
    value: function (attr) {
      var component = components[attr];
      var value = HTMLElement.prototype.getAttribute.call(this, attr);
      if (!component || typeof value !== 'string') { return value; }
      return component.parse(value);
    },
    writable: window.debug
  },

  /**
   * If `attr` is a component, returns ALL component data including applied mixins and
   * defaults.
   *
   * If `attr` is not a component, fall back to HTML getAttribute.
   *
   * @param {string} attr
   * @returns {object|string} Object if component, else string.
   */
  getComputedAttribute: {
    value: function (attr) {
      var component = this.components[attr];
      if (component) { return component.getData(); }
      return HTMLElement.prototype.getAttribute.call(this, attr);
    }
  },

  addState: {
    value: function (state) {
      if (this.is(state)) { return; }
      this.states.push(state);
      this.mapStateMixins(state, this.registerMixin.bind(this));
      this.emit('stateadded', {state: state});
    }
  },

  removeState: {
    value: function (state) {
      var stateIndex = this.is(state);
      if (stateIndex === false) { return; }
      this.states.splice(stateIndex, 1);
      this.mapStateMixins(state, this.unregisterMixin.bind(this));
      this.emit('stateremoved', {state: state});
    }
  },

  /**
   * Checks if the element is in a given state. e.g. el.is('alive');
   * @type {string} state - Name of the state we want to check
   */
  is: {
    value: function (state) {
      var is = false;
      this.states.forEach(function (elState, index) {
        if (elState === state) { is = index; }
      });
      return is;
    }
  }
});

AEntity = registerElement('a-entity', {
  prototype: proto
});
module.exports = AEntity;
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Sat Jul 04 2015 11:53:54 GMT+0200 (CEST)

module.exports = function(config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine', 'requirejs'],

        // list of files / patterns to load in the browser
        files: [{
            pattern: 'node_modules/jquery/dist/jquery.min.js',
            included: false
        }, {
            pattern: 'xml/subscription_manager.xml',
            included: false
        }, {
            pattern: 'js/**/*.js',
            included: false
        }, {
            pattern: 'js/*.js',
            included: false
        }, {
            pattern: 'tests/**/*Spec.js',
            included: false
        }, 'tests/test-main.js'],

        // list of files to exclude
        exclude: [
            'js/main.js'
        ],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {},

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['spec'],

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome', 'Firefox'],

        // If browser does not capture in given timeout [ms], kill it
        captureTimeout: 60000,

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false
    });
};
<commit_msg>Added jasmine-ajax to frameworks<commit_after>// Karma configuration
// Generated on Sat Jul 04 2015 11:53:54 GMT+0200 (CEST)

module.exports = function(config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine-ajax', 'jasmine', 'requirejs'],

        // list of files / patterns to load in the browser
        files: [{
            pattern: 'node_modules/jquery/dist/jquery.min.js',
            included: false
        }, {
            pattern: 'xml/subscription_manager.xml',
            included: false
        }, {
            pattern: 'js/**/*.js',
            included: false
        }, {
            pattern: 'js/*.js',
            included: false
        }, {
            pattern: 'tests/**/*Spec.js',
            included: false
        }, 'tests/test-main.js'],

        // list of files to exclude
        exclude: [
            'js/main.js'
        ],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {},

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['spec'],

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['Chrome', 'Firefox'],

        // If browser does not capture in given timeout [ms], kill it
        captureTimeout: 60000,

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false
    });
};
<|endoftext|>


<commit_before>/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule RelayStoreData
 * @flow
 * @typechecks
 */

'use strict';

const GraphQLQueryRunner = require('GraphQLQueryRunner');
const GraphQLStoreChangeEmitter = require('GraphQLStoreChangeEmitter');
const GraphQLStoreDataHandler = require('GraphQLStoreDataHandler');
const GraphQLStoreRangeUtils = require('GraphQLStoreRangeUtils');
const RelayChangeTracker = require('RelayChangeTracker');
import type {ChangeSet} from 'RelayChangeTracker';
const RelayConnectionInterface = require('RelayConnectionInterface');
import type {GarbageCollectionScheduler} from 'RelayGarbageCollector';
const RelayGarbageCollector = require('RelayGarbageCollector');
const RelayMutationQueue = require('RelayMutationQueue');
import type {
  ClientMutationID,
  DataID,
  NodeRangeMap,
  Records,
  RelayQuerySet,
  RootCallMap,
  UpdateOptions,
} from 'RelayInternalTypes';
const RelayNodeInterface = require('RelayNodeInterface');
const RelayPendingQueryTracker = require('RelayPendingQueryTracker');
const RelayProfiler = require('RelayProfiler');
const RelayQuery = require('RelayQuery');
const RelayQueryTracker = require('RelayQueryTracker');
const RelayQueryWriter = require('RelayQueryWriter');
const RelayRecordStore = require('RelayRecordStore');
import type {CacheManager, CacheReadCallbacks} from 'RelayTypes';

const forEachObject = require('forEachObject');
const invariant = require('invariant');
const generateForceIndex = require('generateForceIndex');
const readRelayDiskCache = require('readRelayDiskCache');
const warning = require('warning');
const writeRelayQueryPayload = require('writeRelayQueryPayload');
const writeRelayUpdatePayload = require('writeRelayUpdatePayload');

var {CLIENT_MUTATION_ID} = RelayConnectionInterface;
var {NODE_TYPE} = RelayNodeInterface;

// The source of truth for application data.
var _instance;

/**
 * @internal
 *
 * Wraps the data caches and associated metadata tracking objects used by
 * GraphQLStore/RelayStore.
 */
class RelayStoreData {
  _cacheManager: ?CacheManager;
  _cachedRecords: Records;
  _cachedRootCallMap: RootCallMap;
  _cachedStore: RelayRecordStore;
  _changeEmitter: GraphQLStoreChangeEmitter;
  _garbageCollector: ?RelayGarbageCollector;
  _mutationQueue: RelayMutationQueue;
  _nodeRangeMap: NodeRangeMap;
  _pendingQueryTracker: RelayPendingQueryTracker;
  _records: Records;
  _queuedRecords: Records;
  _queuedStore: RelayRecordStore;
  _recordStore: RelayRecordStore;
  _queryTracker: RelayQueryTracker;
  _queryRunner: GraphQLQueryRunner;
  _rangeData: GraphQLStoreRangeUtils;
  _rootCallMap: RootCallMap;

  /**
   * Get the data set backing actual Relay operations. Used in GraphQLStore.
   */
  static getDefaultInstance(): RelayStoreData {
    if (!_instance) {
      _instance = new RelayStoreData();
    }
    return _instance;
  }

  constructor() {
    const cachedRecords: Records = ({}: $FixMe);
    const cachedRootCallMap: RootCallMap = {};
    const queuedRecords: Records = ({}: $FixMe);
    const records: Records = ({}: $FixMe);
    const rootCallMap: RootCallMap = {};
    const nodeRangeMap: NodeRangeMap = ({}: $FixMe);
    const {
      cachedStore,
      queuedStore,
      recordStore,
    } = createRecordCollection({
      cachedRecords,
      cachedRootCallMap,
      cacheWriter: null,
      queuedRecords,
      nodeRangeMap,
      records,
      rootCallMap,
    });
    const rangeData = new GraphQLStoreRangeUtils();

    this._cacheManager = null;
    this._cachedRecords = cachedRecords;
    this._cachedRootCallMap = cachedRootCallMap;
    this._cachedStore = cachedStore;
    this._changeEmitter = new GraphQLStoreChangeEmitter(rangeData);
    this._mutationQueue = new RelayMutationQueue(this);
    this._nodeRangeMap = nodeRangeMap;
    this._pendingQueryTracker = new RelayPendingQueryTracker(this);
    this._queryRunner = new GraphQLQueryRunner(this);
    this._queryTracker = new RelayQueryTracker();
    this._queuedRecords = queuedRecords;
    this._queuedStore = queuedStore;
    this._records = records;
    this._recordStore = recordStore;
    this._rangeData = rangeData;
    this._rootCallMap = rootCallMap;
  }

  /**
   * Creates a garbage collector for this instance. After initialization all
   * newly added DataIDs will be registered in the created garbage collector.
   * This will show a warning if data has already been added to the instance.
   */
  initializeGarbageCollector(scheduler: GarbageCollectionScheduler): void {
    invariant(
      !this._garbageCollector,
      'RelayStoreData: Garbage collector is already initialized.'
    );
    var shouldInitialize = this._isStoreDataEmpty();
    warning(
      shouldInitialize,
      'RelayStoreData: Garbage collection can only be initialized when no ' +
      'data is present.'
    );
    if (shouldInitialize) {
      this._garbageCollector = new RelayGarbageCollector(this, scheduler);
    }
  }

  /**
   * Sets/clears the cache manager that is used to cache changes written to
   * the store.
   */
  injectCacheManager(cacheManager: ?CacheManager): void {
    const {
      cachedStore,
      queuedStore,
      recordStore,
    } = createRecordCollection({
      cachedRecords: this._cachedRecords,
      cachedRootCallMap: this._cachedRootCallMap,
      cacheWriter: cacheManager ? cacheManager.getQueryWriter() : null,
      queuedRecords: this._queuedRecords,
      nodeRangeMap: this._nodeRangeMap,
      records: this._records,
      rootCallMap: this._rootCallMap,
    });

    this._cacheManager = cacheManager;
    this._cachedStore = cachedStore;
    this._queuedStore = queuedStore;
    this._recordStore = recordStore;
  }

  clearCacheManager(): void {
    const {
      cachedStore,
      queuedStore,
      recordStore,
    } = createRecordCollection({
      cachedRecords: this._cachedRecords,
      cachedRootCallMap: this._cachedRootCallMap,
      cacheWriter: null,
      queuedRecords: this._queuedRecords,
      nodeRangeMap: this._nodeRangeMap,
      records: this._records,
      rootCallMap: this._rootCallMap,
    });

    this._cacheManager = null;
    this._cachedStore = cachedStore;
    this._queuedStore = queuedStore;
    this._recordStore = recordStore;
  }

  hasCacheManager(): boolean {
    return !!this._cacheManager;
  }

  /**
   * Returns whether a given record is affected by an optimistic update.
   */
  hasOptimisticUpdate(dataID: DataID): boolean {
    dataID = this.getRangeData().getCanonicalClientID(dataID);
    return this.getQueuedStore().hasOptimisticUpdate(dataID);
  }

  /**
   * Returns a list of client mutation IDs for queued mutations whose optimistic
   * updates are affecting the record corresponding the given dataID. Returns
   * null if the record isn't affected by any optimistic updates.
   */
  getClientMutationIDs(dataID: DataID): ?Array<ClientMutationID> {
    dataID = this.getRangeData().getCanonicalClientID(dataID);
    return this.getQueuedStore().getClientMutationIDs(dataID);
  }

  /**
   * Reads data for queries incrementally from disk cache.
   * It calls onSuccess when all the data has been loaded into memory.
   * It calls onFailure when some data is unabled to be satisfied from disk.
   */
  readFromDiskCache(
    queries: RelayQuerySet,
    callbacks: CacheReadCallbacks
  ): void {
    var cacheManager = this._cacheManager;
    invariant(
      cacheManager,
      'RelayStoreData: `readFromDiskCache` should only be called when cache ' +
      'manager is available.'
    );
    var profile = RelayProfiler.profile('RelayStoreData.readFromDiskCache');
    readRelayDiskCache(
      queries,
      this._queuedStore,
      this._cachedRecords,
      this._cachedRootCallMap,
      cacheManager,
      {
        onSuccess: () => {
          profile.stop();
          callbacks.onSuccess && callbacks.onSuccess();
        },
        onFailure: () => {
          profile.stop();
          callbacks.onFailure && callbacks.onFailure();
        },
      }
    );
  }

  /**
   * Write the results of a query into the base record store.
   */
  handleQueryPayload(
    query: RelayQuery.Root,
    response: {[key: string]: mixed},
    forceIndex: ?number
  ): void {
    var profiler = RelayProfiler.profile('RelayStoreData.handleQueryPayload');
    var changeTracker = new RelayChangeTracker();
    var writer = new RelayQueryWriter(
      this._cachedStore,
      this._queryTracker,
      changeTracker,
      {
        forceIndex,
        updateTrackedQueries: true,
      }
    );
    writeRelayQueryPayload(
      writer,
      query,
      response
    );
    this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
    profiler.stop();
  }

  /**
   * Write the results of an update into the base record store.
   */
  handleUpdatePayload(
    operation: RelayQuery.Operation,
    payload: {[key: string]: mixed},
    {configs, isOptimisticUpdate}: UpdateOptions
  ): void {
    var profiler = RelayProfiler.profile('RelayStoreData.handleUpdatePayload');
    var changeTracker = new RelayChangeTracker();
    var store;
    if (isOptimisticUpdate) {
      var clientMutationID = payload[CLIENT_MUTATION_ID];
      invariant(
        typeof clientMutationID === 'string',
        'RelayStoreData.handleUpdatePayload(): Expected optimistic payload ' +
        'to have a valid `%s`.',
        CLIENT_MUTATION_ID
      );
      store = this.getRecordStoreForOptimisticMutation(clientMutationID);
    } else {
      store = this._getRecordStoreForMutation();
    }
    var writer = new RelayQueryWriter(
      store,
      this._queryTracker,
      changeTracker,
      {
        forceIndex: generateForceIndex(),
        isOptimisticUpdate,
        updateTrackedQueries: false,
      }
    );
    writeRelayUpdatePayload(
      writer,
      operation,
      payload,
      {configs, isOptimisticUpdate}
    );
    this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
    profiler.stop();
  }

  /**
   * Given a query fragment and a data ID, returns a root query that applies
   * the fragment to the object specified by the data ID.
   */
  buildFragmentQueryForDataID(
    fragment: RelayQuery.Fragment,
    dataID: DataID
  ): RelayQuery.Root {
    if (GraphQLStoreDataHandler.isClientID(dataID)) {
      const path = this._queuedStore.getPathToRecord(dataID);
      invariant(
        path,
        'RelayStoreData.buildFragmentQueryForDataID(): Cannot refetch ' +
        'record `%s` without a path.',
        dataID
      );
      return path.getQuery(fragment);
    }
    // Fragment fields cannot be spread directly into the root because they
    // may not exist on the `Node` type.
    return RelayQuery.Root.build(
      fragment.getDebugName() || 'UnknownQuery',
      RelayNodeInterface.NODE,
      dataID,
      [fragment],
      {identifyingArgName: RelayNodeInterface.ID},
      NODE_TYPE
    );
  }

  getNodeData(): Records {
    return this._records;
  }

  getQueuedData(): Records {
    return this._queuedRecords;
  }

  clearQueuedData(): void {
    forEachObject(this._queuedRecords, (_, key) => {
      delete this._queuedRecords[key];
      this._changeEmitter.broadcastChangeForID(key);
    });
  }

  getCachedData(): Records {
    return this._cachedRecords;
  }

  getGarbageCollector(): ?RelayGarbageCollector {
    return this._garbageCollector;
  }

  getMutationQueue(): RelayMutationQueue {
    return this._mutationQueue;
  }

  /**
   * Get the record store with only the cached and base data (no queued data).
   */
  getCachedStore(): RelayRecordStore {
    return this._cachedStore;
  }

  /**
   * Get the record store with full data (cached, base, queued).
   */
  getQueuedStore(): RelayRecordStore {
    return this._queuedStore;
  }

  /**
   * Get the record store with only the base data (no queued/cached data).
   */
  getRecordStore(): RelayRecordStore {
    return this._recordStore;
  }

  getQueryTracker(): RelayQueryTracker {
    return this._queryTracker;
  }

  getQueryRunner(): GraphQLQueryRunner {
    return this._queryRunner;
  }

  getChangeEmitter(): GraphQLStoreChangeEmitter {
    return this._changeEmitter;
  }

  getChangeEmitter(): GraphQLStoreChangeEmitter {
    return this._changeEmitter;
  }

  getRangeData(): GraphQLStoreRangeUtils {
    return this._rangeData;
  }

  getPendingQueryTracker(): RelayPendingQueryTracker {
    return this._pendingQueryTracker;
  }

  /**
   * @deprecated
   *
   * Used temporarily by GraphQLStore, but all updates to this object are now
   * handled through a `RelayRecordStore` instance.
   */
  getRootCallData(): RootCallMap {
    return this._rootCallMap;
  }

  _isStoreDataEmpty(): boolean {
    return (
      Object.keys(this._records).length === 0 &&
      Object.keys(this._queuedRecords).length === 0 &&
      Object.keys(this._cachedRecords).length === 0
    );
  }

  /**
   * Given a ChangeSet, broadcasts changes for updated DataIDs
   * and registers new DataIDs with the garbage collector.
   */
  _handleChangedAndNewDataIDs(changeSet: ChangeSet): void {
    var updatedDataIDs = Object.keys(changeSet.updated);
    updatedDataIDs.forEach(id => this._changeEmitter.broadcastChangeForID(id));
    if (this._garbageCollector) {
      var createdDataIDs = Object.keys(changeSet.created);
      var garbageCollector = this._garbageCollector;
      createdDataIDs.forEach(dataID => garbageCollector.register(dataID));
    }
  }

  _getRecordStoreForMutation(): RelayRecordStore {
    var records = this._records;
    var rootCallMap = this._rootCallMap;

    return new RelayRecordStore(
      ({records}: $FixMe),
      ({rootCallMap}: $FixMe),
      (this._nodeRangeMap: $FixMe),
      this._cacheManager ?
        this._cacheManager.getMutationWriter() :
        null
    );
  }

  getRecordStoreForOptimisticMutation(
    clientMutationID: ClientMutationID
  ): RelayRecordStore {
    var cachedRecords = this._cachedRecords;
    var cachedRootCallMap = this._cachedRootCallMap;
    var rootCallMap = this._rootCallMap;
    var queuedRecords = this._queuedRecords;
    var records = this._records;

    return new RelayRecordStore(
      ({cachedRecords, queuedRecords, records}: $FixMe),
      ({cachedRootCallMap, rootCallMap}: $FixMe),
      (this._nodeRangeMap: $FixMe),
      null, // don't cache optimistic data
      clientMutationID
    );
  }
}

function createRecordCollection({
  cachedRecords,
  cachedRootCallMap,
  cacheWriter,
  queuedRecords,
  nodeRangeMap,
  records,
  rootCallMap,
}): {
  cachedStore: RelayRecordStore,
  queuedStore: RelayRecordStore,
  recordStore: RelayRecordStore
} {
  return {
    queuedStore: new RelayRecordStore(
      ({cachedRecords, queuedRecords, records}: $FixMe),
      ({cachedRootCallMap, rootCallMap}: $FixMe),
      (nodeRangeMap: $FixMe)
    ),
    cachedStore: new RelayRecordStore(
      ({cachedRecords, records}: $FixMe),
      ({cachedRootCallMap, rootCallMap}: $FixMe),
      (nodeRangeMap: $FixMe),
      cacheWriter
    ),
    recordStore: new RelayRecordStore(
      ({records}: $FixMe),
      ({rootCallMap}: $FixMe),
      (nodeRangeMap: $FixMe),
      cacheWriter
    ),
  };
}

RelayProfiler.instrumentMethods(RelayStoreData.prototype, {
  handleQueryPayload: 'RelayStoreData.prototype.handleQueryPayload',
  handleUpdatePayload: 'RelayStoreData.prototype.handleUpdatePayload',
});

module.exports = RelayStoreData;
<commit_msg>fix eslint/no-dupe-{keys,class-members} violators<commit_after>/**
 * Copyright 2013-2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule RelayStoreData
 * @flow
 * @typechecks
 */

'use strict';

const GraphQLQueryRunner = require('GraphQLQueryRunner');
const GraphQLStoreChangeEmitter = require('GraphQLStoreChangeEmitter');
const GraphQLStoreDataHandler = require('GraphQLStoreDataHandler');
const GraphQLStoreRangeUtils = require('GraphQLStoreRangeUtils');
const RelayChangeTracker = require('RelayChangeTracker');
import type {ChangeSet} from 'RelayChangeTracker';
const RelayConnectionInterface = require('RelayConnectionInterface');
import type {GarbageCollectionScheduler} from 'RelayGarbageCollector';
const RelayGarbageCollector = require('RelayGarbageCollector');
const RelayMutationQueue = require('RelayMutationQueue');
import type {
  ClientMutationID,
  DataID,
  NodeRangeMap,
  Records,
  RelayQuerySet,
  RootCallMap,
  UpdateOptions,
} from 'RelayInternalTypes';
const RelayNodeInterface = require('RelayNodeInterface');
const RelayPendingQueryTracker = require('RelayPendingQueryTracker');
const RelayProfiler = require('RelayProfiler');
const RelayQuery = require('RelayQuery');
const RelayQueryTracker = require('RelayQueryTracker');
const RelayQueryWriter = require('RelayQueryWriter');
const RelayRecordStore = require('RelayRecordStore');
import type {CacheManager, CacheReadCallbacks} from 'RelayTypes';

const forEachObject = require('forEachObject');
const invariant = require('invariant');
const generateForceIndex = require('generateForceIndex');
const readRelayDiskCache = require('readRelayDiskCache');
const warning = require('warning');
const writeRelayQueryPayload = require('writeRelayQueryPayload');
const writeRelayUpdatePayload = require('writeRelayUpdatePayload');

var {CLIENT_MUTATION_ID} = RelayConnectionInterface;
var {NODE_TYPE} = RelayNodeInterface;

// The source of truth for application data.
var _instance;

/**
 * @internal
 *
 * Wraps the data caches and associated metadata tracking objects used by
 * GraphQLStore/RelayStore.
 */
class RelayStoreData {
  _cacheManager: ?CacheManager;
  _cachedRecords: Records;
  _cachedRootCallMap: RootCallMap;
  _cachedStore: RelayRecordStore;
  _changeEmitter: GraphQLStoreChangeEmitter;
  _garbageCollector: ?RelayGarbageCollector;
  _mutationQueue: RelayMutationQueue;
  _nodeRangeMap: NodeRangeMap;
  _pendingQueryTracker: RelayPendingQueryTracker;
  _records: Records;
  _queuedRecords: Records;
  _queuedStore: RelayRecordStore;
  _recordStore: RelayRecordStore;
  _queryTracker: RelayQueryTracker;
  _queryRunner: GraphQLQueryRunner;
  _rangeData: GraphQLStoreRangeUtils;
  _rootCallMap: RootCallMap;

  /**
   * Get the data set backing actual Relay operations. Used in GraphQLStore.
   */
  static getDefaultInstance(): RelayStoreData {
    if (!_instance) {
      _instance = new RelayStoreData();
    }
    return _instance;
  }

  constructor() {
    const cachedRecords: Records = ({}: $FixMe);
    const cachedRootCallMap: RootCallMap = {};
    const queuedRecords: Records = ({}: $FixMe);
    const records: Records = ({}: $FixMe);
    const rootCallMap: RootCallMap = {};
    const nodeRangeMap: NodeRangeMap = ({}: $FixMe);
    const {
      cachedStore,
      queuedStore,
      recordStore,
    } = createRecordCollection({
      cachedRecords,
      cachedRootCallMap,
      cacheWriter: null,
      queuedRecords,
      nodeRangeMap,
      records,
      rootCallMap,
    });
    const rangeData = new GraphQLStoreRangeUtils();

    this._cacheManager = null;
    this._cachedRecords = cachedRecords;
    this._cachedRootCallMap = cachedRootCallMap;
    this._cachedStore = cachedStore;
    this._changeEmitter = new GraphQLStoreChangeEmitter(rangeData);
    this._mutationQueue = new RelayMutationQueue(this);
    this._nodeRangeMap = nodeRangeMap;
    this._pendingQueryTracker = new RelayPendingQueryTracker(this);
    this._queryRunner = new GraphQLQueryRunner(this);
    this._queryTracker = new RelayQueryTracker();
    this._queuedRecords = queuedRecords;
    this._queuedStore = queuedStore;
    this._records = records;
    this._recordStore = recordStore;
    this._rangeData = rangeData;
    this._rootCallMap = rootCallMap;
  }

  /**
   * Creates a garbage collector for this instance. After initialization all
   * newly added DataIDs will be registered in the created garbage collector.
   * This will show a warning if data has already been added to the instance.
   */
  initializeGarbageCollector(scheduler: GarbageCollectionScheduler): void {
    invariant(
      !this._garbageCollector,
      'RelayStoreData: Garbage collector is already initialized.'
    );
    var shouldInitialize = this._isStoreDataEmpty();
    warning(
      shouldInitialize,
      'RelayStoreData: Garbage collection can only be initialized when no ' +
      'data is present.'
    );
    if (shouldInitialize) {
      this._garbageCollector = new RelayGarbageCollector(this, scheduler);
    }
  }

  /**
   * Sets/clears the cache manager that is used to cache changes written to
   * the store.
   */
  injectCacheManager(cacheManager: ?CacheManager): void {
    const {
      cachedStore,
      queuedStore,
      recordStore,
    } = createRecordCollection({
      cachedRecords: this._cachedRecords,
      cachedRootCallMap: this._cachedRootCallMap,
      cacheWriter: cacheManager ? cacheManager.getQueryWriter() : null,
      queuedRecords: this._queuedRecords,
      nodeRangeMap: this._nodeRangeMap,
      records: this._records,
      rootCallMap: this._rootCallMap,
    });

    this._cacheManager = cacheManager;
    this._cachedStore = cachedStore;
    this._queuedStore = queuedStore;
    this._recordStore = recordStore;
  }

  clearCacheManager(): void {
    const {
      cachedStore,
      queuedStore,
      recordStore,
    } = createRecordCollection({
      cachedRecords: this._cachedRecords,
      cachedRootCallMap: this._cachedRootCallMap,
      cacheWriter: null,
      queuedRecords: this._queuedRecords,
      nodeRangeMap: this._nodeRangeMap,
      records: this._records,
      rootCallMap: this._rootCallMap,
    });

    this._cacheManager = null;
    this._cachedStore = cachedStore;
    this._queuedStore = queuedStore;
    this._recordStore = recordStore;
  }

  hasCacheManager(): boolean {
    return !!this._cacheManager;
  }

  /**
   * Returns whether a given record is affected by an optimistic update.
   */
  hasOptimisticUpdate(dataID: DataID): boolean {
    dataID = this.getRangeData().getCanonicalClientID(dataID);
    return this.getQueuedStore().hasOptimisticUpdate(dataID);
  }

  /**
   * Returns a list of client mutation IDs for queued mutations whose optimistic
   * updates are affecting the record corresponding the given dataID. Returns
   * null if the record isn't affected by any optimistic updates.
   */
  getClientMutationIDs(dataID: DataID): ?Array<ClientMutationID> {
    dataID = this.getRangeData().getCanonicalClientID(dataID);
    return this.getQueuedStore().getClientMutationIDs(dataID);
  }

  /**
   * Reads data for queries incrementally from disk cache.
   * It calls onSuccess when all the data has been loaded into memory.
   * It calls onFailure when some data is unabled to be satisfied from disk.
   */
  readFromDiskCache(
    queries: RelayQuerySet,
    callbacks: CacheReadCallbacks
  ): void {
    var cacheManager = this._cacheManager;
    invariant(
      cacheManager,
      'RelayStoreData: `readFromDiskCache` should only be called when cache ' +
      'manager is available.'
    );
    var profile = RelayProfiler.profile('RelayStoreData.readFromDiskCache');
    readRelayDiskCache(
      queries,
      this._queuedStore,
      this._cachedRecords,
      this._cachedRootCallMap,
      cacheManager,
      {
        onSuccess: () => {
          profile.stop();
          callbacks.onSuccess && callbacks.onSuccess();
        },
        onFailure: () => {
          profile.stop();
          callbacks.onFailure && callbacks.onFailure();
        },
      }
    );
  }

  /**
   * Write the results of a query into the base record store.
   */
  handleQueryPayload(
    query: RelayQuery.Root,
    response: {[key: string]: mixed},
    forceIndex: ?number
  ): void {
    var profiler = RelayProfiler.profile('RelayStoreData.handleQueryPayload');
    var changeTracker = new RelayChangeTracker();
    var writer = new RelayQueryWriter(
      this._cachedStore,
      this._queryTracker,
      changeTracker,
      {
        forceIndex,
        updateTrackedQueries: true,
      }
    );
    writeRelayQueryPayload(
      writer,
      query,
      response
    );
    this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
    profiler.stop();
  }

  /**
   * Write the results of an update into the base record store.
   */
  handleUpdatePayload(
    operation: RelayQuery.Operation,
    payload: {[key: string]: mixed},
    {configs, isOptimisticUpdate}: UpdateOptions
  ): void {
    var profiler = RelayProfiler.profile('RelayStoreData.handleUpdatePayload');
    var changeTracker = new RelayChangeTracker();
    var store;
    if (isOptimisticUpdate) {
      var clientMutationID = payload[CLIENT_MUTATION_ID];
      invariant(
        typeof clientMutationID === 'string',
        'RelayStoreData.handleUpdatePayload(): Expected optimistic payload ' +
        'to have a valid `%s`.',
        CLIENT_MUTATION_ID
      );
      store = this.getRecordStoreForOptimisticMutation(clientMutationID);
    } else {
      store = this._getRecordStoreForMutation();
    }
    var writer = new RelayQueryWriter(
      store,
      this._queryTracker,
      changeTracker,
      {
        forceIndex: generateForceIndex(),
        isOptimisticUpdate,
        updateTrackedQueries: false,
      }
    );
    writeRelayUpdatePayload(
      writer,
      operation,
      payload,
      {configs, isOptimisticUpdate}
    );
    this._handleChangedAndNewDataIDs(changeTracker.getChangeSet());
    profiler.stop();
  }

  /**
   * Given a query fragment and a data ID, returns a root query that applies
   * the fragment to the object specified by the data ID.
   */
  buildFragmentQueryForDataID(
    fragment: RelayQuery.Fragment,
    dataID: DataID
  ): RelayQuery.Root {
    if (GraphQLStoreDataHandler.isClientID(dataID)) {
      const path = this._queuedStore.getPathToRecord(dataID);
      invariant(
        path,
        'RelayStoreData.buildFragmentQueryForDataID(): Cannot refetch ' +
        'record `%s` without a path.',
        dataID
      );
      return path.getQuery(fragment);
    }
    // Fragment fields cannot be spread directly into the root because they
    // may not exist on the `Node` type.
    return RelayQuery.Root.build(
      fragment.getDebugName() || 'UnknownQuery',
      RelayNodeInterface.NODE,
      dataID,
      [fragment],
      {identifyingArgName: RelayNodeInterface.ID},
      NODE_TYPE
    );
  }

  getNodeData(): Records {
    return this._records;
  }

  getQueuedData(): Records {
    return this._queuedRecords;
  }

  clearQueuedData(): void {
    forEachObject(this._queuedRecords, (_, key) => {
      delete this._queuedRecords[key];
      this._changeEmitter.broadcastChangeForID(key);
    });
  }

  getCachedData(): Records {
    return this._cachedRecords;
  }

  getGarbageCollector(): ?RelayGarbageCollector {
    return this._garbageCollector;
  }

  getMutationQueue(): RelayMutationQueue {
    return this._mutationQueue;
  }

  /**
   * Get the record store with only the cached and base data (no queued data).
   */
  getCachedStore(): RelayRecordStore {
    return this._cachedStore;
  }

  /**
   * Get the record store with full data (cached, base, queued).
   */
  getQueuedStore(): RelayRecordStore {
    return this._queuedStore;
  }

  /**
   * Get the record store with only the base data (no queued/cached data).
   */
  getRecordStore(): RelayRecordStore {
    return this._recordStore;
  }

  getQueryTracker(): RelayQueryTracker {
    return this._queryTracker;
  }

  getQueryRunner(): GraphQLQueryRunner {
    return this._queryRunner;
  }

  getChangeEmitter(): GraphQLStoreChangeEmitter {
    return this._changeEmitter;
  }

  getRangeData(): GraphQLStoreRangeUtils {
    return this._rangeData;
  }

  getPendingQueryTracker(): RelayPendingQueryTracker {
    return this._pendingQueryTracker;
  }

  /**
   * @deprecated
   *
   * Used temporarily by GraphQLStore, but all updates to this object are now
   * handled through a `RelayRecordStore` instance.
   */
  getRootCallData(): RootCallMap {
    return this._rootCallMap;
  }

  _isStoreDataEmpty(): boolean {
    return (
      Object.keys(this._records).length === 0 &&
      Object.keys(this._queuedRecords).length === 0 &&
      Object.keys(this._cachedRecords).length === 0
    );
  }

  /**
   * Given a ChangeSet, broadcasts changes for updated DataIDs
   * and registers new DataIDs with the garbage collector.
   */
  _handleChangedAndNewDataIDs(changeSet: ChangeSet): void {
    var updatedDataIDs = Object.keys(changeSet.updated);
    updatedDataIDs.forEach(id => this._changeEmitter.broadcastChangeForID(id));
    if (this._garbageCollector) {
      var createdDataIDs = Object.keys(changeSet.created);
      var garbageCollector = this._garbageCollector;
      createdDataIDs.forEach(dataID => garbageCollector.register(dataID));
    }
  }

  _getRecordStoreForMutation(): RelayRecordStore {
    var records = this._records;
    var rootCallMap = this._rootCallMap;

    return new RelayRecordStore(
      ({records}: $FixMe),
      ({rootCallMap}: $FixMe),
      (this._nodeRangeMap: $FixMe),
      this._cacheManager ?
        this._cacheManager.getMutationWriter() :
        null
    );
  }

  getRecordStoreForOptimisticMutation(
    clientMutationID: ClientMutationID
  ): RelayRecordStore {
    var cachedRecords = this._cachedRecords;
    var cachedRootCallMap = this._cachedRootCallMap;
    var rootCallMap = this._rootCallMap;
    var queuedRecords = this._queuedRecords;
    var records = this._records;

    return new RelayRecordStore(
      ({cachedRecords, queuedRecords, records}: $FixMe),
      ({cachedRootCallMap, rootCallMap}: $FixMe),
      (this._nodeRangeMap: $FixMe),
      null, // don't cache optimistic data
      clientMutationID
    );
  }
}

function createRecordCollection({
  cachedRecords,
  cachedRootCallMap,
  cacheWriter,
  queuedRecords,
  nodeRangeMap,
  records,
  rootCallMap,
}): {
  cachedStore: RelayRecordStore,
  queuedStore: RelayRecordStore,
  recordStore: RelayRecordStore
} {
  return {
    queuedStore: new RelayRecordStore(
      ({cachedRecords, queuedRecords, records}: $FixMe),
      ({cachedRootCallMap, rootCallMap}: $FixMe),
      (nodeRangeMap: $FixMe)
    ),
    cachedStore: new RelayRecordStore(
      ({cachedRecords, records}: $FixMe),
      ({cachedRootCallMap, rootCallMap}: $FixMe),
      (nodeRangeMap: $FixMe),
      cacheWriter
    ),
    recordStore: new RelayRecordStore(
      ({records}: $FixMe),
      ({rootCallMap}: $FixMe),
      (nodeRangeMap: $FixMe),
      cacheWriter
    ),
  };
}

RelayProfiler.instrumentMethods(RelayStoreData.prototype, {
  handleQueryPayload: 'RelayStoreData.prototype.handleQueryPayload',
  handleUpdatePayload: 'RelayStoreData.prototype.handleUpdatePayload',
});

module.exports = RelayStoreData;
<|endoftext|>


<commit_before>var ValidationService = require("services/ValidationService");
var ApiService = require("services/ApiService");

Vue.component("guest-login", {

    props: [
        "template"
    ],

    data: function()
    {
        return {
            email: ""
        };
    },

    created: function()
    {
        this.$options.template = this.template;
    },

    methods: {
        validate: function()
        {
            ValidationService.validate($("#guest-login-form-" + this._uid))
                .done(function()
                {
                    this.sendEMail();
                }.bind(this))
                .fail(function(invalidFields)
                {
                    ValidationService.markInvalidFields(invalidFields, "error");
                });
        },

        sendEMail: function()
        {
            ApiService.post("/rest/io/guest", {email: this.email})
                .done(function(response)
                {
                    window.location.href("/checkout");
                });
        }
    }
});
<commit_msg>FIX redirect to checkout after guest login<commit_after>var ValidationService = require("services/ValidationService");
var ApiService = require("services/ApiService");

Vue.component("guest-login", {

    props: [
        "template"
    ],

    data: function()
    {
        return {
            email: ""
        };
    },

    created: function()
    {
        this.$options.template = this.template;
    },

    methods: {
        validate: function()
        {
            ValidationService.validate($("#guest-login-form-" + this._uid))
                .done(function()
                {
                    this.sendEMail();
                }.bind(this))
                .fail(function(invalidFields)
                {
                    ValidationService.markInvalidFields(invalidFields, "error");
                });
        },

        sendEMail: function()
        {
            ApiService.post("/rest/io/guest", {email: this.email})
                .done(function()
                {
                    window.location.href = "/checkout";
                });
        }
    }
});
<|endoftext|>


<commit_before><commit_msg>include draw and setup in default sketch<commit_after>function setup() {

}

function draw() {

}
<|endoftext|>