<commit_before>define(['footwork', 'lodash', 'jquery', 'tools', 'fetch-mock'],
  function(fw, _, $, tools, fetchMock) {
    describe('components', function() {
      beforeEach(tools.prepareTestEnv);
      afterEach(tools.cleanTestEnv);

      it('can register a component', function() {
        var namespaceName = tools.generateNamespaceName();
        var invalidNamespaceName = tools.generateNamespaceName();
        var viewModelSpy = jasmine.createSpy('viewModelSpy');

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: viewModelSpy
        });

        expect(fw.components.isRegistered(namespaceName)).toBe(true);
        expect(fw.components.isRegistered(invalidNamespaceName)).not.toBe(true);
        expect(viewModelSpy).not.toHaveBeenCalled();

        expect(fw.components.getFileName(namespaceName)).toBe(null);
      });

      it('can instantiate a registered component via a <declarative> statement', function(done) {
        var namespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this);
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('can instantiate a registered component via a <declarative> statement with a dataModel', function(done) {
        var namespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this);
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('can instantiate a registered component via a <declarative> statement with a router', function(done) {
        var namespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this);
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('calls the dispose() callback of a viewModel when the parent component is removed from the DOM', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var viewModelNamespaceName = tools.generateNamespaceName();
        var viewModelInitializeSpy;
        var componentInitializeSpy;
        var componentOnDisposeSpy;
        var containerViewModel;
        var valueToCheckFor = tools.randomString();

        fw.viewModel.register(viewModelNamespaceName, tools.expectCallOrder(0, viewModelInitializeSpy = jasmine.createSpy('viewModelInitializeSpy', function() {
          fw.viewModel.boot(this, {
            namespace: viewModelNamespaceName
          });
          this.show = fw.observable(true);
          containerViewModel = this;
        }).and.callThrough()));

        fw.components.register(componentNamespaceName, {
          template: '',
          viewModel: tools.expectCallOrder(1, componentInitializeSpy = jasmine.createSpy('componentInitializeSpy', function() {
            fw.viewModel.boot(this, {
              namespace: componentNamespaceName,
              onDispose: tools.expectCallOrder(2, componentOnDisposeSpy = jasmine.createSpy('componentOnDisposeSpy', function(containingElement) {
                expect(containingElement.tagName).toBe(componentNamespaceName.toUpperCase());
              }).and.callThrough())
            });
            this.someProperty = valueToCheckFor;
          }).and.callThrough())
        });

        expect(viewModelInitializeSpy).not.toHaveBeenCalled();
        expect(componentInitializeSpy).not.toHaveBeenCalled();
        expect(componentOnDisposeSpy).toBe(undefined);

        fw.start(testContainer = tools.getFixtureContainer(
          '<viewModel module="' + viewModelNamespaceName + '">\

\
              <' + componentNamespaceName + '></' + componentNamespaceName + '>\

\
          </viewModel>'
        ));

        setTimeout(function() {
          expect(viewModelInitializeSpy).toHaveBeenCalled();
          expect(componentInitializeSpy).toHaveBeenCalled();
          expect($(testContainer).find('.content').text().indexOf(valueToCheckFor)).not.toBe(-1);

          containerViewModel.show(false);

          expect(componentOnDisposeSpy).toHaveBeenCalled();
          expect($(testContainer).find('.content').text().indexOf(valueToCheckFor)).toBe(-1);
          done();
        }, ajaxWait);
      });

      it('has the animation classes applied properly', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var viewModelNamespaceName = tools.generateNamespaceName();
        var initializeSpy;
        var afterRenderSpy;
        var theElement;

        fw.components.register(componentNamespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this, {
              afterRender: tools.expectCallOrder(1, afterRenderSpy = jasmine.createSpy('afterRenderSpy', function(element) {
                theElement = element;
                expect($(theElement).hasClass(footworkAnimationClass)).toBe(false);
              }).and.callThrough())
            })
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + componentNamespaceName + '></' + componentNamespaceName + '>'));

        expect(afterRenderSpy).toBe(undefined);

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          expect($(theElement).hasClass(footworkAnimationClass)).toBe(false);
          expect(afterRenderSpy).toHaveBeenCalled();

          setTimeout(function() {
            expect($(theElement).hasClass(footworkAnimationClass)).toBe(true);
            done();
          }, ajaxWait);
        }, 0);
      });

      it('can sequence animations', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var footworkAnimatedElements = '.' + footworkAnimationClass;

        fw.components.register(componentNamespaceName, {
          template: '
a template
',
          viewModel: function() {
            fw.viewModel.boot(this, {
              namespace: componentNamespaceName,
              sequenceAnimations: 30
            })
          }
        });

        testContainer = tools.getFixtureContainer('
\
          <' + componentNamespaceName + '></' + componentNamespaceName + '>\

');

        expect($(testContainer).find(footworkAnimatedElements).length).toBe(0);

        fw.applyBindings({
          things: [ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ]
        }, testContainer);

        setTimeout(function() {
          var $testContainer = $(testContainer);
          var currentThingLength = $testContainer.find(footworkAnimatedElements).length;

          expect(currentThingLength).toBeGreaterThan(0);

          setTimeout(function() {
            expect($testContainer.find(footworkAnimatedElements)).lengthToBeGreaterThan(currentThingLength);
            done();
          }, 120);
        }, 100);
      });

      it('can instantiate nested <components>', function(done) {
        var outerInitializeSpy = jasmine.createSpy('outerInitializeSpy');
        var innerInitializeSpy = jasmine.createSpy('innerInitializeSpy');
        var outerComponentNamespaceName = tools.generateNamespaceName();
        var innerComponentNamespaceName = tools.generateNamespaceName();

        fw.components.register(outerComponentNamespaceName, {
          template: '<' + innerComponentNamespaceName + '></' + innerComponentNamespaceName + '>',
          viewModel: tools.expectCallOrder(0, outerInitializeSpy)
        });

        fw.components.register(innerComponentNamespaceName, {
          template: '

',
          viewModel: tools.expectCallOrder(1, innerInitializeSpy)
        });

        expect(outerInitializeSpy).not.toHaveBeenCalled();
        expect(innerInitializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + outerComponentNamespaceName + '></' + outerComponentNamespaceName + '>'));

        setTimeout(function() {
          expect(outerInitializeSpy).toHaveBeenCalled();
          expect(innerInitializeSpy).toHaveBeenCalled();

          done();
        }, ajaxWait);
      });

      it('can pass params to a component viewModel', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(componentNamespaceName, {
          template: '

',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function(params) {
            expect(params).toEqual({ testValueOne: 1, testValueTwo: [1,2,3] });
          }))
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + componentNamespaceName + ' params="testValueOne: 1, testValueTwo: [1,2,3]"></' + componentNamespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('can set and return fileExtensions correctly', function() {
        var originalExtensions = fw.components.fileExtensions();
        var fileName = tools.randomString();
        var extensions = {
          combined: '.combinedTest',
          viewModel: '.viewModelTest',
          template: function() {
            return '.templateTest';
          }
        };

        expect(fw.components.fileExtensions()).not.toEqual(extensions);
        fw.components.fileExtensions(extensions);
        expect(fw.components.fileExtensions()).toEqual(extensions);
        expect(fw.components.getFileName(fileName, 'template')).toEqual(fileName + extensions.template());

        // reset extensions back to normal
        fw.components.fileExtensions(originalExtensions);
        expect(fw.components.fileExtensions()).not.toEqual(extensions);
      });

      it('can set fileExtensions via a callback', function() {
        var originalExtensions = fw.components.fileExtensions();

        function prependName(name, withName) {
          return _.reduce({
            combined: '.' + name + 'combinedTest',
            viewModel: '.' + name + 'viewModelTest',
            template: function() {
              return (withName ? name : '') + '.' + 'templateTest';
            }
          }, function(ext, extension, property) {
            ext[property] = _.isFunction(extension) ? extension() : (withName ? name : '');
            return ext;
          }, {});
        }

        var getFileExtensionsSpy = jasmine.createSpy('getFileExtensionsSpy', function getFileExtensions(componentName) {
          return prependName(componentName);
        }).and.callThrough();

        fw.components.fileExtensions(tools.expectCallOrder([0, 1, 2], getFileExtensionsSpy));

        var comp1Check = prependName('comp1', true);
        var comp2Check = prependName('comp2', true);

        expect(fw.components.getFileName('comp1', 'viewModel')).toEqual(comp1Check.viewModel);
        expect(fw.components.getFileName('comp2', 'viewModel')).toEqual(comp2Check.viewModel);
        expect(fw.components.getFileName('comp2', 'template')).toEqual(comp2Check.template);

        // reset extensions back to normal
        fw.components.fileExtensions(originalExtensions);
        expect(fw.components.fileExtensions()).toEqual(originalExtensions);
      });

      it('can specify a location and verify it', function() {
        var namespaceName = tools.generateNamespaceName();
        var location = {
          viewModel: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        };

        fw.components.registerLocation(namespaceName, location);
        fw.components.registerLocation(/^regexp-test.*$/, location);

        expect(fw.components.getLocation(namespaceName)).toEqual(location);
        expect(fw.components.getLocation('regexp-test-regexp')).toEqual(location);
      });

      it('can register an array of components to a location and retrieve them proplerly', function() {
        var location = {
          viewModel: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        };

        var namespaceNames = [tools.generateNamespaceName(), tools.generateNamespaceName(), tools.generateNamespaceName()];
        fw.components.registerLocation(namespaceNames, location);

        _.each(namespaceNames, function(namespaceName) {
          expect(fw.components.getLocation(namespaceName)).toEqual(location);
        });
      });

      // it('can specify and load via a registered location', function(done) {
      //   var namespaceName = 'registered-component-location';

      //   fw.components.registerLocation('registered-component-location', {
      //     viewModel: 'tests/assets/fixtures/registeredComponentLocation/',
      //     template: 'tests/assets/fixtures/registeredComponentLocation/'
      //   });

      //   expect(namespaceName).not.toBeLoaded();

      //   fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

      //   setTimeout(function() {
      //     expect(namespaceName).toBeLoaded();
      //     done();
      //   }, ajaxWait);
      // });

      // it('can specify and load via a registered location with a prefixed folder', function(done) {
      //   var namespaceName = 'registered-component-location-prefixed';

      //   fw.components.registerLocation(namespaceName, {
      //     viewModel: 'tests/assets/fixtures/',
      //     template: 'tests/assets/fixtures/'
      //   }, true);

      //   expect(namespaceName).not.toBeLoaded();

      //   fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

      //   setTimeout(function() {
      //     expect(namespaceName).toBeLoaded();
      //     done();
      //   }, ajaxWait);
      // });

      // it('can specify and load via a registered RegExp-based location', function(done) {
      //   var namespaceName = 'registered-regexp-component-location';

      //   fw.components.registerLocation(/registered-regexp-comp.*/, {
      //     viewModel: 'tests/assets/fixtures/registeredRegExpComponentLocation/',
      //     template: 'tests/assets/fixtures/registeredRegExpComponentLocation/'
      //   });

      //   expect(namespaceName).not.toBeLoaded();

      //   fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

      //   setTimeout(function() {
      //     expect(namespaceName).toBeLoaded();
      //     done();
      //   }, ajaxWait);
      // });

      // it('can specify and load via a registered location with full file name', function(done) {
      //   var namespaceName = 'registered-component-location-fullname';

      //   fw.components.registerLocation(namespaceName, {
      //     viewModel: 'tests/assets/fixtures/registeredComponentLocation/registeredComponentLocationFullname.js',
      //     template: 'tests/assets/fixtures/registeredComponentLocation/registeredComponentLocationFullname.html'
      //   });

      //   expect(namespaceName).not.toBeLoaded();

      //   fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

      //   setTimeout(function() {
      //     expect(namespaceName).toBeLoaded();
      //     done();
      //   }, ajaxWait);
      // });

      // it('can specify and load via a registered location for a dataModel enabled component', function(done) {
      //   var namespaceName = 'registered-datamodel-component-location';

      //   fw.components.registerLocation(namespaceName, {
      //     dataModel: 'tests/assets/fixtures/registeredComponentLocation/',
      //     template: 'tests/assets/fixtures/registeredComponentLocation/'
      //   });

      //   expect(namespaceName).not.toBeLoaded();

      //   fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

      //   setTimeout(function() {
      //     expect(namespaceName).toBeLoaded();
      //     done();
      //   }, ajaxWait);
      // });

      // it('can specify and load via a registered location for a router enabled component', function(done) {
      //   var namespaceName = 'registered-router-component-location';

      //   fw.components.registerLocation(namespaceName, {
      //     router: 'tests/assets/fixtures/registeredComponentLocation/',
      //     template: 'tests/assets/fixtures/registeredComponentLocation/'
      //   });

      //   expect(namespaceName).not.toBeLoaded();

      //   fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

      //   setTimeout(function() {
      //     expect(namespaceName).toBeLoaded();
      //     done();
      //   }, ajaxWait);
      // });
    });
  }
);
<commit_msg>all component tests now passing (149 total)<commit_after>define(['footwork', 'lodash', 'jquery', 'tools', 'fetch-mock'],
  function(fw, _, $, tools, fetchMock) {
    describe('components', function() {
      beforeEach(tools.prepareTestEnv);
      afterEach(tools.cleanTestEnv);

      it('can register a component', function() {
        var namespaceName = tools.generateNamespaceName();
        var invalidNamespaceName = tools.generateNamespaceName();
        var viewModelSpy = jasmine.createSpy('viewModelSpy');

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: viewModelSpy
        });

        expect(fw.components.isRegistered(namespaceName)).toBe(true);
        expect(fw.components.isRegistered(invalidNamespaceName)).not.toBe(true);
        expect(viewModelSpy).not.toHaveBeenCalled();

        expect(fw.components.getFileName(namespaceName)).toBe(null);
      });

      it('can instantiate a registered component via a <declarative> statement', function(done) {
        var namespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this);
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('can instantiate a registered component via a <declarative> statement with a dataModel', function(done) {
        var namespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this);
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('can instantiate a registered component via a <declarative> statement with a router', function(done) {
        var namespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(namespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this);
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('calls the dispose() callback of a viewModel when the parent component is removed from the DOM', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var viewModelNamespaceName = tools.generateNamespaceName();
        var viewModelInitializeSpy;
        var componentInitializeSpy;
        var componentOnDisposeSpy;
        var containerViewModel;
        var valueToCheckFor = tools.randomString();

        fw.viewModel.register(viewModelNamespaceName, tools.expectCallOrder(0, viewModelInitializeSpy = jasmine.createSpy('viewModelInitializeSpy', function() {
          fw.viewModel.boot(this, {
            namespace: viewModelNamespaceName
          });
          this.show = fw.observable(true);
          containerViewModel = this;
        }).and.callThrough()));

        fw.components.register(componentNamespaceName, {
          template: '',
          viewModel: tools.expectCallOrder(1, componentInitializeSpy = jasmine.createSpy('componentInitializeSpy', function() {
            fw.viewModel.boot(this, {
              namespace: componentNamespaceName,
              onDispose: tools.expectCallOrder(2, componentOnDisposeSpy = jasmine.createSpy('componentOnDisposeSpy', function(containingElement) {
                expect(containingElement.tagName).toBe(componentNamespaceName.toUpperCase());
              }).and.callThrough())
            });
            this.someProperty = valueToCheckFor;
          }).and.callThrough())
        });

        expect(viewModelInitializeSpy).not.toHaveBeenCalled();
        expect(componentInitializeSpy).not.toHaveBeenCalled();
        expect(componentOnDisposeSpy).toBe(undefined);

        fw.start(testContainer = tools.getFixtureContainer(
          '<viewModel module="' + viewModelNamespaceName + '">\

\
              <' + componentNamespaceName + '></' + componentNamespaceName + '>\

\
          </viewModel>'
        ));

        setTimeout(function() {
          expect(viewModelInitializeSpy).toHaveBeenCalled();
          expect(componentInitializeSpy).toHaveBeenCalled();
          expect($(testContainer).find('.content').text().indexOf(valueToCheckFor)).not.toBe(-1);

          containerViewModel.show(false);

          expect(componentOnDisposeSpy).toHaveBeenCalled();
          expect($(testContainer).find('.content').text().indexOf(valueToCheckFor)).toBe(-1);
          done();
        }, ajaxWait);
      });

      it('has the animation classes applied properly', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var viewModelNamespaceName = tools.generateNamespaceName();
        var initializeSpy;
        var afterRenderSpy;
        var theElement;

        fw.components.register(componentNamespaceName, {
          template: '
a template
',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function() {
            fw.viewModel.boot(this, {
              afterRender: tools.expectCallOrder(1, afterRenderSpy = jasmine.createSpy('afterRenderSpy', function(element) {
                theElement = element;
                expect($(theElement).hasClass(footworkAnimationClass)).toBe(false);
              }).and.callThrough())
            })
          }).and.callThrough())
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + componentNamespaceName + '></' + componentNamespaceName + '>'));

        expect(afterRenderSpy).toBe(undefined);

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          expect($(theElement).hasClass(footworkAnimationClass)).toBe(false);
          expect(afterRenderSpy).toHaveBeenCalled();

          setTimeout(function() {
            expect($(theElement).hasClass(footworkAnimationClass)).toBe(true);
            done();
          }, ajaxWait);
        }, 0);
      });

      it('can sequence animations', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var footworkAnimatedElements = '.' + footworkAnimationClass;

        fw.components.register(componentNamespaceName, {
          template: '
a template
',
          viewModel: function() {
            fw.viewModel.boot(this, {
              namespace: componentNamespaceName,
              sequenceAnimations: 30
            })
          }
        });

        testContainer = tools.getFixtureContainer('
\
          <' + componentNamespaceName + '></' + componentNamespaceName + '>\

');

        expect($(testContainer).find(footworkAnimatedElements).length).toBe(0);

        fw.applyBindings({
          things: [ {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} ]
        }, testContainer);

        setTimeout(function() {
          var $testContainer = $(testContainer);
          var currentThingLength = $testContainer.find(footworkAnimatedElements).length;

          expect(currentThingLength).toBeGreaterThan(0);

          setTimeout(function() {
            expect($testContainer.find(footworkAnimatedElements)).lengthToBeGreaterThan(currentThingLength);
            done();
          }, 120);
        }, 100);
      });

      it('can instantiate nested <components>', function(done) {
        var outerInitializeSpy = jasmine.createSpy('outerInitializeSpy');
        var innerInitializeSpy = jasmine.createSpy('innerInitializeSpy');
        var outerComponentNamespaceName = tools.generateNamespaceName();
        var innerComponentNamespaceName = tools.generateNamespaceName();

        fw.components.register(outerComponentNamespaceName, {
          template: '<' + innerComponentNamespaceName + '></' + innerComponentNamespaceName + '>',
          viewModel: tools.expectCallOrder(0, outerInitializeSpy)
        });

        fw.components.register(innerComponentNamespaceName, {
          template: '

',
          viewModel: tools.expectCallOrder(1, innerInitializeSpy)
        });

        expect(outerInitializeSpy).not.toHaveBeenCalled();
        expect(innerInitializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + outerComponentNamespaceName + '></' + outerComponentNamespaceName + '>'));

        setTimeout(function() {
          expect(outerInitializeSpy).toHaveBeenCalled();
          expect(innerInitializeSpy).toHaveBeenCalled();

          done();
        }, ajaxWait);
      });

      it('can pass params to a component viewModel', function(done) {
        var componentNamespaceName = tools.generateNamespaceName();
        var initializeSpy;

        fw.components.register(componentNamespaceName, {
          template: '

',
          viewModel: tools.expectCallOrder(0, initializeSpy = jasmine.createSpy('initializeSpy', function(params) {
            expect(params).toEqual({ testValueOne: 1, testValueTwo: [1,2,3] });
          }))
        });

        expect(initializeSpy).not.toHaveBeenCalled();

        fw.start(testContainer = tools.getFixtureContainer('<' + componentNamespaceName + ' params="testValueOne: 1, testValueTwo: [1,2,3]"></' + componentNamespaceName + '>'));

        setTimeout(function() {
          expect(initializeSpy).toHaveBeenCalled();
          done();
        }, ajaxWait);
      });

      it('can set and return fileExtensions correctly', function() {
        var originalExtensions = fw.components.fileExtensions();
        var fileName = tools.randomString();
        var extensions = {
          combined: '.combinedTest',
          viewModel: '.viewModelTest',
          template: function() {
            return '.templateTest';
          }
        };

        expect(fw.components.fileExtensions()).not.toEqual(extensions);
        fw.components.fileExtensions(extensions);
        expect(fw.components.fileExtensions()).toEqual(extensions);
        expect(fw.components.getFileName(fileName, 'template')).toEqual(fileName + extensions.template());

        // reset extensions back to normal
        fw.components.fileExtensions(originalExtensions);
        expect(fw.components.fileExtensions()).not.toEqual(extensions);
      });

      it('can set fileExtensions via a callback', function() {
        var originalExtensions = fw.components.fileExtensions();

        function prependName(name, withName) {
          return _.reduce({
            combined: '.' + name + 'combinedTest',
            viewModel: '.' + name + 'viewModelTest',
            template: function() {
              return (withName ? name : '') + '.' + 'templateTest';
            }
          }, function(ext, extension, property) {
            ext[property] = _.isFunction(extension) ? extension() : (withName ? name : '');
            return ext;
          }, {});
        }

        var getFileExtensionsSpy = jasmine.createSpy('getFileExtensionsSpy', function getFileExtensions(componentName) {
          return prependName(componentName);
        }).and.callThrough();

        fw.components.fileExtensions(tools.expectCallOrder([0, 1, 2], getFileExtensionsSpy));

        var comp1Check = prependName('comp1', true);
        var comp2Check = prependName('comp2', true);

        expect(fw.components.getFileName('comp1', 'viewModel')).toEqual(comp1Check.viewModel);
        expect(fw.components.getFileName('comp2', 'viewModel')).toEqual(comp2Check.viewModel);
        expect(fw.components.getFileName('comp2', 'template')).toEqual(comp2Check.template);

        // reset extensions back to normal
        fw.components.fileExtensions(originalExtensions);
        expect(fw.components.fileExtensions()).toEqual(originalExtensions);
      });

      it('can specify a location and verify it', function() {
        var namespaceName = tools.generateNamespaceName();
        var location = {
          viewModel: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        };

        fw.components.registerLocation(namespaceName, location);
        fw.components.registerLocation(/^regexp-test.*$/, location);

        expect(fw.components.getLocation(namespaceName)).toEqual(location);
        expect(fw.components.getLocation('regexp-test-regexp')).toEqual(location);
      });

      it('can register an array of components to a location and retrieve them proplerly', function() {
        var location = {
          viewModel: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        };

        var namespaceNames = [tools.generateNamespaceName(), tools.generateNamespaceName(), tools.generateNamespaceName()];
        fw.components.registerLocation(namespaceNames, location);

        _.each(namespaceNames, function(namespaceName) {
          expect(fw.components.getLocation(namespaceName)).toEqual(location);
        });
      });

      it('can specify and load via a registered location', function(done) {
        var namespaceName = 'registered-component-location';

        fw.components.registerLocation('registered-component-location', {
          viewModel: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        });

        expect(namespaceName).not.toBeLoaded();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(namespaceName).toBeLoaded();
          done();
        }, ajaxWait);
      });

      it('can specify and load via a registered location with a prefixed folder', function(done) {
        var namespaceName = 'registered-component-location-prefixed';

        fw.components.registerLocation(namespaceName, {
          viewModel: 'tests/assets/fixtures/',
          template: 'tests/assets/fixtures/'
        }, true);

        expect(namespaceName).not.toBeLoaded();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(namespaceName).toBeLoaded();
          done();
        }, ajaxWait);
      });

      it('can specify and load via a registered RegExp-based location', function(done) {
        var namespaceName = 'registered-regexp-component-location';

        fw.components.registerLocation(/registered-regexp-comp.*/, {
          viewModel: 'tests/assets/fixtures/registeredRegExpComponentLocation/',
          template: 'tests/assets/fixtures/registeredRegExpComponentLocation/'
        });

        expect(namespaceName).not.toBeLoaded();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(namespaceName).toBeLoaded();
          done();
        }, ajaxWait);
      });

      it('can specify and load via a registered location with full file name', function(done) {
        var namespaceName = 'registered-component-location-fullname';

        fw.components.registerLocation(namespaceName, {
          viewModel: 'tests/assets/fixtures/registeredComponentLocation/registeredComponentLocationFullname.js',
          template: 'tests/assets/fixtures/registeredComponentLocation/registeredComponentLocationFullname.html'
        });

        expect(namespaceName).not.toBeLoaded();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(namespaceName).toBeLoaded();
          done();
        }, ajaxWait);
      });

      it('can specify and load via a registered location for a dataModel enabled component', function(done) {
        var namespaceName = 'registered-datamodel-component-location';

        fw.components.registerLocation(namespaceName, {
          dataModel: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        });

        expect(namespaceName).not.toBeLoaded();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(namespaceName).toBeLoaded();
          done();
        }, ajaxWait);
      });

      it('can specify and load via a registered location for a router enabled component', function(done) {
        var namespaceName = 'registered-router-component-location';

        fw.components.registerLocation(namespaceName, {
          router: 'tests/assets/fixtures/registeredComponentLocation/',
          template: 'tests/assets/fixtures/registeredComponentLocation/'
        });

        expect(namespaceName).not.toBeLoaded();

        fw.start(testContainer = tools.getFixtureContainer('<' + namespaceName + '></' + namespaceName + '>'));

        setTimeout(function() {
          expect(namespaceName).toBeLoaded();
          done();
        }, ajaxWait);
      });
    });
  }
);
<|endoftext|>


