<commit_before>const e = require('eris')

const fs = require('fs')

// Please edit config.json with your token.
var conf = JSON.parse(fs.readFileSync('../config.json', 'utf8'))

// You may change this desc, the owner, and prefix.
const bot = new e.CommandClient(conf.token, {}, {
  description: 'A test bot made with Eris',
  owner: 'lold#4960',
  prefix: '&&'
})

bot.on('ready', () => {
  console.log('dbot - discord responded with ready')
})

bot.connect()
<commit_msg>[bot] Add in previously uncommited stuff<commit_after>const e = require('eris');

const fs = require('fs');

// Please edit config.json with your token.
var conf = JSON.parse(fs.readFileSync('config.json', 'utf8'));

// You may change this desc, the owner, and prefix.
const bot = new e.CommandClient(conf.token, {}, {
    description: "A test bot made with Eris",
    owner: "lold#4960",
    prefix: "&&"
});

bot.on("ready", () => {
    console.log("dbot - discord responded with ready");
});

bot.registerCommand("about", "my name is dbot and I live in a computer. https://github.com/xshotD/dbot", {
    description: "who am I? run this to find out",
    fullDescription: ":thinking:"
});

bot.connect();
<|endoftext|>


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

'use strict';

// MODULES //

var tape = require( 'tape' );
var Complex128 = require( '@stdlib/complex/float64' );
var Complex64 = require( '@stdlib/complex/float32' );
var real = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof real, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function returns the real component of a complex number', function test( t ) {
	var re;
	var z;

	z = new Complex128( 3.14, -3.14 );
	re = real( z );
	t.strictEqual( re, 3.14, 'returns expected value' );

	z = new Complex64( 3.14, -3.14 );
	re = real( z );
	t.strictEqual( re, 3.140000104904175, 'returns expected value' );

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

'use strict';

// MODULES //

var tape = require( 'tape' );
var Complex128 = require( '@stdlib/complex/float64' );
var real = require( './../lib' );

// TESTS //

tape( 'main export is a function', function test( t ) {
	t.ok( true, __filename );
	t.strictEqual( typeof real, 'function', 'main export is a function' );
	t.end();
});

tape( 'the function returns the real component of a complex number', function test( t ) {
	var re;
	var z;

	z = new Complex128( 3.14, -3.14 );
	re = real( z );
	t.strictEqual( re, 3.14, 'returns expected value' );

	t.end();
});
<|endoftext|>


<commit_before>import React from 'react';

var InputValidation = (ComposedComponent) => class extends React.Component {

  /**
   * Get form context to manage validation
   */
  static contextTypes = {
    form: React.PropTypes.object
  }

  componentWillMount = () => {
    if (this.props.validations) {
      this.context.form.attachToForm(this);
    }
  }

  componentWillUnmount = () => {
    if (this.props.validations) {
      if (!this.props.valid) {
        this.context.form.decrementErrorCount()
      }
      this.context.form.detachFromForm(this);
    }
  }

  validate = () => {
    var valid = false;

    this.props.validations.forEach((validation) => {
      valid = validation.validate(this.props.value);

      if (!valid) {
        if (this.state.valid) {
          this.context.form.incrementErrorCount()
          this.setState({ errorMessage: "error!", valid: false })
        }
        return valid;
      }
    });

    return valid;
  }

  handleBlur = () => {
    this.validate();
  }

  handleFocus = () => {
    if (!this.state.valid) {
      this.context.form.decrementErrorCount()
      this.setState({ errorMessage: null, valid: true });
    }
  }

  exposedMethods = () => {
    return {
      handleBlur: this.handleBlur,
      handleFocus: this.handleFocus,
      validate: this.validate
    };
  }

  state = {
    valid: true,
    errorMessage: null
  }

  render() {
    return (
      <ComposedComponent {...this.exposedMethods()} {...this.props} {...this.state} />
    );
  }

}

export default InputValidation;
<commit_msg>added check for validations<commit_after>import React from 'react';

var InputValidation = (ComposedComponent) => class extends React.Component {

  /**
   * Get form context to manage validation
   */
  static contextTypes = {
    form: React.PropTypes.object
  }

  componentWillMount = () => {
    if (this.props.validations) {
      this.context.form.attachToForm(this);
    }
  }

  componentWillUnmount = () => {
    if (this.props.validations) {
      if (!this.props.valid) {
        this.context.form.decrementErrorCount()
      }
      this.context.form.detachFromForm(this);
    }
  }

  validate = () => {
    var valid = false;

    if (this.props.validations) {
      this.props.validations.forEach((validation) => {
        valid = validation.validate(this.props.value);

        if (!valid) {
          if (this.state.valid) {
            this.context.form.incrementErrorCount()
            this.setState({ errorMessage: "error!", valid: false })
          }
          return valid;
        }
      });
    } else {
      valid = true;
    }
    return valid;
  }

  handleBlur = () => {
    this.validate();
  }

  handleFocus = () => {
    if (!this.state.valid) {
      this.context.form.decrementErrorCount()
      this.setState({ errorMessage: null, valid: true });
    }
  }

  exposedMethods = () => {
    return {
      handleBlur: this.handleBlur,
      handleFocus: this.handleFocus,
      validate: this.validate
    };
  }

  state = {
    valid: true,
    errorMessage: null
  }

  render() {
    return (
      <ComposedComponent {...this.exposedMethods()} {...this.props} {...this.state} />
    );
  }

}

export default InputValidation;
<|endoftext|>


<commit_before>  /**
    @class

The field_value_factor function allows you to use a field from a document to
    influence the score. It’s similar to using the script_score function, however,
    it avoids the overhead of scripting. If used on a multi-valued field, only the
    first value of the field is used in calculations.

    @name ejs.FieldValueFactorFunction
    @ejs scorefunction
    @borrows ejs.ScoreFunctionMixin.filter as filter
    @borrows ejs.ScoreFunctionMixin._type as _type
    @borrows ejs.ScoreFunctionMixin.toJSON as toJSON

    @param {String} field the field to apply the function to.

    @desc

Multiply the score by the value of the field, multiplied by the factor.

    */
  ejs.FieldValueFactorFunction = function (field) {

    var
      _common = ejs.ScoreFunctionMixin('field_value_factor'),
      func = _common.toJSON();

    func.field_value_factor.field = field;

    return extend(_common, {

      /**
      Sets the factor.

      @member ejs.FieldValueFactorFunction
      @param {Float} factor the factor.
      @returns {Object} returns this so that calls can be chained.
      */
      factor: function (factor) {
        if (factor == null) {
          return func.field_value_factor.factor;
        }

        func.field_value_factor.factor = factor;
        return this;
      },

      /**
      Sets the modifier.

      @member ejs.FieldValueFactorFunction
      @param {Float} modifier the modifier, one of none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt or reciprocal
      @returns {Object} returns this so that calls can be chained.
      */
      modifier: function (modifier) {
        if (modifier == null) {
          return func.field_value_factor.modifier;
        }

        func.field_value_factor.modifier = modifier;
        return this;
      }

    });
  };
<commit_msg>Source files should not be executable<commit_after>  /**
    @class

The field_value_factor function allows you to use a field from a document to
    influence the score. It’s similar to using the script_score function, however,
    it avoids the overhead of scripting. If used on a multi-valued field, only the
    first value of the field is used in calculations.

    @name ejs.FieldValueFactorFunction
    @ejs scorefunction
    @borrows ejs.ScoreFunctionMixin.filter as filter
    @borrows ejs.ScoreFunctionMixin._type as _type
    @borrows ejs.ScoreFunctionMixin.toJSON as toJSON

    @param {String} field the field to apply the function to.

    @desc

Multiply the score by the value of the field, multiplied by the factor.

    */
  ejs.FieldValueFactorFunction = function (field) {

    var
      _common = ejs.ScoreFunctionMixin('field_value_factor'),
      func = _common.toJSON();

    func.field_value_factor.field = field;

    return extend(_common, {

      /**
      Sets the factor.

      @member ejs.FieldValueFactorFunction
      @param {Float} factor the factor.
      @returns {Object} returns this so that calls can be chained.
      */
      factor: function (factor) {
        if (factor == null) {
          return func.field_value_factor.factor;
        }

        func.field_value_factor.factor = factor;
        return this;
      },

      /**
      Sets the modifier.

      @member ejs.FieldValueFactorFunction
      @param {Float} modifier the modifier, one of none, log, log1p, log2p, ln, ln1p, ln2p, square, sqrt or reciprocal
      @returns {Object} returns this so that calls can be chained.
      */
      modifier: function (modifier) {
        if (modifier == null) {
          return func.field_value_factor.modifier;
        }

        func.field_value_factor.modifier = modifier;
        return this;
      }

    });
  };
<|endoftext|>


<commit_before>const AWS = require('aws-sdk')
var fs = require('fs')
const { spawnSync } = require('child_process')
const Issue = require('../../utils/issues').Issue
const zlib = require('zlib')
const s3 = new AWS.S3()

/**
 * Remote Files
 *
 * Helper functions for checking for and using remote file paths
 *
 */

const remoteFiles = {
  // Initiaties access of a remote file from git-annex remote
  // Get remotes info the call to try successive remotes
  // Called by testFile
  getAnnexedFile: function(file, dir, callback, limit) {
    // Build config object
    const config = {
      file: file,
      dir: dir,
      callback: callback,
    }
    if (limit) config.limit = limit
    config.remotesInfo = this.getRemotesInfo(dir, file)
    config.remoteCount = config.remotesInfo.length
    // Call recursive function to try remotes
    this.tryRemotes(config)
  },

  // Try successive remotes to access file
  tryRemotes: function(config) {
    // Keep track of iterations
    if (!config.i) config.i = 0
    // Increment last value of i if this isn't the first call
    if (config.i != 0) config.i++
    if (config.i == config.remoteCount - 1) config.last = true
    // Get current remote
    config.remote = config.remotesInfo[config.i]
    config.s3Params = this.getSingleRemoteInfo(
      config.dir,
      config.remote.remoteUuid,
    )
    const dir = config.dir.endsWith('/') ? config.dir.slice(0, -1) : config.dir
    const datasetName = dir.split('/')[dir.split('/').length - 1]
    const key = datasetName + config.file.relativePath
    // Add additonal parameters
    config.s3Params['Key'] = key
    config.s3Params['VersionId'] = config.remote.versionId
    this.accessRemoteFile(config)
  },
  // Function for calling local git-annex
  callGitAnnex: function(cmd) {
    const stream = spawnSync(cmd, {
      shell: true,
    })
    return stream.stdout.toString()
  },

  // Download a remote file from its path
  accessRemoteFile: function(config) {
    if (config.limit) config.s3Params['Range'] = 'bytes=0-' + config.limit
    s3.getObject(config.s3Params)
      .promise()
      .then(data => {
        let buffer = data.Body
        if (config.file.name.endsWith('.gz')) {
          this.extractGzipBuffer(buffer, config.callback)
        } else {
          config.callback(null, null, buffer)
        }
      })
      .catch(() => {
        // Log an error if this is the last remote to try
        if (config.last) {
          config.callback(
            new Issue({ code: 98, file: config.file }),
            null,
            null,
          )
        } else {
          // Call to try next remote
          this.tryRemotes(config)
        }
      })
  },

  extractGzipBuffer: function(buffer, callback) {
    const decompressStream = zlib
      .createGunzip()
      .on('data', function(chunk) {
        callback(null, null, chunk)
        decompressStream.pause()
      })
      .on('error', function() {
        callback(new Issue({ code: 28, file: config.file }), null, null)
      })
    decompressStream.write(buffer)
  },

  // Ask git-annex for more information about a file
  getRemotesInfo: function(dir, file) {
    // Remove leading slash from  relativePath
    const relativePath = file.relativePath.startsWith('/')
      ? file.relativePath.substring(1)
      : file.relativePath
    const getRemoteCmd = `cd ${dir}
    git show git-annex:$(git-annex examinekey --json $(git-annex lookupkey ${relativePath}) | jq -r .hashdirlower)$(git-annex lookupkey ${relativePath}).log.rmet`
    const resp = this.callGitAnnex(getRemoteCmd)
    const remotesInfo = this.processGetRemoteResponse(resp)
    return remotesInfo
  },

  // Get info from a given git-annex remote
  getSingleRemoteInfo: function(dir, uuid) {
    const infoCmd = `cd ${dir}
    git-annex info ${uuid}
    `
    const resp = this.callGitAnnex(infoCmd)
    return this.processInfoQuery(resp)
  },
  // Manipulate the response from git-annex info query
  processInfoQuery: function(resp) {
    const params = {
      Bucket: null,
    }
    for (let line of resp.split('\n')) {
      if (line.includes('bucket:')) {
        params.Bucket = line.split(': ')[1]
      }
    }
    return params
  },
  // Manipulate the response from git-annex whereis query
  processGetRemoteResponse: function(resp) {
    const remotesInfo = []
    const lines = resp.split('\n')
    for (let line of lines) {
      if (line != '') {
        const splitSpace = line.split(' ')
        const remoteInfo = {
          timestamp: splitSpace[0],
          remoteUuid: splitSpace[1].split(':')[0],
          fileName: splitSpace[2].split('#')[1],
          versionId: splitSpace[2].split('#')[0].substring(1),
        }
        remotesInfo.push(remoteInfo)
      }
    }
    return remotesInfo
  },
  // Check if a local directory is a git-annex repo
  isGitAnnex: function(path) {
    return fs.existsSync(path + '/.git/annex')
  },
  // Parse JSON from string if it's valid, else return false
  safeParse: function(string) {
    try {
      return JSON.parse(string)
    } catch (e) {
      return false
    }
  },
}

