<commit_before>'use strict'

var https = require('https')
var fs = require('fs')
var EventEmitter = require('events').EventEmitter
var util = require('util')
var path = require('path')
var _ = require('lodash')

// TODO: DON'T WRITE UPDATE PERMISSION TO DISK, KEEP IN MEMORY
// Config should be read-only, from root partition
var Updater = function (config) {
  this.config = config
  this.lastSig = null
  this.lastSigTime = null
  this.key = null
  this.ca = null
  this.httpsOptions = null
  this.extractor = require('./extractor').factory(this.config.extractor)
  this.downloading = false
  this.ack = null
  this.deviceId = this._fetchDeviceId()
}

util.inherits(Updater, EventEmitter)
Updater.factory = function factory (config) {
  return new Updater(config)
}

Updater.prototype._fetchDeviceId = function _fetchDeviceId () {
  return fs.readFileSync('/sys/class/net/wlan0/address',
    {encoding: 'utf8'}).trim().replace(/:/g, '-')
}

Updater.prototype.run = function run () {
  if (!this._init()) {
    console.log('Certificate files not available yet, exiting.')
    return
  }
  this._update()
  var self = this
  setInterval(function () { self._update() }, this.config.updateInterval)
  setInterval(function () { self._die() }, this.config.deathInterval)
}

Updater.prototype.acknowledge = function acknowledge (ack) {
  this.ack = ack
}

// private

function fetchVersion () {
  var str = fs.readFileSync('/opt/apps/machine/lamassu-machine/package.json')
  var packageJson = JSON.parse(str)
  return packageJson.version
}

function fetchPackages () {
  try {
    var manifest = JSON.parse(fs.readFileSync('/opt/apps/machine/manifest.json'))
    return manifest.packages || []
  } catch (ex) {
    return []
  }
}

Updater.prototype._init = function _init () {
  var dataPath = path.resolve(__dirname, '..', '..', this.config.dataPath)

  var certs = {
    certFile: path.resolve(dataPath, this.config.certs.certFile),
    keyFile: path.resolve(dataPath, this.config.certs.keyFile)
  }

  if (!fs.existsSync(certs.keyFile) || !fs.existsSync(certs.certFile)) {
    return false
  }

  this.key = fs.readFileSync(certs.keyFile)
  this.cert = fs.readFileSync(certs.certFile)
  var downloadDir = this.config.downloadDir
  var packagePath = this.config.downloadDir + '/update.tar'
  if (fs.existsSync(downloadDir)) {
    if (fs.existsSync(packagePath)) fs.unlinkSync(packagePath)
  } else {
    fs.mkdirSync(downloadDir)
  }

  this.version = fetchVersion()
  this.installedPackages = fetchPackages()

  this.ca = fs.readFileSync(this.config.caFile)
  this.httpsOptions = this._httpsOptions()
  return true
}

Updater.prototype._die = function _die () {
  if (this.downloading) return
  process.exit(0)
}

Updater.prototype._httpsOptions = function _httpsOptions () {
  var config = this.config
  var options = {
    host: config.host,
    port: config.port,
    path: config.path,
    key: this.key,
    cert: this.cert,
    ca: this.ca,
    ciphers: 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH',
    secureProtocol: 'TLSv1_method',
    rejectUnauthorized: true,
    headers: {
      'application-version': this.version,
      'installed-packages': this.installedPackages.join(','),
      'device-id': this.deviceId
    }
  }
  options.agent = new https.Agent(options)
  //  options.agent = false

  return options
}

Updater.prototype._readyForDownload = function _readyForDownload () {
  var t0 = this.lastSigTime
  var t1 = new Date().getTime()
  var timeLock = this.config.timeLock
  var ready = (t0 !== null) && (t1 - t0 > timeLock)
  return ready
}

Updater.prototype._preUpdate = function _preUpdate () {
  var options = this.httpsOptions
  options.method = 'HEAD'
  var self = this
  var req = https.request(options, function (res) {
    var contentSig = res.headers['content-sig']
    if (contentSig !== self.lastSig) {
      self.lastSig = contentSig
      self.lastSigTime = new Date().getTime()
    }
  }).on('error', function (err) { self.emit('error', err) })
  req.end()
}

Updater.prototype._update = function _update () {
  // this._preUpdate()
  // if (this._readyForDownload()) this._download()
  this._download()
}

function noop () {}

Updater.prototype._download = function _download () {
  if (this.downloading) return
  var self = this
  https.get(this.httpsOptions, function (res) {
    var code = res.statusCode
    switch (code) {
      case 304:
        res.resume()
        break
      case 412:
        res.resume()
        self.emit('error', new Error('Server has lower version!'))
        break
      case 200:
        self._downloadFile(res)
        break
      default:
        res.resume()
        this.emit('error', new Error('Unknown response code: ' + code))
    }
  }).on('error', noop)
}

Updater.prototype._downloadFile = function _downloadFile (res) {
  if (this.downloading) return
  this.downloading = true

  var contentVersion = res.headers['content-version']
  var contentSig = res.headers['content-sig']
  var hashListSig = res.headers['content-hash-list-sig']
  /*  if (!this._readyForDownload()) return TODO add back
    if (contentSig !== lastSig) {
      this.emit('error', new Error('Content signature mismatch! lastSig: ' +
          lastSig + ', contentSig: ' + contentSig))
      return
    }
  */
  this.version = contentVersion
  var self = this
  var packagePath = this.config.downloadDir + '/update.tar'
  var fileOut = fs.createWriteStream(packagePath)
  res.pipe(fileOut)
  res.on('end', function () {
    self._extract({rootPath: self.config.extractDir,
      filePath: packagePath, contentSig: contentSig,
    hashListSig: hashListSig})
  })
  res.on('error', function (err) {
    this.downloading = false
    self.emit('error', err)
  })
}

// TODO: Once extraction is complete, signal user to acknowledge
Updater.prototype._extract = function _extract (fileInfo) {
  var self = this
  this.extractor.extract(fileInfo, function (err) {
    if (err) {
      self.downloading = false
      self.emit('error', err)
    } else {
      self.downloading = false
      self._triggerWatchdog()
      console.log('extracted')
    }
  })
}

Updater.prototype._triggerWatchdog = function _triggerWatchdog () {
  var donePath = this.config.extractDir + '/done.txt'
  fs.writeFile(donePath, 'DONE\n', null, function (err) {
    if (err) throw err
    console.log('watchdog triggered')
  })
}

// TODO: This verifies user acknowledgement and proceeds with update execution
Updater.prototype._verifyAck = function _verifyAck () {}

module.exports = Updater

process.on('SIGUSR2', function () {
  // USR1 is reserved by node
  // TODO: more graceful exit
  console.log('Got SIGUSR2. Exiting.')
  process.exit()
})

process.on('SIGTERM', function () {
  // Immune
})

var SOFTWARE_CONFIG_PATH = path.resolve(__dirname, '../../software_config.json')
var DEVICE_CONFIG_PATH = path.resolve(__dirname, '../../device_config.json')

var softwareConfig = JSON.parse(fs.readFileSync(SOFTWARE_CONFIG_PATH))
var deviceConfig = JSON.parse(fs.readFileSync(DEVICE_CONFIG_PATH))
var config = softwareConfig
_.merge(config, deviceConfig)
config.updater.certs = config.brain.certs
config.updater.dataPath = config.brain.dataPath
var up = Updater.factory(config.updater)

up.run()

/*

5. Wait for permission (signed with private key) (*)
6. Verify hashes again (top hash HMAC, hashes of all files) (* -- same as [4])
7. Run script (*)

*/
<commit_msg>set updater.js keepAlive<commit_after>'use strict'

var https = require('https')
var fs = require('fs')
var EventEmitter = require('events').EventEmitter
var util = require('util')
var path = require('path')
var _ = require('lodash')

// TODO: DON'T WRITE UPDATE PERMISSION TO DISK, KEEP IN MEMORY
// Config should be read-only, from root partition
var Updater = function (config) {
  this.config = config
  this.lastSig = null
  this.lastSigTime = null
  this.key = null
  this.ca = null
  this.httpsOptions = null
  this.extractor = require('./extractor').factory(this.config.extractor)
  this.downloading = false
  this.ack = null
  this.deviceId = this._fetchDeviceId()
}

util.inherits(Updater, EventEmitter)
Updater.factory = function factory (config) {
  return new Updater(config)
}

Updater.prototype._fetchDeviceId = function _fetchDeviceId () {
  return fs.readFileSync('/sys/class/net/wlan0/address',
    {encoding: 'utf8'}).trim().replace(/:/g, '-')
}

Updater.prototype.run = function run () {
  if (!this._init()) {
    console.log('Certificate files not available yet, exiting.')
    return
  }
  this._update()
  var self = this
  setInterval(function () { self._update() }, this.config.updateInterval)
  setInterval(function () { self._die() }, this.config.deathInterval)
}

Updater.prototype.acknowledge = function acknowledge (ack) {
  this.ack = ack
}

// private

function fetchVersion () {
  var str = fs.readFileSync('/opt/apps/machine/lamassu-machine/package.json')
  var packageJson = JSON.parse(str)
  return packageJson.version
}

function fetchPackages () {
  try {
    var manifest = JSON.parse(fs.readFileSync('/opt/apps/machine/manifest.json'))
    return manifest.packages || []
  } catch (ex) {
    return []
  }
}

