<commit_before>/**
 * The Video Component
 *
 * @module aui-video
 */

var Lang = A.Lang,
    UA = A.UA,
    getClassName = A.getClassName,

    CSS_VIDEO_NODE = getClassName('video', 'node'),

    DEFAULT_PLAYER_PATH = A.config.base + 'aui-video/assets/player.swf?t=' + Lang.now(),

    DOC = A.config.doc,

    TPL_VIDEO = '<video id="{id}" controls="controls" class="' + CSS_VIDEO_NODE + '" {height} {width}></video>',
    TPL_VIDEO_FALLBACK = '

';

/**
 * A base class for Video.
 *
 * Check the [live demo](http://alloyui.com/examples/video/).
 *
 * @class A.Video
 * @extends A.Component
 * @param {Object} config Object literal specifying widget configuration
 *     properties.
 * @constructor
 * @include http://alloyui.com/examples/video/basic-markup.html
 * @include http://alloyui.com/examples/video/basic.js
 */
var Video = A.Component.create({
    /**
     * Static property provides a string to identify the class.
     *
     * @property NAME
     * @type String
     * @static
     */
    NAME: 'video',

    /**
     * Static property used to define the default attribute
     * configuration for the Video.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {

        /**
         * The required Flash version for the swf player
         *
         * @attribute flashPlayerVersion
         * @default '9,0,0,0'
         * @type String
         */
        flashPlayerVersion: {
            validator: Lang.isString,
            value: '9,0,0,0'
        },

        /**
         * Variables used by Flash player.
         *
         * @attribute flashVars
         * @default {}
         * @type Object
         */
        flashVars: {
            value: {}
        },

        /**
         * An additional list of attributes.
         *
         * @attribute fixedAttributes
         * @default {}
         * @type Object
         */
        fixedAttributes: {
            value: {}
        },

        /**
         * Sets the `aria-label` for Video.
         *
         * @attribute label
         * @type String
         */
        label: {
            value: 'Toggle play/pause with spacebar. Rewind with left arrow. Fast forward with right arrow. Increase volume with up arrow. Decrease volume with down arrow.',
            validator: Lang.isString
        },

        /**
         * URL (on .ogv format) used by Video to play.
         *
         * @attribute ogvUrl
         * @default ''
         * @type String
         */
        ogvUrl: {
            value: ''
        },

        /**
         * Image displayed before playback starts.
         *
         * @attribute poster
         * @default ''
         * @type String
         */
        poster: {
            value: ''
        },

        /**
         * If `true` the render phase will be automatically invoked
         * preventing the `.render()` manual call.
         *
         * @attribute render
         * @default true
         * @type Boolean
         */
        render: {
            value: true
        },

        /**
         * Sets the `aria-role` for Video.
         *
         * @attribute role
         * @type String
         */
        role: {
            value: 'application',
            validator: Lang.isString
        },

        /**
         * URL (on .swf format) used by Video to create
         * a fallback player with Flash.
         *
         * @attribute swfUrl
         * @default aui-video/assets/player.swf
         * @type String
         */
        swfUrl: {
            value: DEFAULT_PLAYER_PATH
        },

        /**
         * URL used by Video to play.
         *
         * @attribute url
         * @default ''
         * @type String
         */
        url: {
            value: ''
        },

        /**
         * Boolean indicating if use of the WAI-ARIA Roles and States
         * should be enabled.
         *
         * @attribute useARIA
         * @default true
         * @type Boolean
         */
        useARIA: {
            value: true
        }
    },

    /**
     * Static property used to define the attributes
     * for the bindUI lifecycle phase.
     *
     * @property BIND_UI_ATTRS
     * @type Array
     * @static
     */
    BIND_UI_ATTRS: ['url', 'poster', 'ogvUrl', 'swfUrl', 'fixedAttributes', 'flashVars'],

    /**
     * Static property used to define the attributes
     * for the syncUI lifecycle phase.
     *
     * @property SYNC_UI_ATTRS
     * @type Array
     * @static
     */
    SYNC_UI_ATTRS: ['url', 'poster', 'ogvUrl'],

    prototype: {

        /**
         * Render the Video component instance. Lifecycle.
         *
         * @method renderUI
         * @protected
         */
        renderUI: function() {
            var instance = this;

            instance._renderVideoTask = A.debounce(instance._renderVideo, 1, instance);
            instance._renderSwfTask = A.debounce(instance._renderSwf, 1, instance);

            instance._renderVideo(!instance.get('ogvUrl'));

            instance._video.on('play', function (event) {
                instance.fire('play', {
                    cropType: event.type
                });
            });
            instance._video.on('pause', function (event) {
                instance.fire('pause', {
                    cropType: event.type
                });
            });
        },

        /**
         * Bind the events on the Video UI. Lifecycle.
         *
         * @method bindUI
         * @protected
         */
        bindUI: function() {
            var instance = this;

            instance.publish(
                'videoReady', {
                    fireOnce: true
                }
            );

            instance.publish('play');
            instance.publish('pause');
        },

        syncUI: function() {
            var instance = this;

            if (instance.get('useARIA')) {
                instance.plug(A.Plugin.Aria, {
                    attributeNode: instance.get('contentBox'),
                    attributes: {
                        label: 'label'
                    },
                    roleName: instance.get('role'),
                    roleNode: instance.get('contentBox')
                });
            }
        },

        /**
         * Load video track.
         *
         * @method load
         */
        load: function() {
            var instance = this;

            if (instance._video.hasMethod('load')) {
                instance._video.invoke('load');
            }
        },

        /**
         * Pause video track.
         *
         * @method pause
         */
        pause: function() {
            var instance = this;

            if (instance._video.hasMethod('pause')) {
                instance._video.invoke('pause');
            }
        },

        /**
         * Play video track.
         *
         * @method play
         */
        play: function() {
            var instance = this;

            if (instance._video.hasMethod('play')) {
                instance._video.invoke('play');
            }
        },

        /**
         * Create `source` element
         * using passed type attribute.
         *
         * @method _createSource
         * @param type
         * @protected
         */
        _createSource: function(type) {
            var sourceNode = new A.Node(DOC.createElement('source'));

            sourceNode.attr('type', type);

            return sourceNode;
        },

        /**
         * Render SWF in DOM.
         *
         * @method _renderSwf
         * @protected
         */
        _renderSwf: function() {
            var instance = this;

            var swfUrl = instance.get('swfUrl');

            if (swfUrl) {
                var videoUrl = instance.get('url');
                var posterUrl = instance.get('poster');
                var flashVars = instance.get('flashVars');

                A.mix(
                    flashVars, {
                        controls: true,
                        src: videoUrl,
                        poster: posterUrl
                    }
                );

                var flashVarString = A.QueryString.stringify(flashVars);

                if (instance._swfId) {
                    instance._video.removeChild(A.one('#' + instance._swfId));
                }
                else {
                    instance._swfId = A.guid();
                }

                var tplObj = '<object id="' + instance._swfId + '" ';

                if (UA.ie) {
                    tplObj += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' +
                        'codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=' +
                        instance.get('flashPlayerVersion') + '" ';
                }
                else {
                    tplObj += 'type="application/x-shockwave-flash" data="' + swfUrl + '" ';
                }

                tplObj += 'height="100%" width="100%">';

                if (UA.ie) {
                    tplObj += '<param name="movie" value="' + swfUrl + '"/>';
                }

                var fixedAttributes = instance.get('fixedAttributes');

                for (var i in fixedAttributes) {
                    if (fixedAttributes.hasOwnProperty(i)) {
                        tplObj += '<param name="' + i + '" value="' + fixedAttributes[i] + '" />';
                    }
                }

                if (flashVarString) {
                    tplObj += '<param name="flashVars" value="' + flashVarString + '" />';
                }

                if (posterUrl !== '') {
                    tplObj += '';
                }

                tplObj += '</object>';

                instance._video.append(tplObj);
            }
        },

        /**
         * Render Video in DOM.
         *
         * @method _renderVideo
         * @param fallback
         * @protected
         */
        _renderVideo: function(fallback) {
            var instance = this,
                attrHeight,
                attrWidth,
                height,
                tpl,
                tplObj,
                video,
                width;

            tpl = TPL_VIDEO;

            if (UA.gecko && fallback) {
                tpl = TPL_VIDEO_FALLBACK;
            }
            else {
                attrHeight = '';
                attrWidth = '';

                height = instance.get('height');

                width = instance.get('width');

                if (height) {
                    attrHeight = 'height="' + height + '"';
                }

                if (width) {
                    attrWidth = 'width="' + width + '"';
                }
            }

            tplObj = Lang.sub(
                tpl, {
                    height: attrHeight,
                    id: A.guid(),
                    width: attrWidth
                }
            );

            video = A.Node.create(tplObj);

            instance.get('contentBox').append(video);

            instance._video = video;
        },

        /**
         * Set the `fixedAttributes` on the UI.
         *
         * @method _uiSetFixedAttributes
         * @param val
         * @protected
         */
        _uiSetFixedAttributes: function() {
            var instance = this;

            instance._renderSwfTask();
        },

        /**
         * Set the `flashVars` on the UI.
         *
         * @method _uiSetFlashVars
         * @param val
         * @protected
         */
        _uiSetFlashVars: function() {
            var instance = this;

            instance._renderSwfTask();
        },

        /**
         * Set the `ogvUrl` on the UI.
         *
         * @method _uiSetOgvUrl
         * @param val
         * @protected
         */
        _uiSetOgvUrl: function(val) {
            var instance = this;

            if (UA.gecko || UA.opera) {
                var video = instance._video;

                var usingVideo = instance._usingVideo();

                if ((!val && usingVideo) || (val && !usingVideo)) {
                    video.remove(true);

                    instance._renderVideoTask(!val);
                }

                if (!val) {
                    instance._renderSwfTask();
                }
                else {
                    var sourceOgv = instance._sourceOgv;

                    if (!sourceOgv) {
                        sourceOgv = instance._createSource('video/ogg; codecs="theora, vorbis"');

                        video.append(sourceOgv);

                        instance._sourceOgv = sourceOgv;
                    }

                    sourceOgv.attr('src', val);
                }
            }
        },

        /**
         * Set the `poster` on the UI.
         *
         * @method _uiSetPoster
         * @param val
         * @protected
         */
        _uiSetPoster: function(val) {
            var instance = this;

            var video = instance._video;

            if (instance._usingVideo()) {
                video.setAttribute('poster', val);
            }

            instance._renderSwfTask();
        },

        /**
         * Set the `swfUrl` on the UI.
         *
         * @method _uiSetSwfUrl
         * @param val
         * @protected
         */
        _uiSetSwfUrl: function() {
            var instance = this;

            instance._renderSwfTask();
        },

        /**
         * Set the `url` on the UI.
         *
         * @method _uiSetUrl
         * @param val
         * @protected
         */
        _uiSetUrl: function(val) {
            var instance = this;

            var ogvUrl = instance.get('ogvUrl');
            var video = instance._video;

            var sourceMp4 = instance._sourceMp4;

            if (UA.gecko && !instance._usingVideo()) {
                if (sourceMp4 !== null) {
                    sourceMp4.remove(true);

                    instance._sourceMp4 = null;
                }
            }
            else {
                if (video || !ogvUrl) {
                    if (!sourceMp4) {
                        sourceMp4 = instance._createSource('video/mp4;');

                        video.append(sourceMp4);

                        instance._sourceMp4 = sourceMp4;
                    }

                    sourceMp4.attr('src', val);
                }
            }

            instance._renderSwfTask();
        },

        /**
         * Check if it's a `video` node.
         *
         * @method _usingVideo
         * @protected
         */
        _usingVideo: function() {
            var instance = this;

            return (instance._video.get('nodeName').toLowerCase() === 'video');
        }
    }
});

