<commit_before>L.Control.BeeControl = L.Control.extend({

	options: {
		position: "topright"
		, r1: 3
		, r2: 5
		, instructiontext: ""
	},

	initialize: function(options) {
		L.Util.setOptions(this, options);
		this._beeIcon = L.icon({
			iconUrl: 'beemarker.png'
			, iconSize: [37, 65]
			, iconAnchor: [18, 63]
			, popupAnchor: [0, -63]
		});
		this._r1_list = [1, 2, 2.5, 3, 3.5];
		this._r2_list = [4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11];
	},

	onAdd: function(map) {
		this._initLayout();
		this._map.on('updatebeecontrol', this._update_beecontrol, this);
		return this._container;
	},

	_initElementDetailed: function(baseDiv) {
		// add checkbox for bee location
		var input = L.DomUtil.create('input');
		input.type = 'checkbox';
		input.id = 'idBeeControlCenter';
		baseDiv.appendChild(input);

		// add label for bee location
		var name = L.DomUtil.create('label', 'beecontrol-label');
		name.setAttribute('for', input.id);
		name.innerHTML = 'Bienenstandort';
		baseDiv.appendChild(name);
		L.DomEvent.on(input, 'click', this._onInputClickPosition, this);
		L.DomUtil.create('br', '', baseDiv);

		// add checkbox for main area
		var input1 = L.DomUtil.create('input');
		input1.type = 'checkbox';
		input1.id = 'idBeeControlR1';
		baseDiv.appendChild(input1);

		// add label for main area
		var name1 = L.DomUtil.create('label', 'beecontrol-label');
		name1.setAttribute('for', input1.id);
		name1.innerHTML = 'Hauptfluggebiet';
		baseDiv.appendChild(name1);
		L.DomEvent.on(input1, 'click', this._onInputClickRadius1, this);
		L.DomUtil.create('br', '', baseDiv);

		// add radius for main area
		var dummy1 = L.DomUtil.create('input', '', baseDiv);
		dummy1.type = 'checkbox';
		dummy1.style.visibility = 'hidden';
		var nameS1 = L.DomUtil.create('span', 'beecontrol-label');
		nameS1.innerHTML = 'Radius ';
		baseDiv.appendChild(nameS1);
		var select1 = L.DomUtil.create('select');
		select1.id = 'idBeeControlS1';
		for (var i=0; i<this._r1_list.length; i++) {
			var val = this._r1_list[i];
			var opt = document.createElement('option');
			opt.value = val;
			opt.innerHTML = (val + ' km').replace('.', ',');
			if (this.options.r1 == val) {
				opt.selected = true;
			}
			select1.appendChild(opt);
		}
		baseDiv.appendChild(select1);
		L.DomEvent.on(select1, 'change', this._onSelectRadius1, this);
		L.DomUtil.create('br', '', baseDiv);

		// add checkbox for wide area
		var input2 = L.DomUtil.create('input');
		input2.type = 'checkbox';
		input2.id = 'idBeeControlR2';
		baseDiv.appendChild(input2);

		// add label for wide area
		var name2 = L.DomUtil.create('label', 'beecontrol-label');
		name2.setAttribute('for', input2.id);
		name2.innerHTML = 'Erreichbares Gebiet';
		baseDiv.appendChild(name2);
		L.DomEvent.on(input2, 'click', this._onInputClickRadius2, this);
		L.DomUtil.create('br', '', baseDiv);

		// add radius for wide area
		var dummy2 = L.DomUtil.create('input', '', baseDiv);
		dummy2.type = 'checkbox';
		dummy2.style.visibility = 'hidden';
		var nameS2 = L.DomUtil.create('span', 'beecontrol-label');
		nameS2.innerHTML = 'Radius ';
		baseDiv.appendChild(nameS2);
		var select2 = L.DomUtil.create('select');
		select2.id = 'idBeeControlS2';
		for (var i=0; i<this._r2_list.length; i++) {
			var val = this._r2_list[i];
			var opt = document.createElement('option');
			opt.value = val;
			opt.innerHTML = (val + ' km').replace('.', ',');
			if (this.options.r2 == val) {
				opt.selected = true;
			}
			select2.appendChild(opt);
		}
		baseDiv.appendChild(select2);
		L.DomEvent.on(select2, 'change', this._onSelectRadius2, this);
	},

	_initElement: function(baseDiv, beCompact) {
		// beCompact is unused yet
		var beeElement = L.DomUtil.create('div', 'beecontrol-element', baseDiv);
		this._initElementDetailed(beeElement);
	},

	_initLayout: function() {
		this._container = L.DomUtil.create('div', 'beecontrol');
		L.DomEvent.disableClickPropagation(this._container);

		var heading = L.DomUtil.create('div', 'beecontrol-header', this._container);
		heading.innerHTML = 'Flugbereich eines
Bienenvolkes';

		var beeElements = L.DomUtil.create('div', 'beecontrol-elements', this._container);
		this._initElement(beeElements, false);

		var resetLine = L.DomUtil.create('label', 'beecontrol-line', this._container);
		var resetLink = L.DomUtil.create('a', 'beecontrol-link');
		resetLink.innerHTML = 'Einstellungen löschen';
		resetLink.setAttribute('href', 'index.html');
		resetLine.appendChild(resetLink);
	},

	_markPosition: function() {
		var input = document.getElementById('idBeeControlCenter')
		if (input.checked) {
			var center;
			if (this.options.mlat && this.options.mlon) {
				center = new L.LatLng(this.options.mlat, this.options.mlon);
			} else {
				center = this._map.getCenter();
			}
			if (this._marker && this._map.hasLayer(this._marker)) {
				this._map.removeLayer(this._marker);
			}
			this._marker = L.marker(center, {draggable: true, icon: this._beeIcon});
			this._marker.on('dragend', this._onMarkerDragend, this);
			this._marker.addTo(this._map);
			this.options.m = 1;
			this.options.mlat = this._marker.getLatLng().lat;
			this.options.mlon = this._marker.getLatLng().lng;
		} else {
			this._map.removeLayer(this._marker);
			this._marker = null;
			this.options.m = null;
			if (!this._circle1 && !this._circle2) {
				this.options.mlat = null;
				this.options.mlon = null;
			}
		}
	},

	_onInputClickPosition: function() {
		this._markPosition();
		this._map.fire('beecontrolchanged');
	},

	_drawRadius1: function() {
		// clean up first
		if (this._circle1 && this._map.hasLayer(this._circle1)) this._map.removeLayer(this._circle1);
		this._circle1 = null;
		this.options.c1 = null;

		// draw circle if needed
		var input = document.getElementById('idBeeControlR1')
		if (input.checked) {
			this.options.c1 = 1;
			var center;
			if (this.options.mlat && this.options.mlon) {
				center = new L.LatLng(this.options.mlat, this.options.mlon);
			} else {
				center = this._map.getCenter();
				this.options.mlat = center.lat;
				this.options.mlon = center.lng;
			}
			this._circle1 = L.circle(center, this.options.r1*1000, {clickable: true}).addTo(this._map);
		}

		if (!this._marker && !this._circle1 && !this._circle2) {
			this.options.mlat = null;
			this.options.mlon = null;
		}
	},

	_onInputClickRadius1: function() {
		this._drawRadius1();
		this._map.fire('beecontrolchanged');
	},

	_onSelectRadius1: function() {
		var select = document.getElementById('idBeeControlS1')
		this.options.r1 = select.value;
		this._onInputClickRadius1();
	},

	_drawRadius2: function() {
		// clean up first
		if (this._circle2 && this._map.hasLayer(this._circle2)) this._map.removeLayer(this._circle2);
		this._circle2 = null;
		this.options.c2 = null;

		// draw circle if needed
		var input = document.getElementById('idBeeControlR2')
		if (input.checked) {
			this.options.c2 = 1;
			var center;
			if (this.options.mlat && this.options.mlon) {
				center = new L.LatLng(this.options.mlat, this.options.mlon);
			} else {
				center = this._map.getCenter();
				this.options.mlat = center.lat;
				this.options.mlon = center.lng;
			}
			this._circle2 = L.circle(center, this.options.r2*1000, {clickable: true}).addTo(this._map);
		}

		if (!this._marker && !this._circle1 && !this._circle2) {
			this.options.mlat = null;
			this.options.mlon = null;
		}
	},

	_onInputClickRadius2: function() {
		this._drawRadius2();
		this._map.fire('beecontrolchanged');
	},

	_onSelectRadius2: function() {
		var select = document.getElementById('idBeeControlS2')
		this.options.r2 = select.value;
		this._onInputClickRadius2();
	},

	_onMarkerDragend: function(e) {
		var marker = e.target;
		this.options.mlat = marker.getLatLng().lat;
		this.options.mlon = marker.getLatLng().lng;
		this._drawRadius1();
		this._drawRadius2();
		this._map.fire('beecontrolchanged');
	},

	_update_beecontrol: function(e) {
		if (e.params.r1) {
			for (var i=0; i<this._r1_list.length; i++) {
				if (this._r1_list[i] == e.params.r1) {
					this.options.r1 = e.params.r1;
					var elem1 = document.getElementById('idBeeControlS1');
					elem1.value = this.options.r1;
					elem1.selectedIndex = i;

					// This ugly code is needed for Internet Explorer to trigger <select> to change its display
					_s1idx = i; // global with intent
					window.setTimeout(function() {
						document.getElementById('idBeeControlS1').selectedIndex = _s1idx;
					}, 200)
				}
			}
		}
		if (e.params.r2) {
			for (var i=0; i<this._r2_list.length; i++) {
				if (this._r2_list[i] == e.params.r2) {
					this.options.r2 = e.params.r2;
					var elem2 = document.getElementById('idBeeControlS2');
					elem2.value = this.options.r2;
					elem2.selectedIndex = i;

					// This ugly code is needed for Internet Explorer to trigger <select> to change its display
					_s2idx = i; // global with intent
					window.setTimeout(function() {
						document.getElementById('idBeeControlS2').selectedIndex = _s2idx;
					}, 210)
				}
			}
		}
		if (e.params.mlat) {
			this.options.mlat = e.params.mlat;
		}
		if (e.params.mlon) {
			this.options.mlon = parseFloat(e.params.mlon);
		}

		if (e.params.c1 && this.options.mlat && this.options.mlon) {
			this.options.c1 = 1;
			document.getElementById('idBeeControlR1').checked = true;
			this._drawRadius1();
		}
		if (e.params.c2 && this.options.mlat && this.options.mlon) {
			this.options.c2 = 1;
			document.getElementById('idBeeControlR2').checked = true;
			this._drawRadius2();
		}
		if (e.params.m && this.options.mlat && this.options.mlon) {
			this.options.m = 1;
			document.getElementById('idBeeControlCenter').checked = true;
			this._markPosition();
		}
	},

	initMarker: function(askGeolocation) {
		var askGeolocation = askGeolocation && typeof navigator.geolocation != "undefined";
		var center = this._map.getCenter();
		this.options.mlat = center.lat;
		this.options.mlon = center.lng;
		this.options.m = 1;
		document.getElementById('idBeeControlCenter').checked = true;
		this._markPosition();
		var markerText = "Zieh' mich dorthin,
wo deine Bienen stehen.
"
				+ (askGeolocation ? '(Oder lass mich heraus-
'
				+ 'finden, wo du gerade bist)

' : '')
				+ this.options.instructiontext;
		this._marker.bindPopup(markerText).openPopup();
	}

});

L.control.beeControl = function(options) {
	return new L.Control.BeeControl(options);
}

<commit_msg>change ids, use them with counter<commit_after>L.Control.BeeControl = L.Control.extend({

	options: {
		position: "topright"
		, r1: 3
		, r2: 5
		, instructiontext: ""
	},

	initialize: function(options) {
		L.Util.setOptions(this, options);
		this._beeIcon = L.icon({
			iconUrl: 'beemarker.png'
			, iconSize: [37, 65]
			, iconAnchor: [18, 63]
			, popupAnchor: [0, -63]
		});
		this._r1_list = [1, 2, 2.5, 3, 3.5];
		this._r2_list = [4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10, 10.5, 11];
	},

	onAdd: function(map) {
		this._countBees = 1;
		this._initLayout();
		this._map.on('updatebeecontrol', this._update_beecontrol, this);
		return this._container;
	},

	_initElementDetailed: function(baseDiv, counter) {
		// add checkbox for bee location
		var input = L.DomUtil.create('input');
		input.type = 'checkbox';
		input.id = 'idBeeControlCenter_' + counter;
		baseDiv.appendChild(input);

		// add label for bee location
		var name = L.DomUtil.create('label', 'beecontrol-label');
		name.setAttribute('for', input.id);
		name.innerHTML = 'Bienenstandort';
		baseDiv.appendChild(name);
		L.DomEvent.on(input, 'click', this._onInputClickPosition, this);
		L.DomUtil.create('br', '', baseDiv);

		// add checkbox for main area
		var input1 = L.DomUtil.create('input');
		input1.type = 'checkbox';
		input1.id = 'idBeeControlInner_' + counter;
		baseDiv.appendChild(input1);

		// add label for main area
		var name1 = L.DomUtil.create('label', 'beecontrol-label');
		name1.setAttribute('for', input1.id);
		name1.innerHTML = 'Hauptfluggebiet';
		baseDiv.appendChild(name1);
		L.DomEvent.on(input1, 'click', this._onInputClickRadius1, this);
		L.DomUtil.create('br', '', baseDiv);

		// add radius for main area
		var dummy1 = L.DomUtil.create('input', '', baseDiv);
		dummy1.type = 'checkbox';
		dummy1.style.visibility = 'hidden';
		var nameS1 = L.DomUtil.create('span', 'beecontrol-label');
		nameS1.innerHTML = 'Radius ';
		baseDiv.appendChild(nameS1);
		var select1 = L.DomUtil.create('select');
		select1.id = 'idBeeControlRM_' + counter;
		for (var i=0; i<this._r1_list.length; i++) {
			var val = this._r1_list[i];
			var opt = document.createElement('option');
			opt.value = val;
			opt.innerHTML = (val + ' km').replace('.', ',');
			if (this.options.r1 == val) {
				opt.selected = true;
			}
			select1.appendChild(opt);
		}
		baseDiv.appendChild(select1);
		L.DomEvent.on(select1, 'change', this._onSelectRadius, this);
		L.DomUtil.create('br', '', baseDiv);

		// add checkbox for wide area
		var input2 = L.DomUtil.create('input');
		input2.type = 'checkbox';
		input2.id = 'idBeeControlOuter_' + counter;
		baseDiv.appendChild(input2);

		// add label for wide area
		var name2 = L.DomUtil.create('label', 'beecontrol-label');
		name2.setAttribute('for', input2.id);
		name2.innerHTML = 'Erreichbares Gebiet';
		baseDiv.appendChild(name2);
		L.DomEvent.on(input2, 'click', this._onInputClickRadius2, this);
		L.DomUtil.create('br', '', baseDiv);

		// add radius for wide area
		var dummy2 = L.DomUtil.create('input', '', baseDiv);
		dummy2.type = 'checkbox';
		dummy2.style.visibility = 'hidden';
		var nameS2 = L.DomUtil.create('span', 'beecontrol-label');
		nameS2.innerHTML = 'Radius ';
		baseDiv.appendChild(nameS2);
		var select2 = L.DomUtil.create('select');
		select2.id = 'idBeeControlRW_' + counter;
		for (var i=0; i<this._r2_list.length; i++) {
			var val = this._r2_list[i];
			var opt = document.createElement('option');
			opt.value = val;
			opt.innerHTML = (val + ' km').replace('.', ',');
			if (this.options.r2 == val) {
				opt.selected = true;
			}
			select2.appendChild(opt);
		}
		baseDiv.appendChild(select2);
		L.DomEvent.on(select2, 'change', this._onSelectRadius2, this);
	},

	_initElement: function(baseDiv, counter, beCompact) {
		// beCompact is unused yet
		var beeElement = L.DomUtil.create('div', 'beecontrol-element', baseDiv);
		this._initElementDetailed(beeElement, counter);
	},

	_initLayout: function() {
		this._container = L.DomUtil.create('div', 'beecontrol');
		L.DomEvent.disableClickPropagation(this._container);

		var heading = L.DomUtil.create('div', 'beecontrol-header', this._container);
		heading.innerHTML = 'Flugbereich eines
Bienenvolkes';

		var beeElements = L.DomUtil.create('div', 'beecontrol-elements', this._container);
		this._initElement(beeElements, this._countBees++, false);

		var resetLine = L.DomUtil.create('label', 'beecontrol-line', this._container);
		var resetLink = L.DomUtil.create('a', 'beecontrol-link');
		resetLink.innerHTML = 'Einstellungen löschen';
		resetLink.setAttribute('href', 'index.html');
		resetLine.appendChild(resetLink);
	},

	_markPosition: function() {
		var input = document.getElementById('idBeeControlCenter_1'); // TODO: get current center
		if (input.checked) {
			var center;
			if (this.options.mlat && this.options.mlon) {
				center = new L.LatLng(this.options.mlat, this.options.mlon);
			} else {
				center = this._map.getCenter();
			}
			if (this._marker && this._map.hasLayer(this._marker)) {
				this._map.removeLayer(this._marker);
			}
			this._marker = L.marker(center, {draggable: true, icon: this._beeIcon});
			this._marker.on('dragend', this._onMarkerDragend, this);
			this._marker.addTo(this._map);
			this.options.m = 1;
			this.options.mlat = this._marker.getLatLng().lat;
			this.options.mlon = this._marker.getLatLng().lng;
		} else {
			this._map.removeLayer(this._marker);
			this._marker = null;
			this.options.m = null;
			if (!this._circle1 && !this._circle2) {
				this.options.mlat = null;
				this.options.mlon = null;
			}
		}
	},

	_onInputClickPosition: function(evnt) {
		this._markPosition();
		this._map.fire('beecontrolchanged');
	},

	_drawRadius1: function() {
		// clean up first
		if (this._circle1 && this._map.hasLayer(this._circle1)) this._map.removeLayer(this._circle1);
		this._circle1 = null;
		this.options.c1 = null;

		// draw circle if needed
		var input = document.getElementById('idBeeControlInner_1')
		if (input.checked) {
			this.options.c1 = 1;
			var center;
			if (this.options.mlat && this.options.mlon) {
				center = new L.LatLng(this.options.mlat, this.options.mlon);
			} else {
				center = this._map.getCenter();
				this.options.mlat = center.lat;
				this.options.mlon = center.lng;
			}
			this._circle1 = L.circle(center, this.options.r1*1000, {clickable: true}).addTo(this._map);
		}

		if (!this._marker && !this._circle1 && !this._circle2) {
			this.options.mlat = null;
			this.options.mlon = null;
		}
	},

	_onInputClickRadius1: function() {
		this._drawRadius1();
		this._map.fire('beecontrolchanged');
	},

	_onSelectRadius: function(evnt) {
		// TODO: generalize function for any radius selected
		var select = evnt.target;
		this.options.r1 = select.value;
		this._onInputClickRadius1();
	},

	_drawRadius2: function() {
		// clean up first
		if (this._circle2 && this._map.hasLayer(this._circle2)) this._map.removeLayer(this._circle2);
		this._circle2 = null;
		this.options.c2 = null;

		// draw circle if needed
		var input = document.getElementById('idBeeControlOuter_1')
		if (input.checked) {
			this.options.c2 = 1;
			var center;
			if (this.options.mlat && this.options.mlon) {
				center = new L.LatLng(this.options.mlat, this.options.mlon);
			} else {
				center = this._map.getCenter();
				this.options.mlat = center.lat;
				this.options.mlon = center.lng;
			}
			this._circle2 = L.circle(center, this.options.r2*1000, {clickable: true}).addTo(this._map);
		}

		if (!this._marker && !this._circle1 && !this._circle2) {
			this.options.mlat = null;
			this.options.mlon = null;
		}
	},

	_onInputClickRadius2: function() {
		this._drawRadius2();
		this._map.fire('beecontrolchanged');
	},

	_onSelectRadius2: function(evnt) {
		var select = evnt.target;
		this.options.r2 = select.value;
		this._onInputClickRadius2();
	},

	_onMarkerDragend: function(e) {
		var marker = e.target;
		this.options.mlat = marker.getLatLng().lat;
		this.options.mlon = marker.getLatLng().lng;
		this._drawRadius1();
		this._drawRadius2();
		this._map.fire('beecontrolchanged');
	},

	_update_beecontrol: function(e) {
		if (e.params.r1) {
			for (var i=0; i<this._r1_list.length; i++) {
				if (this._r1_list[i] == e.params.r1) {
					this.options.r1 = e.params.r1;
					var elem1 = document.getElementById('idBeeControlRM_1');
					elem1.value = this.options.r1;
					elem1.selectedIndex = i;

					// This ugly code is needed for Internet Explorer to trigger <select> to change its display
					_s1idx = i; // global with intent
					window.setTimeout(function() {
						document.getElementById('idBeeControlRM_1').selectedIndex = _s1idx;
					}, 200)
				}
			}
		}
		if (e.params.r2) {
			for (var i=0; i<this._r2_list.length; i++) {
				if (this._r2_list[i] == e.params.r2) {
					this.options.r2 = e.params.r2;
					var elem2 = document.getElementById('idBeeControlRW_1');
					elem2.value = this.options.r2;
					elem2.selectedIndex = i;

					// This ugly code is needed for Internet Explorer to trigger <select> to change its display
					_s2idx = i; // global with intent
					window.setTimeout(function() {
						document.getElementById('idBeeControlRW_1').selectedIndex = _s2idx;
					}, 210)
				}
			}
		}
		if (e.params.mlat) {
			this.options.mlat = e.params.mlat;
		}
		if (e.params.mlon) {
			this.options.mlon = parseFloat(e.params.mlon);
		}

		if (e.params.c1 && this.options.mlat && this.options.mlon) {
			this.options.c1 = 1;
			document.getElementById('idBeeControlInner_1').checked = true;
			this._drawRadius1();
		}
		if (e.params.c2 && this.options.mlat && this.options.mlon) {
			this.options.c2 = 1;
			document.getElementById('idBeeControlOuter_1').checked = true;
			this._drawRadius2();
		}
		if (e.params.m && this.options.mlat && this.options.mlon) {
			this.options.m = 1;
			document.getElementById('idBeeControlCenter_1').checked = true;
			this._markPosition();
		}
	},

	initMarker: function(askGeolocation) {
		var askGeolocation = askGeolocation && typeof navigator.geolocation != "undefined";
		var center = this._map.getCenter();
		this.options.mlat = center.lat;
		this.options.mlon = center.lng;
		this.options.m = 1;
		document.getElementById('idBeeControlCenter_1').checked = true;
		this._markPosition();
		var markerText = "Zieh' mich dorthin,
wo deine Bienen stehen.
"
				+ (askGeolocation ? '(Oder lass mich heraus-
'
				+ 'finden, wo du gerade bist)

' : '')
				+ this.options.instructiontext;
		this._marker.bindPopup(markerText).openPopup();
	}

});

L.control.beeControl = function(options) {
	return new L.Control.BeeControl(options);
}

<|endoftext|>


<commit_before>var util = require('util');
var crypto = require('crypto');

util.alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_';

/**
 * Slugify a string
 *
 * @param {String} string
 */

util.slugify = function slugify(string) {
  return string
    .toLowerCase()
    .replace(/[\s\-]+/g, '-')
    .replace(/[^a-z0-9_\-]/gi, '');
};

/**
 * Alias for `crypto.createHash(algo).update(str).digest('hex');
 *
 * @param {String} string
 * @param {String} algorithm
 * @return {String} hash
 */
util.hash = function hash(string, algorithm) {
  var sum = crypto.createHash(algorithm || 'sha256');
  return sum.update(string).digest('hex');
};

/**
 * Generate a descriptive sha256 hash with optional salt.
 *
 * @param {String} string
 * @param {String} salt
 * @return {String} sha256 hash
 */

util.sha256 = function sha256(string, salt) {
  var sum = crypto.createHash('sha256');
  sum.update(string);
  if (salt)
    sum.update(salt);
  return util.format('sha256$%s', sum.digest('hex'));
};

/**
 * Get a random integer between 0 and `max`.
 * Not cryptographically strong.
 *
 * @param {Integer} max
 * @return {Integer} random integer
 */

util.randomInt = function randomInt(max) {
  return Math.random() * max | 0;
};

/**
 * Get a random character.
 * Not cryptographically strong.
 *
 * @param {String|Array} charset
 * @return {String} a random character
 */

util.randomChar = function randomChar(charset) {
  var chars = charset || util.alphabet;
  var index = util.randomInt(chars.length);
  return chars[index];
};

/**
 * Get a random string.
 * Not crypographically strong.
 *
 * @param {Integer} length
 * @return {String} a random string
 */

util.randomString = function randomstring(length) {
  var result = [];
  while (length--)
    result.push(util.randomChar());
  return result.join('');
};

/**
 * Get a cryptographically strong random string.
 *
 * @param {Integer} length
 * @return {String} a random string
 */

util.strongRandomString = function strongRandomString(length, alphabet) {
  alphabet = alphabet || util.alphabet;
  var bytes = crypto.randomBytes(length);
  var index = bytes.length;
  var result = [];
  var letter;
  while (index--) {
    letter = alphabet[bytes[index] % alphabet.length];
    result.push(letter);
  }
  return result.join('');
};

/**
 * Array to map
 *
 * @param {Array} array
 * @param {String} key The key to use in the map
 */
util.toMap = function toMap(array, key) {
  return array.reduce(function (o, thing) {
    o[thing[key]] = thing;
    return o;
  }, {});
};

module.exports = util;

<commit_msg>Remove dashes and underscores from alphabet.<commit_after>var util = require('util');
var crypto = require('crypto');

util.alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';

/**
 * Slugify a string
 *
 * @param {String} string
 */

util.slugify = function slugify(string) {
  return string
    .toLowerCase()
    .replace(/[\s\-]+/g, '-')
    .replace(/[^a-z0-9_\-]/gi, '');
};

/**
 * Alias for `crypto.createHash(algo).update(str).digest('hex');
 *
 * @param {String} string
 * @param {String} algorithm
 * @return {String} hash
 */
util.hash = function hash(string, algorithm) {
  var sum = crypto.createHash(algorithm || 'sha256');
  return sum.update(string).digest('hex');
};

/**
 * Generate a descriptive sha256 hash with optional salt.
 *
 * @param {String} string
 * @param {String} salt
 * @return {String} sha256 hash
 */

util.sha256 = function sha256(string, salt) {
  var sum = crypto.createHash('sha256');
  sum.update(string);
  if (salt)
    sum.update(salt);
  return util.format('sha256$%s', sum.digest('hex'));
};

/**
 * Get a random integer between 0 and `max`.
 * Not cryptographically strong.
 *
 * @param {Integer} max
 * @return {Integer} random integer
 */

util.randomInt = function randomInt(max) {
  return Math.random() * max | 0;
};

/**
 * Get a random character.
 * Not cryptographically strong.
 *
 * @param {String|Array} charset
 * @return {String} a random character
 */

util.randomChar = function randomChar(charset) {
  var chars = charset || util.alphabet;
  var index = util.randomInt(chars.length);
  return chars[index];
};

/**
 * Get a random string.
 * Not crypographically strong.
 *
 * @param {Integer} length
 * @return {String} a random string
 */

util.randomString = function randomstring(length) {
  var result = [];
  while (length--)
    result.push(util.randomChar());
  return result.join('');
};

/**
 * Get a cryptographically strong random string.
 *
 * @param {Integer} length
 * @return {String} a random string
 */

util.strongRandomString = function strongRandomString(length, alphabet) {
  alphabet = alphabet || util.alphabet;
  var bytes = crypto.randomBytes(length);
  var index = bytes.length;
  var result = [];
  var letter;
  while (index--) {
    letter = alphabet[bytes[index] % alphabet.length];
    result.push(letter);
  }
  return result.join('');
};

/**
 * Array to map
 *
 * @param {Array} array
 * @param {String} key The key to use in the map
 */
util.toMap = function toMap(array, key) {
  return array.reduce(function (o, thing) {
    o[thing[key]] = thing;
    return o;
  }, {});
};

module.exports = util;

<|endoftext|>


<commit_before>/*

CapServer
  See: http://goo.gl/hBgTu

Globals used:
  JSON.stringify
  JSON.parse
  jQuery.ajax

*/

if (!('freeze' in Object)) {
  Object.freeze = function(x) { return x; };
};

var CapServer = (function() {

  // == UTILITIES ==

  var newUUIDv4 = function() {
    var r = function() { return Math.floor(Math.random() * 0x10000); }
    var s = function(x) { return ("000" + x.toString(16)).slice(-4); }
    var u = function() { return s(r()); }
    var v = function() { return s(r() & 0x0fff | 0x4000); }
    var w = function() { return s(r() & 0x3fff | 0x8000); }
    return u()+u()+'-'+u()+'-'+v()+'-'+w()+'-'+u()+u()+u();
  }

  var newCapID = newUUIDv4;
  var encodeSerialization = function(instID, capID) {
    return "urn:x-cap:" + instID + ":" + capID;
  }
  var decodeSerialization = function(ser) {
    var m = ser.match(/^urn:x-cap:([-0-9a-f]{36}):([-0-9a-f]{36})$/)
    if (m) {
      m.shift();
    }
    return m;
  }
  var decodeCapID = function(ser) {
    var m = decodeSerialization(ser);
    return m[1];
  }

  var nullCapID = "urn:x-cap:00000000-0000-0000-0000-000000000000";

  var callAsAJAX = function(f, data, success, failure) {
    setTimeout(function() {
      try {
          var response;
          response = f(data);
          if(success) success(response);
      }
      catch(e) {
          if(failure) failure({status: 500, message: "exception thrown"});
      }
    }, 0);
  }

  var errorAsAJAX = function(data, success, failure) {
    setTimeout(function() {
      if (failure) failure({status: 404});
    }, 0);
  }

  var makeAsyncAJAX = function(url, data, success, failure) {
    jQuery.ajax({ data: data,
                  type: 'POST',
                  url: url,
                  success: function(data, status, xhr) { success(data); },
                  error: function(xhr, status, message) {
                           failure({status: Number(xhr.status) || 501,
                                    message: message});
                }});
  }

  var makeSyncAJAX = function(url, method, data) {
    var resp;
    jQuery.ajax({
      url: url,
      type: method,
      data: data,
      async: false,
      success: function(v) { resp = v; }
    });
    return resp;
  }

  // == THE FOUR IMPLEMENTATION TYPES ==

  var deadImpl = Object.freeze({
    invoke:     function(d, s, f) { errorAsAJAX(d, s, f); },
    invokeSync: function(v) { return undefined; },
  });

  var ImplFunction = function(fn) { this.fn = fn; }
  ImplFunction.prototype.invoke = function(d, s, f) { callAsAJAX(this.fn, d, s, f); };
  ImplFunction.prototype.invokeSync = function(v) { return this.fn(v); };

  var ImplURL = function(url) { this.url = url; }
  ImplURL.prototype.invoke = function(d, s, f) { makeAsyncAJAX(this.url, d, s, f); };
  ImplURL.prototype.invokeSync = function(v) { return makeSyncAJAX(this.url, 'POST', v); };

  var ImplWrap = function(innerCap) { this.inner = innerCap; }
  ImplWrap.prototype.invoke = function(d, s, f) { this.inner.invoke(d, s, f); }
  ImplWrap.prototype.invokeSync = function(v) { return this.inner.invokeSync(v); }

  var buildImplementation = function(item) {
    var t = typeof(item);
    if (t == "function")  return new ImplFunction(item);
    if (t == "string")    return new ImplURL(item);
    if (item === null)    return deadImpl; // careful: typeof(null) == "object"
    if (t == "object")    return new ImplWrap(item)
    else                  return deadImpl;
  };

  var Capability = function(capID, ser, iface) {
    this.capID = capID;
    this.ser = ser;
    this.iface = iface;
  };
  Capability.prototype.invoke = function(data, success, failure) {
    // TODO(mzero): should check if iface is dead, and if so, re-resolve it
    this.iface.invoke(this.ser, data, success, failure);
  };
  Capability.prototype.invokeSync = function(data) {
    // TODO(mzero): should check if iface is dead, and if so, re-resolve it
    return this.iface.invokeSync(this.ser, data);
  };
  Capability.prototype.serialize = function() {
    return this.ser;
  }
  Capability.prototype.getCapID = function() {
    // TODO(mzero): this is here for unit testing... not sure it should be
    return this.capID;
  }

  // FIXME(mzero): this is bork'd (definitely)
  var deadCap = Object.freeze(new Capability(nullCapID, nullCapID, deadImpl));

  var CapServer = function(snapshot) {
    this.reviveMap = {};  // map capID -> key or cap or url
    this.implMap = {};    // map capID -> impls
    this.capMap = {};     // map capID -> caps
    this.reviver = null;
    this.instanceID = newUUIDv4();
    this.resolver = function(id) { return null; };
    if (snapshot) {
      snapshot = JSON.parse(snapshot);
      this.reviveMap = snapshot.map;
      this.instanceID = snapshot.id;
    }

    this.publicInterface = (function(me) {
      return Object.freeze({
        invoke: function(ser, data, success, failure) {
          me._getImpl(ser).invoke(data, success, failure);
        },
        invokeSync: function(ser, data) {
          return me._getImpl(ser).invokeSync(data);
        }
      });
    })(this);

    this.privateInterface = (function(me) {
      return Object.freeze({
        invoke: function(ser, data, success, failure) {
          if (/^https?:/.test(ser)) {
            return makeAsyncAjax(ser, data, success, failure);
          }
          var m = decodeSerialization(ser);
          if (m) {
            var instID = m[0];
            if(instID == me.instanceID) {
              me._getImpl(ser).invoke(data, success, failure);
            } else {
              var publicInterface = me.resolver(instID);
              if(publicInterface) {
                publicInterface.invoke(ser, data, success, failure);
              }
            }
          }
          return deadImpl.invoke(data, success, failure);
        },
        invokeSync: function(ser, data) {
          if (/^https?:/.test(ser)) {
            return makeSyncAjax(ser, data);
          }
          var m = decodeSerialization(ser);
          if (m) {
            var instID = m[0];
            if(instID == me.instanceID) {
              return me._getImpl(ser).invokeSync(data);
            } else {
              var publicInterface = me.resolver(instID);
              if(publicInterface) {
                return publicInterface.invokeSync(ser, data);
              }
            }
          }
          return deadImpl.invokeSync(data);
        }
      });
    })(this);
  };

  CapServer.prototype._mint = function(capID) {
    if (capID in this.capMap) {
      return this.capMap[capID];
    }
    var ser = encodeSerialization(this.instanceID, capID);
    var cap = Object.freeze(new Capability(capID, ser, this.publicInterface));
    this.capMap[capID] = cap;
    return cap;
  };

  CapServer.prototype._getImpl = function(ser) {
    var capID = decodeCapID(ser);
    if (! (capID in this.implMap)) {
      var info = this.reviveMap[capID];
      if (info) {
        if (info.restoreKey) {
          if (this.reviver) {
            var item = this.reviver(info.restoreKey);
            this.implMap[capID] = buildImplementation(item);
          }
        }
        else if (info.restoreCap) {
          var innerCap = this.restore(info.restoreCap);
          this.implMap[capID] = new ImplWrap(innerCap);
        }
      }
    }
    return this.implMap[capID] || deadImpl;
  };

  CapServer.prototype.grant = function(item, key) {
    var capID = newCapID();

    this.implMap[capID] = buildImplementation(item);
    if (key) { this.reviveMap[capID] = { restoreKey: key } }
    // TODO(mzero): should save URL and cap items in reviveMap

    return this._mint(capID);
  };

  // TODO(jpolitz): get rid of wrap?  get rid of resolvable?
  CapServer.prototype.wrap = function(innerCap, resolvable) {
    var capID = newCapID();

    this.implMap[capID] = new ImplWrap(innerCap);
    this.reviveMap[capID] = { restoreCap: innerCap.serialize() };

    return this._mint(capID);
  };

  CapServer.prototype.revoke = function(ser) {
    var capID = decodeCapID(ser);
    delete this.reviveMap[capID];
    delete this.implMap[capID];
    delete this.capMap[capID];
  };

  CapServer.prototype.revokeAll = function() {
    this.reviveMap = {};
    this.implMap = {};
    this.capMap = {}
  };

  CapServer.prototype.restore = function(ser) {
    return Object.freeze(new Capability(decodeCapID(ser), ser, this.privateInterface));
  };

  CapServer.prototype.setReviver = function(r) { this.reviver = r; };

  CapServer.prototype.snapshot = function() {
    snapshot = {
      id: this.instanceID,
      map: this.reviveMap,
    };
    return JSON.stringify(snapshot);
  };

  CapServer.prototype.setResolver = function(resolver) {
    this.resolver = resolver;
  };

  CapServer.prototype.dataPreProcess = function(v) {
    return JSON.stringify({ value: v }, function(k,v) {
      if (typeof(v) == 'function') {
        throw new TypeError('Passing a function');
      }
      try {
        if (Object.getPrototypeOf(v) === Capability.prototype) {
          return { '@': v.serialize() };
        }
      } catch(e) { }
      return v;
    });
  };

  CapServer.prototype.dataPostProcess = function(w) {
    var me = this;
    return JSON.parse(w, function(k,v){
      try {
        var k = Object.keys(v);
        if (k.length == 1 && k[0] == '@') {
          return me.restore(v['@']);
        }
      }
      catch(e) { }
      return v;
    }).value;
  };

  return CapServer;
})();
<commit_msg>no more capMap on capservers or capID on capabilities<commit_after>/*

CapServer
  See: http://goo.gl/hBgTu

Globals used:
  JSON.stringify
  JSON.parse
  jQuery.ajax

*/

if (!('freeze' in Object)) {
  Object.freeze = function(x) { return x; };
};

var CapServer = (function() {

  // == UTILITIES ==

  var newUUIDv4 = function() {
    var r = function() { return Math.floor(Math.random() * 0x10000); }
    var s = function(x) { return ("000" + x.toString(16)).slice(-4); }
    var u = function() { return s(r()); }
    var v = function() { return s(r() & 0x0fff | 0x4000); }
    var w = function() { return s(r() & 0x3fff | 0x8000); }
    return u()+u()+'-'+u()+'-'+v()+'-'+w()+'-'+u()+u()+u();
  }

  var newCapID = newUUIDv4;
  var encodeSerialization = function(instID, capID) {
    return "urn:x-cap:" + instID + ":" + capID;
  }
  var decodeSerialization = function(ser) {
    var m = ser.match(/^urn:x-cap:([-0-9a-f]{36}):([-0-9a-f]{36})$/)
    if (m) {
      m.shift();
    }
    return m;
  }
  var decodeCapID = function(ser) {
    var m = decodeSerialization(ser);
    return m[1];
  }

  var nullCapID = "urn:x-cap:00000000-0000-0000-0000-000000000000";

  var callAsAJAX = function(f, data, success, failure) {
    setTimeout(function() {
      try {
          var response;
          response = f(data);
          if(success) success(response);
      }
      catch(e) {
          if(failure) failure({status: 500, message: "exception thrown"});
      }
    }, 0);
  }

  var errorAsAJAX = function(data, success, failure) {
    setTimeout(function() {
      if (failure) failure({status: 404});
    }, 0);
  }

  var makeAsyncAJAX = function(url, data, success, failure) {
    jQuery.ajax({ data: data,
                  type: 'POST',
                  url: url,
                  success: function(data, status, xhr) { success(data); },
                  error: function(xhr, status, message) {
                           failure({status: Number(xhr.status) || 501,
                                    message: message});
                }});
  }

  var makeSyncAJAX = function(url, method, data) {
    var resp;
    jQuery.ajax({
      url: url,
      type: method,
      data: data,
      async: false,
      success: function(v) { resp = v; }
    });
    return resp;
  }

  // == THE FOUR IMPLEMENTATION TYPES ==

  var deadImpl = Object.freeze({
    invoke:     function(d, s, f) { errorAsAJAX(d, s, f); },
    invokeSync: function(v) { return undefined; },
  });

  var ImplFunction = function(fn) { this.fn = fn; }
  ImplFunction.prototype.invoke = function(d, s, f) { callAsAJAX(this.fn, d, s, f); };
  ImplFunction.prototype.invokeSync = function(v) { return this.fn(v); };

  var ImplURL = function(url) { this.url = url; }
  ImplURL.prototype.invoke = function(d, s, f) { makeAsyncAJAX(this.url, d, s, f); };
  ImplURL.prototype.invokeSync = function(v) { return makeSyncAJAX(this.url, 'POST', v); };

  var ImplWrap = function(innerCap) { this.inner = innerCap; }
  ImplWrap.prototype.invoke = function(d, s, f) { this.inner.invoke(d, s, f); }
  ImplWrap.prototype.invokeSync = function(v) { return this.inner.invokeSync(v); }

  var buildImplementation = function(item) {
    var t = typeof(item);
    if (t == "function")  return new ImplFunction(item);
    if (t == "string")    return new ImplURL(item);
    if (item === null)    return deadImpl; // careful: typeof(null) == "object"
    if (t == "object")    return new ImplWrap(item)
    else                  return deadImpl;
  };

  var Capability = function(ser, iface) {
    this.ser = ser;
    this.iface = iface;
  };
  Capability.prototype.invoke = function(data, success, failure) {
    // TODO(mzero): should check if iface is dead, and if so, re-resolve it
    this.iface.invoke(this.ser, data, success, failure);
  };
  Capability.prototype.invokeSync = function(data) {
    // TODO(mzero): should check if iface is dead, and if so, re-resolve it
    return this.iface.invokeSync(this.ser, data);
  };
  Capability.prototype.serialize = function() {
    return this.ser;
  }

  // FIXME(mzero): this is bork'd (definitely)
  var deadCap = Object.freeze(new Capability(nullCapID, deadImpl));

  var CapServer = function(snapshot) {
    this.reviveMap = {};  // map capID -> key or cap or url
    this.implMap = {};    // map capID -> impls
    this.reviver = null;
    this.instanceID = newUUIDv4();
    this.resolver = function(id) { return null; };
    if (snapshot) {
      snapshot = JSON.parse(snapshot);
      this.reviveMap = snapshot.map;
      this.instanceID = snapshot.id;
    }

    this.publicInterface = (function(me) {
      return Object.freeze({
        invoke: function(ser, data, success, failure) {
          me._getImpl(ser).invoke(data, success, failure);
        },
        invokeSync: function(ser, data) {
          return me._getImpl(ser).invokeSync(data);
        }
      });
    })(this);

    this.privateInterface = (function(me) {
      return Object.freeze({
        invoke: function(ser, data, success, failure) {
          if (/^https?:/.test(ser)) {
            return makeAsyncAjax(ser, data, success, failure);
          }
          var m = decodeSerialization(ser);
          if (m) {
            var instID = m[0];
            if(instID == me.instanceID) {
              me._getImpl(ser).invoke(data, success, failure);
            } else {
              var publicInterface = me.resolver(instID);
              if(publicInterface) {
                publicInterface.invoke(ser, data, success, failure);
              }
            }
          }
          return deadImpl.invoke(data, success, failure);
        },
        invokeSync: function(ser, data) {
          if (/^https?:/.test(ser)) {
            return makeSyncAjax(ser, data);
          }
          var m = decodeSerialization(ser);
          if (m) {
            var instID = m[0];
            if(instID == me.instanceID) {
              return me._getImpl(ser).invokeSync(data);
            } else {
              var publicInterface = me.resolver(instID);
              if(publicInterface) {
                return publicInterface.invokeSync(ser, data);
              }
            }
          }
          return deadImpl.invokeSync(data);
        }
      });
    })(this);
  };

  CapServer.prototype._mint = function(capID) {
    var ser = encodeSerialization(this.instanceID, capID);
    var cap = Object.freeze(new Capability(ser, this.publicInterface));
    return cap;
  };

  CapServer.prototype._getImpl = function(ser) {
    var capID = decodeCapID(ser);
    if (! (capID in this.implMap)) {
      var info = this.reviveMap[capID];
      if (info) {
        if (info.restoreKey) {
          if (this.reviver) {
            var item = this.reviver(info.restoreKey);
            this.implMap[capID] = buildImplementation(item);
          }
        }
        else if (info.restoreCap) {
          var innerCap = this.restore(info.restoreCap);
          this.implMap[capID] = new ImplWrap(innerCap);
        }
      }
    }
    return this.implMap[capID] || deadImpl;
  };

  CapServer.prototype.grant = function(item, key) {
    var capID = newCapID();

    this.implMap[capID] = buildImplementation(item);
    if (key) { this.reviveMap[capID] = { restoreKey: key } }
    // TODO(mzero): should save URL and cap items in reviveMap

    return this._mint(capID);
  };

  // TODO(jpolitz): get rid of wrap?  get rid of resolvable?
  CapServer.prototype.wrap = function(innerCap, resolvable) {
    var capID = newCapID();

    this.implMap[capID] = new ImplWrap(innerCap);
    this.reviveMap[capID] = { restoreCap: innerCap.serialize() };

    return this._mint(capID);
  };

  CapServer.prototype.revoke = function(ser) {
    var capID = decodeCapID(ser);
    delete this.reviveMap[capID];
    delete this.implMap[capID];
  };

  CapServer.prototype.revokeAll = function() {
    this.reviveMap = {};
    this.implMap = {};
  };

  CapServer.prototype.restore = function(ser) {
    return Object.freeze(new Capability(ser, this.privateInterface));
  };

  CapServer.prototype.setReviver = function(r) { this.reviver = r; };

  CapServer.prototype.snapshot = function() {
    snapshot = {
      id: this.instanceID,
      map: this.reviveMap,
    };
    return JSON.stringify(snapshot);
  };

  CapServer.prototype.setResolver = function(resolver) {
    this.resolver = resolver;
  };

  CapServer.prototype.dataPreProcess = function(v) {
    return JSON.stringify({ value: v }, function(k,v) {
      if (typeof(v) == 'function') {
        throw new TypeError('Passing a function');
      }
      try {
        if (Object.getPrototypeOf(v) === Capability.prototype) {
          return { '@': v.serialize() };
        }
      } catch(e) { }
      return v;
    });
  };

  CapServer.prototype.dataPostProcess = function(w) {
    var me = this;
    return JSON.parse(w, function(k,v){
      try {
        var k = Object.keys(v);
        if (k.length == 1 && k[0] == '@') {
          return me.restore(v['@']);
        }
      }
      catch(e) { }
      return v;
    }).value;
  };

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


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Tool = this.Tool = ToolHandler.extend(new function() {
	function viewToArtwork(event, document) {
		// TODO: always the active view?
		return document.activeView.viewToArtwork(DomEvent.getOffset(event));
	};

	return {
		beans: true,

		initialize: function(handlers, doc) {
			this.base(handlers);
			// Create events once, so they can be removed easily too.
			var that = this, curPoint;
			var dragging = false;
			this.events = {
				mousedown: function(event) {
					curPoint = viewToArtwork(event, that._document);
					that.onHandleEvent('mousedown', curPoint, event);
					if (that.onMouseDown)
						that._document.redraw();
					if (that.eventInterval != null) {
						this.timer = setInterval(that.events.mousemove,
								that.eventInterval);
					}
					dragging = true;
				},

				mousemove: function(event) {
					// If the event was triggered by a touch screen device,
					// prevent the default behaviour, as it will otherwise
					// scroll the page:
					if (event && event.targetTouches)
						event.preventDefault();
					var point = event && viewToArtwork(event, that._document);
					// If there is only an onMouseMove handler, call it when
					// the user is dragging
					var onlyMove = !!(!that.onMouseDrag && that.onMouseMove);
					if (dragging && !onlyMove) {
						curPoint = point || curPoint;
						if (curPoint)
							that.onHandleEvent('mousedrag', curPoint, event);
					} else if (!dragging || onlyMove) {
						that.onHandleEvent('mousemove', point, event);
					}
					if (that.onMouseMove || that.onMouseDrag)
						that._document.redraw();
				},

				mouseup: function(event) {
					if (dragging) {
						curPoint = null;
						if (that.eventInterval != null)
							clearInterval(this.timer);
						that.onHandleEvent('mouseup',
								viewToArtwork(event, that._document), event);
						if (that.onMouseUp)
							that._document.redraw();
						dragging = false;
					}
				},

				touchmove: function(event) {
					that.events.mousemove(event);
				},

				touchstart: function(event) {
					that.events.mousedown(event);
				},

				touchend: function(event) {
					that.events.mouseup(event);
				}
			};

			// Remove old events first.
			if (this._document)
				DomEvent.remove(this._document.canvas, this.events);
			this._document = doc;
			DomEvent.add(doc.canvas, this.events);
		},

		getDocument: function() {
			return this._document;
		},

		/**
		 * The fixed time delay between each call to the {@link #onMouseDrag}
		 * event. Setting this to an interval means the {@link #onMouseDrag}
		 * event is called repeatedly after the initial {@link #onMouseDown}
		 * until the user releases the mouse.
		 *
		 * Sample code:
		 *
		 * // Fire the onMouseDrag event once a second,
		 * // while the mouse button is down
		 * tool.eventInterval = 1000;
		 *
		 *
		 * @return the interval time in milliseconds
		 */
		eventInterval: null
	};
});
<commit_msg>Tool: fix problem where document was being redrawn onMouseMove with only an onMouseDrag handler (should only happen on touch devices)<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Tool = this.Tool = ToolHandler.extend(new function() {
	function viewToArtwork(event, document) {
		// TODO: always the active view?
		return document.activeView.viewToArtwork(DomEvent.getOffset(event));
	};

	return {
		beans: true,

		initialize: function(handlers, doc) {
			this.base(handlers);
			// Create events once, so they can be removed easily too.
			var that = this, curPoint;
			var dragging = false;
			this.events = {
				mousedown: function(event) {
					curPoint = viewToArtwork(event, that._document);
					that.onHandleEvent('mousedown', curPoint, event);
					if (that.onMouseDown)
						that._document.redraw();
					if (that.eventInterval != null) {
						this.timer = setInterval(that.events.mousemove,
								that.eventInterval);
					}
					dragging = true;
				},

				mousemove: function(event) {
					// If the event was triggered by a touch screen device,
					// prevent the default behaviour, as it will otherwise
					// scroll the page:
					var touchDevice = event && event.targetTouches;
					if (touchDevice)
						event.preventDefault();
					var point = event && viewToArtwork(event, that._document);
					// If there is only an onMouseMove handler, call it when
					// the user is dragging
					var onlyMove = !!(!that.onMouseDrag && that.onMouseMove);
					if (dragging && !onlyMove) {
						curPoint = point || curPoint;
						if (curPoint)
							that.onHandleEvent('mousedrag', curPoint, event);
					} else if (!dragging || onlyMove) {
						that.onHandleEvent('mousemove', point, event);
					}
					if (that.onMouseMove || (touchDevice && that.onMouseDrag))
						that._document.redraw();
				},

				mouseup: function(event) {
					if (dragging) {
						curPoint = null;
						if (that.eventInterval != null)
							clearInterval(this.timer);
						that.onHandleEvent('mouseup',
								viewToArtwork(event, that._document), event);
						if (that.onMouseUp)
							that._document.redraw();
						dragging = false;
					}
				},

				touchmove: function(event) {
					that.events.mousemove(event);
				},

				touchstart: function(event) {
					that.events.mousedown(event);
				},

				touchend: function(event) {
					that.events.mouseup(event);
				}
			};

			// Remove old events first.
			if (this._document)
				DomEvent.remove(this._document.canvas, this.events);
			this._document = doc;
			DomEvent.add(doc.canvas, this.events);
		},

		getDocument: function() {
			return this._document;
		},

		/**
		 * The fixed time delay between each call to the {@link #onMouseDrag}
		 * event. Setting this to an interval means the {@link #onMouseDrag}
		 * event is called repeatedly after the initial {@link #onMouseDown}
		 * until the user releases the mouse.
		 *
		 * Sample code:
		 *
		 * // Fire the onMouseDrag event once a second,
		 * // while the mouse button is down
		 * tool.eventInterval = 1000;
		 *
		 *
		 * @return the interval time in milliseconds
		 */
		eventInterval: null
	};
});
<|endoftext|>


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

(function() {
  'use strict';

  /**
   * Class constructor for Tooltip MDL component.
   * Implements MDL component design pattern defined at:
   * https://github.com/jasonmayes/mdl-component-design-pattern
   *
   * @constructor
   * @param {HTMLElement} element The element that will be upgraded.
   */
  var MaterialTooltip = function MaterialTooltip(element) {
    this.element_ = element;

    // Initialize instance.
    this.init();
  };
  window['MaterialTooltip'] = MaterialTooltip;

  /**
   * Store constants in one place so they can be updated easily.
   *
   * @enum {string | number}
   * @private
   */
  MaterialTooltip.prototype.Constant_ = {
    // None for now.
  };

  /**
   * Store strings for class names defined by this component that are used in
   * JavaScript. This allows us to simply change it in one place should we
   * decide to modify at a later date.
   *
   * @enum {string}
   * @private
   */
  MaterialTooltip.prototype.CssClasses_ = {
    IS_ACTIVE: 'is-active',
    BOTTOM: 'mdl-tooltip--bottom',
    LEFT: 'mdl-tooltip--left',
    RIGHT: 'mdl-tooltip--right',
    TOP: 'mdl-tooltip--top'
  };

  /**
   * Handle mouseenter for tooltip.
   *
   * @param {Event} event The event that fired.
   * @private
   */
  MaterialTooltip.prototype.handleMouseEnter_ = function(event) {
    var props = event.target.getBoundingClientRect();
    var left = props.left + (props.width / 2);
    var top = props.top + (props.height / 2);
    var marginLeft = -1 * (this.element_.offsetWidth / 2);
    var marginTop = -1 * (this.element_.offsetHeight / 2);

    if (this.element_.classList.contains(this.CssClasses_.LEFT) || this.element_.classList.contains(this.CssClasses_.RIGHT)) {
      left = (props.width / 2);
      if (top + marginTop < 0) {
        this.element_.style.top = 0;
        this.element_.style.marginTop = 0;
      } else {
        this.element_.style.top = top + 'px';
        this.element_.style.marginTop = marginTop + 'px';
      }
    } else {
      if (left + marginLeft < 0) {
        this.element_.style.left = 0;
        this.element_.style.marginLeft = 0;
      } else {
        this.element_.style.left = left + 'px';
        this.element_.style.marginLeft = marginLeft + 'px';
      }
    }

    if (this.element_.classList.contains(this.CssClasses_.TOP)) {
      this.element_.style.top = props.top - this.element_.offsetHeight - 10 + 'px';
    } else if (this.element_.classList.contains(this.CssClasses_.RIGHT)) {
      this.element_.style.left = props.left + props.width + 10 + 'px';
    } else if (this.element_.classList.contains(this.CssClasses_.LEFT)) {
      this.element_.style.left = props.left - this.element_.offsetWidth - 10 + 'px';
    } else {
      this.element_.style.top = props.top + props.height + 10 + 'px';
    }

    this.element_.classList.add(this.CssClasses_.IS_ACTIVE);
  };

  /**
   * Handle mouseleave for tooltip.
   *
   * @private
   */
  MaterialTooltip.prototype.handleMouseLeave_ = function() {
    this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);
  };

  /**
   * Initialize element.
   */
  MaterialTooltip.prototype.init = function() {

    if (this.element_) {
      var forElId = this.element_.getAttribute('for');

      if (forElId) {
        this.forElement_ = document.getElementById(forElId);
      }

      if (this.forElement_) {
        // It's left here because it prevents accidental text selection on Android
        if (!this.forElement_.hasAttribute('tabindex')) {
          this.forElement_.setAttribute('tabindex', '0');
        }

        this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);
        this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);
        this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);
        this.forElement_.addEventListener('touchend', this.boundMouseEnterHandler, false);
        this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler, false);
        window.addEventListener('touchstart', this.boundMouseLeaveHandler);
      }
    }
  };

  // The component registers itself. It can assume componentHandler is available
  // in the global scope.
  componentHandler.register({
    constructor: MaterialTooltip,
    classAsString: 'MaterialTooltip',
    cssClass: 'mdl-tooltip'
  });
})();
<commit_msg>Fix assignment for closure.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function() {
  'use strict';

  /**
   * Class constructor for Tooltip MDL component.
   * Implements MDL component design pattern defined at:
   * https://github.com/jasonmayes/mdl-component-design-pattern
   *
   * @constructor
   * @param {HTMLElement} element The element that will be upgraded.
   */
  var MaterialTooltip = function MaterialTooltip(element) {
    this.element_ = element;

    // Initialize instance.
    this.init();
  };
  window['MaterialTooltip'] = MaterialTooltip;

  /**
   * Store constants in one place so they can be updated easily.
   *
   * @enum {string | number}
   * @private
   */
  MaterialTooltip.prototype.Constant_ = {
    // None for now.
  };

  /**
   * Store strings for class names defined by this component that are used in
   * JavaScript. This allows us to simply change it in one place should we
   * decide to modify at a later date.
   *
   * @enum {string}
   * @private
   */
  MaterialTooltip.prototype.CssClasses_ = {
    IS_ACTIVE: 'is-active',
    BOTTOM: 'mdl-tooltip--bottom',
    LEFT: 'mdl-tooltip--left',
    RIGHT: 'mdl-tooltip--right',
    TOP: 'mdl-tooltip--top'
  };

  /**
   * Handle mouseenter for tooltip.
   *
   * @param {Event} event The event that fired.
   * @private
   */
  MaterialTooltip.prototype.handleMouseEnter_ = function(event) {
    var props = event.target.getBoundingClientRect();
    var left = props.left + (props.width / 2);
    var top = props.top + (props.height / 2);
    var marginLeft = -1 * (this.element_.offsetWidth / 2);
    var marginTop = -1 * (this.element_.offsetHeight / 2);

    if (this.element_.classList.contains(this.CssClasses_.LEFT) || this.element_.classList.contains(this.CssClasses_.RIGHT)) {
      left = (props.width / 2);
      if (top + marginTop < 0) {
        this.element_.style.top = '0';
        this.element_.style.marginTop = 0;
      } else {
        this.element_.style.top = top + 'px';
        this.element_.style.marginTop = marginTop + 'px';
      }
    } else {
      if (left + marginLeft < 0) {
        this.element_.style.left = '0';
        this.element_.style.marginLeft = 0;
      } else {
        this.element_.style.left = left + 'px';
        this.element_.style.marginLeft = marginLeft + 'px';
      }
    }

    if (this.element_.classList.contains(this.CssClasses_.TOP)) {
      this.element_.style.top = props.top - this.element_.offsetHeight - 10 + 'px';
    } else if (this.element_.classList.contains(this.CssClasses_.RIGHT)) {
      this.element_.style.left = props.left + props.width + 10 + 'px';
    } else if (this.element_.classList.contains(this.CssClasses_.LEFT)) {
      this.element_.style.left = props.left - this.element_.offsetWidth - 10 + 'px';
    } else {
      this.element_.style.top = props.top + props.height + 10 + 'px';
    }

    this.element_.classList.add(this.CssClasses_.IS_ACTIVE);
  };

  /**
   * Handle mouseleave for tooltip.
   *
   * @private
   */
  MaterialTooltip.prototype.handleMouseLeave_ = function() {
    this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);
  };

  /**
   * Initialize element.
   */
  MaterialTooltip.prototype.init = function() {

    if (this.element_) {
      var forElId = this.element_.getAttribute('for');

      if (forElId) {
        this.forElement_ = document.getElementById(forElId);
      }

      if (this.forElement_) {
        // It's left here because it prevents accidental text selection on Android
        if (!this.forElement_.hasAttribute('tabindex')) {
          this.forElement_.setAttribute('tabindex', '0');
        }

        this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);
        this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);
        this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);
        this.forElement_.addEventListener('touchend', this.boundMouseEnterHandler, false);
        this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler, false);
        window.addEventListener('touchstart', this.boundMouseLeaveHandler);
      }
    }
  };

  // The component registers itself. It can assume componentHandler is available
  // in the global scope.
  componentHandler.register({
    constructor: MaterialTooltip,
    classAsString: 'MaterialTooltip',
    cssClass: 'mdl-tooltip'
  });
})();
<|endoftext|>


