<commit_before>var createDefaultStream = require('./lib/default_stream');
var Test = require('./lib/test');
var createResultStream = require('./lib/results');

var canEmitExit = typeof process !== 'undefined' && process
    && typeof process.on === 'function'
;
var canExit = typeof process !== 'undefined' && process
    && typeof process.exit === 'function'
;

var nextTick = typeof setImmediate !== 'undefined'
    ? setImmediate
    : process.nextTick
;

exports = module.exports = (function () {
    var harness;
    var lazyLoad = function () {
        if (!harness) harness = createExitHarness();

        return harness.apply(this, arguments);
    };

    lazyLoad.only = function () {
        if (!harness) harness = createExitHarness();

        return harness.only.apply(this, arguments);
    }

    return lazyLoad
})();

function createExitHarness (conf) {
    if (!conf) conf = {};
    var harness = createHarness();
    var stream = harness.createStream();
    stream.pipe(createDefaultStream());

    var ended = false;
    stream.on('end', function () { ended = true });

    if (conf.exit === false) return harness;
    if (!canEmitExit || !canExit) return harness;

    process.on('exit', function (code) {
        if (!ended) {
            for (var i = 0; i < harness._tests.length; i++) {
                var t = harness._tests[i];
                t._exit();
            }
        }
        process.exit(code || harness._exitCode);
    });
    return harness;
}

exports.createHarness = createHarness;
exports.Test = Test;
exports.test = exports; // tap compat

var exitInterval;

function createHarness (conf_) {
    var results;

    var test = function (name, conf, cb) {
        if (!results) {
            results = createResultStream();
            results.pause();
        }

        var t = new Test(name, conf, cb);
        test._tests.push(t);

        (function inspectCode (st) {
            st.on('test', function sub (st_) {
                inspectCode(st_);
            });
            st.on('result', function (r) {
                if (!r.ok) test._exitCode = 1
            });
        })(t);

        results.push(t);
        return t;
    };

    test._tests = [];

    test.createStream = function () {
        if (!results) results = createResultStream();

        var _pause = results.pause;
        var paused = false;
        results.pause = function () { paused = true };

        nextTick(function () {
            if (!paused) results.resume();
        });
        return results;
    };

    var only = false;
    test.only = function (name) {
        if (only) throw new Error('there can only be one only test');
        results.only(name);
        only = true;
        return test.apply(null, arguments);
    };
    test._exitCode = 0;

    return test;
}
<commit_msg>do not call process.exit() on uncaught exception<commit_after>var createDefaultStream = require('./lib/default_stream');
var Test = require('./lib/test');
var createResultStream = require('./lib/results');

var canEmitExit = typeof process !== 'undefined' && process
    && typeof process.on === 'function'
;
var canExit = typeof process !== 'undefined' && process
    && typeof process.exit === 'function'
;

var nextTick = typeof setImmediate !== 'undefined'
    ? setImmediate
    : process.nextTick
;

exports = module.exports = (function () {
    var harness;
    var lazyLoad = function () {
        if (!harness) harness = createExitHarness();

        return harness.apply(this, arguments);
    };

    lazyLoad.only = function () {
        if (!harness) harness = createExitHarness();

        return harness.only.apply(this, arguments);
    }

    return lazyLoad
})();

function createExitHarness (conf) {
    if (!conf) conf = {};
    var harness = createHarness();
    var stream = harness.createStream();
    stream.pipe(createDefaultStream());

    var ended = false;
    stream.on('end', function () { ended = true });

    if (conf.exit === false) return harness;
    if (!canEmitExit || !canExit) return harness;

    var _error;

    process.on('uncaughtException', function (err) {
        _error = err
    })

    process.on('exit', function (code) {
        if (_error) {
            return
        }

        if (!ended) {
            for (var i = 0; i < harness._tests.length; i++) {
                var t = harness._tests[i];
                t._exit();
            }
        }
        process.exit(code || harness._exitCode);
    });
    return harness;
}

exports.createHarness = createHarness;
exports.Test = Test;
exports.test = exports; // tap compat

var exitInterval;

function createHarness (conf_) {
    var results;

    var test = function (name, conf, cb) {
        if (!results) {
            results = createResultStream();
            results.pause();
        }

        var t = new Test(name, conf, cb);
        test._tests.push(t);

        (function inspectCode (st) {
            st.on('test', function sub (st_) {
                inspectCode(st_);
            });
            st.on('result', function (r) {
                if (!r.ok) test._exitCode = 1
            });
        })(t);

        results.push(t);
        return t;
    };

    test._tests = [];

    test.createStream = function () {
        if (!results) results = createResultStream();

        var _pause = results.pause;
        var paused = false;
        results.pause = function () { paused = true };

        nextTick(function () {
            if (!paused) results.resume();
        });
        return results;
    };

    var only = false;
    test.only = function (name) {
        if (only) throw new Error('there can only be one only test');
        results.only(name);
        only = true;
        return test.apply(null, arguments);
    };
    test._exitCode = 0;

    return test;
}
<|endoftext|>


