<commit_before>var expect = require('chai').expect;
var validate = require('../src/validate');

describe('screener-storybook/src/validate', function() {
  describe('validate.storybookConfig', function() {
    it('should throw error when no value passed in', function() {
      return validate.storybookConfig()
        .catch(function(err) {
          expect(err.message).to.equal('"value" is required');
        });
    });

    it('should throw error when no apiKey', function() {
      return validate.storybookConfig({})
        .catch(function(err) {
          expect(err.message).to.equal('child "apiKey" fails because ["apiKey" is required]');
        });
    });

    it('should throw error when no projectRepo', function() {
      return validate.storybookConfig({apiKey: 'key'})
        .catch(function(err) {
          expect(err.message).to.equal('child "projectRepo" fails because ["projectRepo" is required]');
        });
    });

    it('should throw error when no storybookConfigDir', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo'})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybookConfigDir" fails because ["storybookConfigDir" is required]');
        });
    });

    it('should throw error when no storybookPort', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook'})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybookPort" fails because ["storybookPort" is required]');
        });
    });

    it('should throw error when no storybook', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybook" fails because ["storybook" is required]');
        });
    });

    it('should allow storybook with no stories', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: []})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should throw error when storybook item is invalid', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [{kind: 'Temp'}]})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybook" fails because ["storybook" at position 0 fails because [child "stories" fails because ["stories" is required]]]');
        });
    });

    it('should allow adding optional fields', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookStaticDir: './public', storybookPort: 6006, storybook: [], build: 'build', branch: 'branch', commit: 'commit', resolution: '1280x1024', cssAnimations: true, ignore: 'ignore', includeRules: [], excludeRules: [], initialBaselineBranch: 'master', diffOptions: {}})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should allow include/exclude rules that are strings', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], includeRules: ['string'], excludeRules: ['string']})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should allow include/exclude rules that are regex expressions', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], includeRules: [/^string$/], excludeRules: [/^string$/]})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should throw error when include/exclude rules are not in array', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], includeRules: 'string', excludeRules: 'string'})
        .catch(function(err) {
          expect(err.message).to.equal('child "includeRules" fails because ["includeRules" must be an array]');
        });
    });

    it('should throw error when resolution in incorrect format', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], resolution: 'resolution'})
        .catch(function(err) {
          expect(err.message).to.equal('child "resolution" fails because ["resolution" with value "resolution" fails to match the resolution pattern, "resolution" must be an object, "resolution" must be an object]');
        });
    });

    it('should throw error when field is unknown', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], someKey: 'key'})
        .catch(function(err) {
          expect(err.message).to.equal('"someKey" is not allowed');
        });
    });

    describe('validate.failureExitCode', function() {
      it('should allow setting to 0', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], failureExitCode: 0})
          .catch(function() {
            throw new Error('Should not be here');
          });
      });

      it('should error when setting above 255', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], failureExitCode: 256})
          .catch(function(err) {
            expect(err.message).to.equal('child "failureExitCode" fails because ["failureExitCode" must be less than or equal to 255]');
          });
      });
    });

    describe('validate.storybookBinPath', function() {
      it('should error when storybookBinPath is set without storybookVersion', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookBinPath: '/path'})
          .catch(function(err) {
            expect(err.message).to.equal('"storybookBinPath" missing required peer "storybookVersion"');
          });
      });

      it('should allow setting storybookVersion to 2 or 3', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 2})
          .then(function() {
            return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 3});
          })
          .catch(function() {
            throw new Error('Should not be here');
          });
      });

      it('should error when setting storybookVersion to any value not 2 or 3', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 1})
          .catch(function(err) {
            expect(err.message).to.equal('child "storybookVersion" fails because ["storybookVersion" must be one of [2, 3]]');
            return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 4})
            .catch(function(err) {
              expect(err.message).to.equal('child "storybookVersion" fails because ["storybookVersion" must be one of [2, 3]]');
            });
          });
      });

      it('should allow setting storybookApp to react or vue', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookApp: 'react'})
          .then(function() {
            return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookApp: 'vue'});
          })
          .catch(function() {
            throw new Error('Should not be here');
          });
      });

      it('should error when setting storybookApp to any value not react or vue', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookApp: 'angular'})
          .catch(function(err) {
            expect(err.message).to.equal('child "storybookApp" fails because ["storybookApp" must be one of [react, vue]]');
          });
      });

      it('should allow when both storybookBinPath and storybookVersion', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 3, storybookBinPath: '/path'})
          .catch(function() {
            throw new Error('Should not be here');
          });
      });
    });

    describe('validate.browsers', function() {
      it('should error when browsers is empty', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], browsers: []})
          .catch(function(err) {
            expect(err.message).to.equal('child "browsers" fails because ["browsers" must contain at least 1 items]');
          });
      });

      it('should error when browsers is set without sauce', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], browsers: [{ browserName: 'chrome' }]})
          .catch(function(err) {
            expect(err.message).to.equal('"browsers" missing required peer "sauce"');
          });
      });

      it('should allow browsers with sauce config', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], browsers: [{ browserName: 'firefox', version: '53.0' }], sauce: { username: 'user', accessKey: 'key' }})
          .catch(function() {
            throw new Error('Should not be here');
          });
      });
    });

  });
});
<commit_msg>update unit tests<commit_after>var expect = require('chai').expect;
var validate = require('../src/validate');

describe('screener-storybook/src/validate', function() {
  describe('validate.storybookConfig', function() {
    it('should throw error when no value passed in', function() {
      return validate.storybookConfig()
        .catch(function(err) {
          expect(err.message).to.equal('"value" is required');
        });
    });

    it('should throw error when no apiKey', function() {
      return validate.storybookConfig({})
        .catch(function(err) {
          expect(err.message).to.equal('child "apiKey" fails because ["apiKey" is required]');
        });
    });

    it('should throw error when no projectRepo', function() {
      return validate.storybookConfig({apiKey: 'key'})
        .catch(function(err) {
          expect(err.message).to.equal('child "projectRepo" fails because ["projectRepo" is required]');
        });
    });

    it('should throw error when no storybookConfigDir', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo'})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybookConfigDir" fails because ["storybookConfigDir" is required]');
        });
    });

    it('should throw error when no storybookPort', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook'})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybookPort" fails because ["storybookPort" is required]');
        });
    });

    it('should throw error when no storybook', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybook" fails because ["storybook" is required]');
        });
    });

    it('should allow storybook with no stories', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: []})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should throw error when storybook item is invalid', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [{kind: 'Temp'}]})
        .catch(function(err) {
          expect(err.message).to.equal('child "storybook" fails because ["storybook" at position 0 fails because [child "stories" fails because ["stories" is required]]]');
        });
    });

    it('should allow adding optional fields', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookStaticDir: './public', storybookPort: 6006, storybook: [], build: 'build', branch: 'branch', commit: 'commit', resolution: '1280x1024', cssAnimations: true, ignore: 'ignore', includeRules: [], excludeRules: [], initialBaselineBranch: 'master', baseBranch: 'master', diffOptions: {}})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should allow include/exclude rules that are strings', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], includeRules: ['string'], excludeRules: ['string']})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should allow include/exclude rules that are regex expressions', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], includeRules: [/^string$/], excludeRules: [/^string$/]})
        .catch(function() {
          throw new Error('Should not be here');
        });
    });

    it('should throw error when include/exclude rules are not in array', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], includeRules: 'string', excludeRules: 'string'})
        .catch(function(err) {
          expect(err.message).to.equal('child "includeRules" fails because ["includeRules" must be an array]');
        });
    });

    it('should throw error when resolution in incorrect format', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], resolution: 'resolution'})
        .catch(function(err) {
          expect(err.message).to.equal('child "resolution" fails because ["resolution" with value "resolution" fails to match the resolution pattern, "resolution" must be an object, "resolution" must be an object]');
        });
    });

    it('should throw error when field is unknown', function() {
      return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], someKey: 'key'})
        .catch(function(err) {
          expect(err.message).to.equal('"someKey" is not allowed');
        });
    });

    describe('validate.failureExitCode', function() {
      it('should allow setting to 0', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], failureExitCode: 0})
          .catch(function() {
            throw new Error('Should not be here');
          });
      });

      it('should error when setting above 255', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], failureExitCode: 256})
          .catch(function(err) {
            expect(err.message).to.equal('child "failureExitCode" fails because ["failureExitCode" must be less than or equal to 255]');
          });
      });
    });

    describe('validate.storybookBinPath', function() {
      it('should error when storybookBinPath is set without storybookVersion', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookBinPath: '/path'})
          .catch(function(err) {
            expect(err.message).to.equal('"storybookBinPath" missing required peer "storybookVersion"');
          });
      });

      it('should allow setting storybookVersion to 2 or 3', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 2})
          .then(function() {
            return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 3});
          })
          .catch(function() {
            throw new Error('Should not be here');
          });
      });

      it('should error when setting storybookVersion to any value not 2 or 3', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 1})
          .catch(function(err) {
            expect(err.message).to.equal('child "storybookVersion" fails because ["storybookVersion" must be one of [2, 3]]');
            return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 4})
            .catch(function(err) {
              expect(err.message).to.equal('child "storybookVersion" fails because ["storybookVersion" must be one of [2, 3]]');
            });
          });
      });

      it('should allow setting storybookApp to react or vue', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookApp: 'react'})
          .then(function() {
            return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookApp: 'vue'});
          })
          .catch(function() {
            throw new Error('Should not be here');
          });
      });

      it('should error when setting storybookApp to any value not react or vue', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookApp: 'angular'})
          .catch(function(err) {
            expect(err.message).to.equal('child "storybookApp" fails because ["storybookApp" must be one of [react, vue]]');
          });
      });

      it('should allow when both storybookBinPath and storybookVersion', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], storybookVersion: 3, storybookBinPath: '/path'})
          .catch(function() {
            throw new Error('Should not be here');
          });
      });
    });

    describe('validate.browsers', function() {
      it('should error when browsers is empty', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], browsers: []})
          .catch(function(err) {
            expect(err.message).to.equal('child "browsers" fails because ["browsers" must contain at least 1 items]');
          });
      });

      it('should error when browsers is set without sauce', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], browsers: [{ browserName: 'chrome' }]})
          .catch(function(err) {
            expect(err.message).to.equal('"browsers" missing required peer "sauce"');
          });
      });

      it('should allow browsers with sauce config', function() {
        return validate.storybookConfig({apiKey: 'key', projectRepo: 'repo', storybookConfigDir: '.storybook', storybookPort: 6006, storybook: [], browsers: [{ browserName: 'firefox', version: '53.0' }], sauce: { username: 'user', accessKey: 'key' }})
          .catch(function() {
            throw new Error('Should not be here');
          });
      });
    });

  });
});
<|endoftext|>


