<commit_before>
"use strict";

let isDateTime = new RegExp(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/);
let isDate = new RegExp(/^(\d{4})-(\d{2})-(\d{2})/);
let mapper = {
    Jan: '01',
    Feb: '02',
    Mar: '03',
    Apr: '04',
    May: '05',
    Jun: '06',
    Jul: '07',
    Aug: '08',
    Sep: '09',
    Oct: '10',
    Nov: '11',
    Dec: '12'
};

function map(month) {
    return mapper[month];
};

/**
 * A data object with dirty checking and optional virtual members or other
 * additional logic. Implementors should generally extend this class, but this
 * is not required.
 */
export default class Model {

    /**
     * Class constructor.
     */
    constructor() {
        /**
         * The "initial" state for this model. Used to check for pristineness.
         * This is automatically set when $load is called.
         */
        this.$initial = undefined;
        /**
         * Internal data storage for this model.
         */
        this.$data = {};
        /**
         * Set to true if the model is scheduled for deletion.
         */
        this.$deleted = false;
    }

    /**
     * Static method to create a new Model of this type, optionally specifying
     * default data and a class to derive from.
     *
     * @param object data Hash of key/value pairs of data.
     * @param class derivedClass Optional class to actually use when
     *  instantiating (defaults to the current class).
     * @return mixed A new model of the desired class.
     */
    static $create(data = {}, derivedClass = undefined) {
        derivedClass = derivedClass || Model;
        let instance = new derivedClass();
        instance.$load(data);
        instance.$initial = undefined;
        return instance;
    }

    /**
     * Load data into this model instance.
     *
     * @param object data Hash of key/value pairs of data.
     * @return self
     */
    $load(data = {}) {
        this.$initial = data;
        for (let key in data) {
            this.addField(key);
            this[key] = data[key];
        }
        return this;
    }

    addField(key) {
        if (!this.hasOwnProperty(key)) {
            var props = {enumerable: true, configurable: true};
            props.get = () => {
                return this.$data[key];
            };
            props.set = value => {
                if (value != undefined) {
                    let checkDate = isDateTime.exec(value);
                    if (!checkDate) {
                        checkDate = isDate.exec(value);
                    }
                    if (checkDate) {
                        checkDate.shift();
                        // Javascript months are offset by 0 for reasons only Brendan Eich knows...
                        checkDate[1]--;
                        value = new Date(...checkDate);
                    }
                }
                this.$data[key] = value;
            };
            Object.defineProperty(this, key, props);
        }
    }

    /**
     * Virtual property to check if this model is "new".
     *
     * @return boolean True if new, false if existing.
     */
    get $new() {
        return !this.$promise && this.$initial === undefined;
    }

    /**
     * Virtual property to check if this model is "dirty".
     *
     * @return boolean True if dirty, false if pristine.
     */
    get $dirty() {
        if (this.$deleted) {
            return true;
        }
        for (let key in this) {
            if (key.substring(0, 1) == '$') {
                continue;
            }
            if (!(key in this.$data)) {
                let value = this[key];
                delete this[key];
                this.addField(key, value);
            }
            if (!(this.$initial && this.$data[key] == this.$initial[key])) {
                if (this.$data[key] !== null && this.$data[key] !== undefined && ('' + this.$data[key]).replace(/\s+/, '').length) {
                    return true;
                }
            } else if (typeof this.$data[key] == 'object' && this.$data[key] != null) {
                if ('map' in this.$data[key]) {
                    let dirty = false;
                    this.$data[key].map(elem => {
                        if (typeof elem == 'object' && elem.$dirty || elem.$deleted) {
                            dirty = true;
                        }
                    });
                    if (dirty) {
                        return true;
                    }
                } else if (this.$data[key].$dirty || this.$data[key].$deleted) {
                    return true;
                }
            }
        }
        return false;
    }

};

<commit_msg>use the prepared $data as initial value, might contain dates<commit_after>
"use strict";

let isDateTime = new RegExp(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/);
let isDate = new RegExp(/^(\d{4})-(\d{2})-(\d{2})/);
let mapper = {
    Jan: '01',
    Feb: '02',
    Mar: '03',
    Apr: '04',
    May: '05',
    Jun: '06',
    Jul: '07',
    Aug: '08',
    Sep: '09',
    Oct: '10',
    Nov: '11',
    Dec: '12'
};

function map(month) {
    return mapper[month];
};

/**
 * A data object with dirty checking and optional virtual members or other
 * additional logic. Implementors should generally extend this class, but this
 * is not required.
 */
export default class Model {

    /**
     * Class constructor.
     */
    constructor() {
        /**
         * The "initial" state for this model. Used to check for pristineness.
         * This is automatically set when $load is called.
         */
        this.$initial = undefined;
        /**
         * Internal data storage for this model.
         */
        this.$data = {};
        /**
         * Set to true if the model is scheduled for deletion.
         */
        this.$deleted = false;
    }

    /**
     * Static method to create a new Model of this type, optionally specifying
     * default data and a class to derive from.
     *
     * @param object data Hash of key/value pairs of data.
     * @param class derivedClass Optional class to actually use when
     *  instantiating (defaults to the current class).
     * @return mixed A new model of the desired class.
     */
    static $create(data = {}, derivedClass = undefined) {
        derivedClass = derivedClass || Model;
        let instance = new derivedClass();
        instance.$load(data);
        instance.$initial = undefined;
        return instance;
    }

    /**
     * Load data into this model instance.
     *
     * @param object data Hash of key/value pairs of data.
     * @return self
     */
    $load(data = {}) {
        for (let key in data) {
            this.addField(key);
            this[key] = data[key];
        }
        this.$initial = this.$data;
        return this;
    }

    addField(key) {
        if (!this.hasOwnProperty(key)) {
            var props = {enumerable: true, configurable: true};
            props.get = () => {
                return this.$data[key];
            };
            props.set = value => {
                if (value != undefined) {
                    let checkDate = isDateTime.exec(value);
                    if (!checkDate) {
                        checkDate = isDate.exec(value);
                    }
                    if (checkDate) {
                        checkDate.shift();
                        // Javascript months are offset by 0 for reasons only Brendan Eich knows...
                        checkDate[1]--;
                        value = new Date(...checkDate);
                    }
                }
                this.$data[key] = value;
            };
            Object.defineProperty(this, key, props);
        }
    }

    /**
     * Virtual property to check if this model is "new".
     *
     * @return boolean True if new, false if existing.
     */
    get $new() {
        return !this.$promise && this.$initial === undefined;
    }

    /**
     * Virtual property to check if this model is "dirty".
     *
     * @return boolean True if dirty, false if pristine.
     */
    get $dirty() {
        if (this.$deleted) {
            return true;
        }
        for (let key in this) {
            if (key.substring(0, 1) == '$') {
                continue;
            }
            if (!(key in this.$data)) {
                let value = this[key];
                delete this[key];
                this.addField(key, value);
            }
            if (!(this.$initial && this.$data[key] == this.$initial[key])) {
                if (this.$data[key] !== null && this.$data[key] !== undefined && ('' + this.$data[key]).replace(/\s+/, '').length) {
                    return true;
                }
            } else if (typeof this.$data[key] == 'object' && this.$data[key] != null) {
                if ('map' in this.$data[key]) {
                    let dirty = false;
                    this.$data[key].map(elem => {
                        if (typeof elem == 'object' && elem.$dirty || elem.$deleted) {
                            dirty = true;
                        }
                    });
                    if (dirty) {
                        return true;
                    }
                } else if (this.$data[key].$dirty || this.$data[key].$deleted) {
                    return true;
                }
            }
        }
        return false;
    }

};

<|endoftext|>