A.Video = Video;
<commit_msg>AUI-1367 - removing label per conversation: http://git.io/BgKNzA<commit_after>/**
 * The Video Component
 *
 * @module aui-video
 */

var Lang = A.Lang,
    UA = A.UA,
    getClassName = A.getClassName,

    CSS_VIDEO_NODE = getClassName('video', 'node'),

    DEFAULT_PLAYER_PATH = A.config.base + 'aui-video/assets/player.swf?t=' + Lang.now(),

    DOC = A.config.doc,

    TPL_VIDEO = '<video id="{id}" controls="controls" class="' + CSS_VIDEO_NODE + '" {height} {width}></video>',
    TPL_VIDEO_FALLBACK = '

';

/**
 * A base class for Video.
 *
 * Check the [live demo](http://alloyui.com/examples/video/).
 *
 * @class A.Video
 * @extends A.Component
 * @param {Object} config Object literal specifying widget configuration
 *     properties.
 * @constructor
 * @include http://alloyui.com/examples/video/basic-markup.html
 * @include http://alloyui.com/examples/video/basic.js
 */
var Video = A.Component.create({
    /**
     * Static property provides a string to identify the class.
     *
     * @property NAME
     * @type String
     * @static
     */
    NAME: 'video',

    /**
     * Static property used to define the default attribute
     * configuration for the Video.
     *
     * @property ATTRS
     * @type Object
     * @static
     */
    ATTRS: {

        /**
         * The required Flash version for the swf player
         *
         * @attribute flashPlayerVersion
         * @default '9,0,0,0'
         * @type String
         */
        flashPlayerVersion: {
            validator: Lang.isString,
            value: '9,0,0,0'
        },

        /**
         * Variables used by Flash player.
         *
         * @attribute flashVars
         * @default {}
         * @type Object
         */
        flashVars: {
            value: {}
        },

        /**
         * An additional list of attributes.
         *
         * @attribute fixedAttributes
         * @default {}
         * @type Object
         */
        fixedAttributes: {
            value: {}
        },

        /**
         * URL (on .ogv format) used by Video to play.
         *
         * @attribute ogvUrl
         * @default ''
         * @type String
         */
        ogvUrl: {
            value: ''
        },

        /**
         * Image displayed before playback starts.
         *
         * @attribute poster
         * @default ''
         * @type String
         */
        poster: {
            value: ''
        },

        /**
         * If `true` the render phase will be automatically invoked
         * preventing the `.render()` manual call.
         *
         * @attribute render
         * @default true
         * @type Boolean
         */
        render: {
            value: true
        },

        /**
         * Sets the `aria-role` for Video.
         *
         * @attribute role
         * @type String
         */
        role: {
            value: 'application',
            validator: Lang.isString
        },

        /**
         * URL (on .swf format) used by Video to create
         * a fallback player with Flash.
         *
         * @attribute swfUrl
         * @default aui-video/assets/player.swf
         * @type String
         */
        swfUrl: {
            value: DEFAULT_PLAYER_PATH
        },

        /**
         * URL used by Video to play.
         *
         * @attribute url
         * @default ''
         * @type String
         */
        url: {
            value: ''
        },

        /**
         * Boolean indicating if use of the WAI-ARIA Roles and States
         * should be enabled.
         *
         * @attribute useARIA
         * @default true
         * @type Boolean
         */
        useARIA: {
            value: true
        }
    },

    /**
     * Static property used to define the attributes
     * for the bindUI lifecycle phase.
     *
     * @property BIND_UI_ATTRS
     * @type Array
     * @static
     */
    BIND_UI_ATTRS: ['url', 'poster', 'ogvUrl', 'swfUrl', 'fixedAttributes', 'flashVars'],

    /**
     * Static property used to define the attributes
     * for the syncUI lifecycle phase.
     *
     * @property SYNC_UI_ATTRS
     * @type Array
     * @static
     */
    SYNC_UI_ATTRS: ['url', 'poster', 'ogvUrl'],

    prototype: {

        /**
         * Render the Video component instance. Lifecycle.
         *
         * @method renderUI
         * @protected
         */
        renderUI: function() {
            var instance = this;

            instance._renderVideoTask = A.debounce(instance._renderVideo, 1, instance);
            instance._renderSwfTask = A.debounce(instance._renderSwf, 1, instance);

            instance._renderVideo(!instance.get('ogvUrl'));

            instance._video.on('play', function (event) {
                instance.fire('play', {
                    cropType: event.type
                });
            });
            instance._video.on('pause', function (event) {
                instance.fire('pause', {
                    cropType: event.type
                });
            });
        },

        /**
         * Bind the events on the Video UI. Lifecycle.
         *
         * @method bindUI
         * @protected
         */
        bindUI: function() {
            var instance = this;

            instance.publish(
                'videoReady', {
                    fireOnce: true
                }
            );

            instance.publish('play');
            instance.publish('pause');
        },

        syncUI: function() {
            var instance = this;

            if (instance.get('useARIA')) {
                instance.plug(A.Plugin.Aria, {
                    roleName: instance.get('role'),
                    roleNode: instance.get('contentBox')
                });
            }
        },

        /**
         * Load video track.
         *
         * @method load
         */
        load: function() {
            var instance = this;

            if (instance._video.hasMethod('load')) {
                instance._video.invoke('load');
            }
        },

        /**
         * Pause video track.
         *
         * @method pause
         */
        pause: function() {
            var instance = this;

            if (instance._video.hasMethod('pause')) {
                instance._video.invoke('pause');
            }
        },

        /**
         * Play video track.
         *
         * @method play
         */
        play: function() {
            var instance = this;

            if (instance._video.hasMethod('play')) {
                instance._video.invoke('play');
            }
        },

        /**
         * Create `source` element
         * using passed type attribute.
         *
         * @method _createSource
         * @param type
         * @protected
         */
        _createSource: function(type) {
            var sourceNode = new A.Node(DOC.createElement('source'));

            sourceNode.attr('type', type);

            return sourceNode;
        },

        /**
         * Render SWF in DOM.
         *
         * @method _renderSwf
         * @protected
         */
        _renderSwf: function() {
            var instance = this;

            var swfUrl = instance.get('swfUrl');

            if (swfUrl) {
                var videoUrl = instance.get('url');
                var posterUrl = instance.get('poster');
                var flashVars = instance.get('flashVars');

                A.mix(
                    flashVars, {
                        controls: true,
                        src: videoUrl,
                        poster: posterUrl
                    }
                );

                var flashVarString = A.QueryString.stringify(flashVars);

                if (instance._swfId) {
                    instance._video.removeChild(A.one('#' + instance._swfId));
                }
                else {
                    instance._swfId = A.guid();
                }

                var tplObj = '<object id="' + instance._swfId + '" ';

                if (UA.ie) {
                    tplObj += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' +
                        'codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=' +
                        instance.get('flashPlayerVersion') + '" ';
                }
                else {
                    tplObj += 'type="application/x-shockwave-flash" data="' + swfUrl + '" ';
                }

                tplObj += 'height="100%" width="100%">';

                if (UA.ie) {
                    tplObj += '<param name="movie" value="' + swfUrl + '"/>';
                }

                var fixedAttributes = instance.get('fixedAttributes');

                for (var i in fixedAttributes) {
                    if (fixedAttributes.hasOwnProperty(i)) {
                        tplObj += '<param name="' + i + '" value="' + fixedAttributes[i] + '" />';
                    }
                }

                if (flashVarString) {
                    tplObj += '<param name="flashVars" value="' + flashVarString + '" />';
                }

                if (posterUrl !== '') {
                    tplObj += '';
                }

                tplObj += '</object>';

                instance._video.append(tplObj);
            }
        },

        /**
         * Render Video in DOM.
         *
         * @method _renderVideo
         * @param fallback
         * @protected
         */
        _renderVideo: function(fallback) {
            var instance = this,
                attrHeight,
                attrWidth,
                height,
                tpl,
                tplObj,
                video,
                width;

            tpl = TPL_VIDEO;

            if (UA.gecko && fallback) {
                tpl = TPL_VIDEO_FALLBACK;
            }
            else {
                attrHeight = '';
                attrWidth = '';

                height = instance.get('height');

                width = instance.get('width');

                if (height) {
                    attrHeight = 'height="' + height + '"';
                }

                if (width) {
                    attrWidth = 'width="' + width + '"';
                }
            }

            tplObj = Lang.sub(
                tpl, {
                    height: attrHeight,
                    id: A.guid(),
                    width: attrWidth
                }
            );

            video = A.Node.create(tplObj);

            instance.get('contentBox').append(video);

            instance._video = video;
        },

        /**
         * Set the `fixedAttributes` on the UI.
         *
         * @method _uiSetFixedAttributes
         * @param val
         * @protected
         */
        _uiSetFixedAttributes: function() {
            var instance = this;

            instance._renderSwfTask();
        },

        /**
         * Set the `flashVars` on the UI.
         *
         * @method _uiSetFlashVars
         * @param val
         * @protected
         */
        _uiSetFlashVars: function() {
            var instance = this;

            instance._renderSwfTask();
        },

        /**
         * Set the `ogvUrl` on the UI.
         *
         * @method _uiSetOgvUrl
         * @param val
         * @protected
         */
        _uiSetOgvUrl: function(val) {
            var instance = this;

            if (UA.gecko || UA.opera) {
                var video = instance._video;

                var usingVideo = instance._usingVideo();

                if ((!val && usingVideo) || (val && !usingVideo)) {
                    video.remove(true);

                    instance._renderVideoTask(!val);
                }

                if (!val) {
                    instance._renderSwfTask();
                }
                else {
                    var sourceOgv = instance._sourceOgv;

                    if (!sourceOgv) {
                        sourceOgv = instance._createSource('video/ogg; codecs="theora, vorbis"');

                        video.append(sourceOgv);

                        instance._sourceOgv = sourceOgv;
                    }

                    sourceOgv.attr('src', val);
                }
            }
        },

        /**
         * Set the `poster` on the UI.
         *
         * @method _uiSetPoster
         * @param val
         * @protected
         */
        _uiSetPoster: function(val) {
            var instance = this;

            var video = instance._video;

            if (instance._usingVideo()) {
                video.setAttribute('poster', val);
            }

            instance._renderSwfTask();
        },

        /**
         * Set the `swfUrl` on the UI.
         *
         * @method _uiSetSwfUrl
         * @param val
         * @protected
         */
        _uiSetSwfUrl: function() {
            var instance = this;

            instance._renderSwfTask();
        },

        /**
         * Set the `url` on the UI.
         *
         * @method _uiSetUrl
         * @param val
         * @protected
         */
        _uiSetUrl: function(val) {
            var instance = this;

            var ogvUrl = instance.get('ogvUrl');
            var video = instance._video;

            var sourceMp4 = instance._sourceMp4;

            if (UA.gecko && !instance._usingVideo()) {
                if (sourceMp4 !== null) {
                    sourceMp4.remove(true);

                    instance._sourceMp4 = null;
                }
            }
            else {
                if (video || !ogvUrl) {
                    if (!sourceMp4) {
                        sourceMp4 = instance._createSource('video/mp4;');

                        video.append(sourceMp4);

                        instance._sourceMp4 = sourceMp4;
                    }

                    sourceMp4.attr('src', val);
                }
            }

            instance._renderSwfTask();
        },

        /**
         * Check if it's a `video` node.
         *
         * @method _usingVideo
         * @protected
         */
        _usingVideo: function() {
            var instance = this;

            return (instance._video.get('nodeName').toLowerCase() === 'video');
        }
    }
});

