<commit_before>import './polyfill';
import Delta from 'quill-delta';
import Editor from './editor';
import Emitter from './emitter';
import Module from './module';
import Parchment from 'parchment';
import Selection, { Range } from './selection';
import extend from 'extend';
import logger from './logger';
import Theme from './theme';

let debug = logger('quill');

class Quill {
  static debug(limit) {
    logger.level(limit);
  }

  static import(name) {
    if (this.imports[name] == null) {
      debug.error(`Cannot import ${name}. Are you sure it was registered?`);
    }
    return this.imports[name];
  }

  static register(path, target, overwrite = false) {
    if (typeof path !== 'string') {
      let name = path.attrName || path.blotName;
      if (typeof name === 'string') {
        // register(Blot | Attributor, overwrite)
        this.register('formats/' + name, path, target);
      } else {
        Object.keys(path).forEach((key) => {
          this.register(key, path[key], target);
        });
      }
    } else {
      if (this.imports[path] != null && !overwrite) {
        debug.warn(`Overwriting ${path} with`, target);
      }
      this.imports[path] = target;
      if ((path.startsWith('blots/') || path.startsWith('formats/')) &&
          target.blotName !== 'abstract') {
        Parchment.register(target);
      }
    }
  }

  constructor(container, options = {}) {
    this.options = expandConfig(container, options);
    this.container = this.options.container;
    if (this.container == null) {
      return debug.error('Invalid Quill container', container);
    }
    if (this.options.debug) {
      Quill.debug(this.options.debug);
    }
    let html = this.container.innerHTML.trim();
    this.container.classList.add('ql-container');
    this.container.innerHTML = '';
    this.root = this.addContainer('ql-editor');
    this.emitter = new Emitter();
    this.scroll = Parchment.create(this.root, {
      emitter: this.emitter,
      whitelist: this.options.formats
    });
    this.editor = new Editor(this.scroll, this.emitter);
    this.selection = new Selection(this.scroll, this.emitter);
    this.theme = new this.options.theme(this, this.options);
    this.keyboard = this.theme.addModule('keyboard');
    this.clipboard = this.theme.addModule('clipboard');
    this.history = this.theme.addModule('history');
    this.theme.init();
    let contents = this.clipboard.convert(`<div class='ql-editor' style="white-space: normal;">${html}<p><br></p></div>`);
    this.setContents(contents);
    this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => {
      if (type === Emitter.events.TEXT_CHANGE) {
        this.root.classList.toggle('ql-blank', this.editor.isBlank());
      }
    });
    this.pasteHTML(`<div class='ql-editor' style="white-space: normal;">${html}<p><br></p></div>`);
    this.history.clear();
    if (this.options.readOnly) {
      this.disable();
    }
    if (this.options.placeholder) {
      this.root.setAttribute('data-placeholder', this.options.placeholder);
    }
  }

  addContainer(container, refNode = null) {
    if (typeof container === 'string') {
      let className = container;
      container = document.createElement('div');
      container.classList.add(className);
    }
    this.container.insertBefore(container, refNode);
    return container;
  }

  blur() {
    this.selection.setRange(null);
  }

  deleteText(index, length, source) {
    [index, length, , source] = overload(index, length, source);
    return modify.call(this, source, index, -1*length, () => {
      return this.editor.deleteText(index, length, source);
    });
  }

  disable() {
    this.enable(false);
  }

  enable(enabled = true) {
    this.editor.enable(enabled);
    this.container.classList.toggle('ql-disabled', !enabled);
    if (!enabled) {
      this.blur();
    }
  }

  focus() {
    this.selection.focus();
    this.selection.scrollIntoView();
  }

  format(name, value, source = Emitter.sources.API) {
    if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
      return new Delta();
    }
    let range = this.getSelection(true);
    let change = new Delta();
    if (range == null) return change;
    if (Parchment.query(name, Parchment.Scope.BLOCK)) {
      change = this.formatLine(range, name, value, source);
    } else if (range.length === 0) {
      this.selection.format(name, value);
      return change;
    } else {
      change = this.formatText(range, name, value, source);
    }
    this.setSelection(range, Emitter.sources.SILENT);
    return change;
  }

  formatLine(index, length, name, value, source) {
    let formats;
    [index, length, formats, source] = overload(index, length, name, value, source);
    return modify.call(this, source, index, 0, () => {
      return this.editor.formatLine(index, length, formats, source);
    });
  }

  formatText(index, length, name, value, source) {
    let formats;
    [index, length, formats, source] = overload(index, length, name, value, source);
    return modify.call(this, source, index, 0, () => {
      return this.editor.formatText(index, length, formats, source);
    });
  }

  getBounds(index, length = 0) {
    if (typeof index === 'number') {
      return this.selection.getBounds(index, length);
    } else {
      return this.selection.getBounds(index.index, index.length);
    }
  }

  getContents(index = 0, length = this.getLength() - index) {
    [index, length] = overload(index, length);
    return this.editor.getContents(index, length);
  }

  getFormat(index = this.getSelection(), length = 0) {
    if (typeof index === 'number') {
      return this.editor.getFormat(index, length);
    } else {
      return this.editor.getFormat(index.index, index.length);
    }
  }

  getLength() {
    return this.scroll.length();
  }

  getModule(name) {
    return this.theme.modules[name];
  }

  getSelection(focus = false) {
    if (focus) this.focus();
    this.update();  // Make sure we access getRange with editor in consistent state
    return this.selection.getRange()[0];
  }

  getText(index = 0, length = this.getLength() - index) {
    [index, length] = overload(index, length);
    return this.editor.getText(index, length);
  }

  hasFocus() {
    return this.selection.hasFocus();
  }

  insertEmbed(index, embed, value, source = Quill.sources.API) {
    return modify.call(this, source, index, null, () => {
      return this.editor.insertEmbed(index, embed, value, source);
    });
  }

  insertText(index, text, name, value, source) {
    let formats;
    [index, , formats, source] = overload(index, 0, name, value, source);
    return modify.call(this, source, index, text.length, () => {
      return this.editor.insertText(index, text, formats, source);
    });
  }

  isEnabled() {
    return !this.container.classList.contains('ql-disabled');
  }

  off() {
    return this.emitter.off.apply(this.emitter, arguments);
  }

  on() {
    return this.emitter.on.apply(this.emitter, arguments);
  }

  once() {
    return this.emitter.once.apply(this.emitter, arguments);
  }

  pasteHTML(index, html, source) {
    this.clipboard.dangerouslyPasteHTML(index, html, source);
  }

  removeFormat(index, length, source) {
    [index, length, , source] = overload(index, length, source);
    return modify.call(this, source, index, null, () => {
      return this.editor.removeFormat(index, length, source);
    });
  }

  setContents(delta, source = Emitter.sources.API) {
    if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
      return new Delta();
    }
    delta = new Delta(delta).slice();
    let lastOp = delta.ops[delta.ops.length - 1];
    // Quill contents must always end with newline
    if (lastOp == null || lastOp.insert[lastOp.insert.length-1] !== '\n') {
      delta.insert('\n');
    }
    delta.delete(this.getLength());
    return this.editor.applyDelta(delta, source);
  }

  setSelection(index, length, source) {
    if (index == null) {
      this.selection.setRange(null, length || Quill.sources.API);
    } else {
      [index, length, , source] = overload(index, length, source);
      this.selection.setRange(new Range(index, length), source);
    }
    this.selection.scrollIntoView();
  }

  setText(text, source = Emitter.sources.API) {
    let delta = new Delta().insert(text);
    return this.setContents(delta, source);
  }

  update(source = Emitter.sources.USER) {
    let change = this.scroll.update(source);   // Will update selection before selection.update() does if text changes
    this.selection.update(source);
    return change;
  }

  updateContents(delta, source = Emitter.sources.API) {
    if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
      return new Delta();
    }
    let range = this.getSelection();
    if (Array.isArray(delta)) {
      delta = new Delta(delta.slice());
    }
    let change = this.editor.applyDelta(delta, source);
    if (range != null) {
      range = shiftRange(range, change, source);
      this.setSelection(range, Emitter.sources.SILENT);
    }
    return change;
  }
}
Quill.DEFAULTS = {
  bounds: null,
  formats: null,
  modules: {},
  placeholder: '',
  readOnly: false,
  strict: true,
  theme: 'default'
};
Quill.events = Emitter.events;
Quill.sources = Emitter.sources;
// eslint-disable-next-line no-undef
Quill.version = typeof(QUILL_VERSION) === 'undefined' ? 'dev' : QUILL_VERSION;

Quill.imports = {
  'delta'       : Delta,
  'parchment'   : Parchment,
  'core/module' : Module,
  'core/theme'  : Theme
};

function expandConfig(container, userConfig) {
  userConfig = extend(true, {
    container: container,
    modules: {
      clipboard: true,
      keyboard: true,
      history: true
    }
  }, userConfig);
  if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {
    userConfig.theme = Theme;
  } else {
    userConfig.theme = Quill.import(`themes/${userConfig.theme}`);
    if (userConfig.theme == null) {
      throw new Error(`Invalid theme ${userConfig.theme}. Did you register it?`);
    }
  }
  let themeConfig = extend(true, {}, userConfig.theme.DEFAULTS);
  [themeConfig, userConfig].forEach(function(config) {
    config.modules = config.modules || {};
    Object.keys(config.modules).forEach(function(module) {
      if (config.modules[module] === true) {
        config.modules[module] = {};
      }
    });
  });
  let moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));
  let moduleConfig = moduleNames.reduce(function(config, name) {
    let moduleClass = Quill.import(`modules/${name}`);
    if (moduleClass == null) {
      debug.error(`Cannot load ${name} module. Are you sure you registered it?`);
    } else {
      config[name] = moduleClass.DEFAULTS || {};
    }
    return config;
  }, {});
  // Special case toolbar shorthand
  if (userConfig.modules != null && userConfig.modules.toolbar &&
      userConfig.modules.toolbar.constructor !== Object) {
    userConfig.modules.toolbar = {
      container: userConfig.modules.toolbar
    };
  }
  userConfig = extend(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);
  ['bounds', 'container'].forEach(function(key) {
    if (typeof userConfig[key] === 'string') {
      userConfig[key] = document.querySelector(userConfig[key]);
    }
  });
  userConfig.modules = Object.keys(userConfig.modules).reduce(function(config, name) {
    if (userConfig.modules[name]) {
      config[name] = userConfig.modules[name];
    }
    return config;
  }, {});
  return userConfig;
}

function modify(source, index, shift, modifier) {
  let change = new Delta();
  if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
    return new Delta();
  }
  let range = this.getSelection();
  change = modifier();
  if (range != null) {
    if (shift === null) {
      range = shiftRange(range, index, change, source);
    } else if (shift !== 0) {
      range = shiftRange(range, index, shift, source);
    }
    this.setSelection(range, Emitter.sources.SILENT);
  }
  return change;
}

