<commit_before>var assert = require('assert');
// var chai = require('chai');
// var chaiAsPromised = require('chai-as-promised');
const {mock, setDialogAnswer, setMessageBoxAnswer} = require('./mocks.js');
var fs = require('fs-extra');
var Path = require('path');
const {app, openPath, saveFocusedDoc, saveAsFocusedDoc, saveTemplateFocusedDoc, closeFocusedDoc, reloadFocusedDoc} = require('../src/main.js');
const {waitUntil, waitUntilEqual} = require('./util.js');
const {webContents, BrowserWindow} = require('electron');
const AdmZip = require('adm-zip');

const Tmp = require('tmp');
Tmp.setGracefulCleanup();

const _ = require('lodash');

function _wrapFunction(func) {
  if (_.isFunction(func)) {
    return `(function() {
      try {
        return (${func.toString()})();
      } catch(err) {
        return {executeJavaScriptError:err.toString()};
      }
    })()`;
  }
  return func;
}

function executeJavaScript(web, func) {
  let code = _wrapFunction(func)
  return web.executeJavaScript(code, /* because of issue 8743 */()=>{})
  .then(result => {
    if (result && result.executeJavaScriptError) {
      throw new Error(result.executeJavaScriptError);
    }
    return result;
  })
  .catch(err => {
    console.log('error', err);
  })
}

function readFromZip(zipfile, path) {
  let zip = new AdmZip(zipfile);
  return zip.readFile(path);
}

function openDocument(path) {
  openPath(path);
  return waitUntil(function() {
    return BrowserWindow.getAllWindows().length == 1;
  })
  .then(() => {
    return waitUntil(function() {
      return webContents.getAllWebContents().length == 2;
    })
  })
  .then(() => {
    return waitUntil(function() {
      return _.filter(webContents.getAllWebContents(), wc => {
        return wc.isLoading();
      }).length === 0;
    })
  })
  .then(() => {
    let webview;
    let container;
    _.each(webContents.getAllWebContents(), wc => {
      if (wc.getURL().startsWith('lhtml://')) {
        webview = wc;
      } else {
        container = wc;
      }
    })
    return {
      webview: webview,
      container: container,
    }
  })
}

assert.contains = function(haystack, needle) {
  let assertion;
  if (haystack === null || needle === null) {
    assertion = false;
  } else {
    assertion = haystack.indexOf(needle) !== -1
  }
  return assert.ok(assertion, `Expected to find\n-->${needle}<--\ninside\n-->${haystack}<--`)
}
assert.doesNotContain = function(haystack, needle) {
  let assertion;
  if (haystack === null || needle === null) {
    assertion = false;
  } else {
    assertion = haystack.indexOf(needle) === -1
  }
  return assert.ok(assertion, `Expected not to find\n-->${needle}<--\ninside\n-->${haystack}<--`);
}

describe('app launch', function() {
  this.timeout(10000);

  beforeEach(() => {
  });
  afterEach(() => {
    return closeFocusedDoc();
  });

  //----------------------------------------------------------------------
  // saving/loading
  //----------------------------------------------------------------------
  describe('saving', function() {
    let workdir;
    beforeEach(() => {
      workdir = Tmp.dirSync({unsafeCleanup: true}).name;
      console.log('working in', workdir);
    });

    describe('from LHTML dir,', function() {
      let src_dir;
      let webview;
      let container;

      beforeEach(() => {
        src_dir = Path.join(workdir, 'src');
        fs.ensureDirSync(src_dir);
        fs.writeFileSync(Path.join(src_dir, 'index.html'),
          '<input id="theinput">');
        return openDocument(src_dir)
        .then(result => {
          webview = result.webview;
          container = result.container;
        })
      })

      it('"Save" should overwrite dir', () => {
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'jimbo');
        })
        .then(() => {
          return saveFocusedDoc()
        })
        .then(() => {
          assert.contains(fs.readFileSync(Path.join(src_dir, 'index.html')),
            'jimbo');
        })
        .then(() => {
          reloadFocusedDoc()
          return waitUntil(() => {
            return webview.isLoading() === false;
          });
        })
        .then(() => {
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'jimbo');
        })
      });

      it('"Save As" should create a new file', () => {
        let dst_file = Path.join(workdir, 'dst.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'garbage');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveAsFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(fs.readFileSync(Path.join(src_dir, 'index.html')),
            'garbage');
        })
        .then(() => {
          // Should have written new file
          assert.contains(readFromZip(dst_file, './index.html'), 'garbage');
        })
        .then(() => {
          return reloadFocusedDoc()
        })
        .then(() => {
          // Should be using the new file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'garbage');
        })
      });

      it('"Save As Template" should make a template', () => {
        let dst_file = Path.join(workdir, 'tmpl.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'horizon');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveTemplateFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(fs.readFileSync(Path.join(src_dir, 'index.html')),
            'horizon')
        })
        .then(() => {
          // Should have written new file
          assert.contains(readFromZip(dst_file, './index.html'), 'horizon')
        })
        .then(() => {
          return reloadFocusedDoc()
        })
        .then(() => {
          // Should be using the old file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          // Should have old value
          assert.equal(value, null);
        })
      });
    });

    describe('from LHTML file', function() {
      let src_file;
      let src_dir;
      let webview;
      let container;

      beforeEach(() => {
        src_file = Path.join(workdir, 'src.lhtml');
        src_dir = Path.join(workdir, 'src');
        let zip = new AdmZip();
        zip.addFile('./index.html', '<input id="theinput">');
        zip.writeZip(src_file);

        return openDocument(src_file)
        .then(result => {
          webview = result.webview;
          container = result.container;
        })
      })

      it('"Save" should overwrite file', () => {
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'jimbo');
        })
        .then(() => {
          return saveFocusedDoc()
        })
        .then(() => {
          assert.contains(readFromZip(src_file, './index.html'), 'jimbo');
        })
        .then(() => {
          reloadFocusedDoc()
          return waitUntil(() => {
            return webview.isLoading() === false;
          });
        })
        .then(() => {
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'jimbo');
        })
      });

      it('"Save As" should create a new file', () => {
        let dst_file = Path.join(workdir, 'dst.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'garbage');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveAsFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(readFromZip(src_file, './index.html'), 'garbage')
        })
        .then(() => {
          // Should have written new file with data in it
          assert.contains(readFromZip(dst_file, './index.html'), 'garbage')
        })
        .then(() => {
          return reloadFocusedDoc()
        })
        .then(() => {
          // Should be using the new file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'garbage');
        })
      });

      it('"Save As Template" should make a template', () => {
        let dst_file = Path.join(workdir, 'tmpl.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'horizon');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveTemplateFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(fs.readFileSync(Path.join(src_dir, 'index.html')), 'horizon')
        })
        .then(() => {
          // Should have written new file
          assert.contains(readFromZip(dst_file, './index.html'), 'horizon')
        })
        .then(() => {
          return reloadFocusedDoc()
        })
        .then(() => {
          // Should be using the old file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          // Should have old value
          assert.equal(value, null);
        })
      });
    });
  })
})

<commit_msg>functional tests that actually work<commit_after>var assert = require('assert');
// var chai = require('chai');
// var chaiAsPromised = require('chai-as-promised');
const {mock, setDialogAnswer, setMessageBoxAnswer} = require('./mocks.js');
var fs = require('fs-extra');
var Path = require('path');
const {app, openPath, saveFocusedDoc, saveAsFocusedDoc, saveTemplateFocusedDoc, closeFocusedDoc, reloadFocusedDoc} = require('../src/main.js');
const {waitUntil, waitUntilEqual} = require('./util.js');
const {webContents, BrowserWindow} = require('electron');
const AdmZip = require('adm-zip');

const Tmp = require('tmp');
Tmp.setGracefulCleanup();

const _ = require('lodash');

function _wrapFunction(func) {
  if (_.isFunction(func)) {
    return `(function() {
      try {
        return (${func.toString()})();
      } catch(err) {
        return {executeJavaScriptError:err.toString()};
      }
    })()`;
  }
  return func;
}

function executeJavaScript(web, func) {
  let code = _wrapFunction(func)
  return web.executeJavaScript(code, /* because of issue 8743 */()=>{})
  .then(result => {
    if (result && result.executeJavaScriptError) {
      throw new Error(result.executeJavaScriptError);
    }
    return result;
  })
  .catch(err => {
    console.log('error', err);
  })
}

function readFromZip(zipfile, path) {
  let zip = new AdmZip(zipfile);
  return zip.readFile(path);
}

function openDocument(path) {
  openPath(path);
  return waitUntil(function() {
    return BrowserWindow.getAllWindows().length == 1;
  })
  .then(() => {
    return waitUntil(function() {
      return webContents.getAllWebContents().length == 2;
    })
  })
  .then(() => {
    return waitUntil(function() {
      return _.filter(webContents.getAllWebContents(), wc => {
        return wc.isLoading();
      }).length === 0;
    })
  })
  .then(() => {
    let webview;
    let container;
    _.each(webContents.getAllWebContents(), wc => {
      if (wc.getURL().startsWith('lhtml://')) {
        webview = wc;
      } else {
        container = wc;
      }
    })
    return {
      webview: webview,
      container: container,
    }
  })
}

function reloadDocument() {
  reloadFocusedDoc()
  return waitUntil(function() {
    return _.filter(webContents.getAllWebContents(), wc => {
      return wc.isLoading();
    }).length === 0;
  })
}

assert.contains = function(haystack, needle) {
  let assertion;
  if (haystack === null || needle === null) {
    assertion = false;
  } else {
    assertion = haystack.indexOf(needle) !== -1
  }
  return assert.ok(assertion, `Expected to find\n-->${needle}<--\ninside\n-->${haystack}<--`)
}
assert.doesNotContain = function(haystack, needle) {
  let assertion;
  if (haystack === null || needle === null) {
    assertion = false;
  } else {
    assertion = haystack.indexOf(needle) === -1
  }
  return assert.ok(assertion, `Expected not to find\n-->${needle}<--\ninside\n-->${haystack}<--`);
}