<commit_before>var testHelpers = require('can-test-helpers');
var stache = require('../can-stache');
var DefineMap = require("can-define/map/map");
var DefineList = require("can-define/list/list");
var Scope = require("can-view-scope");

QUnit.module("can-stache: warnings");

testHelpers.dev.devOnlyTest("lineNumber should be set on the scope inside of a rendered string (#415)", function() {
	var scope = new Scope({ foo: "classVal" });
	var template = stache("
Hello
");
	template(scope);

	QUnit.equal(scope.get("scope.lineNumber"), 1);
});

testHelpers.dev.devOnlyTest("should not warn for keys that exist but are `undefined` (#427)", function () {
	var VM = DefineMap.extend({
		propWithoutValue: 'string'
	});
	var vm = new VM();
	var teardown = testHelpers.dev.willWarn('can-stache/expressions/lookup.js: Unable to find key "propWithoutValue".');

	stache('
{{propWithoutValue}}
')(vm);

	QUnit.equal(teardown(), 0, 'did not get warning');
});

testHelpers.dev.devOnlyTest("should not warn for dotted keys that exist", function () {
	var ENV = DefineMap.extend({
		NODE_ENV: "any"
	});

	var VM = DefineMap.extend({
		env: "any"
	});

	// First without anything
	var vm = new VM();
	var teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{env.NODE_ENV}}')(vm);

	QUnit.equal(teardown(), 0, "did not warn");

	// Then with an env but no prop
	vm = new VM({env: new DefineMap()});
	teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{env.NODE_ENV}}')(vm);

	QUnit.equal(teardown(), 1, "did warn");

	// Then with everything
	vm = new VM({env: new ENV()});
	teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{env.NODE_ENV}}')(vm);

	QUnit.equal(teardown(), 0, "did not warn");
});

testHelpers.dev.devOnlyTest("should not warn for scope.<key> keys that exist", function () {
	var VM = DefineMap.extend({
		name: "string",
		list: {
			default: function() {
				return [ "one", "two" ];
			}
		}
	});

	var vm = new VM();
	var scope = new Scope(vm, null, { viewModel: true });
	var teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{#each list}}{{scope.vm.name}}{{/each}}')(scope);

	QUnit.equal(teardown(), 0, "did not warn");
});

testHelpers.dev.devOnlyTest("should not warn for ../<key> keys that exist (#519)", function () {
	var VM = DefineMap.extend({
		name: "string",
		list: {
			default: function() {
				return [ "one", "two" ];
			}
		}
	});

	var vm = new VM();
	var scope = new Scope(vm, null, { viewModel: true });
	var teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{#each list}}{{../name}}{{/each}}')(scope);

	QUnit.equal(teardown(), 0, "did not warn");
});

testHelpers.dev.devOnlyTest("Warnings for nested properties should not suggest using the same key (#536)", function () {
	var Abc = DefineMap.extend({});
	var VM = DefineMap.extend({
		abc: {
			Default: Abc
		}
	});

	var vm = new VM();
	var scope = new Scope(vm, null, { viewModel: true });
	var warningTeardown = testHelpers.dev.willWarn(/Unable to find key/);
	var suggestionTeardown = testHelpers.dev.willWarn(/will read from/);
	stache('{{abc.def}}')(scope);

	QUnit.equal(warningTeardown(), 1, "gave warning");
	QUnit.equal(suggestionTeardown(), 0, "did not give suggestions");
});

testHelpers.dev.devOnlyTest("Variable not found warning should suggest correct keys", function () {
	var origGetPaths = Scope.prototype.getPathsForKey;
	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		paths["foo/" + key] = {};
		paths["../bar/" + key] = {};
		return paths;
	};

	var considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name".* Did you mean one of these\?/);
	var fooOptionTeardown = testHelpers.dev.willWarn(/"foo\/name" which will read from/);
	var barOptionTeardown = testHelpers.dev.willWarn(/"..\/bar\/name" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name}}{{/with}}
')({
		name: 'app',
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(fooOptionTeardown(), 1, 'got foo/name option');
	QUnit.equal(barOptionTeardown(), 1, 'got ../bar/name option');

	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		paths["../baz/" + key] = {};
		return paths;
	};

	considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name".* Did you mean\?/);
	var bazOptionTeardown = testHelpers.dev.willWarn(/"..\/baz\/name" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name}}{{/with}}
')({
		name: 'app',
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(bazOptionTeardown(), 1, 'got baz/name option');

	Scope.prototype.getPathsForKey = origGetPaths;
});

testHelpers.dev.devOnlyTest("Variable not found warning should suggest correct keys for nested properties (#519)", function () {
	var origGetPaths = Scope.prototype.getPathsForKey;
	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		paths["foo/name.first"] = {};
		paths["../bar/name.first"] = {};
		return paths;
	};

	var considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name.first".* Did you mean one of these\?/);
	var fooOptionTeardown = testHelpers.dev.willWarn(/"foo\/name.first" which will read from/);
	var barOptionTeardown = testHelpers.dev.willWarn(/"..\/bar\/name.first" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name.first}}{{/with}}
')({
		name: {},
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(fooOptionTeardown(), 1, 'got foo/name option');
	QUnit.equal(barOptionTeardown(), 1, 'got ../bar/name option');

	Scope.prototype.getPathsForKey = origGetPaths;

});

testHelpers.dev.devOnlyTest("Variable not found warning should suggest correct keys for functions (#529)", function () {
	var origGetPaths = Scope.prototype.getPathsForKey;
	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		if (key === "name") {
			paths["foo/" + key + "()"] = {};
			paths["../bar/" + key + "()"] = {};
		}
		return paths;
	};

	var considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name\(\)".* Did you mean one of these\?/);
	var fooOptionTeardown = testHelpers.dev.willWarn(/"foo\/name\(\)" which will read from/);
	var barOptionTeardown = testHelpers.dev.willWarn(/"..\/bar\/name\(\)" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name()}}{{/with}}