function overload(index, length, name, value, source) {
  let formats = {};
  if (typeof index.index === 'number' && typeof index.length === 'number') {
    // Allow for throwaway end (used by insertText/insertEmbed)
    if (typeof length !== 'number') {
      source = value, value = name, name = length, length = index.length, index = index.index;
    } else {
      length = index.length, index = index.index;
    }
  } else if (typeof length !== 'number') {
    source = value, value = name, name = length, length = 0;
  }
  // Handle format being object, two format name/value strings or excluded
  if (typeof name === 'object') {
    formats = name;
    source = value;
  } else if (typeof name === 'string') {
    if (value != null) {
      formats[name] = value;
    } else {
      source = name;
    }
  }
  // Handle optional source
  source = source || Emitter.sources.API;
  return [index, length, formats, source];
}

function shiftRange(range, index, length, source) {
  if (range == null) return null;
  let start, end;
  if (index instanceof Delta) {
    [start, end] = [range.index, range.index + range.length].map(function(pos) {
      return index.transformPosition(pos, source === Emitter.sources.USER);
    });
  } else {
    [start, end] = [range.index, range.index + range.length].map(function(pos) {
      if (pos < index || (pos === index && source !== Emitter.sources.USER)) return pos;
      if (length >= 0) {
        return pos + length;
      } else {
        return Math.max(index, pos + length);
      }
    });
  }
  return new Range(start, end - start);
}

export { expandConfig, overload, Quill as default };
<commit_msg>already pasted<commit_after>import './polyfill';
import Delta from 'quill-delta';
import Editor from './editor';
import Emitter from './emitter';
import Module from './module';
import Parchment from 'parchment';
import Selection, { Range } from './selection';
import extend from 'extend';
import logger from './logger';
import Theme from './theme';

let debug = logger('quill');

class Quill {
  static debug(limit) {
    logger.level(limit);
  }

  static import(name) {
    if (this.imports[name] == null) {
      debug.error(`Cannot import ${name}. Are you sure it was registered?`);
    }
    return this.imports[name];
  }

  static register(path, target, overwrite = false) {
    if (typeof path !== 'string') {
      let name = path.attrName || path.blotName;
      if (typeof name === 'string') {
        // register(Blot | Attributor, overwrite)
        this.register('formats/' + name, path, target);
      } else {
        Object.keys(path).forEach((key) => {
          this.register(key, path[key], target);
        });
      }
    } else {
      if (this.imports[path] != null && !overwrite) {
        debug.warn(`Overwriting ${path} with`, target);
      }
      this.imports[path] = target;
      if ((path.startsWith('blots/') || path.startsWith('formats/')) &&
          target.blotName !== 'abstract') {
        Parchment.register(target);
      }
    }
  }

  constructor(container, options = {}) {
    this.options = expandConfig(container, options);
    this.container = this.options.container;
    if (this.container == null) {
      return debug.error('Invalid Quill container', container);
    }
    if (this.options.debug) {
      Quill.debug(this.options.debug);
    }
    let html = this.container.innerHTML.trim();
    this.container.classList.add('ql-container');
    this.container.innerHTML = '';
    this.root = this.addContainer('ql-editor');
    this.emitter = new Emitter();
    this.scroll = Parchment.create(this.root, {
      emitter: this.emitter,
      whitelist: this.options.formats
    });
    this.editor = new Editor(this.scroll, this.emitter);
    this.selection = new Selection(this.scroll, this.emitter);
    this.theme = new this.options.theme(this, this.options);
    this.keyboard = this.theme.addModule('keyboard');
    this.clipboard = this.theme.addModule('clipboard');
    this.history = this.theme.addModule('history');
    this.theme.init();
    let contents = this.clipboard.convert(`<div class='ql-editor' style="white-space: normal;">${html}<p><br></p></div>`);
    this.setContents(contents);
    this.emitter.on(Emitter.events.EDITOR_CHANGE, (type) => {
      if (type === Emitter.events.TEXT_CHANGE) {
        this.root.classList.toggle('ql-blank', this.editor.isBlank());
      }
    });
    this.history.clear();
    if (this.options.readOnly) {
      this.disable();
    }
    if (this.options.placeholder) {
      this.root.setAttribute('data-placeholder', this.options.placeholder);
    }
  }

  addContainer(container, refNode = null) {
    if (typeof container === 'string') {
      let className = container;
      container = document.createElement('div');
      container.classList.add(className);
    }
    this.container.insertBefore(container, refNode);
    return container;
  }

  blur() {
    this.selection.setRange(null);
  }

  deleteText(index, length, source) {
    [index, length, , source] = overload(index, length, source);
    return modify.call(this, source, index, -1*length, () => {
      return this.editor.deleteText(index, length, source);
    });
  }

  disable() {
    this.enable(false);
  }

  enable(enabled = true) {
    this.editor.enable(enabled);
    this.container.classList.toggle('ql-disabled', !enabled);
    if (!enabled) {
      this.blur();
    }
  }

  focus() {
    this.selection.focus();
    this.selection.scrollIntoView();
  }

  format(name, value, source = Emitter.sources.API) {
    if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
      return new Delta();
    }
    let range = this.getSelection(true);
    let change = new Delta();
    if (range == null) return change;
    if (Parchment.query(name, Parchment.Scope.BLOCK)) {
      change = this.formatLine(range, name, value, source);
    } else if (range.length === 0) {
      this.selection.format(name, value);
      return change;
    } else {
      change = this.formatText(range, name, value, source);
    }
    this.setSelection(range, Emitter.sources.SILENT);
    return change;
  }

  formatLine(index, length, name, value, source) {
    let formats;
    [index, length, formats, source] = overload(index, length, name, value, source);
    return modify.call(this, source, index, 0, () => {
      return this.editor.formatLine(index, length, formats, source);
    });
  }

  formatText(index, length, name, value, source) {
    let formats;
    [index, length, formats, source] = overload(index, length, name, value, source);
    return modify.call(this, source, index, 0, () => {
      return this.editor.formatText(index, length, formats, source);
    });
  }

  getBounds(index, length = 0) {
    if (typeof index === 'number') {
      return this.selection.getBounds(index, length);
    } else {
      return this.selection.getBounds(index.index, index.length);
    }
  }

  getContents(index = 0, length = this.getLength() - index) {
    [index, length] = overload(index, length);
    return this.editor.getContents(index, length);
  }

  getFormat(index = this.getSelection(), length = 0) {
    if (typeof index === 'number') {
      return this.editor.getFormat(index, length);
    } else {
      return this.editor.getFormat(index.index, index.length);
    }
  }

  getLength() {
    return this.scroll.length();
  }

  getModule(name) {
    return this.theme.modules[name];
  }

  getSelection(focus = false) {
    if (focus) this.focus();
    this.update();  // Make sure we access getRange with editor in consistent state
    return this.selection.getRange()[0];
  }

  getText(index = 0, length = this.getLength() - index) {
    [index, length] = overload(index, length);
    return this.editor.getText(index, length);
  }

  hasFocus() {
    return this.selection.hasFocus();
  }

  insertEmbed(index, embed, value, source = Quill.sources.API) {
    return modify.call(this, source, index, null, () => {
      return this.editor.insertEmbed(index, embed, value, source);
    });
  }

  insertText(index, text, name, value, source) {
    let formats;
    [index, , formats, source] = overload(index, 0, name, value, source);
    return modify.call(this, source, index, text.length, () => {
      return this.editor.insertText(index, text, formats, source);
    });
  }

  isEnabled() {
    return !this.container.classList.contains('ql-disabled');
  }

  off() {
    return this.emitter.off.apply(this.emitter, arguments);
  }

  on() {
    return this.emitter.on.apply(this.emitter, arguments);
  }

  once() {
    return this.emitter.once.apply(this.emitter, arguments);
  }

  pasteHTML(index, html, source) {
    this.clipboard.dangerouslyPasteHTML(index, html, source);
  }

  removeFormat(index, length, source) {
    [index, length, , source] = overload(index, length, source);
    return modify.call(this, source, index, null, () => {
      return this.editor.removeFormat(index, length, source);
    });
  }

  setContents(delta, source = Emitter.sources.API) {
    if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
      return new Delta();
    }
    delta = new Delta(delta).slice();
    let lastOp = delta.ops[delta.ops.length - 1];
    // Quill contents must always end with newline
    if (lastOp == null || lastOp.insert[lastOp.insert.length-1] !== '\n') {
      delta.insert('\n');
    }
    delta.delete(this.getLength());
    return this.editor.applyDelta(delta, source);
  }

  setSelection(index, length, source) {
    if (index == null) {
      this.selection.setRange(null, length || Quill.sources.API);
    } else {
      [index, length, , source] = overload(index, length, source);
      this.selection.setRange(new Range(index, length), source);
    }
    this.selection.scrollIntoView();
  }

  setText(text, source = Emitter.sources.API) {
    let delta = new Delta().insert(text);
    return this.setContents(delta, source);
  }

  update(source = Emitter.sources.USER) {
    let change = this.scroll.update(source);   // Will update selection before selection.update() does if text changes
    this.selection.update(source);
    return change;
  }

  updateContents(delta, source = Emitter.sources.API) {
    if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
      return new Delta();
    }
    let range = this.getSelection();
    if (Array.isArray(delta)) {
      delta = new Delta(delta.slice());
    }
    let change = this.editor.applyDelta(delta, source);
    if (range != null) {
      range = shiftRange(range, change, source);
      this.setSelection(range, Emitter.sources.SILENT);
    }
    return change;
  }
}
Quill.DEFAULTS = {
  bounds: null,
  formats: null,
  modules: {},
  placeholder: '',
  readOnly: false,
  strict: true,
  theme: 'default'
};
Quill.events = Emitter.events;
Quill.sources = Emitter.sources;
// eslint-disable-next-line no-undef
Quill.version = typeof(QUILL_VERSION) === 'undefined' ? 'dev' : QUILL_VERSION;

Quill.imports = {
  'delta'       : Delta,
  'parchment'   : Parchment,
  'core/module' : Module,
  'core/theme'  : Theme
};

function expandConfig(container, userConfig) {
  userConfig = extend(true, {
    container: container,
    modules: {
      clipboard: true,
      keyboard: true,
      history: true
    }
  }, userConfig);
  if (!userConfig.theme || userConfig.theme === Quill.DEFAULTS.theme) {
    userConfig.theme = Theme;
  } else {
    userConfig.theme = Quill.import(`themes/${userConfig.theme}`);
    if (userConfig.theme == null) {
      throw new Error(`Invalid theme ${userConfig.theme}. Did you register it?`);
    }
  }
  let themeConfig = extend(true, {}, userConfig.theme.DEFAULTS);
  [themeConfig, userConfig].forEach(function(config) {
    config.modules = config.modules || {};
    Object.keys(config.modules).forEach(function(module) {
      if (config.modules[module] === true) {
        config.modules[module] = {};
      }
    });
  });
  let moduleNames = Object.keys(themeConfig.modules).concat(Object.keys(userConfig.modules));
  let moduleConfig = moduleNames.reduce(function(config, name) {
    let moduleClass = Quill.import(`modules/${name}`);
    if (moduleClass == null) {
      debug.error(`Cannot load ${name} module. Are you sure you registered it?`);
    } else {
      config[name] = moduleClass.DEFAULTS || {};
    }
    return config;
  }, {});
  // Special case toolbar shorthand
  if (userConfig.modules != null && userConfig.modules.toolbar &&
      userConfig.modules.toolbar.constructor !== Object) {
    userConfig.modules.toolbar = {
      container: userConfig.modules.toolbar
    };
  }
  userConfig = extend(true, {}, Quill.DEFAULTS, { modules: moduleConfig }, themeConfig, userConfig);
  ['bounds', 'container'].forEach(function(key) {
    if (typeof userConfig[key] === 'string') {
      userConfig[key] = document.querySelector(userConfig[key]);
    }
  });
  userConfig.modules = Object.keys(userConfig.modules).reduce(function(config, name) {
    if (userConfig.modules[name]) {
      config[name] = userConfig.modules[name];
    }
    return config;
  }, {});
  return userConfig;
}

