<commit_before>'use strict';

const { iso8601 } = require ('ccxt/js/base/functions/time')
const { extend, deepExtend } = require ('ccxt/js/base/functions/generic.js')
    , {
        Asks,
        Bids,
        LimitedAsks,
        LimitedBids,
        CountedAsks,
        CountedBids,
        IndexedAsks,
        IndexedBids,
        IncrementalAsks,
        IncrementalBids,
        LimitedIndexedAsks,
        LimitedIndexedBids,
        LimitedCountedAsks,
        LimitedCountedBids,
        IncrementalIndexedAsks,
        IncrementalIndexedBids,
    } = require ('./OrderBookSide')

// ----------------------------------------------------------------------------
// overwrites absolute volumes at price levels

class OrderBook {

    constructor (snapshot = {}) {

        const defaults = {
            'bids': [],
            'asks': [],
            'timestamp': undefined,
            'datetime': undefined,
            'nonce': undefined,
        }

        // merge to this
        const entries = Object.entries (extend (defaults, snapshot))
        for (let i = 0; i < entries.length; i++) {
            const [property, value] = entries[i]
            this[property] = value
        }

        // wrap plain arrays with Bids/Asks classes if necessary
        if (this.asks.constructor.name === 'Array') {
            this.asks = new Asks (this.asks)
        }
        if (this.bids.constructor.name === 'Array') {
            this.bids = new Bids (this.bids)
        }
        if (this.timestamp) {
            this.datetime = iso8601 (this.timestamp)
        }
    }

    limit (n = undefined) {
        this.asks.limit (n)
        this.bids.limit (n)
        return this
    }

    update (snapshot) {
        if ((snapshot.nonce !== undefined) &&
            (this.nonce !== undefined) &&
            (snapshot.nonce <= this.nonce)) {
            return this
        }
        return this.reset (
            snapshot.nonce,
            snapshot.timestamp,
            snapshot.asks,
            snapshot.bids
        )
    }

    reset (snapshot) {
        this.asks.update (snapshot.asks)
        this.bids.update (snapshot.bids)
        this.nonce = snapshot.nonce
        this.timestamp = snapshot.timestamp
        this.datetime = iso8601 (this.timestamp)
        return this
    }
}

// ----------------------------------------------------------------------------
// some exchanges limit the number of bids/asks in the aggregated orderbook
// orders beyond the limit threshold are not updated with new ws deltas
// those orders should not be returned to the user, they are outdated quickly

class LimitedOrderBook extends OrderBook {
    constructor (snapshot = {}, depth = undefined) {
        super (extend (snapshot, {
            'asks': new LimitedAsks (snapshot.asks || [], depth),
            'bids': new LimitedBids (snapshot.bids || [], depth),
        }))
    }
}

// ----------------------------------------------------------------------------
// overwrites absolute volumes at price levels
// or deletes price levels based on order counts (3rd value in a bidask delta)

class CountedOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new CountedAsks (snapshot.asks || []),
            'bids': new CountedBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------
// indexed by order ids (3rd value in a bidask delta)

class IndexedOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new IndexedAsks (snapshot.asks || []),
            'bids': new IndexedBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------
// adjusts the volumes by positive or negative relative changes or differences

class IncrementalOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new IncrementalAsks (snapshot.asks || []),
            'bids': new IncrementalBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------
// limited and indexed (2 in 1)

class LimitedIndexedOrderBook extends OrderBook {
    constructor (snapshot = {}, depth = undefined) {
        super (extend (snapshot, {
            'asks': new LimitedIndexedAsks (snapshot.asks || [], depth),
            'bids': new LimitedIndexedBids (snapshot.bids || [], depth),
        }))
    }
}

// ----------------------------------------------------------------------------
// limited and indexed (2 in 1)

class LimitedCountedOrderBook extends OrderBook {
    constructor (snapshot = {}, depth = undefined) {
        super (extend (snapshot, {
            'asks': new LimitedCountedAsks (snapshot.asks || [], depth),
            'bids': new LimitedCountedBids (snapshot.bids || [], depth),
        }))
    }
}

// ----------------------------------------------------------------------------
// incremental and indexed (2 in 1)

class IncrementalIndexedOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new IncrementalIndexedAsks (snapshot.asks || []),
            'bids': new IncrementalIndexedBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------

module.exports = {
    OrderBook,
    LimitedOrderBook,
    CountedOrderBook,
    IndexedOrderBook,
    IncrementalOrderBook,
    LimitedIndexedOrderBook,
    LimitedCountedOrderBook,
    IncrementalIndexedOrderBook,
}
<commit_msg>OrderBook.js cache wip<commit_after>'use strict';

const { iso8601 } = require ('ccxt/js/base/functions/time')
const { extend, deepExtend } = require ('ccxt/js/base/functions/generic.js')
    , {
        Asks,
        Bids,
        LimitedAsks,
        LimitedBids,
        CountedAsks,
        CountedBids,
        IndexedAsks,
        IndexedBids,
        IncrementalAsks,
        IncrementalBids,
        LimitedIndexedAsks,
        LimitedIndexedBids,
        LimitedCountedAsks,
        LimitedCountedBids,
        IncrementalIndexedAsks,
        IncrementalIndexedBids,
    } = require ('./OrderBookSide')

// ----------------------------------------------------------------------------
// overwrites absolute volumes at price levels

class OrderBook {

    constructor (snapshot = {}) {

        Object.defineProperty (this, 'cache', {
            __proto__: null, // make it invisible
            value: [],
            writable: true,
        })

        const defaults = {
            'bids': [],
            'asks': [],
            'timestamp': undefined,
            'datetime': undefined,
            'nonce': undefined,
        }

        // merge to this
        const entries = Object.entries (extend (defaults, snapshot))
        for (let i = 0; i < entries.length; i++) {
            const [property, value] = entries[i]
            this[property] = value
        }

        // wrap plain arrays with Bids/Asks classes if necessary
        if (this.asks.constructor.name === 'Array') {
            this.asks = new Asks (this.asks)
        }
        if (this.bids.constructor.name === 'Array') {
            this.bids = new Bids (this.bids)
        }
        if (this.timestamp) {
            this.datetime = iso8601 (this.timestamp)
        }
    }

    limit (n = undefined) {
        this.asks.limit (n)
        this.bids.limit (n)
        return this
    }

    update (snapshot) {
        if ((snapshot.nonce !== undefined) &&
            (this.nonce !== undefined) &&
            (snapshot.nonce <= this.nonce)) {
            return this
        }
        return this.reset (
            snapshot.nonce,
            snapshot.timestamp,
            snapshot.asks,
            snapshot.bids
        )
    }

    reset (snapshot) {
        this.asks.update (snapshot.asks)
        this.bids.update (snapshot.bids)
        this.nonce = snapshot.nonce
        this.timestamp = snapshot.timestamp
        this.datetime = iso8601 (this.timestamp)
        return this
    }
}

// ----------------------------------------------------------------------------
// some exchanges limit the number of bids/asks in the aggregated orderbook
// orders beyond the limit threshold are not updated with new ws deltas
// those orders should not be returned to the user, they are outdated quickly

class LimitedOrderBook extends OrderBook {
    constructor (snapshot = {}, depth = undefined) {
        super (extend (snapshot, {
            'asks': new LimitedAsks (snapshot.asks || [], depth),
            'bids': new LimitedBids (snapshot.bids || [], depth),
        }))
    }
}

// ----------------------------------------------------------------------------
// overwrites absolute volumes at price levels
// or deletes price levels based on order counts (3rd value in a bidask delta)

class CountedOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new CountedAsks (snapshot.asks || []),
            'bids': new CountedBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------
// indexed by order ids (3rd value in a bidask delta)

class IndexedOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new IndexedAsks (snapshot.asks || []),
            'bids': new IndexedBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------
// adjusts the volumes by positive or negative relative changes or differences

class IncrementalOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new IncrementalAsks (snapshot.asks || []),
            'bids': new IncrementalBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------
// limited and indexed (2 in 1)

class LimitedIndexedOrderBook extends OrderBook {
    constructor (snapshot = {}, depth = undefined) {
        super (extend (snapshot, {
            'asks': new LimitedIndexedAsks (snapshot.asks || [], depth),
            'bids': new LimitedIndexedBids (snapshot.bids || [], depth),
        }))
    }
}

// ----------------------------------------------------------------------------
// limited and indexed (2 in 1)

class LimitedCountedOrderBook extends OrderBook {
    constructor (snapshot = {}, depth = undefined) {
        super (extend (snapshot, {
            'asks': new LimitedCountedAsks (snapshot.asks || [], depth),
            'bids': new LimitedCountedBids (snapshot.bids || [], depth),
        }))
    }
}

// ----------------------------------------------------------------------------
// incremental and indexed (2 in 1)

class IncrementalIndexedOrderBook extends OrderBook {
    constructor (snapshot = {}) {
        super (extend (snapshot, {
            'asks': new IncrementalIndexedAsks (snapshot.asks || []),
            'bids': new IncrementalIndexedBids (snapshot.bids || []),
        }))
    }
}

// ----------------------------------------------------------------------------

module.exports = {
    OrderBook,
    LimitedOrderBook,
    CountedOrderBook,
    IndexedOrderBook,
    IncrementalOrderBook,
    LimitedIndexedOrderBook,
    LimitedCountedOrderBook,
    IncrementalIndexedOrderBook,
}
<|endoftext|>


<commit_before>function getAverageVolume(array) {
	var values = 0;
	var average;

	var length = array.length;

	// get all the frequency amplitudes
	for (var i = 0; i < length; i++) {
		values += array[i];
	}

	average = values / length;
	return average;
}

// BirdSongPlayer
function BirdSongPlayer(audioContext, inCanvasID) {
	this.soundSource = null;

	this.sighting = null;
	this.sightingIndex = null;
	this.soundsForSighting = null;

	this.recordingIndex = null;
	this.recording = null;

	this.gain = audioContext.createGain();

	// see https://developer.mozilla.org/en-US/docs/Web/API/PannerNode
	this.panner = audioContext.createPanner();
	this.panner.panningModel = 'HRTF';
	this.panner.distanceModel = 'inverse';
	this.panner.refDistance = 1;
	this.panner.maxDistance = 10000;
	this.panner.rolloffFactor = 1;
	this.panner.coneInnerAngle = 360;
	this.panner.coneOuterAngle = 0;
	this.panner.coneOuterGain = 0;
	this.panner.setOrientation(1,0,0);
	this.panner.setPosition(2 * Math.random() - 1, 2 * Math.random() - 1, 2 * Math.random() - 1);
	this.panner.setVelocity(0, 0, 0);

	this.gain.connect(this.panner);

	var volumeMeterCanvas = document.getElementById(inCanvasID);
	var graphicsContext = volumeMeterCanvas.getContext('2d');

	// setup a analyzer
	var analyser = audioContext.createAnalyser();
	this.panner.connect(analyser);
	this.panner.connect(audioContext.destination);

	analyser.smoothingTimeConstant = 0.3;
	analyser.fftSize = 64;

	requestAnimationFrame(function vuMeter() {
		// get the average, bincount is fftsize / 2
		var array =  new Uint8Array(analyser.frequencyBinCount);
		analyser.getByteFrequencyData(array);
		var average = getAverageVolume(array);
		average = Math.max(Math.min(average, 125), 5);

		// clear the current state
		graphicsContext.fillStyle = 'rgb(220,220,220)'
		graphicsContext.fillRect(1, 5, 130, 15);

		// set the fill style
		graphicsContext.fillStyle = 'rgb(0,0,0)'

		// create the meters
		graphicsContext.fillRect(1, 5, average, 15);

		requestAnimationFrame(vuMeter);
	});
}