Updater.prototype._init = function _init () {
  var dataPath = path.resolve(__dirname, '..', '..', this.config.dataPath)

  var certs = {
    certFile: path.resolve(dataPath, this.config.certs.certFile),
    keyFile: path.resolve(dataPath, this.config.certs.keyFile)
  }

  if (!fs.existsSync(certs.keyFile) || !fs.existsSync(certs.certFile)) {
    return false
  }

  this.key = fs.readFileSync(certs.keyFile)
  this.cert = fs.readFileSync(certs.certFile)
  var downloadDir = this.config.downloadDir
  var packagePath = this.config.downloadDir + '/update.tar'
  if (fs.existsSync(downloadDir)) {
    if (fs.existsSync(packagePath)) fs.unlinkSync(packagePath)
  } else {
    fs.mkdirSync(downloadDir)
  }

  this.version = fetchVersion()
  this.installedPackages = fetchPackages()

  this.ca = fs.readFileSync(this.config.caFile)
  this.httpsOptions = this._httpsOptions()
  return true
}

Updater.prototype._die = function _die () {
  if (this.downloading) return
  process.exit(0)
}

Updater.prototype._httpsOptions = function _httpsOptions () {
  var config = this.config
  var options = {
    host: config.host,
    port: config.port,
    path: config.path,
    key: this.key,
    cert: this.cert,
    ca: this.ca,
    ciphers: 'AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH',
    secureProtocol: 'TLSv1_method',
    rejectUnauthorized: true,
    headers: {
      'application-version': this.version,
      'installed-packages': this.installedPackages.join(','),
      'device-id': this.deviceId
    }
  }
  options.agent = new https.Agent(options)
  options.agent.keepAlive = true

  return options
}

Updater.prototype._readyForDownload = function _readyForDownload () {
  var t0 = this.lastSigTime
  var t1 = new Date().getTime()
  var timeLock = this.config.timeLock
  var ready = (t0 !== null) && (t1 - t0 > timeLock)
  return ready
}

Updater.prototype._preUpdate = function _preUpdate () {
  var options = this.httpsOptions
  options.method = 'HEAD'
  var self = this
  var req = https.request(options, function (res) {
    var contentSig = res.headers['content-sig']
    if (contentSig !== self.lastSig) {
      self.lastSig = contentSig
      self.lastSigTime = new Date().getTime()
    }
  }).on('error', function (err) { self.emit('error', err) })
  req.end()
}

Updater.prototype._update = function _update () {
  // this._preUpdate()
  // if (this._readyForDownload()) this._download()
  this._download()
}

function noop () {}

Updater.prototype._download = function _download () {
  if (this.downloading) return
  var self = this
  https.get(this.httpsOptions, function (res) {
    var code = res.statusCode
    switch (code) {
      case 304:
        res.resume()
        break
      case 412:
        res.resume()
        self.emit('error', new Error('Server has lower version!'))
        break
      case 200:
        self._downloadFile(res)
        break
      default:
        res.resume()
        this.emit('error', new Error('Unknown response code: ' + code))
    }
  }).on('error', noop)
}

Updater.prototype._downloadFile = function _downloadFile (res) {
  if (this.downloading) return
  this.downloading = true

  var contentVersion = res.headers['content-version']
  var contentSig = res.headers['content-sig']
  var hashListSig = res.headers['content-hash-list-sig']
  /*  if (!this._readyForDownload()) return TODO add back
    if (contentSig !== lastSig) {
      this.emit('error', new Error('Content signature mismatch! lastSig: ' +
          lastSig + ', contentSig: ' + contentSig))
      return
    }
  */
  this.version = contentVersion
  var self = this
  var packagePath = this.config.downloadDir + '/update.tar'
  var fileOut = fs.createWriteStream(packagePath)
  res.pipe(fileOut)
  res.on('end', function () {
    if (res.complete) {
      self._extract({rootPath: self.config.extractDir,
                     filePath: packagePath, contentSig: contentSig,
                     hashListSig: hashListSig})
    }
    else {
      console.log('[WARN]: The update file we\'ve received is incomplete. ' +
                  'The connection was terminated before the whole content ' +
                  'was transmitted. The update will NOT continue.')
    }
  })
  res.on('error', function (err) {
    this.downloading = false
    self.emit('error', err)
  })
}

// TODO: Once extraction is complete, signal user to acknowledge
Updater.prototype._extract = function _extract (fileInfo) {
  var self = this
  this.extractor.extract(fileInfo, function (err) {
    if (err) {
      self.downloading = false
      self.emit('error', err)
    } else {
      self.downloading = false
      self._triggerWatchdog()
      console.log('extracted')
    }
  })
}

Updater.prototype._triggerWatchdog = function _triggerWatchdog () {
  var donePath = this.config.extractDir + '/done.txt'
  fs.writeFile(donePath, 'DONE\n', null, function (err) {
    if (err) throw err
    console.log('watchdog triggered')
  })
}

// TODO: This verifies user acknowledgement and proceeds with update execution
Updater.prototype._verifyAck = function _verifyAck () {}

module.exports = Updater

process.on('SIGUSR2', function () {
  // USR1 is reserved by node
  // TODO: more graceful exit
  console.log('Got SIGUSR2. Exiting.')
  process.exit()
})

process.on('SIGTERM', function () {
  // Immune
})

var SOFTWARE_CONFIG_PATH = path.resolve(__dirname, '../../software_config.json')
var DEVICE_CONFIG_PATH = path.resolve(__dirname, '../../device_config.json')

var softwareConfig = JSON.parse(fs.readFileSync(SOFTWARE_CONFIG_PATH))
var deviceConfig = JSON.parse(fs.readFileSync(DEVICE_CONFIG_PATH))
var config = softwareConfig
_.merge(config, deviceConfig)
config.updater.certs = config.brain.certs
config.updater.dataPath = config.brain.dataPath
var up = Updater.factory(config.updater)

up.run()

/*

5. Wait for permission (signed with private key) (*)
6. Verify hashes again (top hash HMAC, hashes of all files) (* -- same as [4])
7. Run script (*)

*/
<|endoftext|>


<commit_before>var devices = require('./database').collection('devices');

module.exports = function(socket, callback) {

  var newTimestamp = new Date().getTime();
  console.log("update socket: " + JSON.stringify(socket));
  if(socket.uuid && socket.token){

    devices.update({
      uuid: {"$regex":socket.uuid,"$options":"i"}, token: socket.token
    }, {
      $set: {socketId: socket.socketid, online: true, timestamp: newTimestamp, ipAddress: socket.ipAddress, protocol: socket.protocol}
    }, function(err, saved) {
      if(err || saved === 0 || (saved && !saved.updatedExisting)) {
      // if(err || saved === 0) {
        console.log('Device not found or token not valid for: ' + socket.uuid);
        console.log('Error: ' + err);
        callback({uuid: socket.uuid, status: 401});

      } else {
        console.log('SocketId and presence updated for device: ' + socket.uuid);
        callback({uuid: socket.uuid, status: 201});
      }

    });

  } else {

    if(socket.uuid){
      callback({uuid: socket.uuid, status: 401});
    } else {
      // auto-register device if UUID not provided on authentication
      require('./register')({"autoRegister":true, socketId: socket.socketid, online: true, timestamp: newTimestamp, ipAddress: socket.ipAddress}, function(results){
        console.log('Device auto-registered: ' + results.uuid)
        console.log(results);
        callback({uuid: results.uuid, token: results.token, status: 201});
      });
    }

  }

};
<commit_msg>changed timezone format and added luis update back in<commit_after>var devices = require('./database').collection('devices');

module.exports = function(socket, callback) {

  var newTimestamp = new Date().getTime();
  console.log("update socket: " + JSON.stringify(socket));
  if(socket.uuid && socket.token){

    devices.update({
      uuid: {"$regex":socket.uuid,"$options":"i"}, token: socket.token
    }, {
      $set: {socketId: socket.socketid, online: true, timestamp: newTimestamp, ipAddress: socket.ipAddress, protocol: socket.protocol}
    }, function(err, saved) {
      if(err || saved === 0 || (saved && !saved.updatedExisting)) {
      // if(err || saved === 0) {
        console.log('SAVED', saved);
        console.log('Device not found or token not valid for: ' + socket.uuid);
        console.log('Error: ' + err);
        callback({uuid: socket.uuid, status: 401});

      } else {
        console.log('SocketId and presence updated for device: ' + socket.uuid);
        callback({uuid: socket.uuid, status: 201});
      }

    });

  } else {

    if(socket.uuid){
      callback({uuid: socket.uuid, status: 401});
    } else {
      // auto-register device if UUID not provided on authentication
      require('./register')({"autoRegister":true, socketId: socket.socketid, online: true, timestamp: newTimestamp, ipAddress: socket.ipAddress}, function(results){
        console.log('Device auto-registered: ' + results.uuid)
        console.log(results);
        callback({uuid: results.uuid, token: results.token, status: 201});
      });
    }

  }

};
<|endoftext|>