')({
		name: function() { return 'app'; },
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(fooOptionTeardown(), 1, 'got foo/name option');
	QUnit.equal(barOptionTeardown(), 1, 'got ../bar/name option');

	Scope.prototype.getPathsForKey = origGetPaths;

});

testHelpers.dev.devOnlyTest("Should warn when the closing tag of a partial does not match the opener", function () {
	stache.registerPartial('partial', "

");

	var warningTeardown = testHelpers.dev.willWarn(/bar.stache:1: unexpected closing tag {{\/partiall}} expected {{\/partial}}/);

	stache('bar.stache', '
{{<partial}}{{/partiall}}
')({
		name: 'app',
		user: {}
	});

	QUnit.equal(warningTeardown(), 1, 'got expected warning');
});

testHelpers.dev.devOnlyTest("Should give a warning when a partial is not found #493", function () {
	var template,
		items = new DefineList(['one', 'two']),
		teardown = testHelpers.dev.willWarn('Unable to find partial "itemRenderer"');

	template = stache("{{#each(items)}} {{> itemRenderer}} {{/each}}");
	template({
		items: items,
		itemRenderer: stache("{{this}}")
	});
	QUnit.equal(teardown(), 2, "got expected warning");
});
<commit_msg>simplifie the test<commit_after>var testHelpers = require('can-test-helpers');
var stache = require('../can-stache');
var DefineMap = require("can-define/map/map");
var DefineList = require("can-define/list/list");
var Scope = require("can-view-scope");

QUnit.module("can-stache: warnings");

testHelpers.dev.devOnlyTest("lineNumber should be set on the scope inside of a rendered string (#415)", function() {
	var scope = new Scope({ foo: "classVal" });
	var template = stache("
Hello
");
	template(scope);

	QUnit.equal(scope.get("scope.lineNumber"), 1);
});

testHelpers.dev.devOnlyTest("should not warn for keys that exist but are `undefined` (#427)", function () {
	var VM = DefineMap.extend({
		propWithoutValue: 'string'
	});
	var vm = new VM();
	var teardown = testHelpers.dev.willWarn('can-stache/expressions/lookup.js: Unable to find key "propWithoutValue".');

	stache('
{{propWithoutValue}}
')(vm);

	QUnit.equal(teardown(), 0, 'did not get warning');
});

testHelpers.dev.devOnlyTest("should not warn for dotted keys that exist", function () {
	var ENV = DefineMap.extend({
		NODE_ENV: "any"
	});

	var VM = DefineMap.extend({
		env: "any"
	});

	// First without anything
	var vm = new VM();
	var teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{env.NODE_ENV}}')(vm);

	QUnit.equal(teardown(), 0, "did not warn");

	// Then with an env but no prop
	vm = new VM({env: new DefineMap()});
	teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{env.NODE_ENV}}')(vm);

	QUnit.equal(teardown(), 1, "did warn");

	// Then with everything
	vm = new VM({env: new ENV()});
	teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{env.NODE_ENV}}')(vm);

	QUnit.equal(teardown(), 0, "did not warn");
});

testHelpers.dev.devOnlyTest("should not warn for scope.<key> keys that exist", function () {
	var VM = DefineMap.extend({
		name: "string",
		list: {
			default: function() {
				return [ "one", "two" ];
			}
		}
	});

	var vm = new VM();
	var scope = new Scope(vm, null, { viewModel: true });
	var teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{#each list}}{{scope.vm.name}}{{/each}}')(scope);

	QUnit.equal(teardown(), 0, "did not warn");
});

testHelpers.dev.devOnlyTest("should not warn for ../<key> keys that exist (#519)", function () {
	var VM = DefineMap.extend({
		name: "string",
		list: {
			default: function() {
				return [ "one", "two" ];
			}
		}
	});

	var vm = new VM();
	var scope = new Scope(vm, null, { viewModel: true });
	var teardown = testHelpers.dev.willWarn(/Unable to find key/);
	stache('{{#each list}}{{../name}}{{/each}}')(scope);

	QUnit.equal(teardown(), 0, "did not warn");
});

testHelpers.dev.devOnlyTest("Warnings for nested properties should not suggest using the same key (#536)", function () {
	var Abc = DefineMap.extend({});
	var VM = DefineMap.extend({
		abc: {
			Default: Abc
		}
	});

	var vm = new VM();
	var scope = new Scope(vm, null, { viewModel: true });
	var warningTeardown = testHelpers.dev.willWarn(/Unable to find key/);
	var suggestionTeardown = testHelpers.dev.willWarn(/will read from/);
	stache('{{abc.def}}')(scope);

	QUnit.equal(warningTeardown(), 1, "gave warning");
	QUnit.equal(suggestionTeardown(), 0, "did not give suggestions");
});

testHelpers.dev.devOnlyTest("Variable not found warning should suggest correct keys", function () {
	var origGetPaths = Scope.prototype.getPathsForKey;
	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		paths["foo/" + key] = {};
		paths["../bar/" + key] = {};
		return paths;
	};

	var considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name".* Did you mean one of these\?/);
	var fooOptionTeardown = testHelpers.dev.willWarn(/"foo\/name" which will read from/);
	var barOptionTeardown = testHelpers.dev.willWarn(/"..\/bar\/name" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name}}{{/with}}
')({
		name: 'app',
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(fooOptionTeardown(), 1, 'got foo/name option');
	QUnit.equal(barOptionTeardown(), 1, 'got ../bar/name option');

	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		paths["../baz/" + key] = {};
		return paths;
	};

	considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name".* Did you mean\?/);
	var bazOptionTeardown = testHelpers.dev.willWarn(/"..\/baz\/name" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name}}{{/with}}
')({
		name: 'app',
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(bazOptionTeardown(), 1, 'got baz/name option');

	Scope.prototype.getPathsForKey = origGetPaths;
});

testHelpers.dev.devOnlyTest("Variable not found warning should suggest correct keys for nested properties (#519)", function () {
	var origGetPaths = Scope.prototype.getPathsForKey;
	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		paths["foo/name.first"] = {};
		paths["../bar/name.first"] = {};
		return paths;
	};

	var considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name.first".* Did you mean one of these\?/);
	var fooOptionTeardown = testHelpers.dev.willWarn(/"foo\/name.first" which will read from/);
	var barOptionTeardown = testHelpers.dev.willWarn(/"..\/bar\/name.first" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name.first}}{{/with}}
')({
		name: {},
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(fooOptionTeardown(), 1, 'got foo/name option');
	QUnit.equal(barOptionTeardown(), 1, 'got ../bar/name option');

	Scope.prototype.getPathsForKey = origGetPaths;

});

