<commit_before>/*
 * List Widget
 *
 * Dependences:
 * - Row Link Plugin (october.rowlink.js)
 */
+function ($) { "use strict";

    var ListWidget = function (element, options) {

        var $el = this.$el = $(element);

        this.options = options || {};
        this.dropzone = null;

        this.init();
        this.initUploader();
    };

    ListWidget.DEFAULTS = {
    };

    ListWidget.prototype.init = function()
    {
        /*
        this.$el.closest('form').bind('ajaxSuccess', function(context, data, textStatus, jqXHR){
            console.log(context);
            console.log(data);
            console.log(textStatus);
            console.log(jqXHR);
        });
        */
        var t = this;

        $(window).bind('ajaxUpdateComplete', function(context, data, textStatus, jqXHR){
            t.update();
        });

        //ajaxUpdateComplete

        /*
        this.$el.find('[data-control="listwidget"]')

        .closest('[data-control="listwidget"]')

        $(this).trigger('unchange.oc.changeMonitor');
        */
        var
            list = this.$el,
            body = this.$el.find('#list_body').eq(0),
            head = $('thead', list),
            //body = $('tbody', list),
            foot = $('tfoot', list);

        //------------------------------------------------------------------------

        $('input[type="checkbox"]', list).each(function(){
            var $el = $(this);
            if ($el.is(':checked'))
                $el.parents('.item').eq(0).addClass('active');
        });

         //------------------------------------------------------------------------

        list.on('change', 'input[type="checkbox"]', function(){
            var $el = $(this),
                checked = $el.is(':checked');

            /*
            $el.request($el.data('request-handler'),{data:{
                value:555
            }});
            */

            if (checked) {
                $el.parents('.item').eq(0).addClass('active');
            } else {
                $el.parents('.item').eq(0).removeClass('active');
            }
        });

        /**
         * delete record completely
         */
        list.on('click', '*[data-delete-record]', function()
        {
            var x = window.confirm('You want to delete this record completely?');
            if(x){
                $(this).request( $(this).data('request-handler') ,{data:{
                    delete_model: $(this).data('delete-record')
                }});
                return true;
            }else{
                e.preventDefault();
                e.stopPropagation();
                return false;
            }
        });

        /**
         * prevent bubbling up on overlays
         */
        list.on('click', '*[data-click-overlay]', function(e)
        {
            e.stopPropagation();
        });

        /**
         * click action - main action on item click
         */
        list.on('click', '*[data-click-target]', function(e)
        {
            var $link = $(e.currentTarget);
            var target = $link.data('click-target');

            // value can be "checkbox" or "http://url..."
            if(target == 'checkbox'){
                var $checkbox = $link.parents('.item').eq(0).find('input[type="checkbox"]');
                $checkbox.prop('checked', !$checkbox.is(':checked')).trigger('change');
            }else{
                location.href = target;
            }
        });

        //------------------------------------------------------------------------
        /*var $form = list.closest('form');

        list.on('click', '[data-control~="monitor-check"]', function(e)
        {
            if($form.hasClass('oc-data-changed')){
                var x = window.confirm('Your input hasnt been saved. Continue?');
                if(x){
                    return true;
                }else{
                    e.preventDefault();
                    e.stopPropagation();
                    return false;
                }
            }
        });*/
    };

    ListWidget.prototype.initUploader = function()
    {
        var $btn = this.$el.find('[data-control="upload"]').eq(0);

        var postUrl = $btn.data('url');
        var handler = $btn.data('handler');

        if(postUrl && (postUrl != '')){
            var uploaderOptions = {
                clickable: $btn.get(0),
                method: 'POST',
                url: $btn.data('url'),
                paramName: 'file_data',
                createImageThumbnails: false,
                headers: {
                    'X-OCTOBER-REQUEST-HANDLER': handler
                },
                //previewsContainer: this.$el.find('[data-control="upload-ui"] .upload-preview').get(0)
                // fallback: implement method that would set a flag that the uploader is not supported by the browser
            };

            this.dropzone = new Dropzone($btn.get(0), uploaderOptions);
            this.dropzone.on('queuecomplete', this.onUploadQueueComplete);
            /*
            this.dropzone.on('addedfile', this.proxy(this.uploadFileAdded));
            this.dropzone.on('totaluploadprogress', this.proxy(this.uploadUpdateTotalProgress));
            this.dropzone.on('queuecomplete', this.proxy(this.uploadQueueComplete));
            this.dropzone.on('sending', this.proxy(this.uploadSending));
            this.dropzone.on('error', this.proxy(this.uploadError));
            this.dropzone.on('success', this.proxy(this.uploadSuccess));
            */
        }

    };

    ListWidget.prototype.onUploadQueueComplete = function() {
        location.reload(true);
    };

    ListWidget.prototype.update = function()
    {
        // remove uploader
        if (this.dropzone){
            this.dropzone.destroy();
            this.dropzone = null;
        }

        this.initUploader();
    };

    ListWidget.prototype.getChecked = function() {
        var
            list = this.$el,
            body = $('tbody', list)

        return  $('.list-checkbox input[type="checkbox"]', body).map(function(){
            var $el = $(this)
            if ($el.is(':checked'))
                return $el.val()
        }).get();
    }

    ListWidget.prototype.toggleChecked = function(el) {
        var $checkbox = $('.list-checkbox input[type="checkbox"]', $(el).closest('tr'))
        $checkbox.prop('checked', !$checkbox.is(':checked')).trigger('change')
    }

    // LIST WIDGET PLUGIN DEFINITION
    // ============================

    var old = $.fn.listWidget

    $.fn.listWidget = function (option) {
        var args = Array.prototype.slice.call(arguments, 1), result

        this.each(function () {
            var $this   = $(this)
            var data    = $this.data('oc.listwidget')
            var options = $.extend({}, ListWidget.DEFAULTS, $this.data(), typeof option == 'object' && option)
            if (!data) $this.data('oc.listwidget', (data = new ListWidget(this, options)))
            if (typeof option == 'string') result = data[option].apply(data, args)
            if (typeof result != 'undefined') return false
        })

        return result ? result : this
      }

    $.fn.listWidget.Constructor = ListWidget

    // LIST WIDGET NO CONFLICT
    // =================

    $.fn.listWidget.noConflict = function () {
        $.fn.listWidget = old
        return this
    }

    // LIST WIDGET HELPERS
    // =================

    if ($.oc === undefined)
        $.oc = {}

    $.oc.listToggleChecked = function(el) {
        $(el)
            .closest('[data-control="listwidget"]')
            .listWidget('toggleChecked', el)
    }

    $.oc.listGetChecked = function(el) {
        return $(el)
            .closest('[data-control="listwidget"]')
            .listWidget('getChecked')
    }

    // LIST WIDGET DATA-API
    // ==============

    $(document).render(function(){
        $('[data-control="tilelistwidget"]').listWidget();
    })

}(window.jQuery);<commit_msg>js test<commit_after>/*
 * List Widget
 *
 * Dependences:
 * - Row Link Plugin (october.rowlink.js)
 */
