<commit_before>module.exports = function testAdapter(options) {
  describe('micro-analytics adapter ' + options.name, () => {
    const adapter = require(options.modulePath);

    if (typeof options.beforeEach === 'function') {
      beforeEach(async () => { await options.beforeEach(adapter); });
    }

    if (typeof options.afterEach === 'function') {
      afterEach(async () => { await options.afterEach(adapter); });
    }

    if (typeof options.beforeAll === 'function') {
      beforeAll(async () => { await options.beforeAll(adapter); });
    }

    if (typeof options.afterAll === 'function') {
      afterAll(async () => {
        await options.afterAll(adapter);
        if (typeof adapter.close === 'function') {
          await adapter.close();
        }
      });
    }

    test('get() should return a promise', () => {
      expect(adapter.get('/a-key').constructor.name).toEqual("Promise")
    })

    test('getAll() should return a promise', () => {
      expect(adapter.getAll({ pathname: '/' }).constructor.name).toEqual("Promise")
    })

    test('has() should return a promise', () => {
      expect(adapter.has('/a-key').constructor.name).toEqual("Promise")
    })

    test('keys() should return a promise', () => {
      expect(adapter.keys().constructor.name).toEqual("Promise")
    })

    test('put() should return a promise', () => {
      expect(adapter.put('/a-key', {}).constructor.name).toEqual("Promise")
    })

    it('should save and read', async () => {
      await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });

      expect(await adapter.get('/a-key')).toEqual({
        views: [{ time: 1490623474639 }],
      });
    });

    it('should return all saves on getAll', async () => {
      await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });
      await adapter.put('/another-key', { views: [{ time: 1490623474639 }] });

      expect(await adapter.getAll({ pathname: '/' })).toEqual({
        '/a-key': { views: [{ time: 1490623474639 }] },
        '/another-key': { views: [{ time: 1490623474639 }] },
      });
    });

    it('should have check whether a key is stored with has', async () => {
      await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });

      expect(await adapter.has('/a-key')).toEqual(true);
      expect(await adapter.has('/non-existing-key')).toEqual(false);
    });

    if (typeof adapter.subscribe === "function") {

      it('should allow subscription with observables', async () => {
        const listener = jest.fn();
        const unsubscribe = adapter.subscribe(listener);

        await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });

        expect(listener).toHaveBeenCalledWith({
          key: '/a-key',
          value: { views: [{ time: 1490623474639 }] },
        });
      });

      it('should allow multiple subscription with observables and handle unsubscribption', async () => {
        const listener1 = jest.fn();
        const listener2 = jest.fn();
        const subscription = adapter.subscribe(listener1);
        adapter.subscribe(listener2);

        await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });
        subscription.unsubscribe();
        await adapter.put('/b-key', { views: [{ time: 1490623474639 }] });

        expect(listener1).toHaveBeenCalledWith({
          key: '/a-key',
          value: { views: [{ time: 1490623474639 }] },
        });
        expect(listener1).not.toHaveBeenCalledWith({
          key: '/b-key',
          value: { views: [{ time: 1490623474639 }] },
        });
        expect(listener2).toHaveBeenCalledWith({
          key: '/a-key',
          value: { views: [{ time: 1490623474639 }] },
        });
        expect(listener2).toHaveBeenCalledWith({
          key: '/b-key',
          value: { views: [{ time: 1490623474639 }] },
        });
      });
    }
  });
};
<commit_msg>Add adapter-test for non-existing key<commit_after>module.exports = function testAdapter(options) {
  describe('micro-analytics adapter ' + options.name, () => {
    const adapter = require(options.modulePath);

    if (typeof options.beforeEach === 'function') {
      beforeEach(async () => { await options.beforeEach(adapter); });
    }

    if (typeof options.afterEach === 'function') {
      afterEach(async () => { await options.afterEach(adapter); });
    }

    if (typeof options.beforeAll === 'function') {
      beforeAll(async () => { await options.beforeAll(adapter); });
    }

    if (typeof options.afterAll === 'function') {
      afterAll(async () => {
        await options.afterAll(adapter);
        if (typeof adapter.close === 'function') {
          await adapter.close();
        }
      });
    }

    test('get() should return a promise', () => {
      expect(adapter.get('/a-key').constructor.name).toEqual("Promise")
    })

    test('getAll() should return a promise', () => {
      expect(adapter.getAll({ pathname: '/' }).constructor.name).toEqual("Promise")
    })

    test('has() should return a promise', () => {
      expect(adapter.has('/a-key').constructor.name).toEqual("Promise")
    })

    test('keys() should return a promise', () => {
      expect(adapter.keys().constructor.name).toEqual("Promise")
    })

    test('put() should return a promise', () => {
      expect(adapter.put('/a-key', {}).constructor.name).toEqual("Promise")
    })

    it('should save and read', async () => {
      await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });

      expect(await adapter.get('/a-key')).toEqual({
        views: [{ time: 1490623474639 }],
      });
    });

    it('should return empty list of views when key has no views', async () => {
      expect(await adapter.get('/c-key')).toEqual({ views: [] });
    });

    it('should return all saves on getAll', async () => {
      await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });
      await adapter.put('/another-key', { views: [{ time: 1490623474639 }] });

      expect(await adapter.getAll({ pathname: '/' })).toEqual({
        '/a-key': { views: [{ time: 1490623474639 }] },
        '/another-key': { views: [{ time: 1490623474639 }] },
      });
    });

    it('should have check whether a key is stored with has', async () => {
      await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });

      expect(await adapter.has('/a-key')).toEqual(true);
      expect(await adapter.has('/non-existing-key')).toEqual(false);
    });

    if (typeof adapter.subscribe === "function") {

      it('should allow subscription with observables', async () => {
        const listener = jest.fn();
        const unsubscribe = adapter.subscribe(listener);

        await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });

        expect(listener).toHaveBeenCalledWith({
          key: '/a-key',
          value: { views: [{ time: 1490623474639 }] },
        });
      });

      it('should allow multiple subscription with observables and handle unsubscribption', async () => {
        const listener1 = jest.fn();
        const listener2 = jest.fn();
        const subscription = adapter.subscribe(listener1);
        adapter.subscribe(listener2);

        await adapter.put('/a-key', { views: [{ time: 1490623474639 }] });
        subscription.unsubscribe();
        await adapter.put('/b-key', { views: [{ time: 1490623474639 }] });

        expect(listener1).toHaveBeenCalledWith({
          key: '/a-key',
          value: { views: [{ time: 1490623474639 }] },
        });
        expect(listener1).not.toHaveBeenCalledWith({
          key: '/b-key',
          value: { views: [{ time: 1490623474639 }] },
        });
        expect(listener2).toHaveBeenCalledWith({
          key: '/a-key',
          value: { views: [{ time: 1490623474639 }] },
        });
        expect(listener2).toHaveBeenCalledWith({
          key: '/b-key',
          value: { views: [{ time: 1490623474639 }] },
        });
      });
    }
  });
};
<|endoftext|>


<commit_before>/*
 * genlangreg.js - ilib tool to generate the langname and regionname json fragments from the CLDR
 * data files
 *
 * Copyright © 2013-2018, 2020 JEDLSoft
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * This code is intended to be run under node.js
 */
var fs = require('fs');
var common = require("./common");
var cldr = require("cldr-data");
var path = require("path");
var Locale = common.Locale;
var mkdirs = common.makeDirs;

function usage() {
    console.log("Usage: genlangreg [-h] [ toDir ]\n" +
        "Generate the langname.jf and regionname.jf files for each locale.\n" +
        "-h or --help\n" +
        "  this help\n" +
        "toDir\n" +
    "  directory where generated files will be located\n");
    process.exit(1);
}

var toDir;

process.argv.forEach(function (val, index, array) {
    if (val === "-h" || val === "--help") {
        usage();
    }
});

toDir = process.argv[2] || "tmp";

console.log("genlangreg - generate language and region name data.\n" +
    "Copyright (c) 2013-2018, 2020 JEDLSoft");

console.log("locale dir: " + toDir);

if (!fs.existsSync(toDir)) {
    common.makeDirs(toDir);
}

var languagesData, regionData;
var languages, region, script;
var language_name = {
    generated: true
};
var region_name = {
    generated: true
};

var filename = "cldr-data/main/en/languages.json";

try {
    languagesData = require(filename);
    languages = languagesData.main.en.localeDisplayNames.languages;
} catch (e) {
    console.log("Error: Could not load file " + filename);
    process.exit(2);
}

console.log("Generating language name data");

for (var lang in languages) {
    if (lang.search(/[_-]/) === -1) {
        var langdir = path.join(toDir,lang);
        console.log(langdir + ": " + languages[lang]);
        mkdirs(langdir);
        language_name["language.name"] = languages[lang];
        language_name.generated = true;
        fs.writeFileSync(path.join(langdir, "langname.jf"), JSON.stringify(language_name, true, 4), "utf-8");
    }
}

try {
    regionsData = require("cldr-data/main/en/territories.json");
} catch (e) {
    console.log("Error: Could not load file cldr-data/main/en/territories.json");
    process.exit(2);
}
regions = regionsData.main.en.localeDisplayNames.territories;

for (region in regions) {
    if (region.search(/[_\-0123456789]/) === -1) {
        var regdir = path.join(toDir, "und", region);
        console.log(regdir + ": " + regions[region]);
        mkdirs(regdir);
        region_name["region.name"] = regions[region];
        region_name.generated = true;
        fs.writeFileSync(path.join(regdir, "regionname.jf"), JSON.stringify(region_name, true, 4), "utf-8");
    }
}
var xxRegion = {"region.name": "Unknown"};
var xxPath = path.join(toDir, "und", "XX");

if (!fs.existsSync(xxPath)) {
    mkdirs(xxPath);
}
fs.writeFileSync(path.join(xxPath, "regionname.jf"), JSON.stringify(xxRegion, true, 4), "utf-8");<commit_msg>Rollback variable name toDir --> localeDirName<commit_after>/*
 * genlangreg.js - ilib tool to generate the langname and regionname json fragments from the CLDR
 * data files
 *
 * Copyright © 2013-2018, 2020 JEDLSoft
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * This code is intended to be run under node.js
 */
var fs = require('fs');
var common = require("./common");
var cldr = require("cldr-data");
var path = require("path");
var Locale = common.Locale;
var mkdirs = common.makeDirs;

