<commit_before>'use strict';

/*
	GraphDataFetcher

	This class knows how to fetch data from the SIGFOX backend.
	Whenever data is fetched, it is added to the encapsulated graphData array.

	It could in theory fetch data in any order (using any random beforeTime)
	and still maintain graphData properly, but for now it kind of assumes that
	fetchData is called from most recent time to further and further away back
	in time.
*/
function GraphDataFetcher(deviceID, limit, firstFetchTimeMs)
{
	this._deviceID = deviceID;
	this._limit = limit;
	this._firstFetchTimeMs = firstFetchTimeMs;	// The first fetch request will use this 'beforeTime' if specified
	this._graphData = [];		// An array of {x:<epoch time in milliseconds>, temperature:<in celsius>, pressure:<in hPa>, humidity:<in percent>}
	this._xmin = null;			// The most ancient possible x value on the backend. There's no data available before that point. When it is null, we don't know it yet
	this._xminFinal = false;
	this._xmax = null;		// The most recent x value on the backend. There's no data available (yet!) after that point. When null, we don't know it, but we'll query it quickly on first fetch
	this._promiseInProgress = null;
}

GraphDataFetcher._messageIntervalMs = 10.2*60*1000;	// Theoritical interval in milliseconds between weather messages. In practise, it's slightly more

GraphDataFetcher.prototype.isFetching = function()
{
	return !!this._promiseInProgress;
};

GraphDataFetcher.prototype.getXMin = function()
{
	return this._xmin;
};

GraphDataFetcher.prototype.getXMax = function()
{
	return this._xmax;
};

GraphDataFetcher.prototype.canFetchDataForward = function()
{
	if ( !this.getXMax() )
		return true;

	var now = new Date().getTime();
	var expectedNumberOfMessagesReadyForFetch = Math.floor( (now - this.getXMax()) / GraphDataFetcher._messageIntervalMs );
	if ( expectedNumberOfMessagesReadyForFetch<=0 )
		return false;

	return true;
};

GraphDataFetcher.prototype.fetchDataForward = function()
{
	//console.log("fetchDataForward");
	if ( this.isFetching() )
		return null;

	if ( !this.canFetchDataForward() )			// JBM: to enable
		return null;

	var x = this.getXMax();
	if ( x===null )
		x = this._firstFetchTimeMs;

	x += GraphDataFetcher._messageIntervalMs * this._limit * 0.9;	// The 0.9 factor is to allow a bit of overlap between the messages we're requesting and the ones we've got already

	// We can't fetch data past the current time of the backend server (it'd reply with an error).
	// So any request for fetch data forward in the future is clamped to current time.
	// Because our UTS time now might be slightly different, we're cautious a little time from
	// it so we should be safe
	var now = new Date().getTime() - GraphDataFetcher._messageIntervalMs/2;
	if ( x>now )
		x = now;

	var beforeTimeInSeconds = Math.floor(x/1000);

	var uri = GraphDataFetcher._getUri( this._deviceID, this._limit, beforeTimeInSeconds );
	var promise = HttpRequest.request( uri, 'GET')
		.then(
			function( response )
			{
				var messages = JSON.parse(response);
				var newGraphData = GraphDataFetcher._getGraphDataFromSigfoxMessages( messages );

				// Remove from new graph data the data we've already got
				if ( this._graphData.length>0 )
				{
					var xmax = this._graphData[0].x;
					var newGraphData2 = [];
					for ( var i=newGraphData.length-1; i>=0; i-- )
					{
						if ( newGraphData[i].x>xmax )
							newGraphData2.unshift( newGraphData[i] );
					}
					newGraphData = newGraphData2;
				}

				// Insert new graph data in front of the data we've got
				if ( newGraphData.length>0 )
				{
					for ( var i=newGraphData.length-1; i>=0; i-- )
						this._graphData.unshift( newGraphData[i] );
				}

			/*	if ( !this._xmax && newGraphData.length>0 && now-newGraphData[0].x<GraphDataFetcher._messageIntervalMs )
				{
					this._xmax = newGraphData[0].x;
				}
				else*/
				{
					this._xmax = x;
				}

				if ( !this._xmin )
				{
					if ( newGraphData.length>0 )
						this._xmin = newGraphData[newGraphData.length-1].x;		// newGraphData and this._graphData should be the same as this should be the first fetch
					else
						this._xmin = this._xmax;
				}

				this._promiseInProgress = null;

				return Promise.resolve(newGraphData);
			}.bind(this))
		.catch(
			function( error )
			{
				this._promiseInProgress = null;
				throw error;
			}.bind(this));

	this._promiseInProgress = promise;
	return promise;
};

GraphDataFetcher.prototype.canFetchDataBackward = function()
{
	// Once we've identified the final x min, we know there's nothing more to fetch ever
	//return this.getXMin();
	return !this._xminFinal;
};

GraphDataFetcher.prototype.fetchDataBackward = function()
{
	//console.log("fetchDataBackward");
	if ( this.isFetching() )
		return null;

	if ( !this.canFetchDataBackward() )
		return null;

	var x = this.getXMin();
	if ( x===null )
	{
		if ( this._firstFetchTimeMs )
		{
			x = this._firstFetchTimeMs;
		}
		else
		{
			x = new Date().getTime();
		}
	}

	var beforeTimeInSeconds = Math.floor(x/1000);

	var uri = GraphDataFetcher._getUri( this._deviceID, this._limit, beforeTimeInSeconds );
	var promise = HttpRequest.request( uri, 'GET')
		.then(
			function( response )
			{
				var messages = JSON.parse(response);
				var newGraphData = GraphDataFetcher._getGraphDataFromSigfoxMessages( messages );

				// Remove from new graph data the data we've already got
				if ( this._graphData.length>0 )
				{
					var xmin = this._graphData[this._graphData.length-1].x;
					var newGraphData2 = [];
					for ( var i=0; i<newGraphData.length; ++i )
					{
						if ( newGraphData[i].x<xmin )
							newGraphData2.push( newGraphData[i] );
					}
					newGraphData = newGraphData2;
				}

				// Push new graph data at the end of the data we've got
				if ( newGraphData.length>0 )
				{
					for ( var i=0; i<newGraphData.length; ++i )
						this._graphData.push( newGraphData[i] );
				}

				if ( newGraphData.length>0 )
				{
					this._xmin = newGraphData[newGraphData.length-1].x;
				}
				else
				{
					this._xmin = x;
					this._xminFinal = true;
				}

				if ( !this._xmax )
				{
					this._xmax = x;
				}

				this._promiseInProgress = null;

				return Promise.resolve(newGraphData);
			}.bind(this))
		.catch(
			function( error )
			{
				this._promiseInProgress = null;
				throw error;
			}.bind(this));

	this._promiseInProgress = promise;
	return promise;
};

GraphDataFetcher._getGraphDataFromSigfoxMessages = function( messages )
{
	var graphData = [];
	for ( var i=0; i<messages.data.length; ++i  )
	{
		var message = messages.data[i];
		var uint8Array = GraphDataFetcher._createUint8ArrayFromMessageString( message.data );
		var weatherData = GraphDataFetcher._getWeatherDataFromUint8Array( uint8Array );

		// Sigfox message timestamp is a UTC/GMT time expressed in seconds since Unix Epoch (1st of January 1970)
		// Graph data x axis is in milliseconds
		var x = message.time * 1000;
		if ( weatherData.temperature<-100 || weatherData.temperature>100 )
		{
			console.warn("Invalid temperature: " + weatherData.temperature + " at time " + new Date(x) );
			weatherData.temperature = 0;
		}

		if ( weatherData.humidity<0 || weatherData.humidity>100 )
		{
			console.warn("Invalid humidity: " + weatherData.humidity + " at time " + new Date(x) );
			weatherData.humidity = 0;
		}

		if ( weatherData.pressure<900 || weatherData.pressure>1200 )
		{
			console.warn("Invalid pressure: " + weatherData.pressure + " at time " + new Date(x) );
			weatherData.pressure = 0;
		}

		graphData.push(
			{
				x: x,
				temperature: weatherData.temperature,
				humidity: weatherData.humidity,
				pressure: weatherData.pressure,
				message: message  					// We keep track of the raw message in the graph data
			});
	}

	return graphData;
};

GraphDataFetcher._getUri = function( deviceID, limit, beforeTimeInSeconds )
{
	var uri = '/api/devices/' + deviceID + '/messages?limit=' + limit;
	if ( beforeTimeInSeconds!==null && beforeTimeInSeconds!==undefined )
	{
		uri += '&before=' + beforeTimeInSeconds;
	}
	return uri;
};

GraphDataFetcher._createUint8ArrayFromMessageString = function( messageString )
{
	// http://stackoverflow.com/questions/1597709/convert-a-string-with-a-hex-representation-of-an-ieee-754-double-into-javascript
	var messageLengthInBytes = 12;
	var buffer = new ArrayBuffer(messageLengthInBytes);
	var uint8Array = new Uint8Array( buffer );
	for ( var i=0; i<messageLengthInBytes; i++ )
	{
		var byteAsHex = messageString.substr( i*2, 2 );
		var byte = parseInt( byteAsHex, 16 );
		uint8Array[i] = byte;
	}
	return uint8Array;
};

GraphDataFetcher._getWeatherDataFromUint8Array = function( uint8Array )
{
	var dataView = new DataView(uint8Array.buffer);
	var weatherData = {
		pressure: dataView.getUint32(0),
		temperature: dataView.getFloat32(4),
		humidity: dataView.getUint32(8)
	};
	return weatherData;
};
<commit_msg>Added support for new message format (v2) which packs weather data more tightly. This new format is implemented in the sigfox-weather-station fork which can be found here: https://github.com/jbitoniau/sigfox-weather-station. Old message format is still supported<commit_after>'use strict';