function modify(source, index, shift, modifier) {
  let change = new Delta();
  if (!this.options.strict && !this.isEnabled() && source === Emitter.sources.USER) {
    return new Delta();
  }
  let range = this.getSelection();
  change = modifier();
  if (range != null) {
    if (shift === null) {
      range = shiftRange(range, index, change, source);
    } else if (shift !== 0) {
      range = shiftRange(range, index, shift, source);
    }
    this.setSelection(range, Emitter.sources.SILENT);
  }
  return change;
}

function overload(index, length, name, value, source) {
  let formats = {};
  if (typeof index.index === 'number' && typeof index.length === 'number') {
    // Allow for throwaway end (used by insertText/insertEmbed)
    if (typeof length !== 'number') {
      source = value, value = name, name = length, length = index.length, index = index.index;
    } else {
      length = index.length, index = index.index;
    }
  } else if (typeof length !== 'number') {
    source = value, value = name, name = length, length = 0;
  }
  // Handle format being object, two format name/value strings or excluded
  if (typeof name === 'object') {
    formats = name;
    source = value;
  } else if (typeof name === 'string') {
    if (value != null) {
      formats[name] = value;
    } else {
      source = name;
    }
  }
  // Handle optional source
  source = source || Emitter.sources.API;
  return [index, length, formats, source];
}

function shiftRange(range, index, length, source) {
  if (range == null) return null;
  let start, end;
  if (index instanceof Delta) {
    [start, end] = [range.index, range.index + range.length].map(function(pos) {
      return index.transformPosition(pos, source === Emitter.sources.USER);
    });
  } else {
    [start, end] = [range.index, range.index + range.length].map(function(pos) {
      if (pos < index || (pos === index && source !== Emitter.sources.USER)) return pos;
      if (length >= 0) {
        return pos + length;
      } else {
        return Math.max(index, pos + length);
      }
    });
  }
  return new Range(start, end - start);
}

export { expandConfig, overload, Quill as default };
<|endoftext|>


