<commit_before>const _ = require('lodash');
const async = require('async');
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const codec = require('./codec');
const Router = require('./router');
const Subscription = require('./subscription');

module.exports = Client;

const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];

function createLogger(category) {
	const debug = require('debug')(category);
	return _.reduce(levels, (logger, level) => {
		logger[level] = function () {
			debug.apply(undefined, _(['[' + level + '] ']).concat(arguments));
		};
		return logger;
	}, {});
}

/**
 *
 * @constructor
 */
function Client(mqttclient, options, log) {
	if (!(this instanceof Client)) {
		return new Client(mqttclient, options, log);
	}

	options = options || {};

	this.mqttclient = mqttclient;
	this.codec = options.codec || codec.msgpack();
	this.log = log = log || options.log || createLogger('mqttr');

	log.debug('Using codec:', this.codec.name);

	this.router = new Router();

	const that = this;
	mqttclient.on('connect', (connack) => {
		log.debug('connected');
		that._connected();
		that.emit('connect', connack);
	});

	mqttclient.on('reconnect', () => {
		log.debug('reconnect');
		that.emit('reconnect');
	});

	mqttclient.on('close', () => {
		log.debug('close');
		that.emit('close');
	});

	mqttclient.on('offline', () => {
		log.debug('offline');
		that.emit('offline');
	});

	mqttclient.on('error', (error) => {
		that.emit('error', error);
	});

	mqttclient.on('message', (topic, payload, packet) => {
		// Try decode
		try {
			payload = that.codec.decode(payload);
		} catch (e) { // Using origin payload if failed
			// no-op
		}
		that._handleMessage(topic, payload, packet);
	});
}

util.inherits(Client, EventEmitter);

Object.defineProperty(Client.prototype, 'connected', {
	get() {
		return this.mqttclient.connected;
	}
});

Client.prototype._connected = function () {
	const that = this;
	async.eachSeries(this.router.routes, (r, cb) => {
		that._subscribe(r.path, cb);
	});
};

Client.prototype._subscribe = function (topic, options, cb) {
	if (typeof options === 'function') {
		cb = options;
		options = undefined;
	}
	return options ? this.mqttclient.subscribe(mqttTopic(topic), options, cb) : this.mqttclient.subscribe(mqttTopic(topic), cb);
};

Client.prototype._unsubscribe = function (topic, cb) {
	this.mqttclient.unsubscribe(mqttTopic(topic), cb);
};

Client.prototype._handleMessage = function (topic, payload, packet) {
  let matched = this.router.match(topic);

  if (!matched) {
    return this.log.debug('No handler to handle message with topic [%s]', topic);
  }

  const matchedSubs = [];
  while (matched) {
    matchedSubs.push(matched.data);
    matched = matched.next();
  }

  for (let subId in matchedSubs) {

    const sub = matchedSubs[subId];
    if (sub && !sub.cancelled) {
      let message = {
        topic: topic,
        payload: payload,
        params: sub.params,
        splats: sub.splats,
        path: sub.route,
        packet: packet
      };

      if (sub.handler.length === 1) {
        sub.handler.call(sub, message);
      } else {
        sub.handler.call(sub, topic, payload, message);
      }
    }
  }
};

Client.prototype.ready = function (cb) {
	if (!cb) {
		return;
	}

	if (this.connected) {
		cb();
	} else {
		this.once('connect', () => { // ignore connack
			cb();
		});
	}
};

Client.prototype.subscribe = function (topic, handler, options, cb) {
	const sub = new Subscription(this, topic, handler);
	if (this.connected) {
		this._subscribe(topic, options, cb);
	}
	return sub;
};

Client.prototype.publish = function (topic, message, options, cb) {
	this.mqttclient.publish(mqttTopic(topic), this.codec.encode(message), options, cb);
};

Client.prototype.end = function (force, cb) {
	this.mqttclient.end(force, cb);
};

function mqttTopic(topic) {
	return topic.replace(/:[a-zA-Z0-9]+/g, '+')
		.replace(/\*\*/g, '#')
		.replace(/\*/g, '+');
}

<commit_msg>Fix PR #4 wrong message properties and make tests pass<commit_after>const _ = require('lodash');
const async = require('async');
const util = require('util');
const EventEmitter = require('events').EventEmitter;
const codec = require('./codec');
const Router = require('./router');
const Subscription = require('./subscription');

module.exports = Client;

const levels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];

function createLogger(category) {
	const debug = require('debug')(category);
	return _.reduce(levels, (logger, level) => {
		logger[level] = function () {
			debug.apply(undefined, _(['[' + level + '] ']).concat(arguments));
		};
		return logger;
	}, {});
}

/**
 *
 * @constructor
 */
function Client(mqttclient, options, log) {
	if (!(this instanceof Client)) {
		return new Client(mqttclient, options, log);
	}

	options = options || {};

	this.mqttclient = mqttclient;
	this.codec = options.codec || codec.msgpack();
	this.log = log = log || options.log || createLogger('mqttr');

	log.debug('Using codec:', this.codec.name);

	this.router = new Router();

	const that = this;
	mqttclient.on('connect', (connack) => {
		log.debug('connected');
		that._connected();
		that.emit('connect', connack);
	});

	mqttclient.on('reconnect', () => {
		log.debug('reconnect');
		that.emit('reconnect');
	});

	mqttclient.on('close', () => {
		log.debug('close');
		that.emit('close');
	});

	mqttclient.on('offline', () => {
		log.debug('offline');
		that.emit('offline');
	});

	mqttclient.on('error', (error) => {
		that.emit('error', error);
	});

	mqttclient.on('message', (topic, payload, packet) => {
		// Try decode
		try {
			payload = that.codec.decode(payload);
		} catch (e) { // Using origin payload if failed
			// no-op
		}
		that._handleMessage(topic, payload, packet);
	});
}

util.inherits(Client, EventEmitter);

Object.defineProperty(Client.prototype, 'connected', {
	get() {
		return this.mqttclient.connected;
	}
});

Client.prototype._connected = function () {
	const that = this;
	async.eachSeries(this.router.routes, (r, cb) => {
		that._subscribe(r.path, cb);
	});
};

Client.prototype._subscribe = function (topic, options, cb) {
	if (typeof options === 'function') {
		cb = options;
		options = undefined;
	}
	return options ? this.mqttclient.subscribe(mqttTopic(topic), options, cb) : this.mqttclient.subscribe(mqttTopic(topic), cb);
};

Client.prototype._unsubscribe = function (topic, cb) {
	this.mqttclient.unsubscribe(mqttTopic(topic), cb);
};

Client.prototype._handleMessage = function (topic, payload, packet) {
	let matched = this.router.match(topic);

	if (!matched) {
		return this.log.debug('No handler to handle message with topic [%s]', topic);
	}

	const matches = [];
	while (matched) {
		matches.push(matched);
		matched = matched.next();
	}

	for (let i = 0; i < matches.length; i++) {
		const match = matches[i];
		const sub = match.data;
		if (sub && !sub.cancelled) {
			const message = {
				topic,
				payload,
				params: match.params,
				splats: match.splats,
				path: match.route,
				packet
			};

			if (sub.handler.length === 1) {
				sub.handler(message);
			} else {
				sub.handler(topic, payload, message);
			}
		}
	}
};

Client.prototype.ready = function (cb) {
	if (!cb) {
		return;
	}

	if (this.connected) {
		cb();
	} else {
		this.once('connect', () => { // ignore connack
			cb();
		});
	}
};

Client.prototype.subscribe = function (topic, handler, options, cb) {
	const sub = new Subscription(this, topic, handler);
	if (this.connected) {
		this._subscribe(topic, options, cb);
	}
	return sub;
};

Client.prototype.publish = function (topic, message, options, cb) {
	this.mqttclient.publish(mqttTopic(topic), this.codec.encode(message), options, cb);
};

Client.prototype.end = function (force, cb) {
	this.mqttclient.end(force, cb);
};

function mqttTopic(topic) {
	return topic.replace(/:[a-zA-Z0-9]+/g, '+')
		.replace(/\*\*/g, '#')
		.replace(/\*/g, '+');
}

<|endoftext|>


<commit_before>var isNative = require("is_native"),
    createMap = require("create_map");

var NativeMap = typeof(Map) !== "undefined" ? Map : null,
    MapShim, forEach, createCallback;

if (isNative(NativeMap)) {
    MapShim = NativeMap;

    MapShim.prototype.count = function() {
        return this.size;
    };
} else {
    MapShim = function Map() {
        this._map = createMap();
    };
    MapShim.prototype.constructor = MapShim;

    MapShim.prototype.get = function(key) {

        return this._map.get(key);
    };

    MapShim.prototype.set = function(key, value) {

        this._map.set(key, value);
    };

    MapShim.prototype.has = function(key) {

        return this._map.has(key);
    };

    MapShim.prototype["delete"] = function(key) {

        return this._map.remove(key);
    };

    MapShim.prototype.clear = function() {

        this._map.clear();
    };

    if (Object.defineProperty) {
        Object.defineProperty(MapShim.prototype, "size", {
            get: function() {
                return this._map.size();
            }
        });
    }

    MapShim.prototype.count = function() {
        return this._map.size();
    };

    MapShim.prototype.length = 1;

    MapShim.prototype.forEach = function(fn, thisArg) {
        return forEach(
            this,
            this._map,
            thisArg != null ? createCallback(fn, thisArg) : fn
        );
    };

    forEach = function forEach(obj, map, fn) {
        var i = -1,
            length = map.size() - 1;

        while (i++ < length) {
            if (fn(map.value(i), map.key(i), obj) === false) {
                return false;
            }
        }

        return obj;
    };

    createCallback = function createCallback(fn, thisArg) {
        return function callback(value, key, obj) {
            fn.call(thisArg, value, key, obj);
        };
    };
}

MapShim.prototype.remove = MapShim.prototype["delete"];

module.exports = MapShim;
<commit_msg>updates<commit_after>var isNative = require("is_native"),
    createMap = require("create_map");

var NativeMap = typeof(Map) !== "undefined" ? Map : null,
    MapShim, forEach, createCallback;

