<commit_before>  var React = require('react-native')
var MapView = require('react-native-maps')

var {
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
  ActivityIndicatorIOS,
  TextInput,
} = React;

var styles = StyleSheet.create({
  mainContainer: {
    flex: 1,
    padding: 30,
    marginTop: 65,
    flexDirection: 'column',
    justifyContent: 'center',
    backgroundColor: '#48BBEC'
  },
  title: {
    marginBottom: 20,
    fontSize: 25,
    textAlign: 'center',
    color: '#fff'
  },
  searchInput: {
    height: 50,
    padding: 4,
    marginRight: 5,
    fontSize: 23,
    borderWidth: 1,
    borderColor: 'white',
    borderRadius: 8,
    color: 'white'
  },
  buttonText: {
    fontSize: 18,
    color: '#111',
    alignSelf: 'center'
  },
  button: {
    height: 45,
    flexDirection: 'row',
    backgroundColor: 'white',
    borderColor: 'white',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    marginTop: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  },
  map: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0
  }
});

class MapPage extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      crumbs: this.props.trip,
      isLoading: false,
      error: false
    }
  }

  renderRow(rowData){
    return (
      <MapView.Marker
        coordinate={marker.latlng}
        title={marker.title}
        description={marker.description}
      />
    )
  };

  render(){
    var pins = this.props.trip.map((marker,index) => {
      return (
        <MapView.Marker
          coordinate={{
            latitude: marker.pos.coords.latitude,
            longitude: marker.pos.coords.longitude
          }}
          title={marker.title}
          description={marker.note}
          key={index}
        />
      )
    });

    return (
      <View style={styles.mainContainer}>
       <MapView
        debugger
        style={styles.map}
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0,
          longitudeDelta: 0
        }}>
        {pins}
        </MapView>
      </View>
    )
  }
};

MapPage.propTypes = {
  trip: React.PropTypes.array.isRequired
};

module.exports = MapPage;<commit_msg>Add working crumbs on map<commit_after>  var React = require('react-native')
var MapView = require('react-native-maps')

var {
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
  ActivityIndicatorIOS,
  TextInput,
} = React;

var styles = StyleSheet.create({
  mainContainer: {
    flex: 1,
    padding: 30,
    marginTop: 65,
    flexDirection: 'column',
    justifyContent: 'center',
    backgroundColor: '#48BBEC'
  },
  title: {
    marginBottom: 20,
    fontSize: 25,
    textAlign: 'center',
    color: '#fff'
  },
  searchInput: {
    height: 50,
    padding: 4,
    marginRight: 5,
    fontSize: 23,
    borderWidth: 1,
    borderColor: 'white',
    borderRadius: 8,
    color: 'white'
  },
  buttonText: {
    fontSize: 18,
    color: '#111',
    alignSelf: 'center'
  },
  button: {
    height: 45,
    flexDirection: 'row',
    backgroundColor: 'white',
    borderColor: 'white',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    marginTop: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  },
  map: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0
  }
});

class MapPage extends React.Component{

  constructor(props){
    super(props);
    this.state = {
      crumbs: this.props.trip,
      isLoading: false,
      error: false
    }
  }

  renderRow(rowData){
    return (
      <MapView.Marker
        coordinate={marker.latlng}
        title={marker.title}
        description={marker.description}
      />
    )
  };

  render(){
    var pins = this.props.trip.map((marker,index) => {
      return (
        <MapView.Marker
          coordinate={{
            latitude: marker.pos.coords.latitude,
            longitude: marker.pos.coords.longitude
          }}
          title={marker.title}
          description={marker.note}
          key={index}
        />
      )
    });

    return (
      <View style={styles.mainContainer}>
       <MapView
        debugger
        style={styles.map}
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0.09,
          longitudeDelta: 0.09
        }}>
        {pins}
        </MapView>
      </View>
    )
  }
};

MapPage.propTypes = {
  trip: React.PropTypes.array.isRequired
};

module.exports = MapPage;<|endoftext|>


<commit_before>// 粒子特效
var ParticleEffect = {
    ctx: null,
    canvas: null,
    particles: [],
    mouseCoordinates: {x: 0, y: 0},
    config: {
        radius: 5,                      // 默认粒子半径
        vxRange: [-1, 1],               // 默认粒子横向移动速度范围
        vyRange: [-1, 1],               // 默认粒子纵向移动速度范围
        scaleRange: [.5, 1],            // 默认粒子缩放比例范围
        lineLenThreshold: 125,          // 默认连线长度阈值
        color: 'rgba(255,255,255,.2)'   // 默认粒子、线条的颜色
    },
    init: function(newConfig) {

        var _this = this;
        var windowSize = Utils.getWindowSize();
        this.canvas = document.getElementById('canvas');
        this.ctx = this.canvas.getContext('2d');

        // 只有在浏览器支持canvas的情况下才有效
        if(this.ctx) {

            // 设置canvas宽高
            this.canvas.width = windowSize.width;
            this.canvas.height = windowSize.height;

            // 更新config配置
            newConfig && Object.keys(newConfig).forEach(function(key) {
                _this.config[key] = newConfig[key];
            });

            var times = 100;
            this.particles = [];
            while(times--) {
                this.particles.push(new Particle({
                    x: Utils.rangeRandom(this.config.radius, windowSize.width - this.config.radius),
                    y: Utils.rangeRandom(this.config.radius, windowSize.height - this.config.radius),
                    vx: Utils.rangeRandom(this.config.vxRange[0], this.config.vxRange[1]),
                    vy: Utils.rangeRandom(this.config.vyRange[0], this.config.vyRange[1]),
                    color: this.config.color,
                    scale: Utils.rangeRandom(this.config.scaleRange[0], this.config.scaleRange[1]),
                    radius: this.config.radius
                }));
            }
        }

        // 监听鼠标的mouseMove事件，记录下鼠标的x,y坐标
        window.addEventListener('mousemove', this.handleMouseMove.bind(this), false);

        // 监听窗口大小改变事件
        window.addEventListener('resize', this.handleWindowResize.bind(this), false);
    },
    move: function() {

        var windowSize = Utils.getWindowSize();

        this.particles.forEach(function(item) {

            // 更新粒子坐标
            item.x += item.vx;
            item.y += item.vy;

            // 如果粒子碰到了左墙壁或右墙壁，则改变粒子的横向运动方向
            if((item.x - item.radius < 0) || (item.x + item.radius > windowSize.width)) {
                item.vx *= -1;
            }

            // 如果粒子碰到了上墙壁或下墙壁，则改变粒子的纵向运动方向
            if((item.y - item.radius < 0) || (item.y + item.radius > windowSize.height)) {
                item.vy *= -1;
            }
        });
    },
    draw: function() {

        var _this = this;
        var lineLenThreshold = this.config.lineLenThreshold;
        var windowSize = Utils.getWindowSize();

        // 每次重新绘制之前，需要先清空画布，把上一次的内容清空
        this.ctx.clearRect(0, 0, windowSize.width, windowSize.height);

        // 绘制粒子
        this.particles.forEach(function(item) {
            item.draw(_this.ctx);
        });

        // 绘制粒子之间的连线
        for(var i = 0; i < this.particles.length; i++) {
            for(var j = i + 1; j < this.particles.length; j++) {
                var distance = Math.sqrt(Math.pow(this.particles[i].x - this.particles[j].x, 2) + Math.pow(this.particles[i].y - this.particles[j].y, 2));
                if(distance < lineLenThreshold) {
                    // 这里我们让距离远的线透明度淡一点，距离近的线透明度深一点
                    this.ctx.strokeStyle = 'rgba(255,255,255,' + (1 - distance / lineLenThreshold) * .2 + ')';
                    this.ctx.beginPath();
                    this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                    this.ctx.lineTo(this.particles[j].x, this.particles[j].y);
                    this.ctx.closePath();
                    this.ctx.stroke();
                }
            }
        }

        // 绘制粒子和鼠标之间的连线
        for(i = 0; i < this.particles.length; i++) {
            distance = Math.sqrt(Math.pow(this.particles[i].x - this.mouseCoordinates.x, 2) + Math.pow(this.particles[i].y - this.mouseCoordinates.y, 2));
            if(distance < lineLenThreshold) {
                this.ctx.strokeStyle = 'rgba(255,255,255,' + (1 - distance / lineLenThreshold) * .2 + ')';
                this.ctx.beginPath();
                this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                this.ctx.lineTo(this.mouseCoordinates.x, this.mouseCoordinates.y);
                this.ctx.closePath();
                this.ctx.stroke();
            }
        }

        // 粒子移动，更新相应的x, y坐标
        this.move();
    },
    handleMouseMove: function(event) {

        var x, y;
        event = event || window.event;

        if(event.pageX || event.pageY) {
            x = event.pageX;
            y = event.pageY;
        } else {
            x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }

        this.mouseCoordinates = {x: x, y: y};
    },
    handleWindowResize: function() {
        var windowSize = Utils.getWindowSize();
        this.canvas.width = windowSize.width;
        this.canvas.height = windowSize.height;
    },
    run: function(config) {
        this.init(config);
        setInterval(this.draw.bind(this), 1000 / 60);
    }
};