testHelpers.dev.devOnlyTest("Variable not found warning should suggest correct keys for functions (#529)", function () {
	var origGetPaths = Scope.prototype.getPathsForKey;
	Scope.prototype.getPathsForKey = function(key) {
		var paths = {};
		if (key === "name") {
			paths["foo/" + key + "()"] = {};
			paths["../bar/" + key + "()"] = {};
		}
		return paths;
	};

	var considerTeardown = testHelpers.dev.willWarn(/bar.stache:1: Unable to find key "name\(\)".* Did you mean one of these\?/);
	var fooOptionTeardown = testHelpers.dev.willWarn(/"foo\/name\(\)" which will read from/);
	var barOptionTeardown = testHelpers.dev.willWarn(/"..\/bar\/name\(\)" which will read from/);

	stache('bar.stache', '
{{#with user}}{{name()}}{{/with}}
')({
		name: function() { return 'app'; },
		user: {}
	});

	QUnit.equal(considerTeardown(), 1, 'got expected warning');
	QUnit.equal(fooOptionTeardown(), 1, 'got foo/name option');
	QUnit.equal(barOptionTeardown(), 1, 'got ../bar/name option');

	Scope.prototype.getPathsForKey = origGetPaths;

});

testHelpers.dev.devOnlyTest("Should warn when the closing tag of a partial does not match the opener", function () {
	stache.registerPartial('partial', "

");

	var warningTeardown = testHelpers.dev.willWarn(/bar.stache:1: unexpected closing tag {{\/partiall}} expected {{\/partial}}/);

	stache('bar.stache', '
{{<partial}}{{/partiall}}
')({
		name: 'app',
		user: {}
	});

	QUnit.equal(warningTeardown(), 1, 'got expected warning');
});

testHelpers.dev.devOnlyTest("Should give a warning when a partial is not found #493", function () {
	var template,
		teardown = testHelpers.dev.willWarn('Unable to find partial "aPartial"');
	template = stache("{{> aPartial}}");
	template();
	QUnit.equal(teardown(), 1, "got expected warning");
});
<|endoftext|>


<commit_before>/*globals describe, it */
'use strict';

var assert = require("assert");

describe('BuiltIns and globals', function() {
    describe('eval', function() {
        it('evaluates 2+3', function() {
            assert.equal(eval('2+3'), 5);
        });

        it('evaluates variable', function() {
            var foo = 'bar';
            var bar = 'baz';

            assert.equal(eval(foo), 'baz');

            var echo = 'alpha';

            var obj = {
                alpha: 'bravo'
            }

            var bravo = 'charlie';

            assert.equal(eval(obj[echo]), 'charlie');
            assert.equal(eval('obj[echo]'), 'bravo');
        });
    });
    describe('parseInt', function() {
        it('converts number to integer', function() {
            assert.equal(parseInt(4.9, 10), 4);
            assert.equal(parseInt(4.1, 10), 4);
            assert.equal(parseInt('4.1', 10), 4);
        });
    });

    describe('parseFloat', function() {
        it('converts number to float', function() {
            assert.equal(parseFloat('4.9'), 4.9);

        });
    });
});<commit_msg>Add: isNaN test<commit_after>/*globals describe, it */
'use strict';

var assert = require("assert");

describe('BuiltIns and globals', function() {
    describe('eval', function() {
        it('evaluates 2+3', function() {
            assert.equal(eval('2+3'), 5);
        });

        it('evaluates variable', function() {
            var foo = 'bar';
            var bar = 'baz';

            assert.equal(eval(foo), 'baz');

            var echo = 'alpha';

            var obj = {
                alpha: 'bravo'
            }

            var bravo = 'charlie';

            assert.equal(eval(obj[echo]), 'charlie');
            assert.equal(eval('obj[echo]'), 'bravo');
        });
    });
    describe('parseInt', function() {
        it('converts number to integer', function() {
            assert.equal(parseInt(4.9, 10), 4);
            assert.equal(parseInt(4.1, 10), 4);
            assert.equal(parseInt('4.1', 10), 4);
        });
    });

    describe('parseFloat', function() {
        it('converts number to float', function() {
            assert.equal(parseFloat('4.9'), 4.9);
            assert.equal(parseFloat('4.49'), 4.49);
            assert.equal(parseFloat('1'), 1);
        });
    });

    describe('isNaN', function() {
        it('should not be a number', function() {
            assert.equal(isNaN('foo'), true);

        });
    });
});<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var StringCalculator = function() {
  StringCalculator.prototype.add = function(s) {
    if (s.length === 0) {
      return 0;
    }

    var n = s.split(',');
    var sum = 0;
    for (var i=0;i<n.length;++i) {
      sum += n[i]|0;
    }
    return sum;
  };
  return this;
};

describe('string calculator', function() {
  it('exists', function() {
    assert(new StringCalculator());
  });

  it('has an add method', function() {
    assert(new StringCalculator().add);
  });

  describe('add method', function() {
    var stringCalculator = new StringCalculator();

    it('0 args', function() {
      assert.equal(0, stringCalculator.add(''));
    });

    it('1 arg', function() {
      assert.equal(55, stringCalculator.add('55'));
    });

    it('2 args', function() {
      assert.equal(74, stringCalculator.add('69,5'));
    });
  });
});<commit_msg>expected pass<commit_after>"use strict";

var assert = require('assert');

var StringCalculator = function() {
  StringCalculator.prototype.add = function(s) {
    if (s.length === 0) {
      return 0;
    }

    var n = s.split(',');
    var sum = 0;
    for (var i=0;i<n.length;++i) {
      sum += n[i]|0;
    }
    return sum;
  };
  return this;
};

describe('string calculator', function() {
  it('exists', function() {
    assert(new StringCalculator());
  });

  it('has an add method', function() {
    assert(new StringCalculator().add);
  });

  describe('add method', function() {
    var stringCalculator = new StringCalculator();

    it('0 args', function() {
      assert.equal(0, stringCalculator.add(''));
    });

    it('1 arg', function() {
      assert.equal(55, stringCalculator.add('55'));
    });

    it('2 args', function() {
      assert.equal(74, stringCalculator.add('69,5'));
    });

    it('n args', function() {
      assert.equal(1+2+3+4+5, stringCalculator.add('1,2,3,4,5'));
    });
  });
});<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var sumArrayOfStringsAsInts = function(n) {
    var sum = 0;
    for (var i=0;i<n.length;++i) {
      sum += n[i]|0;
    }
    return sum;
};

var tokenize = function(s) {
  s = s.replace(/\n/g, ',');
  return s.split(',');
};

var StringCalculator = function() {
  StringCalculator.prototype.add = function(s) {
    if (s.length === 0) {
      return 0;
    }

    var tokens = tokenize(s);
    return sumArrayOfStringsAsInts(tokens);
  };
  return this;
};

var hasCustomDelimiter = function(s) {
  return s.indexOf('//') === 0;
};

var getDelimiter = function(s) {
  if (hasCustomDelimiter(s)) {
    return { delimiter: s.substr(2, s.indexOf('\n') - 2) };
  }

  return { delimiter: ',' };
};

describe('string calculator', function() {
  it('exists', function() {
    assert(new StringCalculator());
  });

  it('has an add method', function() {
    assert(new StringCalculator().add);
  });

  describe('delimiters', function() {
    it('knows about custom delimters', function() {
      assert(hasCustomDelimiter('//q\n1q2q3'));
    });

    it('ignores normal delimters', function() {
      assert(!hasCustomDelimiter('1,2,3'));
    });

    it('returns delimiter', function() {
      var s = getDelimiter('//q\n1q2q3');
      assert('q', s.delimiter);
    });

    it('returns default delimiters', function() {
      var s = getDelimiter('1,2,3');
      assert(',', s.delimiter);
    });

    it('consumes some of the string', function() {
      assert.equal('1,2,3', getDelimiter('1,2,3').line);
      assert.equal('1q2q3', getDelimiter('//q\n1q2q3').line);
    });
  });

  describe('add method', function() {
    var stringCalculator = new StringCalculator();

    it('0 args', function() {
      assert.equal(0, stringCalculator.add(''));
    });

    it('1 arg', function() {
      assert.equal(55, stringCalculator.add('55'));
    });

    it('2 args', function() {
      assert.equal(74, stringCalculator.add('69,5'));
    });

    it('n args', function() {
      assert.equal(1+2+3+4+5, stringCalculator.add('1,2,3,4,5'));
    });

    it('handles new lines as separators', function() {
      assert.equal(1+2+3, stringCalculator.add('1\n2\n3'));
    });
  });
});<commit_msg>make it pass<commit_after>"use strict";

var assert = require('assert');

var sumArrayOfStringsAsInts = function(n) {
    var sum = 0;
    for (var i=0;i<n.length;++i) {
      sum += n[i]|0;
    }
    return sum;
};

var tokenize = function(s) {
  s = s.replace(/\n/g, ',');
  return s.split(',');
};

var StringCalculator = function() {
  StringCalculator.prototype.add = function(s) {
    if (s.length === 0) {
      return 0;
    }

    var tokens = tokenize(s);
    return sumArrayOfStringsAsInts(tokens);
  };
  return this;
};

var hasCustomDelimiter = function(s) {
  return s.indexOf('//') === 0;
};

var getDelimiter = function(s) {
  if (hasCustomDelimiter(s)) {
    return {
      delimiter: s.substr(2, s.indexOf('\n') - 2),
      line: s.substr(s.indexOf('\n') + 1)
    };
  }

  return { delimiter: ',', line: s };
};

describe('string calculator', function() {
  it('exists', function() {
    assert(new StringCalculator());
  });

  it('has an add method', function() {
    assert(new StringCalculator().add);
  });

  describe('delimiters', function() {
    it('knows about custom delimters', function() {
      assert(hasCustomDelimiter('//q\n1q2q3'));
    });

    it('ignores normal delimters', function() {
      assert(!hasCustomDelimiter('1,2,3'));
    });

    it('returns delimiter', function() {
      var s = getDelimiter('//q\n1q2q3');
      assert('q', s.delimiter);
    });

    it('returns default delimiters', function() {
      var s = getDelimiter('1,2,3');
      assert(',', s.delimiter);
    });

    it('consumes some of the string', function() {
      assert.equal('1,2,3', getDelimiter('1,2,3').line);
      assert.equal('1q2q3', getDelimiter('//q\n1q2q3').line);
    });
  });

  describe('add method', function() {
    var stringCalculator = new StringCalculator();

    it('0 args', function() {
      assert.equal(0, stringCalculator.add(''));
    });

    it('1 arg', function() {
      assert.equal(55, stringCalculator.add('55'));
    });

    it('2 args', function() {
      assert.equal(74, stringCalculator.add('69,5'));
    });

    it('n args', function() {
      assert.equal(1+2+3+4+5, stringCalculator.add('1,2,3,4,5'));
    });

    it('handles new lines as separators', function() {
      assert.equal(1+2+3, stringCalculator.add('1\n2\n3'));
    });
  });
});<|endoftext|>


<commit_before>var Spellcheck = (function() {

  // Unicode character blocks for letters.
  // See: http://jrgraphix.net/research/unicode_blocks.php
  //
  // \\u0041-\\u005A    A-Z (Basic Latin)
  // \\u0061-\\u007A    a-z (Basic Latin)
  // \\u0030-\\u0039    0-9 (Basic Latin)
  // \\u00AA            ª   (Latin-1 Supplement)
  // \\u00B5            µ   (Latin-1 Supplement)
  // \\u00BA            º   (Latin-1 Supplement)
  // \\u00C0-\\u00D6    À-Ö (Latin-1 Supplement)
  // \\u00D8-\\u00F6    Ø-ö (Latin-1 Supplement)
  // \\u00F8-\\u00FF    ø-ÿ (Latin-1 Supplement)
  // \\u0100-\\u017F    Ā-ſ (Latin Extended-A)
  // \\u0180-\\u024F    ƀ-ɏ (Latin Extended-B)
  var letterChars = '\\u0041-\\u005A\\u0061-\\u007A\\u0030-\\u0039\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\u0100-\\u017F\\u0180-\\u024F';

  var escapeRegEx = function(s) {
    return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
  };

  /**
   * Spellcheck class.
   *
   * @class Spellcheck
   * @constructor
   */
  var Spellcheck = function(editable) {
    this.editable = editable;
    this.setup();
  };

  Spellcheck.prototype.setup = function(editable) {
    this.editable.on('focus', $.proxy(this, 'onFocus'));
    this.editable.on('change', $.proxy(this, 'onChange'));
  }

  Spellcheck.prototype.onFocus = function(editableHost) {
    this.checkSpelling(editableHost);
  };

  Spellcheck.prototype.onChange = function(editableHost) {
    // todo: get the editable instance here (transform spellchecker into a class)
    var selection = this.editable.getSelection();
    var that = this;
    if (selection.isCursor) {
      selection.retainVisibleSelection(function() {
        that.checkSpelling(editableHost);
      })
    }

    // todo: keep cursor position intact
  };

  Spellcheck.prototype.createWrapperNode = function() {
    var marker = document.createElement('span');
    marker.className = 'spellcheck';
    marker.setAttribute('data-editable', 'remove');
    return marker;
  };

  Spellcheck.prototype.removeHighlights = function(editableHost) {
    $(editableHost).find('.spellcheck').each(function(index, elem) {
      content.unwrap(elem);
    });
  };

  Spellcheck.prototype.createRegex = function(words) {
    var escapedWords = $.map(words, function(word) {
      return escapeRegEx(word);
    });

    var regex = '';
    regex += '([^' + letterChars + ']|^)';
    regex += '(' + escapedWords.join('|') + ')';
    regex += '(?=[^' + letterChars + ']|$)';

    return new RegExp(regex, 'g');
  };

  Spellcheck.prototype.highlight = function(editableHost, regex) {

    // Remove old highlights
    this.removeHighlights(editableHost);

    // Create new highlights
    var span = this.createWrapperNode();
    findAndReplaceDOMText(editableHost, {
      find: regex,
      wrap: span
    });
  };

  Spellcheck.prototype.checkSpelling = function(editableHost) {
    var regex = this.createRegex(['test', 'xxx', 'Lorem', 'dolor', 'ante', 'nunc.']);
    this.highlight(editableHost, regex);
  }

  return Spellcheck;
})();

<commit_msg>Use highlightText in spellcheck.js<commit_after>var Spellcheck = (function() {

  // Unicode character blocks for letters.
  // See: http://jrgraphix.net/research/unicode_blocks.php
  //
  // \\u0041-\\u005A    A-Z (Basic Latin)
  // \\u0061-\\u007A    a-z (Basic Latin)
  // \\u0030-\\u0039    0-9 (Basic Latin)
  // \\u00AA            ª   (Latin-1 Supplement)
  // \\u00B5            µ   (Latin-1 Supplement)
  // \\u00BA            º   (Latin-1 Supplement)
  // \\u00C0-\\u00D6    À-Ö (Latin-1 Supplement)
  // \\u00D8-\\u00F6    Ø-ö (Latin-1 Supplement)
  // \\u00F8-\\u00FF    ø-ÿ (Latin-1 Supplement)
  // \\u0100-\\u017F    Ā-ſ (Latin Extended-A)
  // \\u0180-\\u024F    ƀ-ɏ (Latin Extended-B)
  var letterChars = '\\u0041-\\u005A\\u0061-\\u007A\\u0030-\\u0039\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u00FF\\u0100-\\u017F\\u0180-\\u024F';

  var escapeRegEx = function(s) {
    return String(s).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
  };

  /**
   * Spellcheck class.
   *
   * @class Spellcheck
   * @constructor
   */
  var Spellcheck = function(editable) {
    this.editable = editable;
    this.setup();
  };

  Spellcheck.prototype.setup = function(editable) {
    this.editable.on('focus', $.proxy(this, 'onFocus'));
    this.editable.on('change', $.proxy(this, 'onChange'));
  }

  Spellcheck.prototype.onFocus = function(editableHost) {
    this.checkSpelling(editableHost);
  };

  Spellcheck.prototype.onChange = function(editableHost) {
    // todo: get the editable instance here (transform spellchecker into a class)
    var selection = this.editable.getSelection();
    var that = this;
    if (selection.isCursor) {
      selection.retainVisibleSelection(function() {
        that.checkSpelling(editableHost);
      })
    }
  };

  Spellcheck.prototype.createWrapperNode = function() {
    var marker = document.createElement('span');
    marker.className = 'spellcheck';
    marker.setAttribute('data-editable', 'remove');
    return marker;
  };

  Spellcheck.prototype.removeHighlights = function(editableHost) {
    $(editableHost).find('.spellcheck').each(function(index, elem) {
      content.unwrap(elem);
    });
  };

  Spellcheck.prototype.createRegex = function(words) {
    var escapedWords = $.map(words, function(word) {
      return escapeRegEx(word);
    });

    var regex = '';
    regex += '([^' + letterChars + ']|^)';
    regex += '(' + escapedWords.join('|') + ')';
    regex += '(?=[^' + letterChars + ']|$)';

    return new RegExp(regex, 'g');
  };

  Spellcheck.prototype.highlight = function(editableHost, regex) {

    // Remove old highlights
    this.removeHighlights(editableHost);

    // Create new highlights
    var span = this.createWrapperNode();
    highlightText.highlight(editableHost, regex, span);
  };

  Spellcheck.prototype.checkSpelling = function(editableHost) {
    var regex = this.createRegex(['test', 'xxx', 'Lorem', 'dolor', 'ante', 'nunc.']);
    this.highlight(editableHost, regex);
  }

  return Spellcheck;
})();

<|endoftext|>


<commit_before>/*
*	Static Map
*	A JS class for rendering static Google Maps and loading dynamic maps on the fly
*
*	Requires jQuery library (http://www.jquery.com),
*   jQuery.Class plug-in (http://github.com/taylanpince/jquery-class)
*
*	Taylan Pince (taylanpince at gmail dot com) - October 24, 2008
*/

$.namespace("core.StaticMap");

core.StaticMap = $.Class.extend({

    url_template : "http://maps.google.com/staticmap?key=%(api_key)&center=%(center)&markers=%(markers)&size=%(size)&format=png8&maptype=%(maptype)",
    api_template : "http://www.google.com/jsapi?key=%(api_key)",
    marker_template : "%(media_path)static_map/images/markers/%(number).png",
    image_template : '%(launch_copy)',
    loader_template : '

',
    info_template : '
%(title)
%(description)',

    launch_copy : "Launch Interactive Map",

    selector : "",
    size : [300, 300],
    center : [0, 0],
    zoom : 14,
    maptype : "roadmap",
    markers : [],
    media_path : "",

    render_template : function(template, values) {
        for (val in values) {
            var re = new RegExp("%\\(" + val + "\\)", "g");

            template = template.replace(re, values[val]);
        }

        return template;
    },

    clean_variable : function(val, delimiter) {
        if (val.charAt(val.length - 1) == delimiter) {
            val = val.substring(0, (val.length - 1));
        }

        return val;
    },

    build_url : function() {
        var center = this.center[0] + "," + this.center[1];

        var markers = "";

        if (this.markers.length > 0) {
            for (var m in this.markers) {
                markers += this.markers[m].coordinates;

                if (this.markers.length > 1 || center != this.markers[m].coordinates) {
                    markers += ",red" + (parseInt(m) + 1) + "|";
                }
            }
        }

        var url = this.render_template(this.url_template, {
            "api_key" : this.api_key,
            "size" : this.size[0] + "x" + this.size[1],
            "center" : center,
            "markers" : this.clean_variable(markers, "|"),
            "maptype" : this.maptype
        });

        url += "&zoom=" + this.zoom;

        return url;
    },

    draw_static_map : function() {
        $(this.selector).html(this.render_template(this.image_template, {
            "map_url" : this.build_url(),
            "launch_copy" : this.launch_copy
        })).find("a").click(this.launch_interactive_map.bind(this));
    },

    draw_interactive_map : function() {
        var gmap = new google.maps.Map2($(this.selector).get(0));
        var gcenter = new google.maps.LatLng(this.center[0], this.center[1]);
        var gbounds = new google.maps.LatLngBounds();

        gbounds.extend(gcenter);
        gmap.setCenter(gcenter);
        gmap.addControl(new google.maps.SmallMapControl());

        for (var m = 0; m < this.markers.length; m++) {
            var gpoint = new google.maps.LatLng(this.markers[m].coordinates[0], this.markers[m].coordinates[1]);
            var gmarker = new google.maps.Marker(gpoint, {
                "icon" : new google.maps.Icon(google.maps.DEFAULT_ICON, this.render_template(this.marker_template, {
                    "media_path": this.media_path,
                    "number": ((this.markers.length == 1 && gcenter.equals(gpoint)) ? "dot" : (parseInt(m) + 1))
                }))
            });

            if (typeof this.markers[m].title || typeof this.markers[m].description) {
                gmarker.bindInfoWindowHtml(this.render_template(this.info_template, {
                    "title" : this.markers[m].title,
                    "description" : this.markers[m].description
                }), {
                    "maxWidth" : (parseInt(this.size[0]) - 100)
                });
            }

            gmap.addOverlay(gmarker);
            gbounds.extend(gpoint);
        }

        gmap.setZoom(gmap.getBoundsZoomLevel(gbounds) - 1);

        if (this.maptype == "terrain") {
            gmap.addMapType(G_PHYSICAL_MAP);
            gmap.setMapType(G_PHYSICAL_MAP);
        }

    },

    load_interactive_map : function() {
        if (typeof google == "object") {
            google.load("maps", "2", {
                "callback" : this.draw_interactive_map.bind(this)
            });
        } else {
            this.draw_static_map();

            alert("There was an error while loading the interactive map. We are sorry for the inconvenience.");
        }
    },

    launch_interactive_map : function() {
        this.show_loader();

        if (typeof google == "object") {
            this.load_interactive_map();
        } else {
            $.getScript(this.render_template(this.api_template, {
                "api_key": this.api_key
            }), this.load_interactive_map.bind(this));
        }

        return false;
    },

    show_loader : function() {
        $(this.selector).append(this.render_template(this.loader_template, {
            "media_path": this.media_path
        }));
    },

    hide_loader : function() {
        $(this.selector).find(".map-loader").remove();
    },

    init : function(selector, options) {
        this.selector = selector;

        $(this.selector).addClass("static-map");

        if (options) {
            for (opt in options) {
                this[opt] = options[opt];
            }
        }

        this.draw_static_map();
    }

});
<commit_msg>fix problem with iterating over markers<commit_after>/*
*	Static Map
*	A JS class for rendering static Google Maps and loading dynamic maps on the fly
*
*	Requires jQuery library (http://www.jquery.com),
*   jQuery.Class plug-in (http://github.com/taylanpince/jquery-class)
*
*	Taylan Pince (taylanpince at gmail dot com) - October 24, 2008
*/

$.namespace("core.StaticMap");

core.StaticMap = $.Class.extend({

    url_template : "http://maps.google.com/staticmap?key=%(api_key)&center=%(center)&markers=%(markers)&size=%(size)&format=png8&maptype=%(maptype)",
    api_template : "http://www.google.com/jsapi?key=%(api_key)",
    marker_template : "%(media_path)static_map/images/markers/%(number).png",
    image_template : '%(launch_copy)',
    loader_template : '

',
    info_template : '
%(title)
%(description)',

    launch_copy : "Launch Interactive Map",

    selector : "",
    size : [300, 300],
    center : [0, 0],
    zoom : 14,
    maptype : "roadmap",
    markers : [],
    media_path : "",

    render_template : function(template, values) {
        for (val in values) {
            var re = new RegExp("%\\(" + val + "\\)", "g");

            template = template.replace(re, values[val]);
        }

        return template;
    },

    clean_variable : function(val, delimiter) {
        if (val.charAt(val.length - 1) == delimiter) {
            val = val.substring(0, (val.length - 1));
        }

        return val;
    },

    build_url : function() {
        var center = this.center[0] + "," + this.center[1];

        var markers = "";

        for (var m=0; m < this.markers.length; m++) {
            markers += this.markers[m].coordinates;

            if (this.markers.length > 1 || center != this.markers[m].coordinates) {
                markers += ",red" + (parseInt(m) + 1) + "|";
            }
        }

        var url = this.render_template(this.url_template, {
            "api_key" : this.api_key,
            "size" : this.size[0] + "x" + this.size[1],
            "center" : center,
            "markers" : this.clean_variable(markers, "|"),
            "maptype" : this.maptype
        });

        url += "&zoom=" + this.zoom;

        return url;
    },

    draw_static_map : function() {
        $(this.selector).html(this.render_template(this.image_template, {
            "map_url" : this.build_url(),
            "launch_copy" : this.launch_copy
        })).find("a").click(this.launch_interactive_map.bind(this));
    },

    draw_interactive_map : function() {
        var gmap = new google.maps.Map2($(this.selector).get(0));
        var gcenter = new google.maps.LatLng(this.center[0], this.center[1]);
        var gbounds = new google.maps.LatLngBounds();

        gbounds.extend(gcenter);
        gmap.setCenter(gcenter);
        gmap.addControl(new google.maps.SmallMapControl());

        for (var m = 0; m < this.markers.length; m++) {
            var gpoint = new google.maps.LatLng(this.markers[m].coordinates[0], this.markers[m].coordinates[1]);
            var gmarker = new google.maps.Marker(gpoint, {
                "icon" : new google.maps.Icon(google.maps.DEFAULT_ICON, this.render_template(this.marker_template, {
                    "media_path": this.media_path,
                    "number": ((this.markers.length == 1 && gcenter.equals(gpoint)) ? "dot" : (parseInt(m) + 1))
                }))
            });

            if (typeof this.markers[m].title || typeof this.markers[m].description) {
                gmarker.bindInfoWindowHtml(this.render_template(this.info_template, {
                    "title" : this.markers[m].title,
                    "description" : this.markers[m].description
                }), {
                    "maxWidth" : (parseInt(this.size[0]) - 100)
                });
            }

            gmap.addOverlay(gmarker);
            gbounds.extend(gpoint);
        }

        gmap.setZoom(gmap.getBoundsZoomLevel(gbounds) - 1);

        if (this.maptype == "terrain") {
            gmap.addMapType(G_PHYSICAL_MAP);
            gmap.setMapType(G_PHYSICAL_MAP);
        }

    },

    load_interactive_map : function() {
        if (typeof google == "object") {
            google.load("maps", "2", {
                "callback" : this.draw_interactive_map.bind(this)
            });
        } else {
            this.draw_static_map();

            alert("There was an error while loading the interactive map. We are sorry for the inconvenience.");
        }
    },

    launch_interactive_map : function() {
        this.show_loader();

        if (typeof google == "object") {
            this.load_interactive_map();
        } else {
            $.getScript(this.render_template(this.api_template, {
                "api_key": this.api_key
            }), this.load_interactive_map.bind(this));
        }

        return false;
    },

    show_loader : function() {
        $(this.selector).append(this.render_template(this.loader_template, {
            "media_path": this.media_path
        }));
    },

    hide_loader : function() {
        $(this.selector).find(".map-loader").remove();
    },

    init : function(selector, options) {
        this.selector = selector;

        $(this.selector).addClass("static-map");

        if (options) {
            for (opt in options) {
                this[opt] = options[opt];
            }
        }

        this.draw_static_map();
    }

});
<|endoftext|>


<commit_before>/**
 * @file MaterialLocalAutoComplete component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import LocalAutoComplete from '../LocalAutoComplete';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialLocalAutoComplete extends Component {

    static Mode = LocalAutoComplete.Mode;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            filter: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.triggerBlurHandler = ::this.triggerBlurHandler;
        this.triggerFilterChangeHandler = ::this.triggerFilterChangeHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        this.setState({
            isFocus: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    triggerFilterChangeHandler(filter) {
        this.setState({
            filter
        }, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimated, popupClassName,
                ...restProps
            } = this.props,
            {isFocus, isHover, value, filter} = this.state,

            wrapperClassName = (isLabelAnimated ? ' animated' : '') + (label ? ' has-label' : '')
                + (filter ? ' has-value' : '') + (isFocus ? ' focused' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <LocalAutoComplete {...restProps}
                                   popupClassName={'material-local-auto-complete-popup ' + popupClassName}
                                   theme={theme}
                                   value={value}
                                   onFocus={this.triggerFocusHandler}
                                   onBlur={this.triggerBlurHandler}
                                   onTriggerMouseOver={this.triggerMouseOverHandler}
                                   onTriggerMouseOut={this.triggerMouseOutHandler}
                                   onFilterChange={this.triggerFilterChangeHandler}
                                   onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialLocalAutoComplete.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The name of the auto complete.
     */
    name: PropTypes.string,

    /**
     * The placeholder of the field.
     */
    placeholder: PropTypes.string,

    label: PropTypes.string,

    /**
     * Children passed into the List.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            itemRenderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * If true, the auto complete will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the popup list automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * The type of dropDown list,can be normal,checkbox,radio.
     */
    mode: PropTypes.oneOf(Util.enumerateValue(LocalAutoComplete.Mode)),

    /**
     * Callback function fired when filter changed.
     */
    filterCallback: PropTypes.func,

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * The visiblity of no matched popup.
     */
    noMatchedPopupVisible: PropTypes.bool,

    /**
     * The message of no matched value.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * If true,the list data will be grouped.
     */
    isGrouped: PropTypes.bool,

    isLabelAnimated: PropTypes.bool,

    popupChildren: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The function that trigger when filter changes.
     */
    onFilterChange: PropTypes.func,

    /**
     * The function that trigger when press enter.
     */
    onFilterPressEnter: PropTypes.func,

    /**
     * The function that trigger when filter cleared.
     */
    onFilterClear: PropTypes.func,

    /**
     * The function that trigger when value changed.
     */
    onChange: PropTypes.func,

    /**
     * The function that trigger when touch-tap the list items.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete get focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete lose focus.
     */
    onBlur: PropTypes.func,

    onTriggerMouseOver: PropTypes.func,
    onTriggerMouseOut: PropTypes.func

};

