<commit_before>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
//  Phaser.Sound.WebAudioSound
var WebAudioSound = new Class({
    Extends: BaseSound,
    initialize: function WebAudioSound(manager, key, config) {
        /**
         * [description]
         *
         * @property {AudioBuffer} audioBuffer
         */
        this.audioBuffer = manager.game.cache.audio.get(key);
        if (!this.audioBuffer) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * [description]
         *
         * @property {AudioBufferSourceNode} source
         */
        this.source = null;
        /**
         * [description]
         *
         * @property {GainNode} muteNode
         */
        this.muteNode = manager.context.createGain();
        /**
         * [description]
         *
         * @property {GainNode} volumeNode
         */
        this.volumeNode = manager.context.createGain();
        this.muteNode.connect(this.volumeNode);
        this.volumeNode.connect(manager.destination);
        if (config === void 0) {
            config = {};
        }
        config.duration = this.audioBuffer.duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (marker, config) {
        if (!BaseSound.prototype.play.call(this, marker, config)) {
            return null;
        }
        this.source = this.manager.context.createBufferSource();
        this.source.buffer = this.audioBuffer;
        this.source.connect(this.muteNode);
        this.applyConfig();
        this.source.start();
        return this;
    },
    pause: function () {
        return this;
    },
    resume: function () {
        return this;
    },
    stop: function () {
        return this;
    },
    update: function () {
    },
    destroy: function () {
    }
});
/**
 * Mute setting.
 * @property {boolean} mute
 */
Object.defineProperty(WebAudioSound.prototype, 'mute', {
    get: function () {
        return this.muteNode.gain.value === 0;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        this.muteNode.gain.value = value ? 0 : 1;
    }
});
/**
 * Volume setting.
 * @property {number} volume
 */
Object.defineProperty(WebAudioSound.prototype, 'volume', {
    get: function () {
        return this.volumeNode.gain.value;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        this.volumeNode.gain.value = value;
    }
});
/**
 * Playback rate.
 * @property {number} rate
 */
Object.defineProperty(WebAudioSound.prototype, 'rate', {
    get: function () {
        return this.currentConfig.rate;
    },
    set: function (value) {
        this.currentConfig.rate = value;
        if (this.source) {
            this.source.playbackRate.value = value * this.manager.rate;
        }
    }
});
/**
 * Detuning of sound.
 * @property {number} detune
 */
Object.defineProperty(WebAudioSound.prototype, 'detune', {
    get: function () {
        return this.currentConfig.detune;
    },
    set: function (value) {
        this.currentConfig.detune = value;
        if (this.source) {
            this.source.detune.value =
                Math.max(-1200, Math.min(value + this.manager.detune, 1200));
        }
    }
});
module.exports = WebAudioSound;
<commit_msg>added startTime field to WebAudioSound class<commit_after>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
//  Phaser.Sound.WebAudioSound
var WebAudioSound = new Class({
    Extends: BaseSound,
    initialize: function WebAudioSound(manager, key, config) {
        /**
         * [description]
         *
         * @property {AudioBuffer} audioBuffer
         */
        this.audioBuffer = manager.game.cache.audio.get(key);
        if (!this.audioBuffer) {
            console.error('No audio loaded in cache with key: \'' + key + '\'!');
            return;
        }
        /**
         * [description]
         *
         * @property {AudioBufferSourceNode} source
         */
        this.source = null;
        /**
         * [description]
         *
         * @property {GainNode} muteNode
         */
        this.muteNode = manager.context.createGain();
        /**
         * [description]
         *
         * @property {GainNode} volumeNode
         */
        this.volumeNode = manager.context.createGain();
        /**
         * The time the previous playback started at based on
         * BaseAudioContext.currentTime value.
         *
         * @property {number} startTime
         */
        this.startTime = 0;
        this.muteNode.connect(this.volumeNode);
        this.volumeNode.connect(manager.destination);
        if (config === void 0) {
            config = {};
        }
        config.duration = this.audioBuffer.duration;
        BaseSound.call(this, manager, key, config);
    },
    play: function (marker, config) {
        if (!BaseSound.prototype.play.call(this, marker, config)) {
            return null;
        }
        this.source = this.manager.context.createBufferSource();
        this.source.buffer = this.audioBuffer;
        this.source.connect(this.muteNode);
        this.applyConfig();
        this.source.start();
        return this;
    },
    pause: function () {
        return this;
    },
    resume: function () {
        return this;
    },
    stop: function () {
        return this;
    },
    update: function () {
    },
    destroy: function () {
    }
});
/**
 * Mute setting.
 * @property {boolean} mute
 */
Object.defineProperty(WebAudioSound.prototype, 'mute', {
    get: function () {
        return this.muteNode.gain.value === 0;
    },
    set: function (value) {
        this.currentConfig.mute = value;
        this.muteNode.gain.value = value ? 0 : 1;
    }
});
/**
 * Volume setting.
 * @property {number} volume
 */
Object.defineProperty(WebAudioSound.prototype, 'volume', {
    get: function () {
        return this.volumeNode.gain.value;
    },
    set: function (value) {
        this.currentConfig.volume = value;
        this.volumeNode.gain.value = value;
    }
});
/**
 * Playback rate.
 * @property {number} rate
 */
Object.defineProperty(WebAudioSound.prototype, 'rate', {
    get: function () {
        return this.currentConfig.rate;
    },
    set: function (value) {
        this.currentConfig.rate = value;
        if (this.source) {
            this.source.playbackRate.value = value * this.manager.rate;
        }
    }
});
/**
 * Detuning of sound.
 * @property {number} detune
 */
Object.defineProperty(WebAudioSound.prototype, 'detune', {
    get: function () {
        return this.currentConfig.detune;
    },
    set: function (value) {
        this.currentConfig.detune = value;
        if (this.source) {
            this.source.detune.value =
                Math.max(-1200, Math.min(value + this.manager.detune, 1200));
        }
    }
});
module.exports = WebAudioSound;
<|endoftext|>


<commit_before>/**
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

"use strict";
var Tinkerforge = require('tinkerforge');
var devices = require('../lib/devices');

module.exports = function(RED) {

	function tinkerForgeRemoteSwitchB(n) {
        RED.nodes.createNode(this,n);
        this.device = n.device;
        this.sensor = n.sensor;
        this.name = n.name;
	this.address = n.address;
	this.unit = n.unit;
        this.topic = n.topic;
        var node = this;

        node.ipcon = new Tinkerforge.IPConnection(); //devices[this.device].ipcon;
        node.ipcon.setAutoReconnect(true);
        var devs = devices.getDevices();
        node.ipcon.connect(devs[node.device].host, devs[node.device].port,function(error){
            if(error) {
                node.warn("couldn't connect");
            }
        });

        node.ipcon.on(Tinkerforge.IPConnection.CALLBACK_CONNECTED,
        function(connectReason) {
            node.md = new Tinkerforge.BrickletRemoteSwitch(node.sensor, node.ipcon);
        });

	node.on('input', function(msg){
	    if(node.ao) {
	      if (typeof msg.payload === 'number') {
	        if (msg.payload == 1) {
		  node.md.switchSocketB(node.md.address, node.md.unit, Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_ON);
		}
		else {
	          node.md.switchSocketB(node.md.address, node.md.unit, Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_OFF);
 		}
	      }
	    }
        });

        node.on('close',function() {
            node.ipcon.disconnect();
        });
    }

    RED.nodes.registerType('RemoteSwitch Type B', tinkerForgeRemoteSwitchB);
}<commit_msg>Update RemoteSwitchB.js<commit_after>/**
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

"use strict";
var Tinkerforge = require('tinkerforge');
var devices = require('../lib/devices');

module.exports = function(RED) {

	function tinkerForgeRemoteSwitchB(n) {
        RED.nodes.createNode(this,n);
        this.device = n.device;
        this.sensor = n.sensor;
        this.name = n.name;
	this.address = n.address;
	this.unit = n.unit;
        this.topic = n.topic;
        var node = this;

        node.ipcon = new Tinkerforge.IPConnection(); //devices[this.device].ipcon;
        node.ipcon.setAutoReconnect(true);
        var devs = devices.getDevices();
        node.ipcon.connect(devs[node.device].host, devs[node.device].port,function(error){
            if(error) {
                node.warn("couldn't connect");
            }
        });

        node.ipcon.on(Tinkerforge.IPConnection.CALLBACK_CONNECTED,
        function(connectReason) {
            node.md = new Tinkerforge.BrickletRemoteSwitch(node.sensor, node.ipcon);
        });

	node.on('input', function(msg){
	    if(node.md) {
	      if (typeof msg.payload === 'number') {
	        if (msg.payload == 1) {
		  node.md.switchSocketB(node.md.address, node.md.unit, Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_ON);
		}
		else {
	          node.md.switchSocketB(node.md.address, node.md.unit, Tinkerforge.BrickletRemoteSwitch.SWITCH_TO_OFF);
 		}
	      }
	    }
        });

        node.on('close',function() {
            node.ipcon.disconnect();
        });
    }

    RED.nodes.registerType('RemoteSwitch Type B', tinkerForgeRemoteSwitchB);
}<|endoftext|>


<commit_before>'use strict';

var toNatural  = require('es5-ext/number/to-pos-integer')
  , location   = require('mano/lib/client/location')
  , _          = require('mano').i18n.bind('Print')
  , arrayToSet = require('../utils/array-to-set')
  , findKey    = require('es5-ext/object/find-key')

  , keys = Object.keys, ceil = Math.ceil;

exports._parent = require('./print-base');

exports.main = function () {
	var statusMap = exports._statusMap(this)
	  , perPage = exports._cacheLimits(this).usersPerPage;

	var getPageCount = function (value) {
		if (!value) return 1;
		return ceil(value / perPage);
	};
	var getSection  = function (businessProcesses, data) {
		return [div({ class: 'print-users-list-caption' },
			data.label, span(" (", businessProcesses._length, ")")),
			table({ class: 'print-users-list' },
				exports._businessProcessesTable(businessProcesses))];
	};

	return section({ id: 'main' },
		mmap(location.query.get('status'), function (value) {
			var status, businessProcesses, pageCount;
			if (value == null) {
				if (statusMap['']) {
					status = '';
				} else {
					status = findKey(statusMap, function (item) { return item.default; });
					if (!status) {
						return;
					}
				}
			} else if (value && statusMap[value]) {
				status = value;
			} else {
				return;
			}
			businessProcesses = statusMap[status].data;
			pageCount = businessProcesses._size.map(getPageCount);
			return mmap(location.query.get('page'), function (value) {
				var page, result = []
				  , businessProcessesSnapshot = businessProcesses.toArray(exports._defaultSort);
				if ((value == null) || (value === '1')) {
					page = 1;
				} else {
					page = toNatural(value);
					if (!page) return;
					if (String(page) !== value) return;
				}
				result.push(_if(gt(pageCount, 1), p({ class: 'page' }, _("Page"), page, " /",
					pageCount)));
				businessProcessesSnapshot =
					businessProcessesSnapshot.slice((page - 1) * perPage, (page - 1) * perPage + perPage);
				if (status !== 'all') {
					result.push(getSection(businessProcessesSnapshot, statusMap[status]));
				} else {
					businessProcessesSnapshot = arrayToSet(businessProcessesSnapshot);
					result.push(ul(keys(statusMap).filter(function (name) {
						return name !== 'all';
					}).sort(function (a, b) {
						return statusMap[a].order - statusMap[b].order;
					}), function (state) {
						return getSection(businessProcessesSnapshot.and(statusMap[state].data)
							.toArray(exports._defaultSort), statusMap[state]);
					}));
				}
				return _if(gt(page, pageCount), null, result);
			});
		}));
};

exports['print-page-title'] = function () { insert(this.processingStep.label); };

exports._businessProcessesTable = Function.prototype;
exports._statusMap = Function.prototype;
exports._defaultSort = Function.prototype;
exports._cacheLimits = Function.prototype;
<commit_msg>Support new manager based handling<commit_after>'use strict';

var ensureArray   = require('es5-ext/array/valid-array')
  , toNatural     = require('es5-ext/number/to-pos-integer')
  , ensureObject  = require('es5-ext/object/valid-object')
  , hyphenToCamel = require('es5-ext/string/#/hyphen-to-camel')
  , replaceContent = require('dom-ext/element/#/replace-content')
  , mano       = require('mano')
  , location   = require('mano/lib/client/location')
  , Manager = require('./components/business-processes-table/manager')
  , arrayToSet = require('../utils/array-to-set')
  , findKey    = require('es5-ext/object/find-key')

  , keys = Object.keys, ceil = Math.ceil
  , env = mano.env, _ = mano.i18n;

var setupQueryHandler =
	require('eregistrations/view/components/business-processes-table/setup-query-handler');

exports._parent = require('./print-base');

var generateMainContent = function () {
	var statusMap = ensureObject(exports._statusMap())
	  , columns = ensureArray(exports._columns())
	  , roleName = hyphenToCamel.call(this.appName.slice('official-'.length))
	  , container, superIsExernalQuery;

	var listManager = new Manager({
		user: this.user,
		roleName: roleName,
		statusMap: statusMap,
		getOrderIndex: exports._getOrderIndex,
		itemsPerPage: env.objectsListItemsPerPage
	});
	superIsExernalQuery = listManager._isExternalQuery;
	listManager._isExternalQuery = function (query) {
		if (!query.status) return true;
		return superIsExernalQuery.call(this, query);
	};

	var statuses = keys(statusMap).filter(function (name) { return (name !== 'all'); })
		.sort(function (a, b) { return statusMap[a].order - statusMap[b].order; });

	setupQueryHandler(listManager, '/print-business-processes-list/');

	var getSection  = function (url, businessProcesses, data) {
		return [div({ class: 'print-users-list-caption' },
			data.label, span(" (", businessProcesses.length, ")")),
			table({ class: 'print-users-list' },
				thead(columns.map(function (column) { return th({ class: column.class }, column.head); })),
				tbody({ onEmpty: tr({ class: 'empty' }, td({ colspan: columns.length },
					_("There are no items"))) }, businessProcesses,
					function (businessProcess) {
						return tr(columns.map(function (column) {
							return td({ class: column.class }, column.data(businessProcess));
						}));
					}))];
	};

	container = div();

	listManager.on('change', function () {
		var result = [];
		if (listManager.pageCount > 1) {
			result.push(p({ class: 'page' }, _("Page"), " ", listManager.page, " / ",
				listManager.pageCount));
		}
		if (listManager.query.status) {
			result.push(getSection(url, listManager.list, statusMap[listManager.query.status]));
		} else {
			result.push(ul(statuses, function (status) {
				if (statusMap[status].redundant) return;
				return getSection(url, listManager.list.filter(function (bp) {
					return (this[bp.__id__] === status);
				}, listManager.serverResult.statusMap), statusMap[status]);
			}));
		}
		replaceContent.call(container, result);
	});

	return container;
};

// Legacy generator (to be removed once ELS is migrated to new data handling)
var legacyGenerateMainContent = function () {
	var statusMap = exports._statusMap(this)
	  , perPage = exports._cacheLimits(this).usersPerPage;

	var getPageCount = function (value) {
		if (!value) return 1;
		return ceil(value / perPage);
	};
	var getSection  = function (businessProcesses, data) {
		return [div({ class: 'print-users-list-caption' },
			data.label, span(" (", businessProcesses._length, ")")),
			table({ class: 'print-users-list' },
				exports._businessProcessesTable(businessProcesses))];
	};

	return section({ id: 'main' },
		mmap(location.query.get('status'), function (value) {
			var status, businessProcesses, pageCount;
			if (value == null) {
				if (statusMap['']) {
					status = '';
				} else {
					status = findKey(statusMap, function (item) { return item.default; });
					if (!status) {
						return;
					}
				}
			} else if (value && statusMap[value]) {
				status = value;
			} else {
				return;
			}
			businessProcesses = statusMap[status].data;
			pageCount = businessProcesses._size.map(getPageCount);
			return mmap(location.query.get('page'), function (value) {
				var page, result = []
				  , businessProcessesSnapshot = businessProcesses.toArray(exports._defaultSort);
				if ((value == null) || (value === '1')) {
					page = 1;
				} else {
					page = toNatural(value);
					if (!page) return;
					if (String(page) !== value) return;
				}
				result.push(_if(gt(pageCount, 1), p({ class: 'page' }, _("Page"), page, " /",
					pageCount)));
				businessProcessesSnapshot =
					businessProcessesSnapshot.slice((page - 1) * perPage, (page - 1) * perPage + perPage);
				if (status !== 'all') {
					result.push(getSection(businessProcessesSnapshot, statusMap[status]));
				} else {
					businessProcessesSnapshot = arrayToSet(businessProcessesSnapshot);
					result.push(ul(keys(statusMap).filter(function (name) {
						return name !== 'all';
					}).sort(function (a, b) {
						return statusMap[a].order - statusMap[b].order;
					}), function (state) {
						return getSection(businessProcessesSnapshot.and(statusMap[state].data)
							.toArray(exports._defaultSort), statusMap[state]);
					}));
				}
				return _if(gt(page, pageCount), null, result);
			});
		}));
};

exports.main = function () {
	if (exports._businessProcessesTable !== Function.prototype) {
		return legacyGenerateMainContent.apply(this, arguments);
	}
	return generateMainContent.apply(this, arguments);
};

exports['print-page-title'] = function () { insert(this.processingStep.label); };

exports._statusMap = Function.prototype;
exports._getOrderIndex = Function.prototype;
exports._columns = Function.prototype;

// Legacy extensions (to be removed once ELS is migrated to new data handling)
exports._businessProcessesTable = Function.prototype;
exports._defaultSort = Function.prototype;
exports._cacheLimits = Function.prototype;
<|endoftext|>


<commit_before>/* mousetrap v1.1 craig.is/killing/mice */
window.Mousetrap=function(){function m(a,e,b){if(a.addEventListener)return a.addEventListener(e,b,!1);a.attachEvent("on"+e,b)}function s(a){return"keypress"!=a.type&&h[a.which]?h[a.which]:"keypress"==a.type?String.fromCharCode(a.which):String.fromCharCode(a.which).toLowerCase()}function n(a){var a=a||{},e=!1,b;for(b in i)a[b]?e=!0:i[b]=0;e||(k=!1)}function t(a,e,b,c,z){var g,d,f=[];if(!j[a])return[];"keyup"==b&&o(a)&&(e=[a]);for(g=0;g<j[a].length;++g)if(d=j[a][g],!(d.seq&&i[d.seq]!=d.level)&&b==d.action&&
("keypress"===b||e.sort().join(",")===d.modifiers.sort().join(",")))c&&d.combo==z&&j[a].splice(g,1),f.push(d);return f}function p(a){a.which="number"==typeof a.which?a.which:a.keyCode;var e=s(a);if(e)if("keyup"===a.type&&q===e)q=!1;else{var b=a.target||a.srcElement,c=b.tagName;if(!(-1<(" "+b.className+" ").indexOf(" mousetrap ")?0:"INPUT"==c||"SELECT"==c||"TEXTAREA"==c)){b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");for(var b=t(e,b,a.type),
f={},g=!1,c=0;c<b.length;++c)b[c].seq?(g=!0,f[b[c].seq]=1,b[c].callback(a)):!g&&!k&&b[c].callback(a);a.type==k&&!o(e)&&n(f)}}}function o(a){return"shift"==a||"ctrl"==a||"alt"==a||"meta"==a}function u(a,e,b){if(!b){if(!l){l={};for(var c in h)95<c&&112>c||h.hasOwnProperty(c)&&(l[h[c]]=c)}b=l[a]?"keydown":"keypress"}"keypress"===b&&e.length&&(b="keydown");return b}function A(a,e,b,c){i[a]=0;c||(c=u(e[0],[]));var f=function(){k=c;++i[a];clearTimeout(v);v=setTimeout(n,1E3)},g=function(a){b(a);"keyup"!==
c&&(q=s(a));setTimeout(n,10)},d;for(d=0;d<e.length;++d)w(e[d],d<e.length-1?f:g,c,a,d)}function w(a,e,b,c,f){var a=a.replace(/\s+/g," "),g=a.split(" "),d,h,i=[];if(1<g.length)return A(a,g,e,b);h="+"===a?["+"]:a.split("+");for(g=0;g<h.length;++g)d=h[g],x[d]&&(d=x[d]),b&&("keypress"!=b&&y[d])&&(d=y[d],i.push("shift")),o(d)&&i.push(d);b=u(d,i,b);j[d]||(j[d]=[]);t(d,i,b,!c,a);j[d][c?"unshift":"push"]({callback:e,modifiers:i,action:b,seq:c,level:f,combo:a})}for(var h={8:"backspace",9:"tab",13:"enter",16:"shift",
17:"ctrl",18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",46:"del",91:"meta",93:"meta",224:"meta",106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},y={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},x={option:"alt",command:"meta","return":"enter",
escape:"esc"},l,j={},r={},i={},v,q=!1,k=!1,f=1;20>f;++f)h[111+f]="f"+f;for(f=96;106>f;++f)h[f]=f-96;m(document,"keydown",p);m(document,"keyup",p);m(document,"keypress",p);return{bind:function(a,e,b){for(var c=a instanceof Array?a:[a],f=0;f<c.length;++f)w(c[f],e,b);r[a+":"+b]=e},trigger:function(a,e){r[a+":"+e]()},reset:function(){j={};r={}}}}();
<commit_msg>Updated to 46d910a80595a7f153d60dfd0c9357206f0155a4<commit_after>/**
 * Copyright 2012 Craig Campbell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Mousetrap is a simple keyboard shortcut library for Javascript with
 * no external dependencies
 *
 * @preserve @version 1.1
 * @url craig.is/killing/mice
 */