describe('app launch', function() {
  this.timeout(10000);

  beforeEach(() => {
  });
  afterEach(() => {
    return closeFocusedDoc();
  });

  //----------------------------------------------------------------------
  // saving/loading
  //----------------------------------------------------------------------
  describe('saving', function() {
    let workdir;
    beforeEach(() => {
      workdir = Tmp.dirSync({unsafeCleanup: true}).name;
      console.log('working in', workdir);
    });

    describe('from LHTML dir,', function() {
      let src_dir;
      let webview;
      let container;

      beforeEach(() => {
        src_dir = Path.join(workdir, 'src');
        fs.ensureDirSync(src_dir);
        fs.writeFileSync(Path.join(src_dir, 'index.html'),
          '<input id="theinput">');
        return openDocument(src_dir)
        .then(result => {
          webview = result.webview;
          container = result.container;
        })
      })

      it('"Save" should overwrite dir', () => {
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'jimbo');
        })
        .then(() => {
          return saveFocusedDoc()
        })
        .then(() => {
          assert.contains(fs.readFileSync(Path.join(src_dir, 'index.html')),
            'jimbo');
        })
        .then(() => {
          return reloadDocument()
        })
        .then(() => {
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'jimbo');
        })
      });

      it('"Save As" should create a new file', () => {
        let dst_file = Path.join(workdir, 'dst.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'garbage');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveAsFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(fs.readFileSync(Path.join(src_dir, 'index.html')),
            'garbage');
        })
        .then(() => {
          // Should have written new file
          assert.contains(readFromZip(dst_file, './index.html'), 'garbage');
        })
        .then(() => {
          return reloadDocument()
        })
        .then(() => {
          // Should be using the new file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'garbage');
        })
      });

      it('"Save As Template" should make a template', () => {
        let dst_file = Path.join(workdir, 'tmpl.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'horizon');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveTemplateFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(fs.readFileSync(Path.join(src_dir, 'index.html')),
            'horizon')
        })
        .then(() => {
          // Should have written new file
          assert.contains(readFromZip(dst_file, './index.html'), 'horizon')
        })
        .then(() => {
          return reloadDocument()
        })
        .then(() => {
          // Should be using the old file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          // Should have old value
          assert.equal(value, null);
        })
      });
    });

    describe('from LHTML file', function() {
      let src_file;
      let src_dir;
      let webview;
      let container;

      beforeEach(() => {
        src_file = Path.join(workdir, 'src.lhtml');
        src_dir = Path.join(workdir, 'src');
        let zip = new AdmZip();
        zip.addFile('./index.html', '<input id="theinput">');
        zip.writeZip(src_file);

        return openDocument(src_file)
        .then(result => {
          webview = result.webview;
          container = result.container;
        })
      })

      it('"Save" should overwrite file', () => {
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'jimbo');
        })
        .then(() => {
          return saveFocusedDoc()
        })
        .then(() => {
          assert.contains(readFromZip(src_file, './index.html'), 'jimbo');
        })
        .then(() => {
          return reloadDocument()
        })
        .then(() => {
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'jimbo');
        })
      });

      it('"Save As" should create a new file', () => {
        let dst_file = Path.join(workdir, 'dst.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'garbage');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveAsFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(readFromZip(src_file, './index.html'), 'garbage')
        })
        .then(() => {
          // Should have written new file with data in it
          assert.contains(readFromZip(dst_file, './index.html'), 'garbage')
        })
        .then(() => {
          return reloadDocument()
        })
        .then(() => {
          // Should be using the new file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          assert.equal(value, 'garbage');
        })
      });

      it('"Save As Template" should make a template', () => {
        let dst_file = Path.join(workdir, 'tmpl.lhtml');
        return executeJavaScript(webview, function() {
          return document.getElementById('theinput').setAttribute('value', 'horizon');
        })
        .then(() => {
          setDialogAnswer(dst_file)
          return saveTemplateFocusedDoc()
        })
        .then(() => {
          // Should not have overwritten original
          assert.doesNotContain(readFromZip(src_file, './index.html'), 'horizon')
        })
        .then(() => {
          // Should have written new file
          assert.contains(readFromZip(dst_file, './index.html'), 'horizon')
        })
        .then(() => {
          return reloadDocument()
        })
        .then(() => {
          // Should be using the old file
          return executeJavaScript(webview, function() {
            return document.getElementById('theinput').getAttribute('value')
          })
        })
        .then(value => {
          // Should have old value
          assert.equal(value, null);
        })
      });
    });
  })
})

<|endoftext|>


<commit_before>//
// Utility functions for the HTML notebook's CasperJS tests.
//

// Get the URL of a notebook server on which to run tests.
casper.getNotebookServer = function () {
    return 'http://127.0.0.1:' + casper.cli.get("port");
};

// Create and open a new notebook.
casper.openNewNotebook = function () {
    var baseUrl = this.getNotebookServer();
    this.start(baseUrl + '/new');
    // initially, the cells aren't created, so wait for them to appear
    this.waitForSelector('.CodeMirror-code');
};

// Shut down the current notebook's kernel.
casper.shutdownCurrentKernel = function () {
    this.thenEvaluate(function() {
        IPython.notebook.kernel.kill();
    });
};

// Delete created notebook.
casper.deleteCurrentNotebook = function () {
    this.thenEvaluate(function() {
        var nbData = $('body').data();
        var url = nbData.baseProjectUrl + 'notebooks/' + nbData.notebookId;
        $.ajax(url, {
            type: 'DELETE',
        });
    });
};

// Wrap a notebook test to reduce boilerplate.
casper.notebookTest = function(test) {
    this.openNewNotebook();
    this.then(test);
    this.shutdownCurrentKernel();
    this.deleteCurrentNotebook();

    // Run the browser automation.
    this.run(function() {
        this.test.done();
    });
};

// Pass `console.log` calls from page JS to casper.
casper.printLog = function () {
    this.on('remote.message', function(msg) {
        this.echo('Remote message caught: ' + msg);
    });
};
<commit_msg>don't try to delete notebooks<commit_after>//
// Utility functions for the HTML notebook's CasperJS tests.
//

// Get the URL of a notebook server on which to run tests.
casper.getNotebookServer = function () {
    return 'http://127.0.0.1:' + casper.cli.get("port");
};

// Create and open a new notebook.
casper.openNewNotebook = function () {
    var baseUrl = this.getNotebookServer();
    this.start(baseUrl + '/new');
    // initially, the cells aren't created, so wait for them to appear
    this.waitForSelector('.CodeMirror-code');
};

// Shut down the current notebook's kernel.
casper.shutdownCurrentKernel = function () {
    this.thenEvaluate(function() {
        IPython.notebook.kernel.kill();
    });
};

// Delete created notebook.
casper.deleteCurrentNotebook = function () {
    this.thenEvaluate(function() {
        var nbData = $('body').data();
        var url = nbData.baseProjectUrl + 'notebooks/' + nbData.notebookId;
        $.ajax(url, {
            type: 'DELETE',
        });
    });
};

// Wrap a notebook test to reduce boilerplate.
casper.notebookTest = function(test) {
    this.openNewNotebook();
    this.then(test);
    this.shutdownCurrentKernel();
    //XXX: the implementation of deleteCurrentNotebook is currently broken
    // it's not a big deal, since the notebook directory will be deleted on
    // cleanup, but we should add tests for deleting the notebook separately
    //this.deleteCurrentNotebook();

    // Run the browser automation.
    this.run(function() {
        this.test.done();
    });
};

// Pass `console.log` calls from page JS to casper.
casper.printLog = function () {
    this.on('remote.message', function(msg) {
        this.echo('Remote message caught: ' + msg);
    });
};
<|endoftext|>


<commit_before>var _ = require('lodash');
var Reflux = require('reflux');
var cardDataActions = require('./actions').cardData;

// I could stick this in a config file or something but it's just as easy for
// me to change this as anything else.
// Might as well keep this value in line with Solforge card releases.
var cardDataVersion = '6.0.1';

var _dictionaryState = {
  cardList: undefined,
  cardDict: undefined,
  state: 'loading'
}

var loadDataFromServer = function() {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', '/data/cards.json');
  xhr.responseType = 'json';
  xhr.setRequestHeader('Accept', 'application/json, text/javascript, */*; q=0.01');
  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onreadystatechange = function(xhrEvent) {
    if (xhr.readyState === 4)  { // Done
      var success = xhr.status < 400;
      if (success) {
        var data = xhr.response;

        localStorage.setItem('solforgeCardData', JSON.stringify(data));
        localStorage.setItem('solforgeCardDataVersion', cardDataVersion);

        cardDataActions.loaded(xhr.response);
      } else {
        cardDataActions.failedToLoad();
      }
    }
  };

  xhr.send();
}

var loadDataFromLocalStorage = function() {
  if (!window.localStorage) {
    return false;
  }

  var localVersion = localStorage.getItem('solforgeCardDataVersion');
  if (localVersion === cardDataVersion) {
    cardDataActions.loaded(JSON.parse(localStorage.getItem('solforgeCardData')));
    return true;
  } else {
    return false;
  }
};

var loadData = function() {
  var loadedFromLocalStorage = loadDataFromLocalStorage();
  if (!loadedFromLocalStorage) {
    loadDataFromServer();
  }
};

var CardDictionary = Reflux.createStore({
  listenables: cardDataActions,

  onLoaded: function(cards) {
    _dictionaryState.cardList = cards;
    _dictionaryState.cardDict = _.reduce(cards, function(dict, card) {
      dict[card.cardId] = card;
      return dict;
    }, {});
    _dictionaryState.state = 'loaded';
    this.trigger(_dictionaryState);
  },

  onFailedToLoad: function() {
    _dictionaryState.cardList = undefined;
    _dictionaryState.cardDict = undefined;
    _dictionaryState.state = 'failed';
    this.trigger(_dictionaryState);
  },

  getInitialState: function() {
    return _dictionaryState;
  },

  getDictionary: function() {
    return _dictionaryState.cardDict;
  },

  getList: function() {
    return _dictionaryState.cardList;
  },

  getBaseLevel: function(card) {
    return _dictionaryState.cardDict[card.cardId].levels[card.level - 1];
  }
});

loadData();

module.exports = CardDictionary;
<commit_msg>Bump data version for 6.1.<commit_after>var _ = require('lodash');
var Reflux = require('reflux');
var cardDataActions = require('./actions').cardData;