/*
	GraphDataFetcher

	This class knows how to fetch data from the SIGFOX backend.
	Whenever data is fetched, it is added to the encapsulated graphData array.

	It could in theory fetch data in any order (using any random beforeTime)
	and still maintain graphData properly, but for now it kind of assumes that
	fetchData is called from most recent time to further and further away back
	in time.
*/
function GraphDataFetcher(deviceID, limit, firstFetchTimeMs)
{
	this._deviceID = deviceID;
	this._limit = limit;
	this._firstFetchTimeMs = firstFetchTimeMs;	// The first fetch request will use this 'beforeTime' if specified
	this._graphData = [];		// An array of {x:<epoch time in milliseconds>, temperature:<in celsius>, pressure:<in hPa>, humidity:<in percent>}
	this._xmin = null;			// The most ancient possible x value on the backend. There's no data available before that point. When it is null, we don't know it yet
	this._xminFinal = false;
	this._xmax = null;		// The most recent x value on the backend. There's no data available (yet!) after that point. When null, we don't know it, but we'll query it quickly on first fetch
	this._promiseInProgress = null;
}

GraphDataFetcher._messageIntervalMs = 10.2*60*1000;	// Theoritical interval in milliseconds between weather messages. In practise, it's slightly more

GraphDataFetcher.prototype.isFetching = function()
{
	return !!this._promiseInProgress;
};

GraphDataFetcher.prototype.getXMin = function()
{
	return this._xmin;
};

GraphDataFetcher.prototype.getXMax = function()
{
	return this._xmax;
};

GraphDataFetcher.prototype.canFetchDataForward = function()
{
	if ( !this.getXMax() )
		return true;

	var now = new Date().getTime();
	var expectedNumberOfMessagesReadyForFetch = Math.floor( (now - this.getXMax()) / GraphDataFetcher._messageIntervalMs );
	if ( expectedNumberOfMessagesReadyForFetch<=0 )
		return false;

	return true;
};

GraphDataFetcher.prototype.fetchDataForward = function()
{
	//console.log("fetchDataForward");
	if ( this.isFetching() )
		return null;

	if ( !this.canFetchDataForward() )			// JBM: to enable
		return null;

	var x = this.getXMax();
	if ( x===null )
		x = this._firstFetchTimeMs;

	x += GraphDataFetcher._messageIntervalMs * this._limit * 0.9;	// The 0.9 factor is to allow a bit of overlap between the messages we're requesting and the ones we've got already

	// We can't fetch data past the current time of the backend server (it'd reply with an error).
	// So any request for fetch data forward in the future is clamped to current time.
	// Because our UTS time now might be slightly different, we're cautious a little time from
	// it so we should be safe
	var now = new Date().getTime() - GraphDataFetcher._messageIntervalMs/2;
	if ( x>now )
		x = now;

	var beforeTimeInSeconds = Math.floor(x/1000);

	var uri = GraphDataFetcher._getUri( this._deviceID, this._limit, beforeTimeInSeconds );
	var promise = HttpRequest.request( uri, 'GET')
		.then(
			function( response )
			{
				var messages = JSON.parse(response);
				var newGraphData = GraphDataFetcher._getGraphDataFromSigfoxMessages( messages );

				// Remove from new graph data the data we've already got
				if ( this._graphData.length>0 )
				{
					var xmax = this._graphData[0].x;
					var newGraphData2 = [];
					for ( var i=newGraphData.length-1; i>=0; i-- )
					{
						if ( newGraphData[i].x>xmax )
							newGraphData2.unshift( newGraphData[i] );
					}
					newGraphData = newGraphData2;
				}

				// Insert new graph data in front of the data we've got
				if ( newGraphData.length>0 )
				{
					for ( var i=newGraphData.length-1; i>=0; i-- )
						this._graphData.unshift( newGraphData[i] );
				}

			/*	if ( !this._xmax && newGraphData.length>0 && now-newGraphData[0].x<GraphDataFetcher._messageIntervalMs )
				{
					this._xmax = newGraphData[0].x;
				}
				else*/
				{
					this._xmax = x;
				}

				if ( !this._xmin )
				{
					if ( newGraphData.length>0 )
						this._xmin = newGraphData[newGraphData.length-1].x;		// newGraphData and this._graphData should be the same as this should be the first fetch
					else
						this._xmin = this._xmax;
				}

				this._promiseInProgress = null;

				return Promise.resolve(newGraphData);
			}.bind(this))
		.catch(
			function( error )
			{
				this._promiseInProgress = null;
				throw error;
			}.bind(this));

	this._promiseInProgress = promise;
	return promise;
};

GraphDataFetcher.prototype.canFetchDataBackward = function()
{
	// Once we've identified the final x min, we know there's nothing more to fetch ever
	//return this.getXMin();
	return !this._xminFinal;
};

GraphDataFetcher.prototype.fetchDataBackward = function()
{
	//console.log("fetchDataBackward");
	if ( this.isFetching() )
		return null;

	if ( !this.canFetchDataBackward() )
		return null;

	var x = this.getXMin();
	if ( x===null )
	{
		if ( this._firstFetchTimeMs )
		{
			x = this._firstFetchTimeMs;
		}
		else
		{
			x = new Date().getTime();
		}
	}

	var beforeTimeInSeconds = Math.floor(x/1000);

	var uri = GraphDataFetcher._getUri( this._deviceID, this._limit, beforeTimeInSeconds );
	var promise = HttpRequest.request( uri, 'GET')
		.then(
			function( response )
			{
				var messages = JSON.parse(response);
				var newGraphData = GraphDataFetcher._getGraphDataFromSigfoxMessages( messages );

				// Remove from new graph data the data we've already got
				if ( this._graphData.length>0 )
				{
					var xmin = this._graphData[this._graphData.length-1].x;
					var newGraphData2 = [];
					for ( var i=0; i<newGraphData.length; ++i )
					{
						if ( newGraphData[i].x<xmin )
							newGraphData2.push( newGraphData[i] );
					}
					newGraphData = newGraphData2;
				}

				// Push new graph data at the end of the data we've got
				if ( newGraphData.length>0 )
				{
					for ( var i=0; i<newGraphData.length; ++i )
						this._graphData.push( newGraphData[i] );
				}

				if ( newGraphData.length>0 )
				{
					this._xmin = newGraphData[newGraphData.length-1].x;
				}
				else
				{
					this._xmin = x;
					this._xminFinal = true;
				}

				if ( !this._xmax )
				{
					this._xmax = x;
				}

				this._promiseInProgress = null;

				return Promise.resolve(newGraphData);
			}.bind(this))
		.catch(
			function( error )
			{
				this._promiseInProgress = null;
				throw error;
			}.bind(this));

	this._promiseInProgress = promise;
	return promise;
};

GraphDataFetcher._getGraphDataFromSigfoxMessages = function( messages )
{
	var graphData = [];
	for ( var i=0; i<messages.data.length; ++i  )
	{
		var message = messages.data[i];
		var uint8Array = GraphDataFetcher._createUint8ArrayFromMessageString( message.data );
		var weatherData = GraphDataFetcher._getWeatherDataFromUint8Array( uint8Array );

		// Sigfox message timestamp is a UTC/GMT time expressed in seconds since Unix Epoch (1st of January 1970)
		// Graph data x axis is in milliseconds
		var x = message.time * 1000;

		graphData.push(
			{
				x: x,
				temperature: weatherData.temperature,
				humidity: weatherData.humidity,
				pressure: weatherData.pressure,
				message: message  					// We keep track of the raw message in the graph data
			});
	}

	return graphData;
};

GraphDataFetcher._getUri = function( deviceID, limit, beforeTimeInSeconds )
{
	var uri = '/api/devices/' + deviceID + '/messages?limit=' + limit;
	if ( beforeTimeInSeconds!==null && beforeTimeInSeconds!==undefined )
	{
		uri += '&before=' + beforeTimeInSeconds;
	}
	return uri;
};

GraphDataFetcher._createUint8ArrayFromMessageString = function( messageString )
{
	// http://stackoverflow.com/questions/1597709/convert-a-string-with-a-hex-representation-of-an-ieee-754-double-into-javascript
	var messageLengthInBytes = 12;
	var buffer = new ArrayBuffer(messageLengthInBytes);
	var uint8Array = new Uint8Array( buffer );
	for ( var i=0; i<messageLengthInBytes; i++ )
	{
		var byteAsHex = messageString.substr( i*2, 2 );
		var byte = parseInt( byteAsHex, 16 );
		uint8Array[i] = byte;
	}
	return uint8Array;
};

GraphDataFetcher._getWeatherDataFromUint8Array = function( uint8Array )
{
	var weatherData = GraphDataFetcher._getWeatherDataVersion1FromUint8Array(uint8Array);
	if ( weatherData.temperature<-50 || weatherData.temperature>50 ||
		 weatherData.humidity<0 || weatherData.humidity>100 ||
		 weatherData.pressure<900 || weatherData.pressure>1200 )
	{
		weatherData = GraphDataFetcher._getWeatherDataVersion2FromUint8Array(uint8Array);
	}
	return weatherData;
};

GraphDataFetcher._getWeatherDataVersion1FromUint8Array = function( uint8Array )
{
	var dataView = new DataView(uint8Array.buffer);
	var weatherData = {
		pressure: dataView.getUint32(0),
		temperature: dataView.getFloat32(4),
		humidity: dataView.getUint32(8)
	};
	return weatherData;
};

