<commit_before>/*
 * Utility helper functions for the client and server side
 */
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define( [], function(){

    //return utility functions
    return {
        /*
         * Determines if the app runs in debug or release mode
         */
        DEBUG : false,

        /*
         * Define the server to connect to
         * Value is either IP of the host, or "self" for connecting to the host where the page loaded from
         */
        ServerIP : "self",

        /*
         * Port number of socket.io server
         */
        ServerPort : 8081,

        /*
         * Type of storage used by the server, possible values are:
         * "test" - mean that it will open the test project file and never saves anything to that
         * "mongodirty" - means that it will use the mongo as source of data, but it will simply left out the versioning
         */
        StorageType : "test",

        /*
         * location of the mongoDB server
         */
        MongoDBLocation : 'localhost',
        MongoDBPort : 27017,

        /*
         * Generated a GUID
         */
        guid: function() {
            var S4 = function() {
                return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
            };

            //return GUID
            return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
        }
    };
});
<commit_msg>JSLinting<commit_after>"use strict";
/*
 * Utility helper functions for the client and server side
 */
if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define([], function () {

    //return utility functions
    return {
        /*
         * Determines if the app runs in debug or release mode
         */
        DEBUG : false,

        /*
         * Define the server to connect to
         * Value is either IP of the host, or "self" for connecting to the host where the page loaded from
         */
        ServerIP : "self",

        /*
         * Port number of socket.io server
         */
        ServerPort : 8081,

        /*
         * Type of storage used by the server, possible values are:
         * "test" - mean that it will open the test project file and never saves anything to that
         * "mongodirty" - means that it will use the mongo as source of data, but it will simply left out the versioning
         */
        StorageType : "test",

        /*
         * location of the mongoDB server
         */
        MongoDBLocation : 'localhost',
        MongoDBPort : 27017,

        /*
         * Generated a GUID
         */
        guid: function () {
            var S4 = function () {
                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
            };

            //return GUID
            return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
        }
    };
});
<|endoftext|>


<commit_before>/*  Copyright (C) 2016 Milan Pässler
    Copyright (C) 2016 HopGlass Server contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

'use strict'

var async = require('async')
var _ = require('lodash')

module.exports = function(receiver, config) {

  function isOnline(node) {
    if (node)
      return Math.abs((node.lastseen ? new Date(node.lastseen) : new Date()) - new Date()) < config.offlineTime * 1000
    else
      return true
  }

  function parsePeerGroup(pg) {
    for (let i in pg) {
      if (i == 'peers') {
        for (let j in pg[i]) {
          if (pg[i][j])
            return true
        }
      } else {
        if (parsePeerGroup(pg[i]))
          return true
      }
    }
  }

  function getNodesJson(stream, query) {
    var data = receiver.getData(query)
    var nJson = {}
    nJson.version = 2
    nJson.nodes = []
    nJson.timestamp = new Date().toISOString()
    async.forEachOf(data, function(n, k, finished) {
      if (n.nodeinfo) {
        var node = {}
        node.nodeinfo = _.get(n, 'nodeinfo', {})
        node.flags = {}
        node.flags.gateway = _.get(n, 'flags.gateway')
        node.flags.online = isOnline(n)
        node.flags.uplink = parsePeerGroup(_.get(n, 'statistics.mesh_vpn'))
        node.statistics = {}
        if (node.flags.online) {
          node.statistics.uptime = _.get(n, 'statistics.uptime')
          node.statistics.gateway = _.get(n, 'statistics.gateway')
          if (_.has(n, 'statistics.memory'))
            node.statistics.memory_usage =
                (_.get(n, 'statistics.memory.total', 0)
              - _.get(n, 'statistics.memory.free', 0))
              / _.get(n, 'statistics.memory.total', 0)
          node.statistics.rootfs_usage = _.get(n, 'statistics.rootfs_usage')
          node.statistics.clients = _.get(n, 'statistics.clients.total', 0)
          node.statistics.loadavg = _.get(n, 'statistics.loadavg')
        }
        node.lastseen = _.get(n, 'lastseen', new Date().toISOString())
        node.firstseen = _.get(n, 'firstseen', new Date().toISOString())
        nJson.nodes.push(node)
      }
      finished()
    }, function() {
      stream.writeHead(200, { 'Content-Type': 'application/json' })
      stream.end(JSON.stringify(nJson))
    })
  }

  function getGraphJson(stream, query) {
    var data = receiver.getData(query)
    var gJson = {}
    gJson.timestamp = new Date().toISOString()
    gJson.version = 1
    gJson.batadv = {}
    gJson.batadv.multigraph = false
    gJson.batadv.directed = true
    gJson.batadv.nodes = []
    gJson.batadv.links = []
    gJson.batadv.graph = null
    var nodeTable = {}
    var macTable = {}
    var typeTable = {}
    var counter = 0
    function createEntry(mac) {
      var nodeEntry = {}
      nodeEntry.id = mac
      nodeEntry.node_id = macTable[mac]
      nodeTable[mac] = counter
      var node = data[macTable[mac]]
      for (let m in _.get(node, 'neighbours.batadv')) {
        nodeTable[m] = counter
      }
      if (!isOnline(node))
        nodeEntry.unseen = true
      counter++
      gJson.batadv.nodes.push(nodeEntry)
    }
    async.forEachOf(data, function(n, k, finished1) {
      if (_.has(n, 'neighbours.batadv') && _.has(n, 'nodeinfo.network.mac'))
        for (let mac in n.neighbours.batadv) {
          macTable[mac] = k
        }
      if (_.has(n, 'nodeinfo.network.mesh'))
        for (let bat in n.nodeinfo.network.mesh) {
          for (let type in n.nodeinfo.network.mesh[bat].interfaces) {
            n.nodeinfo.network.mesh[bat].interfaces[type].forEach(function(d) {
              typeTable[d] = type
            })
          }
        }
      finished1()
    }, function() {
      async.forEachOf(data, function(n, k, finished2) {
        if (_.has(n, 'neighbours.batadv') && isOnline(n)) {
          for (let dest in n.neighbours.batadv) {
            if (_.has(n.neighbours.batadv[dest], 'neighbours'))
              for (let src in n.neighbours.batadv[dest].neighbours) {
                var link = {}
                link.source = nodeTable[src]
                link.target = nodeTable[dest]
                var tq = _.get(n, ['neighbours', 'batadv', dest, 'neighbours', src, 'tq'])
                link.tq = 255 / (tq ? tq : 1)

                if (typeTable[src] === 'l2tp')
                  link.type = 'l2tp'
                else if (typeTable[dest] === 'tunnel')
                  link.type = 'fastd'
                else
                  link.type = typeTable[dest]

                if (isNaN(link.source)) {
                  //unknown node (not in data) -> create nodeentry
                  createEntry(src)
                  link.source = nodeTable[src]
                }
                if (isNaN(link.target)) {
                  createEntry(dest)
                  link.target = nodeTable[dest]
                }
                gJson.batadv.links.push(link)
              }
          }
        }
        finished2()
      }, function() {
        stream.writeHead(200, { 'Content-Type': 'application/json' })
        stream.end(JSON.stringify(gJson))
      })
    })
  }

  var exports = {
    /* eslint-disable quotes */
    "nodes.json": getNodesJson,
    "graph.json": getGraphJson
  }
  return exports
}
<commit_msg>Add heuristic for tunnel type.<commit_after>/*  Copyright (C) 2016 Milan Pässler
    Copyright (C) 2016 HopGlass Server contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

'use strict'

var async = require('async')
var _ = require('lodash')

module.exports = function(receiver, config) {

  function isOnline(node) {
    if (node)
      return Math.abs((node.lastseen ? new Date(node.lastseen) : new Date()) - new Date()) < config.offlineTime * 1000
    else
      return true
  }

  function parsePeerGroup(pg) {
    for (let i in pg) {
      if (i == 'peers') {
        for (let j in pg[i]) {
          if (pg[i][j])
            return true
        }
      } else {
        if (parsePeerGroup(pg[i]))
          return true
      }
    }
  }

  function getNodesJson(stream, query) {
    var data = receiver.getData(query)
    var nJson = {}
    nJson.version = 2
    nJson.nodes = []
    nJson.timestamp = new Date().toISOString()
    async.forEachOf(data, function(n, k, finished) {
      if (n.nodeinfo) {
        var node = {}
        node.nodeinfo = _.get(n, 'nodeinfo', {})
        node.flags = {}
        node.flags.gateway = _.get(n, 'flags.gateway')
        node.flags.online = isOnline(n)
        node.flags.uplink = parsePeerGroup(_.get(n, 'statistics.mesh_vpn'))
        node.statistics = {}
        if (node.flags.online) {
          node.statistics.uptime = _.get(n, 'statistics.uptime')
          node.statistics.gateway = _.get(n, 'statistics.gateway')
          if (_.has(n, 'statistics.memory'))
            node.statistics.memory_usage =
                (_.get(n, 'statistics.memory.total', 0)
              - _.get(n, 'statistics.memory.free', 0))
              / _.get(n, 'statistics.memory.total', 0)
          node.statistics.rootfs_usage = _.get(n, 'statistics.rootfs_usage')
          node.statistics.clients = _.get(n, 'statistics.clients.total', 0)
          node.statistics.loadavg = _.get(n, 'statistics.loadavg')
        }
        node.lastseen = _.get(n, 'lastseen', new Date().toISOString())
        node.firstseen = _.get(n, 'firstseen', new Date().toISOString())
        nJson.nodes.push(node)
      }
      finished()
    }, function() {
      stream.writeHead(200, { 'Content-Type': 'application/json' })
      stream.end(JSON.stringify(nJson))
    })
  }

  function getGraphJson(stream, query) {
    var data = receiver.getData(query)
    var gJson = {}
    gJson.timestamp = new Date().toISOString()
    gJson.version = 1
    gJson.batadv = {}
    gJson.batadv.multigraph = false
    gJson.batadv.directed = true
    gJson.batadv.nodes = []
    gJson.batadv.links = []
    gJson.batadv.graph = null
    var nodeTable = {}
    var macTable = {}
    var typeTable = {}
    var counter = 0
    function createEntry(mac) {
      var nodeEntry = {}
      nodeEntry.id = mac
      nodeEntry.node_id = macTable[mac]
      nodeTable[mac] = counter
      var node = data[macTable[mac]]
      for (let m in _.get(node, 'neighbours.batadv')) {
        nodeTable[m] = counter
      }
      if (!isOnline(node))
        nodeEntry.unseen = true
      counter++
      gJson.batadv.nodes.push(nodeEntry)
    }
    async.forEachOf(data, function(n, k, finished1) {
      if (_.has(n, 'neighbours.batadv') && _.has(n, 'nodeinfo.network.mac'))
        for (let mac in n.neighbours.batadv) {
          macTable[mac] = k
        }
      if (_.has(n, 'nodeinfo.network.mesh'))
        for (let bat in n.nodeinfo.network.mesh) {
          for (let type in n.nodeinfo.network.mesh[bat].interfaces) {
            n.nodeinfo.network.mesh[bat].interfaces[type].forEach(function(d) {
              typeTable[d] = type
            })
          }
        }
      finished1()
    }, function() {
      async.forEachOf(data, function(n, k, finished2) {
        if (_.has(n, 'neighbours.batadv') && isOnline(n)) {
          for (let dest in n.neighbours.batadv) {
            if (_.has(n.neighbours.batadv[dest], 'neighbours'))
              for (let src in n.neighbours.batadv[dest].neighbours) {
                var link = {}
                link.source = nodeTable[src]
                link.target = nodeTable[dest]
                var tq = _.get(n, ['neighbours', 'batadv', dest, 'neighbours', src, 'tq'])
                link.tq = 255 / (tq ? tq : 1)

                var ts = typeTable[src], td = typeTable[dest]
                if (ts === 'l2tp' || td === 'l2tp') {
                  link.type = 'l2tp'
                } else if (ts === 'fastd' || td === 'fastd') {
                  link.type = 'fastd'
                } else if (ts === 'tunnel' || td === 'tunnel') {
                  link.type = 'tunnel';

                  if (td === 'tunnel' && (ts == undefined || ts === 'tunnel')) {
                    var fds = 0, tds = 0, sis = 0
                    if (_.get(data[macTable[dest]], 'nodeinfo.software.fastd.enabled', false)) fds++
                    if (_.get(data[macTable[src]], 'nodeinfo.software.fastd.enabled', false)) fds++
                    if (_.get(data[macTable[dest]], 'nodeinfo.software.tunneldigger.enabled', false)) tds++
                    if (_.get(data[macTable[src]], 'nodeinfo.software.tunneldigger.enabled', false)) tds++
                    if (_.has(data[macTable[dest]], 'nodeinfo.software')) sis++
                    if (_.has(data[macTable[src]], 'nodeinfo.software')) sis++
                    if (sis == fds && fds > 0) link.type = 'fastd'
                    if (sis == tds && tds > 0) link.type = 'l2tp'
                  }
                } else
                  link.type = typeTable[dest]

                if (isNaN(link.source)) {
                  //unknown node (not in data) -> create nodeentry
                  createEntry(src)
                  link.source = nodeTable[src]
                }
                if (isNaN(link.target)) {
                  createEntry(dest)
                  link.target = nodeTable[dest]
                }
                gJson.batadv.links.push(link)
              }
          }
        }
        finished2()
      }, function() {
        stream.writeHead(200, { 'Content-Type': 'application/json' })
        stream.end(JSON.stringify(gJson))
      })
    })
  }

  var exports = {
    /* eslint-disable quotes */
    "nodes.json": getNodesJson,
    "graph.json": getGraphJson
  }
  return exports
}
<|endoftext|>