<commit_before>window.LocalityModal = (function () {
    "use strict";
    // private variables and functions

    // constructor
    var module = function () {

        this.$modal = $('#sidebar-info');
        this.$modal.html(this.template);
        this.$modal_head = this.$modal.find('.sidebar-info-header');
        this.$modal_body = this.$modal.find('.sidebar-info-body');
        this.$modal_footer = this.$modal.find('.sidebar-info-footer');

        this._bindExternalEvents();
        this._bindInternalEvents();

    };

    // prototype
    module.prototype = {
        constructor: module,
        template : [
            '

',
            '
Click a marker on the map to load locality info
',
            '

',
        ].join(''),

        // HARDCODED DOMAIN NAME
        DOMAIN: 'Health',

        _bindInternalEvents: function() {
            var self = this;
            this.$modal.on('hide.bs.modal', function (evt) {
                // remove marker layer from the map
                $APP.trigger('map.remove.point');
            })

            this.$modal.on('handle-xhr-error', this.handleXHRError.bind(this));

            this.$modal.on('get-info', this.getInfo.bind(this));
            this.$modal.on('show-info', this.showInfo.bind(this));
            this.$modal.on('show-info-adjust', this.setInfoWindowHeight.bind(this));
            this.$modal.on('show-edit', this.showEdit.bind(this));

            this.$modal.on('create-new', this.createNewLocality.bind(this));
            this.$modal.on('show-create', this.showCreate.bind(this));
            this.$modal.on('update-coordinates', this.updateCoordinates.bind(this));

            this.$modal_footer.on('click', '.edit', this.showEditForm.bind(this));
            this.$modal_footer.on('click', '.save', this.saveForm.bind(this));
            this.$modal_footer.on('click', '.cancel', this.cancelEdit.bind(this));

            this.$modal_footer.on('click', '.nl-execute', function() {
                var val = $('#nl-form-1').val();
                if (val === '1') {
                    self.showEditForm.call(self);
                }
                if (val === '2') {
                    var val2 = $('#nl-form-2').val();
                    if (val2 === '1') {
                        var loc = 'https://twitter.com/intent/tweet?text=See%20'+self.locality_data.values.name+'.%20Please%20validate&url=http://healthsites.io/'+ self.locality_id +'/';
                        self.sendTweet(loc);
                    }
                }
                if (val === '3') {
                    var val2 = $('#nl-form-2').val();
                    if (val2 === '1') {
                        var loc = 'https://twitter.com/intent/tweet?text=See%20'+self.locality_data.values.name+'&url=http://healthsites.io/'+ self.locality_id +'/';
                        self.sendTweet(loc);
                    }
                }
            });

            this.$modal_footer.on('click', '.save-create', this.saveCreateForm.bind(this));
            this.$modal_footer.on('click', '.cancel-create', this.cancelCreate.bind(this));

            this.$modal_head.on('mouseover', '.label-status', function() {
                self.$modal_head.find('.modal-info-information').addClass('modal-info-expanded').animate({height: "100px"}, 500);
            });

            this.$modal_head.on('mouseout', '.label-status', function() {
                self.$modal_head.find('.modal-info-information').animate({height: "1px"}, 100).removeClass('modal-info-expanded');
            });
        },

        handleXHRError: function(evt, payload) {
            var xhr = payload['xhr'];

            // show error notification
            if (xhr.status === 403 ) {
                alert('Please login before continuing!');
            } else {
                alert('Error processing XHR request!');
            }
        },

        updateCoordinates: function (evt, payload) {
            // set new values
            $('#id_lon').val(payload.latlng.lng);
            $('#id_lat').val(payload.latlng.lat);
        },

        sendTweet: function(text) {
            var windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes';
            var width = 550;
            var height = 420;
            var winHeight = screen.height;
            var winWidth = screen.width;
            var left = Math.round((winWidth / 2) - (width / 2));
            var top = 0;
            if (winHeight > height) {
                top = Math.round((winHeight / 2) - (height / 2));
            }
            window.open(text, 'intent', windowOptions + ',width=' + width + ',height=' + height + ',left=' + left + ',top=' + top);
        },

        cancelEdit: function(evt) {
            $APP.trigger('map.cancel.edit');
            // 'show' modal window backdrop
            this.showInfo();
        },

        cancelCreate: function(evt) {
            $APP.trigger('map.cancel.edit');
        },

        showInfo: function(evt) {
            var modal_head = [
                '
',
                    'This information is considered ',
                    'current. ',
                    'Why?',
                    //'',
                '
',
                '
',
                    '
- It was manualy verified by trusted user
',
                    '
- It was been verified using social harvesting
',
                    '
- 13 people have verfified its existance in last 3 months
',
                '
',
            ].join('');
            // placeholder for info quality
            //this.$modal_head.html(modal_head);
            this.$modal_body.html(this.locality_data.repr);
            this.$modal_footer.html([
                '',
                '<button type="button" id="nl-execute" class="btn btn-xs btn-success nl-execute"> GO </button>',
            ].join(''));
            var form = new NLForm( document.getElementById( 'nl-form' ) );
            $('#sidebar').addClass('active');
            $('#sidebar-helper').addClass('active');
            $('#collapseInfo').collapse('show');
            this.$modal.trigger('show-info-adjust');
        },

        setInfoWindowHeight: function() {
            var nlfH =  this.$modal_footer.height();
            this.$modal_body.height($(window).height() - this.$modal_body.offset().top - nlfH);
        },

        getInfo: function(evt) {
            var self = this;
            $.getJSON('/localities/' + this.locality_id, function (data) {
                self.locality_data = data;
                self.$modal.trigger('show-info');
                // show selected point on the map
                $APP.trigger('map.show.point', {'geom':[data.geom[1], data.geom[0]]});
            });
        },

        showEdit: function(evt, payload) {
            this.$modal_body.html(payload.data);
            this.$modal_footer.html([
                '<button type="button" class="btn btn-default cancel">Cancel</button>',
                '<button type="button" class="btn btn-warning save">Save changes</button>'
            ].join(''))

            this.$modal_body.find('input:text').filter(function() { return $(this).val() == ""; }).each(function() {
                $(this).parents('.form-group').detach().appendTo('#empty-form-tab');
            });
            $.material.init();

        },

        showCreate: function(evt, payload) {
            this.$modal_body.html(payload.data);
            if (payload.geom) {
                $('#id_lon').val(payload.geom[1]);
                $('#id_lat').val(payload.geom[0]);
            }

            this.$modal_footer.html([
                '<button type="button" class="btn btn-default cancel-create">Cancel</button>',
                '<button type="button" class="btn btn-warning save-create">Create</button>'
            ].join(''))

        },

        showEditForm: function(evt) {
            var self = this;
            $.get('/localities/'+this.locality_id+'/form', function (data) {
                self.$modal.trigger('show-edit', {'data':data});
            }).fail(function(xhr, status, error) {
                self.$modal.trigger('handle-xhr-error', {'xhr': xhr});
            });
        },

        createNewLocality: function(evt, payload) {
            var self = this;
            $.get('/localities/form/'+this.DOMAIN, function (data) {
                $APP.trigger('map.show.point', {'geom':[payload.data.lat, payload.data.lng]});
                self.$modal.trigger('show-create', {'data':data, 'geom':[payload.data.lat, payload.data.lng] });
            });
        },

        saveForm: function(evt) {
            var self = this;
            var form = this.$modal_body.find('form');
            var latlng = [$('#id_lat').val(), $('#id_lon').val()];
            var data = form.serializeArray();

            $.ajax('/localities/'+this.locality_id+'/form', {
                'type': 'POST',
                'data': data,
                'success': function (data, status, xhr) {
                    if (data !== 'OK') {
                        // there were some form processing errors
                        self.$modal.trigger('show-edit', {'data': data});
                    } else {
                        self.$modal.trigger('get-info');
                    }
                },
                'error': function (xhr, status, error) {
                    self.$modal.trigger('handle-xhr-error', {'xhr': xhr});
                }
            })
        },

        saveCreateForm: function(evt) {
            var self = this;
            var form = this.$modal_body.find('form');

            $.ajax('/localities/form/'+this.DOMAIN, {
                'type': 'POST',
                'data': form.serializeArray(),
                'success': function (data, status, xhr) {
                    // try to parse data as an integer (ID)
                    var new_loc_id = parseInt(data, 10);
                    var latlng = [$('#id_lat').val(), $('#id_lon').val()];
                    if (isNaN(new_loc_id)) {
                        // there were some form processing errors
                        self.$modal.trigger('show-create', {'data': data});
                    } else {
                        // hide form...
                        // trigger create cleanup
                        self.$modal.modal('hide');
                        $APP.trigger('locality.created', {
                            'loc_id': new_loc_id, 'latlng': latlng
                        });
                    }
                },
                'error': function (xhr, status, error) {
                    self.$modal.trigger('handle-xhr-error', {'xhr': xhr});
                }
            })
        },

        _bindExternalEvents: function () {
            var self = this;

            $APP.on('locality.map.click', function (evt, payload) {
                self.locality_id = payload.locality_id;
                self.$modal.trigger('get-info');
            });
            $APP.on('locality.map.move', function (evt, payload) {
                self.$modal.trigger('update-coordinates', payload);
            });

            $APP.on('locality.new.create', function (evt, payload) {
                self.$modal.trigger('create-new', payload);
            });
            $APP.on('locality.show-info-adjust', function(evt, payload) {
                self.$modal.trigger('show-info-adjust');
            });
        }
    }

// return module
return module;
}());
<commit_msg>added some space below info footer<commit_after>window.LocalityModal = (function () {
    "use strict";
    // private variables and functions

    // constructor
    var module = function () {

        this.$modal = $('#sidebar-info');
        this.$modal.html(this.template);
        this.$modal_head = this.$modal.find('.sidebar-info-header');
        this.$modal_body = this.$modal.find('.sidebar-info-body');
        this.$modal_footer = this.$modal.find('.sidebar-info-footer');

        this._bindExternalEvents();
        this._bindInternalEvents();

    };

    // prototype
    module.prototype = {
        constructor: module,
        template : [
            '

',
            '
Click a marker on the map to load locality info
',
            '

',
        ].join(''),

        // HARDCODED DOMAIN NAME
        DOMAIN: 'Health',

        _bindInternalEvents: function() {
            var self = this;
            this.$modal.on('hide.bs.modal', function (evt) {
                // remove marker layer from the map
                $APP.trigger('map.remove.point');
            })

            this.$modal.on('handle-xhr-error', this.handleXHRError.bind(this));

            this.$modal.on('get-info', this.getInfo.bind(this));
            this.$modal.on('show-info', this.showInfo.bind(this));
            this.$modal.on('show-info-adjust', this.setInfoWindowHeight.bind(this));
            this.$modal.on('show-edit', this.showEdit.bind(this));

            this.$modal.on('create-new', this.createNewLocality.bind(this));
            this.$modal.on('show-create', this.showCreate.bind(this));
            this.$modal.on('update-coordinates', this.updateCoordinates.bind(this));

            this.$modal_footer.on('click', '.edit', this.showEditForm.bind(this));
            this.$modal_footer.on('click', '.save', this.saveForm.bind(this));
            this.$modal_footer.on('click', '.cancel', this.cancelEdit.bind(this));

            this.$modal_footer.on('click', '.nl-execute', function() {
                var val = $('#nl-form-1').val();
                if (val === '1') {
                    self.showEditForm.call(self);
                }
                if (val === '2') {
                    var val2 = $('#nl-form-2').val();
                    if (val2 === '1') {
                        var loc = 'https://twitter.com/intent/tweet?text=See%20'+self.locality_data.values.name+'.%20Please%20validate&url=http://healthsites.io/'+ self.locality_id +'/';
                        self.sendTweet(loc);
                    }
                }
                if (val === '3') {
                    var val2 = $('#nl-form-2').val();
                    if (val2 === '1') {
                        var loc = 'https://twitter.com/intent/tweet?text=See%20'+self.locality_data.values.name+'&url=http://healthsites.io/'+ self.locality_id +'/';
                        self.sendTweet(loc);
                    }
                }
            });

            this.$modal_footer.on('click', '.save-create', this.saveCreateForm.bind(this));
            this.$modal_footer.on('click', '.cancel-create', this.cancelCreate.bind(this));

            this.$modal_head.on('mouseover', '.label-status', function() {
                self.$modal_head.find('.modal-info-information').addClass('modal-info-expanded').animate({height: "100px"}, 500);
            });

            this.$modal_head.on('mouseout', '.label-status', function() {
                self.$modal_head.find('.modal-info-information').animate({height: "1px"}, 100).removeClass('modal-info-expanded');
            });
        },

        handleXHRError: function(evt, payload) {
            var xhr = payload['xhr'];

            // show error notification
            if (xhr.status === 403 ) {
                alert('Please login before continuing!');
            } else {
                alert('Error processing XHR request!');
            }
        },

        updateCoordinates: function (evt, payload) {
            // set new values
            $('#id_lon').val(payload.latlng.lng);
            $('#id_lat').val(payload.latlng.lat);
        },

        sendTweet: function(text) {
            var windowOptions = 'scrollbars=yes,resizable=yes,toolbar=no,location=yes';
            var width = 550;
            var height = 420;
            var winHeight = screen.height;
            var winWidth = screen.width;
            var left = Math.round((winWidth / 2) - (width / 2));
            var top = 0;
            if (winHeight > height) {
                top = Math.round((winHeight / 2) - (height / 2));
            }
            window.open(text, 'intent', windowOptions + ',width=' + width + ',height=' + height + ',left=' + left + ',top=' + top);
        },

        cancelEdit: function(evt) {
            $APP.trigger('map.cancel.edit');
            // 'show' modal window backdrop
            this.showInfo();
        },

        cancelCreate: function(evt) {
            $APP.trigger('map.cancel.edit');
        },

        showInfo: function(evt) {
            var modal_head = [
                '
',
                    'This information is considered ',
                    'current. ',
                    'Why?',
                    //'',
                '
',
                '
',
                    '
- It was manualy verified by trusted user
',
                    '
- It was been verified using social harvesting
',
                    '
- 13 people have verfified its existance in last 3 months
',
                '
',
            ].join('');
            // placeholder for info quality
            //this.$modal_head.html(modal_head);
            this.$modal_body.html(this.locality_data.repr);
            this.$modal_footer.html([
                '',
                '<button type="button" id="nl-execute" class="btn btn-xs btn-success nl-execute"> GO </button>',
            ].join(''));
            var form = new NLForm( document.getElementById( 'nl-form' ) );
            $('#sidebar').addClass('active');
            $('#sidebar-helper').addClass('active');
            $('#collapseInfo').collapse('show');
            this.$modal.trigger('show-info-adjust');
        },

        setInfoWindowHeight: function() {
            var nlfH =  this.$modal_footer.height() + 15;
            this.$modal_body.height($(window).height() - this.$modal_body.offset().top - nlfH);
        },

        getInfo: function(evt) {
            var self = this;
            $.getJSON('/localities/' + this.locality_id, function (data) {
                self.locality_data = data;
                self.$modal.trigger('show-info');
                // show selected point on the map
                $APP.trigger('map.show.point', {'geom':[data.geom[1], data.geom[0]]});
            });
        },

        showEdit: function(evt, payload) {
            this.$modal_body.html(payload.data);
            this.$modal_footer.html([
                '<button type="button" class="btn btn-default cancel">Cancel</button>',
                '<button type="button" class="btn btn-warning save">Save changes</button>'
            ].join(''))

            this.$modal_body.find('input:text').filter(function() { return $(this).val() == ""; }).each(function() {
                $(this).parents('.form-group').detach().appendTo('#empty-form-tab');
            });
            $.material.init();

        },

        showCreate: function(evt, payload) {
            this.$modal_body.html(payload.data);
            if (payload.geom) {
                $('#id_lon').val(payload.geom[1]);
                $('#id_lat').val(payload.geom[0]);
            }

            this.$modal_footer.html([
                '<button type="button" class="btn btn-default cancel-create">Cancel</button>',
                '<button type="button" class="btn btn-warning save-create">Create</button>'
            ].join(''))

        },

        showEditForm: function(evt) {
            var self = this;
            $.get('/localities/'+this.locality_id+'/form', function (data) {
                self.$modal.trigger('show-edit', {'data':data});
            }).fail(function(xhr, status, error) {
                self.$modal.trigger('handle-xhr-error', {'xhr': xhr});
            });
        },

        createNewLocality: function(evt, payload) {
            var self = this;
            $.get('/localities/form/'+this.DOMAIN, function (data) {
                $APP.trigger('map.show.point', {'geom':[payload.data.lat, payload.data.lng]});
                self.$modal.trigger('show-create', {'data':data, 'geom':[payload.data.lat, payload.data.lng] });
            });
        },

        saveForm: function(evt) {
            var self = this;
            var form = this.$modal_body.find('form');
            var latlng = [$('#id_lat').val(), $('#id_lon').val()];
            var data = form.serializeArray();

            $.ajax('/localities/'+this.locality_id+'/form', {
                'type': 'POST',
                'data': data,
                'success': function (data, status, xhr) {
                    if (data !== 'OK') {
                        // there were some form processing errors
                        self.$modal.trigger('show-edit', {'data': data});
                    } else {
                        self.$modal.trigger('get-info');
                    }
                },
                'error': function (xhr, status, error) {
                    self.$modal.trigger('handle-xhr-error', {'xhr': xhr});
                }
            })
        },

        saveCreateForm: function(evt) {
            var self = this;
            var form = this.$modal_body.find('form');

            $.ajax('/localities/form/'+this.DOMAIN, {
                'type': 'POST',
                'data': form.serializeArray(),
                'success': function (data, status, xhr) {
                    // try to parse data as an integer (ID)
                    var new_loc_id = parseInt(data, 10);
                    var latlng = [$('#id_lat').val(), $('#id_lon').val()];
                    if (isNaN(new_loc_id)) {
                        // there were some form processing errors
                        self.$modal.trigger('show-create', {'data': data});
                    } else {
                        // hide form...
                        // trigger create cleanup
                        self.$modal.modal('hide');
                        $APP.trigger('locality.created', {
                            'loc_id': new_loc_id, 'latlng': latlng
                        });
                    }
                },
                'error': function (xhr, status, error) {
                    self.$modal.trigger('handle-xhr-error', {'xhr': xhr});
                }
            })
        },

        _bindExternalEvents: function () {
            var self = this;

            $APP.on('locality.map.click', function (evt, payload) {
                self.locality_id = payload.locality_id;
                self.$modal.trigger('get-info');
            });
            $APP.on('locality.map.move', function (evt, payload) {
                self.$modal.trigger('update-coordinates', payload);
            });

            $APP.on('locality.new.create', function (evt, payload) {
                self.$modal.trigger('create-new', payload);
            });
            $APP.on('locality.show-info-adjust', function(evt, payload) {
                self.$modal.trigger('show-info-adjust');
            });
        }
    }

// return module
return module;
}());
<|endoftext|>