GraphDataFetcher._getWeatherDataVersion2FromUint8Array = function( uint8Array )
{
	var dataView = new DataView(uint8Array.buffer);

	var temperature = (dataView.getUint8(11) -64.0) / 4.0;
	var humidity = dataView.getUint8(10) / 2.0;
	var pressure = dataView.getUint8(9) + 815;

	var weatherData = {
		pressure: pressure,
		temperature: temperature,
		humidity: humidity
	};
	return weatherData;
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Jeeva Kandasamy (jkandasa@gmail.com)
 *
 * 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.
 */
myControllerModule.controller('FirmwareController', function(alertService,
$scope, $filter, FirmwaresFactory, $location, $uibModal, displayRestError, about, $filter) {

  $scope.filteredList=[];
  $scope.orgList=[];
  $scope.config = {
    itemsPerPage: 10,
    maxPages:10,
    fillLastPage: false
  }

  //about, Timezone, etc.,
  $scope.about = about;

  //Filter
  $scope.updateFilteredList = function() {
    $scope.filteredList = $filter("filter")($scope.orgList, $scope.query);
  };

  //Send list of Firmwares
  $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
                    },function(error){
                      displayRestError.display(error);
                    });
  $scope.filteredList = $scope.orgList;

  //Delete a Firmware
  $scope.delete = function (firmware, size) {
    var modalInstance = $uibModal.open({
    templateUrl: 'partials/models/deleteModal.html',
    controller: 'FMdeleteController',
    size: size,
    resolve: {
      firmware: function () {return firmware;}
      }
    });

    modalInstance.result.then(function (selectedFirmware) {
      $scope.selected = selectedFirmware;
      FirmwaresFactory.deleteFirmware({ id: selectedFirmware.id },function(response) {
        alertService.success($filter('translate')('FIRMWARE.NOTIFY_DELETED', selectedFirmware));
        //Update display table
        $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
        },function(error){
          displayRestError.display(error);
        });
      $scope.filteredList = $scope.orgList;
      },function(error){
        displayRestError.display(error);
      });
    }),
    function () {
      //console.log('Modal dismissed at: ' + new Date());
    }
  };

  //Add a Firmware
  $scope.add = function (size) {
    var addModalInstance = $uibModal.open({
    templateUrl: 'partials/firmwares/firmwareAddModal.html',
    controller: 'FMaddController',
    size: size,
    resolve: {}
    });

    addModalInstance.result.then(function (newFirmware) {
      $scope.newFirmware = newFirmware;
      FirmwaresFactory.createFirmware($scope.newFirmware,function(response) {
        alertService.success($filter('translate')('FIRMWARE.NOTIFY_ADDED', newFirmware));
        //Update display table
        $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
        },function(error){
          displayRestError.display(error);
        });
      $scope.filteredList = $scope.orgList;
      },function(error){
        displayRestError.display(error);
      });
    }),
    function () {
      //console.log('Modal dismissed at: ' + new Date());
    }
  };

  //Update a Firmware
  $scope.update = function (firmware, size) {
    var editModalInstance = $uibModal.open({
    templateUrl: 'partials/firmwares/firmwareUpdateModal.html',
    controller: 'FMupdateController',
    size: size,
    resolve: {firmware: function () {return firmware;}}
    });

    editModalInstance.result.then(function (updateFirmware) {
      FirmwaresFactory.updateFirmware(updateFirmware,function(response) {
        alertService.success($filter('translate')('FIRMWARE.NOTIFY_UPDATED', updateFirmware));
        //Update display table
        $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
        },function(error){
          displayRestError.display(error);
        });
      $scope.filteredList = $scope.orgList;
      },function(error){
        displayRestError.display(error);
      });
    }),
    function () {
      //console.log('Modal dismissed at: ' + new Date());
    }
  };
});

//Firmwares Modal
myControllerModule.controller('FMdeleteController', function ($scope, $modalInstance, $sce, firmware, $filter) {
  $scope.firmware = firmware;
  $scope.header = $filter('translate')('FIRMWARE.TITLE_DELETE');
  $scope.deleteMsg = $filter('translate')('FIRMWARE.MESSAGE_DELETE', firmware);
  $scope.remove = function() {
    $modalInstance.close($scope.firmware);
  };
  $scope.cancel = function () { $modalInstance.dismiss('cancel'); }
});

myControllerModule.controller('FMaddController', function ($scope, $modalInstance, FirmwaresFactory) {
  $scope.firmware = {};
  $scope.firmware.type = {};
  $scope.firmware.version = {};
  $scope.header = "Add Firmware";
  $scope.types = FirmwaresFactory.getAllFirmwareTypes();
  $scope.versions = FirmwaresFactory.getAllFirmwareVersions();

  //Read File and put it in textarea
  $scope.displayFileContents = function(contents) {
        $scope.firmware.hexFileString = contents;
  };
  $scope.add = function() {$modalInstance.close($scope.firmware); }
  $scope.cancel = function () { $modalInstance.dismiss('cancel'); }
}).directive('onReadFile', function ($parse) {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, element, attrs) {
            element.bind('change', function(e) {

                var onFileReadFn = $parse(attrs.onReadFile);
                var reader = new FileReader();

                reader.onload = function() {
                    var fileContents = reader.result;
                    // invoke parsed function on scope
                    // special syntax for passing in data
                    // to named parameters
                    // in the parsed function
                    // we are providing a value for the property 'contents'
                    // in the scope we pass in to the function
                    scope.$apply(function() {
                        onFileReadFn(scope, {
                            'contents' : fileContents
                        });
                    });
                };
                reader.readAsText(element[0].files[0]);
            });
        }
    };
});

myControllerModule.controller('FMupdateController', function ($scope, $modalInstance, firmware, $filter) {
  $scope.firmware = firmware;
  $scope.header = $filter('translate')('FIRMWARE.TITLE_UPDATE',firmwareName);
  //Read File and put it in textarea
  $scope.displayFileContents = function(contents) {
        $scope.firmware.hexFileString = contents;
  };
  $scope.update = function() {$modalInstance.close($scope.firmware);}
  $scope.cancel = function () { $modalInstance.dismiss('cancel'); }
}).directive('onReadFile', function ($parse) {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, element, attrs) {
            element.bind('change', function(e) {

                var onFileReadFn = $parse(attrs.onReadFile);
                var reader = new FileReader();

                reader.onload = function() {
                    var fileContents = reader.result;
                    // invoke parsed function on scope
                    // special syntax for passing in data
                    // to named parameters
                    // in the parsed function
                    // we are providing a value for the property 'contents'
                    // in the scope we pass in to the function
                    scope.$apply(function() {
                        onFileReadFn(scope, {
                            'contents' : fileContents
                        });
                    });
                };
                reader.readAsText(element[0].files[0]);
            });
        }
    };
});
<commit_msg>fix in firmware.js<commit_after>/*
 * Copyright (C) 2015 Jeeva Kandasamy (jkandasa@gmail.com)
 *
 * 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.
 */
myControllerModule.controller('FirmwareController', function(alertService,
$scope, $filter, FirmwaresFactory, $location, $uibModal, displayRestError, about, $filter) {

  $scope.filteredList=[];
  $scope.orgList=[];
  $scope.config = {
    itemsPerPage: 10,
    maxPages:10,
    fillLastPage: false
  }

  //about, Timezone, etc.,
  $scope.about = about;

  //Filter
  $scope.updateFilteredList = function() {
    $scope.filteredList = $filter("filter")($scope.orgList, $scope.query);
  };

  //Send list of Firmwares
  $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
                    },function(error){
                      displayRestError.display(error);
                    });
  $scope.filteredList = $scope.orgList;

  //Delete a Firmware
  $scope.delete = function (firmware, size) {
    var modalInstance = $uibModal.open({
    templateUrl: 'partials/models/deleteModal.html',
    controller: 'FMdeleteController',
    size: size,
    resolve: {
      firmware: function () {return firmware;}
      }
    });

    modalInstance.result.then(function (selectedFirmware) {
      $scope.selected = selectedFirmware;
      FirmwaresFactory.deleteFirmware({ id: selectedFirmware.id },function(response) {
        alertService.success($filter('translate')('FIRMWARE.NOTIFY_DELETED', selectedFirmware));
        //Update display table
        $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
        },function(error){
          displayRestError.display(error);
        });
      $scope.filteredList = $scope.orgList;
      },function(error){
        displayRestError.display(error);
      });
    }),
    function () {
      //console.log('Modal dismissed at: ' + new Date());
    }
  };

  //Add a Firmware
  $scope.add = function (size) {
    var addModalInstance = $uibModal.open({
    templateUrl: 'partials/firmwares/firmwareAddModal.html',
    controller: 'FMaddController',
    size: size,
    resolve: {}
    });

    addModalInstance.result.then(function (newFirmware) {
      $scope.newFirmware = newFirmware;
      FirmwaresFactory.createFirmware($scope.newFirmware,function(response) {
        alertService.success($filter('translate')('FIRMWARE.NOTIFY_ADDED', newFirmware));
        //Update display table
        $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
        },function(error){
          displayRestError.display(error);
        });
      $scope.filteredList = $scope.orgList;
      },function(error){
        displayRestError.display(error);
      });
    }),
    function () {
      //console.log('Modal dismissed at: ' + new Date());
    }
  };

  //Update a Firmware
  $scope.update = function (firmware, size) {
    var editModalInstance = $uibModal.open({
    templateUrl: 'partials/firmwares/firmwareUpdateModal.html',
    controller: 'FMupdateController',
    size: size,
    resolve: {firmware: function () {return firmware;}}
    });

    editModalInstance.result.then(function (updateFirmware) {
      FirmwaresFactory.updateFirmware(updateFirmware,function(response) {
        alertService.success($filter('translate')('FIRMWARE.NOTIFY_UPDATED', updateFirmware));
        //Update display table
        $scope.orgList = FirmwaresFactory.getAllFirmwares(function(response) {
        },function(error){
          displayRestError.display(error);
        });
      $scope.filteredList = $scope.orgList;
      },function(error){
        displayRestError.display(error);
      });
    }),
    function () {
      //console.log('Modal dismissed at: ' + new Date());
    }
  };
});

//Firmwares Modal
myControllerModule.controller('FMdeleteController', function ($scope, $modalInstance, $sce, firmware, $filter) {
  $scope.firmware = firmware;
  $scope.header = $filter('translate')('FIRMWARE.TITLE_DELETE');
  $scope.deleteMsg = $filter('translate')('FIRMWARE.MESSAGE_DELETE', firmware);
  $scope.remove = function() {
    $modalInstance.close($scope.firmware);
  };
  $scope.cancel = function () { $modalInstance.dismiss('cancel'); }
});