/**
 * Particle 粒子类
 */
function Particle(attr) {

    // 粒子属性
    this.x = attr.x;            // 粒子在画布中的横坐标
    this.y = attr.y;            // 粒子在画布中的纵坐标
    this.vx = attr.vx;          // 粒子的横向运动速度
    this.vy = attr.vy;          // 粒子的纵向运动速度
    this.color = attr.color;    // 粒子的颜色
    this.scale = attr.scale;    // 粒子的缩放比例
    this.radius = attr.radius;  // 粒子的半径大小

    // 绘制方法
    if(typeof Particle.prototype.draw === 'undefined') {
        Particle.prototype.draw = function(ctx) {
            // canvas画圆方法
            ctx.beginPath();
            ctx.fillStyle = this.color;
            ctx.strokeStyle = this.color;
            ctx.arc(this.x, this.y, this.radius * this.scale, 0, 2 * Math.PI, false);
            ctx.closePath();
            ctx.fill();
        }
    }
}

// 工具
var Utils = {
    getWindowSize: function() {
        return {
            width: this.getWindowWidth(),
            height: this.getWindowHeight()
        };
    },
    getWindowWidth: function() {
        return window.innerWidth || document.documentElement.clientWidth;
    },
    getWindowHeight: function() {
        return window.innerHeight || document.documentElement.clientHeight;
    },
    rangeRandom: function(min, max) {
        const diff = max - min;
        return min + Math.random() * diff;
    }
};<commit_msg>用requestAnimationFrame来代替setInterval，性能更好<commit_after>// 粒子特效
var ParticleEffect = {
    ctx: null,
    canvas: null,
    particles: [],
    mouseCoordinates: {x: 0, y: 0},
    config: {
        radius: 5,                      // 默认粒子半径
        vxRange: [-1, 1],               // 默认粒子横向移动速度范围
        vyRange: [-1, 1],               // 默认粒子纵向移动速度范围
        scaleRange: [.5, 1],            // 默认粒子缩放比例范围
        lineLenThreshold: 125,          // 默认连线长度阈值
        color: 'rgba(255,255,255,.2)'   // 默认粒子、线条的颜色
    },
    init: function(newConfig) {

        var _this = this;
        var windowSize = Utils.getWindowSize();
        this.canvas = document.getElementById('canvas');
        this.ctx = this.canvas.getContext('2d');

        // 只有在浏览器支持canvas的情况下才有效
        if(this.ctx) {

            // 设置canvas宽高
            this.canvas.width = windowSize.width;
            this.canvas.height = windowSize.height;

            // 更新config配置
            newConfig && Object.keys(newConfig).forEach(function(key) {
                _this.config[key] = newConfig[key];
            });

            // 生成粒子
            var times = 100;
            this.particles = [];
            while(times--) {
                this.particles.push(new Particle({
                    x: Utils.rangeRandom(this.config.radius, windowSize.width - this.config.radius),
                    y: Utils.rangeRandom(this.config.radius, windowSize.height - this.config.radius),
                    vx: Utils.rangeRandom(this.config.vxRange[0], this.config.vxRange[1]),
                    vy: Utils.rangeRandom(this.config.vyRange[0], this.config.vyRange[1]),
                    color: this.config.color,
                    scale: Utils.rangeRandom(this.config.scaleRange[0], this.config.scaleRange[1]),
                    radius: this.config.radius
                }));
            }

            // 监听鼠标的mouseMove事件，记录下鼠标的x,y坐标
            window.addEventListener('mousemove', this.handleMouseMove.bind(this), false);

            // 监听窗口大小改变事件
            window.addEventListener('resize', this.handleWindowResize.bind(this), false);

            // 兼容requestAnimationFrame
            this.supportRequestAnimationFrame();
        }
    },
    move: function() {

        var windowSize = Utils.getWindowSize();

        this.particles.forEach(function(item) {

            // 更新粒子坐标
            item.x += item.vx;
            item.y += item.vy;

            // 如果粒子碰到了左墙壁或右墙壁，则改变粒子的横向运动方向
            if((item.x - item.radius < 0) || (item.x + item.radius > windowSize.width)) {
                item.vx *= -1;
            }

            // 如果粒子碰到了上墙壁或下墙壁，则改变粒子的纵向运动方向
            if((item.y - item.radius < 0) || (item.y + item.radius > windowSize.height)) {
                item.vy *= -1;
            }
        });
    },
    draw: function() {

        var _this = this;
        var lineLenThreshold = this.config.lineLenThreshold;
        var windowSize = Utils.getWindowSize();

        // 每次重新绘制之前，需要先清空画布，把上一次的内容清空
        this.ctx.clearRect(0, 0, windowSize.width, windowSize.height);

        // 绘制粒子
        this.particles.forEach(function(item) {
            item.draw(_this.ctx);
        });

        // 绘制粒子之间的连线
        for(var i = 0; i < this.particles.length; i++) {
            for(var j = i + 1; j < this.particles.length; j++) {
                var distance = Math.sqrt(Math.pow(this.particles[i].x - this.particles[j].x, 2) + Math.pow(this.particles[i].y - this.particles[j].y, 2));
                if(distance < lineLenThreshold) {
                    // 这里我们让距离远的线透明度淡一点，距离近的线透明度深一点
                    this.ctx.strokeStyle = 'rgba(255,255,255,' + (1 - distance / lineLenThreshold) * .2 + ')';
                    this.ctx.beginPath();
                    this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                    this.ctx.lineTo(this.particles[j].x, this.particles[j].y);
                    this.ctx.closePath();
                    this.ctx.stroke();
                }
            }
        }

        // 绘制粒子和鼠标之间的连线
        for(i = 0; i < this.particles.length; i++) {
            distance = Math.sqrt(Math.pow(this.particles[i].x - this.mouseCoordinates.x, 2) + Math.pow(this.particles[i].y - this.mouseCoordinates.y, 2));
            if(distance < lineLenThreshold) {
                this.ctx.strokeStyle = 'rgba(255,255,255,' + (1 - distance / lineLenThreshold) * .2 + ')';
                this.ctx.beginPath();
                this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                this.ctx.lineTo(this.mouseCoordinates.x, this.mouseCoordinates.y);
                this.ctx.closePath();
                this.ctx.stroke();
            }
        }

        // 粒子移动，更新相应的x, y坐标
        this.move();

        // 循环调用draw方法
        window.requestAnimationFrame(this.draw.bind(this));
    },
    handleMouseMove: function(event) {

        var x, y;
        event = event || window.event;

        if(event.pageX || event.pageY) {
            x = event.pageX;
            y = event.pageY;
        } else {
            x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }

        this.mouseCoordinates = {x: x, y: y};
    },
    handleWindowResize: function() {
        var windowSize = Utils.getWindowSize();
        this.canvas.width = windowSize.width;
        this.canvas.height = windowSize.height;
    },
    supportRequestAnimationFrame: function() {
        if(!window.requestAnimationFrame) {
            window.requestAnimationFrame = (
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function (callback) {
                    setInterval(callback, 1000 / 60)
                }
            );
        }
    },
    run: function(config) {
        this.init(config);
        window.requestAnimationFrame(this.draw.bind(this));
    }
};

/**
 * Particle 粒子类
 */
function Particle(attr) {

    // 粒子属性
    this.x = attr.x;            // 粒子在画布中的横坐标
    this.y = attr.y;            // 粒子在画布中的纵坐标
    this.vx = attr.vx;          // 粒子的横向运动速度
    this.vy = attr.vy;          // 粒子的纵向运动速度
    this.color = attr.color;    // 粒子的颜色
    this.scale = attr.scale;    // 粒子的缩放比例
    this.radius = attr.radius;  // 粒子的半径大小

    // 绘制方法
    if(typeof Particle.prototype.draw === 'undefined') {
        Particle.prototype.draw = function(ctx) {
            // canvas画圆方法
            ctx.beginPath();
            ctx.fillStyle = this.color;
            ctx.strokeStyle = this.color;
            ctx.arc(this.x, this.y, this.radius * this.scale, 0, 2 * Math.PI, false);
            ctx.closePath();
            ctx.fill();
        }
    }
}