<commit_before>const {KVStore} = require('../models/kv-store');
const {Summoner} = require('../models/summoner');
const async = require('async');

module.exports = class Repo {
  saveSummoners(summonersData, callback) {
    let self = this;
    Summoner.insertMany(summonersData, {ordered: false}, (err, docs) => {
      if(err) {
        err = err
        .filter(error => error.code !== 11000)
        .map(error => error.toJSON());
        console.error('Error Saving Summoners:', err);
      }
      callback(null);
    });
  }
  saveSummoner(summonerData, callback) {
    Summoner.create(summonerData, (err, doc) => {
      if(err && err.code !== 11000) {
        console.error(err);
      }
      callback(null);
    });
  }
}<commit_msg>saving summoners in batches<commit_after>const {KVStore} = require('../models/kv-store');
const {Summoner} = require('../models/summoner');
const async = require('async');

module.exports = class Repo {
  saveSummoners(summonersData, callback) {
    let self = this;
    Summoner.insertMany(summonersData, {ordered: false}, (err, docs) => {
      if(err && err.writeErrors) {
        err = err.writeErrors
        .filter(error => error.code !== 11000)
        .map(error => error.toJSON());
      } else if (err) {
        console.error('Error Saving Summoners:', err);
      }
      callback(null);
    });
  }
  saveSummoner(summonerData, callback) {
    Summoner.create(summonerData, (err, doc) => {
      if(err && err.code !== 11000) {
        console.error(err);
      }
      callback(null);
    });
  }
}<|endoftext|>