function usage() {
    console.log("Usage: genlangreg [-h] [ locale_data_dir ]\n" +
        "Generate the langname.jf and regionname.jf files for each locale.\n" +
        "-h or --help\n" +
        "  this help\n" +
        "localeDirName\n" +
    "  the top level of the ilib locale data directory\n");
    process.exit(1);
}

var localeDirName;

process.argv.forEach(function (val, index, array) {
    if (val === "-h" || val === "--help") {
        usage();
    }
});

localeDirName = process.argv[2] || "tmp";

console.log("genlangreg - generate language and region name data.\n" +
    "Copyright (c) 2013-2018, 2020 JEDLSoft");

console.log("locale dir: " + localeDirName);

if (!fs.existsSync(localeDirName)) {
    common.makeDirs(localeDirName);
}

var languagesData, regionData;
var languages, region, script;
var language_name = {
    generated: true
};
var region_name = {
    generated: true
};

var filename = "cldr-data/main/en/languages.json";

try {
    languagesData = require(filename);
    languages = languagesData.main.en.localeDisplayNames.languages;
} catch (e) {
    console.log("Error: Could not load file " + filename);
    process.exit(2);
}

console.log("Generating language name data");

for (var lang in languages) {
    if (lang.search(/[_-]/) === -1) {
        var langdir = path.join(localeDirName,lang);
        console.log(langdir + ": " + languages[lang]);
        mkdirs(langdir);
        language_name["language.name"] = languages[lang];
        language_name.generated = true;
        fs.writeFileSync(path.join(langdir, "langname.jf"), JSON.stringify(language_name, true, 4), "utf-8");
    }
}

try {
    regionsData = require("cldr-data/main/en/territories.json");
} catch (e) {
    console.log("Error: Could not load file cldr-data/main/en/territories.json");
    process.exit(2);
}
regions = regionsData.main.en.localeDisplayNames.territories;

for (region in regions) {
    if (region.search(/[_\-0123456789]/) === -1) {
        var regdir = path.join(localeDirName, "und", region);
        console.log(regdir + ": " + regions[region]);
        mkdirs(regdir);
        region_name["region.name"] = regions[region];
        region_name.generated = true;
        fs.writeFileSync(path.join(regdir, "regionname.jf"), JSON.stringify(region_name, true, 4), "utf-8");
    }
}
var xxRegion = {"region.name": "Unknown"};
var xxPath = path.join(localeDirName, "und", "XX");

if (!fs.existsSync(xxPath)) {
    mkdirs(xxPath);
}
fs.writeFileSync(path.join(xxPath, "regionname.jf"), JSON.stringify(xxRegion, true, 4), "utf-8");<|endoftext|>


<commit_before>/**
 * @property buttonDropdownLayout
 * @type {Array}
 */
var buttonDropdownLayout = [
    '{{view.label}}',
    '',
    '
',
        '{{yield}}',
    '
'
];

/**
 * @property buttonDropdownTemplate
 * @type {Array}
 */
var buttonDropdownTemplate = [
    '{{#if view.items}}',
    '   {{#each item in view.items}}',
    '
{{view view.Item contextBinding="item"}}
',
    '   {{/each}}',
    '{{/if}}'
];

/**
 * @property Bootstrap.ButtonDropdown
 * @type {Ember.View}
 */
Bootstrap.ButtonDropdown = Ember.View.extend({

    /**
     * @property label
     * @type {String}
     */
    label: null,

    /**
     * @property items
     * @type {Array}
     */
    items: [],

    /**
     * @property classNames
     * @type {Array}
     */
    classNames: ['btn-group'],

    /**
     * @property layout
     * @type {Function}
     */
    layout: Ember.Handlebars.compile(buttonDropdownLayout),

    /**
     * @property template
     * @type {Function}
     */
    template: Ember.Handlebars.compile(buttoDropdownTemplate),

    /**
     * @method didInsertElement
     * @return {void}
     */
    didInsertElement: function() {

        var items   = Ember.get(this, 'items'),
            forEach = Ember.EnumerableUtils.forEach;

        forEach(items, function(item) {

            // Iterate over the items, and if the `disabled` property changes, notify the
            // view of an update.
            Ember.addObserver(item, 'disabled', function() {
                this.notifyPropertyChange('view.items');
            });

        }, this);

    },

    /**
     * @property Item
     * @type {Ember.View}
     */
    Item: Ember.View.extend({

        /**
         * @property tagName
         * @type {String}
         * @default "a"
         */
        tagName: 'a',

        /**
         * @property attributeBindings
         * @type {Array}
         */
        attributeBindings: ['href'],

        /**
         * @property template
         * @type {Function}
         */
        template: Ember.Handlebars.compile('{{label}}'),

        /**
         * @property href
         * @type {Object}
         * @return {String}
         */
        href: Ember.computed(function() {
            return '#';
        }),

        /**
         * @method click
         * Attempt to invoke the specified action name on the controller.
         * @return {void}
         */
        click: function() {

            var item        = Ember.get(this, 'context'),
                actionName  = Ember.get(item, 'actionName'),
                controller  = Ember.get(this, 'controller');

            if (Ember.get(item, 'disabled') === true) {
                // We won't invoke the action if it's disabled.
                return;
            }

            Ember.assert('View `Bootstrap.ButtonDropdown` does not have a controller attached.', !!Ember.get(this, 'controller'));
            Ember.assert('Controller `%@` does not have an action `%@`!'.fmt(controller, actionName), !!Ember.canInvoke(controller, actionName));

            // Invoke the action on the controller, passing in the item as the first param.
            Ember.tryInvoke(controller, actionName, item);

        }

    })

});<commit_msg>Corrected typo in ButtonDropdown class<commit_after>/**
 * @property buttonDropdownLayout
 * @type {Array}
 */
var buttonDropdownLayout = [
    '{{view.label}}',
    '',
    '
',
        '{{yield}}',
    '
'
];

/**
 * @property buttonDropdownTemplate
 * @type {Array}
 */
var buttonDropdownTemplate = [
    '{{#if view.items}}',
    '   {{#each item in view.items}}',
    '
{{view view.Item contextBinding="item"}}
',
    '   {{/each}}',
    '{{/if}}'
];

/**
 * @property Bootstrap.ButtonDropdown
 * @type {Ember.View}
 */
Bootstrap.ButtonDropdown = Ember.View.extend({

    /**
     * @property label
     * @type {String}
     */
    label: null,

    /**
     * @property items
     * @type {Array}
     */
    items: [],

    /**
     * @property classNames
     * @type {Array}
     */
    classNames: ['btn-group'],

    /**
     * @property layout
     * @type {Function}
     */
    layout: Ember.Handlebars.compile(buttonDropdownLayout),

    /**
     * @property template
     * @type {Function}
     */
    template: Ember.Handlebars.compile(buttonDropdownTemplate),

    /**
     * @method didInsertElement
     * @return {void}
     */
    didInsertElement: function() {

        var items   = Ember.get(this, 'items'),
            forEach = Ember.EnumerableUtils.forEach;

        forEach(items, function(item) {

            // Iterate over the items, and if the `disabled` property changes, notify the
            // view of an update.
            Ember.addObserver(item, 'disabled', function() {
                this.notifyPropertyChange('view.items');
            });

        }, this);

    },

    /**
     * @property Item
     * @type {Ember.View}
     */
    Item: Ember.View.extend({

        /**
         * @property tagName
         * @type {String}
         * @default "a"
         */
        tagName: 'a',

        /**
         * @property attributeBindings
         * @type {Array}
         */
        attributeBindings: ['href'],

        /**
         * @property template
         * @type {Function}
         */
        template: Ember.Handlebars.compile('{{label}}'),

        /**
         * @property href
         * @type {Object}
         * @return {String}
         */
        href: Ember.computed(function() {
            return '#';
        }),

        /**
         * @method click
         * Attempt to invoke the specified action name on the controller.
         * @return {void}
         */
        click: function() {

            var item        = Ember.get(this, 'context'),
                actionName  = Ember.get(item, 'actionName'),
                controller  = Ember.get(this, 'controller');

            if (Ember.get(item, 'disabled') === true) {
                // We won't invoke the action if it's disabled.
                return;
            }

            Ember.assert('View `Bootstrap.ButtonDropdown` does not have a controller attached.', !!Ember.get(this, 'controller'));
            Ember.assert('Controller `%@` does not have an action `%@`!'.fmt(controller, actionName), !!Ember.canInvoke(controller, actionName));

            // Invoke the action on the controller, passing in the item as the first param.
            Ember.tryInvoke(controller, actionName, item);

        }

    })

});<|endoftext|>


<commit_before>import React from 'react';

export default function(WrappedComponent) {
  const Field = React.createClass({
    propTypes: {
      name: React.PropTypes.string.isRequired
    },

    getInitialState() {
      return {
        checked: this.checked(),
        message: this.props.message,
        timestamp: 0,
        valid: true,
        value: this.props.initialValue
      };
    },

    componentDidMount() {
      this.validators = this.props.validators || [];
      if (this.component && this.component.validators) {
        this.validators = this.component.validators.concat(this.validators);
      }
    },

    componentWillReceiveProps(nextProps) {
      this.setState({message: nextProps.message});
    },

    validate() {
      for (let validator of this.validators) {
        let result = validator(this.state.value);
        if (result !== true) {
          this.setState({valid: false, message: result});
          return false;
        }
      }
      this.setState({valid: true, message: ''});
      return true;
    },

    handleChange(event) {
      this.setState({
        type: event.target.type,
        timestamp: Date.now()
      });

      return new Promise((resolve) => {
        switch (event.target.type) {
          case 'checkbox':
            this.setState({
              checked: event.target.checked,
              value: event.target.checked ? event.target.value : null
            }, resolve);
            break;
          case 'select-multiple':
            this.setState({
              value: Array.from(event.target.selectedOptions).map((option) => {
                return option.value;
              })}, resolve);
            break;
          default:
            this.setState({value: event.target.value}, resolve);
        }
      });
    },

    checked() {
      return (
        this.props.checked ||
        this.props.value &&
        this.props.value === this.props.initialValue ||
        this.props.initialValue && !this.props.value ||
        this.props.initialValue &&
        this.props.initialValue.constructor === Array &&
        this.props.initialValue.indexOf(this.props.value) >= 0
      );
    },

    elementProps() {
      const elementProps = Object.assign({
        defaultChecked: this.checked(),
        defaultValue: this.props.value || this.props.initialValue,
        onChange: this.handleChange
      }, this.props);

      delete elementProps.checked;
      delete elementProps.initialValue;
      delete elementProps.message;
      delete elementProps.validators;
      delete elementProps.value;

      return elementProps;
    },

    render() {
      return (
        <WrappedComponent
          element={this.elementProps()}
          label={this.props.label}
          message={this.state.message}
          validate={this.validate}
          value={this.state.value}
          initialValue={this.props.initialValue}
          ref={(component) => { this.component = component;}}
        />
      );
    }
  });
  return Field;
}
<commit_msg>Use options instead of selectedOptions for better support<commit_after>import React from 'react';