// 工具
var Utils = {
    getWindowSize: function() {
        return {
            width: this.getWindowWidth(),
            height: this.getWindowHeight()
        };
    },
    getWindowWidth: function() {
        return window.innerWidth || document.documentElement.clientWidth;
    },
    getWindowHeight: function() {
        return window.innerHeight || document.documentElement.clientHeight;
    },
    rangeRandom: function(min, max) {
        const diff = max - min;
        return min + Math.random() * diff;
    }
};<|endoftext|>


<commit_before>/**
 * This is the bootstrap for the nodgine module
 *
 * @module nodgine
 * @main
 * @class nodgine
 * @static
 **/

/**
 * The exporting object, which gets revealed
 *
 * @type {object}
 **/
var EXPORTOBJECT = {},

    mPath = require('path');

/**
 * This function makes the APIs of this framework global
 *
 * @method globalize
 **/
function mGlobalize() {
    'use strict';
    Object.defineProperties(GLOBAL, {
        '$APPLICATION': {
            value   : require('./Controller/Application.js')
        },
        '$ROUTER': {
            value   : require('./Controller/Router.js')
        },
        '$LOGGER': {
            value   : require('./Controller/Logger.js')
        },
        '$SERVICE': {
            value   : require('./Controller/Service.js')
        }
    });
}

require('./Controller/Application.js').addLoadPath(mPath.join(__dirname, 'Libs'));

//region revealing object

Object.defineProperties(EXPORTOBJECT, {

    /**
     * An instance of $APPLICATION
     *
     * @property $APPLICATION
     * @type {$APPLICATION}
     **/
    '$APPLICATION': {
        value   : require('./Controller/Application.js')
    },

    /**
     * An instance of $ROUTER
     *
     * @property $ROUTER
     * @type {$ROUTER}
     **/
    '$ROUTER': {
        value   : require('./Controller/Router.js')
    },

    /**
     * An instance of $LOGGER
     *
     * @property $LOGGER
     * @type {$LOGGER}
     **/
    '$LOGGER': {
        value   : require('./Controller/Logger.js')
    },

    /**
     * An instance of $SERVICE
     *
     * @property $SERVICE
     * @type {$SERVICE}
     **/
    '$SERVICE': {
        value   : require('./Controller/Service.js')
    },

    // define the rest
    'globalize': {
        value   : mGlobalize
    }
});

module.exports = EXPORTOBJECT;<commit_msg>Some little code optimizing<commit_after>/**
 * This is the bootstrap for the nodgine module
 *
 * @module nodgine
 * @main
 * @class nodgine
 * @static
 **/

/**
 * The exporting object, which gets revealed
 *
 * @type {object}
 **/
var EXPORTOBJECT = {};

/**
 * This function makes the APIs of this framework global
 *
 * @method globalize
 **/
function mGlobalize() {
    'use strict';
    Object.defineProperties(GLOBAL, {
        '$APPLICATION': {
            value   : require('./Controller/Application.js')
        },
        '$ROUTER': {
            value   : require('./Controller/Router.js')
        },
        '$LOGGER': {
            value   : require('./Controller/Logger.js')
        },
        '$SERVICE': {
            value   : require('./Controller/Service.js')
        }
    });
}

require('./Controller/Application.js').addLoadPath(__dirname + '/Libs');

//region revealing object

Object.defineProperties(EXPORTOBJECT, {

    /**
     * An instance of $APPLICATION
     *
     * @property $APPLICATION
     * @type {$APPLICATION}
     **/
    '$APPLICATION': {
        value   : require('./Controller/Application.js')
    },

    /**
     * An instance of $ROUTER
     *
     * @property $ROUTER
     * @type {$ROUTER}
     **/
    '$ROUTER': {
        value   : require('./Controller/Router.js')
    },

    /**
     * An instance of $LOGGER
     *
     * @property $LOGGER
     * @type {$LOGGER}
     **/
    '$LOGGER': {
        value   : require('./Controller/Logger.js')
    },

    /**
     * An instance of $SERVICE
     *
     * @property $SERVICE
     * @type {$SERVICE}
     **/
    '$SERVICE': {
        value   : require('./Controller/Service.js')
    },

    // define the rest
    'globalize': {
        value   : mGlobalize
    }
});

module.exports = EXPORTOBJECT;<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { TAPi18n } from 'meteor/tap:i18n';
import { hasRole } from '../../../authorization';
import { Info } from '../../../utils';
import { Users } from '../../../models';
import { settings } from '../../../settings';
import { API } from '../api';

import s from 'underscore.string';

// DEPRECATED
// Will be removed after v1.12.0
API.v1.addRoute('info', { authRequired: false }, {
	get() {
		const warningMessage = 'The endpoint "/v1/info" is deprecated and will be removed after version v1.12.0';
		console.warn(warningMessage);
		const user = this.getLoggedInUser();

		if (user && hasRole(user._id, 'admin')) {
			return API.v1.success(this.deprecationWarning({
				endpoint: 'info',
				versionWillBeRemoved: '1.12.0',
				response: {
					info: Info,
				},
			}));
		}

		return API.v1.success(this.deprecationWarning({
			endpoint: 'info',
			versionWillBeRemoved: '1.12.0',
			response: {
				info: {
					version: Info.version,
				},
			},
		}));
	},
});

API.v1.addRoute('me', { authRequired: true }, {
	get() {
		return API.v1.success(this.getUserInfo(Users.findOneById(this.userId)));
	},
});

let onlineCache = 0;
let onlineCacheDate = 0;
const cacheInvalid = 60000; // 1 minute
API.v1.addRoute('shield.svg', { authRequired: false }, {
	get() {
		const { type, name, icon } = this.queryParams;
		let { channel } = this.queryParams;
		if (!settings.get('API_Enable_Shields')) {
			throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', { route: '/api/v1/shield.svg' });
		}

		const types = settings.get('API_Shield_Types');
		if (type && (types !== '*' && !types.split(',').map((t) => t.trim()).includes(type))) {
			throw new Meteor.Error('error-shield-disabled', 'This shield type is disabled', { route: '/api/v1/shield.svg' });
		}

		const hideIcon = icon === 'false';
		if (hideIcon && (!name || !name.trim())) {
			return API.v1.failure('Name cannot be empty when icon is hidden');
		}

		let text;
		let backgroundColor = '#4c1';
		switch (type) {
			case 'online':
				if (Date.now() - onlineCacheDate > cacheInvalid) {
					onlineCache = Users.findUsersNotOffline().count();
					onlineCacheDate = Date.now();
				}

				text = `${ onlineCache } ${ TAPi18n.__('Online') }`;
				break;
			case 'channel':
				if (!channel) {
					return API.v1.failure('Shield channel is required for type "channel"');
				}

				text = `#${ channel }`;
				break;
			case 'user':
				const user = this.getUserFromParams();

				// Respect the server's choice for using their real names or not
				if (user.name && settings.get('UI_Use_Real_Name')) {
					text = `${ user.name }`;
				} else {
					text = `@${ user.username }`;
				}

				switch (user.status) {
					case 'online':
						backgroundColor = '#1fb31f';
						break;
					case 'away':
						backgroundColor = '#dc9b01';
						break;
					case 'busy':
						backgroundColor = '#bc2031';
						break;
					case 'offline':
						backgroundColor = '#a5a1a1';
				}
				break;
			default:
				text = TAPi18n.__('Join_Chat').toUpperCase();
		}

		const iconSize = hideIcon ? 7 : 24;
		const leftSize = name ? name.length * 6 + 7 + iconSize : iconSize;
		const rightSize = text.length * 6 + 20;
		const width = leftSize + rightSize;
		const height = 20;

		channel = s.escapeHTML(channel);
		text = s.escapeHTML(text);

		return {
			headers: { 'Content-Type': 'image/svg+xml;charset=utf-8' },
			body: `
				<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${ width }" height="${ height }">
					<linearGradient id="b" x2="0" y2="100%">
						<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
						<stop offset="1" stop-opacity=".1"/>
					</linearGradient>
					<mask id="a">
						<rect width="${ width }" height="${ height }" rx="3" fill="#fff"/>
					</mask>
					<g mask="url(#a)">
						<path fill="#555" d="M0 0h${ leftSize }v${ height }H0z"/>
						<path fill="${ backgroundColor }" d="M${ leftSize } 0h${ rightSize }v${ height }H${ leftSize }z"/>
						<path fill="url(#b)" d="M0 0h${ width }v${ height }H0z"/>
					</g>
						${ hideIcon ? '' : '<image x="5" y="3" width="14" height="14" xlink:href="/assets/favicon.svg"/>' }
					<g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
						${ name ? `<text x="${ iconSize }" y="15" fill="#010101" fill-opacity=".3">${ name }</text>
						<text x="${ iconSize }" y="14">${ name }</text>` : '' }
						<text x="${ leftSize + 7 }" y="15" fill="#010101" fill-opacity=".3">${ text }</text>
						<text x="${ leftSize + 7 }" y="14">${ text }</text>
					</g>
				</svg>
			`.trim().replace(/\>[\s]+\</gm, '><'),
		};
	},
});