<commit_before>Emerald.GameObjectLastId = 0;
Emerald.GameObject = function (name,tag) {
    Emerald.GameObjectLastId++;
    var objName = name || "defaultName"+Emerald.GameObjectLastId;
    var objTag = tag||"default";
    var gameObject = {
        id:Emerald.GameObjectLastId,
        name:objName,
        tag:objTag,
        components: {},
        componentNextId:0,
        visible:false,
        parent:null,
        childs:[],
        setVisible:function(value){
            this.visible = value;
        },
        addComponent:function(component){
            var name = component.name;
            component.id = this.componentNextId;
            this.components[name||"def"+this.componentNextId] = component;
            this[name||"def"+this.componentNextId] = component;
            this.componentNextId++;
            component.gameObject = this;
            component.init();
            return component;
        },
        removeComponent:function(obj){
            var tmpDel;
            for (var comp in this.components){
                if (this.compareComponent(this.components[comp], obj,comp)) {
                    tmpDel = this.components[comp];
                    //components.splice(i,1);
                    delete this[comp];
                    delete this.components[comp];
                    break;
                }
            }
            return tmpDel;
        },
        addChild:function(obj){
            this.childs.push(obj);
            obj.parent = this;
        },
        addChilds:function(arrObj){
            for (var i=0;i<arrObj.length;i++){
                this.childs.push(arrObj[i]);
                arrObj[i].parent = this;
            }
        },
        setParent:function(obj){
            if (this.parent)
                this.parent.removeChild(this);
            this.parent = obj;
            obj.addChild(this);
        },
        getParentRoot:function(executeOnEach){
            if (this.parent) {
                executeOnEach(this.parent);
                return this.parent.getParentRoot(executeOnEach);
            }
            return this;
        },
        removeParent:function(){
            if (this.parent)
                this.parent.removeChild(this);
            this.parent = null;
        },
        removeChild:function(dynamic){
            var child = removeElement(this.childs,dynamic);
            if (child) child.removeParent();
        },
        getAllChilds:function(){
            return this.childs;
        },
        getChild:function(dynamic){
            return findElement(this.childs,dynamic);
        },
        getParent:function(){
            return this.parent;
        },
        getComponent:function(obj){
            for (var comp in this.components) {
                if (this.compareComponent(this.components[comp], obj,comp)) {
                    return this.components[comp];
                }
            }
        },
        compareComponent:function(comp,obj,idx){
            return ((isString(obj) && comp.name == obj) ||
                (isString(obj) && idx && obj == idx) ||
                (isObject(obj) && comp.id == obj.id) ||
                (isObject(obj) && equals(comp)));
        }
    };
    // On initialization we add the basic component needed (transform)
    gameObject.addComponent(new Emerald.transform());
    return gameObject;
};
<commit_msg>fixed parenting removal<commit_after>Emerald.GameObjectLastId = 0;
Emerald.GameObject = function (name,tag) {
    Emerald.GameObjectLastId++;
    var objName = name || "defaultName"+Emerald.GameObjectLastId;
    var objTag = tag||"default";
    var gameObject = {
        id:Emerald.GameObjectLastId,
        name:objName,
        tag:objTag,
        components: {},
        componentNextId:0,
        visible:false,
        parent:null,
        childs:[],
        setVisible:function(value){
            this.visible = value;
        },
        addComponent:function(component){
            var name = component.name;
            component.id = this.componentNextId;
            this.components[name||"def"+this.componentNextId] = component;
            this[name||"def"+this.componentNextId] = component;
            this.componentNextId++;
            component.gameObject = this;
            component.init();
            return component;
        },
        removeComponent:function(obj){
            var tmpDel;
            for (var comp in this.components){
                if (this.compareComponent(this.components[comp], obj,comp)) {
                    tmpDel = this.components[comp];
                    //components.splice(i,1);
                    delete this[comp];
                    delete this.components[comp];
                    break;
                }
            }
            return tmpDel;
        },
        addChild:function(obj){
            this.childs.push(obj);
            obj.setParent(this);
        },
        addChilds:function(arrObj){
            for (var i=0;i<arrObj.length;i++){
                this.childs.push(arrObj[i]);
                arrObj[i].setParent(this);
            }
        },
        setParent:function(obj){
            if (this.parent)
                this.parent.removeChild(this);
            this.parent = obj;
            obj.addChild(this);
        },
        getParentRoot:function(executeOnEach){
            if (this.parent) {
                executeOnEach(this.parent);
                return this.parent.getParentRoot(executeOnEach);
            }
            return this;
        },
        removeParent:function(){
            if (this.parent)
                this.parent.removeChild(this);
            this.parent = null;
        },
        removeChild:function(dynamic){
            var child = removeElement(this.childs,dynamic);
            if (child) child.removeParent();
        },
        getAllChilds:function(){
            return this.childs;
        },
        getChild:function(dynamic){
            return findElement(this.childs,dynamic);
        },
        getParent:function(){
            return this.parent;
        },
        getComponent:function(obj){
            for (var comp in this.components) {
                if (this.compareComponent(this.components[comp], obj,comp)) {
                    return this.components[comp];
                }
            }
        },
        compareComponent:function(comp,obj,idx){
            return ((isString(obj) && comp.name == obj) ||
                (isString(obj) && idx && obj == idx) ||
                (isObject(obj) && comp.id == obj.id) ||
                (isObject(obj) && equals(comp)));
        }
    };
    // On initialization we add the basic component needed (transform)
    gameObject.addComponent(new Emerald.transform());
    return gameObject;
};
<|endoftext|>