<commit_before>function launch(prefix, containerId, config) {
    var config = config || {};
    var deps = [
        "element-factory.js",
        "animation.js",
        "preset-manager.js"
    ];
    var loaded = 0;
    for (var i = 0; i < deps.length; i++) {
        var elem = document.createElement('script');
        elem.src = prefix + deps[i];
        elem.onload = function() {
            if (++loaded < deps.length) return;

            var container = document.getElementById(containerId);

            var t = new CanvasFeedback();

            var canvas = yoob.makeCanvas(container, 400, 400);
            canvas.style.background = "white";
            canvas.style.border = "1px solid black";
            config.canvas = canvas;

            var controlPanel = config.controlPanel || container;

            yoob.makeButton(controlPanel, 'Restart', function() { t.reset(); });
            yoob.makeButton(controlPanel, 'Pause', function() { t.pause(); });
            yoob.makeButton(controlPanel, 'Resume', function() { t.resume(); });
            yoob.makeLineBreak(controlPanel);

            urlPanel = yoob.makePanel(controlPanel, "Image URL", false);
            var urlElem = yoob.makeTextInput(urlPanel, 32, config.imgUrl);
            yoob.makeButton(urlPanel, 'Load', function() {
                t.load(urlElem.value);
            });
            urlPanel.style.background = "#e0e0e0";

            presetPanel = yoob.makePanel(controlPanel, "Select Preset", true);
            var presetSelect = yoob.makeSelect(presetPanel, "Preset:", []);
            presetPanel.style.background = "#e0e0e0";

            var sliderPanel = yoob.makePanel(controlPanel, "Adjust Parameters", false);
            sliderPanel.style.textAlign = "right";
            sliderPanel.style.background = "#e0e0e0";

            var rotateSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Rotate (microRadians):', -3000, 3000, 5, 200,
                function(v) { t.rotationRate = v; }
            );
            rotateSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkLeftSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Left (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkLeft = v; }
            );
            shrinkLeftSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkRightSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Right (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkRight = v; }
            );
            shrinkRightSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkTopSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Top (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkTop = v; }
            );
            shrinkTopSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkBottomSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Bottom (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkBottom = v; }
            );
            shrinkBottomSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var p = new yoob.PresetManager();
            p.init({
                'selectElem': presetSelect
            });

            var presets = {
                "Rotate + Shrink ×1": {
                    rotate: 200,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: 1,
                    shrink_bottom: 1
                },
                "Rotate + Shrink ×5": {
                    rotate: 1000,
                    shrink_left: 5,
                    shrink_right: 5,
                    shrink_top: 5,
                    shrink_bottom: 5
                },
                "Rotate + Shrink ×100": {
                    rotate: 4000,
                    shrink_left: 100,
                    shrink_right: 100,
                    shrink_top: 100,
                    shrink_bottom: 100
                },
                "Rotate + Shrink Horiz ×2": {
                    rotate: 500,
                    shrink_left: 2,
                    shrink_right: 2,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Rotate + Shrink Horiz ×100": {
                    rotate: 6000,
                    shrink_left: 100,
                    shrink_right: 100,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Rotate + Shrink Vert ×100": {
                    rotate: 6000,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 100,
                    shrink_bottom: 100
                },
                "Rotate + Expand ×1": {
                    rotate: 200,
                    shrink_left: -1,
                    shrink_right: -1,
                    shrink_top: -1,
                    shrink_bottom: -1
                },
                "Rotate Only": {
                    rotate: 200,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Blast off!": {
                    rotate: 700,
                    shrink_left: 0,
                    shrink_right: 7,
                    shrink_top: 0,
                    shrink_bottom: 5
                },
                "Cosmotic!": {
                    rotate: 1000,
                    shrink_left: -3,
                    shrink_right: 10,
                    shrink_top: -3,
                    shrink_bottom: -3
                },
                "Squash down": {
                    rotate: -4000,
                    shrink_left: -100,
                    shrink_right: -100,
                    shrink_top: 200,
                    shrink_bottom: 0
                },
                "Identity (No effect)": {
                    rotate: 0,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Shrink ×1": {
                    rotate: 0,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: 1,
                    shrink_bottom: 1
                },
                "Expand ×1": {
                    rotate: 0,
                    shrink_left: -1,
                    shrink_right: -1,
                    shrink_top: -1,
                    shrink_bottom: -1
                },
                "Shrink Horiz ×1": {
                    rotate: 0,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Shrink Vert ×1": {
                    rotate: 0,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 1,
                    shrink_bottom: 1
                },
                "Shrink Horiz Expand Vert ×1": {
                    rotate: 0,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: -1,
                    shrink_bottom: -1
                },
                "Shrink Vert Expand Horiz ×1": {
                    rotate: 0,
                    shrink_left: -1,
                    shrink_right: -1,
                    shrink_top: 1,
                    shrink_bottom: 1
                }
            };

            var setPreset = function(n) {
                var obj = presets[n];
                rotateSlider.set(obj.rotate);
                shrinkLeftSlider.set(obj.shrink_left);
                shrinkRightSlider.set(obj.shrink_right);
                shrinkTopSlider.set(obj.shrink_top);
                shrinkBottomSlider.set(obj.shrink_bottom);
            };

            for (n in presets) {
                p.add(n, setPreset);
            }

            t.init(config);
        };
        document.body.appendChild(elem);
    }
}