// I could stick this in a config file or something but it's just as easy for
// me to change this as anything else.
// Might as well keep this value in line with Solforge card releases.
var cardDataVersion = '6.1';

var _dictionaryState = {
  cardList: undefined,
  cardDict: undefined,
  state: 'loading'
}

var loadDataFromServer = function() {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', '/data/cards.json');
  xhr.responseType = 'json';
  xhr.setRequestHeader('Accept', 'application/json, text/javascript, */*; q=0.01');
  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onreadystatechange = function(xhrEvent) {
    if (xhr.readyState === 4)  { // Done
      var success = xhr.status < 400;
      if (success) {
        var data = xhr.response;

        localStorage.setItem('solforgeCardData', JSON.stringify(data));
        localStorage.setItem('solforgeCardDataVersion', cardDataVersion);

        cardDataActions.loaded(xhr.response);
      } else {
        cardDataActions.failedToLoad();
      }
    }
  };

  xhr.send();
}

var loadDataFromLocalStorage = function() {
  if (!window.localStorage) {
    return false;
  }

  var localVersion = localStorage.getItem('solforgeCardDataVersion');
  if (localVersion === cardDataVersion) {
    cardDataActions.loaded(JSON.parse(localStorage.getItem('solforgeCardData')));
    return true;
  } else {
    return false;
  }
};

var loadData = function() {
  var loadedFromLocalStorage = loadDataFromLocalStorage();
  if (!loadedFromLocalStorage) {
    loadDataFromServer();
  }
};

var CardDictionary = Reflux.createStore({
  listenables: cardDataActions,

  onLoaded: function(cards) {
    _dictionaryState.cardList = cards;
    _dictionaryState.cardDict = _.reduce(cards, function(dict, card) {
      dict[card.cardId] = card;
      return dict;
    }, {});
    _dictionaryState.state = 'loaded';
    this.trigger(_dictionaryState);
  },

  onFailedToLoad: function() {
    _dictionaryState.cardList = undefined;
    _dictionaryState.cardDict = undefined;
    _dictionaryState.state = 'failed';
    this.trigger(_dictionaryState);
  },

  getInitialState: function() {
    return _dictionaryState;
  },

  getDictionary: function() {
    return _dictionaryState.cardDict;
  },

  getList: function() {
    return _dictionaryState.cardList;
  },

  getBaseLevel: function(card) {
    return _dictionaryState.cardDict[card.cardId].levels[card.level - 1];
  }
});

loadData();

module.exports = CardDictionary;
<|endoftext|>


<commit_before><commit_msg>componentizd!<commit_after><|endoftext|>


<commit_before>
import AbstractInput from './abstract-input'
import Ember from 'ember'
import computed, {readOnly} from 'ember-computed-decorators'
import layout from 'ember-frost-bunsen/templates/components/frost-bunsen-input-password'

const {get, merge} = Ember

export default AbstractInput.extend({
  // == Component Properties ===================================================

  classNames: [
    'frost-bunsen-input-password',
    'frost-field'
  ],

  layout,

  // == State Properties =======================================================

  getDefaultProps () {
    return {
      required: false,
      revealed: false,
      showErrorMessage: false
    }
  },

  // == Computed Properties ====================================================

  @readOnly
  @computed('cellConfig')
  options (cellConfig) {
    const options = get(cellConfig, 'renderer.options')
    return merge({revealable: true}, options)
  },

  @readOnly
  @computed('options')
  revealable (options) {
    return get(options, 'revealable')
  },

  @readOnly
  @computed('revealed')
  revealText (revealed) {
    return revealed ? 'Hide' : 'Show'
  },

  @readOnly
  @computed('revealed', 'transformedValue')
  staticValue (revealed, value) {
    return revealed ? value : '************'
  },

  // == Actions ==================================================================

  actions: {
    toggleRevealed () {
      this.toggleProperty('revealed')
    }
  }
})
<commit_msg>PR feedback<commit_after>
import AbstractInput from './abstract-input'
import Ember from 'ember'
import computed, {alias, readOnly} from 'ember-computed-decorators'
import layout from 'ember-frost-bunsen/templates/components/frost-bunsen-input-password'

const {get, merge} = Ember

export default AbstractInput.extend({
  // == Component Properties ===================================================

  classNames: [
    'frost-bunsen-input-password',
    'frost-field'
  ],

  layout,

  // == State Properties =======================================================

  getDefaultProps () {
    return {
      required: false,
      revealed: false,
      showErrorMessage: false
    }
  },

  // == Computed Properties ====================================================

  @readOnly
  @computed('cellConfig')
  options (cellConfig) {
    const options = get(cellConfig, 'renderer.options')
    return merge({revealable: true}, options)
  },

  @readOnly
  @alias('options.revealable')
  revealable: null,

  @readOnly
  @computed('revealed')
  revealText (revealed) {
    return revealed ? 'Hide' : 'Show'
  },

  @readOnly
  @computed('revealed', 'transformedValue')
  staticValue (revealed, value) {
    return revealed ? value : '************'
  },

  // == Actions ==================================================================

  actions: {
    toggleRevealed () {
      this.toggleProperty('revealed')
    }
  }
})
<|endoftext|>


<commit_before>/*
 * Author: jsCoin
 */

var ext = {};
ext.maxPayout = 0.5;
ext.maxBet = 0.01;

function createSettings(){
  var div = document.createElement('div');
  div.id = 'settings';
  div.className = 'col-xs-4';
  var label = document.createElement('label');
  label.innerHTML = 'Max Payout';
  label.for = 'maxPayout';
  label.className = 'control-label col-xs-6 text-right';
  var div2 = document.createElement('div');
  div2.className = 'col-xs-6';
  var input = document.createElement('input');
  input.name = 'maxPayout';
  input.id = 'maxPayout';
  input.className = 'form-control';
  input.style = 'padding: 0px;';
  input.type = 'number';
  input.value = ext.maxPayout;
  input.step = 0.00001;
  input.min = 0.01;
  input.max = 0.5;
  input.addEventListener('change',function(){
    ext.maxPayout = document.getElementById('maxPayout').value;
  });
  div.appendChild(label);
  div2.appendChild(input);
  div.appendChild(div2);

  label = label.cloneNode();
  input = input.cloneNode();
  div2 = div2.cloneNode();

  label.for = 'maxBet';
  label.innerHTML = 'Max Bet';
  div.appendChild(label);
  input.name = 'maxBet';
  input.id = 'maxBet';
  input.value = ext.maxBet;
  input.addEventListener('change',function(){
    ext.maxBet = document.getElementById('maxBet').value;
  });
  div2.appendChild(input);
  div.appendChild(div2);

  $('fieldset').append(div);

}

function minimizeBet(payoutPct){
	var bet = ext.maxPayout/(1+payoutPct);
  if(bet>ext.maxBet)
    bet = ext.maxBet;

  var event = document.createEvent("HTMLEvents");
  event.initEvent("change",true,true);
  $('fieldset .input-group .form-control')
    .val(bet)
    .get(0)
    .dispatchEvent(event);
}

/**
 *  Page key controls.
 **/
$(document).keydown(function(e){
  if(e.keyCode==40){
    //down
    // open put trade window
    var $put = getPut();
    var pct = getPct($put);
    minimizeBet(pct);
    $put.click();
  }else if(e.keyCode==38){
    //up
    // open call trade window
    var $call = getCall();
    var pct = getPct($call);
    minimizeBet(pct);
    $call.click();
  }else if(e.keyCode==27){
    //escape
    // close trade window
    $('.panel-danger,.panel-success').find('.panel-heading button').click();
  }else if(e.keyCode==13){
    //enter
    // execute trade
    $('.panel-danger,.panel-success').find('.col-xs-7 button').click();
  }
});

function getCall(){
  return $('.buy-button[value="call"]');
}

function getPut(){
  return $('.buy-button[value="put"]');
}

function getPct($from){
  var pct = $from
      .parents('.row[data-view="views/trading/inverse/pricepanel"]')
      .find('.col-xs-6:last-child')
      .find('div div:last-child').html();
  pct = pct.substring(0,pct.length-1);
  pct = ((pct*1)+1)/100;
  return pct;
}

function putClick(){
    var $put = getPut();
    var pct = getPct($put);
    minimizeBet(pct);
}
ext.months = {
  Jan : 0,
  Feb : 1,
  Mar : 2,
  Apr : 3,
  May : 4,
  Jun : 5,
  Jul : 6,
  Aug : 7,
  Sep : 8,
  Oct : 9,
  Nov : 10,
  Dec : 11
};
function month(monthStr){
  return ext.months[monthStr];
}

/**
 *  Create markup and attach listeners.
 **/
$(document).ready(function(){
//$('.durandal-wrapper[data-view="views/trading/index"]').load(function(){
  console.log("loaded");
  createSettings();
  getPut().click(putClick);
  //mutation observer for .panel-info table
  ext.observer = new MutationObserver(function(mutations){
    var date, time;
    var expires, now;
    var count;
    mutations.forEach(function(mutation){
      if(mutation.target.nodeName == "DIV" && mutation.target.className == 'small'){
        date = mutation.target.innerText;
        date = date.split("-");
        time = mutation.target.previousElementSibling.innerText;
        time = time.split(":");
        expires = new Date();
        expires.setUTCMilliseconds(0);
        now = new Date($('#miniStatus3 span').html());
        now.setFullYear('20'+date[2]);
        expires.setUTCDate(date[0]);
        expires.setUTCMonth(month(date[1]));
        expires.setUTCFullYear('20'+date[2]);
        expires.setUTCHours(time[0]);
        expires.setUTCMinutes(time[1]);
        expires.setUTCSeconds(time[2]);
        count = (expires.getTime() - now.getTime())/1000;
        $(mutation.target).parents('tr')
          .append(""+count+"	");
      }else if(mutation.addedNodes[0] && mutation.addedNodes[0].nodeName == "THEAD")
        $(mutation.target).find('thead tr').append('Remains	');
    });
  });
  ext.observer.observe($('.panel-info table').get(0),{
    childList: true,
    subtree: true
  });
//});
});<commit_msg>switched from document ready listener to body mutation observer.  plugin should load every time now.<commit_after>/*
 * Author: jsCoin
 */