<commit_before>import {citeprocSys} from "./citeproc-sys"
import {citationDefinitions} from "../style/citation-definitions"
import CSL from "citeproc"

/*
* Use CSL and bibDB to format all citations for the given prosemirror json citation nodes
*/
export class FormatCitations {
    constructor(allCitationInfos, citationStyle, bibDB) {
        this.allCitationInfos = allCitationInfos
        this.citationStyle = citationStyle
        this.bibDB = bibDB
    }

    init() {
        this.bibliography = false
        this.citations = []
        this.bibFormats = []
        this.citationTexts = []
        this.citationType = ''
        this.formatAllCitations()
        return this.getFormattedCitations()
    }

    formatAllCitations() {
        let that = this
        this.allCitationInfos.forEach(cInfo => {
            that.bibFormats.push(cInfo.format)
            that.citations.push({
                citationItems: cInfo.references,
                properties: {
                    noteIndex: that.bibFormats.length
                }
            })
        })
    }

    get bibHTML() {
        // HTML
        let html = '', bib = this.bibliography
        html += '

'
        // Add entries to bibliography
        html += bib[0].bibstart + bib[1].join('') + bib[0].bibend
        return html
    }

        // CSS
    get bibCSS()  {
        let css = '\n', bibInfo = this.bibliography[0]
            css += `.csl-entry {margin-bottom: ${bibInfo.entryspacing+1}em;}\n`
            css += `.csl-bib-body {line-height: ${bibInfo.linespacing};}\n`
            if (bibInfo.hangingindent) {
                css += `
                    .csl-entry {
                        text-indent: -0.5in;
                        margin-left: 0.5in;
                    }\n`
            } else if(bibInfo["second-field-align"] === 'margin') {
                css += `
                    .csl-left-margin {
                        text-indent: -${bibInfo.maxoffset}ch;
                        width: ${bibInfo.maxoffset}ch;
                    }
                `
            } else if(bibInfo["second-field-align"] === 'flush') {
                css += `
                    .csl-left-margin {
                        width: ${bibInfo.maxoffset}ch;
                    }
                `
            }
        return css
    }

