<commit_before>var TIMEOUT = 36000;
var CLEAR_INTERVAL = 5000;
var CACHE_TIME = CLEAR_INTERVAL * 2;
var MAX_LENGTH = 512;
var MIN_LENGTH = 432;
var COUNT = 80;
var count = 0;
var ids = [];
var data = {};
var proxy, binded, timeout, interval;

function disable() {
	proxy.removeListener('request', handleRequest);
	ids = [];
	data = {};
	interval && clearInterval(interval);
	interval = null;
	binded = false;
}

function enable() {
	!binded && proxy.on('request', handleRequest);

	binded = true;
	clearTimeout(timeout);
	timeout = setTimeout(disable, TIMEOUT);
	if (!interval) {
		interval = setInterval(clearCache, CLEAR_INTERVAL);
	}
}

/**
 * 如果超过最大缓存数，清理如下请求数据：
 * 1. 已经请求结束且结束时间超过10秒
 * 2. 请求#1前面的未结束且未被ui读取过的请求
 */
function clearCache() {
	var len = ids.length;
	if (len <= MAX_LENGTH) {
		return;
	}

	var index = -1; //已经完成，且缓存超过10s的最后一个请求
	var now = Date.now();
	for (var i = len - 1; i >= 0; i--) {
		var curData = data[ids[i]];
		if (curData.endTime && now - curData.endTime > TIMEOUT) {
			index = i;
			break;
		}
	}

	if (index < 0) {
		return;
	}

	var _ids = [];
	var end;
	++index;
	for (var i = 0; i < index; i++) {
		var id = ids[i];
		var curData = data[id];
		if (curData.read && (!curData.endTime || now - curData.endTime < CACHE_TIME
				|| now - curData.startTime < TIMEOUT)) {
			_ids.push(id);
		} else {
			delete data[id];
			if (--len <= MIN_LENGTH) {
				_ids.push.apply(_ids, ids.slice(i + 1, index));
				break;
			}
		}
	}
	ids = _ids.concat(ids.slice(index));
}

function getIds(startTime, count) {
	var len = ids.length;
	if (!len) {
		return [];
	}

	startTime = ((startTime || Date.now() - 6000) + '').split('-');
	count = Math.min(count || COUNT, len);

	startTime[0] = parseInt(startTime[0], 10) || 0;
	startTime[1] = parseInt(startTime[1], 10) || 0;

	if (compareId(ids[0], startTime)) {

		return ids.slice(0, count);
	}

	var end = len - 1;
	if (!end || !compareId(ids[end], startTime)) {

		return  [];
	}

	var index = getIndex(startTime, 0, end);
	return ids.slice(index, index + count);
}

function compareId(curId, refId) {
	curId = curId.split('-');
	return curId[0] > refId[0] || (curId[0] == refId[0] && curId[1] > refId[1]);
}

function getIndex(startTime, start, end) {
	if (end - start <= 1) {
		return compareId(ids[start], startTime) ? start : end;
	}

	var mid = Math.floor((start + end) / 2);
	var id = ids[mid];
	return compareId(id, startTime) ? getIndex(startTime, start, mid) : getIndex(startTime, mid + 1, end);
}

function getList(ids) {
	var result = [];
	for (var i = 0, len = ids && ids.length; i < len; i++) {
		var id = ids[i];
		var curData = data[id];
		if (curData) {
			curData.read = true;
			result[i] = curData;
		}
	}

	return result;
}

function handleRequest(req) {
	var curData = req.data;
	var startTime = curData.startTime;
	var id = curData.id = startTime + '-' + ++count;
	data[id] = curData;
	ids.push(id);
}

module.exports = function init(_proxy) {
	proxy = _proxy;
	/**
	 * options: {
	 * 		startTime: timestamp || timestamp + '-' + count
	 * 		count: 获取新数据的数量
	 * 		ids: 请未结束的id列表
	 * }
	 *
	 * @param options
	 */
	proxy.getData = function get(options) {
		enable();
		options = options || {};
		var data = {};
		var newIds = options.startTime == -1 ? [] : getIds(options.startTime, options.count);
		var list = getList(newIds).concat(getList(options.ids));
		for (var i = 0, len = list.length; i < len; i++) {
			var item = list[i];
			if (item) {
				data[item.id] = item;
			}
		}

		return {
			ids: options.ids || [],
			newIds: newIds,
			data: data
		};
	};
};<commit_msg>count<commit_after>var TIMEOUT = 36000;
var CLEAR_INTERVAL = 5000;
var CACHE_TIME = CLEAR_INTERVAL * 2;
var MAX_LENGTH = 512;
var MIN_LENGTH = 432;
var COUNT = 100;
var count = 0;
var ids = [];
var data = {};
var proxy, binded, timeout, interval;

function disable() {
	proxy.removeListener('request', handleRequest);
	ids = [];
	data = {};
	interval && clearInterval(interval);
	interval = null;
	binded = false;
}

function enable() {
	!binded && proxy.on('request', handleRequest);

	binded = true;
	clearTimeout(timeout);
	timeout = setTimeout(disable, TIMEOUT);
	if (!interval) {
		interval = setInterval(clearCache, CLEAR_INTERVAL);
	}
}

/**
 * 如果超过最大缓存数，清理如下请求数据：
 * 1. 已经请求结束且结束时间超过10秒
 * 2. 请求#1前面的未结束且未被ui读取过的请求
 */
function clearCache() {
	var len = ids.length;
	if (len <= MAX_LENGTH) {
		return;
	}

	var index = -1; //已经完成，且缓存超过10s的最后一个请求
	var now = Date.now();
	for (var i = len - 1; i >= 0; i--) {
		var curData = data[ids[i]];
		if (curData.endTime && now - curData.endTime > TIMEOUT) {
			index = i;
			break;
		}
	}

	if (index < 0) {
		return;
	}

	var _ids = [];
	var end;
	++index;
	for (var i = 0; i < index; i++) {
		var id = ids[i];
		var curData = data[id];
		if (curData.read && (!curData.endTime || now - curData.endTime < CACHE_TIME
				|| now - curData.startTime < TIMEOUT)) {
			_ids.push(id);
		} else {
			delete data[id];
			if (--len <= MIN_LENGTH) {
				_ids.push.apply(_ids, ids.slice(i + 1, index));
				break;
			}
		}
	}
	ids = _ids.concat(ids.slice(index));
}

function getIds(startTime, count) {
	var len = ids.length;
	if (!len) {
		return [];
	}

	startTime = ((startTime || Date.now() - 6000) + '').split('-');
	count = Math.min(count || COUNT, len);

	startTime[0] = parseInt(startTime[0], 10) || 0;
	startTime[1] = parseInt(startTime[1], 10) || 0;

	if (compareId(ids[0], startTime)) {

		return ids.slice(0, count);
	}

	var end = len - 1;
	if (!end || !compareId(ids[end], startTime)) {

		return  [];
	}

	var index = getIndex(startTime, 0, end);
	return ids.slice(index, index + count);
}

function compareId(curId, refId) {
	curId = curId.split('-');
	return curId[0] > refId[0] || (curId[0] == refId[0] && curId[1] > refId[1]);
}

function getIndex(startTime, start, end) {
	if (end - start <= 1) {
		return compareId(ids[start], startTime) ? start : end;
	}

	var mid = Math.floor((start + end) / 2);
	var id = ids[mid];
	return compareId(id, startTime) ? getIndex(startTime, start, mid) : getIndex(startTime, mid + 1, end);
}

function getList(ids) {
	var result = [];
	for (var i = 0, len = ids && ids.length; i < len; i++) {
		var id = ids[i];
		var curData = data[id];
		if (curData) {
			curData.read = true;
			result[i] = curData;
		}
	}

	return result;
}

function handleRequest(req) {
	var curData = req.data;
	var startTime = curData.startTime;
	var id = curData.id = startTime + '-' + ++count;
	data[id] = curData;
	ids.push(id);
}

module.exports = function init(_proxy) {
	proxy = _proxy;
	/**
	 * options: {
	 * 		startTime: timestamp || timestamp + '-' + count
	 * 		count: 获取新数据的数量
	 * 		ids: 请未结束的id列表
	 * }
	 *
	 * @param options
	 */
	proxy.getData = function get(options) {
		enable();
		options = options || {};
		var data = {};
		var newIds = options.startTime == -1 ? [] : getIds(options.startTime, options.count);
		var list = getList(newIds).concat(getList(options.ids));
		for (var i = 0, len = list.length; i < len; i++) {
			var item = list[i];
			if (item) {
				data[item.id] = item;
			}
		}

		return {
			ids: options.ids || [],
			newIds: newIds,
			data: data
		};
	};
};<|endoftext|>