if (isNative(NativeMap)) {
    MapShim = NativeMap;

    MapShim.prototype.count = function() {
        return this.size;
    };
} else {
    MapShim = function Map() {
        this.__map = createMap();
    };
    MapShim.prototype.constructor = MapShim;

    MapShim.prototype.get = function(key) {

        return this.__map.get(key);
    };

    MapShim.prototype.set = function(key, value) {

        this.__map.set(key, value);
    };

    MapShim.prototype.has = function(key) {

        return this.__map.has(key);
    };

    MapShim.prototype["delete"] = function(key) {

        return this.__map.remove(key);
    };

    MapShim.prototype.clear = function() {

        this.__map.clear();
    };

    if (Object.defineProperty) {
        Object.defineProperty(MapShim.prototype, "size", {
            get: function() {
                return this.__map.size();
            }
        });
    }

    MapShim.prototype.count = function() {
        return this.__map.size();
    };

    MapShim.prototype.length = 1;

    MapShim.prototype.forEach = function(fn, thisArg) {
        return forEach(
            this,
            this.__map,
            thisArg != null ? createCallback(fn, thisArg) : fn
        );
    };

    forEach = function forEach(obj, map, fn) {
        var i = -1,
            il = map.count() - 1;

        while (i++ < il) {
            if (fn(map.value(i), map.key(i), obj) === false) {
                return false;
            }
        }

        return obj;
    };

    createCallback = function createCallback(fn, thisArg) {
        return function callback(value, key, obj) {
            fn.call(thisArg, value, key, obj);
        };
    };
}

MapShim.prototype.remove = MapShim.prototype["delete"];

module.exports = MapShim;
<|endoftext|>


<commit_before>
var factorialZeros = function(n) {
    var i=1, f5=0, m;
    while((m=Math.pow(5,i))<n) {
      f5+=Math.floor(n/m);
      i+=1;
    }
    return f5;
};

console.log(factorialZeros(100));

// http://courses.csail.mit.edu/iap/interview/Hacking_a_Google_Interview_Practice_Questions_Person_A.pdf
// Question: Target Sum

var target_sum_hash = function(target, ar) {
    var i=0, len=ar.length, hash = {};
    for (; i<len; i+=1) {
        if (hash[ar[i]]) return true;
        hash[target-ar[i]] = true;
    }
    return false;
};

var target_sum_pointer = function(target, ar) {
    var i=0, j=ar.length-1;
    ar.sort(function(a,b) { return +a-(+b);});
    while (i<j) {
        while (ar[i]+ar[j]<target && ar[i]!=null) { i+=1; }
        if (ar[i]==null) { return false; }
        if (ar[i]+ar[j]==target) return true;

        while (ar[i]+ar[j]>target && ar[j]!=null) { j-=1; }
        if (ar[j]==null) { return false; }
        if (ar[i]+ar[j]==target) return true;
    }
    return false;
};

var ar = [8,2,6,3,4,3,9,1];
console.log(target_sum_pointer(60,ar));

// http://courses.csail.mit.edu/iap/interview/Hacking_a_Google_Interview_Practice_Questions_Person_B.pdf
// Question: Odd Man Out 2014/4/13 16:00-16:16

var find_odd_hash = function(ar) {
    var i=0, len=ar.length, hash = {};
    for(; i<len; i+=1) {
        if (hash[ar[i]]) {
            delete hash[ar[i]];
        } else {
          hash[ar[i]] = true;
        }
    }
    return Object.keys(hash).shift();
};

// Super awesome!!!
var find_odd_xor = function(ar) {
    return ar.reduce(function(ac,n) {
        return ac^n;
    }, 0);
};

var ar2 = [0,9,5,7,9,2,3,5,0,2,3];
console.log(find_odd_(ar2));

// http://courses.csail.mit.edu/iap/interview/Hacking_a_Google_Interview_Practice_Questions_Person_B.pdf
// Question: Maximal Subarray 18:44-19:25

var max_subarray_memo = function(ar) {
    var i=0, j, len=ar.length, prev, memo=[], m_end, m_start, max = Number.MIN_VALUE;
    for (; i<len; i+=1) {
        memo[i] = [];
        for (j=0; j<=i; j+=1) {
            prev = memo[i-1]==null? 0 : memo[i-1][j] || 0;
            memo[i][j] = prev+ar[i];
        }
    }
    memo.forEach(function(xs, end) {
        xs.forEach(function(val, start) {
            if (val>=max) {
                m_start = start; m_end = end;
                max = val;
            }
        });
    });
    return ar.slice(m_start, m_end+1);
};

var max_subarray = function(ar) {
    var x, i=0, len=ar.length, max_ending_here = max_so_far = 0;
    for (; i<len; i+=1) {
        x = ar[i];
        max_ending_here = Math.max(0, max_ending_here + x);
        max_so_far = Math.max(max_so_far, max_ending_here);
    }
    return max_so_far;
};

var ar = [1, -3, 5, -2, 9, -8, -6, 4];

console.log(max_subarray_(ar));

//ar2 = [-1, -3, -5, -2, -9, -8, -6, -4];
//console.log(max_subarray(ar2));

var factors = function(n) {
    var i=2, result=[];
    for (;i<=n/2; i+=1) {
        if (n % i==0) { result.push(i); }
    }
    return result;
};

console.log(factors(20));

var isPower = function(_a, b) {
    var a = _a
    while(a % b ==0) {
        a /= b;
    }
    return a==1 ? true : false;
};

console.log(isPower(10000,101));

// http://www.geeksforgeeks.org/find-number-zeroes/
// 2014/4/14 22:20?-22:43

// 1,1,1,*1,0,0,0 / len = 7
// mid = 3 5 4
// begin/end = 0/7 3/7 3/5

// 0,0,0,0,0,0,0 / len = 7
// mid = 3 1 0
// begin/end = 0/7 0/3 0/1

// 1,1,1,1,1,1,1 / len = 7
// mid = 3 5 6
// begin/end = 0/7 3/7 5/7

var zero_count = function(ar) {
    var len = ar.length, n, begin=0, end=len, mid, head_0_i;

    while (true) {
        mid = begin + Math.floor((end-begin)/2);
        n = ar[mid];
        if (n===1) {
            if (mid==len-1) { head_0_i = len; break; }
            begin = mid;
        } else {
            if (mid==0 || ar[mid-1]==1) { head_0_i = mid; break; }
            end = mid;
        }
    }
    return len-head_0_i;
};

console.log(zero_count([1,1,1,1,0,0,0]));

// http://www.geeksforgeeks.org/find-if-there-is-a-subarray-with-0-sum/
// 2014/4/14 23:00-

var has_zero_sum = function(ar) {
    var i=0, len=ar.length, j, sum;
    for (; i<len; i+=1) {
        sum = 0;
        for (j=i; j<len; j+=1) {
            sum += ar[j];
            if (sum===0) { return true; }
        }
    }
    return false;
};
console.log(has_zero_sum([-3, 2, 3, 1, 6]));
<commit_msg>Implemented merge in number problems<commit_after>
var factorialZeros = function(n) {
    var i=1, f5=0, m;
    while((m=Math.pow(5,i))<n) {
      f5+=Math.floor(n/m);
      i+=1;
    }
    return f5;
};

console.log(factorialZeros(100));

// http://courses.csail.mit.edu/iap/interview/Hacking_a_Google_Interview_Practice_Questions_Person_A.pdf
// Question: Target Sum

var target_sum_hash = function(target, ar) {
    var i=0, len=ar.length, hash = {};
    for (; i<len; i+=1) {
        if (hash[ar[i]]) return true;
        hash[target-ar[i]] = true;
    }
    return false;
};

var target_sum_pointer = function(target, ar) {
    var i=0, j=ar.length-1;
    ar.sort(function(a,b) { return +a-(+b);});
    while (i<j) {
        while (ar[i]+ar[j]<target && ar[i]!=null) { i+=1; }
        if (ar[i]==null) { return false; }
        if (ar[i]+ar[j]==target) return true;

        while (ar[i]+ar[j]>target && ar[j]!=null) { j-=1; }
        if (ar[j]==null) { return false; }
        if (ar[i]+ar[j]==target) return true;
    }
    return false;
};

var ar = [8,2,6,3,4,3,9,1];
console.log(target_sum_pointer(60,ar));

// http://courses.csail.mit.edu/iap/interview/Hacking_a_Google_Interview_Practice_Questions_Person_B.pdf
// Question: Odd Man Out 2014/4/13 16:00-16:16

var find_odd_hash = function(ar) {
    var i=0, len=ar.length, hash = {};
    for(; i<len; i+=1) {
        if (hash[ar[i]]) {
            delete hash[ar[i]];
        } else {
          hash[ar[i]] = true;
        }
    }
    return Object.keys(hash).shift();
};

// Super awesome!!!
var find_odd_xor = function(ar) {
    return ar.reduce(function(ac,n) {
        return ac^n;
    }, 0);
};

var ar2 = [0,9,5,7,9,2,3,5,0,2,3];
console.log(find_odd_hash(ar2));

// http://courses.csail.mit.edu/iap/interview/Hacking_a_Google_Interview_Practice_Questions_Person_B.pdf
// Question: Maximal Subarray 18:44-19:25

var max_subarray_memo = function(ar) {
    var i=0, j, len=ar.length, prev, memo=[], m_end, m_start, max = Number.MIN_VALUE;
    for (; i<len; i+=1) {
        memo[i] = [];
        for (j=0; j<=i; j+=1) {
            prev = memo[i-1]==null? 0 : memo[i-1][j] || 0;
            memo[i][j] = prev+ar[i];
        }
    }
    memo.forEach(function(xs, end) {
        xs.forEach(function(val, start) {
            if (val>=max) {
                m_start = start; m_end = end;
                max = val;
            }
        });
    });
    return ar.slice(m_start, m_end+1);
};

var max_subarray = function(ar) {
    var x, i=0, len=ar.length, max_ending_here = max_so_far = 0;
    for (; i<len; i+=1) {
        x = ar[i];
        max_ending_here = Math.max(0, max_ending_here + x);
        max_so_far = Math.max(max_so_far, max_ending_here);
    }
    return max_so_far;
};