module.exports = remoteFiles
<commit_msg>refactor remoteFiles.js for readability + improved requests + improved error handling<commit_after>const AWS = require('aws-sdk')
var fs = require('fs')
const { execSync } = require('child_process')
const Issue = require('../issues').Issue
const zlib = require('zlib')
const s3 = new AWS.S3()
const isNode = typeof window === 'undefined'

/**
 * Remote Files
 *
 * Helper functions for checking for and using remote file paths
 *
 */

const remoteFiles = {
  // Initiaties access of a remote file from git-annex remote
  // Get remotes info the call to try successive remotes
  // Called by testFile
  getAnnexedFile: function(file, dir, limit, callback) {
    // Build config object
    const config = {
      file: file,
      dir: dir,
    }
    if (limit) config.limit = limit
    config.remotesInfo = this.getRemotesInfo(dir, file)

    // try all the special git-annex remotes, and exit if there is an issue (reading / fetching files)
    // if all remotes fail, throw issue code 97
    Promise.all(
      config.remotesInfo.map((remote, idx) => {
        return this.tryRemote(remote, config)
          .then(data => callback(null, null, data))
          .catch(err => {
            if (err.code) {
              return callback(err, null, null)
            }
            if (idx == config.remotesInfo.length) {
              return callback(
                new Issue({ code: 97, file: config.file }),
                null,
                null,
              )
            }
          })
      }),
    )
  },

  // Try to access file from a remote
  tryRemote: function(remote, config) {
    // Get current remote
    config.s3Params = this.getSingleRemoteInfo(config.dir, remote.remoteUuid)
    const dir = config.dir.endsWith('/') ? config.dir.slice(0, -1) : config.dir
    const datasetName = dir.split('/')[dir.split('/').length - 1]
    const key = datasetName + config.file.relativePath
    // Add additonal parameters
    config.s3Params['Key'] = key
    config.s3Params['VersionId'] = remote.versionId
    return this.accessRemoteFile(config)
  },

  // Download a remote file from its path
  accessRemoteFile: function(config) {
    if (config.limit) config.s3Params['Range'] = 'bytes=0-' + config.limit
    return new Promise((resolve, reject) => {
      this.constructAwsRequest(config)
        .then(buffer => {
          if (config.file.name.endsWith('.gz')) {
            this.extractGzipBuffer(buffer, config)
              .then(data => resolve(data))
              .catch(err => reject(err))
          } else {
            resolve(buffer)
          }
        })
        .catch(reject)
    })
  },

  constructAwsRequest: function(config) {
    const hasCreds = isNode
      ? Object.keys(process.env).indexOf('AWS_ACCESS_KEY_ID') > -1
      : false
    if (hasCreds) {
      return s3
        .getObject(config.s3Params)
        .promise()
        .then(data => data.Body)
    } else {
      // bucket + key url
      let url =
        s3.getSignedUrl('getObject', config.s3Params) +
        config.s3Params.Bucket +
        '/' +
        config.s3Params.Key

      // add version to url, if exists
      url = config.s3Params.VersionId
        ? url + '?VersionId=' + config.s3Params.VersionId
        : url

      // add range to url, if exists
      url = config.s3Params.Range
        ? url + '?Range=' + config.s3Params.Range
        : url
      return fetch(url).then(resp => resp.buffer())
    }
  },

  extractGzipBuffer: function(buffer, config) {
    return new Promise((resolve, reject) => {
      const decompressStream = zlib
        .createGunzip()
        .on('data', function(chunk) {
          resolve(chunk)
          decompressStream.pause()
        })
        .on('error', function() {
          return reject(new Issue({ code: 28, file: config.file }))
        })
      decompressStream.write(buffer)
    })
  },

  // Function for calling local git-annex
  callGitAnnex: function(cmd) {
    const stream = execSync(cmd, {
      shell: true,
    })
    return stream.toString()
  },

  // Ask git-annex for more information about a file
  getRemotesInfo: function(dir, file) {
    // Remove leading slash from  relativePath
    const relativePath = file.relativePath.startsWith('/')
      ? file.relativePath.substring(1)
      : file.relativePath
    const getRemoteCmd = `cd ${dir}
    git show git-annex:$(git-annex examinekey --json $(git-annex lookupkey ${relativePath}) | jq -r .hashdirlower)$(git-annex lookupkey ${relativePath}).log.rmet`
    const resp = this.callGitAnnex(getRemoteCmd)
    const remotesInfo = this.processRemoteMetadata(resp)
    return remotesInfo
  },

  // Get info from a given git-annex remote
  getSingleRemoteInfo: function(dir, uuid) {
    const infoCmd = `cd ${dir}
    git-annex info ${uuid}`
    const resp = this.callGitAnnex(infoCmd)
    return this.getRemoteBucket(resp)
  },

  // Obtain bucket field from git-annex info query
  getRemoteBucket: function(resp) {
    const params = {
      Bucket: null,
    }
    for (let line of resp.split('\n')) {
      if (line.includes('bucket: ')) {
        params.Bucket = line.split(': ')[1]
      }
    }
    return params
  },

  // Manipulate the response from git-annex lookupkey query
  processRemoteMetadata: function(resp) {
    const remotesInfo = []
    const lines = resp.split('\n')
    for (let line of lines) {
      if (line != '') {
        const splitSpace = line.split(' ')
        if (splitSpace.length == 3) {
          const fileInfo = splitSpace[2].split('#')
          const timestamp = splitSpace[0]
          const annexInfo = splitSpace[1].split(':')
          if (fileInfo.length == 2 && annexInfo.length == 2) {
            const remoteUuid = annexInfo[0]
            const fileName = fileInfo[1]
            const versionId = fileInfo[0].substring(1)
            const remoteInfo = { timestamp, remoteUuid, fileName, versionId }
            remotesInfo.push(remoteInfo)
          }
        }
      }
    }
    return remotesInfo
  },
  // Check if a local directory is a git-annex repo
  isGitAnnex: function(path) {
    return fs.existsSync(path + '/.git/annex')
  },
  // Parse JSON from string if it's valid, else return false
  safeParse: function(string) {
    try {
      return JSON.parse(string)
    } catch (e) {
      return false
    }
  },
}

module.exports = remoteFiles
<|endoftext|>


<commit_before>slideshow = {

    init : function() {
		this.createCanvas();
		this.createImages();

		this.clock = 0;

		this.loop();
	},

	createCanvas : function() {
        this.canvas = document.getElementById("slideshow");
        this.ctx = canvas.getContext("2d");

	},

	createImages : function() {
		this.images = [];
		this.images.push(new Image("/images/asikainen.png"));
		this.images.push(new Image("/images/kakku.png"));
	},

	prepareTick : function() {
        setTimeout($.proxy(this.tick, this), 100);
	},

	loop : function() {
	   this.prepareTick();
	},

	tick : function() {
	  var delta = 0.1;
	  this.clock += 0.1;
	  animate(delta, time);
	},

	animate : function(delta, time) {
		var x = time * 5;

        ctx = this.ctx;

        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect (x+10, 10, x+55, 50);

        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
        ctx.fillRect (30, 30, 55, 50);

	}

};

$(document).ready(slideshow.init);
<commit_msg>Basic animation loop d0n3<commit_after>slideshow = {

    init : function() {
		this.createCanvas();
		this.createImages();

		this.clock = 0;
		this.width = this.height = 400;

	},

	createCanvas : function() {
        this.canvas = document.getElementById("slideshow");
        this.ctx = this.canvas.getContext("2d");

	},

	createImages : function() {

        var targets = [
		  "/static/images/asikainen.png",
		  "/static/images/kakku.png"
		];

		var readyCount = 0;
        var images = this.images = [];

        var self = this;

        function ready() {
			console.log("Ready!");
			readyCount++;
			if(readyCount >= targets.length) {
				self.loop();
			}
		}

        targets.forEach(function(src) {
			console.log("Loading img:" + src);
			var img = new Image();
			img.onload = ready;
			img.src = src;
			images.push(img);
		});

	},

	prepareTick : function() {
        setTimeout($.proxy(this.tick, this), 100);
	},

	loop : function() {
	   console.log("Entering animation loop");
	   this.prepareTick();
	},

	tick : function() {

	  var delta = 0.1;
	  this.clock += 0.1;
	  this.animate(delta, this.clock);
	  this.prepareTick();
	},

	animate : function(delta, time) {

        var ctx = this.ctx;

		var x = time * 5;

        ctx.clearRect(0, 0, this.width, this.height); // clear canvas

        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect (x+10, 10, x+55, 50);

        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
        ctx.fillRect (30, 30, 55, 50);

		ctx.drawImage(this.images[0], 0, x);

	}

};

$(document).ready($.proxy(slideshow.init, slideshow));
<|endoftext|>


<commit_before>import client from 'prom-client';
import connect from 'connect';
import http from 'http';
import _ from 'underscore';

RocketChat.promclient = client;
client.collectDefaultMetrics();

RocketChat.metrics = {};

// one sample metrics only - a counter

RocketChat.metrics.meteorMethods = new client.Summary({
	name: 'rocketchat_meteor_methods',
	help: 'summary of meteor methods count and time',
	labelNames: ['method']
});

RocketChat.metrics.rocketchatCallbacks = new client.Summary({
	name: 'rocketchat_callbacks',
	help: 'summary of rocketchat callbacks count and time',
	labelNames: ['hook', 'callback']
});