window.Mousetrap = (function() {

    /**
     * mapping of special keycodes to their corresponding keys
     *
     * @type {Object}
     */
    var _MAP = {
            8: 'backspace',
            9: 'tab',
            13: 'enter',
            16: 'shift',
            17: 'ctrl',
            18: 'alt',
            20: 'capslock',
            27: 'esc',
            32: 'space',
            33: 'pageup',
            34: 'pagedown',
            35: 'end',
            36: 'home',
            37: 'left',
            38: 'up',
            39: 'right',
            40: 'down',
            45: 'ins',
            46: 'del',
            91: 'meta',
            93: 'meta',
            224: 'meta'
        },

        /**
         * mapping for special characters so they can support
         * keydown and keyup events
         *
         * @type {Object}
         */
        _KEYCODE_MAP = {
            106: '*',
            107: '+',
            109: '-',
            110: '.',
            111 : '/',
            186: ';',
            187: '=',
            188: ',',
            189: '-',
            190: '.',
            191: '/',
            192: '`',
            219: '[',
            220: '\\',
            221: ']',
            222: '\''
        },

        /**
         * this is a mapping of keys that require shift on a US keypad
         * back to the non shift equivelents
         *
         * this is so you can use keyup events with these keys
         *
         * @type {Object}
         */
        _SHIFT_MAP = {
            '~': '`',
            '!': '1',
            '@': '2',
            '#': '3',
            '$': '4',
            '%': '5',
            '^': '6',
            '&': '7',
            '*': '8',
            '(': '9',
            ')': '0',
            '_': '-',
            '+': '=',
            ':': ';',
            '\"': '\'',
            '<': ',',
            '>': '.',
            '?': '/',
            '|': '\\'
        },

        /**
         * this is a list of special strings you can use to map
         * to modifier keys when you specify your keyboard shortcuts
         *
         * @type {Object}
         */
        _SPECIAL_ALIASES = {
            'option': 'alt',
            'command': 'meta',
            'return': 'enter',
            'escape': 'esc'
        },

        /**
         * variable to store the flipped version of _MAP from above
         * needed to check if we should use keypress or not when no action
         * is specified
         *
         * @type {Object|undefined}
         */
        _REVERSE_MAP,

        /**
         * a list of all the callbacks setup via Mousetrap.bind()
         *
         * @type {Object}
         */
        _callbacks = {},

        /**
         * direct map of string combinations to callbacks used for trigger()
         *
         * @type {Object}
         */
        _direct_map = {},

        /**
         * keeps track of what level each sequence is at since multiple
         * sequences can start out with the same sequence
         *
         * @type {Object}
         */
        _sequence_levels = {},

        /**
         * variable to store the setTimeout call
         *
         * @type {null|number}
         */
        _reset_timer,

        /**
         * temporary state where we will ignore the next keyup
         *
         * @type {boolean|string}
         */
        _ignore_next_keyup = false,

        /**
         * are we currently inside of a sequence?
         * type of action ("keyup" or "keydown" or "keypress") or false
         *
         * @type {boolean|string}
         */
        _inside_sequence = false;

    /**
     * loop through the f keys, f1 to f19 and add them to the map
     * programatically
     */
    for (var i = 1; i < 20; ++i) {
        _MAP[111 + i] = 'f' + i;
    }

    /**
     * loop through to map numbers on the numeric keypad
     */
    for (i = 96; i < 106; ++i) {
        _MAP[i] = i - 96;
    }

    /**
     * cross browser add event method
     *
     * @param {Element|HTMLDocument} object
     * @param {string} type
     * @param {Function} callback
     * @returns void
     */
    function _addEvent(object, type, callback) {
        if (object.addEventListener) {
            return object.addEventListener(type, callback, false);
        }

        object.attachEvent('on' + type, callback);
    }

    /**
     * takes the event and returns the keycode
     *
     * @param {Event} e
     * @return {string}
     */
    function _characterFromEvent(e) {

        // for keypress events we should return the character as is
        if (e.type == 'keypress') {
            return String.fromCharCode(e.which);
        }

        // for non keypress events the special maps are needed
        if (_MAP[e.which]) {
            return _MAP[e.which];
        }

        if (_KEYCODE_MAP[e.which]) {
            return _KEYCODE_MAP[e.which];
        }

        // if it is not in the special map
        return String.fromCharCode(e.which).toLowerCase();
    }

    /**
     * should we stop this event before firing off callbacks
     *
     * @param {Event} e
     * @return {boolean}
     */
    function _stop(e) {
        var element = e.target || e.srcElement,
            tag_name = element.tagName;

        // if the element has the class "mousetrap" then no need to stop
        if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
            return false;
        }

        // stop for input, select, and textarea
        return tag_name == 'INPUT' || tag_name == 'SELECT' || tag_name == 'TEXTAREA'/* || element.getAttribute('contenteditable')*/;
    }

    /**
     * checks if two arrays are equal
     *
     * @param {Array} modifiers1
     * @param {Array} modifiers2
     * @returns {boolean}
     */
    function _modifiersMatch(modifiers1, modifiers2) {
        return modifiers1.sort().join(',') === modifiers2.sort().join(',');
    }

    /**
     * resets all sequence counters except for the ones passed in
     *
     * @param {Object} do_not_reset
     * @returns void
     */
    function _resetSequences(do_not_reset) {
        do_not_reset = do_not_reset || {};

        var active_sequences = false;

        for (var key in _sequence_levels) {
            if (!do_not_reset[key]) {
                _sequence_levels[key] = 0;
                continue;
            }
            active_sequences = true;
        }

        if (!active_sequences) {
            _inside_sequence = false;
        }
    }

    /**
     * finds all callbacks that match based on the keycode, modifiers,
     * and action
     *
     * @param {string} character
     * @param {Array} modifiers
     * @param {string} action
     * @param {boolean=} remove - should we remove any matches
     * @param {string=} combination
     * @returns {Array}
     */
    function _getMatches(character, modifiers, action, remove, combination) {
        var i,
            callback,
            matches = [];

        // if there are no events related to this keycode
        if (!_callbacks[character]) {
            return [];
        }

        // if a modifier key is coming up on its own we should allow it
        if (action == 'keyup' && _isModifier(character)) {
            modifiers = [character];
        }

        // loop through all callbacks for the key that was pressed
        // and see if any of them match
        for (i = 0; i < _callbacks[character].length; ++i) {
            callback = _callbacks[character][i];

            // if this is a sequence but it is not at the right level
            // then move onto the next match
            if (callback['seq'] && _sequence_levels[callback['seq']] != callback['level']) {
                continue;
            }

            // if the action we are looking for doesn't match the action we got
            // then we should keep going
            if (action != callback.action) {
                continue;
            }

            // if this is a keypress event that means that we need to only
            // look at the character, otherwise check the modifiers as
            // well
            if (action === 'keypress' || _modifiersMatch(modifiers, callback.modifiers)) {

                // remove is used so if you change your mind and call bind a
                // second time with a new function the first one is overwritten
                if (remove && callback['combo'] == combination) {
                    _callbacks[character].splice(i, 1);
                }

                matches.push(callback);
            }
        }

        return matches;
    }

    /**
     * takes a key event and figures out what the modifiers are
     *
     * @param {Event} e
     * @returns {Array}
     */
    function _eventModifiers(e) {
        var modifiers = [];

        if (e.shiftKey) {
            modifiers.push('shift');
        }

        if (e.altKey) {
            modifiers.push('alt');
        }

        if (e.ctrlKey) {
            modifiers.push('ctrl');
        }

        if (e.metaKey) {
            modifiers.push('meta');
        }

        return modifiers;
    }

    /**
     * fires a callback for a matching keycode
     *
     * @param {string} character
     * @param {Event} e
     * @returns void
     */
    function _fireCallback(character, e) {

        // if this event should not happen stop here
        if (_stop(e)) {
            return;
        }

        var callbacks = _getMatches(character, _eventModifiers(e), e.type),
            i,
            do_not_reset = {},
            processed_sequence_callback = false;

        // loop through matching callbacks for this key event
        for (i = 0; i < callbacks.length; ++i) {

            // fire for all sequence callbacks
            // this is because if for example you have multiple sequences
            // bound such as "g i" and "g t" they both need to fire the
            // callback for matching g cause otherwise you can only ever
            // match the first one
            if (callbacks[i]['seq']) {
                processed_sequence_callback = true;

                // keep a list of which sequences were matches for later
                do_not_reset[callbacks[i]['seq']] = 1;
                callbacks[i].callback(e);
                continue;
            }

            // if there were no sequence matches but we are still here
            // that means this is a regular match so we should fire then break
            if (!processed_sequence_callback && !_inside_sequence) {
                callbacks[i].callback(e);
            }
        }

        // if you are inside of a sequence and the key you are pressing
        // is not a modifier key then we should reset all sequences
        // that were not matched by this key event
        if (e.type == _inside_sequence && !_isModifier(character)) {
            _resetSequences(do_not_reset);
        }
    }

    /**
     * handles a keydown event
     *
     * @param {Event} e
     * @returns void
     */
    function _handleKey(e) {

        // add which for key events
        // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
        e.which = typeof e.which == "number" ? e.which : e.keyCode;

        var character = _characterFromEvent(e);

        // no character found then stop
        if (!character) {
            return;
        }

        if (e.type === 'keyup' && _ignore_next_keyup === character) {
            _ignore_next_keyup = false;
            return;
        }

        _fireCallback(character, e);
    }

    /**
     * determines if the keycode specified is a modifier key or not
     *
     * @param {string} key
     * @returns {boolean}
     */
    function _isModifier(key) {
        return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
    }

    /**
     * called to set a 1 second timeout on the specified sequence
     *
     * this is so after each key press in the sequence you have 1 second
     * to press the next key before you have to start over
     *
     * @returns void
     */
    function _resetSequence() {
        clearTimeout(_reset_timer);
        _reset_timer = setTimeout(_resetSequences, 1000);
    }

    /**
     * reverses the map lookup so that we can look for specific keys
     * to see what can and can't use keypress
     *
     * @return {Object}
     */
    function _getReverseMap() {
        if (!_REVERSE_MAP) {
            _REVERSE_MAP = {};
            for (var key in _MAP) {

                // pull out the numberic keypad from here cause keypress should
                // be able to detect the keys from the character
                if (key > 95 && key < 112) {
                    continue;
                }

                if (_MAP.hasOwnProperty(key)) {
                    _REVERSE_MAP[_MAP[key]] = key;
                }
            }
        }
        return _REVERSE_MAP;
    }

    /**
     * picks the best action based on the key combination
     *
     * @param {string} key - character for key
     * @param {Array} modifiers
     * @param {string=} action passed in
     */
    function _pickBestAction(key, modifiers, action) {

        // if no action was picked in we should try to pick the one
        // that we think would work best for this key
        if (!action) {
            action = _getReverseMap()[key] ? 'keydown' : 'keypress';
        }

        // modifier keys don't work as expected with keypress,
        // switch to keydown
        if (action === 'keypress' && modifiers.length) {
            action = 'keydown';
        }

        return action;
    }

    /**
     * binds a key sequence to an event
     *
     * @param {string} combo - combo specified in bind call
     * @param {Array} keys
     * @param {Function} callback
     * @param {string=} action
     * @returns void
     */
    function _bindSequence(combo, keys, callback, action) {

        // start off by adding a sequence level record for this combination
        // and setting the level to 0
        _sequence_levels[combo] = 0;

        // if there is no action pick the best one for the first key
        // in the sequence
        if (!action) {
            action = _pickBestAction(keys[0], []);
        }

        /**
         * callback to increase the sequence level for this sequence and reset
         * all other sequences that were active
         *
         * @param {Event} e
         * @returns void
         */
        var _increaseSequence = function(e) {
                _inside_sequence = action;
                ++_sequence_levels[combo];
                _resetSequence();
            },

            /**
             * wraps the specified callback inside of another function in order
             * to reset all sequence counters as soon as this sequence is done
             *
             * @param {Event} e
             * @returns void
             */
            _callbackAndReset = function(e) {
                callback(e);

                // we should ignore the next key up if the action is key down
                // or keypress.  this is so if you finish a sequence and
                // release the key the final key will not trigger a keyup
                if (action !== 'keyup') {
                    _ignore_next_keyup = _characterFromEvent(e);
                }

                // weird race condition if a sequence ends with the key
                // another sequence begins with
                setTimeout(_resetSequences, 10);
            },
            i;

        // loop through keys one at a time and bind the appropriate callback
        // function.  for any key leading up to the final one it should
        // increase the sequence. after the final, it should reset all sequences
        for (i = 0; i < keys.length; ++i) {
            _bindSingle(keys[i], i < keys.length - 1 ? _increaseSequence : _callbackAndReset, action, combo, i);
        }
    }

    /**
     * binds a single keyboard combination
     *
     * @param {string} combination
     * @param {Function} callback
     * @param {string=} action
     * @param {string=} sequence_name - name of sequence if part of sequence
     * @param {number=} level - what part of the sequence the command is
     * @returns void
     */
    function _bindSingle(combination, callback, action, sequence_name, level) {

        // make sure multiple spaces in a row become a single space
        combination = combination.replace(/\s+/g, ' ');

        var sequence = combination.split(' '),
            i,
            key,
            keys,
            modifiers = [];

        // if this pattern is a sequence of keys then run through this method
        // to reprocess each pattern one key at a time
        if (sequence.length > 1) {
            return _bindSequence(combination, sequence, callback, action);
        }

        // take the keys from this pattern and figure out what the actual
        // pattern is all about
        keys = combination === '+' ? ['+'] : combination.split('+');

        for (i = 0; i < keys.length; ++i) {
            key = keys[i];

            // normalize key names
            if (_SPECIAL_ALIASES[key]) {
                key = _SPECIAL_ALIASES[key];
            }

            // if this is not a keypress event then we should
            // be smart about using shift keys
            // this will only work for US keyboards however
            if (action && action != 'keypress' && _SHIFT_MAP[key]) {
                key = _SHIFT_MAP[key];
                modifiers.push('shift');
            }

            // if this key is a modifier then add it to the list of modifiers
            if (_isModifier(key)) {
                modifiers.push(key);
            }
        }

        // depending on what the key combination is
        // we will try to pick the best event for it
        action = _pickBestAction(key, modifiers, action);

        // make sure to initialize array if this is the first time
        // a callback is added for this key
        if (!_callbacks[key]) {
            _callbacks[key] = [];
        }

        // remove an existing match if there is one
        _getMatches(key, modifiers, action, !sequence_name, combination);

        // add this call back to the array
        // if it is a sequence put it at the beginning
        // if not put it at the end
        //
        // this is important because the way these are processed expects
        // the sequence ones to come first
        _callbacks[key][sequence_name ? 'unshift' : 'push']({
            callback: callback,
            modifiers: modifiers,
            action: action,
            seq: sequence_name,
            level: level,
            combo: combination
        });
    }

    /**
     * binds multiple combinations to the same callback
     *
     * @param {Array} combinations
     * @param {Function} callback
     * @param {string|undefined} action
     * @returns void
     */
    function _bindMultiple(combinations, callback, action) {
        for (var i = 0; i < combinations.length; ++i) {
            _bindSingle(combinations[i], callback, action);
        }
    }

    // start!
    _addEvent(document, 'keydown', _handleKey);
    _addEvent(document, 'keyup', _handleKey);
    _addEvent(document, 'keypress', _handleKey);

    return {

        /**
         * binds an event to mousetrap
         *
         * can be a single key, a combination of keys separated with +,
         * a comma separated list of keys, an array of keys, or
         * a sequence of keys separated by spaces
         *
         * be sure to list the modifier keys first to make sure that the
         * correct key ends up getting bound (the last key in the pattern)
         *
         * @param {string|Array} keys
         * @param {Function} callback
         * @param {string=} action - 'keypress', 'keydown', or 'keyup'
         * @returns void
         */
        bind: function(keys, callback, action) {
            _bindMultiple(keys instanceof Array ? keys : [keys], callback, action);
            _direct_map[keys + ':' + action] = callback;
        },

        /**
         * unbinds an event to mousetrap
         *
         * the unbinding sets the callback function of the specified key combo
         * to an empty function and deletes the corresponding key in the
         * _direct_map dict.
         *
         * the keycombo+action has to be exactly the same as
         * it was defined in the bind method
         *
         * @todo actually remove this from the _callbacks dictionary instead
         * of binding an empty function
         *
         * @param {string|Array} keys
         * @param {string} action
         * @returns void
         */
        unbind: function(keys, action) {
            if (_direct_map[keys + ':' + action]) {
                delete _direct_map[keys + ':' + action];
                this.bind(keys, function() {}, action);
            }
        },

        /**
         * triggers an event that has already been bound
         *
         * @param {string} keys
         * @param {string=} action
         * @returns void
         */
        trigger: function(keys, action) {
            _direct_map[keys + ':' + action]();
        },

        /**
         * resets the library back to its initial state.  this is useful
         * if you want to clear out the current keyboard shortcuts and bind
         * new ones - for example if you switch to another page
         *
         * @returns void
         */
        reset: function() {
            _callbacks = {};
            _direct_map = {};
        }
    };
}) ();
<|endoftext|>