<commit_before>define(['BlinkForms', 'testUtils', 'underscore', 'moment', 'BIC'], function (Forms, testUtils, _, moment) {
  var nativedate = [
      'date',
      'datetime'
    ], nativetime = [
      'time',
      'datetime'
    ], pickadate = [
      'datenow',
      'datefromnowplus',
      'datefromdate',
      'datetimenow',
      'date column'
    ], pickatime = [
      'timenow',
      'timenowplus',
      'timenowtime',
      'datetimenow',
      'time column'
    ],readonly = [
      'dateTimeRonlyNone',
      'dateTimeRonlyNow',
      'dateTimeRonlyNowP',
      'dateTimeRonlyNowPM'
    ], hidden = [
      'dateTimeHiddenNone',
      'dateTimeHiddenNow',
      'dateTimeHiddenNowP',
      'dateTimeHddenNowPM'
    ];

  function defineModelToViewTests () {
    test('model->value', function (done) {
      setTimeout(function () {
        Forms.current.get('elements').forEach(function (el) {
          var value = el.val();
          var name = el.get('name');
          var time = el.get('_time');
          var date = el.get('_date');
          var type = el.get('type');
          var time$, date$, format;
          if (type === 'hidden') {
            return;
          }
          time$ = el.get('_view').$el.find('input[name$=_time]');
          date$ = el.get('_view').$el.find('input[name$=_date]');
          if (!el.get('readonly')) {
            if (type !== 'time') {
              format = el.mapDateFormats[el.attributes.dateFormat] || 'YYYY-MM-DD';
              assert.lengthOf(date$, 1, name + ': has DOM date');
              if (date$.val() && date) {
                assert.equal(date$.val(), moment(date, 'YYYY-MM-DD').format(format), name + ': DOM date value');
              }
            }
            if (type !== 'date') {
              assert.lengthOf(time$, 1, name + ': has DOM time');
              assert.equal(time$.val(), time, name + ': DOM time value');
            }
          }
        });
        done();
      }, 1e3);
    });
  }

  function defineViewToModelTests () {
    test('view->model', function (done) {
      setTimeout(function () {
        Forms.current.get('elements').forEach(function (el) {
          var date;
          var time;
          var value = el.val();
          var name = el.get('name');
          var type = el.get('type');
          var time$, date$, format, result;
          var nowdate = [
            'datenow',
            'datefromnowplus',
            'dateTimeHiddenNow'
          ];

          if (type === 'hidden') {
            return;
          }
          time$ = el.get('_view').$el.find('input[name$=_time]');
          date$ = el.get('_view').$el.find('input[name$=_date]');
          if (value && !el.get('readonly')) {
            if (type !== 'time') {
              format = el.mapDateFormats[el.attributes.dateFormat] || 'YYYY-MM-DD';
              if (_.indexOf(nowdate, name) > -1) {
                date = moment().format('YYYY-MM-DD');
              } else if (name === 'datefromdate') {
                date = '2014-03-10';
              } else {
                date = '2015-06-10';
              }
              result = moment(date).format(format);
              date$.val(result);
              date$.trigger('change');
              assert.lengthOf(date$, 1, name + ': has DOM date');
              assert.equal(date$.val(), result, name + ': DOM date value');
              assert.equal(date, el.get('_date'), name + ': _date value');
            }
            if (type !== 'date') {
              time = '10:00';
              time$.val(time);
              time$.trigger('change');
              assert.lengthOf(time$, 1, name + ': has DOM time');
              assert.equal(time$.val(), time, name + ': DOM time value');
              assert.equal(time$.val(), el.get('_time'), name + ': _time value');
            }
          }
        });
        done();
      }, 1e3);
    });
  }

  function clearFields () {
    test('clear view', function (done) {
      setTimeout(function () {
        Forms.current.get('elements').forEach(function (el) {
          var value = '';
          var name = el.get('name');
          var type = el.get('type');
          var time$;
          var date$;

          if (type === 'hidden') {
            return;
          }
          time$ = el.get('_view').$el.find('input[name$=_time]');
          date$ = el.get('_view').$el.find('input[name$=_date]');

          if (!el.get('readonly')) {
            if (type !== 'time') {
              date$.val(value);
              date$.trigger('change');
              assert.lengthOf(date$, 1, name + ': has DOM date');
              assert.equal(date$.val(), el.get('_date'), name + ': DOM date value');
            }
            if (type !== 'date') {
              time$.val(value);
              time$.trigger('change');
              assert.lengthOf(time$, 1, name + ': has DOM time');
              assert.equal(time$.val(), el.get('_time'), name + ': DOM time value');
            }
          }
        });
        done();
      }, 1e3);
    });
  }

  testUtils.defineFormLoadSuite('form1', 'add');

  suite('1: date/time', function () {
    test('all now and now_plus models have non-empty values', function () {
      Forms.current.get('elements').forEach(function (el) {
        var defaultValue = el.get('defaultValue');
        if (defaultValue && defaultValue.indexOf('now') !== -1) {
          assert.ok(el.val(), el.get('name') + ': non-empty');
        }
      });
    });

    test('all models with no default have non-empty values', function () {
      Forms.current.get('elements').forEach(function (el) {
        var defaultValue = el.get('defaultValue');
        if (!defaultValue) {
          assert.notOk(el.val(), el.get('name') + ': empty');
        }
      });
    });

    defineModelToViewTests();
    defineViewToModelTests();

    test('use datetime field - avoid undefined', function () {
      var form = Forms.current,
        element = form.getElement('datetimenonative'),
        $fieldset = element.attributes._view.$el,
        date = $fieldset.find('input[name="datetimenonative_date"]'),
        time = $fieldset.find('input[name="datetimenonative_time"]');

      date.val('2014-02-01').change();
      assert.equal(element.val(), '2014-02-01T00:00');

      date.val('').change();
      time.val('').change();
      time.val('10:11').change();
      assert.equal(element.val(), '0000-00-00T10:11');
    });

    test('use datetime field - organise elements properly on screen', function () {
      var form = Forms.current,
        element = form.getElement('datetimenonative'),
        $fieldset = element.attributes._view.$el.children(),
        elementsInOrder = [
          'LABEL',
          'DIV',
          'LABEL',
          'DIV'
        ];

      elementsInOrder.forEach(function (v, k) {
        assert.equal($($fieldset[k]).prop('tagName'), v);
      });
    });

    test('use native picker defaults to native picker', function () {
      var form = Forms.current,
        element,
        $fieldset;
      nativedate.forEach(function (fld) {
        element = form.getElement(fld);
        $fieldset = element.attributes._view.$el;
        assert.equal($fieldset.find('input[name="' + fld + '_date"]').attr('type'), 'date');
      });
      nativetime.forEach(function (fld) {
        element = form.getElement(fld);
        $fieldset = element.attributes._view.$el;
        assert.equal($fieldset.find('input[name="' + fld + '_time"]').attr('type'), 'time');
      });
    });

    ['_date', '_time'].forEach(function (subtype) {
      var elements = subtype === '_date' ? pickadate : pickatime;

      elements.forEach(function (fld) {
        test('pickerElements outside page: ' + fld, function (done) {
          var form = Forms.current;

          this.timeout(3e3);

          setTimeout(function () {
            var element = form.getElement(fld);
            var $fieldset = element.attributes._view.$el;
            var $input = $fieldset.find('input[name="' + fld + subtype + '"]');
            var $root = $('#' + $input.attr('id') + '_root.picker');

            assert.equal($input.hasClass('picker__input'), true);
            assert.equal($root.length, 1);
            assert.equal($root.parent().hasClass('ui-body-c'), true);

            assert(!!element);

            done();
          }, 300);
        });
      });
    });

    suite('readonly, hidden', function () {
      test('check if readonly fields are actually readonly', function () {
        var form = Forms.current,
          element,
          $fieldset;

        readonly.forEach(function (fld) {
          element = form.getElement(fld);
          $fieldset = element.attributes._view.$el;
          // should not contain any input fields
          assert.equal($fieldset.find('input').length, 0);
          assert.equal(element.attributes.picker, 'shown');
          assert(element.attributes.readonly);
        });
      });

      test('check if hidden fields are actually hidden', function () {
        var form = Forms.current,
          element,
          $fieldset;
        hidden.forEach(function (fld) {
          element = form.getElement(fld);
          $fieldset = element.attributes._view.$el;
          // should have display: none set in stylesheet
          assert.equal($fieldset.css('display'), 'none');
          assert.equal(element.attributes.picker, 'hidden');
          assert(element.attributes.hidden);
        });
      });

      defineModelToViewTests();
      defineViewToModelTests();
    });

    suite('native, date assignment', function () {
      var native = [
        // 'dateTimeNativePickerNone',
        'dateTimeNativePickerNow',
        'dateTimeNativePickerNowP',
        'dateTimeNativePckerNowPM'
      ];

      test('check if date fields have correct values', function () {
        var form = Forms.current,
          element,
          $fieldset,
          $input;

        native.forEach(function (fld) {
          element = form.getElement(fld);
          $fieldset = element.attributes._view.$el;
          // should contain date field
          $input = $fieldset.find("input[type='date']");
          assert.equal($input.length, 1);
          assert($input.val(), 'no value assigned to $input');
        });
      });
      defineModelToViewTests();
      defineViewToModelTests();
    });

    suite('picker value assignment', function () {
      var pickadates = [
        'datenonative',
        'datetimenonative',
        'datenow',
        'datefromnowplus',
        'datefromdate',
        'datetimenow',
        'dateTimeHiddenNone',
        'dateTimeHiddenNow',
        'dateTimeHiddenNowP',
        'dateTimeHddenNowPM',
        'dateTimePickerNone',
        'dateTimePickerNow',
        'dateTimePickerNowP',
        'dateTimePckerNowPM'
      ];

      test('element value assignment reflected in picker, model, view ', function (done) {
        setTimeout(function () {
          pickadates.forEach(function (fld) {
            var form = Forms.current;
            var el = form.getElement(fld);
            var type = el.get('type');
            var nowdate = [
              'datenow',
              'datefromnowplus',
              'dateTimeHiddenNow'
            ];
            var date$ = el.get('_view').$el.find('input[name$=_date]');
            var picker = date$.pickadate('picker');
            var dateFormat = el.mapDateFormats[el.attributes.dateFormat] || 'YYYY-MM-DD';
            var expected, date;

            if (type !== 'time') {
              if (_.indexOf(nowdate, fld) > -1) {
                date = moment().format('YYYY-MM-DD');
              } else if (fld === 'datefromdate') {
                date = '2014-03-10';
              } else {
                date = '2015-06-10';
              }
              el.val(date);
              expected = moment(date).format(dateFormat);

              assert.equal(date$.val(), expected, fld + ': DOM date value');
              assert.equal(el.get('_date'), date, fld + ': _date value');
              if (type === 'datetime') {
                assert.equal(el.val().split('T')[0], el.get('_date'), fld + ': element value');
              } else {
                assert.equal(el.val(), el.get('_date'), fld + ': element value');
              }
              if (picker) {
                assert.equal(picker.get('select', 'yyyy-mm-dd'), el.get('_date'), fld + ': picker value');
              }
            }
          });
          done();
        }, 1e3);
      });
    });

    suite('time assignment', function () {
      suiteSetup(function () {
        Forms.current.get('elements').forEach(function (el) {
          var type = el.get('type');
          var value = '13:45';
          if (type !== 'time') {
            return;
          }
          el.val(value);
        });
      });

      defineModelToViewTests();
      defineViewToModelTests();
    });

    suite('model view binding test', function () {
      var form = Forms.current;
      var timeformat;
      var dateformat;
      var expected;
      var values = {
        date: '2015-12-25',
        datetime: '2015-12-25T13:50',
        time: '18:33'
      };
      var nowdate = [
        'datenow',
        'datefromnowplus',
        'dateTimeHiddenNow'
      ];
      test('assigning value to model', function () {
        Forms.current.get('elements').forEach(function (el) {
          var name = el.get('name');
          var type = el.get('type');
          if (type === 'hidden') {
            return;
          }
          if (_.indexOf(nowdate, name) > -1) {
            value = moment().format('YYYY-MM-DD');
          } else if (name === 'datefromdate') {
            value = '2014-03-10';
          } else {
            value = values[type];
          }
          if (!el.get('readonly')) {
            el.val(value);
          }

        });
      });

      defineModelToViewTests();

      test('clearing value of model', function () {
        Forms.current.get('elements').forEach(function (el) {
          var type = el.get('type');

          if (type === 'hidden') {
            return;
          }
          if (!el.get('readonly')) {
            el.val('');
          }
        });
      });

      defineModelToViewTests();
      defineViewToModelTests();
      clearFields();
    });
  }); // END: suite('Form', ...)
});
<commit_msg>Forms-237 datetime/time with picker/native has broken model-view binding<commit_after>define(['BlinkForms', 'testUtils', 'underscore', 'moment', 'BIC'], function (Forms, testUtils, _, moment) {
  var nativedate = [
      'date',
      'datetime'
    ], nativetime = [
      'time',
      'datetime'
    ], pickadate = [
      'datenow',
      'datefromnowplus',
      'datefromdate',
      'datetimenow',
      'date column'
    ], pickatime = [
      'timenow',
      'timenowplus',
      'timenowtime',
      'datetimenow',
      'time column'
    ], readonly = [
      'dateTimeRonlyNone',
      'dateTimeRonlyNow',
      'dateTimeRonlyNowP',
      'dateTimeRonlyNowPM'
    ], hidden = [
      'dateTimeHiddenNone',
      'dateTimeHiddenNow',
      'dateTimeHiddenNowP',
      'dateTimeHddenNowPM'
    ];

  function defineModelToViewTests () {
    test('model->value', function (done) {
      setTimeout(function () {
        Forms.current.get('elements').forEach(function (el) {
          var name = el.get('name');
          var time = el.get('_time');
          var date = el.get('_date');
          var type = el.get('type');
          var time$, date$, format;
          if (type === 'hidden') {
            return;
          }
          time$ = el.get('_view').$el.find('input[name$=_time]');
          date$ = el.get('_view').$el.find('input[name$=_date]');
          if (!el.get('readonly')) {
            if (type !== 'time') {
              format = el.mapDateFormats[el.attributes.dateFormat] || 'YYYY-MM-DD';
              assert.lengthOf(date$, 1, name + ': has DOM date');
              if (date$.val() && date) {
                assert.equal(date$.val(), moment(date, 'YYYY-MM-DD').format(format), name + ': DOM date value');
              }
            }
            if (type !== 'date') {
              assert.lengthOf(time$, 1, name + ': has DOM time');
              assert.equal(time$.val(), time, name + ': DOM time value');
            }
          }
        });
        done();
      }, 1e3);
    });
  }

  function defineViewToModelTests () {
    test('view->model', function (done) {
      setTimeout(function () {
        Forms.current.get('elements').forEach(function (el) {
          var date;
          var time;
          var value = el.val();
          var name = el.get('name');
          var type = el.get('type');
          var time$, date$, format, result;
          var nowdate = [
            'datenow',
            'datefromnowplus',
            'dateTimeHiddenNow'
          ];

          if (type === 'hidden') {
            return;
          }
          time$ = el.get('_view').$el.find('input[name$=_time]');
          date$ = el.get('_view').$el.find('input[name$=_date]');
          if (value && !el.get('readonly')) {
            if (type !== 'time') {
              format = el.mapDateFormats[el.attributes.dateFormat] || 'YYYY-MM-DD';
              if (_.indexOf(nowdate, name) > -1) {
                date = moment().format('YYYY-MM-DD');
              } else if (name === 'datefromdate') {
                date = '2014-03-10';
              } else {
                date = '2015-06-10';
              }
              result = moment(date).format(format);
              date$.val(result);
              date$.trigger('change');
              assert.lengthOf(date$, 1, name + ': has DOM date');
              assert.equal(date$.val(), result, name + ': DOM date value');
              assert.equal(date, el.get('_date'), name + ': _date value');
            }
            if (type !== 'date') {
              time = '10:00';
              time$.val(time);
              time$.trigger('change');
              assert.lengthOf(time$, 1, name + ': has DOM time');
              assert.equal(time$.val(), time, name + ': DOM time value');
              assert.equal(time$.val(), el.get('_time'), name + ': _time value');
            }
          }
        });
        done();
      }, 1e3);
    });
  }

  function clearFields () {
    test('clear view', function (done) {
      setTimeout(function () {
        Forms.current.get('elements').forEach(function (el) {
          var value = '';
          var name = el.get('name');
          var type = el.get('type');
          var time$;
          var date$;

          if (type === 'hidden') {
            return;
          }
          time$ = el.get('_view').$el.find('input[name$=_time]');
          date$ = el.get('_view').$el.find('input[name$=_date]');

          if (!el.get('readonly')) {
            if (type !== 'time') {
              date$.val(value);
              date$.trigger('change');
              assert.lengthOf(date$, 1, name + ': has DOM date');
              assert.equal(date$.val(), el.get('_date'), name + ': DOM date value');
            }
            if (type !== 'date') {
              time$.val(value);
              time$.trigger('change');
              assert.lengthOf(time$, 1, name + ': has DOM time');
              assert.equal(time$.val(), el.get('_time'), name + ': DOM time value');
            }
          }
        });
        done();
      }, 1e3);
    });
  }

  testUtils.defineFormLoadSuite('form1', 'add');

  suite('1: date/time', function () {
    test('all now and now_plus models have non-empty values', function () {
      Forms.current.get('elements').forEach(function (el) {
        var defaultValue = el.get('defaultValue');
        if (defaultValue && defaultValue.indexOf('now') !== -1) {
          assert.ok(el.val(), el.get('name') + ': non-empty');
        }
      });
    });

    test('all models with no default have non-empty values', function () {
      Forms.current.get('elements').forEach(function (el) {
        var defaultValue = el.get('defaultValue');
        if (!defaultValue) {
          assert.notOk(el.val(), el.get('name') + ': empty');
        }
      });
    });

    defineModelToViewTests();
    defineViewToModelTests();

    test('use datetime field - avoid undefined', function () {
      var form = Forms.current,
        element = form.getElement('datetimenonative'),
        $fieldset = element.attributes._view.$el,
        date = $fieldset.find('input[name="datetimenonative_date"]'),
        time = $fieldset.find('input[name="datetimenonative_time"]');

      date.val('2014-02-01').change();
      assert.equal(element.val(), '2014-02-01T00:00');

      date.val('').change();
      time.val('').change();
      time.val('10:11').change();
      assert.equal(element.val(), '0000-00-00T10:11');
    });

    test('use datetime field - organise elements properly on screen', function () {
      var form = Forms.current,
        element = form.getElement('datetimenonative'),
        $fieldset = element.attributes._view.$el.children(),
        elementsInOrder = [
          'LABEL',
          'DIV',
          'LABEL',
          'DIV'
        ];

      elementsInOrder.forEach(function (v, k) {
        assert.equal($($fieldset[k]).prop('tagName'), v);
      });
    });

    test('use native picker defaults to native picker', function () {
      var form = Forms.current,
        element,
        $fieldset;
      nativedate.forEach(function (fld) {
        element = form.getElement(fld);
        $fieldset = element.attributes._view.$el;
        assert.equal($fieldset.find('input[name="' + fld + '_date"]').attr('type'), 'date');
      });
      nativetime.forEach(function (fld) {
        element = form.getElement(fld);
        $fieldset = element.attributes._view.$el;
        assert.equal($fieldset.find('input[name="' + fld + '_time"]').attr('type'), 'time');
      });
    });

    ['_date', '_time'].forEach(function (subtype) {
      var elements = subtype === '_date' ? pickadate : pickatime;

      elements.forEach(function (fld) {
        test('pickerElements outside page: ' + fld, function (done) {
          var form = Forms.current;

          this.timeout(3e3);

          setTimeout(function () {
            var element = form.getElement(fld);
            var $fieldset = element.attributes._view.$el;
            var $input = $fieldset.find('input[name="' + fld + subtype + '"]');
            var $root = $('#' + $input.attr('id') + '_root.picker');

            assert.equal($input.hasClass('picker__input'), true);
            assert.equal($root.length, 1);
            assert.equal($root.parent().hasClass('ui-body-c'), true);

            assert(!!element);

            done();
          }, 300);
        });
      });
    });

    suite('readonly, hidden', function () {
      test('check if readonly fields are actually readonly', function () {
        var form = Forms.current,
          element,
          $fieldset;

        readonly.forEach(function (fld) {
          element = form.getElement(fld);
          $fieldset = element.attributes._view.$el;
          // should not contain any input fields
          assert.equal($fieldset.find('input').length, 0);
          assert.equal(element.attributes.picker, 'shown');
          assert(element.attributes.readonly);
        });
      });

      test('check if hidden fields are actually hidden', function () {
        var form = Forms.current,
          element,
          $fieldset;
        hidden.forEach(function (fld) {
          element = form.getElement(fld);
          $fieldset = element.attributes._view.$el;
          // should have display: none set in stylesheet
          assert.equal($fieldset.css('display'), 'none');
          assert.equal(element.attributes.picker, 'hidden');
          assert(element.attributes.hidden);
        });
      });

      defineModelToViewTests();
      defineViewToModelTests();
    });

    suite('native, date assignment', function () {
      var native = [
        // 'dateTimeNativePickerNone',
        'dateTimeNativePickerNow',
        'dateTimeNativePickerNowP',
        'dateTimeNativePckerNowPM'
      ];

      test('check if date fields have correct values', function () {
        var form = Forms.current,
          element,
          $fieldset,
          $input;

        native.forEach(function (fld) {
          element = form.getElement(fld);
          $fieldset = element.attributes._view.$el;
          // should contain date field
          $input = $fieldset.find("input[type='date']");
          assert.equal($input.length, 1);
          assert($input.val(), 'no value assigned to $input');
        });
      });
      defineModelToViewTests();
      defineViewToModelTests();
    });

    suite('picker value assignment', function () {
      var pickadates = [
        'datenonative',
        'datetimenonative',
        'datenow',
        'datefromnowplus',
        'datefromdate',
        'datetimenow',
        'dateTimeHiddenNone',
        'dateTimeHiddenNow',
        'dateTimeHiddenNowP',
        'dateTimeHddenNowPM',
        'dateTimePickerNone',
        'dateTimePickerNow',
        'dateTimePickerNowP',
        'dateTimePckerNowPM'
      ];

      test('element value assignment reflected in picker, model, view ', function (done) {
        setTimeout(function () {
          pickadates.forEach(function (fld) {
            var form = Forms.current;
            var el = form.getElement(fld);
            var type = el.get('type');
            var nowdate = [
              'datenow',
              'datefromnowplus',
              'dateTimeHiddenNow'
            ];
            var date$ = el.get('_view').$el.find('input[name$=_date]');
            var picker = date$.pickadate('picker');
            var dateFormat = el.mapDateFormats[el.attributes.dateFormat] || 'YYYY-MM-DD';
            var expected, date;

            if (type !== 'time') {
              if (_.indexOf(nowdate, fld) > -1) {
                date = moment().format('YYYY-MM-DD');
              } else if (fld === 'datefromdate') {
                date = '2014-03-10';
              } else {
                date = '2015-06-10';
              }
              el.val(date);
              expected = moment(date).format(dateFormat);

              assert.equal(date$.val(), expected, fld + ': DOM date value');
              assert.equal(el.get('_date'), date, fld + ': _date value');
              if (type === 'datetime') {
                assert.equal(el.val().split('T')[0], el.get('_date'), fld + ': element value');
              } else {
                assert.equal(el.val(), el.get('_date'), fld + ': element value');
              }
              if (picker) {
                assert.equal(picker.get('select', 'yyyy-mm-dd'), el.get('_date'), fld + ': picker value');
              }
            }
          });
          done();
        }, 1e3);
      });
    });

    suite('time assignment', function () {
      suiteSetup(function () {
        Forms.current.get('elements').forEach(function (el) {
          var type = el.get('type');
          var value = '13:45';
          if (type !== 'time') {
            return;
          }
          el.val(value);
        });
      });

      defineModelToViewTests();
      defineViewToModelTests();
    });

    suite('model view binding test', function () {
      var value;
      var values = {
        date: '2015-12-25',
        datetime: '2015-12-25T13:50',
        time: '18:33'
      };
      var nowdate = [
        'datenow',
        'datefromnowplus',
        'dateTimeHiddenNow'
      ];
      test('assigning value to model', function () {
        Forms.current.get('elements').forEach(function (el) {
          var name = el.get('name');
          var type = el.get('type');
          if (type === 'hidden') {
            return;
          }
          if (_.indexOf(nowdate, name) > -1) {
            value = moment().format('YYYY-MM-DD');
          } else if (name === 'datefromdate') {
            value = '2014-03-10';
          } else {
            value = values[type];
          }
          if (!el.get('readonly')) {
            el.val(value);
          }

        });
      });

      defineModelToViewTests();
      test('clearing value of model', function () {
        Forms.current.get('elements').forEach(function (el) {
          var type = el.get('type');

          if (type === 'hidden') {
            return;
          }
          if (!el.get('readonly')) {
            el.val('');
          }
        });
      });

      defineModelToViewTests();
      defineViewToModelTests();
      clearFields();
    });
  }); // END: suite('Form', ...)
});
<|endoftext|>