+function ($) { "use strict";

    var ListWidget = function (element, options) {

        var $el = this.$el = $(element);

        this.options = options || {};
        this.dropzone = null;

        this.init();
        this.initUploader();
    };

    ListWidget.DEFAULTS = {
    };

    ListWidget.prototype.init = function()
    {
        /*
        this.$el.closest('form').bind('ajaxSuccess', function(context, data, textStatus, jqXHR){
            console.log(context);
            console.log(data);
            console.log(textStatus);
            console.log(jqXHR);
        });
        */
        var t = this;

        $(window).bind('ajaxUpdateComplete', function(context, data, textStatus, jqXHR){
            t.update();
        });

        //ajaxUpdateComplete

        /*
        this.$el.find('[data-control="listwidget"]')

        .closest('[data-control="listwidget"]')

        $(this).trigger('unchange.oc.changeMonitor');
        */
        var
            list = this.$el,
            body = this.$el.find('#list_body').eq(0),
            head = $('thead', list),
            //body = $('tbody', list),
            foot = $('tfoot', list);

        //------------------------------------------------------------------------

        $('input[type="checkbox"]', list).each(function(){
            var $el = $(this);
            if ($el.is(':checked'))
                $el.parents('.item').eq(0).addClass('active');
        });

         //------------------------------------------------------------------------

        list.on('change', 'input[type="checkbox"]', function(){
            var $el = $(this),
                checked = $el.is(':checked');

            /*
            $el.request($el.data('request-handler'),{data:{
                value:555
            }});
            */

            if (checked) {
                $el.parents('.item').eq(0).addClass('active');
            } else {
                $el.parents('.item').eq(0).removeClass('active');
            }
        });

        /**
         * delete record completely
         */
        list.on('click', '*[data-delete-record]', function()
        {
            var x = window.confirm('You want to delete this record completely?');
            if(x){
                $(this).request( $(this).data('request-handler') ,{data:{
                    delete_model: $(this).data('delete-record')
                }});
                return true;
            }else{
                e.preventDefault();
                e.stopPropagation();
                return false;
            }
        });

        /**
         * prevent bubbling up on overlays
         */
        list.on('click', '*[data-click-overlay]', function(e)
        {
            e.stopPropagation();
        });

        /**
         * click action - main action on item click
         */
        list.on('click', '*[data-click-target]', function(e)
        {
            var $link = $(e.currentTarget);
            var target = $link.data('click-target');

            // value can be "checkbox" or "http://url..."
            if(target == 'checkbox'){
                var $checkbox = $link.parents('.item').eq(0).find('input[type="checkbox"]');
                $checkbox.prop('checked', !$checkbox.is(':checked')).trigger('change');
            }else{
                location.href = target;
            }
        });

        //------------------------------------------------------------------------
        /*var $form = list.closest('form');

        list.on('click', '[data-control~="monitor-check"]', function(e)
        {
            if($form.hasClass('oc-data-changed')){
                var x = window.confirm('Your input hasnt been saved. Continue?');
                if(x){
                    return true;
                }else{
                    e.preventDefault();
                    e.stopPropagation();
                    return false;
                }
            }
        });*/
    };

    ListWidget.prototype.initUploader = function()
    {
        var $btn = this.$el.find('[data-control="upload"]').eq(0);

        var postUrl = $btn.data('url');
        var handler = $btn.data('handler');

        if(postUrl && (postUrl != '')){
            var uploaderOptions = {
                clickable: $btn.get(0),
                method: 'POST',
                url: $btn.data('url'),
                paramName: 'file_data',
                createImageThumbnails: false,
                headers: {
                    'X-OCTOBER-REQUEST-HANDLER': handler
                },
                //previewsContainer: this.$el.find('[data-control="upload-ui"] .upload-preview').get(0)
                // fallback: implement method that would set a flag that the uploader is not supported by the browser
            };

            this.dropzone = new Dropzone($btn.get(0), uploaderOptions);
            this.dropzone.on('queuecomplete', this.onUploadQueueComplete);
            /*
            this.dropzone.on('addedfile', this.proxy(this.uploadFileAdded));
            this.dropzone.on('totaluploadprogress', this.proxy(this.uploadUpdateTotalProgress));
            this.dropzone.on('queuecomplete', this.proxy(this.uploadQueueComplete));
            this.dropzone.on('sending', this.proxy(this.uploadSending));
            this.dropzone.on('error', this.proxy(this.uploadError));
            this.dropzone.on('success', this.proxy(this.uploadSuccess));
            */
        }

    };

    ListWidget.prototype.onUploadQueueComplete = function() {
        alert('upload complete. please reload site');
        //location.reload(true);
    };

    ListWidget.prototype.update = function()
    {
        // remove uploader
        if (this.dropzone){
            this.dropzone.destroy();
            this.dropzone = null;
        }

        this.initUploader();
    };

    ListWidget.prototype.getChecked = function() {
        var
            list = this.$el,
            body = $('tbody', list)

        return  $('.list-checkbox input[type="checkbox"]', body).map(function(){
            var $el = $(this)
            if ($el.is(':checked'))
                return $el.val()
        }).get();
    }

    ListWidget.prototype.toggleChecked = function(el) {
        var $checkbox = $('.list-checkbox input[type="checkbox"]', $(el).closest('tr'))
        $checkbox.prop('checked', !$checkbox.is(':checked')).trigger('change')
    }

    // LIST WIDGET PLUGIN DEFINITION
    // ============================

    var old = $.fn.listWidget

    $.fn.listWidget = function (option) {
        var args = Array.prototype.slice.call(arguments, 1), result

        this.each(function () {
            var $this   = $(this)
            var data    = $this.data('oc.listwidget')
            var options = $.extend({}, ListWidget.DEFAULTS, $this.data(), typeof option == 'object' && option)
            if (!data) $this.data('oc.listwidget', (data = new ListWidget(this, options)))
            if (typeof option == 'string') result = data[option].apply(data, args)
            if (typeof result != 'undefined') return false
        })

        return result ? result : this
      }

    $.fn.listWidget.Constructor = ListWidget

    // LIST WIDGET NO CONFLICT
    // =================

    $.fn.listWidget.noConflict = function () {
        $.fn.listWidget = old
        return this
    }

    // LIST WIDGET HELPERS
    // =================

    if ($.oc === undefined)
        $.oc = {}

    $.oc.listToggleChecked = function(el) {
        $(el)
            .closest('[data-control="listwidget"]')
            .listWidget('toggleChecked', el)
    }

    $.oc.listGetChecked = function(el) {
        return $(el)
            .closest('[data-control="listwidget"]')
            .listWidget('getChecked')
    }

    // LIST WIDGET DATA-API
    // ==============

    $(document).render(function(){
        $('[data-control="tilelistwidget"]').listWidget();
    })

}(window.jQuery);<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import { modalStrings } from '../localization';

export const MODAL_KEYS = {
  SELECT_CUSTOMER: 'selectCustomer',
  COMMENT_EDIT: 'commentEdit',
  THEIR_REF_EDIT: 'theirRefEdit',
  ITEM_SELECT: 'itemSelect',
  SELECT_SUPPLIER: 'selectSupplier',
  PROGRAM_REQUISITION: 'programRequisition',
};

export const getModalTitle = modalKey => {
  switch (modalKey) {
    default:
      return '';
    case MODAL_KEYS.ITEM_SELECT:
      return modalStrings.search_for_an_item_to_add;
    case MODAL_KEYS.COMMENT_EDIT:
      return modalStrings.edit_the_invoice_comment;
    case MODAL_KEYS.THEIR_REF_EDIT:
      return modalStrings.edit_their_reference;
    case MODAL_KEYS.SELECT_CUSTOMER:
      return modalStrings.search_for_the_customer;
    case MODAL_KEYS.SELECT_SUPPLIER:
      return modalStrings.search_for_the_supplier;
  }
};
<commit_msg>Add selectMonthsToSupply MODAL_KEY<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import { modalStrings } from '../localization';

export const MODAL_KEYS = {
  SELECT_CUSTOMER: 'selectCustomer',
  COMMENT_EDIT: 'commentEdit',
  THEIR_REF_EDIT: 'theirRefEdit',
  ITEM_SELECT: 'itemSelect',
  SELECT_SUPPLIER: 'selectSupplier',
  PROGRAM_REQUISITION: 'programRequisition',
  SELECT_MONTHS_TO_SUPPLY: 'selectMonthsToSupply',
};

export const getModalTitle = modalKey => {
  switch (modalKey) {
    default:
      return '';
    case MODAL_KEYS.ITEM_SELECT:
      return modalStrings.search_for_an_item_to_add;
    case MODAL_KEYS.COMMENT_EDIT:
      return modalStrings.edit_the_invoice_comment;
    case MODAL_KEYS.THEIR_REF_EDIT:
      return modalStrings.edit_their_reference;
    case MODAL_KEYS.SELECT_CUSTOMER:
      return modalStrings.search_for_the_customer;
    case MODAL_KEYS.SELECT_SUPPLIER:
      return modalStrings.search_for_the_supplier;
  }
};
<|endoftext|>


<commit_before>import domReady from 'domready'
import viewloader from 'viewloader'
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Immutable from 'immutable'
import template from 'formalist-standard-react'

/**
 * Simple wrapper to create the form outer
 */
export default class App extends Component {
  constructor(props) {
    super(props)
    let form = this.props.form
    this.state = {
      formState: form.store.getState()
    }
  }

  componentWillMount() {
    let form = this.props.form
    form.store.subscribe(() => {
      this.setState({
        formState: form.store.getState()
      })
    })
  }

  render() {
    let form = this.props.form
    return (

        <form method="post" action="">
          {form.render()}
          <input name="data" type="hidden" value={JSON.stringify(this.state.formState.toJS())}/>
          <button>Submit form</button>
        </form>

{JSON.stringify(this.state.formState.toJS(), null, 2)}

    )
  }
}

/**
 * Object for holding our `viewloader` views
 */
const views = {
  form: (el, ast) => {
    let data = JSON.parse(ast)
    let configuredTemplate = template()
    let form = configuredTemplate(data)
    ReactDOM.render(<App form={form} />, el);
  }
}

/**
 * Kick things off on domReady
 */
domReady(() => {
  viewloader.execute(views);
})

<commit_msg>Add perf stuff.<commit_after>import domReady from 'domready'
import viewloader from 'viewloader'
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Immutable from 'immutable'
import template from 'formalist-standard-react'

import Perf from 'react-addons-perf'
window.Perf = Perf

/**
 * Simple wrapper to create the form outer
 */
export default class App extends Component {
  constructor(props) {
    super(props)
    let form = this.props.form
    this.state = {
      formState: form.store.getState()
    }
  }

  componentWillMount() {
    let form = this.props.form
    form.store.subscribe(() => {
      Perf.start()
      this.setState({
        formState: form.store.getState()
      }, () => {
        setTimeout(() => {
          Perf.stop()
          Perf.printInclusive()
          Perf.printExclusive()
          Perf.printWasted()
          Perf.printDOM()
        }, 1000)
      })
    })
  }

  render() {
    let form = this.props.form
    return (

        <form method="post" action="">
          {form.render()}
          <input name="data" type="hidden" value={JSON.stringify(this.state.formState.toJS())}/>
          <button>Submit form</button>
        </form>

{JSON.stringify(this.state.formState.toJS(), null, 2)}

    )
  }
}

/**
 * Object for holding our `viewloader` views
 */