myControllerModule.controller('FMaddController', function ($scope, $modalInstance, FirmwaresFactory) {
  $scope.firmware = {};
  $scope.firmware.type = {};
  $scope.firmware.version = {};
  $scope.header = "Add Firmware";
  $scope.types = FirmwaresFactory.getAllFirmwareTypes();
  $scope.versions = FirmwaresFactory.getAllFirmwareVersions();

  //Read File and put it in textarea
  $scope.displayFileContents = function(contents) {
        $scope.firmware.hexFileString = contents;
  };
  $scope.add = function() {$modalInstance.close($scope.firmware); }
  $scope.cancel = function () { $modalInstance.dismiss('cancel'); }
}).directive('onReadFile', function ($parse) {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, element, attrs) {
            element.bind('change', function(e) {

                var onFileReadFn = $parse(attrs.onReadFile);
                var reader = new FileReader();

                reader.onload = function() {
                    var fileContents = reader.result;
                    // invoke parsed function on scope
                    // special syntax for passing in data
                    // to named parameters
                    // in the parsed function
                    // we are providing a value for the property 'contents'
                    // in the scope we pass in to the function
                    scope.$apply(function() {
                        onFileReadFn(scope, {
                            'contents' : fileContents
                        });
                    });
                };
                reader.readAsText(element[0].files[0]);
            });
        }
    };
});

myControllerModule.controller('FMupdateController', function ($scope, $modalInstance, firmware, $filter) {
  $scope.firmware = firmware;
  $scope.header = $filter('translate')('FIRMWARE.TITLE_UPDATE',firmware);
  //Read File and put it in textarea
  $scope.displayFileContents = function(contents) {
        $scope.firmware.hexFileString = contents;
  };
  $scope.update = function() {$modalInstance.close($scope.firmware);}
  $scope.cancel = function () { $modalInstance.dismiss('cancel'); }
}).directive('onReadFile', function ($parse) {
    return {
        restrict: 'A',
        scope: false,
        link: function(scope, element, attrs) {
            element.bind('change', function(e) {

                var onFileReadFn = $parse(attrs.onReadFile);
                var reader = new FileReader();

                reader.onload = function() {
                    var fileContents = reader.result;
                    // invoke parsed function on scope
                    // special syntax for passing in data
                    // to named parameters
                    // in the parsed function
                    // we are providing a value for the property 'contents'
                    // in the scope we pass in to the function
                    scope.$apply(function() {
                        onFileReadFn(scope, {
                            'contents' : fileContents
                        });
                    });
                };
                reader.readAsText(element[0].files[0]);
            });
        }
    };
});
<|endoftext|>


<commit_before>exports.names = ['.commands', '!commands'];
exports.hidden = false;
exports.enabled = true;
exports.matchStart = false;
exports.cd_all = 15;
exports.cd_user = 30;
exports.cd_manager = 10;
exports.handler = function (data) {
    if (data.from.role > 2 || data.from.username == 'PAJLADA') {
        bot.sendChat('Commands: ' + _.compact(_.map(commands, function (command) {
            if (command.enabled && !command.hidden && _.first(command.names) != '.commands') {
                return _.first(command.names);
            }
        })).join(', '));
    }
};
<commit_msg>.commands now prints a link to a list of commands on github instead.<commit_after>exports.names = ['.commands', '!commands'];
exports.hidden = false;
exports.enabled = true;
exports.matchStart = false;
exports.cd_all = 15;
exports.cd_user = 30;
exports.cd_manager = 10;
exports.handler = function (data) {
    bot.sendChat('/me Commands: http://git.io/vTObv');
};
<|endoftext|>


<commit_before>'use strict';
const Generator = require('yeoman-generator');
const _ = require('lodash');
const yaml = require('js-yaml');
const fs = require('fs');
const mkdirp = require('mkdirp');
const path = require('path');
const glob = require('glob');

module.exports = class extends Generator {
  initializing() {
    this.option('enable-cors');

    this.currentDir = path.basename(process.cwd());
    this.props = {};

    // Find conf file
    let locate = () => {
      let files = glob.sync('{./,../,../../,../../../}/serverless.yml', {
        nodir: true,
        realpath: true
      });

      if (!files) {
        this.log.error('serverless.yml NOT FOUND, for overwrite go inside a project.');
      }

      return files ? files[0] : './';
    };

    this.configFile = locate();
  }

  prompting() {
    return this.prompt([
      {
        type: 'input',
        name: 'name',
        message: 'Resource name',
        filter: _.kebabCase.toLower,
        validate: value => {
          return !_.isEmpty(value);
        }
      },
      {
        type: 'list',
        name: 'nested',
        message: 'What kind of design?',
        choices: answers => {
          return [
            `Normal "/${answers.name}"`,
            `By id "/${answers.name}/{id}"`,
            `Nested "/${this.currentDir}/{id}/${answers.name}"`
          ];
        }
      },
      {
        type: 'list',
        name: 'method',
        message: 'Which HTTP method?',
        choices: answers => {
          let disabled = answers.nested.match(/By id/);
          return [
            {name: 'GET', disabled: false},
            {name: 'POST', disabled},
            {name: 'PUT', disabled},
            {name: 'DELETE', disabled}
          ];
        },
        filter: _.toLower
      },
      {
        type: 'input',
        name: 'description',
        message: 'Your function description',
        default: answers => {
          let name = (answers.nested.match(/Nested/)) ? `${this.currentDir} ${answers.name}` : answers.name;
          let byId = (answers.nested.match(/By id/)) ? ' by id' : '';
          return `${_.capitalize(answers.method)} ${name}${byId}`;
        }
      }
    ]).then(answers => {
      this.props = answers;
    });
  }

  writing() {
    let method = `${this.props.method}`;
    let handler = 'functions/';
    let filename = method;
    let lambda = `${method}-`;

    // Build the configuration file
    if (this.props.nested.match(/By id/)) {
      // By id
      filename = 'id';
      handler += this.props.name;
    } else if (this.props.nested.match(/Nested/)) {
      // Nested
      handler = this.props.name;
      lambda += this.currentDir;
    } else {
      handler += this.props.name;
    }

    // Final name of lambda function & handler
    let dest = handler;
    lambda += `-${this.props.name}`;
    handler += `${this.props.name}/${filename}.handler`;

    try {
      // Start overwrite config file
      let serverless = yaml.safeLoad(fs.readFileSync(this.configFile, 'utf8'));
      let http = {
        method: _.toUpper(this.props.method),
        path: this.props.name
      };

      // If enable-cors
      if (this.options.enableCors) {
        http.cors = true;
      }

      serverless.functions[lambda] = {
        name: `${serverless.service}-${lambda}`,
        description: this.props.description,
        handler,
        events: [
          {
            http
          }
        ]
      };

      fs.writeFile(
        this.configFile,
        yaml.safeDump(serverless),
        () => this.log.ok(`Function "${lambda}" generated successfully`)
      );
    } catch (ex) {
      this.log.error('Could not read/write serverless.yml file.');
    }

    // Create function handler
    mkdirp(dest);
    this.fs.copyTpl(
      this.templatePath('handler.js'),
      this.destinationPath(`${dest}/${filename}.js`),
      {
        lambda,
        method,
        path: (this.props.nested.match(/Nested/)) ? '../../..' : '../..'
      }
    );
  }
};
<commit_msg>hotfix - incorrect handler name generated in config file<commit_after>'use strict';
const Generator = require('yeoman-generator');
const _ = require('lodash');
const yaml = require('js-yaml');
const fs = require('fs');
const mkdirp = require('mkdirp');
const path = require('path');
const glob = require('glob');

module.exports = class extends Generator {
  initializing() {
    this.option('enable-cors');

    this.currentDir = path.basename(process.cwd());
    this.props = {};

    // Find conf file
    let locate = () => {
      let files = glob.sync('{./,../,../../,../../../}/serverless.yml', {
        nodir: true,
        realpath: true
      });

      if (!files) {
        this.log.error('serverless.yml NOT FOUND, for overwrite go inside a project.');
      }

      return files ? files[0] : './';
    };

    this.configFile = locate();
  }

  prompting() {
    return this.prompt([
      {
        type: 'input',
        name: 'name',
        message: 'Resource name',
        filter: _.kebabCase.toLower,
        validate: value => {
          return !_.isEmpty(value);
        }
      },
      {
        type: 'list',
        name: 'nested',
        message: 'What kind of design?',
        choices: answers => {
          return [
            `Normal "/${answers.name}"`,
            `By id "/${answers.name}/{id}"`,
            `Nested "/${this.currentDir}/{id}/${answers.name}"`
          ];
        }
      },
      {
        type: 'list',
        name: 'method',
        message: 'Which HTTP method?',
        choices: answers => {
          let disabled = answers.nested.match(/By id/);
          return [
            {name: 'GET', disabled: false},
            {name: 'POST', disabled},
            {name: 'PUT', disabled},
            {name: 'DELETE', disabled}
          ];
        },
        filter: _.toLower
      },
      {
        type: 'input',
        name: 'description',
        message: 'Your function description',
        default: answers => {
          let name = (answers.nested.match(/Nested/)) ? `${this.currentDir} ${answers.name}` : answers.name;
          let byId = (answers.nested.match(/By id/)) ? ' by id' : '';
          return `${_.capitalize(answers.method)} ${name}${byId}`;
        }
      }
    ]).then(answers => {
      this.props = answers;
    });
  }

  writing() {
    let method = `${this.props.method}`;
    let handler = 'functions/';
    let filename = method;
    let lambda = `${method}`;
    let dest = handler;

    // Build the configuration file
    if (this.props.nested.match(/By id/)) {
      // By id
      filename = 'id';
      dest += this.props.name;
    } else if (this.props.nested.match(/Nested/)) {
      // Nested
      lambda += `-${this.currentDir}`;
      handler += `${this.currentDir}/`;
      dest = this.props.name;
    } else {
      dest += this.props.name;
    }

    // Final name of lambda function & handler
    handler += this.props.name;
    lambda += `-${this.props.name}`;
    if (this.props.nested.match(/By id/)) {
      lambda += '-id';
    }
    handler += `/${filename}.handler`;

    try {
      // Start overwrite config file
      let serverless = yaml.safeLoad(fs.readFileSync(this.configFile, 'utf8'));
      let http = {
        method: _.toUpper(this.props.method),
        path: this.props.name
      };

      // If enable-cors
      if (this.options.enableCors) {
        http.cors = true;
      }

      serverless.functions[lambda] = {
        name: `${serverless.service}-${lambda}`,
        description: this.props.description,
        handler,
        events: [
          {
            http
          }
        ]
      };

      fs.writeFile(
        this.configFile,
        yaml.safeDump(serverless),
        () => this.log.ok(`Function "${lambda}" generated successfully`)
      );
    } catch (ex) {
      this.log.error('Could not read/write serverless.yml file.');
    }

    // Create function handler
    mkdirp(dest);
    this.fs.copyTpl(
      this.templatePath('handler.js'),
      this.destinationPath(`${dest}/${filename}.js`),
      {
        lambda,
        method,
        path: (this.props.nested.match(/Nested/)) ? '../../..' : '../..'
      }
    );
  }
};
<|endoftext|>