export default function(WrappedComponent) {
  const Field = React.createClass({
    propTypes: {
      name: React.PropTypes.string.isRequired
    },

    getInitialState() {
      return {
        checked: this.checked(),
        message: this.props.message,
        timestamp: 0,
        valid: true,
        value: this.props.initialValue
      };
    },

    componentDidMount() {
      this.validators = this.props.validators || [];
      if (this.component && this.component.validators) {
        this.validators = this.component.validators.concat(this.validators);
      }
    },

    componentWillReceiveProps(nextProps) {
      this.setState({message: nextProps.message});
    },

    validate() {
      for (let validator of this.validators) {
        let result = validator(this.state.value);
        if (result !== true) {
          this.setState({valid: false, message: result});
          return false;
        }
      }
      this.setState({valid: true, message: ''});
      return true;
    },

    handleChange(event) {
      this.setState({
        type: event.target.type,
        timestamp: Date.now()
      });

      return new Promise((resolve) => {
        switch (event.target.type) {
          case 'checkbox':
            this.setState({
              checked: event.target.checked,
              value: event.target.checked ? event.target.value : null
            }, resolve);
            break;
          case 'select-multiple':
            let values = [];
            Array.from(event.target.options).forEach((option) => {
              if (option.selected) {
                values.push(option.value);
              }
            });
            this.setState({value: values}, resolve);
            break;
          default:
            this.setState({value: event.target.value}, resolve);
        }
      });
    },

    checked() {
      return (
        this.props.checked ||
        this.props.value &&
        this.props.value === this.props.initialValue ||
        this.props.initialValue && !this.props.value ||
        this.props.initialValue &&
        this.props.initialValue.constructor === Array &&
        this.props.initialValue.indexOf(this.props.value) >= 0
      );
    },

    elementProps() {
      const elementProps = Object.assign({
        defaultChecked: this.checked(),
        defaultValue: this.props.value || this.props.initialValue,
        onChange: this.handleChange
      }, this.props);

      delete elementProps.checked;
      delete elementProps.initialValue;
      delete elementProps.message;
      delete elementProps.validators;
      delete elementProps.value;

      return elementProps;
    },

    render() {
      return (
        <WrappedComponent
          element={this.elementProps()}
          label={this.props.label}
          message={this.state.message}
          validate={this.validate}
          value={this.state.value}
          initialValue={this.props.initialValue}
          ref={(component) => { this.component = component;}}
        />
      );
    }
  });
  return Field;
}
<|endoftext|>