<commit_before>var scenario = 'm';
var routingProfile = 'truck40t.xml';
var enableRestrictionZones = true;
var enableTruckAttributes = true;
var enableTrafficIncidents = false;
var enableSpeedPatterns = false;

var baseLayers;
var routingControl;

// initialize the map
var map = L.map('map', {
	fullscreenControl: true,
	fullscreenControlOptions: {
		fullscreenElement: document.getElementById('map-container').parentNode // needed for sidebar!
	},
	contextmenu: true,
	contextmenuWidth: 200,
	contextmenuItems: [{
		text: 'Add Waypoint At Start',
		callback: function (ev) {
			if (routingControl._plan._waypoints[0].latLng) {
				routingControl.spliceWaypoints(0, 0, ev.latlng);
			} else {
				routingControl.spliceWaypoints(0, 1, ev.latlng);
			}
		}
	}, {
		text: 'Add Waypoint At End',
		callback: function (ev) {
			if (routingControl._plan._waypoints[routingControl._plan._waypoints.length - 1].latLng) {
				routingControl.spliceWaypoints(routingControl._plan._waypoints.length, 0, ev.latlng);
			} else {
				routingControl.spliceWaypoints(routingControl._plan._waypoints.length - 1, 1, ev.latlng);
			}
		}
	}]
});

// create a new pane for the overlay tiles
map.createPane('tileOverlayPane');
map.getPane('tileOverlayPane').style.zIndex = 500;
map.getPane('tileOverlayPane').style.pointerEvents = 'none';

// get the start and end coordinates for a scenario
var getPlan = function () {
	switch (scenario) {
		case 'm':
			{
				return [
					L.latLng(48.10032397915225, 11.547317504882812),
					L.latLng(48.167001359708934, 11.602249145507814)
				];
			}
		case 'hh':
			{
				return [
					L.latLng(53.55145062603612, 9.934816360473632),
					L.latLng(53.52796226132062, 9.84975814819336)
				];
			}
		case 'na':
			{
				return [
					L.latLng(40.71454, -74.00711),
					L.latLng(42.35867, -71.05672)
				];
			}
		case 'au':
			{
				return [
					L.latLng(-33.86959, 151.20694),
					L.latLng(-35.3065, 149.12659)
				];
			}
		default:
			{ // 'eu'
				return [
					L.latLng(48.8588, 2.3469),
					L.latLng(52.3546, 4.9039)
				];
			}
	}
};

// returns a layer group for xmap back- and foreground layers
var getXMapBaseLayers = function (style) {
	var bg = L.tileLayer('https://s0{s}-xserver2-europe-test.cloud.ptvgroup.com/services/rest/XMap/tile/{z}/{x}/{y}?storedProfile={profile}&layers=background,transport'
			+ '&xtok={token}', {
				profile: style,
				token: token,
				attribution: 'PTV, HERE',
				maxZoom: 22,
				subdomains: '1234'
			});

	var fg = L.tileLayer.xserver('https://s0{s}-xserver2-europe-test.cloud.ptvgroup.com/services/rest/XMap/tile/{z}/{x}/{y}?storedProfile={profile}&layers=labels,{vl1}{vl2}{vl3}{vl4}&contentType=JSON'
		+ '&userLanguage={userLanguage}'
		+ '&timeConsideration={timeConsideration}'
		+ '&referenceTime={referenceTime}&timeSpan={timeSpan}'
		+ '&showOnlyRelevantByTime={showOnlyRelevantByTime}&xtok={token}', {
			profile: style,
			token: token,
			attribution: 'PTV, HERE',
			maxZoom: 22,
			subdomains: '1234',
			timeConsideration: 'SNAPSHOT',
			referenceTime: '2018-01-09T15%3A00%3A00%2B01%3A00',
			timeSpan: 172800,
			showOnlyRelevantByTime: 'false',
			userLanguage: 'de',
			pane: 'tileOverlayPane',
			zIndex: 1,
			isVirtualHost: true,
			vl1: '',
			vl2: '',
			vl3: '',
			vl4: ''
		});

	return L.layerGroup([bg, fg]);
}

var initializeRoutingControl = function () {
	routingControl = L.Routing.control({
	plan: L.Routing.plan(getPlan(), {
		routeWhileDragging: false,
		routeDragInterval: 3000,
		createMarker: function (i, wp) {
			return L.marker(wp.latLng, {
				draggable: true,
				icon: L.icon.glyph({
					glyph: String.fromCharCode(65 + i)
				})
			});
		},
		geocoder: L.Control.Geocoder.ptv({
			serviceUrl: 'https://xserver2-europe-test.cloud.ptvgroup.com/services/rest/XLocate/locations/',
			token: token
		}),
		reverseWaypoints: true
	}),
	lineOptions: {
		styles: [
			// Shadow
			{
				color: 'grey',
				opacity: 0.8,
				weight: 15
			},
			// Outline
			{
				color: 'white',
				opacity: 1,
				weight: 9
			},
			// Center
			{
				color: 'lightblue',
				opacity: 1,
				weight: 6
			}
		]
	},
	router: L.Routing.ptv({
		serviceUrl: 'https://xserver2-europe-test.cloud.ptvgroup.com/services/rs/XRoute/',
		token: token,
		supportsHeadings: true,
		beforeSend: function (request) {
			var blub = $('#reference-date').val() + 'T' + $('#reference-time').val() +  $('#time-zone').val();
			request.storedProfile = routingProfile;
			request.routeOptions = {
				'timeConsideration': {
					'$type': 'SnapshotTimeConsideration',
					'referenceTime': $('#reference-date').val() + 'T' + $('#reference-time').val() +  $('#time-zone').val()
				}
			}
			request.requestProfile = {
				'featureLayerProfile': {
					'themes': [{
						'enabled': enableTruckAttributes,
						'id': 'PTV_TruckAttributes'
					}, {
						'enabled': enableSpeedPatterns,
						'id': 'PTV_SpeedPatterns'
					}, {
						'enabled': enableTrafficIncidents,
						'id': 'PTV_TrafficIncidents'
					}, {
						'enabled': enableRestrictionZones,
						'id': 'PTV_RestrictionZones'
					}]
				}
			};
			return request;
		}
	}),
	collapsible: true,
	routeWhileDragging: false,
	routeDragInterval: 3000,
	formatter: new L.Routing.Formatter({
		roundingSensitivity: 1000
	})
}).addTo(map);

	routingControl.on('routingerror', function (e) {});

	L.Routing.errorControl(routingControl).addTo(map);
};