<commit_before>(function() {
  'use strict';

  window.Ractive.controller('pl-autocomplete', function(component, data, el, config, done) {

    var Autocomplete = component({
          plName: 'pl-autocomplete',
          data: $.extend(true, {
            focused: false,
            inEdition: false,
            listfocused: -1,
            listTop: 0,
            listWidth: 0,
            listHeight: 0,
            selection: ''
          }, data),
          partials: $.extend(true, {
            templateDefault: [
            '
',
              '{{{display}}}',
            '
'
            ].join('')
          }, config.partials),

          refresh: function() {
            _refresh();
          },

          clear: function() {
            _$el.input.val('');

            _change(event);
          },

          focus: function() {
            _$el.input.focus();
          }
        }),
        _$el = {
          window: $(window),
          input: $(Autocomplete.el).find('.pl-autocomplete-input'),
          selection: $(Autocomplete.el).find('.pl-autocomplete-selection'),
          list: $(Autocomplete.el).find('.pl-autocomplete-list'),
          listContent: $(Autocomplete.el).find('.pl-autocomplete-list-content')
        };

    function _refresh() {
      var top = _$el.input.offset().top,
          width = _$el.input.outerWidth(),
          height = _$el.input.outerHeight();

      Autocomplete.set('inEdition', Autocomplete.get('focused') && Autocomplete.get('list') && Autocomplete.get('list').length);

      Autocomplete.set('listWidth', width);
      Autocomplete.set('listHeight', Autocomplete.get('focused') ? _$el.listContent.outerHeight() : 0);

      if (top + (height - 2) + Autocomplete.get('listHeight') > _$el.window.height()) {
        Autocomplete.set('listFromBottom', true);
        Autocomplete.set('listTop', -Autocomplete.get('listHeight') + 2);
      }
      else {
        Autocomplete.set('listFromBottom', false);
        Autocomplete.set('listTop', height - 2);
      }

      Autocomplete.set('selectionWidth', width);
    }

    function _select(event) {
      var select = Autocomplete.get('select');
      if (select) {
        select(event, _$el.input.val(), Autocomplete);
      }
    }

    function _change(event) {
      var change = Autocomplete.get('change');
      if (change) {
        change(event, _$el.input.val(), Autocomplete);
      }
    }

    Autocomplete.partials.template = Autocomplete.partials.template || Autocomplete.partials.templateDefault;

    Autocomplete.on('enterListItem', function(event) {
      Autocomplete.set('listfocused', parseInt(event.keypath.substring(event.keypath.lastIndexOf('.') + 1), 10));
    });

    Autocomplete.on('selectListItem', function(event) {
      event.original.stopPropagation();
      event.original.preventDefault();

      _$el.input.val(event.context.value);

      _change(event);
    });

    Autocomplete.on('inputFocus', function(event) {
      Autocomplete.set('focused', true);

      _refresh();

      var focus = Autocomplete.get('focus');
      if (focus) {
        focus(event, _$el.input.val(), Autocomplete);
      }
    });

    Autocomplete.on('inputBlur', function(event) {
      Autocomplete.set('focused', false);

      _refresh();

      var blur = Autocomplete.get('blur');
      if (blur) {
        blur(event, _$el.input.val(), Autocomplete);
      }
    });

    Autocomplete.on('inputKeydown', function(event) {
      var keydown = Autocomplete.get('keydown');
      if (keydown) {
        keydown(event, Autocomplete);
      }
    });

    Autocomplete.on('inputKeyup', function(event) {
      var keyup = Autocomplete.get('keyup');
      if (keyup) {
        if (keyup(event, Autocomplete) === false) {
          return;
        }
      }

      var charCode = event.original.charCode ? event.original.charCode : event.original.keyCode,
          selection = Autocomplete.get('selection'),
          list = Autocomplete.get('list'),
          listfocused = Autocomplete.get('listfocused');

      // Right
      if (charCode == 39 && selection) {
        _$el.input.val(selection);

        _change(event);

        return;
      }

      // Top - Bottom
      if ((charCode == 38 || charCode == 40) && list && list.length) {
        listfocused = listfocused + (charCode == 38 ? -1 : 1);
        listfocused = listfocused == -2 ? list.length - 1 : listfocused;
        listfocused = listfocused == list.length ? -1 : listfocused;

        Autocomplete.set('listfocused', listfocused);

        return;
      }

      // Enter
      if (charCode == 13) {
        if (!list || !list.length || listfocused === -1) {
          _select(event);
        }
        else if (list && list.length && list[listfocused]) {
          _$el.input.val(list[listfocused].value);

          Autocomplete.set('listfocused', -1);

          _change(event);
        }

        return;
      }

      _change(event);
    });

    Autocomplete.observe('list', function(list) {
      Autocomplete.set('selection', list && list.length && list[0].value || '');
      Autocomplete.set('listfocused', -1);

      setTimeout(_refresh);
    });

    done();
  });

})();
<commit_msg>feat(autocomplete): use tab for validate a selection<commit_after>(function() {
  'use strict';

  window.Ractive.controller('pl-autocomplete', function(component, data, el, config, done) {

    var Autocomplete = component({
          plName: 'pl-autocomplete',
          data: $.extend(true, {
            focused: false,
            inEdition: false,
            listfocused: -1,
            listTop: 0,
            listWidth: 0,
            listHeight: 0,
            selection: ''
          }, data),
          partials: $.extend(true, {
            templateDefault: [
            '
',
              '{{{display}}}',
            '
'
            ].join('')
          }, config.partials),

          refresh: function() {
            _refresh();
          },

          clear: function() {
            _$el.input.val('');

            _change(event);
          },

          focus: function() {
            _$el.input.focus();
          }
        }),
        _$el = {
          window: $(window),
          input: $(Autocomplete.el).find('.pl-autocomplete-input'),
          selection: $(Autocomplete.el).find('.pl-autocomplete-selection'),
          list: $(Autocomplete.el).find('.pl-autocomplete-list'),
          listContent: $(Autocomplete.el).find('.pl-autocomplete-list-content')
        };

    function _refresh() {
      var top = _$el.input.offset().top,
          width = _$el.input.outerWidth(),
          height = _$el.input.outerHeight();

      Autocomplete.set('inEdition', Autocomplete.get('focused') && Autocomplete.get('list') && Autocomplete.get('list').length);

      Autocomplete.set('listWidth', width);
      Autocomplete.set('listHeight', Autocomplete.get('focused') ? _$el.listContent.outerHeight() : 0);

      if (top + (height - 2) + Autocomplete.get('listHeight') > _$el.window.height()) {
        Autocomplete.set('listFromBottom', true);
        Autocomplete.set('listTop', -Autocomplete.get('listHeight') + 2);
      }
      else {
        Autocomplete.set('listFromBottom', false);
        Autocomplete.set('listTop', height - 2);
      }

      Autocomplete.set('selectionWidth', width);
    }

    function _select(event) {
      var select = Autocomplete.get('select');
      if (select) {
        select(event, _$el.input.val(), Autocomplete);
      }
    }

    function _change(event) {
      var change = Autocomplete.get('change');
      if (change) {
        change(event, _$el.input.val(), Autocomplete);
      }
    }

    function _charCode(event) {
      return event.original.charCode ? event.original.charCode : event.original.keyCode;
    }

    Autocomplete.partials.template = Autocomplete.partials.template || Autocomplete.partials.templateDefault;

    Autocomplete.on('enterListItem', function(event) {
      Autocomplete.set('listfocused', parseInt(event.keypath.substring(event.keypath.lastIndexOf('.') + 1), 10));
    });

    Autocomplete.on('selectListItem', function(event) {
      event.original.stopPropagation();
      event.original.preventDefault();

      _$el.input.val(event.context.value);

      _change(event);
    });

    Autocomplete.on('inputFocus', function(event) {
      Autocomplete.set('focused', true);

      _refresh();

      var focus = Autocomplete.get('focus');
      if (focus) {
        focus(event, _$el.input.val(), Autocomplete);
      }
    });

    Autocomplete.on('inputBlur', function(event) {
      Autocomplete.set('focused', false);

      _refresh();

      var blur = Autocomplete.get('blur');
      if (blur) {
        blur(event, _$el.input.val(), Autocomplete);
      }
    });

    Autocomplete.on('inputKeydown', function(event) {
      var charCode = _charCode(event);

      if (charCode == 9) {
        var selection = Autocomplete.get('selection');

        if (selection) {
          event.original.stopPropagation();
          event.original.preventDefault();

          _$el.input.val(selection);

          _change(event);

          return;
        }
      }

      var keydown = Autocomplete.get('keydown');
      if (keydown) {
        keydown(event, Autocomplete);
      }
    });

    Autocomplete.on('inputKeyup', function(event) {
      var keyup = Autocomplete.get('keyup');
      if (keyup) {
        if (keyup(event, Autocomplete) === false) {
          return;
        }
      }

      var charCode = _charCode(event),
          selection = Autocomplete.get('selection'),
          list = Autocomplete.get('list'),
          listfocused = Autocomplete.get('listfocused');

      // Right
      if (charCode == 39 && selection) {
        _$el.input.val(selection);

        _change(event);

        return;
      }

      // Top - Bottom
      if ((charCode == 38 || charCode == 40) && list && list.length) {
        listfocused = listfocused + (charCode == 38 ? -1 : 1);
        listfocused = listfocused == -2 ? list.length - 1 : listfocused;
        listfocused = listfocused == list.length ? -1 : listfocused;

        Autocomplete.set('listfocused', listfocused);

        return;
      }

      // Enter
      if (charCode == 13) {
        if (!list || !list.length || listfocused === -1) {
          _select(event);
        }
        else if (list && list.length && list[listfocused]) {
          _$el.input.val(list[listfocused].value);

          Autocomplete.set('listfocused', -1);

          _change(event);
        }

        return;
      }

      _change(event);
    });

    Autocomplete.observe('list', function(list) {
      Autocomplete.set('selection', list && list.length && list[0].value || '');
      Autocomplete.set('listfocused', -1);

      setTimeout(_refresh);
    });

    done();
  });

})();
<|endoftext|>


<commit_before>/* eslint-disable indent */
import styled from 'styled-components';

const SubCategoryWrapper = styled.div`
  padding-bottom: 0.8rem;
  label {
    cursor: default !important;
    color: ${({ theme }) => theme.main.colors.mediumBlue};
  }
  input[type='checkbox'] {
    &:after {
      color: ${({ theme }) => theme.main.colors.mediumBlue};
    }
  }
  ${({ disabled, theme }) =>
    disabled &&
    `
    label {
      cursor: default !important;
      color: ${theme.main.colors.grey};
    }
    input[type='checkbox'] {
        &:after {
          color: ${theme.main.colors.grey};
        }
      }
    `}
`;

export default SubCategoryWrapper;
<commit_msg>Fix checkbox label color<commit_after>/* eslint-disable indent */
import styled from 'styled-components';

const SubCategoryWrapper = styled.div`
  padding-bottom: 0.8rem;
  input[type='checkbox'] {
    &:after {
      color: ${({ theme }) => theme.main.colors.mediumBlue};
    }
  }
  ${({ disabled, theme }) =>
    disabled &&
    `
    label {
      cursor: default !important;
      color: ${theme.main.colors.grey};
    }
    input[type='checkbox'] {
        &:after {
          color: ${theme.main.colors.grey};
        }
      }
    `}
`;

export default SubCategoryWrapper;
<|endoftext|>


<commit_before>var core = require('../core');

// add some extra variables to the container..
Object.assign(
    core.DisplayObject.prototype,
    require('./accessibleTarget')
);

/**
 * The Accessibility manager reacreates the ability to tab and and have content read by screen readers. This is very important as it can possibly help people with disabilities access pixi content.
 * Much like interaction any DisplayObject can be made accessible. This manager will map the events as if the mouse was being used, minimizing the efferot required to implement.
 *
 * @class
 * @memberof PIXI
 * @param renderer {PIXI.CanvasRenderer|PIXI.WebGLRenderer} A reference to the current renderer
 */
function AccessibilityManager(renderer)
{
	// first we create a div that will sit over the pixi element. This is where the div overlays will go.
    var div = document.createElement('div');

    div.style.width = 100 + 'px';
    div.style.height = 100 + 'px';
    div.style.position = 'absolute';
    div.style.top = 0;
    div.style.left = 0;
   //
    div.style.zIndex = 2;

   	/**
   	 * This is the dom element that will sit over the pixi element. This is where the div overlays will go.
   	 *
   	 * @type {HTMLElement}
   	 * @private
   	 */
   	this.div = div;

   	/**
   	 * A simple pool for storing divs.
   	 *
   	 * @type {Array}
   	 * @private
   	 */
 	this.pool = [];

 	/**
 	 * This is a tick used to check if an object is no longer being rendered.
 	 *
 	 * @type {Number}
 	 * @private
 	 */
   	this.renderId = 0;

   	/**
   	 * Setting this to true will visually show the divs
   	 *
   	 * @type {Boolean}
   	 */
   	this.debug = false;

  	/**
     * The renderer this accessibility manager works for.
     *
     * @member {PIXI.SystemRenderer}
     */
   	this.renderer = renderer;

   	/**
     * The array of currently active accessible items.
     *
     * @member {Array}
     * @private
     */
   	this.children = [];

   	/**
     * pre bind the functions..
     */
   	this._onKeyDown = this._onKeyDown.bind(this);
   	this._onMouseMove = this._onMouseMove.bind(this);

   	/**
     * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
     *
     * @member {Array}
     * @private
     */
   	this.isActive = false;

   	// let listen for tab.. once pressed we can fire up and show the accessibility layer
   	window.addEventListener('keydown', this._onKeyDown, false);
}