CanvasFeedback = function() {
    var canvas;
    var ctx;

    var img = new Image();
    var r = 0;

    this.centerX = 200;
    this.centerY = 200;
    this.unCenterX = -200;
    this.unCenterY = -200;
    this.rotationRate = 200;

    this.shrinkLeft = 1;
    this.shrinkRight = 1;
    this.shrinkTop = 1;
    this.shrinkBottom = 1;

    this.draw = function() {
        ctx.save();
        ctx.translate(this.centerX, this.centerY);
        ctx.rotate(r);
        ctx.translate(this.unCenterX, this.unCenterY);
        r += this.rotationRate / 1000000;
        ctx.drawImage(canvas,
            this.shrinkLeft, this.shrinkTop,
            canvas.width - this.shrinkLeft - this.shrinkRight,
            canvas.height - this.shrinkTop - this.shrinkBottom);
        ctx.restore();
    };

    this.update = function() {
    };

    this.load = function(url) {
        var self = this;
        img.onload = function() {
            self.reset();
        }
        img.src = url;
    };

    this.reset = function() {
        canvas.width = img.width;
        canvas.height = img.height;
        this.centerX = img.width / 2;
        this.centerY = img.height / 2;
        this.unCenterX = -1 * this.centerX;
        this.unCenterY = -1 * this.centerY;
        r = 0;
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        this.resume();
    };

    this.resume = function() {
        this.animation.start();
    };

    this.pause = function() {
        this.animation.stop();
    };

    this.init = function(cfg) {
        canvas = cfg.canvas;
        ctx = canvas.getContext("2d");
        var $this = this;

        this.animation = (new yoob.Animation()).init({
            'object': this,
            'mode': 'quantum'
        });
        this.animation.start();

        img.onload = function() {
            $this.reset();
        }
        img.src = cfg.imgUrl;
    };
};
<commit_msg>Improve presentation of the collapsible sub-panels a bit.<commit_after>function launch(prefix, containerId, config) {
    var config = config || {};
    var deps = [
        "element-factory.js",
        "animation.js",
        "preset-manager.js"
    ];
    var loaded = 0;
    for (var i = 0; i < deps.length; i++) {
        var elem = document.createElement('script');
        elem.src = prefix + deps[i];
        elem.onload = function() {
            if (++loaded < deps.length) return;

            var container = document.getElementById(containerId);

            var t = new CanvasFeedback();

            var canvas = yoob.makeCanvas(container, 400, 400);
            canvas.style.background = "white";
            canvas.style.border = "1px solid black";
            config.canvas = canvas;

            var controlPanel = config.controlPanel || container;

            yoob.makeButton(controlPanel, 'Restart', function() { t.reset(); });
            yoob.makeButton(controlPanel, 'Pause', function() { t.pause(); });
            yoob.makeButton(controlPanel, 'Resume', function() { t.resume(); });
            yoob.makeLineBreak(controlPanel);

            var stylePanel = function(panel) {
                panel.style.background = "#e0e0e0";
                panel.style.padding = "3px";
                panel.parentNode.style.background = "#c0c0c0";
                panel.parentNode.style.textAlign = "left";
            };

            urlPanel = yoob.makePanel(controlPanel, "Image URL", false);
            var urlElem = yoob.makeTextInput(urlPanel, 64, config.imgUrl);
            urlElem.style.width = "80%";
            yoob.makeButton(urlPanel, 'Load', function() {
                t.load(urlElem.value);
            });
            stylePanel(urlPanel);

            presetPanel = yoob.makePanel(controlPanel, "Select Preset", true);
            var presetSelect = yoob.makeSelect(presetPanel, "Preset:", []);
            stylePanel(presetPanel);

            var sliderPanel = yoob.makePanel(controlPanel, "Adjust Parameters", false);
            sliderPanel.style.textAlign = "right";
            stylePanel(sliderPanel);

            var rotateSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Rotate (microRadians):', -3000, 3000, 5, 200,
                function(v) { t.rotationRate = v; }
            );
            rotateSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkLeftSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Left (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkLeft = v; }
            );
            shrinkLeftSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkRightSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Right (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkRight = v; }
            );
            shrinkRightSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkTopSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Top (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkTop = v; }
            );
            shrinkTopSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var shrinkBottomSlider = yoob.makeSliderPlusTextInput(
                sliderPanel, 'Shrink Bottom (pixels):', -200, 200, 5, 1,
                function(v) { t.shrinkBottom = v; }
            );
            shrinkBottomSlider.textInput.style.width = "auto";
            yoob.makeLineBreak(sliderPanel);

            var p = new yoob.PresetManager();
            p.init({
                'selectElem': presetSelect
            });

            var presets = {
                "Rotate + Shrink ×1": {
                    rotate: 200,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: 1,
                    shrink_bottom: 1
                },
                "Rotate + Shrink ×5": {
                    rotate: 1000,
                    shrink_left: 5,
                    shrink_right: 5,
                    shrink_top: 5,
                    shrink_bottom: 5
                },
                "Rotate + Shrink ×100": {
                    rotate: 4000,
                    shrink_left: 100,
                    shrink_right: 100,
                    shrink_top: 100,
                    shrink_bottom: 100
                },
                "Rotate + Shrink Horiz ×2": {
                    rotate: 500,
                    shrink_left: 2,
                    shrink_right: 2,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Rotate + Shrink Horiz ×100": {
                    rotate: 6000,
                    shrink_left: 100,
                    shrink_right: 100,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Rotate + Shrink Vert ×100": {
                    rotate: 6000,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 100,
                    shrink_bottom: 100
                },
                "Rotate + Expand ×1": {
                    rotate: 200,
                    shrink_left: -1,
                    shrink_right: -1,
                    shrink_top: -1,
                    shrink_bottom: -1
                },
                "Rotate Only": {
                    rotate: 200,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Blast off!": {
                    rotate: 700,
                    shrink_left: 0,
                    shrink_right: 7,
                    shrink_top: 0,
                    shrink_bottom: 5
                },
                "Cosmotic!": {
                    rotate: 1000,
                    shrink_left: -3,
                    shrink_right: 10,
                    shrink_top: -3,
                    shrink_bottom: -3
                },
                "Squash down": {
                    rotate: -4000,
                    shrink_left: -100,
                    shrink_right: -100,
                    shrink_top: 200,
                    shrink_bottom: 0
                },
                "Identity (No effect)": {
                    rotate: 0,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Shrink ×1": {
                    rotate: 0,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: 1,
                    shrink_bottom: 1
                },
                "Expand ×1": {
                    rotate: 0,
                    shrink_left: -1,
                    shrink_right: -1,
                    shrink_top: -1,
                    shrink_bottom: -1
                },
                "Shrink Horiz ×1": {
                    rotate: 0,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: 0,
                    shrink_bottom: 0
                },
                "Shrink Vert ×1": {
                    rotate: 0,
                    shrink_left: 0,
                    shrink_right: 0,
                    shrink_top: 1,
                    shrink_bottom: 1
                },
                "Shrink Horiz Expand Vert ×1": {
                    rotate: 0,
                    shrink_left: 1,
                    shrink_right: 1,
                    shrink_top: -1,
                    shrink_bottom: -1
                },
                "Shrink Vert Expand Horiz ×1": {
                    rotate: 0,
                    shrink_left: -1,
                    shrink_right: -1,
                    shrink_top: 1,
                    shrink_bottom: 1
                }
            };

            var setPreset = function(n) {
                var obj = presets[n];
                rotateSlider.set(obj.rotate);
                shrinkLeftSlider.set(obj.shrink_left);
                shrinkRightSlider.set(obj.shrink_right);
                shrinkTopSlider.set(obj.shrink_top);
                shrinkBottomSlider.set(obj.shrink_bottom);
            };

            for (n in presets) {
                p.add(n, setPreset);
            }

            t.init(config);
        };
        document.body.appendChild(elem);
    }
}