A.Video = Video;
<|endoftext|>


<commit_before>import Component from '@ember/component';
import layout from './template';
import { computed, get, set } from '@ember/object';
import { inject as service } from '@ember/service';
import { all as PromiseAll } from 'rsvp';
import $ from 'jquery';

export default Component.extend({
  layout,
  globalStore:     service(),
  access:          service(),

  //new

  //todo verify all of these are used
  editing:         false,
  memberArray:     null,
  memberConfig:    null,
  model:           null,
  primaryResource: null,
  roles:           null,
  type:            null,
  users:           null,
  creator:         null,
  showCreator:     true,
  toAddCustom: null,
  _bindings: null,

  init() {
    this._super(...arguments);
    this.buildUpdateList(get(this,'primaryResource'))
    this.sendAction('registerHook', this.saveMembers.bind(this), 'saveMembers');
  },

  buildUpdateList(resource) {
    let bindings = set(this, 'currentBindings', get(resource, 'roleTemplateBindings').filter(b => b.name !== 'creator'));

    if (get(bindings, 'length') >= 1) {
      //editing
      let mOut = [];

      set(this, 'memberArray', bindings.forEach((b) => {

        let  match = mOut.findBy('principalId', get(b, 'principalIdReference')) ;

        if (match) {

          match.bindings.push(get(b, 'roleTemplateId'));
          match.preEditBindings.push({roleTemplateId: get(b, 'roleTemplateId'), id: get(b, 'id')});

        } else {

          match = {
            principalId: get(b, 'principalIdReference'),
            bindings: [get(b, 'roleTemplateId')],
            preEditBindings: [{roleTemplateId: get(b, 'roleTemplateId'), id: get(b, 'id')}],
            isCustom: get(b, 'isCustom'),
          };

          mOut.push(match);

        }

      }));

      set(this, 'memberArray', mOut);

    } else {

      set(this, 'memberArray', []);
    }

  },

  createRole(inConfig={}) {

    let config = $().extend(true, get(this, 'memberConfig'), inConfig);
    let type = get(this, 'primaryResource.type');

    // set(config, 'subjectKind', 'User');
    set(config, `${type}Id`, get(this, `primaryResource.id`));

    return get(this,'globalStore').createRecord(config);

  },

  saveMembers: function() {
    const memberArray = get(this, 'memberArray');
    const currentBindings = get(this, 'currentBindings');

    const add = [];
    const remove = [];
    const update = [];

    memberArray.forEach((member) => {
      let bindings = get(member, 'bindings');
      let preEditBindings = get(member, 'preEditBindings') || [];

      if (bindings.length > 0) {

        // update
        if (bindings.length === 1 && get(member, 'preEditBindings.length') === 1) {

          let toUpdate = get(preEditBindings, 'firstObject');
          let neuRT = get(bindings, 'firstObject');
          let match = get(this, 'currentBindings').findBy('id', get(toUpdate, 'id'));

          if ( match && get(match, 'roleTemplateId') !== neuRT) {
            set(match, 'roleTemplateId', neuRT);
            update.push(match);
          }

        } else {

          //bindings to add
          bindings.forEach((memberBinding) => {
            if (!preEditBindings.findBy('roleTemplateId', memberBinding)) {
              if (get(member, 'principalId')) {
                let principalId = get(member, 'principalId');
                let type = get(member, 'memberType');
                if (type === 'group') {
                  add.push(this.createRole({groupPrincipalId: principalId, roleTemplateId: memberBinding, subjectKind: 'Group'}));
                } else {
                  add.push(this.createRole({userPrincipalId: principalId, roleTemplateId: memberBinding, subjectKind: 'User'}));
                }
              }
            }
          });

          preEditBindings.forEach((peBinding) => {
            if (!bindings.includes(get(peBinding, 'roleTemplateId'))) {
              remove.push(currentBindings.findBy('id', get(peBinding, 'id')));
            }
          });

        }
      } else {
        //remove
        preEditBindings.forEach((peBinding) => {
          remove.push(currentBindings.findBy('id', get(peBinding, 'id')));
        });
      }

    });
    // console.log(add, update, remove);

    return PromiseAll(add.map(x => x.save())).then(() => {
      return PromiseAll(update.map(x => x.save())).then(() => {
        return PromiseAll(remove.map(x => x.delete())).then(() => {
          if ( this.isDestroyed || this.isDestroying ) {
            return;
          }

          set(this, 'memberArray', []);
          return get(this, 'primaryResource');
        });
      });
    });
  },

  actions: {
    cancel() {
      this.goBack();
    },

    addMember(/* kind */) {
      let out = {
        principalId: null,
        bindings: [],
      }
      get(this,'memberArray').pushObject(out);
    },

    removeMember(obj) {
      set(obj, 'pendingDelete', true);
      set(obj, 'bindings', []);
    },
  },

  filteredRoles: computed('roles.[]', function() {
    let pt = get(this, 'type');
    return get(this, 'roles').filterBy('hidden', false).filter(r => get(r, 'context') === pt || get(r, 'context') === '' || !get(r, 'context') ).sortBy('displayName');
  }),

  filteredUsers: computed('users.@each.{id,state}', function() {
    return get(this, 'users').sortBy('displayName');
  }),

});
<commit_msg>Start with a fresh principal every time<commit_after>import Component from '@ember/component';
import layout from './template';
import { computed, get, set } from '@ember/object';
import { inject as service } from '@ember/service';
import { all as PromiseAll } from 'rsvp';
import $ from 'jquery';

export default Component.extend({
  layout,
  globalStore:     service(),
  access:          service(),

  //new

  //todo verify all of these are used
  editing:         false,
  memberArray:     null,
  memberConfig:    null,
  model:           null,
  primaryResource: null,
  roles:           null,
  type:            null,
  users:           null,
  creator:         null,
  showCreator:     true,
  toAddCustom: null,
  _bindings: null,

  init() {
    this._super(...arguments);
    this.buildUpdateList(get(this,'primaryResource'))
    this.sendAction('registerHook', this.saveMembers.bind(this), 'saveMembers');
  },

  buildUpdateList(resource) {
    let bindings = set(this, 'currentBindings', get(resource, 'roleTemplateBindings').filter(b => b.name !== 'creator'));

    if (get(bindings, 'length') >= 1) {
      //editing
      let mOut = [];

      set(this, 'memberArray', bindings.forEach((b) => {

        let  match = mOut.findBy('principalId', get(b, 'principalIdReference')) ;

        if (match) {

          match.bindings.push(get(b, 'roleTemplateId'));
          match.preEditBindings.push({roleTemplateId: get(b, 'roleTemplateId'), id: get(b, 'id')});

        } else {

          match = {
            principalId: get(b, 'principalIdReference'),
            bindings: [get(b, 'roleTemplateId')],
            preEditBindings: [{roleTemplateId: get(b, 'roleTemplateId'), id: get(b, 'id')}],
            isCustom: get(b, 'isCustom'),
          };

          mOut.push(match);

        }

      }));

      set(this, 'memberArray', mOut);

    } else {

      set(this, 'memberArray', []);
    }

  },

  createRole(inConfig={}) {

    let config = $().extend(true, {}, get(this, 'memberConfig'), inConfig);
    let type = get(this, 'primaryResource.type');

    // set(config, 'subjectKind', 'User');
    set(config, `${type}Id`, get(this, `primaryResource.id`));

    return get(this,'globalStore').createRecord(config);

  },

  saveMembers: function() {
    const memberArray = get(this, 'memberArray');
    const currentBindings = get(this, 'currentBindings');

    const add = [];
    const remove = [];
    const update = [];

    memberArray.forEach((member) => {
      let bindings = get(member, 'bindings');
      let preEditBindings = get(member, 'preEditBindings') || [];

      if (bindings.length > 0) {

        // update
        if (bindings.length === 1 && get(member, 'preEditBindings.length') === 1) {

          let toUpdate = get(preEditBindings, 'firstObject');
          let neuRT = get(bindings, 'firstObject');
          let match = get(this, 'currentBindings').findBy('id', get(toUpdate, 'id'));

          if ( match && get(match, 'roleTemplateId') !== neuRT) {
            set(match, 'roleTemplateId', neuRT);
            update.push(match);
          }

        } else {

          //bindings to add
          bindings.forEach((memberBinding) => {
            if (!preEditBindings.findBy('roleTemplateId', memberBinding)) {
              if (get(member, 'principalId')) {
                let principalId = get(member, 'principalId');
                let type = get(member, 'memberType');
                if (type === 'group') {
                  add.push(this.createRole({groupPrincipalId: principalId, roleTemplateId: memberBinding, subjectKind: 'Group'}));
                } else {
                  add.push(this.createRole({userPrincipalId: principalId, roleTemplateId: memberBinding, subjectKind: 'User'}));
                }
              }
            }
          });

          preEditBindings.forEach((peBinding) => {
            if (!bindings.includes(get(peBinding, 'roleTemplateId'))) {
              remove.push(currentBindings.findBy('id', get(peBinding, 'id')));
            }
          });

        }
      } else {
        //remove
        preEditBindings.forEach((peBinding) => {
          remove.push(currentBindings.findBy('id', get(peBinding, 'id')));
        });
      }

    });
    // console.log(add, update, remove);

    return PromiseAll(add.map(x => x.save())).then(() => {
      return PromiseAll(update.map(x => x.save())).then(() => {
        return PromiseAll(remove.map(x => x.delete())).then(() => {
          if ( this.isDestroyed || this.isDestroying ) {
            return;
          }

          set(this, 'memberArray', []);
          return get(this, 'primaryResource');
        });
      });
    });
  },

  actions: {
    cancel() {
      this.goBack();
    },

    addMember(/* kind */) {
      let out = {
        principalId: null,
        bindings: [],
      }
      get(this,'memberArray').pushObject(out);
    },

    removeMember(obj) {
      set(obj, 'pendingDelete', true);
      set(obj, 'bindings', []);
    },
  },

  filteredRoles: computed('roles.[]', function() {
    let pt = get(this, 'type');
    return get(this, 'roles').filterBy('hidden', false).filter(r => get(r, 'context') === pt || get(r, 'context') === '' || !get(r, 'context') ).sortBy('displayName');
  }),

  filteredUsers: computed('users.@each.{id,state}', function() {
    return get(this, 'users').sortBy('displayName');
  }),

});
<|endoftext|>