<commit_before>module.exports = {
  AdNotExistError:                              'ad not exist',
  AlreadyActivatedError:                        'already activated',
  AppNotExistError:                             'app not exist',
  AppPlatformSettingsNotExistError:             'app platform settings not exist',
  AttachmentNotExistError:                      'attachment not exist',
  ClientNotExistError:                          'client not exist',
  CubePointAggregationFailedError:              'failed to aggregate cube point',
  CubePointNotExistError:                       'cube point not exist',
  FailedToCreateAdClickError:                   'failed to create ad click',
  FailedToCreateAdError:                        'failed to create ad',
  FailedToCreateAppPlatformSettingsError:       'failed to create app platform settings',
  FailedToCreateAttachmentError:                'failed to create attachment',
  FailedToCreateDeviceError:                    'failed to create device, contact us',
  FailedToCreateMPoiWifiRuleError:              'failed to create poi wifi rule',
  FailedToCreatePoiError:                       'failed to create poi',
  FailedToCreateScenarioError:                  'failed to create scenario',
  FailedToCreateScheduleError:                  'failed to create schedule history',
  FailedToCreateUserError:                      'failed to create user',
  FailedToDeleteFileError:                      'failed to delete file',
  FailedToDestroyAdError:                       'failed to destroy ad',
  FailedToDestroyAppPlatformSettingsError:      'failed to destroy app platform settings',
  FailedToDestroyAttachmentError:               'failed to destroy attachment',
  FailedToDestroyMPoiWifiRuleError:             'failed to destroy poi wifi rule',
  FailedToDestroyScenarioError:                 'failed to destroy scenario',
  FailedToEnsureDeviceError:                    'rpc error, failed to ensure device',
  FailedToExportPoiError:                       'failed to export poi',
  FailedToFindAdError:                          'failed to find ad',
  FailedToFindAppError:                         'failed to get app, check if appKey is valid',
  FailedToFindAppPlatformSettingsError:         'failed to find app platform settings',
  FailedToFindAttachmentError:                  'failed to find attachment',
  FailedToFindCubePointError:                   'failed to find cube point',
  FailedToFindFileError:                        'failed to find file',
  FailedToFindJobError:                         'failed to find job',
  FailedToFindPoiPullRequestError:              'failed to find poi pull request, contact us',
  FailedToFindPoisError:                        'failed to find pois',
  FailedToFindRegionsError:                     'failed to find regions',
  FailedToFindResourceError:                    'failed to find specified resource',
  FailedToFindScenarioError:                    'failed to find scenario',
  FailedToFindScheduleError:                    'failed to find schedule history',
  FailedToFindScheduleHistoryError:             'failed to find schedule history',
  FailedToFindUserBehaviorsError:               'failed to find user behaviors',
  FailedToFindUsersError:                       'failed to find users',
  FailedToGenerateTokenError:                   'failed to generate token, contact us',
  FailedToGetAppError:                          'rpc error, failed to Get App',
  FailedToGetDeviceError:                       'failed to get device, contact us',
  FailedToGetJobError:                          'failed to get job status',
  FailedToGetPoiError:                          'rpc error, failed to get poi',
  FailedToHashPasswordError:                    'failed to hash password',
  FailedToImportPoiError:                       'failed to import error',
  FailedToQueueDeviceDataCollectJobError:       'failed to queue device data collect job',
  FailedToQueuePoiExportJobError:               'failed to queue poi export job',
  FailedToQueuePoiImportJobError:               'failed to queue poi import job',
  FailedToQueuePoiRemoveJobError:               'failed to queue poi remove job',
  FailedToQueuePoiRevertJobError:               'failed to queue poi revert job',
  FailedToReadCertificateError:                 'failed to read certificate, please check the password is correct',
  FailedToReadFileError:                        'error when read file',
  FailedToSaveDeviceError:                      'failed to create/update device, contact us',
  FailedToSaveFileError:                        'failed to save file',
  FailedToSaveUserError:                        'failed to save user, please check if required field is correct',
  FailedToSendMailError:                        'failed to send mail',
  FailedToUpdateAdError:                        'failed to update ad',
  FailedToUpdateAdLastOperationError:           'failed to update ad last operation',
  FailedToUpdateAppError:                       'failed to update app',
  FailedToUpdateAppLastOperationError:          'failed to update app last operation',
  FailedToUpdateAppPlatformSettingsError:       'failed to update app platform settings',
  FailedToUpdateAttachmentError:                'failed to update attachment',
  FailedToUpdateDeviceError:                    'failed to update device, contact us',
  FailedToUpdateMPoiWifiRuleError:              'failed to update poi wifi rule',
  FailedToUpdatePoiError:                       'failed to update poi',
  FailedToUpdatePoiPullRequestError:            'failed to update poi pull request',
  FailedToUpdateScenarioError:                  'failed to update scenario',
  FailedToUpdateScenarioLastOperationError:     'failed to update scenario last operation',
  FailedToUpdateScheduleError:                  'failed to update schedule history',
  FailedToUploadDeviceDataError:                'failed to upload data',
  FailedToValidateEmailError:                   'failed to validate email',
  FileNotExistError:                            'file not exist',
  InternalError:                                'internal error, contact us',
  InvalidActivationCodeError:                   'invalid activation code',
  InvalidAdSignatureError:                      'invalid ad signature',
  InvalidAppKeyError:                           'invalid appKey',
  InvalidAppSecretError:                        'invalid appSecret',
  InvalidClientKeyError:                        'invalid clientKey',
  InvalidClientSecretError:                     'invalid clientSecret',
  InvalidLocationError:                         'invalid location',
  InvalidPasswordResetCodeError:                'invalid password reset code',
  InvalidTokenError:                            'invalid token',
  MPoiWifiRuleNotExistError:                    'poi wifi rule not exist',
  NotYourAppError:                              'app is not yours',
  PasswordNotMatchError:                        'password not match',
  PendingPoiPullRequestExistsError:             'pending poi pull request exists',
  PoiNotExistError:                             'poi not exist',
  PoiNotReadyError:                             'poi not ready',
  PoiPullRequestNotActiveError:                 'poi pull request not active',
  PoiPullRequestNotExistError:                  'poi pull request not exist',
  RequireAdClickError:                          'require ad click',
  RequireAdError:                               'require ad',
  RequireAdIdError:                             'require ad id',
  RequireAppBundleError:                        'require appBundle',
  RequireAppError:                              'require app',
  RequireAppIdError:                            'require app id',
  RequireAppKeyError:                           'require appKey',
  RequireAppPlatformSettingsIdError:            'require app platform settings id',
  RequireAppSecretError:                        'require appSecret',
  RequireAttachmentError:                       'require attachment',
  RequireAttachmentIdError:                     'require attachment id',
  RequireBeaconsOrWifisOrLocationError:         'require beacon or wifi or location',
  RequireBssidError:                            'require wifi bssid',
  RequireClientKeyError:                        'require clientKey',
  RequireClientSecretError:                     'require clientSecret',
  RequireDataFileError:                         'require uploaded data file',
  RequireDeviceError:                           'require device',
  RequireDeviceUdidError:                       'require deviceUdid',
  RequireFileBucketError:                       'require file bucket',
  RequireFilePathError:                         'require file path',
  RequireJobIdError:                            'require job id',
  RequireMPoiWifiRuleError:                     'require poi wifi rule',
  RequireMPoiWifiRuleIdError:                   'require poi wifi rule id',
  RequireOwnerError:                            'require owner',
  RequirePasswordError:                         'require password',
  RequirePoiError:                              'require poi',
  RequirePoiIdError:                            'require poi id',
  RequirePoiIdOrUmmError:                       'require poi id or umm',
  RequirePoiPullRequestError:                   'require poi pull request info',
  RequirePoiPullRequestIdError:                 'require poi pull request id',
  RequirePoisError:                             'require pois',
  RequireScenarioError:                         'require scenario',
  RequireScenarioIdError:                       'require scenario id',
  RequireScheduleError:                         'require schedule history',
  RequireScheduleHistoryIdError:                'require schedule history id',
  RequireScheduleIdError:                       'require schedule history id',
  RequireSsidError:                            'require wifi ssid',
  RequireTokenError:                            'require token',
  RequireUmmError:                              'require beacon umm',
  RequireUsernameError:                         'require username',
  RequireUserOrCodeToResetPasswordError:        'require user or code to reset password',
  ScenarioNotExistError:                        'scenario not exist',
  ScheduleHistoryNotExistError:                 'schedule history not exist',
  ScheduleNotExistError:                        'schedule history not exist',
  Success:                                      'success',
  SystemBusyError:                              'system busy, retry later',
  TokenExpiredError:                            'token is expired',
  UserAlreadySignupError:                       'failed to signup, user already signup',
  UserNotExistError:                            'user not exist',
  WrongParameterError:                          'wrong parameter'
};
<commit_msg>add fail to destroy schedule error<commit_after>module.exports = {
  AdNotExistError:                              'ad not exist',
  AlreadyActivatedError:                        'already activated',
  AppNotExistError:                             'app not exist',
  AppPlatformSettingsNotExistError:             'app platform settings not exist',
  AttachmentNotExistError:                      'attachment not exist',
  ClientNotExistError:                          'client not exist',
  CubePointAggregationFailedError:              'failed to aggregate cube point',
  CubePointNotExistError:                       'cube point not exist',
  FailedToCreateAdClickError:                   'failed to create ad click',
  FailedToCreateAdError:                        'failed to create ad',
  FailedToCreateAppPlatformSettingsError:       'failed to create app platform settings',
  FailedToCreateAttachmentError:                'failed to create attachment',
  FailedToCreateDeviceError:                    'failed to create device, contact us',
  FailedToCreateMPoiWifiRuleError:              'failed to create poi wifi rule',
  FailedToCreatePoiError:                       'failed to create poi',
  FailedToCreateScenarioError:                  'failed to create scenario',
  FailedToCreateScheduleError:                  'failed to create schedule history',
  FailedToCreateUserError:                      'failed to create user',
  FailedToDeleteFileError:                      'failed to delete file',
  FailedToDestroyAdError:                       'failed to destroy ad',
  FailedToDestroyAppPlatformSettingsError:      'failed to destroy app platform settings',
  FailedToDestroyAttachmentError:               'failed to destroy attachment',
  FailedToDestroyMPoiWifiRuleError:             'failed to destroy poi wifi rule',
  FailedToDestroyScenarioError:                 'failed to destroy scenario',
  FailedToDestroyScheduleError:                 'failed to destroy schedule',
  FailedToEnsureDeviceError:                    'rpc error, failed to ensure device',
  FailedToExportPoiError:                       'failed to export poi',
  FailedToFindAdError:                          'failed to find ad',
  FailedToFindAppError:                         'failed to get app, check if appKey is valid',
  FailedToFindAppPlatformSettingsError:         'failed to find app platform settings',
  FailedToFindAttachmentError:                  'failed to find attachment',
  FailedToFindCubePointError:                   'failed to find cube point',
  FailedToFindFileError:                        'failed to find file',
  FailedToFindJobError:                         'failed to find job',
  FailedToFindPoiPullRequestError:              'failed to find poi pull request, contact us',
  FailedToFindPoisError:                        'failed to find pois',
  FailedToFindRegionsError:                     'failed to find regions',
  FailedToFindResourceError:                    'failed to find specified resource',
  FailedToFindScenarioError:                    'failed to find scenario',
  FailedToFindScheduleError:                    'failed to find schedule history',
  FailedToFindScheduleHistoryError:             'failed to find schedule history',
  FailedToFindUserBehaviorsError:               'failed to find user behaviors',
  FailedToFindUsersError:                       'failed to find users',
  FailedToGenerateTokenError:                   'failed to generate token, contact us',
  FailedToGetAppError:                          'rpc error, failed to Get App',
  FailedToGetDeviceError:                       'failed to get device, contact us',
  FailedToGetJobError:                          'failed to get job status',
  FailedToGetPoiError:                          'rpc error, failed to get poi',
  FailedToHashPasswordError:                    'failed to hash password',
  FailedToImportPoiError:                       'failed to import error',
  FailedToQueueDeviceDataCollectJobError:       'failed to queue device data collect job',
  FailedToQueuePoiExportJobError:               'failed to queue poi export job',
  FailedToQueuePoiImportJobError:               'failed to queue poi import job',
  FailedToQueuePoiRemoveJobError:               'failed to queue poi remove job',
  FailedToQueuePoiRevertJobError:               'failed to queue poi revert job',
  FailedToReadCertificateError:                 'failed to read certificate, please check the password is correct',
  FailedToReadFileError:                        'error when read file',
  FailedToSaveDeviceError:                      'failed to create/update device, contact us',
  FailedToSaveFileError:                        'failed to save file',
  FailedToSaveUserError:                        'failed to save user, please check if required field is correct',
  FailedToSendMailError:                        'failed to send mail',
  FailedToUpdateAdError:                        'failed to update ad',
  FailedToUpdateAdLastOperationError:           'failed to update ad last operation',
  FailedToUpdateAppError:                       'failed to update app',
  FailedToUpdateAppLastOperationError:          'failed to update app last operation',
  FailedToUpdateAppPlatformSettingsError:       'failed to update app platform settings',
  FailedToUpdateAttachmentError:                'failed to update attachment',
  FailedToUpdateDeviceError:                    'failed to update device, contact us',
  FailedToUpdateMPoiWifiRuleError:              'failed to update poi wifi rule',
  FailedToUpdatePoiError:                       'failed to update poi',
  FailedToUpdatePoiPullRequestError:            'failed to update poi pull request',
  FailedToUpdateScenarioError:                  'failed to update scenario',
  FailedToUpdateScenarioLastOperationError:     'failed to update scenario last operation',
  FailedToUpdateScheduleError:                  'failed to update schedule history',
  FailedToUploadDeviceDataError:                'failed to upload data',
  FailedToValidateEmailError:                   'failed to validate email',
  FileNotExistError:                            'file not exist',
  InternalError:                                'internal error, contact us',
  InvalidActivationCodeError:                   'invalid activation code',
  InvalidAdSignatureError:                      'invalid ad signature',
  InvalidAppKeyError:                           'invalid appKey',
  InvalidAppSecretError:                        'invalid appSecret',
  InvalidClientKeyError:                        'invalid clientKey',
  InvalidClientSecretError:                     'invalid clientSecret',
  InvalidLocationError:                         'invalid location',
  InvalidPasswordResetCodeError:                'invalid password reset code',
  InvalidTokenError:                            'invalid token',
  MPoiWifiRuleNotExistError:                    'poi wifi rule not exist',
  NotYourAppError:                              'app is not yours',
  PasswordNotMatchError:                        'password not match',
  PendingPoiPullRequestExistsError:             'pending poi pull request exists',
  PoiNotExistError:                             'poi not exist',
  PoiNotReadyError:                             'poi not ready',
  PoiPullRequestNotActiveError:                 'poi pull request not active',
  PoiPullRequestNotExistError:                  'poi pull request not exist',
  RequireAdClickError:                          'require ad click',
  RequireAdError:                               'require ad',
  RequireAdIdError:                             'require ad id',
  RequireAppBundleError:                        'require appBundle',
  RequireAppError:                              'require app',
  RequireAppIdError:                            'require app id',
  RequireAppKeyError:                           'require appKey',
  RequireAppPlatformSettingsIdError:            'require app platform settings id',
  RequireAppSecretError:                        'require appSecret',
  RequireAttachmentError:                       'require attachment',
  RequireAttachmentIdError:                     'require attachment id',
  RequireBeaconsOrWifisOrLocationError:         'require beacon or wifi or location',
  RequireBssidError:                            'require wifi bssid',
  RequireClientKeyError:                        'require clientKey',
  RequireClientSecretError:                     'require clientSecret',
  RequireDataFileError:                         'require uploaded data file',
  RequireDeviceError:                           'require device',
  RequireDeviceUdidError:                       'require deviceUdid',
  RequireFileBucketError:                       'require file bucket',
  RequireFilePathError:                         'require file path',
  RequireJobIdError:                            'require job id',
  RequireMPoiWifiRuleError:                     'require poi wifi rule',
  RequireMPoiWifiRuleIdError:                   'require poi wifi rule id',
  RequireOwnerError:                            'require owner',
  RequirePasswordError:                         'require password',
  RequirePoiError:                              'require poi',
  RequirePoiIdError:                            'require poi id',
  RequirePoiIdOrUmmError:                       'require poi id or umm',
  RequirePoiPullRequestError:                   'require poi pull request info',
  RequirePoiPullRequestIdError:                 'require poi pull request id',
  RequirePoisError:                             'require pois',
  RequireScenarioError:                         'require scenario',
  RequireScenarioIdError:                       'require scenario id',
  RequireScheduleError:                         'require schedule history',
  RequireScheduleHistoryIdError:                'require schedule history id',
  RequireScheduleIdError:                       'require schedule history id',
  RequireSsidError:                            'require wifi ssid',
  RequireTokenError:                            'require token',
  RequireUmmError:                              'require beacon umm',
  RequireUsernameError:                         'require username',
  RequireUserOrCodeToResetPasswordError:        'require user or code to reset password',
  ScenarioNotExistError:                        'scenario not exist',
  ScheduleHistoryNotExistError:                 'schedule history not exist',
  ScheduleNotExistError:                        'schedule history not exist',
  Success:                                      'success',
  SystemBusyError:                              'system busy, retry later',
  TokenExpiredError:                            'token is expired',
  UserAlreadySignupError:                       'failed to signup, user already signup',
  UserNotExistError:                            'user not exist',
  WrongParameterError:                          'wrong parameter'
};
<|endoftext|>