API.v1.addRoute('spotlight', { authRequired: true }, {
	get() {
		check(this.queryParams, {
			query: String,
		});

		const { query } = this.queryParams;

		const result = Meteor.runAsUser(this.userId, () =>
			Meteor.call('spotlight', query)
		);

		return API.v1.success(result);
	},
});

API.v1.addRoute('directory', { authRequired: true }, {
	get() {
		const { offset, count } = this.getPaginationItems();
		const { sort, query } = this.parseJsonQuery();

		const { text, type, workspace = 'local' } = query;
		if (sort && Object.keys(sort).length > 1) {
			return API.v1.failure('This method support only one "sort" parameter');
		}
		const sortBy = sort ? Object.keys(sort)[0] : undefined;
		const sortDirection = sort && Object.values(sort)[0] === 1 ? 'asc' : 'desc';

		const result = Meteor.runAsUser(this.userId, () => Meteor.call('browseChannels', {
			text,
			type,
			workspace,
			sortBy,
			sortDirection,
			offset: Math.max(0, offset),
			limit: Math.max(0, count),
		}));

		if (!result) {
			return API.v1.failure('Please verify the parameters');
		}
		return API.v1.success({
			result: result.results,
			count: result.results.length,
			offset,
			total: result.total,
		});
	},
});
<commit_msg>Update misc.js<commit_after>import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { TAPi18n } from 'meteor/tap:i18n';
import { hasRole } from '../../../authorization';
import { Info } from '../../../utils';
import { Users } from '../../../models';
import { settings } from '../../../settings';
import { API } from '../api';

import s from 'underscore.string';

// DEPRECATED
// Will be removed after v1.12.0
API.v1.addRoute('info', { authRequired: false }, {
	get() {
		const warningMessage = 'The endpoint "/v1/info" is deprecated and will be removed after version v1.12.0';
		console.warn(warningMessage);
		const user = this.getLoggedInUser();

		if (user && hasRole(user._id, 'admin')) {
			return API.v1.success(this.deprecationWarning({
				endpoint: 'info',
				versionWillBeRemoved: '1.12.0',
				response: {
					info: Info,
				},
			}));
		}

		return API.v1.success(this.deprecationWarning({
			endpoint: 'info',
			versionWillBeRemoved: '1.12.0',
			response: {
				info: {
					version: Info.version,
				},
			},
		}));
	},
});

API.v1.addRoute('me', { authRequired: true }, {
	get() {
		return API.v1.success(this.getUserInfo(Users.findOneById(this.userId)));
	},
});

let onlineCache = 0;
let onlineCacheDate = 0;
const cacheInvalid = 60000; // 1 minute
API.v1.addRoute('shield.svg', { authRequired: false }, {
	get() {
		const { type, icon } = this.queryParams;
		let { channel, name } = this.queryParams;
		if (!settings.get('API_Enable_Shields')) {
			throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', { route: '/api/v1/shield.svg' });
		}

		const types = settings.get('API_Shield_Types');
		if (type && (types !== '*' && !types.split(',').map((t) => t.trim()).includes(type))) {
			throw new Meteor.Error('error-shield-disabled', 'This shield type is disabled', { route: '/api/v1/shield.svg' });
		}

		const hideIcon = icon === 'false';
		if (hideIcon && (!name || !name.trim())) {
			return API.v1.failure('Name cannot be empty when icon is hidden');
		}

		let text;
		let backgroundColor = '#4c1';
		switch (type) {
			case 'online':
				if (Date.now() - onlineCacheDate > cacheInvalid) {
					onlineCache = Users.findUsersNotOffline().count();
					onlineCacheDate = Date.now();
				}

				text = `${ onlineCache } ${ TAPi18n.__('Online') }`;
				break;
			case 'channel':
				if (!channel) {
					return API.v1.failure('Shield channel is required for type "channel"');
				}

				text = `#${ channel }`;
				break;
			case 'user':
				const user = this.getUserFromParams();

				// Respect the server's choice for using their real names or not
				if (user.name && settings.get('UI_Use_Real_Name')) {
					text = `${ user.name }`;
				} else {
					text = `@${ user.username }`;
				}

				switch (user.status) {
					case 'online':
						backgroundColor = '#1fb31f';
						break;
					case 'away':
						backgroundColor = '#dc9b01';
						break;
					case 'busy':
						backgroundColor = '#bc2031';
						break;
					case 'offline':
						backgroundColor = '#a5a1a1';
				}
				break;
			default:
				text = TAPi18n.__('Join_Chat').toUpperCase();
		}

		const iconSize = hideIcon ? 7 : 24;
		const leftSize = name ? name.length * 6 + 7 + iconSize : iconSize;
		const rightSize = text.length * 6 + 20;
		const width = leftSize + rightSize;
		const height = 20;

		channel = s.escapeHTML(channel);
		text = s.escapeHTML(text);
		name = s.escapeHTML(name);

		return {
			headers: { 'Content-Type': 'image/svg+xml;charset=utf-8' },
			body: `
				<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${ width }" height="${ height }">
					<linearGradient id="b" x2="0" y2="100%">
						<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
						<stop offset="1" stop-opacity=".1"/>
					</linearGradient>
					<mask id="a">
						<rect width="${ width }" height="${ height }" rx="3" fill="#fff"/>
					</mask>
					<g mask="url(#a)">
						<path fill="#555" d="M0 0h${ leftSize }v${ height }H0z"/>
						<path fill="${ backgroundColor }" d="M${ leftSize } 0h${ rightSize }v${ height }H${ leftSize }z"/>
						<path fill="url(#b)" d="M0 0h${ width }v${ height }H0z"/>
					</g>
						${ hideIcon ? '' : '<image x="5" y="3" width="14" height="14" xlink:href="/assets/favicon.svg"/>' }
					<g fill="#fff" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
						${ name ? `<text x="${ iconSize }" y="15" fill="#010101" fill-opacity=".3">${ name }</text>
						<text x="${ iconSize }" y="14">${ name }</text>` : '' }
						<text x="${ leftSize + 7 }" y="15" fill="#010101" fill-opacity=".3">${ text }</text>
						<text x="${ leftSize + 7 }" y="14">${ text }</text>
					</g>
				</svg>
			`.trim().replace(/\>[\s]+\</gm, '><'),
		};
	},
});

API.v1.addRoute('spotlight', { authRequired: true }, {
	get() {
		check(this.queryParams, {
			query: String,
		});

		const { query } = this.queryParams;

		const result = Meteor.runAsUser(this.userId, () =>
			Meteor.call('spotlight', query)
		);

		return API.v1.success(result);
	},
});

API.v1.addRoute('directory', { authRequired: true }, {
	get() {
		const { offset, count } = this.getPaginationItems();
		const { sort, query } = this.parseJsonQuery();

		const { text, type, workspace = 'local' } = query;
		if (sort && Object.keys(sort).length > 1) {
			return API.v1.failure('This method support only one "sort" parameter');
		}
		const sortBy = sort ? Object.keys(sort)[0] : undefined;
		const sortDirection = sort && Object.values(sort)[0] === 1 ? 'asc' : 'desc';

		const result = Meteor.runAsUser(this.userId, () => Meteor.call('browseChannels', {
			text,
			type,
			workspace,
			sortBy,
			sortDirection,
			offset: Math.max(0, offset),
			limit: Math.max(0, count),
		}));

		if (!result) {
			return API.v1.failure('Please verify the parameters');
		}
		return API.v1.success({
			result: result.results,
			count: result.results.length,
			offset,
			total: result.total,
		});
	},
});
<|endoftext|>


<commit_before>var fs = require('fs'),
    // fsio = require('promised-io/fs'),
    path = require('path').posix,
    Promise = require('bluebird')