<commit_before>// O8S - Sigmascape 4.0 Savage
[{
  zoneRegex: /Sigmascape V4\.0 \(Savage\)/,
  timelineFile: 'o8s.txt',
  triggers: [
    {
      id: 'O8S Shockwave',
      regex: / 14:28DB:Graven Image starts using Shockwave/,
      delaySeconds: 5,
      alertText: 'Look for Knockback',
      tts: 'knockback',
    },
    {
      id: 'O8S Indolent Will',
      regex: /Graven Image starts using Indolent Will/,
      alertText: 'Look Away From Statue',
      tts: 'look away',
    },
    {
      id: 'O8S Intemperate Will',
      regex: /14:28DF:Graven Image starts using Intemperate Will/,
      alertText: '<= Get Left/West',
      tts: 'left',
    },
    {
      id: 'O8S Gravitational Wave',
      regex: /14:28DE:Graven Image starts using Gravitational Wave/,
      alertText: 'Get Right/East =>',
      tts: 'right',
    },
    {
      id: 'O8S Ave Maria',
      regex: / 14:28E3:Graven Image starts using Ave Maria/,
      alertText: 'Look At Statue',
      tts: 'look towards',
    },
    {
      id: 'O8S Pasts Forgotten',
      regex: /Kefka starts using Pasts Forgotten/,
      alertText: 'Past: Stack and Stay',
      tts: 'stack and stay',
    },
    {
      id: 'O8S Futures Numbered',
      regex: /Kefka starts using Futures Numbered/,
      alertText: 'Future: Stack and Through',
      tts: 'stack and through',
    },
    {
      id: "O8S Past's End",
      regex: /Kefka starts using Past's End/,
      alertText: 'Past: Bait, then through',
      tts: 'run run run',
    },
    {
      id: "O8S Future's End",
      regex: /Kefka starts using Future's End/,
      alertText: 'Future: Bait, then stay',
      tts: 'stay stay stay',
    },
    {
      id: 'O8S Pulse Wave You',
      regex: /Graven Image starts using Pulse Wave on (\y{Name})/,
      condition: function(data, matches) { return data.me == matches[1]; },
      alertText: 'Knockback on YOU',
      tts: 'knockback',
    },
    {
      id: 'O8S Wings of Destruction',
      regex: / 14:2900:Kefka starts using Wings Of Destruction/,
      alarmText: function(data) {
        if (data.role == 'tank')
          return 'Wings: Be Near/Far';
      },
      infoText: function(data) {
        if (data.role != 'tank')
          return 'Max Melee: Avoid Tanks';
      },
      tts: function(data) {
        if (data.role == 'tank')
          return 'wings';
        else
          return 'max melee';
      },
    },
    {
      id: 'O8S Indulgent Will',
      regex: / 14:28E5:Graven Image starts using Indulgent Will on (\y{Name})/,
      condition: function(data, matches) { return data.me == matches[1]; },
      alarmText: 'Confusion: Go Outside',
      tts: 'confusion',
    },
    {
      id: 'O8S Idyllic Will',
      regex: / 14:28E6:Graven Image starts using Idyllic Will on (\y{Name})/,
      condition: function(data, matches) { return data.me == matches[1]; },
      alarmText: 'Sleep: Go Inside',
      tts: 'sleep',
    },
    {
      // Most thunders and blizzards are used multiple times, so suppress
      // any duplicates.
      // TODO: raidboss really needs a suppressSeconds that doesn't let
      // the same trigger fire for X time.
      regex: /14:....:Kefka starts using Thrumming Thunder/,
      delaySeconds: 1,
      run: function(data) {
        delete data.suppressThunder;
      },
    },
    {
      // Most thunders are blizzards are used multiple times, so suppress
      // any duplicates.
      // TODO: raidboss really needs a suppressSeconds that doesn't let
      // the same trigger fire for X time.
      regex: /14:....:Kefka starts using Blizzard Blitz/,
      delaySeconds: 1,
      run: function(data) {
        delete data.suppressBlizzard;
      },
    },
    {
      id: 'O8S Thrumming Thunder Real',
      regex: /14:(?:28CD|2B31):Kefka starts using Thrumming Thunder/,
      infoText: function(data) {
        if (data.suppressThunder)
          return;
        return 'True Thunder';
      },
      tts: function (data) {
        if (data.suppressThunder)
          return;
        return 'true';
      },
      run: function(data) {
        data.suppressThunder = true;
      },
    },
    {
      id: 'O8S Thrumming Thunder Fake',
      regex: /14:(?:28CC|2B30):Kefka starts using Thrumming Thunder/,
      infoText: function(data) {
        if (data.suppressThunder)
          return;
        return 'Fake Thunder';
      },
      tts: function (data) {
        if (data.suppressThunder)
          return;
        return 'fake';
      },
      run: function(data) {
        data.suppressThunder = true;
      },
    },
    {
      id: 'O8S Blizzard Fake Donut',
      regex: /14:(?:28C5|2B2B):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'Fake Ice: Get Out';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'fake';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
    {
      id: 'O8S Blizzard True Donut',
      regex: /14:(?:28C9|2B2E):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'True Ice: Get In';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'true';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
    {
      id: 'O8S Blizzard Fake Near',
      regex: /14:(?:28C4|2B2A):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'Fake Ice: Get In';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'fake';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
    {
      id: 'O8S Blizzard True Near',
      regex: /14:(?:28C8|2B2D):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'True Ice: Get Out';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'true';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
  ]
}]
<commit_msg>o8s: make past/future tank/healer specific during starstrafe<commit_after>// O8S - Sigmascape 4.0 Savage
[{
  zoneRegex: /Sigmascape V4\.0 \(Savage\)/,
  timelineFile: 'o8s.txt',
  triggers: [
    {
      id: 'O8S Shockwave',
      regex: / 14:28DB:Graven Image starts using Shockwave/,
      delaySeconds: 5,
      alertText: 'Look for Knockback',
      tts: 'knockback',
    },
    {
      id: 'O8S Indolent Will',
      regex: /Graven Image starts using Indolent Will/,
      alertText: 'Look Away From Statue',
      tts: 'look away',
    },
    {
      id: 'O8S Intemperate Will',
      regex: /14:28DF:Graven Image starts using Intemperate Will/,
      alertText: '<= Get Left/West',
      tts: 'left',
    },
    {
      id: 'O8S Gravitational Wave',
      regex: /14:28DE:Graven Image starts using Gravitational Wave/,
      alertText: 'Get Right/East =>',
      tts: 'right',
    },
    {
      id: 'O8S Ave Maria',
      regex: / 14:28E3:Graven Image starts using Ave Maria/,
      alertText: 'Look At Statue',
      tts: 'look towards',
    },
    {
      id: 'O8S Pasts Forgotten',
      regex: /Kefka starts using Pasts Forgotten/,
      alertText: 'Past: Stack and Stay',
      tts: 'stack and stay',
    },
    {
      id: 'O8S Futures Numbered',
      regex: /Kefka starts using Futures Numbered/,
      alertText: 'Future: Stack and Through',
      tts: 'stack and through',
    },
    {
      id: "O8S Past's End",
      regex: /Kefka starts using Past's End/,
      condition: function(data) { return data.role == 'tank' || data.role == 'healer'; },
      alertText: 'Past: Bait, then through',
      tts: 'run run run',
    },
    {
      id: "O8S Future's End",
      regex: /Kefka starts using Future's End/,
      condition: function(data) { return data.role == 'tank' || data.role == 'healer'; },
      alertText: 'Future: Bait, then stay',
      tts: 'stay stay stay',
    },
    {
      id: 'O8S Pulse Wave You',
      regex: /Graven Image starts using Pulse Wave on (\y{Name})/,
      condition: function(data, matches) { return data.me == matches[1]; },
      alertText: 'Knockback on YOU',
      tts: 'knockback',
    },
    {
      id: 'O8S Wings of Destruction',
      regex: / 14:2900:Kefka starts using Wings Of Destruction/,
      alarmText: function(data) {
        if (data.role == 'tank')
          return 'Wings: Be Near/Far';
      },
      infoText: function(data) {
        if (data.role != 'tank')
          return 'Max Melee: Avoid Tanks';
      },
      tts: function(data) {
        if (data.role == 'tank')
          return 'wings';
        else
          return 'max melee';
      },
    },
    {
      id: 'O8S Indulgent Will',
      regex: / 14:28E5:Graven Image starts using Indulgent Will on (\y{Name})/,
      condition: function(data, matches) { return data.me == matches[1]; },
      alarmText: 'Confusion: Go Outside',
      tts: 'confusion',
    },
    {
      id: 'O8S Idyllic Will',
      regex: / 14:28E6:Graven Image starts using Idyllic Will on (\y{Name})/,
      condition: function(data, matches) { return data.me == matches[1]; },
      alarmText: 'Sleep: Go Inside',
      tts: 'sleep',
    },
    {
      // Most thunders and blizzards are used multiple times, so suppress
      // any duplicates.
      // TODO: raidboss really needs a suppressSeconds that doesn't let
      // the same trigger fire for X time.
      regex: /14:....:Kefka starts using Thrumming Thunder/,
      delaySeconds: 1,
      run: function(data) {
        delete data.suppressThunder;
      },
    },
    {
      // Most thunders are blizzards are used multiple times, so suppress
      // any duplicates.
      // TODO: raidboss really needs a suppressSeconds that doesn't let
      // the same trigger fire for X time.
      regex: /14:....:Kefka starts using Blizzard Blitz/,
      delaySeconds: 1,
      run: function(data) {
        delete data.suppressBlizzard;
      },
    },
    {
      id: 'O8S Thrumming Thunder Real',
      regex: /14:(?:28CD|2B31):Kefka starts using Thrumming Thunder/,
      infoText: function(data) {
        if (data.suppressThunder)
          return;
        return 'True Thunder';
      },
      tts: function (data) {
        if (data.suppressThunder)
          return;
        return 'true';
      },
      run: function(data) {
        data.suppressThunder = true;
      },
    },
    {
      id: 'O8S Thrumming Thunder Fake',
      regex: /14:(?:28CC|2B30):Kefka starts using Thrumming Thunder/,
      infoText: function(data) {
        if (data.suppressThunder)
          return;
        return 'Fake Thunder';
      },
      tts: function (data) {
        if (data.suppressThunder)
          return;
        return 'fake';
      },
      run: function(data) {
        data.suppressThunder = true;
      },
    },
    {
      id: 'O8S Blizzard Fake Donut',
      regex: /14:(?:28C5|2B2B):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'Fake Ice: Get Out';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'fake';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
    {
      id: 'O8S Blizzard True Donut',
      regex: /14:(?:28C9|2B2E):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'True Ice: Get In';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'true';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
    {
      id: 'O8S Blizzard Fake Near',
      regex: /14:(?:28C4|2B2A):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'Fake Ice: Get In';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'fake';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
    {
      id: 'O8S Blizzard True Near',
      regex: /14:(?:28C8|2B2D):Kefka starts using Blizzard Blitz/,
      infoText: function(data) {
        if (data.suppressBlizzard)
          return;
        return 'True Ice: Get Out';
      },
      tts: function (data) {
        if (data.suppressBlizzard)
          return;
        return 'true';
      },
      run: function(data) {
        data.suppressBlizzard = true;
      },
    },
  ]
}]
<|endoftext|>


<commit_before>/**
 * @file Tree component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import isArray from 'lodash/isArray';
import classNames from 'classnames';

import TreeNode from '../_TreeNode';
import Tip from '../Tip';
import Theme from '../Theme';

import SelectMode from '../_statics/SelectMode';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import Calculation from '../_vendors/Calculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class Tree extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: Calculation.getInitValue(props),
            isNodeToggling: false
        };

    }

    addRecursiveValue = (node, value) => {

        if (!node || !value) {
            return;
        }

        if (!Calculation.isItemChecked(node, value, this.props)) {
            value.push(node);
        }

        if (!node.children || node.children.length < 1) {
            return;
        }

        for (let item of node.children) {
            this.addRecursiveValue(item, value);
        }

    };

    removeRecursiveValue = (node, value) => {

        if (!node || !value) {
            return;
        }

        const index = Calculation.getMultiSelectItemIndex(node, value, this.props);
        if (index > -1) {
            value.splice(index, 1);
        }

        if (!node.children || node.children.length < 1) {
            return;
        }

        for (let item of node.children) {
            this.removeRecursiveValue(item, value);
        }

    };

    /**
     * traverse tree data to update value when multi recursive select
     * @param value
     * @returns {Array}
     */
    updateValue = value => {

        const {data} = this.props;
        let result = [];

        Util.postOrderTraverse(data, node => {
            if (!node.children || node.children.length < 1) {
                if (value.includes(node)) {
                    result.push(node);
                }
            } else {
                if (node.children.every(child => result.includes(child))) {
                    result.push(node);
                }
            }
        });

        return result;

    };

    treeNodeSelectHandler = (node, path, e) => {

        if (!node) {
            return;
        }

        const {selectMode, isSelectRecursive} = this.props;
        let {value} = this.state;

        if (selectMode === SelectMode.MULTI_SELECT) {

            if (!value || !isArray(value)) {
                value = [];
            }

            if (isSelectRecursive) {
                this.addRecursiveValue(node, value);
                value = this.updateValue(value);
            } else {
                value.push(node);
            }

        } else if (selectMode === SelectMode.SINGLE_SELECT) {
            value = node;
        }

        this.setState({
            value
        }, () => {
            const {onNodeSelect, onChange} = this.props;
            onNodeSelect && onNodeSelect(node, path, e);
            onChange && onChange(value, e);
        });

    };

    treeNodeDeselectHandler = (node, path, e) => {

        const {selectMode} = this.props;

        if (selectMode !== SelectMode.MULTI_SELECT) {
            return;
        }

        const {isSelectRecursive} = this.props;
        let {value} = this.state;

        if (!value || !isArray(value)) {
            value = [];
        } else {
            if (isSelectRecursive) {
                this.removeRecursiveValue(node, value);
                value = this.updateValue(value);
            } else {
                const index = Calculation.getMultiSelectItemIndex(node, value, this.props);
                if (index > -1) {
                    value.splice(index, 1);
                }
            }
        }

        this.setState({
            value
        }, () => {
            const {onNodeDeselect, onChange} = this.props;
            onNodeDeselect && onNodeDeselect(node, path, e);
            onChange && onChange(value, e);
        });

    };

    nodeToggleStartHandler = () => {

        const {beforeNodeToggle} = this.props;

        if (beforeNodeToggle && beforeNodeToggle() === false) {
            return;
        }

        this.setState({
            isNodeToggling: true
        });

    };

    nodeToggleEndHandler = () => {
        this.setState({
            isNodeToggling: false
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: ComponentUtil.getDerivedState(props, state, 'value')
        };
    }

    render() {

        const {
                children, className, style, theme, data, allowCollapse,
                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,
                idField, valueField, displayField, descriptionField, disabled, isLoading, readOnly, selectMode,
                isSelectRecursive, renderer, onNodeClick
            } = this.props,
            {value, isNodeToggling} = this.state,

            treeClassName = classNames('tree', {
                [className]: className
            });

        return (

 {
                     Event.wheelHandler(e, this.props);
                 }}>

                <TreeNode data={data}
                          value={value}
                          theme={theme}
                          idField={idField}
                          valueField={valueField}
                          displayField={displayField}
                          descriptionField={descriptionField}
                          disabled={disabled}
                          isLoading={isLoading}
                          readOnly={readOnly}
                          selectMode={selectMode}
                          renderer={renderer}
                          allowCollapse={allowCollapse}
                          collapsedIconCls={collapsedIconCls}
                          expandedIconCls={expandedIconCls}
                          radioUncheckedIconCls={radioUncheckedIconCls}
                          radioCheckedIconCls={radioCheckedIconCls}
                          checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                          checkboxCheckedIconCls={checkboxCheckedIconCls}
                          checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                          isNodeToggling={isNodeToggling}
                          isSelectRecursive={isSelectRecursive}
                          onClick={(...args) => onNodeClick && onNodeClick(...args)}
                          onNodeToggleStart={this.nodeToggleStartHandler}
                          onNodeToggleEnd={this.nodeToggleEndHandler}
                          onSelect={this.treeNodeSelectHandler}
                          onDeselect={this.treeNodeDeselectHandler}/>

                {children}

        );
    }
}

Tree.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The theme of the tree node.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme of the tree node select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The mode of tree node.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * Children passed into the tree node.
     */
    data: PropTypes.shape({

        /**
         * The CSS class name of the tree node.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the tree node.
         */
        style: PropTypes.object,

        /**
         * The theme of the tree button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the tree button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The tree node's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the tree node. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the tree node will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        children: PropTypes.array,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a tree node touch-tapped.
         */
        onClick: PropTypes.func

    }),

    /**
     * The id field name in data. (default: "id")
     */
    idField: PropTypes.string,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the tree will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the tree will be at loading status.
     */
    isLoading: PropTypes.bool,

    readOnly: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * Callback function fired when the tree node touch tap.
     */
    onNodeClick: PropTypes.func,

    /**
     * Callback function fired when the tree node selected.
     */
    onNodeSelect: PropTypes.func,

    /**
     * Callback function fired when the tree node deselected.
     */
    onNodeDeselect: PropTypes.func,

    /**
     * Callback function fired when the tree changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func,

    beforeNodeToggle: PropTypes.func

};

Tree.defaultProps = {

    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    idField: 'id',
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    disabled: false,
    isLoading: false,
    readOnly: false,
    shouldPreventContainerScroll: true,
    isSelectRecursive: false,
    allowCollapse: true

};

export default Tree;<commit_msg>update Tree<commit_after>/**
 * @file Tree component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import isArray from 'lodash/isArray';
import classNames from 'classnames';

import TreeNode from '../_TreeNode';
import Tip from '../Tip';
import Theme from '../Theme';

import SelectMode from '../_statics/SelectMode';

import Util from '../_vendors/Util';
import Event from '../_vendors/Event';
import Calculation from '../_vendors/Calculation';
import ComponentUtil from '../_vendors/ComponentUtil';

class Tree extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: Calculation.getInitValue(props),
            isNodeToggling: false
        };

    }

    addRecursiveValue = (node, value) => {

        if (!node || !value) {
            return;
        }

        if (!Calculation.isItemChecked(node, value, this.props)) {
            value.push(node);
        }

        if (!node.children || node.children.length < 1) {
            return;
        }

        for (let item of node.children) {
            this.addRecursiveValue(item, value);
        }

    };

    removeRecursiveValue = (node, value) => {

        if (!node || !value) {
            return;
        }

        const index = Calculation.getMultiSelectItemIndex(node, value, this.props);
        if (index > -1) {
            value.splice(index, 1);
        }

        if (!node.children || node.children.length < 1) {
            return;
        }

        for (let item of node.children) {
            this.removeRecursiveValue(item, value);
        }

    };

    /**
     * traverse tree data to update value when multi recursive select
     * @param value
     * @returns {Array}
     */
    updateValue = value => {

        const {data} = this.props;
        let result = [];

        Util.postOrderTraverse(data, node => {
            if (!node.children || node.children.length < 1) {
                if (value.includes(node)) {
                    result.push(node);
                }
            } else {
                if (node.children.every(child => result.includes(child))) {
                    result.push(node);
                }
            }
        });

        return result;

    };

    treeNodeSelectHandler = (node, path, e) => {

        if (!node) {
            return;
        }

        const {selectMode, isSelectRecursive} = this.props;
        let {value} = this.state;

        if (selectMode === SelectMode.MULTI_SELECT) {

            if (!value || !isArray(value)) {
                value = [];
            }

            if (isSelectRecursive) {
                this.addRecursiveValue(node, value);
                value = this.updateValue(value);
            } else {
                value.push(node);
            }

        } else if (selectMode === SelectMode.SINGLE_SELECT) {
            value = node;
        }

        this.setState({
            value
        }, () => {
            const {onNodeSelect, onChange} = this.props;
            onNodeSelect && onNodeSelect(node, path, e);
            onChange && onChange(value, e);
        });

    };

    treeNodeDeselectHandler = (node, path, e) => {

        const {selectMode} = this.props;

        if (selectMode !== SelectMode.MULTI_SELECT) {
            return;
        }

        const {isSelectRecursive} = this.props;
        let {value} = this.state;

        if (!value || !isArray(value)) {
            value = [];
        } else {
            if (isSelectRecursive) {
                this.removeRecursiveValue(node, value);
                value = this.updateValue(value);
            } else {
                const index = Calculation.getMultiSelectItemIndex(node, value, this.props);
                if (index > -1) {
                    value.splice(index, 1);
                }
            }
        }

        this.setState({
            value
        }, () => {
            const {onNodeDeselect, onChange} = this.props;
            onNodeDeselect && onNodeDeselect(node, path, e);
            onChange && onChange(value, e);
        });

    };

    nodeToggleStartHandler = () => {

        const {beforeNodeToggle} = this.props;

        if (beforeNodeToggle && beforeNodeToggle() === false) {
            return;
        }

        this.setState({
            isNodeToggling: true
        });

    };

    nodeToggleEndHandler = () => {
        this.setState({
            isNodeToggling: false
        });
    };

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            value: Calculation.getInitValue({
                value: ComponentUtil.getDerivedState(props, state, 'value'),
                selectMode: props.selectMode
            })
        };
    }

    render() {

        const {
                children, className, style, theme, data, allowCollapse,
                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,
                idField, valueField, displayField, descriptionField, disabled, isLoading, readOnly, selectMode,
                isSelectRecursive, renderer, onNodeClick
            } = this.props,
            {value, isNodeToggling} = this.state,

            treeClassName = classNames('tree', {
                [className]: className
            });

        return (

 Event.wheelHandler(e, this.props)}>

                <TreeNode data={data}
                          value={value}
                          theme={theme}
                          idField={idField}
                          valueField={valueField}
                          displayField={displayField}
                          descriptionField={descriptionField}
                          disabled={disabled}
                          isLoading={isLoading}
                          readOnly={readOnly}
                          selectMode={selectMode}
                          renderer={renderer}
                          allowCollapse={allowCollapse}
                          collapsedIconCls={collapsedIconCls}
                          expandedIconCls={expandedIconCls}
                          radioUncheckedIconCls={radioUncheckedIconCls}
                          radioCheckedIconCls={radioCheckedIconCls}
                          checkboxUncheckedIconCls={checkboxUncheckedIconCls}
                          checkboxCheckedIconCls={checkboxCheckedIconCls}
                          checkboxIndeterminateIconCls={checkboxIndeterminateIconCls}
                          isNodeToggling={isNodeToggling}
                          isSelectRecursive={isSelectRecursive}
                          onClick={(...args) => onNodeClick && onNodeClick(...args)}
                          onNodeToggleStart={this.nodeToggleStartHandler}
                          onNodeToggleEnd={this.nodeToggleEndHandler}
                          onSelect={this.treeNodeSelectHandler}
                          onDeselect={this.treeNodeDeselectHandler}/>

                {children}

        );
    }
}

Tree.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * The theme of the tree node.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The theme of the tree node select radio or checkbox.
     */
    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The mode of tree node.
     */
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    /**
     * Children passed into the tree node.
     */
    data: PropTypes.shape({

        /**
         * The CSS class name of the tree node.
         */
        className: PropTypes.string,

        /**
         * Override the styles of the tree node.
         */
        style: PropTypes.object,

        /**
         * The theme of the tree button.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The text value of the tree button.Type can be string or number.
         */
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The tree node's display text. Type can be string, number or bool.
         */
        text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * The desc value of the tree node. Type can be string or number.
         */
        desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

        /**
         * If true,the tree node will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * If true,the button will be have loading effect.
         */
        isLoading: PropTypes.bool,

        /**
         * Use this property to display an icon. It will display on the left.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon. It will display on the right.
         */
        rightIconCls: PropTypes.string,

        /**
         * The message of tip.
         */
        tip: PropTypes.string,

        /**
         * The position of tip.
         */
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Tip.Position)),

        children: PropTypes.array,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        itemRenderer: PropTypes.func,

        /**
         * Callback function fired when a tree node touch-tapped.
         */
        onClick: PropTypes.func

    }),

    /**
     * The id field name in data. (default: "id")
     */
    idField: PropTypes.string,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the tree will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * If true, the tree will be at loading status.
     */
    isLoading: PropTypes.bool,

    readOnly: PropTypes.bool,

    shouldPreventContainerScroll: PropTypes.bool,
    isSelectRecursive: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * Callback function fired when the tree node touch tap.
     */
    onNodeClick: PropTypes.func,

    /**
     * Callback function fired when the tree node selected.
     */
    onNodeSelect: PropTypes.func,

    /**
     * Callback function fired when the tree node deselected.
     */
    onNodeDeselect: PropTypes.func,

    /**
     * Callback function fired when the tree changed.
     */
    onChange: PropTypes.func,

    /**
     * Callback function fired when wrapper wheeled.
     */
    onWheel: PropTypes.func,

    beforeNodeToggle: PropTypes.func

};