<commit_before>var express = require('express');
var exphbs  = require('express-handlebars');
var config = require('./config.js');
var app = express();
var BME280 = require('node-bme280');
var barometer = new BME280({address: 0x77});
var latestTemp;
var latestPressure;
var latestHumidity;
var latestTime;
var ready = false;

var Datastore = require('nedb');
var dateFormat = require('dateformat');
var open = new Date();
var db = new Datastore({ filename: 'latest.txt', autoload: true }); // 1 min, rename to dbLatest
var dbDaily = new Datastore({ filename: dateFormat(open, "yyyy-mm-dd'T00:00:00'") + "day.txt", autoload: true }); // 5 mins
var dbWeekly = new Datastore({ filename: dateFormat(open, "yyyy-mm-dd'T00:00:00'") + "week.txt", autoload: true }); // 1 hour

barometer.begin(function (err) {
    if (err) {
        console.info('error initializing barometer', err);
        return;
    }
    console.info('barometer running');
	setImmediate(readData);
	setInterval(readData, config.baroInterval);
});

var counter = 0;

var readData = function () {
	barometer.readPressureAndTemparature(function(err, pressure, temperature, humidity) {
		latestTime = new Date();
		latestTemp = temperature.toFixed(2);
		latestPressure = (pressure / 100).toFixed(2);
		latestHumidity = humidity.toFixed(2);
		ready = true;
		db.insert({
			time: latestTime,
			ambientTemperature: latestTemp,
			pressure: latestPressure,
			humidity: latestHumidity
		});
	});
};

var consolidate = function (type) {
	if (type == "day") {

	} else if (type == "week") {

	}
};

app.engine('handlebars', exphbs({defaultLayout: false}));
app.set('view engine', 'handlebars');
app.use(express.static('static'));

app.get('/', function (req, res) {
	if (ready) {
		var now = new Date();
		var secondsPast = (now.getTime() - latestTime.getTime()) / 1000;
		res.render("index", {
			ready: true,
			graphtest: req.query.graphtest,
			measurementTime: secondsPast.toFixed(0),
			sensors: [
				{
					value: parseFloat(latestTemp).toFixed(1),
					unit: "°C",
					measurement: "Temperature",
					location: "ICT office"
				},
				{
					value: parseFloat(latestHumidity).toFixed(1),
					unit: "%",
					measurement: "Humidity",
					location: "ICT office"
				},
				{
					value: parseFloat(latestPressure).toFixed(0),
					unit: "hPa",
					small: true,
					measurement: "Pressure",
					location: "ICT office"
				}
			]
		});
	} else {
		res.render("index", {
			ready: false,
			graphtest: req.query.graphtest
		});
	}
});

app.get('/output.csv', function (req, res) {
	db.find({}).sort({ time: 1 }).exec(function (err, docs) {
		res.write("Time,Temperature,Pressure,Humidity\n");
		for (var i = 0; i < docs.length; i++) {
			var time = docs[i].time;
			var formattedDate = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
			var formattedTime = time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
			var formattedDateTime = formattedDate + " " + formattedTime; // github doesn't like long lines
			res.write(formattedDateTime + ",");
			res.write(docs[i].ambientTemperature + ",");
			res.write(docs[i].pressure + ",");
			res.write(docs[i].humidity + "\n");
		}
		res.end();
	});
});

app.get('/data.json', function (req, res) {
	db.find({ $where: function () {return (this.time % 5) == 0} }).sort({ time: -1 }).limit(20).exec(function (err, docs) {
		res.write("[")
		for (var i = 0; i < docs.length; i++) {
			res.write(docs[i].ambientTemperature);
			if (i < (docs.length - 1)) {
				res.write(",");
			}
		}
		res.write("]");
		res.end();
	});
});

app.listen(80, function () {
	console.log('Weather station online on port 80');
});
<commit_msg>update with dataObject<commit_after>var express = require('express');
var exphbs  = require('express-handlebars');
var config = require('./config.js');
var app = express();
var BME280 = require('node-bme280');
var barometer = new BME280({address: 0x77});
var latestTemp;
var latestPressure;
var latestHumidity;
var latestTime;
var ready = false;

var Datastore = require('nedb');
var dateFormat = require('dateformat');
var open = new Date();
var db = new Datastore({ filename: 'latest.txt', autoload: true }); // 1 min, rename to dbLatest
var dbDaily = new Datastore({ filename: dateFormat(open, "yyyy-mm-dd'T00:00:00'") + "day.txt", autoload: true }); // 5 mins
var dbWeekly = new Datastore({ filename: dateFormat(open, "yyyy-mm-dd'T00:00:00'") + "week.txt", autoload: true }); // 1 hour

barometer.begin(function (err) {
    if (err) {
        console.info('error initializing barometer', err);
        return;
    }
    console.info('barometer running');
	setImmediate(readData);
	setInterval(readData, config.baroInterval);
});

var counter = 0;

var readData = function () {
	barometer.readPressureAndTemparature(function(err, pressure, temperature, humidity) {
		latestTime = new Date();
		latestTemp = temperature.toFixed(2);
		latestPressure = (pressure / 100).toFixed(2);
		latestHumidity = humidity.toFixed(2);
		ready = true;
		db.insert({
			time: latestTime,
			ambientTemperature: latestTemp,
			pressure: latestPressure,
			humidity: latestHumidity
		});
	});
};

var consolidate = function (type) {
	if (type == "day") {

	} else if (type == "week") {

	}
};

app.engine('handlebars', exphbs({defaultLayout: false}));
app.set('view engine', 'handlebars');
app.use(express.static('static'));

app.get('/', function (req, res) {
	if (ready) {
		var now = new Date();
		var secondsPast = (now.getTime() - latestTime.getTime()) / 1000;
		res.render("index", {
			ready: true,
			graphtest: req.query.graphtest,
			measurementTime: secondsPast.toFixed(0),
			sensors: [
				{
					value: parseFloat(latestTemp).toFixed(1),
					unit: "°C",
					measurement: "Temperature",
					location: "ICT office"
				},
				{
					value: parseFloat(latestHumidity).toFixed(1),
					unit: "%",
					measurement: "Humidity",
					location: "ICT office"
				},
				{
					value: parseFloat(latestPressure).toFixed(0),
					unit: "hPa",
					small: true,
					measurement: "Pressure",
					location: "ICT office"
				}
			]
		});
	} else {
		res.render("index", {
			ready: false,
			graphtest: req.query.graphtest
		});
	}
});

app.get('/output.csv', function (req, res) {
	db.find({}).sort({ time: 1 }).exec(function (err, docs) {
		res.write("Time,Temperature,Pressure,Humidity\n");
		for (var i = 0; i < docs.length; i++) {
			var time = docs[i].time;
			var formattedDate = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
			var formattedTime = time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
			var formattedDateTime = formattedDate + " " + formattedTime; // github doesn't like long lines
			res.write(formattedDateTime + ",");
			res.write(docs[i].ambientTemperature + ",");
			res.write(docs[i].pressure + ",");
			res.write(docs[i].humidity + "\n");
		}
		res.end();
	});
});

app.get('/data.json', function (req, res) {
	db.find({ $where: function () {return (this.time % 5) == 0} }).sort({ time: -1 }).limit(20).exec(function (err, docs) {
		var dataObject = {
			ambientTemperature: [],
			pressure: [],
			humidity: []
		};
		for (var i = 0; i < docs.length; i++) {
			res.write(docs[i].ambientTemperature);
			dataObject.ambientTemperature.push(docs[i].ambientTemperature);
			dataObject.pressure.push(docs[i].pressure);
			dataObject.humidity.push(docs[i].humidity);
		}
		res.send(JSON.stringify(dataObject));
	});
});