// sets the X,Y,Z position of the Panner to random values between -1 and +1
BirdSongPlayer.prototype.randomizePanner = function() {
	this.panner.setPosition(2 * Math.random() - 1, 2 * Math.random() - 1, 2 * Math.random() - 1);
}

// Sets the playback rate for the current sound to a random value between 0.1 and 1.1
BirdSongPlayer.prototype.randomizePlaybackRate = function(inPlayerSelector) {
	if (this.soundSource) {
		var newRate = 0.1 + Math.random();
		this.soundSource.playbackRate.cancelScheduledValues(gAudioContext.currentTime);
		this.soundSource.playbackRate.linearRampToValueAtTime(newRate, gAudioContext.currentTime + 3);
		$(inPlayerSelector).find('.playbackRate').text((Math.round(100 * newRate) / 100.0) + "x");
	} else {
		console.log('cannot randomize, no sound source');
	}
}

// Creates a new soundSource using the given buffer
// preserves the old playback rate, if applicable
BirdSongPlayer.prototype.setSourceFromBuffer = function(inBuffer) {
	var oldPlaybackRateValue = 1;

	if (this.soundSource) {
		oldPlaybackRateValue = this.soundSource.playbackRate.value;
		this.soundSource.stop(0);
		this.soundSource = null;
	}

	this.soundSource = gAudioContext.createBufferSource();
	this.soundSource.connect(this.gain);
	this.soundSource.buffer = inBuffer;
	// restore old playback rate value
	this.soundSource.playbackRate.setValueAtTime(oldPlaybackRateValue, gAudioContext.currentTime);

	// set gain to zero and then ramp up.
	this.gain.gain.cancelScheduledValues(gAudioContext.currentTime);
	this.gain.gain.setValueAtTime(0.0, gAudioContext.currentTime);
	this.gain.gain.linearRampToValueAtTime(0.99, gAudioContext.currentTime + 10);

	// start playing immediately in a loop
	this.soundSource.loop = true;
	this.soundSource.start(0);
}

// Reverses the audio buffer so that the sound plays backwards
BirdSongPlayer.prototype.reversePlayback = function() {
	console.log('reversePlayback');

	var oldData = this.soundSource.buffer.getChannelData(0);
	var normalData = Array.prototype.slice.call( oldData );
	Array.prototype.reverse.call(normalData);

	var newBuffer = gAudioContext.createBuffer(1, this.soundSource.buffer.length, this.soundSource.buffer.sampleRate);
	var newData = newBuffer.getChannelData(0);

	for (i = 0; i < newBuffer.length; i++) {
		newData[i] = normalData[i];
	}

	this.setSourceFromBuffer(newBuffer);

	console.log('DONE reversePlayback');
}

BirdSongPlayer.prototype.setBufferFromURL = function(inSoundDataURL, inPlayerSelector) {
	console.log('setBufferFromURL ' + inSoundDataURL);

	var mp3Request = new XMLHttpRequest();

	mp3Request.onerror = function(e) {
		$(inPlayerSelector).find('.status').text('error downloading');
	};

	mp3Request.onprogress = function(e) {
		$(inPlayerSelector).find('.status').text('downloading ' + Math.round(100 * e.loaded / e.total) + '%');
	};

	mp3Request.onload = function(e) {
		$(inPlayerSelector).find('.status').text('decoding');

	    gAudioContext.decodeAudioData(mp3Request.response, function(decodedBuffer) {
	    	this.setSourceFromBuffer(decodedBuffer);
			$(inPlayerSelector).find('.status').text(Math.round(decodedBuffer.duration) + 's');
			$(inPlayerSelector).find('.recordingLocation').text(this.recording.loc);
			$(inPlayerSelector).find('.recordist').text(this.recording.rec);

			var licenseIcon = '';

			if (this.recording.lic.indexOf('by-nc-nd') > 0) {
				licenseIcon = 'http://i.creativecommons.org/l/by-nc-nd/3.0/us/88x31.png';
			} else if (this.recording.lic.indexOf('by-nc-sa') > 0) {
				licenseIcon = 'http://i.creativecommons.org/l/by-nc-sa/3.0/us/88x31.png';
			}

			$(inPlayerSelector).find('.license').attr('src', licenseIcon);

		}.bind(this));
	}.bind(this);

	mp3Request.open("GET", inSoundDataURL, true);
	mp3Request.responseType = 'arraybuffer';
	mp3Request.send();
}

BirdSongPlayer.prototype.chooseRandomRecording = function(inPlayerSelector) {
	if (this.soundsForSighting == null || this.soundsForSighting.recordings.length == 0) {
		$(inPlayerSelector).find('.status').text('retrying');
		console.log('FAILED loading recording for, retrying');
		this.chooseSightingAndPlayRandomSound(inPlayerSelector);
	} else {
		this.recordingIndex = Math.floor(Math.random() * this.soundsForSighting.recordings.length);
		this.recording = this.soundsForSighting.recordings[this.recordingIndex];

		// rewrite URL's from xeno-canto JSON, route through my own server due to missing CORS
		var soundURL = this.recording.file.replace('http://www.xeno-canto.org','/soundfile');

		$(inPlayerSelector).find('.status').text('downloading #' + this.recordingIndex);
		this.setBufferFromURL(soundURL, inPlayerSelector);
	}
}

BirdSongPlayer.prototype.initializeFromSavedSession = function(inSavedData, inPlayerSelector) {
	console.log('restoring ' + inPlayerSelector);
	console.lo9g(inSavedData);
	this.sightingIndex = 0;
	this.sighting = inSavedData.sighting;

	$(inPlayerSelector).find('.speciesName').text(this.sightingIndex + '. ' + this.sighting.comName);
	$(inPlayerSelector).find('.locationName').text(this.sighting.locName);

	this.soundsForSighting = {};
	this.soundsForSighting.recordings = [inSavedData.recording];
	this.chooseRandomRecording(inPlayerSelector);
}

BirdSongPlayer.prototype.saveData = function() {
	return {
		recording: this.recording,
		sighting: this.sighting
	};
}

BirdSongPlayer.prototype.chooseSightingAndPlayRandomSound = function(inPlayerSelector) {
	this.sightingIndex = gBirds.chooseRandomSighting();
	this.sighting = gBirds.sightings[this.sightingIndex];

	console.log('chooseSightingAndPlayRandomSound random sighting ' + this.sightingIndex);
	console.log(this.sighting);

	$(inPlayerSelector).find('.speciesName').text(this.sightingIndex + '. ' + this.sighting.comName);
	$(inPlayerSelector).find('.locationName').text(this.sighting.locName);
	$(inPlayerSelector).find('.status').text('choosing');

	// get sounds for this species if needed, and pick one at random
	gBirds.getSoundsForSightingIndex(this.sightingIndex, function(soundsData) {
		if (soundsData == null) {
			console.log('NO SOUNDS');
			$(inPlayerSelector).find('.status').text('retrying');
			this.chooseSightingAndPlayRandomSound(inPlayerSelector);
		} else {
			this.soundsForSighting = soundsData;
			this.chooseRandomRecording(inPlayerSelector);
		}
	}.bind(this));
}

<commit_msg>store playback rate in an instance variable for ease of save and restore<commit_after>function getAverageVolume(array) {
	var values = 0;
	var average;

	var length = array.length;

	// get all the frequency amplitudes
	for (var i = 0; i < length; i++) {
		values += array[i];
	}

	average = values / length;
	return average;
}

// BirdSongPlayer
function BirdSongPlayer(audioContext, inCanvasID) {
	this.soundSource = null;
	this.playbackRate = 1.0;

	this.sighting = null;
	this.sightingIndex = null;
	this.soundsForSighting = null;

	this.recordingIndex = null;
	this.recording = null;

	this.gain = audioContext.createGain();

	// see https://developer.mozilla.org/en-US/docs/Web/API/PannerNode
	this.panner = audioContext.createPanner();
	this.panner.panningModel = 'HRTF';
	this.panner.distanceModel = 'inverse';
	this.panner.refDistance = 1;
	this.panner.maxDistance = 10000;
	this.panner.rolloffFactor = 1;
	this.panner.coneInnerAngle = 360;
	this.panner.coneOuterAngle = 0;
	this.panner.coneOuterGain = 0;
	this.panner.setOrientation(1,0,0);
	this.panner.setPosition(2 * Math.random() - 1, 2 * Math.random() - 1, 2 * Math.random() - 1);
	this.panner.setVelocity(0, 0, 0);

	this.gain.connect(this.panner);

	var volumeMeterCanvas = document.getElementById(inCanvasID);
	var graphicsContext = volumeMeterCanvas.getContext('2d');

	// setup a analyzer
	var analyser = audioContext.createAnalyser();
	this.panner.connect(analyser);
	this.panner.connect(audioContext.destination);

	analyser.smoothingTimeConstant = 0.3;
	analyser.fftSize = 64;

	requestAnimationFrame(function vuMeter() {
		// get the average, bincount is fftsize / 2
		var array =  new Uint8Array(analyser.frequencyBinCount);
		analyser.getByteFrequencyData(array);
		var average = getAverageVolume(array);
		average = Math.max(Math.min(average, 125), 5);

		// clear the current state
		graphicsContext.fillStyle = 'rgb(220,220,220)'
		graphicsContext.fillRect(1, 5, 130, 15);

		// set the fill style
		graphicsContext.fillStyle = 'rgb(0,0,0)'

		// create the meters
		graphicsContext.fillRect(1, 5, average, 15);

		requestAnimationFrame(vuMeter);
	});
}

// sets the X,Y,Z position of the Panner to random values between -1 and +1
BirdSongPlayer.prototype.randomizePanner = function() {
	this.panner.setPosition(2 * Math.random() - 1, 2 * Math.random() - 1, 2 * Math.random() - 1);
}

// Sets the playback rate for the current sound to a random value between 0.1 and 1.1
BirdSongPlayer.prototype.randomizePlaybackRate = function(inPlayerSelector) {
	if (this.soundSource) {
		this.playbackRate = 0.1 + Math.random();
		this.soundSource.playbackRate.cancelScheduledValues(gAudioContext.currentTime);
		this.soundSource.playbackRate.linearRampToValueAtTime(this.playbackRate, gAudioContext.currentTime + 3);
		$(inPlayerSelector).find('.playbackRate').text((Math.round(100 * this.playbackRate) / 100.0) + "x");
	} else {
		console.log('cannot randomize, no sound source');
	}
}