Promise.promisifyAll(fs)
describe('checks if there is a newer version available', function() {

    describe('#by talking (spybing) to Git', function() {
        context('1.', function() {

            before(function () {
                return global.initAllAsync()

                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerspyOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })

                    .then(global.stopAllAsync)
            })

            it('#notifies of a new release available (remote > local)', function () {
                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 3.0.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release

                this.timeout(10000) //times out on Travis with 5000 timeout.

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })
                    .then(function () {
                        return bottle.container.updateAvailable.getResultsAsync()
                    })
                    .then(function (res) {

                        res.result.should.eq('older')
                    })
                    // check the file now has the right version stored
                    .then(function () {
                        return fs.readFileAsync(path.join(process.cwd(), '/specs/assets/config/config.json'), 'utf-8')
                    })
                    .then(function (configFile) {
                        configFile = JSON.parse(configFile)
                        configFile.meta.notifications.version.remote.version.should.equal('4.1.200')
                        clearTimeout(a)
                        scope.done()
                        myResolve()
                    })

                    .catch(function (err) {
                        console.error(err)
                        'error'.should.equal('not an error')
                        myReject(new Error(err))
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('timeout in update avail spec'))
                }, 3000)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })
            })
        })

        context('2.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_blank.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerspyOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })

            it('#notifies of a new release available (remote > local) with local cached version blank', function () {
                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 3.0.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release

                this.timeout(10000) //times out on Travis with 5000 timeout.

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                Promise.resolve()

                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })

                    .then(function () {
                        //check internally we return the right value
                        return bottle.container.updateAvailable.getResultsAsync()
                    })
                    .then(function (res) {
                        res.result.should.eq('older')
                    })
                    // check the file now has the right version stored
                    .then(function () {
                        return fs.readFileAsync(path.join(process.cwd(), '/specs/assets/config/config.json'), 'utf-8')
                    })
                    .then(function (configFile) {
                        configFile = JSON.parse(configFile)
                        configFile.meta.notifications.version.remote.version.should.equal('4.1.200')
                        clearTimeout(a)
                        scope.done()
                        myResolve()
                    })

                    .catch(function (err) {
                        myReject(new Error(err))
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('timeout in update avail spec'))
                }, 3000)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })
            })
        })

        // it('#returns with equal versions', function(done) {
        // this.timeout(5000)
        // var scope = nock('https://api.github.com')
        //     .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
        //     .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.0.json'))
        //
        // //need to use rewire so variables are not already set
        // var myModule = rewire(path.join(process.cwd(), '/src/lib/helpers/update-available.js'))
        // myModule.__with__({
        //     'location': path.join(process.cwd(), '/specs/assets/package.json')
        // })(function() {
        //     myModule(bottle.container).check()
        //         .then(function() {
        //             loggerInfospy.args[0][0].should.contain('is the same as the')
        //             done()
        //         })
        // })
        // })
        context('3.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_410_dismissfalse.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerspyOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#returns with newer version running locally (newer < remote)', function () {
                // published release: 4.0.0
                // current version running: 4.1.0
                // cached remote release: 4.1.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release
                this.timeout(5000)
                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.0.0.json'))
                    .persist()

                var client = global.ioclient.connect(global.socketURL, global.socketOptions)

                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })

                    .then(function () {

                        client.on('connect', function () {
                            bottle.container.io.emitToClients('updateAvailable')
                        })
                        client.on('updateAvailable', function (msg) {
                            msg.result.should.equal('newer')
                            client.disconnect()
                            clearTimeout(a)
                            scope.done()

                            myResolve()

                        })
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('should not reach timeout'))
                }, 1800)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })
            })
        })

        context('4.', function() {

            before(function () {

                return global.initAllAsync()
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerspyOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#sends updateAvailable with dismissUntilNextRemoteVersionBump=false', function (done) {

                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 3.0.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                var client = global.ioclient.connect(global.socketURL, global.socketOptions)
                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })
                    .then(function () {
                        client.on('connect', function (data) {
                            bottle.container.io.emitToClients('updateAvailable')
                        })
                        client.on('updateAvailable', function (msg) {
                            msg.result.should.equal('older')
                            client.disconnect()
                            clearTimeout(a)
                            scope.done()

                            done()

                        })
                    })

            })
        })

        context('5.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_410_dismisstrue.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerspyOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#should not send updateAvailable equal with dismissUntilNextRemoteVersionBump=true', function () {

                // published release: 4.1.0
                // current version running: 4.1.0
                // cached remote release: 4.1.0
                // dismissUntilNextVerBump: true
                // expected result: no updateAvail socket sent because of dismissUntil; versions equal

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.0.json'))
                    .persist()
                var client = global.ioclient.connect(global.socketURL, global.socketOptions)

                var myResolve, myReject
                var a = setTimeout(function () {

                    return bottle.container.updateAvailable.getResultsAsync()
                        .then(function (res) {
                            client.disconnect()
                            scope.done()
                            res.result.should.equal('equal')
                            myResolve()
                        })

                }, 1800)
                return new Promise(function (resolve, reject) {

                    return Promise.resolve()
                        .then(function () {
                            return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                        })
                        .delay(50)
                        .then(function () {
                            myResolve = resolve
                            myReject = reject

                            client.on('connect', function (data) {
                                bottle.container.io.emitToClients('updateAvailable')
                            })
                            client.on('updateAvailable', function (msg) {
                                msg.result.should.equal('equal')
                                client.disconnect()
                                clearTimeout(a)
                                scope.done()

                                myReject(new Error('should not receive an emit'))

                            })
                        })
                        .catch(function (err) {
                            bottle.container.logger.error('Error!', err)
                        })
                })

            })
        })

        context('6.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_410_dismisstrue.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerspyOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#should send updateAvailable with dismissUntilNextRemoteVersionBump=true (new version available)', function () {

                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 4.1.0
                // dismissUntilNextVerBump: true
                // expected result: current release is 'older'.
                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })
                    .then(function () {
                        var client = global.ioclient.connect(global.socketURL, global.socketOptions)

                        client.on('connect', function (data) {
                            bottle.container.io.emitToClients('updateAvailable')
                        })
                        client.on('updateAvailable', function (msg) {
                            msg.result.should.equal('older')
                            client.disconnect()
                            clearTimeout(a)
                            scope.done()

                            myResolve()

                        })
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('timeout in update avail spec'))
                }, 1900)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })

            })
        })

    })
})

<commit_msg>bug fixes<commit_after>var fs = require('fs'),
    // fsio = require('promised-io/fs'),
    path = require('path').posix,
    Promise = require('bluebird')