AccessibilityManager.prototype.constructor = AccessibilityManager;
module.exports = AccessibilityManager;

/**
 * Activating will cause the Accessibility layer to be shown. This is called when a user preses the tab key
 * @private
 */
AccessibilityManager.prototype.activate = function()
{
	if(this.isActive)
	{
		return;
	}

	this.isActive = true;

	window.document.addEventListener('mousemove', this._onMouseMove, true);
	window.removeEventListener('keydown', this._onKeyDown, false);

	this.renderer.on('postrender', this.update, this);

	this.renderer.view.parentNode.appendChild(this.div);
};

/**
 * Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves the mouse
 * @private
 */
AccessibilityManager.prototype.deactivate = function()
{
	if(!this.isActive)
	{
		return;
	}

	this.isActive = false;

	window.document.removeEventListener('mousemove', this._onMouseMove);
	window.addEventListener('keydown', this._onKeyDown, false);

	this.renderer.off('postrender', this.update);

	this.div.parentNode.removeChild(this.div);

};

/**
 * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
 * @param element {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} the DisplayObject to check.
 * @private
 */
AccessibilityManager.prototype.updateAccessibleObjects = function(displayObject)
{
	if(!displayObject.visible)
	{
		return;
	}

	if(displayObject.accessible && displayObject.interactive)
	{
		if(!displayObject._accessibleActive)
		{
			this.addChild(displayObject);
		}

	   	displayObject.renderId = this.renderId;
	}

	var children = displayObject.children;

	for (var i = children.length - 1; i >= 0; i--) {

		this.updateAccessibleObjects(children[i]);
	}
};

/**
 * Before each render this function will ensure that all divs are mapped correctly to their DisplayObjects
 * @private
 */
AccessibilityManager.prototype.update = function()
{

	// update children...
	this.updateAccessibleObjects(this.renderer._lastObjectRendered);

	var rect = this.renderer.view.getBoundingClientRect();
	var sx = rect.width  / this.renderer.width;
	var sy = rect.height / this.renderer.height;

	var div = this.div;

	div.style.left = rect.left + 'px';
	div.style.top = rect.top + 'px';
	div.style.width = this.renderer.width + 'px';
	div.style.height = this.renderer.height + 'px';

	for (var i = 0; i < this.children.length; i++)
	{

		var child = this.children[i];

		if(child.renderId !== this.renderId)
		{
			child._accessibleActive = false;

            core.utils.removeItems(this.children, i, 1);
			this.div.removeChild( child._accessibleDiv );
			this.pool.push(child._accessibleDiv);
			child._accessibleDiv = null;

			i--;

			if(this.children.length === 0)
			{
				this.deactivate();
			}
		}
		else
		{
			// map div to display..
			div = child._accessibleDiv;
			var hitArea = child.hitArea;
			var wt = child.worldTransform;

			if(child.hitArea)
			{
				div.style.left = ((wt.tx + (hitArea.x * wt.a)) * sx) + 'px';
				div.style.top =  ((wt.ty + (hitArea.y * wt.d)) * sy) +  'px';

				div.style.width = (hitArea.width * wt.a * sx) + 'px';
				div.style.height = (hitArea.height * wt.d * sy) + 'px';

			}
			else
			{
				hitArea = child.getBounds();

				this.capHitArea(hitArea);

				div.style.left = (hitArea.x * sx) + 'px';
				div.style.top =  (hitArea.y * sy) +  'px';

				div.style.width = (hitArea.width * sx) + 'px';
				div.style.height = (hitArea.height * sy) + 'px';
			}
		}
	}

	// increment the render id..
	this.renderId++;
};

AccessibilityManager.prototype.capHitArea = function (hitArea)
{
    if (hitArea.x < 0)
    {
        hitArea.width += hitArea.x;
        hitArea.x = 0;
    }

    if (hitArea.y < 0)
    {
        hitArea.height += hitArea.y;
        hitArea.y = 0;
    }

    if ( hitArea.x + hitArea.width > this.renderer.width )
    {
        hitArea.width = this.renderer.width - hitArea.x;
    }

    if ( hitArea.y + hitArea.height > this.renderer.height )
    {
        hitArea.height = this.renderer.height - hitArea.y;
    }
};

/**
 * Adds a DisplayObject to the accessibility manager
 * @private
 */
AccessibilityManager.prototype.addChild = function(displayObject)
{
//	this.activate();

	var div = this.pool.pop();

	if(!div)
	{
		div = document.createElement('button');

	    div.style.width = 100 + 'px';
	    div.style.height = 100 + 'px';
	    div.style.backgroundColor = this.debug ? 'rgba(255,0,0,0.5)' : 'transparent';
	    div.style.position = 'absolute';
	    div.style.zIndex = 2;
	    div.style.borderStyle = 'none';

	    div.addEventListener('click', this._onClick.bind(this));
	    div.addEventListener('focus', this._onFocus.bind(this));
	    div.addEventListener('focusout', this._onFocusOut.bind(this));
	}

	div.title = displayObject.accessibleTitle || 'displayObject ' + this.tabIndex;

	//

	displayObject._accessibleActive = true;
	displayObject._accessibleDiv = div;
	div.displayObject = displayObject;

	this.children.push(displayObject);
	this.div.appendChild( displayObject._accessibleDiv );
	displayObject._accessibleDiv.tabIndex = displayObject.tabIndex;
};

/**
 * Maps the div button press to pixi's InteractionManager (click)
 * @private
 */
AccessibilityManager.prototype._onClick = function(e)
{
	var interactionManager = this.renderer.plugins.interaction;
	interactionManager.dispatchEvent(e.target.displayObject, 'click', interactionManager.eventData);
};

/**
 * Maps the div focus events to pixis InteractionManager (mouseover)
 * @private
 */
AccessibilityManager.prototype._onFocus = function(e)
{
	var interactionManager = this.renderer.plugins.interaction;
	interactionManager.dispatchEvent(e.target.displayObject, 'mouseover', interactionManager.eventData);
};

/**
 * Maps the div focus events to pixis InteractionManager (mouseout)
 * @private
 */
AccessibilityManager.prototype._onFocusOut = function(e)
{
	var interactionManager = this.renderer.plugins.interaction;
	interactionManager.dispatchEvent(e.target.displayObject, 'mouseout', interactionManager.eventData);
};

/**
 * Is called when a key is pressed
 *
 * @private
 */
AccessibilityManager.prototype._onKeyDown = function(e)
{
	if(e.keyCode !== 9)
	{
		return;
	}

	this.activate();
};

/**
 * Is called when the mouse moves across the renderer element
 *
 * @private
 */
AccessibilityManager.prototype._onMouseMove = function()
{
	this.deactivate();
};

/**
 * Destroys the accessibility manager
 *
 */
AccessibilityManager.prototype.destroy = function ()
{
	this.div = null;

	for (var i = 0; i < this.children.length; i++)
	{
		this.children[i].div = null;
	}

	window.document.removeEventListener('mousemove', this._onMouseMove);
	window.removeEventListener('keydown', this._onKeyDown);

	this.pool = null;
	this.children = null;
	this.renderer = null;

};

core.WebGLRenderer.registerPlugin('accessibility', AccessibilityManager);
core.CanvasRenderer.registerPlugin('accessibility', AccessibilityManager);

<commit_msg>fixed accessibility issue<commit_after>var core = require('../core');

// add some extra variables to the container..
Object.assign(
    core.DisplayObject.prototype,
    require('./accessibleTarget')
);

/**
 * The Accessibility manager reacreates the ability to tab and and have content read by screen readers. This is very important as it can possibly help people with disabilities access pixi content.
 * Much like interaction any DisplayObject can be made accessible. This manager will map the events as if the mouse was being used, minimizing the efferot required to implement.
 *
 * @class
 * @memberof PIXI
 * @param renderer {PIXI.CanvasRenderer|PIXI.WebGLRenderer} A reference to the current renderer
 */
function AccessibilityManager(renderer)
{
	// first we create a div that will sit over the pixi element. This is where the div overlays will go.
    var div = document.createElement('div');

    div.style.width = 100 + 'px';
    div.style.height = 100 + 'px';
    div.style.position = 'absolute';
    div.style.top = 0;
    div.style.left = 0;
   //
    div.style.zIndex = 2;

   	/**
   	 * This is the dom element that will sit over the pixi element. This is where the div overlays will go.
   	 *
   	 * @type {HTMLElement}
   	 * @private
   	 */
   	this.div = div;

   	/**
   	 * A simple pool for storing divs.
   	 *
   	 * @type {Array}
   	 * @private
   	 */
 	this.pool = [];

 	/**
 	 * This is a tick used to check if an object is no longer being rendered.
 	 *
 	 * @type {Number}
 	 * @private
 	 */
   	this.renderId = 0;

   	/**
   	 * Setting this to true will visually show the divs
   	 *
   	 * @type {Boolean}
   	 */
   	this.debug = false;

  	/**
     * The renderer this accessibility manager works for.
     *
     * @member {PIXI.SystemRenderer}
     */
   	this.renderer = renderer;

   	/**
     * The array of currently active accessible items.
     *
     * @member {Array}
     * @private
     */
   	this.children = [];

   	/**
     * pre bind the functions..
     */
   	this._onKeyDown = this._onKeyDown.bind(this);
   	this._onMouseMove = this._onMouseMove.bind(this);

   	/**
     * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
     *
     * @member {Array}
     * @private
     */
   	this.isActive = false;

   	// let listen for tab.. once pressed we can fire up and show the accessibility layer
   	window.addEventListener('keydown', this._onKeyDown, false);
}

AccessibilityManager.prototype.constructor = AccessibilityManager;
module.exports = AccessibilityManager;

/**
 * Activating will cause the Accessibility layer to be shown. This is called when a user preses the tab key
 * @private
 */
AccessibilityManager.prototype.activate = function()
{
	if(this.isActive)
	{
		return;
	}

	this.isActive = true;

	window.document.addEventListener('mousemove', this._onMouseMove, true);
	window.removeEventListener('keydown', this._onKeyDown, false);

	this.renderer.on('postrender', this.update, this);

	this.renderer.view.parentNode.appendChild(this.div);
};