<commit_before>(function () {
    "use strict";
    /*
        Tested only in Chrome, ¯\_(ツ)_/¯
        Please feel free to port/fix/fork.
    */
    const ver = 'V.20161202.154557';
    const src = 'https://github.com/taengstagram/IG-Comments-Bookmarklet/';
    console.info(ver);
    console.info(src);

    let patt = /https?:\/\/www\.instagram\.com\/p\/([^\/]+)/i;
    let matches = patt.exec(document.location.href);
    if (!matches) {
        console.error('Unable to process page');
        return;
    }
    let shortCode = matches[1];

    function getCookie(name) {
      let value = "; " + document.cookie;
      let parts = value.split("; " + name + "=");
      if (parts.length == 2) return parts.pop().split(";").shift();
    }

    function getCustomParam(name) {
        let params = (document.location.hash || document.location.search).replace('#', '&').replace('?', '&');
        let parts = params.split("&" + name + "=");
        if (parts.length == 2)
            return parts.pop().split("&").shift();
    }

    function generateParams(shortCode, cursor) {
        let params = "q=" +
            encodeURIComponent(
                "ig_shortcode(" + shortCode + ") {comments.before(" + cursor + ", 1000) "
                + "{count, nodes {id, created_at, text, user {id, profile_pic_url, username, full_name}}, page_info}}");
        return params;
    }

    function generateDt(created_at) {
        if (typeof(moment) == 'undefined' || typeof(moment.tz) == 'undefined') {
            return new Date(created_at * 1000);
        }
        return moment.tz(created_at * 1000, "Asia/Seoul").format('YYYY-MM-DD h:mm:ssA [KST]');
    }

    function renderComments(nodes) {
        if (nodes.length == 0) {
            return;
        }
        nodes.sort(function(a, b) {
            // sort in asc datetime order
            if (a.created_at > b.created_at) {
                return 1;
            }
            if (a.created_at  0) {
            // has a caption so let's put it back
            parent.appendChild(firstChild);
            parent.appendChild(document.createElement("hr"));
        }

        for (let i = 0; i < nodes.length; i++) {
            let node = nodes[i];
            let container = document.createElement("li");
            container.classList.add(itemClassName);
            container.innerHTML = '' + node.user.username + ' '
                + '' + renderEmoji(node.text) + ''
                + '' + generateDt(node.created_at) + '';
            parent.appendChild(container);

            if (i == nodes.length - 1) {
                container.insertAdjacentHTML('afterend',
                    '//source');
            }
        }

    }

    function renderStatus(text) {
        let parent = uiAnchor.childNodes[0];
        parent.innerHTML = '' + text + '';
    }

    function renderEmoji(text) {
        if (typeof(twemoji) == 'undefined') {
            return text;
        }
        return twemoji.parse(text);
    }

    let WANTED = ['329452045', '1678311178', '453462996', '363567749', '1497851591', '416573427', '1078607901', '2213235565', '1499879597'];
    let additional_ids = getCustomParam('wanted');    // let user specify additional user ids in the form of command sep IDs
    if (additional_ids) {
        WANTED = WANTED.concat(additional_ids.split(','));
    }

    let uiAnchor = document.getElementsByTagName('section')[1];

    let displayComments = [];
    let mentions = [];
    let mentionRegex = /@([a-z0-9_]+([\.][a-z0-9]+)?)/ig;

    let q = 0;

    function sendRequest(shortCode, cursor) {
        renderStatus('Loading... ' + q);
        let xhr = new XMLHttpRequest();
        let params = generateParams(shortCode, cursor);
        xhr.open("POST", "https://www.instagram.com/query/", true);
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhr.setRequestHeader("x-csrftoken", getCookie('csrftoken'));
        xhr.onreadystatechange = function() {
            if (xhr.readyState != 4) {
                return;
            }

            try {
                let info = JSON.parse(xhr.responseText);
                renderStatus('Processing... ' + q);
                let comments = info.comments.nodes;
                comments.sort(function(a, b) {
                    // sort in desc datetime order
                    if (a.created_at > b.created_at) {
                        return -1;
                    }
                    if (a.created_at < b.created_at) {
                        return 1;
                    }
                    return 0;
                });
                for (let i = 0; i < comments.length; i++) {
                    if (WANTED.indexOf(comments[i].user.id) >= 0) {
                        displayComments.push(comments[i]);

                        let matchesMention = null;
                        do {
                            matchesMention = mentionRegex.exec(comments[i].text);
                            if (matchesMention) {
                                mentions.push(matchesMention[1]);
                            }
                        } while (matchesMention);

                    } else {
                        let mentionFound = mentions.indexOf(comments[i].user.username);
                        if (mentionFound >= 0) {
                            displayComments.push(comments[i]);
                            mentions.splice(mentionFound, 1);   // remove from mentions once a match is found
                        }
                    }
                }
                if (info.comments.page_info.has_previous_page && q <= 50) {
                    let cursor = info.comments.page_info.start_cursor;
                    sendRequest(shortCode, cursor);
                    return;
                }
                renderStatus('Done. ' + displayComments.length + ' comment(s) found.');
                if (mentions.length > 0) {
                    for (let i = 0; i < mentions.length; i++) {
                        let mentioned_user = mentions[i].mentioned_user;

                    }
                }
                renderComments(displayComments);

            } catch (err) {
                let count_text = ' ' + displayComments.length + ' comment(s) found.';
                if (xhr.status >= 500) {
                    renderStatus('Instagram Error: "' + xhr.responseText + '"' + count_text);
                } else if (xhr.status == 429) {
                    renderStatus('Too Many Requests: Please try again later.' + count_text);
                } else {
                    renderStatus('Unexpected error: ' + err + count_text);
                }
                renderComments(displayComments);

            }
            return;
        };
        q++;
        xhr.send(params);
    }

    if (typeof(twemoji) == 'undefined' || typeof(moment) == 'undefined' || typeof(moment.tz) == 'undefined') {  // inject if not available
        let head = document.getElementsByTagName('head')[0];
        let scripts = ['https://twemoji.maxcdn.com/2/twemoji.min.js?2.2.2', 'https://unpkg.com/moment/min/moment.min.js', 'https://unpkg.com/moment-timezone/builds/moment-timezone-with-data.min.js'];
        for (let i = 0; i < scripts.length; i++) {
            let scriptCustom = document.createElement('script');
            scriptCustom.src = scripts[i];
            scriptCustom.async = false;
            head.appendChild(scriptCustom);
        }
        let styleCustom = document.createElement('style');
        styleCustom.innerHTML = 'img.emoji { height: 1em; width: 1em; margin: 0 .05em 0 .1em; vertical-align: -0.1em; } '
            + 'span.dt { color: #888; font-size: small; display: block; } .st { color: #1565c0; } .fn a { color: #1565c0; font-size: x-small; }';
        head.appendChild(styleCustom);
    }
    sendRequest(shortCode, '0');
})();
<commit_msg>Use mentions in usertags and caption<commit_after>(function () {
    "use strict";
    /*
        Tested only in Chrome, ¯\_(ツ)_/¯
        Please feel free to port/fix/fork.
    */
    const ver = 'V.20161205.212031';
    const src = 'https://github.com/taengstagram/IG-Comments-Bookmarklet/';
    console.info(ver);
    console.info(src);

    let patt = /https?:\/\/www\.instagram\.com\/p\/([^\/]+)/i;
    let matches = patt.exec(document.location.href);
    if (!matches) {
        console.error('Unable to process page');
        return;
    }
    let shortCode = matches[1];
    let maxPages = 0;

    function getCookie(name) {
      let value = "; " + document.cookie;
      let parts = value.split("; " + name + "=");
      if (parts.length == 2) return parts.pop().split(";").shift();
    }

    function getCustomParam(name) {
        let params = (document.location.hash || document.location.search).replace('#', '&').replace('?', '&');
        let parts = params.split("&" + name + "=");
        if (parts.length == 2)
            return parts.pop().split("&").shift();
    }

    function generateParams(shortCode, cursor) {
        let params = "q=" +
            encodeURIComponent(
                "ig_shortcode(" + shortCode + ") {comments.before(" + cursor + ", 1000) "
                + "{count, nodes {id, created_at, text, user {id, profile_pic_url, username, full_name}}, page_info}}");
        return params;
    }

    function generateDt(created_at) {
        if (typeof(moment) == 'undefined' || typeof(moment.tz) == 'undefined') {
            return new Date(created_at * 1000);
        }
        return moment.tz(created_at * 1000, "Asia/Seoul").format('YYYY-MM-DD h:mm:ssA [KST]');
    }

    function renderComments(nodes) {
        if (nodes.length == 0) {
            return;
        }
        nodes.sort(function(a, b) {
            // sort in asc datetime order
            if (a.created_at > b.created_at) {
                return 1;
            }
            if (a.created_at  0) {
            // has a caption so let's put it back
            parent.appendChild(firstChild);
            parent.appendChild(document.createElement("hr"));
        }

        for (let i = 0; i < nodes.length; i++) {
            let node = nodes[i];
            let container = document.createElement("li");
            container.classList.add(itemClassName);
            container.innerHTML = '' + node.user.username + ' '
                + '' + renderEmoji(node.text) + ''
                + '' + generateDt(node.created_at) + '';
            parent.appendChild(container);

            if (i == nodes.length - 1) {
                container.insertAdjacentHTML('afterend',
                    '//source');
            }
        }

    }

    function renderStatus(text) {
        let parent = uiAnchor.childNodes[0];
        parent.innerHTML = '' + text + '';
    }

    function renderEmoji(text) {
        if (typeof(twemoji) == 'undefined') {
            return text;
        }
        return twemoji.parse(text);
    }

    let WANTED = ['329452045', '1678311178', '453462996', '363567749', '1497851591', '416573427', '1078607901', '2213235565', '1499879597'];
    let additional_ids = getCustomParam('wanted');    // let user specify additional user ids in the form of command sep IDs
    if (additional_ids) {
        WANTED = WANTED.concat(additional_ids.split(','));
    }

    let uiAnchor = document.getElementsByTagName('section')[1];

    let displayComments = [];
    let mentions = [];
    let mentionRegex = /@([a-z0-9_]+([\.][a-z0-9]+)?)/ig;

    let q = 1;

    function sendRequest(shortCode, cursor) {
        renderStatus('Loading... ' + q + '/' + maxPages);
        let xhr = new XMLHttpRequest();
        let params = generateParams(shortCode, cursor);
        xhr.open("POST", "https://www.instagram.com/query/", true);
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhr.setRequestHeader("x-csrftoken", getCookie('csrftoken'));
        xhr.onreadystatechange = function() {
            if (xhr.readyState != 4) {
                return;
            }

            try {
                let info = JSON.parse(xhr.responseText);
                renderStatus('Processing... ' + q + '/' + maxPages);
                let comments = info.comments.nodes;
                comments.sort(function(a, b) {
                    // sort in desc datetime order
                    if (a.created_at > b.created_at) {
                        return -1;
                    }
                    if (a.created_at < b.created_at) {
                        return 1;
                    }
                    return 0;
                });
                for (let i = 0; i < comments.length; i++) {
                    if (WANTED.indexOf(comments[i].user.id) >= 0) {
                        displayComments.push(comments[i]);

                        // extract mention from comment
                        let matchesMention = null;
                        do {
                            matchesMention = mentionRegex.exec(comments[i].text);
                            if (matchesMention && mentions.indexOf(matchesMention[1]) < 0) {
                                mentions.push(matchesMention[1]);
                            }
                        } while (matchesMention);

                    } else {
                        let mentionFound = mentions.indexOf(comments[i].user.username);
                        if (mentionFound >= 0) {
                            displayComments.push(comments[i]);
                            mentions.splice(mentionFound, 1);   // remove from mentions once a match is found
                        }
                    }
                }
                if (info.comments.page_info.has_previous_page) {
                    let cursor = info.comments.page_info.start_cursor;
                    sendRequest(shortCode, cursor);
                    return;
                }
                renderStatus('Done. ' + displayComments.length + ' comment(s) found.');
                renderComments(displayComments);

            } catch (err) {
                let count_text = ' ' + displayComments.length + ' comment(s) found.';
                if (xhr.status >= 500) {
                    renderStatus('Instagram Error: "' + xhr.responseText + '"' + count_text);
                } else if (xhr.status == 429) {
                    renderStatus('Too Many Requests: Please try again later.' + count_text);
                } else {
                    renderStatus('Unexpected error: ' + err + count_text);
                }
                renderComments(displayComments);
            }
        };
        q++;
        xhr.send(params);
    }

    if (typeof(twemoji) == 'undefined' || typeof(moment) == 'undefined' || typeof(moment.tz) == 'undefined') {  // inject if not available
        let head = document.getElementsByTagName('head')[0];
        let scripts = [
            'https://twemoji.maxcdn.com/2/twemoji.min.js?2.2.2'
            , 'https://unpkg.com/moment/min/moment.min.js'
            , 'https://unpkg.com/moment-timezone/builds/moment-timezone-with-data.min.js'
        ];
        for (let i = 0; i < scripts.length; i++) {
            let scriptCustom = document.createElement('script');
            scriptCustom.src = scripts[i];
            scriptCustom.async = false;
            head.appendChild(scriptCustom);
        }
        let styleCustom = document.createElement('style');
        styleCustom.innerHTML = 'img.emoji { height: 1em; width: 1em; margin: 0 .05em 0 .1em; vertical-align: -0.1em; } '
            + 'span.dt { color: #888; font-size: small; display: block; } .st { color: #1565c0; } '
            + '.fn a { color: #1565c0; font-size: x-small; }';
        head.appendChild(styleCustom);
    }
    let xhr = new XMLHttpRequest()
    xhr.open("GET", "https://www.instagram.com/p/" + shortCode + "/?__a=1", true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState != 4) {
            return;
        }
        let info = JSON.parse(xhr.responseText);
        if (WANTED.indexOf(info.media.owner.id) < 0) {
            WANTED.push(info.media.owner.id);
        }
        if (info.media.caption) {
            // extract mentions in caption
            let matchesMention = null;
            do {
                matchesMention = mentionRegex.exec(info.media.caption);
                if (matchesMention && mentions.indexOf(matchesMention[1]) < 0) {
                    mentions.push(matchesMention[1]);
                }
            } while (matchesMention);
        }
        if (info.media.usertags.nodes.length > 0) {
            // extract user tags as mentions
            for (let i = 0; i < info.media.usertags.nodes.length; i++) {
                let username = info.media.usertags.nodes[i].user.username;
                if (mentions.indexOf(username) < 0) {
                    mentions.push(username);
                }
            }
        }
        maxPages = Math.ceil(info.media.comments.count / 1000);
        sendRequest(info.media.code, '0');
    };
    xhr.send();
})();
<|endoftext|>


<commit_before>define(["dojo", "dijit", "dijit/_Widget", "dijit/_Templated", "dojox/highlight"], function(dojo, dijit){

	dojo.declare("dojox.highlight.widget.Code",[dijit._Widget, dijit._Templated],{
		// summary:
		//		A simple source code formatting widget that adds line numbering, alternating line colors
		//		and line range support on top of dojox.highlight module.

		url: "",
		range:null,
		style:"",
		listType:"1",
		lang:"",

		// Note: If more control over formatting is required, the order list items can be replaced
		// with a table implementation instead... Excercise is left for those that need it...
		templateString:
			'
'+
				'

'+
				'

' +
				'

' +
			'
',

		postCreate: function(){
			this.inherited(arguments);
			if(this.url){
				// load from a url
				dojo.xhrGet({
					url: this.url,
					// then poopulate:
					load: dojo.hitch(this,"_populate"),
					error: dojo.hitch(this,"_loadError")
				});
			}else{
				// or just populate from our internal content
				this._populate(this.containerNode.innerHTML);
			}
		},

		_populate: function(data){
			// put the content in a common node
			this.containerNode.innerHTML =
				"
" +
					data.replace(/\</g,"<") +
				"
";
			// highlight it
			dojo.query("pre > code",this.containerNode).forEach(dojox.highlight.init);
			// FIXME: in ie7, the innerHTML in a real
 isn't split by \n's ?
			// split the content into lines
			var lines = this.containerNode.innerHTML.split("\n");
			dojo.forEach(lines,function(line,i){
				// setup all the lines of the content as
's
				var li = dojo.doc.createElement('li');
				// add some style sugar:
				dojo.addClass(li, (i % 2 !== 0 ? "even" : "odd"));
				line = "
" + line + "
";
				line = line.replace(/\t/g,"   ");
				li.innerHTML = line;
				this.codeList.appendChild(li);
			},this);
			// save our data
			this._lines = dojo.query("li",this.codeList);
			this._updateView();
		},

		// FIXME: user _setRangeAttr pattern? so you can code.set('range', [1, 100]);
		setRange: function(/* Array */range){
			// summary: update the view to a new passed range
			if(dojo.isArray(range)){
				this.range = range;
				this._updateView();
			}
		},

		_updateView: function(){
			// summary: set the list to the current range
			if(this.range){
				var r = this.range;
				this._lines
					// hide them all
					.style({ display:"none" })
					.filter(function(n,i){
						// remove nodes out of range
						return (i + 1 >= r[0] && i + 1 <= r[1]);
					})
					// set them visible again
					.style({ display:"" })
				;
				// set the "start" attribute on the OL so numbering works
				dojo.attr(this.codeList,"start",r[0]);
			}
		},

		_loadError: function(error){
			// summary: a generic error handler for the url=""
			console.warn("loading: ", this.url, " FAILED", error);
		}

	});

});<commit_msg>fix module return value<commit_after>define(["dojo", "dijit", "dijit/_Widget", "dijit/_Templated", "dojox/highlight"], function(dojo, dijit){

	return dojo.declare("dojox.highlight.widget.Code",[dijit._Widget, dijit._Templated],{
		// summary:
		//		A simple source code formatting widget that adds line numbering, alternating line colors
		//		and line range support on top of dojox.highlight module.

		url: "",
		range:null,
		style:"",
		listType:"1",
		lang:"",

		// Note: If more control over formatting is required, the order list items can be replaced
		// with a table implementation instead... exercise is left for those that need it...
		templateString:
			'
'+
				'

'+
				'

' +
				'

' +
			'
',

		postCreate: function(){
			this.inherited(arguments);
			if(this.url){
				// load from a url
				dojo.xhrGet({
					url: this.url,
					// then poopulate:
					load: dojo.hitch(this,"_populate"),
					error: dojo.hitch(this,"_loadError")
				});
			}else{
				// or just populate from our internal content
				this._populate(this.containerNode.innerHTML);
			}
		},

		_populate: function(data){
			// put the content in a common node
			this.containerNode.innerHTML =
				"
" +
					data.replace(/\</g,"<") +
				"
";
			// highlight it
			dojo.query("pre > code",this.containerNode).forEach(dojox.highlight.init);
			// FIXME: in ie7, the innerHTML in a real
 isn't split by \n's ?
			// split the content into lines
			var lines = this.containerNode.innerHTML.split("\n");
			dojo.forEach(lines,function(line,i){
				// setup all the lines of the content as
's
				var li = dojo.doc.createElement('li');
				// add some style sugar:
				dojo.addClass(li, (i % 2 !== 0 ? "even" : "odd"));
				line = "
" + line + "
";
				line = line.replace(/\t/g,"   ");
				li.innerHTML = line;
				this.codeList.appendChild(li);
			},this);
			// save our data
			this._lines = dojo.query("li",this.codeList);
			this._updateView();
		},

		// FIXME: user _setRangeAttr pattern? so you can code.set('range', [1, 100]);
		setRange: function(/* Array */range){
			// summary: update the view to a new passed range
			if(dojo.isArray(range)){
				this.range = range;
				this._updateView();
			}
		},

		_updateView: function(){
			// summary: set the list to the current range
			if(this.range){
				var r = this.range;
				this._lines
					// hide them all
					.style({ display:"none" })
					.filter(function(n,i){
						// remove nodes out of range
						return (i + 1 >= r[0] && i + 1 <= r[1]);
					})
					// set them visible again
					.style({ display:"" })
				;
				// set the "start" attribute on the OL so numbering works
				dojo.attr(this.codeList,"start",r[0]);
			}
		},

		_loadError: function(error){
			// summary: a generic error handler for the url=""
			console.warn("loading: ", this.url, " FAILED", error);
		}

	});

});<|endoftext|>


<commit_before>// passwort-generator.js 1.1.1
// http://passwort-generieren.de
// (c) 2014 Jan Krause
(function() {
    "use strict";

    var root = this;

    var PasswortGenerator = function(options) {
        if(!options){
            options = {};
            options.el = this._document.body;
        }

        this.options = this.extend(options, this.default_options);
    };

    // Export the object for **Node.js**
    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            exports = module.exports = PasswortGenerator;
        }
        exports.PasswortGenerator = PasswortGenerator;
    } else {
        root.PasswortGenerator = PasswortGenerator;
    }

    // Current version.
    PasswortGenerator.VERSION = '1.1.1';

    //base
    PasswortGenerator._document = document;

    PasswortGenerator.prototype = {
        options: {},
        default_options: {
            length: 11,
            lowercase: true,
            uppercase: true,
            numbers: true,
            special_character: true,
            brackets: true,
            minus: true,
            underscore: true,
            space: true
        },
        _passwort: '',

        extend: function(options,defaults){
            var extended = {};
            var prop;
            for (prop in defaults) {
                if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
                    extended[prop] = defaults[prop];
                }
            }
            for (prop in options) {
                if (Object.prototype.hasOwnProperty.call(options, prop)) {
                    extended[prop] = options[prop];
                }
            }
            return extended;
        },

        generate: function() {
            var _i, _len, _passwort = '';

            if(this.options.lowercase){
                _passwort += 'abcdefghijklmnopqrstuvwxyz';
            }

            if(this.options.uppercase){
                _passwort += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            }

            if(this.options.numbers){
                _passwort += '0123456789';
            }

            if(this.options.special_character){
                _passwort += ',.;:#+~*=&%$§!|/€@""^°`´\'\\';
            }

            if(this.options.brackets){
                _passwort += '<>[](){}';
            }

            if(this.options.minus){
                _passwort += '-';
            }

            if(this.options.underscore){
                _passwort += '_';
            }

            if(this.options.space){
                _passwort += ' ';
            }

            this._passwort = '';
            for (_i = 0, _len = this.options.length; _i < _len; _i++) {
                this._passwort += _passwort.charAt(Math.floor(Math.random() * _passwort.length));
            }
        },

        set: function(param) {
            this.options = this.extend(param,this.options);
        },

        get: function() {
            this.generate();
            return this._passwort;
        },

        render: function() {
            this.options.el.innerHTML = this.get();
        }
    };

}.call(this));<commit_msg>rename _passwort to _passwortString<commit_after>// passwort-generator.js 1.1.1
// http://passwort-generieren.de
// (c) 2014 Jan Krause
(function() {
    "use strict";

    var root = this;

    var PasswortGenerator = function(options) {
        if(!options){
            options = {};
            options.el = this._document.body;
        }

        this.options = this.extend(options, this.default_options);
    };

    // Export the object for **Node.js**
    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            exports = module.exports = PasswortGenerator;
        }
        exports.PasswortGenerator = PasswortGenerator;
    } else {
        root.PasswortGenerator = PasswortGenerator;
    }

    // Current version.
    PasswortGenerator.VERSION = '1.1.1';

    //base
    PasswortGenerator._document = document;

    PasswortGenerator.prototype = {
        options: {},
        default_options: {
            length: 11,
            lowercase: true,
            uppercase: true,
            numbers: true,
            special_character: true,
            brackets: true,
            minus: true,
            underscore: true,
            space: true
        },
        _passwort: '',

        extend: function(options,defaults){
            var extended = {};
            var prop;
            for (prop in defaults) {
                if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
                    extended[prop] = defaults[prop];
                }
            }
            for (prop in options) {
                if (Object.prototype.hasOwnProperty.call(options, prop)) {
                    extended[prop] = options[prop];
                }
            }
            return extended;
        },

        generate: function() {
            var _i, _len, _passwortString = '';

            if(this.options.lowercase){
                _passwortString += 'abcdefghijklmnopqrstuvwxyz';
            }

            if(this.options.uppercase){
                _passwortString += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
            }

            if(this.options.numbers){
                _passwortString += '0123456789';
            }

            if(this.options.special_character){
                _passwortString += ',.;:#+~*=&%$§!|/€@""^°`´\'\\';
            }

            if(this.options.brackets){
                _passwortString += '<>[](){}';
            }

            if(this.options.minus){
                _passwortString += '-';
            }

            if(this.options.underscore){
                _passwortString += '_';
            }

            if(this.options.space){
                _passwortString += ' ';
            }

            this._passwort = '';
            for (_i = 0, _len = this.options.length; _i < _len; _i++) {
                this._passwort += _passwortString.charAt(Math.floor(Math.random() * _passwortString.length));
            }
        },

        set: function(param) {
            this.options = this.extend(param,this.options);
        },

        get: function() {
            this.generate();
            return this._passwort;
        },

        render: function() {
            this.options.el.innerHTML = this.get();
        }
    };

}.call(this));<|endoftext|>