app.listen(80, function () {
	console.log('Weather station online on port 80');
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from "react";
import moment from "moment";
import { Glyphicon } from "react-bootstrap";
import DateTimePicker from "./DateTimePicker.js";
import Constants from "./Constants.js";

export default class DateTimeField extends Component {
  static defaultProps = {
    dateTime: moment().format("x"),
    format: "x",
    showToday: true,
    viewMode: "days",
    daysOfWeekDisabled: [],
    mode: Constants.MODE_DATETIME,
    onChange: (x) => {
      console.log(x);
    }
  }

  resolvePropsInputFormat = () => {
    if (this.props.inputFormat) { return this.props.inputFormat; }
    switch (this.props.mode) {
      case Constants.MODE_TIME:
        return "h:mm A";
      case Constants.MODE_DATE:
        return "MM/DD/YY";
      default:
        return "MM/DD/YY h:mm A";
    }
  }

  static propTypes = {
    dateTime: PropTypes.string,
    onChange: PropTypes.func,
    format: PropTypes.string,
    inputProps: PropTypes.object,
    inputFormat: PropTypes.string,
    defaultText: PropTypes.string,
    mode: PropTypes.oneOf([Constants.MODE_DATE, Constants.MODE_DATETIME, Constants.MODE_TIME]),
    minDate: PropTypes.object,
    maxDate: PropTypes.object,
    direction: PropTypes.string,
    showToday: PropTypes.bool,
    viewMode: PropTypes.string,
    daysOfWeekDisabled: PropTypes.arrayOf(PropTypes.integer)
  }

  state = {
      showDatePicker: this.props.mode !== Constants.MODE_TIME,
      showTimePicker: this.props.mode === Constants.MODE_TIME,
      inputFormat: this.resolvePropsInputFormat(),
      buttonIcon: this.props.mode === Constants.MODE_TIME ? "time" : "calendar",
      widgetStyle: {
        display: "block",
        position: "absolute",
        left: -9999,
        zIndex: "9999 !important"
      },
      viewDate: moment(this.props.dateTime, this.props.format, true).startOf("month"),
      selectedDate: moment(this.props.dateTime, this.props.format, true),
      inputValue: typeof this.props.defaultText !== "undefined" ? this.props.defaultText : moment(this.props.dateTime, this.props.format, true).format(this.resolvePropsInputFormat())
  }

  componentWillReceiveProps = (nextProps) => {
    let state = {};
    if (nextProps.inputFormat !== this.props.inputFormat) {
        state.inputFormat = nextProps.inputFormat;
        state.inputValue = moment(nextProps.dateTime, nextProps.format, true).format(nextProps.inputFormat);
    }

    if (nextProps.dateTime !== this.props.dateTime && moment(nextProps.dateTime, nextProps.format, true).isValid()) {
      state.viewDate = moment(nextProps.dateTime, nextProps.format, true).startOf("month");
      state.selectedDate = moment(nextProps.dateTime, nextProps.format, true);
      state.inputValue = moment(nextProps.dateTime, nextProps.format, true).format(nextProps.inputFormat ? nextProps.inputFormat : this.state.inputFormat);
    }
    return this.setState(state);
  }

  onChange = (event) => {
    const value = event.target == null ? event : event.target.value;
    if (moment(value, this.state.inputFormat, true).isValid()) {
      this.setState({
        selectedDate: moment(value, this.state.inputFormat, true),
        viewDate: moment(value, this.state.inputFormat, true).startOf("month")
      });
    }

    return this.setState({
      inputValue: value
    }, function() {
      return this.props.onChange(moment(this.state.inputValue, this.state.inputFormat, true).format(this.props.format), value);
    });

  }

  getValue = () => {
    return moment(this.state.inputValue, this.props.inputFormat, true).format(this.props.format);
  }

  setSelectedDate = (e) => {
    const { target } = e;
    if (target.className && !target.className.match(/disabled/g)) {
      let month;
      if (target.className.indexOf("new") >= 0) month = this.state.viewDate.month() + 1;
      else if (target.className.indexOf("old") >= 0) month = this.state.viewDate.month() - 1;
      else month = this.state.viewDate.month();
      return this.setState({
        selectedDate: this.state.viewDate.clone().month(month).date(parseInt(e.target.innerHTML)).hour(this.state.selectedDate.hours()).minute(this.state.selectedDate.minutes())
      }, function() {
        this.closePicker();
        this.props.onChange(this.state.selectedDate.format(this.props.format));
        return this.setState({
          inputValue: this.state.selectedDate.format(this.state.inputFormat)
        });
      });
    }
  }

  setSelectedHour = (e) => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().hour(parseInt(e.target.innerHTML)).minute(this.state.selectedDate.minutes())
    }, function() {
      this.closePicker();
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.state.inputFormat)
      });
    });
  }

  setSelectedMinute = (e) => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().hour(this.state.selectedDate.hours()).minute(parseInt(e.target.innerHTML))
    }, function() {
      this.closePicker();
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.state.inputFormat)
      });
    });
  }

  setViewMonth = (month) => {
    return this.setState({
      viewDate: this.state.viewDate.clone().month(month)
    });
  }

  setViewYear = (year) => {
    return this.setState({
      viewDate: this.state.viewDate.clone().year(year)
    });
  }

  addMinute = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().add(1, "minutes")
    }, function() {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  addHour = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().add(1, "hours")
    }, function() {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  addMonth = () => {
    return this.setState({
      viewDate: this.state.viewDate.add(1, "months")
    });
  }

  addYear = () => {
    return this.setState({
      viewDate: this.state.viewDate.add(1, "years")
    });
  }

  addDecade = () => {
    return this.setState({
      viewDate: this.state.viewDate.add(10, "years")
    });
  }

  subtractMinute = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().subtract(1, "minutes")
    }, () => {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  subtractHour = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().subtract(1, "hours")
    }, () => {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  subtractMonth = () => {
    return this.setState({
      viewDate: this.state.viewDate.subtract(1, "months")
    });
  }

  subtractYear = () => {
    return this.setState({
      viewDate: this.state.viewDate.subtract(1, "years")
    });
  }

  subtractDecade = () => {
    return this.setState({
      viewDate: this.state.viewDate.subtract(10, "years")
    });
  }

  togglePeriod = () => {
    if (this.state.selectedDate.hour() > 12) {
      return this.onChange(this.state.selectedDate.clone().subtract(12, "hours").format(this.state.inputFormat));
    } else {
      return this.onChange(this.state.selectedDate.clone().add(12, "hours").format(this.state.inputFormat));
    }
  }

  togglePicker = () => {
    return this.setState({
      showDatePicker: !this.state.showDatePicker,
      showTimePicker: !this.state.showTimePicker
    });
  }

  onClick = () => {
    let classes, gBCR, offset, placePosition, scrollTop, styles;
    if (this.state.showPicker) {
      return this.closePicker();
    } else {
      this.setState({
        showPicker: true
      });
      gBCR = this.refs.dtpbutton.getDOMNode().getBoundingClientRect();
      classes = {
        "bootstrap-datetimepicker-widget": true,
        "dropdown-menu": true
      };
      offset = {
        top: gBCR.top + window.pageYOffset - document.documentElement.clientTop,
        left: gBCR.left + window.pageXOffset - document.documentElement.clientLeft
      };
      offset.top = offset.top + this.refs.datetimepicker.getDOMNode().offsetHeight;
      scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
      placePosition = this.props.direction === "up" ? "top" : this.props.direction === "bottom" ? "bottom" : this.props.direction === "auto" ? offset.top + this.refs.widget.getDOMNode().offsetHeight > window.offsetHeight + scrollTop && this.refs.widget.offsetHeight + this.refs.datetimepicker.getDOMNode().offsetHeight > offset.top ? "top" : "bottom" : void 0;
      if (placePosition === "top") {
        offset.top = -this.refs.widget.getDOMNode().offsetHeight - this.getDOMNode().clientHeight - 2;
        classes.top = true;
        classes.bottom = false;
        classes["pull-right"] = true;
      } else {
        offset.top = 40;
        classes.top = false;
        classes.bottom = true;
        classes["pull-right"] = true;
      }
      styles = {
        display: "block",
        position: "absolute",
        top: offset.top,
        left: "auto",
        right: 40
      };
      return this.setState({
        widgetStyle: styles,
        widgetClasses: classes
      });
    }
  }

  closePicker = () => {
    let style = this.state.widgetStyle;
    style.left = -9999;
    style.display = "none";
    return this.setState({
      showPicker: false,
      widgetStyle: style
    });
  }

  renderOverlay = () => {
    const styles = {
      position: "fixed",
      top: 0,
      bottom: 0,
      left: 0,
      right: 0,
      zIndex: "999"
    };
    if (this.state.showPicker) {
      return (
);
    } else {
      return ;
    }
  }

  render() {
    return (

            {this.renderOverlay()}
            <DateTimePicker ref="widget"
                  addDecade={this.addDecade}
                  addHour={this.addHour}
                  addMinute={this.addMinute}
                  addMonth={this.addMonth}
                  addYear={this.addYear}
                  daysOfWeekDisabled={this.props.daysOfWeekDisabled}
                  maxDate={this.props.maxDate}
                  minDate={this.props.minDate}
                  mode={this.props.mode}
                  selectedDate={this.state.selectedDate}
                  setSelectedDate={this.setSelectedDate}
                  setSelectedHour={this.setSelectedHour}
                  setSelectedMinute={this.setSelectedMinute}
                  setViewMonth={this.setViewMonth}
                  setViewYear={this.setViewYear}
                  showDatePicker={this.state.showDatePicker}
                  showTimePicker={this.state.showTimePicker}
                  showToday={this.props.showToday}
                  subtractDecade={this.subtractDecade}
                  subtractHour={this.subtractHour}
                  subtractMinute={this.subtractMinute}
                  subtractMonth={this.subtractMonth}
                  subtractYear={this.subtractYear}
                  togglePeriod={this.togglePeriod}
                  togglePicker={this.togglePicker}
                  viewDate={this.state.viewDate}
                  viewMode={this.props.viewMode}
                  widgetClasses={this.state.widgetClasses}
                  widgetStyle={this.state.widgetStyle}
            />

              <input type="text" className="form-control" onChange={this.onChange} value={this.state.inputValue} {...this.props.inputProps}/>
              <Glyphicon glyph={this.state.buttonIcon} />

    );
  }
}

<commit_msg>add numeric type support for "dateTime" attribute<commit_after>import React, { Component, PropTypes } from "react";
import moment from "moment";
import { Glyphicon } from "react-bootstrap";
import DateTimePicker from "./DateTimePicker.js";
import Constants from "./Constants.js";

export default class DateTimeField extends Component {
  static defaultProps = {
    dateTime: moment().format("x"),
    format: "x",
    showToday: true,
    viewMode: "days",
    daysOfWeekDisabled: [],
    mode: Constants.MODE_DATETIME,
    onChange: (x) => {
      console.log(x);
    }
  }

  resolvePropsInputFormat = () => {
    if (this.props.inputFormat) { return this.props.inputFormat; }
    switch (this.props.mode) {
      case Constants.MODE_TIME:
        return "h:mm A";
      case Constants.MODE_DATE:
        return "MM/DD/YY";
      default:
        return "MM/DD/YY h:mm A";
    }
  }