var ar = [1, -3, 5, -2, 9, -8, -6, 4];

console.log(max_subarray(ar));

//ar2 = [-1, -3, -5, -2, -9, -8, -6, -4];
//console.log(max_subarray(ar2));

var factors = function(n) {
    var i=2, result=[];
    for (;i<=n/2; i+=1) {
        if (n % i==0) { result.push(i); }
    }
    return result;
};

console.log(factors(20));

var isPower = function(_a, b) {
    var a = _a
    while(a % b ==0) {
        a /= b;
    }
    return a==1 ? true : false;
};

console.log(isPower(10000,101));

// http://www.geeksforgeeks.org/find-number-zeroes/
// 2014/4/14 22:20?-22:43

// 1,1,1,*1,0,0,0 / len = 7
// mid = 3 5 4
// begin/end = 0/7 3/7 3/5

// 0,0,0,0,0,0,0 / len = 7
// mid = 3 1 0
// begin/end = 0/7 0/3 0/1

// 1,1,1,1,1,1,1 / len = 7
// mid = 3 5 6
// begin/end = 0/7 3/7 5/7

var zero_count = function(ar) {
    var len = ar.length, n, begin=0, end=len, mid, head_0_i;

    while (true) {
        mid = begin + Math.floor((end-begin)/2);
        n = ar[mid];
        if (n===1) {
            if (mid==len-1) { head_0_i = len; break; }
            begin = mid;
        } else {
            if (mid==0 || ar[mid-1]==1) { head_0_i = mid; break; }
            end = mid;
        }
    }
    return len-head_0_i;
};

console.log(zero_count([1,1,1,1,0,0,0]));

// http://www.geeksforgeeks.org/find-if-there-is-a-subarray-with-0-sum/
// 2014/4/14 23:00-

var has_zero_sum = function(ar) {
    var i=0, len=ar.length, j, sum;
    for (; i<len; i+=1) {
        sum = 0;
        for (j=i; j<len; j+=1) {
            sum += ar[j];
            if (sum===0) { return true; }
        }
    }
    return false;
};
console.log(has_zero_sum([-3, 2, 3, 1, 6]));

var merge_arrays = function(ars) {
  return ars.reduce(function(acc, array) {
    return _merge(array, acc);
  }, []);
};
var _merge = function(ar1, ar2) {
  var result = [], l_i=0, r_i=0, left, right, l_len=ar1.length, r_len=ar2.length;
  while(l_i<l_len || r_i<r_len) {
    left  = ar1[l_i] || Number.MAX_VALUE;
    right = ar2[r_i] || Number.MAX_VALUE;
    if (left<=right) {
      result.push(ar1[l_i]); l_i+=1;
    } else {
      result.push(ar2[r_i]); r_i+=1;
    }
  }
  return result;
};

console.log(merge_arrays([
  [2, 5, 10],
  [25, 100, 105],
  [7, 56, 42]
]));
<|endoftext|>


<commit_before>'use strict';

var dgram = require('dgram');

/**
 *  noop
 */

function noop() {};

/**
 *  Expose `Artnet`
 */

module.exports = Artnet;

function Artnet(opts) {
  var opts = opts || {};

  this.host = opts.host || '255.255.255.255';
  this.port = opts.port || 6454;
  this.refresh = opts.refresh || 0;

  this.header =  [65, 114, 116, 45, 78, 101, 116, 0, 0, 80, 0, 14, 0, 0, 1, 0, 2, 0];

  this.socket = null;
  this.interval = null;
  this.timer = null;

  this.data = [];
};

/**
 *  Connect to host
 */

Artnet.prototype.connect = function connect() {
  this.socket = dgram.createSocket('udp4');

  this._startInterval();

  return this;
};

/**
 *  Close a connection
 *
 *  @param {Function} callback - optional
 */

Artnet.prototype.disconnect = function disconnect(callback) {
  callback = callback || noop;

  var self = this;

  this.socket && this.socket.close(function() {
    self.socket = null;
    callback();
  });

  return this;
};

/**
 *  Start the interval
 *
 *  @param {Number} interval
 */

Artnet.prototype._startInterval = function _startInterval(interval) {

  if (interval) this.interval = interval;

  if (!this.interval || this.interval < 50) return null;

  var self = this;

  this.timer = setInterval(function() {
    self._send();
  }, this.interval);

  return this.timer;
}

/**
 *  Stop the interval
 */

Artnet.prototype._stopInterval = function _startInterval() {
  if (!this.timer) return null;

  clearInterval(this.timer);
};

/**
 *  Set a value
 *
 *  @param {Number} universe
 *  @param {Number} channel
 *  @param {Number} value
 */

Artnet.prototype.set = function set(universe, channel, value) {

  if (value < 0) value = 0;
  if (value > 255) value = 255;

  if (channel < 0) channel = 0;
  if (channel > 512) channel = 512;

  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe][channel] = value;
};

Artnet.prototype.setAll = function set(universe, data) {
  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe] = data;
};

/**
 *  Transmit the data
 */

Artnet.prototype.send = function send() {

  this.data.forEach(function(universe, uIndex){

    this.header[14] = uIndex;

    var data = this.header.concat(this.data[uIndex]);
    var buff = new Buffer(data);

    this.socket.send(buff, 0, buff.length, this.port, this.host);
  }, this);

};

/**
 *  Create a universe (a 512 member array of zeros)
 */

function _createUniverse() {
  return Array.apply(null, new Array(512)).map(Number.prototype.valueOf, 0);
};
<commit_msg>Removed intervals<commit_after>'use strict';

var dgram = require('dgram');

/**
 *  noop
 */

function noop() {};

/**
 *  Expose `Artnet`
 */

module.exports = Artnet;

function Artnet(opts) {
  var opts = opts || {};

  this.host = opts.host || '255.255.255.255';
  this.port = opts.port || 6454;
  //this.refresh = opts.refresh || 0;

  this.header =  [65, 114, 116, 45, 78, 101, 116, 0, 0, 80, 0, 14, 0, 0, 1, 0, 2, 0];

  this.socket = null;
  //this.interval = null;
  //this.timer = null;

  this.data = [];
};

/**
 *  Connect to host
 */

Artnet.prototype.connect = function connect() {
  this.socket = dgram.createSocket('udp4');

  //this._startInterval();

  return this;
};

/**
 *  Close a connection
 *
 *  @param {Function} callback - optional
 */

Artnet.prototype.disconnect = function disconnect(callback) {
  callback = callback || noop;

  var self = this;

  this.socket && this.socket.close(function() {
    self.socket = null;
    callback();
  });

  return this;
};

/**
 *  Start the interval
 *
 *  @param {Number} interval
 */

/**
Artnet.prototype._startInterval = function _startInterval(interval) {

  if (interval) this.interval = interval;

  if (!this.interval || this.interval < 50) return null;

  var self = this;

  this.timer = setInterval(function() {
    self._send();
  }, this.interval);

  return this.timer;
}
*/

/**
 *  Stop the interval
 */

/**
Artnet.prototype._stopInterval = function _startInterval() {
  if (!this.timer) return null;

  clearInterval(this.timer);
};
*/

/**
 *  Set a value
 *
 *  @param {Number} universe
 *  @param {Number} channel
 *  @param {Number} value
 */

Artnet.prototype.set = function set(universe, channel, value) {

  if (value < 0) value = 0;
  if (value > 255) value = 255;

  if (channel < 0) channel = 0;
  if (channel > 512) channel = 512;

  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe][channel] = value;
};

Artnet.prototype.setAll = function set(universe, data) {
  if (!this.data[universe]) this.data[universe] = _createUniverse();

  this.data[universe] = data;
};

/**
 *  Transmit the data
 */

Artnet.prototype.send = function send() {

  this.data.forEach(function(universe, uIndex){

    this.header[14] = uIndex;

    var data = this.header.concat(this.data[uIndex]);
    var buff = new Buffer(data);

    this.socket.send(buff, 0, buff.length, this.port, this.host);
  }, this);

};

/**
 *  Create a universe (a 512 member array of zeros)
 */

function _createUniverse() {
  return Array.apply(null, new Array(512)).map(Number.prototype.valueOf, 0);
};
<|endoftext|>


<commit_before>export { default as withDeprecationWarning } from './withDeprecationWarning'
export { default as withDisplayName } from './withDisplayName'
export { default as withFocusProps } from './withFocusProps'
export { default as withNotifyOnLowFPS } from './withNotifyOnLowFPS'
export { default as withHoverProps } from './withHoverProps'
export { default as withMouseDownProps } from './withMouseDownProps'
export { default as withOverrideFromContext } from './withOverrideFromContext'
export { default as withTheme } from './withTheme'
export { default as withTouchProps } from './withTouchProps'
export { default as withUncontrolledProp } from './withUncontrolledProp'
export { default as withUniqueFormIdentifier } from './withUniqueFormIdentifier'
<commit_msg>withStyleSheetOverride<commit_after>export { default as withDeprecationWarning } from './withDeprecationWarning'
export { default as withDisplayName } from './withDisplayName'
export { default as withFocusProps } from './withFocusProps'
export { default as withNotifyOnLowFPS } from './withNotifyOnLowFPS'
export { default as withHoverProps } from './withHoverProps'
export { default as withMouseDownProps } from './withMouseDownProps'
export { default as withOverrideFromContext } from './withOverrideFromContext'
export { default as withStyleSheetOverride } from './withStyleSheetOverride'
export { default as withTheme } from './withTheme'
export { default as withTouchProps } from './withTouchProps'
export { default as withUncontrolledProp } from './withUncontrolledProp'
export { default as withUniqueFormIdentifier } from './withUniqueFormIdentifier'
<|endoftext|>


<commit_before>var Stream = require('stream')
  , gutil = require('gulp-util')
  , BufferStreams = require('bufferstreams')
  , svg2ttf = require('svg2ttf')
  , path = require('path')
;

const PLUGIN_NAME = 'gulp-svg2ttf';