<commit_before>
const NULL_OBJECT = null;

class Test {
    constructor() {

    }

}

Test.prototype.getXXX = function() {

};

const GameObject = function() {
    this.$objID = GameObject.ID++;
};

GameObject.prototype.getID = function() {return this.$objID;};
GameObject.prototype.setID = function(id) {this.$objID = id;};

GameObject.prototype.getRef = function() {
    return {'#objRef': this.$objID};
};

GameObject.prototype.getRefAndVal = function() {
    return {'#objRef': this.$objID, value: this};
};

GameObject.prototype.serialize = function() {
    const json = {
        '#proto': GameObject.getProtoName(this)
    };
    for (const key in this) {
        if (this.hasOwnProperty(key)) {
            json[key] = GameObject.serialize(this[key]);
        }
    }
    return json;
};
GameObject.ID = 1;

GameObject.deref = function(objRef) {
    if (objRef) {
        return objRef.value;
    }
    return NULL_OBJECT;
};

GameObject.createObjectID = function() {
    return GameObject.ID++;
};

GameObject.getProtoName = function(obj) {
    return Object.getPrototypeOf(obj).constructor.name;
};

GameObject.isPrimitive = function(obj) {
    return typeof obj !== 'object';
};

GameObject.serialize = function(obj) {
    if (GameObject.isPrimitive(obj)) {
        return obj;
    }
    else if (Array.isArray(obj)) {
        const arr = [];
        obj.forEach(val => {
            arr.push(GameObject.serialize(val));
        });
        return arr;
    }
    else if (obj.$objID) {
        return obj.serialize();
    }
    else if (obj.toJSON) {
        return obj.toJSON(); // Legacy support
    }
    else if (obj['#objRef']) {
        return {'#objRef': obj.obj.getID()};
    }
    else {
        return obj;
    }
};

GameObject.deserialize = function(input, namespace, seenObjs = {}) {
    const obj = new namespace[input['#proto']]();
    for (const key in input) {
        if (input.hasOwnProperty(input)) {
            const value = input[key];
            if (GameObject.isPrimitive(value)) {
                obj[key] = value;
            }
            else if (Array.isArray(value)) {
                const arr = [];
                obj.forEach(val => {
                    arr.push(GameObject.deserialize(val, namespace, seenObjs));
                });
                obj[key] = arr;
            }
            else if (value.$objID) {
                if (!seenObjs.hasOwnProperty(value.$objID)) {
                    obj[key] = GameObject.deserialize(value, seenObjs);
                }
                else {
                    obj[key] = seenObjs[value.$objID];
                }
            }
            else {
                obj[key] = value; // Just assign simple object
            }
        }
    }
    delete obj['#proto'];
    /* eslint: enable */
    return obj;
};

module.exports = GameObject;
<commit_msg>Changed #objRef to .<commit_after>
const NULL_OBJECT = null;

class Test {
    constructor() {

    }

}

Test.prototype.getXXX = function() {

};

const GameObject = function() {
    this.$objID = GameObject.ID++;
};

GameObject.prototype.getID = function() {return this.$objID;};
GameObject.prototype.setID = function(id) {this.$objID = id;};

GameObject.prototype.getObjRef = function() {
    return {$objRef: {type: 'object', id: this.$objID}};
};

GameObject.prototype.getRefAndVal = function() {
    const refObj = this.getObjRef();
    refObj.value = this;
    return refObj;
};

GameObject.prototype.serialize = function() {
    const json = {
        $proto: GameObject.getProtoName(this)
    };
    for (const key in this) {
        if (this.hasOwnProperty(key)) {
            json[key] = GameObject.serialize(this[key]);
        }
    }
    return json;
};
GameObject.ID = 1;

GameObject.deref = function(objRef) {
    if (objRef) {
        return objRef.value;
    }
    return NULL_OBJECT;
};

GameObject.createObjectID = function() {
    return GameObject.ID++;
};

GameObject.getProtoName = function(obj) {
    return Object.getPrototypeOf(obj).constructor.name;
};

GameObject.isPrimitive = function(obj) {
    return typeof obj !== 'object';
};

GameObject.serialize = function(obj) {
    if (GameObject.isPrimitive(obj)) {
        return obj;
    }
    else if (Array.isArray(obj)) {
        const arr = [];
        obj.forEach(val => {
            arr.push(GameObject.serialize(val));
        });
        return arr;
    }
    else if (obj.$objID) {
        return obj.serialize();
    }
    else if (obj.toJSON) {
        return obj.toJSON(); // Legacy support
    }
    else if (obj.$objRef) {
        return {$objRef: obj.value.getID()};
    }
    else {
        return obj;
    }
};

function deserialize(input, namespace, seenObjs = {}) {
    const obj = new namespace[input.$proto]();
    for (const key in input) {
        if (input.hasOwnProperty(key)) {
            const value = input[key];
            if (GameObject.isPrimitive(value)) {
                obj[key] = value;
            }
            else if (Array.isArray(value)) {
                const arr = [];
                obj.forEach(val => {
                    arr.push(deserialize(val, namespace, seenObjs));
                });
                obj[key] = arr;
            }
            else if (value.$objID) {
                if (!seenObjs.hasOwnProperty(value.$objID)) {
                    obj[key] = deserialize(value, namespace, seenObjs);
                }
                else {
                    obj[key] = seenObjs[value.$objID];
                }
            }
            else {
                obj[key] = value; // Just assign simple object
            }
        }
    }
    delete obj.$proto;
    /* eslint: enable */
    return obj;
}
GameObject.deserialize = deserialize;

module.exports = GameObject;
<|endoftext|>


<commit_before>/**
 * This is where we load and initialize components of our app.
 */
define("app", function (require) {
  "use strict";

  var $ = require("jquery");

  // let's get going
  var Finder   = require("finder/Finder");
  var Donate   = require("donate/Donate");
  var Revealer = require("revealer/Revealer");
  var Swapper  = require("swapper/Swapper");
  var Reportback = require("campaign/Reportback");

  // Initialize modules on load
  require("neue/main");
  require("campaign/SchoolFinder");
  require("campaign/sources");
  require("campaign/tips");
  require("campaign/tabs");
  require("campaign/ImageUploader");
  require("validation/auth");
  require("validation/reportback");
  require("validation/address");
  require("validation/donate");
  require("Analytics");
  require("tiles");

  $(document).ready(function () {
    var $body = $("body");

    var $campaignFinderForm = $(".js-finder-form");
    if( $campaignFinderForm.length ) {
      var $results = $(".js-campaign-results");
      var $blankSlate = $(".js-campaign-blankslate");
      Finder.init($campaignFinderForm, $results, $blankSlate);
    }

    var $donateForm = $("#modal--donate-form");
    if( $donateForm.length ) {
      Donate.init();
    }

    var $revealGalleries = $body.find('[data-show-more="true"]');
    /**
     * Show hide large galleries.
     */
    if ($revealGalleries.length > 0) {
      var galleryList = [];

      $revealGalleries.each(function (index, gallery) {
        var $gallery = $(gallery);
        var $tiles = $gallery.find("li");
        var isMosaicGallery = $gallery.hasClass('-mosaic');
        var initialItems = isMosaicGallery.hasClass('-mosaic') ? 5 : 6;
        var stepItems = isMosaicGallery.hasClass('-mosaic') ? 8 : 6;

        if ($tiles.length > initialItems) {
          galleryList[index] = new Revealer($gallery, $tiles, "gallery", initialItems, stepItems);
        }
      });
    }

    var $mosaicGalleries = $body.find(".gallery.-mosaic");

    /**
     * Swap low resolution images for feature tiles in mosaic gallery
     * with higher resolution version from tablet size up.
     */
    if ($mosaicGalleries.length && $mosaicGalleries.hasClass("-featured")) {
      var swappedImagesList = [];

      $(window).on("resize", _.debounce(function () {

        if (window.matchMedia("(min-width: 768px)").matches && !swappedImagesList.length) {
          $mosaicGalleries.each(function (index, gallery) {
            var $featureTile = $(this).find("li").first();
            swappedImagesList[index] = new Swapper($featureTile);
          });
        }
      }, 500));

    }

    /**
     * Initialize Reportback js.
     */
    var $reportback = $body.find("#reportback");

    if ($reportback.length) {
      Reportback.init($reportback);
    }

    $("html").addClass("js-ready");

  });

});
<commit_msg>Removed hasClas class<commit_after>/**
 * This is where we load and initialize components of our app.
 */
define("app", function (require) {
  "use strict";

  var $ = require("jquery");

  // let's get going
  var Finder   = require("finder/Finder");
  var Donate   = require("donate/Donate");
  var Revealer = require("revealer/Revealer");
  var Swapper  = require("swapper/Swapper");
  var Reportback = require("campaign/Reportback");

  // Initialize modules on load
  require("neue/main");
  require("campaign/SchoolFinder");
  require("campaign/sources");
  require("campaign/tips");
  require("campaign/tabs");
  require("campaign/ImageUploader");
  require("validation/auth");
  require("validation/reportback");
  require("validation/address");
  require("validation/donate");
  require("Analytics");
  require("tiles");

  $(document).ready(function () {
    var $body = $("body");

    var $campaignFinderForm = $(".js-finder-form");
    if( $campaignFinderForm.length ) {
      var $results = $(".js-campaign-results");
      var $blankSlate = $(".js-campaign-blankslate");
      Finder.init($campaignFinderForm, $results, $blankSlate);
    }

    var $donateForm = $("#modal--donate-form");
    if( $donateForm.length ) {
      Donate.init();
    }

    var $revealGalleries = $body.find('[data-show-more="true"]');
    /**
     * Show hide large galleries.
     */
    if ($revealGalleries.length > 0) {
      var galleryList = [];

      $revealGalleries.each(function (index, gallery) {
        var $gallery = $(gallery);
        var $tiles = $gallery.find("li");
        var isMosaicGallery = $gallery.hasClass('-mosaic');
        var initialItems = isMosaicGallery ? 5 : 6;
        var stepItems = isMosaicGallery ? 8 : 6;

        if ($tiles.length > initialItems) {
          galleryList[index] = new Revealer($gallery, $tiles, "gallery", initialItems, stepItems);
        }
      });
    }

    var $mosaicGalleries = $body.find(".gallery.-mosaic");

    /**
     * Swap low resolution images for feature tiles in mosaic gallery
     * with higher resolution version from tablet size up.
     */
    if ($mosaicGalleries.length && $mosaicGalleries.hasClass("-featured")) {
      var swappedImagesList = [];

      $(window).on("resize", _.debounce(function () {

        if (window.matchMedia("(min-width: 768px)").matches && !swappedImagesList.length) {
          $mosaicGalleries.each(function (index, gallery) {
            var $featureTile = $(this).find("li").first();
            swappedImagesList[index] = new Swapper($featureTile);
          });
        }
      }, 500));

    }

    /**
     * Initialize Reportback js.
     */
    var $reportback = $body.find("#reportback");

    if ($reportback.length) {
      Reportback.init($reportback);
    }

    $("html").addClass("js-ready");

  });

});
<|endoftext|>