// Creates a new soundSource using the given buffer
// preserves the old playback rate, if applicable
BirdSongPlayer.prototype.setSourceFromBuffer = function(inBuffer) {
	if (this.soundSource) {
		this.soundSource.stop(0);
		this.soundSource = null;
	}

	this.soundSource = gAudioContext.createBufferSource();
	this.soundSource.connect(this.gain);
	this.soundSource.buffer = inBuffer;
	// restore old playback rate value
	console.log('restore playbackRate to ' + this.playbackRate);
	this.soundSource.playbackRate.setValueAtTime(this.playbackRate, gAudioContext.currentTime);

	// set gain to zero and then ramp up.
	this.gain.gain.cancelScheduledValues(gAudioContext.currentTime);
	this.gain.gain.setValueAtTime(0.0, gAudioContext.currentTime);
	this.gain.gain.linearRampToValueAtTime(0.99, gAudioContext.currentTime + 10);

	// start playing immediately in a loop
	this.soundSource.loop = true;
	this.soundSource.start(0);
}

// Reverses the audio buffer so that the sound plays backwards
BirdSongPlayer.prototype.reversePlayback = function() {
	console.log('reversePlayback');

	var oldData = this.soundSource.buffer.getChannelData(0);
	var normalData = Array.prototype.slice.call( oldData );
	Array.prototype.reverse.call(normalData);

	var newBuffer = gAudioContext.createBuffer(1, this.soundSource.buffer.length, this.soundSource.buffer.sampleRate);
	var newData = newBuffer.getChannelData(0);

	for (i = 0; i < newBuffer.length; i++) {
		newData[i] = normalData[i];
	}

	this.setSourceFromBuffer(newBuffer);

	console.log('DONE reversePlayback');
}

BirdSongPlayer.prototype.setBufferFromURL = function(inSoundDataURL, inPlayerSelector) {
	console.log('setBufferFromURL ' + inSoundDataURL);

	var mp3Request = new XMLHttpRequest();

	mp3Request.onerror = function(e) {
		$(inPlayerSelector).find('.status').text('error downloading');
	};

	mp3Request.onprogress = function(e) {
		$(inPlayerSelector).find('.status').text('downloading ' + Math.round(100 * e.loaded / e.total) + '%');
	};

	mp3Request.onload = function(e) {
		$(inPlayerSelector).find('.status').text('decoding');

	    gAudioContext.decodeAudioData(mp3Request.response, function(decodedBuffer) {
	    	this.setSourceFromBuffer(decodedBuffer);
			$(inPlayerSelector).find('.status').text(Math.round(decodedBuffer.duration) + 's');
			$(inPlayerSelector).find('.recordingLocation').text(this.recording.loc);
			$(inPlayerSelector).find('.recordist').text(this.recording.rec);
			$(inPlayerSelector).find('.playbackRate').text((Math.round(100 * this.playbackRate) / 100.0) + "x");

			var licenseIcon = '';

			if (this.recording.lic.indexOf('by-nc-nd') > 0) {
				licenseIcon = 'http://i.creativecommons.org/l/by-nc-nd/3.0/us/88x31.png';
			} else if (this.recording.lic.indexOf('by-nc-sa') > 0) {
				licenseIcon = 'http://i.creativecommons.org/l/by-nc-sa/3.0/us/88x31.png';
			}

			$(inPlayerSelector).find('.license').attr('src', licenseIcon);

		}.bind(this));
	}.bind(this);

	mp3Request.open("GET", inSoundDataURL, true);
	mp3Request.responseType = 'arraybuffer';
	mp3Request.send();
}

BirdSongPlayer.prototype.chooseRandomRecording = function(inPlayerSelector) {
	if (this.soundsForSighting == null || this.soundsForSighting.recordings.length == 0) {
		$(inPlayerSelector).find('.status').text('retrying');
		console.log('FAILED loading recording for, retrying');
		this.chooseSightingAndPlayRandomSound(inPlayerSelector);
	} else {
		this.recordingIndex = Math.floor(Math.random() * this.soundsForSighting.recordings.length);
		this.recording = this.soundsForSighting.recordings[this.recordingIndex];

		// rewrite URL's from xeno-canto JSON, route through my own server due to missing CORS
		var soundURL = this.recording.file.replace('http://www.xeno-canto.org','/soundfile');

		$(inPlayerSelector).find('.status').text('downloading #' + this.recordingIndex);
		this.setBufferFromURL(soundURL, inPlayerSelector);
	}
}

BirdSongPlayer.prototype.initializeFromSavedSession = function(inSavedData, inPlayerSelector) {
	console.log('restoring ' + inPlayerSelector);
	console.lo9g(inSavedData);
	this.sightingIndex = 0;
	this.sighting = inSavedData.sighting;

	$(inPlayerSelector).find('.speciesName').text(this.sightingIndex + '. ' + this.sighting.comName);
	$(inPlayerSelector).find('.locationName').text(this.sighting.locName);

	this.playbackRate = parseFloat(inSavedData.playbackRate);
	console.log('restored playbackRate ' + this.playbackRate);
	this.soundsForSighting = {};
	this.soundsForSighting.recordings = [inSavedData.recording];
	this.chooseRandomRecording(inPlayerSelector);
}

BirdSongPlayer.prototype.saveData = function() {
	return {
		recording: this.recording,
		sighting: this.sighting,
		playbackRate: this.playbackRate
	};
}

BirdSongPlayer.prototype.chooseSightingAndPlayRandomSound = function(inPlayerSelector) {
	this.sightingIndex = gBirds.chooseRandomSighting();
	this.sighting = gBirds.sightings[this.sightingIndex];

	console.log('chooseSightingAndPlayRandomSound random sighting ' + this.sightingIndex);
	console.log(this.sighting);

	$(inPlayerSelector).find('.speciesName').text(this.sightingIndex + '. ' + this.sighting.comName);
	$(inPlayerSelector).find('.locationName').text(this.sighting.locName);
	$(inPlayerSelector).find('.status').text('choosing');

	// get sounds for this species if needed, and pick one at random
	gBirds.getSoundsForSightingIndex(this.sightingIndex, function(soundsData) {
		if (soundsData == null) {
			console.log('NO SOUNDS');
			$(inPlayerSelector).find('.status').text('retrying');
			this.chooseSightingAndPlayRandomSound(inPlayerSelector);
		} else {
			this.soundsForSighting = soundsData;
			this.chooseRandomRecording(inPlayerSelector);
		}
	}.bind(this));
}

<|endoftext|>


<commit_before>XMLDocument.prototype.init= function (startNode) {
	if (!startNode) {
		startNode = this.documentElement;
	}
	var walker = this.createTreeWalker(
		startNode,NodeFilter.SHOW_ALL,
	{
		acceptNode : function(node) {
			return NodeFilter.FILTER_ACCEPT;
		}
	}
	, true);

	var node = startNode;
	var firstChild = false;
	if (startNode == this.documentElement) {
		this.XMLNode = new XMLNodeDocument();
		this.XMLNode._node = this;
	}
	do  {
		if (node.nodeType == 1) {
			node.XMLNode = new XMLNodeElement(node);
		} else {
			node.XMLNode = new XMLNode(node);
		}
		node = walker.nextNode();
	}  while(node );

	walker.currentNode = startNode;
	if (startNode == this.documentElement) {
		this.XMLNode.documentElement = this.documentElement.XMLNode;
		this.XMLNode.nodeType = 9;
		this.documentElement.XMLNode.parentNode = this.XMLNode;
	} else {
	}

	node = walker.currentNode;
	do  {

		x = node.XMLNode;
		x.ownerDocument = this.XMLNode;
		if (node.parentNode) {
			x.parentNode = node.parentNode.XMLNode;
		}
		if (node.previousSibling) {
			x.previousSibling = node.previousSibling.XMLNode;
		}
		if (node.nextSibling) {
			x.nextSibling = node.nextSibling.XMLNode;
		}
		if (node.firstChild) {
			x.firstChild = node.firstChild.XMLNode;
		}
		if (node.lastChild) {
			x.lastChild = node.lastChild.XMLNode;
		}
		x.nodeType = node.nodeType;
		x.prefix = node.prefix;
		node = walker.nextNode();
	}  while(node );
	return startNode.XMLNode;
/*	var xw = new XMLNodeWalker(this.documentElement.XMLNode.firstChild);
	node = xw.currentNode;
	while (node) {

		dump(node + node.localName + "\n");
		node = xw.nextNode();

	}*/

}

XMLDocument.prototype.insertIntoHTMLDocument = function(htmlnode) {

	var nsResolver = new bxe_nsResolver(this.documentElement);

	var nodes = bxe_getAllEditableAreas();
	var bxe_areaHolder;
	for (var i = 0; i < nodes.length; i++) {

		nodes[i].removeAllChildren();
		var xpath = nodes[i].getAttribute("bxe_xpath");
		var xmlresult = this.evaluate(xpath, this.documentElement, nsResolver, 0, null);

		if (document.defaultView.getComputedStyle(nodes[i], null).getPropertyValue("display") == "inline") {
			bxe_areaHolder = document.createElement("span");
			nodes[i].display = "inline";
		} else {
			bxe_areaHolder = document.createElement("div");
			nodes[i].display = "block";
		}
		bxe_areaHolder.setAttribute("name","bxe_areaHolder");
		nodes[i].parentNode.insertBefore(bxe_areaHolder,nodes[i]);
		bxe_areaHolder.appendChild(nodes[i]);
		xmlnode = xmlresult.iterateNext();
		var xmlresults = new Array;
		while (xmlnode) {
			xmlresults.push(xmlnode);
			xmlnode = xmlresult.iterateNext();
		}
		for (var j = 0; j < xmlresults.length; j++) {
			if (xmlresults[j].nodeType == 1) {
				var fc = xmlresults[j].XMLNode.insertIntoHTMLDocument(nodes[i],true);

			} else {
				xmlresults[j].XMLNode.insertIntoHTMLDocument(nodes[i],false);
			}
			xmlresults[j].XMLNode.xmlBridge = xmlresults[j];
			var menu = new Widget_AreaInfo(nodes[i]);
			bxe_alignAreaNode(menu,nodes[i]);
			nodes[i].AreaInfo = menu;
			menu.editableArea = nodes[i];

		}

	}

	bxe_draw_widgets();
}

XMLDocument.prototype.checkParserError = function()
{
	if(this.documentElement && this.documentElement.nodeName=="parsererror")
	{
		var alerttext = "Parse Error: \n \n";
		alerttext += this.documentElement.firstChild.data +"\n\n";
		alerttext += "Sourcetext:\n\n";
		alerttext += this.documentElement.childNodes[1].firstChild.data;

		return (alerttext);
	}
	return true;
}

XMLDocument.prototype.transformToXPathMode = function(xslfile) {
	bxe_about_box.addText("Load XSLT ...");
	var xsldoc = document.implementation.createDocument("", "", null);
	xsldoc.addEventListener("load", onload_xsl, false);
	xsldoc.xmldoc = this;
	try {
		xsldoc.load(xslfile);
	} catch(e) {
		alert("The xslfile: '" + xslfile + "' was not found");
	}

	function onload_xsl(e) {
		bxe_about_box.addText("XSLT loaded...");
		xsldoc = e.currentTarget;
		var xsltransformdoc = document.implementation.createDocument("", "", null);
		xsltransformdoc.addEventListener("load", onload_xsltransform, false);
		xsltransformdoc.xsldoc = xsldoc;
		xsltransformdoc.load(mozile_root_dir + "xsl/transformxsl.xsl");
	}

	function onload_xsltransform (e) {
		var processor = new XSLTProcessor();
		xsltransformdoc = e.currentTarget;
		processor.importStylesheet(xsltransformdoc);
		try {
			var newDocument = processor.transformToDocument(xsltransformdoc.xsldoc);
		}
		catch (e) {
			alert ( e + "\n\n xsltransformdoc.xsldoc is : " + xsltransformdoc.xsldoc);
		}
		processor = new XSLTProcessor();
		try {
			processor.importStylesheet(newDocument);
		} catch(e) {
			alert("Something went wrong during importing the XSLT document.\n" + bxe_catch_alert_message(e) + "\n" + newDocument.saveXML(newDocument));
		}
		var xmldoc = processor.transformToFragment(xsltransformdoc.xsldoc.xmldoc,document);
		var bxe_area = document.getElementById("bxe_area");
		bxe_area.parentNode.replaceChild(xmldoc,bxe_area);
		xsltransformdoc.xsldoc.xmldoc.insertIntoHTMLDocument();
		xml_loaded(xsltransformdoc.xsldoc.xmldoc);
	}

}