// File level transform function
function svg2ttfTransform(opt) {
  // Return a callback function handling the buffered content
  return function(err, buf, cb) {

    // Handle any error
    if(err) {
      cb(new gutil.PluginError(PLUGIN_NAME, err, {showStack: true}));
    }

    // Use the buffered content
      try {
        buf = new Buffer(svg2ttf(String(buf)).buffer);
        cb(null, buf);
      } catch(err) {
        cb(new gutil.PluginError(PLUGIN_NAME, err, {showStack: true}));
      }

  };
}

// Plugin function
function svg2ttfGulp(options) {

  options = options || {};
  options.ignoreExt = options.ignoreExt || false;
  options.clone = options.clone || false;

  var stream = Stream.Transform({objectMode: true});

  stream._transform = function(file, unused, done) {
     // When null just pass through
    if(file.isNull()) {
      stream.push(file); done();
      return;
    }

    // If the ext doesn't match, pass it through
    if((!options.ignoreExt) && '.svg' !== path.extname(file.path)) {
      stream.push(file); done();
      return;
    }

    if(options.clone) {
      stream.push(file.clone());
    }

    file.path = gutil.replaceExtension(file.path, ".ttf");

    // Buffers
    if(file.isBuffer()) {
      try {
        file.contents = new Buffer(svg2ttf(String(file.contents)).buffer);
      } catch(err) {
        stream.emit('error',
          new gutil.PluginError(PLUGIN_NAME, err, {showStack: true}));
      }

    // Streams
    } else {
      file.contents = file.contents.pipe(new BufferStreams(svg2ttfTransform()));
    }

    stream.push(file);
    done();
  };

  return stream;

};

// Export the file level transform function for other plugins usage
svg2ttfGulp.fileTransform = svg2ttfTransform;

// Export the plugin main function
module.exports = svg2ttfGulp;

<commit_msg>Adding readable-stream dep closes #2<commit_after>var Stream = require('readable-stream')
  , gutil = require('gulp-util')
  , BufferStreams = require('bufferstreams')
  , svg2ttf = require('svg2ttf')
  , path = require('path')
;

const PLUGIN_NAME = 'gulp-svg2ttf';

// File level transform function
function svg2ttfTransform(opt) {
  // Return a callback function handling the buffered content
  return function(err, buf, cb) {

    // Handle any error
    if(err) {
      cb(new gutil.PluginError(PLUGIN_NAME, err, {showStack: true}));
    }

    // Use the buffered content
      try {
        buf = new Buffer(svg2ttf(String(buf)).buffer);
        cb(null, buf);
      } catch(err) {
        cb(new gutil.PluginError(PLUGIN_NAME, err, {showStack: true}));
      }

  };
}

// Plugin function
function svg2ttfGulp(options) {

  options = options || {};
  options.ignoreExt = options.ignoreExt || false;
  options.clone = options.clone || false;

  var stream = Stream.Transform({objectMode: true});

  stream._transform = function(file, unused, done) {
     // When null just pass through
    if(file.isNull()) {
      stream.push(file); done();
      return;
    }

    // If the ext doesn't match, pass it through
    if((!options.ignoreExt) && '.svg' !== path.extname(file.path)) {
      stream.push(file); done();
      return;
    }

    if(options.clone) {
      stream.push(file.clone());
    }

    file.path = gutil.replaceExtension(file.path, ".ttf");

    // Buffers
    if(file.isBuffer()) {
      try {
        file.contents = new Buffer(svg2ttf(String(file.contents)).buffer);
      } catch(err) {
        stream.emit('error',
          new gutil.PluginError(PLUGIN_NAME, err, {showStack: true}));
      }

    // Streams
    } else {
      file.contents = file.contents.pipe(new BufferStreams(svg2ttfTransform()));
    }

    stream.push(file);
    done();
  };

  return stream;

};

// Export the file level transform function for other plugins usage
svg2ttfGulp.fileTransform = svg2ttfTransform;

// Export the plugin main function
module.exports = svg2ttfGulp;

<|endoftext|>


<commit_before>var io, load, exec, join, Emitify, loadRemote;

(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports  = new SperoProto();
    else
        global.spero    = new SperoProto();

    function SperoProto() {
        var Progress;

        function Spero(prefix, callback) {
            if (!callback) {
                callback    = prefix;
                prefix      = '/spero';
            }

            loadAll(prefix, function() {
                Progress = new ProgressProto(prefix);
                Object.setPrototypeOf(Spero, Emitify());

                if (typeof callback === 'function')
                    callback();
            });
        }

        Spero.copy  = function(from, to, files) {
            Progress.copy(from, to, files);
        };

        Spero.abort = function() {
            Progress.abort();
        };

        Spero.pause = function() {
            Progress.pause();
        };

        Spero.continue = function() {
            Progress.continue();
        };

        function loadAll(prefix, callback) {
            var scripts = [];

            if (!exec)
                scripts.push('/modules/execon/lib/exec.js');

            if (!scripts.length)
                loadFiles(prefix, callback);
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), function() {
                    loadFiles(prefix, callback);
                });
        }

        function getModulePath(name) {
            var dir     = '/modules/',
                lib     = '/lib/',
                path    = dir + name + lib + name + '.js';

            return path;
        }

        function loadFiles(prefix, callback) {
            exec.series([
                function(callback) {
                    var obj     = {
                            loadRemote  : getModulePath('loadremote'),
                            load        : getModulePath('load'),
                            Emitify     : getModulePath('emitify'),
                            join        : '/join/join.js'
                        },

                        scripts = Object.keys(obj)
                            .filter(function(name) {
                                return !window[name];
                            })
                            .map(function(name) {
                                return prefix + obj[name];
                            });

                    exec.if(!scripts.length, callback, function() {
                        loadScript(scripts, callback);
                    });
                },

                function(callback) {
                    loadRemote('socket', {
                        name : 'io',
                        prefix: prefix,
                        noPrefix: true
                    }, callback);
                },

                function() {
                    callback();
                }
            ]);
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function ProgressProto(room) {
            var socket;

            init(room);

            function init(room) {
                var href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('err', function(error) {
                    Spero.emit('error', error);
                });

                socket.on('file', function(name) {
                    Spero.emit('file', name);
                });

                socket.on('progress', function(percent) {
                    Spero.emit('progress', percent);
                });

                socket.on('end', function() {
                    Spero.emit('end');
                });

                socket.on('connect', function() {
                    Spero.emit('connect');
                });

                socket.on('disconnect', function() {
                    Spero.emit('disconnect');
                });
            }

            this.pause       = function() {
                socket.emit('pause');
            };

            this.continue   = function() {
                socket.emit('continue');
            };

            this.abort       = function() {
                socket.emit('abort');
            };

            this.copy       = function(from, to, files) {
                socket.emit('copy', from, to, files);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }
        }

        return Spero;
    }

})(this);
<commit_msg>fix(client) getModulesPath: lib<commit_after>var io, load, exec, join, Emitify, loadRemote;

(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports  = new SperoProto();
    else
        global.spero    = new SperoProto();

    function SperoProto() {
        var Progress;

        function Spero(prefix, callback) {
            if (!callback) {
                callback    = prefix;
                prefix      = '/spero';
            }

            loadAll(prefix, function() {
                Progress = new ProgressProto(prefix);
                Object.setPrototypeOf(Spero, Emitify());

                if (typeof callback === 'function')
                    callback();
            });
        }

        Spero.copy  = function(from, to, files) {
            Progress.copy(from, to, files);
        };

        Spero.abort = function() {
            Progress.abort();
        };

        Spero.pause = function() {
            Progress.pause();
        };

        Spero.continue = function() {
            Progress.continue();
        };

        function loadAll(prefix, callback) {
            var scripts = [];

            if (!exec)
                scripts.push('/modules/execon/lib/exec.js');

            if (!scripts.length)
                loadFiles(prefix, callback);
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), function() {
                    loadFiles(prefix, callback);
                });
        }

        function getModulePath(name, lib) {
            var dir     = '/modules/',
                libdir  = (lib || '') + '/',
                path    = dir + name + libdir + name + '.js';

            return path;
        }

        function loadFiles(prefix, callback) {
            exec.series([
                function(callback) {
                    var obj     = {
                            loadRemote  : getModulePath('loadremote', 'lib'),
                            load        : getModulePath('load'),
                            Emitify     : getModulePath('emitify', 'lib'),
                            join        : '/join/join.js'
                        },

                        scripts = Object.keys(obj)
                            .filter(function(name) {
                                return !window[name];
                            })
                            .map(function(name) {
                                return prefix + obj[name];
                            });

                    exec.if(!scripts.length, callback, function() {
                        loadScript(scripts, callback);
                    });
                },

                function(callback) {
                    loadRemote('socket', {
                        name : 'io',
                        prefix: prefix,
                        noPrefix: true
                    }, callback);
                },

                function() {
                    callback();
                }
            ]);
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function ProgressProto(room) {
            var socket;

            init(room);

            function init(room) {
                var href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('err', function(error) {
                    Spero.emit('error', error);
                });

                socket.on('file', function(name) {
                    Spero.emit('file', name);
                });

                socket.on('progress', function(percent) {
                    Spero.emit('progress', percent);
                });

                socket.on('end', function() {
                    Spero.emit('end');
                });

                socket.on('connect', function() {
                    Spero.emit('connect');
                });

                socket.on('disconnect', function() {
                    Spero.emit('disconnect');
                });
            }

            this.pause       = function() {
                socket.emit('pause');
            };

            this.continue   = function() {
                socket.emit('continue');
            };

            this.abort       = function() {
                socket.emit('abort');
            };

            this.copy       = function(from, to, files) {
                socket.emit('copy', from, to, files);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }
        }

        return Spero;
    }

})(this);
<|endoftext|>


<commit_before>const AudioContext = require('audio-context');

const log = require('./log');
const uid = require('./uid');

const PitchEffect = require('./effects/PitchEffect');
const PanEffect = require('./effects/PanEffect');

const SoundPlayer = require('./SoundPlayer');
const ADPCMSoundDecoder = require('./ADPCMSoundDecoder');
const InstrumentPlayer = require('./InstrumentPlayer');
const DrumPlayer = require('./DrumPlayer');