    reloadCitations(missingItems) {
        // Not all citations could be found in the database.
        // Reload the database, but don't cycle if no new matches are found.
        return this.bibDB.getDB().then(() => {
            if (missingItems.some(item => this.bibDB.db.hasOwnProperty(item))) {
                return this.init()
            } else {
                return Promise.resolve()
            }
        })
    }

    getFormattedCitations() {
        if (citationDefinitions.styles.hasOwnProperty(this.citationStyle)) {
            this.citationStyle = citationDefinitions.styles[this.citationStyle]
        } else {
            for (let styleName in citationDefinitions.styles) {
                this.citationStyle = citationDefinitions.styles[styleName]
                break
            }
        }
        let citeprocConnector = new citeprocSys(this.bibDB)
        let citeprocInstance = new CSL.Engine(
            citeprocConnector,
            this.citationStyle.definition
        )
        let allIds = []
        this.citations.forEach(cit =>
            cit.citationItems.forEach(item => allIds.push('' + item.id))
        )
        citeprocInstance.updateItems(allIds)

        let inText = citeprocInstance.cslXml.dataObj.attrs.class === 'in-text'
        let len = this.citations.length
        for (let i = 0; i < len; i++) {
            let citation = this.citations[i],
                citationText = citeprocInstance.appendCitationCluster(citation, true)
            if (inText && 'textcite' == this.bibFormats[i]) {
                let newCiteText = '',
                    items = citation.citationItems,
                    len2 = items.length

                for (let j = 0; j < len2; j++) {
                    let onlyNameOption = [{
                        id: items[j].id,
                        "author-only": 1
                    }]

                    let onlyDateOption = [{
                        id: items[j].id,
                        "suppress-author": 1
                    }]

                    if (items[j].locator) {
                        onlyDateOption[0].locator = items[j].locator
                    }

                    if (items[j].prefix) {
                        onlyDateOption[0].prefix = items[j].prefix
                    }

                    if (0 < j) {
                        newCiteText += '; '
                    }
                    newCiteText += citeprocInstance.makeCitationCluster(onlyNameOption)
                    newCiteText += ' ' + citeprocInstance.makeCitationCluster(onlyDateOption)
                }
                citationText[0].push(newCiteText)
            }
            this.citationTexts.push(citationText)
        }
        this.citationType = citeprocInstance.cslXml.dataObj.attrs.class
        this.bibliography = citeprocInstance.makeBibliography()

        if (citeprocConnector.missingItems.length > 0) {
            return this.reloadCitations(citeprocConnector.missingItems)
        } else {
            return Promise.resolve()
        }

    }
}
<commit_msg>fix inlien citations<commit_after>import {citeprocSys} from "./citeproc-sys"
import {citationDefinitions} from "../style/citation-definitions"
import CSL from "citeproc"

/*
* Use CSL and bibDB to format all citations for the given prosemirror json citation nodes
*/
export class FormatCitations {
    constructor(allCitationInfos, citationStyle, bibDB) {
        this.allCitationInfos = allCitationInfos
        this.citationStyle = citationStyle
        this.bibDB = bibDB
    }

    init() {
        this.bibliography = false
        this.citations = []
        this.bibFormats = []
        this.citationTexts = []
        this.citationType = ''
        this.formatAllCitations()
        return this.getFormattedCitations()
    }

    formatAllCitations() {
        this.allCitationInfos.forEach(cInfo => {
            this.bibFormats.push(cInfo.format)
            this.citations.push({
                citationItems: cInfo.references,
                properties: {
                    noteIndex: this.bibFormats.length
                }
            })
        })
    }

    get bibHTML() {
        // HTML
        let html = '', bib = this.bibliography
        html += '

'
        // Add entries to bibliography
        html += bib[0].bibstart + bib[1].join('') + bib[0].bibend
        return html
    }

        // CSS
    get bibCSS()  {
        let css = '\n', bibInfo = this.bibliography[0]
            css += `.csl-entry {margin-bottom: ${bibInfo.entryspacing+1}em;}\n`
            css += `.csl-bib-body {line-height: ${bibInfo.linespacing};}\n`
            if (bibInfo.hangingindent) {
                css += `
                    .csl-entry {
                        text-indent: -0.5in;
                        margin-left: 0.5in;
                    }\n`
            } else if(bibInfo["second-field-align"] === 'margin') {
                css += `
                    .csl-left-margin {
                        text-indent: -${bibInfo.maxoffset}ch;
                        width: ${bibInfo.maxoffset}ch;
                    }
                `
            } else if(bibInfo["second-field-align"] === 'flush') {
                css += `
                    .csl-left-margin {
                        width: ${bibInfo.maxoffset}ch;
                    }
                `
            }
        return css
    }

    reloadCitations(missingItems) {
        // Not all citations could be found in the database.
        // Reload the database, but don't cycle if no new matches are found.
        return this.bibDB.getDB().then(() => {
            if (missingItems.some(item => this.bibDB.db.hasOwnProperty(item))) {
                return this.init()
            } else {
                return Promise.resolve()
            }
        })
    }

    getFormattedCitations() {
        if (citationDefinitions.styles.hasOwnProperty(this.citationStyle)) {
            this.citationStyle = citationDefinitions.styles[this.citationStyle]
        } else {
            for (let styleName in citationDefinitions.styles) {
                this.citationStyle = citationDefinitions.styles[styleName]
                break
            }
        }
        let citeprocConnector = new citeprocSys(this.bibDB)
        let citeprocInstance = new CSL.Engine(
            citeprocConnector,
            this.citationStyle.definition
        )
        let allIds = []
        this.citations.forEach(cit =>
            cit.citationItems.forEach(item => allIds.push('' + item.id))
        )
        citeprocInstance.updateItems(allIds)

        let inText = citeprocInstance.cslXml.dataObj.attrs.class === 'in-text'
        let len = this.citations.length
        for (let i = 0; i < len; i++) {
            let citation = this.citations[i],
                citationText = citeprocInstance.appendCitationCluster(citation, true)
            if (inText && 'textcite' == this.bibFormats[i]) {
                let newCiteText = '',
                    items = citation.citationItems,
                    len2 = items.length

                for (let j = 0; j < len2; j++) {
                    let onlyNameOption = [{
                        id: items[j].id,
                        "author-only": 1
                    }]

                    let onlyDateOption = [{
                        id: items[j].id,
                        "suppress-author": 1
                    }]

                    if (items[j].locator) {
                        onlyDateOption[0].locator = items[j].locator
                    }

                    if (items[j].prefix) {
                        onlyDateOption[0].prefix = items[j].prefix
                    }

                    if (0 < j) {
                        newCiteText += '; '
                    }
                    newCiteText += `${citeprocInstance.makeCitationCluster(onlyNameOption)} ${citeprocInstance.makeCitationCluster(onlyDateOption)}`
                }
                citationText[0][1] = newCiteText
            }
            this.citationTexts.push(citationText)
        }
        this.citationType = citeprocInstance.cslXml.dataObj.attrs.class
        this.bibliography = citeprocInstance.makeBibliography()

        if (citeprocConnector.missingItems.length > 0) {
            return this.reloadCitations(citeprocConnector.missingItems)
        } else {
            return Promise.resolve()
        }

    }
}
<|endoftext|>


<commit_before>var cordova = require('cordova');

 /* definition of named properties expected by the native side,
    all arrays are stored in order of how they are received from common js code.
    When other platforms evolve to using named args this will be removed.
 */

/**
 * Execute a cordova command.  It is up to the native side whether this action
 * is synchronous or asynchronous.  The native side can return:
 *      Synchronous: PluginResult object as a JSON string
 *      Asynchrounous: Empty string ""
 * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
 * depending upon the result of the action.
 *
 * @param {Function} success    The success callback
 * @param {Function} fail       The fail callback
 * @param {String} service      The name of the service to use
 * @param {String} action       Action to be run in cordova
 * @param {String[]} [args]     Zero or more arguments to pass to the method

 */