<commit_before>module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
  },
};
<commit_msg>chore: allow upper-case subject in commit messages<commit_after>module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "subject-case": [1, "always"],
  },
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
var argv = require('minimist')(process.argv.slice(2))
var _ = require('lodash')
var async = require('async')

var tag = require('./mp3tag')
var File = require('./file')

var out = require('./output')

var parser = require('./cli/taskParser')

var options = {
  verbose: false
}

/** Command line interface to the mp3tag library
 *  Primary options:
 *    --in [filename]              The source file to read. If not set, an empty file will be genrated.
 *
 *    --write [filename]           Specifies that the changes, made to the tags should be written into filename. If filename is left out, the input file is used.
 *
 *  Other options:
 *    --export-cover [destination]   Export the cover image (if any) to destination (if given, default is "./cover.[mimetype]")
 *    --v                            Output debug info
 *    --show-data                    Prints out the contained tag data
 *    --set-album [name]             Sets/Unsets the album
 *    --set-track [trackNr]          Sets/Unsets the track number
 */

var sourceFile

//Options
parser.defineTask('v', {
  type:'option',
  help_text: 'Verbose output'
}, function(cb) {
  options.verbose = true
  out.config().debug = true //Enable debug logger
  cb()
})

 //Sources
parser.defineTask('in', {
  max_args:1,
  type:'source',
  arg_display:'[filename]',
  help_text: 'The source file to read. If not set, an empty file will be generated.'
  }, function(cb) {
    sourceFile = this.args[0] //Set the source from which the file has been read
    getHeader(sourceFile, cb)
})

//Read operations
parser.defineTask('export-cover', {
  max_args:1,
  type:'read',
  arg_display:'[destination]',
  help_text: 'Export the cover image (if any) to destination (if given, default is "./cover.[mimetype]")'
}, function(tagData, cb) {
  exportCover(tagData, this.args[0], function(err, res) {
    if (err)
      return cb("Cover export failed: " + err)

    out.info("Exported cover picture to: " + res.filename + " (" + res.bytes + " bytes written)")
    cb()
  })
})

var exportProperties = {} //The properties to export
parser.defineTask('export-title', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the file\'s title in the export-format task or an empty string if none is set'
}, function(tagData, cb) {
  var property = this.args[0] || 'title'
  var buffer = tagData.getFrameBuffer('TIT2')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-publisher', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the file\'s publisher in the export-format task or an empty string if none is set'
}, function(tagData, cb) {
  var property = this.args[0] || 'publisher'
  var buffer = tagData.getFrameBuffer('TPUB')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-format', {
  max_args: 1,
  type: 'read',
  arg_display: '[format]',
  help_text: 'actually exports the properties, which were exported via export-* tasks. Supported formats are: json,...'
}, function(tagData, cb) {
  var format = this.args[0] || 'json'

  if (format === 'json') {
    console.log(JSON.stringify(exportProperties))
  }
  //TODO support other formats aswell

  exportProperties = {}
  cb()
})

parser.defineTask('show-data', {
  type:'read',
  help_text:'Prints out the contained tag data'
}, function(tagData, cb) {
  showData(tagData)
  cb()
})

//Write operations
parser.defineTask('set-album', {
  max_args: 1,
  type: 'write',
  arg_display: '[name]',
  help_text: 'Sets/Unsets album name'
}, function(tagData, cb) {
  setFrameString(tagData, 'TALB', this.args[0])
  cb()
})

parser.defineTask('set-artist', {
  max_args: 1,
  type: 'write',
  arg_display: '[name]',
  help_text: 'Sets/Unsets artist name'
}, function(tagData, cb) {
  setFrameString(tagData, 'TPE1', this.args[0])
  cb()
})

parser.defineTask('set-band', {
  max_args: 1,
  type: 'write',
  arg_display: '[name]',
  help_text: 'Sets/Unsets band name'
}, function(tagData, cb) {
  setFrameString(tagData, 'TPE2', this.args[0])
  cb()
})

parser.defineTask('set-title', {
  max_args: 1,
  type: 'write',
  arg_display: '[title]',
  help_text: 'Sets/Unsets the title'
}, function(tagData, cb) {
  setFrameString(tagData, 'TIT2', this.args[0])
  cb()
})

parser.defineTask('set-track', {
  max_args: 1,
  type: 'write',
  arg_display: '[trackNr]',
  help_text: 'Sets/Unsets the track number'
}, function(tagData, cb) {
  setFrameString(tagData, 'TRCK', this.args[0])
  cb()
})

parser.defineTask('set-publisher', {
  max_args: 1,
  type: 'write',
  arg_display: '[publisher]',
  help_text: 'Sets/Unsets the publisher'
}, function(tagData, cb) {
  setFrameString(tagData, 'TPUB', this.args[0])
  cb()
})

///----------------------
//TODO define other tasks
///----------------------

//Sinks
parser.defineTask('write', {
  max_args:1,
  type:'sink',
  arg_display:'[filename]',
  help_text:'Specifies that the changes, made to the tags should be written into filename. If filename is left out, the input file is used.' +
  'The filename may not be left out if not filename was specified with --in'
}, function(tagData, cb) {
  var target = this.args[0] || sourceFile
  if (!target)
    return cb("Failed to write changes into file. No file passed to --in or --write task.")

  out.debug("Writing mp3 to '" + target + "'")
  tagData.writeToFile(target, function(err) {
    if (err)
      return cb("Write to file failed: " + err)

    out.info("Successfully written '" + target + "'")
    return cb()
  })
})

try {
  parser.run(process.argv.slice(2)) //TODO handle errors in callback
} catch (err) { //Parsing might have failed
  out.error("Startup error: " + err.message)
  console.error(err)
  process.exit(1)
}

/** Simply prints all known data about the audio
 */
function showData(tagData) {
  console.dir(tagData)
  console.log("\n")

  function printOut(id, asName, decodefn) {
    var decfn = decodefn || tag.decodeString
    var buffer = tagData.getFrameBuffer(id)

    var result = buffer ? decfn(tagData.getFrameBuffer(id)) : ""
    if (typeof result !== 'string')
      result = JSON.stringify(result, null, 2)

    console.log(asName + ": " + result)
  }

  //TODO define methods to read/write these properties

  printOut('TIT2', "Title")
  printOut('TRCK', "Track")
  printOut('TALB', "Album")
  printOut('COMM', "Comment", tag.decodeComment)
  printOut('TYER', "Year")
  printOut('TPE1', "Lead performer")
  printOut('TPE2', "Band")
  printOut('POPM', "Popularimeter", tag.decodePopularity)
  printOut('APIC', "Picture", function(buffer) {
    var res = tag.decodePicture(buffer)
    res.pictureData = res.pictureData.inspect()
    return res
  })

}

/** This function returns an mp3tag header based on the source.
 *  If the passed source is not a string, then an empty header will be returned.
 *
 * @param source the mp3 file to read the header from
 * @param callback(err,tagData) the callback which will be called upon completion
 */
function getHeader(source, callback) {
  if (typeof source == "string") {
    out.debug("Loading audio file form '" + source + "'")
    tag.readHeader(source, callback)
  } else {
    out.debug("No source passed, generating empty audio file")
    process.nextTick(function() { callback(null, tag.newHeader()) })
  }
}

/** This function is used to export the cover picture from the mp3 file.
 *
 * @param tagData the TagData object which contains the picture
 * @param destination a filepath or undefined, if default filepath should be used
 * @param callback(err,{bytes,filename}) will be called upon error or completion.
 *                          bytes will be the number of bytes, written into the file.
 */
function exportCover(tagData, destination, callback) {
  var frameBuffer = tagData.getFrameBuffer('APIC')
  if (!frameBuffer) {
    return callback("File has no picture frame")
  }
  var pic = tag.decodePicture(frameBuffer)
  var filename = destination || ("cover." + pic.mimeType.split('/')[1])
  File.open(filename, "w", function(err, file) {
    if (err)
      return callback(err)

    pic.pictureData.writeInto(file, function(err,bytes) {
      if (err)
        return callback(err)

      file.close()
      process.nextTick(function() { callback(null, {bytes:bytes, filename:filename}) })
    })
  })
}

/** Generic string writing utiltiy for string properties
 */