const views = {
  form: (el, ast) => {
    let data = JSON.parse(ast)
    let configuredTemplate = template()
    let form = configuredTemplate(data)
    Perf.start()
    ReactDOM.render(<App form={form} />, el)
    Perf.stop()
    Perf.printInclusive()
    Perf.printExclusive()
    Perf.printWasted()
    Perf.printDOM()
  }
}

/**
 * Kick things off on domReady
 */
domReady(() => {
  viewloader.execute(views);
})

<|endoftext|>


<commit_before>import { getScope, debounce, warn } from './utils/helpers';

const DEFAULT_EVENT_NAME = 'veeValidate';

export default class ListenerGenerator
{
    constructor(el, binding, vnode, options) {
        this.callbacks = [];
        this.el = el;
        this.binding = binding;
        this.vm = vnode.context;
        this.component = vnode.child;
        this.options = options;
        this.fieldName = this.component ? this.component.name : (binding.expression || el.name);
    }

    /**
     * Determines if the validation rule requires additional listeners on target fields.
     */
    _hasFieldDependency(rules) {
        const results = rules.split('|').filter(r => !! r.match(/\b(confirmed|after|before):/));
        if (! results.length) {
            return false;
        }

        return results[0].split(':')[1];
    }

    /**
     * Validates input value, triggered by 'input' event.
     */
    _inputListener() {
        this._validate(this.el.value);
    }

    /**
     * Validates files, triggered by 'change' event.
     */
    _fileListener() {
        const isValid = this._validate(this.el.files);

        if (! isValid && this.binding.modifiers.reject) {
            // eslint-disable-next-line
            el.value = '';
        }
    }

    /**
     * Validates radio buttons, triggered by 'change' event.
     */
    _radioListener() {
        const checked = document.querySelector(`input[name="${this.el.name}"]:checked`);
        this._validate(checked ? checked.value : null);
    }

    /**
     * Validates checkboxes, triggered by change event.
     */
    _checkboxListener() {
        const checkedBoxes = document.querySelectorAll(`input[name="${this.el.name}"]:checked`);
        if (! checkedBoxes || ! checkedBoxes.length) {
            this._validate(null);
            return;
        }

        [...checkedBoxes].forEach(box => {
            this._validate(box.value);
        });
    }

    /**
     * Trigger the validation for a specific value.
     */
    _validate(value) {
        return this.vm.$validator.validate(this.fieldName, value, getScope(this.el));
    }

    /**
     * Returns a scoped callback, only runs if the el scope is the same as the recieved scope
     * From the event.
     */
    _getScopedListener(callback) {
        return (scope) => {
            if (! scope || scope === getScope(this.el) || scope instanceof Event) {
                callback();
            }
        };
    }

    /**
     * Attaches validator event-triggered validation.
     */
    _attachValidatorEvent() {
        const listener = this._getScopedListener(this._getSuitableListener().listener.bind(this));

        this.vm.$on(DEFAULT_EVENT_NAME, listener);
        this.callbacks.push({ name: DEFAULT_EVENT_NAME, listener });
        this.vm.$on('VALIDATOR_OFF', (field) => {
            if (this.fieldName === field) {
                this.detach();
            }
        });

        const fieldName = this._hasFieldDependency(this.el.dataset.vvRules);
        if (fieldName) {
            // Wait for the validator ready triggered when vm is mounted because maybe
            // the element isn't mounted yet.
            this.vm.$once('validatorReady', () => {
                const target = document.querySelector(`input[name='${fieldName}']`);
                if (! target) {
                    warn('Cannot find target field, no additional listeners were attached.');
                    return;
                }

                target.addEventListener('input', listener);
                this.callbacks.push({ name: 'input', listener, el: target });
            });
        }
    }

    /**
     * Determines a suitable listener for the element.
     */
    _getSuitableListener() {
        let listener;

        // determine the suitable listener and events to handle
        switch (this.el.type) {
        case 'file':
            listener = {
                names: ['change'],
                listener: this._fileListener
            };
            break;

        case 'radio':
            listener = {
                names: ['change'],
                listener: this._radioListener
            };
            break;

        case 'checkbox':
            listener = {
                names: ['change'],
                listener: this._checkboxListener
            };
            break;

        default:
            listener = {
                names: ['input', 'blur'],
                listener: this._inputListener
            };
            break;
        }

        // users are able to specify which events they want to validate on
        // pipe separated list of handler names to use
        if (this.el.dataset.vvValidateOn) {
            listener.names = this.el.dataset.vvValidateOn.split('|');
        }

        return listener;
    }

    /**
     * Attaches neccessary validation events for the component.
     */
    _attachForComponent() {
        this.component.$on('input', (value) => {
            this.vm.$validator.validate(this.fieldName, value);
        });
    }

    /**
     * Attachs a suitable listener for the input.
     */
    _attachFieldListeners() {
        // If it is a component, use vue events instead.
        if (this.component) {
            this._attachForComponent();

            return;
        }

        const handler = this._getSuitableListener();
        const listener = debounce(
            handler.listener.bind(this),
            this.el.dataset.vvDelay || this.options.delay
        );

        if (~['radio', 'checkbox'].indexOf(this.el.type)) {
            this.vm.$once('validatorReady', () => {
                [...document.querySelectorAll(`input[name="${this.el.name}"]`)].forEach(input => {
                    handler.names.forEach(handlerName => {
                        input.addEventListener(handlerName, listener);
                        this.callbacks.push({ name: handlerName, listener, el: input });
                    });
                });
            });

            return;
        }

        handler.names.forEach(handlerName => {
            this.el.addEventListener(handlerName, listener);
            this.callbacks.push({ name: handlerName, listener, el: this.el });
        });
    }

    /**
     * Attaches the Event Listeners.
     */
    attach() {
        this.vm.$validator.attach(this.fieldName, this.el.dataset.vvRules, this.el.dataset.vvAs);
        this._attachValidatorEvent();

        if (this.binding.expression) {
            // if its bound, validate it. (since update doesn't trigger after bind).
            if (! this.binding.modifiers.initial) {
                this.vm.$validator.validate(
                    this.binding.expression,
                    this.binding.value,
                    getScope(this.el)
                );
            }

            return;
        }

        this._attachFieldListeners();
    }

    /**
     * Removes all attached event listeners.
     */
    detach() {
        this.callbacks.filter(({ name }) => name === DEFAULT_EVENT_NAME).forEach(h => {
            this.vm.$off(DEFAULT_EVENT_NAME, h.listener);
        });

        this.callbacks.filter(({ name }) => name !== DEFAULT_EVENT_NAME).forEach(h => {
            h.el.removeEventListener(h.name, h.listener);
        });
    }
}
<commit_msg>add support for data-vv-name for 3rd party components and as another fallback for naming fields<commit_after>import { getScope, debounce, warn } from './utils/helpers';

const DEFAULT_EVENT_NAME = 'veeValidate';

export default class ListenerGenerator
{
    constructor(el, binding, vnode, options) {
        this.callbacks = [];
        this.el = el;
        this.binding = binding;
        this.vm = vnode.context;
        this.component = vnode.child;
        this.options = options;
        this.fieldName = this._resolveFieldName();
    }

    /**
     * Resolves the field name to trigger validations.
     * @return {String} The field name.
     */
    _resolveFieldName() {
        if (this.component) {
            return this.component.name || this.el.dataset.vvName;
        }

        return this.binding.expression || this.el.name || this.el.dataset.vvName;
    }

    /**
     * Determines if the validation rule requires additional listeners on target fields.
     */
    _hasFieldDependency(rules) {
        const results = rules.split('|').filter(r => !! r.match(/\b(confirmed|after|before):/));
        if (! results.length) {
            return false;
        }

        return results[0].split(':')[1];
    }

    /**
     * Validates input value, triggered by 'input' event.
     */
    _inputListener() {
        this._validate(this.el.value);
    }

    /**
     * Validates files, triggered by 'change' event.
     */
    _fileListener() {
        const isValid = this._validate(this.el.files);

        if (! isValid && this.binding.modifiers.reject) {
            // eslint-disable-next-line
            el.value = '';
        }
    }

    /**
     * Validates radio buttons, triggered by 'change' event.
     */
    _radioListener() {
        const checked = document.querySelector(`input[name="${this.el.name}"]:checked`);
        this._validate(checked ? checked.value : null);
    }

    /**
     * Validates checkboxes, triggered by change event.
     */
    _checkboxListener() {
        const checkedBoxes = document.querySelectorAll(`input[name="${this.el.name}"]:checked`);
        if (! checkedBoxes || ! checkedBoxes.length) {
            this._validate(null);
            return;
        }

        [...checkedBoxes].forEach(box => {
            this._validate(box.value);
        });
    }

    /**
     * Trigger the validation for a specific value.
     */
    _validate(value) {
        return this.vm.$validator.validate(this.fieldName, value, getScope(this.el));
    }

    /**
     * Returns a scoped callback, only runs if the el scope is the same as the recieved scope
     * From the event.
     */
    _getScopedListener(callback) {
        return (scope) => {
            if (! scope || scope === getScope(this.el) || scope instanceof Event) {
                callback();
            }
        };
    }

