<commit_before>import Renderer from './renderer'
import utils from './utils'
import Controls from './controls'

import THREE from 'threejs360';

export default class ThreeSixtyViewer {
  constructor(options={}) {
    Object.assign(this, {height: 360, width: 640, initialYaw: 90, verticalPanning: true}, options);
    let {
        height,
        width,
        container,
        containerId,
        initialYaw,
        verticalPanning,
        onDragStart,
        onDragStop,
    } = this;
    this.renderer = new Renderer({height, width});
    this.camera = new THREE.PerspectiveCamera(80, this.width / this.height, 0.1, 100);
    this.controls = new Controls({
        camera: this.camera,
        renderer: this.renderer,
        initialYaw,
        verticalPanning,
        onDragStart,
        onDragStop,
    });
    this.update = this.update.bind(this);
    this.stopVideoLoop = this.stopVideoLoop.bind(this);
    this.onError = this.onError.bind(this);
    this.startVideoLoop = this.startVideoLoop.bind(this);
    this.needsUpdate = false;
    this.scene = this.createScene();
    this.scene.add(this.camera);
    this.element = this.getElement();
    this.element.addEventListener('playing', this.startVideoLoop);
    this.element.addEventListener('pause', this.stopVideoLoop);
    this.element.addEventListener('ended', this.stopVideoLoop);
    this.texture = this.createTexture();
    this.renderer.setTexture(this.texture);
    this.scene.getObjectByName('photo').children = [this.renderer.mesh];
    this.target = this.container ? this.container : document.querySelector(this.containerId);
  }

  play() {
    this.element.play && this.element.play();
  }

  pause() {
    this.element.pause && this.element.pause();
  }

  centralize() {
    this.controls.centralize();
  }

  stopVideoLoop() {
    clearTimeout(this.videoLoopId);
    this.needsUpdate = false;
  }

  destroy() {
    this.element.style.display = '';
    clearTimeout(this.videoLoopId);
    cancelAnimationFrame(this.animationFrameId);
    this.element.pause && this.element.pause();
    this.target.removeChild(this.renderer.el);
    this.controls.destroy();
    this.renderer.destroy();
  }

  setSize(size) {
    this.camera.aspect = size.width / size.height;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(size);
  }

  getElement() {
    if (this.source && this.source.tagName)
      return this.source;
    let video = document.createElement('video');
    video.loop = this.loop || false;
    video.muted = this.muted || false;
    video.setAttribute('crossorigin', 'anonymous');
    video.setAttribute('webkit-playsinline', 'true');
    video.setAttribute('playsinline', 'true');
    video.setAttribute('src', this.source);
    video.autoplay = this.autoplay !== undefined ? this.autoplay : true;
    video.addEventListener('error', this.onError);
    return video;
  }

  createTexture() {
    let texture = new THREE.Texture(this.element);
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;
    texture.format = THREE.RGBFormat;
    texture.generateMipmaps = false;
    texture.needsUpdate = true;
    return texture;
  }

  createScene() {
    let scene = new THREE.Scene();
    let group = new THREE.Object3D();
    group.name = 'photo';
    scene.add(group);
    return scene;
  }

  onError(err) {
    console.error('error loading', this.source, err);
  }

  startVideoLoop() {
    let videoFps = 1000 / 25;
    let videoLoop = () => {
      this.needsUpdate = true;
      this.videoLoopId = setTimeout(videoLoop, videoFps);
    }

    videoLoop();
  }

  render() {
    this.target.appendChild(this.renderer.el);
    this.element.style.display = 'none';

    let loop = () => {
      this.animationFrameId = requestAnimationFrame(loop);
      let cameraUpdated = this.controls.update();
      this.renderer.render(this.scene, this.camera, this.needsUpdate || cameraUpdated);
      this.needsUpdate = false;
    };

    this.startVideoLoop();
    loop();
  }
}

<commit_msg>remove reference for unused method<commit_after>import Renderer from './renderer'
import utils from './utils'
import Controls from './controls'

import THREE from 'threejs360';

export default class ThreeSixtyViewer {
  constructor(options={}) {
    Object.assign(this, {height: 360, width: 640, initialYaw: 90, verticalPanning: true}, options);
    let {
        height,
        width,
        container,
        containerId,
        initialYaw,
        verticalPanning,
        onDragStart,
        onDragStop,
    } = this;
    this.renderer = new Renderer({height, width});
    this.camera = new THREE.PerspectiveCamera(80, this.width / this.height, 0.1, 100);
    this.controls = new Controls({
        camera: this.camera,
        renderer: this.renderer,
        initialYaw,
        verticalPanning,
        onDragStart,
        onDragStop,
    });
    this.stopVideoLoop = this.stopVideoLoop.bind(this);
    this.onError = this.onError.bind(this);
    this.startVideoLoop = this.startVideoLoop.bind(this);
    this.needsUpdate = false;
    this.scene = this.createScene();
    this.scene.add(this.camera);
    this.element = this.getElement();
    this.element.addEventListener('playing', this.startVideoLoop);
    this.element.addEventListener('pause', this.stopVideoLoop);
    this.element.addEventListener('ended', this.stopVideoLoop);
    this.texture = this.createTexture();
    this.renderer.setTexture(this.texture);
    this.scene.getObjectByName('photo').children = [this.renderer.mesh];
    this.target = this.container ? this.container : document.querySelector(this.containerId);
  }

  play() {
    this.element.play && this.element.play();
  }

  pause() {
    this.element.pause && this.element.pause();
  }

  centralize() {
    this.controls.centralize();
  }

  stopVideoLoop() {
    clearTimeout(this.videoLoopId);
    this.needsUpdate = false;
  }

  destroy() {
    this.element.style.display = '';
    clearTimeout(this.videoLoopId);
    cancelAnimationFrame(this.animationFrameId);
    this.element.pause && this.element.pause();
    this.target.removeChild(this.renderer.el);
    this.controls.destroy();
    this.renderer.destroy();
  }

  setSize(size) {
    this.camera.aspect = size.width / size.height;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(size);
  }

  getElement() {
    if (this.source && this.source.tagName)
      return this.source;
    let video = document.createElement('video');
    video.loop = this.loop || false;
    video.muted = this.muted || false;
    video.setAttribute('crossorigin', 'anonymous');
    video.setAttribute('webkit-playsinline', 'true');
    video.setAttribute('playsinline', 'true');
    video.setAttribute('src', this.source);
    video.autoplay = this.autoplay !== undefined ? this.autoplay : true;
    video.addEventListener('error', this.onError);
    return video;
  }

  createTexture() {
    let texture = new THREE.Texture(this.element);
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;
    texture.format = THREE.RGBFormat;
    texture.generateMipmaps = false;
    texture.needsUpdate = true;
    return texture;
  }

  createScene() {
    let scene = new THREE.Scene();
    let group = new THREE.Object3D();
    group.name = 'photo';
    scene.add(group);
    return scene;
  }

  onError(err) {
    console.error('error loading', this.source, err);
  }

  startVideoLoop() {
    let videoFps = 1000 / 25;
    let videoLoop = () => {
      this.needsUpdate = true;
      this.videoLoopId = setTimeout(videoLoop, videoFps);
    }

    videoLoop();
  }

  render() {
    this.target.appendChild(this.renderer.el);
    this.element.style.display = 'none';

    let loop = () => {
      this.animationFrameId = requestAnimationFrame(loop);
      let cameraUpdated = this.controls.update();
      this.renderer.render(this.scene, this.camera, this.needsUpdate || cameraUpdated);
      this.needsUpdate = false;
    };

    this.startVideoLoop();
    loop();
  }
}

<|endoftext|>


<commit_before>"use strict";

var async = require('cordova.plugin.boni.аsync'),
  config = require('cordova.plugin.boni.config'),
  Spot = require('cordova.plugin.boni.spot'),
  _ = require('cordova.plugin.boni.lodash');

/**
 * Collection of all iBeacons in range
 */
var BeaconRegistry = function() {};

BeaconRegistry.prototype = function() {
  var _spots = [],
    /**
     * Event callbacks
     */
    _onFarFromSpot = null,
    _onNearToSpot = null,
    _onImmediateToSpot = null,

    get = function(uuid, major, minor) {

      if (!uuid || !major || !minor) {
        /**
         * If beacon's major id, minor id or uuid is not defined,
         * return spot is not exists
         */
        return;
      }

      major = parseInt(major);
      minor = parseInt(minor);

      /**
       * If all input arguments are valid, check whether the beacon already exists
       * in the beacon registry and return it. Otherwise undefined.
       */
      return _.find(_spots, function(spot) {
        var beacon = spot.getBeacon();

        return (
          beacon.uuid == uuid &&
          beacon.major == major &&
          beacon.minor == minor
        );
      });
    },

    add = function(error, spot) {

      if (!error) {

        /**
         * If there are no errors, add spot to beacon registry
         */
        _spots.push(spot);

      }
    },

    clear = function() {
      _spots = [];
    },

    size = function() {
      return _spots.length;
    },

    applyProximityStrategy = function(spot) {

      if (spot) {
        var beacon = spot.getBeacon();
        var proximityFactor = calculateProximityFactor(beacon);

        if (proximityFactor <= config.proximity.immediate.factor) {
          beacon.proximity = config.proximity.immediate.name;
        } else if (proximityFactor >=
          config.proximity.immediate.factor + config.proximity.buffer &&
          proximityFactor <= config.proximity.near.factor) {
          beacon.proximity = config.proximity.near.name;
        } else if (proximityFactor >=
          config.proximity.near.factor + config.proximity.buffer) {
          beacon.proximity = config.proximity.far.name;
        } else {
          /**
           * If the proximityFactor is not in any of the buffers, keep the latest proximity
           */
          beacon.proximity = beacon.previouseProximity;
        }
      }

      function calculateProximityFactor(beacon) {
        return parseInt(parseInt(100 * beacon.rssi - beacon.tx) / beacon.tx);
      }
    },

    register = function(beacon, done) {

      if (beacon) {

        var spot = new Spot(beacon);

        if (spot) {
          applyProximityStrategy(spot);

          async.waterfall([
            function(callback) {

              spot.getData(callback);
            }
          ], function(err, spotWithData) {

            if (!err && spotWithData && spotWithData.data && spotWithData.data.length > 0) {

              add(null, spotWithData);
              done(beacon);
            } else {
              done(null);
            }
          });
        }
      } else {
        throw 'Beacon is not valid';
      }
    },

    process = function(beacon) {

      if (!beacon) {
        return;
      }

      var spot = get(beacon.uuid, beacon.major,
        beacon.minor);

      /**
       * Check whether the spots is registered
       */
      if (spot) {

        /**
         * Update iBeacon metadata
         */
        var currentBeacon = spot.getBeacon();
        currentBeacon.proximity = beacon.proximity;
        currentBeacon.rssi = beacon.rssi;
        currentBeacon.tx = beacon.tx;
        currentBeacon.accuracy = beacon.accuracy;

        applyProximityStrategy(spot);

        if (currentBeacon.proximity) {

          /**
           * If previouse proximity is not set or previouse proximity is different
           * than the current. The idea is to call callback only on change.
           */
          if (!currentBeacon.previouseProximity || currentBeacon.proximity !==
            currentBeacon.previouseProximity) {

            /**
             * update the previouse proximity
             */
            currentBeacon.previouseProximity = currentBeacon.proximity;

            /**
             * Call the appropreate callbacks if there are registered
             */
            switch (currentBeacon.proximity) {
              case config.proximity.immediate.name:
                callRegisteredCallback(_onImmediateToSpot, spot);
                break;
              case config.proximity.near.name:
                callRegisteredCallback(_onNearToSpot, spot);
                break;
              case config.proximity.far.name:
                callRegisteredCallback(_onFarFromSpot, spot);
                break;
              default:

            }
          }
        }

      } else {
        /**
         * If it is not registered, retrieve its data.
         * In this way only one server call is initiated to retrieve iBeacon Content (cloud) data.
         */
        register(beacon, process);
      }

      function callRegisteredCallback(callback, spot) {

        if (_.isFunction(callback) && spot) {
          if (spot.data) {
            callback(null, spot.data);
          }
        }
      }
    },

    /**
     * Register onImmediateToSpot callback
     * @param  {Function} callback function to be called when the user is immediate to spot
     */
    onImmediateToSpot = function(callback) {
      _onImmediateToSpot = callback;
    },

    /**
     * Register onNearToSpot callback
     * @param  {Function} callback function to be called when the user is near to spot
     */
    onNearToSpot = function(callback) {
      _onNearToSpot = callback;
    },

    /**
     * Register onFarFromSpot callback
     * @param  {Function} callback function to be called when the user is far from spot
     */
    onFarFromSpot = function(callback) {
      _onFarFromSpot = callback;
    };

  return {
    clear: clear,
    size: size,
    add: add,
    register: register,
    applyProximityStrategy: applyProximityStrategy,
    process: process,
    get: get,
    onFarFromSpot: onFarFromSpot,
    onNearToSpot: onNearToSpot,
    onImmediateToSpot: onImmediateToSpot
  };
}();