/**
 * @fileOverview Scratch Audio is divided into a single AudioEngine,
 * that handles global functionality, and AudioPlayers, belonging to individual sprites and clones.
 */

class AudioPlayer {
    /**
     * Each sprite or clone has an audio player
     * the audio player handles sound playback, volume, and the sprite-specific audio effects:
     * pitch and pan
     * @param {AudioEngine} audioEngine AudioEngine for player
     * @constructor
     */
    constructor (audioEngine) {
        this.audioEngine = audioEngine;

        // Create the audio effects
        this.pitchEffect = new PitchEffect();
        this.panEffect = new PanEffect(this.audioEngine.audioContext);

        // Chain the audio effects together
        // effectsNode -> panEffect -> audioEngine.input
        this.effectsNode = this.audioEngine.audioContext.createGain();
        this.effectsNode.connect(this.panEffect.panner);
        this.panEffect.connect(this.audioEngine.input);

        // reset effects to their default parameters
        this.clearEffects();

        // sound players that are currently playing, indexed by the sound's soundId
        this.activeSoundPlayers = {};
    }

    /**
     * Play a sound
     * @param  {string} soundId - the soundId id of a sound file
     * @return {Promise} a Promise that resolves when the sound finishes playing
     */
    playSound (soundId) {
        // if this sound is not in the audio engine, return
        if (!this.audioEngine.audioBuffers[soundId]) {
            return;
        }

        // if this sprite or clone is already playing this sound, stop it first
        if (this.activeSoundPlayers[soundId]) {
            this.activeSoundPlayers[soundId].stop();
        }

        // create a new soundplayer to play the sound
        const player = new SoundPlayer(this.audioEngine.audioContext);
        player.setBuffer(this.audioEngine.audioBuffers[soundId]);
        player.connect(this.effectsNode);
        this.pitchEffect.updatePlayer(player);
        player.start();

        // add it to the list of active sound players
        this.activeSoundPlayers[soundId] = player;

        // remove sounds that are not playing from the active sound players array
        for (const id in this.activeSoundPlayers) {
            if (this.activeSoundPlayers.hasOwnProperty(id)) {
                if (!this.activeSoundPlayers[id].isPlaying) {
                    delete this.activeSoundPlayers[id];
                }
            }
        }

        return player.finished();
    }

    /**
     * Play a drum sound. The AudioEngine contains the DrumPlayer, but the AudioPlayer
     * calls this function so that it can pass a reference to its own effects node.
     * @param  {number} drum - a drum number (0-indexed)
     * @param  {number} beats - a duration in beats
     * @return {Promise} a Promise that resolves after the duration has elapsed
     */
    playDrumForBeats (drum, beats) {
        this.audioEngine.drumPlayer.play(drum, this.effectsNode);
        return this.audioEngine.waitForBeats(beats);
    }

    /**
     * Stop all sounds, notes and drums that are playing
     */
    stopAllSounds () {
        // stop all active sound players
        for (const soundId in this.activeSoundPlayers) {
            this.activeSoundPlayers[soundId].stop();
        }

        // stop all instruments
        this.audioEngine.instrumentPlayer.stopAll();

        // stop drum notes
        this.audioEngine.drumPlayer.stopAll();
    }

    /**
     * Set an audio effect to a value
     * @param {string} effect - the name of the effect
     * @param {number} value - the value to set the effect to
     */
    setEffect (effect, value) {
        switch (effect) {
        case this.audioEngine.EFFECT_NAMES.pitch:
            this.pitchEffect.set(value, this.activeSoundPlayers);
            break;
        case this.audioEngine.EFFECT_NAMES.pan:
            this.panEffect.set(value);
            break;
        }
    }

    /**
     * Clear all audio effects
     */
    clearEffects () {
        this.panEffect.set(0);
        this.pitchEffect.set(0, this.activeSoundPlayers);
        this.effectsNode.gain.value = 1;
    }

    /**
     * Set the volume for sounds played by this AudioPlayer
     * @param {number} value - the volume in range 0-100
     */
    setVolume (value) {
        this.effectsNode.gain.value = value / 100;
    }
}

/**
 * There is a single instance of the AudioEngine. It handles global audio properties and effects,
 * loads all the audio buffers for sounds belonging to sprites, and creates a single instrument player
 * and a drum player, used by all play note and play drum blocks.
 */
class AudioEngine {
    constructor () {
        this.audioContext = new AudioContext();

        this.input = this.audioContext.createGain();
        this.input.connect(this.audioContext.destination);

        // global tempo in bpm (beats per minute)
        this.currentTempo = 60;

        // instrument player for play note blocks
        this.instrumentPlayer = new InstrumentPlayer(this.audioContext);
        this.instrumentPlayer.outputNode = this.input;
        this.numInstruments = this.instrumentPlayer.instrumentNames.length;

        // drum player for play drum blocks
        this.drumPlayer = new DrumPlayer(this.audioContext);
        this.numDrums = this.drumPlayer.drumSounds.length;

        // a map of soundIds to audio buffers, holding sounds for all sprites
        this.audioBuffers = {};

        // microphone, for measuring loudness, with a level meter analyzer
        this.mic = null;
    }

    /**
     * Names of the audio effects.
     * @enum {string}
     */
    get EFFECT_NAMES () {
        return {
            pitch: 'pitch',
            pan: 'pan'
        };
    }

    /**
     * Decode a sound, decompressing it into audio samples.
     * Store a reference to it the sound in the audioBuffers dictionary, indexed by soundId
     * @param  {object} sound - an object containing audio data and metadata for a sound
     * @property {Buffer} data - sound data loaded from scratch-storage.
     * @property {string} format - format type, either empty or adpcm.
     * @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored.
     */
    decodeSound (sound) {
        const soundId = uid();
        let loaderPromise = null;

        // Make a copy of the buffer because decoding detaches the original buffer
        const bufferCopy = sound.data.buffer.slice(0);

        switch (sound.format) {
        case '':
            loaderPromise = this.audioContext.decodeAudioData(bufferCopy);
            break;
        case 'adpcm':
            loaderPromise = (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy);
            break;
        default:
            return log.warn('unknown sound format', sound.format);
        }

        const storedContext = this;
        return loaderPromise.then(
            decodedAudio => {
                storedContext.audioBuffers[soundId] = decodedAudio;
                return soundId;
            },
            error => {
                log.warn('audio data could not be decoded', error);
            }
        );
    }

    /**
     * Retrieve the audio buffer as held in memory for a given sound id.
     * @param {!string} soundId - the id of the sound buffer to get
     * @return {AudioBuffer} the buffer corresponding to the given sound id.
     */
    getSoundBuffer (soundId, newBuffer) {
        return this.audioBuffers[soundId] = newBuffer;
    }

    /**
     * Update the in-memory audio buffer to a new one by soundId.
     * @param {!string} soundId - the id of the sound buffer to update.
     * @param {AudioBuffer} newBuffer - the new buffer to swap in.
     */
    updateSoundBuffer (soundId, newBuffer) {
        this.audioBuffers[soundId] = newBuffer;
    }

    /**
     * An older version of the AudioEngine had this function to load all sounds
     * This is a stub to provide a warning when it is called
     * @todo remove this
     */
    loadSounds () {
        log.warn('The loadSounds function is no longer available. Please use Scratch Storage.');
    }

    /**
     * Play a note for a duration on an instrument with a volume
     * @param  {number} note - a MIDI note number
     * @param  {number} beats - a duration in beats
     * @param  {number} inst - an instrument number (0-indexed)
     * @param  {number} vol - a volume level (0-100%)
     * @return {Promise} a Promise that resolves after the duration has elapsed
     */
    playNoteForBeatsWithInstAndVol (note, beats, inst, vol) {
        const sec = this.beatsToSec(beats);
        this.instrumentPlayer.playNoteForSecWithInstAndVol(note, sec, inst, vol);
        return this.waitForBeats(beats);
    }

    /**
     * Convert a number of beats to a number of seconds, using the current tempo
     * @param  {number} beats number of beats to convert to secs
     * @return {number} seconds number of seconds `beats` will last
     */
    beatsToSec (beats) {
        return (60 / this.currentTempo) * beats;
    }

    /**
     * Wait for some number of beats
     * @param  {number} beats number of beats to wait for
     * @return {Promise} a Promise that resolves after the duration has elapsed
     */
    waitForBeats (beats) {
        const storedContext = this;
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, storedContext.beatsToSec(beats) * 1000);
        });
    }

    /**
     * Set the global tempo in bpm (beats per minute)
     * @param {number} value - the new tempo to set
     */
    setTempo (value) {
        this.currentTempo = value;
    }

    /**
     * Change the tempo by some number of bpm (beats per minute)
     * @param  {number} value - the number of bpm to change the tempo by
     */
    changeTempo (value) {
        this.setTempo(this.currentTempo + value);
    }

    /**
     * Get the current loudness of sound received by the microphone.
     * Sound is measured in RMS and smoothed.
     * Some code adapted from Tone.js: https://github.com/Tonejs/Tone.js
     * @return {number} loudness scaled 0 to 100
     */
    getLoudness () {
        // The microphone has not been set up, so try to connect to it
        if (!this.mic && !this.connectingToMic) {
            this.connectingToMic = true; // prevent multiple connection attempts
            navigator.mediaDevices.getUserMedia({audio: true}).then(stream => {
                this.mic = this.audioContext.createMediaStreamSource(stream);
                this.analyser = this.audioContext.createAnalyser();
                this.mic.connect(this.analyser);
                this.micDataArray = new Float32Array(this.analyser.fftSize);
            })
            .catch(err => {
                log.warn(err);
            });
        }

        // If the microphone is set up and active, measure the loudness
        if (this.mic && this.mic.mediaStream.active) {
            this.analyser.getFloatTimeDomainData(this.micDataArray);
            let sum = 0;
            // compute the RMS of the sound
            for (let i = 0; i < this.micDataArray.length; i++){
                sum += Math.pow(this.micDataArray[i], 2);
            }
            let rms = Math.sqrt(sum / this.micDataArray.length);
            // smooth the value, if it is descending
            if (this._lastValue) {
                rms = Math.max(rms, this._lastValue * 0.6);
            }
            this._lastValue = rms;

            // Scale the measurement so it's more sensitive to quieter sounds
            rms *= 1.63;
            rms = Math.sqrt(rms);
            // Scale it up to 0-100 and round
            rms = Math.round(rms * 100);
            // Prevent it from going above 100
            rms = Math.min(rms, 100);
            return rms;
        }

        // if there is no microphone input, return -1
        return -1;
    }

    /**
     * Create an AudioPlayer. Each sprite or clone has an AudioPlayer.
     * It includes a reference to the AudioEngine so it can use global
     * functionality such as playing notes.
     * @return {AudioPlayer} new AudioPlayer instance
     */
    createPlayer () {
        return new AudioPlayer(this);
    }
}