    /**
     * Attaches validator event-triggered validation.
     */
    _attachValidatorEvent() {
        const listener = this._getScopedListener(this._getSuitableListener().listener.bind(this));

        this.vm.$on(DEFAULT_EVENT_NAME, listener);
        this.callbacks.push({ name: DEFAULT_EVENT_NAME, listener });
        this.vm.$on('VALIDATOR_OFF', (field) => {
            if (this.fieldName === field) {
                this.detach();
            }
        });

        const fieldName = this._hasFieldDependency(this.el.dataset.vvRules);
        if (fieldName) {
            // Wait for the validator ready triggered when vm is mounted because maybe
            // the element isn't mounted yet.
            this.vm.$once('validatorReady', () => {
                const target = document.querySelector(`input[name='${fieldName}']`);
                if (! target) {
                    warn('Cannot find target field, no additional listeners were attached.');
                    return;
                }

                target.addEventListener('input', listener);
                this.callbacks.push({ name: 'input', listener, el: target });
            });
        }
    }

    /**
     * Determines a suitable listener for the element.
     */
    _getSuitableListener() {
        let listener;

        // determine the suitable listener and events to handle
        switch (this.el.type) {
        case 'file':
            listener = {
                names: ['change'],
                listener: this._fileListener
            };
            break;

        case 'radio':
            listener = {
                names: ['change'],
                listener: this._radioListener
            };
            break;

        case 'checkbox':
            listener = {
                names: ['change'],
                listener: this._checkboxListener
            };
            break;

        default:
            listener = {
                names: ['input', 'blur'],
                listener: this._inputListener
            };
            break;
        }

        // users are able to specify which events they want to validate on
        // pipe separated list of handler names to use
        if (this.el.dataset.vvValidateOn) {
            listener.names = this.el.dataset.vvValidateOn.split('|');
        }

        return listener;
    }

    /**
     * Attaches neccessary validation events for the component.
     */
    _attachForComponent() {
        this.component.$on('input', (value) => {
            this.vm.$validator.validate(this.fieldName, value);
        });
    }

    /**
     * Attachs a suitable listener for the input.
     */
    _attachFieldListeners() {
        // If it is a component, use vue events instead.
        if (this.component) {
            this._attachForComponent();

            return;
        }

        const handler = this._getSuitableListener();
        const listener = debounce(
            handler.listener.bind(this),
            this.el.dataset.vvDelay || this.options.delay
        );

        if (~['radio', 'checkbox'].indexOf(this.el.type)) {
            this.vm.$once('validatorReady', () => {
                [...document.querySelectorAll(`input[name="${this.el.name}"]`)].forEach(input => {
                    handler.names.forEach(handlerName => {
                        input.addEventListener(handlerName, listener);
                        this.callbacks.push({ name: handlerName, listener, el: input });
                    });
                });
            });

            return;
        }

        handler.names.forEach(handlerName => {
            this.el.addEventListener(handlerName, listener);
            this.callbacks.push({ name: handlerName, listener, el: this.el });
        });
    }

    /**
     * Attaches the Event Listeners.
     */
    attach() {
        this.vm.$validator.attach(this.fieldName, this.el.dataset.vvRules, this.el.dataset.vvAs);
        this._attachValidatorEvent();

        if (this.binding.expression) {
            // if its bound, validate it. (since update doesn't trigger after bind).
            if (! this.binding.modifiers.initial) {
                this.vm.$validator.validate(
                    this.binding.expression,
                    this.binding.value,
                    getScope(this.el)
                );
            }

            return;
        }

        this._attachFieldListeners();
    }

    /**
     * Removes all attached event listeners.
     */
    detach() {
        this.callbacks.filter(({ name }) => name === DEFAULT_EVENT_NAME).forEach(h => {
            this.vm.$off(DEFAULT_EVENT_NAME, h.listener);
        });

        this.callbacks.filter(({ name }) => name !== DEFAULT_EVENT_NAME).forEach(h => {
            h.el.removeEventListener(h.name, h.listener);
        });
    }
}
<|endoftext|>


<commit_before>const DEFAULT_DOCUMENT_TITLE = 'Gallery';

const HOME = 'home';
const CATALOG = 'catalog';
const LOGIN = 'login';
const SETTINGS = 'settings';
const VISUALIZE = 'visualize';
const DATA_MODEL = 'datamodel';
const DATASETS = 'datasets';
const LINK = 'link';
const SEARCH = 'search';
const ADVANCED_SEARCH = 'advanced_search';
const ORGS = 'orgs';
const ENTITY_SETS = 'entitysets';
const EDIT_ACCOUNT = 'edit_account';

export default {
  DEFAULT_DOCUMENT_TITLE,
  HOME,
  CATALOG,
  LOGIN,
  SETTINGS,
  VISUALIZE,
  DATA_MODEL,
  DATASETS,
  LINK,
  SEARCH,
  ADVANCED_SEARCH,
  ORGS,
  ENTITY_SETS,
  EDIT_ACCOUNT
};
<commit_msg>remove settings page constant<commit_after>const DEFAULT_DOCUMENT_TITLE = 'Gallery';

const HOME = 'home';
const CATALOG = 'catalog';
const LOGIN = 'login';
const VISUALIZE = 'visualize';
const DATA_MODEL = 'datamodel';
const DATASETS = 'datasets';
const LINK = 'link';
const SEARCH = 'search';
const ADVANCED_SEARCH = 'advanced_search';
const ORGS = 'orgs';
const ENTITY_SETS = 'entitysets';
const EDIT_ACCOUNT = 'edit_account';

export default {
  DEFAULT_DOCUMENT_TITLE,
  HOME,
  CATALOG,
  LOGIN,
  VISUALIZE,
  DATA_MODEL,
  DATASETS,
  LINK,
  SEARCH,
  ADVANCED_SEARCH,
  ORGS,
  ENTITY_SETS,
  EDIT_ACCOUNT
};
<|endoftext|>


<commit_before>import Context from "../context"
import ContextTypes from "../context-types"
import Expression from "./expression"
export default class Variable extends Expression {
    /** @type {boolean} */
    get byref() {
        return this.node.byref;
    }
    /** @type {string|_Node} */
    get name() {
        return this.cacheOptionalNode("name")
    }
    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, in_call = false) {
        super.check(context)
        if(context.assigningType) {
            return new ContextTypes(context.setName(
                '$' + this.name,
                context.assigningType
            ))
        } else {
            return new ContextTypes(this.assertHasName(context, '$' + this.name))
        }
    }
}<commit_msg>Apparently variable name is never a node (in practice only?)<commit_after>import Context from "../context"
import ContextTypes from "../context-types"
import Expression from "./expression"
export default class Variable extends Expression {
    /** @type {boolean} */
    get byref() {
        return this.node.byref;
    }
    /** @type {string} */
    get name() {
        return this.cacheOptionalNode("name")
    }
    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, in_call = false) {
        super.check(context)
        if(context.assigningType) {
            return new ContextTypes(context.setName(
                '$' + this.name,
                context.assigningType
            ))
        } else {
            return new ContextTypes(this.assertHasName(context, '$' + this.name))
        }
    }
}<|endoftext|>


<commit_before>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, {Component} from 'react'
import { DragSource, DropTarget } from 'react-dnd'
import {
  Checkbox,
  Col,
  Collapse,
  ControlLabel,
  FormControl,
  FormGroup,
  Row
} from 'react-bootstrap'

import * as activeActions from '../../actions/active'
import * as stopStrategiesActions from '../../actions/map/stopStrategies'
import * as tripPatternActions from '../../actions/tripPattern'
import {getEntityName, getAbbreviatedStopName} from '../../util/gtfs'
import MinuteSecondInput from '../MinuteSecondInput'
import NormalizeStopTimesTip from './NormalizeStopTimesTip'
import PatternStopButtons from './PatternStopButtons'

import type {Feed, Pattern, PatternStop} from '../../../types'

type Props = {
  active: boolean,
  activePattern: Pattern,
  addStopToPattern: typeof stopStrategiesActions.addStopToPattern,
  // property is available through react dnd?
  connectDragSource: any,
  controlLabel: string,
  cumulativeTravelTime: number,
  feedSource: Feed,
  findCard: string => { card: PatternStop, index: number },
  id: string,
  index: number,
  isDragging: boolean,
  moveCard: (string, number) => void,
  onChange: () => void,
  patternEdited: boolean,
  patternStop: PatternStop,
  removeStopFromPattern: typeof stopStrategiesActions.removeStopFromPattern,
  rowStyle: {[string]: number | string},
  saveActiveGtfsEntity: typeof activeActions.saveActiveGtfsEntity,
  selectType: string,
  selectValue: string,
  setActiveEntity: typeof activeActions.setActiveEntity,
  setActiveStop: typeof tripPatternActions.setActiveStop,
  shouldHaveDisabledOption: boolean,
  status: any,
  stop: any,
  stopIsActive: boolean,
  title: string,
  updateActiveGtfsEntity: typeof activeActions.updateActiveGtfsEntity,
  updatePatternStops: typeof tripPatternActions.updatePatternStops
}

type State = {
  initialDwellTime: number,
  initialTravelTime: number,
  update: boolean
}