<commit_before>// @flow
import {SLOVAKIA_COORDINATES} from '../constants'
import type {ObjectMap} from '../types/commonTypes'

export type Candidate = {
  id: number,
  eid: number,
  customer: string,
  ico: string,
  name: string,
  title: string,
  text: string,
  price: number,
  score: number,
}

export type Notice = {|
  id: number,
  eid: number,
  curstomer: string,
  bulletin_number: number,
  title: string,
  text: string,
  price: number,
  price_stdev: number,
  bulletin_day: number,
  bulletin_month: number,
  bulletin_year: number,
  bulletin_date: string, // string representation of day/month/year
  kandidati: Array<Candidate | []>,
  price_num: number,
  price_avg: number,
  customer: string,
|}

export type PoliticianDetail = {|
  picture: string,
  party_nom: string,
  surname: string,
  party_abbreviation: string,
  firstname: string,
  title: string,
  term_finish: number,
  office_name_male: string,
  term_start: number,
  office_name_female: string,
|}

export type Politician = {|
  num_fields_gardens: number,
  picture: string,
  surname: string,
  party_abbreviation: string,
  firstname: string,
  title: string,
  term_finish: number,
  party_nom: string,
  num_houses_flats: number,
  office_name_male: string,
  num_others: number,
  term_start: number,
  office_name_female: string,
  id: number,
|}

export type CadastralData = {|
  lon: number,
  cadastralunitcode: number,
  landusename: string,
  cadastralunitname: string,
  parcelno: string,
  lat: number,
  foliono: number,
|}

export type AssetDeclaration = {|
  compensations: ?string,
  source: string,
  year: number,
  offices_other: string,
  other_income: ?string,
  income: string,
  movable_assets: string,
  unmovable_assets: string,
|}

export type Entity = {
  eid: string,
  lng: string,
  lat: string,
  selected: string,
  title: string,
  size: string,
  level: string,
  visible: string,
  ds: Array<any>,
  name: string,
}

export type CompanyStat = {
  datum_vzniku: string,
  datum_zaniku: string,
  zisk2016: ?number,
  trzby2016: ?number,
  zisk2015: ?number,
  trzby2015: ?number,
  zisk2014: ?number,
  trzby2014: ?number,
  trzby2016: ?number,
  zamestnanci2016: string,
  zamestnanci2015: string,
}

export type Company = {
  id: number,
  eid: number,
  zrsr_data: Array<any>,
  company_stats: Array<CompanyStat>,
  contracts: Array<any>,
  new_orsr_data: Array<any>,
  sponzori_stran_data: Array<any>,
  related: Array<any>,
  auditori_data: Array<any>,
  audiovizfond_data: Array<any>,
  entities: Array<Entity>,
  firmy_data: Array<any>,
  total_contracts: number,
  advokati_data: Array<any>,
  nadacie_data: Array<any>,
  orsresd_data: Array<any>,
  politicians_data: Array<any>,
  stranicke_prispevky_data: Array<any>,
  uzivatelia_vyhody_ludia_data: Array<any>,
}

export type NoticeMap = {[string]: Notice}

export type CompanyMap = {[string]: Company}

export type GeolocationPoint = {
  lat: number,
  lng: number,
}

export type MapBounds = {
  ne: GeolocationPoint,
  nw: GeolocationPoint,
  se: GeolocationPoint,
  sw: GeolocationPoint,
}

export type MapOptions = {
  center: [number, number],
  zoom: number,
  bounds: ?MapBounds,
}

export type Center = {lat: number, lng: number}

export type SearchedEntity = {
  eids: string[],
  id: string,
}

export type Connections = {
  entities: {[string]: Entity},
  detail: {[string]: {ids: string[]}},
  entityDetails: {
    [string]: {
      name: string,
      data: any, //TODO: TBD
    },
  },
}

export type Address = {
  address_id: number,
  lat: number,
  lng: number,
}

// Entity returned from api call getEntitiesAtAddressId
export type NewEntity = {
  id: number,
  name: string,
}

export type NewEntityState = NewEntity & {addressId: number}

export type RelatedEntity = {
  name: string,
  stakeholder_type_id: number,
  eid: number,
  address: string,
  lat: number,
  lng: number,
}

export type EntityDetails = {
  name: string,
  related: RelatedEntity[],
  address: string,
  lat: number,
  lng: number,
  companyinfo: {
    established_on: string,
    ico: number,
    terminated_on: string,
  },
}

// Each property must begin with '+' to be made read only and each object
// must be enclosed in '|' so no properties can be added to state at runtime
export type State = {|
  +count: number,
  +companies: CompanyMap,
  +notices: {|
    +list: ObjectMap<Notice>,
    +details: ObjectMap<Notice>,
    +searchQuery: string,
  |},
  +profile: {|
    +list: ObjectMap<Politician>,
    +details: ObjectMap<PoliticianDetail>,
    +cadastral: ObjectMap<CadastralData>,
    +assetDeclarations: ObjectMap<ObjectMap<AssetDeclaration>>,
    +query: string,
  |},
  +publicly: {|
    +currentPage: number,
    +autocompleteValue: string,
    +entitySearchValue: string,
    +entitySearchModalOpen: boolean,
    +entitySearchFor: string,
    +entitySearchEids: Array<string>,
    +showInfo: any, //TODO: TBD
    +openedAddressDetail: ?number,
  |},
  +mapOptions: MapOptions,
  +connections: Connections,
  +addresses: ObjectMap<Address>,
  +newEntities: ObjectMap<NewEntityState>,
  +entityDetails: ObjectMap<EntityDetails>,
|}

const getInitialState = (): State => ({
  count: 0,
  companies: {},
  notices: {
    list: {},
    details: {},
    searchQuery: '',
  },
  profile: {
    list: {},
    details: {},
    cadastral: {},
    assetDeclarations: {},
    query: '',
  },
  publicly: {
    currentPage: 1,
    autocompleteValue: '',
    entitySearchValue: '',
    entitySearchModalOpen: false,
    entitySearchFor: '',
    entitySearchEids: [],
    showInfo: {},
    openedAddressDetail: undefined,
  },
  mapOptions: {
    center: SLOVAKIA_COORDINATES,
    zoom: 8,
    bounds: undefined,
  },
  newEntities: {},
  entityDetails: {},
  addresses: {},
  connections: {
    entities: {},
    detail: {},
    entityDetails: {},
  },
})

export default getInitialState
<commit_msg>Add missing properties to EntityDetial type<commit_after>// @flow
import {SLOVAKIA_COORDINATES} from '../constants'
import type {ObjectMap} from '../types/commonTypes'

export type Candidate = {
  id: number,
  eid: number,
  customer: string,
  ico: string,
  name: string,
  title: string,
  text: string,
  price: number,
  score: number,
}

export type Notice = {|
  id: number,
  eid: number,
  curstomer: string,
  bulletin_number: number,
  title: string,
  text: string,
  price: number,
  price_stdev: number,
  bulletin_day: number,
  bulletin_month: number,
  bulletin_year: number,
  bulletin_date: string, // string representation of day/month/year
  kandidati: Array<Candidate | []>,
  price_num: number,
  price_avg: number,
  customer: string,
|}

export type PoliticianDetail = {|
  picture: string,
  party_nom: string,
  surname: string,
  party_abbreviation: string,
  firstname: string,
  title: string,
  term_finish: number,
  office_name_male: string,
  term_start: number,
  office_name_female: string,
|}

export type Politician = {|
  num_fields_gardens: number,
  picture: string,
  surname: string,
  party_abbreviation: string,
  firstname: string,
  title: string,
  term_finish: number,
  party_nom: string,
  num_houses_flats: number,
  office_name_male: string,
  num_others: number,
  term_start: number,
  office_name_female: string,
  id: number,
|}

export type CadastralData = {|
  lon: number,
  cadastralunitcode: number,
  landusename: string,
  cadastralunitname: string,
  parcelno: string,
  lat: number,
  foliono: number,
|}

export type AssetDeclaration = {|
  compensations: ?string,
  source: string,
  year: number,
  offices_other: string,
  other_income: ?string,
  income: string,
  movable_assets: string,
  unmovable_assets: string,
|}

export type Entity = {
  eid: string,
  lng: string,
  lat: string,
  selected: string,
  title: string,
  size: string,
  level: string,
  visible: string,
  ds: Array<any>,
  name: string,
}

export type CompanyStat = {
  datum_vzniku: string,
  datum_zaniku: string,
  zisk2016: ?number,
  trzby2016: ?number,
  zisk2015: ?number,
  trzby2015: ?number,
  zisk2014: ?number,
  trzby2014: ?number,
  trzby2016: ?number,
  zamestnanci2016: string,
  zamestnanci2015: string,
}

export type Company = {
  id: number,
  eid: number,
  zrsr_data: Array<any>,
  company_stats: Array<CompanyStat>,
  contracts: Array<any>,
  new_orsr_data: Array<any>,
  sponzori_stran_data: Array<any>,
  related: Array<any>,
  auditori_data: Array<any>,
  audiovizfond_data: Array<any>,
  entities: Array<Entity>,
  firmy_data: Array<any>,
  total_contracts: number,
  advokati_data: Array<any>,
  nadacie_data: Array<any>,
  orsresd_data: Array<any>,
  politicians_data: Array<any>,
  stranicke_prispevky_data: Array<any>,
  uzivatelia_vyhody_ludia_data: Array<any>,
}

export type NoticeMap = {[string]: Notice}

export type CompanyMap = {[string]: Company}

export type GeolocationPoint = {
  lat: number,
  lng: number,
}