  static propTypes = {
    dateTime: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number
    ]),
    onChange: PropTypes.func,
    format: PropTypes.string,
    inputProps: PropTypes.object,
    inputFormat: PropTypes.string,
    defaultText: PropTypes.string,
    mode: PropTypes.oneOf([Constants.MODE_DATE, Constants.MODE_DATETIME, Constants.MODE_TIME]),
    minDate: PropTypes.object,
    maxDate: PropTypes.object,
    direction: PropTypes.string,
    showToday: PropTypes.bool,
    viewMode: PropTypes.string,
    daysOfWeekDisabled: PropTypes.arrayOf(PropTypes.integer)
  }

  state = {
      showDatePicker: this.props.mode !== Constants.MODE_TIME,
      showTimePicker: this.props.mode === Constants.MODE_TIME,
      inputFormat: this.resolvePropsInputFormat(),
      buttonIcon: this.props.mode === Constants.MODE_TIME ? "time" : "calendar",
      widgetStyle: {
        display: "block",
        position: "absolute",
        left: -9999,
        zIndex: "9999 !important"
      },
      viewDate: moment(this.props.dateTime, this.props.format, true).startOf("month"),
      selectedDate: moment(this.props.dateTime, this.props.format, true),
      inputValue: typeof this.props.defaultText !== "undefined" ? this.props.defaultText : moment(this.props.dateTime, this.props.format, true).format(this.resolvePropsInputFormat())
  }

  componentWillReceiveProps = (nextProps) => {
    let state = {};
    if (nextProps.inputFormat !== this.props.inputFormat) {
        state.inputFormat = nextProps.inputFormat;
        state.inputValue = moment(nextProps.dateTime, nextProps.format, true).format(nextProps.inputFormat);
    }

    if (nextProps.dateTime !== this.props.dateTime && moment(nextProps.dateTime, nextProps.format, true).isValid()) {
      state.viewDate = moment(nextProps.dateTime, nextProps.format, true).startOf("month");
      state.selectedDate = moment(nextProps.dateTime, nextProps.format, true);
      state.inputValue = moment(nextProps.dateTime, nextProps.format, true).format(nextProps.inputFormat ? nextProps.inputFormat : this.state.inputFormat);
    }
    return this.setState(state);
  }

  onChange = (event) => {
    const value = event.target == null ? event : event.target.value;
    if (moment(value, this.state.inputFormat, true).isValid()) {
      this.setState({
        selectedDate: moment(value, this.state.inputFormat, true),
        viewDate: moment(value, this.state.inputFormat, true).startOf("month")
      });
    }

    return this.setState({
      inputValue: value
    }, function() {
      return this.props.onChange(moment(this.state.inputValue, this.state.inputFormat, true).format(this.props.format), value);
    });

  }

  getValue = () => {
    return moment(this.state.inputValue, this.props.inputFormat, true).format(this.props.format);
  }

  setSelectedDate = (e) => {
    const { target } = e;
    if (target.className && !target.className.match(/disabled/g)) {
      let month;
      if (target.className.indexOf("new") >= 0) month = this.state.viewDate.month() + 1;
      else if (target.className.indexOf("old") >= 0) month = this.state.viewDate.month() - 1;
      else month = this.state.viewDate.month();
      return this.setState({
        selectedDate: this.state.viewDate.clone().month(month).date(parseInt(e.target.innerHTML)).hour(this.state.selectedDate.hours()).minute(this.state.selectedDate.minutes())
      }, function() {
        this.closePicker();
        this.props.onChange(this.state.selectedDate.format(this.props.format));
        return this.setState({
          inputValue: this.state.selectedDate.format(this.state.inputFormat)
        });
      });
    }
  }

  setSelectedHour = (e) => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().hour(parseInt(e.target.innerHTML)).minute(this.state.selectedDate.minutes())
    }, function() {
      this.closePicker();
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.state.inputFormat)
      });
    });
  }

  setSelectedMinute = (e) => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().hour(this.state.selectedDate.hours()).minute(parseInt(e.target.innerHTML))
    }, function() {
      this.closePicker();
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.state.inputFormat)
      });
    });
  }

  setViewMonth = (month) => {
    return this.setState({
      viewDate: this.state.viewDate.clone().month(month)
    });
  }

  setViewYear = (year) => {
    return this.setState({
      viewDate: this.state.viewDate.clone().year(year)
    });
  }

  addMinute = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().add(1, "minutes")
    }, function() {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  addHour = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().add(1, "hours")
    }, function() {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  addMonth = () => {
    return this.setState({
      viewDate: this.state.viewDate.add(1, "months")
    });
  }

  addYear = () => {
    return this.setState({
      viewDate: this.state.viewDate.add(1, "years")
    });
  }

  addDecade = () => {
    return this.setState({
      viewDate: this.state.viewDate.add(10, "years")
    });
  }

  subtractMinute = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().subtract(1, "minutes")
    }, () => {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  subtractHour = () => {
    return this.setState({
      selectedDate: this.state.selectedDate.clone().subtract(1, "hours")
    }, () => {
      this.props.onChange(this.state.selectedDate.format(this.props.format));
      return this.setState({
        inputValue: this.state.selectedDate.format(this.resolvePropsInputFormat())
      });
    });
  }

  subtractMonth = () => {
    return this.setState({
      viewDate: this.state.viewDate.subtract(1, "months")
    });
  }

  subtractYear = () => {
    return this.setState({
      viewDate: this.state.viewDate.subtract(1, "years")
    });
  }

  subtractDecade = () => {
    return this.setState({
      viewDate: this.state.viewDate.subtract(10, "years")
    });
  }

  togglePeriod = () => {
    if (this.state.selectedDate.hour() > 12) {
      return this.onChange(this.state.selectedDate.clone().subtract(12, "hours").format(this.state.inputFormat));
    } else {
      return this.onChange(this.state.selectedDate.clone().add(12, "hours").format(this.state.inputFormat));
    }
  }

  togglePicker = () => {
    return this.setState({
      showDatePicker: !this.state.showDatePicker,
      showTimePicker: !this.state.showTimePicker
    });
  }

  onClick = () => {
    let classes, gBCR, offset, placePosition, scrollTop, styles;
    if (this.state.showPicker) {
      return this.closePicker();
    } else {
      this.setState({
        showPicker: true
      });
      gBCR = this.refs.dtpbutton.getDOMNode().getBoundingClientRect();
      classes = {
        "bootstrap-datetimepicker-widget": true,
        "dropdown-menu": true
      };
      offset = {
        top: gBCR.top + window.pageYOffset - document.documentElement.clientTop,
        left: gBCR.left + window.pageXOffset - document.documentElement.clientLeft
      };
      offset.top = offset.top + this.refs.datetimepicker.getDOMNode().offsetHeight;
      scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
      placePosition = this.props.direction === "up" ? "top" : this.props.direction === "bottom" ? "bottom" : this.props.direction === "auto" ? offset.top + this.refs.widget.getDOMNode().offsetHeight > window.offsetHeight + scrollTop && this.refs.widget.offsetHeight + this.refs.datetimepicker.getDOMNode().offsetHeight > offset.top ? "top" : "bottom" : void 0;
      if (placePosition === "top") {
        offset.top = -this.refs.widget.getDOMNode().offsetHeight - this.getDOMNode().clientHeight - 2;
        classes.top = true;
        classes.bottom = false;
        classes["pull-right"] = true;
      } else {
        offset.top = 40;
        classes.top = false;
        classes.bottom = true;
        classes["pull-right"] = true;
      }
      styles = {
        display: "block",
        position: "absolute",
        top: offset.top,
        left: "auto",
        right: 40
      };
      return this.setState({
        widgetStyle: styles,
        widgetClasses: classes
      });
    }
  }

  closePicker = () => {
    let style = this.state.widgetStyle;
    style.left = -9999;
    style.display = "none";
    return this.setState({
      showPicker: false,
      widgetStyle: style
    });
  }

  renderOverlay = () => {
    const styles = {
      position: "fixed",
      top: 0,
      bottom: 0,
      left: 0,
      right: 0,
      zIndex: "999"
    };
    if (this.state.showPicker) {
      return (
);
    } else {
      return ;
    }
  }

  render() {
    return (

            {this.renderOverlay()}
            <DateTimePicker ref="widget"
                  addDecade={this.addDecade}
                  addHour={this.addHour}
                  addMinute={this.addMinute}
                  addMonth={this.addMonth}
                  addYear={this.addYear}
                  daysOfWeekDisabled={this.props.daysOfWeekDisabled}
                  maxDate={this.props.maxDate}
                  minDate={this.props.minDate}
                  mode={this.props.mode}
                  selectedDate={this.state.selectedDate}
                  setSelectedDate={this.setSelectedDate}
                  setSelectedHour={this.setSelectedHour}
                  setSelectedMinute={this.setSelectedMinute}
                  setViewMonth={this.setViewMonth}
                  setViewYear={this.setViewYear}
                  showDatePicker={this.state.showDatePicker}
                  showTimePicker={this.state.showTimePicker}
                  showToday={this.props.showToday}
                  subtractDecade={this.subtractDecade}
                  subtractHour={this.subtractHour}
                  subtractMinute={this.subtractMinute}
                  subtractMonth={this.subtractMonth}
                  subtractYear={this.subtractYear}
                  togglePeriod={this.togglePeriod}
                  togglePicker={this.togglePicker}
                  viewDate={this.state.viewDate}
                  viewMode={this.props.viewMode}
                  widgetClasses={this.state.widgetClasses}
                  widgetStyle={this.state.widgetStyle}
            />

              <input type="text" className="form-control" onChange={this.onChange} value={this.state.inputValue} {...this.props.inputProps}/>
              <Glyphicon glyph={this.state.buttonIcon} />

    );
  }
}

<|endoftext|>


<commit_before>/*!
 * days <https://github.com/jonschlinkert/days>
 *
 * Copyright (c) 2014 Jon Schlinkert, contributors.
 * Licensed under the MIT license.
 */

// English
module.exports.en = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
module.exports.en.abbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
module.exports.en.short = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];

// French translation
module.exports.fr = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'];
module.exports.fr.abbr = ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'];
module.exports.fr.short = ['di', 'lu', 'ma', 'me', 'je', 've', 'sa'];

// In order not to break compatibility
module.exports = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
module.exports.abbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
module.exports.short = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
<commit_msg>Added Italian translation<commit_after>/*!
 * days <https://github.com/jonschlinkert/days>
 *
 * Copyright (c) 2014 Jon Schlinkert, contributors.
 * Licensed under the MIT license.
 */

// English
module.exports.en = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
module.exports.en.abbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
module.exports.en.short = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];

// French translation
module.exports.fr = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'];
module.exports.fr.abbr = ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'];
module.exports.fr.short = ['di', 'lu', 'ma', 'me', 'je', 've', 'sa'];

// Italian translation
module.exports.it = ['Domenica', 'Lunedi', 'Martedi', 'Mercoledi', 'Giovedi', 'Venerdi', 'Sabato'];
module.exports.it.abbr = ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'];
module.exports.it.short = ['D', 'L', 'Ma', 'Me', 'G', 'V', 'S'];

// In order not to break compatibility
module.exports = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
module.exports.abbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
module.exports.short = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
<|endoftext|>