function setFrameString(tagData, frameID, value) {
  if (typeof(value) !== 'string') {
    tagData.removeFrame(frameID) //Just remove the frame
  } else {
    tagData.setFrameBuffer(frameID, tag.encodeString(value))
  }
}<commit_msg>Added a few more export commands<commit_after>#!/usr/bin/env node
var argv = require('minimist')(process.argv.slice(2))
var _ = require('lodash')
var async = require('async')

var tag = require('./mp3tag')
var File = require('./file')

var out = require('./output')

var parser = require('./cli/taskParser')

var options = {
  verbose: false
}

/** Command line interface to the mp3tag library
 *  Primary options:
 *    --in [filename]              The source file to read. If not set, an empty file will be genrated.
 *
 *    --write [filename]           Specifies that the changes, made to the tags should be written into filename. If filename is left out, the input file is used.
 *
 *  Other options:
 *    --export-cover [destination]   Export the cover image (if any) to destination (if given, default is "./cover.[mimetype]")
 *    --v                            Output debug info
 *    --show-data                    Prints out the contained tag data
 *    --set-album [name]             Sets/Unsets the album
 *    --set-track [trackNr]          Sets/Unsets the track number
 */

var sourceFile

//Options
parser.defineTask('v', {
  type:'option',
  help_text: 'Verbose output'
}, function(cb) {
  options.verbose = true
  out.config().debug = true //Enable debug logger
  cb()
})

 //Sources
parser.defineTask('in', {
  max_args:1,
  type:'source',
  arg_display:'[filename]',
  help_text: 'The source file to read. If not set, an empty file will be generated.'
  }, function(cb) {
    sourceFile = this.args[0] //Set the source from which the file has been read
    getHeader(sourceFile, cb)
})

//Read operations
var exportProperties = {} //The collected properties to export
parser.defineTask('export-album', {
  max_args:1,
  type:'read',
  arg_display: '[property name]',
  help_text: 'exports the album name'
}, function(tagData, cb) {
  var property = this.args[0] || 'album'
  var buffer = tagData.getFrameBuffer('TALB')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
});

parser.defineTask('export-artist', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the artist'
}, function(tagData, cb) {
  var property = this.args[0] || 'artist'
  var buffer = tagData.getFrameBuffer('TPE1')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-band', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the band name'
}, function(tagData, cb) {
  var property = this.args[0] || 'band'
  var buffer = tagData.getFrameBuffer('TPE2')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-cover', {
  max_args:1,
  type:'read',
  arg_display:'[destination]',
  help_text: 'Export the cover image (if any) to destination (if given, default is "./cover.[mimetype]")'
}, function(tagData, cb) {
  exportCover(tagData, this.args[0], function(err, res) {
    if (err)
      return cb("Cover export failed: " + err)

    out.info("Exported cover picture to: " + res.filename + " (" + res.bytes + " bytes written)")
    cb()
  })
})

parser.defineTask('export-title', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the title'
}, function(tagData, cb) {
  var property = this.args[0] || 'title'
  var buffer = tagData.getFrameBuffer('TIT2')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-track', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the track'
}, function(tagData, cb) {
  var property = this.args[0] || 'track'
  var buffer = tagData.getFrameBuffer('TRCK')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-publisher', {
  max_args: 1,
  type: 'read',
  arg_display: '[property name]',
  help_text: 'exports the publisher'
}, function(tagData, cb) {
  var property = this.args[0] || 'publisher'
  var buffer = tagData.getFrameBuffer('TPUB')
  exportProperties[property] = buffer ? tag.decodeString(buffer) : ''
  cb()
})

parser.defineTask('export-format', {
  max_args: 1,
  type: 'read',
  arg_display: '[format]',
  help_text: 'actually exports the properties, which were exported via export-* tasks. Supported formats are: json,...'
}, function(tagData, cb) {
  var format = this.args[0] || 'json'

  if (format === 'json') {
    console.log(JSON.stringify(exportProperties))
  }
  //TODO support other formats aswell

  exportProperties = {}
  cb()
})

parser.defineTask('show-data', {
  type:'read',
  help_text:'Prints out the contained tag data'
}, function(tagData, cb) {
  showData(tagData)
  cb()
})

//Write operations
parser.defineTask('set-album', {
  max_args: 1,
  type: 'write',
  arg_display: '[name]',
  help_text: 'Sets/Unsets album name'
}, function(tagData, cb) {
  setFrameString(tagData, 'TALB', this.args[0])
  cb()
})

parser.defineTask('set-artist', {
  max_args: 1,
  type: 'write',
  arg_display: '[name]',
  help_text: 'Sets/Unsets artist name'
}, function(tagData, cb) {
  setFrameString(tagData, 'TPE1', this.args[0])
  cb()
})

parser.defineTask('set-band', {
  max_args: 1,
  type: 'write',
  arg_display: '[name]',
  help_text: 'Sets/Unsets band name'
}, function(tagData, cb) {
  setFrameString(tagData, 'TPE2', this.args[0])
  cb()
})

parser.defineTask('set-title', {
  max_args: 1,
  type: 'write',
  arg_display: '[title]',
  help_text: 'Sets/Unsets the title'
}, function(tagData, cb) {
  setFrameString(tagData, 'TIT2', this.args[0])
  cb()
})

parser.defineTask('set-track', {
  max_args: 1,
  type: 'write',
  arg_display: '[trackNr]',
  help_text: 'Sets/Unsets the track number'
}, function(tagData, cb) {
  setFrameString(tagData, 'TRCK', this.args[0])
  cb()
})

parser.defineTask('set-publisher', {
  max_args: 1,
  type: 'write',
  arg_display: '[publisher]',
  help_text: 'Sets/Unsets the publisher'
}, function(tagData, cb) {
  setFrameString(tagData, 'TPUB', this.args[0])
  cb()
})

///----------------------
//TODO define other tasks
///----------------------

//Sinks
parser.defineTask('write', {
  max_args:1,
  type:'sink',
  arg_display:'[filename]',
  help_text:'Specifies that the changes, made to the tags should be written into filename. If filename is left out, the input file is used.' +
  'The filename may not be left out if not filename was specified with --in'
}, function(tagData, cb) {
  var target = this.args[0] || sourceFile
  if (!target)
    return cb("Failed to write changes into file. No file passed to --in or --write task.")

  out.debug("Writing mp3 to '" + target + "'")
  tagData.writeToFile(target, function(err) {
    if (err)
      return cb("Write to file failed: " + err)

    out.info("Successfully written '" + target + "'")
    return cb()
  })
})

try {
  parser.run(process.argv.slice(2)) //TODO handle errors in callback
} catch (err) { //Parsing might have failed
  out.error("Startup error: " + err.message)
  console.error(err)
  process.exit(1)
}

/** Simply prints all known data about the audio
 */
function showData(tagData) {
  console.dir(tagData)
  console.log("\n")

  function printOut(id, asName, decodefn) {
    var decfn = decodefn || tag.decodeString
    var buffer = tagData.getFrameBuffer(id)

    var result = buffer ? decfn(tagData.getFrameBuffer(id)) : ""
    if (typeof result !== 'string')
      result = JSON.stringify(result, null, 2)

    console.log(asName + ": " + result)
  }

  //TODO define methods to read/write these properties

  printOut('TIT2', "Title")
  printOut('TRCK', "Track")
  printOut('TALB', "Album")
  printOut('COMM', "Comment", tag.decodeComment)
  printOut('TYER', "Year")
  printOut('TPE1', "Lead performer")
  printOut('TPE2', "Band")
  printOut('POPM', "Popularimeter", tag.decodePopularity)
  printOut('APIC', "Picture", function(buffer) {
    var res = tag.decodePicture(buffer)
    res.pictureData = res.pictureData.inspect()
    return res
  })

}

/** This function returns an mp3tag header based on the source.
 *  If the passed source is not a string, then an empty header will be returned.
 *
 * @param source the mp3 file to read the header from
 * @param callback(err,tagData) the callback which will be called upon completion
 */
function getHeader(source, callback) {
  if (typeof source == "string") {
    out.debug("Loading audio file form '" + source + "'")
    tag.readHeader(source, callback)
  } else {
    out.debug("No source passed, generating empty audio file")
    process.nextTick(function() { callback(null, tag.newHeader()) })
  }
}

/** This function is used to export the cover picture from the mp3 file.
 *
 * @param tagData the TagData object which contains the picture
 * @param destination a filepath or undefined, if default filepath should be used
 * @param callback(err,{bytes,filename}) will be called upon error or completion.
 *                          bytes will be the number of bytes, written into the file.
 */
function exportCover(tagData, destination, callback) {
  var frameBuffer = tagData.getFrameBuffer('APIC')
  if (!frameBuffer) {
    return callback("File has no picture frame")
  }
  var pic = tag.decodePicture(frameBuffer)
  var filename = destination || ("cover." + pic.mimeType.split('/')[1])
  File.open(filename, "w", function(err, file) {
    if (err)
      return callback(err)

    pic.pictureData.writeInto(file, function(err,bytes) {
      if (err)
        return callback(err)

      file.close()
      process.nextTick(function() { callback(null, {bytes:bytes, filename:filename}) })
    })
  })
}

/** Generic string writing utiltiy for string properties
 */
function setFrameString(tagData, frameID, value) {
  if (typeof(value) !== 'string') {
    tagData.removeFrame(frameID) //Just remove the frame
  } else {
    tagData.setFrameBuffer(frameID, tag.encodeString(value))
  }
}<|endoftext|>


<commit_before>module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    // Set a saner max-length.
    "header-max-length": [2, "always", 200],

    // Disable requirement to not allow a full stop.
    "subject-full-stop": [0, "never", "."],

    // Disable restrictions on subject case.
    "subject-case": [0, "never", []]
  }
};
<commit_msg>ci: relaxes commitlint rules<commit_after>module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    // Set a saner max-lengths.
    "header-max-length": [2, "always", 200],
    "body-max-line-length": [2, "always", 300],

    // Disable requirement to not allow a full stop.
    "subject-full-stop": [0, "never", "."],

    // Disable restrictions on subject case.
    "subject-case": [0, "never", []],
  },
};
<|endoftext|>