var CommandProxy  = {

    "Accelerometer": require('cordova/plugin/win8metro/AccelerometerProxy'),
    "Camera":require('cordova/plugin/win8metro/CameraProxy'),
    "Compass":require('cordova/plugin/win8metro/CompassProxy'),
    "Device":require('cordova/plugin/win8metro/DeviceProxy'),
    "File":require('cordova/plugin/win8metro/FileProxy'),
    "NetworkStatus":require('cordova/plugin/win8metro/NetworkStatusProxy')
};

var Exec = function(success, fail, service, action, args) {

    if(CommandProxy[service] && CommandProxy[service][action]) {

        var callbackId = service + cordova.callbackId++;
        console.log("EXEC:" + service + " : " + action);

        if (typeof success == "function" || typeof fail == "function") {
            cordova.callbacks[callbackId] = {success:success, fail:fail};
        }
        // pass it on to Notify
        try {
            CommandProxy [service][action](success, fail, args);
        }
        catch(e) {
            console.log("Exception calling native with command :: " + service + " :: " + action  + " ::exception=" + e);
        }
    }
    else
    {
        if(fail) { fail("Missing Command Error"); }
    }

};

Exec.addCommandProxy = function(id,proxyObj) {

    CommandProxy[id] = proxyObj;
};

module.exports = Exec;

<commit_msg>addCommandProxy+removeCommandProxy<commit_after>var cordova = require('cordova');

 /* definition of named properties expected by the native side,
    all arrays are stored in order of how they are received from common js code.
    When other platforms evolve to using named args this will be removed.
 */

/**
 * Execute a cordova command.  It is up to the native side whether this action
 * is synchronous or asynchronous.  The native side can return:
 *      Synchronous: PluginResult object as a JSON string
 *      Asynchrounous: Empty string ""
 * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
 * depending upon the result of the action.
 *
 * @param {Function} success    The success callback
 * @param {Function} fail       The fail callback
 * @param {String} service      The name of the service to use
 * @param {String} action       Action to be run in cordova
 * @param {String[]} [args]     Zero or more arguments to pass to the method

 */

var CommandProxy  = {

    "Accelerometer": require('cordova/plugin/win8metro/AccelerometerProxy'),
    "Camera":require('cordova/plugin/win8metro/CameraProxy'),
    "Compass":require('cordova/plugin/win8metro/CompassProxy'),
    "Device":require('cordova/plugin/win8metro/DeviceProxy'),
    "File":require('cordova/plugin/win8metro/FileProxy'),
    "NetworkStatus":require('cordova/plugin/win8metro/NetworkStatusProxy')
};

module.exports = function(success, fail, service, action, args) {

    if(CommandProxy[service] && CommandProxy[service][action]) {

        var callbackId = service + cordova.callbackId++;
        console.log("EXEC:" + service + " : " + action);

        if (typeof success == "function" || typeof fail == "function") {
            cordova.callbacks[callbackId] = {success:success, fail:fail};
        }
        // pass it on to Notify
        try {
            CommandProxy [service][action](success, fail, args);
        }
        catch(e) {
            console.log("Exception calling native with command :: " + service + " :: " + action  + " ::exception=" + e);
        }
    }
    else
    {
        if(fail) { fail("Missing Command Error"); }
    }

};

// cordova.exec.addCommandProxy("Accelerometer",{getCurrentAcceleration: function(successCallback, errorCallback, options) {...},...);
module.exports.addCommandProxy = function(id,proxyObj) {

    CommandProxy[id] = proxyObj;
};

// cordova.exec.removeCommandProxy("Accelerometer");
module.exports.removeCommandProxy = function(id) {

    var proxy = CommandProxy[id];
    delete CommandProxy[id];
    CommandProxy[id] = null;
    return proxy;
};

<|endoftext|>


<commit_before>/*
 * syslog.js: Transport for logging to a remote syslog consumer
 *
 * (C) 2011 Squeeks and Charlie Robbins
 * MIT LICENCE
 *
 */

const dgram = require('dgram');
const net = require('net');
const secNet = require('tls');
const utils = require('./utils');
const glossy = require('glossy');
const winston = require('winston');
const Transport = require('winston-transport');
const { MESSAGE, LEVEL } = require('triple-beam');

const _noop = () => {};

// Ensure we have the correct winston here.
if (Number(winston.version.split('.')[0]) < 3) {
  throw new Error('Winston-syslog requires winston >= 3.0.0');
}

const levels = Object.keys({
  debug: 0,
  info: 1,
  notice: 2,
  warning: 3,
  warn: 3,
  error: 4,
  err: 4,
  crit: 5,
  alert: 6,
  emerg: 7
});

//
// ### function Syslog (options)
// #### @options {Object} Options for this instance.
// Constructor function for the Syslog Transport capable of sending
// RFC 3164 and RFC 5424 compliant messages.
//
class Syslog extends Transport {
  //
  // Expose the name of this Transport on the prototype
  //
  get name() {
    return 'syslog';
  }

  constructor(options = {}) {
    //
    // Inherit from `winston-transport`.
    //
    super(options);

    //
    // Setup connection state
    //
    this.connected = false;
    this.congested = false;
    this.retries = 0;
    this.queue = [];
    this.inFlight = 0;

    //
    // Merge the options for the target Syslog server.
    //
    this.setOptions(options);

    //
    // Setup our Syslog and network members for later use.
    //
    this.socket = null;
    var Producer = options.customProducer || glossy.Produce;
    this.producer = new Producer({
      type: this.type,
      appName: this.appName,
      pid: this.pid,
      facility: this.facility
    });
  }

  setOptions(options) {
    this.host = options.host || 'localhost';
    this.port = options.port || 514;
    this.path = options.path || null;
    this.protocol = options.protocol || 'udp4';
    this.protocolOptions = options.protocolOptions || {};
    this.endOfLine = options.eol;

    this.parseProtocol(this.protocol);

    //
    // Merge the default message options.
    //
    this.localhost =
      typeof options.localhost !== 'undefined'
        ? options.localhost
        : 'localhost';
    this.type = options.type || 'BSD';
    this.facility = options.facility || 'local0';
    this.pid = options.pid || process.pid;
    this.appName = options.appName || options.app_name || process.title;
  }

  parseProtocol(protocol = this.protocol) {
    const parsedProtocol = utils.parseProtocol(protocol);

    this.protocolType = parsedProtocol.type;
    this.protocolFamily = parsedProtocol.family;
    this.isDgram = parsedProtocol.isDgram;

    if (this.protocolType === 'unix' && !this.path) {
      throw new Error('`options.path` is required on unix dgram sockets.');
    }
  }

  //
  // ### function chunkMessage (buffer, callback)
  // #### @buffer {Buffer} Syslog message buffer.
  // #### @callback {function} Continuation to respond to when complete.
  // Syslog messages sent over the UDP transport must be 64KB bytes or less. In
  // order to avoid silent failures messages should be chunked when the buffer
  // to write is larger than the maximum message size.
  //
  chunkMessage(buffer, callback = _noop) {
    if (!this.connected) {
      this.queue.push(buffer);
    } else {
      // Maximum payload for the UDP transport
      // 65535 − 8 bytes UDP header − 20 bytes IP header
      // https://tools.ietf.org/html/rfc5426#section-3.2
      // This makes sense on loopback interfaces with MTU = 65536
      // For non-loopback messages, it's impossible to know in advance
      // the MTU of each interface through which a packet might be routed
      // https://nodejs.org/api/dgram.html
      const MAX_UDP_PAYLOAD = 65507;
      let offset = 0;

      while (offset < buffer.length) {
        this.inFlight++;
        const length =
          offset + MAX_UDP_PAYLOAD > buffer.length
            ? buffer.length - offset
            : MAX_UDP_PAYLOAD;
        this._sendChunk(
          buffer,
          { offset: offset, length: length, port: this.port, host: this.host },
          callback
        );
        offset += length;
      }
    }
  }

  //
  // ### function _sendChunk (buffer, options, callback)
  // #### @buffer {Buffer} Syslog message buffer.
  // #### @options {object} Options for the message send method.
  // #### @callback {function} Continuation to respond to when complete.
  // Sends a single chunk from an oversize UDP buffer.
  //
  _sendChunk(buffer, options, callback) {
    try {
      this.socket.send(
        buffer,
        options.offset,
        options.length,
        options.port,
        options.host,
        callback
      );
    } catch (err) {
      callback(err);
    }
  }