<commit_before>/**
 * @file Dialog component
 * @author zhouqinghuai@baidu.com
 */

import {create} from '../common/util/cx';
import Layer from '../Layer/Layer';
import * as Mask from '../Mask';
import css from '../common/util/css';

const cx = create('dialog');

export default class Dialog extends Layer {

    static template = `

                <slot name="title">{{title}}</slot>

                <slot></slot>

                <slot name="actions"></slot>

    `;

    static computed = {
        style() {
            return css({
                display: this.data.get('open') ? 'block' : 'none',
                width: `${this.data.get('width')}px`
            });
        },

        computedClassName() {
            return cx(this).build();
        }

    };

    initData() {
        return {
            open: false,
            useMask: true,
            closeOnClickMask: true,
            width: 760
        };
    }

    attached() {
        super.attached();
        let onMaskClick = this.onMaskClick = this.onMaskClick.bind(this);
        this.watch('open', open => {
            if (this.data.get('useMask')) {
                open ? Mask.show(onMaskClick) : Mask.hide();
            }
        });
    }

    onMaskClick() {
        if (this.data.get('closeOnClickMask')) {
            this.data.set('open', false);
        }
    }

}
<commit_msg>fix dialog mask close<commit_after>/**
 * @file Dialog component
 * @author zhouqinghuai@baidu.com
 */

import {create} from '../common/util/cx';
import Layer from '../Layer/Layer';
import * as Mask from '../Mask';
import css from '../common/util/css';

const cx = create('dialog');

export default class Dialog extends Layer {

    static template = `

                <slot name="title">{{title}}</slot>

                <slot></slot>

                <slot name="actions"></slot>

    `;

    static computed = {
        style() {
            return css({
                display: this.data.get('open') ? 'block' : 'none',
                width: `${this.data.get('width')}px`
            });
        },

        computedClassName() {
            return cx(this).build();
        }

    };

    initData() {
        return {
            open: false,
            useMask: true,
            closeOnClickMask: true,
            width: 760
        };
    }

    attached() {
        super.attached();
        let onMaskClick = this.onMaskClick = this.onMaskClick.bind(this);
        this.watch('open', open => {
            if (this.data.get('useMask')) {
                open ? Mask.show(onMaskClick) : Mask.hide();
            }
        });
    }

    onMaskClick() {
        if (this.data.get('closeOnClickMask')) {
            this.data.set('open', false);
        }
    }

    detached() {
        this.data.set('open', false);
    }

}
<|endoftext|>


<commit_before>var fs = require("fs");
var zlib = require("zlib");
var FdSlicer = require("fd-slicer");
var util = require("util");
var EventEmitter = require("events").EventEmitter;
var PassThrough = require("stream").PassThrough;
var Iconv = require("iconv").Iconv;

exports.open = open;
exports.fromFd = fromFd;
exports.fromBuffer = fromBuffer;
exports.ZipFile = ZipFile;
exports.Entry = Entry;
exports.dosDateTimeToDate = dosDateTimeToDate;

// cd - Central Directory
// cdr - Central Directory Record
// eocdr - End of Central Directory Record

function open(path, options, callback) {
  if (typeof options === "function") {
    callback = options;
    options = null;
  }
  if (options == null) options = {autoClose: true};
  if (callback == null) callback = defaultCallback;
  fs.open(path, "r", function(err, fd) {
    if (err) return callback(err);
    fromFd(fd, options, function(err, zipfile) {
      if (err) fs.close(fd, defaultCallback);
      callback(err, zipfile);
    });
  });
}

function fromFd(fd, options, callback) {
  if (typeof options === "function") {
    callback = options;
    options = null;
  }
  if (options == null) options = {autoClose: false};
  if (callback == null) callback = defaultCallback;
  fs.fstat(fd, function(err, stats) {
    if (err) return callback(err);
    var fdSlicer = new FdSlicer(fd, {autoClose: true});
    // this ref is unreffed in zipfile.close()
    fdSlicer.ref();
    fromFdSlicer(fdSlicer, stats.size, options, callback);
  });
}

function fromBuffer(buffer, callback) {
  if (callback == null) callback = defaultCallback;
  // i got your open file right here.
  var fdSlicer = new FakeFdSlicer(buffer);
  fromFdSlicer(fdSlicer, buffer.length, {}, callback);
}
function fromFdSlicer(fdSlicer, totalSize, options, callback) {
  // search backwards for the eocdr signature.
  // the last field of the eocdr is a variable-length comment.
  // the comment size is encoded in a 2-byte field in the eocdr, which we can't find without trudging backwards through the comment to find it.
  // as a consequence of this design decision, it's possible to have ambiguous zip file metadata if a coherent eocdr was in the comment.
  // we search backwards for a eocdr signature, and hope that whoever made the zip file was smart enough to forbid the eocdr signature in the comment.
  var eocdrWithoutCommentSize = 22;
  var maxCommentSize = 0x10000; // 2-byte size
  var bufferSize = Math.min(eocdrWithoutCommentSize + maxCommentSize, totalSize);
  var buffer = new Buffer(bufferSize);
  var bufferReadStart = totalSize - buffer.length;
  readFdSlicerNoEof(fdSlicer, buffer, 0, bufferSize, bufferReadStart, function(err) {
    if (err) return callback(err);
    for (var i = bufferSize - eocdrWithoutCommentSize; i >= 0; i -= 1) {
      if (buffer.readUInt32LE(i) !== 0x06054b50) continue;
      // found eocdr
      var eocdrBuffer = buffer.slice(i);

      // 0 - End of central directory signature = 0x06054b50
      // 4 - Number of this disk
      var diskNumber = eocdrBuffer.readUInt16LE(4);
      if (diskNumber !== 0) return callback(new Error("multi-disk zip files are not supported: found disk number: " + diskNumber));
      // 6 - Disk where central directory starts
      // 8 - Number of central directory records on this disk
      // 10 - Total number of central directory records
      var entryCount = eocdrBuffer.readUInt16LE(10);
      // 12 - Size of central directory (bytes)
      // 16 - Offset of start of central directory, relative to start of archive
      var cdOffset = eocdrBuffer.readUInt32LE(16);
      // 20 - Comment length
      var commentLength = eocdrBuffer.readUInt16LE(20);
      var expectedCommentLength = eocdrBuffer.length - eocdrWithoutCommentSize;
      if (commentLength !== expectedCommentLength) {
        return callback(new Error("invalid comment length. expected: " + expectedCommentLength + ". found: " + commentLength));
      }
      // 22 - Comment
      // the encoding is always cp437.
      var comment = bufferToString(eocdrBuffer, 22, eocdrBuffer.length, false);
      return callback(null, new ZipFile(fdSlicer, cdOffset, totalSize, entryCount, comment, options.autoClose));
    }
    callback(new Error("end of central directory record signature not found"));
  });
}

util.inherits(ZipFile, EventEmitter);
function ZipFile(fdSlicer, cdOffset, fileSize, entryCount, comment, autoClose) {
  var self = this;
  EventEmitter.call(self);
  self.fdSlicer = fdSlicer;
  // forward close events
  self.fdSlicer.on("error", function(err) {
    // error closing the fd
    self.emit("error", err);
  });
  self.fdSlicer.once("close", function() {
    self.emit("close");
  });
  self.readEntryCursor = cdOffset;
  self.fileSize = fileSize;
  self.entryCount = entryCount;
  self.comment = comment;
  self.entriesRead = 0;
  self.autoClose = !!autoClose;
  self.isOpen = true;
  // make sure events don't fire outta here until the client has a chance to attach listeners
  setImmediate(function() { readEntries(self); });
}
ZipFile.prototype.close = function() {
  if (!this.isOpen) return;
  this.isOpen = false;
  this.fdSlicer.unref();
};

function emitErrorAndAutoClose(self, err) {
  if (self.autoClose) self.close();
  self.emit("error", err);
}