module.exports = BeaconRegistry;
<commit_msg>add spot in registry even it has no content items. In this way the number of http requests is dramatically decreased<commit_after>"use strict";

var async = require('cordova.plugin.boni.аsync'),
  config = require('cordova.plugin.boni.config'),
  Spot = require('cordova.plugin.boni.spot'),
  _ = require('cordova.plugin.boni.lodash');

/**
 * Collection of all iBeacons in range
 */
var BeaconRegistry = function() {};

BeaconRegistry.prototype = function() {
  var _spots = [],
    /**
     * Event callbacks
     */
    _onFarFromSpot = null,
    _onNearToSpot = null,
    _onImmediateToSpot = null,

    get = function(uuid, major, minor) {

      if (!uuid || !major || !minor) {
        /**
         * If beacon's major id, minor id or uuid is not defined,
         * return spot is not exists
         */
        return;
      }

      major = parseInt(major);
      minor = parseInt(minor);

      /**
       * If all input arguments are valid, check whether the beacon already exists
       * in the beacon registry and return it. Otherwise undefined.
       */
      return _.find(_spots, function(spot) {
        var beacon = spot.getBeacon();

        return (
          beacon.uuid == uuid &&
          beacon.major == major &&
          beacon.minor == minor
        );
      });
    },

    add = function(error, spot) {

      if (!error) {

        /**
         * If there are no errors, add spot to beacon registry
         */
        _spots.push(spot);

      }
    },

    clear = function() {
      _spots = [];
    },

    size = function() {
      return _spots.length;
    },

    applyProximityStrategy = function(spot) {

      if (spot) {
        var beacon = spot.getBeacon();
        var proximityFactor = calculateProximityFactor(beacon);

        if (proximityFactor <= config.proximity.immediate.factor) {
          beacon.proximity = config.proximity.immediate.name;
        } else if (proximityFactor >=
          config.proximity.immediate.factor + config.proximity.buffer &&
          proximityFactor <= config.proximity.near.factor) {
          beacon.proximity = config.proximity.near.name;
        } else if (proximityFactor >=
          config.proximity.near.factor + config.proximity.buffer) {
          beacon.proximity = config.proximity.far.name;
        } else {
          /**
           * If the proximityFactor is not in any of the buffers, keep the latest proximity
           */
          beacon.proximity = beacon.previouseProximity;
        }
      }

      function calculateProximityFactor(beacon) {
        return parseInt(parseInt(100 * beacon.rssi - beacon.tx) / beacon.tx);
      }
    },

    register = function(beacon, done) {

      if (beacon) {

        var spot = new Spot(beacon);

        if (spot) {
          applyProximityStrategy(spot);

          async.waterfall([
            function(callback) {

              spot.getData(callback);
            }
          ], function(err, spotWithData) {

            if (!err && spotWithData && spotWithData.data && spotWithData.data.length > 0) {

              add(null, spotWithData);
              done(beacon);
            } else {
              add(null, spot);
              done(null);
            }
          });
        }
      } else {
        throw 'Beacon is not valid';
      }
    },

    process = function(beacon) {

      if (!beacon) {
        return;
      }

      var spot = get(beacon.uuid, beacon.major,
        beacon.minor);

      /**
       * Check whether the spots is registered
       */
      if (spot) {

        /**
         * Update iBeacon metadata
         */
        var currentBeacon = spot.getBeacon();
        currentBeacon.proximity = beacon.proximity;
        currentBeacon.rssi = beacon.rssi;
        currentBeacon.tx = beacon.tx;
        currentBeacon.accuracy = beacon.accuracy;

        applyProximityStrategy(spot);

        if (currentBeacon.proximity) {

          /**
           * If previouse proximity is not set or previouse proximity is different
           * than the current. The idea is to call callback only on change.
           */
          if (!currentBeacon.previouseProximity || currentBeacon.proximity !==
            currentBeacon.previouseProximity) {

            /**
             * update the previouse proximity
             */
            currentBeacon.previouseProximity = currentBeacon.proximity;

            /**
             * Call the appropreate callbacks if there are registered
             */
            switch (currentBeacon.proximity) {
              case config.proximity.immediate.name:
                callRegisteredCallback(_onImmediateToSpot, spot);
                break;
              case config.proximity.near.name:
                callRegisteredCallback(_onNearToSpot, spot);
                break;
              case config.proximity.far.name:
                callRegisteredCallback(_onFarFromSpot, spot);
                break;
              default:

            }
          }
        }

      } else {
        /**
         * If it is not registered, retrieve its data.
         * In this way only one server call is initiated to retrieve iBeacon Content (cloud) data.
         */
        register(beacon, process);
      }

      function callRegisteredCallback(callback, spot) {

        if (_.isFunction(callback) && spot) {
          if (spot.data) {
            callback(null, spot.data);
          }
        }
      }
    },

    /**
     * Register onImmediateToSpot callback
     * @param  {Function} callback function to be called when the user is immediate to spot
     */
    onImmediateToSpot = function(callback) {
      _onImmediateToSpot = callback;
    },

    /**
     * Register onNearToSpot callback
     * @param  {Function} callback function to be called when the user is near to spot
     */
    onNearToSpot = function(callback) {
      _onNearToSpot = callback;
    },

    /**
     * Register onFarFromSpot callback
     * @param  {Function} callback function to be called when the user is far from spot
     */
    onFarFromSpot = function(callback) {
      _onFarFromSpot = callback;
    };

  return {
    clear: clear,
    size: size,
    add: add,
    register: register,
    applyProximityStrategy: applyProximityStrategy,
    process: process,
    get: get,
    onFarFromSpot: onFarFromSpot,
    onNearToSpot: onNearToSpot,
    onImmediateToSpot: onImmediateToSpot
  };
}();

module.exports = BeaconRegistry;
<|endoftext|>


<commit_before>const cytosnap = require('cytosnap');
const getRenderOptions = require('./getRenderOptions');

/* renderImage (graphValues)
   Render a collection of images representing the most recent layouts
   - Requires a valid graphValues object which consists of a layouts field and a graph field
   - Returns a collection of PNGs in base64
*/
function renderImage(graphValues) {
  //Create a cytosnap object
  var snap = cytosnap();
  var layouts = graphValues.layouts;
  var graph = graphValues.graph;

  //Start Cytosnap Instance
  return snap.start().then(function () {
    //Create an array of images
    let images = layouts.map(layout => {
      //Build a render options object
      let renderJson = getRenderOptions(graph, layout.positions);

      //Generate an image of a layout.
      return snap.shot(renderJson).then(res => ({
        id: layout.id,
        date: layout.date_added,
        img: res
      }));
    });

    return Promise.all(images);
  });
}

module.exports = renderImage; <commit_msg>Updated comments<commit_after>const cytosnap = require('cytosnap');
const getRenderOptions = require('./getRenderOptions');

/* renderImage (graphValues)
   Render a collection of images representing cytoscape graphs
   - Requires a valid graphValues object which consists of a layouts field and a graph field
   - Returns a collection of PNGs in base64
*/
function renderImage(graphValues) {
  //Create a cytosnap object
  var snap = cytosnap();
  var layouts = graphValues.layouts;
  var graph = graphValues.graph;

  //Start Cytosnap Instance
  return snap.start().then(function () {
    //Create an array of images
    let images = layouts.map(layout => {
      //Build a render options object
      let renderJson = getRenderOptions(graph, layout.positions);

      //Generate an image of a layout.
      return snap.shot(renderJson).then(res => ({
        id: layout.id,
        date: layout.date_added,
        img: res
      }));
    });

    return Promise.all(images);
  });
}

module.exports = renderImage; <|endoftext|>