/**
 * Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves the mouse
 * @private
 */
AccessibilityManager.prototype.deactivate = function()
{
	if(!this.isActive)
	{
		return;
	}

	this.isActive = false;

	window.document.removeEventListener('mousemove', this._onMouseMove);
	window.addEventListener('keydown', this._onKeyDown, false);

	this.renderer.off('postrender', this.update);

	this.div.parentNode.removeChild(this.div);

};

/**
 * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
 * @param element {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} the DisplayObject to check.
 * @private
 */
AccessibilityManager.prototype.updateAccessibleObjects = function(displayObject)
{
	if(!displayObject.visible)
	{
		return;
	}

	if(displayObject.accessible && displayObject.interactive)
	{
		if(!displayObject._accessibleActive)
		{
			this.addChild(displayObject);
		}

	   	displayObject.renderId = this.renderId;
	}

	if(displayObject.interactiveChildren)
	{

		var children = displayObject.children;

		for (var i = children.length - 1; i >= 0; i--) {

			this.updateAccessibleObjects(children[i]);
		}
	}
};

/**
 * Before each render this function will ensure that all divs are mapped correctly to their DisplayObjects
 * @private
 */
AccessibilityManager.prototype.update = function()
{

	// update children...
	this.updateAccessibleObjects(this.renderer._lastObjectRendered);

	var rect = this.renderer.view.getBoundingClientRect();
	var sx = rect.width  / this.renderer.width;
	var sy = rect.height / this.renderer.height;

	var div = this.div;

	div.style.left = rect.left + 'px';
	div.style.top = rect.top + 'px';
	div.style.width = this.renderer.width + 'px';
	div.style.height = this.renderer.height + 'px';

	for (var i = 0; i < this.children.length; i++)
	{

		var child = this.children[i];

		if(child.renderId !== this.renderId)
		{
			child._accessibleActive = false;

            core.utils.removeItems(this.children, i, 1);
			this.div.removeChild( child._accessibleDiv );
			this.pool.push(child._accessibleDiv);
			child._accessibleDiv = null;

			i--;

			if(this.children.length === 0)
			{
				this.deactivate();
			}
		}
		else
		{
			// map div to display..
			div = child._accessibleDiv;
			var hitArea = child.hitArea;
			var wt = child.worldTransform;

			if(child.hitArea)
			{
				div.style.left = ((wt.tx + (hitArea.x * wt.a)) * sx) + 'px';
				div.style.top =  ((wt.ty + (hitArea.y * wt.d)) * sy) +  'px';

				div.style.width = (hitArea.width * wt.a * sx) + 'px';
				div.style.height = (hitArea.height * wt.d * sy) + 'px';

			}
			else
			{
				hitArea = child.getBounds();

				this.capHitArea(hitArea);

				div.style.left = (hitArea.x * sx) + 'px';
				div.style.top =  (hitArea.y * sy) +  'px';

				div.style.width = (hitArea.width * sx) + 'px';
				div.style.height = (hitArea.height * sy) + 'px';
			}
		}
	}

	// increment the render id..
	this.renderId++;
};

AccessibilityManager.prototype.capHitArea = function (hitArea)
{
    if (hitArea.x < 0)
    {
        hitArea.width += hitArea.x;
        hitArea.x = 0;
    }

    if (hitArea.y < 0)
    {
        hitArea.height += hitArea.y;
        hitArea.y = 0;
    }

    if ( hitArea.x + hitArea.width > this.renderer.width )
    {
        hitArea.width = this.renderer.width - hitArea.x;
    }

    if ( hitArea.y + hitArea.height > this.renderer.height )
    {
        hitArea.height = this.renderer.height - hitArea.y;
    }
};

/**
 * Adds a DisplayObject to the accessibility manager
 * @private
 */
AccessibilityManager.prototype.addChild = function(displayObject)
{
//	this.activate();

	var div = this.pool.pop();

	if(!div)
	{
		div = document.createElement('button');

	    div.style.width = 100 + 'px';
	    div.style.height = 100 + 'px';
	    div.style.backgroundColor = this.debug ? 'rgba(255,0,0,0.5)' : 'transparent';
	    div.style.position = 'absolute';
	    div.style.zIndex = 2;
	    div.style.borderStyle = 'none';

	    div.addEventListener('click', this._onClick.bind(this));
	    div.addEventListener('focus', this._onFocus.bind(this));
	    div.addEventListener('focusout', this._onFocusOut.bind(this));
	}

	div.title = displayObject.accessibleTitle || 'displayObject ' + this.tabIndex;

	//

	displayObject._accessibleActive = true;
	displayObject._accessibleDiv = div;
	div.displayObject = displayObject;

	this.children.push(displayObject);
	this.div.appendChild( displayObject._accessibleDiv );
	displayObject._accessibleDiv.tabIndex = displayObject.tabIndex;
};

/**
 * Maps the div button press to pixi's InteractionManager (click)
 * @private
 */
AccessibilityManager.prototype._onClick = function(e)
{
	var interactionManager = this.renderer.plugins.interaction;
	interactionManager.dispatchEvent(e.target.displayObject, 'click', interactionManager.eventData);
};

/**
 * Maps the div focus events to pixis InteractionManager (mouseover)
 * @private
 */
AccessibilityManager.prototype._onFocus = function(e)
{
	var interactionManager = this.renderer.plugins.interaction;
	interactionManager.dispatchEvent(e.target.displayObject, 'mouseover', interactionManager.eventData);
};

/**
 * Maps the div focus events to pixis InteractionManager (mouseout)
 * @private
 */
AccessibilityManager.prototype._onFocusOut = function(e)
{
	var interactionManager = this.renderer.plugins.interaction;
	interactionManager.dispatchEvent(e.target.displayObject, 'mouseout', interactionManager.eventData);
};

/**
 * Is called when a key is pressed
 *
 * @private
 */
AccessibilityManager.prototype._onKeyDown = function(e)
{
	if(e.keyCode !== 9)
	{
		return;
	}

	this.activate();
};

/**
 * Is called when the mouse moves across the renderer element
 *
 * @private
 */
AccessibilityManager.prototype._onMouseMove = function()
{
	this.deactivate();
};

/**
 * Destroys the accessibility manager
 *
 */
AccessibilityManager.prototype.destroy = function ()
{
	this.div = null;

	for (var i = 0; i < this.children.length; i++)
	{
		this.children[i].div = null;
	}

	window.document.removeEventListener('mousemove', this._onMouseMove);
	window.removeEventListener('keydown', this._onKeyDown);

	this.pool = null;
	this.children = null;
	this.renderer = null;

};

core.WebGLRenderer.registerPlugin('accessibility', AccessibilityManager);
core.CanvasRenderer.registerPlugin('accessibility', AccessibilityManager);

<|endoftext|>


<commit_before>/*
 *   Copyright 2015 OSBI
 *
 *   Created by Andreas Verleysen
 */

/**
 * Changes the locale of the cube
 */
var ChangeLocale = Backbone.View.extend({

    initialize: function (args) {
        this.visible = false;

        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("changeLocale_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "show", "handleClick", "processDatasourcesWithoutAddingNewTab", "toggleLocaleScreen", "setUserFeedback");

        this.datasources = new Connections({}, { dialog: this });

        // Add change locale button to saiku toolbar
        this.add_button();
        this.workspace.toolbar.changeLocale = this.show;

        // Create locale screen
        this.localeOptionsScreen = $("
  " +
            "
" +
            "
         <button class='button' id='en_US' > English </button>
 " +
            "
   	    <button class='button' id='nl_BE' > Dutch </button>
 " +
            "
   	    <button class='button' id='fr_FR' > French </button>
" +
            "
" +
            "
 " +
            " Choose your language ")
        ;

        // attach event handler
        this.localeOptionsScreen.find('button').click(this.handleClick);

        //function for menu
        $("#nav ul ").css({display: "none"}); // Opera Fix
        $("#nav li").hover(function () {
            $(this).find('ul:first').css({visibility: "visible", display: "none"}).show(400);
        }, function () {
            $(this).find('ul:first').css({visibility: "hidden"});
        });

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.localeOptionsScreen.hide());

    },

    add_button: function () {
        var $chart_button =
            $('')
                .css({  'background-image': "url('js/saiku/plugins/ChangeLocale/images/change_locale.png')",
                    'background-repeat': 'no-repeat',
                    'background-position': '7px 7px'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function (event, ui) {
        this.toggleWorkspaceWithLocaleScreen();
//        if ($(event.target).hasClass('on')) {
//            this.render();
//        } else {
//            this.workspace.table.render({ data: this.workspace.query.result.lastresult() });
//        }
    },

    toggleWorkspaceWithLocaleScreen: function () {
        this.toggleWorkSpace();
        this.toggleLocaleScreen();
    },

    toggleWorkSpace: function () {
        $(this.workspace.table.el).toggle();
        $(this.el).toggle();
    },

    toggleLocaleScreen: function () {
        if (this.visible) {
            this.setUserFeedback("Choose language");
            $(this.workspace.el).find('#languageOptions').addClass('hide');
            $(this.workspace.el).find('#feedback').addClass('hide');
            this.workspace.toolbar.run_query();
        }
        else {
            $(this.workspace.el).find('#languageOptions').removeClass('hide').show();
            $(this.workspace.el).find('#feedback').removeClass('hide').show();
        }
        $("#changeLocaleBtn").toggleClass('on');
        this.visible = !this.visible;
    },

    handleClick: function (event) {
        // Keep a reference to the main plugin object.
        var this_p = this;
        this_p.setUserFeedback("Changing locale...");
        // get selected locale
        var newLocale = $(event.target).attr('id');

        // get current selected connection name (without URL data)
        var selectedCube = $(".cubes option:selected").val();
        var selectedConnectionName = selectedCube.substring(0, selectedCube.indexOf("/"));

        // Get all connections from back-end
        var getUrl = Settings.REST_URL + "admin" + "/datasources";
        $.get(getUrl, function (data) {
            var allConnections = data;
            // match
            var selectedConnection = _.find(allConnections, function (connection) {
                return connection.connectionname == selectedConnectionName
            });

            var selectedDataSource = new DataSource(selectedConnection);

            this_p.setUserFeedback("Persisting locale...");
            this_p.persistLocaleOfDataSource(selectedDataSource, newLocale);
            this_p.setUserFeedback("Refreshing data source with new locale...");
            this_p.refreshDatasources();

        });
        return false;
    },

    persistLocaleOfDataSource: function (selectedDataSource, newLocale) {
        putUrl = Settings.REST_URL + "admin/datasources/" + selectedDataSource.id + "/locale";

        var xhr = new XMLHttpRequest();
        xhr.open('PUT', putUrl, false);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(newLocale);
    },

    refreshDatasources: function () {
        // keep reference to this plugin
        var this_plugin = this;
        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.clear();
        }

        Saiku.session.sessionworkspace.clear();

        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.setItem('saiku-version', Settings.VERSION);
        }

        Saiku.session.sessionworkspace.fetch({success: this_plugin.processDatasourcesWithoutAddingNewTab}, {});
    },

    processDatasourcesWithoutAddingNewTab: function (model, response) {
        // Save session in localStorage for other tabs to use
        if (typeof localStorage !== "undefined" && localStorage && localStorage.getItem('session') === null) {
            localStorage.setItem('session', JSON.stringify(response));

            // Set expiration on localStorage to one day in the future
            var expires = (new Date()).getTime() + Settings.LOCALSTORAGE_EXPIRATION;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem('expiration', expires);
            }
        }

        // Generate cube navigation for reuse
        Saiku.session.sessionworkspace.cube_navigation = _.template($("#template-cubes").html())({
            connections: response
        });

        // Create cube objects
        Saiku.session.sessionworkspace.cube = {};
        Saiku.session.sessionworkspace.connections = response;
        _.delay(Saiku.session.sessionworkspace.prefetch_dimensions, 20);

        this.show();
    },

    setUserFeedback: function (msg) {
        $("#feedback").html(msg);
    }
});