var ext = {};
ext.maxPayout = 0.5;
ext.maxBet = 0.01;

function createSettings(){
  var div = document.createElement('div');
  div.id = 'settings';
  div.className = 'col-xs-4';
  var label = document.createElement('label');
  label.innerHTML = 'Max Payout';
  label.for = 'maxPayout';
  label.className = 'control-label col-xs-6 text-right';
  var div2 = document.createElement('div');
  div2.className = 'col-xs-6';
  var input = document.createElement('input');
  input.name = 'maxPayout';
  input.id = 'maxPayout';
  input.className = 'form-control';
  input.style = 'padding: 0px;';
  input.type = 'number';
  input.value = ext.maxPayout;
  input.step = 0.00001;
  input.min = 0.01;
  input.max = 0.5;
  input.addEventListener('change',function(){
    ext.maxPayout = document.getElementById('maxPayout').value;
  });
  div.appendChild(label);
  div2.appendChild(input);
  div.appendChild(div2);

  label = label.cloneNode();
  input = input.cloneNode();
  div2 = div2.cloneNode();

  label.for = 'maxBet';
  label.innerHTML = 'Max Bet';
  div.appendChild(label);
  input.name = 'maxBet';
  input.id = 'maxBet';
  input.value = ext.maxBet;
  input.addEventListener('change',function(){
    ext.maxBet = document.getElementById('maxBet').value;
  });
  div2.appendChild(input);
  div.appendChild(div2);
  $('fieldset').append(div);

}

function minimizeBet(payoutPct){
	var bet = ext.maxPayout/(1+payoutPct);
  if(bet>ext.maxBet)
    bet = ext.maxBet;

  var event = document.createEvent("HTMLEvents");
  event.initEvent("change",true,true);
  $('fieldset .input-group .form-control')
    .val(bet)
    .get(0)
    .dispatchEvent(event);
}

/**
 *  Page key controls.
 **/
$(document).keydown(function(e){
  if(e.keyCode==40){
    //down
    // open put trade window
    var $put = getPut();
    var pct = getPct($put);
    minimizeBet(pct);
    $put.click();
  }else if(e.keyCode==38){
    //up
    // open call trade window
    var $call = getCall();
    var pct = getPct($call);
    minimizeBet(pct);
    $call.click();
  }else if(e.keyCode==27){
    //escape
    // close trade window
    $('.panel-danger,.panel-success').find('.panel-heading button').click();
  }else if(e.keyCode==13){
    //enter
    // execute trade
    $('.panel-danger,.panel-success').find('.col-xs-7 button').click();
  }
});

function getCall(){
  return $('.buy-button[value="call"]');
}

function getPut(){
  return $('.buy-button[value="put"]');
}

function getPct($from){
  var pct = $from
      .parents('.row[data-view="views/trading/inverse/pricepanel"]')
      .find('.col-xs-6:last-child')
      .find('div div:last-child').html();
  pct = pct.substring(0,pct.length-1);
  pct = ((pct*1)+1)/100;
  return pct;
}

function putClick(){
    console.log('put clicked');
    console.log(this);
    var $put = getPut();
    var pct = getPct($put);
    minimizeBet(pct);
}
ext.months = {
  Jan : 0,
  Feb : 1,
  Mar : 2,
  Apr : 3,
  May : 4,
  Jun : 5,
  Jul : 6,
  Aug : 7,
  Sep : 8,
  Oct : 9,
  Nov : 10,
  Dec : 11
};
function month(monthStr){
  return ext.months[monthStr];
}

/**
 *  Create markup and attach listeners.
 **/
function initTableObserver(){
  ext.observer = new MutationObserver(function(mutations){
  var date, time;
  var expires, now;
  var count;
  mutations.forEach(function(mutation){
    if(mutation.target.nodeName == "DIV" && mutation.target.className == 'small'){
      date = mutation.target.innerText;
      date = date.split("-");
      time = mutation.target.previousElementSibling.innerText;
      time = time.split(":");
      expires = new Date();
      expires.setUTCMilliseconds(0);
      now = new Date($('#miniStatus3 span').html());
      now.setFullYear('20'+date[2]);
      expires.setUTCDate(date[0]);
      expires.setUTCMonth(month(date[1]));
      expires.setUTCFullYear('20'+date[2]);
      expires.setUTCHours(time[0]);
      expires.setUTCMinutes(time[1]);
      expires.setUTCSeconds(time[2]);
      count = (expires.getTime() - now.getTime())/1000;
      $(mutation.target).parents('tr')
        .append(""+count+"	");
    }else if(mutation.addedNodes[0] && mutation.addedNodes[0].nodeName == "THEAD")
      $(mutation.target).find('thead tr').append('Remains	');
    });
  });
  ext.observer.observe($('.panel-info table').get(0),{
    childList: true,
    subtree: true
  });
}
ext.init = false;
ext.bodyObserver = new MutationObserver(function(mutations){
  mutations.forEach(function(mutation){
    if(mutation.addedNodes[0]
      && mutation.addedNodes[0].nodeName == "DIV"){
      if(mutation.addedNodes[0].id == 'pricesViewInverse'){
        createSettings();
        if(ext.init)
          ext.bodyObserver.disconnect();
        ext.init = true;
      }
      if(mutation.addedNodes[0].attributes[0].value=='views/trading/simplePortfolio'){
        initTableObserver();
        if(ext.init)
          ext.bodyObserver.disconnect();
        ext.init = true;
      }
    }
   });
});
ext.bodyObserver.observe(document.body,{
  childList: true,
  subtree: true
});<|endoftext|>


<commit_before>import {Link, NavLink} from 'react-router-dom';
import List, {ListItem} from '../../../src/List';
import {makeURL} from '../../utils/globals';
import Paper from '../../../src/Paper';
import PropTypes from 'prop-types';
import React from 'react';
import Scrollable from '../../../src/Scrollable';
import Styles from './Shared.css';
import Typography from '../../../src/Typography';

const items = [
  {
    label: 'AppBar',
    url: makeURL('/AppBar')
  },
  {
    label: 'BottomNavigation',
    url: makeURL('/BottomNavigation')
  },
  {
    label: 'Button',
    url: makeURL('/Button')
  },
  {
    label: 'Collapse',
    url: makeURL('/Collapse')
  },
  {
    label: 'Dialog',
    url: makeURL('/Dialog')
  },
  {
    label: 'Grid',
    url: makeURL('/Grid')
  },
  {
    label: 'List',
    url: makeURL('/List')
  },
  {
    label: 'Paper',
    url: makeURL('/Paper')
  },
  {
    label: 'SelectField',
    url: makeURL('/SelectField')
  },
  {
    label: 'SnackBar',
    url: makeURL('/SnackBar')
  },
  {
    label: 'Switch',
    url: makeURL('/Switch')
  },
  {
    label: 'Tabs',
    url: makeURL('/Tabs')
  },
  {
    label: 'TextField',
    url: makeURL('/TextField')
  },
  {
    label: 'Typography',
    url: makeURL('/Typography')
  }
];

function Shared({onClick}) {
  return (
    <Scrollable>
      <Paper className={Styles.root}>
        <Typography className={Styles.title}>
          <Link to={makeURL()}>material-react-components</Link>

By CollegePulse

        </Typography>
        <List>
          {items.map(item => (
            <ListItem
              key={item.url}
              component={NavLink}
              activeClassName={Styles.active}
              className={Styles.inactive}
              to={item.url}
              onClick={onClick}
              primary={(

                  {item.label}

              )}
            />
          ))}
        </List>
      </Paper>
    </Scrollable>
  );
}

Shared.defaultProps = {
  onClick: () => {}
};

Shared.propTypes = {
  onClick: PropTypes.func
};

export default Shared;

<commit_msg>[docs] [Navigation] refactor to updated ListItem interface<commit_after>import {Link, NavLink} from 'react-router-dom';
import List, {ListItem} from '../../../src/List';
import {makeURL} from '../../utils/globals';
import Paper from '../../../src/Paper';
import PropTypes from 'prop-types';
import React from 'react';
import Scrollable from '../../../src/Scrollable';
import Styles from './Shared.css';
import Typography from '../../../src/Typography';

const items = [
  {
    label: 'AppBar',
    url: makeURL('/AppBar')
  },
  {
    label: 'BottomNavigation',
    url: makeURL('/BottomNavigation')
  },
  {
    label: 'Button',
    url: makeURL('/Button')
  },
  {
    label: 'Collapse',
    url: makeURL('/Collapse')
  },
  {
    label: 'Dialog',
    url: makeURL('/Dialog')
  },
  {
    label: 'Grid',
    url: makeURL('/Grid')
  },
  {
    label: 'List',
    url: makeURL('/List')
  },
  {
    label: 'Paper',
    url: makeURL('/Paper')
  },
  {
    label: 'SelectField',
    url: makeURL('/SelectField')
  },
  {
    label: 'SnackBar',
    url: makeURL('/SnackBar')
  },
  {
    label: 'Switch',
    url: makeURL('/Switch')
  },
  {
    label: 'Tabs',
    url: makeURL('/Tabs')
  },
  {
    label: 'TextField',
    url: makeURL('/TextField')
  },
  {
    label: 'Typography',
    url: makeURL('/Typography')
  }
];

function Shared({onClick}) {
  return (
    <Scrollable>
      <Paper className={Styles.root}>
        <Typography className={Styles.title}>
          <Link to={makeURL()}>material-react-components</Link>

By CollegePulse

        </Typography>
        <List>
          {items.map(item => (
            <ListItem
              key={item.url}
              buttonProps={{
                activeClassName: Styles.active,
                component: NavLink,
                to: item.url,
                onClick
              }}
              className={Styles.inactive}
              primary={(

                  {item.label}

              )}
            />
          ))}
        </List>
      </Paper>
    </Scrollable>
  );
}

Shared.defaultProps = {
  onClick: () => {}
};

Shared.propTypes = {
  onClick: PropTypes.func
};

export default Shared;

<|endoftext|>


<commit_before>'use strict';