XMLDocument.prototype.importXHTMLDocument = function(xhtmlfile) {

	function onload_xhtml(e) {
		var xhtmldoc = e.currentTarget;

		bxe_about_box.addText("XHTML loaded...");
		var bxe_area = document.getElementsByTagName("body")[0];
		var new_body = document.importNode(xhtmldoc.getElementsByTagName("body")[0],true);
		bxe_about_box.node = new_body.appendChild(bxe_about_box.node);
		bxe_area.removeAllChildren();
		bxe_area.appendAllChildren(new_body);
		xhtmldoc.xmldoc.insertIntoHTMLDocument();
		xml_loaded(xhtmldoc.xmldoc);
	}

	bxe_about_box.addText("Import external XHTML ...");
	var xhtmldoc = document.implementation.createDocument("", "", null);
	xhtmldoc.addEventListener("load", onload_xhtml, false);
	xhtmldoc.xmldoc = this;
	try {
		xhtmldoc.load(xhtmlfile);
	} catch(e) {
		alert("The xhtmlfile: '" + xhtmlfile + "' was not found");
	}

}

function XMLNodeDocument () {

}

XMLNodeDocument.prototype.validateDocument = function () {
	if (!this.vdom) {
		alert ("no Schema assigned to Document");
		return false;
	}

	//check root element
	//var vdomCurrentChild = this.documentElement.vdom.firstChild;
	return this.documentElement._isNodeValid(true);
}

XMLNodeDocument.prototype.__defineGetter__(
	"documentElement",
	function()
	{
		return this._documentElement;
	}
);

XMLNodeDocument.prototype.__defineSetter__(
	"documentElement",
	function(value)
	{
		this._documentElement = value;
	}
);

XMLNodeDocument.prototype.buildXML = function() {

	var node = this.documentElement.buildXML();
	return node.ownerDocument;
}

XMLNode.prototype.buildXML = function () {
	var nsResolver = new bxe_nsResolver(this.ownerDocument.documentElement);

	var walker = new XMLNodeWalker(this);
	var srcNode;
	if (this.xmlBridge) {
		srcNode = this.xmlBridge;
	} else {
		srcNode = this._node;
	}
	srcNode.removeAllChildren();
	var xmldoc = srcNode.ownerDocument;
	var node = walker.nextNode();

	srcNode.XMLNode._sernode = srcNode;
	var child ;
	var attribs;
	while (node) {
		if (node.nodeType == 1 && node.localName != 0){
			child = xmldoc.createElementNS(node.namespaceURI, node.localName);
			child.prefix = nsResolver.lookupNamespacePrefix(node.namespaceURI);
			attribs = node.attributes;
			for (var i = 0; i< attribs.length; i++) {
				child.setAttributeNode(attribs[i]);
			}
		} else if (node.nodeType == 3) {
			child = xmldoc.createTextNode(node.data);
		} else {
			child = xmldoc.importNode(node._node.cloneNode(true),true);
		}

		node._sernode = child;
		if (node.parentNode && node.parentNode._sernode) {
			node.parentNode._sernode.appendChild(child);
		}
		node = walker.nextNode();
	}

	return srcNode;
}

<commit_msg>set XPath string after insertIntoHTMLDocument<commit_after>XMLDocument.prototype.init= function (startNode) {
	if (!startNode) {
		startNode = this.documentElement;
	}
	var walker = this.createTreeWalker(
		startNode,NodeFilter.SHOW_ALL,
	{
		acceptNode : function(node) {
			return NodeFilter.FILTER_ACCEPT;
		}
	}
	, true);

	var node = startNode;
	var firstChild = false;
	if (startNode == this.documentElement) {
		this.XMLNode = new XMLNodeDocument();
		this.XMLNode._node = this;
	}
	do  {
		if (node.nodeType == 1) {
			node.XMLNode = new XMLNodeElement(node);
		} else {
			node.XMLNode = new XMLNode(node);
		}
		node = walker.nextNode();
	}  while(node );

	walker.currentNode = startNode;
	if (startNode == this.documentElement) {
		this.XMLNode.documentElement = this.documentElement.XMLNode;
		this.XMLNode.nodeType = 9;
		this.documentElement.XMLNode.parentNode = this.XMLNode;
	} else {
	}

	node = walker.currentNode;
	do  {

		x = node.XMLNode;
		x.ownerDocument = this.XMLNode;
		if (node.parentNode) {
			x.parentNode = node.parentNode.XMLNode;
		}
		if (node.previousSibling) {
			x.previousSibling = node.previousSibling.XMLNode;
		}
		if (node.nextSibling) {
			x.nextSibling = node.nextSibling.XMLNode;
		}
		if (node.firstChild) {
			x.firstChild = node.firstChild.XMLNode;
		}
		if (node.lastChild) {
			x.lastChild = node.lastChild.XMLNode;
		}
		x.nodeType = node.nodeType;
		x.prefix = node.prefix;
		node = walker.nextNode();
	}  while(node );
	return startNode.XMLNode;
/*	var xw = new XMLNodeWalker(this.documentElement.XMLNode.firstChild);
	node = xw.currentNode;
	while (node) {

		dump(node + node.localName + "\n");
		node = xw.nextNode();

	}*/

}

XMLDocument.prototype.insertIntoHTMLDocument = function(htmlnode) {

	var nsResolver = new bxe_nsResolver(this.documentElement);

	var nodes = bxe_getAllEditableAreas();
	var bxe_areaHolder;
	for (var i = 0; i < nodes.length; i++) {
		nodes[i].removeAllChildren();
		var xpath = nodes[i].getAttribute("bxe_xpath");
		var xmlresult = this.evaluate(xpath, this.documentElement, nsResolver, 0, null);

		if (document.defaultView.getComputedStyle(nodes[i], null).getPropertyValue("display") == "inline") {
			bxe_areaHolder = document.createElement("span");
			nodes[i].display = "inline";
		} else {
			bxe_areaHolder = document.createElement("div");
			nodes[i].display = "block";
		}
		bxe_areaHolder.setAttribute("name","bxe_areaHolder");
		nodes[i].parentNode.insertBefore(bxe_areaHolder,nodes[i]);
		bxe_areaHolder.appendChild(nodes[i]);
		xmlnode = xmlresult.iterateNext();
		var xmlresults = new Array;
		while (xmlnode) {
			xmlresults.push(xmlnode);
			xmlnode = xmlresult.iterateNext();
		}
		for (var j = 0; j < xmlresults.length; j++) {
			//dump ("result node type " + xmlresults[j].nodeType + xmlresults[j].nodeName+ "\n");
			xmlresults[j].XMLNode.xmlBridge = xmlresults[j];
			var menu = new Widget_AreaInfo(nodes[i]);
			bxe_alignAreaNode(menu,nodes[i]);
			nodes[i].AreaInfo = menu;
			menu.editableArea = nodes[i];

			if (xmlresults[j].nodeType == 1) {
				var fc = xmlresults[j].XMLNode.insertIntoHTMLDocument(nodes[i],true);

			} else {
				xmlresults[j].XMLNode.insertIntoHTMLDocument(nodes[i],false);
			}
			menu.MenuPopup.setTitle(xmlresults[j].XMLNode.getXPathString());

		}

	}

	bxe_draw_widgets();
}

XMLDocument.prototype.checkParserError = function()
{
	if(this.documentElement && this.documentElement.nodeName=="parsererror")
	{
		var alerttext = "Parse Error: \n \n";
		alerttext += this.documentElement.firstChild.data +"\n\n";
		alerttext += "Sourcetext:\n\n";
		alerttext += this.documentElement.childNodes[1].firstChild.data;

		return (alerttext);
	}
	return true;
}

XMLDocument.prototype.transformToXPathMode = function(xslfile) {
	bxe_about_box.addText("Load XSLT ...");
	var xsldoc = document.implementation.createDocument("", "", null);
	xsldoc.addEventListener("load", onload_xsl, false);
	xsldoc.xmldoc = this;
	try {
		xsldoc.load(xslfile);
	} catch(e) {
		alert("The xslfile: '" + xslfile + "' was not found");
	}

	function onload_xsl(e) {
		bxe_about_box.addText("XSLT loaded...");
		xsldoc = e.currentTarget;
		var xsltransformdoc = document.implementation.createDocument("", "", null);
		xsltransformdoc.addEventListener("load", onload_xsltransform, false);
		xsltransformdoc.xsldoc = xsldoc;
		xsltransformdoc.load(mozile_root_dir + "xsl/transformxsl.xsl");
	}

	function onload_xsltransform (e) {
		var processor = new XSLTProcessor();
		xsltransformdoc = e.currentTarget;
		processor.importStylesheet(xsltransformdoc);
		try {
			var newDocument = processor.transformToDocument(xsltransformdoc.xsldoc);
		}
		catch (e) {
			alert ( e + "\n\n xsltransformdoc.xsldoc is : " + xsltransformdoc.xsldoc);
		}
		processor = new XSLTProcessor();
		try {
			processor.importStylesheet(newDocument);
		} catch(e) {
			alert("Something went wrong during importing the XSLT document.\n" + bxe_catch_alert_message(e) + "\n" + newDocument.saveXML(newDocument));
		}
		var xmldoc = processor.transformToFragment(xsltransformdoc.xsldoc.xmldoc,document);
		var bxe_area = document.getElementById("bxe_area");
		bxe_area.parentNode.replaceChild(xmldoc,bxe_area);
		xsltransformdoc.xsldoc.xmldoc.insertIntoHTMLDocument();
		xml_loaded(xsltransformdoc.xsldoc.xmldoc);
	}

}

XMLDocument.prototype.importXHTMLDocument = function(xhtmlfile) {

	function onload_xhtml(e) {
		var xhtmldoc = e.currentTarget;

		bxe_about_box.addText("XHTML loaded...");
		var bxe_area = document.getElementsByTagName("body")[0];
		var new_body = document.importNode(xhtmldoc.getElementsByTagName("body")[0],true);
		bxe_about_box.node = new_body.appendChild(bxe_about_box.node);
		bxe_area.removeAllChildren();
		bxe_area.appendAllChildren(new_body);
		xhtmldoc.xmldoc.insertIntoHTMLDocument();
		xml_loaded(xhtmldoc.xmldoc);
	}

	bxe_about_box.addText("Import external XHTML ...");
	var xhtmldoc = document.implementation.createDocument("", "", null);
	xhtmldoc.addEventListener("load", onload_xhtml, false);
	xhtmldoc.xmldoc = this;
	try {
		xhtmldoc.load(xhtmlfile);
	} catch(e) {
		alert("The xhtmlfile: '" + xhtmlfile + "' was not found");
	}

}