CanvasFeedback = function() {
    var canvas;
    var ctx;

    var img = new Image();
    var r = 0;

    this.centerX = 200;
    this.centerY = 200;
    this.unCenterX = -200;
    this.unCenterY = -200;
    this.rotationRate = 200;

    this.shrinkLeft = 1;
    this.shrinkRight = 1;
    this.shrinkTop = 1;
    this.shrinkBottom = 1;

    this.draw = function() {
        ctx.save();
        ctx.translate(this.centerX, this.centerY);
        ctx.rotate(r);
        ctx.translate(this.unCenterX, this.unCenterY);
        r += this.rotationRate / 1000000;
        ctx.drawImage(canvas,
            this.shrinkLeft, this.shrinkTop,
            canvas.width - this.shrinkLeft - this.shrinkRight,
            canvas.height - this.shrinkTop - this.shrinkBottom);
        ctx.restore();
    };

    this.update = function() {
    };

    this.load = function(url) {
        var self = this;
        img.onload = function() {
            self.reset();
        }
        img.src = url;
    };

    this.reset = function() {
        canvas.width = img.width;
        canvas.height = img.height;
        this.centerX = img.width / 2;
        this.centerY = img.height / 2;
        this.unCenterX = -1 * this.centerX;
        this.unCenterY = -1 * this.centerY;
        r = 0;
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        this.resume();
    };

    this.resume = function() {
        this.animation.start();
    };

    this.pause = function() {
        this.animation.stop();
    };

    this.init = function(cfg) {
        canvas = cfg.canvas;
        ctx = canvas.getContext("2d");
        var $this = this;

        this.animation = (new yoob.Animation()).init({
            'object': this,
            'mode': 'quantum'
        });
        this.animation.start();

        img.onload = function() {
            $this.reset();
        }
        img.src = cfg.imgUrl;
    };
};
<|endoftext|>