<commit_before>(function() {
  'use strict';
  var expect     = require('chai').expect;
  var shell      = require('shelljs');
  var fs         = require('fs');
  var ls         = shell.ls;

  var projectNameMock            = 'testProject';
  var projectNameWithoutDepsMock = 'testProjectNoDeps';
  var projectNameDevMock         = 'testProjectDev';

  describe('[e2e] koappCli', function() {
    this.timeout(180000);

    describe('init test', function() {
      describe('on success', function () {
        it('should create a standard koapp project', function(done) {
          var command = 'koapp init ' + projectNameMock;
          shell.exec(command, function(err) {
            var files = ls().stdout;
            var visualizer = 'com.kingofapp.visualizer';
            var projectFiles = ls(projectNameMock).stdout.replace(/\n/g, '');
            expect(err).to.be.equal(0);
            expect(files.indexOf('testProject')).to.be.not.equal(-1);
            expect(projectFiles).to.be.a('string');
            expect(projectFiles).to.be.equal(visualizer);
            done();
          });
        });

        it('should create a no deps koapp project', function(done) {
          var command = 'koapp init ' + projectNameWithoutDepsMock + ' --no-deps';
          shell.exec(command, function(err) {
            var files = ls().stdout;
            var visualizer = 'com.kingofapp.visualizer';
            var projectFiles = ls(projectNameWithoutDepsMock).stdout.replace(/\n/g, '');

            expect(err).to.be.equal(0);
            expect(files.indexOf('testProject')).to.be.not.equal(-1);
            expect(projectFiles).to.be.a('string');
            expect(projectFiles).to.be.equal(visualizer);
            done();
          });
        });

        it('should create a dev koapp project', function(done) {
          var command = 'koapp init ' + projectNameDevMock + ' --dev';
          shell.exec(command, function(err) {
            var files = ls().stdout;
            var visualizer = 'com.kingofapp.visualizer.dev';
            var projectFiles = ls(projectNameDevMock).stdout.replace(/\n/g, '');
            expect(err).to.be.equal(0);
            expect(files.indexOf('testProjectDev')).to.be.not.equal(-1);
            expect(projectFiles).to.be.a('string');
            expect(projectFiles).to.be.equal(visualizer);
            done();
          });
        });
      });
    });

    describe('add test', function() {
      beforeEach(function(done) {
        shell.cd(__dirname);
        done();
      });

      describe('on a standard koapp project', function() {
        describe('on success', function() {
          it('should add a service to a standard koapp project', function(done) {
            changeFolderToTest({
              env: 'standard',
              pluginType: 'service',
              pluginName: 'test',
              done: done
            });
          });

          it('should add a theme to a standard koapp project', function(done) {
            changeFolderToTest({
              env: 'standard',
              pluginType: 'theme',
              pluginName: 'candy',
              done: done
            });
          });

          it('should add a spinner to a standard koapp project', function(done) {
            changeFolderToTest({
              env: 'standard',
              pluginType: 'spinner',
              pluginName: 'fitness',
              done: done
            });
          });
        });
      });

      describe('on a dev koapp project', function() {
        describe('on success', function() {
          it('should add a service to a dev koapp project', function(done) {
            changeFolderToTest({
              env: 'dev',
              pluginType: 'service',
              pluginName: 'test',
              done: done
            });
          });

          it('should add a theme to a dev koapp project', function(done) {
            changeFolderToTest({
              env: 'dev',
              pluginType: 'theme',
              pluginName: 'candy',
              done: done
            });
          });

          it('should add a spinner to a dev koapp project', function(done) {
            changeFolderToTest({
              env: 'dev',
              pluginType: 'spinner',
              pluginName: 'fitness',
              done: done
            });
          });
        });
      });
    });

    describe('remove test', function() {
      beforeEach(function(done) {
        shell.cd(__dirname);
        done();
      });

      describe('on a standard koapp project', function() {
        describe('on success', function() {
          it('should remove a module from a standard koapp project', function(done) {
            var command = 'koapp remove module "/menu-abcd/elements-abcd"';
            shell.cd('../testProject/com.kingofapp.visualizer/www/modules');

            testPlugin({
              pluginType: 'module',
              pluginName: 'demo',
              command: command,
              action: 'remove',
              done: done
            });
          });

          it('should remove a service from a standard koapp project', function(done) {
            var command = 'koapp remove service test';
            shell.cd('../testProject/com.kingofapp.visualizer/www/modules');

            testPlugin({
              pluginType: 'service',
              pluginName: 'test',
              command: command,
              action: 'remove',
              done: done
            });
          });
        });
      });
    });

    after(function(done) {
      shell.cd('../../../..');
      shell.rm('-rf', ['./testProject', './testProjectNoDeps', './testProjectDev']);

      var files = shell.ls().stdout;
      expect(files.indexOf(projectNameMock)).to.be.equal(-1);
      expect(files.indexOf(projectNameWithoutDepsMock)).to.be.equal(-1);
      expect(files.indexOf(projectNameDevMock)).to.be.equal(-1);
      done();
    });
  });

  // Helper functions
  function changeFolderToTest(options) {
    var command = 'koapp add ' + options.pluginType + ' ' + options.pluginName;
    var visualizerFolder = options.env === 'standard' ?
                                           './../' + projectNameMock + '/com.kingofapp.visualizer/www/' + options.pluginType + 's' :
                                           './../' + projectNameDevMock + '/com.kingofapp.visualizer.dev/www/' + options.pluginType + 's';
    shell.cd(visualizerFolder);

    options.command = command;
    testPlugin(options);
  }

  function testPlugin(options) {
    shell.exec(options.command, function(err) {
      expect(err).to.be.equal(0);

      var files = shell.ls().stdout.split('\n');

      if (options.action === 'remove') {
        checkStructureFile(options);
      } else {
        expect(files.indexOf(options.pluginName)).to.be.not.equal(-1);
        checkStructureFile(options);
      }
    });
  }

  function checkStructureFile(options) {
    shell.cd('./../core');
    var files = shell.ls().stdout.split('\n');

    expect(files.indexOf('structure.json')).to.be.not.equal(-1);

    fs.readFile('structure.json', 'utf-8', function(err, data) {
      expect(err).to.be.a('null');

      var jsonData = JSON.parse(data);

      if (options.action && options.action === 'remove') {
        options.pluginType === 'service' ?
                               removeServiceStructureExpects(options, jsonData) :
                               removeModuleStructureExpects(options, jsonData) ;
      } else {
        options.pluginType === 'service' ?
                               addServiceExpects(options, jsonData) :
                               addThemeExpects(options, jsonData);
      }
    });
  }

  function addServiceExpects(options, jsonData) {
    expect(jsonData).to.have.property('services');
    expect(jsonData.services).to.have.property('test');
    expect(jsonData.services.test.name).to.be.equal('test');
    checkPluginFolder(options);
  }

  function addThemeExpects(options, jsonData) {
    expect(jsonData).to.have.property('config');
    expect(jsonData.config).to.have.property(options.pluginType);
    expect(jsonData.config[options.pluginType]).to.have.property('identifier');
    expect(jsonData.config[options.pluginType].identifier).to.be.equal(options.pluginName);
    options.done();
  }

  function removeServiceStructureExpects(options, jsonData) {
    expect(jsonData).to.have.property('services');
    expect(jsonData.services).to.not.have.property('');
    removePluginFolder(options);
  }

  function removeModuleStructureExpects(options, jsonData) {
    expect(jsonData).to.have.property('modules');
    expect(jsonData.modules).to.have.property('/menu-abcd');
    expect(jsonData.modules['/menu-abcd']).to.not.have.property('/elements-abcd');
    removePluginFolder(options);
  }

  function checkPluginFolder(options) {
    shell.cd('..');
    shell.cd('./' + options.pluginType + 's');
    var files = ls().stdout.split('\n');

    expect(files.indexOf(options.pluginName)).to.be.not.equal(-1);
    options.done();
  }

  function removePluginFolder(options) {
    shell.cd('..');
    shell.cd('./' + options.pluginType + 's');
    shell.rm('-rf', './' + options.pluginName);

    var files = ls().stdout.split('\n');

    expect(files.indexOf(options.pluginName)).to.be.equal(-1);
    options.done();
  }
})();
<commit_msg>Extend e2e tests timeout<commit_after>(function() {
  'use strict';
  var expect     = require('chai').expect;
  var shell      = require('shelljs');
  var fs         = require('fs');
  var ls         = shell.ls;

  var projectNameMock            = 'testProject';
  var projectNameWithoutDepsMock = 'testProjectNoDeps';
  var projectNameDevMock         = 'testProjectDev';

  describe('[e2e] koappCli', function() {
    this.timeout(300000);

    describe('init test', function() {
      describe('on success', function () {
        it('should create a standard koapp project', function(done) {
          var command = 'koapp init ' + projectNameMock;
          shell.exec(command, function(err) {
            var files = ls().stdout;
            var visualizer = 'com.kingofapp.visualizer';
            var projectFiles = ls(projectNameMock).stdout.replace(/\n/g, '');
            expect(err).to.be.equal(0);
            expect(files.indexOf('testProject')).to.be.not.equal(-1);
            expect(projectFiles).to.be.a('string');
            expect(projectFiles).to.be.equal(visualizer);
            done();
          });
        });

        it('should create a no deps koapp project', function(done) {
          var command = 'koapp init ' + projectNameWithoutDepsMock + ' --no-deps';
          shell.exec(command, function(err) {
            var files = ls().stdout;
            var visualizer = 'com.kingofapp.visualizer';
            var projectFiles = ls(projectNameWithoutDepsMock).stdout.replace(/\n/g, '');

            expect(err).to.be.equal(0);
            expect(files.indexOf('testProject')).to.be.not.equal(-1);
            expect(projectFiles).to.be.a('string');
            expect(projectFiles).to.be.equal(visualizer);
            done();
          });
        });

        it('should create a dev koapp project', function(done) {
          var command = 'koapp init ' + projectNameDevMock + ' --dev';
          shell.exec(command, function(err) {
            var files = ls().stdout;
            var visualizer = 'com.kingofapp.visualizer.dev';
            var projectFiles = ls(projectNameDevMock).stdout.replace(/\n/g, '');
            expect(err).to.be.equal(0);
            expect(files.indexOf('testProjectDev')).to.be.not.equal(-1);
            expect(projectFiles).to.be.a('string');
            expect(projectFiles).to.be.equal(visualizer);
            done();
          });
        });
      });
    });

    describe('add test', function() {
      beforeEach(function(done) {
        shell.cd(__dirname);
        done();
      });

      describe('on a standard koapp project', function() {
        describe('on success', function() {
          it('should add a service to a standard koapp project', function(done) {
            changeFolderToTest({
              env: 'standard',
              pluginType: 'service',
              pluginName: 'test',
              done: done
            });
          });

          it('should add a theme to a standard koapp project', function(done) {
            changeFolderToTest({
              env: 'standard',
              pluginType: 'theme',
              pluginName: 'candy',
              done: done
            });
          });

          it('should add a spinner to a standard koapp project', function(done) {
            changeFolderToTest({
              env: 'standard',
              pluginType: 'spinner',
              pluginName: 'fitness',
              done: done
            });
          });
        });
      });

      describe('on a dev koapp project', function() {
        describe('on success', function() {
          it('should add a service to a dev koapp project', function(done) {
            changeFolderToTest({
              env: 'dev',
              pluginType: 'service',
              pluginName: 'test',
              done: done
            });
          });

          it('should add a theme to a dev koapp project', function(done) {
            changeFolderToTest({
              env: 'dev',
              pluginType: 'theme',
              pluginName: 'candy',
              done: done
            });
          });

          it('should add a spinner to a dev koapp project', function(done) {
            changeFolderToTest({
              env: 'dev',
              pluginType: 'spinner',
              pluginName: 'fitness',
              done: done
            });
          });
        });
      });
    });

    describe('remove test', function() {
      beforeEach(function(done) {
        shell.cd(__dirname);
        done();
      });

      describe('on a standard koapp project', function() {
        describe('on success', function() {
          it('should remove a module from a standard koapp project', function(done) {
            var command = 'koapp remove module "/menu-abcd/elements-abcd"';
            shell.cd('../testProject/com.kingofapp.visualizer/www/modules');

            testPlugin({
              pluginType: 'module',
              pluginName: 'demo',
              command: command,
              action: 'remove',
              done: done
            });
          });

          it('should remove a service from a standard koapp project', function(done) {
            var command = 'koapp remove service test';
            shell.cd('../testProject/com.kingofapp.visualizer/www/modules');

            testPlugin({
              pluginType: 'service',
              pluginName: 'test',
              command: command,
              action: 'remove',
              done: done
            });
          });
        });
      });
    });

    after(function(done) {
      shell.cd('../../../..');
      shell.rm('-rf', ['./testProject', './testProjectNoDeps', './testProjectDev']);

      var files = shell.ls().stdout;
      expect(files.indexOf(projectNameMock)).to.be.equal(-1);
      expect(files.indexOf(projectNameWithoutDepsMock)).to.be.equal(-1);
      expect(files.indexOf(projectNameDevMock)).to.be.equal(-1);
      done();
    });
  });

  // Helper functions
  function changeFolderToTest(options) {
    var command = 'koapp add ' + options.pluginType + ' ' + options.pluginName;
    var visualizerFolder = options.env === 'standard' ?
                                           './../' + projectNameMock + '/com.kingofapp.visualizer/www/' + options.pluginType + 's' :
                                           './../' + projectNameDevMock + '/com.kingofapp.visualizer.dev/www/' + options.pluginType + 's';
    shell.cd(visualizerFolder);

    options.command = command;
    testPlugin(options);
  }

  function testPlugin(options) {
    shell.exec(options.command, function(err) {
      expect(err).to.be.equal(0);

      var files = shell.ls().stdout.split('\n');

      if (options.action === 'remove') {
        checkStructureFile(options);
      } else {
        expect(files.indexOf(options.pluginName)).to.be.not.equal(-1);
        checkStructureFile(options);
      }
    });
  }

  function checkStructureFile(options) {
    shell.cd('./../core');
    var files = shell.ls().stdout.split('\n');

    expect(files.indexOf('structure.json')).to.be.not.equal(-1);

    fs.readFile('structure.json', 'utf-8', function(err, data) {
      expect(err).to.be.a('null');

      var jsonData = JSON.parse(data);

      if (options.action && options.action === 'remove') {
        options.pluginType === 'service' ?
                               removeServiceStructureExpects(options, jsonData) :
                               removeModuleStructureExpects(options, jsonData) ;
      } else {
        options.pluginType === 'service' ?
                               addServiceExpects(options, jsonData) :
                               addThemeExpects(options, jsonData);
      }
    });
  }

  function addServiceExpects(options, jsonData) {
    expect(jsonData).to.have.property('services');
    expect(jsonData.services).to.have.property('test');
    expect(jsonData.services.test.name).to.be.equal('test');
    checkPluginFolder(options);
  }

  function addThemeExpects(options, jsonData) {
    expect(jsonData).to.have.property('config');
    expect(jsonData.config).to.have.property(options.pluginType);
    expect(jsonData.config[options.pluginType]).to.have.property('identifier');
    expect(jsonData.config[options.pluginType].identifier).to.be.equal(options.pluginName);
    options.done();
  }

  function removeServiceStructureExpects(options, jsonData) {
    expect(jsonData).to.have.property('services');
    expect(jsonData.services).to.not.have.property('');
    removePluginFolder(options);
  }

  function removeModuleStructureExpects(options, jsonData) {
    expect(jsonData).to.have.property('modules');
    expect(jsonData.modules).to.have.property('/menu-abcd');
    expect(jsonData.modules['/menu-abcd']).to.not.have.property('/elements-abcd');
    removePluginFolder(options);
  }

  function checkPluginFolder(options) {
    shell.cd('..');
    shell.cd('./' + options.pluginType + 's');
    var files = ls().stdout.split('\n');

    expect(files.indexOf(options.pluginName)).to.be.not.equal(-1);
    options.done();
  }

  function removePluginFolder(options) {
    shell.cd('..');
    shell.cd('./' + options.pluginType + 's');
    shell.rm('-rf', './' + options.pluginName);

    var files = ls().stdout.split('\n');

    expect(files.indexOf(options.pluginName)).to.be.equal(-1);
    options.done();
  }
})();
<|endoftext|>