function XMLNodeDocument () {

}

XMLNodeDocument.prototype.validateDocument = function () {
	if (!this.vdom) {
		alert ("no Schema assigned to Document");
		return false;
	}

	//check root element
	//var vdomCurrentChild = this.documentElement.vdom.firstChild;
	return this.documentElement._isNodeValid(true);
}

XMLNodeDocument.prototype.__defineGetter__(
	"documentElement",
	function()
	{
		return this._documentElement;
	}
);

XMLNodeDocument.prototype.__defineSetter__(
	"documentElement",
	function(value)
	{
		this._documentElement = value;
	}
);

XMLNodeDocument.prototype.buildXML = function() {

	var node = this.documentElement.buildXML();
	return node.ownerDocument;
}

XMLNode.prototype.buildXML = function () {
	var nsResolver = new bxe_nsResolver(this.ownerDocument.documentElement);

	var walker = new XMLNodeWalker(this);
	var srcNode;
	if (this.xmlBridge) {
		srcNode = this.xmlBridge;
	} else {
		srcNode = this._node;
	}
	srcNode.removeAllChildren();
	var xmldoc = srcNode.ownerDocument;
	var node = walker.nextNode();

	srcNode.XMLNode._sernode = srcNode;
	var child ;
	var attribs;
	while (node) {
		if (node.nodeType == 1 && node.localName != 0){
			child = xmldoc.createElementNS(node.namespaceURI, node.localName);
			if (node.namespaceURI  != XHTMLNS) {
				child.prefix = nsResolver.lookupNamespacePrefix(node.namespaceURI);
			}
			attribs = node.attributes;
			for (var i = 0; i< attribs.length; i++) {
				child.setAttributeNode(attribs[i]);
			}
		} else if (node.nodeType == 3) {
			child = xmldoc.createTextNode(node.data);
		} else {
			child = xmldoc.importNode(node._node.cloneNode(true),true);
		}

		node._sernode = child;
		if (node.parentNode && node.parentNode._sernode) {
			node.parentNode._sernode.appendChild(child);
		}
		node = walker.nextNode();
	}

	return srcNode;
}

<|endoftext|>