Promise.promisifyAll(fs)
describe('checks if there is a newer version available', function() {

    describe('#by talking (spybing) to Git', function() {
        context('1.', function() {

            before(function () {
                return global.initAllAsync()

                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerStubOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })

                    .then(global.stopAllAsync)
            })

            it('#notifies of a new release available (remote > local)', function () {
                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 3.0.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release

                this.timeout(10000) //times out on Travis with 5000 timeout.

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })
                    .then(function () {
                        return bottle.container.updateAvailable.getResultsAsync()
                    })
                    .then(function (res) {

                        res.result.should.eq('older')
                    })
                    // check the file now has the right version stored
                    .then(function () {
                        return fs.readFileAsync(path.join(process.cwd(), '/specs/assets/config/config.json'), 'utf-8')
                    })
                    .then(function (configFile) {
                        configFile = JSON.parse(configFile)
                        configFile.meta.notifications.version.remote.version.should.equal('4.1.200')
                        clearTimeout(a)
                        scope.done()
                        myResolve()
                    })

                    .catch(function (err) {
                        console.error(err)
                        'error'.should.equal('not an error')
                        myReject(new Error(err))
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('timeout in update avail spec'))
                }, 3000)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })
            })
        })

        context('2.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_blank.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerStubOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })

            it('#notifies of a new release available (remote > local) with local cached version blank', function () {
                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 3.0.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release

                this.timeout(10000) //times out on Travis with 5000 timeout.

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                Promise.resolve()

                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })

                    .then(function () {
                        //check internally we return the right value
                        return bottle.container.updateAvailable.getResultsAsync()
                    })
                    .then(function (res) {
                        res.result.should.eq('older')
                    })
                    // check the file now has the right version stored
                    .then(function () {
                        return fs.readFileAsync(path.join(process.cwd(), '/specs/assets/config/config.json'), 'utf-8')
                    })
                    .then(function (configFile) {
                        configFile = JSON.parse(configFile)
                        configFile.meta.notifications.version.remote.version.should.equal('4.1.200')
                        clearTimeout(a)
                        scope.done()
                        myResolve()
                    })

                    .catch(function (err) {
                        myReject(new Error(err))
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('timeout in update avail spec'))
                }, 3000)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })
            })
        })

        // it('#returns with equal versions', function(done) {
        // this.timeout(5000)
        // var scope = nock('https://api.github.com')
        //     .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
        //     .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.0.json'))
        //
        // //need to use rewire so variables are not already set
        // var myModule = rewire(path.join(process.cwd(), '/src/lib/helpers/update-available.js'))
        // myModule.__with__({
        //     'location': path.join(process.cwd(), '/specs/assets/package.json')
        // })(function() {
        //     myModule(bottle.container).check()
        //         .then(function() {
        //             loggerInfospy.args[0][0].should.contain('is the same as the')
        //             done()
        //         })
        // })
        // })
        context('3.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_410_dismissfalse.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerStubOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#returns with newer version running locally (newer < remote)', function () {
                // published release: 4.0.0
                // current version running: 4.1.0
                // cached remote release: 4.1.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release
                this.timeout(5000)
                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.0.0.json'))
                    .persist()

                var client = global.ioclient.connect(global.socketURL, global.socketOptions)

                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })

                    .then(function () {

                        client.on('connect', function () {
                            bottle.container.io.emitToClients('updateAvailable')
                        })
                        client.on('updateAvailable', function (msg) {
                            msg.result.should.equal('newer')
                            client.disconnect()
                            clearTimeout(a)
                            scope.done()

                            myResolve()

                        })
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('should not reach timeout'))
                }, 1800)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })
            })
        })

        context('4.', function() {

            before(function () {

                return global.initAllAsync()
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerStubOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#sends updateAvailable with dismissUntilNextRemoteVersionBump=false', function (done) {

                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 3.0.0
                // dismissUntilNextVerBump: false
                // expected result: update avail notifies of new release

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                var client = global.ioclient.connect(global.socketURL, global.socketOptions)
                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })
                    .then(function () {
                        client.on('connect', function (data) {
                            bottle.container.io.emitToClients('updateAvailable')
                        })
                        client.on('updateAvailable', function (msg) {
                            msg.result.should.equal('older')
                            client.disconnect()
                            clearTimeout(a)
                            scope.done()

                            done()

                        })
                    })

            })
        })

        context('5.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_410_dismisstrue.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerStubOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#should not send updateAvailable equal with dismissUntilNextRemoteVersionBump=true', function () {

                // published release: 4.1.0
                // current version running: 4.1.0
                // cached remote release: 4.1.0
                // dismissUntilNextVerBump: true
                // expected result: no updateAvail socket sent because of dismissUntil; versions equal

                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.0.json'))
                    .persist()
                var client = global.ioclient.connect(global.socketURL, global.socketOptions)

                var myResolve, myReject
                var a = setTimeout(function () {

                    return bottle.container.updateAvailable.getResultsAsync()
                        .then(function (res) {
                            client.disconnect()
                            scope.done()
                            res.result.should.equal('equal')
                            myResolve()
                        })

                }, 1800)
                return new Promise(function (resolve, reject) {

                    return Promise.resolve()
                        .then(function () {
                            return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                        })
                        .delay(50)
                        .then(function () {
                            myResolve = resolve
                            myReject = reject

                            client.on('connect', function (data) {
                                bottle.container.io.emitToClients('updateAvailable')
                            })
                            client.on('updateAvailable', function (msg) {
                                msg.result.should.equal('equal')
                                client.disconnect()
                                clearTimeout(a)
                                scope.done()

                                myReject(new Error('should not receive an emit'))

                            })
                        })
                        .catch(function (err) {
                            bottle.container.logger.error('Error!', err)
                        })
                })

            })
        })

        context('6.', function() {

            before(function () {

                return global.initAllAsync('/specs/assets/config/templates/config_updateavail_410_dismisstrue.json')
                    .then(function () {
                        sandbox = sinon.sandbox.create()

                        loggers = setupLoggerStubOrSpy('spy','spy')
                    })

            })

            after(function () {

                return Promise.resolve()
                    .then(function () {
                        nock.cleanAll();
                        sandbox.restore()
                    })
                    .then(global.stopAllAsync)
            })
            it('#should send updateAvailable with dismissUntilNextRemoteVersionBump=true (new version available)', function () {

                // published release: 4.1.200
                // current version running: 4.1.0
                // cached remote release: 4.1.0
                // dismissUntilNextVerBump: true
                // expected result: current release is 'older'.
                var scope = nock('https://api.github.com')
                    .get('/repos/tagyoureit/nodejs-poolController/releases/latest')
                    .replyWithFile(200, path.join(process.cwd(), '/specs/assets/webJsonReturns/gitLatestRelease4.1.200.json'))
                    .persist()

                Promise.resolve()
                    .then(function () {
                        return bottle.container.updateAvailable.initAsync('/specs/assets/package.json')
                    })
                    .then(function () {
                        var client = global.ioclient.connect(global.socketURL, global.socketOptions)

                        client.on('connect', function (data) {
                            bottle.container.io.emitToClients('updateAvailable')
                        })
                        client.on('updateAvailable', function (msg) {
                            msg.result.should.equal('older')
                            client.disconnect()
                            clearTimeout(a)
                            scope.done()

                            myResolve()

                        })
                    })

                var myResolve, myReject
                var a = setTimeout(function () {
                    myReject(new Error('timeout in update avail spec'))
                }, 1900)
                return new Promise(function (resolve, reject) {
                    myResolve = resolve
                    myReject = reject
                })

            })
        })

    })
})

<|endoftext|>


<commit_before>const electron = require('electron')
const ipc = electron.ipcMain
const messages = require('../../js/constants/messages')
const WebTorrentRemoteServer = require('webtorrent-remote/server')

module.exports = {init}

// Set to see communication between WebTorrent and torrent viewer tabs
const DEBUG_IPC = false
if (DEBUG_IPC) console.log('WebTorrent IPC debugging enabled')

// Connects to the BitTorrent network
// Communicates with the WebTorrentRemoteClients via message passing
let server = null
let channels = {}

// Receive messages via the window process, ultimately from the UI in a <webview> process
function init () {
  if (DEBUG_IPC) console.log('WebTorrent IPC init')
  server = new WebTorrentRemoteServer(send, {trace: DEBUG_IPC})
  ipc.on(messages.TORRENT_MESSAGE, function (e, msg) {
    if (DEBUG_IPC) console.log('Received IPC: ' + JSON.stringify(msg))
    channels[msg.clientKey] = e.sender
    server.receive(msg)
  })
}

// Send messages from the browser process (here), thru the window process, to the <webview>
function send (msg) {
  if (DEBUG_IPC) console.log('Sending IPC: ' + JSON.stringify(msg))
  const channel = channels[msg.clientKey]
  if (!channel) {
    if (DEBUG_IPC) console.error('Ignoring unrecognized clientKey ' + msg.clientKey)
    return
  }
  if (channel.isDestroyed()) {
    if (DEBUG_IPC) console.log('Removing destroyed channel, clientKey ' + msg.clientKey)
    delete channels[msg.clientKey]
    return
  }
  channel.send(messages.TORRENT_MESSAGE, msg)
}
<commit_msg>lazy-load WebTorrentRemoteServer<commit_after>const electron = require('electron')
const ipc = electron.ipcMain
const messages = require('../../js/constants/messages')

module.exports = {init}

// Set to see communication between WebTorrent and torrent viewer tabs
const DEBUG_IPC = false
if (DEBUG_IPC) console.log('WebTorrent IPC debugging enabled')

// Connects to the BitTorrent network
// Communicates with the WebTorrentRemoteClients via message passing
let server = null
let channels = {}

// Receive messages via the window process, ultimately from the UI in a <webview> process
function init () {
  if (DEBUG_IPC) console.log('WebTorrent IPC init')
  ipc.on(messages.TORRENT_MESSAGE, function (e, msg) {
    if (server === null) {
      const WebTorrentRemoteServer = require('webtorrent-remote/server')
      server = new WebTorrentRemoteServer(send, {trace: DEBUG_IPC})
    }
    if (DEBUG_IPC) console.log('Received IPC: ' + JSON.stringify(msg))
    channels[msg.clientKey] = e.sender
    server.receive(msg)
  })
}

// Send messages from the browser process (here), thru the window process, to the <webview>
function send (msg) {
  if (DEBUG_IPC) console.log('Sending IPC: ' + JSON.stringify(msg))
  const channel = channels[msg.clientKey]
  if (!channel) {
    if (DEBUG_IPC) console.error('Ignoring unrecognized clientKey ' + msg.clientKey)
    return
  }
  if (channel.isDestroyed()) {
    if (DEBUG_IPC) console.log('Removing destroyed channel, clientKey ' + msg.clientKey)
    delete channels[msg.clientKey]
    return
  }
  channel.send(messages.TORRENT_MESSAGE, msg)
}
<|endoftext|>


<commit_before>import React from "react";

export default class ClipRow extends React.Component {
    render(){
        const {clip} = this.props;
        let loc = "";

        if(clip.location.type==='page'){
            loc = `Page: ${clip.location.value}`;
        } else if(clip.location.type==='location') {
            loc = `Location: ${clip.location.value}`;
        }

        let icon = ;

        if(clip.location.type==='note'){
            icon = ;
        }

        return (

{icon} {loc} -- {clip.date.format("MMMM DD, YYYY h:mm:ss a")}

{clip.text}

        );
    }
}<commit_msg>Modified style of the icons.<commit_after>import React from "react";