MaterialLocalAutoComplete.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,
    theme: Theme.DEFAULT,

    name: '',
    placeholder: '',
    label: '',
    isLabelAnimated: true,
    data: [],
    disabled: false,
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    autoClose: false,
    iconCls: '',
    rightIconCls: 'fa fa-search',
    noMatchedPopupVisible: true,
    noMatchedMsg: '',
    isGrouped: false,

    popupChildren: null

};<commit_msg>update MaterialLocalAutoComplete<commit_after>/**
 * @file MaterialLocalAutoComplete component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import LocalAutoComplete from '../LocalAutoComplete';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialLocalAutoComplete extends Component {

    static Mode = LocalAutoComplete.Mode;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            filter: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.triggerBlurHandler = ::this.triggerBlurHandler;
        this.triggerFilterChangeHandler = ::this.triggerFilterChangeHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;
        this.closePopup = ::this.closePopup;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    triggerBlurHandler(...args) {
        this.setState({
            isFocus: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(...args);
        });
    }

    triggerFilterChangeHandler(filter) {
        this.setState({
            filter
        }, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    closePopup() {
        this.refs.localAutoComplete && this.refs.localAutoComplete.closePopup();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimated, popupClassName,
                ...restProps
            } = this.props,
            {isFocus, isHover, value, filter} = this.state,

            wrapperClassName = (isLabelAnimated ? ' animated' : '') + (label ? ' has-label' : '')
                + (filter ? ' has-value' : '') + (isFocus ? ' focused' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <LocalAutoComplete {...restProps}
                                   ref="localAutoComplete"
                                   popupClassName={'material-local-auto-complete-popup ' + popupClassName}
                                   theme={theme}
                                   value={value}
                                   onFocus={this.triggerFocusHandler}
                                   onBlur={this.triggerBlurHandler}
                                   onTriggerMouseOver={this.triggerMouseOverHandler}
                                   onTriggerMouseOut={this.triggerMouseOutHandler}
                                   onFilterChange={this.triggerFilterChangeHandler}
                                   onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialLocalAutoComplete.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The name of the auto complete.
     */
    name: PropTypes.string,

    /**
     * The placeholder of the field.
     */
    placeholder: PropTypes.string,

    label: PropTypes.string,

    /**
     * Children passed into the List.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            itemRenderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * If true, the auto complete will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the popup list automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * The type of dropDown list,can be normal,checkbox,radio.
     */
    mode: PropTypes.oneOf(Util.enumerateValue(LocalAutoComplete.Mode)),

    /**
     * Callback function fired when filter changed.
     */
    filterCallback: PropTypes.func,

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * The visiblity of no matched popup.
     */
    noMatchedPopupVisible: PropTypes.bool,

    /**
     * The message of no matched value.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * If true,the list data will be grouped.
     */
    isGrouped: PropTypes.bool,

    isLabelAnimated: PropTypes.bool,

    popupChildren: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The function that trigger when filter changes.
     */
    onFilterChange: PropTypes.func,

    /**
     * The function that trigger when press enter.
     */
    onFilterPressEnter: PropTypes.func,

    /**
     * The function that trigger when filter cleared.
     */
    onFilterClear: PropTypes.func,

    /**
     * The function that trigger when value changed.
     */
    onChange: PropTypes.func,

    /**
     * The function that trigger when touch-tap the list items.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete get focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete lose focus.
     */
    onBlur: PropTypes.func,

    onTriggerMouseOver: PropTypes.func,
    onTriggerMouseOut: PropTypes.func

};