  //
  // ### function log (info, callback)
  // #### @info {object} All relevant log information
  // #### @callback {function} Continuation to respond to when complete.
  // Core logging method exposed to Winston. Logs the `msg` and optional
  // metadata, `meta`, to the specified `level`.
  //
  log(info, callback) {
    let level = info[LEVEL];
    if (!~levels.indexOf(level)) {
      return callback(
        new Error('Cannot log unknown syslog level: ' + info[LEVEL])
      );
    }
    level = level === 'warn' ? 'warning' : level;
    const output = info[MESSAGE];

    const syslogMsg = this.producer.produce({
      severity: level,
      host: this.localhost,
      date: new Date(),
      message: this.endOfLine ? output + this.endOfLine : output
    });

    //
    // Attempt to connect to the socket
    //
    this.connect(err => {
      if (err) {
        //
        // If there was an error enqueue the message
        //
        this.queue.push(syslogMsg);

        return callback();
      }

      //
      // On any error writing to the socket, enqueue the message
      //
      const onError = logErr => {
        if (logErr) {
          this.queue.push(syslogMsg);
          this.emit('error', logErr);
        }
        this.emit('logged', info);
        this.inFlight--;
      };

      const onCongestion = () => {
        onError(new Error('Congestion Error'));
      };

      const sendDgram = () => {
        const buffer = Buffer.from(syslogMsg);

        if (this.protocolType === 'udp') {
          this.chunkMessage(buffer, onError);
        } else if (this.protocol === 'unix') {
          this.inFlight++;
          this.socket.send(buffer, 0, buffer.length, this.path, onError);
        } else if (this.congested) {
          this.queue.push(syslogMsg);
        } else {
          this.socket.once('congestion', onCongestion);
          this.inFlight++;
          this.socket.send(buffer, e => {
            this.socket.removeListener('congestion', onCongestion);
            onError(e);
          });
        }
      };

      //
      // Write to the `tcp*`, `udp*`, or `unix` socket.
      //
      if (this.isDgram) {
        sendDgram();
      } else {
        this.socket.write(syslogMsg, 'utf8', onError);
      }

      callback(null, true);
    });
  }

  //
  // ### function close ()
  // Closes the socket used by this transport freeing the resource.
  //
  close() {
    const max = 6;
    let attempt = 0;

    const _close = () => {
      if (attempt >= max || (this.queue.length === 0 && this.inFlight <= 0)) {
        if (this.socket) {
          if (this.socket.destroy) {
            // https://nodejs.org/api/net.html#net_socket_destroy_exception
            this.socket.destroy();
          } else if (this.socket.close) {
            // https://nodejs.org/api/dgram.html#dgram_socket_close_callback
            // https://www.npmjs.com/package/unix-dgram#socketclose
            this.socket.close();
          }
        }

        this.emit('closed', this.socket);
      } else {
        attempt++;
        setTimeout(_close, 200 * attempt);
      }
    };
    _close();
  }

  connectDgram(callback) {
    if (this.protocol === 'unix-connect') {
      return this._unixDgramConnect(callback);
    } else if (this.protocol === 'unix') {
      this.socket = require('unix-dgram').createSocket('unix_dgram');
    } else if (!this.socket) {
      // UDP protocol
      const proto = this.protocol === 'udp' ? 'udp4' : this.protocol;
      // https://nodejs.org/api/all.html#dgram_class_dgram_socket
      this.socket = dgram.createSocket({
        type: proto
      })
        .on('listening', () => {
          this.connected = true;
          let msg = this.queue.shift();
          while (msg) {
            this.chunkMessage(msg);
            msg = this.queue.shift();
          }
        });
      this.socket.bind();
    }

    return callback(null);
  }
  //
  // ### function connect (callback)
  // #### @callback {function} Continuation to respond to when complete.
  // Connects to the remote syslog server using `dgram` or `net` depending
  // on the `protocol` for this instance.
  //
  connect(callback) {
    //
    // If the socket already exists then respond
    //
    if (this.socket) {
      return !this.socket.readyState ||
        this.socket.readyState === 'open' ||
        this.socket.connected
        ? callback(null)
        : callback(true);
    }

    //
    // Create the appropriate socket type.
    //
    if (this.isDgram) {
      return this.connectDgram(callback);
    }

    this.socket = /^tls[4|6]?$/.test(this.protocol)
      ? new secNet.TLSSocket()
      : new net.Socket();
    this.socket.setKeepAlive(true);
    this.socket.setNoDelay();

    this.setupEvents();

    const connectConfig = Object.assign({}, this.protocolOptions, {
      host: this.host,
      port: this.port
    });

    if (this.protocolFamily) {
      connectConfig.family = this.protocolFamily;
    }

    this.socket.connect(connectConfig);

    //
    // Indicate to the callee that the socket is not ready. This
    // will enqueue the current message for later.
    //
    callback(true);
  }

  setupEvents() {
    const readyEvent = 'connect';
    //
    // On any error writing to the socket, emit the `logged` event
    // and the `error` event.
    //
    const onError = logErr => {
      if (logErr) {
        this.emit('error', logErr);
      }
      this.emit('logged');
      this.inFlight--;
    };

    //
    // Listen to the appropriate events on the socket that
    // was just created.
    //
    this.socket
      .on(readyEvent, () => {
        //
        // When the socket is ready, write the current queue
        // to it.
        //
        this.socket.write(this.queue.join(''), 'utf8', onError);

        this.emit('logged');
        this.queue = [];
        this.retries = 0;
        this.connected = true;
      })
      .on('error', function () {
        //
        // TODO: Pass this error back up
        //
      })
      .on('close', () => {
        //
        // Attempt to reconnect on lost connection(s), progressively
        // increasing the amount of time between each try.
        //
        const interval = Math.pow(2, this.retries);
        this.connected = false;

        setTimeout(() => {
          this.retries++;
          this.socket.connect(this.port, this.host);
        }, interval * 1000);
      })
      .on('timeout', () => {
        if (this.socket.destroy) {
          // https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback
          this.socket.destroy();
        } else if (this.socket.close) {
          // https://nodejs.org/api/dgram.html#dgram_socket_close_callback
          // https://www.npmjs.com/package/unix-dgram#socketclose
          this.socket.close();
        }
      });
  }

  _unixDgramConnect(callback) {
    const self = this;

    const flushQueue = () => {
      let sentMsgs = 0;
      this.queue.forEach(msg => {
        const buffer = Buffer.from(msg);

        if (!this.congested) {
          if (this.protocolType === 'udp') {
            this.chunkMessage(buffer, () => {
              ++sentMsgs;
            });
          } else {
            this.socket.send(buffer, function () {
              ++sentMsgs;
            });
          }
        }
      });

      this.queue.splice(0, sentMsgs);
    };

    this.socket = require('unix-dgram').createSocket('unix_dgram');
    this.socket.on('error', err => {
      this.emit('error', err);

      if (err.syscall === 'connect') {
        this.socket.close();
        this.socket = null;
        return callback(err);
      }
      if (err.syscall === 'send') {
        this.socket.close();
        this.socket = null;
      }
    });

    this.socket.on('connect', function () {
      this.on('congestion', () => {
        self.congested = true;
      });

      this.on('writable', () => {
        self.congested = false;
        flushQueue();
      });

      flushQueue();
      callback();
    });

    this.socket.connect(this.path);
  }
}

//
// Define a getter so that `winston.transports.Syslog`
// is available and thus backwards compatible.
//
winston.transports.Syslog = Syslog;

module.exports = {
  Syslog
};
<commit_msg>Fix eslint issue<commit_after>/*
 * syslog.js: Transport for logging to a remote syslog consumer
 *
 * (C) 2011 Squeeks and Charlie Robbins
 * MIT LICENCE
 *
 */

const dgram = require('dgram');
const net = require('net');
const secNet = require('tls');
const utils = require('./utils');
const glossy = require('glossy');
const winston = require('winston');
const Transport = require('winston-transport');
const { MESSAGE, LEVEL } = require('triple-beam');

const _noop = () => {};

// Ensure we have the correct winston here.
if (Number(winston.version.split('.')[0]) < 3) {
  throw new Error('Winston-syslog requires winston >= 3.0.0');
}

const levels = Object.keys({
  debug: 0,
  info: 1,
  notice: 2,
  warning: 3,
  warn: 3,
  error: 4,
  err: 4,
  crit: 5,
  alert: 6,
  emerg: 7
});

//
// ### function Syslog (options)
// #### @options {Object} Options for this instance.
// Constructor function for the Syslog Transport capable of sending
// RFC 3164 and RFC 5424 compliant messages.
//
class Syslog extends Transport {
  //
  // Expose the name of this Transport on the prototype
  //
  get name() {
    return 'syslog';
  }

  constructor(options = {}) {
    //
    // Inherit from `winston-transport`.
    //
    super(options);

    //
    // Setup connection state
    //
    this.connected = false;
    this.congested = false;
    this.retries = 0;
    this.queue = [];
    this.inFlight = 0;

    //
    // Merge the options for the target Syslog server.
    //
    this.setOptions(options);

    //
    // Setup our Syslog and network members for later use.
    //
    this.socket = null;
    var Producer = options.customProducer || glossy.Produce;
    this.producer = new Producer({
      type: this.type,
      appName: this.appName,
      pid: this.pid,
      facility: this.facility
    });
  }

  setOptions(options) {
    this.host = options.host || 'localhost';
    this.port = options.port || 514;
    this.path = options.path || null;
    this.protocol = options.protocol || 'udp4';
    this.protocolOptions = options.protocolOptions || {};
    this.endOfLine = options.eol;

    this.parseProtocol(this.protocol);

    //
    // Merge the default message options.
    //
    this.localhost =
      typeof options.localhost !== 'undefined'
        ? options.localhost
        : 'localhost';
    this.type = options.type || 'BSD';
    this.facility = options.facility || 'local0';
    this.pid = options.pid || process.pid;
    this.appName = options.appName || options.app_name || process.title;
  }

  parseProtocol(protocol = this.protocol) {
    const parsedProtocol = utils.parseProtocol(protocol);

    this.protocolType = parsedProtocol.type;
    this.protocolFamily = parsedProtocol.family;
    this.isDgram = parsedProtocol.isDgram;

    if (this.protocolType === 'unix' && !this.path) {
      throw new Error('`options.path` is required on unix dgram sockets.');
    }
  }