RocketChat.metrics.rocketchatHooks = new client.Summary({
	name: 'rocketchat_hooks',
	help: 'summary of rocketchat hooks count and time',
	labelNames: ['hook', 'callbacks_length']
});

RocketChat.metrics.rocketchatRestApi = new client.Summary({
	name: 'rocketchat_rest_api',
	help: 'summary of rocketchat rest api count and time',
	labelNames: ['method', 'entrypoint', 'user_agent', 'status', 'version']
});

RocketChat.metrics.meteorSubscriptions = new client.Summary({
	name: 'rocketchat_meteor_subscriptions',
	help: 'summary of meteor subscriptions count and time',
	labelNames: ['subscription']
});

RocketChat.metrics.messagesSent = new client.Counter({'name': 'rocketchat_message_sent', 'help': 'cumulated number of messages sent'});
RocketChat.metrics.notificationsSent = new client.Counter({'name': 'rocketchat_notification_sent', labelNames: ['notification_type'], 'help': 'cumulated number of notifications sent'});

RocketChat.metrics.ddpSessions = new client.Gauge({'name': 'rocketchat_ddp_sessions_count', 'help': 'number of open ddp sessions'});
RocketChat.metrics.ddpAthenticatedSessions = new client.Gauge({'name': 'rocketchat_ddp_sessions_auth', 'help': 'number of authenticated open ddp sessions'});
RocketChat.metrics.ddpConnectedUsers = new client.Gauge({'name': 'rocketchat_ddp_connected_users', 'help': 'number of unique connected users'});

RocketChat.metrics.version = new client.Gauge({'name': 'rocketchat_version', labelNames: ['version'], 'help': 'Rocket.Chat version'});
RocketChat.metrics.migration = new client.Gauge({'name': 'rocketchat_migration', 'help': 'migration versoin'});
RocketChat.metrics.instanceCount = new client.Gauge({'name': 'rocketchat_instance_count', 'help': 'instances running'});
RocketChat.metrics.oplogEnabled = new client.Gauge({'name': 'rocketchat_oplog_enabled', labelNames: ['enabled'], 'help': 'oplog enabled'});

// User statistics
RocketChat.metrics.totalUsers = new client.Gauge({'name': 'rocketchat_users_total', 'help': 'total of users'});
RocketChat.metrics.activeUsers = new client.Gauge({'name': 'rocketchat_users_active', 'help': 'total of active users'});
RocketChat.metrics.nonActiveUsers = new client.Gauge({'name': 'rocketchat_users_non_active', 'help': 'total of non active users'});
RocketChat.metrics.onlineUsers = new client.Gauge({'name': 'rocketchat_users_online', 'help': 'total of users online'});
RocketChat.metrics.awayUsers = new client.Gauge({'name': 'rocketchat_users_away', 'help': 'total of users away'});
RocketChat.metrics.offlineUsers = new client.Gauge({'name': 'rocketchat_users_offline', 'help': 'total of users offline'});

// Room statistics
RocketChat.metrics.totalRooms = new client.Gauge({'name': 'rocketchat_rooms_total', 'help': 'total of rooms'});
RocketChat.metrics.totalChannels = new client.Gauge({'name': 'rocketchat_channels_total', 'help': 'total of public rooms/channels'});
RocketChat.metrics.totalPrivateGroups = new client.Gauge({'name': 'rocketchat_private_groups_total', 'help': 'total of private rooms'});
RocketChat.metrics.totalDirect = new client.Gauge({'name': 'rocketchat_direct_total', 'help': 'total of direct rooms'});
RocketChat.metrics.totalLivechat = new client.Gauge({'name': 'rocketchat_livechat_total', 'help': 'total of livechat rooms'});

// Message statistics
RocketChat.metrics.totalMessages = new client.Gauge({'name': 'rocketchat_messages_total', 'help': 'total of messages'});
RocketChat.metrics.totalChannelMessages = new client.Gauge({'name': 'rocketchat_channel_messages_total', 'help': 'total of messages in public rooms'});
RocketChat.metrics.totalPrivateGroupMessages = new client.Gauge({'name': 'rocketchat_private_group_messages_total', 'help': 'total of messages in private rooms'});
RocketChat.metrics.totalDirectMessages = new client.Gauge({'name': 'rocketchat_direct_messages_total', 'help': 'total of messages in direct rooms'});
RocketChat.metrics.totalLivechatMessages = new client.Gauge({'name': 'rocketchat_livechat_messages_total', 'help': 'total of messages in livechat rooms'});

client.register.setDefaultLabels({
	uniqueId: RocketChat.settings.get('uniqueID'),
	siteUrl: RocketChat.settings.get('Site_Url')
});

const setPrometheusData = () => {
	const date = new Date();

	client.register.setDefaultLabels({
		unique_id: RocketChat.settings.get('uniqueID'),
		site_url: RocketChat.settings.get('Site_Url'),
		version: RocketChat.Info.version
	});

	const sessions = Object.values(Meteor.server.sessions);
	const authenticatedSessions = sessions.filter(s => s.userId);
	RocketChat.metrics.ddpSessions.set(sessions.length, date);
	RocketChat.metrics.ddpAthenticatedSessions.set(authenticatedSessions.length, date);
	RocketChat.metrics.ddpConnectedUsers.set(_.unique(authenticatedSessions.map(s => s.userId)).length, date);

	if (!RocketChat.models.Statistics) {
		return;
	}

	const statistics = RocketChat.models.Statistics.findLast();
	if (!statistics) {
		return;
	}

	RocketChat.metrics.version.set({version: statistics.version}, 1, date);
	RocketChat.metrics.migration.set(RocketChat.Migrations._getControl().version, date);
	RocketChat.metrics.instanceCount.set(statistics.instanceCount, date);
	RocketChat.metrics.oplogEnabled.set({enabled: statistics.oplogEnabled}, 1, date);

	// User statistics
	RocketChat.metrics.totalUsers.set(statistics.totalUsers, date);
	RocketChat.metrics.activeUsers.set(statistics.activeUsers, date);
	RocketChat.metrics.nonActiveUsers.set(statistics.nonActiveUsers, date);
	RocketChat.metrics.onlineUsers.set(statistics.onlineUsers, date);
	RocketChat.metrics.awayUsers.set(statistics.awayUsers, date);
	RocketChat.metrics.offlineUsers.set(statistics.offlineUsers, date);

	// Room statistics
	RocketChat.metrics.totalRooms.set(statistics.totalRooms, date);
	RocketChat.metrics.totalChannels.set(statistics.totalChannels, date);
	RocketChat.metrics.totalPrivateGroups.set(statistics.totalPrivateGroups, date);
	RocketChat.metrics.totalDirect.set(statistics.totalDirect, date);
	RocketChat.metrics.totalLivechat.set(statistics.totalLivechat, date);

	// Message statistics
	RocketChat.metrics.totalMessages.set(statistics.totalMessages, date);
	RocketChat.metrics.totalChannelMessages.set(statistics.totalChannelMessages, date);
	RocketChat.metrics.totalPrivateGroupMessages.set(statistics.totalPrivateGroupMessages, date);
	RocketChat.metrics.totalDirectMessages.set(statistics.totalDirectMessages, date);
	RocketChat.metrics.totalLivechatMessages.set(statistics.totalLivechatMessages, date);
};

const app = connect();

// const compression = require('compression');
// app.use(compression());

app.use('/metrics', (req, res) => {
	res.setHeader('Content-Type', 'text/plain');
	res.end(RocketChat.promclient.register.metrics());
});

const server = http.createServer(app);