MaterialLocalAutoComplete.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,
    theme: Theme.DEFAULT,

    name: '',
    placeholder: '',
    label: '',
    isLabelAnimated: true,
    data: [],
    disabled: false,
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    autoClose: false,
    iconCls: '',
    rightIconCls: 'fa fa-search',
    noMatchedPopupVisible: true,
    noMatchedMsg: '',
    isGrouped: false,

    popupChildren: null

};<|endoftext|>


<commit_before>const sass = require('node-sass');

module.exports = grunt => {

	require('load-grunt-tasks')(grunt);

	let port = grunt.option('port') || 8000;
	let root = grunt.option('root') || '.';

	if (!Array.isArray(root)) root = [root];

	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		meta: {
			banner:
				'/*!\n' +
				' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
				' * http://revealjs.com\n' +
				' * MIT licensed\n' +
				' *\n' +
				' * Copyright (C) 2019 Hakim El Hattab, http://hakim.se\n' +
				' */'
		},

		qunit: {
			files: [ 'test/*.html' ]
		},

		uglify: {
			options: {
				banner: '<%= meta.banner %>\n',
				ie8: true
			},
			build: {
				src: 'js/reveal.js',
				dest: 'js/reveal.min.js'
			}
		},

		sass: {
			options: {
				implementation: sass,
				sourceMap: false
			},
			core: {
				src: 'css/reveal.scss',
				dest: 'css/reveal.css'
			},
			themes: {
				expand: true,
				cwd: 'css/theme/source',
				src: ['*.sass', '*.scss'],
				dest: 'css/theme',
				ext: '.css'
			}
		},

		autoprefixer: {
			core: {
				src: 'css/reveal.css'
			}
		},

		cssmin: {
			options: {
				compatibility: 'ie9'
			},
			compress: {
				src: 'css/reveal.css',
				dest: 'css/reveal.min.css'
			}
		},

		jshint: {
			options: {
				curly: false,
				eqeqeq: true,
				immed: true,
				esnext: true,
				latedef: 'nofunc',
				newcap: true,
				noarg: true,
				sub: true,
				undef: true,
				eqnull: true,
				browser: true,
				expr: true,
				loopfunc: true,
				globals: {
					head: false,
					module: false,
					console: false,
					unescape: false,
					define: false,
					exports: false,
					require: false
				}
			},
			files: [ 'gruntfile.js', 'js/reveal.js' ]
		},

		connect: {
			server: {
				options: {
					port: port,
					base: root,
					livereload: true,
					open: true,
					useAvailablePort: true
				}
			}
		},

		zip: {
			bundle: {
				src: [
					'index.html',
					'css/**',
					'js/**',
					'lib/**',
					'images/**',
					'plugin/**',
					'**.md'
				],
				dest: 'reveal-js-presentation.zip'
			}
		},

		watch: {
			js: {
				files: [ 'gruntfile.js', 'js/reveal.js' ],
				tasks: 'js'
			},
			theme: {
				files: [
					'css/theme/source/*.sass',
					'css/theme/source/*.scss',
					'css/theme/template/*.sass',
					'css/theme/template/*.scss'
				],
				tasks: 'css-themes'
			},
			css: {
				files: [ 'css/reveal.scss' ],
				tasks: 'css-core'
			},
			test: {
				files: [ 'test/*.html' ],
				tasks: 'test'
			},
			html: {
				files: root.map(path => path + '/*.html')
			},
			markdown: {
				files: root.map(path => path + '/*.md')
			},
			options: {
				livereload: true
			}
		}

	});

	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-nodeunit');

	// Default task
	grunt.registerTask( 'default', [ 'css', 'js' ] );

	// JS task
	grunt.registerTask( 'js', [ 'jshint', 'uglify', 'qunit' ] );

	// Theme CSS
	grunt.registerTask( 'css-themes', [ 'sass:themes' ] );

	// Core framework CSS
	grunt.registerTask( 'css-core', [ 'sass:core', 'autoprefixer', 'cssmin' ] );

	// All CSS
	grunt.registerTask( 'css', [ 'sass', 'autoprefixer', 'cssmin' ] );

	// Package presentation to archive
	grunt.registerTask( 'package', [ 'default', 'zip' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );

	// Run tests
	grunt.registerTask( 'test', [ 'jshint', 'qunit' ] );

};
<commit_msg>Remove calls to missing modules from gruntfile<commit_after>const sass = require('node-sass');