<commit_before>controllers.Map = {
	activeGame: null,
	activeGameIndex: -1,
	circles: [],
	init: function() {
		var me = this;
		me.drawCircles();
		me.bindEvents();
	},
	bindEvents: function() {
		var me = this,
			view = views.Map,
			navigationBar = views.NavigationBar,
			messageBox = views.MessageBox;

		view.on('touchstart', '.circle.active', me.onActiveCircleTap);
		view.on('touchstart', '.circle.scanned', me.onScannedCircleTap);
		messageBox.on('touchstart', '.button.scan', me.onScanButtonTap);
		messageBox.on('touchstart', '.button.play-again', me.onPlayAgain);
                navigationBar.on('touchstart', '.left-button.back-from-map', me.onBack);
	},
	onBack: function() {
		controllers.Common.switchNavigationBar(
			"ZAGREB",
			"left-button close",
			"right-button info",
			"fa fa-times",
			"fa fa-info"
		);

		views.Map.hide(200);
		views.MainMenu.show(200);
	},
	getCirclesContainer: function() {
		return views.Map.find(".circles").eq(0);
	},
	drawCircles: function() {
		var me = this,
			circlesContainer = me.getCirclesContainer(),
			games = models.Games.getAllGames(),
			html = '',
			game;

		circlesContainer.empty();
		me.activeGame = null;
		me.activeGameIndex = -1;

		for(var i = 0; i < 7; i++) {
			game = games[i];
			html = me.generateCircleHtml(i, game);
			circlesContainer.append(html);

			if(game.status === "active" || game.status === "scanned") {
				me.activeGame = game;
				me.activeGameIndex = i;
			}
		}

		if(me.activeGame == null) {
			me.gameFinished();
		}

		var circles = circlesContainer.find(".circle"),
			doc = $(document),
			docWidth = doc.width(),
			docHeight = doc.height(),
			size = (docWidth - 20) * 0.24,
			vertical = (docHeight - 110) / 7,
			left = 0,
			top = 0;

		me.circles = circles;

		for(var i = 0; i < circles.length; i++) {
			var circle = circles.eq(i);
			circle.css({
				"height": size,
				"width": size,
				"top": top,
				"left": left
			});
			switch(i) {
				case 0:
					left += size + 5;
					top += 2;
					break;
				case 1:
					left -= 10;
					top += vertical + 10;
					break;
				case 2:
					left += 10;
					top += vertical + 10;
					break;
				case 3:
					left += size + 5;
					top += 4;
					break;
				case 4:
					left += 25;
					top += vertical + 10;
					break;
				case 5:
					left += size - 25;
					top += vertical + 10;
					break;
			}
		}
	},
	refreshCircles: function() {
		var me = this,
			games = models.Games.getAllGames(),
			circles = me.circles;

		me.activeGame = null;
		me.activeGameIndex = -1;

		for(var i = 0; i < circles.length; i++) {
			var circle = circles.eq(i),
				game = games[i];

			circle.removeClass();
			circle.addClass("circle " + game.status);

			if(game.status === "active" || game.status === "scanned") {
				me.activeGame = game;
				me.activeGameIndex = i;
			}
		}

		if(me.activeGame == null) {
			me.gameFinished();
		}
	},
	gameFinished: function() {
		controllers.Common.showMessageBox("Game finished" + '
Unesite svoje ime!

<input class="textInput" id="nameField" type="text"/>

'
                , "Play again", "button play-again", false);
                $('#nameField').bind('keypress', function(e) {
                    if((e.keyCode || e.which) == 13){
                            $('#nameField').blur();
                    }
                });
        },
	onPlayAgain: function() {
                var cname = document.getElementById("nameField").value;
                if(cname.length > 0) {controllers.Score.updateScoreBoard(cname);}
                models.Score.setCurrentScore(0);
                models.Score.syncData();
		controllers.Main.generateGames();
		controllers.Map.refreshCircles();
		views.MessageBox.hide(200);
	},
	generateCircleHtml: function(number, game) {
		return '
' +
					'' + (number + 1) + '' +
				'
';
	},
	onActiveCircleTap: function() {
		var index = $(this).attr('data-index'),
			games = models.Games,
			controller = controllers.Map;

		var game = games.getGameByIndex(index);

		controller.showGameScanMessageBox(game);
	},
	showGameScanMessageBox: function(game) {
		controllers.Common.showMessageBox(game.location.address, "Scan", "button scan", true);
	},
	onScannedCircleTap: function() {
		var controller = controllers.Map;
		controller.selectGame();
	},
	onScanButtonTap: function() {
		var me = controllers.Map,
			isProduction = controllers.Main.isProduction();

		if(isProduction) {
	        cordova.plugins.barcodeScanner.scan(
	            function(result) {
	                if(!result.cancelled)
	                    me.onScanSuccessful(result);
	            },
	            function(error) {
	                me.onScanError(error);
	            }
	        );
    	}
    	else {
    		me.onScanSuccessful({
    			text: me.activeGame.location.code
    		});
    	}
	},
	onScanSuccessful: function(result) {
		var controller = controllers.Map,
			activeGame = controller.activeGame,
			gameNumber = controller.activeGameIndex;

		if(result.text == activeGame.location.code) {
			activeGame.status = "scanned";
			models.Games.setGameByIndex(gameNumber, activeGame);
			models.Games.syncData();

			controller.refreshCircles();

			controller.selectGame();
		}
		else {
			controllers.Common.showMessageBox("Skenirali ste krivi kod.", "Scan", "button no-button", true);
		}
	},
	onScanError: function(error) {
		controllers.Common.showMessageBox("Pogreška prilikom skeniranja.", "Scan", "button no-button", true);
	},
	selectGame: function() {
		var controller = controllers.Map,
			activeGame = controller.activeGame,
			gameNumber = controller.activeGameIndex,
			gameType = activeGame.game.questionType;

		switch(gameType) {
			case "A":
				controller.showQuestionAbcd(gameNumber + 1, activeGame);
				break;
			case "B":
				controller.showQuestionInput(gameNumber + 1, activeGame);
				break;
			case "C":
				controller.showQuestionOrder(gameNumber + 1, activeGame);
		}
	},
	showQuestionAbcd: function(gameNumber, activeGame) {
		var questionView = views.QuestionAbcd,
			a1 = questionView.find(".a.one .text").eq(0),
			a2 = questionView.find(".a.two .text").eq(0),
			a3 = questionView.find(".a.three .text").eq(0),
			a4 = questionView.find(".a.four .text").eq(0),
			question = questionView.find(".question .text").eq(0),
			correct = activeGame.game.question.correctAnswer;

			question.html(activeGame.game.question.text);

			a1.html(activeGame.game.question.a1);
			a1.attr("data-correct", (correct == "a1") ? "Y" : "N");

			a2.html(activeGame.game.question.a2);
			a2.attr("data-correct", (correct == "a2") ? "Y" : "N");

			a3.html(activeGame.game.question.a3);
			a3.attr("data-correct", (correct == "a3") ? "Y" : "N");

			a4.html(activeGame.game.question.a4);
			a4.attr("data-correct", (correct == "a4") ? "Y" : "N");

		controllers.Common.switchNavigationBar(
			"GAME " + gameNumber,
			"left-button back-from-abcd",
			"right-button confirm-abcd",
			"fa fa-chevron-left",
			"fa fa-check"
		);

		controllers.QuestionAbcd.clearAnswers();
                controllers.QuestionAbcd.startTimer();
		views.Map.hide(200);
		views.MessageBox.hide(200);
		views.QuestionAbcd.show(200);
	},
	showQuestionInput: function(gameNumber, activeGame){
		var questionView = views.QuestionInput,
		    question = questionView.find(".question .text").eq(0);

		question.html(activeGame.game.question.text);

		controllers.Common.switchNavigationBar(
			"GAME " + gameNumber,
			"left-button back-from-input",
			"right-button confirm-input",
			"fa fa-chevron-left",
			"fa fa-check"
		);

                controllers.QuestionInput.startTimer();
		views.Map.hide(200);
		views.MessageBox.hide(200);
		views.QuestionInput.show(200);
	},
        showQuestionOrder: function(gameNumber, activeGame) {
            var questionView = views.QuestionOrder,
                    a1 = questionView.find("#a1").eq(0),
                    a2 = questionView.find("#a2").eq(0),
                    a3 = questionView.find("#a3").eq(0),
                    a4 = questionView.find("#a4").eq(0),
                    question = questionView.find(".question .text").eq(0);

                question.html(activeGame.game.question.text);

                a1.html(activeGame.game.question.a1);
                a1.removeClass("selected");
                a2.html(activeGame.game.question.a2);
                a2.removeClass("selected");
                a3.html(activeGame.game.question.a3);
                a3.removeClass("selected");
                a4.html(activeGame.game.question.a4);
                a4.removeClass("selected");

                controllers.Common.switchNavigationBar(
			"GAME " + gameNumber,
			"left-button back-from-order",
			"right-button confirm-order",
			"fa fa-chevron-left",
			"fa fa-check"
                );

                controllers.QuestionOrder.startTimer();
                views.Map.hide(200);
                views.MessageBox.hide(200);
                views.QuestionOrder.show(200);
        }
};
<commit_msg>jos debugova<commit_after>controllers.Map = {
	activeGame: null,
	activeGameIndex: -1,
	circles: [],
	init: function() {
		var me = this;
		me.drawCircles();
		me.bindEvents();
	},
	bindEvents: function() {
		var me = this,
			view = views.Map,
			navigationBar = views.NavigationBar,
			messageBox = views.MessageBox;

		view.on('touchstart', '.circle.active', me.onActiveCircleTap);
		view.on('touchstart', '.circle.scanned', me.onScannedCircleTap);
		messageBox.on('touchstart', '.button.scan', me.onScanButtonTap);
		messageBox.on('touchstart', '.button.play-again', me.onPlayAgain);
                navigationBar.on('touchstart', '.left-button.back-from-map', me.onBack);
	},
	onBack: function() {
		controllers.Common.switchNavigationBar(
			"ZAGREB",
			"left-button close",
			"right-button info",
			"fa fa-times",
			"fa fa-info"
		);

		views.Map.hide(200);
		views.MainMenu.show(200);
	},
	getCirclesContainer: function() {
		return views.Map.find(".circles").eq(0);
	},
	drawCircles: function() {
		var me = this,
			circlesContainer = me.getCirclesContainer(),
			games = models.Games.getAllGames(),
			html = '',
			game;

		circlesContainer.empty();
		me.activeGame = null;
		me.activeGameIndex = -1;

		for(var i = 0; i < 7; i++) {
			game = games[i];
			html = me.generateCircleHtml(i, game);
			circlesContainer.append(html);

			if(game.status === "active" || game.status === "scanned") {
				me.activeGame = game;
				me.activeGameIndex = i;
			}
		}

		if(me.activeGame == null) {
			me.gameFinished();
		}

		var circles = circlesContainer.find(".circle"),
			doc = $(document),
			docWidth = doc.width(),
			docHeight = doc.height(),
			size = (docWidth - 40) * 0.23,
			vertical = (docHeight - 110) / 7,
			left = 0,
			top = 0;

		me.circles = circles;

		for(var i = 0; i < circles.length; i++) {
			var circle = circles.eq(i);
			circle.css({
				"height": size,
				"width": size,
				"top": top,
				"left": left
			});
			switch(i) {
				case 0:
					left += size + 5;
					top += 2;
					break;
				case 1:
					left -= 10;
					top += vertical + 10;
					break;
				case 2:
					left += 10;
					top += vertical + 10;
					break;
				case 3:
					left += size + 5;
					top += 4;
					break;
				case 4:
					left += 25;
					top += vertical + 10;
					break;
				case 5:
					left += size - 25;
					top += vertical + 10;
					break;
			}
		}
	},
	refreshCircles: function() {
		var me = this,
			games = models.Games.getAllGames(),
			circles = me.circles;

		me.activeGame = null;
		me.activeGameIndex = -1;

		for(var i = 0; i < circles.length; i++) {
			var circle = circles.eq(i),
				game = games[i];

			circle.removeClass();
			circle.addClass("circle " + game.status);

			if(game.status === "active" || game.status === "scanned") {
				me.activeGame = game;
				me.activeGameIndex = i;
			}
		}

		if(me.activeGame == null) {
			me.gameFinished();
		}
	},
	gameFinished: function() {
		controllers.Common.showMessageBox("Game finished" + '
Unesite svoje ime!

<input class="textInput" id="nameField" type="text"/>

'
                , "Play again", "button play-again", false);
                $('#nameField').bind('keypress', function(e) {
                    if((e.keyCode || e.which) == 13){
                            $('#nameField').blur();
                    }
                });
        },
	onPlayAgain: function() {
                var cname = document.getElementById("nameField").value;
                if(cname.length > 0) {controllers.Score.updateScoreBoard(cname);}
                models.Score.setCurrentScore(0);
                models.Score.syncData();
		controllers.Main.generateGames();
		controllers.Map.refreshCircles();
		views.MessageBox.hide(200);
	},
	generateCircleHtml: function(number, game) {
		return '
' +
					'' + (number + 1) + '' +
				'
';
	},
	onActiveCircleTap: function() {
		var index = $(this).attr('data-index'),
			games = models.Games,
			controller = controllers.Map;

		var game = games.getGameByIndex(index);

		controller.showGameScanMessageBox(game);
	},
	showGameScanMessageBox: function(game) {
		controllers.Common.showMessageBox(game.location.address, "Scan", "button scan", true);
	},
	onScannedCircleTap: function() {
		var controller = controllers.Map;
		controller.selectGame();
	},
	onScanButtonTap: function() {
		var me = controllers.Map,
			isProduction = controllers.Main.isProduction();

		if(isProduction) {
	        cordova.plugins.barcodeScanner.scan(
	            function(result) {
	                if(!result.cancelled)
	                    me.onScanSuccessful(result);
	            },
	            function(error) {
	                me.onScanError(error);
	            }
	        );
    	}
    	else {
    		me.onScanSuccessful({
    			text: me.activeGame.location.code
    		});
    	}
	},
	onScanSuccessful: function(result) {
		var controller = controllers.Map,
			activeGame = controller.activeGame,
			gameNumber = controller.activeGameIndex;

		if(result.text == activeGame.location.code) {
			activeGame.status = "scanned";
			models.Games.setGameByIndex(gameNumber, activeGame);
			models.Games.syncData();

			controller.refreshCircles();

			controller.selectGame();
		}
		else {
			controllers.Common.showMessageBox("Skenirali ste krivi kod.", "Scan", "button no-button", true);
		}
	},
	onScanError: function(error) {
		controllers.Common.showMessageBox("Pogreška prilikom skeniranja.", "Scan", "button no-button", true);
	},
	selectGame: function() {
		var controller = controllers.Map,
			activeGame = controller.activeGame,
			gameNumber = controller.activeGameIndex,
			gameType = activeGame.game.questionType;

		switch(gameType) {
			case "A":
				controller.showQuestionAbcd(gameNumber + 1, activeGame);
				break;
			case "B":
				controller.showQuestionInput(gameNumber + 1, activeGame);
				break;
			case "C":
				controller.showQuestionOrder(gameNumber + 1, activeGame);
		}
	},
	showQuestionAbcd: function(gameNumber, activeGame) {
		var questionView = views.QuestionAbcd,
			a1 = questionView.find(".a.one .text").eq(0),
			a2 = questionView.find(".a.two .text").eq(0),
			a3 = questionView.find(".a.three .text").eq(0),
			a4 = questionView.find(".a.four .text").eq(0),
			question = questionView.find(".question .text").eq(0),
			correct = activeGame.game.question.correctAnswer;

			question.html(activeGame.game.question.text);

			a1.html(activeGame.game.question.a1);
			a1.attr("data-correct", (correct == "a1") ? "Y" : "N");

			a2.html(activeGame.game.question.a2);
			a2.attr("data-correct", (correct == "a2") ? "Y" : "N");

			a3.html(activeGame.game.question.a3);
			a3.attr("data-correct", (correct == "a3") ? "Y" : "N");

			a4.html(activeGame.game.question.a4);
			a4.attr("data-correct", (correct == "a4") ? "Y" : "N");

		controllers.Common.switchNavigationBar(
			"GAME " + gameNumber,
			"left-button back-from-abcd",
			"right-button confirm-abcd",
			"fa fa-chevron-left",
			"fa fa-check"
		);

		controllers.QuestionAbcd.clearAnswers();
                controllers.QuestionAbcd.startTimer();
		views.Map.hide(200);
		views.MessageBox.hide(200);
		views.QuestionAbcd.show(200);
	},
	showQuestionInput: function(gameNumber, activeGame){
		var questionView = views.QuestionInput,
		    question = questionView.find(".question .text").eq(0);

		question.html(activeGame.game.question.text);

		controllers.Common.switchNavigationBar(
			"GAME " + gameNumber,
			"left-button back-from-input",
			"right-button confirm-input",
			"fa fa-chevron-left",
			"fa fa-check"
		);

                controllers.QuestionInput.startTimer();
		views.Map.hide(200);
		views.MessageBox.hide(200);
		views.QuestionInput.show(200);
	},
        showQuestionOrder: function(gameNumber, activeGame) {
            var questionView = views.QuestionOrder,
                    a1 = questionView.find("#a1").eq(0),
                    a2 = questionView.find("#a2").eq(0),
                    a3 = questionView.find("#a3").eq(0),
                    a4 = questionView.find("#a4").eq(0),
                    question = questionView.find(".question .text").eq(0);

                question.html(activeGame.game.question.text);

                a1.html(activeGame.game.question.a1);
                a1.removeClass("selected");
                a2.html(activeGame.game.question.a2);
                a2.removeClass("selected");
                a3.html(activeGame.game.question.a3);
                a3.removeClass("selected");
                a4.html(activeGame.game.question.a4);
                a4.removeClass("selected");

                controllers.Common.switchNavigationBar(
			"GAME " + gameNumber,
			"left-button back-from-order",
			"right-button confirm-order",
			"fa fa-chevron-left",
			"fa fa-check"
                );

                controllers.QuestionOrder.startTimer();
                views.Map.hide(200);
                views.MessageBox.hide(200);
                views.QuestionOrder.show(200);
        }
};
<|endoftext|>