let timer;
RocketChat.settings.get('Prometheus_Enabled', (key, value) => {
	if (value === true) {
		server.listen({
			port: RocketChat.settings.get('Prometheus_Port'),
			host: process.env.BIND_IP || '0.0.0.0'
		});
		timer = Meteor.setInterval(setPrometheusData, 5000);
	} else {
		server.close();
		Meteor.clearInterval(timer);
	}
});
<commit_msg>Regression: Prometheus was not being enable in some cases (#11249)<commit_after>import client from 'prom-client';
import connect from 'connect';
import http from 'http';
import _ from 'underscore';

RocketChat.promclient = client;
client.collectDefaultMetrics();

RocketChat.metrics = {};

// one sample metrics only - a counter

RocketChat.metrics.meteorMethods = new client.Summary({
	name: 'rocketchat_meteor_methods',
	help: 'summary of meteor methods count and time',
	labelNames: ['method']
});

RocketChat.metrics.rocketchatCallbacks = new client.Summary({
	name: 'rocketchat_callbacks',
	help: 'summary of rocketchat callbacks count and time',
	labelNames: ['hook', 'callback']
});

RocketChat.metrics.rocketchatHooks = new client.Summary({
	name: 'rocketchat_hooks',
	help: 'summary of rocketchat hooks count and time',
	labelNames: ['hook', 'callbacks_length']
});

RocketChat.metrics.rocketchatRestApi = new client.Summary({
	name: 'rocketchat_rest_api',
	help: 'summary of rocketchat rest api count and time',
	labelNames: ['method', 'entrypoint', 'user_agent', 'status', 'version']
});

RocketChat.metrics.meteorSubscriptions = new client.Summary({
	name: 'rocketchat_meteor_subscriptions',
	help: 'summary of meteor subscriptions count and time',
	labelNames: ['subscription']
});

RocketChat.metrics.messagesSent = new client.Counter({'name': 'rocketchat_message_sent', 'help': 'cumulated number of messages sent'});
RocketChat.metrics.notificationsSent = new client.Counter({'name': 'rocketchat_notification_sent', labelNames: ['notification_type'], 'help': 'cumulated number of notifications sent'});

RocketChat.metrics.ddpSessions = new client.Gauge({'name': 'rocketchat_ddp_sessions_count', 'help': 'number of open ddp sessions'});
RocketChat.metrics.ddpAthenticatedSessions = new client.Gauge({'name': 'rocketchat_ddp_sessions_auth', 'help': 'number of authenticated open ddp sessions'});
RocketChat.metrics.ddpConnectedUsers = new client.Gauge({'name': 'rocketchat_ddp_connected_users', 'help': 'number of unique connected users'});

RocketChat.metrics.version = new client.Gauge({'name': 'rocketchat_version', labelNames: ['version'], 'help': 'Rocket.Chat version'});
RocketChat.metrics.migration = new client.Gauge({'name': 'rocketchat_migration', 'help': 'migration versoin'});
RocketChat.metrics.instanceCount = new client.Gauge({'name': 'rocketchat_instance_count', 'help': 'instances running'});
RocketChat.metrics.oplogEnabled = new client.Gauge({'name': 'rocketchat_oplog_enabled', labelNames: ['enabled'], 'help': 'oplog enabled'});

// User statistics
RocketChat.metrics.totalUsers = new client.Gauge({'name': 'rocketchat_users_total', 'help': 'total of users'});
RocketChat.metrics.activeUsers = new client.Gauge({'name': 'rocketchat_users_active', 'help': 'total of active users'});
RocketChat.metrics.nonActiveUsers = new client.Gauge({'name': 'rocketchat_users_non_active', 'help': 'total of non active users'});
RocketChat.metrics.onlineUsers = new client.Gauge({'name': 'rocketchat_users_online', 'help': 'total of users online'});
RocketChat.metrics.awayUsers = new client.Gauge({'name': 'rocketchat_users_away', 'help': 'total of users away'});
RocketChat.metrics.offlineUsers = new client.Gauge({'name': 'rocketchat_users_offline', 'help': 'total of users offline'});

// Room statistics
RocketChat.metrics.totalRooms = new client.Gauge({'name': 'rocketchat_rooms_total', 'help': 'total of rooms'});
RocketChat.metrics.totalChannels = new client.Gauge({'name': 'rocketchat_channels_total', 'help': 'total of public rooms/channels'});
RocketChat.metrics.totalPrivateGroups = new client.Gauge({'name': 'rocketchat_private_groups_total', 'help': 'total of private rooms'});
RocketChat.metrics.totalDirect = new client.Gauge({'name': 'rocketchat_direct_total', 'help': 'total of direct rooms'});
RocketChat.metrics.totalLivechat = new client.Gauge({'name': 'rocketchat_livechat_total', 'help': 'total of livechat rooms'});

// Message statistics
RocketChat.metrics.totalMessages = new client.Gauge({'name': 'rocketchat_messages_total', 'help': 'total of messages'});
RocketChat.metrics.totalChannelMessages = new client.Gauge({'name': 'rocketchat_channel_messages_total', 'help': 'total of messages in public rooms'});
RocketChat.metrics.totalPrivateGroupMessages = new client.Gauge({'name': 'rocketchat_private_group_messages_total', 'help': 'total of messages in private rooms'});
RocketChat.metrics.totalDirectMessages = new client.Gauge({'name': 'rocketchat_direct_messages_total', 'help': 'total of messages in direct rooms'});
RocketChat.metrics.totalLivechatMessages = new client.Gauge({'name': 'rocketchat_livechat_messages_total', 'help': 'total of messages in livechat rooms'});

client.register.setDefaultLabels({
	uniqueId: RocketChat.settings.get('uniqueID'),
	siteUrl: RocketChat.settings.get('Site_Url')
});

const setPrometheusData = () => {
	const date = new Date();

	client.register.setDefaultLabels({
		unique_id: RocketChat.settings.get('uniqueID'),
		site_url: RocketChat.settings.get('Site_Url'),
		version: RocketChat.Info.version
	});

	const sessions = Object.values(Meteor.server.sessions);
	const authenticatedSessions = sessions.filter(s => s.userId);
	RocketChat.metrics.ddpSessions.set(sessions.length, date);
	RocketChat.metrics.ddpAthenticatedSessions.set(authenticatedSessions.length, date);
	RocketChat.metrics.ddpConnectedUsers.set(_.unique(authenticatedSessions.map(s => s.userId)).length, date);

	if (!RocketChat.models.Statistics) {
		return;
	}

	const statistics = RocketChat.models.Statistics.findLast();
	if (!statistics) {
		return;
	}

	RocketChat.metrics.version.set({version: statistics.version}, 1, date);
	RocketChat.metrics.migration.set(RocketChat.Migrations._getControl().version, date);
	RocketChat.metrics.instanceCount.set(statistics.instanceCount, date);
	RocketChat.metrics.oplogEnabled.set({enabled: statistics.oplogEnabled}, 1, date);

	// User statistics
	RocketChat.metrics.totalUsers.set(statistics.totalUsers, date);
	RocketChat.metrics.activeUsers.set(statistics.activeUsers, date);
	RocketChat.metrics.nonActiveUsers.set(statistics.nonActiveUsers, date);
	RocketChat.metrics.onlineUsers.set(statistics.onlineUsers, date);
	RocketChat.metrics.awayUsers.set(statistics.awayUsers, date);
	RocketChat.metrics.offlineUsers.set(statistics.offlineUsers, date);

	// Room statistics
	RocketChat.metrics.totalRooms.set(statistics.totalRooms, date);
	RocketChat.metrics.totalChannels.set(statistics.totalChannels, date);
	RocketChat.metrics.totalPrivateGroups.set(statistics.totalPrivateGroups, date);
	RocketChat.metrics.totalDirect.set(statistics.totalDirect, date);
	RocketChat.metrics.totalLivechat.set(statistics.totalLivechat, date);

	// Message statistics
	RocketChat.metrics.totalMessages.set(statistics.totalMessages, date);
	RocketChat.metrics.totalChannelMessages.set(statistics.totalChannelMessages, date);
	RocketChat.metrics.totalPrivateGroupMessages.set(statistics.totalPrivateGroupMessages, date);
	RocketChat.metrics.totalDirectMessages.set(statistics.totalDirectMessages, date);
	RocketChat.metrics.totalLivechatMessages.set(statistics.totalLivechatMessages, date);
};

const app = connect();

// const compression = require('compression');
// app.use(compression());

app.use('/metrics', (req, res) => {
	res.setHeader('Content-Type', 'text/plain');
	res.end(RocketChat.promclient.register.metrics());
});

const server = http.createServer(app);

let timer;
const updatePrometheusConfig = () => {
	const port = RocketChat.settings.get('Prometheus_Port');
	const enabled = RocketChat.settings.get('Prometheus_Enabled');

	if (port == null || enabled == null) {
		return;
	}

	if (enabled === true) {
		server.listen({
			port,
			host: process.env.BIND_IP || '0.0.0.0'
		});
		timer = Meteor.setInterval(setPrometheusData, 5000);
	} else {
		server.close();
		Meteor.clearInterval(timer);
	}
};

RocketChat.settings.get('Prometheus_Enabled', updatePrometheusConfig);
RocketChat.settings.get('Prometheus_Port', updatePrometheusConfig);
<|endoftext|>


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

foam.CLASS({
  package: 'foam.core',
  name: 'ValidationPredicate',
  properties: [
    {
      name: 'predicateFactory'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.mlang.predicate.Predicate',
      name: 'predicate',
      expression: function(predicateFactory) {
        return predicateFactory ?
          predicateFactory(foam.mlang.ExpressionsSingleton.create()) :
          null;
      }
    },
    {
      class: 'StringArray',
      name: 'args'
    },
    {
      class: 'Function',
      name: 'jsFunc',
      expression: function(predicate, jsErr) {
        return function() {
          if ( ! predicate.f(this) ) return jsErr(this);
        };
      }
    },
    {
      class: 'String',
      name: 'errorString'
    },
    {
      class: 'Function',
      name: 'jsErr',
      expression: function(errorString) {
        return function() { return errorString; };
      }
    }
  ],
  methods: [
    function createErrorSlotFor(data) {
      return this.ExpressionSlot.create({
        args: this.args.map(a => data[a+'$']),
        code: this.jsFunc.bind(data)
      });
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'PropertyValidationRefinement',
  refines: 'foam.core.Property',

  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      // We override 'compare' here because we need to avoid infinite recursion
      // that occurs when a validation predicate for a given property contains a
      // reference to the property itself.
      // This is an incorrect implementation of compare since it will always
      // return a match, even if the validation predicates are different. It
      // would be preferable to find a way to deal with circular references.
      compare: function() { return 0; }
    },
    {
      name: 'validateObj',
      expression: function(name, label, required, validationPredicates) {
        if ( validationPredicates.length ) {
          var args = foam.Array.unique(validationPredicates
            .map(vp => vp.args)
            .flat());
          return [args, function() {
            for ( var i = 0 ; i < validationPredicates.length ; i++ ) {
              var vp = validationPredicates[i];
              if ( vp.jsFunc.bind(this)() ) return vp.jsErr.bind(this)();
            }
            return null;
          }];
        }
        return !required ? null : [[name],
          function() {
            return !this.hasOwnProperty(name) && (`Please enter ${label.toLowerCase()}`);
          }]
      },
    },
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'StringPropertyValidationRefinement',
  refines: 'foam.core.String',
  properties: [
    'minLength',
    'maxLength',
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        var a = [];
        if ( foam.Number.isInstance(this.minLength) ) {
          a.push({
            args: [this.name],
            predicateFactory: function(e) {
              return e.GTE(foam.mlang.StringLength.create({ arg1: self }), self.minLength);
            },
            errorString: `Please enter ${this.label.toLowerCase()} with at least ${this.minLength} character${this.minLength>1?'s':''}`
          });
        }
        if ( foam.Number.isInstance(this.maxLength) ) {
          a.push({
            args: [this.name],
            predicateFactory: function(e) {
              return e.LTE(foam.mlang.StringLength.create({ arg1: self }), self.maxLength);
            },
            errorString: `Please enter ${this.label.toLowerCase()} with at most ${this.maxLength} character${this.maxLength>1?'s':''}`
          });
        }
        if ( this.required && ! foam.Number.isInstance(this.minLength) ) {
          a.push({
            args: [this.name],
            predicateFactory: function(e) {
              return e.GTE(foam.mlang.StringLength.create({ arg1: self }), 1);
            },
            errorString: `Please enter ${this.label.toLowerCase()}`
          });
        }
        return a;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'FObjectPropertyValidationRefinement',
  refines: 'foam.core.FObjectProperty',
  properties: [
    {
      class: 'Boolean',
      name: 'autoValidate'
    },
    {
      name: 'validateObj',
      expression: function(name, label, required, validationPredicates, autoValidate) {
        if ( autoValidate ) {
          return [
            [`${name}$errors_`],
            function(errs) {
              return errs ? `Please enter valid ${label.toLowerCase()}` : null;
            }
          ];
        }
        return foam.core.Property.VALIDATE_OBJ.expression.apply(this, arguments);
      },
    },
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'IntPropertyValidationRefinement',
  refines: 'foam.core.Int',
  properties: [
    {
      class: 'Boolean',
      name: 'autoValidate'
    },
    {
      name: 'validationPredicates',
      factory: function() {
        if ( ! this.autoValidate ) return [];
        var self = this;
        var a = [];
        if ( foam.Number.isInstance(self.min) ) {
          a.push({
            args: [self.name],
            predicateFactory: function(e) {
              return e.GTE(self, self.min);
            },
            errorString: `Please enter ${self.label.toLowerCase()} greater than or equal to ${self.min}.`
          });
        }
        if ( foam.Number.isInstance(self.max) ) {
          a.push({
            args: [self.name],
            predicateFactory: function(e) {
              return e.LTE(self, self.max);
            },
            errorString: `Please enter ${self.label.toLowerCase()} less than or equal to ${self.max}`
          });
        }
        return a;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core.internal',
  name: 'Errors',
//  extends: 'foam.core.Property',

  documentation: `
    A psedo-Property Axiom added to FObject which contains an object\'s validation errors.
    Adds the following attributes to an Object:

errors_

list of current errors

errors_$

Slot representation of errors_

validateObject()

calls the validateObj() method of all property Axioms, allowing them to populate errors_

  `,

  properties: [
    [ 'name', 'errors_' ]
  ],

  methods: [
    function installInProto(proto) {
      var self = this;
      Object.defineProperty(proto, 'errors_', {
        get: function() {
          return self.toSlot(this).get();
        },
        configurable: true,
        enumerable: false
      });

      Object.defineProperty(proto, 'errors_$', {
        get: function() {
          return self.toSlot(this);
        },
        configurable: true,
        enumerable: false
      });
    },

    function toSlot(obj) {
      var slotName = this.slotName_ || ( this.slotName_ = this.name + '$' );
      var slot     = obj.getPrivate_(slotName);

      if ( ! slot ) {
        slot = this.createErrorSlot_(obj)
        obj.setPrivate_(slotName, slot);
      }

      return slot;
    },

    function createErrorSlot_(obj) {
      var args = [];
      var ps   = obj.cls_.getAxiomsByClass(foam.core.Property).
        filter(function(a) { return a.validateObj; });

      for ( var i = 0 ; i < ps.length ; i++ ) {
        var p = ps[i];
        args.push(obj.slot(p.validateObj));
      }

      function validateObject() {
        var ret;

        for ( var i = 0 ; i < ps.length ; i++ ) {
          var p = ps[i];
          var err = args[i].get();
          if ( err ) (ret || (ret = [])).push([p, err]);
        }

        return ret;
      }

      return foam.core.ExpressionSlot.create({
        obj: obj,
        code: validateObject,
        args: args
      });
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'ValidationFObjectRefinement',
  refines: 'foam.core.FObject',

  axioms: [
    foam.core.internal.Errors.create()
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'EmailPropertyValidationRefinement',
  refines: 'foam.core.EMail',
  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        var ret = [
          {
            args: [this.name],
            predicateFactory: function(e) {
              return e.REG_EXP(self, /^$|.+@.+\..+/);
            },
            errorString: 'Please enter email address'
          }
        ];
        if ( this.required ) {
          ret.push(
            {
              args: [this.name],
              predicateFactory: function(e) {
                return e.NEQ(self, '');
              },
              errorString: 'Please enter email address'
            }
          );
        }
        return ret;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'PhoneNumberPropertyValidationRefinement',
  refines: 'foam.core.PhoneNumber',
  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        if ( ! this.required ) return [];
        return [
          {
            args: [this.name],
            predicateFactory: function(e) {
                return e.REG_EXP(
                  self,
                  /^(?:\+?1[-.●]?)?\(?([0-9]{3})\)?[-.●]?([0-9]{3})[-.●]?([0-9]{4})$/
                 );
            },
            errorString: 'Please enter phone number'
          }
        ];
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'DatePropertyValidationRefinement',
  refines: 'foam.core.Date',
  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        return [
          {
            args: [self.name],
            predicateFactory: function(e) {
              return e.OR(
                e.NOT(e.HAS(self)), // Allow null dates.
                e.AND(
                  e.LTE(
                    self,
                    // Maximum date supported by FOAM
                    // (bounded by JavaScript's limit)
                    new Date(8640000000000000)
                  ),
                  e.GTE(
                    self,
                    // Minimum date supported by FOAM
                    // (bounded by JavaScript's limit)
                    new Date(-8640000000000000)
                  )
                )
              );
            },
            errorString: 'Invalid date value'
          }
        ];
      }
    }
  ]
});
<commit_msg>Fix phone number validation when not required<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.core',
  name: 'ValidationPredicate',
  properties: [
    {
      name: 'predicateFactory'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.mlang.predicate.Predicate',
      name: 'predicate',
      expression: function(predicateFactory) {
        return predicateFactory ?
          predicateFactory(foam.mlang.ExpressionsSingleton.create()) :
          null;
      }
    },
    {
      class: 'StringArray',
      name: 'args'
    },
    {
      class: 'Function',
      name: 'jsFunc',
      expression: function(predicate, jsErr) {
        return function() {
          if ( ! predicate.f(this) ) return jsErr(this);
        };
      }
    },
    {
      class: 'String',
      name: 'errorString'
    },
    {
      class: 'Function',
      name: 'jsErr',
      expression: function(errorString) {
        return function() { return errorString; };
      }
    }
  ],
  methods: [
    function createErrorSlotFor(data) {
      return this.ExpressionSlot.create({
        args: this.args.map(a => data[a+'$']),
        code: this.jsFunc.bind(data)
      });
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'PropertyValidationRefinement',
  refines: 'foam.core.Property',

  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      // We override 'compare' here because we need to avoid infinite recursion
      // that occurs when a validation predicate for a given property contains a
      // reference to the property itself.
      // This is an incorrect implementation of compare since it will always
      // return a match, even if the validation predicates are different. It
      // would be preferable to find a way to deal with circular references.
      compare: function() { return 0; }
    },
    {
      name: 'validateObj',
      expression: function(name, label, required, validationPredicates) {
        if ( validationPredicates.length ) {
          var args = foam.Array.unique(validationPredicates
            .map(vp => vp.args)
            .flat());
          return [args, function() {
            for ( var i = 0 ; i < validationPredicates.length ; i++ ) {
              var vp = validationPredicates[i];
              if ( vp.jsFunc.bind(this)() ) return vp.jsErr.bind(this)();
            }
            return null;
          }];
        }
        return !required ? null : [[name],
          function() {
            return !this.hasOwnProperty(name) && (`Please enter ${label.toLowerCase()}`);
          }]
      },
    },
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'StringPropertyValidationRefinement',
  refines: 'foam.core.String',
  properties: [
    'minLength',
    'maxLength',
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        var a = [];
        if ( foam.Number.isInstance(this.minLength) ) {
          a.push({
            args: [this.name],
            predicateFactory: function(e) {
              return e.GTE(foam.mlang.StringLength.create({ arg1: self }), self.minLength);
            },
            errorString: `Please enter ${this.label.toLowerCase()} with at least ${this.minLength} character${this.minLength>1?'s':''}`
          });
        }
        if ( foam.Number.isInstance(this.maxLength) ) {
          a.push({
            args: [this.name],
            predicateFactory: function(e) {
              return e.LTE(foam.mlang.StringLength.create({ arg1: self }), self.maxLength);
            },
            errorString: `Please enter ${this.label.toLowerCase()} with at most ${this.maxLength} character${this.maxLength>1?'s':''}`
          });
        }
        if ( this.required && ! foam.Number.isInstance(this.minLength) ) {
          a.push({
            args: [this.name],
            predicateFactory: function(e) {
              return e.GTE(foam.mlang.StringLength.create({ arg1: self }), 1);
            },
            errorString: `Please enter ${this.label.toLowerCase()}`
          });
        }
        return a;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'FObjectPropertyValidationRefinement',
  refines: 'foam.core.FObjectProperty',
  properties: [
    {
      class: 'Boolean',
      name: 'autoValidate'
    },
    {
      name: 'validateObj',
      expression: function(name, label, required, validationPredicates, autoValidate) {
        if ( autoValidate ) {
          return [
            [`${name}$errors_`],
            function(errs) {
              return errs ? `Please enter valid ${label.toLowerCase()}` : null;
            }
          ];
        }
        return foam.core.Property.VALIDATE_OBJ.expression.apply(this, arguments);
      },
    },
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'IntPropertyValidationRefinement',
  refines: 'foam.core.Int',
  properties: [
    {
      class: 'Boolean',
      name: 'autoValidate'
    },
    {
      name: 'validationPredicates',
      factory: function() {
        if ( ! this.autoValidate ) return [];
        var self = this;
        var a = [];
        if ( foam.Number.isInstance(self.min) ) {
          a.push({
            args: [self.name],
            predicateFactory: function(e) {
              return e.GTE(self, self.min);
            },
            errorString: `Please enter ${self.label.toLowerCase()} greater than or equal to ${self.min}.`
          });
        }
        if ( foam.Number.isInstance(self.max) ) {
          a.push({
            args: [self.name],
            predicateFactory: function(e) {
              return e.LTE(self, self.max);
            },
            errorString: `Please enter ${self.label.toLowerCase()} less than or equal to ${self.max}`
          });
        }
        return a;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core.internal',
  name: 'Errors',
//  extends: 'foam.core.Property',

  documentation: `
    A psedo-Property Axiom added to FObject which contains an object\'s validation errors.
    Adds the following attributes to an Object:

errors_

list of current errors

errors_$

Slot representation of errors_

validateObject()

calls the validateObj() method of all property Axioms, allowing them to populate errors_

  `,

  properties: [
    [ 'name', 'errors_' ]
  ],

  methods: [
    function installInProto(proto) {
      var self = this;
      Object.defineProperty(proto, 'errors_', {
        get: function() {
          return self.toSlot(this).get();
        },
        configurable: true,
        enumerable: false
      });

      Object.defineProperty(proto, 'errors_$', {
        get: function() {
          return self.toSlot(this);
        },
        configurable: true,
        enumerable: false
      });
    },

    function toSlot(obj) {
      var slotName = this.slotName_ || ( this.slotName_ = this.name + '$' );
      var slot     = obj.getPrivate_(slotName);

      if ( ! slot ) {
        slot = this.createErrorSlot_(obj)
        obj.setPrivate_(slotName, slot);
      }

      return slot;
    },

    function createErrorSlot_(obj) {
      var args = [];
      var ps   = obj.cls_.getAxiomsByClass(foam.core.Property).
        filter(function(a) { return a.validateObj; });

      for ( var i = 0 ; i < ps.length ; i++ ) {
        var p = ps[i];
        args.push(obj.slot(p.validateObj));
      }

      function validateObject() {
        var ret;

        for ( var i = 0 ; i < ps.length ; i++ ) {
          var p = ps[i];
          var err = args[i].get();
          if ( err ) (ret || (ret = [])).push([p, err]);
        }

        return ret;
      }

      return foam.core.ExpressionSlot.create({
        obj: obj,
        code: validateObject,
        args: args
      });
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'ValidationFObjectRefinement',
  refines: 'foam.core.FObject',

  axioms: [
    foam.core.internal.Errors.create()
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'EmailPropertyValidationRefinement',
  refines: 'foam.core.EMail',
  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        var ret = [
          {
            args: [this.name],
            predicateFactory: function(e) {
              return e.REG_EXP(self, /^$|.+@.+\..+/);
            },
            errorString: 'Please enter email address'
          }
        ];
        if ( this.required ) {
          ret.push(
            {
              args: [this.name],
              predicateFactory: function(e) {
                return e.NEQ(self, '');
              },
              errorString: 'Please enter email address'
            }
          );
        }
        return ret;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'PhoneNumberPropertyValidationRefinement',
  refines: 'foam.core.PhoneNumber',

  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        const PHONE_NUMBER_REGEX = /^(?:\+?1[-.●]?)?\(?([0-9]{3})\)?[-.●]?([0-9]{3})[-.●]?([0-9]{4})$/;
        return this.required
          ? [
              {
                args: [this.name],
                predicateFactory: function(e) {
                  return e.HAS(self);
                },
                errorString: 'Phone number required.'
              },
              {
                args: [this.name],
                predicateFactory: function(e) {
                  return e.REG_EXP(self, PHONE_NUMBER_REGEX);
                },
                errorString: 'Invalid phone number.'
              }
            ]
          : [
              {
                args: [this.name],
                predicateFactory: function(e) {
                    return e.OR(
                      e.EQ(foam.mlang.StringLength.create({ arg1: self }), 0),
                      e.REG_EXP(self, PHONE_NUMBER_REGEX)
                    );
                },
                errorString: 'Invalid phone number.'
              }
            ];
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'DatePropertyValidationRefinement',
  refines: 'foam.core.Date',
  properties: [
    {
      class: 'FObjectArray',
      of: 'foam.core.ValidationPredicate',
      name: 'validationPredicates',
      factory: function() {
        var self = this;
        return [
          {
            args: [self.name],
            predicateFactory: function(e) {
              return e.OR(
                e.NOT(e.HAS(self)), // Allow null dates.
                e.AND(
                  e.LTE(
                    self,
                    // Maximum date supported by FOAM
                    // (bounded by JavaScript's limit)
                    new Date(8640000000000000)
                  ),
                  e.GTE(
                    self,
                    // Minimum date supported by FOAM
                    // (bounded by JavaScript's limit)
                    new Date(-8640000000000000)
                  )
                )
              );
            },
            errorString: 'Invalid date value'
          }
        ];
      }
    }
  ]
});
<|endoftext|>


<commit_before>var updating = false;
var searching = false;
var idling = false;
var timeout;
var inputtimer;
var lastsort = 'trackno';
var doscroll = false;

// Get a list of subdirectories with AJAX
function html_dirlist(path) {
	$.get($SCRIPT_ROOT + "/ajax/html_dirlist/?p="+path, function(data) {
		$('#dirlist').html(data);
		show_breadcrumbs(path);
		add_dir_click(path);
	});
}

function add_dir_click(path) {
	$('a[data-dir]').click(function() {
		mpd_add_dir(path, $(this));
		return false;
	});
}

// Get a list of songs with AJAX
function html_songlist(path, sort) {

	var url = $SCRIPT_ROOT + "/ajax/html_songlist/?p="+path;
	if (typeof sort !== 'undefined') {
		url += "&s=" + sort;
		lastsort = sort;
	}

	$("#songlist").html('      Loading...').spin({ lines: 8, length: 2, width: 2, radius: 3, left: 0 });
	$.get(url, function(data) {
		$('#songlist').html(data);

		$('a[data-song]').click(function() {
			songid = $(this).attr('data-song');
			i = $(this).find('i[class="icon-play"]');
			if (i.length == 1) {
				// This is the case where the 'play now' icon was clicked
				mpd_add(songid, $(this), true);
			}
			else {
				mpd_add(songid, $(this));
			}
			return false;
		});

		init_songinfo();

	});
	folder_jpg(path);
}

function folder_jpg(path) {
	$('#folderjpg').html('');
	$('#folderjpgimg').click(function() {
		src=$(this).attr('src');
		//$('#imageinmodal').attr('src', src).css('height: 250px');
		$('#imagemodal').css('width', '430px');
		$('#imageinmodal').attr('src', src).on('load', function () {
			$(this).css('height', '400px');
		});
		$('#imagemodal').modal();
	});
}

// Get song info with AJAX
function init_songinfo() {
	$('.icon-info-sign').clickover({beforeShow: function() {
			element = this
			songid = this.options.info;
			$.ajax({
				url: $SCRIPT_ROOT + "/ajax/html_songinfo/?i=" + songid,
				success: function(result) {
					element.options.content = result;
				},
				async: false
			});
		},
		html: true,
		title: 'Song info',
		placement: 'left'
	});
}

function show_breadcrumbs(currentdir) {
	//comp = decodeURIComponent(currentdir).replace(/\+/g, ' ').split('/');
	comp = decodeURIComponent(currentdir).split('/');
	str = ' Start  ';
	link = encodeURIComponent('/');
	$.each(comp, function(k,v) {
		if (v != '') {
			link += encodeURIComponent(v + '/');
			str += '' + v + '  ';
		}
	});
	str += '';
	$('#breadcrumbs').html(str);
}

function infopop (element,data) {

		options = { 'content': data }
		options['placement'] = 'right';
		options['trigger'] = 'manual';
		options['html'] = true;
		options['title'] = 'Song info';
		$(element).clickover(options);
		$(element).clickover('show');
}

function pop (element,data,placement,hide) {
		var do_hide = 2000;
		options = { 'content': data }
		if (typeof placement != 'undefined') {
			options['placement'] = placement
		}
		if (typeof hide != 'undefined') {
			do_hide = hide
		}
		$(element).popover(options);
		$(element).popover('show');

		if (do_hide > 0) {
			window.setTimeout(function() { $(element).popover('destroy'); }, do_hide);
		}
}

function mpd_add_dir(dir, element) {
	$.get($SCRIPT_ROOT + "/ajax/mpd_add_dir/?p=" + dir + '&s=' + lastsort, function(data) {
		pop(element, data);
	});
}

function mpd_add(id, element, playnow) {
	param = '';
	if (typeof(playnow) != 'undefined') {
		if (playnow) {
			param = '&playnow=true';
		}
	}

	$.get($SCRIPT_ROOT + "/ajax/mpd_add/?i=" + id + param, function(data) {
		pop(element, data);
	});
}

function mpd_cmd(cmd, element, options) {
	url = $SCRIPT_ROOT + "/ajax/mpd_" + cmd + "/";
	if ('i' in options) {
		url += "?i=" + options.i
	}
	$.get(url, function(data) {
		if ('placement' in options) {
			pop(element, data, options.placement);
		}
		else {
			pop(element, data);
		}
		if (!(cmd == 'enableoutput' || cmd == 'disableoutput')) {
			load_playlist_if_not_updating();
		}
	});
}

function load_playlist() {

	updating = true;
	$('#playlistmodal').find('.modal-body').load($SCRIPT_ROOT + "/ajax/html_playlist/", function () {

		if (doscroll) {
			// Scroll down to show the current song near the top of the list
			if (song > 3) {
				// First set the scrollbar at the top, to get the correct position
				$('#mpdplaylist').scrollTop(0);
				var scroll = parseInt($('#pos' + (song - 3)).position().top);
				$('#mpdplaylist').scrollTop(scroll);
			}
			doscroll = false;
		}

		// set css on the cells, because the .error class on the row loses
		// the battle with Bootstrap's .table-striped
		$('.error').children('td').css('background-color', '#f2dede');

		// Click handler for the 'delete' icon
		$('a[data-action="delete"]').click(function() {
			mpd_cmd('delete', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for the 'move up' icon
		$('a[data-action="moveup"]').click(function() {
			mpd_cmd('moveup', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for the 'move down' icon
		$('a[data-action="movedown"]').click(function() {
			mpd_cmd('movedown', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for the 'requeue' icon
		$('a[data-action="requeue"]').click(function() {
			mpd_cmd('requeue', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for playlist entries
		$('a[data-pos]').click(function() {
			mpd_cmd('skipto', $('#mpdstatus'), {i: $(this).attr('data-pos'), placement: 'top'})
		});

		// Start MPD idle request
		do_idle();
		updating = false;
	});
}

function load_playlist_if_not_updating () {
  if (!updating) {
    load_playlist ();
  }
}

function show_playlist() {
		$('#playlistmodal').modal();
		idle = true;

		/* Fires too often
		$('#playlistmodal').on('hidden', function() {
			idle = false;
		});
		*/
		doscroll = true;
		load_playlist_if_not_updating();
}

// Send an 'idle' command to the player and handle the result
// If the result indicates the playlist has changed, then
// reload it.
function do_idle () {
	vis = $('#playlistmodal').css('display');
	if (vis == 'block') {
		if (! idling) {
			idling = true

			// This should use $.ajax, also defining an error handler. The error
			// handler should set idling = false, otherwise this function will
			// not recover from a failed request, like a webserver restart.
			$.getJSON($SCRIPT_ROOT + "/ajax/json_mpd_idle/", function (data) {
				//lines = data.split("|");
				$.each(data, function (index, value) {
					if (value == "playlist" || value == "player") {
						load_playlist_if_not_updating ();
					}
				});
				vis = $('#playlistmodal').css('display');
				if (vis == 'block') {
					// delay a little
					timeout = setTimeout('do_idle', 300);
				}
				else {
					clearTimeout(timeout);
				}
				idling = false
			});
		}
	}
}

function handle_search (sort) {
	if (searching) { return; }
	var q = $('#search').val()
	if (q == '') { return; }
	var url = $SCRIPT_ROOT + "/ajax/html_song_search/?q="+encodeURIComponent(q);
	if (typeof sort !== 'undefined') {
		url += "&s=" + sort;
		lastsort = sort;
	}
	// Put an identifiying div in the dirlist, so the sort button can find out which function to call
	$('#dirlist').html('

');
	$('#songlist').load(url, function () {
		searching = false;
		$('a[data-song]').click(function() {
			songid = $(this).attr('data-song');
			mpd_add(songid, $(this));
			return false;
		});
		init_songinfo();
	});
}

jQuery(document).ready(function($) {

	// initialize the AJAX containers, using the path from the global 'initpath'
	// variable, which is set from the index.html template. It's already urlencoded.
	html_dirlist(initpath);
	html_songlist(initpath);

	//plsize = { width: '[Audio:"400px"]', height: '[Audio:"100px"]' };
	//$('#jquery_jplayer').jPlayer({swfPath:"lib/jplayer", backgroundColor:"#00FFFF", size: plsize, solution: 'html' });
	//$('#jquery_jplayer').jPlayer({swfPath:"lib/jplayer", backgroundColor:"#00FFFF", ready: function() {
	//	$('jplayermodal').modal();
	//} });
	$('#playlistmodal').on('show', function () {
		$(this).css({'margin-left': function () {
				return -($(this).width() / 2);
			}
		});
	});

	$('#showplaylistmodal').click(function() {
		show_playlist()
		return false;
	});

	$('#showstreams').click(function() {
		$('#streamsmodal').modal();
		$('#streamsmodal').find('.modal-body').load($SCRIPT_ROOT + "/ajax/html_streams/", function () {
			$('.addstream').click(function() {
				streamid = $(this).attr('data-stream');
				mpd_cmd('addstream', $(this), {'i': streamid});
			});
		});
	});

	$('#showoutputs').click(function() {
		$('#outputsmodal').modal();
		$('#outputsmodal').find('.modal-body').load($SCRIPT_ROOT + "/ajax/html_outputs/", function () {
  		$('.switch')['bootstrapSwitch']();

			$('div[data-outputid]').on('switch-change', function(e, data) {
				id = $(this).attr('data-outputid');
				if (data.value) {
					cmd='enableoutput';
				}
				else {
					cmd='disableoutput';
				}
				mpd_cmd(cmd, $(this), {'i': id, 'placement': 'left'});
			})
		});
	});

	$('a[data-sort]').click(function() {
		sortcol = $(this).attr('data-sort');
		if ($('#currentdiv').attr('data-currentdir') == 'SEARCHRESULT') {
			handle_search(sortcol);
		}
		else {
			html_songlist(currentdir, sortcol);
		}
		// Hide the sort menu
		$('#sortbutton').dropdown('toggle');
		return false;
	});

	/* Playlist modal, player control buttons */
	$.each(['play','stop','pause','prev','next'], function(index,value) {
		$('#mpd_'+ value).click(function(e) {
			mpd_cmd(value, $(this), {'placement': 'top'});
			load_playlist_if_not_updating()
			return false;
		});
	});

	$('#mpd_clear').click(function(e) {
		/*
		bootbox.confirm('Clear playlist. Are you sure?', function (do_clear) {
			if (do_clear) {
				mpd_cmd('clear', $(this), {'placement': 'top'});
			}
			//return false;
		});
		*/
		if (confirm('Clear entire playlist. Are you sure?')) {
			mpd_cmd('clear', $('#mpdplaylist'), {'placement': 'top'});
		}
		return false;
	});

	$('#mpd_cleanup').click(function(e) {
		if (confirm('Clean up old songs from playlist. Are you sure?')) {
			mpd_cmd('cleanup', $('#mpdplaylist'), {'placement': 'top'});
		}
		return false;
	});

	$(document).pjax('a[data-pjax]', '#dirlist');

	$(document).on('pjax:end', function(e) {
		currentdir = $('#currentdiv').attr('data-currentdir');
		add_dir_click(currentdir);
		show_breadcrumbs(currentdir);
		html_songlist(currentdir);
	})

	/* Near-real-time search. On input, wait 600ms for more input before doing search */
	$('#search').on('input', function() {
		//if (typeof inputtimer !== 'undefined')
		if (inputtimer) {
			clearTimeout(inputtimer);
		}
		inputtimer = setTimeout(function () { handle_search(); }, 600);
	});

	$('#searchbutton').click(function() {
		handle_search();
	});
});
<commit_msg>Start restructuring and documenting site.js<commit_after>// Global variables
var
	// A flag indicating whether the playlist is currently being updated
	updating = false,

	// A flag indicating whether a search request is cuurently in progress
	searching = false,

	// A flag indicating whether an 'idle' request is currently running
	idling = false,

	// A timeout ID used for delayed restarting of the 'idle' process
	timeout,

	// A timeout ID used for delaying the incremental search function
	inputtimer,

	// A gloabl record of the last chosen sort field
	lastsort = 'trackno',

	// A flag indicating whether the playlist should scroll to the current item
	doscroll = false;

// Get a list of subdirectories for a given path with AJAX
function html_dirlist(path) {
	$.get($SCRIPT_ROOT + "/ajax/html_dirlist/?p="+path, function(data) {
		$('#dirlist').html(data);
		show_breadcrumbs(path);
		add_dir_click(path);
	});
}

// Initialize the 'Play all songs' link
function add_dir_click(path) {
	$('a[data-dir]').click(function() {
		mpd_add_dir(path, $(this));
		return false;
	});
}

// Get a list of songs for a given path with AJAX, using the given sort field
function html_songlist(path, sort) {

	var url = $SCRIPT_ROOT + "/ajax/html_songlist/?p="+path;
	if (typeof sort !== 'undefined') {
		url += "&s=" + sort;
		lastsort = sort;
	}

	// Show a spinner while loading the songlist
	$("#songlist").html('      Loading...').spin({ lines: 8, length: 2, width: 2, radius: 3, left: 0 });

	// Get and process the songlist data
	$.get(url, function(data) {
		$('#songlist').html(data);

		// Initialize the song links
		$('a[data-song]').click(function() {
			songid = $(this).attr('data-song');
			i = $(this).find('i[class="icon-play"]');
			if (i.length == 1) {
				// This is the case where the 'play now' icon was clicked
				mpd_add(songid, $(this), true);
			}
			else {
				mpd_add(songid, $(this));
			}
			return false;
		});

		init_songinfo();

	});
	folder_jpg(path);
}

// Load 'folder.jpg' and initialize a click handler for the image
function folder_jpg(path) {
	$('#folderjpg').html('');

	// On click, load the image in a modal div, set the size and show the modal
	$('#folderjpgimg').click(function() {
		src=$(this).attr('src');
		$('#imagemodal').css('width', '430px');
		$('#imageinmodal').attr('src', src).on('load', function () {
			$(this).css('height', '400px');
		});
		$('#imagemodal').modal();
	});
}

// Initialize the 'Song info' click handler to show an AJAX-primed clickover
function init_songinfo() {
	$('.icon-info-sign').clickover({beforeShow: function() {
			element = this
			songid = this.options.info;
			$.ajax({
				url: $SCRIPT_ROOT + "/ajax/html_songinfo/?i=" + songid,
				success: function(result) {
					element.options.content = result;
				},
				async: false
			});
		},
		html: true,
		title: 'Song info',
		placement: 'left'
	});
}

// Show the breadcrumbs naviation in the designated div
function show_breadcrumbs(currentdir) {
	comp = decodeURIComponent(currentdir).split('/');
	str = ' Start  ';
	link = encodeURIComponent('/');
	$.each(comp, function(k,v) {
		if (v != '') {
			link += encodeURIComponent(v + '/');
			str += '' + v + '  ';
		}
	});
	str += '';
	$('#breadcrumbs').html(str);
}

/* not used ?
function infopop (element,data) {
		options = { 'content': data }
		options['placement'] = 'right';
		options['trigger'] = 'manual';
		options['html'] = true;
		options['title'] = 'Song info';
		$(element).clickover(options);
		$(element).clickover('show');
}
*/

// Show a popover near the given element, with given content
function pop( element, data, placement, hide ) {
		var do_hide = 2000;
		var options = { 'content': data }
		if (typeof placement != 'undefined') {
			options['placement'] = placement
		}
		if (typeof hide != 'undefined') {
			do_hide = hide
		}
		$(element).popover(options);
		$(element).popover('show');

		if (do_hide > 0) {
			window.setTimeout(function() { $(element).popover('destroy'); }, do_hide);
		}
}

// Call 'mpd_add_dir' for given dir
function mpd_add_dir(dir, element) {
	$.get($SCRIPT_ROOT + "/ajax/mpd_add_dir/?p=" + dir + '&s=' + lastsort, function(data) {
		pop(element, data);
	});
}

// Call 'mpd_add' for given song ID, optionally telling the server to play it immediately
function mpd_add(id, element, playnow) {
	param = '';
	if (typeof(playnow) != 'undefined') {
		if (playnow) {
			param = '&playnow=true';
		}
	}

	$.get($SCRIPT_ROOT + "/ajax/mpd_add/?i=" + id + param, function(data) {
		pop(element, data);
	});
}

function mpd_cmd(cmd, element, options) {
	url = $SCRIPT_ROOT + "/ajax/mpd_" + cmd + "/";
	if ('i' in options) {
		url += "?i=" + options.i
	}
	$.get(url, function(data) {
		if ('placement' in options) {
			pop(element, data, options.placement);
		}
		else {
			pop(element, data);
		}
		if (!(cmd == 'enableoutput' || cmd == 'disableoutput')) {
			load_playlist_if_not_updating();
		}
	});
}

function load_playlist() {

	updating = true;
	$('#playlistmodal').find('.modal-body').load($SCRIPT_ROOT + "/ajax/html_playlist/", function () {

		if (doscroll) {
			// Scroll down to show the current song near the top of the list
			if (song > 3) {
				// First set the scrollbar at the top, to get the correct position
				$('#mpdplaylist').scrollTop(0);
				var scroll = parseInt($('#pos' + (song - 3)).position().top);
				$('#mpdplaylist').scrollTop(scroll);
			}
			doscroll = false;
		}

		// set css on the cells, because the .error class on the row loses
		// the battle with Bootstrap's .table-striped
		$('.error').children('td').css('background-color', '#f2dede');

		// Click handler for the 'delete' icon
		$('a[data-action="delete"]').click(function() {
			mpd_cmd('delete', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for the 'move up' icon
		$('a[data-action="moveup"]').click(function() {
			mpd_cmd('moveup', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for the 'move down' icon
		$('a[data-action="movedown"]').click(function() {
			mpd_cmd('movedown', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for the 'requeue' icon
		$('a[data-action="requeue"]').click(function() {
			mpd_cmd('requeue', $('#mpdplaylist'), {i: $(this).attr('data-ppos'), placement: 'top'})
		});

		// Click handler for playlist entries
		$('a[data-pos]').click(function() {
			mpd_cmd('skipto', $('#mpdstatus'), {i: $(this).attr('data-pos'), placement: 'top'})
		});

		// Start MPD idle request
		do_idle();
		updating = false;
	});
}

function load_playlist_if_not_updating () {
  if (!updating) {
    load_playlist ();
  }
}

function show_playlist() {
		$('#playlistmodal').modal();
		idle = true;
		doscroll = true;
		load_playlist_if_not_updating();
}

// Send an 'idle' command to the player and handle the result
// If the result indicates the playlist has changed, then
// reload it.
function do_idle () {
	vis = $('#playlistmodal').css('display');
	if (vis == 'block') {
		if (! idling) {
			idling = true

			// This should use $.ajax, also defining an error handler. The error
			// handler should set idling = false, otherwise this function will
			// not recover from a failed request, like a webserver restart.
			$.getJSON($SCRIPT_ROOT + "/ajax/json_mpd_idle/", function (data) {
				//lines = data.split("|");
				$.each(data, function (index, value) {
					if (value == "playlist" || value == "player") {
						load_playlist_if_not_updating ();
					}
				});
				vis = $('#playlistmodal').css('display');
				if (vis == 'block') {
					// delay a little
					timeout = setTimeout('do_idle', 300);
				}
				else {
					clearTimeout(timeout);
				}
				idling = false
			});
		}
	}
}

function handle_search (sort) {
	if (searching) { return; }
	var q = $('#search').val()
	if (q == '') { return; }
	var url = $SCRIPT_ROOT + "/ajax/html_song_search/?q="+encodeURIComponent(q);
	if (typeof sort !== 'undefined') {
		url += "&s=" + sort;
		lastsort = sort;
	}
	// Put an identifiying div in the dirlist, so the sort button can find out which function to call
	$('#dirlist').html('

');
	$('#songlist').load(url, function () {
		searching = false;
		$('a[data-song]').click(function() {
			songid = $(this).attr('data-song');
			mpd_add(songid, $(this));
			return false;
		});
		init_songinfo();
	});
}

function init_playlist_button() {
	$('#showplaylistmodal').click(function() {
		show_playlist()
		return false;
	});
	$('#playlistmodal').on('show', function () {
		$(this).css({'margin-left': function () {
				return -($(this).width() / 2);
			}
		});
	});
}

function init_streams_button() {
	$('#showstreams').click(function() {
		$('#streamsmodal').modal();
		$('#streamsmodal').find('.modal-body').load($SCRIPT_ROOT + "/ajax/html_streams/", function () {
			$('.addstream').click(function() {
				streamid = $(this).attr('data-stream');
				mpd_cmd('addstream', $(this), {'i': streamid});
			});
		});
	});
}

function init_outputs_button() {
	$('#showoutputs').click(function() {
		$('#outputsmodal').modal();
		$('#outputsmodal').find('.modal-body').load($SCRIPT_ROOT + "/ajax/html_outputs/", function () {
  		$('.switch')['bootstrapSwitch']();

			$('div[data-outputid]').on('switch-change', function(e, data) {
				id = $(this).attr('data-outputid');
				if (data.value) {
					cmd='enableoutput';
				}
				else {
					cmd='disableoutput';
				}
				mpd_cmd(cmd, $(this), {'i': id, 'placement': 'left'});
			})
		});
	});
}

function init_search() {
	/* Near-real-time search. On input, wait 600ms for more input before doing search */
	$('#search').on('input', function() {
		if (inputtimer) {
			clearTimeout(inputtimer);
		}
		inputtimer = setTimeout(function () { handle_search(); }, 600);
	});

	$('#searchbutton').click(function() {
		handle_search();
	});
}

function init_sort_button_links() {
	$('a[data-sort]').click(function() {
		sortcol = $(this).attr('data-sort');
		if ($('#currentdiv').attr('data-currentdir') == 'SEARCHRESULT') {
			handle_search(sortcol);
		}
		else {
			html_songlist(currentdir, sortcol);
		}
		// Hide the sort menu
		$('#sortbutton').dropdown('toggle');
		return false;
	});
}

function init_player_control_buttons() {
	$.each(['play','stop','pause','prev','next'], function(index,value) {
		$('#mpd_'+ value).click(function(e) {
			mpd_cmd(value, $(this), {'placement': 'top'});
			load_playlist_if_not_updating()
			return false;
		});
	});

	$('#mpd_clear').click(function(e) {
		if (confirm('Clear entire playlist. Are you sure?')) {
			mpd_cmd('clear', $('#mpdplaylist'), {'placement': 'top'});
		}
		return false;
	});

	$('#mpd_cleanup').click(function(e) {
		if (confirm('Clean up old songs from playlist. Are you sure?')) {
			mpd_cmd('cleanup', $('#mpdplaylist'), {'placement': 'top'});
		}
		return false;
	});
}

function init_pjax() {
	// Use PJAX for #dirlist div
	$(document).pjax('a[data-pjax]', '#dirlist');

	// When dirlist is loaded, initialize related elements
	$(document).on('pjax:end', function(e) {
		currentdir = $('#currentdiv').attr('data-currentdir');
		add_dir_click(currentdir);
		show_breadcrumbs(currentdir);
		html_songlist(currentdir);
	})
}

jQuery(document).ready(function($) {

	// Initialize the AJAX containers using the path from the global 'initpath'
	// variable, which is set from the index.html template. It's already
	// urlencoded.
	html_dirlist(initpath);
	html_songlist(initpath);

	// Initilize UI event handlers
	init_playlist_button();
	init_streams_button();
	init_outputs_button();
	init_search();
	init_sort_button_links();
	init_player_control_buttons();
	init_pjax();

});
<|endoftext|>


<commit_before>'use strict';
/**
 * byTagName
 * @param {object} node
 * @param {object} tagName
 * @return {object}
 */
function byTagName(node, tagName) {
	return true;
}

if (typeof module != 'undefined' && module.exports)
  module.exports = {byTagName};
<commit_msg>13.2 Elements by tag name, solution<commit_after>'use strict';
/**
 * byTagName
 * @param {object} node
 * @param {object} tagName
 * @return {object}
 */
function byTagName(node, tagName) {
  var found = [];
  tagName = tagName.toUpperCase();

  function explore(node) {
    for (var i = 0; i < node.childNodes.length; i++) {
      var child = node.childNodes[i];
      if (child.nodeType == document.ELEMENT_NODE) {
        if (child.nodeName == tagName)
          found.push(child);
        explore(child);
      }
    }
  }

  explore(node);
  return found;
}

if (typeof module != 'undefined' && module.exports)
	module.exports = {byTagName};
<|endoftext|>


<commit_before>//v0.9
	window.tempModeratorSkin=""
		if ($("#nick").val().includes('℄')){
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
	}
	if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID]){
		if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID].reason == "Moderator"){
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
		}
		if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID].reason == "LMauthor"){
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'

		}
		if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID].reason == "Shiro"){
			window.tempModeratorSkin += '

'
		}
	}
	window.tempAnimatedCool=""
	if (animatedskins){
		Object.keys(animatedskins).forEach(function(key) {
			if (animatedskins[key] && animatedskins[key].id && animatedskins[key].frames[0] && animatedskins[key].frames[0].id)
			window.tempAnimatedCool += '

'
			window.tempAnimatedCoolArray.push(key)
		});
	}

            $('#helloContainer').after('
' +
				'<link rel="stylesheet" type="text/css" href="https://legendmod.ml/css/specialeffects.css">' +
				'

' +
                //'
' +
				'
' +
                '
' +
                '
<button id="CloseLMShop" type="button" class="close" data-dismiss="modal">×' + Premadeletter113 + '</button> <button id="FAQLMShop" type="button" class="close" data-dismiss="modal">?' + Premadeletter113 + '</button>' +
                '
' + 'Shop' + '
' +
                '
' +

				'
' +

        '
' +
    '
' +
        '
' +
            '
' +

                '
Premium
' +
				'
WaterMarks
' +
				'
Animated-Free
' +
				'
Free
' +
				'
In Use
' +
                '
<input type="text" id="skin-url" placeholder="Special Skin Effect" readonly>
' +
		'
Use
' +
		'
Stop
' +
		    '
' +
        '
' +
    '
' +

    '
' +
		'

' +
        '

' +
        '

' +
		'

' +
		'

' +
        '

' +
        '

' +
		'

' +
	    '

' +
	    '

' +
	    '

' +
	    '

' +
	    '

' +
	    '

' +
		'

' +
		window.tempModeratorSkin +
   '
' +
	        '
' +
			//'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
	'
' +
	        '
' +
			window.tempAnimatedCool	+
	'
' +
	        '
' +
			'

' +
	'
' +
        '
' +
	'
' +
	'
' +
    '' +
    '' +
                '
' +
                '
' +
                '
' +
                '
');
			$("#animatedLikeGif").children().css("width", "55px");
			$("#animatedLikeGif").css("padding-right", "100px");
            $("#CloseLMShop").click(function() {
                $("#LMShop").remove();
            });
            $("#FAQLMShop").click(function() {
				window.open('https://legendmod.ml/', '_blank');
            });
$(".modal-dialog").draggable()
<commit_msg>Update shop.js<commit_after>//v0.9
	window.tempModeratorSkin=""
		if ($("#nick").val().includes('℄')){
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
	}
	if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID]){
		if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID].reason == "Moderator"){
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
		}
		if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID].reason == "LMauthor"){
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'
			window.tempModeratorSkin += '

'

		}
		if (ProLicenceUsersTable.ProLicenceUsers[window.agarioUID].reason == "Shiro"){
			window.tempModeratorSkin += '

'
		}
	}
	window.tempAnimatedCool=""
	if (animatedskins){
		Object.keys(animatedskins).forEach(function(key) {
			if (animatedskins[key] && animatedskins[key].id && animatedskins[key].frames[0] && animatedskins[key].frames[0].id)
			window.tempAnimatedCool += '

'
			window.tempAnimatedCoolArray.push(key)
		});
	}

            $('#helloContainer').after('
' +
				'<link rel="stylesheet" type="text/css" href="https://legendmod.ml/css/specialeffects.css">' +
				'

' +
                //'
' +
				'
' +
                '
' +
                '
<button id="CloseLMShop" type="button" class="close" data-dismiss="modal">×' + Premadeletter113 + '</button> <button id="FAQLMShop" type="button" class="close" data-dismiss="modal">?' + Premadeletter113 + '</button>' +
                '
' + 'Shop' + '
' +
                '
' +

				'
' +

        '
' +
    '
' +
        '
' +
            '
' +

                '
Premium
' +
				'
WaterMarks
' +
				'
Animated-Free
' +
				'
Free
' +
				'
In Use
' +
                '
<input type="text" id="skin-url" placeholder="Special Skin Effect" readonly>
' +
		'
Use
' +
		'
Stop
' +
		    '
' +
        '
' +
    '
' +

    '
' +
		'

' +
        '

' +
        '

' +
		'

' +
		'

' +
        '

' +
        '

' +
		'

' +
	    '

' +
	    '

' +
	    '

' +
	    '

' +
	    '

' +
	    '

' +
		'

' +
		window.tempModeratorSkin +
   '
' +
	        '
' +
			//'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
			'

' +
	'
' +
	        '
' +
			window.tempAnimatedCool	+
	'
' +
	        '
' +
			'

' +
	'
' +
        '
' +
	'
' +
	'
' +
    '' +
    '' +
                '
' +
                '
' +
                '
' +
                '
');
			$("#animatedLikeGif").children().css("width", "55px");
			$("#animatedLikeGif").css("padding-right", "100px");
            $("#CloseLMShop").click(function() {
                $("#LMShop").remove();
            });
            $("#FAQLMShop").click(function() {
				window.open('https://legendmod.ml/', '_blank');
            });
$(".modal-dialog").draggable()
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
const { clearPK } = require('../store/authentication').actions;

let idleTime = 0;
let interval;
function startInterval(props) {
  //Increment the idle time counter every minute.
  interval = setInterval(async () => {
    idleTime++;
    console.log('timeout: ', idleTime);
    if (idleTime > 20) { // 20 minute timeout
      await props.clearPK();
      props.router.push('/unlock-account');
      idleTime = 0;
    }
  }, 6000); // 1 minute
}

function stopInterval() {
  clearInterval(interval);
}

(function setActivityListeners() {
  console.log('setting global listeners');
  // //Zero the idle timer on mouse movement.
  document.addEventListener('mousemove', () => {
    idleTime = 0;
  }, false);
  document.addEventListener('keydown', () => {
    idleTime = 0;
  }, false);
})();

export default function(ComposedComponent) {
  class Authenticate extends React.Component {
    constructor(props) {
      super(props);
    }

    componentWillUnmount() {
      stopInterval();
    }

    componentDidMount() {
      console.log('hoc pk did mount');
      if (this.props.user) {
        startInterval(this.props);
      }
    }

    render() {
      return (<ComposedComponent {...this.props} />);
    }
  }

  const mapDispatchToProps = {
    clearPK
  };

  function mapStateToProps(state) {
    return {
      user: state.auth.user
    };
  }

  return connect(mapStateToProps, mapDispatchToProps)(Authenticate);
}
<commit_msg>extended private key time<commit_after>import React from 'react';
import { connect } from 'react-redux';
const { clearPK } = require('../store/authentication').actions;

let idleTime = 0;
let interval;
function startInterval(props) {
  //Increment the idle time counter every minute.
  interval = setInterval(async () => {
    idleTime++;
    console.log('timeout: ', idleTime);
    if (idleTime > 20) { // 20 minute timeout
      await props.clearPK();
      props.router.push('/unlock-account');
      idleTime = 0;
    }
  }, 60000); // 1 minute
}

function stopInterval() {
  clearInterval(interval);
}

(function setActivityListeners() {
  console.log('setting global listeners');
  // //Zero the idle timer on mouse movement.
  document.addEventListener('mousemove', () => {
    idleTime = 0;
  }, false);
  document.addEventListener('keydown', () => {
    idleTime = 0;
  }, false);
})();

export default function(ComposedComponent) {
  class Authenticate extends React.Component {
    constructor(props) {
      super(props);
    }

    componentWillUnmount() {
      stopInterval();
    }

    componentDidMount() {
      console.log('hoc pk did mount');
      if (this.props.user) {
        startInterval(this.props);
      }
    }

    render() {
      return (<ComposedComponent {...this.props} />);
    }
  }

  const mapDispatchToProps = {
    clearPK
  };

  function mapStateToProps(state) {
    return {
      user: state.auth.user
    };
  }

  return connect(mapStateToProps, mapDispatchToProps)(Authenticate);
}
<|endoftext|>


<commit_before>var u = require('../util')
var h = require('hyperscript')
var pull = require('pull-stream')
var mime = require('mime-types')
var split = require('split-buffer')

var plugs = require('../plugs')

var add = plugs.first(exports.sbot_blobs_add = [])

exports.file_input = function FileInput(onAdded) {

  return h('input', { type: 'file',
    onchange: function (ev) {
      var file = ev.target.files[0]
      var reader = new FileReader()
      reader.onload = function () {
        pull(
          pull.values(split(new Buffer(reader.result), 64*1024)),
          add(function (err, blob) {
            if(err) return console.error(err)
            onAdded({
              link: blob,
              name: file.name,
              size: reader.result.length || reader.result.byteLength,
              type: mime.contentType(file.name)
            })

          })
        )
      }
      reader.readAsArrayBuffer(file)
    }
  })
}

<commit_msg>use a way smaller mime module<commit_after>var u = require('../util')
var h = require('hyperscript')
var pull = require('pull-stream')
var mime = require('simple-mime')('application/octect-stream')
var split = require('split-buffer')

var plugs = require('../plugs')

var add = plugs.first(exports.sbot_blobs_add = [])

exports.file_input = function FileInput(onAdded) {

  return h('input', { type: 'file',
    onchange: function (ev) {
      var file = ev.target.files[0]
      var reader = new FileReader()
      reader.onload = function () {
        pull(
          pull.values(split(new Buffer(reader.result), 64*1024)),
          add(function (err, blob) {
            if(err) return console.error(err)
            onAdded({
              link: blob,
              name: file.name,
              size: reader.result.length || reader.result.byteLength,
              type: mime(file.name)
            })

          })
        )
      }
      reader.readAsArrayBuffer(file)
    }
  })
}

<|endoftext|>