<commit_before>
function formatSize(bytes) {
	if(typeof bytes === "undefined" || bytes == "") {
		return "-";
	} else if (bytes < 1024) {
		return bytes + "  B";
	} else if (bytes < 1024*1024) {
		return (bytes/ 1024.0).toFixed(0)  + " KB";
	} else if (bytes < 1024*1024*1024) {
		return (bytes/1024.0/1024.0).toFixed(1)  + " MB";
	} else {
		return (bytes/1024.0/1024.0/1024.0).toFixed(2) + " GB";
	}
}

send("/cgi-bin/home", { }, function(data) {
	var obj = fromUCI(data).misc.data;
	for(var key in obj) {
		var value = obj[key];

		if(key == 'stype') {
			continue;
		}

		if(/_bytes$/.test(key)) {
			value = formatSize(value);
		}

		setText(key, value);
	}
});

addHelpText($("system"), "Eine \xdcbersicht \xfcber den Router.");
addHelpText($("public"), "Das \xf6ffentliche Freifunknetz..");
addHelpText($("private"), "Das private Netz bzw. LAN.");
addHelpText($("wan"), "Das Netz \xfcber dass das Internet erreicht wird.");
addHelpText($("software"), "Einige installierte Softwareversionen.");
addHelpText($("public_user_count"), "Die Anzahl der Nutzer an diesem Router in den letzten zwei Stunden.");
addHelpText($("private_user_count"), "Die Anzahl der Nutzer an diesem Router in den letzten zwei Stunden.");
<commit_msg>fix MB/GB/TB units<commit_after>
function formatSize(bytes) {
	if(typeof bytes === "undefined" || bytes == "") {
		return "-";
	} else if (bytes < 1000) {
		return bytes + "  B";
	} else if (bytes < 1000*1000) {
		return (bytes/ 1000.0).toFixed(0)  + " KB";
	} else if (bytes < 1000*1000*1000) {
		return (bytes/1000.0/1000.0).toFixed(1)  + " MB";
	} else {
		return (bytes/1000.0/1000.0/1000.0).toFixed(2) + " GB";
	}
}

send("/cgi-bin/home", { }, function(data) {
	var obj = fromUCI(data).misc.data;
	for(var key in obj) {
		var value = obj[key];

		if(key == 'stype') {
			continue;
		}

		if(/_bytes$/.test(key)) {
			value = formatSize(value);
		}

		setText(key, value);
	}
});

addHelpText($("system"), "Eine \xdcbersicht \xfcber den Router.");
addHelpText($("public"), "Das \xf6ffentliche Freifunknetz..");
addHelpText($("private"), "Das private Netz bzw. LAN.");
addHelpText($("wan"), "Das Netz \xfcber dass das Internet erreicht wird.");
addHelpText($("software"), "Einige installierte Softwareversionen.");
addHelpText($("public_user_count"), "Die Anzahl der Nutzer an diesem Router in den letzten zwei Stunden.");
addHelpText($("private_user_count"), "Die Anzahl der Nutzer an diesem Router in den letzten zwei Stunden.");
<|endoftext|>


<commit_before>// ==UserScript==
// @name				Geocaching Map Enhancements
// @version			0.7.1
// @author			JRI
// @oujs:author JRI
// @namespace		inge.org.uk/userscripts
// @description Adds Ordnance Survey maps and grid reference search to Geocaching.com, along with several other enhancements.
// @include			http://www.geocaching.com/*
// @include			https://www.geocaching.com/*
// @license			MIT License; http://www.opensource.org/licenses/mit-license.php
// @copyright		2011-14, James Inge (http://geo.inge.org.uk/)
// @attribution GeoNames (http://www.geonames.org/)
// @attribution Chris Veness (http://www.movable-type.co.uk/scripts/latlong-gridref.html)
// @grant				GM_xmlhttpRequest
// @grant				GM_log
// @icon				https://raw.githubusercontent.com/JRInge/userscripts/master/GeocachingMapEnhancements/GeocachingMap48.png
// @icon64			https://raw.githubusercontent.com/JRInge/userscripts/master/GeocachingMapEnhancements/GeocachingMap64.png
// @updateURL		http://geo.inge.org.uk/userscripts/GeocachingMapEnhancements.meta.js
// @downloadURL https://openuserjs.org/install/JRI/Geocaching_Map_Enhancements.user.js
// ==/UserScript==
<commit_msg>Version 0.7.2<commit_after>// ==UserScript==
// @name        Geocaching Map Enhancements
// @version     0.7.2
// @author      JRI
// @oujs:author JRI
// @namespace   inge.org.uk/userscripts
// @description Adds Ordnance Survey maps and grid reference search to Geocaching.com, along with several other enhancements.
// @include     http://www.geocaching.com/*
// @include     https://www.geocaching.com/*
// @license     MIT License; http://www.opensource.org/licenses/mit-license.php
// @copyright   2011-14, James Inge (http://geo.inge.org.uk/)
// @attribution GeoNames (http://www.geonames.org/)
// @attribution Postcodes.io (http://postcodes.io/)
// @attribution Chris Veness (http://www.movable-type.co.uk/scripts/latlong-gridref.html)
// @grant       GM_xmlhttpRequest
// @grant       GM_log
// @icon        https://raw.githubusercontent.com/JRInge/userscripts/master/GeocachingMapEnhancements/GeocachingMap48.png
// @icon64      https://raw.githubusercontent.com/JRInge/userscripts/master/GeocachingMapEnhancements/GeocachingMap64.png
// @updateURL   http://geo.inge.org.uk/userscripts/GeocachingMapEnhancements.meta.js
// @downloadURL https://openuserjs.org/install/JRI/Geocaching_Map_Enhancements.user.js
// ==/UserScript==
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'

import Packery from 'packery'
import imagesloaded from 'imagesloaded'
import Draggabilly from 'draggabilly'

const refName = 'packeryContainer'

class PackeryComponent extends Component{

  constructor(props) {
    super(props)
      this.packery = false
      this.domChildren = []
      this.displayName = 'PackeryComponent'

      //bind this to all non-react functions
      //if not familiar, see https://github.com/goatslacker/alt/issues/283
      this.initializePackery = this.initializePackery.bind(this)
      this.makeEachDraggable = this.makeEachDraggable.bind(this)
      this.imagesLoaded = this.imagesLoaded.bind(this)
      this.performLayout = this.performLayout.bind(this)
  }

  initializePackery = (force) => {
    this.packery = new Packery(
        this.refs[refName],
        this.props.options
        )

      this.packery.getItemElements().forEach( this.makeEachDraggable )
      this.domChildren = this.getNewDomChildren()
  }

  getNewDomChildren = () => {
    const node = this.refs[refName]
      const children = this.props.options.itemSelector ? node.querySelectorAll(this.props.options.itemSelector) : node.children
      return Array.prototype.slice.call(children)
  }

  makeEachDraggable( itemElem ) {
    // make element draggable with Draggabilly
    const draggie = new Draggabilly( itemElem )
      // bind Draggabilly events to Packery
      this.packery.bindDraggabillyEvents( draggie )
  }

  diffDomChildren = () => {
    const oldChildren = this.domChildren.filter((element) => {
      /*
       * take only elements attached to DOM
       * (aka the parent is the packery container, not null)
       */
      return !!element.parentNode
    })

    const newChildren = this.getNewDomChildren()

      const removed = oldChildren.filter((oldChild) => {
        return !~newChildren.indexOf(oldChild)
      })

    const domDiff = newChildren.filter((newChild) => {
      return !~oldChildren.indexOf(newChild)
    })

    let beginningIndex = 0

      // get everything added to the beginning of the DOMNode list
      const prepended = domDiff.filter((newChild, i) => {
        const prepend = (beginningIndex === newChildren.indexOf(newChild))

          if (prepend) {
            // increase the index
            beginningIndex++
          }

        return prepend
      })

    // we assume that everything else is appended
    const appended = domDiff.filter((el) => {
      return prepended.indexOf(el) === -1
    })

    this.domChildren = newChildren

      return {
        old: oldChildren,
        new: newChildren,
        removed: removed,
        appended: appended,
        prepended: prepended
      }
  }

  performLayout = () => {
    const diff = this.diffDomChildren()

      if (diff.removed.length > 0) {
        this.packery.remove(diff.removed)
      }

    if (diff.appended.length > 0) {
      this.packery.appended(diff.appended)
        diff.appended.forEach( this.makeEachDraggable )
    }

    if (diff.prepended.length > 0) {
      this.packery.prepended(diff.prepended)
        diff.prepended.forEach( this.makeEachDraggable )
    }
    this.packery.reloadItems()
      this.packery.layout()
  }

  imagesLoaded = () => {
    if (this.props.disableImagesLoaded) return

      imagesloaded(
          this.refs[refName],
          (instance) => {
            this.packery.layout()
          }
          )
  }

  componentDidMount = () => {
    this.initializePackery()
      this.imagesLoaded()
  }

  componentDidUpdate = () => {
    this.performLayout()
      this.imagesLoaded()
  }

  componentWillReceiveProps = () => {
    this._timer = setTimeout(() => {
      this.packery.reloadItems()
        this.packery.layout()
    })
    }

    componentWillUnmount = () => {
      clearTimeout(this._timer)
    }

    render = () => {
      return React.createElement(this.props.elementType, {
        className: this.props.className,
        ref: refName
      }, this.props.children)
    }
    }

    PackeryComponent.propTypes = {
      disableImagesLoaded: React.PropTypes.bool,
      options: React.PropTypes.object
    }

PackeryComponent.defaultProps = {
  disableImagesLoaded: false,
  options: {},
  className: '',
  elementType: 'div'
}

export default PackeryComponent
<commit_msg>Don't Drag Me Down<commit_after>import React, { Component, PropTypes } from 'react'