module.exports = grunt => {

	require('load-grunt-tasks')(grunt);

	let port = grunt.option('port') || 8000;
	let root = grunt.option('root') || '.';

	if (!Array.isArray(root)) root = [root];

	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		meta: {
			banner:
				'/*!\n' +
				' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
				' * http://revealjs.com\n' +
				' * MIT licensed\n' +
				' *\n' +
				' * Copyright (C) 2019 Hakim El Hattab, http://hakim.se\n' +
				' */'
		},

		qunit: {
			files: [ 'test/*.html' ]
		},

		uglify: {
			options: {
				banner: '<%= meta.banner %>\n',
				ie8: true
			},
			build: {
				src: 'js/reveal.js',
				dest: 'js/reveal.min.js'
			}
		},

		sass: {
			options: {
				implementation: sass,
				sourceMap: false
			},
			core: {
				src: 'css/reveal.scss',
				dest: 'css/reveal.css'
			},
			themes: {
				expand: true,
				cwd: 'css/theme/source',
				src: ['*.sass', '*.scss'],
				dest: 'css/theme',
				ext: '.css'
			}
		},

		autoprefixer: {
			core: {
				src: 'css/reveal.css'
			}
		},

		cssmin: {
			options: {
				compatibility: 'ie9'
			},
			compress: {
				src: 'css/reveal.css',
				dest: 'css/reveal.min.css'
			}
		},

		jshint: {
			options: {
				curly: false,
				eqeqeq: true,
				immed: true,
				esnext: true,
				latedef: 'nofunc',
				newcap: true,
				noarg: true,
				sub: true,
				undef: true,
				eqnull: true,
				browser: true,
				expr: true,
				loopfunc: true,
				globals: {
					head: false,
					module: false,
					console: false,
					unescape: false,
					define: false,
					exports: false,
					require: false
				}
			},
			files: [ 'gruntfile.js', 'js/reveal.js' ]
		},

		connect: {
			server: {
				options: {
					port: port,
					base: root,
					livereload: true,
					open: true,
					useAvailablePort: true
				}
			}
		},

		zip: {
			bundle: {
				src: [
					'index.html',
					'css/**',
					'js/**',
					'lib/**',
					'images/**',
					'plugin/**',
					'**.md'
				],
				dest: 'reveal-js-presentation.zip'
			}
		},

		watch: {
			js: {
				files: [ 'gruntfile.js', 'js/reveal.js' ],
				tasks: 'js'
			},
			theme: {
				files: [
					'css/theme/source/*.sass',
					'css/theme/source/*.scss',
					'css/theme/template/*.sass',
					'css/theme/template/*.scss'
				],
				tasks: 'css-themes'
			},
			css: {
				files: [ 'css/reveal.scss' ],
				tasks: 'css-core'
			},
			test: {
				files: [ 'test/*.html' ],
				tasks: 'test'
			},
			html: {
				files: root.map(path => path + '/*.html')
			},
			markdown: {
				files: root.map(path => path + '/*.md')
			},
			options: {
				livereload: true
			}
		}

	});

	// Default task
	grunt.registerTask( 'default', [ 'css', 'js' ] );

	// JS task
	grunt.registerTask( 'js', [ 'jshint', 'uglify', 'qunit' ] );

	// Theme CSS
	grunt.registerTask( 'css-themes', [ 'sass:themes' ] );

	// Core framework CSS
	grunt.registerTask( 'css-core', [ 'sass:core', 'autoprefixer', 'cssmin' ] );

	// All CSS
	grunt.registerTask( 'css', [ 'sass', 'autoprefixer', 'cssmin' ] );

	// Package presentation to archive
	grunt.registerTask( 'package', [ 'default', 'zip' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );

	// Run tests
	grunt.registerTask( 'test', [ 'jshint', 'qunit' ] );

};
<|endoftext|>