export type MapBounds = {
  ne: GeolocationPoint,
  nw: GeolocationPoint,
  se: GeolocationPoint,
  sw: GeolocationPoint,
}

export type MapOptions = {
  center: [number, number],
  zoom: number,
  bounds: ?MapBounds,
}

export type Center = {lat: number, lng: number}

export type SearchedEntity = {
  eids: string[],
  id: string,
}

export type Connections = {
  entities: {[string]: Entity},
  detail: {[string]: {ids: string[]}},
  entityDetails: {
    [string]: {
      name: string,
      data: any, //TODO: TBD
    },
  },
}

export type Address = {
  address_id: number,
  lat: number,
  lng: number,
}

// Entity returned from api call getEntitiesAtAddressId
export type NewEntity = {
  id: number,
  name: string,
}

export type NewEntityState = NewEntity & {addressId: number}

export type EuFund = {
  title: string,
  link: string,
  price: number,
  state: string,
  call_state: string,
  call_title: string,
}

export type CompanyFinancial = {
  revenue: number,
  profit: number,
  employees: string,
}

export type Contract = {
  client_eid: number,
  client_name: string,
  id: number,
  contract_price_amount: number,
  contract_price_total_amount: number,
  signed_on: string,
  effective_from: string,
  effective_to: string,
  status_id: number,
  contract_id: number,
  contract_identifier: string,
}

// TODO rename to Notice when old one is gone
export type NoticeNew = {
  client_eid: number,
  client_name: string,
  id: number,
  notice_id: number,
  contract_id: number,
  title: string,
  estimated_value_amount: number,
  estimated_value_currency: string,
  bulletin_issue_id: number,
  notice_type_id: number,
  short_description: string,
  total_final_value_amount: number,
  total_final_value_currency: string,
  body: string,
}

export type RelatedEntity = {
  eid: number,
  name: string,
  stakeholder_type_id: number,
  lat: number,
  lng: number,
  address: string,
}

export type EntityDetails = {
  eid: number,
  name: string,
  lat: number,
  lng: number,
  address: string,
  eufunds: {
    eufunds_count: number,
    eufunds_price_sum: number,
    largest: Array<EuFund>,
  },
  companyfinancials: {
    [year: number]: CompanyFinancial,
  },
  companyinfo: {
    ico: number,
    established_on: string,
    terminated_on: string,
  },
  contracts: {
    count: number,
    price_amount_sum: number,
    most_recent: Array<Contract>,
    largest: Array<Contract>,
  },
  notices: {
    count: number,
    total_final_value_amount_eur_sum: number,
    most_recent: Array<NoticeNew>,
    largest: Array<NoticeNew>,
  },
  related: RelatedEntity[],
}

// Each property must begin with '+' to be made read only and each object
// must be enclosed in '|' so no properties can be added to state at runtime
export type State = {|
  +count: number,
  +companies: CompanyMap,
  +notices: {|
    +list: ObjectMap<Notice>,
    +details: ObjectMap<Notice>,
    +searchQuery: string,
  |},
  +profile: {|
    +list: ObjectMap<Politician>,
    +details: ObjectMap<PoliticianDetail>,
    +cadastral: ObjectMap<CadastralData>,
    +assetDeclarations: ObjectMap<ObjectMap<AssetDeclaration>>,
    +query: string,
  |},
  +publicly: {|
    +currentPage: number,
    +autocompleteValue: string,
    +entitySearchValue: string,
    +entitySearchModalOpen: boolean,
    +entitySearchFor: string,
    +entitySearchEids: Array<string>,
    +showInfo: any, //TODO: TBD
    +openedAddressDetail: ?number,
  |},
  +mapOptions: MapOptions,
  +connections: Connections,
  +addresses: ObjectMap<Address>,
  +newEntities: ObjectMap<NewEntityState>,
  +entityDetails: ObjectMap<EntityDetails>,
|}

const getInitialState = (): State => ({
  count: 0,
  companies: {},
  notices: {
    list: {},
    details: {},
    searchQuery: '',
  },
  profile: {
    list: {},
    details: {},
    cadastral: {},
    assetDeclarations: {},
    query: '',
  },
  publicly: {
    currentPage: 1,
    autocompleteValue: '',
    entitySearchValue: '',
    entitySearchModalOpen: false,
    entitySearchFor: '',
    entitySearchEids: [],
    showInfo: {},
    openedAddressDetail: undefined,
  },
  mapOptions: {
    center: SLOVAKIA_COORDINATES,
    zoom: 8,
    bounds: undefined,
  },
  newEntities: {},
  entityDetails: {},
  addresses: {},
  connections: {
    entities: {},
    detail: {},
    entityDetails: {},
  },
})

export default getInitialState
<|endoftext|>


<commit_before>//= require ../lib/noConflict
//= require ../vendor/modern-stack
//= require ../lib
//= require_self
//= require ../extension
//= require ../data
//= require ../plugins
//= require ../views
//= require ../screens
//= require ../component
//= require_tree .
//= require ../extension/LateLoaded

Lanes.namespace( 'Workspace' );
<commit_msg>require_tree vs individual files<commit_after>//= require_self
//= require_tree .

Lanes.namespace( 'Workspace' );
<|endoftext|>


<commit_before>/*
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill
* @license Copyright(c) 2017 by Maximilian Franzke
* Supported by Christian, Johannes and Michael - many thanks for that !
*/
/*
* A lightweight and library dependency free vanilla JavaScript datalist polyfill.
* Tests for native support of an inputs elements datalist functionality.
* Elsewhere the functionality gets emulated by a select element.
*/