function readEntries(self) {
  if (self.entryCount === self.entriesRead) {
    // done with metadata
    if (self.autoClose) self.close();
    return self.emit("end");
  }
  var buffer = new Buffer(46);
  readFdSlicerNoEof(self.fdSlicer, buffer, 0, buffer.length, self.readEntryCursor, function(err) {
    if (err) return emitErrorAndAutoClose(self, err);
    var entry = new Entry();
    // 0 - Central directory file header signature
    var signature = buffer.readUInt32LE(0);
    if (signature !== 0x02014b50) return emitErrorAndAutoClose(self, new Error("invalid central directory file header signature: 0x" + signature.toString(16)));
    // 4 - Version made by
    entry.versionMadeBy = buffer.readUInt16LE(4);
    // 6 - Version needed to extract (minimum)
    entry.versionNeededToExtract = buffer.readUInt16LE(6);
    // 8 - General purpose bit flag
    entry.generalPurposeBitFlag = buffer.readUInt16LE(8);
    // 10 - Compression method
    entry.compressionMethod = buffer.readUInt16LE(10);
    // 12 - File last modification time
    entry.lastModFileTime = buffer.readUInt16LE(12);
    // 14 - File last modification date
    entry.lastModFileDate = buffer.readUInt16LE(14);
    // 16 - CRC-32
    entry.crc32 = buffer.readUInt32LE(16);
    // 20 - Compressed size
    entry.compressedSize = buffer.readUInt32LE(20);
    // 24 - Uncompressed size
    entry.uncompressedSize = buffer.readUInt32LE(24);
    // 28 - File name length (n)
    entry.fileNameLength = buffer.readUInt16LE(28);
    // 30 - Extra field length (m)
    entry.extraFieldLength = buffer.readUInt16LE(30);
    // 32 - File comment length (k)
    entry.fileCommentLength = buffer.readUInt16LE(32);
    // 34 - Disk number where file starts
    // 36 - Internal file attributes
    entry.internalFileAttributes = buffer.readUInt16LE(36);
    // 38 - External file attributes
    entry.externalFileAttributes = buffer.readUInt32LE(38);
    // 42 - Relative offset of local file header
    entry.relativeOffsetOfLocalHeader = buffer.readUInt32LE(42);

    self.readEntryCursor += 46;

    buffer = new Buffer(entry.fileNameLength + entry.extraFieldLength + entry.fileCommentLength);
    readFdSlicerNoEof(self.fdSlicer, buffer, 0, buffer.length, self.readEntryCursor, function(err) {
      if (err) return emitErrorAndAutoClose(self, err);
      // 46 - File name
      var isUtf8 = entry.generalPurposeBitFlag & 0x800
      try {
        entry.fileName = bufferToString(buffer, 0, entry.fileNameLength, isUtf8);
      } catch (e) {
        return emitErrorAndAutoClose(self, e);
      }

      // 46+n - Extra field
      var fileCommentStart = entry.fileNameLength + entry.extraFieldLength;
      var extraFieldBuffer = buffer.slice(entry.fileNameLength, fileCommentStart);
      entry.extraFields = [];
      var i = 0;
      while (i < extraFieldBuffer.length) {
        var headerId = extraFieldBuffer.readUInt16LE(i + 0);
        var dataSize = extraFieldBuffer.readUInt16LE(i + 2);
        var dataStart = i + 4;
        var dataEnd = dataStart + dataSize;
        var dataBuffer = new Buffer(dataSize);
        extraFieldBuffer.copy(dataBuffer, 0, dataStart, dataEnd);
        entry.extraFields.push({
          id: headerId,
          data: dataBuffer,
        });
        i = dataEnd;
      }

      // 46+n+m - File comment
      try {
        entry.fileComment = bufferToString(buffer, fileCommentStart, fileCommentStart + entry.fileCommentLength, isUtf8);
      } catch (e) {
        return emitErrorAndAutoClose(self, e);
      }

      self.readEntryCursor += buffer.length;
      self.entriesRead += 1;

      // validate file name
      if (entry.fileName.indexOf("\\") !== -1) return emitErrorAndAutoClose(self, new Error("invalid characters in fileName: " + entry.fileName));
      if (/^[a-zA-Z]:/.test(entry.fileName) || /^\//.test(entry.fileName)) return emitErrorAndAutoClose(self, new Error("absolute path: " + entry.fileName));
      if (entry.fileName.split("/").indexOf("..") !== -1) return emitErrorAndAutoClose(self, new Error("invalid relative path: " + entry.fileName));
      self.emit("entry", entry);
      readEntries(self);
    });
  });
}

ZipFile.prototype.openReadStream = function(entry, callback) {
  var self = this;
  if (!self.isOpen) return self.emit("error", new Error("closed"));
  // make sure we don't lose the fd before we open the actual read stream
  self.fdSlicer.ref();
  var buffer = new Buffer(30);
  readFdSlicerNoEof(self.fdSlicer, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader, function(err) {
    try {
      if (err) return callback(err);
      // 0 - Local file header signature = 0x04034b50
      var signature = buffer.readUInt32LE(0);
      if (signature !== 0x04034b50) return callback(new Error("invalid local file header signature: 0x" + signature.toString(16)));
      // all this should be redundant
      // 4 - Version needed to extract (minimum)
      // 6 - General purpose bit flag
      // 8 - Compression method
      // 10 - File last modification time
      // 12 - File last modification date
      // 14 - CRC-32
      // 18 - Compressed size
      // 22 - Uncompressed size
      // 26 - File name length (n)
      var fileNameLength = buffer.readUInt16LE(26);
      // 28 - Extra field length (m)
      var extraFieldLength = buffer.readUInt16LE(28);
      // 30 - File name
      // 30+n - Extra field
      var localFileHeaderEnd = entry.relativeOffsetOfLocalHeader + buffer.length + fileNameLength + extraFieldLength;
      var filterStream = null;
      if (entry.compressionMethod === 0) {
        // 0 - The file is stored (no compression)
      } else if (entry.compressionMethod === 8) {
        // 8 - The file is Deflated
        filterStream = zlib.createInflateRaw();
      } else {
        return callback(new Error("unsupported compression method: " + entry.compressionMethod));
      }
      var fileDataStart = localFileHeaderEnd;
      var fileDataEnd = fileDataStart + entry.compressedSize;
      if (entry.compressedSize !== 0) {
        // bounds check now, because the read streams will probably not complain loud enough.
        // since we're dealing with an unsigned offset plus an unsigned size,
        // we only have 1 thing to check for.
        if (fileDataEnd > self.fileSize) {
          return callback(new Error("file data overflows file bounds: " +
              fileDataStart + " + " + entry.compressedSize + " > " + self.fileSize));
        }
      }
      var stream = self.fdSlicer.createReadStream({start: fileDataStart, end: fileDataEnd});
      if (filterStream != null) {
        stream = stream.pipe(filterStream);
      }
      callback(null, stream);
    } finally {
      self.fdSlicer.unref();
    }
  });
};

function Entry() {
}
Entry.prototype.getLastModDate = function() {
  return dosDateTimeToDate(this.lastModFileDate, this.lastModFileTime);
};

function dosDateTimeToDate(date, time) {
  var day = date & 0x1f; // 1-31
  var month = (date >> 5 & 0xf) - 1; // 1-12, 0-11
  var year = (date >> 9 & 0x7f) + 1980; // 0-128, 1980-2108

  var millisecond = 0;
  var second = (time & 0x1f) * 2; // 0-29, 0-58 (even numbers)
  var minute = time >> 5 & 0x3f; // 0-59
  var hour = time >> 11 & 0x1f; // 0-23

  return new Date(year, month, day, hour, minute, second, millisecond);
}

function readFdSlicerNoEof(fdSlicer, buffer, offset, length, position, callback) {
  fdSlicer.read(buffer, offset, length, position, function(err, bytesRead) {
    if (err) return callback(err);
    if (bytesRead < length) return callback(new Error("unexpected EOF"));
    callback();
  });
}
var cp437_to_utf8 = new Iconv("cp437", "utf8");
function bufferToString(buffer, start, end, isUtf8) {
  if (isUtf8) {
    return buffer.toString("utf8", start, end);
  } else {
    return cp437_to_utf8.convert(buffer.slice(start, end)).toString("utf8");
  }
}

function FakeFdSlicer(buffer) {
  // pretend that a buffer is an FdSlicer
  this.buffer = buffer;
}
FakeFdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
  this.buffer.copy(buffer, offset, position, position + length);
  setImmediate(function() {
    callback(null, length);
  });
};
FakeFdSlicer.prototype.createReadStream = function(options) {
  var start = options.start;
  var end = options.end;
  var buffer = new Buffer(end - start);
  this.buffer.copy(buffer, 0, start, end);
  var stream = new PassThrough();
  stream.write(buffer);
  stream.end();
  return stream;
};
// i promise these functions are working properly :|
FakeFdSlicer.prototype.ref = function() {};
FakeFdSlicer.prototype.unref = function() {};
FakeFdSlicer.prototype.on = function() {};
FakeFdSlicer.prototype.once = function() {};

function defaultCallback(err) {
  if (err) throw err;
}
<commit_msg>prevent assertion failure with some versions of iconv<commit_after>var fs = require("fs");
var zlib = require("zlib");
var FdSlicer = require("fd-slicer");
var util = require("util");
var EventEmitter = require("events").EventEmitter;
var PassThrough = require("stream").PassThrough;
var Iconv = require("iconv").Iconv;

exports.open = open;
exports.fromFd = fromFd;
exports.fromBuffer = fromBuffer;
exports.ZipFile = ZipFile;
exports.Entry = Entry;
exports.dosDateTimeToDate = dosDateTimeToDate;

// cd - Central Directory
// cdr - Central Directory Record
// eocdr - End of Central Directory Record

function open(path, options, callback) {
  if (typeof options === "function") {
    callback = options;
    options = null;
  }
  if (options == null) options = {autoClose: true};
  if (callback == null) callback = defaultCallback;
  fs.open(path, "r", function(err, fd) {
    if (err) return callback(err);
    fromFd(fd, options, function(err, zipfile) {
      if (err) fs.close(fd, defaultCallback);
      callback(err, zipfile);
    });
  });
}

function fromFd(fd, options, callback) {
  if (typeof options === "function") {
    callback = options;
    options = null;
  }
  if (options == null) options = {autoClose: false};
  if (callback == null) callback = defaultCallback;
  fs.fstat(fd, function(err, stats) {
    if (err) return callback(err);
    var fdSlicer = new FdSlicer(fd, {autoClose: true});
    // this ref is unreffed in zipfile.close()
    fdSlicer.ref();
    fromFdSlicer(fdSlicer, stats.size, options, callback);
  });
}

function fromBuffer(buffer, callback) {
  if (callback == null) callback = defaultCallback;
  // i got your open file right here.
  var fdSlicer = new FakeFdSlicer(buffer);
  fromFdSlicer(fdSlicer, buffer.length, {}, callback);
}
function fromFdSlicer(fdSlicer, totalSize, options, callback) {
  // search backwards for the eocdr signature.
  // the last field of the eocdr is a variable-length comment.
  // the comment size is encoded in a 2-byte field in the eocdr, which we can't find without trudging backwards through the comment to find it.
  // as a consequence of this design decision, it's possible to have ambiguous zip file metadata if a coherent eocdr was in the comment.
  // we search backwards for a eocdr signature, and hope that whoever made the zip file was smart enough to forbid the eocdr signature in the comment.
  var eocdrWithoutCommentSize = 22;
  var maxCommentSize = 0x10000; // 2-byte size
  var bufferSize = Math.min(eocdrWithoutCommentSize + maxCommentSize, totalSize);
  var buffer = new Buffer(bufferSize);
  var bufferReadStart = totalSize - buffer.length;
  readFdSlicerNoEof(fdSlicer, buffer, 0, bufferSize, bufferReadStart, function(err) {
    if (err) return callback(err);
    for (var i = bufferSize - eocdrWithoutCommentSize; i >= 0; i -= 1) {
      if (buffer.readUInt32LE(i) !== 0x06054b50) continue;
      // found eocdr
      var eocdrBuffer = buffer.slice(i);

      // 0 - End of central directory signature = 0x06054b50
      // 4 - Number of this disk
      var diskNumber = eocdrBuffer.readUInt16LE(4);
      if (diskNumber !== 0) return callback(new Error("multi-disk zip files are not supported: found disk number: " + diskNumber));
      // 6 - Disk where central directory starts
      // 8 - Number of central directory records on this disk
      // 10 - Total number of central directory records
      var entryCount = eocdrBuffer.readUInt16LE(10);
      // 12 - Size of central directory (bytes)
      // 16 - Offset of start of central directory, relative to start of archive
      var cdOffset = eocdrBuffer.readUInt32LE(16);
      // 20 - Comment length
      var commentLength = eocdrBuffer.readUInt16LE(20);
      var expectedCommentLength = eocdrBuffer.length - eocdrWithoutCommentSize;
      if (commentLength !== expectedCommentLength) {
        return callback(new Error("invalid comment length. expected: " + expectedCommentLength + ". found: " + commentLength));
      }
      // 22 - Comment
      // the encoding is always cp437.
      var comment = bufferToString(eocdrBuffer, 22, eocdrBuffer.length, false);
      return callback(null, new ZipFile(fdSlicer, cdOffset, totalSize, entryCount, comment, options.autoClose));
    }
    callback(new Error("end of central directory record signature not found"));
  });
}