// update ui
$('#scenarioSelect').val(scenario);
$('#routingProfile').val(routingProfile);
$('#enableRestrictionZones').prop('checked', enableRestrictionZones);
$('#enableTruckAttributes').prop('checked', enableTruckAttributes);
$('#enableSpeedPatterns').prop('checked', enableSpeedPatterns);
$('#enableTrafficIncidents').prop('checked', enableTrafficIncidents);

// add side bar
var sidebar = L.control.sidebar('sidebar').addTo(map);
sidebar.open('home');

// add scale control
L.control.scale().addTo(map);

var baseLayers = {
	'PTV gravelpit': getXMapBaseLayers('gravelpit'),
	'PTV sandbox': getXMapBaseLayers('sandbox'),
	'PTV silkysand': getXMapBaseLayers('silkysand').addTo(map)
};

var truckAttributesLayer = L.virtualLayer('PTV_TruckAttributes,', 'vl1');
var restrictionZonesLayer = L.virtualLayer('PTV_RestrictionZones,', 'vl2');
var trafficIncidentsLayer = L.virtualLayer('PTV_TrafficIncidents,', 'vl3');
var speedPatternsLayer = L.virtualLayer('PTV_SpeedPatterns,', 'vl4');

if (enableTruckAttributes)
	map.addLayer(truckAttributesLayer);
if (enableTrafficIncidents)
	map.addLayer(trafficIncidentsLayer);
if (enableRestrictionZones)
	map.addLayer(restrictionZonesLayer);
if (enableSpeedPatterns)
	map.addLayer(speedPatternsLayer);

L.control.layers(baseLayers, {
	'Truck Attributes': truckAttributesLayer,
	'Restriction Zones': restrictionZonesLayer,
	'Traffic Incidents': trafficIncidentsLayer,
	'Speed Patterns': speedPatternsLayer
}, {
	position: 'bottomleft',
	autoZIndex: false
}).addTo(map);

var indSelf = false;

var _onLayerAdd = function (e) {
	if (indSelf) // event was triggered by panel
		return;

	if (e.layer === truckAttributesLayer) {
		enableTruckAttributes = true;
		$('#enableTruckAttributes').prop('checked', enableTruckAttributes);
	} else if (e.layer === trafficIncidentsLayer) {
		enableTrafficIncidents = true;
		$('#enableTrafficIncidents').prop('checked', enableTrafficIncidents);
	} else if (e.layer === speedPatternsLayer) {
		enableSpeedPatterns = true;
		$('#enableSpeedPatterns').prop('checked', enableSpeedPatterns);
	} else if (e.layer === restrictionZonesLayer) {
		enableRestrictionZones = true;
		$('#enableRestrictionZones').prop('checked', enableRestrictionZones);
	} else return;

	if(routingControl)
		routingControl.route();
};

var _onLayerRemove = function (e) {
	if (indSelf) // event was triggered by panel
		return;

	if (e.layer === truckAttributesLayer) {
		enableTruckAttributes = false;
		$('#enableTruckAttributes').prop('checked', enableTruckAttributes);
	} else if (e.layer === trafficIncidentsLayer) {
		enableTrafficIncidents = false;
		$('#enableTrafficIncidents').prop('checked', enableTrafficIncidents);
	} else if (e.layer === speedPatternsLayer) {
		enableSpeedPatterns = false;
		$('#enableSpeedPatterns').prop('checked', enableSpeedPatterns);
	} else if (e.layer === restrictionZonesLayer) {
		enableRestrictionZones = false;
		$('#enableRestrictionZones').prop('checked', enableRestrictionZones);
	} else return;

	if(routingControl)
		routingControl.route();
};

var _onMapLoad = function (e) {
	new L.Control.ReferenceTimeControl().onAdd(map);
	initializeRoutingControl();
};

map.on('layeradd', _onLayerAdd, this);
map.on('layerremove', _onLayerRemove, this);

map.on('load', _onMapLoad, this);
map.setView([0,0], 1);

// update the map scenario
var updateScenario = function () {
	scenario = $('#scenarioSelect option:selected').val();
	updateParams(true);
};

// update the routing params
var updateParams = function (updateWayPoints) {
	routingProfile = $('#vehicleType').val();

	enableRestrictionZones = $('#enableRestrictionZones').is(':checked');
	enableTruckAttributes = $('#enableTruckAttributes').is(':checked');
	enableTrafficIncidents = $('#enableTrafficIncidents').is(':checked');
	enableSpeedPatterns = $('#enableSpeedPatterns').is(':checked');

	// sync panel->layers
	indSelf = true;

	if (enableTruckAttributes)
		map.addLayer(truckAttributesLayer);
	else
		map.removeLayer(truckAttributesLayer);

	if (enableTrafficIncidents)
		map.addLayer(trafficIncidentsLayer);
	else
		map.removeLayer(trafficIncidentsLayer);

	if (enableSpeedPatterns)
		map.addLayer(speedPatternsLayer);
	else
		map.removeLayer(speedPatternsLayer);

	if (enableRestrictionZones)
		map.addLayer(restrictionZonesLayer);
	else
		map.removeLayer(restrictionZonesLayer);

	indSelf = false;

	if (updateWayPoints)
		routingControl.setWaypoints(getPlan());
	routingControl._router.options.numberOfAlternatives = 0;
	routingControl.route();
};

<commit_msg>using gravelpit<commit_after>var scenario = 'm';
var routingProfile = 'truck40t.xml';
var enableRestrictionZones = true;
var enableTruckAttributes = true;
var enableTrafficIncidents = false;
var enableSpeedPatterns = false;

var baseLayers;
var routingControl;

// initialize the map
var map = L.map('map', {
	fullscreenControl: true,
	fullscreenControlOptions: {
		fullscreenElement: document.getElementById('map-container').parentNode // needed for sidebar!
	},
	contextmenu: true,
	contextmenuWidth: 200,
	contextmenuItems: [{
		text: 'Add Waypoint At Start',
		callback: function (ev) {
			if (routingControl._plan._waypoints[0].latLng) {
				routingControl.spliceWaypoints(0, 0, ev.latlng);
			} else {
				routingControl.spliceWaypoints(0, 1, ev.latlng);
			}
		}
	}, {
		text: 'Add Waypoint At End',
		callback: function (ev) {
			if (routingControl._plan._waypoints[routingControl._plan._waypoints.length - 1].latLng) {
				routingControl.spliceWaypoints(routingControl._plan._waypoints.length, 0, ev.latlng);
			} else {
				routingControl.spliceWaypoints(routingControl._plan._waypoints.length - 1, 1, ev.latlng);
			}
		}
	}]
});

// create a new pane for the overlay tiles
map.createPane('tileOverlayPane');
map.getPane('tileOverlayPane').style.zIndex = 500;
map.getPane('tileOverlayPane').style.pointerEvents = 'none';

// get the start and end coordinates for a scenario
var getPlan = function () {
	switch (scenario) {
		case 'm':
			{
				return [
					L.latLng(48.10032397915225, 11.547317504882812),
					L.latLng(48.167001359708934, 11.602249145507814)
				];
			}
		case 'hh':
			{
				return [
					L.latLng(53.55145062603612, 9.934816360473632),
					L.latLng(53.52796226132062, 9.84975814819336)
				];
			}
		case 'na':
			{
				return [
					L.latLng(40.71454, -74.00711),
					L.latLng(42.35867, -71.05672)
				];
			}
		case 'au':
			{
				return [
					L.latLng(-33.86959, 151.20694),
					L.latLng(-35.3065, 149.12659)
				];
			}
		default:
			{ // 'eu'
				return [
					L.latLng(48.8588, 2.3469),
					L.latLng(52.3546, 4.9039)
				];
			}
	}
};

// returns a layer group for xmap back- and foreground layers
var getXMapBaseLayers = function (style) {
	var bg = L.tileLayer('https://s0{s}-xserver2-europe-test.cloud.ptvgroup.com/services/rest/XMap/tile/{z}/{x}/{y}?storedProfile={profile}&layers=background,transport'
			+ '&xtok={token}', {
				profile: style,
				token: token,
				attribution: 'PTV, HERE',
				maxZoom: 22,
				subdomains: '1234'
			});

	var fg = L.tileLayer.xserver('https://s0{s}-xserver2-europe-test.cloud.ptvgroup.com/services/rest/XMap/tile/{z}/{x}/{y}?storedProfile={profile}&layers=labels,{vl1}{vl2}{vl3}{vl4}&contentType=JSON'
		+ '&userLanguage={userLanguage}'
		+ '&timeConsideration={timeConsideration}'
		+ '&referenceTime={referenceTime}&timeSpan={timeSpan}'
		+ '&showOnlyRelevantByTime={showOnlyRelevantByTime}&xtok={token}', {
			profile: style,
			token: token,
			attribution: 'PTV, HERE',
			maxZoom: 22,
			subdomains: '1234',
			timeConsideration: 'SNAPSHOT',
			referenceTime: '2018-01-09T15%3A00%3A00%2B01%3A00',
			timeSpan: 172800,
			showOnlyRelevantByTime: 'false',
			userLanguage: 'de',
			pane: 'tileOverlayPane',
			zIndex: 1,
			isVirtualHost: true,
			vl1: '',
			vl2: '',
			vl3: '',
			vl4: ''
		});

	return L.layerGroup([bg, fg]);
}