<commit_before>// CHANGELOG check
if (_.includes(git.modified_files, "CHANGELOG.md") === false) {
  fail("No CHANGELOG added.")
}

const test_files = _.filter(git.modified_files, function(path){
  return _.includes(path, '__tests__/');
})

const logical_test_paths = _.map(test_files, function(path){
  // turns "lib/__tests__/i-am-good-tests.js" into "lib/i-am-good.js"
  return path.replace(/__tests__\//, '').replace(/-tests\./, '.')
})

const source_paths = _.filter(git.modified_files, function(path){
  return _.includes(path, 'lib/') &&  !_.includes(path, '__tests__/');
})

// Check that any new file has a corresponding tests file
const untested_files = _.difference(source_paths, logical_test_paths)
if (untested_files.length > 0) {
  warn("The following files do not have tests: " + github.html_link(untested_files))
}
<commit_msg>[Danger] Add a check for lib code before failing the build<commit_after>// CHANGELOG check
const hasAppChanges = _.filter(git.modified_files, function(path){
  return _.includes(path, 'lib/');
}).length > 0

if (hasAppChanges && _.includes(git.modified_files, "CHANGELOG.md") === false) {
  fail("No CHANGELOG added.")
}

const testFiles = _.filter(git.modified_files, function(path){
  return _.includes(path, '__tests__/');
})

const logicalTestPaths = _.map(testFiles, function(path){
  // turns "lib/__tests__/i-am-good-tests.js" into "lib/i-am-good.js"
  return path.replace(/__tests__\//, '').replace(/-tests\./, '.')
})

const sourcePaths = _.filter(git.modified_files, function(path){
  return _.includes(path, 'lib/') &&  !_.includes(path, '__tests__/');
})

// Check that any new file has a corresponding tests file
const untestedFiles = _.difference(sourcePaths, logicalTestPaths)
if (untestedFiles.length > 0) {
  warn("The following files do not have tests: " + github.html_link(untestedFiles))
}
<|endoftext|>


<commit_before><commit_msg>Assign ragged ranges<commit_after><|endoftext|>


<commit_before>/* eslint-disable no-param-reassign */
/* eslint-disable class-methods-use-this */
const { BadRequest, Forbidden } = require('@feathersjs/errors');
const logger = require('../../../logger');

const { userModel } = require('../model');
const roleModel = require('../../role/model');

const getCurrentUserInfo = (id) => userModel.findById(id)
	.select('schoolId')
	.populate('roles')
	.lean()
	.exec();

const getRoles = () => roleModel.find()
	.select('name')
	.lean()
	.exec();

const getAllUsers = (ref, schoolId, role, sortObject) => ref.app.service('usersModel').find({
	query: {
		schoolId,
		roles: role.toString(),
		$limit: sortObject.$limit,
		$skip: sortObject.$skip,
		$sort: sortObject.$sort,
		$select: ['firstName', 'lastName', 'email', 'createdAt', 'importHash', 'birthday'],
	},
});

const getClasses = (app, schoolId, schoolYearId) => app.service('classes')
	.find({
		query: {
			schoolId,
			$or: [
				{ year: schoolYearId },
				{ year: { $exists: false } },
			],
			$limit: 1000,
		},
	})
	.then((classes) => classes.data)
	.catch((err) => {
		logger.warning(`Can not execute app.service("classes").find for ${schoolId}`, err);
		return err;
	});

const findConsents = (ref, userIds, $limit) => ref.app.service('/consents')
	.find({
		query: {
			userId: { $in: userIds },
			$limit,
			$select: [
				'userId',
				'userConsent.form',
				'userConsent.privacyConsent',
				'userConsent.termsOfUseConsent',
				'parentConsents.parentId',
				'parentConsents.form',
				'parentConsents.privacyConsent',
				'parentConsents.termsOfUseConsent'],
		},
	})
	.then((consents) => consents.data);

const getCurrentYear = (ref, schoolId) => ref.app.service('schools')
	.get(schoolId, {
		query: { $select: ['currentYear'] },
	})
	.then(({ currentYear }) => currentYear.toString());

class AdminUsers {
	constructor(role) {
		this.role = role || {};
		this.docs = {};
		this.rolesThatCanAccess = ['teacher', 'administrator', 'superhero'];
	}

	async find(params) {
		try {
			const { query, account } = params;
			const currentUserId = account.userId.toString();
			const {
				$limit,
				$skip,
				$sort,
				consentStatus,
			} = query;

			// fetch base data
			const [currentUser, roles] = await Promise.all([getCurrentUserInfo(currentUserId), getRoles()]);

			// permission check
			if (!currentUser.roles.some((role) => this.rolesThatCanAccess.includes(role.name))) {
				throw new Forbidden();
			}
			const { schoolId } = currentUser;
			const currentYear = await getCurrentYear(this, schoolId);

			// fetch data that are scoped to schoolId
			const searchedRole = roles.find((role) => role.name === this.role);
			const [usersData, classes] = await Promise.all([
				getAllUsers(this, schoolId, searchedRole._id, query),
				getClasses(this.app, schoolId, currentYear),
			]);
			const { total } = usersData;
			const users = usersData.data;
			const userIds = users.map((user) => user._id.toString());
			const consents = await findConsents(this, userIds, $limit).then((data) => {
				// rebuild consent to object for faster sorting
				const out = {};
				data.forEach((e) => {
					out[e.userId.toString()] = e;
				});
				return out;
			});
			// bsonId to stringId that it can use .includes for is in test
			classes.forEach((c) => {
				if (Array.isArray(c.userIds)) {
					c.userIds = c.userIds.map((id) => id.toString());
				} else {
					c.userIds = [];
				}
				if (Array.isArray(c.teacherIds)) {
					c.teacherIds = c.teacherIds.map((id) => id.toString());
				} else {
					c.teacherIds = [];
				}
			});

			// patch classes and consent into user
			users.forEach((user) => {
				user.classes = [];
				const userId = user._id.toString();
				user.consent = consents[userId] || {};
				classes.forEach((c) => {
					if (c.userIds.includes(userId) || c.teacherIds.includes(userId)) {
						user.classes.push(c.displayName);
					}
				});
				return user;
			});

			// sorting by class and by consent is implemented manually,
			// as classes and consents are fetched from seperate db collection
			const classSortParam = (($sort || {}).class || {}).toString();
			if (classSortParam === '1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() > (b.classes[0] || '').toLowerCase());
			} else if (classSortParam === '-1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() < (b.classes[0] || '').toLowerCase());
			}

			const sortOrder = {
				ok: 1,
				parentsAgreed: 2,
				missing: 3,
			};
			const consentSortParam = (($sort || {}).consent || {}).toString();
			if (consentSortParam === '1') {
				users.sort((a, b) => (sortOrder[a.consent.consentStatus || 'missing']
					- sortOrder[b.consent.consentStatus || 'missing']));
			} else if (consentSortParam === '-1') {
				users.sort((a, b) => (sortOrder[b.consent.consentStatus || 'missing']
					- sortOrder[a.consent.consentStatus || 'missing']));
			}

			const filteredUsers = users.filter((user) => {
				if ((consentStatus || {}).$in) {
					const userStatus = user.consent.consentStatus || 'missing';
					return consentStatus.$in.includes(userStatus);
				}
				return true;
			});

			return {
				total,
				limit: $limit,
				skip: $skip,
				data: filteredUsers,
			};
		} catch (err) {
			if ((err || {}).code === 403) {
				throw new Forbidden('You have not the permission to execute this.', err);
			}
			throw new BadRequest('Can not fetch data.', err);
		}
	}

	setup(app) {
		this.app = app;
	}
}

module.exports = AdminUsers;
<commit_msg>add collation to user administration<commit_after>/* eslint-disable no-param-reassign */
/* eslint-disable class-methods-use-this */
const { BadRequest, Forbidden } = require('@feathersjs/errors');
const logger = require('../../../logger');

const { userModel } = require('../model');
const roleModel = require('../../role/model');

const getCurrentUserInfo = (id) => userModel.findById(id)
	.select('schoolId')
	.populate('roles')
	.lean()
	.exec();

const getRoles = () => roleModel.find()
	.select('name')
	.lean()
	.exec();

const getAllUsers = (ref, schoolId, role, sortObject) => ref.app.service('usersModel').find({
	collation: { locale: 'de', caseLevel: true },
	query: {
		schoolId,
		roles: role.toString(),
		$limit: sortObject.$limit,
		$skip: sortObject.$skip,
		$sort: sortObject.$sort,
		$select: ['firstName', 'lastName', 'email', 'createdAt', 'importHash', 'birthday'],
	},
});

const getClasses = (app, schoolId, schoolYearId) => app.service('classes')
	.find({
		query: {
			schoolId,
			$or: [
				{ year: schoolYearId },
				{ year: { $exists: false } },
			],
			$limit: 1000,
		},
	})
	.then((classes) => classes.data)
	.catch((err) => {
		logger.warning(`Can not execute app.service("classes").find for ${schoolId}`, err);
		return err;
	});

const findConsents = (ref, userIds, $limit) => ref.app.service('/consents')
	.find({
		query: {
			userId: { $in: userIds },
			$limit,
			$select: [
				'userId',
				'userConsent.form',
				'userConsent.privacyConsent',
				'userConsent.termsOfUseConsent',
				'parentConsents.parentId',
				'parentConsents.form',
				'parentConsents.privacyConsent',
				'parentConsents.termsOfUseConsent'],
		},
	})
	.then((consents) => consents.data);

const getCurrentYear = (ref, schoolId) => ref.app.service('schools')
	.get(schoolId, {
		query: { $select: ['currentYear'] },
	})
	.then(({ currentYear }) => currentYear.toString());

class AdminUsers {
	constructor(role) {
		this.role = role || {};
		this.docs = {};
		this.rolesThatCanAccess = ['teacher', 'administrator', 'superhero'];
	}

	async find(params) {
		try {
			const { query, account } = params;
			const currentUserId = account.userId.toString();
			const {
				$limit,
				$skip,
				$sort,
				consentStatus,
			} = query;

			// fetch base data
			const [currentUser, roles] = await Promise.all([getCurrentUserInfo(currentUserId), getRoles()]);

			// permission check
			if (!currentUser.roles.some((role) => this.rolesThatCanAccess.includes(role.name))) {
				throw new Forbidden();
			}
			const { schoolId } = currentUser;
			const currentYear = await getCurrentYear(this, schoolId);

			// fetch data that are scoped to schoolId
			const searchedRole = roles.find((role) => role.name === this.role);
			const [usersData, classes] = await Promise.all([
				getAllUsers(this, schoolId, searchedRole._id, query),
				getClasses(this.app, schoolId, currentYear),
			]);
			const { total } = usersData;
			const users = usersData.data;
			const userIds = users.map((user) => user._id.toString());
			const consents = await findConsents(this, userIds, $limit).then((data) => {
				// rebuild consent to object for faster sorting
				const out = {};
				data.forEach((e) => {
					out[e.userId.toString()] = e;
				});
				return out;
			});
			// bsonId to stringId that it can use .includes for is in test
			classes.forEach((c) => {
				if (Array.isArray(c.userIds)) {
					c.userIds = c.userIds.map((id) => id.toString());
				} else {
					c.userIds = [];
				}
				if (Array.isArray(c.teacherIds)) {
					c.teacherIds = c.teacherIds.map((id) => id.toString());
				} else {
					c.teacherIds = [];
				}
			});

			// patch classes and consent into user
			users.forEach((user) => {
				user.classes = [];
				const userId = user._id.toString();
				user.consent = consents[userId] || {};
				classes.forEach((c) => {
					if (c.userIds.includes(userId) || c.teacherIds.includes(userId)) {
						user.classes.push(c.displayName);
					}
				});
				return user;
			});

			// sorting by class and by consent is implemented manually,
			// as classes and consents are fetched from seperate db collection
			const classSortParam = (($sort || {}).class || {}).toString();
			if (classSortParam === '1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() > (b.classes[0] || '').toLowerCase());
			} else if (classSortParam === '-1') {
				users.sort((a, b) => (a.classes[0] || '').toLowerCase() < (b.classes[0] || '').toLowerCase());
			}

			const sortOrder = {
				ok: 1,
				parentsAgreed: 2,
				missing: 3,
			};
			const consentSortParam = (($sort || {}).consent || {}).toString();
			if (consentSortParam === '1') {
				users.sort((a, b) => (sortOrder[a.consent.consentStatus || 'missing']
					- sortOrder[b.consent.consentStatus || 'missing']));
			} else if (consentSortParam === '-1') {
				users.sort((a, b) => (sortOrder[b.consent.consentStatus || 'missing']
					- sortOrder[a.consent.consentStatus || 'missing']));
			}

			const filteredUsers = users.filter((user) => {
				if ((consentStatus || {}).$in) {
					const userStatus = user.consent.consentStatus || 'missing';
					return consentStatus.$in.includes(userStatus);
				}
				return true;
			});

			return {
				total,
				limit: $limit,
				skip: $skip,
				data: filteredUsers,
			};
		} catch (err) {
			if ((err || {}).code === 403) {
				throw new Forbidden('You have not the permission to execute this.', err);
			}
			throw new BadRequest('Can not fetch data.', err);
		}
	}

	setup(app) {
		this.app = app;
	}
}

module.exports = AdminUsers;
<|endoftext|>


<commit_before><commit_msg>The core codes of twve<commit_after><|endoftext|>


<commit_before>function throwHandler(effect, execution) {
  let err = effect.err

  if (!err.event) {
    Object.defineProperty(err, 'event', {
      value: execution.context,
      enumerable: false,
    })
  }

  execution.status = 'completed'
  throw err
}

function tootHandler(effect, execution) {
  let { event, args, cb } = effect
  let hooter = execution.state.hooter
  let value = hooter._toot(event, args, cb)
  return { effect: 'resolve', value }
}

function toot(event, ...args) {
  return { effect: 'toot', event, args }
}

function tootWith(event, cb, ...args) {
  return { effect: 'toot', event, args, cb }
}

function hookHandler(effect, execution) {
  let { args, mode } = effect
  let hooter = execution.state.hooter

  if (mode === 'start') {
    return hooter.hookStart(...args)
  } else if (mode === 'end') {
    return hooter.hookEnd(...args)
  } else {
    return hooter.hook(...args)
  }
}

function hook(...args) {
  return { effect: 'hook', args }
}

function hookStart(...args) {
  return { effect: 'hook', mode: 'start', args }
}

function hookEnd(...args) {
  return { effect: 'hook', mode: 'end', args }
}

module.exports = {
  throwHandler,
  tootHandler,
  toot,
  tootWith,
  hookHandler,
  hook,
  hookStart,
  hookEnd,
}
<commit_msg>Fix the throw effect handler<commit_after>function throwHandler(effect, execution) {
  let err = effect.err

  if (err instanceof Error && !err.event) {
    Object.defineProperty(err, 'event', {
      value: execution.context,
      enumerable: false,
    })
  }

  execution.status = 'completed'
  throw err
}

function tootHandler(effect, execution) {
  let { event, args, cb } = effect
  let hooter = execution.state.hooter
  let value = hooter._toot(event, args, cb)
  return { effect: 'resolve', value }
}

function toot(event, ...args) {
  return { effect: 'toot', event, args }
}

function tootWith(event, cb, ...args) {
  return { effect: 'toot', event, args, cb }
}

function hookHandler(effect, execution) {
  let { args, mode } = effect
  let hooter = execution.state.hooter

  if (mode === 'start') {
    return hooter.hookStart(...args)
  } else if (mode === 'end') {
    return hooter.hookEnd(...args)
  } else {
    return hooter.hook(...args)
  }
}

function hook(...args) {
  return { effect: 'hook', args }
}

function hookStart(...args) {
  return { effect: 'hook', mode: 'start', args }
}

function hookEnd(...args) {
  return { effect: 'hook', mode: 'end', args }
}

module.exports = {
  throwHandler,
  tootHandler,
  toot,
  tootWith,
  hookHandler,
  hook,
  hookStart,
  hookEnd,
}
<|endoftext|>


<commit_before>import React, {Component} from "react";
import Dropdown from "react-dropdown";
import moment from "moment";
import range from "lodash/range";

class DateSelect extends Component {
    constructor(params) {
        super(params);
        const dateObject = this.props.initialValue && this.props.format ? moment(this.props.initialValue,this.props.format) : null;
        this.state = {
            selectedYear: dateObject ? dateObject.year() : null,
            selectedMonth: dateObject ? dateObject.month() : null,
            selectedDayOfMonth: dateObject ? dateObject.date() : null,
            date: "",
            daysOfMonth: [],
            months: [],
            years: []
        };
        this.onMonthChange = this.onMonthChange.bind(this);
        this.onYearChange = this.onYearChange.bind(this);
        this.onDayOfMonthChange = this.onDayOfMonthChange.bind(this);
        this.onChange = this.onChange.bind(this);
    }

    onChange() {
        // this.props.onChange(date);
        console.log("on valid change invoked");
    }

    findNumberOfDaysInMonth() {
        const _self = this;
        const MM = this.state.selectedMonth;
        const YYYY = this.state.selectedYear;
        if (MM && YYYY) {
            const numberOfDaysInMonth = moment(`${MM}-${YYYY}`, 'MM-YYYY').daysInMonth();
            _self.setState({daysOfMonth: range(1, numberOfDaysInMonth + 1)}, () => {
                if (_self.state.selectedDayOfMonth && _self.state.selectedDayOfMonth > numberOfDaysInMonth) {
                    this.setState({selectedDayOfMonth: 1}, () => {
                        _self.onChange();
                    })
                } else if (_self.state.selectedDayOfMonth && _self.state.selectedDayOfMonth <= numberOfDaysInMonth) {
                    _self.onChange();
                }
            })
        }
    }

    onYearChange(year) {
        this.setState({selectedYear: year.value}, this.findNumberOfDaysInMonth);
    }

    onMonthChange(month) {
        this.setState({selectedMonth: month.value}, this.findNumberOfDaysInMonth);
    }

    onDayOfMonthChange(day) {
        this.setState({selectedDayOfMonth: day.value}, this.onChange);
    }

    componentWillMount() {
        let startYear = this.props.startYear || moment().subtract(100, 'years').year();
        let endYear = this.props.endYear || moment().add(20, "years").year();
        let years = range(startYear, endYear);
        let dateObject = null;
        let months = range(1, 13).map((thisMonth) => {
            return {
                value: thisMonth,
                label: moment(`${thisMonth}`, "MM").format("MMMM").toUpperCase()
            }
        });
        // let endYear = moment().subtract('18',years);
        if (!this.props.initialValue) {
            // dateObject = moment();
        } else {
            dateObject = moment(this.props.initialValue)
        }

        this.setState({
            months: months,
            years: years,
            selectedYear: dateObject ? dateObject.year() : "",
            selectedMonth: dateObject ? dateObject.month() : "",
            selectedDayOfMonth: dateObject ? dateObject.day() : ""
        })

    }

    render() {
        return (

                {
                    this.state.selectedMonth ?
                        <Dropdown className={"select-month"} options={this.state.months}
                                  value={ {
                                      value: this.state.selectedMonth,
                                      label: moment(`${this.state.selectedMonth}`, "MM").format("MMMM").toUpperCase()
                                  }}
                                  placeholder={"MM"}
                                  onChange={this.onMonthChange}/>
                        : <Dropdown className={"select-month"} options={this.state.months}
                                    placeholder={"MM"}
                                    onChange={this.onMonthChange}/>
                }
                {   this.state.selectedDayOfMonth ?
                    <Dropdown className={"select-date"} options={this.state.daysOfMonth} placeholder={"DD"}
                              value={ {value: this.state.selectedDayOfMonth, label: this.state.selectedDayOfMonth}}
                              disabled={!(this.state.selectedMonth && this.state.selectedYear)}
                              onChange={this.onDayOfMonthChange}/>
                    : <Dropdown className={"select-date"} options={this.state.daysOfMonth} placeholder={"DD"}
                                disabled={!(this.state.selectedMonth && this.state.selectedYear)}
                                onChange={this.onDayOfMonthChange}/>
                }
                {
                    this.state.selectedYear ?
                        <Dropdown className={"select-year"} options={this.state.years}
                                  value={ {value: this.state.selectedYear, label: this.state.selectedYear}}
                                  placeholder={"YYYY"}
                                  onChange={this.onYearChange}/>
                        : <Dropdown className={"select-year"} options={this.state.years}
                                    placeholder={"YYYY"}
                                    onChange={this.onYearChange}/>
                }

        )
    }

}

export default DateSelect;<commit_msg>custom util completed<commit_after>import React, {Component} from "react";
import Dropdown from "react-dropdown";
import moment from "moment";
import range from "lodash/range";

class DateSelect extends Component {
    constructor(params) {
        super(params);
        const dateObject = this.props.initialValue ? moment(moment(this.props.initialValue).format(this.props.format || "YYYY-MM-DD")) : null;
        this.state = {
            selectedYear: dateObject ? dateObject.year() : null,
            selectedMonth: dateObject ? dateObject.month() + 1 : null,
            selectedDayOfMonth: dateObject ? dateObject.date() : null,
            daysOfMonth: [],
            months: [],
            years: []
        };

        this.onMonthChange = this.onMonthChange.bind(this);
        this.onYearChange = this.onYearChange.bind(this);
        this.onDayOfMonthChange = this.onDayOfMonthChange.bind(this);
        this.onChange = this.onChange.bind(this);
        this.findNumberOfDaysInMonth = this.findNumberOfDaysInMonth.bind(this);

        if (this.state.selectedYear && this.state.selectedMonth) {
            this.findNumberOfDaysInMonth(true);
        }
    }

    onChange() {
        const year = this.state.selectedYear;
        const month =  this.state.selectedMonth;
        const dayOfMonth =  this.state.selectedDayOfMonth;
        const formattedValue = moment(`${year}-${month}-${dayOfMonth}`).format(this.props.format || "YYYY-MM-DD");
        const value = moment(formattedValue);
        this.props.onChange(value, formattedValue);
    }

    findNumberOfDaysInMonth(noCallbackNeeded) {
        const _self = this;
        const MM = this.state.selectedMonth;
        const YYYY = this.state.selectedYear;
        if (MM && YYYY) {
            const numberOfDaysInMonth = moment(`${MM}-${YYYY}`, 'MM-YYYY').daysInMonth();
            _self.setState({daysOfMonth: range(1, numberOfDaysInMonth + 1)}, () => {
                if (_self.state.selectedDayOfMonth && _self.state.selectedDayOfMonth > numberOfDaysInMonth) {
                    this.setState({selectedDayOfMonth: 1}, () => {
                        !noCallbackNeeded && _self.onChange();
                    })
                } else if (_self.state.selectedDayOfMonth && _self.state.selectedDayOfMonth <= numberOfDaysInMonth) {
                    !noCallbackNeeded && _self.onChange();
                }
            })
        }
    }

    onYearChange(year) {
        this.setState({selectedYear: year.value}, this.findNumberOfDaysInMonth);
    }

    onMonthChange(month) {
        this.setState({selectedMonth: month.value}, this.findNumberOfDaysInMonth);
    }

    onDayOfMonthChange(day) {
        this.setState({selectedDayOfMonth: day.value}, this.onChange);
    }

    componentWillMount() {
        let startYear = this.props.startYear || moment().subtract(100, 'years').year();
        let endYear = this.props.endYear || moment().add(20, "years").year();
        let years = range(startYear, endYear);
        let months = range(1, 13).map((thisMonth) => {
            return {
                value: thisMonth,
                label: moment(`${thisMonth}`, "MM").format("MMMM").toUpperCase()
            }
        });

        this.setState({
            months: months,
            years: years
        })

    }

    render() {
        return (

                {
                    this.state.selectedMonth ?
                        <Dropdown className={"select-month"} options={this.state.months}
                                  value={ {
                                      value: this.state.selectedMonth,
                                      label: moment(`${this.state.selectedMonth}`, "MM").format("MMMM").toUpperCase()
                                  }}
                                  placeholder={"MM"}
                                  onChange={this.onMonthChange}/>
                        : <Dropdown className={"select-month"} options={this.state.months}
                                    placeholder={"MM"}
                                    onChange={this.onMonthChange}/>
                }
                {   this.state.selectedDayOfMonth ?
                    <Dropdown className={"select-date"} options={this.state.daysOfMonth} placeholder={"DD"}
                              value={ {value: this.state.selectedDayOfMonth, label: this.state.selectedDayOfMonth}}
                              disabled={!(this.state.selectedMonth && this.state.selectedYear)}
                              onChange={this.onDayOfMonthChange}/>
                    : <Dropdown className={"select-date"} options={this.state.daysOfMonth} placeholder={"DD"}
                                disabled={!(this.state.selectedMonth && this.state.selectedYear)}
                                onChange={this.onDayOfMonthChange}/>
                }
                {
                    this.state.selectedYear ?
                        <Dropdown className={"select-year"} options={this.state.years}
                                  value={ {value: this.state.selectedYear, label: this.state.selectedYear}}
                                  placeholder={"YYYY"}
                                  onChange={this.onYearChange}/>
                        : <Dropdown className={"select-year"} options={this.state.years}
                                    placeholder={"YYYY"}
                                    onChange={this.onYearChange}/>
                }

                    Please select Year and Month First

        )
    }

}

export default DateSelect;<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

'use strict';

/**
 * NodePath class encapsulates a traversing node,
 * its parent node, property name in the parent node, and
 * an index (in case if a node is part of a collection).
 * It also provides set of methods for AST manipulation.
 */
class NodePath {
  /**
   * NodePath constructor.
   *
   * @param Object node - an AST node
   * @param NodePath parentPath - a nullable parent path
   * @param string property - property name of the node in the parent
   * @param number index - index of the node in a collection.
   */
  constructor(
    node,
    parentPath = null,
    property = null,
    index = null
  ) {
    this.node = node;
    this.parentPath = parentPath;
    this.parent = parentPath ? parentPath.node : null;
    this.property = property;
    this.index = index;
  }

  /**
   * sets a node into a children collection or the single child.
   * By default child nodes are supposed to be under `expressions` property.
   * An explicit property can be passed.
   *
   * @param Object node - a node to set into a collection or as single child
   * @param number index - index at which to set
   * @param string property - name of the collection or single property
   */
  setChild(node, index = null, property = null) {

    if (index != null) {
      if (!property) property = "expressions";
      if (!this.node.hasOwnProperty(property)) {
        throw new Error(
          `Node of type ${this.node.type} doesn't have "${property}" collection.`
        );
      }
      this.node[property][index] = node;
      return NodePath.getForNode(node, this, property, index);
    } else {
      if (!property) property = "expression";
      if (!this.node.hasOwnProperty(property)) {
        throw new Error(
          `Node of type ${this.node.type} doesn't have "${property}" collection.`
        );
      }
      this.node[property] = node;
      return NodePath.getForNode(node, this, property);
    }
  }

  /**
   * sets a node into a children collection or the single child.
   * By default child nodes are supposed to be under `expressions` property.
   * An explicit property can be passed.
   *
   * @param Object node - a node to set into a collection or as single child
   * @param number index - index at which to set
   * @param string property - name of the collection or single property
   */
  appendChild(node, property = null) {

    if (!property) property = "expressions";
    if (!this.node.hasOwnProperty(property)) {
      throw new Error(
        `Node of type ${this.node.type} doesn't have "${property}" collection.`
      );
    }
    const end = this.node[property].length;
    return this.setChild(node, end, property);
  }

  /**
   * Inserts a node into a collection.
   * By default child nodes are supposed to be under `expressions` property.
   * An explicit property can be passed.
   *
   * @param Object node - a node to insert into a collection
   * @param number index - index at which to insert
   * @param string property - name of the collection property
   */
  insertChildAt(node, index, property = 'expressions') {
    if (!this.node.hasOwnProperty(property)) {
      throw new Error(
        `Node of type ${this.node.type} doesn't have "${property}" collection.`
      );
    }

    this.node[property].splice(index, 0, node);

    // If we inserted a node before the traversing index,
    // we should increase the later.
    if (index <= NodePath.getTraversingIndex()) {
      NodePath.updateTraversingIndex(+1);
    }

    this._rebuildIndex(this.node, property);
  }

  /**
   * Removes a node.
   */
  remove() {
    if (this.isRemoved()) {
      return;
    }

    this.node = null;

    if (!this.parent) {
      return;
    }

    // A node is in a collection.
    if (this.index !== null) {
      this.parent[this.property].splice(this.index, 1);

      // If we remove a node before the traversing index,
      // we should increase the later.
      if (this.index <= NodePath.getTraversingIndex()) {
        NodePath.updateTraversingIndex(-1);
      }

      // Rebuild index.
      this._rebuildIndex(this.parent, this.property);

      this.index = null;
      this.property = null;

      return;
    }

    // A simple node.
    delete this.parent[this.property];
    this.property = null;
  }

  /**
   * Rebuilds child nodes index (used on remove/insert).
   */
  _rebuildIndex(parent, property) {
    const parentPath = NodePath.getForNode(parent);

    for (let i = 0; i < parent[property].length; i++) {
      const path = NodePath.getForNode(
        parent[property][i],
        parentPath,
        property,
        i
      );
      path.index = i;
    }
  }

  /**
   * Whether the path was removed.
   */
  isRemoved() {
    return this.node === null;
  }

  /**
   * Replaces a node with the passed one.
   */
  replace(newNode) {
    this.node = newNode;

    if (!this.parent) {
      return;
    }

    // A node is in a collection.
    if (this.index !== null) {
      this.parent[this.property][this.index] = newNode;
      return;
    }

    // A simple node.
    this.parent[this.property] = newNode;
  }

  /**
   * Updates a node inline.
   */
  update(nodeProps) {
    Object.assign(this.node, nodeProps);
  }

  /**
   * Returns parent.
   */
  getParent() {
    return this.parentPath;
  }

  /**
   * Returns nth child.
   */
  getChild(n = 0) {
    if (this.node.expressions) {
      return NodePath.getForNode(
        this.node.expressions[n],
        this,
        "expressions",
        n
      );
    } else if (this.node.expression && n == 0) {
      return NodePath.getForNode(this.node.expression, this, "expression");
    }
    return null;
  }

  /**
   * Whether a path node is syntactically equal to the passed one.
   *
   * NOTE: we don't rely on `source` property from the `loc` data
   * (which would be the fastest comparison), since it might be unsync
   * after several modifications. We use here simple `JSON.stringify`
   * excluding the `loc` data.
   *
   * @param NodePath other - path to compare to.
   * @return boolean
   */
  hasEqualSource(path) {
    const skipLoc = (prop, value) => {
      if (prop === 'loc') {
        return undefined;
      }
      return value;
    };

    return (
      JSON.stringify(this.node, skipLoc) ===
      JSON.stringify(path.node, skipLoc)
    );
  }

  /**
   * Returns previous sibling.
   */
  getPreviousSibling() {
    if (!this.parent || this.index == null) {
      return null;
    }
    return NodePath.getForNode(
      this.parent[this.property][this.index - 1],
      NodePath.getForNode(this.parent),
      this.property,
      this.index - 1
    );
  }

  /**
   * Returns next sibling.
   */
  getNextSibling() {
    if (!this.parent || this.index == null) {
      return null;
    }
    return NodePath.getForNode(
      this.parent[this.property][this.index + 1],
      NodePath.getForNode(this.parent),
      this.property,
      this.index + 1
    );
  }

  /**
   * Returns a NodePath instance for a node.
   *
   * The same NodePath can be reused in several places, e.g.
   * a parent node passed for all its children.
   */
  static getForNode(node, parentPath = null, prop = null, index = null) {
    if (!node) {
      return null;
    }

    if (!NodePath.registry.has(node)) {
      NodePath.registry.set(
        node,
        new NodePath(node, parentPath, prop, index)
      );
    }

    let path = NodePath.registry.get(node);
    if (parentPath != null) path.parentPath = parentPath;
    if (prop       != null) path.property   = prop;
    if (index      != null) path.index      = index;
    return path;
  }

  /**
   * Initializes the NodePath registry. The registry is a map from
   * a node to its NodePath instance.
   */
  static initRegistry() {
    if (!NodePath.registry) {
      NodePath.registry = new Map();
    }
    NodePath.registry.clear();
  }

  /**
   * Updates index of a currently traversing collection.
   */
  static updateTraversingIndex(dx) {
    return (
      NodePath.traversingIndexStack[
        NodePath.traversingIndexStack.length - 1
      ] += dx
    );
  }

  /**
   * Returns current traversing index.
   */
  static getTraversingIndex() {
    return NodePath.traversingIndexStack[
      NodePath.traversingIndexStack.length - 1
    ];
  }
}

NodePath.initRegistry();

/**
 * Index of a currently traversing collection is stored on top of the
 * `NodePath.traversingIndexStack`. Remove/insert methods can adjust
 * this index.
 */
NodePath.traversingIndexStack = [];

module.exports = NodePath;
<commit_msg>requested changes, style, etc.<commit_after>/**
 * The MIT License (MIT)
 * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
 */

'use strict';

const DEFAULT_COLLECTION_PROP = 'expressions';
const DEFAULT_SINGLE_PROP     = 'expression';

/**
 * NodePath class encapsulates a traversing node,
 * its parent node, property name in the parent node, and
 * an index (in case if a node is part of a collection).
 * It also provides set of methods for AST manipulation.
 */
class NodePath {
  /**
   * NodePath constructor.
   *
   * @param Object node - an AST node
   * @param NodePath parentPath - a nullable parent path
   * @param string property - property name of the node in the parent
   * @param number index - index of the node in a collection.
   */
  constructor(
    node,
    parentPath = null,
    property = null,
    index = null
  ) {
    this.node = node;
    this.parentPath = parentPath;
    this.parent = parentPath ? parentPath.node : null;
    this.property = property;
    this.index = index;
  }

  _enforceProp(property) {
    if (!this.node.hasOwnProperty(property)) {
      throw new Error(
        `Node of type ${this.node.type} doesn't have "${property}" collection.`
      );
    }
  }

  /**
   * Sets a node into a children collection or the single child.
   * By default child nodes are supposed to be under `expressions` property.
   * An explicit property can be passed.
   *
   * @param Object node - a node to set into a collection or as single child
   * @param number index - index at which to set
   * @param string property - name of the collection or single property
   */
  setChild(node, index = null, property = null) {

    if (index != null) {
      if (!property) {
        property = DEFAULT_COLLECTION_PROP;
      }
      this._enforceProp(property);
      this.node[property][index] = node;
      return NodePath.getForNode(node, this, property, index);
    } else {
      if (!property) {
        property = DEFAULT_SINGLE_PROP;
      }
      this._enforceProp(property);
      this.node[property] = node;
      return NodePath.getForNode(node, this, property);
    }
  }

  /**
   * Appends a node to a children collection.
   * By default child nodes are supposed to be under `expressions` property.
   * An explicit property can be passed.
   *
   * @param Object node - a node to set into a collection or as single child
   * @param string property - name of the collection or single property
   */
  appendChild(node, property = null) {

    if (!property) {
      property = DEFAULT_COLLECTION_PROP;
    }
    this._enforceProp(property);
    const end = this.node[property].length;
    return this.setChild(node, end, property);
  }

  /**
   * Inserts a node into a collection.
   * By default child nodes are supposed to be under `expressions` property.
   * An explicit property can be passed.
   *
   * @param Object node - a node to insert into a collection
   * @param number index - index at which to insert
   * @param string property - name of the collection property
   */
  insertChildAt(node, index, property = DEFAULT_COLLECTION_PROP) {
    this._enforceProp(property);

    this.node[property].splice(index, 0, node);

    // If we inserted a node before the traversing index,
    // we should increase the later.
    if (index <= NodePath.getTraversingIndex()) {
      NodePath.updateTraversingIndex(+1);
    }

    this._rebuildIndex(this.node, property);
  }

  /**
   * Removes a node.
   */
  remove() {
    if (this.isRemoved()) {
      return;
    }

    this.node = null;

    if (!this.parent) {
      return;
    }

    // A node is in a collection.
    if (this.index !== null) {
      this.parent[this.property].splice(this.index, 1);

      // If we remove a node before the traversing index,
      // we should increase the later.
      if (this.index <= NodePath.getTraversingIndex()) {
        NodePath.updateTraversingIndex(-1);
      }

      // Rebuild index.
      this._rebuildIndex(this.parent, this.property);

      this.index = null;
      this.property = null;

      return;
    }

    // A simple node.
    delete this.parent[this.property];
    this.property = null;
  }

  /**
   * Rebuilds child nodes index (used on remove/insert).
   */
  _rebuildIndex(parent, property) {
    const parentPath = NodePath.getForNode(parent);

    for (let i = 0; i < parent[property].length; i++) {
      const path = NodePath.getForNode(
        parent[property][i],
        parentPath,
        property,
        i
      );
      path.index = i;
    }
  }

  /**
   * Whether the path was removed.
   */
  isRemoved() {
    return this.node === null;
  }

  /**
   * Replaces a node with the passed one.
   */
  replace(newNode) {
    this.node = newNode;

    if (!this.parent) {
      return;
    }

    // A node is in a collection.
    if (this.index !== null) {
      this.parent[this.property][this.index] = newNode;
      return;
    }

    // A simple node.
    this.parent[this.property] = newNode;
  }

  /**
   * Updates a node inline.
   */
  update(nodeProps) {
    Object.assign(this.node, nodeProps);
  }

  /**
   * Returns parent.
   */
  getParent() {
    return this.parentPath;
  }

  /**
   * Returns nth child.
   */
  getChild(n = 0) {
    if (this.node.expressions) {
      return NodePath.getForNode(
        this.node.expressions[n],
        this,
        DEFAULT_COLLECTION_PROP,
        n
      );
    } else if (this.node.expression && n == 0) {
      return NodePath.getForNode(this.node.expression, this, DEFAULT_SINGLE_PROP);
    }
    return null;
  }

  /**
   * Whether a path node is syntactically equal to the passed one.
   *
   * NOTE: we don't rely on `source` property from the `loc` data
   * (which would be the fastest comparison), since it might be unsync
   * after several modifications. We use here simple `JSON.stringify`
   * excluding the `loc` data.
   *
   * @param NodePath other - path to compare to.
   * @return boolean
   */
  hasEqualSource(path) {
    const skipLoc = (prop, value) => {
      if (prop === 'loc') {
        return undefined;
      }
      return value;
    };

    return (
      JSON.stringify(this.node, skipLoc) ===
      JSON.stringify(path.node, skipLoc)
    );
  }

  /**
   * Returns previous sibling.
   */
  getPreviousSibling() {
    if (!this.parent || this.index == null) {
      return null;
    }
    return NodePath.getForNode(
      this.parent[this.property][this.index - 1],
      NodePath.getForNode(this.parent),
      this.property,
      this.index - 1
    );
  }

  /**
   * Returns next sibling.
   */
  getNextSibling() {
    if (!this.parent || this.index == null) {
      return null;
    }
    return NodePath.getForNode(
      this.parent[this.property][this.index + 1],
      NodePath.getForNode(this.parent),
      this.property,
      this.index + 1
    );
  }

  /**
   * Returns a NodePath instance for a node.
   *
   * The same NodePath can be reused in several places, e.g.
   * a parent node passed for all its children.
   */
  static getForNode(node, parentPath = null, prop = null, index = null) {
    if (!node) {
      return null;
    }

    if (!NodePath.registry.has(node)) {
      NodePath.registry.set(
        node,
        new NodePath(node, parentPath, prop, index)
      );
    }

    let path = NodePath.registry.get(node);

    if (parentPath !== null) {
      path.parentPath = parentPath;
    }
    if (prop !== null) {
      path.property = prop;
    }
    if (index !== null) {
      path.index = index;
    }
    return path;
  }

  /**
   * Initializes the NodePath registry. The registry is a map from
   * a node to its NodePath instance.
   */
  static initRegistry() {
    if (!NodePath.registry) {
      NodePath.registry = new Map();
    }
    NodePath.registry.clear();
  }

  /**
   * Updates index of a currently traversing collection.
   */
  static updateTraversingIndex(dx) {
    return (
      NodePath.traversingIndexStack[
        NodePath.traversingIndexStack.length - 1
      ] += dx
    );
  }

  /**
   * Returns current traversing index.
   */
  static getTraversingIndex() {
    return NodePath.traversingIndexStack[
      NodePath.traversingIndexStack.length - 1
    ];
  }
}

NodePath.initRegistry();

/**
 * Index of a currently traversing collection is stored on top of the
 * `NodePath.traversingIndexStack`. Remove/insert methods can adjust
 * this index.
 */
NodePath.traversingIndexStack = [];

module.exports = NodePath;
<|endoftext|>


<commit_before>// See: https://gist.github.com/xiaodaigh/6445698
var swichTab = function(tabId, i) {
    tabs = $(tabId).find('li');
    tabs.each(function() {
        $(this).removeClass('active')
    });
    $(tabs[i]).addClass('active');
    tabsContents = $(tabId).next('.tab-content').children('.tab-pane');
    tabsContents.each(function() {
        $(this).removeClass('active')
    });
    $(tabsContents[i]).addClass('active');
}

function initTour() {

    var steps = [{
            content: '
Welcome...
',
            target: [50, 120],
            nextButton: true,
            my: 'bottom left'
    }, {
        content: [
            '
Choose expression dataset to upload.
',
            '
It should contain a sinlge column with gene names ',
            'followed by the  expression profiles.
',
            '
If you want to test this app you can use ',
            '',
            'expression_example.csv.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_container'),
        my: 'top left',
        at: 'bottom center',
    }, {
        content: [
            '
Select input file separator
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#sep'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Check if file has been loaded as expected.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preview'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Choose control samples
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#control_samples'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
If you prefer you can use disease signature dataset ',
            'obtained from the Gene Expression Omnibus
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_type'),
        my: 'top left',
        at: 'bottom right',
        teardown: function(tour, options) {
            $('input:radio[name=datain_type]').filter('[value=disease]').trigger('click');
        }
    }, {
        content: [
            '
Add preprocessing steps
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preprocessing'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Switch to the Characteristic Direction Analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'top center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab('#workflow_panel', 1);
        }
    }, {
        content: [
            '
Check input data summary
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_input_summary'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Set parameters...
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_parameters'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
...and run chdir
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_chdir_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When chdir analysis is finished ',
            'you can download the results...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#chdir_downloads'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
...send results to the Enrichr...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#enrichr_form'),
        my: 'top center',
        at: 'bottom center'
    }, {
        content: [
            '
or start PAE analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'bottom center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab('#workflow_panel', 2);
        }
    }, {
        content: [
            '
To run PAEA click Run Principle Angle Enrichment
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_paea_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When PAEA is ready results will be shown below
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#pae_results'),
        my: 'top left',
        at: 'top left'
    },  {
        content: [
            '
... and will be available for download
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#paea_downloads'),
        my: 'bottom center',
        at: 'top center'
    }];

    var tour = new Tourist.Tour({
        steps: steps,
        tipClass: 'Bootstrap',
        tipOptions:{ showEffect: 'slidein' }
    });
    tour.start();
}

$(document).ready(function() {
    $('#start_tour').click(function(event) {
        event.preventDefault();
        initTour();
    });
})<commit_msg>Switch to upload before executing upload step<commit_after>// See: https://gist.github.com/xiaodaigh/6445698
var swichTab = function(tabId, i) {
    tabs = $(tabId).find('li');
    tabs.each(function() {
        $(this).removeClass('active')
    });
    $(tabs[i]).addClass('active');
    tabsContents = $(tabId).next('.tab-content').children('.tab-pane');
    tabsContents.each(function() {
        $(this).removeClass('active')
    });
    $(tabsContents[i]).addClass('active');
}

function initTour() {

    var steps = [{
            content: '
Welcome...
',
            target: [50, 120],
            nextButton: true,
            my: 'bottom left',
            teardown: function(tour, options) {
                $('input:radio[name=datain_type]').filter('[value=upload]').trigger('click');
            }
    }, {
        content: [
            '
Choose expression dataset to upload.
',
            '
It should contain a sinlge column with gene names ',
            'followed by the  expression profiles.
',
            '
If you want to test this app you can use ',
            '',
            'expression_example.csv.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_container'),
        my: 'top left',
        at: 'bottom center',
    }, {
        content: [
            '
Select input file separator
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#sep'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Check if file has been loaded as expected.
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preview'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Choose control samples
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#control_samples'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
If you prefer you can use disease signature dataset ',
            'obtained from the Gene Expression Omnibus
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_type'),
        my: 'top left',
        at: 'bottom right',
        teardown: function(tour, options) {
            $('input:radio[name=datain_type]').filter('[value=disease]').trigger('click');
        }
    }, {
        content: [
            '
Add preprocessing steps
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#datain_preprocessing'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Switch to the Characteristic Direction Analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'top center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab('#workflow_panel', 1);
        }
    }, {
        content: [
            '
Check input data summary
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_input_summary'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
Set parameters...
'
        ].join(''),
        nextButton: true,
        closeButton: true,
        target: $('#chdir_parameters'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
...and run chdir
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_chdir_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When chdir analysis is finished ',
            'you can download the results...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#chdir_downloads'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
...send results to the Enrichr...
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#enrichr_form'),
        my: 'top center',
        at: 'bottom center'
    }, {
        content: [
            '
or start PAE analysis
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#workflow_panel'),
        my: 'bottom center',
        at: 'bottom center',
        teardown: function(tour, options) {
            swichTab('#workflow_panel', 2);
        }
    }, {
        content: [
            '
To run PAEA click Run Principle Angle Enrichment
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#run_paea_container'),
        my: 'bottom center',
        at: 'top center'
    }, {
        content: [
            '
When PAEA is ready results will be shown below
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#pae_results'),
        my: 'top left',
        at: 'top left'
    },  {
        content: [
            '
... and will be available for download
'
        ].join(''),
        highlightTarget: true,
        nextButton: true,
        closeButton: true,
        target: $('#paea_downloads'),
        my: 'bottom center',
        at: 'top center'
    }];

    var tour = new Tourist.Tour({
        steps: steps,
        tipClass: 'Bootstrap',
        tipOptions:{ showEffect: 'slidein' }
    });
    tour.start();
}

$(document).ready(function() {
    $('#start_tour').click(function(event) {
        event.preventDefault();
        initTour();
    });
})<|endoftext|>