module.exports = AudioEngine;
<commit_msg>Fix getter<commit_after>const AudioContext = require('audio-context');

const log = require('./log');
const uid = require('./uid');

const PitchEffect = require('./effects/PitchEffect');
const PanEffect = require('./effects/PanEffect');

const SoundPlayer = require('./SoundPlayer');
const ADPCMSoundDecoder = require('./ADPCMSoundDecoder');
const InstrumentPlayer = require('./InstrumentPlayer');
const DrumPlayer = require('./DrumPlayer');

/**
 * @fileOverview Scratch Audio is divided into a single AudioEngine,
 * that handles global functionality, and AudioPlayers, belonging to individual sprites and clones.
 */

class AudioPlayer {
    /**
     * Each sprite or clone has an audio player
     * the audio player handles sound playback, volume, and the sprite-specific audio effects:
     * pitch and pan
     * @param {AudioEngine} audioEngine AudioEngine for player
     * @constructor
     */
    constructor (audioEngine) {
        this.audioEngine = audioEngine;

        // Create the audio effects
        this.pitchEffect = new PitchEffect();
        this.panEffect = new PanEffect(this.audioEngine.audioContext);

        // Chain the audio effects together
        // effectsNode -> panEffect -> audioEngine.input
        this.effectsNode = this.audioEngine.audioContext.createGain();
        this.effectsNode.connect(this.panEffect.panner);
        this.panEffect.connect(this.audioEngine.input);

        // reset effects to their default parameters
        this.clearEffects();

        // sound players that are currently playing, indexed by the sound's soundId
        this.activeSoundPlayers = {};
    }

    /**
     * Play a sound
     * @param  {string} soundId - the soundId id of a sound file
     * @return {Promise} a Promise that resolves when the sound finishes playing
     */
    playSound (soundId) {
        // if this sound is not in the audio engine, return
        if (!this.audioEngine.audioBuffers[soundId]) {
            return;
        }

        // if this sprite or clone is already playing this sound, stop it first
        if (this.activeSoundPlayers[soundId]) {
            this.activeSoundPlayers[soundId].stop();
        }

        // create a new soundplayer to play the sound
        const player = new SoundPlayer(this.audioEngine.audioContext);
        player.setBuffer(this.audioEngine.audioBuffers[soundId]);
        player.connect(this.effectsNode);
        this.pitchEffect.updatePlayer(player);
        player.start();

        // add it to the list of active sound players
        this.activeSoundPlayers[soundId] = player;

        // remove sounds that are not playing from the active sound players array
        for (const id in this.activeSoundPlayers) {
            if (this.activeSoundPlayers.hasOwnProperty(id)) {
                if (!this.activeSoundPlayers[id].isPlaying) {
                    delete this.activeSoundPlayers[id];
                }
            }
        }

        return player.finished();
    }

    /**
     * Play a drum sound. The AudioEngine contains the DrumPlayer, but the AudioPlayer
     * calls this function so that it can pass a reference to its own effects node.
     * @param  {number} drum - a drum number (0-indexed)
     * @param  {number} beats - a duration in beats
     * @return {Promise} a Promise that resolves after the duration has elapsed
     */
    playDrumForBeats (drum, beats) {
        this.audioEngine.drumPlayer.play(drum, this.effectsNode);
        return this.audioEngine.waitForBeats(beats);
    }

    /**
     * Stop all sounds, notes and drums that are playing
     */
    stopAllSounds () {
        // stop all active sound players
        for (const soundId in this.activeSoundPlayers) {
            this.activeSoundPlayers[soundId].stop();
        }

        // stop all instruments
        this.audioEngine.instrumentPlayer.stopAll();

        // stop drum notes
        this.audioEngine.drumPlayer.stopAll();
    }

    /**
     * Set an audio effect to a value
     * @param {string} effect - the name of the effect
     * @param {number} value - the value to set the effect to
     */
    setEffect (effect, value) {
        switch (effect) {
        case this.audioEngine.EFFECT_NAMES.pitch:
            this.pitchEffect.set(value, this.activeSoundPlayers);
            break;
        case this.audioEngine.EFFECT_NAMES.pan:
            this.panEffect.set(value);
            break;
        }
    }

    /**
     * Clear all audio effects
     */
    clearEffects () {
        this.panEffect.set(0);
        this.pitchEffect.set(0, this.activeSoundPlayers);
        this.effectsNode.gain.value = 1;
    }

    /**
     * Set the volume for sounds played by this AudioPlayer
     * @param {number} value - the volume in range 0-100
     */
    setVolume (value) {
        this.effectsNode.gain.value = value / 100;
    }
}

/**
 * There is a single instance of the AudioEngine. It handles global audio properties and effects,
 * loads all the audio buffers for sounds belonging to sprites, and creates a single instrument player
 * and a drum player, used by all play note and play drum blocks.
 */
class AudioEngine {
    constructor () {
        this.audioContext = new AudioContext();

        this.input = this.audioContext.createGain();
        this.input.connect(this.audioContext.destination);

        // global tempo in bpm (beats per minute)
        this.currentTempo = 60;

        // instrument player for play note blocks
        this.instrumentPlayer = new InstrumentPlayer(this.audioContext);
        this.instrumentPlayer.outputNode = this.input;
        this.numInstruments = this.instrumentPlayer.instrumentNames.length;

        // drum player for play drum blocks
        this.drumPlayer = new DrumPlayer(this.audioContext);
        this.numDrums = this.drumPlayer.drumSounds.length;

        // a map of soundIds to audio buffers, holding sounds for all sprites
        this.audioBuffers = {};

        // microphone, for measuring loudness, with a level meter analyzer
        this.mic = null;
    }

    /**
     * Names of the audio effects.
     * @enum {string}
     */
    get EFFECT_NAMES () {
        return {
            pitch: 'pitch',
            pan: 'pan'
        };
    }

    /**
     * Decode a sound, decompressing it into audio samples.
     * Store a reference to it the sound in the audioBuffers dictionary, indexed by soundId
     * @param  {object} sound - an object containing audio data and metadata for a sound
     * @property {Buffer} data - sound data loaded from scratch-storage.
     * @property {string} format - format type, either empty or adpcm.
     * @returns {?Promise} - a promise which will resolve to the soundId if decoded and stored.
     */
    decodeSound (sound) {
        const soundId = uid();
        let loaderPromise = null;

        // Make a copy of the buffer because decoding detaches the original buffer
        const bufferCopy = sound.data.buffer.slice(0);

        switch (sound.format) {
        case '':
            loaderPromise = this.audioContext.decodeAudioData(bufferCopy);
            break;
        case 'adpcm':
            loaderPromise = (new ADPCMSoundDecoder(this.audioContext)).decode(bufferCopy);
            break;
        default:
            return log.warn('unknown sound format', sound.format);
        }

        const storedContext = this;
        return loaderPromise.then(
            decodedAudio => {
                storedContext.audioBuffers[soundId] = decodedAudio;
                return soundId;
            },
            error => {
                log.warn('audio data could not be decoded', error);
            }
        );
    }

    /**
     * Retrieve the audio buffer as held in memory for a given sound id.
     * @param {!string} soundId - the id of the sound buffer to get
     * @return {AudioBuffer} the buffer corresponding to the given sound id.
     */
    getSoundBuffer (soundId) {
        return this.audioBuffers[soundId];
    }

    /**
     * Update the in-memory audio buffer to a new one by soundId.
     * @param {!string} soundId - the id of the sound buffer to update.
     * @param {AudioBuffer} newBuffer - the new buffer to swap in.
     */
    updateSoundBuffer (soundId, newBuffer) {
        this.audioBuffers[soundId] = newBuffer;
    }

    /**
     * An older version of the AudioEngine had this function to load all sounds
     * This is a stub to provide a warning when it is called
     * @todo remove this
     */
    loadSounds () {
        log.warn('The loadSounds function is no longer available. Please use Scratch Storage.');
    }

    /**
     * Play a note for a duration on an instrument with a volume
     * @param  {number} note - a MIDI note number
     * @param  {number} beats - a duration in beats
     * @param  {number} inst - an instrument number (0-indexed)
     * @param  {number} vol - a volume level (0-100%)
     * @return {Promise} a Promise that resolves after the duration has elapsed
     */
    playNoteForBeatsWithInstAndVol (note, beats, inst, vol) {
        const sec = this.beatsToSec(beats);
        this.instrumentPlayer.playNoteForSecWithInstAndVol(note, sec, inst, vol);
        return this.waitForBeats(beats);
    }

    /**
     * Convert a number of beats to a number of seconds, using the current tempo
     * @param  {number} beats number of beats to convert to secs
     * @return {number} seconds number of seconds `beats` will last
     */
    beatsToSec (beats) {
        return (60 / this.currentTempo) * beats;
    }