<commit_before>const path = require('path');
const babel = require('babel-core/register');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const webpack = require('webpack');

const src = './src';
//const dest = './';
const dest = '';

const relativeSrcPath = path.relative('.', src);

module.exports = {
  dest: dest,

  js: {
    src: src + '/js/**',
    dest: dest,
    uglify: false
  },

  eslint: {
    src: [
      src + '/js/**',
      './test/**/*.js',
      '!' + src + '/js/third_party/*.js'
    ],
    opts: {
      useEslintrc: true
    }
  },

  webpack: {
    node: {
      target: 'node',
      entry: src + '/js/main.js',
      output: {
        path: dest,
        filename: 'index.node.js',
        library: 'JSceneKit',
        libraryTarget: 'commonjs2'
      },
      resolve: {
        extensions: ['', '.js']
      },
      plugins: [
        new webpack.DefinePlugin({'process.env.BROWSER': false})
      ],
      module: {
        loaders: [
          {
            test: /ammo.js$/,
            loader: 'script-loader'
          },
          {
            test: /\.js$/,
            exclude: [/node_modules/, /ammo.js$/],
            loader: 'babel-loader',
            query: {
              presets: ['es2015']
            }
          }
        ]
      },
      externals: {
        fs: 'fs'
        //'../third_party/ammo': 'ammo'
      }
    },
    web: {
      target: 'web',
      entry: src + '/js/main.js',
      output: {
        path: dest,
        filename: 'index.web.js',
        library: 'JSceneKit',
        libraryTarget: 'commonjs2'
      },
      resolve: {
        extensions: ['', '.js']
      },
      plugins: [
        new webpack.DefinePlugin({'process.env.BROWSER': true})
      ],
      module: {
        loaders: [
          {
            test: /ammo.js$/,
            loader: 'script-loader'
          },
          {
            test: /\.js$/,
            exclude: [/node_modules/, /ammo.js$/],
            loader: 'babel-loader',
            query: {
              presets: ['es2015']
            }
          }
        ]
      },
      node: {
        fs: false,
        Buffer: true
      },
      externals: {
        //fs: 'fs'
        //'../third_party/ammo': 'ammo'
      }
    },
    webmin: {
      target: 'web',
      entry: src + '/js/main.js',
      output: {
        path: dest,
        filename: 'index.web.min.js',
        library: 'JSceneKit',
        libraryTarget: 'var'
      },
      resolve: {
        extensions: ['', '.js']
      },
      plugins: [
        new webpack.DefinePlugin({'process.env.BROWSER': true}),
        new webpack.optimize.UglifyJsPlugin()
      ],
      module: {
        loaders: [
          {
            test: /ammo.js$/,
            loader: 'script-loader'
          },
          {
            test: /\.js$/,
            exclude: [/node_modules/, /ammo.js$/],
            loader: 'babel-loader',
            query: {
              presets: ['es2015']
            }
          }
        ]
      },
      node: {
        fs: false,
        Buffer: true
      },
      externals: {
        //fs: 'fs'
        //'../third_party/ammo': 'ammo'
      }
    }
  },

  mocha: {
    src: ['test/**/*.js', 'src/**/*.js', '!src/**/*.web.js'],
    compilers: {
      js: babel
    },
    opts: {
      ui: 'bdd',
      reporter: 'spec', // or nyan
      globals: [],
      require: ['test/helper/testHelper', 'chai']
    }
  },

  copy: {
    src: [
    ],
    dest: dest
  },

  watch: {
    js: relativeSrcPath + '/js/**'
  }
}