(function() {
  'use strict';

  // feature detection
  var nativedatalist = !!( 'list' in document.createElement('input') ) &&
    !!( document.createElement('datalist') && window.HTMLDataListElement );

  // in case of that the feature doesn't exist, emulate it's functionality
  if (!nativedatalist) {

    // emulate the two properties regarding the datalist and input elements
    // list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement
    (function(constructor) {
      if (constructor &&
        constructor.prototype &&
        constructor.prototype.list === undefined) {
        Object.defineProperty(constructor.prototype, 'list', {
          get: function() {
            if (typeof( this ) === 'object' && this instanceof constructor) {
              var list = this.getAttribute('list');

              return document.getElementById(list);
            }
            return null;
          }
        });
      }
    })(window.HTMLInputElement);
    // options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement
    (function(constructor) {
      if (constructor &&
        constructor.prototype &&
        constructor.prototype.options === undefined) {
        Object.defineProperty(constructor.prototype, 'options', {
          get: function() {
            if (typeof( this ) === 'object' && this instanceof constructor) {
              return this.getElementsByTagName('option');
            }
          }
        });
      }
    })(window.HTMLElement);

    // identify whether a select multiple is feasible
    var touched = false,

      // introduced speaking variables for the different keycodes
      keyENTER = 13,
      keyESC = 27,
      keyUP = 38,
      keyDOWN = 40,

      // and defining the different input types that are supported by this polyfill
      supportedTypes = ['text', 'email', 'number', 'search', 'tel', 'url'];

    // differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685
    window.addEventListener('touchstart', function onFirstTouch() {
      touched = true;

      window.removeEventListener('touchstart', onFirstTouch);
    });

    // function regarding the inputs interactions
    var inputInputList = function(event) {

      var $eventTarget = event.target,
        eventTargetTagName = $eventTarget.tagName.toLowerCase();

      // check for whether the events target was an input datalist
      if (eventTargetTagName && eventTargetTagName === 'input' && $eventTarget.getAttribute('list')) {

        var list = $eventTarget.getAttribute('list'),
          $dataList = document.getElementById(list);

        // still check for an existing instance
        if ($dataList !== null) {

          var $dataListSelect = $dataList.getElementsByClassName('polyfilling')[0];

          // still check for an existing instance
          if ($dataListSelect !== undefined) {

            // on an ESC key press within the input, let's break here and hide the select
            if (event.keyCode === keyESC) {
              $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
              $dataListSelect.setAttribute('aria-hidden', 'true');

              return;
            }

            var $dataListOptions = $dataList.querySelectorAll('option:not([disabled]):not(.message)'),
              inputValue = $eventTarget.value,
              newSelectValues = document.createDocumentFragment(),
              disabledValues = document.createDocumentFragment(),
              visible = false,
              multipleEmails = ( $eventTarget.type === 'email' && $eventTarget.multiple ),
              keyOpen = ( event.keyCode === keyUP || event.keyCode === keyDOWN );

            // in case of type=email and multiple attribute, we would need to split the inputs value into pieces
            if (multipleEmails) {
              var multipleEntries = inputValue.split(','),
                relevantIndex = multipleEntries.length - 1;

              inputValue = multipleEntries[relevantIndex].trim();
            }

            // if the input contains a value, than ...
            if (inputValue !== '' || keyOpen) {

              // ... create an array out of the options list
              var nodeArray = Array.prototype.slice.call($dataListOptions);

              // ... sort all entries and
              nodeArray.sort(function(a, b) {
                return a.value.localeCompare(b.value);
              })
                .forEach(function(opt) {
                  var optionValue = opt.value;

                  // ... put this option into the fragment that is meant to get inserted into the select
                  // "Each option element that is a descendant of the datalist element, that is not disabled, and whose value is a string that isn't the empty string, represents a suggestion. Each suggestion has a value and a label." (W3C)
                  if (optionValue !== '' && optionValue.toLowerCase()
                      .indexOf(inputValue.toLowerCase()) !== -1 && opt.disabled === false) {

                    var label = opt.label,
                      labelValueSeperator = ' / ',
                      labelOptionPart = label.substr(0, optionValue.length + labelValueSeperator.length),
                      optionPart = optionValue + labelValueSeperator;

                    // the innertext should be value / label in case of that they are different
                    if (label && label !== optionValue && labelOptionPart !== optionPart) {
                      opt.innerText = optionValue + labelValueSeperator + label;

                      // remove the label attribute, as it's being displayed differently on desktop and iOS Safari for our construct
                      opt.setAttribute('data-label', opt.getAttribute('label'));
                      opt.removeAttribute('label');
                    } else // manipulating the option inner text, that would get displayed
                    if (!opt.innerText.trim()) {
                      opt.innerText = optionValue;
                    }

                    newSelectValues.appendChild(opt);

                    // ... and set the state of the select to get displayed in that case
                    visible = true;
                  } else {
                    // ... or put this option that isn't relevant to the users into the fragment that is supposed to get inserted outside of the select
                    disabledValues.appendChild(opt);
                  }
                });

              // input the options fragment into the datalists select
              $dataListSelect.appendChild(newSelectValues);

              var dataListSelectOptionsLength = $dataListSelect.options.length,
                firstEntry = 0,
                lastEntry = dataListSelectOptionsLength - 1;

              $dataListSelect.size = ( dataListSelectOptionsLength > 10 ) ? 10 : dataListSelectOptionsLength;
              $dataListSelect.multiple = ( !touched && dataListSelectOptionsLength < 2 );

              if (touched) {
                // preselect best fitting index
                $dataListSelect.selectedIndex = firstEntry;

              } else if ($dataListSelect.selectedIndex === -1) {

                switch (event.keyCode) {
                  case keyUP:
                    $dataListSelect.selectedIndex = lastEntry;
                    break;
                  case keyDOWN:
                    $dataListSelect.selectedIndex = firstEntry;
                    break;
                }
              }

              // input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront
              var $dataListAppend = $dataList.getElementsByClassName('ie9_fix')[0] || $dataList;

              $dataListAppend.appendChild(disabledValues);
            }

            // toggle the visibility of the select according to previous checks
            if (visible) {
              $dataListSelect.removeAttribute('hidden');
            } else {
              $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
            }
            $dataListSelect.setAttribute('aria-hidden', ( !visible ).toString());

            // on arrow up or down keys, focus the select
            if (keyOpen) {

              $dataListSelect.focus();
            }
          }
        }
      }
    };

    // focus or blur events
    var changesInputList = function(event) {

      var $eventTarget = event.target,
        eventTargetTagName = $eventTarget.tagName.toLowerCase(),
        inputType = $eventTarget.type,
        inputStyles = window.getComputedStyle($eventTarget);

      // check for whether the events target was an input datalist and whether it's of one of the supported input types defined above
      if (eventTargetTagName && eventTargetTagName === 'input' && $eventTarget.getAttribute('list') && supportedTypes.indexOf(inputType) > -1) {

        var eventType = event.type,
          list = $eventTarget.getAttribute('list'),
          $dataList = document.getElementById(list);

        // still check for an existing instance
        if ($dataList !== null) {

          var $dataListSelect = $dataList.getElementsByClassName('polyfilling')[0],
            // either have the select set to the state to get displayed in case of that it would have been focused or because it's the target on the inputs blur
            visible = ( ( eventType === 'focus' && event.target.value !== '' ) || ( event.relatedTarget && event.relatedTarget === $dataListSelect ) ),
            message = $dataList.title;

          // creating the select if there's no instance so far (e.g. because of that it hasn't been handled or it has been dynamically inserted)
          if ($dataListSelect === undefined) {
            var rects = $eventTarget.getClientRects(),
              // measurements
              inputStyleMarginRight = parseFloat(inputStyles.getPropertyValue('margin-right')),
              inputStyleMarginLeft = parseFloat(inputStyles.getPropertyValue('margin-left'));

            $dataListSelect = document.createElement('select');

            // setting a class for easier selecting that select afterwards
            $dataListSelect.setAttribute('class', 'polyfilling');

            // set general styling related definitions
            $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
            $dataListSelect.style.position = 'absolute';

            // WAI ARIA attributes
            $dataListSelect.setAttribute('aria-hidden', 'true');
            $dataListSelect.setAttribute('aria-live', 'polite');
            $dataListSelect.setAttribute('role', 'listbox');
            if (!touched) {
              $dataListSelect.setAttribute('aria-multiselectable', 'false');
            }

            // the select should get positioned underneath the input field ...
            if (inputStyles.getPropertyValue('direction') === 'rtl') {
              $dataListSelect.style.marginRight = '-' + ( rects[0].width + inputStyleMarginLeft ) + 'px';
            } else {
              $dataListSelect.style.marginLeft = '-' + ( rects[0].width + inputStyleMarginRight ) + 'px';
            }

            // set the polyfilling selects border-radius equally as the one by the polyfilled input
            $dataListSelect.style.borderRadius = inputStyles.getPropertyValue('border-radius');

            $dataListSelect.style.marginTop = rects[0].height + 'px';
            $dataListSelect.style.minWidth = rects[0].width + 'px';

            if (touched) {
              var $message = document.createElement('option');

              // ... and it's first entry should contain the localized message to select an entry
              $message.innerText = message;
              // ... and disable this option, as it shouldn't get selected by the user
              $message.disabled = true;
              // ... and assign a dividable class to it
              $message.setAttribute('class', 'message');
              // ... and finally insert it into the select
              $dataListSelect.appendChild($message);
            }

            // add select to datalist element ...
            $dataList.appendChild($dataListSelect);

            // ... and our upfollowing function to the change event

            if (touched) {
              $dataListSelect.addEventListener('change', changeDataListSelect);
            } else {
              $dataListSelect.addEventListener('click', changeDataListSelect);
            }
            $dataListSelect.addEventListener('blur', changeDataListSelect);
            $dataListSelect.addEventListener('keyup', changeDataListSelect);

            // plus we'd like to prevent autocomplete on the input datalist field
            $eventTarget.setAttribute('autocomplete', 'off');

            // WAI ARIA attributes
            $eventTarget.setAttribute('role', 'textbox');
            $eventTarget.setAttribute('aria-haspopup', 'true');
            $eventTarget.setAttribute('aria-autocomplete', 'list');
            $eventTarget.setAttribute('aria-owns', list);
          }

          // toggle the visibility of the select according to previous checks
          if (visible) {
            $dataListSelect.removeAttribute('hidden');
          } else {
            $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          }
          $dataListSelect.setAttribute('aria-hidden', ( !visible ).toString());

          // bind the keyup event on the related datalists input
          switch (eventType) {
            case 'focus':
              $eventTarget.addEventListener('keyup', inputInputList);

              $eventTarget.addEventListener('focusOut', changesInputList, true);
              break;
            case 'blur':
              $eventTarget.removeEventListener('keyup', inputInputList);

              $eventTarget.removeEventListener('focusOut', changesInputList, true);
              break;
          }
        }
      }
    };

    // function regarding changes to the datalist polyfilling created select
    var changeDataListSelect = function(event) {

      var $eventTarget = event.target,
        eventTargetTagName = $eventTarget.tagName.toLowerCase();

      // check for whether the events target was a select or an option
      if (eventTargetTagName && ( eventTargetTagName === 'select' || eventTargetTagName === 'option' )) {

        var $select = ( eventTargetTagName === 'select' ) ? $eventTarget : $eventTarget.parentNode,
          $datalist = $select.parentNode,
          message = $datalist.title,
          eventType = event.type,
          // ENTER and ESC
          visible = ( eventType === 'keyup' && ( event.keyCode !== keyENTER && event.keyCode !== keyESC ) );

        // change or mouseup event or ENTER key
        if (eventType === 'change' || eventType === 'click' || ( eventType === 'keyup' && event.keyCode === keyENTER )) {

          var list = $datalist.id,
            $inputList = document.querySelector('input[list="' + list + '"]'),
            selectValue = $select.value;

          // input the selects value into the input on a change within the list
          if ($inputList !== null && typeof( selectValue ) !== 'undefined' && selectValue.length > 0 && selectValue !== message) {
            var inputListValue = $inputList.value,
              lastSeperator,
              multipleEmails = ( $inputList.type === 'email' && $inputList.multiple );

            // in case of type=email and multiple attribute, we need to set up the resulting inputs value differently
            if (multipleEmails && ( lastSeperator = inputListValue.lastIndexOf(',') ) > -1) {
              $inputList.value = inputListValue.slice(0, lastSeperator) + ',' + selectValue;
            } else {
              $inputList.value = selectValue;
            }

            // set the visibility to false afterwards, as we're done here
            visible = false;
          }
        }

        // toggle the visibility of the select according to previous checks
        if (visible) {
          $select.removeAttribute('hidden');
        } else {
          $select.setAttributeNode(document.createAttribute('hidden'));
        }
        $select.setAttribute('aria-hidden', ( !visible ).toString());
      }
    };

    // binding the focus event - matching the input[list]s happens in the function afterwards
    document.addEventListener('focus', changesInputList, true);

  }
})();<commit_msg>less indentation through reverse conditional<commit_after>/*
* datalist-polyfill.js - https://github.com/mfranzke/datalist-polyfill
* @license Copyright(c) 2017 by Maximilian Franzke
* Supported by Christian, Johannes and Michael - many thanks for that !
*/
/*
* A lightweight and library dependency free vanilla JavaScript datalist polyfill.
* Tests for native support of an inputs elements datalist functionality.
* Elsewhere the functionality gets emulated by a select element.
*/