export default class ClipRow extends React.Component {
    render(){
        const {clip} = this.props;
        let loc = "";

        if(clip.location.type==='page'){
            loc = `Page: ${clip.location.value}`;
        } else if(clip.location.type==='location') {
            loc = `Location: ${clip.location.value}`;
        }

        let icon = ;

        if(clip.location.type==='note'){
            icon = ;
        }

        return (

{icon}  {loc} -- {clip.date.format("MMMM DD, YYYY h:mm:ss a")}

{clip.text}

        );
    }
}<|endoftext|>


<commit_before>const React = require('react')
const e = React.createElement
const Component = React.Component
const Branches = require('./Branches')
const Contributors = require('./Contributors')
const AuthSetter = require('./AuthSetter')

module.exports = class Project extends Component {

	render() {
		return (
			e(
				'div',
				null,
				e('h1', null, 'Detail projektu'),
				e(
					AuthSetter,
					{
						project: this.props.projects.active,
					}
				),
				e(
					Contributors,
					{
						project: this.props.projects.active,
					}
				)/*,
				e(
					Branches,
					{
						project: this.props.projects.active,
					}
				)*/
			)
		)
	}
}
<commit_msg>Project: auth setter last to prevent jump<commit_after>const React = require('react')
const e = React.createElement
const Component = React.Component
const Branches = require('./Branches')
const Contributors = require('./Contributors')
const AuthSetter = require('./AuthSetter')

module.exports = class Project extends Component {

	render() {
		return (
			e(
				'div',
				null,
				e('h1', null, 'Detail projektu'),
				e(
					Contributors,
					{
						project: this.props.projects.active,
					}
				),
				e(
					AuthSetter,
					{
						project: this.props.projects.active,
					}
				)/*,
				e(
					Branches,
					{
						project: this.props.projects.active,
					}
				)*/
			)
		)
	}
}
<|endoftext|>


<commit_before>var filters = angular.module('maritime.filters', [])

filters.filter('duration', function() {
    return function(seconds) {
        return moment.duration(seconds, 'seconds').humanize();
    };
});
<commit_msg>Changed output of duration<commit_after>var filters = angular.module('maritime.filters', [])

function pad(num, size) {
    var s = num+"";
    while (s.length < size) s = "0" + s;
    return s;
}

filters.filter('duration', function() {
    return function(sec) {
        var duration = moment.duration(sec, 'seconds');
        return pad(duration.hours(), 2)+":"+pad(duration.minutes(), 2)+":"+pad(duration.seconds(), 2)
    };
});
<|endoftext|>


<commit_before>import Ember from 'ember';

/**
 * @module components
 * @class sl-menu
 */
export default Ember.Component.extend({

    /**
     * Object of component actions
     * @property {object} actions
     */
    actions: {

        /**
         * Send selected action when menu item is selected
         * @method actions.selected
         */
        selected: function() {
            this.performAction();
        },

        /**
         * @method showAll
         */
        showAll: function() {
            if ( this.$() ) {
                this.$().addClass( 'active' );

                if ( this.get( 'isRoot' )) {
                    this.$().addClass( 'showall' );
                }
            }

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'showAll' );
            });
        },

        /**
         * @method closeAll
         */
        closeAll: function() {
            if ( this.$() ) {
                this.$().removeClass( 'active' );

                if ( this.get( 'isRoot' )) {
                    this.$().removeClass( 'showall' );
                }
            }

            this.set( 'keyHandler', false );

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'closeAll' );
            });

            if ( this.get( 'isRoot' )) {
               this.set( 'keyHandler', true );
            }
        },

        /**
         * @method drillDown
         */
        drillDown: function() {
            var child = this.get( 'activeChild' );

            if ( this.get( 'keyHandler' )) {
                if ( child ) {
                    child.set( 'keyHandler', true );
                    this.set( 'keyHandler', false );
                }
            } else if ( child ) {
                child.drillDown();
            }
        }
    },

    /**
     * @method activateChild
     * @param {mixed} child
     */
    activateChild: function( child ) {
        if ( typeof child === 'number' ) {
            child = this.get( 'children' )[child - 1]; // convert to 0 base
        }

        this.get( 'children' ).forEach( function( item ) {
            if ( item === child ) {
                child.performAction();
            } else {
                item.$().removeClass( 'active' );
            }
        }.bind( this ));
    },

    /**
     * @property {mixed} activeChild
     * @default null
     */
    activeChild: null,

    /**
     * @property {array} attributeBindings
     */
    attributeBindings: [ 'class' ],

    /**
     * @method childSelected
     */
    childSelected: function( childIndex ) {
        if ( this.get( 'isRoot' ) && this.$().hasClass( 'showall' )) {
            this.send( 'closeAll' );
        }

        if ( this.get( 'keyHandler' )) {
            this.activateChild( childIndex );
        } else {
            var child = this.get( 'activeChild' );

            if ( child ) {
                child.childSelected( childIndex );
            }
        }
    },

    /**
     * @property {array} children
     * @default null
     */
    children: null,

    /**
     * @property {array} classNames
     */
    classNames: [ 'sl-menu' ],

    /**
     * @method click
     */
    click: function() {
        this.performAction();
        return false;
    },

    /**
     * @method initChildren
     */
    initChildren: function() {
        this.set( 'children', Ember.A() );
    }.on( 'init' ),

    /**
     * @method initKeyListeners
     */
    initKeyListeners: function() {
        var ke = this.get( 'keyEvents' );
        if ( ke ) {
            console.log( 'Init key listeners' );
            this.set( 'keyHandler', true );

            ke.on( 'childSelected', function( key ) {
                this.childSelected( key );
            }.bind( this )).on( 'drillDown', function() {
                if ( this.get( 'useDrillDownKey' )) {
                    this.send( 'drillDown' );
                }
            }.bind( this )).on( 'closeAll', function() {
                this.send( 'closeAll' );
            }.bind( this )).on( 'showAll', function() {
                this.send( 'showAll' );
            }.bind( this ));
        }

        // Register child
        var parent = this.get( 'parentView' );
        if ( typeof parent.registerChild === 'function' ) {
            parent.registerChild( this );
        }

        // Set path & root info
        var path = Ember.A(),
            rootNode = this;

        while( !rootNode.get( 'isRoot' )) {
            path.insertAt( 0, rootNode.get( 'menu.label' ));
            rootNode = rootNode.get( 'parentView' );
        }

        this.setProperties({
            path: path,
            rootNode: rootNode
        });
    }.observes( 'keyEvents' ).on( 'didInsertElement' ),

    cleanUp: function() {
        var parent = this.get( 'parentView' );
        if ( typeof parent.unregisterChild === 'function' ) {
            parent.unregisterChild( this );
        }

        var ke = this.get( 'keyEvents' );
        if ( ke ) {
            ke.off( 'childSelected' )
              .off( 'drillDown' )
              .off( 'closeAll' )
              .off( 'showAll' );
        }
    }.on( 'willDestroyElement' ),

    /**
     * @property {boolean} isRoot
     * @default true
     */
    isRoot: true,

    /**
     * @property {array} keyEvents
     * @default null
     */
    keyEvents: null,

    /**
     * @property {boolean} keyHandler
     * @default false
     */
    keyHandler: false,

    /**
     * @method mouseEnter
     */
    mouseEnter: function() {
        this.$().addClass( 'active' );
    },

    /**
     * @method mouseLeave
     */
    mouseLeave: function() {
        if ( this.get( 'isRoot' )) {
            this.send( 'closeAll' );
        } else if ( !this.get( 'rootNode' ).$().hasClass( 'showall' )) {
            this.$().removeClass( 'active' );
        }
    },

    /**
     * @method performAction
     */
    performAction: function() {
        this.$().addClass( 'active' );

        var rootNode = this.get( 'rootNode' ),
            path     = this.get( 'path' );

        rootNode.sendAction( 'selectionMade', path );

        if ( this.get( 'menu.pages' )) {
            this.showContent();

            if ( !this.get( 'useDrillDownKey' )) {
                this.set( 'keyHandler', true );
                this.get( 'parentView' ).set( 'keyHandler', false );
            }
        } else {
            if ( this.get( 'menu.emberAction' )) {
                var action = this.get( 'menu.emberAction' );

                if ( typeof action === 'function' ) {
                    action.call( this );
                } else if ( typeof action === 'object' ) {
                    rootNode.sendAction( 'actionInitiated',
                            action.actionName,
                            action.data );
                } else {
                    rootNode.sendAction( 'actionInitiated', action);
                }
            } else if ( this.get( 'menu.emberRoute' )) {
                rootNode.sendAction( 'changeRoute', this.get( 'menu.emberRoute' ));
            }

            rootNode.send( 'closeAll' );
        }
    },

    /**
     * Append a child to the children array
     * @method registerChild
     * @param {mixed} child
     */
    registerChild: function( child ) {
        this.get( 'children' ).push( child );
    },

    /**
     * @method showContent
     */
    showContent: function() {
        this.get( 'parentView' ).set( 'activeChild', this );
    },

    /**
     * Root element HTML tag type
     * @property {string} tagName
     * @default "div"
     */
    tagName: 'div',

    /**
     * @method unregisterChild
     */
    unregisterChild: function( child ) {
        if ( child === this.get( 'activeChild' )) {
            this.set( 'activeChild', null );
        }
        this.get( 'children' ).removeObject( child );
    },

    /**
     * @property {boolean} useDrillDownKey
     */
    useDrillDownKey: true,

    AllView: Ember.View.extend({

        tagName: 'li',

        classNames: [ 'all' ],

        target: function() {
            return this.get( 'parentView' );
        }.property( 'parentView' ),

        mouseEnter: function() {
            this.send( 'showAll' )
        }
    })
});
<commit_msg>Remove console output<commit_after>import Ember from 'ember';