var DataSource = Backbone.Model.extend({
//    url: "admin/datasources",
    urlRoot: "admin/datasources",
    refresh: function () {
        $.ajax({
            type: 'GET',
            url: Settings.REST_URL + "admin" + "/datasources/" + this.get("connectionname") + "/refresh"
        });
    }
});

/**
 * Start Plugin
 */
Saiku.events.bind('session:new', function (session) {

    function new_workspace(args) {
        // Add stats element
        if (typeof args.workspace.changeLocale == "undefined") {
            args.workspace.changeLocale = new ChangeLocale({ workspace: args.workspace });
        }
    };

    function clear_workspace(args) {
        if (typeof args.workspace.changeLocale != "undefined") {
            $(args.workspace.changeLocale.localeOptionsScreen).hide();
            $(args.workspace.changeLocale.el).parents().find('.workspace_results table').show();
            $(args.workspace.changeLocale.el).hide();
        }
    };

    // Attach stats to existing tabs
    for (var i = 0; i < Saiku.tabs._tabs.length; i++) {
        var tab = Saiku.tabs._tabs[i];
        new_workspace({
            workspace: tab.content
        });
    }
    ;

    // Attach stats to future tabs
    Saiku.session.bind("workspace:new", new_workspace);
    Saiku.session.bind("workspace:clear", clear_workspace);
});
<commit_msg>ChangeLocale: use underscore templating to generate locale buttons<commit_after>/*
 *   Copyright 2015 OSBI
 *
 *   Created by Andreas Verleysen
 */

/**
 * Changes the locale of the cube
 */
var ChangeLocale = Backbone.View.extend({

    initialize: function (args) {
        this.visible = false;

        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("changeLocale_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "show", "handleClick", "processDatasourcesWithoutAddingNewTab", "toggleLocaleScreen", "setUserFeedback");

        this.datasources = new Connections({}, { dialog: this });

        // Add change locale button to saiku toolbar
        this.add_button();
        this.workspace.toolbar.changeLocale = this.show;

        this.localeOptionsScreenTemplate = _.template("" +
            "
  " +
            "
" +
            "<%_.each(localeMap, function(localeCode, language){ %>" +
            "
         <button class='button' id='<%=localeCode%>' > <%=language%> </button>
 " +
            "<%})%>" +
            "
" +
            "
 " +
            " Choose your language " +
            "");

        // Create locale screen
        this.localeOptionsScreen = $(this.localeOptionsScreenTemplate({localeMap: Settings.MONDRIAN_LOCALES}));

        // attach event handler
        this.localeOptionsScreen.find('button').click(this.handleClick);

        //function for menu
        $("#nav ul ").css({display: "none"}); // Opera Fix
        $("#nav li").hover(function () {
            $(this).find('ul:first').css({visibility: "visible", display: "none"}).show(400);
        }, function () {
            $(this).find('ul:first').css({visibility: "hidden"});
        });

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.localeOptionsScreen.hide());

    },

    add_button: function () {
        var $chart_button =
            $('')
                .css({  'background-image': "url('js/saiku/plugins/ChangeLocale/images/change_locale.png')",
                    'background-repeat': 'no-repeat',
                    'background-position': '7px 7px'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function (event, ui) {
        this.toggleWorkspaceWithLocaleScreen();
//        if ($(event.target).hasClass('on')) {
//            this.render();
//        } else {
//            this.workspace.table.render({ data: this.workspace.query.result.lastresult() });
//        }
    },

    toggleWorkspaceWithLocaleScreen: function () {
        this.toggleWorkSpace();
        this.toggleLocaleScreen();
    },

    toggleWorkSpace: function () {
        $(this.workspace.table.el).toggle();
        $(this.el).toggle();
    },

    toggleLocaleScreen: function () {
        if (this.visible) {
            this.setUserFeedback("Choose language");
            $(this.workspace.el).find('#languageOptions').addClass('hide');
            $(this.workspace.el).find('#feedback').addClass('hide');
            this.workspace.toolbar.run_query();
        }
        else {
            $(this.workspace.el).find('#languageOptions').removeClass('hide').show();
            $(this.workspace.el).find('#feedback').removeClass('hide').show();
        }
        $("#changeLocaleBtn").toggleClass('on');
        this.visible = !this.visible;
    },

    handleClick: function (event) {
        // Keep a reference to the main plugin object.
        var this_p = this;
        this_p.setUserFeedback("Changing locale...");
        // get selected locale
        var newLocale = $(event.target).attr('id');

        // get current selected connection name (without URL data)
        var selectedCube = $(".cubes option:selected").val();
        var selectedConnectionName = selectedCube.substring(0, selectedCube.indexOf("/"));

        //send connection name to back-end for update
        this_p.setUserFeedback("Persisting locale...");
        this_p.persistLocaleOfDataSource(selectedConnectionName, newLocale);
        this_p.setUserFeedback("Refreshing data source with new locale...");
        this_p.refreshDatasources();

        return false;
    },

    persistLocaleOfDataSource: function (selectedDataSourceName, newLocale) {
        putUrl = Settings.REST_URL + "admin/datasources/" + selectedDataSourceName + "/locale";

        var xhr = new XMLHttpRequest();
        xhr.open('PUT', putUrl, false);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(newLocale);
    },

    refreshDatasources: function () {
        // keep reference to this plugin
        var this_plugin = this;
        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.clear();
        }

        Saiku.session.sessionworkspace.clear();

        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.setItem('saiku-version', Settings.VERSION);
        }

        Saiku.session.sessionworkspace.fetch({success: this_plugin.processDatasourcesWithoutAddingNewTab}, {});
    },

    processDatasourcesWithoutAddingNewTab: function (model, response) {
        // Save session in localStorage for other tabs to use
        if (typeof localStorage !== "undefined" && localStorage && localStorage.getItem('session') === null) {
            localStorage.setItem('session', JSON.stringify(response));

            // Set expiration on localStorage to one day in the future
            var expires = (new Date()).getTime() + Settings.LOCALSTORAGE_EXPIRATION;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem('expiration', expires);
            }
        }

        // Generate cube navigation for reuse
        Saiku.session.sessionworkspace.cube_navigation = _.template($("#template-cubes").html())({
            connections: response
        });

        // Create cube objects
        Saiku.session.sessionworkspace.cube = {};
        Saiku.session.sessionworkspace.connections = response;
        _.delay(Saiku.session.sessionworkspace.prefetch_dimensions, 20);

        this.show();
    },

    setUserFeedback: function (msg) {
        $("#feedback").html(msg);
    }
});

var DataSource = Backbone.Model.extend({
//    url: "admin/datasources",
    urlRoot: "admin/datasources",
    refresh: function () {
        $.ajax({
            type: 'GET',
            url: Settings.REST_URL + "admin" + "/datasources/" + this.get("connectionname") + "/refresh"
        });
    }
});

/**
 * Start Plugin
 */
Saiku.events.bind('session:new', function (session) {

    function new_workspace(args) {
        if (typeof args.workspace.changeLocale == "undefined") {
            args.workspace.changeLocale = new ChangeLocale({ workspace: args.workspace });
        }
    };

    function clear_workspace(args) {
        if (typeof args.workspace.changeLocale != "undefined") {
            $(args.workspace.changeLocale.localeOptionsScreen).hide();
            $(args.workspace.changeLocale.el).parents().find('.workspace_results table').show();
            $(args.workspace.changeLocale.el).hide();
        }
    };

    if (Settings.PLUGINS.indexOf("ChangeLocale") != -1) {
        // Attach to existing tabs
        for (var i = 0; i < Saiku.tabs._tabs.length; i++) {
            var tab = Saiku.tabs._tabs[i];
            new_workspace({
                workspace: tab.content
            });
        }
        ;

        // Attach to future tabs
        Saiku.session.bind("workspace:new", new_workspace);
        Saiku.session.bind("workspace:clear", clear_workspace);
    }
});
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var mongo = require('mongodb').MongoClient;
var path = require('path');
var randomstring = require('randomstring');
var bodyParser = require('body-parser');

var app = express();
require('dotenv').load();

app.use(express.static(__dirname + '/public'));
app.use('/polls', express.static(__dirname + '/public'))
app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
    extended: false
}));
app.use(bodyParser.json())
app.set('view engine', 'jade');

app.get('/', function(req, res) {

    var polls = {};

    mongo.connect(process.env.MONGOLAB_URI, function(err, db) {
       if(err) throw err;

        var collection = db.collection('polls');
        var data = collection.find().toArray(function(err, docs) {
            if(err) throw err;
            polls.data = docs;
            polls.currentPage = 'Home'
            res.render('index', polls);
            db.close();
        });
    });
});

app.get('/polls/create', function(req, res) {
    var obj = {
        currentPage: 'Create'
    }
    res.render('create', obj);
});

app.get('/polls/:STRING', function(req, res) {
    var str = req.params.STRING;
    // logic here
    res.redirect('/');
});

app.post('/polls/create', function(req, res) {
    var doc = req.body;

    mongo.connect(process.env.MONGOLAB_URI, function(err, db) {
        if(err) throw err;

        var collection = db.collection('polls');

        function getNewUrl() {
            // generate new URL
            var urlStr = randomstring.generate(10);
            // check if url already exists
            collection.find({
                url: '/polls/' + urlStr
            }).toArray(function(err, docs) {
                if(err) throw err;
                if(docs.length > 0)
                    getNewUrl();
            });
            return urlStr;
        }

        doc.url = '/polls/' + getNewUrl();

        collection.insert(doc, function(err) {
            if(err) throw err;
            var obj = {
                pollSubmitted: true,
                poll: {
                    url: process.env.APP_URL + doc.url
                }
            }
            res.render('create', obj);
            db.close();
        });
    });

})