<commit_before>/*
zoomwall.js

The MIT License (MIT)

Copyright (c) 2014 Eric Leong

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

export var zoomwall = {

  create: function (blocks, enableKeys) {
    this.blocks = blocks;

    const imgChildren = blocks.querySelectorAll('img');
    this.imgChildren = imgChildren;

    zoomwall.resize(imgChildren);

    blocks.classList.remove('loading');
    // shrink blocks if an empty space is clicked
    blocks.addEventListener('click', function () {
      if (this.children && this.children.length > 0) {
        zoomwall.shrink(this.children[0]);
      }
    });

    // add click listeners to blocks
    for (var i = 0; i < imgChildren.length; i++) {
      imgChildren[i].addEventListener('click', zoomwall.animate);
    }

    // add key down listener
    if (enableKeys) {
      zoomwall.keys(blocks);
    }
  },

  keys: function (blocks) {
    var keyPager = function (e) {
      if (e.defaultPrevented) {
        return;
      }

      // either use the provided blocks, or query for the first lightboxed zoomwall
      var elem = blocks || document.getElementsByClassName('zoomwall lightbox')[0];

      if (elem) {
        switch (e.keyCode) {
        case 27: // escape
          if (elem.children && elem.children.length > 0) {
            zoomwall.shrink(elem.children[0]);
          }
          e.preventDefault();

          break;

        case 37: // left
          zoomwall.page(elem, false);
          e.preventDefault();

          break;

        case 39: // right
          zoomwall.page(elem, true);
          e.preventDefault();

          break;
        }
      }
    };

    document.addEventListener('keydown', keyPager);

    return keyPager;
  },

  resizeRow: function (row, width) {
    if (row && row.length > 1) {
      for (var i in row) {
        row[i].style.width = (parseInt(window.getComputedStyle(row[i]).width, 10) / width * 100) + '%';
        row[i].style.height = 'auto';
      }
    }
  },

  calcRowWidth: function (row) {
    var width = 0;

    for (var i in row) {
      width += parseInt(window.getComputedStyle(row[i]).width, 10);
    }

    return width;
  },

  resize: function (blocks) {
    var row = [];
    var top = -1;

    for (var c = 0; c < blocks.length; c++) {
      var block = blocks[c];

      if (block) {
        if (top == -1) {
          top = block.offsetTop;

        } else if (block.offsetTop != top) {
          zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));

          row = [];
          top = block.offsetTop;
        }

        row.push(block);
      }
    }

    zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));
  },

  reset: function (block) {
    block.style.transform = 'translate(0, 0) scale(1)';
    block.style.webkitTransform = 'translate(0, 0) scale(1)';
    block.classList.remove('active');
  },

  shrink: function (block) {
    this.blocks.classList.remove('lightbox');

    // reset all blocks
    zoomwall.reset(block);

    const imgChildrenArray = [...this.imgChildren];
    const blockIndex = imgChildrenArray.indexOf(block)

    var prev = this.imgChildren[blockIndex - 1];
    while (prev) {
      zoomwall.reset(prev);
      prev = this.imgChildren[imgChildrenArray.indexOf(prev) - 1];
    }

    var next = this.imgChildren[blockIndex + 1];
    while (next) {
      zoomwall.reset(next);
      next = this.imgChildren[imgChildrenArray.indexOf(next) + 1];
    }

    // swap images
    if (block.dataset.lowres) {
      block.src = block.dataset.lowres;
    }
    if (block.dataset.sizes) {
      block.sizes = block.dataset.sizes;
    }
  },

  expand: function (block) {

    const gallery = this.blocks;

    block.classList.add('active');
    gallery.classList.add('lightbox');

    // parent dimensions
    var parentStyle = window.getComputedStyle(gallery);

    var parentWidth = parseInt(parentStyle.width, 10);
    var parentHeight = parseInt(parentStyle.height, 10);

    var parentTop = gallery.getBoundingClientRect().top;

    // block dimensions
    var blockStyle = window.getComputedStyle(block);

    var blockWidth = parseInt(blockStyle.width, 10);
    var blockHeight = parseInt(blockStyle.height, 10);

    // determine maximum height
    var targetHeight = window.innerHeight;

    if (parentHeight < window.innerHeight) {
      targetHeight = parentHeight;
    } else if (parentTop > 0) {
      targetHeight -= parentTop;
    }

    // swap images
    if (block.dataset.highres) {
      if (block.src != block.dataset.highres && block.dataset.lowres === undefined) {
        block.dataset.lowres = block.src;
      }
      block.src = block.dataset.highres;
    }
    if (block.sizes) { // responsive images
      block.dataset.sizes = block.sizes;
      block.sizes = '100vw'; // image is now 100% of the viewport width
    }

    // determine what blocks are on this row
    var row = [];
    row.push(block);

    const imgChildrenArray = [...this.imgChildren];

    var next = this.imgChildren[imgChildrenArray.indexOf(block) + 1];

    while (next && next.offsetTop == block.offsetTop) {
      row.push(next);

      next = this.imgChildren[imgChildrenArray.indexOf(next) + 1];
    }

    var prev = this.imgChildren[imgChildrenArray.indexOf(block) - 1];

    while (prev && prev.offsetTop == block.offsetTop) {
      row.unshift(prev);

      prev = this.imgChildren[imgChildrenArray.indexOf(prev) - 1];
    }

    // calculate scale
    var scale = targetHeight / blockHeight;

    if (blockWidth * scale > parentWidth) {
      scale = parentWidth / blockWidth;
    }

    // determine offset
    var offsetY = parentTop - gallery.offsetTop + block.offsetTop;

    if (offsetY > 0) {
      if (parentHeight < window.innerHeight) {
        offsetY -= targetHeight / 2 - blockHeight * scale / 2;
      }

      if (parentTop > 0) {
        offsetY -= parentTop;
      }
    }

    var leftOffsetX = 0;  // shift in current row

    for (var i = 0; i < row.length && row[i] != block; i++) {
      leftOffsetX += parseInt(window.getComputedStyle(row[i]).width, 10) * scale;
    }

    leftOffsetX = parentWidth / 2 - blockWidth * scale / 2 - leftOffsetX;

    var rightOffsetX = 0;  // shift in current row

    for (var j = row.length - 1; j >= 0 && row[j] != block; j--) {
      rightOffsetX += parseInt(window.getComputedStyle(row[j]).width, 10) * scale;
    }

    rightOffsetX = parentWidth / 2 - blockWidth * scale / 2 - rightOffsetX;

    var percentageOffsetX;
    var percentageOffsetY;

    // transform current row
    var itemOffset = 0; // offset due to scaling of previous items
    var prevWidth = 0;

    for (var k = 0; k < row.length; k++) {
      itemOffset += (prevWidth * scale - prevWidth);
      prevWidth = parseInt(window.getComputedStyle(row[k]).width, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = -offsetY / parseInt(window.getComputedStyle(row[k]).height, 10) * 100;

      row[k].style.transformOrigin = '0% 0%';
      row[k].style.webkitTransformOrigin = '0% 0%';
      row[k].style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      row[k].style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
    }

    // transform items after
    var curTop;
    var nextOffsetY = blockHeight * (scale - 1) - offsetY;
    var prevHeight;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var nextItem = this.imgChildren[imgChildrenArray.indexOf(row[row.length - 1]) + 1];
    var nextRowTop = -1;

    while (nextItem) {
      curTop = nextItem.offsetTop;

      if (curTop == nextRowTop) {
        itemOffset += prevWidth * scale - prevWidth;
      } else {

        if (nextRowTop != -1) {
          itemOffset = 0;
          nextOffsetY += prevHeight * (scale - 1);
        }

        nextRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(nextItem).width, 10);
      prevHeight = parseInt(window.getComputedStyle(nextItem).height, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = nextOffsetY / prevHeight * 100;

      nextItem.style.transformOrigin = '0% 0%';
      nextItem.style.webkitTransformOrigin = '0% 0%';
      nextItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      nextItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      nextItem = this.imgChildren[imgChildrenArray.indexOf(nextItem) + 1];
    }

    // transform items before
    var prevOffsetY = -offsetY;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var prevItem = this.imgChildren[imgChildrenArray.indexOf(row[0]) - 1];
    var prevRowTop = -1;

    while (prevItem) {
      curTop = prevItem.offsetTop;

      if (curTop == prevRowTop) {
        itemOffset -= prevWidth * scale - prevWidth;
      } else {
        itemOffset = 0;
        prevOffsetY -= parseInt(window.getComputedStyle(prevItem).height, 10) * (scale - 1);
        prevRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(prevItem).width, 10);

      percentageOffsetX = (itemOffset - rightOffsetX) / prevWidth * 100;
      percentageOffsetY = prevOffsetY / parseInt(window.getComputedStyle(prevItem).height, 10) * 100;

      prevItem.style.transformOrigin = '100% 0%';
      prevItem.style.webkitTransformOrigin = '100% 0%';
      prevItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      prevItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      prevItem = this.imgChildren[imgChildrenArray.indexOf(prevItem) - 1];
    }
  },

  animate: function (e) {
    if (this.classList.contains('active')) {
      zoomwall.shrink(this);
    } else {
      var actives = this.parentNode.getElementsByClassName('active');

      for (var i = 0; i < actives.length; i++) {
        actives[i].classList.remove('active');
      }

      zoomwall.expand(this);
    }

    e.stopPropagation();
  },

  page: function (blocks, isNext) {
    var actives = blocks.getElementsByClassName('active');

    if (actives && actives.length > 0) {

      var current = actives[0];
      var next;

      const imgChildrenArray = [...this.imgChildren];
      const blockIndex = imgChildrenArray.indexOf(current)

      if (isNext) {
        next = this.imgChildren[blockIndex + 1];
      } else {
        next = this.imgChildren[blockIndex - 1];
      }

      if (next) {
        current.classList.remove('active');
        // swap images
        if (current.dataset.lowres) {
          current.src = current.dataset.lowres;
        }
        if (current.dataset.sizes) {
          current.sizes = current.dataset.sizes;
        }

        zoomwall.expand(next);
      }
    }
  }
};<commit_msg>Do not store any member variables.<commit_after>/*
zoomwall.js

The MIT License (MIT)

Copyright (c) 2014 Eric Leong

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

export var zoomwall = {

  create: function (blocks, enableKeys) {
    const imgChildren = blocks.querySelectorAll('img');

    zoomwall.resize(imgChildren);

    blocks.classList.remove('loading');
    // shrink blocks if an empty space is clicked
    blocks.addEventListener('click', function () {
      if (this.children && this.children.length > 0) {
        zoomwall.shrink(this.children[0]);
      }
    });

    // add click listeners to blocks
    for (var i = 0; i < imgChildren.length; i++) {
      imgChildren[i].addEventListener('click', zoomwall.animate);
    }

    // add key down listener
    if (enableKeys) {
      zoomwall.keys(blocks);
    }
  },

  findWall: function(elem) {
    var parent;

    do {
      parent = elem.parentElement;

      if (parent.classList.contains('zoomwall')) {
        return parent;
      }
    } while (parent);

    return null;
  },

  keys: function (blocks) {
    var keyPager = function (e) {
      if (e.defaultPrevented) {
        return;
      }

      // either use the provided blocks, or query for the first lightboxed zoomwall
      var elem = blocks || document.getElementsByClassName('zoomwall lightbox')[0];

      if (elem) {
        switch (e.keyCode) {
        case 27: // escape
          if (elem.children && elem.children.length > 0) {
            zoomwall.shrink(elem.children[0]);
          }
          e.preventDefault();

          break;

        case 37: // left
          zoomwall.page(elem, false);
          e.preventDefault();

          break;

        case 39: // right
          zoomwall.page(elem, true);
          e.preventDefault();

          break;
        }
      }
    };

    document.addEventListener('keydown', keyPager);

    return keyPager;
  },

  resizeRow: function (row, width) {
    if (row && row.length > 1) {
      for (var i in row) {
        row[i].style.width = (parseInt(window.getComputedStyle(row[i]).width, 10) / width * 100) + '%';
        row[i].style.height = 'auto';
      }
    }
  },

  calcRowWidth: function (row) {
    var width = 0;

    for (var i in row) {
      width += parseInt(window.getComputedStyle(row[i]).width, 10);
    }

    return width;
  },

  resize: function (blocks) {
    var row = [];
    var top = -1;

    for (var c = 0; c < blocks.length; c++) {
      var block = blocks[c];

      if (block) {
        if (top == -1) {
          top = block.offsetTop;

        } else if (block.offsetTop != top) {
          zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));

          row = [];
          top = block.offsetTop;
        }

        row.push(block);
      }
    }

    zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));
  },

  reset: function (block) {
    block.style.transform = 'translate(0, 0) scale(1)';
    block.style.webkitTransform = 'translate(0, 0) scale(1)';
    block.classList.remove('active');
  },

  shrink: function (block) {
    const blocks = zoomwall.findWall(block);

    if (blocks) {
      blocks.classList.remove('lightbox');
    }

    // reset all blocks
    zoomwall.reset(block);

    const imgChildren = blocks.querySelectorAll('img');

    const imgChildrenArray = [...imgChildren];
    const blockIndex = imgChildrenArray.indexOf(block)

    var prev = imgChildren[blockIndex - 1];
    while (prev) {
      zoomwall.reset(prev);
      prev = imgChildren[imgChildrenArray.indexOf(prev) - 1];
    }

    var next = imgChildren[blockIndex + 1];
    while (next) {
      zoomwall.reset(next);
      next = imgChildren[imgChildrenArray.indexOf(next) + 1];
    }

    // swap images
    if (block.dataset.lowres) {
      block.src = block.dataset.lowres;
    }
    if (block.dataset.sizes) {
      block.sizes = block.dataset.sizes;
    }
  },

  expand: function (block) {

    const blocks = zoomwall.findWall(block);

    block.classList.add('active');
    blocks.classList.add('lightbox');

    // parent dimensions
    var parentStyle = window.getComputedStyle(blocks);

    var parentWidth = parseInt(parentStyle.width, 10);
    var parentHeight = parseInt(parentStyle.height, 10);

    var parentTop = blocks.getBoundingClientRect().top;

    // block dimensions
    var blockStyle = window.getComputedStyle(block);

    var blockWidth = parseInt(blockStyle.width, 10);
    var blockHeight = parseInt(blockStyle.height, 10);

    // determine maximum height
    var targetHeight = window.innerHeight;

    if (parentHeight < window.innerHeight) {
      targetHeight = parentHeight;
    } else if (parentTop > 0) {
      targetHeight -= parentTop;
    }

    // swap images
    if (block.dataset.highres) {
      if (block.src != block.dataset.highres && block.dataset.lowres === undefined) {
        block.dataset.lowres = block.src;
      }
      block.src = block.dataset.highres;
    }
    if (block.sizes) { // responsive images
      block.dataset.sizes = block.sizes;
      block.sizes = '100vw'; // image is now 100% of the viewport width
    }

    // determine what blocks are on this row
    var row = [];
    row.push(block);

    const imgChildren = blocks.querySelectorAll('img');
    const imgChildrenArray = [...imgChildren];

    var next = imgChildren[imgChildrenArray.indexOf(block) + 1];

    while (next && next.offsetTop == block.offsetTop) {
      row.push(next);

      next = imgChildren[imgChildrenArray.indexOf(next) + 1];
    }

    var prev = imgChildren[imgChildrenArray.indexOf(block) - 1];

    while (prev && prev.offsetTop == block.offsetTop) {
      row.unshift(prev);

      prev = imgChildren[imgChildrenArray.indexOf(prev) - 1];
    }

    // calculate scale
    var scale = targetHeight / blockHeight;

    if (blockWidth * scale > parentWidth) {
      scale = parentWidth / blockWidth;
    }

    // determine offset
    var offsetY = parentTop - blocks.offsetTop + block.offsetTop;

    if (offsetY > 0) {
      if (parentHeight < window.innerHeight) {
        offsetY -= targetHeight / 2 - blockHeight * scale / 2;
      }

      if (parentTop > 0) {
        offsetY -= parentTop;
      }
    }

    var leftOffsetX = 0;  // shift in current row

    for (var i = 0; i < row.length && row[i] != block; i++) {
      leftOffsetX += parseInt(window.getComputedStyle(row[i]).width, 10) * scale;
    }

    leftOffsetX = parentWidth / 2 - blockWidth * scale / 2 - leftOffsetX;

    var rightOffsetX = 0;  // shift in current row

    for (var j = row.length - 1; j >= 0 && row[j] != block; j--) {
      rightOffsetX += parseInt(window.getComputedStyle(row[j]).width, 10) * scale;
    }

    rightOffsetX = parentWidth / 2 - blockWidth * scale / 2 - rightOffsetX;

    var percentageOffsetX;
    var percentageOffsetY;

    // transform current row
    var itemOffset = 0; // offset due to scaling of previous items
    var prevWidth = 0;

    for (var k = 0; k < row.length; k++) {
      itemOffset += (prevWidth * scale - prevWidth);
      prevWidth = parseInt(window.getComputedStyle(row[k]).width, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = -offsetY / parseInt(window.getComputedStyle(row[k]).height, 10) * 100;

      row[k].style.transformOrigin = '0% 0%';
      row[k].style.webkitTransformOrigin = '0% 0%';
      row[k].style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      row[k].style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
    }

    // transform items after
    var curTop;
    var nextOffsetY = blockHeight * (scale - 1) - offsetY;
    var prevHeight;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var nextItem = imgChildren[imgChildrenArray.indexOf(row[row.length - 1]) + 1];
    var nextRowTop = -1;

    while (nextItem) {
      curTop = nextItem.offsetTop;

      if (curTop == nextRowTop) {
        itemOffset += prevWidth * scale - prevWidth;
      } else {

        if (nextRowTop != -1) {
          itemOffset = 0;
          nextOffsetY += prevHeight * (scale - 1);
        }

        nextRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(nextItem).width, 10);
      prevHeight = parseInt(window.getComputedStyle(nextItem).height, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = nextOffsetY / prevHeight * 100;

      nextItem.style.transformOrigin = '0% 0%';
      nextItem.style.webkitTransformOrigin = '0% 0%';
      nextItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      nextItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      nextItem = imgChildren[imgChildrenArray.indexOf(nextItem) + 1];
    }

    // transform items before
    var prevOffsetY = -offsetY;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var prevItem = imgChildren[imgChildrenArray.indexOf(row[0]) - 1];
    var prevRowTop = -1;

    while (prevItem) {
      curTop = prevItem.offsetTop;

      if (curTop == prevRowTop) {
        itemOffset -= prevWidth * scale - prevWidth;
      } else {
        itemOffset = 0;
        prevOffsetY -= parseInt(window.getComputedStyle(prevItem).height, 10) * (scale - 1);
        prevRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(prevItem).width, 10);

      percentageOffsetX = (itemOffset - rightOffsetX) / prevWidth * 100;
      percentageOffsetY = prevOffsetY / parseInt(window.getComputedStyle(prevItem).height, 10) * 100;

      prevItem.style.transformOrigin = '100% 0%';
      prevItem.style.webkitTransformOrigin = '100% 0%';
      prevItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      prevItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      prevItem = imgChildren[imgChildrenArray.indexOf(prevItem) - 1];
    }
  },

  animate: function (e) {
    const blocks = zoomwall.findWall(e.target);

    if (this.classList.contains('active')) {
      zoomwall.shrink(this);
    } else {
      var actives = blocks.getElementsByClassName('active');

      for (var i = 0; i < actives.length; i++) {
        actives[i].classList.remove('active');
      }

      zoomwall.expand(this);
    }

    e.stopPropagation();
  },

  page: function (blocks, isNext) {
    var actives = blocks.getElementsByClassName('active');

    if (actives && actives.length > 0) {

      var current = actives[0];
      var next;

      const wall = zoomwall.findWall(current);
      const imgChildren = wall.querySelectorAll('img');

      const imgChildrenArray = [...imgChildren];
      const blockIndex = imgChildrenArray.indexOf(current)

      if (isNext) {
        next = imgChildren[blockIndex + 1];
      } else {
        next = imgChildren[blockIndex - 1];
      }

      if (next) {
        current.classList.remove('active');
        // swap images
        if (current.dataset.lowres) {
          current.src = current.dataset.lowres;
        }
        if (current.dataset.sizes) {
          current.sizes = current.dataset.sizes;
        }

        zoomwall.expand(next);
      }
    }
  }
};<|endoftext|>


<commit_before>import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import opn from 'opn';
import webpackConfig from '../webpack.config.babel';

const host = 'localhost';
const port = 3000;
const url = `http://${host}:${port}/`;

const config = webpackConfig({dev: true});

config.entry.unshift(
  `webpack-dev-server/client?${url}`,
  'webpack/hot/only-dev-server',
);

const devServer = new WebpackDevServer(webpack(config), {
  hot: true,
  historyApiFallback: true,
  stats: {
    colors: true,
  },
});

devServer.listen(port, host, (err) => {
  if (err) {
    console.error(err); // eslint-disable-line no-console

    return;
  }

  console.log(`Listening at ${url}`); // eslint-disable-line no-console
  opn(url);
});
<commit_msg>Update `serve.js`<commit_after>import webpack from 'webpack';
import WebpackDevServer from 'webpack-dev-server';
import opn from 'opn';
import webpackConfig from '../webpack.config.babel';

const host = 'localhost';
const port = 3000;
const url = `http://${host}:${port}/`;

const config = webpackConfig({dev: true});

config.entry.unshift(
  `webpack-dev-server/client?${url}`,
  'webpack/hot/only-dev-server',
);

const devServer = new WebpackDevServer(webpack(config), {
  hot: true,
  historyApiFallback: true,
  stats: {
    colors: true,
  },
});

devServer.listen(port, host, async (err) => {
  if (err) {
    console.error(err); // eslint-disable-line no-console

    return;
  }

  console.log(`Listening at ${url}`); // eslint-disable-line no-console
  await opn(url);
});
<|endoftext|>


<commit_before>    var yf1 = 0;
    var pyf1 = 0;
    var fixj = 0;
    var pfixj = 0;
    var ci = 0;
    var tetr = 0;
    var a = 0;
    var b = 0;
    var c = 0;
    var promoter1 = 1;
    var promoter2 = 0;
    var promoterA = 1;
    var promoterB = 0;
    var promoterC = 0;
    var rbs1 = 1;
    var rbs2 = 0.75;
    var dePhosCoeff1 = 0.5;
    var dePhosCoeff2 = 0.5;
    var phosp = 1;
    var blue_intensity = 0;
    var red_intensity = 0;  // for the intensity switch
    var degCoeffYF1 = 1;
    var degCoeffFixJ = 1;
    var degCoeffCI = 1;
    var degCoeffTetR = 1;
    var degCoeffA = 0.5;
    var degCoeffB = 0.5;
    var degCoeffC = 0.5;
    var timeStep = 0.1;
    var iterations = 600;
    var data = {"YF1":[yf1],
                "PYF1":[pyf1],
                "FixJ":[fixj],
                "PFixJ":[pfixj],
                "CI":[ci],
                "TetR":[tetr],
                "A":[a],
                "B":[b],
                "C":[c]
            }
    var timesteps = [0]; // ??
    //var visualization = Visualization(self) // ??
    //var visualization.start() // ??

function getAmount(proteinName){
    return data[proteinName][data[proteinName].length -1];
}

function derivativeYF1(currentConcentration){
    return promoter1 * rbs1 + dePhosCoeff1 * getAmount('PYF1') - (degCoeffYF1 + blue_intensity) * currentConcentration;
}

function derivativePYF1(currentConcentration){
    return blue_intensity * getAmount('YF1') - (dePhosCoeff1 + degCoeffYF1) * currentConcentration;
}

function derivativeFixJ(currentConcentration){
    return promoter1 * rbs1 + dePhosCoeff2 * getAmount('PFixJ') - (phosp * getAmount('PYF1') + degCoeffFixJ) * currentConcentration;
}

function derivativePFixJ(currentConcentration){
    return phosp * getAmount('FixJ') * getAmount('PYF1') - (dePhosCoeff2 + degCoeffFixJ) * currentConcentration;
}

function derivativeCI(currentConcentration){
    return promoter2 * rbs1 - degCoeffCI * currentConcentration;
}

function derivativeTetR(currentConcentration){
    return (promoterA + promoterB) * rbs2 - degCoeffTetR * currentConcentration;
}

function derivativeA(currentConcentration){
    return promoterA * rbs1 - degCoeffA * currentConcentration;
}

function derivativeB(currentConcentration){
    return promoterB * rbs1 - degCoeffB * currentConcentration;
}

function derivativeC(currentConcentration){
    return promoterC * rbs1 - degCoeffC * currentConcentration;
}

function derivativeSelect(proteinName, currentConcentration){
    //Select appropriate derivative
    if (proteinName == 'YF1'){
        return derivativeYF1(currentConcentration);
    }
    else if (proteinName == 'PYF1'){
        return derivativePYF1(currentConcentration);
    }
    else if (proteinName == 'FixJ'){
        return derivativeFixJ(currentConcentration);
    }
    else if (proteinName == 'PFixJ'){
        return derivativePFixJ(currentConcentration);
    }
    else if (proteinName == 'CI'){
        return derivativeCI(currentConcentration);
    }
    else if (proteinName == 'TetR'){
        return derivativeTetR(currentConcentration);
    }
    else if (proteinName == 'A'){
        return derivativeA(currentConcentration);
    }
    else if (proteinName == 'B'){
        return derivativeB(currentConcentration);
    }
    else if (proteinName == 'C'){
        return derivativeC(currentConcentration);
    }
}

function promoterUpdate(){
    promoter2 = 7 * getAmount('PFixJ');
    promoterA = (1 - red_intensity) * (1 - getAmount('CI'));
    if (getAmount('CI') < 1){
        promoterB = (1 - red_intensity) * (getAmount('CI'));
    }
    else{
        promoterB = (1 - red_intensity) * (1 - 2.5 * (getAmount('CI') - 1));
    }
    promoterC = (1 - red_intensity) * (1 - (1.75 / 0.75) * getAmount('TetR'));
}

function oneStep(i){
    var keyList = Object.keys(data);

    for(var j = 0; j < keyList.length;j++){
        x = getAmount(keyList[j]);
        coeff1 = derivativeSelect(keyList[j], x);
        coeff2 = derivativeSelect(
            keyList[j], (x + (coeff1) * timeStep / 2));
        coeff3 = derivativeSelect(
            keyList[j], (x + (coeff2) * timeStep / 2));
        coeff4 = derivativeSelect(
            keyList[j], (x + (coeff3) * timeStep));
        x = x + (timeStep / 6) * (coeff1 + 2 * coeff2 + 2 * coeff3 + coeff4);
        if(x > 0){
            data[(keyList[j])].push(x);
        }
        else{
            data[(keyList[j])].push(0);
        }
    }
    timesteps.push(i + 1);
    promoterUpdate();
    //console.log(getAmount('A') + "," + getAmount('B') + "," + getAmount('C'));
    //console.log("RGB(" + (getAmount('A') * 225)  + ","  + (getAmount('B') * 225) + "," + (getAmount('C') * 225) + ")");
    var R = 200 + (getAmount('A') * 27.5) - (getAmount('B') * 45) - (getAmount('C') * 45);
    var G = 185 + (getAmount('B') * 35) - (getAmount('A') * 40) - (getAmount('C') * 40);
    var B = 125 + (getAmount('C') * 65) - (getAmount('B') * 25) - (getAmount('C') * 25);
    setColonyColor("RGB(" + R  + ","  + G + "," + B + ")");

}

function run(){
    //Runge-Kutta computation for protein concentrations
    var i = 0;
    setInterval(function() {
        var light = getLightIntensities();
        blue_intensity = light.blue/100;
        red_intensity = 1 - light.red/100;
        console.log(blue_intensity);
        console.log(red_intensity);
        i++;
        oneStep(i);
    }, 100)
}

run();
<commit_msg>explanation for colors and comments<commit_after>    var yf1 = 0;
    var pyf1 = 0;
    var fixj = 0;
    var pfixj = 0;
    var ci = 0;
    var tetr = 0;
    var a = 0;
    var b = 0;
    var c = 0;
    var promoter1 = 1;
    var promoter2 = 0;
    var promoterA = 1;
    var promoterB = 0;
    var promoterC = 0;
    var rbs1 = 1;
    var rbs2 = 0.75;
    var dePhosCoeff1 = 0.5;
    var dePhosCoeff2 = 0.5;
    var phosp = 1;
    var blue_intensity = 0;
    var red_intensity = 0;  // for the intensity switch
    var degCoeffYF1 = 1;
    var degCoeffFixJ = 1;
    var degCoeffCI = 1;
    var degCoeffTetR = 1;
    var degCoeffA = 0.5;
    var degCoeffB = 0.5;
    var degCoeffC = 0.5;
    var timeStep = 0.1;
    var iterations = 600;
    var data = {"YF1":[yf1],
                "PYF1":[pyf1],
                "FixJ":[fixj],
                "PFixJ":[pfixj],
                "CI":[ci],
                "TetR":[tetr],
                "A":[a],
                "B":[b],
                "C":[c]
            }
    var timesteps = [0]; // ??
    //var visualization = Visualization(self) // ??
    //var visualization.start() // ??

function getAmount(proteinName){
    return data[proteinName][data[proteinName].length -1];
}

function derivativeYF1(currentConcentration){
    return promoter1 * rbs1 + dePhosCoeff1 * getAmount('PYF1') - (degCoeffYF1 + blue_intensity) * currentConcentration;
}

function derivativePYF1(currentConcentration){
    return blue_intensity * getAmount('YF1') - (dePhosCoeff1 + degCoeffYF1) * currentConcentration;
}

function derivativeFixJ(currentConcentration){
    return promoter1 * rbs1 + dePhosCoeff2 * getAmount('PFixJ') - (phosp * getAmount('PYF1') + degCoeffFixJ) * currentConcentration;
}

function derivativePFixJ(currentConcentration){
    return phosp * getAmount('FixJ') * getAmount('PYF1') - (dePhosCoeff2 + degCoeffFixJ) * currentConcentration;
}

function derivativeCI(currentConcentration){
    return promoter2 * rbs1 - degCoeffCI * currentConcentration;
}

function derivativeTetR(currentConcentration){
    return (promoterA + promoterB) * rbs2 - degCoeffTetR * currentConcentration;
}

function derivativeA(currentConcentration){
    return promoterA * rbs1 - degCoeffA * currentConcentration;
}

function derivativeB(currentConcentration){
    return promoterB * rbs1 - degCoeffB * currentConcentration;
}

function derivativeC(currentConcentration){
    return promoterC * rbs1 - degCoeffC * currentConcentration;
}

function derivativeSelect(proteinName, currentConcentration){
    //Select appropriate derivative
    if (proteinName == 'YF1'){
        return derivativeYF1(currentConcentration);
    }
    else if (proteinName == 'PYF1'){
        return derivativePYF1(currentConcentration);
    }
    else if (proteinName == 'FixJ'){
        return derivativeFixJ(currentConcentration);
    }
    else if (proteinName == 'PFixJ'){
        return derivativePFixJ(currentConcentration);
    }
    else if (proteinName == 'CI'){
        return derivativeCI(currentConcentration);
    }
    else if (proteinName == 'TetR'){
        return derivativeTetR(currentConcentration);
    }
    else if (proteinName == 'A'){
        return derivativeA(currentConcentration);
    }
    else if (proteinName == 'B'){
        return derivativeB(currentConcentration);
    }
    else if (proteinName == 'C'){
        return derivativeC(currentConcentration);
    }
}

function promoterUpdate(){
    promoter2 = 7 * getAmount('PFixJ');
    promoterA = (1 - red_intensity) * (1 - getAmount('CI'));
    if (getAmount('CI') < 1){
        promoterB = (1 - red_intensity) * (getAmount('CI'));
    }
    else{
        promoterB = (1 - red_intensity) * (1 - 2.5 * (getAmount('CI') - 1));
    }
    promoterC = (1 - red_intensity) * (1 - (1.75 / 0.75) * getAmount('TetR'));
}

function oneStep(i){
    var keyList = Object.keys(data);

    for(var j = 0; j < keyList.length;j++){
        x = getAmount(keyList[j]);
        coeff1 = derivativeSelect(keyList[j], x);
        coeff2 = derivativeSelect(
            keyList[j], (x + (coeff1) * timeStep / 2));
        coeff3 = derivativeSelect(
            keyList[j], (x + (coeff2) * timeStep / 2));
        coeff4 = derivativeSelect(
            keyList[j], (x + (coeff3) * timeStep));
        x = x + (timeStep / 6) * (coeff1 + 2 * coeff2 + 2 * coeff3 + coeff4);
        if(x > 0){
            data[(keyList[j])].push(x);
        }
        else{
            data[(keyList[j])].push(0);
        }
    }
    timesteps.push(i + 1);
    promoterUpdate();
    var R = 200 + (getAmount('A') * 27.5) - (getAmount('B') * 45) - (getAmount('C') * 45);
    var G = 185 + (getAmount('B') * 35) - (getAmount('A') * 40) - (getAmount('C') * 40);
    var B = 125 + (getAmount('C') * 65) - (getAmount('B') * 25) - (getAmount('C') * 25);
    // A = red, B = green, C = blue. They reduce the others to make the colors more vivid.
    // The base values for each color are for a nice E.Coli light brown.
    setColonyColor("RGB(" + R  + ","  + G + "," + B + ")");
}

function run(){
    //Runge-Kutta computation for protein concentrations
    var i = 0;
    setInterval(function() {
        var light = getLightIntensities();
        blue_intensity = light.blue/100;
        red_intensity = 1 - light.red/100;
        console.log(blue_intensity);
        console.log(red_intensity);
        i++;
        oneStep(i);
    }, 100)
}

run();
<|endoftext|>


<commit_before>define(function (require) {
  "use strict";

  var React = require('react'),
      stores = require('stores'),
      Backbone = require('backbone'),
      context = require('context'),
      globals = require('globals'),
      Header = require('./Header.react'),
      Footer = require('./Footer.react'),
      AdminSettings = require('../AdminSettings'),
      actions = require('actions'),
      NullProject = require('models/NullProject');

  // Routing
  var Router = require('react-router'),
      RouteHandler = Router.RouteHandler;

  return React.createClass({

    mixins: [Router.State],

    getState: function() {
      return {};
    },

    getInitialState: function() {
      return this.getState();
    },

    updateState: function() {
      if (this.isMounted()) this.setState(this.getState())
    },

    componentDidMount: function () {
      // subscribe to all Stores
      Object.keys(stores).forEach(function(storeName){
        stores[storeName].addChangeListener(this.updateState);
      }.bind(this));

      // The code below is only relevant to logged in users
      if(!context.profile) return;

      // IMPORTANT! We get one shot at this. If the instances and volumes aren't
      // fetched before this component is mounted we miss our opportunity to migrate
      // the users resources (so make sure they're fetched in the Splash Screen)
      var instances = stores.InstanceStore.getInstancesNotInAProject(),
          volumes = stores.VolumeStore.getVolumesNotInAProject(),
          nullProject = new NullProject({instances: instances, volumes: volumes});

      if(!nullProject.isEmpty()){
        actions.NullProjectActions.migrateResourcesIntoProject(nullProject);
      }else{
        actions.NullProjectActions.moveAttachedVolumesIntoCorrectProject();
      }
    },

    componentWillUnmount: function () {
      // un-subscribe from all Stores
      Object.keys(stores).forEach(function(storeName){
        stores[storeName].removeChangeListener(this.updateState);
      }.bind(this));
    },

    // --------------
    // Render Helpers
    // --------------

    render: function () {
      var maintenanceMessages = stores.MaintenanceMessageStore.getAll() || new Backbone.Collection(),
          marginTop = maintenanceMessages.length * 24 + "px";

      return (

            <RouteHandler/>

      );
    }

  });

});<commit_msg>removed old reference to "AdminSettings"<commit_after>define(function (require) {
  "use strict";

  var React = require('react'),
      stores = require('stores'),
      Backbone = require('backbone'),
      context = require('context'),
      globals = require('globals'),
      Header = require('./Header.react'),
      Footer = require('./Footer.react'),
      actions = require('actions'),
      NullProject = require('models/NullProject');

  // Routing
  var Router = require('react-router'),
      RouteHandler = Router.RouteHandler;

  return React.createClass({

    mixins: [Router.State],

    getState: function() {
      return {};
    },

    getInitialState: function() {
      return this.getState();
    },

    updateState: function() {
      if (this.isMounted()) this.setState(this.getState())
    },

    componentDidMount: function () {
      // subscribe to all Stores
      Object.keys(stores).forEach(function(storeName){
        stores[storeName].addChangeListener(this.updateState);
      }.bind(this));

      // The code below is only relevant to logged in users
      if(!context.profile) return;

      // IMPORTANT! We get one shot at this. If the instances and volumes aren't
      // fetched before this component is mounted we miss our opportunity to migrate
      // the users resources (so make sure they're fetched in the Splash Screen)
      var instances = stores.InstanceStore.getInstancesNotInAProject(),
          volumes = stores.VolumeStore.getVolumesNotInAProject(),
          nullProject = new NullProject({instances: instances, volumes: volumes});

      if(!nullProject.isEmpty()){
        actions.NullProjectActions.migrateResourcesIntoProject(nullProject);
      }else{
        actions.NullProjectActions.moveAttachedVolumesIntoCorrectProject();
      }
    },

    componentWillUnmount: function () {
      // un-subscribe from all Stores
      Object.keys(stores).forEach(function(storeName){
        stores[storeName].removeChangeListener(this.updateState);
      }.bind(this));
    },

    // --------------
    // Render Helpers
    // --------------

    render: function () {
      var maintenanceMessages = stores.MaintenanceMessageStore.getAll() || new Backbone.Collection(),
          marginTop = maintenanceMessages.length * 24 + "px";

      return (

            <RouteHandler/>

      );
    }

  });

});<|endoftext|>


<commit_before>/*
zoomwall.js

The MIT License (MIT)

Copyright (c) 2014 Eric Leong

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

var zoomwall = {

  create: function(blocks, enableKeys) {
    zoomwall.resize(blocks.children);

    blocks.classList.remove('loading');
    // shrink blocks if an empty space is clicked
    blocks.addEventListener('click', function() {
      if (this.children && this.children.length > 0) {
        zoomwall.shrink(this.children[0]);
      }
    });

    // add click listeners to blocks
    for (var i = 0; i < blocks.children.length; i++) {
      blocks.children[i].addEventListener('click', zoomwall.animate);
    }

    // add key down listener
    if (enableKeys) {
      zoomwall.keys(blocks);
    }
  },

  keys: function(blocks) {
    var keyPager = function(e) {
      if (e.defaultPrevented) {
        return;
      }

      // either use the provided blocks, or query for the first lightboxed zoomwall
      var elem = blocks || document.getElementsByClassName('zoomwall lightbox')[0];

      if (elem) {
        switch (e.keyCode) {
        case 27: // escape
          if (elem.children && elem.children.length > 0) {
            zoomwall.shrink(elem.children[0]);
          }
          e.preventDefault();

          break;

        case 37: // left
          zoomwall.page(elem, false);
          e.preventDefault();

          break;

        case 39: // right
          zoomwall.page(elem, true);
          e.preventDefault();

          break;
        }
      }
    };

    document.addEventListener('keydown', keyPager);

    return keyPager;
  },

  resizeRow: function(row, width) {
    if (row && row.length > 1) {
      for (var i in row) {
        row[i].style.width = (parseInt(window.getComputedStyle(row[i]).width, 10) / width * 100) + '%';
        row[i].style.height = 'auto';
      }
    }
  },

  calcRowWidth: function(row) {
    var width = 0;

    for (var i in row) {
      width += parseInt(window.getComputedStyle(row[i]).width, 10);
    }

    return width;
  },

  resize: function(blocks) {
    var row = [];
    var top = -1;

    for (var c = 0; c < blocks.length; c++) {
      var block = blocks[c];

      if (block) {
        if (top == -1) {
          top = block.offsetTop;

        } else if (block.offsetTop != top) {
          zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));

          row = [];
          top = block.offsetTop;
        }

        row.push(block);
      }
    }

    zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));
  },

  reset: function(block) {
    block.style.transform = 'translate(0, 0) scale(1)';
    block.style.webkitTransform = 'translate(0, 0) scale(1)';
    block.classList.remove('active');
  },

  shrink: function(block) {
    block.parentNode.classList.remove('lightbox');

    // reset all blocks
    zoomwall.reset(block);

    var prev = block.previousElementSibling;
    while (prev) {
      zoomwall.reset(prev);
      prev = prev.previousElementSibling;
    }

    var next = block.nextElementSibling;
    while (next) {
      zoomwall.reset(next);
      next = next.nextElementSibling;
    }

    // swap images
    if (block.dataset.lowres) {
      block.src = block.dataset.lowres;
    }
  },

  expand: function(block) {

    block.classList.add('active');
    block.parentNode.classList.add('lightbox');

    // parent dimensions
    var parentStyle = window.getComputedStyle(block.parentNode);

    var parentWidth = parseInt(parentStyle.width, 10);
    var parentHeight = parseInt(parentStyle.height, 10);

    var parentTop = block.parentNode.getBoundingClientRect().top;

    // block dimensions
    var blockStyle = window.getComputedStyle(block);

    var blockWidth = parseInt(blockStyle.width, 10);
    var blockHeight = parseInt(blockStyle.height, 10);

    // determine maximum height
    var targetHeight = window.innerHeight;

    if (parentHeight < window.innerHeight) {
      targetHeight = parentHeight;
    } else if (parentTop > 0) {
      targetHeight -= parentTop;
    }

    // swap images
    if (block.dataset.highres) {
      if (block.src != block.dataset.highres && block.dataset.lowres === undefined) {
        block.dataset.lowres = block.src;
      }
      block.src = block.dataset.highres;
    }

    // determine what blocks are on this row
    var row = [];
    row.push(block);

    var next = block.nextElementSibling;

    while (next && next.offsetTop == block.offsetTop) {
      row.push(next);

      next = next.nextElementSibling;
    }

    var prev = block.previousElementSibling;

    while (prev && prev.offsetTop == block.offsetTop) {
      row.unshift(prev);

      prev = prev.previousElementSibling;
    }

    // calculate scale
    var scale = targetHeight / blockHeight;

    if (blockWidth * scale > parentWidth) {
      scale = parentWidth / blockWidth;
    }

    // determine offset
    var offsetY = parentTop - block.parentNode.offsetTop + block.offsetTop;

    if (offsetY > 0) {
      if (parentHeight < window.innerHeight) {
        offsetY -= targetHeight / 2 - blockHeight * scale / 2;
      }

      if (parentTop > 0) {
        offsetY -= parentTop;
      }
    }

    var leftOffsetX = 0;  // shift in current row

    for (var i = 0; i < row.length && row[i] != block; i++) {
      leftOffsetX += parseInt(window.getComputedStyle(row[i]).width, 10) * scale;
    }

    leftOffsetX = parentWidth / 2 - blockWidth * scale / 2 - leftOffsetX;

    var rightOffsetX = 0;  // shift in current row

    for (var j = row.length - 1; j >= 0 && row[j] != block; j--) {
      rightOffsetX += parseInt(window.getComputedStyle(row[j]).width, 10) * scale;
    }

    rightOffsetX = parentWidth / 2 - blockWidth * scale / 2 - rightOffsetX;

    var percentageOffsetX;
    var percentageOffsetY;

    // transform current row
    var itemOffset = 0; // offset due to scaling of previous items
    var prevWidth = 0;

    for (var k = 0; k < row.length; k++) {
      itemOffset += (prevWidth * scale - prevWidth);
      prevWidth = parseInt(window.getComputedStyle(row[k]).width, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = -offsetY / parseInt(window.getComputedStyle(row[k]).height, 10) * 100;

      row[k].style.transformOrigin = '0% 0%';
      row[k].style.webkitTransformOrigin = '0% 0%';
      row[k].style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      row[k].style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
    }

    // transform items after
    var curTop;
    var nextOffsetY = blockHeight * (scale - 1) - offsetY;
    var prevHeight;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var nextItem = row[row.length - 1].nextElementSibling;
    var nextRowTop = -1;

    while (nextItem) {
      curTop = nextItem.offsetTop;

      if (curTop == nextRowTop) {
        itemOffset += prevWidth * scale - prevWidth;
      } else {

        if (nextRowTop != -1) {
          itemOffset = 0;
          nextOffsetY += prevHeight * (scale - 1);
        }

        nextRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(nextItem).width, 10);
      prevHeight = parseInt(window.getComputedStyle(nextItem).height, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = nextOffsetY / prevHeight * 100;

      nextItem.style.transformOrigin = '0% 0%';
      nextItem.style.webkitTransformOrigin = '0% 0%';
      nextItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      nextItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      nextItem = nextItem.nextElementSibling;
    }

    // transform items before
    var prevOffsetY = -offsetY;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var prevItem = row[0].previousElementSibling;
    var prevRowTop = -1;

    while (prevItem) {
      curTop = prevItem.offsetTop;

      if (curTop == prevRowTop) {
        itemOffset -= prevWidth * scale - prevWidth;
      } else {
        itemOffset = 0;
        prevOffsetY -= parseInt(window.getComputedStyle(prevItem).height, 10) * (scale - 1);
        prevRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(prevItem).width, 10);

      percentageOffsetX = (itemOffset - rightOffsetX) / prevWidth * 100;
      percentageOffsetY = prevOffsetY / parseInt(window.getComputedStyle(prevItem).height, 10) * 100;

      prevItem.style.transformOrigin = '100% 0%';
      prevItem.style.webkitTransformOrigin = '100% 0%';
      prevItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      prevItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      prevItem = prevItem.previousElementSibling;
    }
  },

  animate: function(e) {
    if (this.classList.contains('active')) {
      zoomwall.shrink(this);
    } else {
      var actives = this.parentNode.getElementsByClassName('active');

      for (var i = 0; i < actives.length; i++) {
        actives[i].classList.remove('active');
      }

      zoomwall.expand(this);
    }

    e.stopPropagation();
  },

  page: function(blocks, isNext) {
    var actives = blocks.getElementsByClassName('active');

    if (actives && actives.length > 0) {

      var current = actives[0];
      var next;

      if (isNext) {
        next = current.nextElementSibling;
      } else {
        next = current.previousElementSibling;
      }

      if (next) {
        current.classList.remove('active');
        // swap images
        if (current.dataset.lowres) {
          current.src = current.dataset.lowres;
        }

        zoomwall.expand(next);
      }
    }
  }
};<commit_msg>Added export for using it as a module<commit_after>/*
zoomwall.js

The MIT License (MIT)

Copyright (c) 2014 Eric Leong

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

export var zoomwall = {

  create: function (blocks, enableKeys) {
    zoomwall.resize(blocks.children);

    blocks.classList.remove('loading');
    // shrink blocks if an empty space is clicked
    blocks.addEventListener('click', function () {
      if (this.children && this.children.length > 0) {
        zoomwall.shrink(this.children[0]);
      }
    });

    // add click listeners to blocks
    for (var i = 0; i < blocks.children.length; i++) {
      blocks.children[i].addEventListener('click', zoomwall.animate);
    }

    // add key down listener
    if (enableKeys) {
      zoomwall.keys(blocks);
    }
  },

  keys: function (blocks) {
    var keyPager = function (e) {
      if (e.defaultPrevented) {
        return;
      }

      // either use the provided blocks, or query for the first lightboxed zoomwall
      var elem = blocks || document.getElementsByClassName('zoomwall lightbox')[0];

      if (elem) {
        switch (e.keyCode) {
          case 27: // escape
            if (elem.children && elem.children.length > 0) {
              zoomwall.shrink(elem.children[0]);
            }
            e.preventDefault();

            break;

          case 37: // left
            zoomwall.page(elem, false);
            e.preventDefault();

            break;

          case 39: // right
            zoomwall.page(elem, true);
            e.preventDefault();

            break;
        }
      }
    };

    document.addEventListener('keydown', keyPager);

    return keyPager;
  },

  resizeRow: function (row, width) {
    if (row && row.length > 1) {
      for (var i in row) {
        row[i].style.width = (parseInt(window.getComputedStyle(row[i]).width, 10) / width * 100) + '%';
        row[i].style.height = 'auto';
      }
    }
  },

  calcRowWidth: function (row) {
    var width = 0;

    for (var i in row) {
      width += parseInt(window.getComputedStyle(row[i]).width, 10);
    }

    return width;
  },

  resize: function (blocks) {
    var row = [];
    var top = -1;

    for (var c = 0; c < blocks.length; c++) {
      var block = blocks[c];

      if (block) {
        if (top == -1) {
          top = block.offsetTop;

        } else if (block.offsetTop != top) {
          zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));

          row = [];
          top = block.offsetTop;
        }

        row.push(block);
      }
    }

    zoomwall.resizeRow(row, zoomwall.calcRowWidth(row));
  },

  reset: function (block) {
    block.style.transform = 'translate(0, 0) scale(1)';
    block.style.webkitTransform = 'translate(0, 0) scale(1)';
    block.classList.remove('active');
  },

  shrink: function (block) {
    block.parentNode.classList.remove('lightbox');

    // reset all blocks
    zoomwall.reset(block);

    var prev = block.previousElementSibling;
    while (prev) {
      zoomwall.reset(prev);
      prev = prev.previousElementSibling;
    }

    var next = block.nextElementSibling;
    while (next) {
      zoomwall.reset(next);
      next = next.nextElementSibling;
    }

    // swap images
    if (block.dataset.lowres) {
      block.src = block.dataset.lowres;
    }
  },

  expand: function (block) {

    block.classList.add('active');
    block.parentNode.classList.add('lightbox');

    // parent dimensions
    var parentStyle = window.getComputedStyle(block.parentNode);

    var parentWidth = parseInt(parentStyle.width, 10);
    var parentHeight = parseInt(parentStyle.height, 10);

    var parentTop = block.parentNode.getBoundingClientRect().top;

    // block dimensions
    var blockStyle = window.getComputedStyle(block);

    var blockWidth = parseInt(blockStyle.width, 10);
    var blockHeight = parseInt(blockStyle.height, 10);

    // determine maximum height
    var targetHeight = window.innerHeight;

    if (parentHeight < window.innerHeight) {
      targetHeight = parentHeight;
    } else if (parentTop > 0) {
      targetHeight -= parentTop;
    }

    // swap images
    if (block.dataset.highres) {
      if (block.src != block.dataset.highres && block.dataset.lowres === undefined) {
        block.dataset.lowres = block.src;
      }
      block.src = block.dataset.highres;
    }

    // determine what blocks are on this row
    var row = [];
    row.push(block);

    var next = block.nextElementSibling;

    while (next && next.offsetTop == block.offsetTop) {
      row.push(next);

      next = next.nextElementSibling;
    }

    var prev = block.previousElementSibling;

    while (prev && prev.offsetTop == block.offsetTop) {
      row.unshift(prev);

      prev = prev.previousElementSibling;
    }

    // calculate scale
    var scale = targetHeight / blockHeight;

    if (blockWidth * scale > parentWidth) {
      scale = parentWidth / blockWidth;
    }

    // determine offset
    var offsetY = parentTop - block.parentNode.offsetTop + block.offsetTop;

    if (offsetY > 0) {
      if (parentHeight < window.innerHeight) {
        offsetY -= targetHeight / 2 - blockHeight * scale / 2;
      }

      if (parentTop > 0) {
        offsetY -= parentTop;
      }
    }

    var leftOffsetX = 0;  // shift in current row

    for (var i = 0; i < row.length && row[i] != block; i++) {
      leftOffsetX += parseInt(window.getComputedStyle(row[i]).width, 10) * scale;
    }

    leftOffsetX = parentWidth / 2 - blockWidth * scale / 2 - leftOffsetX;

    var rightOffsetX = 0;  // shift in current row

    for (var j = row.length - 1; j >= 0 && row[j] != block; j--) {
      rightOffsetX += parseInt(window.getComputedStyle(row[j]).width, 10) * scale;
    }

    rightOffsetX = parentWidth / 2 - blockWidth * scale / 2 - rightOffsetX;

    var percentageOffsetX;
    var percentageOffsetY;

    // transform current row
    var itemOffset = 0; // offset due to scaling of previous items
    var prevWidth = 0;

    for (var k = 0; k < row.length; k++) {
      itemOffset += (prevWidth * scale - prevWidth);
      prevWidth = parseInt(window.getComputedStyle(row[k]).width, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = -offsetY / parseInt(window.getComputedStyle(row[k]).height, 10) * 100;

      row[k].style.transformOrigin = '0% 0%';
      row[k].style.webkitTransformOrigin = '0% 0%';
      row[k].style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      row[k].style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
    }

    // transform items after
    var curTop;
    var nextOffsetY = blockHeight * (scale - 1) - offsetY;
    var prevHeight;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var nextItem = row[row.length - 1].nextElementSibling;
    var nextRowTop = -1;

    while (nextItem) {
      curTop = nextItem.offsetTop;

      if (curTop == nextRowTop) {
        itemOffset += prevWidth * scale - prevWidth;
      } else {

        if (nextRowTop != -1) {
          itemOffset = 0;
          nextOffsetY += prevHeight * (scale - 1);
        }

        nextRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(nextItem).width, 10);
      prevHeight = parseInt(window.getComputedStyle(nextItem).height, 10);

      percentageOffsetX = (itemOffset + leftOffsetX) / prevWidth * 100;
      percentageOffsetY = nextOffsetY / prevHeight * 100;

      nextItem.style.transformOrigin = '0% 0%';
      nextItem.style.webkitTransformOrigin = '0% 0%';
      nextItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      nextItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      nextItem = nextItem.nextElementSibling;
    }

    // transform items before
    var prevOffsetY = -offsetY;
    itemOffset = 0; // offset due to scaling of previous items
    prevWidth = 0;

    var prevItem = row[0].previousElementSibling;
    var prevRowTop = -1;

    while (prevItem) {
      curTop = prevItem.offsetTop;

      if (curTop == prevRowTop) {
        itemOffset -= prevWidth * scale - prevWidth;
      } else {
        itemOffset = 0;
        prevOffsetY -= parseInt(window.getComputedStyle(prevItem).height, 10) * (scale - 1);
        prevRowTop = curTop;
      }

      prevWidth = parseInt(window.getComputedStyle(prevItem).width, 10);

      percentageOffsetX = (itemOffset - rightOffsetX) / prevWidth * 100;
      percentageOffsetY = prevOffsetY / parseInt(window.getComputedStyle(prevItem).height, 10) * 100;

      prevItem.style.transformOrigin = '100% 0%';
      prevItem.style.webkitTransformOrigin = '100% 0%';
      prevItem.style.transform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';
      prevItem.style.webkitTransform = 'translate(' + percentageOffsetX.toFixed(8) + '%, ' + percentageOffsetY.toFixed(8) + '%) scale(' + scale.toFixed(8) + ')';

      prevItem = prevItem.previousElementSibling;
    }
  },

  animate: function (e) {
    if (this.classList.contains('active')) {
      zoomwall.shrink(this);
    } else {
      var actives = this.parentNode.getElementsByClassName('active');

      for (var i = 0; i < actives.length; i++) {
        actives[i].classList.remove('active');
      }

      zoomwall.expand(this);
    }

    e.stopPropagation();
  },

  page: function (blocks, isNext) {
    var actives = blocks.getElementsByClassName('active');

    if (actives && actives.length > 0) {

      var current = actives[0];
      var next;

      if (isNext) {
        next = current.nextElementSibling;
      } else {
        next = current.previousElementSibling;
      }

      if (next) {
        current.classList.remove('active');
        // swap images
        if (current.dataset.lowres) {
          current.src = current.dataset.lowres;
        }

        zoomwall.expand(next);
      }
    }
  }
};<|endoftext|>


<commit_before>import DiscoveryList from './components/discovery_list'
import CategoryList from './components/category_list'
import ConnectedList from './components/connected_list'

export default [
  {
    path: '/category',
    component: CategoryList
  },
  {
    path: '/discovery',
    alias: '/',
    component: DiscoveryList
  },
  {
    path: '/connected',
    component: ConnectedList
  }
]
<commit_msg>[fix] redirect root instead of aliasing it<commit_after>import DiscoveryList from './components/discovery_list'
import CategoryList from './components/category_list'
import ConnectedList from './components/connected_list'

export default [{
  path: '/category',
  component: CategoryList
},
{
  path: '/discovery',
  component: DiscoveryList
},
{
  path: '/connected',
  component: ConnectedList
},
{
  path: '/',
  redirect: 'discovery'
}]
<|endoftext|>