var initializeRoutingControl = function () {
	routingControl = L.Routing.control({
	plan: L.Routing.plan(getPlan(), {
		routeWhileDragging: false,
		routeDragInterval: 3000,
		createMarker: function (i, wp) {
			return L.marker(wp.latLng, {
				draggable: true,
				icon: L.icon.glyph({
					glyph: String.fromCharCode(65 + i)
				})
			});
		},
		geocoder: L.Control.Geocoder.ptv({
			serviceUrl: 'https://xserver2-europe-test.cloud.ptvgroup.com/services/rest/XLocate/locations/',
			token: token
		}),
		reverseWaypoints: true
	}),
	lineOptions: {
		styles: [
			// Shadow
			{
				color: 'grey',
				opacity: 0.8,
				weight: 15
			},
			// Outline
			{
				color: 'white',
				opacity: 1,
				weight: 9
			},
			// Center
			{
				color: 'lightblue',
				opacity: 1,
				weight: 6
			}
		]
	},
	router: L.Routing.ptv({
		serviceUrl: 'https://xserver2-europe-test.cloud.ptvgroup.com/services/rs/XRoute/',
		token: token,
		supportsHeadings: true,
		beforeSend: function (request) {
			var blub = $('#reference-date').val() + 'T' + $('#reference-time').val() +  $('#time-zone').val();
			request.storedProfile = routingProfile;
			request.routeOptions = {
				'timeConsideration': {
					'$type': 'SnapshotTimeConsideration',
					'referenceTime': $('#reference-date').val() + 'T' + $('#reference-time').val() +  $('#time-zone').val()
				}
			}
			request.requestProfile = {
				'featureLayerProfile': {
					'themes': [{
						'enabled': enableTruckAttributes,
						'id': 'PTV_TruckAttributes'
					}, {
						'enabled': enableSpeedPatterns,
						'id': 'PTV_SpeedPatterns'
					}, {
						'enabled': enableTrafficIncidents,
						'id': 'PTV_TrafficIncidents'
					}, {
						'enabled': enableRestrictionZones,
						'id': 'PTV_RestrictionZones'
					}]
				}
			};
			return request;
		}
	}),
	collapsible: true,
	routeWhileDragging: false,
	routeDragInterval: 3000,
	formatter: new L.Routing.Formatter({
		roundingSensitivity: 1000
	})
}).addTo(map);

	routingControl.on('routingerror', function (e) {});

	L.Routing.errorControl(routingControl).addTo(map);
};

// update ui
$('#scenarioSelect').val(scenario);
$('#routingProfile').val(routingProfile);
$('#enableRestrictionZones').prop('checked', enableRestrictionZones);
$('#enableTruckAttributes').prop('checked', enableTruckAttributes);
$('#enableSpeedPatterns').prop('checked', enableSpeedPatterns);
$('#enableTrafficIncidents').prop('checked', enableTrafficIncidents);

// add side bar
var sidebar = L.control.sidebar('sidebar').addTo(map);
sidebar.open('home');

// add scale control
L.control.scale().addTo(map);

var baseLayers = {
	'PTV gravelpit': getXMapBaseLayers('gravelpit').addTo(map),
	'PTV sandbox': getXMapBaseLayers('sandbox'),
	'PTV silkysand': getXMapBaseLayers('silkysand')
};

var truckAttributesLayer = L.virtualLayer('PTV_TruckAttributes,', 'vl1');
var restrictionZonesLayer = L.virtualLayer('PTV_RestrictionZones,', 'vl2');
var trafficIncidentsLayer = L.virtualLayer('PTV_TrafficIncidents,', 'vl3');
var speedPatternsLayer = L.virtualLayer('PTV_SpeedPatterns,', 'vl4');

if (enableTruckAttributes)
	map.addLayer(truckAttributesLayer);
if (enableTrafficIncidents)
	map.addLayer(trafficIncidentsLayer);
if (enableRestrictionZones)
	map.addLayer(restrictionZonesLayer);
if (enableSpeedPatterns)
	map.addLayer(speedPatternsLayer);

L.control.layers(baseLayers, {
	'Truck Attributes': truckAttributesLayer,
	'Restriction Zones': restrictionZonesLayer,
	'Traffic Incidents': trafficIncidentsLayer,
	'Speed Patterns': speedPatternsLayer
}, {
	position: 'bottomleft',
	autoZIndex: false
}).addTo(map);

var indSelf = false;

var _onLayerAdd = function (e) {
	return; // only one-way sync

	if (indSelf) // event was triggered by panel
		return;

	if (e.layer === truckAttributesLayer) {
		enableTruckAttributes = true;
		$('#enableTruckAttributes').prop('checked', enableTruckAttributes);
	} else if (e.layer === trafficIncidentsLayer) {
		enableTrafficIncidents = true;
		$('#enableTrafficIncidents').prop('checked', enableTrafficIncidents);
	} else if (e.layer === speedPatternsLayer) {
		enableSpeedPatterns = true;
		$('#enableSpeedPatterns').prop('checked', enableSpeedPatterns);
	} else if (e.layer === restrictionZonesLayer) {
		enableRestrictionZones = true;
		$('#enableRestrictionZones').prop('checked', enableRestrictionZones);
	} else return;

	if(routingControl)
		routingControl.route();
};

var _onLayerRemove = function (e) {
	return; // only one-way sync

	if (indSelf) // event was triggered by panel
		return;

	if (e.layer === truckAttributesLayer) {
		enableTruckAttributes = false;
		$('#enableTruckAttributes').prop('checked', enableTruckAttributes);
	} else if (e.layer === trafficIncidentsLayer) {
		enableTrafficIncidents = false;
		$('#enableTrafficIncidents').prop('checked', enableTrafficIncidents);
	} else if (e.layer === speedPatternsLayer) {
		enableSpeedPatterns = false;
		$('#enableSpeedPatterns').prop('checked', enableSpeedPatterns);
	} else if (e.layer === restrictionZonesLayer) {
		enableRestrictionZones = false;
		$('#enableRestrictionZones').prop('checked', enableRestrictionZones);
	} else return;

	if(routingControl)
		routingControl.route();
};

var _onMapLoad = function (e) {
	new L.Control.ReferenceTimeControl().onAdd(map);
	initializeRoutingControl();
};

map.on('layeradd', _onLayerAdd, this);
map.on('layerremove', _onLayerRemove, this);

map.on('load', _onMapLoad, this);
map.setView([0,0], 1);

// update the map scenario
var updateScenario = function () {
	scenario = $('#scenarioSelect option:selected').val();
	updateParams(true);
};

// update the routing params
var updateParams = function (updateWayPoints) {
	routingProfile = $('#vehicleType').val();

	enableRestrictionZones = $('#enableRestrictionZones').is(':checked');
	enableTruckAttributes = $('#enableTruckAttributes').is(':checked');
	enableTrafficIncidents = $('#enableTrafficIncidents').is(':checked');
	enableSpeedPatterns = $('#enableSpeedPatterns').is(':checked');

	// sync panel->layers
	indSelf = true;

	if (enableTruckAttributes)
		map.addLayer(truckAttributesLayer);
	else
		map.removeLayer(truckAttributesLayer);

	if (enableTrafficIncidents)
		map.addLayer(trafficIncidentsLayer);
	else
		map.removeLayer(trafficIncidentsLayer);

	if (enableSpeedPatterns)
		map.addLayer(speedPatternsLayer);
	else
		map.removeLayer(speedPatternsLayer);

	if (enableRestrictionZones)
		map.addLayer(restrictionZonesLayer);
	else
		map.removeLayer(restrictionZonesLayer);

	indSelf = false;

	if (updateWayPoints)
		routingControl.setWaypoints(getPlan());
	routingControl._router.options.numberOfAlternatives = 0;
	routingControl.route();
};

<|endoftext|>


<commit_before>/* global document describe beforeEach afterEach it window sinon */
import { autocomplete } from '../components/autocomplete';

describe('Autocomplete tests', () => {
  let container;
  let inputEl;
  let listEl;
  let listItem1;
  let listItem2;
  let listItem3;
  let outsideElement;

  beforeEach(() => {
    container = document.createElement('div');
    container.setAttribute('data-sprk-autocomplete', 'container');

    inputEl = document.createElement('input');
    inputEl.setAttribute('data-sprk-autocomplete', 'input');

    listEl = document.createElement('ul');
    listEl.setAttribute('data-sprk-autocomplete', 'filtered');
    listEl.setAttribute('data-sprk-autocomplete', 'results');

    listItem1 = document.createElement('li');
    listItem1.setAttribute('data-sprk-autocomplete', 'result');

    listItem2 = document.createElement('li');
    listItem2.setAttribute('data-sprk-autocomplete', 'result');

    listItem3 = document.createElement('li');
    listItem3.setAttribute('data-sprk-autocomplete', 'result');

    listEl.appendChild(listItem1);
    listEl.appendChild(listItem2);
    listEl.appendChild(listItem3);

    container.appendChild(inputEl);
    container.appendChild(listEl);

    outsideElement = document.createElement('button');
    outsideElement.innerHTML = 'Outside Button';
    document.body.appendChild(outsideElement);
    document.body.appendChild(container);

    sinon.spy(document, 'querySelectorAll');
    autocomplete();
  });

  afterEach(() => {
    document.querySelectorAll.restore(); // Unwraps the spy
    document.body.innerHTML = '';
  });

  it('should generate an id if needed', () => {
    // The id on the list, aria-controls on the input, and aria-owns on the
    // input container should all match.
    listEl.removeAttribute('id');
    inputEl.removeAttribute('aria-controls');
    inputEl.parentNode.removeAttribute('aria-owns');
    expect(listEl.getAttribute('id')).toBe(null);
    expect(inputEl.getAttribute('aria-controls')).toBe(null);
    expect(inputEl.parentNode.getAttribute('aria-owns')).toBe(null);
    autocomplete();
    expect(listEl.hasAttribute('id')).toBeTruthy();
    expect(inputEl.hasAttribute('aria-controls')).toBeTruthy();
    expect(inputEl.parentNode.hasAttribute('aria-owns')).toBeTruthy();
    expect(listEl.getAttribute('id')).toEqual(
      inputEl.getAttribute('aria-controls'),
    );
    expect(listEl.getAttribute('id')).toEqual(
      inputEl.parentNode.getAttribute('aria-owns'),
    );
  });

  it('should not generate an id if one is provided', () => {
    const testString = 'test_string';
    listEl.setAttribute('id', testString);
    inputEl.removeAttribute('aria-controls');
    inputEl.parentNode.removeAttribute('aria-owns');
    autocomplete();
    expect(inputEl.getAttribute('aria-controls')).toEqual(testString);
    expect(listEl.id).toEqual(testString);
  });

  it('should set aria-expanded="false" when the list is closed', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    const escKeyEvent = new window.Event('keydown');
    escKeyEvent.keyCode = 27;
    document.dispatchEvent(escKeyEvent);

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
    expect(inputEl.parentNode.getAttribute('aria-expanded')).toEqual('false');
  });

  it('should close the search results if escape is pressed', () => {
    expect(listEL.classList.contains('sprk-u-Display--none')).toBe(false);

    const escKeyEvent = new window.Event('keydown');
    escKeyEvent.keyCode = 27;
    document.dispatchEvent(escKeyEvent);

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
  });

  it('should close the search results if document is clicked', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    document.dispatchEvent(new window.Event('click'));

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
  });

  it('should close the search results if an outside element is clicked', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    outsideElement.click();
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
  });

  it(`it should not close the search results if search
  results are clicked`, () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    listEl.dispatchEvent(new window.Event('click'));

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);
  });

  it('it should not close the search results if input is clicked', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    inputEl.dispatchEvent(new window.Event('click'));
    inputEl.click();
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);
  });

  it('should move visual focus with down arrow', () => {
    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should overflow visual focus with down arrow', () => {
    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);

    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it(`should not move visual focus with down arrow if the
  list is hidden`, () => {
    listEl.classList.add('sprk-u-Display--none');

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);
  });

  it('should move visual focus with up arrow', () => {
    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 38;
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should overflow visual focus with up arrow', () => {
    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 38;
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);

    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should not move visual focus with up arrow if the list is hidden', () => {
    listEl.classList.add('sprk-u-Display--none');

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 38;
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);
  });

  it('should skip non-results when moving visual focus with down arrow', () => {
    listItem2.setAttribute('data-sprk-autocomplete-no-select', 'true');
    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);

    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should call getElements once with the correct selector', () => {
    expect(document.querySelectorAll.getCall(0).args[0]).toBe(
      '[data-sprk-autocomplete="container"]',
    );
  });

  it('should not break if you press other keys', () => {
    const event = new window.Event('keydown');
    event.keyCode = 111;
    inputEl.dispatchEvent(event);

    document.dispatchEvent(event);
  });

  it('should run functions on window resize', () => {
    const spy = jest.fn();
    window.addEventListener('resize', spy);
    const event = new window.Event('resize');
    window.resizeTo(3000, 3000);
    window.dispatchEvent(event);
    expect(spy).toHaveBeenCalled();
  });

  it(`Should set the scrolltop to equal offsetTop if
  listItem top is less than list top`, () => {
    const event = new window.Event('keydown');
    event.keyCode = 40;

    // Set window height & width
    window.innerWidth = 300;
    window.innerHeight = 300;

    // Set the top of the list
    listItem2.parentNode.getBoundingClientRect = () => {
      return { top: 25 };
    };

    // Set the top of the list item
    listItem2.getBoundingClientRect = () => {
      return { top: 24 };
    };
    inputEl.dispatchEvent(event);
    expect(listItem2.parentNode.scrollTop).toEqual(listItem2.offsetTop);
  });

  it(`Should set the scrolltop to equal offsetTop if
  listItem bottom is greater than list bottom`, () => {
    const event = new window.Event('keydown');
    event.keyCode = 40;

    // Set window height & width
    window.innerWidth = 300;
    window.innerHeight = 300;

    // Set the bottom of the list
    listItem2.parentNode.getBoundingClientRect = () => {
      return { bottom: 25 };
    };

    // Set the bottom of the list item
    listItem2.getBoundingClientRect = () => {
      return { bottom: 26 };
    };
    inputEl.dispatchEvent(event);
    expect(listItem2.parentNode.scrollTop).toEqual(listItem2.offsetTop);
  });
});
<commit_msg>rebase<commit_after>/* global document describe beforeEach afterEach it window sinon */
import { autocomplete } from '../components/autocomplete';