<commit_before>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.requireStylesheet('cc.layer_picker');

base.require('cc.constants');
base.require('cc.layer_tree_host_impl');
base.require('tracing.analysis.generic_object_view');
base.require('ui.drag_handle');
base.require('ui.list_view');

base.exportTo('cc', function() {
  var constants = cc.constants;

  /**
   * @constructor
   */
  var LayerPicker = ui.define('layer-picker');

  LayerPicker.prototype = {
    __proto__: HTMLUnknownElement.prototype,

    decorate: function() {
      this.lthi_ = undefined;
      this.whichTree_ = constants.ACTIVE_TREE;

      this.controls_ = document.createElement('top-controls');

      this.layerList_ = new ui.ListView();
      this.layerDataView_ = new tracing.analysis.GenericObjectView();
      this.appendChild(this.controls_);

      this.appendChild(this.layerList_);
      var dragHandle = new ui.DragHandle();
      dragHandle.horizontal = true;
      dragHandle.target = this.layerDataView_;
      this.appendChild(dragHandle);
      this.appendChild(this.layerDataView_);

      this.layerList_.addEventListener(
          'selection-changed', this.onLayerSelectionChanged_.bind(this));

      this.titleEl_ = document.createElement('span');
      this.controls_.appendChild(this.titleEl_);

      this.controls_.appendChild(ui.createSelector(
          this, 'whichTree',
          [{label: 'Active tree', value: constants.ACTIVE_TREE},
           {label: 'Pending tree', value: constants.PENDING_TREE}]));

      this.hidePureTransformLayers_ = true;
      this.controls_.appendChild(ui.createCheckBox(
          this, 'hidePureTransformLayers', 'Hide transform layers'));
    },

    get lthiSnapshot() {
      return this.lthiSnapshot_;
    },

    set lthiSnapshot(lthiSnapshot) {
      this.lthiSnapshot_ = lthiSnapshot;
      this.updateContents_();
    },

    get whichTree() {
      return this.whichTree_;
    },

    set whichTree(whichTree) {
      this.whichTree_ = whichTree;
      this.updateContents_();
    },

    get hidePureTransformLayers() {
      return this.hidePureTransformLayers_;
    },

    set hidePureTransformLayers(hide) {
      this.hidePureTransformLayers_ = hide;
      this.updateContents_();
    },

    getLayerInfos_: function() {
      if (!this.lthiSnapshot_)
        return [];

      var tree = this.lthiSnapshot_.getTree(this.whichTree_);
      if (!tree)
        return [];

      var layerInfos = [];

      var hidePureTransformLayers = this.hidePureTransformLayers_;

      function isPureTransformLayer(layer) {
        if (layer.args.compositingReasons &&
            layer.args.compositingReasons.length != 1 &&
            layer.args.compositingReasons[0] != 'No reasons given')
          return false;

        if (layer.args.drawsContent)
          return false;

        return true;
      }
      function visitLayer(layer, depth, note) {
        var info = {layer: layer,
          depth: depth};

        if (layer.args.drawsContent)
          info.name = layer.objectInstance.name;
        else
          info.name = 'cc::LayerImpl';

        if (!hidePureTransformLayers || !isPureTransformLayer(layer))
          layerInfos.push(info);

        var childInfo;
        for (var i = 0; i < layer.children.length; i++)
          visitLayer(layer.children[i], depth + 1);

        if (layer.maskLayer) {
          childInfo = visitLayer(layer.maskLayer, depth + 2);
          childInfo.isMaskLayer = true;
        }

        if (layer.replicaLayer) {
          var childInfo = visitLayer(layer.replicaLayer, depth + 2);
          childInfo.replicaLayer = true;
        }

        return info;
      };
      visitLayer(tree.rootLayer, 0);
      return layerInfos;
    },

    updateContents_: function() {
      var oldSelectedLayer = this.selectedLayer;
      this.updateContentsInner_();

      if (!oldSelectedLayer) {
        if (!this.layerList_.selectedElement) {
          // Try to select the first layer that drawsContent.
          for (var i = 0; i < this.layerList_.children.length; i++) {
            if (this.layerList_.children[i].layer.args.drawsContent) {
              this.layerList_.children[i].selected = true;
              return;
            }
          }
          // Barring that, select the first layer.
          if (this.layerList_.children.length > 0)
            this.layerList_.children[0].selected = true;
        }
        return;
      }

      // Try to resync the selection to what it was before.
      var goal = oldSelectedLayer.objectInstance;
      for (var i = 0; i < this.layerList_.children.length; i++) {
        if (this.layerList_.children[i].layer.objectInstance == goal) {
          this.layerList_.children[i].selected = true;
          return;
        }
      }

      // TODO(nduca): If matching failed, try to match on layer_id. The instance
      // may have changed but the id could persist.
    },

    updateContentsInner_: function() {
      this.titleEl_.textContent = 'CC::LayerTreeHostImpl ' +
          this.lthiSnapshot_.objectInstance.id;

      this.layerList_.clear();

      var layerInfos = this.getLayerInfos_();
      layerInfos.forEach(function(layerInfo) {
        var layer = layerInfo.layer;

        var item = document.createElement('div');

        var indentEl = item.appendChild(ui.createSpan());
        indentEl.style.whiteSpace = 'pre';
        for (var i = 0; i < layerInfo.depth; i++)
          indentEl.textContent = indentEl.textContent + ' ';

        var labelEl = item.appendChild(ui.createSpan());
        labelEl.textContent = layerInfo.name + ' ' +
            layer.objectInstance.id;

        var notesEl = item.appendChild(ui.createSpan());
        if (layerInfo.isMaskLayer)
          notesEl.textContent += '(mask)';
        if (layerInfo.isReplicaLayer)
          notesEl.textContent += '(replica)';

        item.layer = layer;
        this.layerList_.appendChild(item);
      }, this);
    },

    onLayerSelectionChanged_: function(e) {
      base.dispatchSimpleEvent(this, 'selection-changed', false);
      if (this.selectedLayer)
        this.layerDataView_.object = this.selectedLayer.args;
      else
        this.layerDataView_.object = '<no layer selected>';
    },

    get selectedLayer() {
      if (!this.layerList_.selectedElement)
        return undefined;
      return this.layerList_.selectedElement.layer;
    }
  };

  return {
    LayerPicker: LayerPicker
  };
});
<commit_msg>Show layer id instead of pointer to scare people less.<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