const pickupDropoffOptions = [
  {
    value: 0,
    text: 'Available (0)'
  },
  {
    value: 1,
    text: 'Not available (1)'
  },
  {
    value: 2,
    text: 'Must phone agency to arrange (2)'
  },
  {
    value: 3,
    text: 'Must coordinate with driver to arrange (3)'
  }
]

// renders the form control drop downs for dropOff/Pick up and also continuous
const PickupDropoffSelect = (props: Props) => {
  const {activePattern, controlLabel, selectType, shouldHaveDisabledOption, title, onChange, selectValue} = props
  const hasShapeId = activePattern.shapeId === null
  return (
    <FormGroup
      controlId={selectType}
      bsSize='small'>
      <ControlLabel
        className='small'
        title={title}
      >
        {controlLabel}
      </ControlLabel>
      <FormControl
        disabled={shouldHaveDisabledOption && hasShapeId}
        componentClass='select'
        onChange={onChange}
        value={selectValue}
        placeholder='select'>
        {pickupDropoffOptions.map(o => (
          <option key={o.value} value={o.value}>{o.text}</option>
        ))}
      </FormControl>
    </FormGroup>
  )
}

const cardSource = {
  beginDrag (props: Props) {
    return {
      id: props.id,
      originalIndex: props.findCard(props.id).index
    }
  },

  endDrag (props: Props, monitor: any) {
    const { id: droppedId, originalIndex } = monitor.getItem()
    const didDrop = monitor.didDrop()
    if (!didDrop) {
      console.log('endDrag')
      props.moveCard(droppedId, originalIndex)
    }
  },

  // disable dragging if request is pending (i.e., save trip pattern is in progress)
  canDrag (props: Props, monitor: any) {
    if (props.status.savePending || props.active) {
      const item = monitor.getItem()
      // Ensure item exists before getting ID.
      const id = item && item.id
      console.warn(`Cannot drag card (id=${id}). Card is active or save is in progress.`)
      return false
    }
    return true
  }
}

const cardTarget = {
  drop (props, monitor) {
    const { id: droppedId, originalIndex } = monitor.getItem()
    const { index: droppedIndex } = props.findCard(droppedId)
    if (droppedIndex !== originalIndex) {
      props.dropCard()
    }
  },

  hover (props, monitor) {
    const { id: draggedId } = monitor.getItem()
    const { id: overId } = props
    if (draggedId !== overId) {
      const { index: overIndex } = props.findCard(overId)
      props.moveCard(draggedId, overIndex)
    }
  }
}

class PatternStopCard extends Component<Props> {
  _formatTravelTime (cumulativeTravelTime, patternStop) {
    return `${Math.round(cumulativeTravelTime / 60)} (+${Math.round(patternStop.defaultTravelTime / 60)}${patternStop.defaultDwellTime > 0 ? ` +${Math.round(patternStop.defaultDwellTime / 60)}` : ''})`
  }

  handleClick = () => {
    const {active, id, index, setActiveStop} = this.props
    if (!active) setActiveStop({id, index})
    else setActiveStop({id: null, index: null})
  }

  _onKeyDown = (e) => {
    if (e.keyCode === 13) {
      this.handleClick()
    }
  }

  render () {
    const {
      active,
      connectDragSource,
      // $FlowFixMe https://github.com/flow-typed/flow-typed/issues/1564
      connectDropTarget,
      stop,
      index,
      patternStop,
      cumulativeTravelTime
    } = this.props
    const stopName = getEntityName(stop)
    const abbreviatedStopName = getAbbreviatedStopName(stop)
    const titleStopName = stop
      ? `${index + 1}. ${stopName}`
      : `${index + 1}. [unknown stop]`
    const fullStopName = stop
      ? `${index + 1}. ${abbreviatedStopName}`
      : `${index + 1}. [unknown stop]`
    return connectDragSource(connectDropTarget(

        {/* Main card title */}

              <Icon type={active ? 'caret-down' : 'caret-right'} />
              {fullStopName.length > 25
                ? fullStopName.substr(0, 25) + '...'
                : fullStopName
              }

                {this._formatTravelTime(cumulativeTravelTime, patternStop)}

              {'    '}

                <Icon type='bars' />

        <PatternStopContents {...this.props} />

    ))
  }
}

class PatternStopContents extends Component<Props, State> {
  componentWillMount () {
    this.setState({
      initialDwellTime: this.props.patternStop.defaultDwellTime,
      initialTravelTime: this.props.patternStop.defaultTravelTime,
      update: false
    })
  }

  componentWillReceiveProps (nextProps) {
    if (!nextProps.patternEdited && this.props.patternEdited) {
      this.setState({update: false})
    }
  }

  /**
   * Only update component if id changes, active state changes, or if pattern has
   * been saved (patternEdited changes to false). This is to ensure that default
   * times are not overwritten.
   * FIXME: id shouldn't change anymore (not generated client-side). Check that
   * this has no negative effects (check elsewhere, too, for example, pattern geom).
   */
  shouldComponentUpdate (nextProps, nextState) {
    if (nextProps.active !== this.props.active ||
      nextProps.id !== this.props.id ||
      nextState !== this.state ||
      (!nextProps.patternEdited && this.props.patternEdited)
    ) {
      return true
    }
    return false
  }