describe('Autocomplete tests', () => {
  let container;
  let inputEl;
  let listEl;
  let listItem1;
  let listItem2;
  let listItem3;
  let outsideElement;

  beforeEach(() => {
    container = document.createElement('div');
    container.setAttribute('data-sprk-autocomplete', 'container');

    inputEl = document.createElement('input');
    inputEl.setAttribute('data-sprk-autocomplete', 'input');

    listEl = document.createElement('ul');
    listEl.setAttribute('data-sprk-autocomplete', 'filtered');
    listEl.setAttribute('data-sprk-autocomplete', 'results');

    listItem1 = document.createElement('li');
    listItem1.setAttribute('data-sprk-autocomplete', 'result');

    listItem2 = document.createElement('li');
    listItem2.setAttribute('data-sprk-autocomplete', 'result');

    listItem3 = document.createElement('li');
    listItem3.setAttribute('data-sprk-autocomplete', 'result');

    listEl.appendChild(listItem1);
    listEl.appendChild(listItem2);
    listEl.appendChild(listItem3);

    container.appendChild(inputEl);
    container.appendChild(listEl);

    outsideElement = document.createElement('button');
    outsideElement.innerHTML = 'Outside Button';
    document.body.appendChild(outsideElement);
    document.body.appendChild(container);

    sinon.spy(document, 'querySelectorAll');
    autocomplete();
  });

  afterEach(() => {
    document.querySelectorAll.restore(); // Unwraps the spy
    document.body.innerHTML = '';
  });

  it('should generate an id if needed', () => {
    // The id on the list, aria-controls on the input, and aria-owns on the
    // input container should all match.
    listEl.removeAttribute('id');
    inputEl.removeAttribute('aria-controls');
    inputEl.parentNode.removeAttribute('aria-owns');
    expect(listEl.getAttribute('id')).toBe(null);
    expect(inputEl.getAttribute('aria-controls')).toBe(null);
    expect(inputEl.parentNode.getAttribute('aria-owns')).toBe(null);
    autocomplete();
    expect(listEl.hasAttribute('id')).toBeTruthy();
    expect(inputEl.hasAttribute('aria-controls')).toBeTruthy();
    expect(inputEl.parentNode.hasAttribute('aria-owns')).toBeTruthy();
    expect(listEl.getAttribute('id')).toEqual(
      inputEl.getAttribute('aria-controls'),
    );
    expect(listEl.getAttribute('id')).toEqual(
      inputEl.parentNode.getAttribute('aria-owns'),
    );
  });

  it('should not generate an id if one is provided', () => {
    const testString = 'test_string';
    listEl.setAttribute('id', testString);
    inputEl.removeAttribute('aria-controls');
    inputEl.parentNode.removeAttribute('aria-owns');
    autocomplete();
    expect(inputEl.getAttribute('aria-controls')).toEqual(testString);
    expect(listEl.id).toEqual(testString);
  });

  it('should set aria-expanded="false" when the list is closed', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    const escKeyEvent = new window.Event('keydown');
    escKeyEvent.keyCode = 27;
    document.dispatchEvent(escKeyEvent);

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
    expect(inputEl.parentNode.getAttribute('aria-expanded')).toEqual('false');
  });

  it('should close the search results if escape is pressed', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    const escKeyEvent = new window.Event('keydown');
    escKeyEvent.keyCode = 27;
    document.dispatchEvent(escKeyEvent);

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
  });

  it('should close the search results if document is clicked', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    document.dispatchEvent(new window.Event('click'));

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
  });

  it('should close the search results if an outside element is clicked', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    outsideElement.click();
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(true);
  });

  it(`it should not close the search results if search
  results are clicked`, () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    listEl.dispatchEvent(new window.Event('click'));

    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);
  });

  it('it should not close the search results if input is clicked', () => {
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);

    inputEl.dispatchEvent(new window.Event('click'));
    inputEl.click();
    expect(listEl.classList.contains('sprk-u-Display--none')).toBe(false);
  });

  it('should move visual focus with down arrow', () => {
    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should overflow visual focus with down arrow', () => {
    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);

    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it(`should not move visual focus with down arrow if the
  list is hidden`, () => {
    listEl.classList.add('sprk-u-Display--none');

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);
  });

  it('should move visual focus with up arrow', () => {
    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 38;
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should overflow visual focus with up arrow', () => {
    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 38;
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);

    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should not move visual focus with up arrow if the list is hidden', () => {
    listEl.classList.add('sprk-u-Display--none');

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);

    const event = new window.Event('keydown');
    event.keyCode = 38;
    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(false);
  });

  it('should skip non-results when moving visual focus with down arrow', () => {
    listItem2.setAttribute('data-sprk-autocomplete-no-select', 'true');
    const event = new window.Event('keydown');
    event.keyCode = 40;
    inputEl.dispatchEvent(event);

    expect(
      listItem1.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);

    inputEl.dispatchEvent(event);

    expect(
      listItem3.classList.contains('sprk-c-Autocomplete__result--active'),
    ).toBe(true);
  });

  it('should call getElements once with the correct selector', () => {
    expect(document.querySelectorAll.getCall(0).args[0]).toBe(
      '[data-sprk-autocomplete="container"]',
    );
  });

  it('should not break if you press other keys', () => {
    const event = new window.Event('keydown');
    event.keyCode = 111;
    inputEl.dispatchEvent(event);

    document.dispatchEvent(event);
  });

  it('should run functions on window resize', () => {
    const spy = jest.fn();
    window.addEventListener('resize', spy);
    const event = new window.Event('resize');
    window.resizeTo(3000, 3000);
    window.dispatchEvent(event);
    expect(spy).toHaveBeenCalled();
  });

  it(`Should set the scrolltop to equal offsetTop if
  listItem top is less than list top`, () => {
    const event = new window.Event('keydown');
    event.keyCode = 40;

    // Set window height & width
    window.innerWidth = 300;
    window.innerHeight = 300;

    // Set the top of the list
    listItem2.parentNode.getBoundingClientRect = () => {
      return { top: 25 };
    };

    // Set the top of the list item
    listItem2.getBoundingClientRect = () => {
      return { top: 24 };
    };
    inputEl.dispatchEvent(event);
    expect(listItem2.parentNode.scrollTop).toEqual(listItem2.offsetTop);
  });

  it(`Should set the scrolltop to equal offsetTop if
  listItem bottom is greater than list bottom`, () => {
    const event = new window.Event('keydown');
    event.keyCode = 40;

    // Set window height & width
    window.innerWidth = 300;
    window.innerHeight = 300;

    // Set the bottom of the list
    listItem2.parentNode.getBoundingClientRect = () => {
      return { bottom: 25 };
    };

    // Set the bottom of the list item
    listItem2.getBoundingClientRect = () => {
      return { bottom: 26 };
    };
    inputEl.dispatchEvent(event);
    expect(listItem2.parentNode.scrollTop).toEqual(listItem2.offsetTop);
  });
});
<|endoftext|>


<commit_before>const router = require('koa-router')()
const project = require('project')
const koaBody = require('koa-body')()
const path = require('path')
const serve = require('koa-static')(path.resolve(''))

// router.redirect('/', 'index.html');
router.get('/', function * () {
  this.redirect('index.html')
})

router.get('/projects/:project/src/:file', function * () {
  yield serve
})

router.post('/projects/:project/src/:file', koaBody, function * () {
  this.body = yield project.uploadInline(this.params.project, this.params.file, this.request.body.content)
  this.status = 202
})

router.get('/projects', function * () {
  this.body = yield project.listProjects(this.params.project)
})

router.get('/projects/:project', function * () {
  this.body = yield project.listInlines(this.params.project)
})

export default router
<commit_msg>increase upload file size limit<commit_after>const router = require('koa-router')()
const project = require('project')
const koaBody = require('koa-body')({
  jsonLimit: '10mb'
})
const path = require('path')
const serve = require('koa-static')(path.resolve(''))

// router.redirect('/', 'index.html');
router.get('/', function * () {
  this.redirect('index.html')
})

router.get('/projects/:project/src/:file', function * () {
  yield serve
})

router.post('/projects/:project/src/:file', koaBody, function * () {
  this.body = yield project.uploadInline(this.params.project, this.params.file, this.request.body.content)
  this.status = 202
})

router.get('/projects', function * () {
  this.body = yield project.listProjects(this.params.project)
})

router.get('/projects/:project', function * () {
  this.body = yield project.listInlines(this.params.project)
})

export default router
<|endoftext|>