/**
 * @module components
 * @class sl-menu
 */
export default Ember.Component.extend({

    /**
     * Object of component actions
     * @property {object} actions
     */
    actions: {

        /**
         * Send selected action when menu item is selected
         * @method actions.selected
         */
        selected: function() {
            this.performAction();
        },

        /**
         * @method showAll
         */
        showAll: function() {
            if ( this.$() ) {
                this.$().addClass( 'active' );

                if ( this.get( 'isRoot' )) {
                    this.$().addClass( 'showall' );
                }
            }

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'showAll' );
            });
        },

        /**
         * @method closeAll
         */
        closeAll: function() {
            if ( this.$() ) {
                this.$().removeClass( 'active' );

                if ( this.get( 'isRoot' )) {
                    this.$().removeClass( 'showall' );
                }
            }

            this.set( 'keyHandler', false );

            this.get( 'children' ).forEach( function( item ) {
                item.send( 'closeAll' );
            });

            if ( this.get( 'isRoot' )) {
               this.set( 'keyHandler', true );
            }
        },

        /**
         * @method drillDown
         */
        drillDown: function() {
            var child = this.get( 'activeChild' );

            if ( this.get( 'keyHandler' )) {
                if ( child ) {
                    child.set( 'keyHandler', true );
                    this.set( 'keyHandler', false );
                }
            } else if ( child ) {
                child.drillDown();
            }
        }
    },

    /**
     * @method activateChild
     * @param {mixed} child
     */
    activateChild: function( child ) {
        if ( typeof child === 'number' ) {
            child = this.get( 'children' )[child - 1]; // convert to 0 base
        }

        this.get( 'children' ).forEach( function( item ) {
            if ( item === child ) {
                child.performAction();
            } else {
                item.$().removeClass( 'active' );
            }
        }.bind( this ));
    },

    /**
     * @property {mixed} activeChild
     * @default null
     */
    activeChild: null,

    /**
     * @property {array} attributeBindings
     */
    attributeBindings: [ 'class' ],

    /**
     * @method childSelected
     */
    childSelected: function( childIndex ) {
        if ( this.get( 'isRoot' ) && this.$().hasClass( 'showall' )) {
            this.send( 'closeAll' );
        }

        if ( this.get( 'keyHandler' )) {
            this.activateChild( childIndex );
        } else {
            var child = this.get( 'activeChild' );

            if ( child ) {
                child.childSelected( childIndex );
            }
        }
    },

    /**
     * @property {array} children
     * @default null
     */
    children: null,

    /**
     * @property {array} classNames
     */
    classNames: [ 'sl-menu' ],

    /**
     * @method click
     */
    click: function() {
        this.performAction();
        return false;
    },

    /**
     * @method initChildren
     */
    initChildren: function() {
        this.set( 'children', Ember.A() );
    }.on( 'init' ),

    /**
     * @method initKeyListeners
     */
    initKeyListeners: function() {
        var ke = this.get( 'keyEvents' );
        if ( ke ) {
            this.set( 'keyHandler', true );

            ke.on( 'childSelected', function( key ) {
                this.childSelected( key );
            }.bind( this )).on( 'drillDown', function() {
                if ( this.get( 'useDrillDownKey' )) {
                    this.send( 'drillDown' );
                }
            }.bind( this )).on( 'closeAll', function() {
                this.send( 'closeAll' );
            }.bind( this )).on( 'showAll', function() {
                this.send( 'showAll' );
            }.bind( this ));
        }

        // Register child
        var parent = this.get( 'parentView' );
        if ( typeof parent.registerChild === 'function' ) {
            parent.registerChild( this );
        }

        // Set path & root info
        var path = Ember.A(),
            rootNode = this;

        while( !rootNode.get( 'isRoot' )) {
            path.insertAt( 0, rootNode.get( 'menu.label' ));
            rootNode = rootNode.get( 'parentView' );
        }

        this.setProperties({
            path: path,
            rootNode: rootNode
        });
    }.observes( 'keyEvents' ).on( 'didInsertElement' ),

    cleanUp: function() {
        var parent = this.get( 'parentView' );
        if ( typeof parent.unregisterChild === 'function' ) {
            parent.unregisterChild( this );
        }

        var ke = this.get( 'keyEvents' );
        if ( ke ) {
            ke.off( 'childSelected' )
              .off( 'drillDown' )
              .off( 'closeAll' )
              .off( 'showAll' );
        }
    }.on( 'willDestroyElement' ),

    /**
     * @property {boolean} isRoot
     * @default true
     */
    isRoot: true,

    /**
     * @property {array} keyEvents
     * @default null
     */
    keyEvents: null,

    /**
     * @property {boolean} keyHandler
     * @default false
     */
    keyHandler: false,

    /**
     * @method mouseEnter
     */
    mouseEnter: function() {
        this.$().addClass( 'active' );
    },

    /**
     * @method mouseLeave
     */
    mouseLeave: function() {
        if ( this.get( 'isRoot' )) {
            this.send( 'closeAll' );
        } else if ( !this.get( 'rootNode' ).$().hasClass( 'showall' )) {
            this.$().removeClass( 'active' );
        }
    },

    /**
     * @method performAction
     */
    performAction: function() {
        this.$().addClass( 'active' );

        var rootNode = this.get( 'rootNode' ),
            path     = this.get( 'path' );

        rootNode.sendAction( 'selectionMade', path );

        if ( this.get( 'menu.pages' )) {
            this.showContent();

            if ( !this.get( 'useDrillDownKey' )) {
                this.set( 'keyHandler', true );
                this.get( 'parentView' ).set( 'keyHandler', false );
            }
        } else {
            if ( this.get( 'menu.emberAction' )) {
                var action = this.get( 'menu.emberAction' );

                if ( typeof action === 'function' ) {
                    action.call( this );
                } else if ( typeof action === 'object' ) {
                    rootNode.sendAction( 'actionInitiated',
                            action.actionName,
                            action.data );
                } else {
                    rootNode.sendAction( 'actionInitiated', action);
                }
            } else if ( this.get( 'menu.emberRoute' )) {
                rootNode.sendAction( 'changeRoute', this.get( 'menu.emberRoute' ));
            }

            rootNode.send( 'closeAll' );
        }
    },

    /**
     * Append a child to the children array
     * @method registerChild
     * @param {mixed} child
     */
    registerChild: function( child ) {
        this.get( 'children' ).push( child );
    },

    /**
     * @method showContent
     */
    showContent: function() {
        this.get( 'parentView' ).set( 'activeChild', this );
    },

    /**
     * Root element HTML tag type
     * @property {string} tagName
     * @default "div"
     */
    tagName: 'div',

    /**
     * @method unregisterChild
     */
    unregisterChild: function( child ) {
        if ( child === this.get( 'activeChild' )) {
            this.set( 'activeChild', null );
        }
        this.get( 'children' ).removeObject( child );
    },

    /**
     * @property {boolean} useDrillDownKey
     */
    useDrillDownKey: true,

    AllView: Ember.View.extend({

        tagName: 'li',

        classNames: [ 'all' ],

        target: function() {
            return this.get( 'parentView' );
        }.property( 'parentView' ),

        mouseEnter: function() {
            this.send( 'showAll' )
        }
    })
});
<|endoftext|>