  _onChangeTimepoint = () => {
    const {activePattern, index, patternStop, saveActiveGtfsEntity, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    const newValue = patternStop.timepoint ? 0 : 1
    patternStops[index].timepoint = newValue
    updatePatternStops(activePattern, patternStops)
    saveActiveGtfsEntity('trippattern')
  }

  _onClickRemovePatternStop = () => {
    const {activePattern, index, saveActiveGtfsEntity, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    patternStops.splice(index, 1)
    updatePatternStops(activePattern, patternStops)
    saveActiveGtfsEntity('trippattern')
  }

  _onDwellTimeChange = (defaultDwellTime: number) => {
    const {activePattern, index, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    patternStops[index].defaultDwellTime = defaultDwellTime
    this.setState({update: true})
    updatePatternStops(activePattern, patternStops)
  }

  _onTravelTimeChange = (defaultTravelTime: number) => {
    const {activePattern, index, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    patternStops[index].defaultTravelTime = defaultTravelTime
    this.setState({update: true})
    updatePatternStops(activePattern, patternStops)
  }

  onChange = (evt: SyntheticInputEvent<HTMLInputElement>) => {
    const selectedOptionValue: number = parseInt(evt.target.value, 10)
    const {activePattern, index, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]

    patternStops[index][evt.target.id] = selectedOptionValue
    this.setState({update: true})
    updatePatternStops(activePattern, patternStops)
  }

  render () {
    const {active, patternEdited, patternStop} = this.props
    // This component has a special shouldComponentUpdate to ensure that state
    // is not overwritten with new props, so use state.update to check edited
    // state.
    const isEdited = patternEdited || this.state.update

    let innerDiv
    if (active) {
      innerDiv =

        {/* Remove from pattern button */}
        <Row>
          <Col xs={4}>
            <Checkbox
              checked={patternStop.timepoint}
              onChange={this._onChangeTimepoint}>
              Timepoint?
            </Checkbox>
          </Col>
          <Col xs={8}>
            <PatternStopButtons
              {...this.props}
              patternEdited={isEdited}
              size='xsmall'
              style={{marginTop: '10px'}} />
          </Col>
        </Row>
        {/* default travel time inputs */}
        <Row>
          <Col xs={6}>
            <FormGroup
              controlId='defaultTravelTime'
              bsSize='small'>
              <ControlLabel
                title={this.props.index === 0
                  ? 'Travel time for first stop must be zero'
                  : 'Define the default time it takes to travel to this stop from the previous stop.'
                }
                className='small'>Default travel time</ControlLabel>
              <MinuteSecondInput
                disabled={this.props.index === 0}
                seconds={this.state.initialTravelTime}
                onChange={this._onTravelTimeChange} />
            </FormGroup>
          </Col>
          <Col xs={6}>
            <FormGroup
              controlId='defaultDwellTime'
              bsSize='small'>
              <ControlLabel className='small'>Default dwell time</ControlLabel>
              <MinuteSecondInput
                seconds={this.state.initialDwellTime}
                onChange={this._onDwellTimeChange} />
            </FormGroup>
          </Col>
        </Row>
        {/* Pickup and drop off type selectors */}
        <Row>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption={false}
              selectType='pickupType'
              title='Define the pickup method/availability at this stop.'
              controlLabel='Pickup'
              value={patternStop.pickupType || ''}
              onChange={this.onChange}
            />
          </Col>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption={false}
              selectType='dropOffType'
              title='Define the dropff method/availability at this stop.'
              controlLabel='Drop-off'
              value={patternStop.dropOffType || ''}
              onChange={this.onChange}
            />
          </Col>
        </Row>
        <Row>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption
              selectType='continuousPickup'
              title='Indicates whether a rider can board the transit vehicle anywhere along the vehicle’s travel path.'
              controlLabel='Continuous pickup'
              value={patternStop.continuousPickup || ''}
              onChange={this.onChange}
            />
          </Col>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption
              selectType='continuousDropOff'
              title='Indicates whether a rider can alight from the transit vehicle at any point along the vehicle’s travel path.'
              controlLabel='Continuous drop-off'
              value={patternStop.continuousDropOff || ''}
              onChange={this.onChange}
            />
          </Col>
        </Row>
        <NormalizeStopTimesTip />

    }
    return (
      <Collapse // collapsible div
        in={active}>

{innerDiv}

      </Collapse>
    )
  }
}

const dropTargetCollect = (connect) => ({connectDropTarget: connect.dropTarget()})
const dragSourceCollect = (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging()
})

export default DropTarget('card', cardTarget, dropTargetCollect)(
  DragSource('card', cardSource, dragSourceCollect)(PatternStopCard)
)
<commit_msg>fix(PatternSTopCard.js): Fixed prop bug<commit_after>// @flow

import Icon from '@conveyal/woonerf/components/icon'
import React, {Component} from 'react'
import { DragSource, DropTarget } from 'react-dnd'
import {
  Checkbox,
  Col,
  Collapse,
  ControlLabel,
  FormControl,
  FormGroup,
  Row
} from 'react-bootstrap'

import * as activeActions from '../../actions/active'
import * as stopStrategiesActions from '../../actions/map/stopStrategies'
import * as tripPatternActions from '../../actions/tripPattern'
import {getEntityName, getAbbreviatedStopName} from '../../util/gtfs'
import MinuteSecondInput from '../MinuteSecondInput'
import NormalizeStopTimesTip from './NormalizeStopTimesTip'
import PatternStopButtons from './PatternStopButtons'

import type {Feed, Pattern, PatternStop} from '../../../types'

type Props = {
  active: boolean,
  activePattern: Pattern,
  addStopToPattern: typeof stopStrategiesActions.addStopToPattern,
  // property is available through react dnd?
  connectDragSource: any,
  controlLabel: string,
  cumulativeTravelTime: number,
  feedSource: Feed,
  findCard: string => { card: PatternStop, index: number },
  id: string,
  index: number,
  isDragging: boolean,
  moveCard: (string, number) => void,
  onChange: () => void,
  patternEdited: boolean,
  patternStop: PatternStop,
  removeStopFromPattern: typeof stopStrategiesActions.removeStopFromPattern,
  rowStyle: {[string]: number | string},
  saveActiveGtfsEntity: typeof activeActions.saveActiveGtfsEntity,
  selectType: string,
  setActiveEntity: typeof activeActions.setActiveEntity,
  setActiveStop: typeof tripPatternActions.setActiveStop,
  shouldHaveDisabledOption: boolean,
  status: any,
  stop: any,
  stopIsActive: boolean,
  title: string,
  updateActiveGtfsEntity: typeof activeActions.updateActiveGtfsEntity,
  updatePatternStops: typeof tripPatternActions.updatePatternStops,
  value: string
}

type State = {
  initialDwellTime: number,
  initialTravelTime: number,
  update: boolean
}

const pickupDropoffOptions = [
  {
    value: 0,
    text: 'Available (0)'
  },
  {
    value: 1,
    text: 'Not available (1)'
  },
  {
    value: 2,
    text: 'Must phone agency to arrange (2)'
  },
  {
    value: 3,
    text: 'Must coordinate with driver to arrange (3)'
  }
]

// renders the form control drop downs for dropOff/Pick up and also continuous
const PickupDropoffSelect = (props: Props) => {
  const {activePattern, controlLabel, selectType, shouldHaveDisabledOption, title, onChange, value} = props
  const hasShapeId = activePattern.shapeId === null
  return (
    <FormGroup
      controlId={selectType}
      bsSize='small'>
      <ControlLabel
        className='small'
        title={title}
      >
        {controlLabel}
      </ControlLabel>
      <FormControl
        disabled={shouldHaveDisabledOption && hasShapeId}
        componentClass='select'
        onChange={onChange}
        value={value}
        placeholder='select'>
        {pickupDropoffOptions.map(o => (
          <option key={o.value} value={o.value}>{o.text}</option>
        ))}
      </FormControl>
    </FormGroup>
  )
}

const cardSource = {
  beginDrag (props: Props) {
    return {
      id: props.id,
      originalIndex: props.findCard(props.id).index
    }
  },

  endDrag (props: Props, monitor: any) {
    const { id: droppedId, originalIndex } = monitor.getItem()
    const didDrop = monitor.didDrop()
    if (!didDrop) {
      console.log('endDrag')
      props.moveCard(droppedId, originalIndex)
    }
  },

  // disable dragging if request is pending (i.e., save trip pattern is in progress)
  canDrag (props: Props, monitor: any) {
    if (props.status.savePending || props.active) {
      const item = monitor.getItem()
      // Ensure item exists before getting ID.
      const id = item && item.id
      console.warn(`Cannot drag card (id=${id}). Card is active or save is in progress.`)
      return false
    }
    return true
  }
}

const cardTarget = {
  drop (props, monitor) {
    const { id: droppedId, originalIndex } = monitor.getItem()
    const { index: droppedIndex } = props.findCard(droppedId)
    if (droppedIndex !== originalIndex) {
      props.dropCard()
    }
  },

  hover (props, monitor) {
    const { id: draggedId } = monitor.getItem()
    const { id: overId } = props
    if (draggedId !== overId) {
      const { index: overIndex } = props.findCard(overId)
      props.moveCard(draggedId, overIndex)
    }
  }
}

class PatternStopCard extends Component<Props> {
  _formatTravelTime (cumulativeTravelTime, patternStop) {
    return `${Math.round(cumulativeTravelTime / 60)} (+${Math.round(patternStop.defaultTravelTime / 60)}${patternStop.defaultDwellTime > 0 ? ` +${Math.round(patternStop.defaultDwellTime / 60)}` : ''})`
  }

  handleClick = () => {
    const {active, id, index, setActiveStop} = this.props
    if (!active) setActiveStop({id, index})
    else setActiveStop({id: null, index: null})
  }

  _onKeyDown = (e) => {
    if (e.keyCode === 13) {
      this.handleClick()
    }
  }

  render () {
    const {
      active,
      connectDragSource,
      // $FlowFixMe https://github.com/flow-typed/flow-typed/issues/1564
      connectDropTarget,
      stop,
      index,
      patternStop,
      cumulativeTravelTime
    } = this.props
    const stopName = getEntityName(stop)
    const abbreviatedStopName = getAbbreviatedStopName(stop)
    const titleStopName = stop
      ? `${index + 1}. ${stopName}`
      : `${index + 1}. [unknown stop]`
    const fullStopName = stop
      ? `${index + 1}. ${abbreviatedStopName}`
      : `${index + 1}. [unknown stop]`
    return connectDragSource(connectDropTarget(

        {/* Main card title */}

              <Icon type={active ? 'caret-down' : 'caret-right'} />
              {fullStopName.length > 25
                ? fullStopName.substr(0, 25) + '...'
                : fullStopName
              }

                {this._formatTravelTime(cumulativeTravelTime, patternStop)}

              {'    '}

                <Icon type='bars' />

        <PatternStopContents {...this.props} />

    ))
  }
}

class PatternStopContents extends Component<Props, State> {
  componentWillMount () {
    this.setState({
      initialDwellTime: this.props.patternStop.defaultDwellTime,
      initialTravelTime: this.props.patternStop.defaultTravelTime,
      update: false
    })
  }

  componentWillReceiveProps (nextProps) {
    if (!nextProps.patternEdited && this.props.patternEdited) {
      this.setState({update: false})
    }
  }

  /**
   * Only update component if id changes, active state changes, or if pattern has
   * been saved (patternEdited changes to false). This is to ensure that default
   * times are not overwritten.
   * FIXME: id shouldn't change anymore (not generated client-side). Check that
   * this has no negative effects (check elsewhere, too, for example, pattern geom).
   */
  shouldComponentUpdate (nextProps, nextState) {
    if (nextProps.active !== this.props.active ||
      nextProps.id !== this.props.id ||
      nextState !== this.state ||
      (!nextProps.patternEdited && this.props.patternEdited)
    ) {
      return true
    }
    return false
  }

  _onChangeTimepoint = () => {
    const {activePattern, index, patternStop, saveActiveGtfsEntity, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    const newValue = patternStop.timepoint ? 0 : 1
    patternStops[index].timepoint = newValue
    updatePatternStops(activePattern, patternStops)
    saveActiveGtfsEntity('trippattern')
  }

  _onClickRemovePatternStop = () => {
    const {activePattern, index, saveActiveGtfsEntity, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    patternStops.splice(index, 1)
    updatePatternStops(activePattern, patternStops)
    saveActiveGtfsEntity('trippattern')
  }

  _onDwellTimeChange = (defaultDwellTime: number) => {
    const {activePattern, index, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    patternStops[index].defaultDwellTime = defaultDwellTime
    this.setState({update: true})
    updatePatternStops(activePattern, patternStops)
  }

  _onTravelTimeChange = (defaultTravelTime: number) => {
    const {activePattern, index, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]
    patternStops[index].defaultTravelTime = defaultTravelTime
    this.setState({update: true})
    updatePatternStops(activePattern, patternStops)
  }

  _onPickupOrDropOffChange = (evt: SyntheticInputEvent<HTMLInputElement>) => {
    const selectedOptionValue: number = parseInt(evt.target.value, 10)
    const {activePattern, index, updatePatternStops} = this.props
    const patternStops = [...activePattern.patternStops]

    patternStops[index][evt.target.id] = selectedOptionValue
    this.setState({update: true})
    updatePatternStops(activePattern, patternStops)
    console.log('made it to here')
  }

  render () {
    const {active, patternEdited, patternStop} = this.props
    // This component has a special shouldComponentUpdate to ensure that state
    // is not overwritten with new props, so use state.update to check edited
    // state.
    const isEdited = patternEdited || this.state.update

    let innerDiv
    if (active) {
      innerDiv =

        {/* Remove from pattern button */}
        <Row>
          <Col xs={4}>
            <Checkbox
              checked={patternStop.timepoint}
              onChange={this._onChangeTimepoint}>
              Timepoint?
            </Checkbox>
          </Col>
          <Col xs={8}>
            <PatternStopButtons
              {...this.props}
              patternEdited={isEdited}
              size='xsmall'
              style={{marginTop: '10px'}} />
          </Col>
        </Row>
        {/* default travel time inputs */}
        <Row>
          <Col xs={6}>
            <FormGroup
              controlId='defaultTravelTime'
              bsSize='small'>
              <ControlLabel
                title={this.props.index === 0
                  ? 'Travel time for first stop must be zero'
                  : 'Define the default time it takes to travel to this stop from the previous stop.'
                }
                className='small'>Default travel time</ControlLabel>
              <MinuteSecondInput
                disabled={this.props.index === 0}
                seconds={this.state.initialTravelTime}
                onChange={this._onTravelTimeChange} />
            </FormGroup>
          </Col>
          <Col xs={6}>
            <FormGroup
              controlId='defaultDwellTime'
              bsSize='small'>
              <ControlLabel className='small'>Default dwell time</ControlLabel>
              <MinuteSecondInput
                seconds={this.state.initialDwellTime}
                onChange={this._onDwellTimeChange} />
            </FormGroup>
          </Col>
        </Row>
        {/* Pickup and drop off type selectors */}
        <Row>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption={false}
              selectType='pickupType'
              title='Define the pickup method/availability at this stop.'
              controlLabel='Pickup'
              value={patternStop.pickupType || ''}
              onChange={this._onPickupOrDropOffChange}
            />
          </Col>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption={false}
              selectType='dropOffType'
              title='Define the dropff method/availability at this stop.'
              controlLabel='Drop-off'
              value={patternStop.dropOffType || ''}
              onChange={this._onPickupOrDropOffChange}
            />
          </Col>
        </Row>
        <Row>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption
              selectType='continuousPickup'
              title='Indicates whether a rider can board the transit vehicle anywhere along the vehicle’s travel path.'
              controlLabel='Continuous pickup'
              value={patternStop.continuousPickup || ''}
              onChange={this._onPickupOrDropOffChange}
            />
          </Col>
          <Col xs={6}>
            <PickupDropoffSelect
              {...this.props}
              shouldHaveDisabledOption
              selectType='continuousDropOff'
              title='Indicates whether a rider can alight from the transit vehicle at any point along the vehicle’s travel path.'
              controlLabel='Continuous drop-off'
              value={patternStop.continuousDropOff || ''}
              onChange={this._onPickupOrDropOffChange}
            />
          </Col>
        </Row>
        <NormalizeStopTimesTip />

    }
    return (
      <Collapse // collapsible div
        in={active}>

{innerDiv}

      </Collapse>
    )
  }
}

const dropTargetCollect = (connect) => ({connectDropTarget: connect.dropTarget()})
const dragSourceCollect = (connect, monitor) => ({
  connectDragSource: connect.dragSource(),
  isDragging: monitor.isDragging()
})

export default DropTarget('card', cardTarget, dropTargetCollect)(
  DragSource('card', cardSource, dragSourceCollect)(PatternStopCard)
)
<|endoftext|>


<commit_before>/*jslint browser: true, eqeqeq: true, bitwise: true, newcap: true, immed: true, regexp: false */

/**
LazyLoad makes it easy and painless to lazily load one or more external
JavaScript or CSS files on demand either during or after the rendering of a web
page.

Supported browsers include Firefox 2+, IE6+, Safari 3+ (including Mobile
Safari), Google Chrome, and Opera 9+. Other browsers may or may not work and
are not officially supported.

Visit https://github.com/rgrove/lazyload/ for more info.

Copyright (c) 2011 Ryan Grove <ryan@wonko.com>
All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the 'Software'), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@module lazyload
@class LazyLoad
@static
*/

LazyLoad = (function (doc) {
  // -- Private Variables ------------------------------------------------------

  // User agent and feature test information.
  var env,

  // Reference to the  element (populated lazily).
  head,

  // Requests currently in progress, if any.
  pending = {},

  // Number of times we've polled to check whether a pending stylesheet has
  // finished loading. If this gets too high, we're probably stalled.
  pollCount = 0,

  // Queued requests.
  queue = {css: [], js: []},

  // Reference to the browser's list of stylesheets.
  styleSheets = doc.styleSheets;

  // -- Private Methods --------------------------------------------------------

  /**
  Creates and returns an HTML element with the specified name and attributes.

  @method createNode
  @param {String} name element name
  @param {Object} attrs name/value mapping of element attributes
  @return {HTMLElement}
  @private
  */
  function createNode(name, attrs) {
    var node = doc.createElement(name), attr;

    for (attr in attrs) {
      if (attrs.hasOwnProperty(attr)) {
        node.setAttribute(attr, attrs[attr]);
      }
    }

    return node;
  }

  /**
  Called when the current pending resource of the specified type has finished
  loading. Executes the associated callback (if any) and loads the next
  resource in the queue.

  @method finish
  @param {String} type resource type ('css' or 'js')
  @private
  */
  function finish(type) {
    var p = pending[type],
        callback,
        urls;

    if (p) {
      callback = p.callback;
      urls     = p.urls;

      urls.shift();
      pollCount = 0;

      // If this is the last of the pending URLs, execute the callback and
      // start the next request in the queue (if any).
      if (!urls.length) {
        callback && callback.call(p.context, p.obj);
        pending[type] = null;
        queue[type].length && load(type);
      }
    }
  }

  /**
  Populates the env variable with user agent and feature test
  information.

  @method getEnv
  @private
  */
  function getEnv() {
    var ua = navigator.userAgent;

    env = {
      // True if this browser supports disabling async mode on dynamically
      // created script nodes. See
      // http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
      async: doc.createElement('script').async === true
    };

    (env.webkit = /AppleWebKit\//.test(ua))
      || (env.ie = /MSIE|Trident/.test(ua))
      || (env.opera = /Opera/.test(ua))
      || (env.gecko = /Gecko\//.test(ua))
      || (env.unknown = true);
  }

  /**
  Loads the specified resources, or the next resource of the specified type
  in the queue if no resources are specified. If a resource of the specified
  type is already being loaded, the new request will be queued until the
  first request has been finished.

  When an array of resource URLs is specified, those URLs will be loaded in
  parallel if it is possible to do so while preserving execution order. All
  browsers support parallel loading of CSS, but only Firefox and Opera
  support parallel loading of scripts. In other browsers, scripts will be
  queued and loaded one at a time to ensure correct execution order.

  @method load
  @param {String} type resource type ('css' or 'js')
  @param {String|Array} urls (optional) URL or array of URLs to load
  @param {Function} callback (optional) callback function to execute when the
    resource is loaded
  @param {Object} obj (optional) object to pass to the callback function
  @param {Object} context (optional) if provided, the callback function will
    be executed in this object's context
  @private
  */
  function load(type, urls, callback, obj, context) {
    var _finish = function () { finish(type); },
        isCSS   = type === 'css',
        nodes   = [],
        i, len, node, p, pendingUrls, url;

    env || getEnv();

    if (urls) {
      // If urls is a string, wrap it in an array. Otherwise assume it's an
      // array and create a copy of it so modifications won't be made to the
      // original.
      urls = typeof urls === 'string' ? [urls] : urls.concat();

      // Create a request object for each URL. If multiple URLs are specified,
      // the callback will only be executed after all URLs have been loaded.
      //
      // Sadly, Firefox and Opera are the only browsers capable of loading
      // scripts in parallel while preserving execution order. In all other
      // browsers, scripts must be loaded sequentially.
      //
      // All browsers respect CSS specificity based on the order of the link
      // elements in the DOM, regardless of the order in which the stylesheets
      // are actually downloaded.
      if (isCSS || env.async || env.gecko || env.opera) {
        // Load in parallel.
        queue[type].push({
          urls    : urls,
          callback: callback,
          obj     : obj,
          context : context
        });
      } else {
        // Load sequentially.
        for (i = 0, len = urls.length; i < len; ++i) {
          queue[type].push({
            urls    : [urls[i]],
            callback: i === len - 1 ? callback : null, // callback is only added to the last URL
            obj     : obj,
            context : context
          });
        }
      }
    }

    // If a previous load request of this type is currently in progress, we'll
    // wait our turn. Otherwise, grab the next item in the queue.
    if (pending[type] || !(p = pending[type] = queue[type].shift())) {
      return;
    }

    head || (head = doc.head || doc.getElementsByTagName('head')[0]);
    pendingUrls = [];

    // Copy original array (prevents CSS loaded array shift to mess up current loop index)
    for (i = 0, len = p.urls.length; i < len; ++i) {
      pendingUrls.push(p.urls[i]);
    }

    for (i = 0, len = pendingUrls.length; i < len; ++i) {
      url = pendingUrls[i];

      if (isCSS) {
          node = env.gecko ? createNode('style') : createNode('link', {
            href: url,
            rel : 'stylesheet'
          });
      } else {
        node = createNode('script', {src: url});
        node.async = false;
      }

      node.className = 'lazyload';
      node.setAttribute('charset', 'utf-8');

      if (env.ie && !isCSS && 'onreadystatechange' in node && !('draggable' in node)) {
        node.onreadystatechange = function () {
          if (/loaded|complete/.test(node.readyState)) {
            node.onreadystatechange = null;
            _finish();
          }
        };
      } else if (isCSS && (env.gecko || env.webkit)) {
        // Gecko and WebKit don't support the onload event on link nodes.
        if (env.webkit) {
          // In WebKit, we can poll for changes to document.styleSheets to
          // figure out when stylesheets have loaded.
          p.urls[i] = node.href; // resolve relative URLs (or polling won't work)
          pollWebKit();
        } else {
          // In Gecko, we can import the requested URL into a


<commit_before>/**
 * CareWheels - Login Controller
 *
 */
angular.module('careWheels')

  .controller('loginController', function($scope, $controller, User, $state, $ionicLoading, GroupInfo, $interval){

    var DOWNLOAD_INTERVAL = 1000 * 60 * 5; // constant interval for download, 5mins
    var dataDownload = $scope.$new();
    var dataAnalysis = $scope.$new();
    var loginTimeout = false;
    var loginIntervalSteps = 0;
    var popupTemplate = '<ion-spinner></ion-spinner>' + '
Contacting Server...
';
    var info = '';

    var credentials = angular.fromJson(window.localStorage['loginCredentials']);

    $controller('DownloadCtrl', {$scope : dataDownload});
    $controller('AnalysisCtrl', {$scope : dataAnalysis});

    $scope.rememberMe = false;
    $scope.logoImage = 'img/CareWheelsLogo.png';

    /**
     * Login function is called from app.js. This method
     * goes through the following steps
     *
     *      1. login into the carebank
     *      2. download data from sen.se
     *      3. analyze the data
     *      4. if success - redirect to group view, otherwise
     *         reload the login controller, and try again.
     *         (user will have to manually input credentials at this point)
     * */
    $scope.login = function(uname, passwd, rmbr) {
      User.login(uname, passwd, rmbr).then(function(response) {

        if (User.credentials()) {
          //pull up loading overlay so user knows App hasn't frozen
          $ionicLoading.show({ template: popupTemplate });

          // do the data download
          dataDownload.DownloadData();

          // store the interval promise in this variable
          var intervalPromise = $interval( function(){

            // its taking to long, timeout of the interval
            if (loginIntervalSteps > 120) // 120 * 500 = 1.5 min timeout
              loginTimeout = true;
            // keep track of how many times we step through the interval
            loginIntervalSteps++;

            // alright, lets try to analyze the data
            try {
              info = GroupInfo.groupInfo();
              dataAnalysis.AnalyzeData();
            }
            catch (Exception){ console.log('caught! during analyze data'); } // oh no...

            // were taking way to long, ABORT
            if (loginTimeout){
              loginTimeout = false;               // reset timeout flag
              loginIntervalSteps = 0;             // reset step count
              $interval.cancel(intervalPromise);  // break out of interval
              $ionicLoading.hide();               // kill the loading screen
              $state.reload();                    // reload the view (try again)
            }

            // sweet we got data, lets break out of this interval
            if (info[4].analysisData !== null){
              $interval.cancel(intervalPromise);  // break out of interval
              $ionicLoading.hide();               // hide loading screen
              scheduleDownload();                 // spin up a download/analyze scheduler
              $state.go('app.groupStatus');       // go to group view
            }
          }, 500 );
        }
      });
    };

    /**
     * Schedule a download every on an interval:
     *      1. download data
     *      2. wait
     *      3. analyze data
     * */
    function scheduleDownload(){
      $interval(function(){
        dataDownload.DownloadData();
        setTimeout(function(){
          dataAnalysis.AnalyzeData();
        }, 1000 * 60 * 2); // give the download two mins before analyze
      }, DOWNLOAD_INTERVAL ); // 5 min interval
    }

    if (credentials)
      $scope.login(credentials.username, credentials.password, true);

  });
<commit_msg>only allow analyze if sensor data present<commit_after>/**
 * CareWheels - Login Controller
 *
 */
angular.module('careWheels')

  .controller('loginController', function($scope, $controller, User, $state, $ionicLoading, GroupInfo, $interval){

    var DOWNLOAD_INTERVAL = 1000 * 60 * 5; // constant interval for download, 5mins
    var dataDownload = $scope.$new();
    var dataAnalysis = $scope.$new();
    var loginTimeout = false;
    var loginIntervalSteps = 0;
    var popupTemplate = '<ion-spinner></ion-spinner>' + '
Contacting Server...
';
    var info = '';

    var credentials = angular.fromJson(window.localStorage['loginCredentials']);

    $controller('DownloadCtrl', {$scope : dataDownload});
    $controller('AnalysisCtrl', {$scope : dataAnalysis});

    $scope.rememberMe = false;
    $scope.logoImage = 'img/CareWheelsLogo.png';

    /**
     * Login function is called from app.js. This method
     * goes through the following steps
     *
     *      1. login into the carebank
     *      2. download data from sen.se
     *      3. analyze the data
     *      4. if success - redirect to group view, otherwise
     *         reload the login controller, and try again.
     *         (user will have to manually input credentials at this point)
     * */
    $scope.login = function(uname, passwd, rmbr) {
      User.login(uname, passwd, rmbr).then(function(response) {

        if (User.credentials()) {
          //pull up loading overlay so user knows App hasn't frozen
          $ionicLoading.show({ template: popupTemplate });

          // do the data download
          dataDownload.DownloadData();

          // store the interval promise in this variable
          var intervalPromise = $interval( function(){
            // keep track of how many times we step through the interval
            loginIntervalSteps++;
            info = GroupInfo.groupInfo();

            // its taking to long, timeout of the interval
            if (loginIntervalSteps > 120) // 120 * 500 = 1.5 min timeout
              loginTimeout = true;

            // alright, lets try to analyze the data
            try {
              // only run analyze if sensor data is present
              if (info[4].sensorData != null){
                dataAnalysis.AnalyzeData();
              }

            }
            catch (Exception){ console.log('caught! during analyze data'); } // oh no...

            // were taking way to long, ABORT
            if (loginTimeout){
              loginTimeout = false;               // reset timeout flag
              loginIntervalSteps = 0;             // reset step count
              $interval.cancel(intervalPromise);  // break out of interval
              $ionicLoading.hide();               // kill the loading screen
              $state.reload();                    // reload the view (try again)
            }

            // sweet we got data, lets break out of this interval
            if (info[4].analysisData != null){
              $interval.cancel(intervalPromise);  // break out of interval
              $ionicLoading.hide();               // hide loading screen
              scheduleDownload();                 // spin up a download/analyze scheduler
              $state.go('app.groupStatus');       // go to group view
            }
          }, 500 );
        }
      });
    };

    /**
     * Schedule a download every on an interval:
     *      1. download data
     *      2. wait
     *      3. analyze data
     * */
    function scheduleDownload(){
      $interval(function(){
        dataDownload.DownloadData();
        setTimeout(function(){
          dataAnalysis.AnalyzeData();
        }, 1000 * 60 * 2); // give the download two mins before analyze
      }, DOWNLOAD_INTERVAL ); // 5 min interval
    }

    if (credentials)
      $scope.login(credentials.username, credentials.password, true);

  });
<|endoftext|>


<commit_before>'use strict';

(function () {
	var editorHasFocus = false;
	var commandIsActive = false;

	CKEDITOR.plugins.add('autoid', {
		init: function (editor) {
			var self = this;

			if (editor && !editor.config.autoid) {
				editor.config.autoid = {};
			}
			self.settings = editor.config.autoid;
			if (!self.settings) {
				self.settings = {};
			}

			editor.addCommand('autoid', {
				exec: function (editor) {
					if (!commandIsActive) {
						start();
					} else {
						stop();
					}
				},
				editorFocus: true
			});

		}
	});
})();
<commit_msg>Add start and stop functions<commit_after>'use strict';

(function () {
	var editorHasFocus = false;
	var commandIsActive = false;

	CKEDITOR.plugins.add('autoid', {
		init: function (editor) {
			var self = this;

			if (editor && !editor.config.autoid) {
				editor.config.autoid = {};
			}
			self.settings = editor.config.autoid;
			if (!self.settings) {
				self.settings = {};
			}

			editor.addCommand('autoid', {
				exec: function (editor) {
					if (!commandIsActive) {
						start();
					} else {
						stop();
					}
				},
				editorFocus: true
			});

			function start() {
				editor.getCommand('autoid').setState(CKEDITOR.TRISTATE_ON);
				commandIsActive = true;

				addAllAnchors();
			}

			function stop() {
				editor.getCommand('autoid').setState(CKEDITOR.TRISTATE_OFF);
				commandIsActive = false;
			}

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