<commit_msg>delete unnecessary line from config.js<commit_after>const path = require('path');
const babel = require('babel-core/register');
const webpack = require('webpack');

const src = './src';
//const dest = './';
const dest = '';

const relativeSrcPath = path.relative('.', src);

module.exports = {
  dest: dest,

  js: {
    src: src + '/js/**',
    dest: dest,
    uglify: false
  },

  eslint: {
    src: [
      src + '/js/**',
      './test/**/*.js',
      '!' + src + '/js/third_party/*.js'
    ],
    opts: {
      useEslintrc: true
    }
  },

  webpack: {
    node: {
      target: 'node',
      entry: src + '/js/main.js',
      output: {
        path: dest,
        filename: 'index.node.js',
        library: 'JSceneKit',
        libraryTarget: 'commonjs2'
      },
      resolve: {
        extensions: ['', '.js']
      },
      plugins: [
        new webpack.DefinePlugin({'process.env.BROWSER': false})
      ],
      module: {
        loaders: [
          {
            test: /ammo.js$/,
            loader: 'script-loader'
          },
          {
            test: /\.js$/,
            exclude: [/node_modules/, /ammo.js$/],
            loader: 'babel-loader',
            query: {
              presets: ['es2015']
            }
          }
        ]
      },
      externals: {
        fs: 'fs'
        //'../third_party/ammo': 'ammo'
      }
    },
    web: {
      target: 'web',
      entry: src + '/js/main.js',
      output: {
        path: dest,
        filename: 'index.web.js',
        library: 'JSceneKit',
        libraryTarget: 'commonjs2'
      },
      resolve: {
        extensions: ['', '.js']
      },
      plugins: [
        new webpack.DefinePlugin({'process.env.BROWSER': true})
      ],
      module: {
        loaders: [
          {
            test: /ammo.js$/,
            loader: 'script-loader'
          },
          {
            test: /\.js$/,
            exclude: [/node_modules/, /ammo.js$/],
            loader: 'babel-loader',
            query: {
              presets: ['es2015']
            }
          }
        ]
      },
      node: {
        fs: false,
        Buffer: true
      },
      externals: {
        //fs: 'fs'
        //'../third_party/ammo': 'ammo'
      }
    },
    webmin: {
      target: 'web',
      entry: src + '/js/main.js',
      output: {
        path: dest,
        filename: 'index.web.min.js',
        library: 'JSceneKit',
        libraryTarget: 'var'
      },
      resolve: {
        extensions: ['', '.js']
      },
      plugins: [
        new webpack.DefinePlugin({'process.env.BROWSER': true}),
        new webpack.optimize.UglifyJsPlugin()
      ],
      module: {
        loaders: [
          {
            test: /ammo.js$/,
            loader: 'script-loader'
          },
          {
            test: /\.js$/,
            exclude: [/node_modules/, /ammo.js$/],
            loader: 'babel-loader',
            query: {
              presets: ['es2015']
            }
          }
        ]
      },
      node: {
        fs: false,
        Buffer: true
      },
      externals: {
        //fs: 'fs'
        //'../third_party/ammo': 'ammo'
      }
    }
  },

  mocha: {
    src: ['test/**/*.js', 'src/**/*.js', '!src/**/*.web.js'],
    compilers: {
      js: babel
    },
    opts: {
      ui: 'bdd',
      reporter: 'spec', // or nyan
      globals: [],
      require: ['test/helper/testHelper', 'chai']
    }
  },

  copy: {
    src: [
    ],
    dest: dest
  },

  watch: {
    js: relativeSrcPath + '/js/**'
  }
}

<|endoftext|>


<commit_before>import { DOM_CHANGED_EVENT } from '../classes/fef-dom-observer';
import { FefResponsiveHelper } from '../classes/fef-responsive-helper';
import { FefBouncePrevention } from './fef-bounce-prevention';
import { KEYCODES } from '../utils/fef-keycodes';

const ANIMATION_FADE_IN_OUT = 'fade-in-out';
const ANIMATION_SCALE_FROM_ORIGIN = 'scale-from-origin';
const ANIMATION_FLYOUT = 'as-flyout-from-origin';
const ANIMATION_SLIDE_FROM_BOTTOM = 'slide-from-bottom';

const ANIMATION_SPEED = (window.matchMedia('(prefers-reduced-motion)').matches) ? 0 : 200;
const END_OF_MODAL = '.js-end-of-modal';

let existingModals = {};
let scrollbarWidth = 0;

$(window).on(DOM_CHANGED_EVENT, (e) => {
    scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
    $('[data-modal-id]').each((index, element) => {

        $(element).on('click', (event) => {
            event.preventDefault();
            event.stopPropagation();

            let $caller = $(element);
            let modalId = $caller.attr('data-modal-id');
            let $modalElement = $(`[data-id=${modalId}]`);

            if (existingModals[modalId]) {
                existingModals[modalId].setCaller($caller);
                existingModals[modalId].show();
            } else if ($modalElement.length > 0) {
                existingModals[modalId] = new FefModal($modalElement, $caller);
            }
        });
    });
});

/**
 * Handles showing and hiding a modal element
 */
export class FefModal {

    /**
     * @param $element jQuery element
     * @param $caller jQuery element
     */
    constructor($element, $caller) {
        this.$element = $element;
        this.$caller = $caller;
        this.$focusTarget = this.$element.find('.js-focus-target').first();
        this.$mainWrapper = this.$element.find('.js-modal-main-wrapper');
        this.$mainContent = this.$element.find('.js-modal-main-content');
        this.animation = this.$element.attr('data-animation');
        this.previousScrollPosition = null;
        this.browserSupportsElasticScrolling = FefBouncePrevention.checkSupport();

        // Accsessibility: when opening the modal, set all other content on the page to aria-hidden, so that screenreaders can't access them anymore.
        this.$A11YElements = this.$element.siblings('div, section, footer, span, h1, a, img');

        $element.append('');
        this.$mainWrapper.append('Schliessen');

        this.bindEvents();

        if (this.$element.hasClass('js-min-height-of-masthead')) {
            this.$mainContent.css('min-height', $('.js-masthead').outerHeight());
        }

        this.postInit();
    }

    postInit() {
        this.show();
    }

    /**
     * Binds the relevant events for this modal:
     * - Click on a close-button or the overlay
     * - Pressing Escape
     */
    bindEvents() {
        this.$element.find('.js-close-modal, .js-modal-overlay').on('click', (event) => {
            event.preventDefault();
            event.stopPropagation();
            this.close();
        });

        this.$element.on('keydown', (e) => {
            if (e.keyCode === KEYCODES.escape) {
                this.close();
            }
        });

        // A11Y Helper: when tabbing out of the modal --> on focus, close modal, set focus to the caller
        $(END_OF_MODAL).on('focus', () => {
            this.close();
        });
    }

    /**
     * Show the modal, depending on the provided animation.
     * The actual _showing_ of the modal is done by jQuery ($.show() or $.fadeIn()).
     */
    show() {
        this.$caller.attr({'aria-expanded': true, 'aria-haspopup': true});

        switch (this.animation) {
            case ANIMATION_SCALE_FROM_ORIGIN:
                this.scaleFromOrigin(() => this.onShowFinished());
                break;
            case ANIMATION_FLYOUT:
                this.asFlyoutFromOrigin(() => this.onShowFinished());
                break;
            case ANIMATION_FADE_IN_OUT:
                this.$element.stop(true, true).fadeIn(ANIMATION_SPEED, () => this.onShowFinished());
                break;
            case ANIMATION_SLIDE_FROM_BOTTOM:
                this.slideFromBottom(() => this.onShowFinished());
                break;
            default:
                this.$element.show(() => this.onShowFinished());
                break;
        }
    }

    onShowFinished() {
        this.preventScrolling();

        if (this.animation !== ANIMATION_FLYOUT) {
            this.setA11YProperties(true);
        }

        if (this.$focusTarget && this.$focusTarget.length === 1) {
            this.setFocus(this.$focusTarget);
        }
    }

    /**
     * Hide the modal, depending on the provided animation.
     */
    close() {
        this.scrollToPreviousPosition();

        this.$caller.attr({'aria-expanded': false, 'aria-haspopup': false});

        switch (this.animation) {
            case ANIMATION_FADE_IN_OUT:
                this.$element.stop(true, true).fadeOut(ANIMATION_SPEED, () => this.setFocus(this.$caller));
                this.setA11YProperties(false);
                break;
            case ANIMATION_FLYOUT:
                this.$element.fadeOut(ANIMATION_SPEED, () => this.setFocus(this.$caller)).hide();
                break;
            case ANIMATION_SLIDE_FROM_BOTTOM:
                this.slideFromBottomClose();
                this.setA11YProperties(false);
                break;
            default:
                this.$element.hide(ANIMATION_SPEED, '', () => this.setFocus(this.$caller));
                this.setA11YProperties(false);
                break;
        }
    }

    /**
     * Simply using .focus() doesn't suffice.
     *
     * @param $element jQuery.Element
     */
    setFocus($element) {
        $element.attr('tabindex', -1).on('blur focusout', () => {
            $element.removeAttr('tabindex');
        }).get(0).focus({preventScroll: true});
    }

    /**
     * Fancy menu opening animation:
     * - fades the modal in
     * - 'opens' it from the originating element
     * - fades in the content (otherwise it'll be resized)
     * - calls an optional callback
     *
     * For aesthetical reasons we have to animate to the previous height and not 100% max-height directly.
     */
    scaleFromOrigin(callBack) {
        this.$mainContent.css('opacity', 0);
        this.$element.show();

        let originalHeight = this.$mainWrapper.height();
        let box = this.$caller[0].getBoundingClientRect();
        this.$mainWrapper.css({
            'left': box.left,
            'width': box.width,
            'max-height': box.height,
            'top': box.top,
            'opacity': 0
        }).animate({
            'left': 0,
            'width': '100%',
            'max-height': originalHeight,
            'top': 0,
            'opacity': 1
        }, ANIMATION_SPEED, 'easeInOutSine', () => {
            // remove the scrollbars
            this.$mainWrapper.css({
                'max-height': '100%',
                'width': `calc(100% + ${scrollbarWidth}px)`,
                'margin-right': scrollbarWidth
            });
            this.$mainContent.animate({
                'opacity': 1
            }, ANIMATION_SPEED, callBack);
        });
    }