<commit_before>module.exports = exports = function load(grunt) {
    var config = {
        bundle: {
            main: {
                rootModule: 'plat',
                license: './license.txt',
                version: '<%= pkg.version %>',
                src: './app/index.html',
                dest: [
                    './platypus.ts'
                ],
                disableLint: true
            }
        },
        pkg: grunt.file.readJSON('package.json')
    };

    grunt.initConfig(config);
    grunt.loadNpmTasks('grunt-ts-bundle');
    // By default, run all tests.
    grunt.registerTask('default', ['bundle']);
};
<commit_msg>updated grunt file<commit_after>module.exports = exports = function load(grunt) {
    var config = {
        bundle: {
            main: {
                rootModule: 'plat',
                license: './license.txt',
                version: '<%= pkg.version %>',
                src: './app/index.html',
                dest: [
                    './platypus.ts'
                ],
                disableLint: true,
                preSave: function (data, done) {
                    done(data);
                }
            }
        },
        pkg: grunt.file.readJSON('package.json')
    };

    grunt.initConfig(config);
    grunt.loadNpmTasks('grunt-ts-bundle');
    // By default, run all tests.
    grunt.registerTask('default', ['bundle']);
};
<|endoftext|>


<commit_before>describe('Cartesian frame', function () {
    var $el, el;
    var narwhal;

    beforeEach(function () {
        $el = $('<div>');
        el = $el.get(0);
    });

    function createNarwhal(options) {
        options = _.extend({ el: el }, options);
        narwhal = new Narwhal(options).cartesian();
        return narwhal;
    }

    it('should expose "cartesian" constructor function', function () {
        expect(Narwhal.prototype.cartesian).toBeDefined();
    });

    describe('constructor', function () {
    });

    describe('default xScale', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should throw if no data is set', function () {
            expect(narwhal.render).toThrow();
        });

        it('should be an ordinal scaling', function () {
            narwhal.data([0,10,20,30]).render();

            // TODO: NEED TO FIX THIS TEST

            // expect(narwhal.xScale(0)).toEqual(0);
            // expect(narwhal.xScale(1)).toEqual(100);
            // expect(narwhal.xScale(2)).toEqual(200);
            // expect(narwhal.xScale(3)).toEqual(300);
        });
    });

    describe('default yScale', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should be an inverted linear scaling', function () {
            narwhal.data([0,10,20,30,40]).render();
            var h = narwhal.options.chart.plotHeight;

            expect(narwhal.yScale(0)).toEqual(h);
            expect(narwhal.yScale(20)).toEqual(h/2);
            expect(narwhal.yScale(40)).toEqual(0);
        });

    });

    describe('default xAxis', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should not have anyTicks', function () {
            narwhal.data([0,10,20,30]).render();
            var ticks = $el.find('.x.axis ');
            expect(_.all(ticks.find('.tick.major line'), function (t) { return $(t).attr('x2') === '0' && $(t).attr('y2') === '0'; })).toBe(true);
            expect(ticks.find('.domain').attr('d')).toContain('M0');
        });

        describe('with options.xAxis.min set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ xAxis: { min: 5 }});
            });

            xit('should use the options.xAxis.min as the min of the domain', function () {
                narwhal.data([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]).render();
                // a value equal to the max should be scaled at the top of the chart (y=0)
                expect(narwhal.xScale(5)).toBe(0);
            });

            xit('should merge options.xAxis.min as the first tick', function () {
                // narwhal.data([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]).render();

                // var ticks = $el.find('.x.axis .tick.major');
                // var topTick = ticks.first();
                // var correctOrder = true;

                // expect(topTick.find('text').text()).toBe('5');
                // expect(topTick.attr('transform')).toBe('translate(0,0)');
                // ticks.each(function (t, i) { correctOrder = correctOrder && $(t).find('text').text() === (i + 5) + ''; });
                // expect(correctOrder).toBe(true);
            });
        });

        describe('with options.xAxis.max set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ xAxis: { max: 7 }});
            });

            xit('should use the options.xAxis.max as the max of the domain', function () {
                narwhal.data([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]).render();
                // a value equal to the max should be scaled at the top of the chart (y=0)
                expect(narwhal.xScale(7)).toBe(narwhal.options.chart.plotWidth);
            });
        });

        it('should only show first and last category labels by default', function () {
           // var config = { xAxis: { firstAndLast: true } };
            narwhal = createNarwhal()
                .data([10, 20, 30])
                .render();

            var xLabels = narwhal.svg.selectAll('.x.axis .tick text')[0];
            expect(xLabels.length).toBe(2);
            expect($(xLabels[0]).text()).toBe('0');
            expect($(xLabels[1]).text()).toBe('2');
        });

        it('should show all category labels if firstAndLast is set to false', function () {
            var config = { xAxis: { firstAndLast: false } };
            narwhal = createNarwhal(config)
                .data([10, 20, 30])
                .render();

            var xLabels = narwhal.svg.selectAll('.x.axis .tick text')[0];
            expect(xLabels.length).toBe(3);
            expect($(xLabels[0]).text()).toBe('0');
            expect($(xLabels[1]).text()).toBe('1');
            expect($(xLabels[2]).text()).toBe('2');
        });

        it('should render category labels if xAxis.categories is defined', function () {
            narwhal = createNarwhal({ xAxis: { categories: ['one', 'two', 'three'], firstAndLast: false }})
                .data([10, 20, 30])
                .render();

            var xLabels = narwhal.svg.selectAll('.x.axis .tick text')[0];
            expect(xLabels.length).toBe(3);
            expect($(xLabels[0]).text()).toBe('one');
            expect($(xLabels[1]).text()).toBe('two');
            expect($(xLabels[2]).text()).toBe('three');
        });

        it('should be time scale if data has x field as date/time', function () {
            var now = new Date();
            var data = [
                {x: now, y: 1},
                {x: now, y: 2},
                {x: now, y: 3}
            ];

            narwhal = createNarwhal()
                .data(data).render();

        });

    });

    describe('default yAxis', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should have outerTick', function () {
            narwhal.data([0,10,20,30]).render();
            // the actual axis path should start at -6 (the default outerTickSize)
            expect($el.find('.y.axis .domain').attr('d')).toContain('M-6');
        });

        it('should align the top of the label to the tick', function () {
            narwhal.data([0,10,20,30]).render();
            var ticks = $el.find('.y.axis .tick.major text');
            expect(_.all(ticks, function (t) { return $(t).attr('dy') === '.9em'; })).toBe(true);
        });

        describe('with options.yAxis.max set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ yAxis: { max: 100 }});
            });

            it('should use the options.yAxis.max as the max of the domain', function () {
                narwhal.data([0,10,20,30]).render();
                // a value equal to the max should be scaled at the top of the chart (y=0)
                expect(narwhal.yScale(100)).toBe(0);
            });

            it('should merge options.yAxis.min as the last tick', function () {
                narwhal.data([0,10,20,30]).render();
                var topTick = $el.find('.y.axis .tick').last();
                expect(topTick.find('text').text()).toBe('100');
                expect(topTick.attr('transform')).toBe('translate(0,0)');
            });

            it('should handle the case where max is less than the data set\'s min', function () {
                narwhal.data([200, 300, 400]).render();
                var ticks = $el.find('.y.axis .tick');

                expect(ticks.length).toBe(1);
            });
        });

        describe('with options.yAxis.min set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ yAxis: { min: 3 }});
            });

            it('should merge options.yAxis.min as the first tick', function () {
                narwhal.data([10,20,30]).render();
                var topTick = $el.find('.y.axis .tick').first();
                expect(topTick.find('text').text()).toBe('3');
                expect(topTick.attr('transform')).toBe('translate(0,' + narwhal.options.chart.plotHeight + ')');
            });

            it('should use it as the abs min of the domain', function () {
                narwhal.data([10,20,30]).render();
                // a value equals to the min should be at the bottom of the chart
                // so it should be equal plotHeight (y grows down)
                expect(narwhal.yScale(3)).toBe(narwhal.options.chart.plotHeight);
            });

            it('should handle the case where min is greater than the data set\'s max', function () {
                narwhal.data([1,2,3]).render();
                var ticks = $el.find('.y.axis .tick');

                expect(ticks.length).toBe(1);
            });
        });

        describe('with both min and max set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({yAxis: { min: -2, max: 500 }});
            });

            it('should set the domain to be [min, max]', function () {
                narwhal.data([1,2,3]).render();

                expect(narwhal.yScale(500)).toBe(0);
                expect(narwhal.yScale(-2)).toBe(narwhal.options.chart.plotHeight);
            });
        });

    });

    describe('default', function () {
        describe('xAxis title', function () {
            it('should be included if specificed in the options', function () {
                narwhal = createNarwhal({ xAxis: { title: 'xAxis' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.axis-title');
                expect(titles.length).toBe(1);
                expect(titles.text()).toBe('xAxis');
            });

            it('should NOT be included if NOT specificed in the options', function () {
                narwhal = createNarwhal({ xAxis: { title: '' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.x.axis-title');
                expect(titles.length).toBe(0);
            });

            it('should adjust bottom padding and plot height to fit the title', function () {
                createNarwhal({ xAxis: { title: 'hello world' }}).data([1,2,3]).render();
                var y = narwhal.options.chart.height - narwhal.options.chart.padding.bottom - narwhal.options.chart.padding.top;

                expect(narwhal.options.chart.padding.bottom).toBeGreaterThan(0);
                expect(narwhal.options.chart.plotHeight).toEqual(y);
            });

        });

        describe('yAxis title', function () {
            it('should be included if specificed in the options', function () {
                narwhal = createNarwhal({ yAxis: { title: 'yAxis' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.y.axis-title');
                expect(titles.length).toBe(1);
                expect(titles.text()).toBe('yAxis');
            });

            it('should NOT be included if NOT specificed in the options', function () {
                narwhal = createNarwhal({ yAxis: { title: '' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.axis-title');
                expect(titles.length).toBe(0);
            });

        });
    });

    describe('render', function () {
        // this should be in the render, because is data dependent
        it('should provide scaling function (xScale & yScale) for visualizations to use', function () {
            var target = createNarwhal()
                .data([1,2,3])
                .render();

            expect(target.xScale).toBeDefined();
            expect(target.yScale).toBeDefined();
        });

        it('should call visualizations to render!', function () {
            var mock = { render: function () { }};
            var target = createNarwhal();
            spyOn(mock, 'render');

            target.visualizations.push(mock.render);
            target.data([1,2]).render();

            expect(mock.render).toHaveBeenCalled();
        });

        it('should render an xAxis at the bottom of the chart', function () {
            createNarwhal().data([1,2,3]).render();
            var axis = $el.find('.x.axis');
            var y = narwhal.options.chart.plotHeight + narwhal.options.chart.padding.top;
            var x = narwhal.options.chart.padding.left;

            expect(axis.length).toEqual(1);
            expect(axis.attr('transform')).toEqual('translate(' + x + ',' + y + ')');
        });

        it('should adjust bottom padding and plot height to fit the xAxis and labels', function () {
            createNarwhal().data([1,2,3]).render();
            var y = narwhal.options.chart.height - narwhal.options.chart.padding.bottom - narwhal.options.chart.padding.top;

            expect(narwhal.options.chart.padding.bottom).toBeGreaterThan(0);
            expect(narwhal.options.chart.plotHeight).toEqual(y);
        });

        it('should render an yAxis at the left of the chart', function () {
            createNarwhal().data([1,2,3]).render();
            var axis = $el.find('.y.axis');
            var x = narwhal.options.chart.padding.left;
            var y = narwhal.options.chart.padding.top;

            expect(axis.length).toEqual(1);
            expect(axis.attr('transform')).toEqual('translate(' + x + ',' + y + ')');
        });

        it('should position the yAxis at the left with some padding', function () {
            createNarwhal().data([1,2,3]).render();
            var x = narwhal.options.chart.width - narwhal.options.chart.padding.left - narwhal.options.chart.padding.right;

            expect(narwhal.options.chart.padding.left).toBeGreaterThan(0);
            expect(narwhal.options.chart.plotWidth).toEqual(x);
        });

        describe('rangeBand (width of each x axis band)', function () {
            it('should be set default to 0 (rangePoint)', function () {
                createNarwhal().data([1,2,3]).render();
                expect(narwhal.rangeBand).toBe(0);
            });
        });

        it('should render visualizations after all axis and other elements (zindex)', function () {
            createNarwhal().data([1,2,3]);
            narwhal.visualizations.push(function (svg) {
                // add a dummy svg element
                svg.append('g')
                    .attr('name', 'myDummyVisualization');
            });

            narwhal.render();

            var dummy = $(narwhal.svg[0]).children().last();
            expect(dummy.attr('name')).toBe('myDummyVisualization');
        });
    });

});
<commit_msg>Refactor test<commit_after>describe('Cartesian frame', function () {
    var $el, el;
    var narwhal;

    beforeEach(function () {
        $el = $('<div>');
        el = $el.get(0);
    });

    function createNarwhal(options) {
        options = _.extend({ el: el }, options);
        narwhal = new Narwhal(options).cartesian();
        return narwhal;
    }

    it('should expose "cartesian" constructor function', function () {
        expect(Narwhal.prototype.cartesian).toBeDefined();
    });

    describe('constructor', function () {
    });

    describe('default xScale', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should throw if no data is set', function () {
            expect(narwhal.render).toThrow();
        });

        it('should be an ordinal scaling', function () {
            narwhal.data([0,10,20,30]).render();

            // TODO: NEED TO FIX THIS TEST

            // expect(narwhal.xScale(0)).toEqual(0);
            // expect(narwhal.xScale(1)).toEqual(100);
            // expect(narwhal.xScale(2)).toEqual(200);
            // expect(narwhal.xScale(3)).toEqual(300);
        });
    });

    describe('default yScale', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should be an inverted linear scaling', function () {
            narwhal.data([0,10,20,30,40]).render();
            var h = narwhal.options.chart.plotHeight;

            expect(narwhal.yScale(0)).toEqual(h);
            expect(narwhal.yScale(20)).toEqual(h/2);
            expect(narwhal.yScale(40)).toEqual(0);
        });

    });

    describe('default xAxis', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should not have anyTicks', function () {
            narwhal.data([0,10,20,30]).render();
            var ticks = $el.find('.x.axis ');
            expect(_.all(ticks.find('.tick.major line'), function (t) { return $(t).attr('x2') === '0' && $(t).attr('y2') === '0'; })).toBe(true);
            expect(ticks.find('.domain').attr('d')).toContain('M0');
        });

        describe('with options.xAxis.min set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ xAxis: { min: 5 }});
            });

            xit('should use the options.xAxis.min as the min of the domain', function () {
                narwhal.data([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]).render();
                // a value equal to the max should be scaled at the top of the chart (y=0)
                expect(narwhal.xScale(5)).toBe(0);
            });

            xit('should merge options.xAxis.min as the first tick', function () {
                // narwhal.data([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]).render();

                // var ticks = $el.find('.x.axis .tick.major');
                // var topTick = ticks.first();
                // var correctOrder = true;

                // expect(topTick.find('text').text()).toBe('5');
                // expect(topTick.attr('transform')).toBe('translate(0,0)');
                // ticks.each(function (t, i) { correctOrder = correctOrder && $(t).find('text').text() === (i + 5) + ''; });
                // expect(correctOrder).toBe(true);
            });
        });

        describe('with options.xAxis.max set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ xAxis: { max: 7 }});
            });

            xit('should use the options.xAxis.max as the max of the domain', function () {
                narwhal.data([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]).render();
                // a value equal to the max should be scaled at the top of the chart (y=0)
                expect(narwhal.xScale(7)).toBe(narwhal.options.chart.plotWidth);
            });
        });

        it('should only show first and last tick labels by default', function () {
           // var config = { xAxis: { firstAndLast: true } };
            narwhal = createNarwhal()
                .data([10, 20, 30])
                .render();

            var xLabels = narwhal.svg.selectAll('.x.axis .tick text')[0];
            expect(xLabels.length).toBe(2);
            expect($(xLabels[0]).text()).toBe('0');
            expect($(xLabels[1]).text()).toBe('2');
        });

        it('should show all category labels if firstAndLast is set to false', function () {
            var config = { xAxis: { firstAndLast: false } };
            narwhal = createNarwhal(config)
                .data([10, 20, 30])
                .render();

            var xLabels = narwhal.svg.selectAll('.x.axis .tick text')[0];
            expect(xLabels.length).toBe(3);
            expect($(xLabels[0]).text()).toBe('0');
            expect($(xLabels[1]).text()).toBe('1');
            expect($(xLabels[2]).text()).toBe('2');
        });

        it('should render category labels if xAxis.categories is defined', function () {
            narwhal = createNarwhal({ xAxis: { categories: ['one', 'two', 'three'], firstAndLast: false }})
                .data([10, 20, 30])
                .render();

            var xLabels = narwhal.svg.selectAll('.x.axis .tick text')[0];
            expect(xLabels.length).toBe(3);
            expect($(xLabels[0]).text()).toBe('one');
            expect($(xLabels[1]).text()).toBe('two');
            expect($(xLabels[2]).text()).toBe('three');
        });

        it('should be time scale if data has x field as date/time', function () {
            var now = new Date();
            var data = [
                {x: now, y: 1},
                {x: now, y: 2},
                {x: now, y: 3}
            ];

            narwhal = createNarwhal()
                .data(data).render();

        });

    });

    describe('default yAxis', function () {
        beforeEach(function () {
            narwhal = createNarwhal();
        });

        it('should have outerTick', function () {
            narwhal.data([0,10,20,30]).render();
            // the actual axis path should start at -6 (the default outerTickSize)
            expect($el.find('.y.axis .domain').attr('d')).toContain('M-6');
        });

        it('should align the top of the label to the tick', function () {
            narwhal.data([0,10,20,30]).render();
            var ticks = $el.find('.y.axis .tick.major text');
            expect(_.all(ticks, function (t) { return $(t).attr('dy') === '.9em'; })).toBe(true);
        });

        describe('with options.yAxis.max set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ yAxis: { max: 100 }});
            });

            it('should use the options.yAxis.max as the max of the domain', function () {
                narwhal.data([0,10,20,30]).render();
                // a value equal to the max should be scaled at the top of the chart (y=0)
                expect(narwhal.yScale(100)).toBe(0);
            });

            it('should merge options.yAxis.min as the last tick', function () {
                narwhal.data([0,10,20,30]).render();
                var topTick = $el.find('.y.axis .tick').last();
                expect(topTick.find('text').text()).toBe('100');
                expect(topTick.attr('transform')).toBe('translate(0,0)');
            });

            it('should handle the case where max is less than the data set\'s min', function () {
                narwhal.data([200, 300, 400]).render();
                var ticks = $el.find('.y.axis .tick');

                expect(ticks.length).toBe(1);
            });
        });

        describe('with options.yAxis.min set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({ yAxis: { min: 3 }});
            });

            it('should merge options.yAxis.min as the first tick', function () {
                narwhal.data([10,20,30]).render();
                var topTick = $el.find('.y.axis .tick').first();
                expect(topTick.find('text').text()).toBe('3');
                expect(topTick.attr('transform')).toBe('translate(0,' + narwhal.options.chart.plotHeight + ')');
            });

            it('should use it as the abs min of the domain', function () {
                narwhal.data([10,20,30]).render();
                // a value equals to the min should be at the bottom of the chart
                // so it should be equal plotHeight (y grows down)
                expect(narwhal.yScale(3)).toBe(narwhal.options.chart.plotHeight);
            });

            it('should handle the case where min is greater than the data set\'s max', function () {
                narwhal.data([1,2,3]).render();
                var ticks = $el.find('.y.axis .tick');

                expect(ticks.length).toBe(1);
            });
        });

        describe('with both min and max set', function () {
            beforeEach(function () {
                narwhal = createNarwhal({yAxis: { min: -2, max: 500 }});
            });

            it('should set the domain to be [min, max]', function () {
                narwhal.data([1,2,3]).render();

                expect(narwhal.yScale(500)).toBe(0);
                expect(narwhal.yScale(-2)).toBe(narwhal.options.chart.plotHeight);
            });
        });

    });

    describe('default', function () {
        describe('xAxis title', function () {
            it('should be included if specificed in the options', function () {
                narwhal = createNarwhal({ xAxis: { title: 'xAxis' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.axis-title');
                expect(titles.length).toBe(1);
                expect(titles.text()).toBe('xAxis');
            });

            it('should NOT be included if NOT specificed in the options', function () {
                narwhal = createNarwhal({ xAxis: { title: '' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.x.axis-title');
                expect(titles.length).toBe(0);
            });

            it('should adjust bottom padding and plot height to fit the title', function () {
                createNarwhal({ xAxis: { title: 'hello world' }}).data([1,2,3]).render();
                var y = narwhal.options.chart.height - narwhal.options.chart.padding.bottom - narwhal.options.chart.padding.top;

                expect(narwhal.options.chart.padding.bottom).toBeGreaterThan(0);
                expect(narwhal.options.chart.plotHeight).toEqual(y);
            });

        });

        describe('yAxis title', function () {
            it('should be included if specificed in the options', function () {
                narwhal = createNarwhal({ yAxis: { title: 'yAxis' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.y.axis-title');
                expect(titles.length).toBe(1);
                expect(titles.text()).toBe('yAxis');
            });

            it('should NOT be included if NOT specificed in the options', function () {
                narwhal = createNarwhal({ yAxis: { title: '' } });
                narwhal.data([1,2,3]).render();
                titles = $el.find('.axis-title');
                expect(titles.length).toBe(0);
            });

        });
    });

    describe('render', function () {
        // this should be in the render, because is data dependent
        it('should provide scaling function (xScale & yScale) for visualizations to use', function () {
            var target = createNarwhal()
                .data([1,2,3])
                .render();

            expect(target.xScale).toBeDefined();
            expect(target.yScale).toBeDefined();
        });

        it('should call visualizations to render!', function () {
            var mock = { render: function () { }};
            var target = createNarwhal();
            spyOn(mock, 'render');

            target.visualizations.push(mock.render);
            target.data([1,2]).render();

            expect(mock.render).toHaveBeenCalled();
        });

        it('should render an xAxis at the bottom of the chart', function () {
            createNarwhal().data([1,2,3]).render();
            var axis = $el.find('.x.axis');
            var y = narwhal.options.chart.plotHeight + narwhal.options.chart.padding.top;
            var x = narwhal.options.chart.padding.left;

            expect(axis.length).toEqual(1);
            expect(axis.attr('transform')).toEqual('translate(' + x + ',' + y + ')');
        });

        it('should adjust bottom padding and plot height to fit the xAxis and labels', function () {
            createNarwhal().data([1,2,3]).render();
            var y = narwhal.options.chart.height - narwhal.options.chart.padding.bottom - narwhal.options.chart.padding.top;

            expect(narwhal.options.chart.padding.bottom).toBeGreaterThan(0);
            expect(narwhal.options.chart.plotHeight).toEqual(y);
        });

        it('should render an yAxis at the left of the chart', function () {
            createNarwhal().data([1,2,3]).render();
            var axis = $el.find('.y.axis');
            var x = narwhal.options.chart.padding.left;
            var y = narwhal.options.chart.padding.top;

            expect(axis.length).toEqual(1);
            expect(axis.attr('transform')).toEqual('translate(' + x + ',' + y + ')');
        });

        it('should position the yAxis at the left with some padding', function () {
            createNarwhal().data([1,2,3]).render();
            var x = narwhal.options.chart.width - narwhal.options.chart.padding.left - narwhal.options.chart.padding.right;

            expect(narwhal.options.chart.padding.left).toBeGreaterThan(0);
            expect(narwhal.options.chart.plotWidth).toEqual(x);
        });

        describe('rangeBand (width of each x axis band)', function () {
            it('should be set default to 0 (rangePoint)', function () {
                createNarwhal().data([1,2,3]).render();
                expect(narwhal.rangeBand).toBe(0);
            });
        });

        it('should render visualizations after all axis and other elements (zindex)', function () {
            createNarwhal().data([1,2,3]);
            narwhal.visualizations.push(function (svg) {
                // add a dummy svg element
                svg.append('g')
                    .attr('name', 'myDummyVisualization');
            });

            narwhal.render();

            var dummy = $(narwhal.svg[0]).children().last();
            expect(dummy.attr('name')).toBe('myDummyVisualization');
        });
    });

});
<|endoftext|>


<commit_before>"use strict";

var CityTour = CityTour || {};
CityTour.Scene = CityTour.Scene || {};

CityTour.Scene.TerrainGeometryBuilder = function() {
  var TERRAIN_COLOR_1 = new THREE.Color(0.0, 0.48, 0.0);
  var TERRAIN_COLOR_2 = new THREE.Color(0.0, 0.49, 0.0);
  var WATER_COLOR_1 = new THREE.Color(0.1, 0.2, 1.0);
  var WATER_COLOR_2 = new THREE.Color(0.1, 0.19, 1.0);

  var reusableTriangle = new THREE.Geometry();
  reusableTriangle.faces = [new THREE.Face3(0, 1, 2)];

  var buildTriangleGeometry = function(x1, y1, z1, material1, x2, y2, z2, material2, x3, y3, z3, material3) {
    reusableTriangle.vertices = [ new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x2, y2, z2), new THREE.Vector3(x3, y3, z3) ];
    reusableTriangle.faces[0].vertexColors = [(material1 === CityTour.Terrain.WATER) ? WATER_COLOR_1 : TERRAIN_COLOR_1,
                                              (material2 === CityTour.Terrain.WATER) ? WATER_COLOR_1 : TERRAIN_COLOR_1,
                                              (material3 === CityTour.Terrain.WATER) ? WATER_COLOR_1 : TERRAIN_COLOR_1,];
    reusableTriangle.computeFaceNormals();

    return reusableTriangle;
  };

  var terrainGeometryBuilder = {};

  terrainGeometryBuilder.build = function(terrain, roadNetwork) {
    var mapX, mapZ, triangle;
    var leftRoad, topRoad, bottomRoad, rightRoad;
    var topLeftRoad, topRightRoad, bottomLeftRoad, bottomRightRoad;
    var topLeftX, topLeftZ, topRightX, topRightZ, bottomLeftX, bottomLeftZ, bottomRightX, bottomRightZ;
    var topLeftHeight, topRightHeight, bottomLeftHeight, bottomRightHeight;
    var topLeftMaterial, topRightMaterial, bottomLeftMaterial, bottomRightMaterial;

    var halfStreetWidth = CityTour.Config.STREET_WIDTH / 2;
    var halfStreetDepth = CityTour.Config.STREET_DEPTH / 2;

    var triangleWidth = 1 / terrain.subDivisions();
    var triangleDepth = 1 / terrain.subDivisions();

    var terrainGeometry1 = new THREE.Geometry();
    var terrainGeometry2 = new THREE.Geometry();
    var waterGeometry1 = new THREE.Geometry();
    var waterGeometry2 = new THREE.Geometry();
    var terrainMaterial1 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });
    var terrainMaterial2 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });
    var waterMaterial1 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });
    var waterMaterial2 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });

    for (mapX = terrain.minColumn(); mapX < terrain.maxColumn(); mapX += triangleWidth) {
      for (mapZ = terrain.minRow(); mapZ < terrain.maxRow(); mapZ += triangleDepth) {
        topLeftRoad     = roadNetwork.getIntersectionSurfaceType(mapX, mapZ) === CityTour.RoadNetwork.TERRAIN_SURFACE;
        topRightRoad    = roadNetwork.getIntersectionSurfaceType(mapX + triangleWidth, mapZ) === CityTour.RoadNetwork.TERRAIN_SURFACE;
        bottomLeftRoad  = roadNetwork.getIntersectionSurfaceType(mapX, mapZ + triangleDepth) === CityTour.RoadNetwork.TERRAIN_SURFACE;
        bottomRightRoad = roadNetwork.getIntersectionSurfaceType(mapX + triangleWidth, mapZ + triangleDepth) === CityTour.RoadNetwork.TERRAIN_SURFACE;

        leftRoad = (Math.floor(mapX) === mapX) && roadNetwork.hasEdgeBetween(mapX, Math.floor(mapZ), mapX, Math.floor(mapZ) + 1, CityTour.RoadNetwork.TERRAIN_SURFACE);
        rightRoad = (Math.ceil(mapX + triangleWidth) === (mapX + triangleWidth)) && roadNetwork.hasEdgeBetween(mapX + triangleWidth, Math.floor(mapZ), mapX + triangleWidth, Math.floor(mapZ) + 1, CityTour.RoadNetwork.TERRAIN_SURFACE);
        topRoad = (Math.floor(mapZ) === mapZ) && roadNetwork.hasEdgeBetween(Math.floor(mapX), mapZ, Math.floor(mapX) + 1, mapZ, CityTour.RoadNetwork.TERRAIN_SURFACE);
        bottomRoad = (Math.ceil(mapZ + triangleDepth) === (mapZ + triangleDepth)) && roadNetwork.hasEdgeBetween(Math.floor(mapX), mapZ + triangleDepth, Math.floor(mapX) + 1, mapZ + triangleDepth, CityTour.RoadNetwork.TERRAIN_SURFACE);

        topLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        topLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topLeftRoad || leftRoad) {
          topLeftX += halfStreetWidth;
        }
        if (topLeftRoad || topRoad) {
          topLeftZ += halfStreetDepth;
        }

        topRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        topRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topRightRoad || rightRoad) {
          topRightX -= halfStreetWidth;
        }
        if (topRightRoad || topRoad) {
          topRightZ += halfStreetDepth;
        }

        bottomLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        bottomLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomLeftRoad || leftRoad) {
          bottomLeftX += halfStreetWidth;
        }
        if (bottomLeftRoad || bottomRoad) {
          bottomLeftZ -= halfStreetDepth;
        }

        bottomRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        bottomRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomRightRoad || rightRoad) {
          bottomRightX -= halfStreetWidth;
        }
        if (bottomRightRoad || bottomRoad) {
          bottomRightZ -= halfStreetDepth;
        }

        topLeftHeight = terrain.heightAtCoordinates(mapX, mapZ);
        topRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftHeight = terrain.heightAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        topLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ);
        topRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        // Core triangles
        triangle = buildTriangleGeometry(topLeftX,    topLeftHeight,    topLeftZ, topLeftMaterial,
                                         bottomLeftX, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                         topRightX,   topRightHeight,   topRightZ, topRightMaterial);
        if (topLeftMaterial === CityTour.Terrain.WATER && topRightMaterial === CityTour.Terrain.WATER && bottomLeftMaterial === CityTour.Terrain.WATER) {
          waterGeometry1.merge(triangle);
        }
        else {
          terrainGeometry1.merge(triangle);
        }

        triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                         bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                         topRightX,    topRightHeight, topRightZ, topRightMaterial);
        if (bottomLeftMaterial === CityTour.Terrain.WATER && topRightMaterial === CityTour.Terrain.WATER && bottomRightMaterial === CityTour.Terrain.WATER) {
          waterGeometry2.merge(triangle);
        }
        else {
          terrainGeometry2.merge(triangle);
        }

        // Extra left-side triangles
        if (!leftRoad) {
          if (topLeftRoad && !bottomLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, topLeftZ, topLeftMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             topLeftX, topLeftHeight, topLeftZ, topLeftMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (bottomLeftRoad && !topLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, topLeftZ, topLeftMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (topLeftRoad && bottomLeftRoad) {
            triangle = buildTriangleGeometry(bottomLeftX - CityTour.Config.STREET_WIDTH, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             topLeftX, topLeftHeight, topLeftZ, topLeftMaterial);
            terrainGeometry2.merge(triangle);
          }
        }

        // Extra right-side triangles
        if (!rightRoad) {
          if (topRightRoad && !bottomRightRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ, topRightMaterial,
                                             topRightX, topRightHeight, topRightZ, topRightMaterial);

            terrainGeometry1.merge(triangle);
          }

          if (bottomRightRoad && !topRightRoad) {
            triangle = buildTriangleGeometry(bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ, topRightMaterial);
            terrainGeometry1.merge(triangle);
          }

          if (topRightRoad && bottomRightRoad) {
            triangle = buildTriangleGeometry(topRightX,    topRightHeight, topRightZ, topRightMaterial,
                                             bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             topRightX + CityTour.Config.STREET_WIDTH, topRightHeight, topRightZ, topRightMaterial);
            terrainGeometry1.merge(triangle);
          }
        }

        // Extra top-side triangles
        if (!topRoad) {
          if (topLeftRoad && !topRightRoad) {
            triangle = buildTriangleGeometry(topLeftX,  topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topLeftMaterial,
                                             topLeftX,  topLeftHeight, topLeftZ, topLeftMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topRightMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (topRightRoad && !topLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topLeftMaterial,
                                             topRightX, topRightHeight, topRightZ, topRightMaterial,
                                             topRightX, topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topRightMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (topLeftRoad && topRightRoad) {
            triangle = buildTriangleGeometry(topLeftX, topLeftHeight, topLeftZ, topLeftMaterial,
                                             topRightX, topRightHeight, topRightZ, topRightMaterial,
                                             topRightX, topRightHeight, topRightZ - CityTour.Config.STREET_DEPTH, topRightMaterial);

            terrainGeometry2.merge(triangle);
          }
        }

        // Extra bottom-side triangles
        if (!bottomRoad) {
          if (bottomLeftRoad && !bottomRightRoad) {
            triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX,  bottomLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth), bottomLeftMaterial,
                                             bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth), bottomRightMaterial);
            terrainGeometry1.merge(triangle);
          }

          if (bottomRightRoad && !bottomLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth), bottomRightMaterial,
                                             bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial);
            terrainGeometry1.merge(triangle);
          }

          if (bottomLeftRoad && bottomRightRoad) {
            triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX,  bottomLeftHeight, bottomLeftZ + CityTour.Config.STREET_DEPTH, bottomLeftMaterial,
                                             bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial);

            terrainGeometry1.merge(triangle);
          }
        }

      }
    }

    var mesh1 = new THREE.Mesh(terrainGeometry1, terrainMaterial1);
    var mesh2 = new THREE.Mesh(terrainGeometry2, terrainMaterial2);
    var waterMesh1 = new THREE.Mesh(waterGeometry1, waterMaterial1);
    var waterMesh2 = new THREE.Mesh(waterGeometry2, waterMaterial2);

    return [mesh1, mesh2, waterMesh1, waterMesh2];
  };

  return terrainGeometryBuilder;
};
<commit_msg>Solid vs. gradient terrain shading can be set via constant<commit_after>"use strict";