  //
  // ### function chunkMessage (buffer, callback)
  // #### @buffer {Buffer} Syslog message buffer.
  // #### @callback {function} Continuation to respond to when complete.
  // Syslog messages sent over the UDP transport must be 64KB bytes or less. In
  // order to avoid silent failures messages should be chunked when the buffer
  // to write is larger than the maximum message size.
  //
  chunkMessage(buffer, callback = _noop) {
    if (!this.connected) {
      this.queue.push(buffer);
    } else {
      // Maximum payload for the UDP transport
      // 65535 − 8 bytes UDP header − 20 bytes IP header
      // https://tools.ietf.org/html/rfc5426#section-3.2
      // This makes sense on loopback interfaces with MTU = 65536
      // For non-loopback messages, it's impossible to know in advance
      // the MTU of each interface through which a packet might be routed
      // https://nodejs.org/api/dgram.html
      const MAX_UDP_PAYLOAD = 65507;
      let offset = 0;

      while (offset < buffer.length) {
        this.inFlight++;
        const length =
          offset + MAX_UDP_PAYLOAD > buffer.length
            ? buffer.length - offset
            : MAX_UDP_PAYLOAD;
        this._sendChunk(
          buffer,
          { offset: offset, length: length, port: this.port, host: this.host },
          callback
        );
        offset += length;
      }
    }
  }

  //
  // ### function _sendChunk (buffer, options, callback)
  // #### @buffer {Buffer} Syslog message buffer.
  // #### @options {object} Options for the message send method.
  // #### @callback {function} Continuation to respond to when complete.
  // Sends a single chunk from an oversize UDP buffer.
  //
  _sendChunk(buffer, options, callback) {
    try {
      this.socket.send(
        buffer,
        options.offset,
        options.length,
        options.port,
        options.host,
        callback
      );
    } catch (err) {
      return callback(err);
    }
  }

  //
  // ### function log (info, callback)
  // #### @info {object} All relevant log information
  // #### @callback {function} Continuation to respond to when complete.
  // Core logging method exposed to Winston. Logs the `msg` and optional
  // metadata, `meta`, to the specified `level`.
  //
  log(info, callback) {
    let level = info[LEVEL];
    if (!~levels.indexOf(level)) {
      return callback(
        new Error('Cannot log unknown syslog level: ' + info[LEVEL])
      );
    }
    level = level === 'warn' ? 'warning' : level;
    const output = info[MESSAGE];

    const syslogMsg = this.producer.produce({
      severity: level,
      host: this.localhost,
      date: new Date(),
      message: this.endOfLine ? output + this.endOfLine : output
    });

    //
    // Attempt to connect to the socket
    //
    this.connect(err => {
      if (err) {
        //
        // If there was an error enqueue the message
        //
        this.queue.push(syslogMsg);

        return callback();
      }

      //
      // On any error writing to the socket, enqueue the message
      //
      const onError = logErr => {
        if (logErr) {
          this.queue.push(syslogMsg);
          this.emit('error', logErr);
        }
        this.emit('logged', info);
        this.inFlight--;
      };

      const onCongestion = () => {
        onError(new Error('Congestion Error'));
      };

      const sendDgram = () => {
        const buffer = Buffer.from(syslogMsg);

        if (this.protocolType === 'udp') {
          this.chunkMessage(buffer, onError);
        } else if (this.protocol === 'unix') {
          this.inFlight++;
          this.socket.send(buffer, 0, buffer.length, this.path, onError);
        } else if (this.congested) {
          this.queue.push(syslogMsg);
        } else {
          this.socket.once('congestion', onCongestion);
          this.inFlight++;
          this.socket.send(buffer, e => {
            this.socket.removeListener('congestion', onCongestion);
            onError(e);
          });
        }
      };

      //
      // Write to the `tcp*`, `udp*`, or `unix` socket.
      //
      if (this.isDgram) {
        sendDgram();
      } else {
        this.socket.write(syslogMsg, 'utf8', onError);
      }

      callback(null, true);
    });
  }

  //
  // ### function close ()
  // Closes the socket used by this transport freeing the resource.
  //
  close() {
    const max = 6;
    let attempt = 0;

    const _close = () => {
      if (attempt >= max || (this.queue.length === 0 && this.inFlight <= 0)) {
        if (this.socket) {
          if (this.socket.destroy) {
            // https://nodejs.org/api/net.html#net_socket_destroy_exception
            this.socket.destroy();
          } else if (this.socket.close) {
            // https://nodejs.org/api/dgram.html#dgram_socket_close_callback
            // https://www.npmjs.com/package/unix-dgram#socketclose
            this.socket.close();
          }
        }

        this.emit('closed', this.socket);
      } else {
        attempt++;
        setTimeout(_close, 200 * attempt);
      }
    };
    _close();
  }

  connectDgram(callback) {
    if (this.protocol === 'unix-connect') {
      return this._unixDgramConnect(callback);
    } else if (this.protocol === 'unix') {
      this.socket = require('unix-dgram').createSocket('unix_dgram');
    } else if (!this.socket) {
      // UDP protocol
      const proto = this.protocol === 'udp' ? 'udp4' : this.protocol;
      // https://nodejs.org/api/all.html#dgram_class_dgram_socket
      this.socket = dgram.createSocket({
        type: proto
      })
        .on('listening', () => {
          this.connected = true;
          let msg = this.queue.shift();
          while (msg) {
            this.chunkMessage(msg);
            msg = this.queue.shift();
          }
        });
      this.socket.bind();
    }

    return callback(null);
  }
  //
  // ### function connect (callback)
  // #### @callback {function} Continuation to respond to when complete.
  // Connects to the remote syslog server using `dgram` or `net` depending
  // on the `protocol` for this instance.
  //
  connect(callback) {
    //
    // If the socket already exists then respond
    //
    if (this.socket) {
      return !this.socket.readyState ||
        this.socket.readyState === 'open' ||
        this.socket.connected
        ? callback(null)
        : callback(true);
    }

    //
    // Create the appropriate socket type.
    //
    if (this.isDgram) {
      return this.connectDgram(callback);
    }

    this.socket = /^tls[4|6]?$/.test(this.protocol)
      ? new secNet.TLSSocket()
      : new net.Socket();
    this.socket.setKeepAlive(true);
    this.socket.setNoDelay();

    this.setupEvents();

    const connectConfig = Object.assign({}, this.protocolOptions, {
      host: this.host,
      port: this.port
    });

    if (this.protocolFamily) {
      connectConfig.family = this.protocolFamily;
    }

    this.socket.connect(connectConfig);

    //
    // Indicate to the callee that the socket is not ready. This
    // will enqueue the current message for later.
    //
    callback(true);
  }

  setupEvents() {
    const readyEvent = 'connect';
    //
    // On any error writing to the socket, emit the `logged` event
    // and the `error` event.
    //
    const onError = logErr => {
      if (logErr) {
        this.emit('error', logErr);
      }
      this.emit('logged');
      this.inFlight--;
    };

    //
    // Listen to the appropriate events on the socket that
    // was just created.
    //
    this.socket
      .on(readyEvent, () => {
        //
        // When the socket is ready, write the current queue
        // to it.
        //
        this.socket.write(this.queue.join(''), 'utf8', onError);

        this.emit('logged');
        this.queue = [];
        this.retries = 0;
        this.connected = true;
      })
      .on('error', function () {
        //
        // TODO: Pass this error back up
        //
      })
      .on('close', () => {
        //
        // Attempt to reconnect on lost connection(s), progressively
        // increasing the amount of time between each try.
        //
        const interval = Math.pow(2, this.retries);
        this.connected = false;

        setTimeout(() => {
          this.retries++;
          this.socket.connect(this.port, this.host);
        }, interval * 1000);
      })
      .on('timeout', () => {
        if (this.socket.destroy) {
          // https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback
          this.socket.destroy();
        } else if (this.socket.close) {
          // https://nodejs.org/api/dgram.html#dgram_socket_close_callback
          // https://www.npmjs.com/package/unix-dgram#socketclose
          this.socket.close();
        }
      });
  }

  _unixDgramConnect(callback) {
    const self = this;

    const flushQueue = () => {
      let sentMsgs = 0;
      this.queue.forEach(msg => {
        const buffer = Buffer.from(msg);

        if (!this.congested) {
          if (this.protocolType === 'udp') {
            this.chunkMessage(buffer, () => {
              ++sentMsgs;
            });
          } else {
            this.socket.send(buffer, function () {
              ++sentMsgs;
            });
          }
        }
      });

      this.queue.splice(0, sentMsgs);
    };

    this.socket = require('unix-dgram').createSocket('unix_dgram');
    this.socket.on('error', err => {
      this.emit('error', err);

      if (err.syscall === 'connect') {
        this.socket.close();
        this.socket = null;
        return callback(err);
      }
      if (err.syscall === 'send') {
        this.socket.close();
        this.socket = null;
      }
    });

    this.socket.on('connect', function () {
      this.on('congestion', () => {
        self.congested = true;
      });

      this.on('writable', () => {
        self.congested = false;
        flushQueue();
      });

      flushQueue();
      callback();
    });

    this.socket.connect(this.path);
  }
}

//
// Define a getter so that `winston.transports.Syslog`
// is available and thus backwards compatible.
//
winston.transports.Syslog = Syslog;

module.exports = {
  Syslog
};
<|endoftext|>