util.inherits(ZipFile, EventEmitter);
function ZipFile(fdSlicer, cdOffset, fileSize, entryCount, comment, autoClose) {
  var self = this;
  EventEmitter.call(self);
  self.fdSlicer = fdSlicer;
  // forward close events
  self.fdSlicer.on("error", function(err) {
    // error closing the fd
    self.emit("error", err);
  });
  self.fdSlicer.once("close", function() {
    self.emit("close");
  });
  self.readEntryCursor = cdOffset;
  self.fileSize = fileSize;
  self.entryCount = entryCount;
  self.comment = comment;
  self.entriesRead = 0;
  self.autoClose = !!autoClose;
  self.isOpen = true;
  // make sure events don't fire outta here until the client has a chance to attach listeners
  setImmediate(function() { readEntries(self); });
}
ZipFile.prototype.close = function() {
  if (!this.isOpen) return;
  this.isOpen = false;
  this.fdSlicer.unref();
};

function emitErrorAndAutoClose(self, err) {
  if (self.autoClose) self.close();
  self.emit("error", err);
}

function readEntries(self) {
  if (self.entryCount === self.entriesRead) {
    // done with metadata
    if (self.autoClose) self.close();
    return self.emit("end");
  }
  var buffer = new Buffer(46);
  readFdSlicerNoEof(self.fdSlicer, buffer, 0, buffer.length, self.readEntryCursor, function(err) {
    if (err) return emitErrorAndAutoClose(self, err);
    var entry = new Entry();
    // 0 - Central directory file header signature
    var signature = buffer.readUInt32LE(0);
    if (signature !== 0x02014b50) return emitErrorAndAutoClose(self, new Error("invalid central directory file header signature: 0x" + signature.toString(16)));
    // 4 - Version made by
    entry.versionMadeBy = buffer.readUInt16LE(4);
    // 6 - Version needed to extract (minimum)
    entry.versionNeededToExtract = buffer.readUInt16LE(6);
    // 8 - General purpose bit flag
    entry.generalPurposeBitFlag = buffer.readUInt16LE(8);
    // 10 - Compression method
    entry.compressionMethod = buffer.readUInt16LE(10);
    // 12 - File last modification time
    entry.lastModFileTime = buffer.readUInt16LE(12);
    // 14 - File last modification date
    entry.lastModFileDate = buffer.readUInt16LE(14);
    // 16 - CRC-32
    entry.crc32 = buffer.readUInt32LE(16);
    // 20 - Compressed size
    entry.compressedSize = buffer.readUInt32LE(20);
    // 24 - Uncompressed size
    entry.uncompressedSize = buffer.readUInt32LE(24);
    // 28 - File name length (n)
    entry.fileNameLength = buffer.readUInt16LE(28);
    // 30 - Extra field length (m)
    entry.extraFieldLength = buffer.readUInt16LE(30);
    // 32 - File comment length (k)
    entry.fileCommentLength = buffer.readUInt16LE(32);
    // 34 - Disk number where file starts
    // 36 - Internal file attributes
    entry.internalFileAttributes = buffer.readUInt16LE(36);
    // 38 - External file attributes
    entry.externalFileAttributes = buffer.readUInt32LE(38);
    // 42 - Relative offset of local file header
    entry.relativeOffsetOfLocalHeader = buffer.readUInt32LE(42);

    self.readEntryCursor += 46;

    buffer = new Buffer(entry.fileNameLength + entry.extraFieldLength + entry.fileCommentLength);
    readFdSlicerNoEof(self.fdSlicer, buffer, 0, buffer.length, self.readEntryCursor, function(err) {
      if (err) return emitErrorAndAutoClose(self, err);
      // 46 - File name
      var isUtf8 = entry.generalPurposeBitFlag & 0x800
      try {
        entry.fileName = bufferToString(buffer, 0, entry.fileNameLength, isUtf8);
      } catch (e) {
        return emitErrorAndAutoClose(self, e);
      }

      // 46+n - Extra field
      var fileCommentStart = entry.fileNameLength + entry.extraFieldLength;
      var extraFieldBuffer = buffer.slice(entry.fileNameLength, fileCommentStart);
      entry.extraFields = [];
      var i = 0;
      while (i < extraFieldBuffer.length) {
        var headerId = extraFieldBuffer.readUInt16LE(i + 0);
        var dataSize = extraFieldBuffer.readUInt16LE(i + 2);
        var dataStart = i + 4;
        var dataEnd = dataStart + dataSize;
        var dataBuffer = new Buffer(dataSize);
        extraFieldBuffer.copy(dataBuffer, 0, dataStart, dataEnd);
        entry.extraFields.push({
          id: headerId,
          data: dataBuffer,
        });
        i = dataEnd;
      }

      // 46+n+m - File comment
      try {
        entry.fileComment = bufferToString(buffer, fileCommentStart, fileCommentStart + entry.fileCommentLength, isUtf8);
      } catch (e) {
        return emitErrorAndAutoClose(self, e);
      }

      self.readEntryCursor += buffer.length;
      self.entriesRead += 1;

      // validate file name
      if (entry.fileName.indexOf("\\") !== -1) return emitErrorAndAutoClose(self, new Error("invalid characters in fileName: " + entry.fileName));
      if (/^[a-zA-Z]:/.test(entry.fileName) || /^\//.test(entry.fileName)) return emitErrorAndAutoClose(self, new Error("absolute path: " + entry.fileName));
      if (entry.fileName.split("/").indexOf("..") !== -1) return emitErrorAndAutoClose(self, new Error("invalid relative path: " + entry.fileName));
      self.emit("entry", entry);
      readEntries(self);
    });
  });
}

ZipFile.prototype.openReadStream = function(entry, callback) {
  var self = this;
  if (!self.isOpen) return self.emit("error", new Error("closed"));
  // make sure we don't lose the fd before we open the actual read stream
  self.fdSlicer.ref();
  var buffer = new Buffer(30);
  readFdSlicerNoEof(self.fdSlicer, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader, function(err) {
    try {
      if (err) return callback(err);
      // 0 - Local file header signature = 0x04034b50
      var signature = buffer.readUInt32LE(0);
      if (signature !== 0x04034b50) return callback(new Error("invalid local file header signature: 0x" + signature.toString(16)));
      // all this should be redundant
      // 4 - Version needed to extract (minimum)
      // 6 - General purpose bit flag
      // 8 - Compression method
      // 10 - File last modification time
      // 12 - File last modification date
      // 14 - CRC-32
      // 18 - Compressed size
      // 22 - Uncompressed size
      // 26 - File name length (n)
      var fileNameLength = buffer.readUInt16LE(26);
      // 28 - Extra field length (m)
      var extraFieldLength = buffer.readUInt16LE(28);
      // 30 - File name
      // 30+n - Extra field
      var localFileHeaderEnd = entry.relativeOffsetOfLocalHeader + buffer.length + fileNameLength + extraFieldLength;
      var filterStream = null;
      if (entry.compressionMethod === 0) {
        // 0 - The file is stored (no compression)
      } else if (entry.compressionMethod === 8) {
        // 8 - The file is Deflated
        filterStream = zlib.createInflateRaw();
      } else {
        return callback(new Error("unsupported compression method: " + entry.compressionMethod));
      }
      var fileDataStart = localFileHeaderEnd;
      var fileDataEnd = fileDataStart + entry.compressedSize;
      if (entry.compressedSize !== 0) {
        // bounds check now, because the read streams will probably not complain loud enough.
        // since we're dealing with an unsigned offset plus an unsigned size,
        // we only have 1 thing to check for.
        if (fileDataEnd > self.fileSize) {
          return callback(new Error("file data overflows file bounds: " +
              fileDataStart + " + " + entry.compressedSize + " > " + self.fileSize));
        }
      }
      var stream = self.fdSlicer.createReadStream({start: fileDataStart, end: fileDataEnd});
      if (filterStream != null) {
        stream = stream.pipe(filterStream);
      }
      callback(null, stream);
    } finally {
      self.fdSlicer.unref();
    }
  });
};

function Entry() {
}
Entry.prototype.getLastModDate = function() {
  return dosDateTimeToDate(this.lastModFileDate, this.lastModFileTime);
};

function dosDateTimeToDate(date, time) {
  var day = date & 0x1f; // 1-31
  var month = (date >> 5 & 0xf) - 1; // 1-12, 0-11
  var year = (date >> 9 & 0x7f) + 1980; // 0-128, 1980-2108

  var millisecond = 0;
  var second = (time & 0x1f) * 2; // 0-29, 0-58 (even numbers)
  var minute = time >> 5 & 0x3f; // 0-59
  var hour = time >> 11 & 0x1f; // 0-23

  return new Date(year, month, day, hour, minute, second, millisecond);
}

function readFdSlicerNoEof(fdSlicer, buffer, offset, length, position, callback) {
  fdSlicer.read(buffer, offset, length, position, function(err, bytesRead) {
    if (err) return callback(err);
    if (bytesRead < length) return callback(new Error("unexpected EOF"));
    callback();
  });
}
var cp437_to_utf8 = new Iconv("cp437", "utf8");
function bufferToString(buffer, start, end, isUtf8) {
  if (isUtf8) {
    return buffer.toString("utf8", start, end);
  } else {
    var slice = buffer.slice(start, end);
    if (slice.length === 0) {
      return "";
    } else {
      return cp437_to_utf8.convert(slice).toString("utf8");
    }
  }
}

function FakeFdSlicer(buffer) {
  // pretend that a buffer is an FdSlicer
  this.buffer = buffer;
}
FakeFdSlicer.prototype.read = function(buffer, offset, length, position, callback) {
  this.buffer.copy(buffer, offset, position, position + length);
  setImmediate(function() {
    callback(null, length);
  });
};
FakeFdSlicer.prototype.createReadStream = function(options) {
  var start = options.start;
  var end = options.end;
  var buffer = new Buffer(end - start);
  this.buffer.copy(buffer, 0, start, end);
  var stream = new PassThrough();
  stream.write(buffer);
  stream.end();
  return stream;
};
// i promise these functions are working properly :|
FakeFdSlicer.prototype.ref = function() {};
FakeFdSlicer.prototype.unref = function() {};
FakeFdSlicer.prototype.on = function() {};
FakeFdSlicer.prototype.once = function() {};

function defaultCallback(err) {
  if (err) throw err;
}
<|endoftext|>