Tree.defaultProps = {

    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    idField: 'id',
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    disabled: false,
    isLoading: false,
    readOnly: false,
    shouldPreventContainerScroll: true,
    isSelectRecursive: false,
    allowCollapse: true

};

export default Tree;<|endoftext|>


<commit_before>// Escape URI-sensitive chars, but leave forward slashes alone.
module.exports = function escapePath(p) {
  return encodeURIComponent(p).replace(/%2F/g, '/');
}
<commit_msg>Fix paren escaping bug.<commit_after>// Escape URI-sensitive chars, but leave forward slashes alone.
module.exports = function escapePath(p) {
  return encodeURIComponent(p)
    .replace(/%2F/g, '/')
    .replace(/\)/g, '%29')
    .replace(/\(/g, '%28');
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Convert HTML in even better HTML.
 * @param {String} string
 * @return {String}
 * @constructor
 */
var markyMark = function markyMark (string) {
  var internal = {};
  var external = {};

  external.chunks = [];
  internal.mode   = '';

  /**
   * Do actual conversion
   * @param {String} string
   * @return {String}
   */
  external.convert = function convert (string) {
    external.chunks = [];
    internal.mode   = '';
    var currentChunk = '';
    for (var i = 0, len = string.length; i < len; i++) {
      var c = string[i];

      if (internal.mode === '<>' || internal.mode === '</>') {
        internal.pushChunk(currentChunk, '');
        currentChunk = '';
      }
      if (c === '<') {
        internal.pushChunk(currentChunk, c);
        currentChunk = '';
      }
      else if (internal.mode === '<' && c === '/') {
        internal.mode += c;
      }
      else if (c === '>') {
        internal.mode += c;
      }
      currentChunk += c;
    }
    internal.pushChunk(currentChunk);
    return external.chunks.join('');
  };

  /**
   * Push a single chunk to the chunk array, but convert chunks by looking at the current mode
   * @param {String} chunk
   * @param {String} mode
   * @return {Boolean}
   */
  internal.pushChunk = function pushChunk (chunk, newMode) {
    newMode = newMode || '';
    if (chunk) {
      switch (internal.mode) {
        case '':
          // Text node
          chunk = internal.convertText(chunk);
          break;
        case '':
          chunk = internal.convertCode(chunk);
          break;
        case '<css>':
          chunk = internal.convertCss(chunk);
          break;
        case '':
          chunk = internal.convertHtml(chunk);
          break;
        case '<markdown>':
          chunk = internal.convertMarkdown(chunk);
          break;
        case '<>':
          if (chunk.match(/<code class/)) {
            var lang = chunk.match(/(css|html|markdown)/);
            if (lang && lang[1]) {
              newMode = '<'+lang[1]+'>';
            }
            else {
              newMode = '';
            }
          }
          else if (chunk === '') {
              newMode = '<no>';
          }
          break;
      }
      external.chunks.push(chunk);
    }
    internal.mode = newMode;
    return newMode;
  };

  /**
   * Convert text node. Will do some pretty clever UTF-8 emoji stunts as well.
   * @see    http://apps.timwhitlock.info/emoji/tables/unicode
   * @param  {String} string
   * @return {String}
   */
  internal.convertText = function convertText(string) {
    var entityMap = {
      '...': '…',
      '… …': '…',
      '(C)': '©',
      '(R)': '®',
      '(TM)': '™',
      '(+-)': '±',
      '(1/4)': '¼',
      '(1/2)': '½',
      '(3/4)': '¾',
      '->': '→',
      '=>': '⇒',
      '<-': '←',
      '<=': '⇐',
    };
    string = string.replace(/(\.\.\.|… …|\(C\)|\(R\)|\(TM\)|\(+-\)|\(1\/4\)|\(1\/2\)|\(3\/4\)|->|=>|<-|<=)/g, function(s) {
      return entityMap[s];
    });

    entityMap = {
      ':)': '😊',
      ':(': '🙁',
      ':|': '😐',
      ':/': '😕',
      ':D': '😄',
      ':P': '😛',
      ':O': '😮',
      ':*': '😘',
      ';)': '😉',
      'B)': '😎',
      'XP': '😝'
    };
    string = string.replace(/(\W|^)(:(?:\(|\)|\||\/|D|P|O|\*)|(?:;|B)\)|XP)(\W|$)/g, function(all, before, s, after) {
      return before + entityMap[s] + after;
    });

    return string
      .replace(/\s--\s/g, ' — ')
      .replace(/(\d)\s*-\s*(\d)/g,'$1–$2')
      .replace(/(\s)-(\s)/g,'$1–$2')
      .replace(/(\d\s*)(x|\*)(\s*\d)/g,'$1×$3')
      .replace(/(^|[^\S])"(\S.*?\S)"([^\S]|$)/g,'$1„$2“$3') // TODO: Fix quotation with inline tags
      .replace(/(^|[^\S])(?:'|')(\S.*?\S)(?:'|')([^\S]|$)/g,'$1‚$2‘$3')
    ;
  };

  /**
   * Convert code text node
   * @param {String} string
   * @return {String}
   */
  internal.convertCode = function convertCode(string) {
    return string
      .replace(/(\b)(var|function|method|class|const|external|internal|protected|use|public|private)(\b)/g, '$1$2$3')
      .replace(/(\b)(and|array|break|case|die|do|echo|s?printf?|else(if)?|elsif|final|for(each|Each)?|map|try|catch|then|global|if|include(_once)?|length|list|map|new|or|require(_once)?|return|self|switch|this|throw|while)(\b)/g, '$1$2$3')
      .replace(/([\s|=|;])([\d\.]+)([\s|=|;])/g, '$1$2$3')
      .replace(/([^\\])("|'|')(.*?)("|'|')/g,'$1$2$3$4')
      .replace(/(\b)(null|undefined|true|false)(\b)/gi, '$1$2$3')
      .replace(/((?:\/\/|\s#).+?(?:\n|$))/g, '$1')
      .replace(/(\/\*[\s\S]+?\*\/)/g, '$1')
    ;
  };

  /**
   * Convert code text node
   * @param {String} string
   * @return {String}
   */
  internal.convertCss = function convertCss(string) {
    return string
      .replace(/(\b)(color|background-color|float|text-align|position|display)(\b)/g, '$1$2$3')
      .replace(/(\b)(inherit|top|bottom|left|right|auto|center|middle|block|inline|inline-block|none)(\b)/g, '$1$2$3')
      .replace(/(\b)((?:\.|#)[a-zA-Z0-9_\-]+)(\b)/g, '$1$2$3')
      .replace(/(\b)((?:$)[a-zA-Z0-9_\-]+)(\b)/g, '$1$2$3')
      .replace(/(\b)(@(?:include|if|extend|mixin|function|else|elseif))(\b)/g, '$1$2$3')
      .replace(/([^\\])("|'|')(.*?)("|'|')/g,'$1$2$3$4')
      .replace(/([\d\.]+[a-z]+)/g, '$1$2$3')
      .replace(/(\/\/.+?(?:\n|$))/g, '$1')
      .replace(/(\/\*[\s\S]+?\*\/)/g, '$1')
      ;
  };

  /**
   * Convert code text node
   * @param {String} string
   * @return {String}
   */
  internal.convertHtml = function convertHtml(string) {
    return string
      .replace(/(<\/?)([a-zA-Z0-9]+)/g, '$1$2')
      .replace(/(&(?:#x)?[a-zA-F0-9]+?;)/g, '$1')
      .replace(/(\s)([a-zA-Z0-9_\-]+?)(=")(.+?)(")/g, '$1$2$3$4$5')
      .replace(/(<--.+?-->)/g, '$1')
    ;
  };

  /**
   * Convert Markdown code text node
   * @param {String}
   * @return {String}
   */
  internal.convertMarkdown = function convertMarkdown(string) {
    return string
      .replace(/(^|\n|\r)([=\-]{3,})(\n|\r|$)/g, '$1$2')
      .replace(/(#+.+?(?:\n|$))/g, '$1')
    ;
  };

  return external.convert(string);
};

module.exports = markyMark;
<commit_msg>Replaced emojis after checking compatibility on Win7 and older Android<commit_after>'use strict';

/**
 * Convert HTML in even better HTML.
 * @param {String} string
 * @return {String}
 * @constructor
 */
var markyMark = function markyMark (string) {
  var internal = {};
  var external = {};

  external.chunks = [];
  internal.mode   = '';

  /**
   * Do actual conversion
   * @param {String} string
   * @return {String}
   */
  external.convert = function convert (string) {
    external.chunks = [];
    internal.mode   = '';
    var currentChunk = '';
    for (var i = 0, len = string.length; i < len; i++) {
      var c = string[i];

      if (internal.mode === '<>' || internal.mode === '</>') {
        internal.pushChunk(currentChunk, '');
        currentChunk = '';
      }
      if (c === '<') {
        internal.pushChunk(currentChunk, c);
        currentChunk = '';
      }
      else if (internal.mode === '<' && c === '/') {
        internal.mode += c;
      }
      else if (c === '>') {
        internal.mode += c;
      }
      currentChunk += c;
    }
    internal.pushChunk(currentChunk);
    return external.chunks.join('');
  };

  /**
   * Push a single chunk to the chunk array, but convert chunks by looking at the current mode
   * @param {String} chunk
   * @param {String} mode
   * @return {Boolean}
   */
  internal.pushChunk = function pushChunk (chunk, newMode) {
    newMode = newMode || '';
    if (chunk) {
      switch (internal.mode) {
        case '':
          // Text node
          chunk = internal.convertText(chunk);
          break;
        case '':
          chunk = internal.convertCode(chunk);
          break;
        case '<css>':
          chunk = internal.convertCss(chunk);
          break;
        case '':
          chunk = internal.convertHtml(chunk);
          break;
        case '<markdown>':
          chunk = internal.convertMarkdown(chunk);
          break;
        case '<>':
          if (chunk.match(/<code class/)) {
            var lang = chunk.match(/(css|html|markdown)/);
            if (lang && lang[1]) {
              newMode = '<'+lang[1]+'>';
            }
            else {
              newMode = '';
            }
          }
          else if (chunk === '') {
              newMode = '<no>';
          }
          break;
      }
      external.chunks.push(chunk);
    }
    internal.mode = newMode;
    return newMode;
  };

  /**
   * Convert text node. Will do some pretty clever UTF-8 emoji stunts as well.
   * @see    http://apps.timwhitlock.info/emoji/tables/unicode
   * @param  {String} string
   * @return {String}
   */
  internal.convertText = function convertText(string) {
    var entityMap = {
      '...': '…',
      '… …': '…',
      '(C)': '©',
      '(R)': '®',
      '(TM)': '™',
      '(+-)': '±',
      '(1/4)': '¼',
      '(1/2)': '½',
      '(3/4)': '¾',
      '->': '→',
      '=>': '⇒',
      '<-': '←',
      '<=': '⇐',
    };
    string = string.replace(/(\.\.\.|… …|\(C\)|\(R\)|\(TM\)|\(+-\)|\(1\/4\)|\(1\/2\)|\(3\/4\)|->|=>|<-|<=)/g, function(s) {
      return entityMap[s];
    });

    entityMap = {
      ':)': '😊',
      ':(': '😩',
      ':|': '😐',
      ':/': '😒',
      ':D': '😄',
      ':P': '😋',
      ':O': '😲',
      ':*': '😘',
      ';)': '😉',
      'B)': '😎',
      'XP': '😝'
    };
    string = string.replace(/(\W|^)(:(?:\(|\)|\||\/|D|P|O|\*)|(?:;|B)\)|XP)(\W|$)/g, function(all, before, s, after) {
      return before + entityMap[s] + after;
    });

    return string
      .replace(/\s--\s/g, ' — ')
      .replace(/(\d)\s*-\s*(\d)/g,'$1–$2')
      .replace(/(\s)-(\s)/g,'$1–$2')
      .replace(/(\d\s*)(x|\*)(\s*\d)/g,'$1×$3')
      .replace(/(^|[^\S])"(\S.*?\S)"([^\S]|$)/g,'$1„$2“$3') // TODO: Fix quotation with inline tags
      .replace(/(^|[^\S])(?:'|')(\S.*?\S)(?:'|')([^\S]|$)/g,'$1‚$2‘$3')
    ;
  };

  /**
   * Convert code text node
   * @param {String} string
   * @return {String}
   */
  internal.convertCode = function convertCode(string) {
    return string
      .replace(/(\b)(var|function|method|class|const|external|internal|protected|use|public|private)(\b)/g, '$1$2$3')
      .replace(/(\b)(and|array|break|case|die|do|echo|s?printf?|else(if)?|elsif|final|for(each|Each)?|map|try|catch|then|global|if|include(_once)?|length|list|map|new|or|require(_once)?|return|self|switch|this|throw|while)(\b)/g, '$1$2$3')
      .replace(/([\s|=|;])([\d\.]+)([\s|=|;])/g, '$1$2$3')
      .replace(/([^\\])("|'|')(.*?)("|'|')/g,'$1$2$3$4')
      .replace(/(\b)(null|undefined|true|false)(\b)/gi, '$1$2$3')
      .replace(/((?:\/\/|\s#).+?(?:\n|$))/g, '$1')
      .replace(/(\/\*[\s\S]+?\*\/)/g, '$1')
    ;
  };

  /**
   * Convert code text node
   * @param {String} string
   * @return {String}
   */
  internal.convertCss = function convertCss(string) {
    return string
      .replace(/(\b)(color|background-color|float|text-align|position|display)(\b)/g, '$1$2$3')
      .replace(/(\b)(inherit|top|bottom|left|right|auto|center|middle|block|inline|inline-block|none)(\b)/g, '$1$2$3')
      .replace(/(\b)((?:\.|#)[a-zA-Z0-9_\-]+)(\b)/g, '$1$2$3')
      .replace(/(\b)((?:$)[a-zA-Z0-9_\-]+)(\b)/g, '$1$2$3')
      .replace(/(\b)(@(?:include|if|extend|mixin|function|else|elseif))(\b)/g, '$1$2$3')
      .replace(/([^\\])("|'|')(.*?)("|'|')/g,'$1$2$3$4')
      .replace(/([\d\.]+[a-z]+)/g, '$1$2$3')
      .replace(/(\/\/.+?(?:\n|$))/g, '$1')
      .replace(/(\/\*[\s\S]+?\*\/)/g, '$1')
      ;
  };

  /**
   * Convert code text node
   * @param {String} string
   * @return {String}
   */
  internal.convertHtml = function convertHtml(string) {
    return string
      .replace(/(<\/?)([a-zA-Z0-9]+)/g, '$1$2')
      .replace(/(&(?:#x)?[a-zA-F0-9]+?;)/g, '$1')
      .replace(/(\s)([a-zA-Z0-9_\-]+?)(=")(.+?)(")/g, '$1$2$3$4$5')
      .replace(/(<--.+?-->)/g, '$1')
    ;
  };

  /**
   * Convert Markdown code text node
   * @param {String}
   * @return {String}
   */
  internal.convertMarkdown = function convertMarkdown(string) {
    return string
      .replace(/(^|\n|\r)([=\-]{3,})(\n|\r|$)/g, '$1$2')
      .replace(/(#+.+?(?:\n|$))/g, '$1')
    ;
  };

  return external.convert(string);
};

module.exports = markyMark;
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import {
  StyleSheet,
  Dimensions,
  ScrollView,
  View,
} from 'react-native'
import Mirror, { scrollviewBootstrap } from 'react-native-mirror'
import Page from './Page'

const VIEWPORT_WIDTH = Dimensions.get('window').width

class ViewPager extends PureComponent {

  static defaultProps = {
    thresholdPages: 1,
    pageWidth: VIEWPORT_WIDTH,
    pagingEnabled: true,
    contentContainerStyle: {},
    renderPage: () => {},
    onPageChange: () => {},
    onScroll: () => {},
    scrollEnabled: true,
    data: [],
    experimentalMirroring: false,
    showNativeScrollIndicator: false,
    lazyrender: false,
  }

  static propTypes = {
    contentContainerStyle: React.PropTypes.any,
    containerStyle: React.PropTypes.any,
    data: React.PropTypes.arrayOf(
      React.PropTypes.object
    ),
    thresholdPages: React.PropTypes.number,
    pageWidth: React.PropTypes.number,
    scrollEnabled: React.PropTypes.bool,
    pagingEnabled: React.PropTypes.bool,
    experimentalMirroring: React.PropTypes.bool,
    showNativeScrollIndicator: React.PropTypes.bool,
    lazyrender: React.PropTypes.bool,

    renderPage: React.PropTypes.func,
    onPageChange: React.PropTypes.func,
    onScroll: React.PropTypes.func,
  }

  constructor(props) {
    super(props)

    this._pageWithDelta = (VIEWPORT_WIDTH - this.props.pageWidth) / 2

    this.pageReferences = {}
    this.pageNumberBeforeDrag = 1

    this.state = {
      dataSource: [...this._prepareData(this.props.data || [])],
    }
  }

  componentDidMount() {
    setTimeout(() => {
      this.scrollView.scrollTo({
        x: (VIEWPORT_WIDTH - this._pageWithDelta) * this.props.thresholdPages,
        animated: false,
      })
    }, 0)
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      dataSource: [...this._prepareData(nextProps.data || [])],
    })
  }

  _setPageNumber = (data) => {
    return data.map((_data, index) => {
      return Object.assign({}, _data, {
        _pageNumber: index + 1,
      })
    })
  }

  _setPageIndex = data => {
    return data.map((_data, index) => {
      return Object.assign({}, _data, {
        _pageIndex: index,
      })
    })
  }

  _prepareData = (data) => {

    const multiplicator = data.length > 0 ? Math.ceil(this.props.thresholdPages / data.length) : 0

    const initializedData = this._setPageNumber(data)

    let thresholdDataFront = []
    let thresholdDataEnd = []

    for (let i = 0; i < multiplicator; i++) {
      thresholdDataFront = [...thresholdDataFront, ...[...initializedData].reverse()]
      thresholdDataEnd = [...thresholdDataEnd, ...initializedData]
    }

    const thresholdFront = thresholdDataFront.slice(0, this.props.thresholdPages).reverse()

    const thresholdEnd = thresholdDataEnd.slice(0, this.props.thresholdPages)

    let preparedData = [...thresholdFront, ...initializedData, ...thresholdEnd]
    preparedData = this._setPageIndex(preparedData)

    return [...preparedData]
  }

  _getPageNumberByIndex = index => {
    const roundedIndex = Math.round(index)
    if (roundedIndex === 0) return this.state.dataSource.length - 1
    if (roundedIndex === this.state.dataSource.length - 1) return 1
    return roundedIndex
  }

  _onScroll = (event) => {
    const offsetX = event.nativeEvent.contentOffset.x
    this.props.onScroll(offsetX)

    this.pageIndex = Math.ceil((offsetX / VIEWPORT_WIDTH) * 100) / 100

    if (this.pageIndex % 1 === 0) {
      if (this.pageIndex === 0) {

        this.scrollView.scrollTo({
          animated: false,
          x: VIEWPORT_WIDTH * (this.state.dataSource.length - 2),
        })

      } else if (this.pageIndex === this.state.dataSource.length - 1) {

        this.scrollView.scrollTo({
          animated: false,
          x: VIEWPORT_WIDTH,
        })

      }
    }
  }

  _onScrollBeginDrag = () => {
    const pageNumber = this._getPageNumberByIndex(this.pageIndex)
    this.pageNumberBeforeDrag = pageNumber
  }

  _onMomentumScrollEnd = () => {
    const pageNumber = this._getPageNumberByIndex(this.pageIndex)
    for (const key in this.pageReferences) {
      this.pageReferences[key].onPageChange(pageNumber)
    }
    if (this.pageNumberBeforeDrag !== pageNumber) {
      this.pageNumberBeforeDrag = pageNumber
      this.props.onPageChange(pageNumber)
    }
  }

  _triggerOnMomentumScrollEnd = () => {
    if (this._onMomentumScrollEndTimeout)
      clearTimeout(this._onMomentumScrollEndTimeout)
    this._onMomentumScrollEndTimeout = setTimeout(() => {
      this._onMomentumScrollEnd()
    }, 500)
  }

  /*
   * public methods
   */

  scroll = dx => {
    this.scrollView.scrollTo({
      animated: false,
      x: dx / (VIEWPORT_WIDTH / (VIEWPORT_WIDTH / 2)) - this.props.pageWidth / 2 + (VIEWPORT_WIDTH / this.props.thresholdPages),
    })
  }

  scrollToPage = pageNumber => {
    this._triggerOnMomentumScrollEnd()
    this.scrollView.scrollTo({
      animated: true,
      x: ((pageNumber - 1) + this.props.thresholdPages) * VIEWPORT_WIDTH,
    })
  }

  scrollToIndex = pageIndex => {
    this._triggerOnMomentumScrollEnd()
    this.scrollView.scrollTo({
      animated: true,
      x: pageIndex * VIEWPORT_WIDTH,
    })
  }

  /*
   * render parts
   */

  _renderPage = (item, index) => {

    let row = (
      <View
        key={index}
        style={[styles.rowContainer, {
          width: this.props.pageWidth,
        }]}
      >
        {this.props.renderPage({
          data: item,
          _pageNumber: item._pageNumber,
          _pageIndex: item._pageIndex,
        })}
      </View>
    )

    if (this.props.experimentalMirroring === true) {
      if (index <= this.props.thresholdPages) {
        row = (
          <Mirror
            key={index}
            connectionId={'mirror-' + index}
            containerStyle={styles.mirror}
            experimentalComponentDetection={true}
            mirroredProps={[
              scrollviewBootstrap,
            ]}
          >
            {row}
          </Mirror>
        )
      }

      if (index >= this.state.dataSource.length - this.props.thresholdPages - 1) {
        const idIndex = index - (this.state.dataSource.length - this.props.thresholdPages - 1)
        row = (
          <Mirror
            key={index}
            connectionId={'mirror-' + idIndex}
            containerStyle={styles.mirror}
            experimentalComponentDetection={true}
            mirroredProps={[
              scrollviewBootstrap,
            ]}
          >
            {row}
          </Mirror>
        )
      }
    }

    return row
  }

  render() {

    this.pageReferences = {}

    return (
      <View
        style={this.props.containerStyle}
      >
        <ScrollView
          ref={(scrollView) => {
            this.scrollView = scrollView
          }}
          onScrollBeginDrag={this._onScrollBeginDrag}
          onMomentumScrollEnd={this._onMomentumScrollEnd}
          horizontal={true}
          pagingEnabled={this.props.pagingEnabled}
          scrollEnabled={this.props.scrollEnabled}
          showsHorizontalScrollIndicator={this.props.showNativeScrollIndicator}
          showsVerticalScrollIndicator={this.props.showNativeScrollIndicator}
          onScroll={this._onScroll}
          scrollEventThrottle={1}
          contentContainerStyle={[styles.container, this.props.contentContainerStyle, {
            width: this.props.pageWidth * this.state.dataSource.length,
          }]}>
          {this.state.dataSource.map((item, index) => {
            return (
              <Page
                key={index}
                ref={_page => {
                  this.pageReferences[index] = _page
                }}
                pageNumber={item._pageNumber}
                lazyrender={this.props.lazyrender}
                pageWidth={this.props.pageWidth}
              >
                {this._renderPage(item, index)}
              </Page>
            )
          })}
        </ScrollView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    /*
     * bug in react-native
     * overflow style has to be set
     * https://github.com/facebook/react-native/issues/12926
     */
    overflow: 'scroll',
  },
  rowContainer: {
    flexGrow: 1,
  },
  mirror: {
    flexGrow: 1,
  },
})

export default ViewPager<commit_msg>fixed issue #9 - try to access null reference<commit_after>import React, { PureComponent } from 'react'
import {
  StyleSheet,
  Dimensions,
  ScrollView,
  View,
} from 'react-native'
import Mirror, { scrollviewBootstrap } from 'react-native-mirror'
import Page from './Page'

const VIEWPORT_WIDTH = Dimensions.get('window').width

class ViewPager extends PureComponent {

  static defaultProps = {
    thresholdPages: 1,
    pageWidth: VIEWPORT_WIDTH,
    pagingEnabled: true,
    contentContainerStyle: {},
    renderPage: () => {},
    onPageChange: () => {},
    onScroll: () => {},
    scrollEnabled: true,
    data: [],
    experimentalMirroring: false,
    showNativeScrollIndicator: false,
    lazyrender: false,
  }

  static propTypes = {
    contentContainerStyle: React.PropTypes.any,
    containerStyle: React.PropTypes.any,
    data: React.PropTypes.arrayOf(
      React.PropTypes.object
    ),
    thresholdPages: React.PropTypes.number,
    pageWidth: React.PropTypes.number,
    scrollEnabled: React.PropTypes.bool,
    pagingEnabled: React.PropTypes.bool,
    experimentalMirroring: React.PropTypes.bool,
    showNativeScrollIndicator: React.PropTypes.bool,
    lazyrender: React.PropTypes.bool,

    renderPage: React.PropTypes.func,
    onPageChange: React.PropTypes.func,
    onScroll: React.PropTypes.func,
  }

  constructor(props) {
    super(props)

    this._pageWithDelta = (VIEWPORT_WIDTH - this.props.pageWidth) / 2

    this.pageReferences = {}
    this.pageNumberBeforeDrag = 1

    this.state = {
      dataSource: [...this._prepareData(this.props.data || [])],
    }
  }

  componentDidMount() {
    setTimeout(() => {
      this._scrollTo({
        x: (VIEWPORT_WIDTH - this._pageWithDelta) * this.props.thresholdPages,
        animated: false,
      })
    }, 0)
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      dataSource: [...this._prepareData(nextProps.data || [])],
    })
  }

  _setPageNumber = (data) => {
    return data.map((_data, index) => {
      return Object.assign({}, _data, {
        _pageNumber: index + 1,
      })
    })
  }

  _setPageIndex = data => {
    return data.map((_data, index) => {
      return Object.assign({}, _data, {
        _pageIndex: index,
      })
    })
  }

  _prepareData = (data) => {

    const multiplicator = data.length > 0 ? Math.ceil(this.props.thresholdPages / data.length) : 0

    const initializedData = this._setPageNumber(data)

    let thresholdDataFront = []
    let thresholdDataEnd = []

    for (let i = 0; i < multiplicator; i++) {
      thresholdDataFront = [...thresholdDataFront, ...[...initializedData].reverse()]
      thresholdDataEnd = [...thresholdDataEnd, ...initializedData]
    }

    const thresholdFront = thresholdDataFront.slice(0, this.props.thresholdPages).reverse()

    const thresholdEnd = thresholdDataEnd.slice(0, this.props.thresholdPages)

    let preparedData = [...thresholdFront, ...initializedData, ...thresholdEnd]
    preparedData = this._setPageIndex(preparedData)

    return [...preparedData]
  }

  _getPageNumberByIndex = index => {
    const roundedIndex = Math.round(index)
    if (roundedIndex === 0) return this.state.dataSource.length - 1
    if (roundedIndex === this.state.dataSource.length - 1) return 1
    return roundedIndex
  }

  _scrollTo = (options) => {
    if (this.scrollView) {
      this.scrollView.scrollTo(options)
    }
  }

  _onScroll = (event) => {
    const offsetX = event.nativeEvent.contentOffset.x
    this.props.onScroll(offsetX)

    this.pageIndex = Math.ceil((offsetX / VIEWPORT_WIDTH) * 100) / 100

    if (this.pageIndex % 1 === 0) {
      if (this.pageIndex === 0) {

        this._scrollTo({
          animated: false,
          x: VIEWPORT_WIDTH * (this.state.dataSource.length - 2),
        })

      } else if (this.pageIndex === this.state.dataSource.length - 1) {

        this._scrollTo({
          animated: false,
          x: VIEWPORT_WIDTH,
        })

      }
    }
  }

  _onScrollBeginDrag = () => {
    const pageNumber = this._getPageNumberByIndex(this.pageIndex)
    this.pageNumberBeforeDrag = pageNumber
  }

  _onMomentumScrollEnd = () => {
    const pageNumber = this._getPageNumberByIndex(this.pageIndex)
    for (const key in this.pageReferences) {
      this.pageReferences[key].onPageChange(pageNumber)
    }
    if (this.pageNumberBeforeDrag !== pageNumber) {
      this.pageNumberBeforeDrag = pageNumber
      this.props.onPageChange(pageNumber)
    }
  }

  _triggerOnMomentumScrollEnd = () => {
    if (this._onMomentumScrollEndTimeout)
      clearTimeout(this._onMomentumScrollEndTimeout)
    this._onMomentumScrollEndTimeout = setTimeout(() => {
      this._onMomentumScrollEnd()
    }, 500)
  }

  /*
   * public methods
   */

  scroll = dx => {
    this._scrollTo({
      animated: false,
      x: dx / (VIEWPORT_WIDTH / (VIEWPORT_WIDTH / 2)) - this.props.pageWidth / 2 + (VIEWPORT_WIDTH / this.props.thresholdPages),
    })
  }

  scrollToPage = pageNumber => {
    this._triggerOnMomentumScrollEnd()
    this._scrollTo({
      animated: true,
      x: ((pageNumber - 1) + this.props.thresholdPages) * VIEWPORT_WIDTH,
    })
  }

  scrollToIndex = pageIndex => {
    this._triggerOnMomentumScrollEnd()
    this._scrollTo({
      animated: true,
      x: pageIndex * VIEWPORT_WIDTH,
    })
  }

  /*
   * render parts
   */

  _renderPage = (item, index) => {

    let row = (
      <View
        key={index}
        style={[styles.rowContainer, {
          width: this.props.pageWidth,
        }]}
      >
        {this.props.renderPage({
          data: item,
          _pageNumber: item._pageNumber,
          _pageIndex: item._pageIndex,
        })}
      </View>
    )

    if (this.props.experimentalMirroring === true) {
      if (index <= this.props.thresholdPages) {
        row = (
          <Mirror
            key={index}
            connectionId={'mirror-' + index}
            containerStyle={styles.mirror}
            experimentalComponentDetection={true}
            mirroredProps={[
              scrollviewBootstrap,
            ]}
          >
            {row}
          </Mirror>
        )
      }

      if (index >= this.state.dataSource.length - this.props.thresholdPages - 1) {
        const idIndex = index - (this.state.dataSource.length - this.props.thresholdPages - 1)
        row = (
          <Mirror
            key={index}
            connectionId={'mirror-' + idIndex}
            containerStyle={styles.mirror}
            experimentalComponentDetection={true}
            mirroredProps={[
              scrollviewBootstrap,
            ]}
          >
            {row}
          </Mirror>
        )
      }
    }

    return row
  }

  render() {

    this.pageReferences = {}

    return (
      <View
        style={this.props.containerStyle}
      >
        <ScrollView
          ref={(scrollView) => {
            this.scrollView = scrollView
          }}
          onScrollBeginDrag={this._onScrollBeginDrag}
          onMomentumScrollEnd={this._onMomentumScrollEnd}
          horizontal={true}
          pagingEnabled={this.props.pagingEnabled}
          scrollEnabled={this.props.scrollEnabled}
          showsHorizontalScrollIndicator={this.props.showNativeScrollIndicator}
          showsVerticalScrollIndicator={this.props.showNativeScrollIndicator}
          onScroll={this._onScroll}
          scrollEventThrottle={1}
          contentContainerStyle={[styles.container, this.props.contentContainerStyle, {
            width: this.props.pageWidth * this.state.dataSource.length,
          }]}>
          {this.state.dataSource.map((item, index) => {
            return (
              <Page
                key={index}
                ref={_page => {
                  this.pageReferences[index] = _page
                }}
                pageNumber={item._pageNumber}
                lazyrender={this.props.lazyrender}
                pageWidth={this.props.pageWidth}
              >
                {this._renderPage(item, index)}
              </Page>
            )
          })}
        </ScrollView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    /*
     * bug in react-native
     * overflow style has to be set
     * https://github.com/facebook/react-native/issues/12926
     */
    overflow: 'scroll',
  },
  rowContainer: {
    flexGrow: 1,
  },
  mirror: {
    flexGrow: 1,
  },
})

export default ViewPager<|endoftext|>


<commit_before>/**
 * Interact with local and remote project repos using Git.
 */

"use strict";

var git = require("nodegit");
var path = require("path");
var url = require("url");
var _ = require('underscore');
var Q = require('q');
var path = require('path');
var fs = require("q-io/fs"); // https://github.com/kriskowal/q-io

// Mac OSX certificate issue http://www.nodegit.org/guides/cloning/
var copingWithMacCertBug = {
    certificateCheck: function () {
        return 1;
    }
};

/**
 * Parse the tag and branch names out of the refs list
 * and generate an object of data to return.
 */
function parseTagsAndBranches(refs, projectRef) {
    return refs
        .map(function (ref) {
            var shortName = ref.shorthand();
            var safeName = encodeURIComponent(ref.name());
            return {
                name: shortName,
                tag: ref.isTag(),
                branch: ref.isBranch(),
                urlEncodedName: safeName,
                current: ref.name() === projectRef.name()
            }
        });
}

// Wrap up the supplied repository metadata in an object.
// TODO: this model should not be generated here.
function generateRepoMetaData(projectData, sha, tagsAndBranches, currentBranchName, files, remoteUrl) {
    var updatedData = {
        // Copied
        repoName: projectData.repoName,
        localPath: projectData.localPath,
        projectLink: projectData.projectLink,
        // New
        commit: sha,
        shortCommit: sha.substring(0, 7),
        tagsAndBranches: tagsAndBranches,
        currentBranchName: currentBranchName,
        currentBranchNameShort: currentBranchName,
        featureFilePaths: files,
        repoUrl: remoteUrl,
    };

    // Filter falsey values and empty arrays.
    function findFalsey(data) {
      return function(key) {
        var value = data[key];
        return !data[key] || (value.length !== undefined ? value.length === 0 : false);
      }
    }

    var missingValues = Object.keys(updatedData).filter(findFalsey(updatedData));
    if (missingValues.length) {
        console.warn("Missing values:");
        console.warn(missingValues);
    }

    return updatedData;
}

/**
 * Clone the project.
 *
 * @return a promise for the repo metadata.
 */
function clone(projectData, cloneOptions) {
    var repository;
    if (!projectData.repoUrl) {
      throw new TypeError('Clone called without a repo URL.');
    }

    return git.Clone(projectData.repoUrl, projectData.localPath, cloneOptions)
        .then(function (repo) {
            repository = repo;
            return repository.getCurrentBranch();
        })
        .then(function (branch) {
            return getRefInformation(projectData, branch.shorthand());
        });
}

/**
 * Perform a fetch on the project.
 *
 * @return a promise for the repo metadata.
 */
function update(projectData) {
    var repository;

    return git.Repository.openBare(projectData.localPath)
        .then(function (repo) {
            repository = repo;
            return repository.fetchAll(copingWithMacCertBug, true);
        })
        .then(function () {
            return getRefInformation(projectData, projectData.currentBranchName);
        });
}

/**
 * Get or update the project repo using Git.
 *
 * @return a promise for the repo metadata.
 */
function getProject(projectData) {
    return fs.exists(projectData.localPath)
        .then(function (pathExists) {

            // Need to define this here as the Clone function modifies the passed object (face palm).
            var cloneOptions = {};
            cloneOptions.bare = 1;
            cloneOptions.remoteCallbacks = copingWithMacCertBug;

            /**
             * If there is no matching directory then clone the repo
             * else perform a pull on it.
             */
            if (!pathExists) {
                return clone(projectData, cloneOptions);
            } else {
                return update(projectData);
            }
        });
};

function getBranch(projectData, branchName) {
    return git.Repository.open(projectData.localPath)
        .then(function(repo) {
            if(branchName) {
                return repo.getBranch(branchName);
            }
            return repo.getCurrentBranch();
        });
}

function getRefInformation(projectData, targetBranchName) {
    // The targetBranchName is the full remote branch ref.
    // The shortBranchName is what the local branch would be called.
    var currentRef;
    var shortBranchName;

    var repository;
    var remoteUrl;
    var headCommit;
    var listOfFiles;

    return getBranch(projectData, targetBranchName)
        .then(function(ref) {
            currentRef = ref;
            shortBranchName = currentRef.shorthand()
            return git.Repository.open(projectData.localPath)
        })
        .then(function (repo) {
            repository = repo;
            return repository.getRemote('origin');
        }).then(function (remote) {
            remoteUrl = remote.url();
            return repository.getReferenceCommit(currentRef.name());
        })
        .then(function (commit) {
            headCommit = commit.sha();
            return commit.getTree();
        })
        .then(getAllFiles)
        .then(function(files) {
            listOfFiles = files.map(function(fileOfInterestName) {
                return {
                    ref: currentRef.name(),
                    featureRoute: url.resolve('project/', projectData.repoName + "/" + fileOfInterestName),
                    featureName: fileOfInterestName,
                    featureNameShort: fileOfInterestName.replace(/\.feature$/, '') }
            });
            return repository.getReferences(git.Reference.TYPE.OID);
        })
        .then(function (refs) {
            var tagsAndBranches = parseTagsAndBranches(refs, currentRef);
            return generateRepoMetaData(projectData, headCommit, tagsAndBranches, currentRef.name(), listOfFiles, remoteUrl);
        });
}

function getFileContents(projectData, filePath) {
    var repository;

    return git.Repository.open(projectData.localPath)
        .then(function (repo) {
            repository = repo;
            return repository.getReferenceCommit(projectData.currentBranchName);
        })
        .then(function(commit) {
            return commit.getTree()
        })
        .then(function(tree) {
            return tree.getEntry(filePath)
        })
        .then(function(entry) {
            return entry.getBlob();
        })
        .then(function(blob) {
            return blob.toString();
        });
}

function _getAllFiles(tree) {
    var files = [];

    tree.entries().forEach(function (e) {
        if (e.isFile())
            files.push(e.path());

        if (e.isTree())
            files.push(e.getTree().then(function (t) {
                return _getAllFiles(t);
            }));
    });

    return Q.all(files)
        .then(function(files) {
            return _.flatten(files);
        });
}

function getAllFiles(tree) {
    return _getAllFiles(tree)
        .then(function(files) {
            return files.filter(function(f) {
                return /\.(feature|md)/.test(f);
            });
        });
}

module.exports = {
    getProject: getProject,
    getRefInformation: getRefInformation,
    getFileContents: getFileContents
};
<commit_msg>Changing indentiation<commit_after>/**
 * Interact with local and remote project repos using Git.
 */

"use strict";

var git = require("nodegit");
var path = require("path");
var url = require("url");
var _ = require('underscore');
var Q = require('q');
var path = require('path');
var fs = require("q-io/fs"); // https://github.com/kriskowal/q-io

// Mac OSX certificate issue http://www.nodegit.org/guides/cloning/
var copingWithMacCertBug = {
  certificateCheck: function () {
    return 1;
  }
};

/**
 * Parse the tag and branch names out of the refs list
 * and generate an object of data to return.
 */
function parseTagsAndBranches(refs, projectRef) {
  return refs
    .map(function (ref) {
      var shortName = ref.shorthand();
      var safeName = encodeURIComponent(ref.name());
      return {
        name: shortName,
        tag: ref.isTag(),
        branch: ref.isBranch(),
        urlEncodedName: safeName,
        current: ref.name() === projectRef.name()
      }
    });
}

// Wrap up the supplied repository metadata in an object.
// TODO: this model should not be generated here.
function generateRepoMetaData(projectData, sha, tagsAndBranches, currentBranchName, files, remoteUrl) {
  var updatedData = {
    // Copied
    repoName: projectData.repoName,
    localPath: projectData.localPath,
    projectLink: projectData.projectLink,
    // New
    commit: sha,
    shortCommit: sha.substring(0, 7),
    tagsAndBranches: tagsAndBranches,
    currentBranchName: currentBranchName,
    currentBranchNameShort: currentBranchName,
    featureFilePaths: files,
    repoUrl: remoteUrl,
  };

  // Filter falsey values and empty arrays.
  function findFalsey(data) {
    return function (key) {
      var value = data[key];
      return !data[key] || (value.length !== undefined ? value.length === 0 : false);
    }
  }

  var missingValues = Object.keys(updatedData).filter(findFalsey(updatedData));
  if (missingValues.length) {
    console.warn("Missing values:");
    console.warn(missingValues);
  }

  return updatedData;
}

/**
 * Clone the project.
 *
 * @return a promise for the repo metadata.
 */
function clone(projectData, cloneOptions) {
  var repository;
  if (!projectData.repoUrl) {
    throw new TypeError('Clone called without a repo URL.');
  }

  return git.Clone(projectData.repoUrl, projectData.localPath, cloneOptions)
    .then(function (repo) {
      repository = repo;
      return repository.getCurrentBranch();
    })
    .then(function (branch) {
      return getRefInformation(projectData, branch.shorthand());
    });
}

/**
 * Perform a fetch on the project.
 *
 * @return a promise for the repo metadata.
 */
function update(projectData) {
  var repository;

  return git.Repository.openBare(projectData.localPath)
    .then(function (repo) {
      repository = repo;
      return repository.fetchAll(copingWithMacCertBug, true);
    })
    .then(function () {
      return getRefInformation(projectData, projectData.currentBranchName);
    });
}

/**
 * Get or update the project repo using Git.
 *
 * @return a promise for the repo metadata.
 */
function getProject(projectData) {
  return fs.exists(projectData.localPath)
    .then(function (pathExists) {

      // Need to define this here as the Clone function modifies the passed object (face palm).
      var cloneOptions = {};
      cloneOptions.bare = 1;
      cloneOptions.remoteCallbacks = copingWithMacCertBug;

      /**
       * If there is no matching directory then clone the repo
       * else perform a pull on it.
       */
      if (!pathExists) {
        return clone(projectData, cloneOptions);
      } else {
        return update(projectData);
      }
    });
};

function getBranch(projectData, branchName) {
  return git.Repository.open(projectData.localPath)
    .then(function (repo) {
      if (branchName) {
        return repo.getBranch(branchName);
      }
      return repo.getCurrentBranch();
    });
}

function getRefInformation(projectData, targetBranchName) {
  // The targetBranchName is the full remote branch ref.
  // The shortBranchName is what the local branch would be called.
  var currentRef;
  var shortBranchName;

  var repository;
  var remoteUrl;
  var headCommit;
  var listOfFiles;

  return getBranch(projectData, targetBranchName)
    .then(function (ref) {
      currentRef = ref;
      shortBranchName = currentRef.shorthand()
      return git.Repository.open(projectData.localPath)
    })
    .then(function (repo) {
      repository = repo;
      return repository.getRemote('origin');
    }).then(function (remote) {
      remoteUrl = remote.url();
      return repository.getReferenceCommit(currentRef.name());
    })
    .then(function (commit) {
      headCommit = commit.sha();
      return commit.getTree();
    })
    .then(getAllFiles)
    .then(function (files) {
      listOfFiles = files.map(function (fileOfInterestName) {
        return {
          ref: currentRef.name(),
          featureRoute: url.resolve('project/', projectData.repoName + "/" + fileOfInterestName),
          featureName: fileOfInterestName,
          featureNameShort: fileOfInterestName.replace(/\.feature$/, '')
        }
      });
      return repository.getReferences(git.Reference.TYPE.OID);
    })
    .then(function (refs) {
      var tagsAndBranches = parseTagsAndBranches(refs, currentRef);
      return generateRepoMetaData(projectData, headCommit, tagsAndBranches, currentRef.name(), listOfFiles, remoteUrl);
    });
}

function getFileContents(projectData, filePath) {
  var repository;

  return git.Repository.open(projectData.localPath)
    .then(function (repo) {
      repository = repo;
      return repository.getReferenceCommit(projectData.currentBranchName);
    })
    .then(function (commit) {
      return commit.getTree()
    })
    .then(function (tree) {
      return tree.getEntry(filePath)
    })
    .then(function (entry) {
      return entry.getBlob();
    })
    .then(function (blob) {
      return blob.toString();
    });
}

function _getAllFiles(tree) {
  var files = [];

  tree.entries().forEach(function (e) {
    if (e.isFile())
      files.push(e.path());

    if (e.isTree())
      files.push(e.getTree().then(function (t) {
        return _getAllFiles(t);
      }));
  });

  return Q.all(files)
    .then(function (files) {
      return _.flatten(files);
    });
}

function getAllFiles(tree) {
  return _getAllFiles(tree)
    .then(function (files) {
      return files.filter(function (f) {
        return /\.(feature|md)/.test(f);
      });
    });
}

module.exports = {
  getProject: getProject,
  getRefInformation: getRefInformation,
  getFileContents: getFileContents
};
<|endoftext|>


<commit_before>(function() {

	exports.getRandomID = function() {
		return String(Math.floor(Math.random() * 98765862));
	}

	exports.getSkinsDir = function() {
		Titanium.Filesystem.getFile(Titanium.Filesystem.getApplicationDataDirectory() + 'skins/').createDirectory();
		return Titanium.Filesystem.getApplicationDataDirectory() + 'skins/';
	}

	exports.getBGImage = function() {
		if(Ti.Platform.getOsname() === 'iphone') {
			return '/img/bg.png';
		} else if(Ti.Platform.getOsname() === 'ipad') {
			return '/img/block_stonebrick.png';
		}
	}

	exports.getNavColor = function() {
		return '#888888';
	}

	exports.isiPhone() {
		return Ti.Platform.getOsname() === 'iphone';
	}

	exports.isiPad() {
		return Ti.Platform.getOsname() === 'ipad';
	}

	exports.createLoadingWindow = function() {
		var win = Ti.UI.createWindow({
			width: 320,
			height: 480
		});

		var view = Ti.UI.createView({
			height: 60,
			width: 60,
			borderRadius: 10,
			backgroundColor: 'black',
			opacity: 0.6
		});

		win.add(view);

		var spinWheel = Ti.UI.createActivityIndicator({
			style: Ti.UI.iPhone.ActivityIndicatorStyle.BIG
		});

		view.add(spinWheel);
		spinWheel.show();

		return win;
	}

	exports.getTextFieldRow = function(text, animate) {
		var row = Ti.UI.createTableViewRow({
			title: text,
			selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
		});

		var textfield = Ti.UI.createTextField({
			color: '#336699',
			height: 30,
			right: 5,
			width: I('addProcess.skinInfo.fieldWidth'),
			borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE,
			returnKeyType: Ti.UI.RETURNKEY_NEXT,
			clearButtonMode: Ti.UI.INPUT_BUTTONMODE_ONFOCUS
		});

		if(animate) {
			textfield.addEventListener('focus', function() {
				view.animate({
					bottom: 90,
					duration: 300
				});
			});

			textfield.addEventListener('blur', function() {
				view.animate({
					bottom: 0,
					duration: 300
				});
			});
		}

		row.add(textfield);
		return row;
	}

	exports.getTextAreaRow = function(text, animate) {
		var row = Ti.UI.createTableViewRow({
			title: text,
			selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
			height: 100
		});

		var textarea = Ti.UI.createTextArea({
			color: '#336699',
			height: Ti.UI.FILL,
			right: 5,
			width: I('addProcess.skinInfo.fieldWidth'),
			borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE,
			returnKeyType: Ti.UI.RETURNKEY_NEXT,
			clearButtonMode: Ti.UI.INPUT_BUTTONMODE_ONFOCUS,
			font: {
				fontSize: 15
			}
		});

		if(animate) {
			textarea.addEventListener('focus', function() {
				view.animate({
					bottom: 90,
					duration: 300
				});
			});

			textarea.addEventListener('blur', function() {
				view.animate({
					bottom: 0,
					duration: 300
				});
			});
		}

		row.add(textarea);
		return row;
	}

})();
<commit_msg>Fix parse error<commit_after>(function() {

	exports.getRandomID = function() {
		return String(Math.floor(Math.random() * 98765862));
	}

	exports.getSkinsDir = function() {
		Titanium.Filesystem.getFile(Titanium.Filesystem.getApplicationDataDirectory() + 'skins/').createDirectory();
		return Titanium.Filesystem.getApplicationDataDirectory() + 'skins/';
	}

	exports.getBGImage = function() {
		if(Ti.Platform.getOsname() === 'iphone') {
			return '/img/bg.png';
		} else if(Ti.Platform.getOsname() === 'ipad') {
			return '/img/block_stonebrick.png';
		}
	}

	exports.getNavColor = function() {
		return '#888888';
	}

	exports.isiPhone = function() {
		return Ti.Platform.getOsname() === 'iphone';
	}

	exports.isiPad = function() {
		return Ti.Platform.getOsname() === 'ipad';
	}

	exports.createLoadingWindow = function() {
		var win = Ti.UI.createWindow({
			width: 320,
			height: 480
		});

		var view = Ti.UI.createView({
			height: 60,
			width: 60,
			borderRadius: 10,
			backgroundColor: 'black',
			opacity: 0.6
		});

		win.add(view);

		var spinWheel = Ti.UI.createActivityIndicator({
			style: Ti.UI.iPhone.ActivityIndicatorStyle.BIG
		});

		view.add(spinWheel);
		spinWheel.show();

		return win;
	}

	exports.getTextFieldRow = function(text, animate) {
		var row = Ti.UI.createTableViewRow({
			title: text,
			selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE
		});

		var textfield = Ti.UI.createTextField({
			color: '#336699',
			height: 30,
			right: 5,
			width: I('addProcess.skinInfo.fieldWidth'),
			borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE,
			returnKeyType: Ti.UI.RETURNKEY_NEXT,
			clearButtonMode: Ti.UI.INPUT_BUTTONMODE_ONFOCUS
		});

		if(animate) {
			textfield.addEventListener('focus', function() {
				view.animate({
					bottom: 90,
					duration: 300
				});
			});

			textfield.addEventListener('blur', function() {
				view.animate({
					bottom: 0,
					duration: 300
				});
			});
		}

		row.add(textfield);
		return row;
	}

	exports.getTextAreaRow = function(text, animate) {
		var row = Ti.UI.createTableViewRow({
			title: text,
			selectionStyle: Ti.UI.iPhone.TableViewCellSelectionStyle.NONE,
			height: 100
		});

		var textarea = Ti.UI.createTextArea({
			color: '#336699',
			height: Ti.UI.FILL,
			right: 5,
			width: I('addProcess.skinInfo.fieldWidth'),
			borderStyle: Ti.UI.INPUT_BORDERSTYLE_NONE,
			returnKeyType: Ti.UI.RETURNKEY_NEXT,
			clearButtonMode: Ti.UI.INPUT_BUTTONMODE_ONFOCUS,
			font: {
				fontSize: 15
			}
		});

		if(animate) {
			textarea.addEventListener('focus', function() {
				view.animate({
					bottom: 90,
					duration: 300
				});
			});

			textarea.addEventListener('blur', function() {
				view.animate({
					bottom: 0,
					duration: 300
				});
			});
		}

		row.add(textarea);
		return row;
	}

})();
<|endoftext|>


<commit_before>/*
 * patron - The cleanest command framework for discord.js and eris.
 * Copyright (C) 2019  patron contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
"use strict";
const fs = require("fs");
const path = require("path");
const util = require("util");

const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);

/**
 * A function which returns all exports in a folder. This is only available when using CommonJS modules.
 * @param {String} directory The file path to a directory.
 * @returns {Promise<Array<*>>} An array of all the exports.
 */
async function RequireAll(dir) {
  const files = await readdir(dir);
  const modules = [];

  for(const file of files) {
    const loc = path.join(dir, file);

    if((await lstat(path.join(dir, file))).isDirectory() && !file.startsWith("."))
      modules.push(...await RequireAll(loc));
    else if(file.endsWith(".js"))
      modules.push(require(loc));
  }

  return modules;
}

/**
 * A function which returns all exports in a folder. This is only available when using CommonJS modules.
 * @param {String} directory The file path to a directory.
 * @returns {Array<*>} An array of all the exports.
 */
const RequireAllSync = function(dir) {
  const files = fs.readdirSync(dir);
  const modules = [];

  for(const file of files) {
    const loc = path.join(dir, file);

    if(fs.lstatSync(path.join(dir, file)).isDirectory() && !file.startsWith("."))
      modules.push(RequireAllSync(loc));
    else if(file.endsWith(".js"))
      modules.push(require(loc));
  }

  return modules;
};

module.exports = {RequireAll, RequireAllSync};
<commit_msg>Fix RequireAllSync<commit_after>/*
 * patron - The cleanest command framework for discord.js and eris.
 * Copyright (C) 2019  patron contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
"use strict";
const fs = require("fs");
const path = require("path");
const util = require("util");

const readdir = util.promisify(fs.readdir);
const lstat = util.promisify(fs.lstat);

/**
 * A function which returns all exports in a folder. This is only available when using CommonJS modules.
 * @param {String} directory The file path to a directory.
 * @returns {Promise<Array<*>>} An array of all the exports.
 */
async function RequireAll(dir) {
  const files = await readdir(dir);
  const modules = [];

  for(const file of files) {
    const loc = path.join(dir, file);

    if((await lstat(path.join(dir, file))).isDirectory() && !file.startsWith("."))
      modules.push(...await RequireAll(loc));
    else if(file.endsWith(".js"))
      modules.push(require(loc));
  }

  return modules;
}

/**
 * A function which returns all exports in a folder. This is only available when using CommonJS modules.
 * @param {String} directory The file path to a directory.
 * @returns {Array<*>} An array of all the exports.
 */
const RequireAllSync = function(dir) {
  const files = fs.readdirSync(dir);
  const modules = [];

  for(const file of files) {
    const loc = path.join(dir, file);

    if(fs.lstatSync(path.join(dir, file)).isDirectory() && !file.startsWith("."))
      modules.push(...RequireAllSync(loc));
    else if(file.endsWith(".js"))
      modules.push(require(loc));
  }

  return modules;
};

module.exports = {RequireAll, RequireAllSync};
<|endoftext|>