    /**
     * Wait for some number of beats
     * @param  {number} beats number of beats to wait for
     * @return {Promise} a Promise that resolves after the duration has elapsed
     */
    waitForBeats (beats) {
        const storedContext = this;
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, storedContext.beatsToSec(beats) * 1000);
        });
    }

    /**
     * Set the global tempo in bpm (beats per minute)
     * @param {number} value - the new tempo to set
     */
    setTempo (value) {
        this.currentTempo = value;
    }

    /**
     * Change the tempo by some number of bpm (beats per minute)
     * @param  {number} value - the number of bpm to change the tempo by
     */
    changeTempo (value) {
        this.setTempo(this.currentTempo + value);
    }

    /**
     * Get the current loudness of sound received by the microphone.
     * Sound is measured in RMS and smoothed.
     * Some code adapted from Tone.js: https://github.com/Tonejs/Tone.js
     * @return {number} loudness scaled 0 to 100
     */
    getLoudness () {
        // The microphone has not been set up, so try to connect to it
        if (!this.mic && !this.connectingToMic) {
            this.connectingToMic = true; // prevent multiple connection attempts
            navigator.mediaDevices.getUserMedia({audio: true}).then(stream => {
                this.mic = this.audioContext.createMediaStreamSource(stream);
                this.analyser = this.audioContext.createAnalyser();
                this.mic.connect(this.analyser);
                this.micDataArray = new Float32Array(this.analyser.fftSize);
            })
            .catch(err => {
                log.warn(err);
            });
        }

        // If the microphone is set up and active, measure the loudness
        if (this.mic && this.mic.mediaStream.active) {
            this.analyser.getFloatTimeDomainData(this.micDataArray);
            let sum = 0;
            // compute the RMS of the sound
            for (let i = 0; i < this.micDataArray.length; i++){
                sum += Math.pow(this.micDataArray[i], 2);
            }
            let rms = Math.sqrt(sum / this.micDataArray.length);
            // smooth the value, if it is descending
            if (this._lastValue) {
                rms = Math.max(rms, this._lastValue * 0.6);
            }
            this._lastValue = rms;

            // Scale the measurement so it's more sensitive to quieter sounds
            rms *= 1.63;
            rms = Math.sqrt(rms);
            // Scale it up to 0-100 and round
            rms = Math.round(rms * 100);
            // Prevent it from going above 100
            rms = Math.min(rms, 100);
            return rms;
        }

        // if there is no microphone input, return -1
        return -1;
    }

    /**
     * Create an AudioPlayer. Each sprite or clone has an AudioPlayer.
     * It includes a reference to the AudioEngine so it can use global
     * functionality such as playing notes.
     * @return {AudioPlayer} new AudioPlayer instance
     */
    createPlayer () {
        return new AudioPlayer(this);
    }
}

module.exports = AudioEngine;
<|endoftext|>


<commit_before>/**
 * Created by yzyzsun on 2016/12/21.
 */

'use strict';

class PyObject {
  constructor(type) {
    this.type = type;
    this.members = new Map();
  }
  get(identifier) {
    if (this.members.has(identifier)) {
      return this.members.get(identifier);
    } else {
      return this.type.members.get(identifier);
    }
  }
  set(identifier) {
    this.members.set(identifier, value);
  }
  delete(identifier) {
    return this.members.delete(identifier);
  }
}

class PyTypeObject extends PyObject {
  constructor(name, bases) {
    super({ name: 'type', bases: [] });
    this.name = name;
    this.bases = bases;
  }
  get(identifier) {
    if (this.members.has(identifier)) {
      return this.members.get(identifier);
    }
    for (const base of bases) {
      if (base.members.has(identifier)) {
        return base.members.get(identifier);
      }
    }
    return undefined;
  }
}

class PyBuiltinObject extends PyObject {
  constructor(type, value) {
    super(type);
    this.value = value;
  }
}

const objectType = new PyTypeObject('object', []);
objectType.members.set('__str__', '<object>');
objectType.members.set('__eq__', (self, other) => self === other);
objectType.members.set('__ne__', (self, other) => self !== other);

const noneType = new PyTypeObject('NoneType', [objectType]);
noneType.members.set('__str__', self => 'None');
const noneObject = new PyBuiltinObject(noneType, null);

const numericType = new PyTypeObject('numeric', [objectType]);
numericType.members.set('__lt__', (self, other) => self < other);
numericType.members.set('__le__', (self, other) => self <= other);
numericType.members.set('__gt__', (self, other) => self > other);
numericType.members.set('__ge__', (self, other) => self >= other);
numericType.members.set('__bool__', self => self !== 0);
numericType.members.set('__pos__', self => +self);
numericType.members.set('__neg__', self => -self);
numericType.members.set('__abs__', self => Math.abs(self));
numericType.members.set('__invert__', self => ~self);
numericType.members.set('__add__', (self, other) => self + other);
numericType.members.set('__sub__', (self, other) => self - other);
numericType.members.set('__mul__', (self, other) => self * other);
numericType.members.set('__truediv__', (self, other) => self / other);
numericType.members.set('__floordiv__', (self, other) => Math.floor(self / other));
numericType.members.set('__mod__', (self, other) => self % other);
numericType.members.set('__pow__', (self, other) => self ** other);
numericType.members.set('__lshift__', (self, other) => self << other);
numericType.members.set('__rshift__', (self, other) => self >> other);
numericType.members.set('__and__', (self, other) => self & other);
numericType.members.set('__xor__', (self, other) => self ^ other);
numericType.members.set('__or__', (self, other) => self | other);

const intType = new PyTypeObject('int', [numericType]);
intType.members.set('__str__', self => self.toString());
class PyIntObject extends PyBuiltinObject {
  constructor(int) {
    super(intType, int);
  }
}

const floatType = new PyTypeObject('float', [numericType]);
floatType.members.set('__str__', self => self % 1 === 0 ? self.toFixed(1) : self.toString());
floatType.members.set('is_integer', self => self % 1 === 0);
class PyFloatObject extends PyBuiltinObject {
  constructor(float) {
    super(floatType, float);
  }
}

const boolType = new PyTypeObject('bool', [numericType]);
boolType.members.set('__str__', self => self ? 'True' : 'False');
const falseObject = new PyBuiltinObject(boolType, 0);
const trueObject = new PyBuiltinObject(boolType, 1);

const strType = new PyTypeObject('str', [objectType]);
strType.members.set('__str__', self => self);
strType.members.set('__len__', self => self.length);
strType.members.set('__getitem__', (self, key) => self[key]);
strType.members.set('__contains__', (self, value) => self.includes(value));
strType.members.set('__add__', (self, other) => self + other);
strType.members.set('__mul__', (self, other) => self.repeat(other));
strType.members.set('endswith', (self, suffix) => self.endsWith(suffix));
strType.members.set('find', (self, sub) => self.indexOf(sub));
strType.members.set('isalpha', self => self.search(/[^A-Za-z]/) === -1);
strType.members.set('isdecimal', self => self.search(/\D/) === -1);
strType.members.set('isidentifier', self => self.search(/^[A-Za-z_]\w*$/) !== -1);
strType.members.set('islower', self => self.search(/[^a-z]/) === -1);
strType.members.set('isspace', self => self.search(/\S/) === -1);
strType.members.set('isupper', self => self.search(/[^A-Z]/) === -1);
strType.members.set('join', (self, iterable) => iterable.join(self));
strType.members.set('lower', self => self.toLowerCase());
strType.members.set('replace', (self, oldValue, newValue) => self.replace(oldValue, newValue));
strType.members.set('rfind', (self, sub) => self.lastIndexOf(sub));
strType.members.set('split', (self, sep = /\s+/) => self.split(sep));
strType.members.set('startswith', (self, prefix) => self.startsWith(prefix));
strType.members.set('strip', (self, prefix) => self.trim());
strType.members.set('upper', self => self.toUpperCase());
class PyStrObject extends PyBuiltinObject {
  constructor(str) {
    super(strType, str);
  }
}

const listType = new PyTypeObject('list', [objectType]);
listType.members.set('__str__', self => `[${self.join(', ')}]`);
listType.members.set('__len__', self => self.length);
listType.members.set('__getitem__', (self, key) => self[key]);
listType.members.set('__setitem__', (self, key, value) => { self[key] = value; });
listType.members.set('__delitem__', (self, key) => { self.splice(key, 1); });
listType.members.set('__contains__', (self, value) => self.includes(value));
listType.members.set('__add__', (self, other) => self.concat(other));
listType.members.set('__mul__', (self, other) => [].concat(...(new Array(other)).fill(self)));
listType.members.set('append', (self, value) => { self.push(value); });
listType.members.set('clear', self => { self.length = 0; });
listType.members.set('copy', self => self.slice());
listType.members.set('insert', (self, key, value) => { self.splice(key, 0, value); });
listType.members.set('pop', (self, key = null) => key === null ? self.pop() : self.splice(key, 1)[0]);
listType.members.set('remove', (self, value) => { self.splice(self.indexOf(value), 1); });
listType.members.set('reverse', self => { self.reverse(); });
listType.members.set('sort', self => { self.sort((x, y) => x - y); });
class PyListObject extends PyBuiltinObject {
  constructor(list) {
    super(listType, list);
  }
}

const dictType = new PyTypeObject('dict', [objectType]);
dictType.members.set('__str__', self => `{${[...self.entries()].map(a => `${a[0]}: ${a[1]}`).join(', ')}}`);
dictType.members.set('__len__', self => self.size);
dictType.members.set('__getitem__', (self, key) => self.get(key));
dictType.members.set('__setitem__', (self, key, value) => { self.set(key, value); });
dictType.members.set('__delitem__', (self, key) => { self.delete(key); });
dictType.members.set('__contains__', (self, value) => self.has(value));
dictType.members.set('clear', self => { self.clear(); });
dictType.members.set('copy', self => new Map(self));
dictType.members.set('get', (self, key, defaultValue = null) => self.has(key) ? self.get(key) : defaultValue);
dictType.members.set('pop', (self, key, defaultValue = null) => {
  const value = this.get(self, key, defaultValue);
  self.delete(self, key);
  return value;
});
dictType.members.set('update', (self, other) => {
  for (const item of other) {
    self.set(item[0], item[1]);
  }
});
class PyDictObject extends PyBuiltinObject {
  constructor(dict) {
    super(dictType, dict);
  }
}