module.exports = {
    "rules": {
        "no-await-in-loop"             : "off",
        "no-template-curly-in-string"  : "off",
        "class-methods-use-this"       : "off",
        "no-empty-pattern"             : "off",
        "no-return-await"              : "off",
        "require-await"                : "off",
        "no-misleading-character-class": "off",
        "no-sync"                      : "off",
        "comma-dangle"                 : [
            "error",
            {
                "arrays"   : "always-multiline",
                "objects"  : "always-multiline",
                "imports"  : "always-multiline",
                "exports"  : "always-multiline",
                "functions": "never"
            }
        ],
        "func-names"                   : [
            "error",
            "always"
        ],
        "computed-property-spacing"    : "off",
        "no-buffer-constructor"        : "off",
        "require-unicode-regexp"       : 'off',
        "arrow-body-style"             : "off",
        "arrow-parens"                 : "off",
        "arrow-spacing"                : "off",
        "constructor-super"            : "off",
        "generator-star-spacing"       : "off",
        "no-class-assign"              : "off",
        "no-confusing-arrow"           : "off",
        "no-const-assign"              : "off",
        "no-dupe-class-members"        : "off",
        "no-duplicate-imports"         : "off",
        "no-new-symbol"                : "off",
        "no-restricted-imports"        : "off",
        "no-this-before-super"         : "off",
        "no-useless-computed-key"      : "off",
        "no-useless-constructor"       : "off",
        "no-useless-rename"            : "off",
        "no-var"                       : "off",
        "object-shorthand"             : "off",
        "prefer-arrow-callback"        : "off",
        "prefer-const"                 : "off",
        "prefer-destructuring"         : "off",
        "prefer-numeric-literals"      : "off",
        "prefer-promise-reject-errors" : "off",
        "prefer-rest-params"           : "off",
        "prefer-spread"                : "off",
        "prefer-template"              : "off",
        "require-yield"                : "off",
        "rest-spread-spacing"          : "off",
        "sort-imports"                 : "off",
        "symbol-description"           : "off",
        "template-curly-spacing"       : "off",
        "template-tag-spacing"         : "off",
        "yield-star-spacing"           : "off"
    }
}
;
<commit_msg>disable `require-atomic-update` rule for ES5<commit_after>'use strict';

module.exports = {
    "rules": {
        "no-await-in-loop"             : "off",
        "no-template-curly-in-string"  : "off",
        "class-methods-use-this"       : "off",
        "no-empty-pattern"             : "off",
        "no-return-await"              : "off",
        "require-atomic-update"        : "off",
        "require-await"                : "off",
        "no-misleading-character-class": "off",
        "no-sync"                      : "off",
        "comma-dangle"                 : [
            "error",
            {
                "arrays"   : "always-multiline",
                "objects"  : "always-multiline",
                "imports"  : "always-multiline",
                "exports"  : "always-multiline",
                "functions": "never"
            }
        ],
        "func-names"                   : [
            "error",
            "always"
        ],
        "computed-property-spacing"    : "off",
        "no-buffer-constructor"        : "off",
        "require-unicode-regexp"       : 'off',
        "arrow-body-style"             : "off",
        "arrow-parens"                 : "off",
        "arrow-spacing"                : "off",
        "constructor-super"            : "off",
        "generator-star-spacing"       : "off",
        "no-class-assign"              : "off",
        "no-confusing-arrow"           : "off",
        "no-const-assign"              : "off",
        "no-dupe-class-members"        : "off",
        "no-duplicate-imports"         : "off",
        "no-new-symbol"                : "off",
        "no-restricted-imports"        : "off",
        "no-this-before-super"         : "off",
        "no-useless-computed-key"      : "off",
        "no-useless-constructor"       : "off",
        "no-useless-rename"            : "off",
        "no-var"                       : "off",
        "object-shorthand"             : "off",
        "prefer-arrow-callback"        : "off",
        "prefer-const"                 : "off",
        "prefer-destructuring"         : "off",
        "prefer-numeric-literals"      : "off",
        "prefer-promise-reject-errors" : "off",
        "prefer-rest-params"           : "off",
        "prefer-spread"                : "off",
        "prefer-template"              : "off",
        "require-yield"                : "off",
        "rest-spread-spacing"          : "off",
        "sort-imports"                 : "off",
        "symbol-description"           : "off",
        "template-curly-spacing"       : "off",
        "template-tag-spacing"         : "off",
        "yield-star-spacing"           : "off"
    }
}
;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Route, Switch, Redirect } from 'react-router-dom';
import { Subnav } from '../../components/index'
import { Interface } from './Interface/Interface.js'

export default class GroupList extends Component {

  static propTypes = {
    match: PropTypes.object
  }

  state = {
  }

  constructor(props) {
    super(props)
  }

  componentWillMount() {
  }

  render () {
    const { match } = this.props;
    console.log('project')
    return (

        <Subnav
          default={'接口'}
          data={[{
            name: '接口',
            path: `/project/${match.params.id}/interface`
          }, {
            name: '设置',
            path: `/project/${match.params.id}/setting`
          }, {
            name: '动态',
            path: `/project/${match.params.id}/activity`
          }]}/>
        <Switch>
          <Redirect exact from ="/project/:id" to={`/project/${match.params.id}/activity`}/>
          <Route path="/project/:id/activity" component={null} />
          <Route path="/project/:id/interface" component={Interface} />
          <Route path="/project/:id/setting" component={null} />
        </Switch>

    )
  }
}
<commit_msg>feat: 项目页面<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Route, Switch, Redirect } from 'react-router-dom';
import { Subnav } from '../../components/index'
import { Interface } from './Interface/Interface.js'
import { Activity } from './Activity/Activity.js'
import { Setting } from './Setting/Setting.js'

export default class GroupList extends Component {

  static propTypes = {
    match: PropTypes.object
  }

  state = {
  }

  constructor(props) {
    super(props)
  }

  componentWillMount() {
  }

  render () {
    const { match } = this.props;
    console.log('project')
    return (

        <Subnav
          default={'接口'}
          data={[{
            name: '接口',
            path: `/project/${match.params.id}/interface`
          }, {
            name: '设置',
            path: `/project/${match.params.id}/setting`
          }, {
            name: '动态',
            path: `/project/${match.params.id}/activity`
          }]}/>
        <Switch>
          <Redirect exact from ="/project/:id" to={`/project/${match.params.id}/activity`}/>
          <Route path="/project/:id/activity" component={Activity} />
          <Route path="/project/:id/interface" component={Interface} />
          <Route path="/project/:id/setting" component={Setting} />
        </Switch>

    )
  }
}
<|endoftext|>


<commit_before>// Dependencies
var $ = require("/libs/jquery");

/**
 * jq
 * Calls a jQuery method.
 *
 * @name jq
 * @function
 * @param {Event} ev The event object.
 * @param {Object} data An object containing the following fields:
 *
 *  - `sel` (String): The jQuery selector.
 *  - `meth` (String): The jQuery method to call.
 *  - `args` (Array): The method arguments (default: `[]`).
 *
 * @return {undefined}
 */
exports.jq = function (data) {
    data.args = data.args || [];
    $.fn[data.meth].apply($(data.sel), data.args);
};

exports.text = function (data) {
    exports.jq({
        sel: data.sel
      , args: [data.text]
      , meth: "text"
    });
};
<commit_msg>Export eh jQuery function<commit_after>// Dependencies
var $ = require("/libs/jquery");

/**
 * jq
 * Calls a jQuery method.
 *
 * @name jq
 * @function
 * @param {Event} ev The event object.
 * @param {Object} data An object containing the following fields:
 *
 *  - `sel` (String): The jQuery selector.
 *  - `meth` (String): The jQuery method to call.
 *  - `args` (Array): The method arguments (default: `[]`).
 *
 * @return {undefined}
 */
exports.jq = function (data) {
    data.args = data.args || [];
    $.fn[data.meth].apply($(data.sel), data.args);
};

/**
 * text
 *
 * @name text
 * @function
 * @param {Object} data An object contaning:
 *
 *  - `sel` (String): The jQuery selector.
 *  - `text` (String): The text to set.
 */
exports.text = function (data) {
    exports.jq({
        sel: data.sel
      , args: [data.text]
      , meth: "text"
    });
};

// Export the jQuery function itself
exports.$ = $;
<|endoftext|>


<commit_before>/**
 * sameAs Lite
 * jQuery for Symbols pages
 */