(function() {
  'use strict';

  // feature detection
  var nativedatalist = !!( 'list' in document.createElement('input') ) &&
    !!( document.createElement('datalist') && window.HTMLDataListElement );

  // in case of that the feature doesn't exist, emulate it's functionality
  if (nativedatalist) {
    return false;
  }

  // emulate the two properties regarding the datalist and input elements
  // list property / https://developer.mozilla.org/en/docs/Web/API/HTMLInputElement
  (function(constructor) {
    if (constructor &&
      constructor.prototype &&
      constructor.prototype.list === undefined) {
      Object.defineProperty(constructor.prototype, 'list', {
        get: function() {
          if (typeof( this ) === 'object' && this instanceof constructor) {
            var list = this.getAttribute('list');

            return document.getElementById(list);
          }
          return null;
        }
      });
    }
  })(window.HTMLInputElement);
  // options property / https://developer.mozilla.org/en/docs/Web/API/HTMLDataListElement
  (function(constructor) {
    if (constructor &&
      constructor.prototype &&
      constructor.prototype.options === undefined) {
      Object.defineProperty(constructor.prototype, 'options', {
        get: function() {
          if (typeof( this ) === 'object' && this instanceof constructor) {
            return this.getElementsByTagName('option');
          }
        }
      });
    }
  })(window.HTMLElement);

  // identify whether a select multiple is feasible
  var touched = false,

    // introduced speaking variables for the different keycodes
    keyENTER = 13,
    keyESC = 27,
    keyUP = 38,
    keyDOWN = 40,

    // and defining the different input types that are supported by this polyfill
    supportedTypes = ['text', 'email', 'number', 'search', 'tel', 'url'];

  // differentiate for touch interactions, adapted by https://medium.com/@david.gilbertson/the-only-way-to-detect-touch-with-javascript-7791a3346685
  window.addEventListener('touchstart', function onFirstTouch() {
    touched = true;

    window.removeEventListener('touchstart', onFirstTouch);
  });

  // function regarding the inputs interactions
  var inputInputList = function(event) {

    var $eventTarget = event.target,
      eventTargetTagName = $eventTarget.tagName.toLowerCase();

    // check for whether the events target was an input datalist
    if (eventTargetTagName && eventTargetTagName === 'input' && $eventTarget.getAttribute('list')) {

      var list = $eventTarget.getAttribute('list'),
        $dataList = document.getElementById(list);

      // still check for an existing instance
      if ($dataList !== null) {

        var $dataListSelect = $dataList.getElementsByClassName('polyfilling')[0];

        // still check for an existing instance
        if ($dataListSelect !== undefined) {

          // on an ESC key press within the input, let's break here and hide the select
          if (event.keyCode === keyESC) {
            $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
            $dataListSelect.setAttribute('aria-hidden', 'true');

            return;
          }

          var $dataListOptions = $dataList.querySelectorAll('option:not([disabled]):not(.message)'),
            inputValue = $eventTarget.value,
            newSelectValues = document.createDocumentFragment(),
            disabledValues = document.createDocumentFragment(),
            visible = false,
            multipleEmails = ( $eventTarget.type === 'email' && $eventTarget.multiple ),
            keyOpen = ( event.keyCode === keyUP || event.keyCode === keyDOWN );

          // in case of type=email and multiple attribute, we would need to split the inputs value into pieces
          if (multipleEmails) {
            var multipleEntries = inputValue.split(','),
              relevantIndex = multipleEntries.length - 1;

            inputValue = multipleEntries[relevantIndex].trim();
          }

          // if the input contains a value, than ...
          if (inputValue !== '' || keyOpen) {

            // ... create an array out of the options list
            var nodeArray = Array.prototype.slice.call($dataListOptions);

            // ... sort all entries and
            nodeArray.sort(function(a, b) {
              return a.value.localeCompare(b.value);
            })
              .forEach(function(opt) {
                var optionValue = opt.value;

                // ... put this option into the fragment that is meant to get inserted into the select
                // "Each option element that is a descendant of the datalist element, that is not disabled, and whose value is a string that isn't the empty string, represents a suggestion. Each suggestion has a value and a label." (W3C)
                if (optionValue !== '' && optionValue.toLowerCase()
                    .indexOf(inputValue.toLowerCase()) !== -1 && opt.disabled === false) {

                  var label = opt.label,
                    labelValueSeperator = ' / ',
                    labelOptionPart = label.substr(0, optionValue.length + labelValueSeperator.length),
                    optionPart = optionValue + labelValueSeperator;

                  // the innertext should be value / label in case of that they are different
                  if (label && label !== optionValue && labelOptionPart !== optionPart) {
                    opt.innerText = optionValue + labelValueSeperator + label;

                    // remove the label attribute, as it's being displayed differently on desktop and iOS Safari for our construct
                    opt.setAttribute('data-label', opt.getAttribute('label'));
                    opt.removeAttribute('label');
                  } else // manipulating the option inner text, that would get displayed
                  if (!opt.innerText.trim()) {
                    opt.innerText = optionValue;
                  }

                  newSelectValues.appendChild(opt);

                  // ... and set the state of the select to get displayed in that case
                  visible = true;
                } else {
                  // ... or put this option that isn't relevant to the users into the fragment that is supposed to get inserted outside of the select
                  disabledValues.appendChild(opt);
                }
              });

            // input the options fragment into the datalists select
            $dataListSelect.appendChild(newSelectValues);

            var dataListSelectOptionsLength = $dataListSelect.options.length,
              firstEntry = 0,
              lastEntry = dataListSelectOptionsLength - 1;

            $dataListSelect.size = ( dataListSelectOptionsLength > 10 ) ? 10 : dataListSelectOptionsLength;
            $dataListSelect.multiple = ( !touched && dataListSelectOptionsLength < 2 );

            if (touched) {
              // preselect best fitting index
              $dataListSelect.selectedIndex = firstEntry;

            } else if ($dataListSelect.selectedIndex === -1) {

              switch (event.keyCode) {
                case keyUP:
                  $dataListSelect.selectedIndex = lastEntry;
                  break;
                case keyDOWN:
                  $dataListSelect.selectedIndex = firstEntry;
                  break;
              }
            }

            // input the unused options as siblings next to the select - and differentiate in between the regular, and the IE9 fix syntax upfront
            var $dataListAppend = $dataList.getElementsByClassName('ie9_fix')[0] || $dataList;

            $dataListAppend.appendChild(disabledValues);
          }

          // toggle the visibility of the select according to previous checks
          if (visible) {
            $dataListSelect.removeAttribute('hidden');
          } else {
            $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          }
          $dataListSelect.setAttribute('aria-hidden', ( !visible ).toString());

          // on arrow up or down keys, focus the select
          if (keyOpen) {

            $dataListSelect.focus();
          }
        }
      }
    }
  };

  // focus or blur events
  var changesInputList = function(event) {

    var $eventTarget = event.target,
      eventTargetTagName = $eventTarget.tagName.toLowerCase(),
      inputType = $eventTarget.type,
      inputStyles = window.getComputedStyle($eventTarget);

    // check for whether the events target was an input datalist and whether it's of one of the supported input types defined above
    if (eventTargetTagName && eventTargetTagName === 'input' && $eventTarget.getAttribute('list') && supportedTypes.indexOf(inputType) > -1) {

      var eventType = event.type,
        list = $eventTarget.getAttribute('list'),
        $dataList = document.getElementById(list);

      // still check for an existing instance
      if ($dataList !== null) {

        var $dataListSelect = $dataList.getElementsByClassName('polyfilling')[0],
          // either have the select set to the state to get displayed in case of that it would have been focused or because it's the target on the inputs blur
          visible = ( ( eventType === 'focus' && event.target.value !== '' ) || ( event.relatedTarget && event.relatedTarget === $dataListSelect ) ),
          message = $dataList.title;

        // creating the select if there's no instance so far (e.g. because of that it hasn't been handled or it has been dynamically inserted)
        if ($dataListSelect === undefined) {
          var rects = $eventTarget.getClientRects(),
            // measurements
            inputStyleMarginRight = parseFloat(inputStyles.getPropertyValue('margin-right')),
            inputStyleMarginLeft = parseFloat(inputStyles.getPropertyValue('margin-left'));

          $dataListSelect = document.createElement('select');

          // setting a class for easier selecting that select afterwards
          $dataListSelect.setAttribute('class', 'polyfilling');

          // set general styling related definitions
          $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
          $dataListSelect.style.position = 'absolute';

          // WAI ARIA attributes
          $dataListSelect.setAttribute('aria-hidden', 'true');
          $dataListSelect.setAttribute('aria-live', 'polite');
          $dataListSelect.setAttribute('role', 'listbox');
          if (!touched) {
            $dataListSelect.setAttribute('aria-multiselectable', 'false');
          }

          // the select should get positioned underneath the input field ...
          if (inputStyles.getPropertyValue('direction') === 'rtl') {
            $dataListSelect.style.marginRight = '-' + ( rects[0].width + inputStyleMarginLeft ) + 'px';
          } else {
            $dataListSelect.style.marginLeft = '-' + ( rects[0].width + inputStyleMarginRight ) + 'px';
          }

          // set the polyfilling selects border-radius equally as the one by the polyfilled input
          $dataListSelect.style.borderRadius = inputStyles.getPropertyValue('border-radius');

          $dataListSelect.style.marginTop = rects[0].height + 'px';
          $dataListSelect.style.minWidth = rects[0].width + 'px';

          if (touched) {
            var $message = document.createElement('option');

            // ... and it's first entry should contain the localized message to select an entry
            $message.innerText = message;
            // ... and disable this option, as it shouldn't get selected by the user
            $message.disabled = true;
            // ... and assign a dividable class to it
            $message.setAttribute('class', 'message');
            // ... and finally insert it into the select
            $dataListSelect.appendChild($message);
          }

          // add select to datalist element ...
          $dataList.appendChild($dataListSelect);

          // ... and our upfollowing function to the change event

          if (touched) {
            $dataListSelect.addEventListener('change', changeDataListSelect);
          } else {
            $dataListSelect.addEventListener('click', changeDataListSelect);
          }
          $dataListSelect.addEventListener('blur', changeDataListSelect);
          $dataListSelect.addEventListener('keyup', changeDataListSelect);

          // plus we'd like to prevent autocomplete on the input datalist field
          $eventTarget.setAttribute('autocomplete', 'off');

          // WAI ARIA attributes
          $eventTarget.setAttribute('role', 'textbox');
          $eventTarget.setAttribute('aria-haspopup', 'true');
          $eventTarget.setAttribute('aria-autocomplete', 'list');
          $eventTarget.setAttribute('aria-owns', list);
        }

        // toggle the visibility of the select according to previous checks
        if (visible) {
          $dataListSelect.removeAttribute('hidden');
        } else {
          $dataListSelect.setAttributeNode(document.createAttribute('hidden'));
        }
        $dataListSelect.setAttribute('aria-hidden', ( !visible ).toString());

        // bind the keyup event on the related datalists input
        switch (eventType) {
          case 'focus':
            $eventTarget.addEventListener('keyup', inputInputList);

            $eventTarget.addEventListener('focusOut', changesInputList, true);
            break;
          case 'blur':
            $eventTarget.removeEventListener('keyup', inputInputList);

            $eventTarget.removeEventListener('focusOut', changesInputList, true);
            break;
        }
      }
    }
  };

  // function regarding changes to the datalist polyfilling created select
  var changeDataListSelect = function(event) {

    var $eventTarget = event.target,
      eventTargetTagName = $eventTarget.tagName.toLowerCase();

    // check for whether the events target was a select or an option
    if (eventTargetTagName && ( eventTargetTagName === 'select' || eventTargetTagName === 'option' )) {

      var $select = ( eventTargetTagName === 'select' ) ? $eventTarget : $eventTarget.parentNode,
        $datalist = $select.parentNode,
        message = $datalist.title,
        eventType = event.type,
        // ENTER and ESC
        visible = ( eventType === 'keyup' && ( event.keyCode !== keyENTER && event.keyCode !== keyESC ) );

      // change or mouseup event or ENTER key
      if (eventType === 'change' || eventType === 'click' || ( eventType === 'keyup' && event.keyCode === keyENTER )) {

        var list = $datalist.id,
          $inputList = document.querySelector('input[list="' + list + '"]'),
          selectValue = $select.value;

        // input the selects value into the input on a change within the list
        if ($inputList !== null && typeof( selectValue ) !== 'undefined' && selectValue.length > 0 && selectValue !== message) {
          var inputListValue = $inputList.value,
            lastSeperator,
            multipleEmails = ( $inputList.type === 'email' && $inputList.multiple );

          // in case of type=email and multiple attribute, we need to set up the resulting inputs value differently
          if (multipleEmails && ( lastSeperator = inputListValue.lastIndexOf(',') ) > -1) {
            $inputList.value = inputListValue.slice(0, lastSeperator) + ',' + selectValue;
          } else {
            $inputList.value = selectValue;
          }

          // set the visibility to false afterwards, as we're done here
          visible = false;
        }
      }

      // toggle the visibility of the select according to previous checks
      if (visible) {
        $select.removeAttribute('hidden');
      } else {
        $select.setAttributeNode(document.createAttribute('hidden'));
      }
      $select.setAttribute('aria-hidden', ( !visible ).toString());
    }
  };

  // binding the focus event - matching the input[list]s happens in the function afterwards
  document.addEventListener('focus', changesInputList, true);
})();<|endoftext|>


<commit_before><commit_msg>annihilation not default<commit_after><|endoftext|>