const setType = new PyTypeObject('set', [objectType]);
setType.members.set('__str__', self => `{${[...self.values()].join(', ')}}`);
setType.members.set('__len__', self => self.size);
setType.members.set('__contains__', (self, value) => self.has(value));
setType.members.set('add', (self, value) => { self.add(value); });
setType.members.set('clear', self => { self.clear(); });
setType.members.set('copy', self => new Set(self));
setType.members.set('discard', (self, value) => { self.delete(value); });
class PySetObject extends PyBuiltinObject {
  constructor(dict) {
    super(setType, set);
  }
}

const functionType = new PyTypeObject('function', [objectType]);
class PyFunctionObject extends PyObject {
  constructor(funcname, parameters, statements) {
    super(functionType);
    this.funcname = funcname;
    this.parameters = parameters;
    this.statements = statements;
  }
  has(identifier) {
    return this.locals.has(identifier);
  }
  get(identifier) {
    this.locals.get(identifier);
  }
  set(identifier, value) {
    this.locals.set(identifier, value);
  }
  delete(identifier) {
    this.locals.delete(identifier);
  }
}

module.exports = {
  PyObject, PyTypeObject, PyFunctionObject,
  PyBuiltinObject, PyIntObject, PyFloatObject, PyStrObject, PyListObject, PyDictObject, PySetObject,
  noneObject, falseObject, trueObject,
};
<commit_msg>Fix attribute finding<commit_after>/**
 * Created by yzyzsun on 2016/12/21.
 */

'use strict';

class PyObject {
  constructor(type) {
    this.type = type;
    this.members = new Map();
  }
  get(identifier) {
    if (this.members.has(identifier)) {
      return this.members.get(identifier);
    } else {
      return this.type.get(identifier);
    }
  }
  set(identifier) {
    this.members.set(identifier, value);
  }
  delete(identifier) {
    return this.members.delete(identifier);
  }
}

class PyTypeObject extends PyObject {
  constructor(name, bases) {
    super({ name: 'type', bases: [] });
    this.name = name;
    this.bases = bases;
  }
  get(identifier) {
    if (this.members.has(identifier)) {
      return this.members.get(identifier);
    }
    for (const base of this.bases) {
      const member = base.get(identifier);
      if (member !== undefined) return member;
    }
    return undefined;
  }
}

class PyBuiltinObject extends PyObject {
  constructor(type, value) {
    super(type);
    this.value = value;
  }
}

const objectType = new PyTypeObject('object', []);
objectType.members.set('__str__', '<object>');
objectType.members.set('__eq__', (self, other) => self === other);
objectType.members.set('__ne__', (self, other) => self !== other);

const noneType = new PyTypeObject('NoneType', [objectType]);
noneType.members.set('__str__', self => 'None');
const noneObject = new PyBuiltinObject(noneType, null);

const numericType = new PyTypeObject('numeric', [objectType]);
numericType.members.set('__lt__', (self, other) => self < other);
numericType.members.set('__le__', (self, other) => self <= other);
numericType.members.set('__gt__', (self, other) => self > other);
numericType.members.set('__ge__', (self, other) => self >= other);
numericType.members.set('__bool__', self => self !== 0);
numericType.members.set('__pos__', self => +self);
numericType.members.set('__neg__', self => -self);
numericType.members.set('__abs__', self => Math.abs(self));
numericType.members.set('__invert__', self => ~self);
numericType.members.set('__add__', (self, other) => self + other);
numericType.members.set('__sub__', (self, other) => self - other);
numericType.members.set('__mul__', (self, other) => self * other);
numericType.members.set('__truediv__', (self, other) => self / other);
numericType.members.set('__floordiv__', (self, other) => Math.floor(self / other));
numericType.members.set('__mod__', (self, other) => self % other);
numericType.members.set('__pow__', (self, other) => self ** other);
numericType.members.set('__lshift__', (self, other) => self << other);
numericType.members.set('__rshift__', (self, other) => self >> other);
numericType.members.set('__and__', (self, other) => self & other);
numericType.members.set('__xor__', (self, other) => self ^ other);
numericType.members.set('__or__', (self, other) => self | other);

const intType = new PyTypeObject('int', [numericType]);
intType.members.set('__str__', self => self.toString());
class PyIntObject extends PyBuiltinObject {
  constructor(int) {
    super(intType, int);
  }
}

const floatType = new PyTypeObject('float', [numericType]);
floatType.members.set('__str__', self => self % 1 === 0 ? self.toFixed(1) : self.toString());
floatType.members.set('is_integer', self => self % 1 === 0);
class PyFloatObject extends PyBuiltinObject {
  constructor(float) {
    super(floatType, float);
  }
}

const boolType = new PyTypeObject('bool', [numericType]);
boolType.members.set('__str__', self => self ? 'True' : 'False');
const falseObject = new PyBuiltinObject(boolType, 0);
const trueObject = new PyBuiltinObject(boolType, 1);

const strType = new PyTypeObject('str', [objectType]);
strType.members.set('__str__', self => self);
strType.members.set('__len__', self => self.length);
strType.members.set('__getitem__', (self, key) => self[key]);
strType.members.set('__contains__', (self, value) => self.includes(value));
strType.members.set('__add__', (self, other) => self + other);
strType.members.set('__mul__', (self, other) => self.repeat(other));
strType.members.set('endswith', (self, suffix) => self.endsWith(suffix));
strType.members.set('find', (self, sub) => self.indexOf(sub));
strType.members.set('isalpha', self => self.search(/[^A-Za-z]/) === -1);
strType.members.set('isdecimal', self => self.search(/\D/) === -1);
strType.members.set('isidentifier', self => self.search(/^[A-Za-z_]\w*$/) !== -1);
strType.members.set('islower', self => self.search(/[^a-z]/) === -1);
strType.members.set('isspace', self => self.search(/\S/) === -1);
strType.members.set('isupper', self => self.search(/[^A-Z]/) === -1);
strType.members.set('join', (self, iterable) => iterable.join(self));
strType.members.set('lower', self => self.toLowerCase());
strType.members.set('replace', (self, oldValue, newValue) => self.replace(oldValue, newValue));
strType.members.set('rfind', (self, sub) => self.lastIndexOf(sub));
strType.members.set('split', (self, sep = /\s+/) => self.split(sep));
strType.members.set('startswith', (self, prefix) => self.startsWith(prefix));
strType.members.set('strip', (self, prefix) => self.trim());
strType.members.set('upper', self => self.toUpperCase());
class PyStrObject extends PyBuiltinObject {
  constructor(str) {
    super(strType, str);
  }
}

const listType = new PyTypeObject('list', [objectType]);
listType.members.set('__str__', self => `[${self.join(', ')}]`);
listType.members.set('__len__', self => self.length);
listType.members.set('__getitem__', (self, key) => self[key]);
listType.members.set('__setitem__', (self, key, value) => { self[key] = value; });
listType.members.set('__delitem__', (self, key) => { self.splice(key, 1); });
listType.members.set('__contains__', (self, value) => self.includes(value));
listType.members.set('__add__', (self, other) => self.concat(other));
listType.members.set('__mul__', (self, other) => [].concat(...(new Array(other)).fill(self)));
listType.members.set('append', (self, value) => { self.push(value); });
listType.members.set('clear', self => { self.length = 0; });
listType.members.set('copy', self => self.slice());
listType.members.set('insert', (self, key, value) => { self.splice(key, 0, value); });
listType.members.set('pop', (self, key = null) => key === null ? self.pop() : self.splice(key, 1)[0]);
listType.members.set('remove', (self, value) => { self.splice(self.indexOf(value), 1); });
listType.members.set('reverse', self => { self.reverse(); });
listType.members.set('sort', self => { self.sort((x, y) => x - y); });
class PyListObject extends PyBuiltinObject {
  constructor(list) {
    super(listType, list);
  }
}

const dictType = new PyTypeObject('dict', [objectType]);
dictType.members.set('__str__', self => `{${[...self.entries()].map(a => `${a[0]}: ${a[1]}`).join(', ')}}`);
dictType.members.set('__len__', self => self.size);
dictType.members.set('__getitem__', (self, key) => self.get(key));
dictType.members.set('__setitem__', (self, key, value) => { self.set(key, value); });
dictType.members.set('__delitem__', (self, key) => { self.delete(key); });
dictType.members.set('__contains__', (self, value) => self.has(value));
dictType.members.set('clear', self => { self.clear(); });
dictType.members.set('copy', self => new Map(self));
dictType.members.set('get', (self, key, defaultValue = null) => self.has(key) ? self.get(key) : defaultValue);
dictType.members.set('pop', (self, key, defaultValue = null) => {
  const value = this.get(self, key, defaultValue);
  self.delete(self, key);
  return value;
});
dictType.members.set('update', (self, other) => {
  for (const item of other) {
    self.set(item[0], item[1]);
  }
});
class PyDictObject extends PyBuiltinObject {
  constructor(dict) {
    super(dictType, dict);
  }
}

const setType = new PyTypeObject('set', [objectType]);
setType.members.set('__str__', self => `{${[...self.values()].join(', ')}}`);
setType.members.set('__len__', self => self.size);
setType.members.set('__contains__', (self, value) => self.has(value));
setType.members.set('add', (self, value) => { self.add(value); });
setType.members.set('clear', self => { self.clear(); });
setType.members.set('copy', self => new Set(self));
setType.members.set('discard', (self, value) => { self.delete(value); });
class PySetObject extends PyBuiltinObject {
  constructor(dict) {
    super(setType, set);
  }
}

const functionType = new PyTypeObject('function', [objectType]);
class PyFunctionObject extends PyObject {
  constructor(funcname, parameters, statements) {
    super(functionType);
    this.funcname = funcname;
    this.parameters = parameters;
    this.statements = statements;
  }
  has(identifier) {
    return this.locals.has(identifier);
  }
  get(identifier) {
    this.locals.get(identifier);
  }
  set(identifier, value) {
    this.locals.set(identifier, value);
  }
  delete(identifier) {
    this.locals.delete(identifier);
  }
}

module.exports = {
  PyObject, PyTypeObject, PyFunctionObject,
  PyBuiltinObject, PyIntObject, PyFloatObject, PyStrObject, PyListObject, PyDictObject, PySetObject,
  noneObject, falseObject, trueObject,
};
<|endoftext|>