import Packery from 'packery'
import imagesloaded from 'imagesloaded'
import Draggabilly from 'draggabilly'

const refName = 'packeryContainer'

class PackeryComponent extends Component{

  constructor(props) {
    super(props)
    this.packery = false
    this.domChildren = []
    this.displayName = 'PackeryComponent'

    //bind this to all non-react functions
    //if not familiar, see https://github.com/goatslacker/alt/issues/283
    this.initializePackery = this.initializePackery.bind(this)
    this.makeEachDraggable = this.makeEachDraggable.bind(this)
    this.imagesLoaded = this.imagesLoaded.bind(this)
    this.performLayout = this.performLayout.bind(this)
  }

  initializePackery = (force) => {
    this.packery = new Packery(
        this.refs[refName],
        this.props.options
    )

    //this.packery.getItemElements().forEach( this.makeEachDraggable )
    this.domChildren = this.getNewDomChildren()
  }

  getNewDomChildren = () => {
    const node = this.refs[refName]
    const children = this.props.options.itemSelector ? node.querySelectorAll(this.props.options.itemSelector) : node.children
    return Array.prototype.slice.call(children)
  }

  makeEachDraggable( itemElem ) {
    // make element draggable with Draggabilly
    const draggie = new Draggabilly( itemElem )
    // bind Draggabilly events to Packery
    this.packery.bindDraggabillyEvents( draggie )
  }

  diffDomChildren = () => {
    const oldChildren = this.domChildren.filter((element) => {
        /*
         * take only elements attached to DOM
         * (aka the parent is the packery container, not null)
         */
        return !!element.parentNode
    })

    const newChildren = this.getNewDomChildren()

    const removed = oldChildren.filter((oldChild) => {
        return !~newChildren.indexOf(oldChild)
    })

    const domDiff = newChildren.filter((newChild) => {
        return !~oldChildren.indexOf(newChild)
    })

    let beginningIndex = 0

    // get everything added to the beginning of the DOMNode list
    const prepended = domDiff.filter((newChild, i) => {
        const prepend = (beginningIndex === newChildren.indexOf(newChild))

        if (prepend) {
            // increase the index
            beginningIndex++
        }

        return prepend
    })

    // we assume that everything else is appended
    const appended = domDiff.filter((el) => {
        return prepended.indexOf(el) === -1
    })

    this.domChildren = newChildren

    return {
        old: oldChildren,
        new: newChildren,
        removed: removed,
        appended: appended,
        prepended: prepended
    }
  }

  performLayout = () => {
    const diff = this.diffDomChildren()

    if (diff.removed.length > 0) {
        this.packery.remove(diff.removed)
    }

    if (diff.appended.length > 0) {
        this.packery.appended(diff.appended)
       // diff.appended.forEach( this.makeEachDraggable )
    }

    if (diff.prepended.length > 0) {
        this.packery.prepended(diff.prepended)
       // diff.prepended.forEach( this.makeEachDraggable )
    }
    this.packery.reloadItems()
    this.packery.layout()
  }

  imagesLoaded = () => {
    if (this.props.disableImagesLoaded) return

    imagesloaded(
        this.refs[refName],
        (instance) => {
            this.packery.layout()
        }
    )
  }

  componentDidMount = () => {
    this.initializePackery()
    this.imagesLoaded()
  }

  componentDidUpdate = () => {
    this.performLayout()
    this.imagesLoaded()
  }

  componentWillReceiveProps = () => {
    this._timer = setTimeout(() => {
        this.packery.reloadItems()
        this.packery.layout()
    });
  }

  componentWillUnmount = () => {
    clearTimeout(this._timer)
  }

  render = () => {
    return React.createElement(this.props.elementType, {
        className: this.props.className,
        ref: refName
    }, this.props.children)
  }
}

PackeryComponent.propTypes = {
  disableImagesLoaded: React.PropTypes.bool,
  options: React.PropTypes.object
}

PackeryComponent.defaultProps = {
  disableImagesLoaded: false,
  options: {},
  className: '',
  elementType: 'div'
}

module.exports = PackeryComponent
<|endoftext|>


<commit_before>// Requirements
const mqtt = require('mqtt')
const Redis = require('redis')
const express = require('express')

const logging = require('./homeautomation-js-lib/logging.js')

// Set up Logging
logging.set_enabled(true)

// Config
const host = process.env.MQTT_HOST
const port = process.env.LISTENING_PORT
const redisHost = process.env.REDIS_HOST
const redisPort = process.env.REDIS_PORT
const redisDB = process.env.REDIS_DATABASE

const redis = Redis.createClient({
    host: redisHost,
    port: redisPort,
    db: redisDB,
    retry_strategy: function(options) {
        if (options.error && options.error.code === 'ECONNREFUSED') {
            // End reconnecting on a specific error and flush all commands with a individual error
            return new Error('The server refused the connection')
        }
        if (options.total_retry_time > 1000 * 60 * 60) {
            // End reconnecting after a specific timeout and flush all commands with a individual error
            return new Error('Retry time exhausted')
        }
        if (options.times_connected > 10) {
            // End reconnecting with built in error
            return undefined
        }
        // reconnect after
        return Math.min(options.attempt * 100, 3000)
    }
})

// Setup MQTT
const client = mqtt.connect(host)

// MQTT Observation

client.on('connect', () => {
    logging.log('Reconnecting...\n')
    client.subscribe('#')
})

client.on('disconnect', () => {
    logging.log('Reconnecting...\n')
    client.connect(host)
})

client.on('message', (topic, message) => {
    if (topic.endsWith('/set')) return
    if (topic.startsWith('/homeseer/action/')) return

    const components = topic.split('/')
    if (components[0] === 'homeseer' && components[1] === 'action') return

    redis.get(topic, function(err, result) {
        if (result !== null) {
            // logging.log('already have: ' + topic + ' value: ' + result)
        } else {
            logging.log('adding: ' + topic + ' value: ' + message)
        }
        redis.set(topic, message)
    })
})

// redis callbacks

redis.on('error', function(err) {
    logging.log('redis error ' + err)
})

redis.on('connect', function() {
    logging.log('redis connected')
    redis.keys('*', function(err, result) {
        logging.log(' keys:' + result)
    })

})

// Express
const app = express()

app.get('/', function(req, res) {
    redis.keys('*', function(err, result) {
        logging.log('keys: ' + result)
        const keys = result.sort()
        redis.mget(keys, function(err, values) {
            var html = ''

            html += '<!DOCTYPE html>'
            html += ''
            html += ''
            html += '
All Devices
'
            html += '
'

            for (var index = 0; index < keys.length; index++) {
                var key = keys[index]
                var value = values[index]

                html += '
'
                html += ''
                html += key
                html += '	'
                html += ''
                html += value
                html += '	'

                html += '
'
            }

            html += '
'
            html += ''
            html += ''

            res.send(html)
        })
    })
})

app.listen(port, function() {
    logging.log('MQTT Store listening on port: ', port)
})<commit_msg>Fixing query<commit_after>// Requirements
const mqtt = require('mqtt')
const Redis = require('redis')
const express = require('express')

const logging = require('./homeautomation-js-lib/logging.js')

// Set up Logging
logging.set_enabled(true)

// Config
const host = process.env.MQTT_HOST
const port = process.env.LISTENING_PORT
const redisHost = process.env.REDIS_HOST
const redisPort = process.env.REDIS_PORT
const redisDB = process.env.REDIS_DATABASE

const redis = Redis.createClient({
    host: redisHost,
    port: redisPort,
    db: redisDB,
    retry_strategy: function(options) {
        if (options.error && options.error.code === 'ECONNREFUSED') {
            // End reconnecting on a specific error and flush all commands with a individual error
            return new Error('The server refused the connection')
        }
        if (options.total_retry_time > 1000 * 60 * 60) {
            // End reconnecting after a specific timeout and flush all commands with a individual error
            return new Error('Retry time exhausted')
        }
        if (options.times_connected > 10) {
            // End reconnecting with built in error
            return undefined
        }
        // reconnect after
        return Math.min(options.attempt * 100, 3000)
    }
})

// Setup MQTT
const client = mqtt.connect(host)

// MQTT Observation

client.on('connect', () => {
    logging.log('Reconnecting...\n')
    client.subscribe('#')
})

client.on('disconnect', () => {
    logging.log('Reconnecting...\n')
    client.connect(host)
})

client.on('message', (topic, message) => {
    if (topic.endsWith('/set')) return
    if (topic.startsWith('/homeseer/action/')) return

    const components = topic.split('/')
    if (components[0] === 'homeseer' && components[1] === 'action') return

    redis.get(topic, function(err, result) {
        if (result !== null) {
            // logging.log('already have: ' + topic + ' value: ' + result)
        } else {
            logging.log('adding: ' + topic + ' value: ' + message)
        }
        redis.set(topic, message)
    })
})

// redis callbacks

redis.on('error', function(err) {
    logging.log('redis error ' + err)
})

redis.on('connect', function() {
    logging.log('redis connected')
    redis.keys('*', function(err, result) {
        logging.log(' keys:' + result)
    })

})

// Express
const app = express()

app.get('/', function(req, res) {
    redis.keys('*', function(err, result) {
        logging.log('keys: ' + result)
        const keys = result.sort()
        redis.mget(keys, function(err, values) {
            var html = ''

            html += '<!DOCTYPE html>'
            html += ''
            html += ''
            html += '
All Devices
'
            html += '
'

            for (var index = 0; index < keys.length; index++) {
                var key = keys[index]
                var value = values[index]

                if (key.endsWith('/set')) continue
                if (key.startsWith('/homeseer/action/')) continue

                html += '
'
                html += ''
                html += key
                html += '	'
                html += ''
                html += value
                html += '	'

                html += '
'
            }

            html += '
'
            html += ''
            html += ''

            res.send(html)
        })
    })
})

app.listen(port, function() {
    logging.log('MQTT Store listening on port: ', port)
})<|endoftext|>