var app = {
    ajaxMimeType: 'application/json', // default mime type
    outputMimeType: 'text/html', // default mime type
    page: 0, //the current page (default = 0: do not append page parameter to url)
    wrap: function ($result, pre) {
        if (pre === undefined) {
            pre = 'pre';
        }
        // do not wrap if wrap already exists
        if ($result.parent(pre).length === 0) {
            $result.wrap('<' + pre + '></' + pre + '>');
        }
    },
    unwrap: function ($result, pre) {
        if (pre === undefined) {
            pre = 'pre';
        }
        // for html table output, we do not want
 tags
        if ($result.parent(pre).length) {
            $result.unwrap();
        }
    },
    markCurrent: function ($items, current, attrType) {
        $items.removeClass('current');
        $items.each(function(){
            if (attrType === 'data') {
                if ($(this).data('mime') == current) {
                    $(this).addClass('current');
                    return false; //break
                }
            } else {
                if ($(this).text() == app.page) {
                    $(this).addClass('current');
                    return false; //break
                }
            }
        });
    },
    updateState: function(data) {

        // loop over the mime buttons to mark the current one
        this.markCurrent($('.label.alternate_format'), this.outputMimeType, 'data');

        // loop over the page buttons to mark the current one
        this.markCurrent($('.pagination .label.page'), this.page, 'text');

        // update the url
        //window.location.href.replace(/page=\d+/i, "?page=" + app.page);
        if (this.page) {
            window.history.pushState(data, "SameAs-Lite Result Page " + this.page, "?page=" + this.page);
        }

    },
    getPageContents: function() {
        // issue an ajax request to the current page for the selected mime type
        // then replace the content of the page with the result

        // if this is a call with mime type text/html
        // we run into the problem that it would load
        // the full wepage instead of a fragment
        // so we cheat and execute a call for json
        // then process the result on the client side
        // to update the table on the page
        if (app.outputMimeType === 'text/html') {
            app.ajaxMimeType = 'application/json';
        } else {
            app.ajaxMimeType = app.outputMimeType;
        }

        var $result = $('#result');
        $result.html('');

        $.ajax({
            url: (app.page > 1 ? '?page=' + app.page : ''),
            dataType: "text",
            headers: {
                "Accept": app.ajaxMimeType
            },
            beforeSend: function(xhr) {
                xhr.overrideMimeType(app.ajaxMimeType);
            },
            success: function (data, textStatus, jObj) {

                if (data) {
                    // clear
                    // $result.html("");

                    if (app.outputMimeType !== 'text/html') {

                        $result.text(data);
                        // wrap pre tags around result (only once)
                        app.wrap($result);

                    } else {

                        // build a table from the incoming json data
                        // and replace the contents of the div container

                        data = $.parseJSON(data);
                        //console.log(data);

                        var header_values = [],
                            $header = '',
                            $body = '',
                            $row = '',
                            data_rows = [],
                            i = 0,
                            s = data.length;

                        // is this tabular data or a simple list of values?
                        if (typeof data[0] === 'string') {

                            // no headers
                            header_values = false;
                            data_rows = data;

                            // add results as an unordered list
                            $ul = $('

').addClass('table');
                            $.each(data_rows, function(index, row) {
                                $row = $('

').append(row);
                                $ul.append($row);
                            });
                            $result.html($ul);

                        } else {

                            $table = $('

').addClass('table');

                            // if the first element is an object,
                            // then there are several levels to the array
                            // see for example analysis output

                            // get the table header from the first element
                            if (data[0]) {
                                header_values = Object.keys(data[0]);
                            }

                            // get the table rows
                            for (i = 0; i < s; i++){
                                data_rows[i] = [];
                                $.each(header_values, function() {
                                    data_rows[i].push(data[i][this]);
                                });
                            }

                            // add the processed data to a table
                            // headers
                            if (header_values) {
                                $header = $('

');
                                $header_row = $('

');
                                $.each(header_values, function() {
                                    $header_row.append('' + this + '	');
                                });
                                $header.append($header_row);
                                $table.append($header);
                            }
                            // rows
                            $body = $('

');
                            $.each(data_rows, function(index, row) {
                                $row = $('

');
                                if (typeof row === 'string') {
                                    $row.append('' + row + '	');
                                } else {
                                    $.each(row, function() {
                                        $row.append('' + this + '	');
                                    });
                                }
                                $body.append($row);
                            });
                            $table.append($body);

                            $result.html($table);

                        }

                        // for html table output, we do not want
 tags
                        app.unwrap($result);

                    }

                    //always update the buttons and the url to reflect the page we are on
                    app.updateState(data);

                }

            },//end success()
            error: function (jObj) {

                var data = jObj.responseText;

                app.updateState(data);

                app.unwrap($result);

                switch (app.ajaxMimeType) {

                    case 'application/rdf+xml':
                    case 'text/turtle':

                        data = JSON.parse(data);
                        data = JSON.stringify(data);
                        data = data.replace('\n', '');
                        data = data.replace("\n", "
");

                        break;

                    default:

                        data = data.replace("\n", "
");

                        break;

                }//end switch

                $result.html(data);

            }//end error()
        });
    }
};

$(document).ready(function() {

    // mime type ajax button
    $("#alternate_mime_options .label.alternate_format").click(function (e) {
        e.preventDefault();
        e.stopPropagation();

        //get the required format and remember the mime type
        app.outputMimeType = $(this).data('mime');

        app.getPageContents();
    });

    // ajax pagination
    $(".pagination .label.page").click(function (e) {
        e.preventDefault();
        e.stopPropagation();

        var button_text = $(this).text();

        if (app.page == button_text) {
            return; // we do not need to get the results for the already loaded page
        }

        app.page = button_text;
        app.getPageContents();
    });

});

// vim: set filetype=javascript expandtab tabstop=4 shiftwidth=4:
<commit_msg>update button state if result is empty<commit_after>/**
 * sameAs Lite
 * jQuery for Symbols pages
 */

var app = {
    ajaxMimeType: 'application/json', // default mime type
    outputMimeType: 'text/html', // default mime type
    page: 0, //the current page (default = 0: do not append page parameter to url)
    wrap: function ($result, pre) {
        if (pre === undefined) {
            pre = 'pre';
        }
        // do not wrap if wrap already exists
        if ($result.parent(pre).length === 0) {
            $result.wrap('<' + pre + '></' + pre + '>');
        }
    },
    unwrap: function ($result, pre) {
        if (pre === undefined) {
            pre = 'pre';
        }
        // for html table output, we do not want
 tags
        if ($result.parent(pre).length) {
            $result.unwrap();
        }
    },
    markCurrent: function ($items, current, attrType) {
        $items.removeClass('current');
        $items.each(function(){
            if (attrType === 'data') {
                if ($(this).data('mime') == current) {
                    $(this).addClass('current');
                    return false; //break
                }
            } else {
                if ($(this).text() == app.page) {
                    $(this).addClass('current');
                    return false; //break
                }
            }
        });
    },
    updateState: function(data) {

        // loop over the mime buttons to mark the current one
        this.markCurrent($('.label.alternate_format'), this.outputMimeType, 'data');

        // loop over the page buttons to mark the current one
        this.markCurrent($('.pagination .label.page'), this.page, 'text');

        // update the url
        //window.location.href.replace(/page=\d+/i, "?page=" + app.page);
        if (this.page) {
            window.history.pushState(data, "SameAs-Lite Result Page " + this.page, "?page=" + this.page);
        }

    },
    getPageContents: function() {
        // issue an ajax request to the current page for the selected mime type
        // then replace the content of the page with the result

        // if this is a call with mime type text/html
        // we run into the problem that it would load
        // the full wepage instead of a fragment
        // so we cheat and execute a call for json
        // then process the result on the client side
        // to update the table on the page
        if (app.outputMimeType === 'text/html') {
            app.ajaxMimeType = 'application/json';
        } else {
            app.ajaxMimeType = app.outputMimeType;
        }

        var $result = $('#result');
        $result.html('');

        $.ajax({
            url: (app.page > 1 ? '?page=' + app.page : ''),
            dataType: "text",
            headers: {
                "Accept": app.ajaxMimeType
            },
            beforeSend: function(xhr) {
                xhr.overrideMimeType(app.ajaxMimeType);
            },
            success: function (data, textStatus, jObj) {

                if (data) {
                    // clear
                    // $result.html("");

                    if (app.outputMimeType !== 'text/html') {

                        $result.text(data);
                        // wrap pre tags around result (only once)
                        app.wrap($result);

                    } else {

                        // build a table from the incoming json data
                        // and replace the contents of the div container

                        data = $.parseJSON(data);
                        //console.log(data);

                        var header_values = [],
                            $header = '',
                            $body = '',
                            $row = '',
                            data_rows = [],
                            i = 0,
                            s = data.length;

                        // is this tabular data or a simple list of values?
                        if (typeof data[0] === 'string') {

                            // no headers
                            header_values = false;
                            data_rows = data;

                            // add results as an unordered list
                            $ul = $('

').addClass('table');
                            $.each(data_rows, function(index, row) {
                                $row = $('

').append(row);
                                $ul.append($row);
                            });
                            $result.html($ul);

                        } else {

                            $table = $('

').addClass('table');

                            // if the first element is an object,
                            // then there are several levels to the array
                            // see for example analysis output

                            // get the table header from the first element
                            if (data[0]) {
                                header_values = Object.keys(data[0]);
                            }

                            // get the table rows
                            for (i = 0; i < s; i++){
                                data_rows[i] = [];
                                $.each(header_values, function() {
                                    data_rows[i].push(data[i][this]);
                                });
                            }

                            // add the processed data to a table
                            // headers
                            if (header_values) {
                                $header = $('

');
                                $header_row = $('

');
                                $.each(header_values, function() {
                                    $header_row.append('' + this + '	');
                                });
                                $header.append($header_row);
                                $table.append($header);
                            }
                            // rows
                            $body = $('

');
                            $.each(data_rows, function(index, row) {
                                $row = $('

');
                                if (typeof row === 'string') {
                                    $row.append('' + row + '	');
                                } else {
                                    $.each(row, function() {
                                        $row.append('' + this + '	');
                                    });
                                }
                                $body.append($row);
                            });
                            $table.append($body);

                            $result.html($table);

                        }

                        // for html table output, we do not want
 tags
                        app.unwrap($result);

                    }

                }

                //always update the buttons and the url to reflect the page we are on
                app.updateState(data);

            },//end success()
            error: function (jObj) {

                var data = jObj.responseText;

                app.updateState(data);

                app.unwrap($result);

                switch (app.ajaxMimeType) {

                    case 'application/rdf+xml':
                    case 'text/turtle':

                        data = JSON.parse(data);
                        data = JSON.stringify(data);
                        data = data.replace('\n', '');
                        data = data.replace("\n", "
");

                        break;

                    default:

                        data = data.replace("\n", "
");

                        break;

                }//end switch

                $result.html(data);

            }//end error()
        });
    }
};

$(document).ready(function() {

    // mime type ajax button
    $("#alternate_mime_options .label.alternate_format").click(function (e) {
        e.preventDefault();
        e.stopPropagation();

        //get the required format and remember the mime type
        app.outputMimeType = $(this).data('mime');

        app.getPageContents();
    });

    // ajax pagination
    $(".pagination .label.page").click(function (e) {
        e.preventDefault();
        e.stopPropagation();

        var button_text = $(this).text();

        if (app.page == button_text) {
            return; // we do not need to get the results for the already loaded page
        }

        app.page = button_text;
        app.getPageContents();
    });

});

// vim: set filetype=javascript expandtab tabstop=4 shiftwidth=4:
<|endoftext|>


<commit_before>import SPELLS from 'common/SPELLS';

import CoreCooldownTracker, { BUILT_IN_SUMMARY_TYPES } from 'Parser/Core/Modules/CooldownTracker';

class CooldownTracker extends CoreCooldownTracker {
  static cooldownSpells = [
    ...CooldownTracker.cooldownSpells,
    {
      spell: SPELLS.TRUESHOT,
      summary: [
        BUILT_IN_SUMMARY_TYPES.DAMAGE,
      ],
    },
  ];

  static ignoredSpells = [
    ...CooldownTracker.ignoredSpells,
    SPELLS.WINDBURST_MOVEMENT_SPEED.id,
  ];
}

export default CooldownTracker;
<commit_msg>Cleaned up code to better showcase what it actually does, and added two comments to further clarify if needed<commit_after>import SPELLS from 'common/SPELLS';

import CoreCooldownTracker, { BUILT_IN_SUMMARY_TYPES } from 'Parser/Core/Modules/CooldownTracker';

class CooldownTracker extends CoreCooldownTracker {
  static cooldownSpells = [
    ...CooldownTracker.cooldownSpells,
    {
      spell: SPELLS.TRUESHOT,
      summary: [
        BUILT_IN_SUMMARY_TYPES.DAMAGE,
      ],
    },
  ];

  static ignoredSpells = [
    ...CooldownTracker.ignoredSpells,
    SPELLS.WINDBURST_MOVEMENT_SPEED.id,
  ];

export default CooldownTracker;
<|endoftext|>


<commit_before>/* global describe:true, it:true, before:true, beforeEach:true, afterEach:true */
import assert from 'assert';
import { contains } from 'lodash';
import { setupTestRekapi, setupTestActor } from './test-utils';

import Rekapi, { Actor } from '../src/main';
import {
  Tweenable,
  interpolate,
  setBezierFunction,
  unsetBezierFunction
} from 'shifty';

describe('Actor', () => {
  let rekapi, actor;

  beforeEach(() => {
    rekapi = setupTestRekapi();
    actor = setupTestActor(rekapi);
  });

  describe('constructor', () => {
    it('is a function', () => {
      assert.equal(typeof Actor, 'function');
    });
  });

  describe('#_updateState', () => {
    describe('interpolating positions', () => {
      describe('actors that start at 0', () => {
        it('interpolates actor positions at arbitrary positions mid-frame', () => {
          actor.keyframe(0, {
            x: 0,
            y: 0
          }).keyframe(1000, {
            x: 100,
            y:100
          });

          actor._updateState(0);
          assert.equal(actor.get().x, 0);
          assert.equal(actor.get().y, 0);

          actor._updateState(500);
          assert.equal(actor.get().x, 50);
          assert.equal(actor.get().y, 50);

          actor._updateState(1000);
          assert.equal(actor.get().x, 100);
          assert.equal(actor.get().y, 100);
        });
      });

      describe('actors that start later than 0', () => {
        it('interpolates actor positions at arbitrary positions mid-frame', () => {
          actor.keyframe(1000, {
            x: 0,
            y: 0
          }).keyframe(2000, {
            x: 100,
            y: 100
          });

          actor._updateState(1000);
          assert.equal(actor.get().x, 0);
          assert.equal(actor.get().y, 0);

          actor._updateState(1500);

          assert.equal(actor.get().x, 50);
          assert.equal(actor.get().y, 50,
            'Value "y" was properly interpolated at position 0.5');

          actor._updateState(2000);
          assert.equal(actor.get().x, 100);
          assert.equal(actor.get().y, 100);
        });

        describe('property look-ahead', () => {
          describe('single track', () => {
            it('looks ahead to first keyframe when computing states prior to actor start', () => {
              actor.keyframe(0, {
                // Nothing!
              }).keyframe(1000, {
                y: 100
              });

              actor._updateState(500);
              assert.equal(actor.get().y, 100);
            });
          });

          describe('multiple tracks', () => {
            it('looks ahead to first keyframe when computing states prior to actor start', () => {
              actor.keyframe(0, {
                x: 50
              }).keyframe(1000, {
                y: 100
              });

              actor._updateState(500);
              assert.equal(actor.get().y, 100);
            });
          });
        });
      });
    });

    describe('computing state past a keyframe track end', () => {
      it('leaves keyframe tracks at their final position', () => {
        actor.keyframe(0,{
          x: 100
        }).keyframe(1000, {
          y: 200
        });

        actor._updateState(500);
        assert.equal(actor.get().x, 100);
      });
    });

    describe('applying easing curves', () => {
      it('easing is taken from the destination frame', () => {
        let tweenableComparator;

        actor
          .keyframe(0, { x: 0 }, 'linear')
          .keyframe(1000, { x: 100 }, 'easeInSine')
          .keyframe(2000, { x: 200 }, 'easeOutCirc');

        tweenableComparator =
          interpolate({ x: 0 }, { x: 100 }, 0.5, 'easeInSine');

        actor._updateState(500);
        assert.equal(actor.get().x, tweenableComparator.x);

        tweenableComparator =
          interpolate({ x: 100 }, { x: 200 }, 0.5, 'easeOutCirc');

        actor._updateState(1500);
        assert.equal(actor.get().x, tweenableComparator.x);
      });
    });

    describe('#wasActive management', () => {
      it('updates wasActive for arbitrary state updates', () => {
        actor
          .keyframe(0,{ x: 0 })
          .setActive(250, false)
          .setActive(750, true)
          .keyframe(1000, { x: 100 });

        actor._updateState(100);
        assert(actor.wasActive);
        assert.equal(actor.get().x, 10);

        actor._updateState(500);
        assert.equal(actor.wasActive, false);
        assert.equal(actor.get().x, 10);

        actor._updateState(900);
        assert.ok(actor.wasActive);
        assert.equal(actor.get().x, 90);
      });
    });
  });

  describe('#addKeyframeProperty', () => {
    it('creates a link between property and actor', () => {
      const keyframeProperty = new Rekapi.KeyframeProperty(0, 'x', 50);
      actor.addKeyframeProperty(keyframeProperty);

      assert.equal(keyframeProperty.actor, actor);
      assert.equal(actor._keyframeProperties[keyframeProperty.id], keyframeProperty);
    });
  });

  describe('#removeKeyframe', () => {
    it('removes arbitrary keyframes', () => {
      actor
        .keyframe(0, { x: 1 })
        .keyframe(1000, { x: 2 })
        .keyframe(2000, { x: 3 });

      actor.removeKeyframe(1000);

      assert.equal(Object.keys(actor._keyframeProperties).length, 2);
      assert.equal(actor._propertyTracks.x.length, 2);
      assert.equal(actor._propertyTracks.x[0].value, 1);
      assert.equal(actor._propertyTracks.x[1].value, 3);
    });

    it('removes all keyframes at a given millisecond', () => {
      actor.keyframe(0, {
        x: 0,
        y: 1
      }).keyframe(1000, {
        x: 50,
        y: 51
      }).keyframe(2000, {
        x: 100,
        y: 101
      });

     actor.removeKeyframe(1000);

     assert.equal(actor._propertyTracks.x.length, 2);
     assert.equal(actor._propertyTracks.y.length, 2);
     assert.equal(actor._propertyTracks.x[0].value, 0);
     assert.equal(actor._propertyTracks.x[1].value, 100);
     assert.equal(actor._propertyTracks.y[0].value, 1);
     assert.equal(actor._propertyTracks.y[1].value, 101);
     assert.equal(actor._propertyTracks.x[0].nextProperty, actor._propertyTracks.x[1]);
     assert.equal(actor._propertyTracks.x[1].nextProperty, null);
    });

    it('removes keyframes at the end of a timeline', () => {
      actor
        .keyframe(0, { x: 1 })
        .keyframe(1000, { x: 2 })
        .keyframe(2000, { x: 3 });

      actor.removeKeyframe(2000);

      assert.equal(rekapi.getAnimationLength(), 1000);
      assert.equal(actor._propertyTracks.x.length, 2);
      assert.equal(actor._propertyTracks.x[0].nextProperty, actor._propertyTracks.x[1]);
      assert.equal(actor._propertyTracks.x[1].nextProperty, null);
    });
  });

  describe('#removeAllKeyframes', () => {
    it('removes all keyframes', () => {
      actor
        .keyframe(0, { x: 0 })
        .keyframe(1000, { x: 1 })
        .keyframe(2000, { x: 2 });

      actor.removeAllKeyframes();

      assert.equal(rekapi.getAnimationLength(), 0);
      assert.equal(actor._propertyTracks.x, undefined);
    });
  });

  describe('#copyKeyframe', () => {
    it('copies keyframe properties', () => {
      actor = setupTestActor(rekapi);
      actor.keyframe(0, {
          x: 50
        }, {
          x: 'easeInQuad'
        });

      actor.copyKeyframe(1000, 0);

      const { _propertyTracks } = actor;

      assert.equal(_propertyTracks.x.length, 2);
      assert(_propertyTracks.x[0] !== _propertyTracks.x[1]);
      assert.equal(_propertyTracks.x[0].value, _propertyTracks.x[1].value);
      assert.equal(_propertyTracks.x[0].easing, _propertyTracks.x[1].easing);
    });
  });

  describe('events', () => {
    describe('beforeAddKeyframeProperty', () => {
      it('when fired, reflects the state of the animation prior to adding the keyframe property', () => {
        actor.keyframe(50, { x: 0 });

        rekapi.on('beforeAddKeyframeProperty', () => {
          assert.equal(rekapi.getAnimationLength(), 50);
        });

        actor.keyframe(100, { x: 10 });
      });
    });

    describe('beforeRemoveKeyframeProperty', () => {
      it('when fired, reflects the state of the animation prior to removing the keyframe property', () => {
        actor.keyframe(0, { x: 0 }).keyframe(100, { x: 10 });

        rekapi.on('beforeRemoveKeyframeProperty', () => {
          assert.equal(rekapi.getAnimationLength(), 100);
        });

        actor.removeKeyframeProperty('x', 100);
      });
    });

    describe('removeKeyframePropertyComplete', () => {
      it('when fired, reflects the new state of the animation', () => {
        actor.keyframe(0, { x: 0 }).keyframe(100, { x: 10 });

        rekapi.on('removeKeyframePropertyComplete', () => {
          assert.equal(rekapi.getAnimationLength(), 0);
        });

        actor.removeKeyframeProperty('x', 100);
      });
    });

    describe('removeKeyframePropertyTrack', () => {
      it('fires when a track is removed', () => {
        let eventWasCalled, removedTrackName;

        actor.keyframe(0, { x: 10 });

        rekapi.on('removeKeyframePropertyTrack', (rekapi, trackName) => {
          eventWasCalled = true;
          removedTrackName = trackName;
        });

        actor.removeKeyframe(0);
        assert(eventWasCalled);
        assert.equal(removedTrackName, 'x');
      });
    });
  });
});
<commit_msg>Port/improve modifyKeyframe tests<commit_after>/* global describe:true, it:true, before:true, beforeEach:true, afterEach:true */
import assert from 'assert';
import { contains } from 'lodash';
import { setupTestRekapi, setupTestActor } from './test-utils';

import Rekapi, { Actor } from '../src/main';
import {
  Tweenable,
  interpolate,
  setBezierFunction,
  unsetBezierFunction
} from 'shifty';

describe('Actor', () => {
  let rekapi, actor;

  beforeEach(() => {
    rekapi = setupTestRekapi();
    actor = setupTestActor(rekapi);
  });

  describe('constructor', () => {
    it('is a function', () => {
      assert.equal(typeof Actor, 'function');
    });
  });

  describe('#_updateState', () => {
    describe('interpolating positions', () => {
      describe('actors that start at 0', () => {
        it('interpolates actor positions at arbitrary positions mid-frame', () => {
          actor.keyframe(0, {
            x: 0,
            y: 0
          }).keyframe(1000, {
            x: 100,
            y:100
          });

          actor._updateState(0);
          assert.equal(actor.get().x, 0);
          assert.equal(actor.get().y, 0);

          actor._updateState(500);
          assert.equal(actor.get().x, 50);
          assert.equal(actor.get().y, 50);

          actor._updateState(1000);
          assert.equal(actor.get().x, 100);
          assert.equal(actor.get().y, 100);
        });
      });

      describe('actors that start later than 0', () => {
        it('interpolates actor positions at arbitrary positions mid-frame', () => {
          actor.keyframe(1000, {
            x: 0,
            y: 0
          }).keyframe(2000, {
            x: 100,
            y: 100
          });

          actor._updateState(1000);
          assert.equal(actor.get().x, 0);
          assert.equal(actor.get().y, 0);

          actor._updateState(1500);

          assert.equal(actor.get().x, 50);
          assert.equal(actor.get().y, 50,
            'Value "y" was properly interpolated at position 0.5');

          actor._updateState(2000);
          assert.equal(actor.get().x, 100);
          assert.equal(actor.get().y, 100);
        });

        describe('property look-ahead', () => {
          describe('single track', () => {
            it('looks ahead to first keyframe when computing states prior to actor start', () => {
              actor.keyframe(0, {
                // Nothing!
              }).keyframe(1000, {
                y: 100
              });

              actor._updateState(500);
              assert.equal(actor.get().y, 100);
            });
          });

          describe('multiple tracks', () => {
            it('looks ahead to first keyframe when computing states prior to actor start', () => {
              actor.keyframe(0, {
                x: 50
              }).keyframe(1000, {
                y: 100
              });

              actor._updateState(500);
              assert.equal(actor.get().y, 100);
            });
          });
        });
      });
    });

    describe('computing state past a keyframe track end', () => {
      it('leaves keyframe tracks at their final position', () => {
        actor.keyframe(0,{
          x: 100
        }).keyframe(1000, {
          y: 200
        });

        actor._updateState(500);
        assert.equal(actor.get().x, 100);
      });
    });

    describe('applying easing curves', () => {
      it('easing is taken from the destination frame', () => {
        let tweenableComparator;

        actor
          .keyframe(0, { x: 0 }, 'linear')
          .keyframe(1000, { x: 100 }, 'easeInSine')
          .keyframe(2000, { x: 200 }, 'easeOutCirc');

        tweenableComparator =
          interpolate({ x: 0 }, { x: 100 }, 0.5, 'easeInSine');

        actor._updateState(500);
        assert.equal(actor.get().x, tweenableComparator.x);

        tweenableComparator =
          interpolate({ x: 100 }, { x: 200 }, 0.5, 'easeOutCirc');

        actor._updateState(1500);
        assert.equal(actor.get().x, tweenableComparator.x);
      });
    });

    describe('#wasActive management', () => {
      it('updates wasActive for arbitrary state updates', () => {
        actor
          .keyframe(0,{ x: 0 })
          .setActive(250, false)
          .setActive(750, true)
          .keyframe(1000, { x: 100 });

        actor._updateState(100);
        assert(actor.wasActive);
        assert.equal(actor.get().x, 10);

        actor._updateState(500);
        assert.equal(actor.wasActive, false);
        assert.equal(actor.get().x, 10);

        actor._updateState(900);
        assert.ok(actor.wasActive);
        assert.equal(actor.get().x, 90);
      });
    });
  });

  describe('#addKeyframeProperty', () => {
    it('creates a link between property and actor', () => {
      const keyframeProperty = new Rekapi.KeyframeProperty(0, 'x', 50);
      actor.addKeyframeProperty(keyframeProperty);

      assert.equal(keyframeProperty.actor, actor);
      assert.equal(actor._keyframeProperties[keyframeProperty.id], keyframeProperty);
    });
  });

  describe('#removeKeyframe', () => {
    it('removes arbitrary keyframes', () => {
      actor
        .keyframe(0, { x: 1 })
        .keyframe(1000, { x: 2 })
        .keyframe(2000, { x: 3 });

      actor.removeKeyframe(1000);

      assert.equal(Object.keys(actor._keyframeProperties).length, 2);
      assert.equal(actor._propertyTracks.x.length, 2);
      assert.equal(actor._propertyTracks.x[0].value, 1);
      assert.equal(actor._propertyTracks.x[1].value, 3);
    });

    it('removes all keyframes at a given millisecond', () => {
      actor.keyframe(0, {
        x: 0,
        y: 1
      }).keyframe(1000, {
        x: 50,
        y: 51
      }).keyframe(2000, {
        x: 100,
        y: 101
      });

     actor.removeKeyframe(1000);

     assert.equal(actor._propertyTracks.x.length, 2);
     assert.equal(actor._propertyTracks.y.length, 2);
     assert.equal(actor._propertyTracks.x[0].value, 0);
     assert.equal(actor._propertyTracks.x[1].value, 100);
     assert.equal(actor._propertyTracks.y[0].value, 1);
     assert.equal(actor._propertyTracks.y[1].value, 101);
     assert.equal(actor._propertyTracks.x[0].nextProperty, actor._propertyTracks.x[1]);
     assert.equal(actor._propertyTracks.x[1].nextProperty, null);
    });

    it('removes keyframes at the end of a timeline', () => {
      actor
        .keyframe(0, { x: 1 })
        .keyframe(1000, { x: 2 })
        .keyframe(2000, { x: 3 });

      actor.removeKeyframe(2000);

      assert.equal(rekapi.getAnimationLength(), 1000);
      assert.equal(actor._propertyTracks.x.length, 2);
      assert.equal(actor._propertyTracks.x[0].nextProperty, actor._propertyTracks.x[1]);
      assert.equal(actor._propertyTracks.x[1].nextProperty, null);
    });
  });

  describe('#removeAllKeyframes', () => {
    it('removes all keyframes', () => {
      actor
        .keyframe(0, { x: 0 })
        .keyframe(1000, { x: 1 })
        .keyframe(2000, { x: 2 });

      actor.removeAllKeyframes();

      assert.equal(rekapi.getAnimationLength(), 0);
      assert.equal(actor._propertyTracks.x, undefined);
    });
  });

  describe('#copyKeyframe', () => {
    it('copies keyframe properties', () => {
      actor = setupTestActor(rekapi);
      actor.keyframe(0, {
          x: 50
        }, {
          x: 'easeInQuad'
        });

      actor.copyKeyframe(1000, 0);

      const { _propertyTracks } = actor;

      assert.equal(_propertyTracks.x.length, 2);
      assert(_propertyTracks.x[0] !== _propertyTracks.x[1]);
      assert.equal(_propertyTracks.x[0].value, _propertyTracks.x[1].value);
      assert.equal(_propertyTracks.x[0].easing, _propertyTracks.x[1].easing);
    });
  });

  describe('#modifyKeyframe', () => {
    it('modifies keyframe property value', () => {
      actor
        .keyframe(0, { x: 100 })
        .keyframe(1000, { x: 200 });

      actor.modifyKeyframe(0, { x: 0 });
      assert.equal(actor._propertyTracks.x[0].value, 0);
    });

    it('modifies keyframe property easing', () => {
      actor
        .keyframe(0, { x: 0 }, { x: 'elastic' })
        .keyframe(1000, { x: 100 }, { x: 'elastic' });

      actor.modifyKeyframe(1000, {}, { x: 'linear' });

      assert.equal(actor._propertyTracks.x[1].easing, 'linear');
    });
  });

  describe('events', () => {
    describe('beforeAddKeyframeProperty', () => {
      it('when fired, reflects the state of the animation prior to adding the keyframe property', () => {
        actor.keyframe(50, { x: 0 });

        rekapi.on('beforeAddKeyframeProperty', () => {
          assert.equal(rekapi.getAnimationLength(), 50);
        });

        actor.keyframe(100, { x: 10 });
      });
    });

    describe('beforeRemoveKeyframeProperty', () => {
      it('when fired, reflects the state of the animation prior to removing the keyframe property', () => {
        actor.keyframe(0, { x: 0 }).keyframe(100, { x: 10 });

        rekapi.on('beforeRemoveKeyframeProperty', () => {
          assert.equal(rekapi.getAnimationLength(), 100);
        });

        actor.removeKeyframeProperty('x', 100);
      });
    });

    describe('removeKeyframePropertyComplete', () => {
      it('when fired, reflects the new state of the animation', () => {
        actor.keyframe(0, { x: 0 }).keyframe(100, { x: 10 });

        rekapi.on('removeKeyframePropertyComplete', () => {
          assert.equal(rekapi.getAnimationLength(), 0);
        });

        actor.removeKeyframeProperty('x', 100);
      });
    });

    describe('removeKeyframePropertyTrack', () => {
      it('fires when a track is removed', () => {
        let eventWasCalled, removedTrackName;

        actor.keyframe(0, { x: 10 });

        rekapi.on('removeKeyframePropertyTrack', (rekapi, trackName) => {
          eventWasCalled = true;
          removedTrackName = trackName;
        });

        actor.removeKeyframe(0);
        assert(eventWasCalled);
        assert.equal(removedTrackName, 'x');
      });
    });
  });
});
<|endoftext|>