app.get('/*', function(req, res) {
    // redirect invalid paths to main page
    res.redirect('/');
});

var port = process.env.PORT || 8080;
app.listen(port,  function () {
	console.log('Node.js listening on port ' + port + '...');
});

// to do
// error handling
// login
// db
// poll<commit_msg>fix url issue<commit_after>'use strict';

var express = require('express');
var mongo = require('mongodb').MongoClient;
var path = require('path');
var randomstring = require('randomstring');
var bodyParser = require('body-parser');

var app = express();
require('dotenv').load();

app.use(express.static(__dirname + '/public'));
app.use('/polls', express.static(__dirname + '/public'))
app.use(bodyParser.urlencoded({     // to support URL-encoded bodies
    extended: false
}));
app.use(bodyParser.json())
app.set('view engine', 'jade');

app.get('/', function(req, res) {

    var polls = {};

    mongo.connect(process.env.MONGOLAB_URI, function(err, db) {
       if(err) throw err;

        var collection = db.collection('polls');
        var data = collection.find().toArray(function(err, docs) {
            if(err) throw err;
            polls.data = docs;
            polls.currentPage = 'Home'
            res.render('index', polls);
            db.close();
        });
    });
});

app.get('/polls/create', function(req, res) {
    var obj = {
        currentPage: 'Create'
    }
    res.render('create', obj);
});

app.get('/polls/:STRING', function(req, res) {
    var str = req.params.STRING;
    // logic here
    res.redirect('/');
});

app.post('/polls/create', function(req, res) {
    var doc = req.body;

    mongo.connect(process.env.MONGOLAB_URI, function(err, db) {
        if(err) throw err;

        var collection = db.collection('polls');

        function getNewUrl() {
            // generate new URL
            var urlStr = randomstring.generate(10);
            // check if url already exists
            collection.find({
                url: '/polls/' + urlStr
            }).toArray(function(err, docs) {
                if(err) throw err;
                if(docs.length > 0)
                    getNewUrl();
            });
            return urlStr;
        }

        doc.url = '/polls/' + getNewUrl();

        collection.insert(doc, function(err) {
            if(err) throw err;
            var obj = {
                pollSubmitted: true,
                poll: {
                    url: process.env.APP_URL + doc.url.substr(1)
                }
            }
            res.render('create', obj);
            db.close();
        });
    });

})

app.get('/*', function(req, res) {
    // redirect invalid paths to main page
    res.redirect('/');
});

var port = process.env.PORT || 8080;
app.listen(port,  function () {
	console.log('Node.js listening on port ' + port + '...');
});

// to do
// error handling
// login
// db
// poll<|endoftext|>


<commit_before>import {
  getContext,
  setContext,
  call,
  put,
  all,
  fork,
  take,
  cancelled,
} from 'redux-saga/effects';
import { cloneableGenerator } from 'redux-saga/utils';

import {
  success,
  error,
  abort,
  successAction,
  errorAction,
  abortAction,
} from './actions';
import {
  REQUEST_INSTANCE,
  REQUESTS_CONFIG,
  INCORRECT_PAYLOAD_ERROR,
} from './constants';
import {
  defaultConfig,
  createRequestInstance,
  getRequestInstance,
  getRequestsConfig,
  sendRequest,
  watchRequests,
  abortRequestIfDefined,
  voidCallback,
} from './sagas';
import { isRequestAction } from './helpers';

// TODO: implement those tests with a saga test library

const dummyDriver = {
  getSuccessPayload: () => {},
  getErrorPayload: () => {},
  getRequestHandlers: () => ({
    sendRequest: () => {},
    abortRequest: () => {},
  }),
};