<commit_before>(function($){
  $.MarkdownSettings = {
    previewParserPath:  '',
    onShiftEnter:    {keepDefault:false, openWith:'\n\n'},
    markupSet: [
      {name:'First Level Heading', key:'1', placeHolder:'Your title here...',
        closeWith:function(markItUp) { return markdownTitle(markItUp, '=') } },
      {name:'Second Level Heading', key:'2', placeHolder:'Your title here...',
        closeWith:function(markItUp) { return markdownTitle(markItUp, '-') } },
      {name:'Heading 3', key:'3', openWith:'### ', placeHolder:'Your title here...' },
      {name:'Heading 4', key:'4', openWith:'#### ', placeHolder:'Your title here...' },
      {name:'Heading 5', key:'5', openWith:'##### ', placeHolder:'Your title here...' },
      {name:'Heading 6', key:'6', openWith:'###### ', placeHolder:'Your title here...' },
      {separator:'---------------' },
      {name:'Bold', key:'B', openWith:'**', closeWith:'**'},
      {name:'Italic', key:'I', openWith:'_', closeWith:'_'},
      {separator:'---------------' },
      {name:'Bulleted List', openWith:'- ' },
      {name:'Numeric List', openWith:function(markItUp) {
        return markItUp.line+'. ';
      }},
      {separator:'---------------' },
      {name:'Picture', key:'P', replaceWith:'![[![Alternative text]!]]([![Url:!:http://]!] "[![Title]!]")'},
      {name:'Link', key:'L', openWith:'[', closeWith:']([![Url:!:http://]!] "[![Title]!]")', placeHolder:'Your text to link here...' },
      {name:'Video', key:'V', className:'tinyUrl', openWith:function(markItUp) { return findVideo(markItUp) } },
      {separator:'---------------'},
      {name:'Quotes', openWith:'> '},
      {name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'},
      {separator:'---------------'}
    ]
  }

  function findVideo(markItUp) {
    var url, image = "no-image", title = "no-title";
    url = prompt("Url:", "http://");
    if (url) {
      $.ajaxSetup({ async: false });
      $.getJSON("/admin/posts/video_info.json", "url="+url, function(obj) {
        if(obj && obj["video"]){
          title = obj["video"]["title"];
          image = '!['+title+']('+obj["video"]["thumbnail_large"]+')';
        }
      });
    }
    return '['+image+']('+url+' "'+title+'")';
  }

  function markdownTitle(markItUp, char) {
    heading = '';
    n = $.trim(markItUp.selection||markItUp.placeHolder).length;
    for(i = 0; i < n; i++) {
      heading += char;
    }
    return '\n'+heading;
  }
})(jQuery);
<commit_msg>fixed video url issue<commit_after>(function($){
  $.MarkdownSettings = {
    previewParserPath:  '',
    onShiftEnter:    {keepDefault:false, openWith:'\n\n'},
    markupSet: [
      {name:'First Level Heading', key:'1', placeHolder:'Your title here...',
        closeWith:function(markItUp) { return markdownTitle(markItUp, '=') } },
      {name:'Second Level Heading', key:'2', placeHolder:'Your title here...',
        closeWith:function(markItUp) { return markdownTitle(markItUp, '-') } },
      {name:'Heading 3', key:'3', openWith:'### ', placeHolder:'Your title here...' },
      {name:'Heading 4', key:'4', openWith:'#### ', placeHolder:'Your title here...' },
      {name:'Heading 5', key:'5', openWith:'##### ', placeHolder:'Your title here...' },
      {name:'Heading 6', key:'6', openWith:'###### ', placeHolder:'Your title here...' },
      {separator:'---------------' },
      {name:'Bold', key:'B', openWith:'**', closeWith:'**'},
      {name:'Italic', key:'I', openWith:'_', closeWith:'_'},
      {separator:'---------------' },
      {name:'Bulleted List', openWith:'- ' },
      {name:'Numeric List', openWith:function(markItUp) {
        return markItUp.line+'. ';
      }},
      {separator:'---------------' },
      {name:'Picture', key:'P', replaceWith:'![[![Alternative text]!]]([![Url:!:http://]!] "[![Title]!]")'},
      {name:'Link', key:'L', openWith:'[', closeWith:']([![Url:!:http://]!] "[![Title]!]")', placeHolder:'Your text to link here...' },
      {name:'Video', key:'V', className:'tinyUrl', openWith:function(markItUp) { return findVideo(markItUp) } },
      {separator:'---------------'},
      {name:'Quotes', openWith:'> '},
      {name:'Code Block / Code', openWith:'(!(\t|!|`)!)', closeWith:'(!(`)!)'},
      {separator:'---------------'}
    ]
  }

  function findVideo(markItUp) {
    var url, image = "no-image", title = "no-title";
    url = prompt("Url:", "http://");
    if (url) {
      $.ajaxSetup({ async: false });
      $.getJSON("/admin/posts/video_info.json", {url: url}, function(obj) {
        if(obj && obj["video"]){
          url   = obj["video"]["url"];
          title = obj["video"]["title"];
          image = '!['+title+']('+obj["video"]["thumbnail_large"]+')';
        }
      });
      return '['+image+']('+url+' "'+title+'")';
    }
  }

  function markdownTitle(markItUp, char) {
    heading = '';
    n = $.trim(markItUp.selection||markItUp.placeHolder).length;
    for(i = 0; i < n; i++) {
      heading += char;
    }
    return '\n'+heading;
  }
})(jQuery);
<|endoftext|>


<commit_before>var fs = require('fs');
var net = require('net');
var https = require('https');
var crypto = require('crypto');
var urlParse = require('url').parse;
var WebSocketServer = require('websocket').server;

process.chdir(__dirname);

var argv = require('optimist').argv;
var pidfile;

//kill an already running instance
if (argv.kill) {
  pidfile = argv.kill;
  if (!pidfile.match(/\.pid$/i))
    pidfile += '.pid';
  try {
    var pid = fs.readFileSync(pidfile, 'utf8');
    fs.unlinkSync(pidfile);
    process.kill(parseInt(pid, 10));
    console.log('Killed process ' + pid);
  } catch(e) {
    console.log('Error killing process ' + (pid || argv.kill));
  }
  process.exit();
}

//write pid to file so it can be killed with --kill
if (argv.pidfile) {
  pidfile = argv.pidfile;
  if (!pidfile.match(/\.pid$/i))
    pidfile += '.pid';
  fs.writeFileSync(pidfile, process.pid);
}

// var key = fs.readFileSync('./keys/ssl.key', 'utf8');
// var cert = fs.readFileSync('./keys/ssl.crt', 'utf8');
var users = loadUsers();

var server = https.createServer();

server.on('request', function(req, res) {
  if (req.url == '/') {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('This is a secure HTTP/1.1 WebSocket server.\n');
  } else
  if (req.url == '/auth') {
    if (authenticate(req)) {
      res.writeHead(204);
    } else {
      res.writeHead(401, {'WWW-Authenticate': 'Basic'});
    }
    res.end();
  } else {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.end('404 Not Found.\n');
  }
});

var addr = parseAddr(argv._[0] || '', {port: 443, host: '0.0.0.0'});

server.listen(addr.port, addr.host, function() {
  var addr = server.address();
  console.log('listening on ' + addr.address + ':' + addr.port);
});

var wsServer = new WebSocketServer({
  httpServer: server,
  autoAcceptConnections: true
});

wsServer.on('request', function(request) {
  var url = urlParse(request.resource, true);
  var args = url.pathname.split('/').slice(1);
  var action = args.shift();
  var params = url.query;
  if (action == 'tunnel') {
    createTunnel(request, params.port, params.host);
  } else {
    request.reject(404);
  }
});

function authenticate(request) {
  var encoded = request.headers['authorization'] || '', credentials;
  encoded = encoded.replace(/Basic /i, '');
  try {
    credentials = new Buffer(encoded, 'base64').toString('utf8').split(':');
  } catch(e) {
    credentials = [];
  }
  console.log("User " + user + " tried to connect with password " + credentials[1]);
  var user = credentials[0], hash = md5(credentials[1]);
  return (users[user] == hash);
}

function createTunnel(request, port, host) {
  console.log("Trying to create a tunnel...");
  // if (!authenticate(request.httpRequest)) {
  //   request.reject(403);
  //   return;
  // }
  var webSock = request.accept();
  console.log(webSock.remoteAddress + ' connected - Protocol Version ' + webSock.websocketVersion);

  var tcpSock = new net.Socket();

  tcpSock.on('error', function(err) {
    webSock.send(JSON.stringify({status: 'error', details: 'Upstream socket error; ' + err}));
  });

  tcpSock.on('data', function(data) {
    webSock.send(data);
  });

  tcpSock.on('close', function() {
    webSock.close();
  });

  tcpSock.connect(port, host || '127.0.0.1', function() {
    webSock.on('message', function(msg) {
      if (msg.type === 'utf8') {
        //console.log('received utf message: ' + msg.utf8Data);
      } else {
        //console.log('received binary message of length ' + msg.binaryData.length);
        tcpSock.write(msg.binaryData);
      }
    });
    webSock.send(JSON.stringify({status: 'ready', details: 'Upstream socket connected'}));
  });

  webSock.on('close', function() {
    tcpSock.destroy();
    console.log(webSock.remoteAddress + ' disconnected');
  });
}

function loadUsers() {
  var lines = fs.readFileSync('./users.txt', 'utf8');
  var users = {};
  lines.split(/[\r\n]+/g).forEach(function(line) {
    var parts = line.split(':');
    if (parts.length == 2) {
      users[parts[0]] = parts[1];
    }
  });
  return users;
}

function md5(s) {
  var hash = crypto.createHash('md5');
  hash.update(new Buffer(String(s)));
  return hash.digest('hex');
}

function parseAddr(str, addr) {
  if (str) {
    var parts = str.split(':');
    if (parts.length == 1) {
      if (parts[0] == parseInt(parts[0], 10).toString()) {
        addr.port = parts[0];
      } else {
        addr.host = parts[0];
      }
    } else
    if (parts.length == 2) {
      addr = {host: parts[0], port: parts[1]};
    }
  }
  return addr;
}
<commit_msg>dump https<commit_after>var fs = require('fs');
var net = require('net');
var http = require('http');
var https = require('https');
var crypto = require('crypto');
var urlParse = require('url').parse;
var WebSocketServer = require('websocket').server;

process.chdir(__dirname);

var argv = require('optimist').argv;
var pidfile;

//kill an already running instance
if (argv.kill) {
  pidfile = argv.kill;
  if (!pidfile.match(/\.pid$/i))
    pidfile += '.pid';
  try {
    var pid = fs.readFileSync(pidfile, 'utf8');
    fs.unlinkSync(pidfile);
    process.kill(parseInt(pid, 10));
    console.log('Killed process ' + pid);
  } catch(e) {
    console.log('Error killing process ' + (pid || argv.kill));
  }
  process.exit();
}

//write pid to file so it can be killed with --kill
if (argv.pidfile) {
  pidfile = argv.pidfile;
  if (!pidfile.match(/\.pid$/i))
    pidfile += '.pid';
  fs.writeFileSync(pidfile, process.pid);
}

var key = fs.readFileSync('./keys/ssl.key', 'utf8');
var cert = fs.readFileSync('./keys/ssl.crt', 'utf8');
var users = loadUsers();

var server = http.createServer();

server.on('request', function(req, res) {
  if (req.url == '/') {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('This is a secure HTTP/1.1 WebSocket server.\n');
  } else
  if (req.url == '/auth') {
    if (authenticate(req)) {
      res.writeHead(204);
    } else {
      res.writeHead(401, {'WWW-Authenticate': 'Basic'});
    }
    res.end();
  } else {
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.end('404 Not Found.\n');
  }
});

var addr = parseAddr(argv._[0] || '', {port: 443, host: '0.0.0.0'});

server.listen(addr.port, addr.host, function() {
  var addr = server.address();
  console.log('listening on ' + addr.address + ':' + addr.port);
});

var wsServer = new WebSocketServer({
  httpServer: server,
  autoAcceptConnections: true
});

wsServer.on('request', function(request) {
  var url = urlParse(request.resource, true);
  var args = url.pathname.split('/').slice(1);
  var action = args.shift();
  var params = url.query;
  if (action == 'tunnel') {
    createTunnel(request, params.port, params.host);
  } else {
    request.reject(404);
  }
});

function authenticate(request) {
  var encoded = request.headers['authorization'] || '', credentials;
  encoded = encoded.replace(/Basic /i, '');
  try {
    credentials = new Buffer(encoded, 'base64').toString('utf8').split(':');
  } catch(e) {
    credentials = [];
  }
  console.log("User " + user + " tried to connect with password " + credentials[1]);
  var user = credentials[0], hash = md5(credentials[1]);
  return (users[user] == hash);
}

function createTunnel(request, port, host) {
  console.log("Trying to create a tunnel...");
  // if (!authenticate(request.httpRequest)) {
  //   request.reject(403);
  //   return;
  // }
  var webSock = request.accept();
  console.log(webSock.remoteAddress + ' connected - Protocol Version ' + webSock.websocketVersion);

  var tcpSock = new net.Socket();

  tcpSock.on('error', function(err) {
    webSock.send(JSON.stringify({status: 'error', details: 'Upstream socket error; ' + err}));
  });

  tcpSock.on('data', function(data) {
    webSock.send(data);
  });

  tcpSock.on('close', function() {
    webSock.close();
  });

  tcpSock.connect(port, host || '127.0.0.1', function() {
    webSock.on('message', function(msg) {
      if (msg.type === 'utf8') {
        //console.log('received utf message: ' + msg.utf8Data);
      } else {
        //console.log('received binary message of length ' + msg.binaryData.length);
        tcpSock.write(msg.binaryData);
      }
    });
    webSock.send(JSON.stringify({status: 'ready', details: 'Upstream socket connected'}));
  });

  webSock.on('close', function() {
    tcpSock.destroy();
    console.log(webSock.remoteAddress + ' disconnected');
  });
}

function loadUsers() {
  var lines = fs.readFileSync('./users.txt', 'utf8');
  var users = {};
  lines.split(/[\r\n]+/g).forEach(function(line) {
    var parts = line.split(':');
    if (parts.length == 2) {
      users[parts[0]] = parts[1];
    }
  });
  return users;
}

function md5(s) {
  var hash = crypto.createHash('md5');
  hash.update(new Buffer(String(s)));
  return hash.digest('hex');
}

function parseAddr(str, addr) {
  if (str) {
    var parts = str.split(':');
    if (parts.length == 1) {
      if (parts[0] == parseInt(parts[0], 10).toString()) {
        addr.port = parts[0];
      } else {
        addr.host = parts[0];
      }
    } else
    if (parts.length == 2) {
      addr = {host: parts[0], port: parts[1]};
    }
  }
  return addr;
}
<|endoftext|>


<commit_before>// @flow
import React, { Component } from 'react';
import {
    View,
    NavigationExperimental,
    StyleSheet,
    Animated,
    Text,
    Dimensions,
    UIManager,
    InteractionManager,
} from 'react-native';

import type {NavigationTransitionProps } from 'NavigationTypeDefinition';

import SharedItems from './SharedItems';

import type { Metrics, SharedItem, UpdateRequest } from './SharedItems';

const {
    Transitioner,
} = NavigationExperimental;

type State = {
    sharedItems: SharedItems,
    itemsToMeasure: Array<SharedItem>,
}

class MaterialSharedElementTransitioner extends Component {
    state: State;
    static childContextTypes = {
        registerSharedView: React.PropTypes.func,
        unregisterSharedView: React.PropTypes.func,
    }
    constructor(props) {
        super(props);
        this.state = {
            sharedItems: new SharedItems(),
            itemsToMeasure: [],
        }
    }
    measure(sharedItem: SharedItem): Promise<Metrics> {
        // console.log('measuring:', sharedItem.name, sharedItem.containerRouteName)
        return new Promise((resolve, reject) => {
            UIManager.measureInWindow(
                sharedItem.nativeHandle,
                (x, y, width, height) => {
                    resolve({ x, y, width, height });
                }
            );
        });
    }
    setSharedItemsState(fun: (prevState: State) => SharedItems, callback) {
        this.setState((prevState) => (
            { sharedItems: fun(prevState) }
        ), callback);
    }
    addSharedItem(sharedItem: SharedItem) {
        this.setSharedItemsState(prevState =>
            prevState.sharedItems.add(sharedItem)
        );
    }
    removeSharedItem(name: string, containerRouteName: string) {
        this.setSharedItemsState(prevState =>
            prevState.sharedItems.remove(name, containerRouteName)
        );
    }
    getChildContext() {
        const self = this;
        return {
            registerSharedView(sharedItem: SharedItem) {
                self.addSharedItem(sharedItem);
                const {name, containerRouteName} = sharedItem;

                const matchingItem = self.state.sharedItems.findMatchByName(name, containerRouteName);
                // schedule to measure (on layout) if another view with the same name is mounted
                if (matchingItem) {
                    self.setState({ itemsToMeasure: [sharedItem, matchingItem] });
                }
            },
            unregisterSharedView(name: string, containerRouteName: string) {
                self.removeSharedItem(name, containerRouteName);
            },
        };
    }
    shouldComponentUpdate(nextProps, nextState: State) {
        /*
          state / prop changes
          - navigation change: nextProps !== this.props                       => true
          - onLayout: state: itemsToMeasure, sharedItems.metrics              => measured?
          - afterInteraction: state: itemsToMeasure, sharedElements.metrics   => false
          - register: state.sharedElements, state.itemsToMeasure              => false
          - unregister: statee.sharedElements                                 => false
        */
        return this.props !== nextProps || nextState.itemsToMeasure.length === 0;
    }
    async _onLayout() {
        let toUpdate = [];
        for (let item of this.state.itemsToMeasure) {
            const { name, containerRouteName } = item;
            const metrics = await this.measure(item);
            toUpdate.push({ name, containerRouteName, metrics });
        }
        if (toUpdate.length > 0) {
            // console.log('measured, setting meatured state:', toUpdate)
            this.setState((prevState: State): State => ({
                sharedItems: prevState.sharedItems.updateMetrics(toUpdate),
                itemsToMeasure: [],
            }));
        }
    }
    render() {
        return (
            <View style={{ flex: 1 }} onLayout={this._onLayout.bind(this)}>
                <Transitioner
                    configureTransition={this._configureTransition.bind(this)}
                    render={this._render.bind(this)}
                    navigationState={this.props.navigationState}
                    style={this.props.style}
                    />
            </View>
        );
    }
    _configureTransition() {
        return {
            duration: 300,
            // useNativeDriver: false,
        }
    }
    _render(props: NavigationTransitionProps, prevProps: NavigationTransitionProps) {
        const scenes = props.scenes.map(scene => this._renderScene({ ...props, scene }));
        const overlay = this._renderOverlay(props, prevProps);
        return (
            <View style={styles.scenes}>
                {scenes}
                {overlay}
            </View>
        )
    }
    _getOverlayContainerStyle(progress) {
        const left = progress.interpolate({
            inputRange: [0, 0.999999, 1],
            outputRange: [0, 0, 100000], // move it off screen after transition is done
        });
        return {
            left,
        };
    }
    _getSharedElementStyle(props, prevProps, itemFrom, itemTo) {
        const { position, progress, navigationState: {index} } = props;
        const prevIndex = prevProps.navigationState.index;
        const toVsFromScaleX = itemTo.scaleRelativeTo(itemFrom).x;
        const toVsFromScaleY = itemTo.scaleRelativeTo(itemFrom).y;
        const minIdx = Math.min(index, prevIndex);
        const maxIdx = Math.max(index, prevIndex);
        const inputRange = [minIdx, maxIdx];
        const adaptRange = (range) => index > prevIndex ? range : range.reverse();
        const scaleX = position.interpolate({
            inputRange,
            outputRange: adaptRange([1, toVsFromScaleX]),
        });
        const scaleY = position.interpolate({
            inputRange,
            outputRange: adaptRange([1, toVsFromScaleY]),
        });
        const width = itemFrom.metrics.width;
        const height = itemFrom.metrics.height;
        const left = position.interpolate({
            inputRange,
            outputRange: adaptRange([itemFrom.metrics.x, itemTo.metrics.x + width / 2 * (toVsFromScaleX - 1)]),
        });
        const top = position.interpolate({
            inputRange,
            outputRange: adaptRange([itemFrom.metrics.y, itemTo.metrics.y + height / 2 * (toVsFromScaleY - 1)]),
        });
        return {
            width,
            height,
            elevation: this._interpolateElevation(props, prevProps, 1), // make sure shared elements stay above the faked container
            position: 'absolute',
            left,
            top,
            right: null,
            bottom: null,
            transform: [
                { scaleX },
                { scaleY },
            ],
        };
    }
    _getBBox(metricsArray: Array<Metrics>) {
        let left, top, right, bottom;
        left = top = Number.MAX_VALUE;
        right = bottom = Number.MIN_VALUE;
        metricsArray.forEach(m => {
            if (m.x < left) left = m.x;
            if (m.y < top) top = m.y;
            if (m.x + m.width > right) right = m.x + m.width;
            if (m.y + m.height > bottom) bottom = m.y + m.height;
        });
        const width = right - left;
        const height = bottom - top;
        return { left, top, right, bottom, width, height };
    }
    _interpolateElevation(props, prevProps, base: number) {
        const { position, navigationState: {index} } = props;
        const prevIndex = prevProps.navigationState.index;
        const minIdx = Math.min(index, prevIndex);
        const maxIdx = Math.max(index, prevIndex);

        return position.interpolate({
            inputRange: [minIdx, maxIdx],
            outputRange: [5 + base, 25 + base],
        });
    }
    _renderFakedSEContainer(pairs, props, prevProps) {
        if (!prevProps) return null;

        const fromItemBBox = this._getBBox(pairs.map(p => p.fromItem.metrics));
        const toItemBBox = this._getBBox(pairs.map(p => p.toItem.metrics));
        const { position, progress, navigationState: {index} } = props;
        const prevIndex = prevProps.navigationState.index;
        const minIdx = Math.min(index, prevIndex);
        const maxIdx = Math.max(index, prevIndex);
        const inputRange = [minIdx, maxIdx];
        const adaptRange = (range) => index > prevIndex ? range : range.reverse();
        const left = position.interpolate({
            inputRange,
            outputRange: adaptRange([fromItemBBox.left, toItemBBox.left]),
        });
        const top = position.interpolate({
            inputRange,
            outputRange: adaptRange([fromItemBBox.top, toItemBBox.top]),
        });
        const { height: windowHeight, width: windowWidth } = Dimensions.get("window");
        const width = position.interpolate({
            inputRange,
            outputRange: [index > prevIndex ? fromItemBBox.width : toItemBBox.width, windowWidth],
        });
        const height = position.interpolate({
            inputRange,
            outputRange: [index > prevIndex ? fromItemBBox.height : toItemBBox.height, windowHeight],
        });
        const elevation = this._interpolateElevation(props, prevProps, 0);
        const style = {
            backgroundColor: '#e2e2e2',
            elevation,
            position: 'absolute',
            left,
            top,
            right: null,
            bottom: null,
            width,
            height,
        };
        return <Animated.View style={style} />;
    }
    _renderOverlay(props: NavigationTransitionProps, prevProps: NavigationTransitionProps) {
        // TODO change to routeName after switching to react-navigation
        const fromRoute = prevProps ? prevProps.scene.route.key : 'unknownRoute';
        const toRoute = props.scene.route.key;
        const pairs = this.state.sharedItems.getMeasuredItemPairs(fromRoute, toRoute);
        const sharedElements = pairs.map((pair, idx) => {
            const {fromItem, toItem} = pair;
            const animatedStyle = this._getSharedElementStyle(props, prevProps, fromItem, toItem);
            const element = fromItem.reactElement;
            const cloned = React.cloneElement(element, {
                onLayout: null,
                ref: null,
            });
            return (
                <Animated.View style={[animatedStyle]} key={idx}>
                    {cloned}
                </Animated.View>
            );
        });
        // const pairsStr = pairs.map(p => Object.keys(p).map(k => `${k}: ${JSON.stringify(p[k].metrics)}`))
        // console.log('from:', fromRoute, 'to:', toRoute, 'pairs:', pairsStr, 'allReady?:', this.state.sharedItems.areMetricsReadyForAllPairs(fromRoute, toRoute), 'items:', this.state.sharedItems._items.filter(i => i.metrics).map(i => `${i.name.slice(i.name.lastIndexOf('?'))} ${i.containerRouteName} ${JSON.stringify(i.metrics)}`));
        // console.log(this.state.sharedItems._items.map(i => `${i.name} ${i.containerRouteName} ${JSON.stringify(i.metrics)}`))
        // if (pairs.length > 0) {
        //     InteractionManager.runAfterInteractions(() => {
        //         this.setState((prevState: State): State => ({
        //             // remove metrics of sharedViews on the target scene so that it'll be re-measured when moving to the next scene.
        //             // This guarantees the location of the cloned view on the overlay is always consistent with the original view.
        //             sharedItems: prevState.sharedItems.updateMetrics(pairs.map(p => ({
        //                 name: p.toItem.name,
        //                 containerRouteName: p.toItem.containerRouteName,
        //                 metrics: null,
        //             }))),
        //             itemsToMeasure: pairs.map(p => p.toItem),
        //         }));
        //     });
        // }
        const containerStyle = this._getOverlayContainerStyle(props.progress);
        return (
            <Animated.View style={[styles.overlay, this.props.style, containerStyle]}>
                {sharedElements}
                {this._renderFakedSEContainer(pairs, props, prevProps)}
            </Animated.View>
        );
    }
    _renderDarkeningOverlay(progress, position, sceneIndex: number) {
        const backgroundColor = position.interpolate({
            inputRange: [sceneIndex - 1, sceneIndex, sceneIndex + 0.2, sceneIndex + 1],
            outputRange: ['rgba(0,0,0,0)', 'rgba(0,0,0,0)', 'rgba(0,0,0,0.4)', 'rgba(0,0,0,0.5)'],
        });
        const animatedStyle = {
            elevation: 5, // to ensure the overlay covers toolbar
            backgroundColor,
            ...this._getOverlayContainerStyle(progress)
        };
        return <Animated.View style={[styles.overlay, animatedStyle]} />;
    }
    _renderScene(props) {
        const { position, scene, progress } = props;
        const { index } = scene;
        const inputRange = [index - 1, index - 0.01, index, index + 0.99, index + 1];
        const opacity = position.interpolate({
            inputRange,
            outputRange: [0, 0, 1, 1, 0],
        });
        const style = { opacity };

        return (
            <Animated.View key={props.scene.route.key} style={[style, styles.scene]}>
                {this.props.renderScene(props)}
                {this._renderDarkeningOverlay(progress, position, index)}
            </Animated.View>
        )
    }
}

const styles = StyleSheet.create({
    scenes: {
        flex: 1,
    },
    scene: {
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
    },
    overlay: {
        position: 'absolute',
        top: 0,
        left: 100000, // invisible by default
        right: 0,
        bottom: 0,
    }
});

export default MaterialSharedElementTransitioner;<commit_msg>Make sure to preserve original itemsToShare to support multiple shared views<commit_after>// @flow
import React, { Component } from 'react';
import {
    View,
    NavigationExperimental,
    StyleSheet,
    Animated,
    Text,
    Dimensions,
    UIManager,
    InteractionManager,
} from 'react-native';

import type {NavigationTransitionProps } from 'NavigationTypeDefinition';

import SharedItems from './SharedItems';

import type { Metrics, SharedItem, UpdateRequest } from './SharedItems';

const {
    Transitioner,
} = NavigationExperimental;

type State = {
    sharedItems: SharedItems,
    itemsToMeasure: Array<SharedItem>,
}

class MaterialSharedElementTransitioner extends Component {
    state: State;
    static childContextTypes = {
        registerSharedView: React.PropTypes.func,
        unregisterSharedView: React.PropTypes.func,
    }
    constructor(props) {
        super(props);
        this.state = {
            sharedItems: new SharedItems(),
            itemsToMeasure: [],
        }
    }
    measure(sharedItem: SharedItem): Promise<Metrics> {
        // console.log('measuring:', sharedItem.name, sharedItem.containerRouteName)
        return new Promise((resolve, reject) => {
            UIManager.measureInWindow(
                sharedItem.nativeHandle,
                (x, y, width, height) => {
                    resolve({ x, y, width, height });
                }
            );
        });
    }
    setSharedItemsState(fun: (prevState: State) => SharedItems, callback) {
        this.setState((prevState) => (
            { sharedItems: fun(prevState) }
        ), callback);
    }
    addSharedItem(sharedItem: SharedItem) {
        this.setSharedItemsState(prevState =>
            prevState.sharedItems.add(sharedItem)
        );
    }
    removeSharedItem(name: string, containerRouteName: string) {
        this.setSharedItemsState(prevState =>
            prevState.sharedItems.remove(name, containerRouteName)
        );
    }
    getChildContext() {
        const self = this;
        return {
            registerSharedView(sharedItem: SharedItem) {
                self.addSharedItem(sharedItem);
                const {name, containerRouteName} = sharedItem;

                const matchingItem = self.state.sharedItems.findMatchByName(name, containerRouteName);
                // schedule to measure (on layout) if another view with the same name is mounted
                if (matchingItem) {
                    self.setState((prevState:State) => ({
                        sharedItems: prevState.sharedItems,
                        itemsToMeasure: [...prevState.itemsToMeasure, sharedItem, matchingItem]
                    }));
                }
            },
            unregisterSharedView(name: string, containerRouteName: string) {
                self.removeSharedItem(name, containerRouteName);
            },
        };
    }
    shouldComponentUpdate(nextProps, nextState: State) {
        /*
          state / prop changes
          - navigation change: nextProps !== this.props                       => true
          - onLayout: state: itemsToMeasure, sharedItems.metrics              => measured?
          - afterInteraction: state: itemsToMeasure, sharedElements.metrics   => false
          - register: state.sharedElements, state.itemsToMeasure              => false
          - unregister: statee.sharedElements                                 => false
        */
        return this.props !== nextProps || nextState.itemsToMeasure.length === 0;
    }
    async _onLayout() {
        let toUpdate = [];
        for (let item of this.state.itemsToMeasure) {
            const { name, containerRouteName } = item;
            const metrics = await this.measure(item);
            toUpdate.push({ name, containerRouteName, metrics });
        }
        if (toUpdate.length > 0) {
            // console.log('measured, setting meatured state:', toUpdate)
            this.setState((prevState: State): State => ({
                sharedItems: prevState.sharedItems.updateMetrics(toUpdate),
                itemsToMeasure: [],
            }));
        }
    }
    render() {
        return (
            <View style={{ flex: 1 }} onLayout={this._onLayout.bind(this)}>
                <Transitioner
                    configureTransition={this._configureTransition.bind(this)}
                    render={this._render.bind(this)}
                    navigationState={this.props.navigationState}
                    style={this.props.style}
                    />
            </View>
        );
    }
    _configureTransition() {
        return {
            duration: 300,
            // useNativeDriver: false,
        }
    }
    _render(props: NavigationTransitionProps, prevProps: NavigationTransitionProps) {
        const scenes = props.scenes.map(scene => this._renderScene({ ...props, scene }));
        const overlay = this._renderOverlay(props, prevProps);
        return (
            <View style={styles.scenes}>
                {scenes}
                {overlay}
            </View>
        )
    }
    _getOverlayContainerStyle(progress) {
        const left = progress.interpolate({
            inputRange: [0, 0.999999, 1],
            outputRange: [0, 0, 100000], // move it off screen after transition is done
        });
        return {
            left,
        };
    }
    _getSharedElementStyle(props, prevProps, itemFrom, itemTo) {
        const { position, progress, navigationState: {index} } = props;
        const prevIndex = prevProps.navigationState.index;
        const toVsFromScaleX = itemTo.scaleRelativeTo(itemFrom).x;
        const toVsFromScaleY = itemTo.scaleRelativeTo(itemFrom).y;
        const minIdx = Math.min(index, prevIndex);
        const maxIdx = Math.max(index, prevIndex);
        const inputRange = [minIdx, maxIdx];
        const adaptRange = (range) => index > prevIndex ? range : range.reverse();
        const scaleX = position.interpolate({
            inputRange,
            outputRange: adaptRange([1, toVsFromScaleX]),
        });
        const scaleY = position.interpolate({
            inputRange,
            outputRange: adaptRange([1, toVsFromScaleY]),
        });
        const width = itemFrom.metrics.width;
        const height = itemFrom.metrics.height;
        const left = position.interpolate({
            inputRange,
            outputRange: adaptRange([itemFrom.metrics.x, itemTo.metrics.x + width / 2 * (toVsFromScaleX - 1)]),
        });
        const top = position.interpolate({
            inputRange,
            outputRange: adaptRange([itemFrom.metrics.y, itemTo.metrics.y + height / 2 * (toVsFromScaleY - 1)]),
        });
        return {
            width,
            height,
            elevation: this._interpolateElevation(props, prevProps, 1), // make sure shared elements stay above the faked container
            position: 'absolute',
            left,
            top,
            right: null,
            bottom: null,
            transform: [
                { scaleX },
                { scaleY },
            ],
        };
    }
    _getBBox(metricsArray: Array<Metrics>) {
        let left, top, right, bottom;
        left = top = Number.MAX_VALUE;
        right = bottom = Number.MIN_VALUE;
        metricsArray.forEach(m => {
            if (m.x < left) left = m.x;
            if (m.y < top) top = m.y;
            if (m.x + m.width > right) right = m.x + m.width;
            if (m.y + m.height > bottom) bottom = m.y + m.height;
        });
        const width = right - left;
        const height = bottom - top;
        return { left, top, right, bottom, width, height };
    }
    _interpolateElevation(props, prevProps, base: number) {
        const { position, navigationState: {index} } = props;
        const prevIndex = prevProps.navigationState.index;
        const minIdx = Math.min(index, prevIndex);
        const maxIdx = Math.max(index, prevIndex);

        return position.interpolate({
            inputRange: [minIdx, maxIdx],
            outputRange: [5 + base, 25 + base],
        });
    }
    _renderFakedSEContainer(pairs, props, prevProps) {
        if (!prevProps) return null;

        const fromItemBBox = this._getBBox(pairs.map(p => p.fromItem.metrics));
        const toItemBBox = this._getBBox(pairs.map(p => p.toItem.metrics));
        const { position, progress, navigationState: {index} } = props;
        const prevIndex = prevProps.navigationState.index;
        const minIdx = Math.min(index, prevIndex);
        const maxIdx = Math.max(index, prevIndex);
        const inputRange = [minIdx, maxIdx];
        const adaptRange = (range) => index > prevIndex ? range : range.reverse();
        const left = position.interpolate({
            inputRange,
            outputRange: adaptRange([fromItemBBox.left, toItemBBox.left]),
        });
        const top = position.interpolate({
            inputRange,
            outputRange: adaptRange([fromItemBBox.top, toItemBBox.top]),
        });
        const { height: windowHeight, width: windowWidth } = Dimensions.get("window");
        const width = position.interpolate({
            inputRange,
            outputRange: [index > prevIndex ? fromItemBBox.width : toItemBBox.width, windowWidth],
        });
        const height = position.interpolate({
            inputRange,
            outputRange: [index > prevIndex ? fromItemBBox.height : toItemBBox.height, windowHeight],
        });
        const elevation = this._interpolateElevation(props, prevProps, 0);
        const style = {
            backgroundColor: '#e2e2e2',
            elevation,
            position: 'absolute',
            left,
            top,
            right: null,
            bottom: null,
            width,
            height,
        };
        return <Animated.View style={style} />;
    }
    _renderOverlay(props: NavigationTransitionProps, prevProps: NavigationTransitionProps) {
        // TODO change to routeName after switching to react-navigation
        const fromRoute = prevProps ? prevProps.scene.route.key : 'unknownRoute';
        const toRoute = props.scene.route.key;
        const pairs = this.state.sharedItems.getMeasuredItemPairs(fromRoute, toRoute);
        const sharedElements = pairs.map((pair, idx) => {
            const {fromItem, toItem} = pair;
            const animatedStyle = this._getSharedElementStyle(props, prevProps, fromItem, toItem);
            const element = fromItem.reactElement;
            const cloned = React.cloneElement(element, {
                onLayout: null,
                ref: null,
            });
            return (
                <Animated.View style={[animatedStyle]} key={idx}>
                    {cloned}
                </Animated.View>
            );
        });
        // const pairsStr = pairs.map(p => Object.keys(p).map(k => `${k}: ${JSON.stringify(p[k].metrics)}`))
        // console.log('from:', fromRoute, 'to:', toRoute, 'pairs:', pairsStr, 'items:', this.state.sharedItems._items.filter(i => i.metrics).map(i => `${i.name} ${i.containerRouteName} ${JSON.stringify(i.metrics)}`));
        // console.log(this.state.sharedItems._items.map(i => `${i.name} ${i.containerRouteName} ${JSON.stringify(i.metrics)}`))
        // if (pairs.length > 0) {
        //     InteractionManager.runAfterInteractions(() => {
        //         this.setState((prevState: State): State => ({
        //             // remove metrics of sharedViews on the target scene so that it'll be re-measured when moving to the next scene.
        //             // This guarantees the location of the cloned view on the overlay is always consistent with the original view.
        //             sharedItems: prevState.sharedItems.updateMetrics(pairs.map(p => ({
        //                 name: p.toItem.name,
        //                 containerRouteName: p.toItem.containerRouteName,
        //                 metrics: null,
        //             }))),
        //             itemsToMeasure: pairs.map(p => p.toItem),
        //         }));
        //     });
        // }
        const containerStyle = this._getOverlayContainerStyle(props.progress);
        return (
            <Animated.View style={[styles.overlay, this.props.style, containerStyle]}>
                {sharedElements}
                {this._renderFakedSEContainer(pairs, props, prevProps)}
            </Animated.View>
        );
    }
    _renderDarkeningOverlay(progress, position, sceneIndex: number) {
        const backgroundColor = position.interpolate({
            inputRange: [sceneIndex - 1, sceneIndex, sceneIndex + 0.2, sceneIndex + 1],
            outputRange: ['rgba(0,0,0,0)', 'rgba(0,0,0,0)', 'rgba(0,0,0,0.4)', 'rgba(0,0,0,0.5)'],
        });
        const animatedStyle = {
            elevation: 5, // to ensure the overlay covers toolbar
            backgroundColor,
            ...this._getOverlayContainerStyle(progress)
        };
        return <Animated.View style={[styles.overlay, animatedStyle]} />;
    }
    _renderScene(props) {
        const { position, scene, progress } = props;
        const { index } = scene;
        const inputRange = [index - 1, index - 0.01, index, index + 0.99, index + 1];
        const opacity = position.interpolate({
            inputRange,
            outputRange: [0, 0, 1, 1, 0],
        });
        const style = { opacity };

        return (
            <Animated.View key={props.scene.route.key} style={[style, styles.scene]}>
                {this.props.renderScene(props)}
                {this._renderDarkeningOverlay(progress, position, index)}
            </Animated.View>
        )
    }
}

const styles = StyleSheet.create({
    scenes: {
        flex: 1,
    },
    scene: {
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
    },
    overlay: {
        position: 'absolute',
        top: 0,
        left: 100000, // invisible by default
        right: 0,
        bottom: 0,
    }
});

export default MaterialSharedElementTransitioner;<|endoftext|>