| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 | import Delta from 'quill-delta';import Parchment from 'parchment';import Quill from '../core/quill';import logger from '../core/logger';import Module from '../core/module';let debug = logger('quill:toolbar');class Toolbar extends Module {  constructor(quill, options) {    super(quill, options);    if (Array.isArray(this.options.container)) {      let container = document.createElement('div');      addControls(container, this.options.container);      quill.container.parentNode.insertBefore(container, quill.container);      this.container = container;    } else if (typeof this.options.container === 'string') {      this.container = document.querySelector(this.options.container);    } else {      this.container = this.options.container;    }    if (!(this.container instanceof HTMLElement)) {      return debug.error('Container required for toolbar', this.options);    }    this.container.classList.add('ql-toolbar');    this.controls = [];    this.handlers = {};    Object.keys(this.options.handlers).forEach((format) => {      this.addHandler(format, this.options.handlers[format]);    });    [].forEach.call(this.container.querySelectorAll('button, select'), (input) => {      this.attach(input);    });    this.quill.on(Quill.events.EDITOR_CHANGE, (type, range) => {      if (type === Quill.events.SELECTION_CHANGE) {        this.update(range);      }    });    this.quill.on(Quill.events.SCROLL_OPTIMIZE, () => {      let [range, ] = this.quill.selection.getRange();  // quill.getSelection triggers update      this.update(range);    });  }  addHandler(format, handler) {    this.handlers[format] = handler;  }  attach(input) {    let format = [].find.call(input.classList, (className) => {      return className.indexOf('ql-') === 0;    });    if (!format) return;    format = format.slice('ql-'.length);    if (input.tagName === 'BUTTON') {      input.setAttribute('type', 'button');    }    if (this.handlers[format] == null) {      if (this.quill.scroll.whitelist != null && this.quill.scroll.whitelist[format] == null) {        debug.warn('ignoring attaching to disabled format', format, input);        return;      }      if (Parchment.query(format) == null) {        debug.warn('ignoring attaching to nonexistent format', format, input);        return;      }    }    let eventName = input.tagName === 'SELECT' ? 'change' : 'click';    input.addEventListener(eventName, (e) => {      let value;      if (input.tagName === 'SELECT') {        if (input.selectedIndex < 0) return;        let selected = input.options[input.selectedIndex];        if (selected.hasAttribute('selected')) {          value = false;        } else {          value = selected.value || false;        }      } else {        if (input.classList.contains('ql-active')) {          value = false;        } else {          value = input.value || !input.hasAttribute('value');        }        e.preventDefault();      }      this.quill.focus();      let [range, ] = this.quill.selection.getRange();      if (this.handlers[format] != null) {        this.handlers[format].call(this, value);      } else if (Parchment.query(format).prototype instanceof Parchment.Embed) {        value = prompt(`Enter ${format}`);        if (!value) return;        this.quill.updateContents(new Delta()          .retain(range.index)          .delete(range.length)          .insert({ [format]: value })        , Quill.sources.USER);      } else {        this.quill.format(format, value, Quill.sources.USER);      }      this.update(range);    });    // TODO use weakmap    this.controls.push([format, input]);  }  update(range) {    let formats = range == null ? {} : this.quill.getFormat(range);    this.controls.forEach(function(pair) {      let [format, input] = pair;      if (input.tagName === 'SELECT') {        let option;        if (range == null) {          option = null;        } else if (formats[format] == null) {          option = input.querySelector('option[selected]');        } else if (!Array.isArray(formats[format])) {          let value = formats[format];          if (typeof value === 'string') {            value = value.replace(/\"/g, '\\"');          }          option = input.querySelector(`option[value="${value}"]`);        }        if (option == null) {          input.value = '';   // TODO make configurable?          input.selectedIndex = -1;        } else {          option.selected = true;        }      } else {        if (range == null) {          input.classList.remove('ql-active');        } else if (input.hasAttribute('value')) {          // both being null should match (default values)          // '1' should match with 1 (headers)          let isActive = formats[format] === input.getAttribute('value') ||                         (formats[format] != null && formats[format].toString() === input.getAttribute('value')) ||                         (formats[format] == null && !input.getAttribute('value'));          input.classList.toggle('ql-active', isActive);        } else {          input.classList.toggle('ql-active', formats[format] != null);        }      }    });  }}Toolbar.DEFAULTS = {};function addButton(container, format, value) {  let input = document.createElement('button');  input.setAttribute('type', 'button');  input.classList.add('ql-' + format);  if (value != null) {    input.value = value;  }  container.appendChild(input);}function addControls(container, groups) {  if (!Array.isArray(groups[0])) {    groups = [groups];  }  groups.forEach(function(controls) {    let group = document.createElement('span');    group.classList.add('ql-formats');    controls.forEach(function(control) {      if (typeof control === 'string') {        addButton(group, control);      } else {        let format = Object.keys(control)[0];        let value = control[format];        if (Array.isArray(value)) {          addSelect(group, format, value);        } else {          addButton(group, format, value);        }      }    });    container.appendChild(group);  });}function addSelect(container, format, values) {  let input = document.createElement('select');  input.classList.add('ql-' + format);  values.forEach(function(value) {    let option = document.createElement('option');    if (value !== false) {      option.setAttribute('value', value);    } else {      option.setAttribute('selected', 'selected');    }    input.appendChild(option);  });  container.appendChild(input);}Toolbar.DEFAULTS = {  container: null,  handlers: {    clean: function() {      let range = this.quill.getSelection();      if (range == null) return;      if (range.length == 0) {        let formats = this.quill.getFormat();        Object.keys(formats).forEach((name) => {          // Clean functionality in existing apps only clean inline formats          if (Parchment.query(name, Parchment.Scope.INLINE) != null) {            this.quill.format(name, false);          }        });      } else {        this.quill.removeFormat(range, Quill.sources.USER);      }    },    direction: function(value) {      let align = this.quill.getFormat()['align'];      if (value === 'rtl' && align == null) {        this.quill.format('align', 'right', Quill.sources.USER);      } else if (!value && align === 'right') {        this.quill.format('align', false, Quill.sources.USER);      }      this.quill.format('direction', value, Quill.sources.USER);    },    indent: function(value) {      let range = this.quill.getSelection();      let formats = this.quill.getFormat(range);      let indent = parseInt(formats.indent || 0);      if (value === '+1' || value === '-1') {        let modifier = (value === '+1') ? 1 : -1;        if (formats.direction === 'rtl') modifier *= -1;        this.quill.format('indent', indent + modifier, Quill.sources.USER);      }    },    link: function(value) {      if (value === true) {        value = prompt('Enter link URL:');      }      this.quill.format('link', value, Quill.sources.USER);    },    list: function(value) {      let range = this.quill.getSelection();      let formats = this.quill.getFormat(range);      if (value === 'check') {        if (formats['list'] === 'checked' || formats['list'] === 'unchecked') {          this.quill.format('list', false, Quill.sources.USER);        } else {          this.quill.format('list', 'unchecked', Quill.sources.USER);        }      } else {        this.quill.format('list', value, Quill.sources.USER);      }    }  }}export { Toolbar as default, addControls };
 |