describe('sagas', () => {
  describe('voidCallback', () => {
    it('returns undefined', () => {
      assert.equal(voidCallback(), undefined);
    });
  });

  describe('defaultConfig', () => {
    it('has correct value', () => {
      const expected = {
        success,
        error,
        abort,
        successAction,
        errorAction,
        abortAction,
        driver: null,
        onRequest: null,
        onSuccess: null,
        onError: null,
        onAbort: null,
      };

      assert.deepEqual(defaultConfig, expected);
    });
  });

  describe('createRequestInstance', () => {
    const requestInstance = { type: 'axios' };

    it('returns correct effect with default config', () => {
      const expected = setContext({
        [REQUEST_INSTANCE]: requestInstance,
        [REQUESTS_CONFIG]: defaultConfig,
      });
      assert.deepEqual(createRequestInstance(requestInstance), expected);
    });

    it('returns correct effect with overwritten config', () => {
      const config = {
        success: 'success',
        successAction,
        error: 'error',
        errorAction,
        abort: 'abort',
        abortAction: voidCallback,
        driver: 'some driver',
        onRequest: voidCallback,
        onSuccess: voidCallback,
        onError: voidCallback,
        onAbort: voidCallback,
      };

      const expected = setContext({
        [REQUEST_INSTANCE]: requestInstance,
        [REQUESTS_CONFIG]: config,
      });
      assert.deepEqual(
        createRequestInstance(requestInstance, config),
        expected,
      );
    });
  });

  describe('getRequestInstance', () => {
    it('returns correct effect', () => {
      assert.deepEqual(getRequestInstance(), getContext(REQUEST_INSTANCE));
    });
  });

  describe('getRequestsConfig', () => {
    it('returns correct effect', () => {
      assert.deepEqual(getRequestsConfig(), getContext(REQUESTS_CONFIG));
    });
  });

  describe('abortRequestIfDefined', () => {
    it('returns call effect when abortRequest defined', () => {
      const abortRequest = () => {};
      assert.deepEqual(abortRequestIfDefined(abortRequest), call(abortRequest));
    });

    it('returns null when abortRequest undefined', () => {
      assert.equal(abortRequestIfDefined(undefined), null);
    });
  });

  describe('sendRequest', () => {
    describe('with correct payload with dispatchRequestAction', () => {
      it('dispatches request action', () => {
        const action = { type: 'FETCH', request: { url: '/url' } };
        const gen = sendRequest(action, { dispatchRequestAction: true });
        gen.next();
        gen.next();
        assert.deepEqual(gen.next().value, put(action));
      });
    });

    describe('with correct payload', () => {
      const config = { ...defaultConfig, driver: dummyDriver };
      const action = {
        type: 'FETCH',
        payload: {
          request: { url: '/url' },
        },
      };
      const gen = cloneableGenerator(sendRequest)(action);
      const requestInstance = () => ({ type: 'axios' });
      const response = { data: 'some response' };
      const requestHandlers = dummyDriver.getRequestHandlers(requestInstance);

      it('gets request instance', () => {
        assert.deepEqual(gen.next().value, getRequestInstance());
      });

      it('gets request config', () => {
        assert.deepEqual(gen.next(requestInstance).value, getRequestsConfig());
      });

      it('gets request handlers', () => {
        const expected = call(
          [dummyDriver, 'getRequestHandlers'],
          requestInstance,
          config,
        );
        assert.deepEqual(gen.next(config).value, expected);
      });

      // it('calls onRequest', () => {
      //   const expected = call(config.onRequest, action.payload.request);
      //   assert.deepEqual(gen.next(requestHandlers).value, expected);
      // });

      it('calls sendRequest', () => {
        const expected = call(
          requestHandlers.sendRequest,
          action.payload.request,
        );
        assert.deepEqual(gen.next(requestHandlers).value, expected);
      });

      it('dispatches error, calls on Error and returns request error action when there is an error', () => {
        const errorGen = gen.clone();
        const requestError = new Error('Something went wrong');
        const errorPayload = 'error payload';
        assert.deepEqual(
          errorGen.throw(requestError).value,
          call(dummyDriver.getErrorPayload, requestError),
        );
        const expected = put({
          type: error(action.type),
          payload: errorPayload,
          error: true,
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(errorGen.next(errorPayload).value, expected);
        // assert.deepEqual(
        //   errorGen.next(requestHandlers).value,
        //   call(config.onError, requestError),
        // );
        errorGen.next(); // to fire finally yield
        assert.deepEqual(errorGen.next().value, { error: requestError });
      });

      it('dispatches request success action when response is successful', () => {
        assert.deepEqual(
          gen.next(response).value,
          call(dummyDriver.getSuccessPayload, response, action.payload.request),
        );
        const expected = put({
          type: success(action.type),
          payload: {
            data: response.data,
          },
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(gen.next(response.data).value, expected);
      });

      // it('calls onSuccess', () => {
      //   const expected = call(config.onSuccess, response);
      //   assert.deepEqual(gen.next().value, expected);
      // });

      it('awaits cancellation', () => {
        assert.deepEqual(gen.next().value, cancelled());
      });

      it('handles cancellation when cancelled', () => {
        assert.deepEqual(
          gen.next(true).value,
          abortRequestIfDefined(requestHandlers.abortRequest),
        );
        const expected = put({
          type: abort(action.type),
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(gen.next().value, expected);
        // assert.deepEqual(gen.next(requestHandlers).value, call(config.onAbort));
      });

      it('returns response', () => {
        assert.deepEqual(gen.clone().next().value, { response });
      });
    });

    describe('with correct payload with multiple requests', () => {
      const config = { ...defaultConfig, driver: dummyDriver };
      const action = {
        type: 'FETCH_MULTIPLE',
        request: [{ url: '/url1' }, { url: '/url2' }],
      };
      const gen = sendRequest(action);
      const requestInstance = () => ({ type: 'axios' });
      const responses = [
        { data: 'some response' },
        { data: 'another response' },
      ];
      const requestHandlers = dummyDriver.getRequestHandlers(requestInstance);

      it('gets request instance', () => {
        assert.deepEqual(gen.next().value, getRequestInstance());
      });

      it('gets request config', () => {
        assert.deepEqual(gen.next(requestInstance).value, getRequestsConfig());
      });

      it('gets request handlers', () => {
        const expected = call(
          [dummyDriver, 'getRequestHandlers'],
          requestInstance,
          config,
        );
        assert.deepEqual(gen.next(config).value, expected);
      });

      // it('calls onRequest', () => {
      //   const expected = call(config.onRequest, action.request);
      //   assert.deepEqual(gen.next(requestHandlers).value, expected);
      // });

      it('calls sendRequests', () => {
        const expected = all([
          call(requestHandlers.sendRequest, action.request[0]),
          call(requestHandlers.sendRequest, action.request[1]),
        ]);
        assert.deepEqual(gen.next(requestHandlers).value, expected);
      });

      it('dispatches request success action when reponse is successful', () => {
        assert.deepEqual(
          gen.next(responses).value,
          call(dummyDriver.getSuccessPayload, responses, action.request),
        );
        const data = [responses[0].data, responses[1].data];
        const expected = put({
          type: success(action.type),
          data,
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(gen.next(data).value, expected);
      });

      // it('calls onSuccess', () => {
      //   const expected = call(config.onSuccess, responses);
      //   assert.deepEqual(gen.next(requestHandlers).value, expected);
      // });

      it('returns response array', () => {
        gen.next(); // to fire finally yield
        assert.deepEqual(gen.next().value, { response: responses });
      });
    });

    describe('with incorrect payload', () => {
      it('throws error when action payload is invalid', () => {
        const invalidAction = { type: 'FETCH' };
        const gen = sendRequest(invalidAction);
        assert.throws(() => gen.next(), INCORRECT_PAYLOAD_ERROR);
      });
    });
  });

  describe('watchRequests', () => {
    const gen = watchRequests();

    it('waits for a request action', () => {
      assert.deepEqual(gen.next().value, take(isRequestAction));
    });

    it('forks sendRequest', () => {
      const action = { type: 'REQUEST' };
      assert.deepEqual(gen.next(action).value, fork(sendRequest, action));
    });

    it('waits for another request action', () => {
      assert.deepEqual(gen.next().value, take(isRequestAction));
    });
  });
});
<commit_msg>Fixed watchRequests test<commit_after>import {
  getContext,
  setContext,
  call,
  put,
  all,
  fork,
  take,
  cancelled,
} from 'redux-saga/effects';
import { cloneableGenerator } from 'redux-saga/utils';

import {
  success,
  error,
  abort,
  successAction,
  errorAction,
  abortAction,
} from './actions';
import {
  REQUEST_INSTANCE,
  REQUESTS_CONFIG,
  INCORRECT_PAYLOAD_ERROR,
} from './constants';
import {
  defaultConfig,
  createRequestInstance,
  getRequestInstance,
  getRequestsConfig,
  sendRequest,
  watchRequests,
  abortRequestIfDefined,
  voidCallback,
} from './sagas';
import { isRequestAction } from './helpers';

// TODO: implement those tests with a saga test library

const dummyDriver = {
  getSuccessPayload: () => {},
  getErrorPayload: () => {},
  getRequestHandlers: () => ({
    sendRequest: () => {},
    abortRequest: () => {},
  }),
};

describe('sagas', () => {
  describe('voidCallback', () => {
    it('returns undefined', () => {
      assert.equal(voidCallback(), undefined);
    });
  });

  describe('defaultConfig', () => {
    it('has correct value', () => {
      const expected = {
        success,
        error,
        abort,
        successAction,
        errorAction,
        abortAction,
        driver: null,
        onRequest: null,
        onSuccess: null,
        onError: null,
        onAbort: null,
      };

      assert.deepEqual(defaultConfig, expected);
    });
  });

  describe('createRequestInstance', () => {
    const requestInstance = { type: 'axios' };

    it('returns correct effect with default config', () => {
      const expected = setContext({
        [REQUEST_INSTANCE]: requestInstance,
        [REQUESTS_CONFIG]: defaultConfig,
      });
      assert.deepEqual(createRequestInstance(requestInstance), expected);
    });

    it('returns correct effect with overwritten config', () => {
      const config = {
        success: 'success',
        successAction,
        error: 'error',
        errorAction,
        abort: 'abort',
        abortAction: voidCallback,
        driver: 'some driver',
        onRequest: voidCallback,
        onSuccess: voidCallback,
        onError: voidCallback,
        onAbort: voidCallback,
      };

      const expected = setContext({
        [REQUEST_INSTANCE]: requestInstance,
        [REQUESTS_CONFIG]: config,
      });
      assert.deepEqual(
        createRequestInstance(requestInstance, config),
        expected,
      );
    });
  });

  describe('getRequestInstance', () => {
    it('returns correct effect', () => {
      assert.deepEqual(getRequestInstance(), getContext(REQUEST_INSTANCE));
    });
  });

  describe('getRequestsConfig', () => {
    it('returns correct effect', () => {
      assert.deepEqual(getRequestsConfig(), getContext(REQUESTS_CONFIG));
    });
  });

  describe('abortRequestIfDefined', () => {
    it('returns call effect when abortRequest defined', () => {
      const abortRequest = () => {};
      assert.deepEqual(abortRequestIfDefined(abortRequest), call(abortRequest));
    });

    it('returns null when abortRequest undefined', () => {
      assert.equal(abortRequestIfDefined(undefined), null);
    });
  });

  describe('sendRequest', () => {
    describe('with correct payload with dispatchRequestAction', () => {
      it('dispatches request action', () => {
        const action = { type: 'FETCH', request: { url: '/url' } };
        const gen = sendRequest(action, { dispatchRequestAction: true });
        gen.next();
        gen.next();
        assert.deepEqual(gen.next().value, put(action));
      });
    });

    describe('with correct payload', () => {
      const config = { ...defaultConfig, driver: dummyDriver };
      const action = {
        type: 'FETCH',
        payload: {
          request: { url: '/url' },
        },
      };
      const gen = cloneableGenerator(sendRequest)(action);
      const requestInstance = () => ({ type: 'axios' });
      const response = { data: 'some response' };
      const requestHandlers = dummyDriver.getRequestHandlers(requestInstance);

      it('gets request instance', () => {
        assert.deepEqual(gen.next().value, getRequestInstance());
      });

      it('gets request config', () => {
        assert.deepEqual(gen.next(requestInstance).value, getRequestsConfig());
      });

      it('gets request handlers', () => {
        const expected = call(
          [dummyDriver, 'getRequestHandlers'],
          requestInstance,
          config,
        );
        assert.deepEqual(gen.next(config).value, expected);
      });

      // it('calls onRequest', () => {
      //   const expected = call(config.onRequest, action.payload.request);
      //   assert.deepEqual(gen.next(requestHandlers).value, expected);
      // });

      it('calls sendRequest', () => {
        const expected = call(
          requestHandlers.sendRequest,
          action.payload.request,
        );
        assert.deepEqual(gen.next(requestHandlers).value, expected);
      });

      it('dispatches error, calls on Error and returns request error action when there is an error', () => {
        const errorGen = gen.clone();
        const requestError = new Error('Something went wrong');
        const errorPayload = 'error payload';
        assert.deepEqual(
          errorGen.throw(requestError).value,
          call(dummyDriver.getErrorPayload, requestError),
        );
        const expected = put({
          type: error(action.type),
          payload: errorPayload,
          error: true,
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(errorGen.next(errorPayload).value, expected);
        // assert.deepEqual(
        //   errorGen.next(requestHandlers).value,
        //   call(config.onError, requestError),
        // );
        errorGen.next(); // to fire finally yield
        assert.deepEqual(errorGen.next().value, { error: requestError });
      });

      it('dispatches request success action when response is successful', () => {
        assert.deepEqual(
          gen.next(response).value,
          call(dummyDriver.getSuccessPayload, response, action.payload.request),
        );
        const expected = put({
          type: success(action.type),
          payload: {
            data: response.data,
          },
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(gen.next(response.data).value, expected);
      });

      // it('calls onSuccess', () => {
      //   const expected = call(config.onSuccess, response);
      //   assert.deepEqual(gen.next().value, expected);
      // });

      it('awaits cancellation', () => {
        assert.deepEqual(gen.next().value, cancelled());
      });

      it('handles cancellation when cancelled', () => {
        assert.deepEqual(
          gen.next(true).value,
          abortRequestIfDefined(requestHandlers.abortRequest),
        );
        const expected = put({
          type: abort(action.type),
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(gen.next().value, expected);
        // assert.deepEqual(gen.next(requestHandlers).value, call(config.onAbort));
      });

      it('returns response', () => {
        assert.deepEqual(gen.clone().next().value, { response });
      });
    });

    describe('with correct payload with multiple requests', () => {
      const config = { ...defaultConfig, driver: dummyDriver };
      const action = {
        type: 'FETCH_MULTIPLE',
        request: [{ url: '/url1' }, { url: '/url2' }],
      };
      const gen = sendRequest(action);
      const requestInstance = () => ({ type: 'axios' });
      const responses = [
        { data: 'some response' },
        { data: 'another response' },
      ];
      const requestHandlers = dummyDriver.getRequestHandlers(requestInstance);

      it('gets request instance', () => {
        assert.deepEqual(gen.next().value, getRequestInstance());
      });

      it('gets request config', () => {
        assert.deepEqual(gen.next(requestInstance).value, getRequestsConfig());
      });

      it('gets request handlers', () => {
        const expected = call(
          [dummyDriver, 'getRequestHandlers'],
          requestInstance,
          config,
        );
        assert.deepEqual(gen.next(config).value, expected);
      });

      // it('calls onRequest', () => {
      //   const expected = call(config.onRequest, action.request);
      //   assert.deepEqual(gen.next(requestHandlers).value, expected);
      // });

      it('calls sendRequests', () => {
        const expected = all([
          call(requestHandlers.sendRequest, action.request[0]),
          call(requestHandlers.sendRequest, action.request[1]),
        ]);
        assert.deepEqual(gen.next(requestHandlers).value, expected);
      });

      it('dispatches request success action when reponse is successful', () => {
        assert.deepEqual(
          gen.next(responses).value,
          call(dummyDriver.getSuccessPayload, responses, action.request),
        );
        const data = [responses[0].data, responses[1].data];
        const expected = put({
          type: success(action.type),
          data,
          meta: {
            requestAction: action,
          },
        });
        assert.deepEqual(gen.next(data).value, expected);
      });

      // it('calls onSuccess', () => {
      //   const expected = call(config.onSuccess, responses);
      //   assert.deepEqual(gen.next(requestHandlers).value, expected);
      // });

      it('returns response array', () => {
        gen.next(); // to fire finally yield
        assert.deepEqual(gen.next().value, { response: responses });
      });
    });

    describe('with incorrect payload', () => {
      it('throws error when action payload is invalid', () => {
        const invalidAction = { type: 'FETCH' };
        const gen = sendRequest(invalidAction);
        assert.throws(() => gen.next(), INCORRECT_PAYLOAD_ERROR);
      });
    });
  });

  describe('watchRequests', () => {
    const gen = watchRequests();

    it('waits for a request action', () => {
      assert.deepEqual(gen.next().value, take(isRequestAction));
    });

    it('forks sendRequest', () => {
      const action = { type: 'REQUEST', request: {} };
      assert.deepEqual(gen.next(action).value, fork(sendRequest, action));
    });

    it('waits for another request action', () => {
      assert.deepEqual(gen.next().value, take(isRequestAction));
    });
  });
});
<|endoftext|>