    /**
     * Flyout opening animation:
     * - used for flyout-modals
     * - smartphone: flyout is fixed to the bottom of the viewport
     * - tablet-up: flyout is centered over the caller element (i.e. a button)
     */
    asFlyoutFromOrigin(callBack) {
        // clear existing inline styles on flyout (in case a resizing of the viewport happened)
        this.$element.attr('style', '');

        this.$element.css({
            'display': 'block',
            'opacity': 0
        });

        if (!FefResponsiveHelper.isSmartphone()) {
            // a flyout can be placed anywhere in the dom. but for positioning it relative to the caller (while staying
            // at place on scrolling), it must be positioned absolutely relative to the page. that's why we move it in
            // the DOM to be a first-level child of the body element, if needed.

            if(this.$element.parent().get(0).tagName !== 'BODY') {
                $('body').append(this.$element);
            }

            let callerBox = this.$caller.offset();
            let flyoutBox = this.$element[0].getBoundingClientRect();

            let newPosLeft = Math.ceil(callerBox.left + (this.$caller.width()/2) - (flyoutBox.width/2));
            let newPosTop = Math.ceil(callerBox.top + (this.$caller.height()/2) - (flyoutBox.height/2));

            this.$element.css({
                'position': 'absolute',
                'left': newPosLeft+'px',
                'top': newPosTop+'px'
            });
        }

        this.$element.animate({
            'opacity': 1
        }, ANIMATION_SPEED, 'easeInOutSine', callBack);
    }

    /**
     * modal opening animation:
     * - slides the modal in from the bottom
     * - adjusts the animation speed depending on modal height
     * - calls an optional callback
     */
    slideFromBottom(callBack) {
        this.$element.show();
        let modalHeight = this.$mainWrapper.outerHeight();
        let animationSpeed = (ANIMATION_SPEED > 0) ? ANIMATION_SPEED + (Math.floor(modalHeight / 100) * 25) : 0; // adjusting animation speed

        this.$mainWrapper.css({
            'bottom': `-${modalHeight}px`,
            'transition': `transform ${animationSpeed}ms ease-in-out`,
            'transform': `translateY(-${modalHeight}px)`
        });

        callBack();
    }

    /**
     * corresponding closing animation for slideFromBottom:
     * - slides the modal out to the bottom
     * - sets the focus to the caller
     */
    slideFromBottomClose() {
        this.$mainWrapper.one('transitionend', () => {
            this.$element.hide();
            this.setFocus(this.$caller);
        });

        this.$mainWrapper.css({
            'transform': 'translateY(0)'
        });
    }

    /**
     * Prevent scrollable page when the modal is open.
     * We achieve this by setting the body to overflow: hidden and setting the height to 100%, thus
     * effectively cutting the rest of the page off. This scrolls to the top of the page, so we
     * also have to save the previous scroll state.
     *
     * Additionally, we prevent bouncy body scrolling which can lead to subpar
     * experience on iOS devices.
     *
     * We only do this if the modal covers the whole page and on mobile/tablet.
     */
    preventScrolling() {
        if (this.$mainContent.outerHeight() >= $(window).outerHeight() && (FefResponsiveHelper.isTablet() || FefResponsiveHelper.isSmartphone())) {
            this.previousScrollPosition = $(window).scrollTop();
            $('html').addClass('h-prevent-scrolling');

            if (this.browserSupportsElasticScrolling) {
                FefBouncePrevention.enable();
            }
        }
    }

    /**
     * If, upon opening the modal, the ability to scroll was removed, we give it back now. This means:
     * - removing the class that prevents the scrolling
     * - scrolling back to the previously saved scroll position
     * - additionally we re-enable bouncy body scrolling
     *
     * This makes it appear as if we never even scrolled away.
     */
    scrollToPreviousPosition() {
        if (this.previousScrollPosition !== null) {
            $('html').removeClass('h-prevent-scrolling');
            $(window).scrollTop(this.previousScrollPosition);
            this.previousScrollPosition = null;

        }

        if (this.browserSupportsElasticScrolling) {
            FefBouncePrevention.disable();
        }
    }

    /**
     * When the modal is open, make it accessible to screenreaders and
     * hide the rest of the page from them.
     *
     * @param modalIsOpened {boolean}
     */
    setA11YProperties(modalIsOpened) {
        this.$A11YElements.attr({
            'aria-hidden': modalIsOpened,
            'role': modalIsOpened ? 'presentation': ''
        });
    }

    /**
     * In some cases the same modal may be opened by different caller.
     *
     * @param $caller jQuery.Element
     */
    setCaller($caller) {
        this.$caller = $caller;
    }
}
<commit_msg>Adds ajax feature to fef-modal<commit_after>import { DOM_CHANGED_EVENT, FefDomObserver } from '../classes/fef-dom-observer';
import { FefResponsiveHelper } from '../classes/fef-responsive-helper';
import { FefBouncePrevention } from './fef-bounce-prevention';
import { KEYCODES } from '../utils/fef-keycodes';

const ANIMATION_FADE_IN_OUT = 'fade-in-out';
const ANIMATION_SCALE_FROM_ORIGIN = 'scale-from-origin';
const ANIMATION_FLYOUT = 'as-flyout-from-origin';
const ANIMATION_SLIDE_FROM_BOTTOM = 'slide-from-bottom';

const ANIMATION_SPEED = (window.matchMedia('(prefers-reduced-motion)').matches) ? 0 : 200;
const END_OF_MODAL = '.js-end-of-modal';

let existingModals = {};
let scrollbarWidth = 0;

$(window).on(DOM_CHANGED_EVENT, (e) => {
    scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
    $('[data-modal-id]').each((index, element) => {

        $(element).on('click', (event) => {
            event.preventDefault();
            event.stopPropagation();

            let $caller = $(element);
            let modalId = $caller.attr('data-modal-id');
            let $modalElement = $(`[data-id=${modalId}]`);

            if (existingModals[modalId]) {
                existingModals[modalId].setCaller($caller);
                existingModals[modalId].load();
            } else if ($modalElement.length > 0) {
                existingModals[modalId] = new FefModal($modalElement, $caller);
            }
        });
    });
});

/**
 * Handles showing and hiding a modal element
 */
export class FefModal {

    /**
     * @param $element jQuery element
     * @param $caller jQuery element
     */
    constructor($element, $caller) {
        this.$element = $element;
        this.$caller = $caller;
        this.domObserver = new FefDomObserver();
        this.$focusTarget = this.$element.find('.js-focus-target').first();
        this.$mainWrapper = this.$element.find('.js-modal-main-wrapper');
        this.$mainContent = this.$element.find('.js-modal-main-content');
        this.$contentContainer = this.$element.find('.js-modal-content-container');
        this.animation = this.$element.attr('data-animation');
        this.previousScrollPosition = null;
        this.browserSupportsElasticScrolling = FefBouncePrevention.checkSupport();

        // Accsessibility: when opening the modal, set all other content on the page to aria-hidden, so that screenreaders can't access them anymore.
        this.$A11YElements = this.$element.siblings('div, section, footer, span, h1, a, img');

        $element.append('');
        this.$mainWrapper.append('Schliessen');

        this.bindEvents();

        if (this.$element.hasClass('js-min-height-of-masthead')) {
            this.$mainContent.css('min-height', $('.js-masthead').outerHeight());
        }

        this.postInit();
    }

    postInit() {
        this.load();
    }

    /**
     * Binds the relevant events for this modal:
     * - Click on a close-button or the overlay
     * - Pressing Escape
     */
    bindEvents() {
        this.$element.find('.js-close-modal, .js-modal-overlay').on('click', (event) => {
            event.preventDefault();
            event.stopPropagation();
            this.close();
        });

        this.$element.on('keydown', (e) => {
            if (e.keyCode === KEYCODES.escape) {
                this.close();
            }
        });

        // A11Y Helper: when tabbing out of the modal --> on focus, close modal, set focus to the caller
        $(END_OF_MODAL).on('focus', () => {
            this.close();
        });
    }

    load() {
        if (this.$caller.data('remote-content') !== null) {
            $.get(this.$caller.data('remote-content'), (content) => {
                this.$contentContainer.html(content);
                this.domObserver.triggerDomChangedEvent();
                this.show();
            })
        } else {
            this.show();
        }

    }
    /**
     * Show the modal, depending on the provided animation.
     * The actual _showing_ of the modal is done by jQuery ($.show() or $.fadeIn()).
     */
    show() {
        this.$caller.attr({'aria-expanded': true, 'aria-haspopup': true});

        switch (this.animation) {
            case ANIMATION_SCALE_FROM_ORIGIN:
                this.scaleFromOrigin(() => this.onShowFinished());
                break;
            case ANIMATION_FLYOUT:
                this.asFlyoutFromOrigin(() => this.onShowFinished());
                break;
            case ANIMATION_FADE_IN_OUT:
                this.$element.stop(true, true).fadeIn(ANIMATION_SPEED, () => this.onShowFinished());
                break;
            case ANIMATION_SLIDE_FROM_BOTTOM:
                this.slideFromBottom(() => this.onShowFinished());
                break;
            default:
                this.$element.show(() => this.onShowFinished());
                break;
        }
    }

    onShowFinished() {
        this.preventScrolling();

        if (this.animation !== ANIMATION_FLYOUT) {
            this.setA11YProperties(true);
        }

        if (this.$focusTarget && this.$focusTarget.length === 1) {
            this.setFocus(this.$focusTarget);
        }
    }

    /**
     * Hide the modal, depending on the provided animation.
     */
    close() {
        this.scrollToPreviousPosition();

        this.$caller.attr({'aria-expanded': false, 'aria-haspopup': false});

        switch (this.animation) {
            case ANIMATION_FADE_IN_OUT:
                this.$element.stop(true, true).fadeOut(ANIMATION_SPEED, () => this.setFocus(this.$caller));
                this.setA11YProperties(false);
                break;
            case ANIMATION_FLYOUT:
                this.$element.fadeOut(ANIMATION_SPEED, () => this.setFocus(this.$caller)).hide();
                break;
            case ANIMATION_SLIDE_FROM_BOTTOM:
                this.slideFromBottomClose();
                this.setA11YProperties(false);
                break;
            default:
                this.$element.hide(ANIMATION_SPEED, '', () => this.setFocus(this.$caller));
                this.setA11YProperties(false);
                break;
        }
    }