var CityTour = CityTour || {};
CityTour.Scene = CityTour.Scene || {};

CityTour.Scene.TerrainGeometryBuilder = function() {
  var TERRAIN_COLOR_1 = new THREE.Color(0.0, 0.48, 0.0);
  var TERRAIN_COLOR_2 = new THREE.Color(0.0, 0.49, 0.0);
  var WATER_COLOR_1 = new THREE.Color(0.1, 0.2, 1.0);
  var WATER_COLOR_2 = new THREE.Color(0.1, 0.19, 1.0);

  var SOLID_SHADING_MODE = 1;
  var GRADIENT_SHADING_MODE = 2;
  var SHADING_MODE = SOLID_SHADING_MODE;

  var reusableTriangle = new THREE.Geometry();
  reusableTriangle.faces = [new THREE.Face3(0, 1, 2)];

  var buildTriangleGeometry = function(x1, y1, z1, material1, x2, y2, z2, material2, x3, y3, z3, material3) {
    reusableTriangle.vertices = [ new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x2, y2, z2), new THREE.Vector3(x3, y3, z3) ];

    if (SHADING_MODE === SOLID_SHADING_MODE) {
      if (material1 === CityTour.Terrain.WATER && material2 === CityTour.Terrain.WATER && material3 == CityTour.Terrain.WATER) {
        reusableTriangle.faces[0].vertexColors = [WATER_COLOR_1, WATER_COLOR_1, WATER_COLOR_1];
      }
      else {
        reusableTriangle.faces[0].vertexColors = [TERRAIN_COLOR_1, TERRAIN_COLOR_1, TERRAIN_COLOR_1];
      }
    }
    else {
      reusableTriangle.faces[0].vertexColors = [(material1 === CityTour.Terrain.WATER) ? WATER_COLOR_1 : TERRAIN_COLOR_1,
                                                (material2 === CityTour.Terrain.WATER) ? WATER_COLOR_1 : TERRAIN_COLOR_1,
                                                (material3 === CityTour.Terrain.WATER) ? WATER_COLOR_1 : TERRAIN_COLOR_1,];
    }

    reusableTriangle.computeFaceNormals();

    return reusableTriangle;
  };

  var terrainGeometryBuilder = {};

  terrainGeometryBuilder.build = function(terrain, roadNetwork) {
    var mapX, mapZ, triangle;
    var leftRoad, topRoad, bottomRoad, rightRoad;
    var topLeftRoad, topRightRoad, bottomLeftRoad, bottomRightRoad;
    var topLeftX, topLeftZ, topRightX, topRightZ, bottomLeftX, bottomLeftZ, bottomRightX, bottomRightZ;
    var topLeftHeight, topRightHeight, bottomLeftHeight, bottomRightHeight;
    var topLeftMaterial, topRightMaterial, bottomLeftMaterial, bottomRightMaterial;

    var halfStreetWidth = CityTour.Config.STREET_WIDTH / 2;
    var halfStreetDepth = CityTour.Config.STREET_DEPTH / 2;

    var triangleWidth = 1 / terrain.subDivisions();
    var triangleDepth = 1 / terrain.subDivisions();

    var terrainGeometry1 = new THREE.Geometry();
    var terrainGeometry2 = new THREE.Geometry();
    var waterGeometry1 = new THREE.Geometry();
    var waterGeometry2 = new THREE.Geometry();
    var terrainMaterial1 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });
    var terrainMaterial2 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });
    var waterMaterial1 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });
    var waterMaterial2 = new THREE.MeshLambertMaterial({ vertexColors: THREE.VertexColors });

    for (mapX = terrain.minColumn(); mapX < terrain.maxColumn(); mapX += triangleWidth) {
      for (mapZ = terrain.minRow(); mapZ < terrain.maxRow(); mapZ += triangleDepth) {
        topLeftRoad     = roadNetwork.getIntersectionSurfaceType(mapX, mapZ) === CityTour.RoadNetwork.TERRAIN_SURFACE;
        topRightRoad    = roadNetwork.getIntersectionSurfaceType(mapX + triangleWidth, mapZ) === CityTour.RoadNetwork.TERRAIN_SURFACE;
        bottomLeftRoad  = roadNetwork.getIntersectionSurfaceType(mapX, mapZ + triangleDepth) === CityTour.RoadNetwork.TERRAIN_SURFACE;
        bottomRightRoad = roadNetwork.getIntersectionSurfaceType(mapX + triangleWidth, mapZ + triangleDepth) === CityTour.RoadNetwork.TERRAIN_SURFACE;

        leftRoad = (Math.floor(mapX) === mapX) && roadNetwork.hasEdgeBetween(mapX, Math.floor(mapZ), mapX, Math.floor(mapZ) + 1, CityTour.RoadNetwork.TERRAIN_SURFACE);
        rightRoad = (Math.ceil(mapX + triangleWidth) === (mapX + triangleWidth)) && roadNetwork.hasEdgeBetween(mapX + triangleWidth, Math.floor(mapZ), mapX + triangleWidth, Math.floor(mapZ) + 1, CityTour.RoadNetwork.TERRAIN_SURFACE);
        topRoad = (Math.floor(mapZ) === mapZ) && roadNetwork.hasEdgeBetween(Math.floor(mapX), mapZ, Math.floor(mapX) + 1, mapZ, CityTour.RoadNetwork.TERRAIN_SURFACE);
        bottomRoad = (Math.ceil(mapZ + triangleDepth) === (mapZ + triangleDepth)) && roadNetwork.hasEdgeBetween(Math.floor(mapX), mapZ + triangleDepth, Math.floor(mapX) + 1, mapZ + triangleDepth, CityTour.RoadNetwork.TERRAIN_SURFACE);

        topLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        topLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topLeftRoad || leftRoad) {
          topLeftX += halfStreetWidth;
        }
        if (topLeftRoad || topRoad) {
          topLeftZ += halfStreetDepth;
        }

        topRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        topRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ);
        if (topRightRoad || rightRoad) {
          topRightX -= halfStreetWidth;
        }
        if (topRightRoad || topRoad) {
          topRightZ += halfStreetDepth;
        }

        bottomLeftX = CityTour.Coordinates.mapXToSceneX(mapX);
        bottomLeftZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomLeftRoad || leftRoad) {
          bottomLeftX += halfStreetWidth;
        }
        if (bottomLeftRoad || bottomRoad) {
          bottomLeftZ -= halfStreetDepth;
        }

        bottomRightX = CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth);
        bottomRightZ = CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth);
        if (bottomRightRoad || rightRoad) {
          bottomRightX -= halfStreetWidth;
        }
        if (bottomRightRoad || bottomRoad) {
          bottomRightZ -= halfStreetDepth;
        }

        topLeftHeight = terrain.heightAtCoordinates(mapX, mapZ);
        topRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftHeight = terrain.heightAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightHeight = terrain.heightAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        topLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ);
        topRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ);
        bottomLeftMaterial = terrain.materialAtCoordinates(mapX, mapZ + triangleDepth);
        bottomRightMaterial = terrain.materialAtCoordinates(mapX + triangleWidth, mapZ + triangleDepth);

        // Core triangles
        triangle = buildTriangleGeometry(topLeftX,    topLeftHeight,    topLeftZ, topLeftMaterial,
                                         bottomLeftX, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                         topRightX,   topRightHeight,   topRightZ, topRightMaterial);
        if (topLeftMaterial === CityTour.Terrain.WATER && topRightMaterial === CityTour.Terrain.WATER && bottomLeftMaterial === CityTour.Terrain.WATER) {
          waterGeometry1.merge(triangle);
        }
        else {
          terrainGeometry1.merge(triangle);
        }

        triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                         bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                         topRightX,    topRightHeight, topRightZ, topRightMaterial);
        if (bottomLeftMaterial === CityTour.Terrain.WATER && topRightMaterial === CityTour.Terrain.WATER && bottomRightMaterial === CityTour.Terrain.WATER) {
          waterGeometry2.merge(triangle);
        }
        else {
          terrainGeometry2.merge(triangle);
        }

        // Extra left-side triangles
        if (!leftRoad) {
          if (topLeftRoad && !bottomLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, topLeftZ, topLeftMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             topLeftX, topLeftHeight, topLeftZ, topLeftMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (bottomLeftRoad && !topLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, topLeftZ, topLeftMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (topLeftRoad && bottomLeftRoad) {
            triangle = buildTriangleGeometry(bottomLeftX - CityTour.Config.STREET_WIDTH, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX, bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             topLeftX, topLeftHeight, topLeftZ, topLeftMaterial);
            terrainGeometry2.merge(triangle);
          }
        }

        // Extra right-side triangles
        if (!rightRoad) {
          if (topRightRoad && !bottomRightRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ, topRightMaterial,
                                             topRightX, topRightHeight, topRightZ, topRightMaterial);

            terrainGeometry1.merge(triangle);
          }

          if (bottomRightRoad && !topRightRoad) {
            triangle = buildTriangleGeometry(bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, topRightZ, topRightMaterial);
            terrainGeometry1.merge(triangle);
          }

          if (topRightRoad && bottomRightRoad) {
            triangle = buildTriangleGeometry(topRightX,    topRightHeight, topRightZ, topRightMaterial,
                                             bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial,
                                             topRightX + CityTour.Config.STREET_WIDTH, topRightHeight, topRightZ, topRightMaterial);
            terrainGeometry1.merge(triangle);
          }
        }

        // Extra top-side triangles
        if (!topRoad) {
          if (topLeftRoad && !topRightRoad) {
            triangle = buildTriangleGeometry(topLeftX,  topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topLeftMaterial,
                                             topLeftX,  topLeftHeight, topLeftZ, topLeftMaterial,
                                             CityTour.Coordinates.mapXToSceneX(mapX + triangleWidth), topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topRightMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (topRightRoad && !topLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), topLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topLeftMaterial,
                                             topRightX, topRightHeight, topRightZ, topRightMaterial,
                                             topRightX, topRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ), topRightMaterial);
            terrainGeometry2.merge(triangle);
          }

          if (topLeftRoad && topRightRoad) {
            triangle = buildTriangleGeometry(topLeftX, topLeftHeight, topLeftZ, topLeftMaterial,
                                             topRightX, topRightHeight, topRightZ, topRightMaterial,
                                             topRightX, topRightHeight, topRightZ - CityTour.Config.STREET_DEPTH, topRightMaterial);

            terrainGeometry2.merge(triangle);
          }
        }

        // Extra bottom-side triangles
        if (!bottomRoad) {
          if (bottomLeftRoad && !bottomRightRoad) {
            triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX,  bottomLeftHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth), bottomLeftMaterial,
                                             bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth), bottomRightMaterial);
            terrainGeometry1.merge(triangle);
          }

          if (bottomRightRoad && !bottomLeftRoad) {
            triangle = buildTriangleGeometry(CityTour.Coordinates.mapXToSceneX(mapX), bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomRightX, bottomRightHeight, CityTour.Coordinates.mapZToSceneZ(mapZ + triangleDepth), bottomRightMaterial,
                                             bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial);
            terrainGeometry1.merge(triangle);
          }

          if (bottomLeftRoad && bottomRightRoad) {
            triangle = buildTriangleGeometry(bottomLeftX,  bottomLeftHeight, bottomLeftZ, bottomLeftMaterial,
                                             bottomLeftX,  bottomLeftHeight, bottomLeftZ + CityTour.Config.STREET_DEPTH, bottomLeftMaterial,
                                             bottomRightX, bottomRightHeight, bottomRightZ, bottomRightMaterial);

            terrainGeometry1.merge(triangle);
          }
        }

      }
    }

    var mesh1 = new THREE.Mesh(terrainGeometry1, terrainMaterial1);
    var mesh2 = new THREE.Mesh(terrainGeometry2, terrainMaterial2);
    var waterMesh1 = new THREE.Mesh(waterGeometry1, waterMaterial1);
    var waterMesh2 = new THREE.Mesh(waterGeometry2, waterMaterial2);

    return [mesh1, mesh2, waterMesh1, waterMesh2];
  };

  return terrainGeometryBuilder;
};
<|endoftext|>