<commit_before>let ModeManager = (function () {
    function ModeManager(elem) {
        this.element = elem;
        this.dataset = elem.dataset;
        this.parse('');
    }
    ModeManager.prototype.execute = function (line, term) {
        let parsed = this.parse(line);
        if (!parsed.is_parsed) {
            console.log("AnalyzeError: " + parsed.message);
            return parsed.code;
        }
        if (parsed.code == '1001') {
            return true;
        }
        if (this.func_name === "") {
            console.log("AnalyzeError: Undefined execution function name.");
            return 0x2F01;
        }
        if (typeof parsed.cmd_list[0].execute !== "function") {
            console.log("ElementError: Undefined execution function.");
            return 0x2F02;
        }
        return parsed.cmd_list[0].execute(term, this);
    };
    ModeManager.prototype.getCompletion = function (line) {
        let cmd_list = this.result.cmd_list;
        let completion = [];
        for (let i = 0; i < cmd_list.length; i++) {
            completion.push(
                  cmd_list[i].type === 'paramater' ? ('<' + cmd_list[i].name + '>')
                : cmd_list[i].type === 'number'    ? ('[' + cmd_list[i].name + ']')
                : (cmd_list[i].name)
            );
        }
        //this.debug();
        return completion;
    };
    ModeManager.prototype.information = function (line) {
        //?を押された時の一覧を返す
        let cmd_list = this.parse(line).cmd_list;
        let information = ['Exec commands:'];
        let cmd_name;
        for (let i = 0; i < cmd_list.length; i++) {
            if (cmd_list[i].type == 'paramater') {
                cmd_name = ('<' + cmd_list[i].name + '>');
            }
            else if (cmd_list[i].type == 'number') {
                let max = parseInt(cmd_list[i].max);
                let min = parseInt(cmd_list[i].min);
                cmd_name = ('<' + (isNaN(min) ? '' : min) + '-' + (isNaN(max) ? '' : max) + '>');
            }
            else {
                cmd_name = cmd_list[i].name;
            }
            information.push({
                command: cmd_name,
                description: cmd_list[i].description
            });
        }
        let cur_cmd = this.line_parsed[this.line_parsed.length - 1];
        if (typeof cur_cmd !== 'undefined'
            && typeof cur_cmd.execute === 'function'
            && cur_cmd.type !== 'paramater'
            && cur_cmd.type !== 'number'
        ) {
            if (information.length === 2) {
                information.pop();
            }
            information.push({
                command: '<cr>',
                description: ''
            });
        }
        return information;
    };
    /*
    Object.defineProperty(ModeManager.prototype, "lineanalyzer", {
        get: function () {
            return this;
        },
        enumerable: true,
        configurable: true
    });*/
    /**
     * コマンドラインを解析する
     * @param {String} line パーサの対象となる文字列
     * @param {Number} position 現在の読み取り位置
     * @return {Array} 成功したかどうか、パース結果、新しい読み取り位置の３つの要素を持った配列
     */
    ModeManager.prototype.parse = function (line, position) {
        if (position === void 0) { position = 0; }
        /* 初期化 */
        this.line = line;
        this.line_parsed = [];
        this.is_parsed = true;
        this.err_code = 0x0000;
        this.err_msg = "";
        this.position = position;
        this.completion = "";
        this.cmd_list = this.dataset;
        this.paramaters = {};
        this.optional = {};
        let i  = 0;
        let i2 = 0;
        let p  = "";
        let stored = "";
        let quate = '';
        /* 解析前処理 */
        if (line.length <= 0) {
            return this.result;
        }
        else if (line.match(/^\s*!/)) {
            this.is_parsed = true;
            this.err_code = 0x1001;
            this.err_msg = "comment";
            return this.result;
        }
        /* 解析メイン */
        for (i = this.position; i <= line.length; i++) {
            p = line.charAt(i);
            if (p === " " && quate === '') {
                stored = "";
                quate = "";
                while (1) {
                    p = line.charAt(++i);
                    if (p !== " " || p === "")
                        break;
                }
                this.completion = line.substring(0, i);
                if (this.cmd_list.length === 1) {
                    this.line_parsed.push(this.cmd_list[0]);
                    this.cmd_list = (typeof this.cmd_list[0].children === "object")
                        ? this.cmd_list[0].children
                        : [];
                }
                else if (this.cmd_list.length > 1) {
                    this.err_code = 0x1F02;
                    this.err_msg = "% Ambiguous command:  \"" + this.line + "\"";
                    break;
                }
            }
            stored += p;
            let is_quate = stored.match(/^(?:(')[^']+|(")[^"]+)$/);
            if (is_quate) {
                quate =
                    is_quate[1] ? '\'' :
                    is_quate[2] ? '"' : '';
            }
            else {
                quate = '';
            }
            let filter = [];
            for (var j = 0; j < this.cmd_list.length; j++) {
                if (this.cmd_list[j].type === "command") {
                    let reg = new RegExp("^" + stored, "i");
                    if (this.cmd_list[j].name.match(reg)) {
                        filter.push(this.cmd_list[j]);
                    }
                }
                else if (this.cmd_list[j].type === "number") {
                    let num = parseInt(stored);
                    let max = parseInt(this.cmd_list[j].max);
                    let min = parseInt(this.cmd_list[j].min);

                    if (stored.length === 0) {
                        this.cmd_list[j].param = parseInt(stored);
                        filter.push(this.cmd_list[j]);
                    }
                    else if (isNaN(num)) {
                        continue;
                    }
                    else if (!isNaN(max) && max < num) {
                        continue;
                    }
                    else if (!isNaN(min) && min > num) {
                        continue;
                    }
                    else {
                        this.cmd_list[j].param = parseInt(stored);
                        filter.push(this.cmd_list[j]);
                    }
                }
                else if (this.cmd_list[j].type === "paramater") {
                    //1ブロックまたはクォーテーションで囲まれた範囲をパラメータとして処理する。
                    let re = new RegExp('^["\']?(.*?)["\']?$');
                    this.cmd_list[j].param = stored.replace(re, '$1');
                    filter.push(this.cmd_list[j]);
                }
                else if (this.cmd_list[j].type === "description") {
                    if (this.cmd_list.length === 1) {
                        i = line.length;
                        break;
                    }
                }
                else {
                    /* 想定外エラー：datasetに適切なtype設定がない */
                    this.err_code = 0xF001;
                    this.err_msg = "parsed error: undefined type '" + this.cmd_list[j].type + "'";
                    return this.result;
                }
            }
            this.cmd_list = filter;
            if (this.cmd_list.length === 0) {
                this.is_parsed = false;
                this.err_code = 0x1F01;
                let spacer;
                for (spacer = ''; spacer.length < i; spacer += ' ');
                spacer += $.terminal.active().get_prompt().replace(/\S/g, ' ');
                this.err_msg = spacer + "^\n% Invalid input detected at '^' marker.";
                this.cmd_list = [];
                break;
            }
        }
        /* 解析後処理 */
        this.position = i;
        if (this.cmd_list.length === 1) {
            this.line_parsed.push(this.cmd_list[0]);
            if (typeof this.cmd_list[0].execute === "undefined") {
                this.is_parsed = false;
                this.err_code = 0x1101;
                this.err_msg = "% Incomplete command.";
            }
            for (let i = 0; i < this.line_parsed.length; i++) {
                if (this.line_parsed[i].type === 'paramater'
                    || this.line_parsed[i].type === 'number'
                ){
                    this.paramaters[this.line_parsed[i].name] = this.line_parsed[i].param;
                }
                if (this.line_parsed[i].hasOwnProperty('optional')) {
                    this.optional[this.line_parsed[i].optional] = true;
                }
            }
        }
        if (this.cmd_list.length === 1 && this.cmd_list[0].type === "command") {
            this.completion += this.cmd_list[0].name + " ";
        }
        else {
            this.completion = this.line;
        }
        return this.result;
    };
    Object.defineProperty(ModeManager.prototype, "func_name", {
        get: function () {
            if (this.err_code !== 0) {
                return "";
            }
            else {
                let last = this.line_parsed[this.line_parsed.length - 1];
                return (typeof last.execute === "undefined")
                    ? ""
                    : last.execute;
            }
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ModeManager.prototype, "result", {
        get: function () {
            return {
                is_parsed: this.is_parsed,
                cmd_list: this.cmd_list,
                code: this.err_code.toString(16),
                message: this.err_msg,
                position: this.position,
                line_parsed: this.line_parsed
            };
        },
        enumerable: true,
        configurable: true
    });
    ModeManager.prototype.debug = function () {
        console.log("**************************************************");
        console.log("===== line");
        console.log(this.line);
        console.log("===== line_parsed");
        console.log(this.line_parsed);
        console.log("===== is_parsed");
        console.log(this.is_parsed);
        console.log("===== err_code");
        console.log(this.err_code.toString(16));
        console.log("===== err_msg");
        console.log(this.err_msg);
        console.log("===== line.length");
        console.log(this.line.length);
        console.log("===== position");
        console.log(this.position);
        console.log("===== completion");
        console.log(this.completion);
        console.log("===== dataset");
        console.log(this.dataset);
        console.log("===== cmd_list");
        console.log(this.cmd_list);
    };
    return ModeManager;
}());

let ConfigManager = (function () {
    function ConfigManager(def, start) {
        this.default = JSON.parse(JSON.stringify(def));
        this.config = !start ? def : start;
        this.config = Object.assign(def, this.config);
    }

    ConfigManager.prototype.find = function (nodes) {
        if (typeof nodes === 'string') {
            nodes = nodes.split('.');
        }
        if (!Array.isArray(nodes)) {
            return undefined;
        }
        let cf = this.config;
        for (let i = 0; i < nodes.length; i++) {
            if (typeof cf[nodes[i]] !== 'undefined') {
                cf = cf[nodes[i]];
            }
            else {
                cf = undefined;
                break;
            }
        }
        if (typeof cf !== 'undefined') {
            return cf;
        }

        cf = this.default;
        for (let i = 0; i < nodes.length; i++) {
            cf = cf[nodes[i]];
            if (typeof cf === 'undefined') {
                break;
            }
        }
        return cf;
    };

    ConfigManager.prototype.write = function(nodes, param) {
        if (typeof nodes === 'string') {
            nodes = nodes.split('.');
        }
        if (!Array.isArray(nodes)) {
            return false;
        }
        let cf = this.config;
        let child;
        for (let i = 0; i < nodes.length - 1; i++) {
            child = nodes[i];
            if (typeof cf[child] === 'undefined') {
                cf[child] = {};
            }
            cf = cf[child];
        }
        child = nodes.pop();
        cf[child] = param;
        return true;
    }

    ConfigManager.prototype.erase = function(nodes) {
        if (typeof nodes === 'string') {
            nodes = nodes.split('.');
        }
        if (!Array.isArray(nodes)) {
            return false;
        }
        let cf = this.config;
        let child;
        for (let i = 0; i < nodes.length - 1; i++) {
            child = nodes[i];
            if (typeof cf[child] === 'undefined') {
                return true;
            }
            cf = cf[child];
        }
        child = nodes.pop();
        delete(cf[child]);
        return true;
    }

    return ConfigManager;
}());

let InstanceManager = (function () {
    function InstanceManager() {
        let ins_str = localStorage.getItem('instances');
        if (ins_str) {
            this.instances = JSON.parse(ins_str);
            this.parse_acl();
        }
        else {
            this.instances = {};
        }
        this._ins = undefined;
        this._name = '';
    }

    InstanceManager.prototype.parse_acl = function () {
        this.acls = {};
        for (let ins_name in this.instances) {
            this.acls[ins_name] = {};
            if (!this.instances[ins_name].hasOwnProperty('acl')) {
                continue;
            }
            for (let acl_num in this.instances[ins_name].acl) {
                let acl = this.instances[ins_name].acl[acl_num];
                let color = acl.color ? acl.color : 'dark-blue';
                let filter = acl.regexp;
                let filter_r = filter.match(/^\/(.+)\/([igym]*)$/);
                let re;
                if (filter_r) {
                    re = new RegExp(filter_r[1], filter_r[2])
                }
                else {
                    re = new RegExp(filter);
                }
                this.acls[ins_name][acl_num] = {
                    type: acl.type,
                    regexp: re,
                    color: color,
                    notify: (acl.notify === true),
                    voice: acl.voice
                };
            }
        }
    }

    InstanceManager.prototype.name = function (name) {
        if (typeof name === 'string') {
            this._name = name;
            this._ins = this.instances[name];
        }
        return this._name;
    }

    InstanceManager.prototype.save = function () {
        for (let _ins in this.instances) {
            if (!this.instances[_ins].hasOwnProperty('client_id')) {
                delete(this.instances[_ins]);
            }
        }
        let ins_str = JSON.stringify(this.instances);
        localStorage.setItem('instances', ins_str);
        return true;
    }

    InstanceManager.prototype.get = function (name) {
        if (typeof name === 'undefined') {
            return this._ins;
        }
        else {
            return this.instances[name];
        }
    }

    InstanceManager.prototype.set = function (ins, name) {
        if (typeof name === 'undefined') {
            name = this.name();
        }
        if (!name) {
            return false;
        }
        this.instances[name] = ins;
        this._ins = ins;
        this.save();
    }

    InstanceManager.prototype.delete = function (name) {
        if (typeof name === 'undefined') {
            name = this._name;
        }
        result = delete(this.instances[name]);
        if (result) {
            this._name = '';
            this._ins = undefined;
        }
        this.save();
        return result;
    }

    InstanceManager.prototype.create = function (name) {
        if (!this.instances.hasOwnProperty(name)) {
            this.instances[name] = {};
            this.name(name);
        }
        return this.instances[name];
    }

    return InstanceManager;
}());<commit_msg>sh accの表示がおかしくなってたのを修正<commit_after>let ModeManager = (function () {
    function ModeManager(elem) {
        this.element = elem;
        this.dataset = elem.dataset;
        this.parse('');
    }
    ModeManager.prototype.execute = function (line, term) {
        let parsed = this.parse(line);
        if (!parsed.is_parsed) {
            console.log("AnalyzeError: " + parsed.message);
            return parsed.code;
        }
        if (parsed.code == '1001') {
            return true;
        }
        if (this.func_name === "") {
            console.log("AnalyzeError: Undefined execution function name.");
            return 0x2F01;
        }
        if (typeof parsed.cmd_list[0].execute !== "function") {
            console.log("ElementError: Undefined execution function.");
            return 0x2F02;
        }
        return parsed.cmd_list[0].execute(term, this);
    };
    ModeManager.prototype.getCompletion = function (line) {
        let cmd_list = this.result.cmd_list;
        let completion = [];
        for (let i = 0; i < cmd_list.length; i++) {
            completion.push(
                  cmd_list[i].type === 'paramater' ? ('<' + cmd_list[i].name + '>')
                : cmd_list[i].type === 'number'    ? ('[' + cmd_list[i].name + ']')
                : (cmd_list[i].name)
            );
        }
        //this.debug();
        return completion;
    };
    ModeManager.prototype.information = function (line) {
        //?を押された時の一覧を返す
        let cmd_list = this.parse(line).cmd_list;
        let information = ['Exec commands:'];
        let cmd_name;
        for (let i = 0; i < cmd_list.length; i++) {
            if (cmd_list[i].type == 'paramater') {
                cmd_name = ('<' + cmd_list[i].name + '>');
            }
            else if (cmd_list[i].type == 'number') {
                let max = parseInt(cmd_list[i].max);
                let min = parseInt(cmd_list[i].min);
                cmd_name = ('<' + (isNaN(min) ? '' : min) + '-' + (isNaN(max) ? '' : max) + '>');
            }
            else {
                cmd_name = cmd_list[i].name;
            }
            information.push({
                command: cmd_name,
                description: cmd_list[i].description
            });
        }
        let cur_cmd = this.line_parsed[this.line_parsed.length - 1];
        if (typeof cur_cmd !== 'undefined'
            && typeof cur_cmd.execute === 'function'
            && cur_cmd.type !== 'paramater'
            && cur_cmd.type !== 'number'
        ) {
            if (information.length === 2) {
                information.pop();
            }
            information.push({
                command: '<cr>',
                description: ''
            });
        }
        return information;
    };
    /*
    Object.defineProperty(ModeManager.prototype, "lineanalyzer", {
        get: function () {
            return this;
        },
        enumerable: true,
        configurable: true
    });*/
    /**
     * コマンドラインを解析する
     * @param {String} line パーサの対象となる文字列
     * @param {Number} position 現在の読み取り位置
     * @return {Array} 成功したかどうか、パース結果、新しい読み取り位置の３つの要素を持った配列
     */
    ModeManager.prototype.parse = function (line, position) {
        if (position === void 0) { position = 0; }
        /* 初期化 */
        this.line = line;
        this.line_parsed = [];
        this.is_parsed = true;
        this.err_code = 0x0000;
        this.err_msg = "";
        this.position = position;
        this.completion = "";
        this.cmd_list = this.dataset;
        this.paramaters = {};
        this.optional = {};
        let i  = 0;
        let i2 = 0;
        let p  = "";
        let stored = "";
        let quate = '';
        /* 解析前処理 */
        if (line.length <= 0) {
            return this.result;
        }
        else if (line.match(/^\s*!/)) {
            this.is_parsed = true;
            this.err_code = 0x1001;
            this.err_msg = "comment";
            return this.result;
        }
        /* 解析メイン */
        for (i = this.position; i <= line.length; i++) {
            p = line.charAt(i);
            if (p === " " && quate === '') {
                stored = "";
                quate = "";
                while (1) {
                    p = line.charAt(++i);
                    if (p !== " " || p === "")
                        break;
                }
                this.completion = line.substring(0, i);
                if (this.cmd_list.length === 1) {
                    this.line_parsed.push(this.cmd_list[0]);
                    this.cmd_list = (typeof this.cmd_list[0].children === "object")
                        ? this.cmd_list[0].children
                        : [];
                }
                else if (this.cmd_list.length > 1) {
                    this.err_code = 0x1F02;
                    this.err_msg = "% Ambiguous command:  \"" + this.line + "\"";
                    break;
                }
            }
            stored += p;
            let is_quate = stored.match(/^(?:(')[^']+|(")[^"]+)$/);
            if (is_quate) {
                quate =
                    is_quate[1] ? '\'' :
                    is_quate[2] ? '"' : '';
            }
            else {
                quate = '';
            }
            let filter = [];
            for (var j = 0; j < this.cmd_list.length; j++) {
                if (this.cmd_list[j].type === "command") {
                    let reg = new RegExp("^" + stored, "i");
                    if (this.cmd_list[j].name.match(reg)) {
                        filter.push(this.cmd_list[j]);
                    }
                }
                else if (this.cmd_list[j].type === "number") {
                    let num = parseInt(stored);
                    let max = parseInt(this.cmd_list[j].max);
                    let min = parseInt(this.cmd_list[j].min);

                    if (stored.length === 0) {
                        this.cmd_list[j].param = parseInt(stored);
                        filter.push(this.cmd_list[j]);
                    }
                    else if (isNaN(num)) {
                        continue;
                    }
                    else if (!isNaN(max) && max < num) {
                        continue;
                    }
                    else if (!isNaN(min) && min > num) {
                        continue;
                    }
                    else {
                        this.cmd_list[j].param = parseInt(stored);
                        filter.push(this.cmd_list[j]);
                    }
                }
                else if (this.cmd_list[j].type === "paramater") {
                    //1ブロックまたはクォーテーションで囲まれた範囲をパラメータとして処理する。
                    let re = new RegExp('^["\']?(.*?)["\']?$');
                    this.cmd_list[j].param = stored.replace(re, '$1');
                    filter.push(this.cmd_list[j]);
                }
                else if (this.cmd_list[j].type === "description") {
                    if (this.cmd_list.length === 1) {
                        i = line.length;
                        break;
                    }
                }
                else {
                    /* 想定外エラー：datasetに適切なtype設定がない */
                    this.err_code = 0xF001;
                    this.err_msg = "parsed error: undefined type '" + this.cmd_list[j].type + "'";
                    return this.result;
                }
            }
            this.cmd_list = filter;
            if (this.cmd_list.length === 0) {
                this.is_parsed = false;
                this.err_code = 0x1F01;
                let spacer;
                for (spacer = ''; spacer.length < i; spacer += ' ');
                spacer += $.terminal.active().get_prompt().replace(/\S/g, ' ');
                this.err_msg = spacer + "^\n% Invalid input detected at '^' marker.";
                this.cmd_list = [];
                break;
            }
        }
        /* 解析後処理 */
        this.position = i;
        if (this.cmd_list.length === 1) {
            this.line_parsed.push(this.cmd_list[0]);
            if (typeof this.cmd_list[0].execute === "undefined") {
                this.is_parsed = false;
                this.err_code = 0x1101;
                this.err_msg = "% Incomplete command.";
            }
            for (let i = 0; i < this.line_parsed.length; i++) {
                if (this.line_parsed[i].type === 'paramater'
                    || this.line_parsed[i].type === 'number'
                ){
                    this.paramaters[this.line_parsed[i].name] = this.line_parsed[i].param;
                }
                if (this.line_parsed[i].hasOwnProperty('optional')) {
                    this.optional[this.line_parsed[i].optional] = true;
                }
            }
        }
        if (this.cmd_list.length === 1 && this.cmd_list[0].type === "command") {
            this.completion += this.cmd_list[0].name + " ";
        }
        else {
            this.completion = this.line;
        }
        return this.result;
    };
    Object.defineProperty(ModeManager.prototype, "func_name", {
        get: function () {
            if (this.err_code !== 0) {
                return "";
            }
            else {
                let last = this.line_parsed[this.line_parsed.length - 1];
                return (typeof last.execute === "undefined")
                    ? ""
                    : last.execute;
            }
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(ModeManager.prototype, "result", {
        get: function () {
            return {
                is_parsed: this.is_parsed,
                cmd_list: this.cmd_list,
                code: this.err_code.toString(16),
                message: this.err_msg,
                position: this.position,
                line_parsed: this.line_parsed
            };
        },
        enumerable: true,
        configurable: true
    });
    ModeManager.prototype.debug = function () {
        console.log("**************************************************");
        console.log("===== line");
        console.log(this.line);
        console.log("===== line_parsed");
        console.log(this.line_parsed);
        console.log("===== is_parsed");
        console.log(this.is_parsed);
        console.log("===== err_code");
        console.log(this.err_code.toString(16));
        console.log("===== err_msg");
        console.log(this.err_msg);
        console.log("===== line.length");
        console.log(this.line.length);
        console.log("===== position");
        console.log(this.position);
        console.log("===== completion");
        console.log(this.completion);
        console.log("===== dataset");
        console.log(this.dataset);
        console.log("===== cmd_list");
        console.log(this.cmd_list);
    };
    return ModeManager;
}());

let ConfigManager = (function () {
    function ConfigManager(def, start) {
        this.default = JSON.parse(JSON.stringify(def));
        this.config = !start ? def : start;
        this.config = Object.assign(def, this.config);
    }

    ConfigManager.prototype.find = function (nodes) {
        if (typeof nodes === 'string') {
            nodes = nodes.split('.');
        }
        if (!Array.isArray(nodes)) {
            return undefined;
        }
        let cf = this.config;
        for (let i = 0; i < nodes.length; i++) {
            if (typeof cf[nodes[i]] !== 'undefined') {
                cf = cf[nodes[i]];
            }
            else {
                cf = undefined;
                break;
            }
        }
        if (typeof cf !== 'undefined') {
            return cf;
        }

        cf = this.default;
        for (let i = 0; i < nodes.length; i++) {
            cf = cf[nodes[i]];
            if (typeof cf === 'undefined') {
                break;
            }
        }
        return cf;
    };

    ConfigManager.prototype.write = function(nodes, param) {
        if (typeof nodes === 'string') {
            nodes = nodes.split('.');
        }
        if (!Array.isArray(nodes)) {
            return false;
        }
        let cf = this.config;
        let child;
        for (let i = 0; i < nodes.length - 1; i++) {
            child = nodes[i];
            if (typeof cf[child] === 'undefined') {
                cf[child] = {};
            }
            cf = cf[child];
        }
        child = nodes.pop();
        cf[child] = param;
        return true;
    }

    ConfigManager.prototype.erase = function(nodes) {
        if (typeof nodes === 'string') {
            nodes = nodes.split('.');
        }
        if (!Array.isArray(nodes)) {
            return false;
        }
        let cf = this.config;
        let child;
        for (let i = 0; i < nodes.length - 1; i++) {
            child = nodes[i];
            if (typeof cf[child] === 'undefined') {
                return true;
            }
            cf = cf[child];
        }
        child = nodes.pop();
        delete(cf[child]);
        return true;
    }

    return ConfigManager;
}());

let InstanceManager = (function () {
    function InstanceManager() {
        let ins_str = localStorage.getItem('instances');
        if (ins_str) {
            this.instances = JSON.parse(ins_str);
            this.parse_acl();
        }
        else {
            this.instances = {};
        }
        this._ins = undefined;
        this._name = '';
    }

    InstanceManager.prototype.parse_acl = function () {
        this.acls = {};
        for (let ins_name in this.instances) {
            this.acls[ins_name] = {};
            if (!this.instances[ins_name].hasOwnProperty('acl')) {
                continue;
            }
            for (let acl_num in this.instances[ins_name].acl) {
                let acl = this.instances[ins_name].acl[acl_num];
                let filter = acl.regexp;
                let filter_r = filter.match(/^\/(.+)\/([igym]*)$/);
                let re;
                if (filter_r) {
                    re = new RegExp(filter_r[1], filter_r[2])
                }
                else {
                    re = new RegExp(filter);
                }
                this.acls[ins_name][acl_num] = {
                    type: acl.type,
                    regexp: re,
                    notify: (acl.notify === true)
                };
                if (acl.color) {
                    this.acls[ins_name][acl_num].color = acl.color;
                }
                if (acl.voice) {
                    this.acls[ins_name][acl_num].voice = acl.voice;
                }
            }
        }
    }

    InstanceManager.prototype.name = function (name) {
        if (typeof name === 'string') {
            this._name = name;
            this._ins = this.instances[name];
        }
        return this._name;
    }

    InstanceManager.prototype.save = function () {
        for (let _ins in this.instances) {
            if (!this.instances[_ins].hasOwnProperty('client_id')) {
                delete(this.instances[_ins]);
            }
        }
        let ins_str = JSON.stringify(this.instances);
        localStorage.setItem('instances', ins_str);
        return true;
    }

    InstanceManager.prototype.get = function (name) {
        if (typeof name === 'undefined') {
            return this._ins;
        }
        else {
            return this.instances[name];
        }
    }

    InstanceManager.prototype.set = function (ins, name) {
        if (typeof name === 'undefined') {
            name = this.name();
        }
        if (!name) {
            return false;
        }
        this.instances[name] = ins;
        this._ins = ins;
        this.save();
    }

    InstanceManager.prototype.delete = function (name) {
        if (typeof name === 'undefined') {
            name = this._name;
        }
        result = delete(this.instances[name]);
        if (result) {
            this._name = '';
            this._ins = undefined;
        }
        this.save();
        return result;
    }

    InstanceManager.prototype.create = function (name) {
        if (!this.instances.hasOwnProperty(name)) {
            this.instances[name] = {};
            this.name(name);
        }
        return this.instances[name];
    }

    return InstanceManager;
}());<|endoftext|>


<commit_before>import BABYLON from 'babylonjs';
import React from 'react';
import Relay from 'react-relay';

import TileHoverInAnimationFactory from '../babylonjs/animations/tiles/TileHoverInAnimationFactory';
import TileHoverOutAnimationFactory from '../babylonjs/animations/tiles/TileHoverOutAnimationFactory';

import ChangeTileTypeMutation from '../mutations/ChangeTileTypeMutation';

const VIEWPORT_SIZE = 5;
const TILE_TYPES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; //TODO get it from backend

const SIZE_X = 20;
const SIZE_Y = 20;

class Map extends React.Component {
    render() {
        const component = this;

        const tileset = this.props.tileset;

        return (
            <canvas className="map" ref="mapRef"></canvas>
        );
    }
    _onTileTypeChanged(tile, newTileType){
        this.props.relay.commitUpdate(
            new ChangeTileTypeMutation({
                tile: tile,
                tileType: newTileType
            })
        );
    }
    componentDidMount(){
        const mapRef = this.refs.mapRef;

        this._initBabylon(mapRef);
    }
    _initBabylon(canvas){
        const component = this;

        // Load the BABYLON 3D engine
        const engine = new BABYLON.Engine(canvas, true);
        this._engine = engine;

        const scene = this._createScene(canvas);
        scene.ambientColor = new BABYLON.Color3(1, 1, 1);
        scene.lightsEnabled = false;
        scene.debugLayer.show();
        scene.createOrUpdateSelectionOctree();

        this._updateAspectRatio(scene);

        // Register a render loop to repeatedly render the scene
        engine.runRenderLoop(function () {
            scene.render();
        });

        // Watch for browser/canvas resize events
        window.addEventListener('resize', function () {
            component._updateAspectRatio.apply(component, [scene]);

            component._engine.resize();
        });
    }
    _updateAspectRatio(scene){
        const camera = this._mainCamera;

        let height = this.refs.mapRef.height;
        const width = this.refs.mapRef.width;

        if (height == 0) height = 1;

        /* Calculate aspect ratio --------------------------------------------------------------------*/
        const aspectRatio = width / height;

        if (width >= height)
        {
            camera.orthoLeft = -VIEWPORT_SIZE*aspectRatio;
            camera.orthoRight = VIEWPORT_SIZE*aspectRatio;
            camera.orthoBottom = -VIEWPORT_SIZE;
            camera.orthoTop = VIEWPORT_SIZE;
        }
        else
        {
            camera.orthoLeft = -VIEWPORT_SIZE;
            camera.orthoRight = VIEWPORT_SIZE;
            camera.orthoBottom = -VIEWPORT_SIZE*aspectRatio;
            camera.orthoTop = VIEWPORT_SIZE*aspectRatio;
        }
    }
    componentWillUnmount(){
        const engine = this._engine;
        engine.dispose();
    }
    _createScene(canvas){
        const engine = this._engine;
        const scene =  new BABYLON.Scene(engine);
        scene.shadowsEnabled = false;

        const camera = new BABYLON.TargetCamera('mainCamera', new BABYLON.Vector3(0, 0, -10), scene);
       camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;

        // attach the camera to the canvas
        camera.attachControl(canvas, false);

        this._mainCamera = camera;

        const materials =this._createMaterials(scene);
        this._createTiles(scene, materials);

        return scene;
    }
    _createTiles(scene, materials){
        var xSize = 20;
        var ySize = 20;

        this._firstTileMesh = null;

        for(var tileY = 0; tileY < SIZE_Y; tileY++){
            this._createTilesRow(scene, materials, tileY);
        }
    }
    _createTilesRow(scene, materials, tileY){
        for (var tileX = 0; tileX < SIZE_X; tileX++){
            const tileTag = this._createTileTag(tileX, tileY);
            const tileMesh = this._createTileMesh(scene, tileTag);

            const materialIdx = Math.floor(Math.random() * TILE_TYPES.length);

            const origMaterial = materials[materialIdx];
            tileMesh.material = origMaterial;
            tileMesh.position.x = tileX;
            tileMesh.position.y = tileY;

            this._setUpTileActions(scene, tileMesh, origMaterial);
        }
    }
    _createTileTag(tileX, tileY){
        return 'tile-' + tileX + '-' + tileY;
    }
    _createTileMesh(scene, tileTag){
        let tileMesh = null;

        if(this._firstTileMesh === null){
            this._firstTileMesh = BABYLON.Mesh.CreatePlane(tileTag, 1.0, scene);
            tileMesh = this._firstTileMesh;
        }else{
            tileMesh = this._firstTileMesh.clone(tileTag);
        }

        return tileMesh;
    }
    _setUpTileActions(scene, tileMesh, origMaterial){
        tileMesh.actionManager = new BABYLON.ActionManager(scene);

        this._setUpTileHoverInAction(scene, tileMesh, origMaterial);
        this._setUpTileHoverOutAction(scene, tileMesh, origMaterial);
    }
    _setUpTileHoverInAction(scene, tileMesh,origMaterial){
        const hoverInAnimation = TileHoverInAnimationFactory.create('anim-hover-in-' + tileMesh.tag);
        tileMesh.animations.push(hoverInAnimation);

        const hoverInAction = new BABYLON.ExecuteCodeAction(
            BABYLON.ActionManager.OnPointerOverTrigger,
            function(){
                if(tileMesh.material === origMaterial){
                    //Clone the material
                    tileMesh.material = origMaterial.clone();
                }

                scene.stopAnimation(tileMesh);
                scene.beginDirectAnimation(tileMesh, [hoverInAnimation], 0, 100, false, 1);
            }
        );

        tileMesh.actionManager.registerAction(hoverInAction);
    }
    _setUpTileHoverOutAction(scene, tileMesh, origMaterial){
        const hoverOutAnimation = TileHoverOutAnimationFactory.create('anim-hover-out-' + tileMesh.tag);
        tileMesh.animations.push(hoverOutAnimation);

        const hoverOutAction = new BABYLON.ExecuteCodeAction(
            BABYLON.ActionManager.OnPointerOutTrigger,
            function(){
                if(tileMesh.material === origMaterial){
                    //Clone the material
                    tileMesh.material = origMaterial.clone();
                }

                scene.stopAnimation(tileMesh);
                scene.beginDirectAnimation(tileMesh, [hoverOutAnimation], 0, 100, false);
            }
        );

        tileMesh.actionManager.registerAction(hoverOutAction);
    }
    _createMaterials(scene){
        return TILE_TYPES.map(function(tileType, index){
            const material = new BABYLON.StandardMaterial('material-tile-type-' + index, scene);
            material.ambientColor = new BABYLON.Color3(1 / (index + 1), 1 / (index + 1), 1 / (index + 1));
            material.freeze();

            return material;
        });
    }
}

export default Relay.createContainer(Map, {
    fragments: {
        tileset: () => Relay.QL`
            fragment on Tileset {
                rows{
                    id,
                    tiles {
                        id,
                        type,
                        ${ChangeTileTypeMutation.getFragment('tile')}
                    },
                },
                availableTileTypes
            }
        `
    }
});
<commit_msg>Add borders to the tiles<commit_after>import BABYLON from 'babylonjs';
import React from 'react';
import Relay from 'react-relay';

import TileHoverInAnimationFactory from '../babylonjs/animations/tiles/TileHoverInAnimationFactory';
import TileHoverOutAnimationFactory from '../babylonjs/animations/tiles/TileHoverOutAnimationFactory';

import ChangeTileTypeMutation from '../mutations/ChangeTileTypeMutation';

const VIEWPORT_SIZE = 5;
const TILE_TYPES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; //TODO get it from backend

const SIZE_X = 20;
const SIZE_Y = 20;

class Map extends React.Component {
    render() {
        const component = this;

        const tileset = this.props.tileset;

        return (
            <canvas className="map" ref="mapRef"></canvas>
        );
    }
    _onTileTypeChanged(tile, newTileType){
        this.props.relay.commitUpdate(
            new ChangeTileTypeMutation({
                tile: tile,
                tileType: newTileType
            })
        );
    }
    componentDidMount(){
        const mapRef = this.refs.mapRef;

        this._initBabylon(mapRef);
    }
    _initBabylon(canvas){
        const component = this;

        // Load the BABYLON 3D engine
        const engine = new BABYLON.Engine(canvas, true);
        this._engine = engine;

        const scene = this._createScene(canvas);
        scene.ambientColor = new BABYLON.Color3(1, 1, 1);
        scene.lightsEnabled = false;
        scene.debugLayer.show();
        scene.createOrUpdateSelectionOctree();

        this._updateAspectRatio(scene);

        // Register a render loop to repeatedly render the scene
        engine.runRenderLoop(function () {
            scene.render();
        });

        // Watch for browser/canvas resize events
        window.addEventListener('resize', function () {
            component._updateAspectRatio.apply(component, [scene]);

            component._engine.resize();
        });
    }
    _updateAspectRatio(scene){
        const camera = this._mainCamera;

        let height = this.refs.mapRef.height;
        const width = this.refs.mapRef.width;

        if (height == 0) height = 1;

        /* Calculate aspect ratio --------------------------------------------------------------------*/
        const aspectRatio = width / height;

        if (width >= height)
        {
            camera.orthoLeft = -VIEWPORT_SIZE*aspectRatio;
            camera.orthoRight = VIEWPORT_SIZE*aspectRatio;
            camera.orthoBottom = -VIEWPORT_SIZE;
            camera.orthoTop = VIEWPORT_SIZE;
        }
        else
        {
            camera.orthoLeft = -VIEWPORT_SIZE;
            camera.orthoRight = VIEWPORT_SIZE;
            camera.orthoBottom = -VIEWPORT_SIZE*aspectRatio;
            camera.orthoTop = VIEWPORT_SIZE*aspectRatio;
        }
    }
    componentWillUnmount(){
        const engine = this._engine;
        engine.dispose();
    }
    _createScene(canvas){
        const engine = this._engine;
        const scene =  new BABYLON.Scene(engine);
        scene.shadowsEnabled = false;

        const camera = new BABYLON.TargetCamera('mainCamera', new BABYLON.Vector3(0, 0, -10), scene);
       camera.mode = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;

        // attach the camera to the canvas
        camera.attachControl(canvas, false);

        this._mainCamera = camera;

        const materials =this._createMaterials(scene);
        this._createTiles(scene, materials);

        return scene;
    }
    _createTiles(scene, materials){
        var xSize = 20;
        var ySize = 20;

        this._firstTileMesh = null;
        this._firstTileBorderMesh = null;

        for(var tileY = 0; tileY < SIZE_Y; tileY++){
            this._createTilesRow(scene, materials, tileY);
        }
    }
    _createTilesRow(scene, materials, tileY){
        for (var tileX = 0; tileX < SIZE_X; tileX++){
            const tileTag = this._createTileTag(tileX, tileY);

            const tileMesh = this._createTileMesh(scene, tileTag);
            const tileBorderMesh = this._createTileBorderMesh(scene, tileTag);
            tileBorderMesh.parent = tileMesh;

            const materialIdx = Math.floor(Math.random() * TILE_TYPES.length);

            const origMaterial = materials[materialIdx];
            tileMesh.material = origMaterial;
            tileMesh.position.x = tileX;
            tileMesh.position.y = tileY;

            this._setUpTileActions(scene, tileMesh, origMaterial);
        }
    }
    _createTileTag(tileX, tileY){
        return 'tile-' + tileX + '-' + tileY;
    }
    _createTileMesh(scene, tileTag){
        let tileMesh = null;

        if(this._firstTileMesh === null){
            this._firstTileMesh = BABYLON.Mesh.CreatePlane(tileTag, 1.0, scene);
            tileMesh = this._firstTileMesh;
        }else{
            tileMesh = this._firstTileMesh.clone(tileTag);
        }
        return tileMesh;
    }
    _createTileBorderMesh(scene, tileTag){
        let tileBorderMesh = null;

        const borderMeshTag = 'border-' + tileTag;

        if(this._firstTileBorderMesh === null){
            this._firstTileBorderMesh = BABYLON.Mesh.CreateLines(borderMeshTag, [
                new BABYLON.Vector3(-0.5, -0.5, 0),
                new BABYLON.Vector3(0.5, -0.5, 0),
                new BABYLON.Vector3(0.5, 0.5, 0),
                new BABYLON.Vector3(-0.5, 0.5, 0),
                new BABYLON.Vector3(-0.5, -0.5, 0)
            ], scene);

            tileBorderMesh = this._firstTileBorderMesh;
        }else{
            tileBorderMesh = this._firstTileBorderMesh.clone(borderMeshTag);
        }
        return tileBorderMesh;
    }
    _setUpTileActions(scene, tileMesh, origMaterial){
        tileMesh.actionManager = new BABYLON.ActionManager(scene);

        this._setUpTileHoverInAction(scene, tileMesh, origMaterial);
        this._setUpTileHoverOutAction(scene, tileMesh, origMaterial);
    }
    _setUpTileHoverInAction(scene, tileMesh,origMaterial){
        const hoverInAnimation = TileHoverInAnimationFactory.create('anim-hover-in-' + tileMesh.tag);
        tileMesh.animations.push(hoverInAnimation);

        const hoverInAction = new BABYLON.ExecuteCodeAction(
            BABYLON.ActionManager.OnPointerOverTrigger,
            function(){
                if(tileMesh.material === origMaterial){
                    //Clone the material
                    tileMesh.material = origMaterial.clone();
                }

                scene.stopAnimation(tileMesh);
                scene.beginDirectAnimation(tileMesh, [hoverInAnimation], 0, 100, false, 1);
            }
        );

        tileMesh.actionManager.registerAction(hoverInAction);
    }
    _setUpTileHoverOutAction(scene, tileMesh, origMaterial){
        const hoverOutAnimation = TileHoverOutAnimationFactory.create('anim-hover-out-' + tileMesh.tag);
        tileMesh.animations.push(hoverOutAnimation);

        const hoverOutAction = new BABYLON.ExecuteCodeAction(
            BABYLON.ActionManager.OnPointerOutTrigger,
            function(){
                if(tileMesh.material === origMaterial){
                    //Clone the material
                    tileMesh.material = origMaterial.clone();
                }

                scene.stopAnimation(tileMesh);
                scene.beginDirectAnimation(tileMesh, [hoverOutAnimation], 0, 100, false);
            }
        );

        tileMesh.actionManager.registerAction(hoverOutAction);
    }
    _createMaterials(scene){
        return TILE_TYPES.map(function(tileType, index){
            const material = new BABYLON.StandardMaterial('material-tile-type-' + index, scene);
            material.ambientColor = new BABYLON.Color3(1 / (index + 1), 1 / (index + 1), 1 / (index + 1));
            material.freeze();

            return material;
        });
    }
}

export default Relay.createContainer(Map, {
    fragments: {
        tileset: () => Relay.QL`
            fragment on Tileset {
                rows{
                    id,
                    tiles {
                        id,
                        type,
                        ${ChangeTileTypeMutation.getFragment('tile')}
                    },
                },
                availableTileTypes
            }
        `
    }
});
<|endoftext|>