    /**
     * Simply using .focus() doesn't suffice.
     *
     * @param $element jQuery.Element
     */
    setFocus($element) {
        $element.attr('tabindex', -1).on('blur focusout', () => {
            $element.removeAttr('tabindex');
        }).get(0).focus({preventScroll: true});
    }

    /**
     * Fancy menu opening animation:
     * - fades the modal in
     * - 'opens' it from the originating element
     * - fades in the content (otherwise it'll be resized)
     * - calls an optional callback
     *
     * For aesthetical reasons we have to animate to the previous height and not 100% max-height directly.
     */
    scaleFromOrigin(callBack) {
        this.$mainContent.css('opacity', 0);
        this.$element.show();

        let originalHeight = this.$mainWrapper.height();
        let box = this.$caller[0].getBoundingClientRect();
        this.$mainWrapper.css({
            'left': box.left,
            'width': box.width,
            'max-height': box.height,
            'top': box.top,
            'opacity': 0
        }).animate({
            'left': 0,
            'width': '100%',
            'max-height': originalHeight,
            'top': 0,
            'opacity': 1
        }, ANIMATION_SPEED, 'easeInOutSine', () => {
            // remove the scrollbars
            this.$mainWrapper.css({
                'max-height': '100%',
                'width': `calc(100% + ${scrollbarWidth}px)`,
                'margin-right': scrollbarWidth
            });
            this.$mainContent.animate({
                'opacity': 1
            }, ANIMATION_SPEED, callBack);
        });
    }

    /**
     * Flyout opening animation:
     * - used for flyout-modals
     * - smartphone: flyout is fixed to the bottom of the viewport
     * - tablet-up: flyout is centered over the caller element (i.e. a button)
     */
    asFlyoutFromOrigin(callBack) {
        // clear existing inline styles on flyout (in case a resizing of the viewport happened)
        this.$element.attr('style', '');

        this.$element.css({
            'display': 'block',
            'opacity': 0
        });

        if (!FefResponsiveHelper.isSmartphone()) {
            // a flyout can be placed anywhere in the dom. but for positioning it relative to the caller (while staying
            // at place on scrolling), it must be positioned absolutely relative to the page. that's why we move it in
            // the DOM to be a first-level child of the body element, if needed.

            if(this.$element.parent().get(0).tagName !== 'BODY') {
                $('body').append(this.$element);
            }

            let callerBox = this.$caller.offset();
            let flyoutBox = this.$element[0].getBoundingClientRect();

            let newPosLeft = Math.ceil(callerBox.left + (this.$caller.width()/2) - (flyoutBox.width/2));
            let newPosTop = Math.ceil(callerBox.top + (this.$caller.height()/2) - (flyoutBox.height/2));

            this.$element.css({
                'position': 'absolute',
                'left': newPosLeft+'px',
                'top': newPosTop+'px'
            });
        }

        this.$element.animate({
            'opacity': 1
        }, ANIMATION_SPEED, 'easeInOutSine', callBack);
    }

    /**
     * modal opening animation:
     * - slides the modal in from the bottom
     * - adjusts the animation speed depending on modal height
     * - calls an optional callback
     */
    slideFromBottom(callBack) {
        this.$element.show();
        let modalHeight = this.$mainWrapper.outerHeight();
        let animationSpeed = (ANIMATION_SPEED > 0) ? ANIMATION_SPEED + (Math.floor(modalHeight / 100) * 25) : 0; // adjusting animation speed

        this.$mainWrapper.css({
            'bottom': `-${modalHeight}px`,
            'transition': `transform ${animationSpeed}ms ease-in-out`,
            'transform': `translateY(-${modalHeight}px)`
        });

        callBack();
    }

    /**
     * corresponding closing animation for slideFromBottom:
     * - slides the modal out to the bottom
     * - sets the focus to the caller
     */
    slideFromBottomClose() {
        this.$mainWrapper.one('transitionend', () => {
            this.$element.hide();
            this.setFocus(this.$caller);
        });

        this.$mainWrapper.css({
            'transform': 'translateY(0)'
        });
    }

    /**
     * Prevent scrollable page when the modal is open.
     * We achieve this by setting the body to overflow: hidden and setting the height to 100%, thus
     * effectively cutting the rest of the page off. This scrolls to the top of the page, so we
     * also have to save the previous scroll state.
     *
     * Additionally, we prevent bouncy body scrolling which can lead to subpar
     * experience on iOS devices.
     *
     * We only do this if the modal covers the whole page and on mobile/tablet.
     */
    preventScrolling() {
        if (this.$mainContent.outerHeight() >= $(window).outerHeight() && (FefResponsiveHelper.isTablet() || FefResponsiveHelper.isSmartphone())) {
            this.previousScrollPosition = $(window).scrollTop();
            $('html').addClass('h-prevent-scrolling');

            if (this.browserSupportsElasticScrolling) {
                FefBouncePrevention.enable();
            }
        }
    }

    /**
     * If, upon opening the modal, the ability to scroll was removed, we give it back now. This means:
     * - removing the class that prevents the scrolling
     * - scrolling back to the previously saved scroll position
     * - additionally we re-enable bouncy body scrolling
     *
     * This makes it appear as if we never even scrolled away.
     */
    scrollToPreviousPosition() {
        if (this.previousScrollPosition !== null) {
            $('html').removeClass('h-prevent-scrolling');
            $(window).scrollTop(this.previousScrollPosition);
            this.previousScrollPosition = null;

        }

        if (this.browserSupportsElasticScrolling) {
            FefBouncePrevention.disable();
        }
    }

    /**
     * When the modal is open, make it accessible to screenreaders and
     * hide the rest of the page from them.
     *
     * @param modalIsOpened {boolean}
     */
    setA11YProperties(modalIsOpened) {
        this.$A11YElements.attr({
            'aria-hidden': modalIsOpened,
            'role': modalIsOpened ? 'presentation': ''
        });
    }

    /**
     * In some cases the same modal may be opened by different caller.
     *
     * @param $caller jQuery.Element
     */
    setCaller($caller) {
        this.$caller = $caller;
    }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

import 'babel-polyfill';
import fetch from 'node-fetch';
import path from 'path';
import process from 'process';
import nodeFs from 'fs';
import memFs from 'mem-fs';
import editor from 'mem-fs-editor';
import * as firebase from 'firebase';
import chalk from 'chalk';

import link from 'constants/link';

const store = memFs.create();
const fs = editor.create(store);
const root = path.resolve(__dirname, './../../data');

firebase.initializeApp({
  apiKey: process.env.apiKey,
  authDomain: process.env.authDomain,
  databaseURL: process.env.databaseURL,
  projectId: process.env.projectId,
  storageBucket: process.env.storageBucket,
  messagingSenderId: process.env.messagingSenderId
});

process.on('unhandledRejection', reason => {
  console.log(reason);
});

(async () => {
  try {
    await firebase.auth().signInWithEmailAndPassword(
      process.env.email,
      process.env.password
    );

    const snapshot = await firebase.database().ref('/').once('value');
    const data = snapshot.val();

    await Promise.all(
      nodeFs.readdirSync(path.resolve(__dirname, './../schemas'))
        .map(async schema => {
          try {
            if(!nodeFs.lstatSync(path.resolve(__dirname, './../schemas', schema)).isDirectory())
              return;

            const name = schema[0].toUpperCase() + schema.slice(1);

            if(!Object.keys(data).includes(name)) {
              if(!link[name])
                throw new Error(`[link] ${name} is not included.`);

              const data = await fetch(link[name]).then(res => res.json());

              fs.write(
                path.resolve(root, `${name}.json`),
                JSON.stringify(data, null, 2)
              );

              return firebase.database().ref(`/${name}/`).set(data);
            }
          } catch(e) {
            throw new Error(e);
          }
        })
    );

    Object.keys(data).forEach(name => {
      fs.write(
        path.resolve(root, `${name}.json`),
        JSON.stringify(data[name], null, 2)
      );
    });

    fs.commit(err => {
      if(err)
        console.log(err);

      console.log(chalk.cyan('Done'));
      process.exit();
    });
  } catch(e) {
    console.log(e);
    process.exit();
  }
})();
<commit_msg>use src/schemas, not lib/schemas<commit_after>#!/usr/bin/env node
'use strict';

import 'babel-polyfill';
import fetch from 'node-fetch';
import path from 'path';
import process from 'process';
import nodeFs from 'fs';
import memFs from 'mem-fs';
import editor from 'mem-fs-editor';
import * as firebase from 'firebase';
import chalk from 'chalk';

import link from 'constants/link';

const store = memFs.create();
const fs = editor.create(store);
const root = path.resolve(__dirname, './../../data');

firebase.initializeApp({
  apiKey: process.env.apiKey,
  authDomain: process.env.authDomain,
  databaseURL: process.env.databaseURL,
  projectId: process.env.projectId,
  storageBucket: process.env.storageBucket,
  messagingSenderId: process.env.messagingSenderId
});

process.on('unhandledRejection', reason => {
  console.log(reason);
});

(async () => {
  try {
    await firebase.auth().signInWithEmailAndPassword(
      process.env.email,
      process.env.password
    );

    const snapshot = await firebase.database().ref('/').once('value');
    const data = snapshot.val();

    await Promise.all(
      nodeFs.readdirSync(path.resolve(process.cwd(), './src/schemas'))
        .map(async schema => {
          try {
            if(!nodeFs.lstatSync(path.resolve(process.cwd(), './src/schemas', schema)).isDirectory())
              return;

            const name = schema[0].toUpperCase() + schema.slice(1);

            if(!Object.keys(data).includes(name)) {
              if(!link[name])
                throw new Error(`[link] ${name} is not included.`);

              const data = await fetch(link[name]).then(res => res.json());

              fs.write(
                path.resolve(root, `${name}.json`),
                JSON.stringify(data, null, 2)
              );

              return firebase.database().ref(`/${name}/`).set(data);
            }
          } catch(e) {
            throw new Error(e);
          }
        })
    );

    Object.keys(data).forEach(name => {
      fs.write(
        path.resolve(root, `${name}.json`),
        JSON.stringify(data[name], null, 2)
      );
    });

    fs.commit(err => {
      if(err)
        console.log(err);

      console.log(chalk.cyan('Done'));
      process.exit();
    });
  } catch(e) {
    console.log(e);
    process.exit();
  }
})();
<|endoftext|>