<commit_before>'use strict';

module.exports.generic = require('./generic');

module.exports.outcome = module.exports.generic('outcome');
module.exports.outcome.success = module.exports.outcome('success');
module.exports.outcome.failure = module.exports.outcome('failure');
module.exports.outcome.timeout = module.exports.outcome('timeout');

module.exports.protocol = module.exports.generic('protocol');
module.exports.service = module.exports.generic('service');

module.exports.api = module.exports.generic('api');
module.exports.apicall = module.exports.generic('apicall');

module.exports.testcase = module.exports.generic('testcase');
module.exports.testcaseCompletion = module.exports.generic('testcaseCompletion');

module.exports.section = module.exports.generic('section');
module.exports.sectionCompletion = module.exports.generic('sectionCompletion');

module.exports.raw = require('./raw');
<commit_msg>Add a couple more tags.<commit_after>'use strict';

module.exports.generic = require('./generic');

module.exports.everything = module.exports.generic('everything','everything');

module.exports.outcome = module.exports.generic('outcome');
module.exports.outcome.success = module.exports.outcome('success');
module.exports.outcome.failure = module.exports.outcome('failure');
module.exports.outcome.timeout = module.exports.outcome('timeout');

module.exports.protocol = module.exports.generic('protocol');

module.exports.service = module.exports.generic('service');
module.exports.serviceCall = module.exports.generic('service-call');

module.exports.api = module.exports.generic('api');
module.exports.apiCall = module.exports.generic('api-call');

module.exports.testcase = module.exports.generic('testcase');
module.exports.testcaseCompletion = module.exports.generic('testcaseCompletion');

module.exports.section = module.exports.generic('section');
module.exports.sectionCompletion = module.exports.generic('sectionCompletion');

module.exports.raw = require('./raw');
<|endoftext|>