base.requireStylesheet('cc.layer_picker');

base.require('cc.constants');
base.require('cc.layer_tree_host_impl');
base.require('tracing.analysis.generic_object_view');
base.require('ui.drag_handle');
base.require('ui.list_view');

base.exportTo('cc', function() {
  var constants = cc.constants;

  /**
   * @constructor
   */
  var LayerPicker = ui.define('layer-picker');

  LayerPicker.prototype = {
    __proto__: HTMLUnknownElement.prototype,

    decorate: function() {
      this.lthi_ = undefined;
      this.whichTree_ = constants.ACTIVE_TREE;

      this.controls_ = document.createElement('top-controls');

      this.layerList_ = new ui.ListView();
      this.layerDataView_ = new tracing.analysis.GenericObjectView();
      this.appendChild(this.controls_);

      this.appendChild(this.layerList_);
      var dragHandle = new ui.DragHandle();
      dragHandle.horizontal = true;
      dragHandle.target = this.layerDataView_;
      this.appendChild(dragHandle);
      this.appendChild(this.layerDataView_);

      this.layerList_.addEventListener(
          'selection-changed', this.onLayerSelectionChanged_.bind(this));

      this.titleEl_ = document.createElement('span');
      this.controls_.appendChild(this.titleEl_);

      this.controls_.appendChild(ui.createSelector(
          this, 'whichTree',
          [{label: 'Active tree', value: constants.ACTIVE_TREE},
           {label: 'Pending tree', value: constants.PENDING_TREE}]));

      this.hidePureTransformLayers_ = true;
      this.controls_.appendChild(ui.createCheckBox(
          this, 'hidePureTransformLayers', 'Hide transform layers'));
    },

    get lthiSnapshot() {
      return this.lthiSnapshot_;
    },

    set lthiSnapshot(lthiSnapshot) {
      this.lthiSnapshot_ = lthiSnapshot;
      this.updateContents_();
    },

    get whichTree() {
      return this.whichTree_;
    },

    set whichTree(whichTree) {
      this.whichTree_ = whichTree;
      this.updateContents_();
    },

    get hidePureTransformLayers() {
      return this.hidePureTransformLayers_;
    },

    set hidePureTransformLayers(hide) {
      this.hidePureTransformLayers_ = hide;
      this.updateContents_();
    },

    getLayerInfos_: function() {
      if (!this.lthiSnapshot_)
        return [];

      var tree = this.lthiSnapshot_.getTree(this.whichTree_);
      if (!tree)
        return [];

      var layerInfos = [];

      var hidePureTransformLayers = this.hidePureTransformLayers_;

      function isPureTransformLayer(layer) {
        if (layer.args.compositingReasons &&
            layer.args.compositingReasons.length != 1 &&
            layer.args.compositingReasons[0] != 'No reasons given')
          return false;

        if (layer.args.drawsContent)
          return false;

        return true;
      }
      function visitLayer(layer, depth, note) {
        var info = {layer: layer,
          depth: depth};

        if (layer.args.drawsContent)
          info.name = layer.objectInstance.name;
        else
          info.name = 'cc::LayerImpl';

        if (!hidePureTransformLayers || !isPureTransformLayer(layer))
          layerInfos.push(info);

        var childInfo;
        for (var i = 0; i < layer.children.length; i++)
          visitLayer(layer.children[i], depth + 1);

        if (layer.maskLayer) {
          childInfo = visitLayer(layer.maskLayer, depth + 2);
          childInfo.isMaskLayer = true;
        }

        if (layer.replicaLayer) {
          var childInfo = visitLayer(layer.replicaLayer, depth + 2);
          childInfo.replicaLayer = true;
        }

        return info;
      };
      visitLayer(tree.rootLayer, 0);
      return layerInfos;
    },

    updateContents_: function() {
      var oldSelectedLayer = this.selectedLayer;
      this.updateContentsInner_();

      if (!oldSelectedLayer) {
        if (!this.layerList_.selectedElement) {
          // Try to select the first layer that drawsContent.
          for (var i = 0; i < this.layerList_.children.length; i++) {
            if (this.layerList_.children[i].layer.args.drawsContent) {
              this.layerList_.children[i].selected = true;
              return;
            }
          }
          // Barring that, select the first layer.
          if (this.layerList_.children.length > 0)
            this.layerList_.children[0].selected = true;
        }
        return;
      }

      // Try to resync the selection to what it was before.
      var goal = oldSelectedLayer.objectInstance;
      for (var i = 0; i < this.layerList_.children.length; i++) {
        if (this.layerList_.children[i].layer.objectInstance == goal) {
          this.layerList_.children[i].selected = true;
          return;
        }
      }

      // TODO(nduca): If matching failed, try to match on layer_id. The instance
      // may have changed but the id could persist.
    },

    updateContentsInner_: function() {
      this.titleEl_.textContent = 'CC::LayerTreeHostImpl ' +
          this.lthiSnapshot_.objectInstance.id;

      this.layerList_.clear();

      var layerInfos = this.getLayerInfos_();
      layerInfos.forEach(function(layerInfo) {
        var layer = layerInfo.layer;

        var item = document.createElement('div');

        var indentEl = item.appendChild(ui.createSpan());
        indentEl.style.whiteSpace = 'pre';
        for (var i = 0; i < layerInfo.depth; i++)
          indentEl.textContent = indentEl.textContent + ' ';

        var labelEl = item.appendChild(ui.createSpan());
        var id;
        if (layer.args.layerId !== undefined)
          id = layer.args.layerId;
        else
          id = layer.objectInstance.id;
        labelEl.textContent = layerInfo.name + ' ' + id;

        var notesEl = item.appendChild(ui.createSpan());
        if (layerInfo.isMaskLayer)
          notesEl.textContent += '(mask)';
        if (layerInfo.isReplicaLayer)
          notesEl.textContent += '(replica)';

        item.layer = layer;
        this.layerList_.appendChild(item);
      }, this);
    },

    onLayerSelectionChanged_: function(e) {
      base.dispatchSimpleEvent(this, 'selection-changed', false);
      if (this.selectedLayer)
        this.layerDataView_.object = this.selectedLayer.args;
      else
        this.layerDataView_.object = '<no layer selected>';
    },

    get selectedLayer() {
      if (!this.layerList_.selectedElement)
        return undefined;
      return this.layerList_.selectedElement.layer;
    }
  };

  return {
    LayerPicker: LayerPicker
  };
});
<|endoftext|>