<commit_before>#! /usr/bin/env node
'use strict';

const program = require('commander');
const fs = require('fs');
const spotify = require('spotify-web-api-node');
const prompt = require('prompt');
const parseSearchResults = require('./parsers/');
const printer = require('./printers/');
const spotifyClient = require('./osascripts/');
const nconf = require('nconf');
const path = require('path');
const readlineSync = require('readline-sync');
nconf.file(path.join(__dirname, '/config.json'));

// need client access token for genius
let lyricist = require('lyricist');

// let rl = readline.createInterface({
//   input: process.stdin,
//   output: process.stdout
// });

let GENIUS_API_KEY = nconf.get('GeniusAPIClientKey');
let GENIUS_API_KEY_SET = GENIUS_API_KEY !== 'YOUR_CLIENT_ACCESS_TOKEN_HERE';
if(GENIUS_API_KEY_SET){
	lyricist = lyricist(GENIUS_API_KEY);
}

let SPOTIFY_CLIENT_ID = nconf.get('spotifyClientID');
let SPOTIFY_CLIENT_ID_SET = SPOTIFY_CLIENT_ID !== 'YOUR_SPOTIFY_CLIENT_ID_HERE';
let SPOTIFY_CLIENT_SECRET = nconf.get('spotifyClientSecret');
let SPOTIFY_CLIENT_SECRET_SET = SPOTIFY_CLIENT_SECRET !== 'YOUR_SPOTIFY_CLIENT_SECRET_HERE';

let spotifyApi = null;

	if(SPOTIFY_CLIENT_ID_SET && SPOTIFY_CLIENT_SECRET_SET) {
		spotifyApi = new spotify({
			clientId : SPOTIFY_CLIENT_ID,
			clientSecret : SPOTIFY_CLIENT_SECRET
		});

		const SearchOptions = {
			'track': {
				'fn': spotifyApi.searchTracks.bind(spotifyApi),
				'type': 'tracks'
			},
			't': {
				'fn': spotifyApi.searchTracks.bind(spotifyApi),
				'type': 'tracks'
			},
			'artist': {
				'fn': spotifyApi.searchArtists.bind(spotifyApi),
				'type': 'artists'
			},
			'ar': {
				'fn': spotifyApi.searchArtists.bind(spotifyApi),
				'type': 'artists'
			},
			'album': {
				'fn': spotifyApi.searchAlbums.bind(spotifyApi),
				'type': 'albums'
			},
			'al': {
				'fn': spotifyApi.searchAlbums.bind(spotifyApi),
				'type': 'albums'
			},
			'playlist': {
				'fn': spotifyApi.searchPlaylists.bind(spotifyApi),
				'type': 'playlists'
			},
			'p': {
				'fn': spotifyApi.searchPlaylists.bind(spotifyApi),
				'type': 'playlists'
			}
		};
		return;
	}
	else {
		let clientID = readlineSync.question('What is your Spotify Client ID? \n');
		let clientSecret = readlineSync.question('What is your Spotify Client Secret? \n')
		nconf.set('spotifyClientID', clientID);
		nconf.set('spotifyClientSecret', clientSecret);
		nconf.save(function (err) {
			fs.readFile(path.join(__dirname, '/config.json'), function (err, data) {
			});
		});
	}

program
	.version('0.0.2')
	.command('search <type> [query...]')
	.description('Search for a <track (t) | artist (ar) | album (al) | playlist (p) > (searches tracks by default)')
	.alias('s')
	.action((type, query) => {
		// search for tracks by default
		var searchFn = SearchOptions['track'].fn;
		var searchQry = `${type} ${query.join(' ')}`;
		var resultType = SearchOptions['track'].type;

		if (SearchOptions[type]){
			searchFn = SearchOptions[type].fn;
			searchQry = query.join(' ');
			resultType = SearchOptions[type].type;
		}

		// Retrieve an access token.
		spotifyApi.clientCredentialsGrant()
			.then(function(data) {
				spotifyApi.setAccessToken(data.body['access_token']);
				searchFn(searchQry).then((data) => {
					var results = parseSearchResults(resultType, data);
					printer.printSearchResults(resultType, results);
					prompt.start();
					prompt.get(['selection'], function (err, result) {
						if (err) {
							return process.stdout.write('\n');
						}
						if(results[result.selection-1]){
							var selectedSpotifyURI = results[result.selection-1].spotifyURI;
							spotifyClient.play(selectedSpotifyURI).then(() => {
								spotifyClient.status().then((result) => {
									printer.printPlayerStatus(result);
								});
							});
						}
					});
				});
			}, function(err) {
				console.log('Something went wrong when retrieving an access token', err);
			});
	});

program
	.command('info')
	.alias('i')
	.description('Display information about the current track along with player status')
	.action(() => {
		spotifyClient.status().then((result) => {
			printer.printPlayerStatus(result);
		});
	});

program
	.command('play [uri]')
	.description('Continue playing current track or play the track with the provided URI')
	.action((uri) => {
		if(uri){
			spotifyClient.play(uri).then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
		else {
			spotifyClient.play().then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
	});

program
	.command('pause')
	.description('Pause the current track')
	.action(() => {
		spotifyClient.pause().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPlayerStatus(result);
			});
		});
	});

program
	.command('next')
	.alias('n')
	.description('Play the next track in the queue')
	.action(() => {
		spotifyClient.next().then(() => {
			spotifyClient.status().then((result) => {
				printer.printNext(result);
			});
		});
	});

program
	.command('back')
	.alias('b')
	.description('Play the previous track')
	.action(() => {
		spotifyClient.previous().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPrevious(result);
			});
		});
	});

program
	.command('mute')
	.alias('m')
	.description('Mute player')
	.action(() => {
		spotifyClient.mute().then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printMute(result);
			});
		});
	});

program
	.command('unmute')
	.alias('u')
	.description('Unmute player')
	.action(() => {
		spotifyClient.unmute().then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printUnmute(result);
			});
		});
	});

program
	.command('volume [newVolume]')
	.alias('v')
	.description('Display player volume')
	.action((newVolume) => {
		if(newVolume){
			spotifyClient.setVolume(newVolume).then(() => {
				spotifyClient.getVolume().then((result) => {
					printer.printSetVolume(result);
				});
			});
		}
		else {
			spotifyClient.getVolume().then(() => {
				spotifyClient.getVolume().then((result) => {
					printer.printVolume(result);
				});
			});
		}
	});

program
	.command('+ [deltaVolume]')
	.description('Turn the volume up by given amount (0-100), default:10')
	.action((deltaVolume) => {
		var changeInVolume = deltaVolume ? deltaVolume : 10;
		spotifyClient.changeVolume(changeInVolume).then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printVolumeIncrease(changeInVolume, result);
			});
		});
	});

program
	.command('- [deltaVolume]')
	.description('Turn the volume down by given amount (0-100), default:10')
	.action((deltaVolume) => {
		var changeInVolume = deltaVolume ? -deltaVolume : -10;
		spotifyClient.changeVolume(changeInVolume).then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printVolumeDecrease(changeInVolume, result);
			});
		});
	});

program
	.command('p')
	.description('Toggle play/pause')
	.action(() => {
		spotifyClient.togglePlayPause().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPlayerStatus(result);
			});
		});
	});

program
	.command('replay')
	.alias('r')
	.description('Replay current track')
	.action(() => {
		spotifyClient.replay().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPlayerStatus(result);
			});
		});
	});

program
	.command('position [newPosition]')
	.alias('pos')
	.description('Get or set player position [mm:ss], e.g: pos 1:23')
	.action((newPosition) => {
		if(newPosition){
			spotifyClient.setPosition(newPosition).then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
		else {
			spotifyClient.getPosition().then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
	});

program
	.command('quit')
	.alias('q')
	.description('Quit Spotify :(')
	.action(() =>{
		spotifyClient.quit();
	});

program
	.command('open')
	.alias('o')
	.description('Open Spotify :)')
	.action(() =>{
		spotifyClient.start();
	});

program
	.command('shuffle')
	.alias('ts')
	.description('Toggle shuffle on/off')
	.action(() => {
		spotifyClient.shuffle().then((status) => {
			printer.printToggleShuffle(status);
		});
	});

program
	.command('repeat')
	.alias('tr')
	.description('Toggle repeat on/off')
	.action(() => {
		spotifyClient.repeat().then((status) => {
			printer.printToggleRepeat(status);
		});
	});

program
	.command('share [type]')
	.alias('sh')
	.description('Display share <uri|url> and copy value to clipboard')
	.action((type) => {
		spotifyClient.share(type);
	});

program
	.command('token')
	.alias('tk')
	.description('Change Client tokens')
	.action(() => {
		let clientID = readlineSync.question('What is your Spotify Client ID? \n');
		let clientSecret = readlineSync.question('What is your Spotify Client Secret? \n')
		nconf.set('spotifyClientID', clientID);
		nconf.set('spotifyClientSecret', clientSecret);
		nconf.save(function (err) {
			fs.readFile(path.join(__dirname, '/config.json'), function (err, data) {
			});
		});
	})
program
	.command('lyrics')
	.alias('ly')
	.description('Display the lyrics of currently playing track')
	.action(() => {
		spotifyClient.status().then((trackInfo) => {
			if(!GENIUS_API_KEY_SET){
				console.log('You need to set the Client Access Token for Genius API.');
				console.log('Sign up for API access here: https://genius.com/api-clients');
				console.log('Update config.json file with your credentials');
				return;
			}
			lyricist.song({search: `${trackInfo.artist} ${trackInfo.track}`}, function (err, song) {
				if(err){
					console.log(`Could not find lyrics for track: ${trackInfo.track} - ${trackInfo.artist}`);
				}
				else {
					console.log(`${trackInfo.track} - ${trackInfo.artist} Lyrics`);
					console.log(song.lyrics);
				}
			});
		});
	});

program.parse(process.argv);
<commit_msg>small return fix<commit_after>#! /usr/bin/env node
'use strict';

const program = require('commander');
const fs = require('fs');
const spotify = require('spotify-web-api-node');
const prompt = require('prompt');
const parseSearchResults = require('./parsers/');
const printer = require('./printers/');
const spotifyClient = require('./osascripts/');
const nconf = require('nconf');
const path = require('path');
const readlineSync = require('readline-sync');
nconf.file(path.join(__dirname, '/config.json'));

// need client access token for genius
let lyricist = require('lyricist');

// let rl = readline.createInterface({
//   input: process.stdin,
//   output: process.stdout
// });

let GENIUS_API_KEY = nconf.get('GeniusAPIClientKey');
let GENIUS_API_KEY_SET = GENIUS_API_KEY !== 'YOUR_CLIENT_ACCESS_TOKEN_HERE';
if(GENIUS_API_KEY_SET){
	lyricist = lyricist(GENIUS_API_KEY);
}

let SPOTIFY_CLIENT_ID = nconf.get('spotifyClientID');
let SPOTIFY_CLIENT_ID_SET = SPOTIFY_CLIENT_ID !== 'YOUR_SPOTIFY_CLIENT_ID_HERE';
let SPOTIFY_CLIENT_SECRET = nconf.get('spotifyClientSecret');
let SPOTIFY_CLIENT_SECRET_SET = SPOTIFY_CLIENT_SECRET !== 'YOUR_SPOTIFY_CLIENT_SECRET_HERE';

let spotifyApi = null;

	if(SPOTIFY_CLIENT_ID_SET && SPOTIFY_CLIENT_SECRET_SET) {
		spotifyApi = new spotify({
			clientId : SPOTIFY_CLIENT_ID,
			clientSecret : SPOTIFY_CLIENT_SECRET
		});

		const SearchOptions = {
			'track': {
				'fn': spotifyApi.searchTracks.bind(spotifyApi),
				'type': 'tracks'
			},
			't': {
				'fn': spotifyApi.searchTracks.bind(spotifyApi),
				'type': 'tracks'
			},
			'artist': {
				'fn': spotifyApi.searchArtists.bind(spotifyApi),
				'type': 'artists'
			},
			'ar': {
				'fn': spotifyApi.searchArtists.bind(spotifyApi),
				'type': 'artists'
			},
			'album': {
				'fn': spotifyApi.searchAlbums.bind(spotifyApi),
				'type': 'albums'
			},
			'al': {
				'fn': spotifyApi.searchAlbums.bind(spotifyApi),
				'type': 'albums'
			},
			'playlist': {
				'fn': spotifyApi.searchPlaylists.bind(spotifyApi),
				'type': 'playlists'
			},
			'p': {
				'fn': spotifyApi.searchPlaylists.bind(spotifyApi),
				'type': 'playlists'
			}
		};
	}
	else {
		let clientID = readlineSync.question('What is your Spotify Client ID? \n');
		let clientSecret = readlineSync.question('What is your Spotify Client Secret? \n')
		nconf.set('spotifyClientID', clientID);
		nconf.set('spotifyClientSecret', clientSecret);
		nconf.save(function (err) {
			fs.readFile(path.join(__dirname, '/config.json'), function (err, data) {
			});
		});
	}

program
	.version('0.0.2')
	.command('search <type> [query...]')
	.description('Search for a <track (t) | artist (ar) | album (al) | playlist (p) > (searches tracks by default)')
	.alias('s')
	.action((type, query) => {
		// search for tracks by default
		var searchFn = SearchOptions['track'].fn;
		var searchQry = `${type} ${query.join(' ')}`;
		var resultType = SearchOptions['track'].type;

		if (SearchOptions[type]){
			searchFn = SearchOptions[type].fn;
			searchQry = query.join(' ');
			resultType = SearchOptions[type].type;
		}

		// Retrieve an access token.
		spotifyApi.clientCredentialsGrant()
			.then(function(data) {
				spotifyApi.setAccessToken(data.body['access_token']);
				searchFn(searchQry).then((data) => {
					var results = parseSearchResults(resultType, data);
					printer.printSearchResults(resultType, results);
					prompt.start();
					prompt.get(['selection'], function (err, result) {
						if (err) {
							return process.stdout.write('\n');
						}
						if(results[result.selection-1]){
							var selectedSpotifyURI = results[result.selection-1].spotifyURI;
							spotifyClient.play(selectedSpotifyURI).then(() => {
								spotifyClient.status().then((result) => {
									printer.printPlayerStatus(result);
								});
							});
						}
					});
				});
			}, function(err) {
				console.log('Something went wrong when retrieving an access token', err);
			});
	});

program
	.command('info')
	.alias('i')
	.description('Display information about the current track along with player status')
	.action(() => {
		spotifyClient.status().then((result) => {
			printer.printPlayerStatus(result);
		});
	});

program
	.command('play [uri]')
	.description('Continue playing current track or play the track with the provided URI')
	.action((uri) => {
		if(uri){
			spotifyClient.play(uri).then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
		else {
			spotifyClient.play().then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
	});

program
	.command('pause')
	.description('Pause the current track')
	.action(() => {
		spotifyClient.pause().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPlayerStatus(result);
			});
		});
	});

program
	.command('next')
	.alias('n')
	.description('Play the next track in the queue')
	.action(() => {
		spotifyClient.next().then(() => {
			spotifyClient.status().then((result) => {
				printer.printNext(result);
			});
		});
	});

program
	.command('back')
	.alias('b')
	.description('Play the previous track')
	.action(() => {
		spotifyClient.previous().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPrevious(result);
			});
		});
	});

program
	.command('mute')
	.alias('m')
	.description('Mute player')
	.action(() => {
		spotifyClient.mute().then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printMute(result);
			});
		});
	});

program
	.command('unmute')
	.alias('u')
	.description('Unmute player')
	.action(() => {
		spotifyClient.unmute().then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printUnmute(result);
			});
		});
	});

program
	.command('volume [newVolume]')
	.alias('v')
	.description('Display player volume')
	.action((newVolume) => {
		if(newVolume){
			spotifyClient.setVolume(newVolume).then(() => {
				spotifyClient.getVolume().then((result) => {
					printer.printSetVolume(result);
				});
			});
		}
		else {
			spotifyClient.getVolume().then(() => {
				spotifyClient.getVolume().then((result) => {
					printer.printVolume(result);
				});
			});
		}
	});
program
	.command('+ [deltaVolume]')
	.description('Turn the volume up by given amount (0-100), default:10')
	.action((deltaVolume) => {
		var changeInVolume = deltaVolume ? deltaVolume : 10;
		spotifyClient.changeVolume(changeInVolume).then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printVolumeIncrease(changeInVolume, result);
			});
		});
	});

program
	.command('- [deltaVolume]')
	.description('Turn the volume down by given amount (0-100), default:10')
	.action((deltaVolume) => {
		var changeInVolume = deltaVolume ? -deltaVolume : -10;
		spotifyClient.changeVolume(changeInVolume).then(() => {
			spotifyClient.getVolume().then((result) => {
				printer.printVolumeDecrease(changeInVolume, result);
			});
		});
	});

program
	.command('p')
	.description('Toggle play/pause')
	.action(() => {
		spotifyClient.togglePlayPause().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPlayerStatus(result);
			});
		});
	});

program
	.command('replay')
	.alias('r')
	.description('Replay current track')
	.action(() => {
		spotifyClient.replay().then(() => {
			spotifyClient.status().then((result) => {
				printer.printPlayerStatus(result);
			});
		});
	});

program
	.command('position [newPosition]')
	.alias('pos')
	.description('Get or set player position [mm:ss], e.g: pos 1:23')
	.action((newPosition) => {
		if(newPosition){
			spotifyClient.setPosition(newPosition).then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
		else {
			spotifyClient.getPosition().then(() => {
				spotifyClient.status().then((result) => {
					printer.printPlayerStatus(result);
				});
			});
		}
	});

program
	.command('quit')
	.alias('q')
	.description('Quit Spotify :(')
	.action(() =>{
		spotifyClient.quit();
	});

program
	.command('open')
	.alias('o')
	.description('Open Spotify :)')
	.action(() =>{
		spotifyClient.start();
	});

program
	.command('shuffle')
	.alias('ts')
	.description('Toggle shuffle on/off')
	.action(() => {
		spotifyClient.shuffle().then((status) => {
			printer.printToggleShuffle(status);
		});
	});

program
	.command('repeat')
	.alias('tr')
	.description('Toggle repeat on/off')
	.action(() => {
		spotifyClient.repeat().then((status) => {
			printer.printToggleRepeat(status);
		});
	});

program
	.command('share [type]')
	.alias('sh')
	.description('Display share <uri|url> and copy value to clipboard')
	.action((type) => {
		spotifyClient.share(type);
	});

program
	.command('token')
	.alias('tk')
	.description('Change Client tokens')
	.action(() => {
		let clientID = readlineSync.question('What is your Spotify Client ID? \n');
		let clientSecret = readlineSync.question('What is your Spotify Client Secret? \n')
		nconf.set('spotifyClientID', clientID);
		nconf.set('spotifyClientSecret', clientSecret);
		nconf.save(function (err) {
			fs.readFile(path.join(__dirname, '/config.json'), function (err, data) {
			});
		});
	})
program
	.command('lyrics')
	.alias('ly')
	.description('Display the lyrics of currently playing track')
	.action(() => {
		spotifyClient.status().then((trackInfo) => {
			if(!GENIUS_API_KEY_SET){
				console.log('You need to set the Client Access Token for Genius API.');
				console.log('Sign up for API access here: https://genius.com/api-clients');
				console.log('Update config.json file with your credentials');
				return;
			}
			lyricist.song({search: `${trackInfo.artist} ${trackInfo.track}`}, function (err, song) {
				if(err){
					console.log(`Could not find lyrics for track: ${trackInfo.track} - ${trackInfo.artist}`);
				}
				else {
					console.log(`${trackInfo.track} - ${trackInfo.artist} Lyrics`);
					console.log(song.lyrics);
				}
			});
		});
	});

program.parse(process.argv);
<|endoftext|>


<commit_before>/*
@license https://github.com/t2ym/gulp-i18n-add-locales/blob/master/LICENSE.md
Copyright (c) 2016, Tetsuya Mori <t2y3141592@gmail.com>. All rights reserved.
*/
'use strict';

var path = require('path');
var fs = require('fs');
var gutil = require('gulp-util');
var through = require('through2');
var dom5 = require('dom5');

/**
 * Gulp plug-in to add placeholder JSON files in target locales for i18n-behavior.
 *
 * @namespace gulp-i18n-add-locales
 */
module.exports = function (locales) {
  var localesFolder = 'locales';
  return through.obj(function (file, enc, callback) {
    if (file.isNull()) {
      return callback(null, file);
    }
    if (!file.isBuffer()) {
      return callback(null, file);
    }

    var stream = this;
    var dirname = path.dirname(file.path);
    var basenames = [];
    var cwd = file.cwd;
    var base = file.base;
    var firstFile = true;

    var contents = String(file.contents);
    var document = dom5.parse(contents);
    var templates = [];
    var i, j;

    dom5.nodeWalkAll(document, function (node) {
      if (dom5.predicates.hasTagName('template')(node)) {
        var is = dom5.getAttribute(node, 'is');
        var parent = node.parentNode;
        var parentTagName = parent && parent.tagName ?
                              parent.tagName.toLowerCase() : null;
        if (is === 'i18n-dom-bind') {
          return true;
        }
        switch (parentTagName) {
        case 'dom-module':
        case 'body':
        case 'head':
        case 'html':
          return true;
        default:
          return false;
        }
      }
      else {
        return false;
      }
    }, templates);

    for (i = 0; i < templates.length; i++) {
      var moduleId = dom5.getAttribute(templates[i], 'id') ||
                      dom5.getAttribute(templates[i].parentNode, 'id');
      //console.log('module id = ' + moduleId);
      basenames.push(moduleId);
    }

    var targetDir = path.join(path.dirname(file.path), localesFolder);
    for (j in basenames) {
      var basename = basenames[j];
      try {
        fs.mkdirSync(targetDir);
      }
      catch (e) {}
      for (i in locales) {
        var target = path.join(targetDir, basename + '.' + locales[i] + '.json');
        var stats;
        try {
          stats = undefined;
          stats = fs.statSync(target);
        }
        catch (e) {}
        if (stats) {
          //console.log('addLocales: existing ' + target);
        }
        else {
          // create an empty placeholder file
          if (firstFile) {
            firstFile = false;
            file.path = target;
            file.contents = new Buffer('{}');
          }
          else {
            stream.push(new gutil.File({
              cwd: cwd,
              base: base,
              path: target,
              contents: new Buffer('{}')
            }));
          }
          //console.log('addLocales: creating ' + target);
        }
      }
    }

    callback(null, firstFile ? null : file);
  });
};
<commit_msg>Fix #3. Support i18n-dom-bind for Polymer 2.x<commit_after>/*
@license https://github.com/t2ym/gulp-i18n-add-locales/blob/master/LICENSE.md
Copyright (c) 2016, Tetsuya Mori <t2y3141592@gmail.com>. All rights reserved.
*/
'use strict';

var path = require('path');
var fs = require('fs');
var gutil = require('gulp-util');
var through = require('through2');
var dom5 = require('dom5');

/**
 * Gulp plug-in to add placeholder JSON files in target locales for i18n-behavior.
 *
 * @namespace gulp-i18n-add-locales
 */
module.exports = function (locales) {
  var localesFolder = 'locales';
  return through.obj(function (file, enc, callback) {
    if (file.isNull()) {
      return callback(null, file);
    }
    if (!file.isBuffer()) {
      return callback(null, file);
    }

    var stream = this;
    var dirname = path.dirname(file.path);
    var basenames = [];
    var cwd = file.cwd;
    var base = file.base;
    var firstFile = true;

    var contents = String(file.contents);
    var document = dom5.parse(contents);
    var templates = [];
    var i, j;

    dom5.nodeWalkAll(document, function (node) {
      if (dom5.predicates.hasTagName('template')(node)) {
        var is = dom5.getAttribute(node, 'is');
        var parent = node.parentNode;
        var parentTagName = parent && parent.tagName ?
                              parent.tagName.toLowerCase() : null;
        if (is === 'i18n-dom-bind') {
          return true;
        }
        switch (parentTagName) {
        case 'dom-module':
        case 'body':
        case 'head':
        case 'html':
        case 'i18n-dom-bind':
          return true;
        default:
          return false;
        }
      }
      else {
        return false;
      }
    }, templates);

    for (i = 0; i < templates.length; i++) {
      var moduleId = dom5.getAttribute(templates[i], 'id') ||
                      dom5.getAttribute(templates[i].parentNode, 'id');
      //console.log('module id = ' + moduleId);
      basenames.push(moduleId);
    }

    var targetDir = path.join(path.dirname(file.path), localesFolder);
    for (j in basenames) {
      var basename = basenames[j];
      try {
        fs.mkdirSync(targetDir);
      }
      catch (e) {}
      for (i in locales) {
        var target = path.join(targetDir, basename + '.' + locales[i] + '.json');
        var stats;
        try {
          stats = undefined;
          stats = fs.statSync(target);
        }
        catch (e) {}
        if (stats) {
          //console.log('addLocales: existing ' + target);
        }
        else {
          // create an empty placeholder file
          if (firstFile) {
            firstFile = false;
            file.path = target;
            file.contents = new Buffer('{}');
          }
          else {
            stream.push(new gutil.File({
              cwd: cwd,
              base: base,
              path: target,
              contents: new Buffer('{}')
            }));
          }
          //console.log('addLocales: creating ' + target);
        }
      }
    }

    callback(null, firstFile ? null : file);
  });
};
<|endoftext|>


<commit_before>(function(fs, path){
  "use strict";
  var Reader = {};

  Reader.__cache = {};

  Reader.read = function(file, call){
    var file = path.resolve(file),
        config = {},
        error = null;

    if (typeof call !== "undefined") {
      if (typeof Reader.__cache[file] !== "undefined") {
        call(error, Reader.__cache[file]);
        return config;
      };
      fs.readFile(file, function(err, data){
        if (!err) {
          try {
            config = JSON.parse(data);
            Reader.__cache[file] = config;
          } catch (err) {
            error = err;
          }

        } else {
          error = err;
        }
        call(error, config);

      });

    } else {
      if (typeof Reader.__cache[file] !== "undefined") return Reader.__cache[file];
      try {
        config = JSON.parse(fs.readFileSync(file));
        Reader.__cache[file] = config;
        return config;
      } catch (err) {
        return err;
      }
    }
  };

  Reader.purgeCache = function(file){
    /* Since I've heard delete is really slow, I'm not sure what to do here for now, I'll just set it to undefined: */
    if (typeof file !== "undefined") {
      Reader.__cache[path.resolve(file)] = undefined;
    } else {
      for (var i in Reader.__cache) {
        Reader.__cache[i] = undefined;
      }
    }
  };

  module.exports = exports = Reader;
})(require("fs"),
   require("path"));
<commit_msg>Made cache global, and also made it more flexible.<commit_after>(function(fs, path){
  "use strict";
  var Reader = {};

  global.__jsoncache = global.__jsoncache || {};

  Reader.read = function(file, call){
    var file = path.resolve(file),
        config = {},
        error = null;

    if (typeof call !== "undefined") {
      if (typeof global.__jsoncache[file] !== "undefined") {
        call(error, global.__jsoncache[file]);
        return config;
      };
      fs.readFile(file, function(err, data){
        if (!err) {
          try {
            config = JSON.parse(data);
            global.__jsoncache[file] = config;
          } catch (err) {
            error = err;
          }

        } else {
          error = err;
        }
        call(error, config);

      });

    } else {
      if (typeof global.__jsoncache[file] !== "undefined") return global.__jsoncache[file];
      try {
        config = JSON.parse(fs.readFileSync(file));
        global.__jsoncache[file] = config;
        return config;
      } catch (err) {
        return err;
      }
    }
  };

  Reader.purgeCache = function(file){
    /* Since I've heard delete is really slow, I'm not sure what to do here for now, I'll just set it to undefined: */
    if (typeof file !== "undefined") {
      global.__jsoncache[path.resolve(file)] = undefined;
    } else {
      for (var i in global.__jsoncache) {
        global.__jsoncache[i] = undefined;
      }
    }
  };

  module.exports = exports = Reader;
})(require("fs"),
   require("path"));
<|endoftext|>


<commit_before>var util = require('util');
var Transform = require('stream').Transform;
var binding = require('./build/Release/binding.node');

exports.framesPerPacket = binding.kALACDefaultFramesPerPacket;
exports.sampleRate = 44100;
exports.channels = 2;
exports.bitDepth = 16;

function ALACEncoder(options) {
  Transform.call(this, options);

  this.framesPerPacket = options.framesPerPacket || exports.framesPerPacket;
  this.sampleRate = options.sampleRate || exports.ampleRate;
  this.channels = options.channels || exports.channels;
  this.bitDepth = options.bitDepth || exports.bitDepth;

  this.bytesPerPacket = this.channels * (this.bitDepth >> 3) * this.framesPerPacket;

  this._enc = new binding.Encoder({
    framesPerPacket: this.framesPerPacket,
    sampleRate: this.sampleRate,
    channels: this.channels,
    bitDepth: this.bitDepth
  });

  this._streamPos = 0;
  this._partial = null;

  this.cookie = this._enc.cookie;
  this.packets = [];
}
util.inherits(ALACEncoder, Transform);

ALACEncoder.prototype._transform = function(chunk, encoding, done) {
  if (this._partial) {
    chunk = Buffer.concat([this._partial, chunk]);
    this._partial = null;
  }

  var pos = 0;
  var remainder = chunk.length;
  var enc = this._enc;
  var bpp = this.bytesPerPacket;
  var outSize = bpp + binding.kALACMaxEscapeHeaderBytes;
  while (remainder >= bpp) {
    var out = new Buffer(outSize);

    var end = pos + bpp;
    var frame = chunk.slice(pos, end);
    var bytes = enc.encode(frame, out);
    pos = end;
    remainder -= bpp;

    if (!bytes)
      continue;

    this.packets.push(this._streamPos);
    this._streamPos += bytes;

    if (bytes === outSize)
      this.push(out);
    else
      this.push(out.slice(0, bytes));
  }

  if (remainder !== 0)
    this._partial = chunk.slice(pos);

  done();
};

ALACEncoder.prototype._flush = function(done) {
  if (this._partial) {
    var outSize = this._partial.length + binding.kALACMaxEscapeHeaderBytes;
    var out = new Buffer(outSize);

    var bytes = this._enc.encode(this._partial, out);

    if (bytes === outSize)
      this.push(out);
    else if (bytes)
      this.push(out.slice(0, bytes));
  }

  done();
};

exports.encoder = function(options) {
  return new ALACEncoder(options || {});
};
<commit_msg>Packets contains sizes, matching CAF pakt chunk.<commit_after>var util = require('util');
var Transform = require('stream').Transform;
var binding = require('./build/Release/binding.node');

exports.framesPerPacket = binding.kALACDefaultFramesPerPacket;
exports.sampleRate = 44100;
exports.channels = 2;
exports.bitDepth = 16;

function ALACEncoder(options) {
  Transform.call(this, options);

  this.framesPerPacket = options.framesPerPacket || exports.framesPerPacket;
  this.sampleRate = options.sampleRate || exports.ampleRate;
  this.channels = options.channels || exports.channels;
  this.bitDepth = options.bitDepth || exports.bitDepth;

  this.bytesPerPacket = this.channels * (this.bitDepth >> 3) * this.framesPerPacket;

  this._enc = new binding.Encoder({
    framesPerPacket: this.framesPerPacket,
    sampleRate: this.sampleRate,
    channels: this.channels,
    bitDepth: this.bitDepth
  });

  this._streamPos = 0;
  this._partial = null;

  this.cookie = this._enc.cookie;
  this.packets = [];
}
util.inherits(ALACEncoder, Transform);

ALACEncoder.prototype._transform = function(chunk, encoding, done) {
  if (this._partial) {
    chunk = Buffer.concat([this._partial, chunk]);
    this._partial = null;
  }

  var pos = 0;
  var remainder = chunk.length;
  var enc = this._enc;
  var bpp = this.bytesPerPacket;
  var outSize = bpp + binding.kALACMaxEscapeHeaderBytes;
  while (remainder >= bpp) {
    var out = new Buffer(outSize);

    var end = pos + bpp;
    var frame = chunk.slice(pos, end);
    var bytes = enc.encode(frame, out);
    pos = end;
    remainder -= bpp;

    if (!bytes)
      continue;

    this.packets.push(bytes);
    this._streamPos += bytes;

    if (bytes === outSize)
      this.push(out);
    else
      this.push(out.slice(0, bytes));
  }

  if (remainder !== 0)
    this._partial = chunk.slice(pos);

  done();
};

ALACEncoder.prototype._flush = function(done) {
  if (this._partial) {
    var outSize = this._partial.length + binding.kALACMaxEscapeHeaderBytes;
    var out = new Buffer(outSize);

    var bytes = this._enc.encode(this._partial, out);

    this.packets.push(bytes);

    if (bytes === outSize)
      this.push(out);
    else if (bytes)
      this.push(out.slice(0, bytes));
  }

  done();
};

exports.encoder = function(options) {
  return new ALACEncoder(options || {});
};
<|endoftext|>


<commit_before>const chalk = require('chalk');

const textPrefixes = {
    error: chalk.red(process.platform === 'win32' ? '×' : '✖'),
    warn: chalk.yellow(process.platform === 'win32' ? '‼' : '⚠'),
    warning: chalk.yellow(process.platform === 'win32' ? '‼' : '⚠'),
    http: chalk.cyan(process.platform === 'win32' ? '≡' : '☷'),
    info: chalk.green(process.platform === 'win32' ? 'i' : 'ℹ'),
    verbose: chalk.blue(process.platform === 'win32' ? 'i' : 'ℹ'),
    debug: chalk.dim(process.platform === 'win32' ? 'i': 'ℹ'),
    silly: chalk.inverse(process.platform === 'win32' ? '☺' : '☺'),
    log: ''
};

const emojiPrefixes = {
    error: '❌',
    warn: '💡',
    warning: "💡",
    http: '🌐',
    info: '📢',
    verbose: '🎤',
    debug: '🔬',
    silly: '🙃',
    log: ''
};

const defaultColors = {
    error: chalk.red,
    warn: chalk.yellow,
    warning: chalk.yellow,
    http: chalk.cyan,
    info: chalk.green,
    verbose: chalk.blue,
    debug: chalk.dim,
    silly: chalk.inverse,
    log: t => t
};

const defaultCfg = {
    prefixStyle: 'text',
    color: true,
    colorStyle: {}
};

class Log {
    constructor (cfg) {
        this._indentation = 0;
        this.cfg = Object.assign({}, cfg);
        if (this.cfg.prefixStyle === 'text') {
            this.cfg.prefixes = textPrefixes;
        } else if (this.cfg.prefixStyle === 'emoji') {
            this.cfg.prefixes = emojiPrefixes;
        } else if (this.cfg.prefixStyle === 'none') {
            this.cfg.prefixes = {};
            if (!this.cfg.color) {
                this.cfg.colorStyle = {};
            } else {
                this.cfg.colorStyle = defaultColors;
            }
        }
    }

    _getLogFn(level) {
        let me = this;
        return function () {
            let args = Array.prototype.slice.call(arguments);
            if (me.cfg.prefixes[level]) {
                args.unshift(me.cfg.prefixes[level]);
            }
            args.unshift(" ".repeat(this._indentation));
            if (me.cfg.colorStyle[level]) {
                console.log(me.cfg.colorStyle[level](args.join(' ')));
            } else {
                console.log.apply(null, args);
            }
        }
    }

    indent () {
        this._indentation++;
    }

    outdent () {
        this._indentation--;
    }
}

Object.keys(textPrefixes).forEach(level => {
    Object.defineProperty(Log.prototype, level, {
        get: function () {
            if (!this[`_${level}`]) {
                this[`_${level}`] = this._getLogFn(level);
            }
            return this[`_${level}`];
        }
    });
});

let _instance = new Log(defaultCfg);
function wrap () {
    let ex = function reconfigure (cfg) {
        _instance = new Log(Object.assign({}, defaultCfg, cfg));
        return wrap();
    }
    Object.getOwnPropertyNames(Log.prototype).forEach((method)=> {
        if (method !== "constructor" && method.charAt(0) !== "_") {
            ex[method] = _instance[method].bind(_instance);
        }
    });
    return ex;
}

module.exports =wrap();<commit_msg>Fixed warning and info emojis<commit_after>const chalk = require('chalk');

const textPrefixes = {
    error: chalk.red(process.platform === 'win32' ? '×' : '✖'),
    warn: chalk.yellow(process.platform === 'win32' ? '‼' : '⚠'),
    warning: chalk.yellow(process.platform === 'win32' ? '‼' : '⚠'),
    http: chalk.cyan(process.platform === 'win32' ? '≡' : '☷'),
    info: chalk.green(process.platform === 'win32' ? 'i' : 'ℹ'),
    verbose: chalk.blue(process.platform === 'win32' ? 'i' : 'ℹ'),
    debug: chalk.dim(process.platform === 'win32' ? 'i': 'ℹ'),
    silly: chalk.inverse(process.platform === 'win32' ? '☺' : '☺'),
    log: ''
};

const emojiPrefixes = {
    error: '❌',
    warn: '〽',
    warning: "〽",
    http: '🌐',
    info: '➡',
    verbose: '🎤',
    debug: '🔬',
    silly: '🙃',
    log: ''
};

const defaultColors = {
    error: chalk.red,
    warn: chalk.yellow,
    warning: chalk.yellow,
    http: chalk.cyan,
    info: chalk.green,
    verbose: chalk.blue,
    debug: chalk.dim,
    silly: chalk.inverse,
    log: t => t
};

const defaultCfg = {
    prefixStyle: 'text',
    color: true,
    colorStyle: {}
};

class Log {
    constructor (cfg) {
        this._indentation = 0;
        this.cfg = Object.assign({}, cfg);
        if (this.cfg.prefixStyle === 'text') {
            this.cfg.prefixes = textPrefixes;
        } else if (this.cfg.prefixStyle === 'emoji') {
            this.cfg.prefixes = emojiPrefixes;
        } else if (this.cfg.prefixStyle === 'none') {
            this.cfg.prefixes = {};
            if (!this.cfg.color) {
                this.cfg.colorStyle = {};
            } else {
                this.cfg.colorStyle = defaultColors;
            }
        }
    }

    _getLogFn(level) {
        let me = this;
        return function () {
            let args = Array.prototype.slice.call(arguments);
            if (me.cfg.prefixes[level]) {
                args.unshift(me.cfg.prefixes[level]);
            }
            args.unshift(" ".repeat(this._indentation));
            if (me.cfg.colorStyle[level]) {
                console.log(me.cfg.colorStyle[level](args.join(' ')));
            } else {
                console.log.apply(null, args);
            }
        }
    }

    indent () {
        this._indentation++;
    }

    outdent () {
        this._indentation--;
    }
}

Object.keys(textPrefixes).forEach(level => {
    Object.defineProperty(Log.prototype, level, {
        get: function () {
            if (!this[`_${level}`]) {
                this[`_${level}`] = this._getLogFn(level);
            }
            return this[`_${level}`];
        }
    });
});

let _instance = new Log(defaultCfg);
function wrap () {
    let ex = function reconfigure (cfg) {
        _instance = new Log(Object.assign({}, defaultCfg, cfg));
        return wrap();
    }
    Object.getOwnPropertyNames(Log.prototype).forEach((method)=> {
        if (method !== "constructor" && method.charAt(0) !== "_") {
            ex[method] = _instance[method].bind(_instance);
        }
    });
    return ex;
}

module.exports =wrap();<|endoftext|>


<commit_before>/*!
 * promised-handlebars <https://github.com/nknapp/promised-handlebars>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */

'use strict'

var Q = require('q')

/**
 * Returns a new Handlebars instance that
 * * Allows helpers to return promises
 * * Creates `compiled` templates that always
 *   return a promise for the resulting string.
 *   The promise is fulfilled after all helper-promsises
 *   are resolved.
 *
 * @param {Handlebars} Handlebars
 * @param {object} options
 * @param {string=} options.placeholder
 * @returns {Handlebars} a modified Handlebars object
 */
module.exports = function promisedHandlebars (Handlebars, options) {
  options = options || {}
  options.placeholder = options.placeholder || '\u0001'

  // one line from from substack's quotemeta-package
  var regex = new RegExp(String(options.placeholder).replace(/(\W)/g, '\\$1'), 'g')

  var promises = null
  var engine = Handlebars.create()

  // Wrap `registerHelper` with a custom function
  var oldRegisterHelper = engine.registerHelper
  engine.registerHelper = function (keyOrObject, fn) {

    if (typeof keyOrObject === 'string') {
      // Register a custom helper-function instead of actual helper
      oldRegisterHelper.call(this, keyOrObject, function () {
        var result = Q(fn.apply(this, arguments))
        // Remember promise and return placeholder instead
        promises.push(result)
        return options.placeholder
      })
    } else {
      // `keyOrObject` is actual an object of helpers
      // Call `registerHelper` again for each object
      // This simulates the default Handlebars-behaviour
      Object.keys(keyOrObject).forEach(function (key) {
        engine.registerHelper(key, keyOrObject[key])
      })
    }
  }

  var oldCompile = engine.compile
  engine.compile = function () {
    var fn = oldCompile.apply(this, arguments)
    // Wrap the compiled function
    return function () {
      if (promises) {
        // "promises" array already exists: We are executing a partial
        // That means we are called from somewhere within Handlebars.
        // Handlebars does not like promises, so we act as normal as possible.
        return fn.apply(engine, arguments)
      }
      try {
        // We are called from outside Handlebars (initial call)
        // Initialize the `promise` variable (see above) with an empty
        // array, so that helpers can store their promises during the
        // template execution.
        promises = []
        // Execute template (helpers are getting called and store promises
        // into the array
        var resultWithPlaceholders = fn.apply(engine, arguments)
        return Q.all(promises).then(function (results) {
          // Promises are fulfilled. Insert real values into the result.
          return resultWithPlaceholders.replace(regex, function () {
            return results.shift()
          })
        })
      } finally {
        // Reset promises for the next execution run
        promises = null
      }

    }

  }
  return engine
}
<commit_msg>Typos<commit_after>/*!
 * promised-handlebars <https://github.com/nknapp/promised-handlebars>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */

'use strict'

var Q = require('q')

/**
 * Returns a new Handlebars instance that
 * * allows helpers to return promises
 * * creates `compiled` templates that always
 *   return a promise for the resulting string.
 *   The promise is fulfilled after all helper-promsises
 *   are resolved.
 *
 * @param {Handlebars} Handlebars
 * @param {object} options
 * @param {string=} options.placeholder
 * @returns {Handlebars} a modified Handlebars object
 */
module.exports = function promisedHandlebars (Handlebars, options) {
  options = options || {}
  options.placeholder = options.placeholder || '\u0001'

  // one line from from substack's quotemeta-package
  var regex = new RegExp(String(options.placeholder).replace(/(\W)/g, '\\$1'), 'g')

  var promises = null
  var engine = Handlebars.create()

  // Wrap `registerHelper` with a custom function
  var oldRegisterHelper = engine.registerHelper
  engine.registerHelper = function (keyOrObject, fn) {

    if (typeof keyOrObject === 'string') {
      // Register a custom helper-function instead of actual helper
      oldRegisterHelper.call(this, keyOrObject, function () {
        var result = Q(fn.apply(this, arguments))
        // Remember promise and return placeholder instead
        promises.push(result)
        return options.placeholder
      })
    } else {
      // `keyOrObject` is actual an object of helpers
      // Call `registerHelper` again for each object
      // This simulates the default Handlebars-behaviour
      Object.keys(keyOrObject).forEach(function (key) {
        engine.registerHelper(key, keyOrObject[key])
      })
    }
  }

  var oldCompile = engine.compile
  engine.compile = function () {
    var fn = oldCompile.apply(this, arguments)
    // Wrap the compiled function
    return function () {
      if (promises) {
        // "promises" array already exists: We are executing a partial
        // That means we are called from somewhere within Handlebars.
        // Handlebars does not like promises, so we act as normal as possible.
        return fn.apply(engine, arguments)
      }
      try {
        // We are called from outside Handlebars (initial call)
        // Initialize the `promise` variable (see above) with an empty
        // array, so that helpers can store their promises during the
        // template execution.
        promises = []
        // Execute template (helpers are getting called and store promises
        // into the array
        var resultWithPlaceholders = fn.apply(engine, arguments)
        return Q.all(promises).then(function (results) {
          // Promises are fulfilled. Insert real values into the result.
          return resultWithPlaceholders.replace(regex, function () {
            return results.shift()
          })
        })
      } finally {
        // Reset promises for the next execution run
        promises = null
      }

    }

  }
  return engine
}
<|endoftext|>


<commit_before>'use strict';

var timers = require('timers');
var process = global.process;
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var _toobusy;
var _gcstats;

function StatsdReporter(statsd, clusterStatsd, prefix) {
    this.statsd = statsd;
    this.clusterStatsd = clusterStatsd;
    this.prefix = prefix;
}

StatsdReporter.prototype.reportHandles =
function _reportHandles(num) {
    this.statsd.timing(this.prefix + 'process-reporter.handles', num);
};

StatsdReporter.prototype.reportRequests =
function _reportRequests(num) {
    this.statsd.timing(this.prefix + 'process-reporter.requests', num);
};

StatsdReporter.prototype.reportMemoryUsage =
function _reportMemoryUsage(usage) {
    var memPrefix = this.prefix + 'process-reporter.memory-usage';

    this.statsd.gauge(memPrefix + '.rss', usage.rss);
    this.statsd.gauge(memPrefix + '.heap-used', usage.heapUsed);
    this.statsd.gauge(memPrefix + '.heap-total', usage.heapTotal);
};

StatsdReporter.prototype.reportLag =
function _reportLag(lagTime) {
    this.statsd.timing(this.prefix + 'process-reporter.lag-sampler', lagTime);

    if (this.clusterStatsd) {
        this.clusterStatsd.timing(
            this.prefix + 'process-reporter.lag-sampler', lagTime
        );
    }
};

StatsdReporter.prototype.reportGC =
function _reportGC(gcType, gcInfo) {
    var prefix = this.prefix + 'process-reporter.gc.' + gcType;

    this.statsd.timing(prefix + '.pause-ms', gcInfo.pauseMS);
    this.statsd.gauge(prefix + '.heap-used', gcInfo.diff.usedHeapSize);
    this.statsd.gauge(prefix + '.heap-total', gcInfo.diff.totalHeapSize);

    if (this.clusterStatsd) {
        this.clusterStatsd.timing(prefix + '.pause-ms', gcInfo.pauseMS);
    }
};

var _gcEmitter = new EventEmitter();
_gcEmitter.setMaxListeners(100);

/*eslint max-statements: [2, 50]*/
/*eslint complexity: [2, 15]*/
function ProcessReporter(options) {
    assert(typeof options === 'object', 'options required');

    this.statsd = options.statsd;
    assert(typeof this.statsd === 'object', 'options.statsd required');

    this.clusterStatsd = options.clusterStatsd;

    this.handleInterval = options.handleInterval || 1000;
    assert(
        typeof this.handleInterval === 'number',
        'expected options.handleInterval to be number'
    );

    this.requestInterval = options.requestInterval || 100;
    assert(
        typeof this.requestInterval === 'number',
        'expected options.requestInterval to be number'
    );

    this.memoryInterval = options.memoryInterval || 1000;
    assert(
        typeof this.memoryInterval === 'number',
        'expected options.memoryInterval to be number'
    );

    this.lagInterval = options.lagInterval || 500;
    assert(
        typeof this.lagInterval === 'number',
        'expected options.lagInterval to be number'
    );

    this.timers = options.timers || timers;
    assert(
        typeof this.timers === 'object' &&
            typeof this.timers.setTimeout === 'function' &&
            typeof this.timers.clearTimeout === 'function',
        'expected options.timers to be object with setTimeout and ' +
            'clearTimeout functions'
    );

    this.prefix = options.prefix || '';
    assert(
        typeof this.prefix === 'string',
        'expected options.prefix to be string'
    );

    if (this.prefix[this.prefix.length - 1] !== '.' && this.prefix !== '') {
        this.prefix = this.prefix + '.';
    }

    if (typeof options.handleEnabled === 'boolean') {
        this.handleEnabled = options.handleEnabled;
    } else {
        this.handleEnabled = true;
    }

    if (typeof options.requestEnabled === 'boolean') {
        this.requestEnabled = options.requestEnabled;
    } else {
        this.requestEnabled = true;
    }

    if (typeof options.memoryEnabled === 'boolean') {
        this.memoryEnabled = options.memoryEnabled;
    } else {
        this.memoryEnabled = true;
    }

    if (typeof options.lagEnabled === 'boolean') {
        this.lagEnabled = options.lagEnabled;
    } else {
        this.lagEnabled = true;
    }

    if (typeof options.gcEnabled === 'boolean') {
        this.gcEnabled = options.gcEnabled;
    } else {
        this.gcEnabled = true;
    }

    this.reporter = new StatsdReporter(
        this.statsd, this.clusterStatsd, this.prefix
    );
    this.handleTimer = null;
    this.requestTimer = null;
    this.memoryTimer = null;
    this.lagTimer = null;
    this._onStatsListener = null;

    this.cachedMemoryUsage = null;
    this.cachedLagTime = null;

    this._setupClosure();
}

ProcessReporter.prototype._setupClosure =
function _setupClosure() {
    var self = this;

    if (self.gcEnabled) {
        self._onStatsListener = onStats;
    }

    function onStats(gcInfo) {
        self._reportGCStats(gcInfo);
    }
};

ProcessReporter.prototype.bootstrap = function bootstrap() {
    var self = this;

    if (!_toobusy && self.lagEnabled) {
        /*eslint-disable global-require*/
        _toobusy = require('toobusy-js');
        /*eslint-enable global-require*/
    }

    if (!_gcstats && self.gcEnabled) {
        /*eslint-disable camelcase, global-require */
        _gcstats = require('gc-stats/binding');
        /*eslint-enable global-require, camelcase*/
        _gcstats.afterGC(onGC);
    }

    if (self.handleEnabled) {
        self.handleTimer = self.timers.setTimeout(
            onHandle,
            self.handleInterval
        );
    }

    if (self.requestEnabled) {
        self.requestTimer = self.timers.setTimeout(
            onRequest,
            self.requestInterval
        );
    }

    if (self.memoryEnabled) {
        self.memoryTimer = self.timers.setTimeout(
            onMemory,
            self.memoryInterval
        );
    }

    if (self.lagEnabled) {
        self.lagTimer = self.timers.setTimeout(
            onLag,
            self.lagInterval
        );
    }

    if (self.gcEnabled) {
        _gcEmitter.on('stats', self._onStatsListener);
    }

    function onHandle() {
        self._reportHandle();
        self.handleTimer =
            self.timers.setTimeout(onHandle, self.handleInterval);
    }

    function onRequest() {
        self._reportRequest();
        self.requestTimer =
            self.timers.setTimeout(onRequest, self.requestInterval);
    }

    function onMemory() {
        self._reportMemory();
        self.memoryTimer =
            self.timers.setTimeout(onMemory, self.memoryInterval);
    }

    function onLag() {
        self._reportLag();
        self.lagTimer = self.timers.setTimeout(onLag, self.lagInterval);
    }
};

ProcessReporter.prototype.destroy = function destroy() {
    var self = this;

    self.timers.clearTimeout(self.handleTimer);
    self.timers.clearTimeout(self.requestTimer);
    self.timers.clearTimeout(self.memoryTimer);
    self.timers.clearTimeout(self.lagTimer);

    if (_toobusy) {
        _toobusy.shutdown();
    }

    if (self.gcEnabled) {
        _gcEmitter.removeListener('stats', self._onStatsListener);
    }
};

ProcessReporter.prototype._reportHandle = function _reportHandle() {
    var self = this;

    var num = process._getActiveHandles().length;
    self.reporter.reportHandles(num);
};

ProcessReporter.prototype._reportRequest = function _reportRequest() {
    var self = this;

    var num = process._getActiveRequests().length;
    self.reporter.reportRequests(num);
};

ProcessReporter.prototype.getCachedMemoryUsage =
function getCachedMemoryUsage() {
    return this.cachedMemoryUsage;
};

ProcessReporter.prototype._reportMemory = function _reportMemory() {
    var self = this;

    var usage = self._memoryUsage();
    // Evidently, process.memoryUsage() may throw EMFILE.
    if (!usage) {
        return;
    }
    self.reporter.reportMemoryUsage(usage);

    self.cachedMemoryUsage = usage;
};

ProcessReporter.prototype._memoryUsage = function _memoryUsage() {
    /*eslint-disable no-restricted-syntax*/
    try {
        return process.memoryUsage();
    } catch (err) {
        return null;
    }
    /*eslint-enable no-restricted-syntax*/
};

ProcessReporter.prototype.getCachedLagTime = function getCachedLagTime() {
    return this.cachedLagTime;
};

ProcessReporter.prototype._reportLag = function _reportLag() {
    var self = this;

    var lagTime = _toobusy.lag();
    self.reporter.reportLag(lagTime);

    self.cachedLagTime = lagTime;
};

ProcessReporter.prototype._reportGCStats = function _reportGCStats(gcInfo) {
    var self = this;

    var gcType = formatGCType(gcInfo);
    self.reporter.reportGC(gcType, gcInfo);
};

module.exports = createProcessReporter;

function createProcessReporter(options) {
    return new ProcessReporter(options);
}

function formatGCType(gcInfo) {
    var type;
    switch (gcInfo.gctype) {
        case 1:
            type = 'minor';
            break;

        case 2:
            type = 'major';
            break;

        case 3:
            type = 'both';
            break;

        default:
            type = 'unknown';
            break;
    }

    return type;
}

function onGC(gcInfo) {
    _gcEmitter.emit('stats', gcInfo);
}
<commit_msg>allow passing in reporter from options<commit_after>'use strict';

var timers = require('timers');
var process = global.process;
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var _toobusy;
var _gcstats;

function StatsdReporter(statsd, clusterStatsd, prefix) {
    this.statsd = statsd;
    this.clusterStatsd = clusterStatsd;
    this.prefix = prefix;
}

StatsdReporter.prototype.reportHandles =
function _reportHandles(num) {
    this.statsd.timing(this.prefix + 'process-reporter.handles', num);
};

StatsdReporter.prototype.reportRequests =
function _reportRequests(num) {
    this.statsd.timing(this.prefix + 'process-reporter.requests', num);
};

StatsdReporter.prototype.reportMemoryUsage =
function _reportMemoryUsage(usage) {
    var memPrefix = this.prefix + 'process-reporter.memory-usage';

    this.statsd.gauge(memPrefix + '.rss', usage.rss);
    this.statsd.gauge(memPrefix + '.heap-used', usage.heapUsed);
    this.statsd.gauge(memPrefix + '.heap-total', usage.heapTotal);
};

StatsdReporter.prototype.reportLag =
function _reportLag(lagTime) {
    this.statsd.timing(this.prefix + 'process-reporter.lag-sampler', lagTime);

    if (this.clusterStatsd) {
        this.clusterStatsd.timing(
            this.prefix + 'process-reporter.lag-sampler', lagTime
        );
    }
};

StatsdReporter.prototype.reportGC =
function _reportGC(gcType, gcInfo) {
    var prefix = this.prefix + 'process-reporter.gc.' + gcType;

    this.statsd.timing(prefix + '.pause-ms', gcInfo.pauseMS);
    this.statsd.gauge(prefix + '.heap-used', gcInfo.diff.usedHeapSize);
    this.statsd.gauge(prefix + '.heap-total', gcInfo.diff.totalHeapSize);

    if (this.clusterStatsd) {
        this.clusterStatsd.timing(prefix + '.pause-ms', gcInfo.pauseMS);
    }
};

var _gcEmitter = new EventEmitter();
_gcEmitter.setMaxListeners(100);

/*eslint max-statements: [2, 50]*/
/*eslint complexity: [2, 15]*/
function ProcessReporter(options) {
    assert(typeof options === 'object', 'options required');

    if (options.reporter) {
        this.repoter = options.reporter;
    } else {
        var statsd = options.statsd;
        assert(typeof statsd === 'object', 'options.statsd required');

        var clusterStatsd = options.clusterStatsd;

        var prefix = options.prefix || '';
        assert(
            typeof prefix === 'string',
            'expected options.prefix to be string'
        );

        if (prefix[prefix.length - 1] !== '.' && prefix !== '') {
            prefix = prefix + '.';
        }

        this.reporter = new StatsdReporter(statsd, clusterStatsd, prefix);
    }

    this.handleInterval = options.handleInterval || 1000;
    assert(
        typeof this.handleInterval === 'number',
        'expected options.handleInterval to be number'
    );

    this.requestInterval = options.requestInterval || 100;
    assert(
        typeof this.requestInterval === 'number',
        'expected options.requestInterval to be number'
    );

    this.memoryInterval = options.memoryInterval || 1000;
    assert(
        typeof this.memoryInterval === 'number',
        'expected options.memoryInterval to be number'
    );

    this.lagInterval = options.lagInterval || 500;
    assert(
        typeof this.lagInterval === 'number',
        'expected options.lagInterval to be number'
    );

    this.timers = options.timers || timers;
    assert(
        typeof this.timers === 'object' &&
            typeof this.timers.setTimeout === 'function' &&
            typeof this.timers.clearTimeout === 'function',
        'expected options.timers to be object with setTimeout and ' +
            'clearTimeout functions'
    );

    if (typeof options.handleEnabled === 'boolean') {
        this.handleEnabled = options.handleEnabled;
    } else {
        this.handleEnabled = true;
    }

    if (typeof options.requestEnabled === 'boolean') {
        this.requestEnabled = options.requestEnabled;
    } else {
        this.requestEnabled = true;
    }

    if (typeof options.memoryEnabled === 'boolean') {
        this.memoryEnabled = options.memoryEnabled;
    } else {
        this.memoryEnabled = true;
    }

    if (typeof options.lagEnabled === 'boolean') {
        this.lagEnabled = options.lagEnabled;
    } else {
        this.lagEnabled = true;
    }

    if (typeof options.gcEnabled === 'boolean') {
        this.gcEnabled = options.gcEnabled;
    } else {
        this.gcEnabled = true;
    }

    this.handleTimer = null;
    this.requestTimer = null;
    this.memoryTimer = null;
    this.lagTimer = null;
    this._onStatsListener = null;

    this.cachedMemoryUsage = null;
    this.cachedLagTime = null;

    this._setupClosure();
}

ProcessReporter.prototype._setupClosure =
function _setupClosure() {
    var self = this;

    if (self.gcEnabled) {
        self._onStatsListener = onStats;
    }

    function onStats(gcInfo) {
        self._reportGCStats(gcInfo);
    }
};

ProcessReporter.prototype.bootstrap = function bootstrap() {
    var self = this;

    if (!_toobusy && self.lagEnabled) {
        /*eslint-disable global-require*/
        _toobusy = require('toobusy-js');
        /*eslint-enable global-require*/
    }

    if (!_gcstats && self.gcEnabled) {
        /*eslint-disable camelcase, global-require */
        _gcstats = require('gc-stats/binding');
        /*eslint-enable global-require, camelcase*/
        _gcstats.afterGC(onGC);
    }

    if (self.handleEnabled) {
        self.handleTimer = self.timers.setTimeout(
            onHandle,
            self.handleInterval
        );
    }

    if (self.requestEnabled) {
        self.requestTimer = self.timers.setTimeout(
            onRequest,
            self.requestInterval
        );
    }

    if (self.memoryEnabled) {
        self.memoryTimer = self.timers.setTimeout(
            onMemory,
            self.memoryInterval
        );
    }

    if (self.lagEnabled) {
        self.lagTimer = self.timers.setTimeout(
            onLag,
            self.lagInterval
        );
    }

    if (self.gcEnabled) {
        _gcEmitter.on('stats', self._onStatsListener);
    }

    function onHandle() {
        self._reportHandle();
        self.handleTimer =
            self.timers.setTimeout(onHandle, self.handleInterval);
    }

    function onRequest() {
        self._reportRequest();
        self.requestTimer =
            self.timers.setTimeout(onRequest, self.requestInterval);
    }

    function onMemory() {
        self._reportMemory();
        self.memoryTimer =
            self.timers.setTimeout(onMemory, self.memoryInterval);
    }

    function onLag() {
        self._reportLag();
        self.lagTimer = self.timers.setTimeout(onLag, self.lagInterval);
    }
};

ProcessReporter.prototype.destroy = function destroy() {
    var self = this;

    self.timers.clearTimeout(self.handleTimer);
    self.timers.clearTimeout(self.requestTimer);
    self.timers.clearTimeout(self.memoryTimer);
    self.timers.clearTimeout(self.lagTimer);

    if (_toobusy) {
        _toobusy.shutdown();
    }

    if (self.gcEnabled) {
        _gcEmitter.removeListener('stats', self._onStatsListener);
    }
};

ProcessReporter.prototype._reportHandle = function _reportHandle() {
    var self = this;

    var num = process._getActiveHandles().length;
    self.reporter.reportHandles(num);
};

ProcessReporter.prototype._reportRequest = function _reportRequest() {
    var self = this;

    var num = process._getActiveRequests().length;
    self.reporter.reportRequests(num);
};

ProcessReporter.prototype.getCachedMemoryUsage =
function getCachedMemoryUsage() {
    return this.cachedMemoryUsage;
};

ProcessReporter.prototype._reportMemory = function _reportMemory() {
    var self = this;

    var usage = self._memoryUsage();
    // Evidently, process.memoryUsage() may throw EMFILE.
    if (!usage) {
        return;
    }
    self.reporter.reportMemoryUsage(usage);

    self.cachedMemoryUsage = usage;
};

ProcessReporter.prototype._memoryUsage = function _memoryUsage() {
    /*eslint-disable no-restricted-syntax*/
    try {
        return process.memoryUsage();
    } catch (err) {
        return null;
    }
    /*eslint-enable no-restricted-syntax*/
};

ProcessReporter.prototype.getCachedLagTime = function getCachedLagTime() {
    return this.cachedLagTime;
};

ProcessReporter.prototype._reportLag = function _reportLag() {
    var self = this;

    var lagTime = _toobusy.lag();
    self.reporter.reportLag(lagTime);

    self.cachedLagTime = lagTime;
};

ProcessReporter.prototype._reportGCStats = function _reportGCStats(gcInfo) {
    var self = this;

    var gcType = formatGCType(gcInfo);
    self.reporter.reportGC(gcType, gcInfo);
};

module.exports = createProcessReporter;

function createProcessReporter(options) {
    return new ProcessReporter(options);
}

function formatGCType(gcInfo) {
    var type;
    switch (gcInfo.gctype) {
        case 1:
            type = 'minor';
            break;

        case 2:
            type = 'major';
            break;

        case 3:
            type = 'both';
            break;

        default:
            type = 'unknown';
            break;
    }

    return type;
}

function onGC(gcInfo) {
    _gcEmitter.emit('stats', gcInfo);
}
<|endoftext|>


<commit_before>var isCanonicalBase64 = require('is-canonical-base64')
var isDomain = require('is-valid-domain')
var Querystring = require('querystring')
var ip = require('ip')

var parseLinkRegex = /^((@|%|&)[A-Za-z0-9\/+]{43}=\.[\w\d]+)(\?(.+))?$/
var linkRegex = exports.linkRegex = /^(@|%|&)[A-Za-z0-9\/+]{43}=\.[\w\d]+$/
var feedIdRegex = exports.feedIdRegex = isCanonicalBase64('@', '\.(?:sha256|ed25519)', 32)
var blobIdRegex = exports.blobIdRegex = isCanonicalBase64('&', '\.sha256', 32)
var msgIdRegex = exports.msgIdRegex = isCanonicalBase64('%', '\.sha256', 32)

var extractRegex = /([@%&][A-Za-z0-9\/+]{43}=\.[\w\d]+)/

var MultiServerAddress = require('multiserver-address')

function isMultiServerAddress (str) {
  //a http url fits into the multiserver scheme,
  //but all ssb address must have a transport and a transform
  //so check there is at least one unescaped ~ in the address
  return MultiServerAddress.check(str) && /[^!][~]/.test(str)
}

function isIP (s) {
  return ip.isV4Format(s) || ip.isV6Format(s)
}

var isInteger = Number.isInteger
var DEFAULT_PORT = 8008

function isString(s) {
  return 'string' === typeof s
}

var isHost = function (addr) {
  if(!isString(addr)) return
  addr = addr.replace(/^wss?:\/\//, '')
  return (isIP(addr)) || isDomain(addr) || addr === 'localhost'
}

var isPort = function (p) {
  return isInteger(p) && p <= 65536
}

function isObject (o) {
  return o && 'object' === typeof o && !Array.isArray(o)
}

var isFeedId = exports.isFeed = exports.isFeedId =
  function (data) {
    return isString(data) && feedIdRegex.test(data)
  }

var isMsgId = exports.isMsg = exports.isMsgId =
  function (data) {
    return isString(data) && msgIdRegex.test(data)
  }

var isBlobId = exports.isBlob = exports.isBlobId =
  function (data) {
    return isString(data) && blobIdRegex.test(data)
  }

var isLink = exports.isLink =
  function (data) {
    if(!isString(data)) return false
    var index = data.indexOf('?')
    data = ~index ? data.substring(0, index) : data
    return isString(data) && (isFeedId(data) || isMsgId(data) || isBlobId(data))
  }

exports.isBlobLink = function (s) {
  return s[0] === '&' && isLink(s)
}

exports.isMsgLink = function (s) {
  return s[0] === '%' && isLink(s)
}

var normalizeChannel = exports.normalizeChannel =
  function (data) {
    if (typeof data === 'string') {
      data = data.toLowerCase().replace(/\s|,|\.|\?|!|<|>|\(|\)|\[|\]|"|#/g, '')
      if (data.length > 0 && data.length < 30) {
        return data
      }
    }
  }

function deprecate (name, fn) {
  var logged = false
  return function () {
    var args = [].slice.call(arguments)
    if(!logged) {
      console.trace('deprecated api used: '+name)
      logged = true
    }
    return fn.apply(this, args)
  }
}

var parseMultiServerAddress = deprecate('ssb-ref.parseMultiServerAddress', function (data) {
  if(!isString(data)) return false
  if(!MultiServerAddress.check(data)) return false

  var addr = MultiServerAddress.decode(data)
  addr = addr.find(function (address) {
    if (!address[0]) return false
    if (!address[1]) return false
    return /^(net|wss?|onion)$/.test(address[0].name) && /^shs/.test(address[1].name)
  })
  if (!Array.isArray(addr)) {
    return false
  }
  var port = +addr[0].data.pop() //last item always port, to handle ipv6

  //preserve protocol type on websocket addresses
  var host = (/^wss?$/.test(addr[0].name) ? addr[0].name+':' : '') + addr[0].data.join(':')
  var key = '@'+addr[1].data[0]+'.ed25519'
  var seed = addr[1].data[2]
  // allow multiserver addresses that are not currently understood!
  if(!(isHost(host) && isPort(+port) && isFeedId(key))) return false
  var address = {
    host: host,
    port: port,
    key: key,
  }
  if(seed)
    address.seed = seed

  return address
})

var toLegacyAddress = exports.toLegacyAddress = parseMultiServerAddress

var isLegacyAddress = exports.isLegacyAddress = function (addr) {
  return isObject(addr) && isHost(addr.host) && isPort(addr.port) && isFeedId(addr.key)
}

var toMultiServerAddress = exports.toMultiServerAddress = function (addr) {
  if(MultiServerAddress.check(addr)) return addr
  if(!isPort(addr.port)) throw new Error('ssb-ref.toMultiServerAddress - invalid port:'+addr.port)
  if(!isHost(addr.host)) throw new Error('ssb-ref.toMultiServerAddress - invalid host:'+addr.host)
  if(!isFeedId(addr.key)) throw new Error('ssb-ref.toMultiServerAddress - invalid key:'+addr.key)

  return (
    /^wss?:/.test(addr.host)   ? addr.host
  : /\.onion$/.test(addr.host) ? 'onion:'+addr.host
  :                              'net:'+addr.host
  )+':'+addr.port+'~shs:'+addr.key.substring(1, addr.key.indexOf('.'))
}

var isAddress = exports.isAddress = function (data) {
  var host, port, id
  if(isObject(data)) {
    id = data.key
    host = data.host
    port = data.port
  }
  else if(!isString(data)) return false
  else if(isMultiServerAddress(data)) return true
  else {
    var parts = data.split(':')
    var id = parts.pop(), port = parts.pop(), host = parts.join(':')
    return (
      isFeedId(id) && isPort(+port)
      && isHost(host)
    )
  }
}

//This is somewhat fragile, because maybe non-shs protocols get added...
//it would be better to treat all addresses as opaque or have multiserver handle
//extraction of a signing key from the address.
var getKeyFromAddress = exports.getKeyFromAddress = function (addr) {
  if(addr.key) return addr.key
  var data = MultiServerAddress.decode(addr)
  if(!data) return
  for(var k in data) {
    var address = data[k]
    for(var j in address) {
      var protocol = address[j]
      if(/^shs/.test(protocol.name)) //forwards compatible with future shs versions...
        return '@'+protocol.data[0]+'.ed25519'
    }
  }
}

var parseAddress = exports.parseAddress = deprecate('ssb-ref.parseAddress', function (e) {
  if(isString(e)) {
    if(~e.indexOf('~'))
      return parseMultiServerAddress(e)
    var parts = e.split(':')
    var id = parts.pop(), port = parts.pop(), host = parts.join(':')
    var e = {
      host: host,
      port: +(port || DEFAULT_PORT),
      key: id
    }
    return e
  }
  return e
})

var toAddress = exports.toAddress = function (e) {
  e = exports.parseAddress(e)
  e.port = e.port || DEFAULT_PORT
  e.host = e.host || 'localhost'
  return e
}

var legacyInviteRegex = /^[A-Za-z0-9\/+]{43}=$/
var legacyInviteFixerRegex = /#.*$/
var isLegacyInvite = exports.isLegacyInvite =
  function (data) {
    if(!isString(data)) return false
    data = data.replace(legacyInviteFixerRegex, '')
    var parts = data.split('~')
    return parts.length == 2 && isAddress(parts[0]) && legacyInviteRegex.test(parts[1])
  }

var isMultiServerInvite = exports.isMultiServerInvite =
  function (data) {
    if(!isString(data)) return false
    return !!parseMultiServerInvite(data)
  }

var isInvite = exports.isInvite =
  function (data) {
    if(!isString(data)) return false
    return isLegacyInvite(data) || isMultiServerInvite(data)
  }

exports.parseLink = function parseBlob (ref) {
  var match = parseLinkRegex.exec(ref)
  if (match && match[1]) {
    if (match[3]) {
      return {link: match[1], query: Querystring.parse(match[4])}
    } else {
      return {link: match[1]}
    }
  }
}

function parseLegacyInvite (invite) {
  var redirect = invite.split('#')
  invite = redirect.shift()
  var parts = invite.split('~')
  var addr = toAddress(parts[0])//.split(':')
  //convert legacy code to multiserver invite code.
  invite = remote+':'+parts[1]
  var remote = toMultiServerAddress(addr)
  return {
    invite: remote + ':' + parts[1],
    key: addr.key,
    redirect: null,
    remote: remote,
    redirect: redirect.length ? '#' + redirect.join('#') : null
  }
}

function parseMultiServerInvite (invite) {

  var redirect = invite.split('#')
  if(!redirect.length) return null

  invite = redirect.shift()
  var addr = toLegacyAddress(invite)
  if(!addr) return null
  delete addr.seed
  return {
    invite: invite,
    remote: toMultiServerAddress(addr),
    key: addr.key,
    redirect: redirect.length ? '#' + redirect.join('#') : null
  }
}

exports.parseLegacyInvite = deprecate('ssb-ref.parseLegacyInvite', parseLegacyInvite)
exports.parseMultiServerInvite = deprecate('ssb-ref.parseMultiServerInvite', parseMultiServerInvite)

exports.parseInvite = deprecate('ssb-ref.parseInvite', function (invite) {
  return (
    isLegacyInvite(invite)
  ? parseLegacyInvite(invite)
  : isMultiServerInvite(invite)
  ? parseMultiServerInvite(invite)
  : null
  )
})

exports.type =
  function (id) {
    if(!isString(id)) return false
    var c = id.charAt(0)
    if (c == '@' && isFeedId(id))
      return 'feed'
    else if (c == '%' && isMsgId(id))
      return 'msg'
    else if (c == '&' && isBlobId(id))
      return 'blob'
    else if(isAddress(id)) return 'address'
    else if(isInvite(id)) return 'invite'
    else
    return false
  }

exports.extract =
  function (data) {
    if (!isString(data))
      return false

    var _data = data

    var res = extractRegex.exec(_data)
    if (res) {
      return res && res[0]
    } else {
      try { _data = decodeURIComponent(data) }
      catch (e) {} // this may fail if it's not encoded, so don't worry if it does
      _data = _data.replace(/&/g, '&')

      var res = extractRegex.exec(_data)
      return res && res[0]
    }
  }

<commit_msg>Rename internally used deprecated functions<commit_after>var isCanonicalBase64 = require('is-canonical-base64')
var isDomain = require('is-valid-domain')
var Querystring = require('querystring')
var ip = require('ip')

var parseLinkRegex = /^((@|%|&)[A-Za-z0-9\/+]{43}=\.[\w\d]+)(\?(.+))?$/
var linkRegex = exports.linkRegex = /^(@|%|&)[A-Za-z0-9\/+]{43}=\.[\w\d]+$/
var feedIdRegex = exports.feedIdRegex = isCanonicalBase64('@', '\.(?:sha256|ed25519)', 32)
var blobIdRegex = exports.blobIdRegex = isCanonicalBase64('&', '\.sha256', 32)
var msgIdRegex = exports.msgIdRegex = isCanonicalBase64('%', '\.sha256', 32)

var extractRegex = /([@%&][A-Za-z0-9\/+]{43}=\.[\w\d]+)/

var MultiServerAddress = require('multiserver-address')

function isMultiServerAddress (str) {
  //a http url fits into the multiserver scheme,
  //but all ssb address must have a transport and a transform
  //so check there is at least one unescaped ~ in the address
  return MultiServerAddress.check(str) && /[^!][~]/.test(str)
}

function isIP (s) {
  return ip.isV4Format(s) || ip.isV6Format(s)
}

var isInteger = Number.isInteger
var DEFAULT_PORT = 8008

function isString(s) {
  return 'string' === typeof s
}

var isHost = function (addr) {
  if(!isString(addr)) return
  addr = addr.replace(/^wss?:\/\//, '')
  return (isIP(addr)) || isDomain(addr) || addr === 'localhost'
}

var isPort = function (p) {
  return isInteger(p) && p <= 65536
}

function isObject (o) {
  return o && 'object' === typeof o && !Array.isArray(o)
}

var isFeedId = exports.isFeed = exports.isFeedId =
  function (data) {
    return isString(data) && feedIdRegex.test(data)
  }

var isMsgId = exports.isMsg = exports.isMsgId =
  function (data) {
    return isString(data) && msgIdRegex.test(data)
  }

var isBlobId = exports.isBlob = exports.isBlobId =
  function (data) {
    return isString(data) && blobIdRegex.test(data)
  }

var isLink = exports.isLink =
  function (data) {
    if(!isString(data)) return false
    var index = data.indexOf('?')
    data = ~index ? data.substring(0, index) : data
    return isString(data) && (isFeedId(data) || isMsgId(data) || isBlobId(data))
  }

exports.isBlobLink = function (s) {
  return s[0] === '&' && isLink(s)
}

exports.isMsgLink = function (s) {
  return s[0] === '%' && isLink(s)
}

var normalizeChannel = exports.normalizeChannel =
  function (data) {
    if (typeof data === 'string') {
      data = data.toLowerCase().replace(/\s|,|\.|\?|!|<|>|\(|\)|\[|\]|"|#/g, '')
      if (data.length > 0 && data.length < 30) {
        return data
      }
    }
  }

function deprecate (name, fn) {
  var logged = false
  return function () {
    var args = [].slice.call(arguments)
    if(!logged) {
      console.trace('deprecated api used: '+name)
      logged = true
    }
    return fn.apply(this, args)
  }
}

function _parseMultiServerAddress (data) {
  if(!isString(data)) return false
  if(!MultiServerAddress.check(data)) return false

  var addr = MultiServerAddress.decode(data)
  addr = addr.find(function (address) {
    if (!address[0]) return false
    if (!address[1]) return false
    return /^(net|wss?|onion)$/.test(address[0].name) && /^shs/.test(address[1].name)
  })
  if (!Array.isArray(addr)) {
    return false
  }
  var port = +addr[0].data.pop() //last item always port, to handle ipv6

  //preserve protocol type on websocket addresses
  var host = (/^wss?$/.test(addr[0].name) ? addr[0].name+':' : '') + addr[0].data.join(':')
  var key = '@'+addr[1].data[0]+'.ed25519'
  var seed = addr[1].data[2]
  // allow multiserver addresses that are not currently understood!
  if(!(isHost(host) && isPort(+port) && isFeedId(key))) return false
  var address = {
    host: host,
    port: port,
    key: key,
  }
  if(seed)
    address.seed = seed

  return address
}

var parseMultiServerAddress = deprecate('ssb-ref.parseMultiServerAddress', _parseMultiServerAddress)

var toLegacyAddress = exports.toLegacyAddress = parseMultiServerAddress

var isLegacyAddress = exports.isLegacyAddress = function (addr) {
  return isObject(addr) && isHost(addr.host) && isPort(addr.port) && isFeedId(addr.key)
}

var toMultiServerAddress = exports.toMultiServerAddress = function (addr) {
  if(MultiServerAddress.check(addr)) return addr
  if(!isPort(addr.port)) throw new Error('ssb-ref.toMultiServerAddress - invalid port:'+addr.port)
  if(!isHost(addr.host)) throw new Error('ssb-ref.toMultiServerAddress - invalid host:'+addr.host)
  if(!isFeedId(addr.key)) throw new Error('ssb-ref.toMultiServerAddress - invalid key:'+addr.key)

  return (
    /^wss?:/.test(addr.host)   ? addr.host
  : /\.onion$/.test(addr.host) ? 'onion:'+addr.host
  :                              'net:'+addr.host
  )+':'+addr.port+'~shs:'+addr.key.substring(1, addr.key.indexOf('.'))
}

var isAddress = exports.isAddress = function (data) {
  var host, port, id
  if(isObject(data)) {
    id = data.key
    host = data.host
    port = data.port
  }
  else if(!isString(data)) return false
  else if(isMultiServerAddress(data)) return true
  else {
    var parts = data.split(':')
    var id = parts.pop(), port = parts.pop(), host = parts.join(':')
    return (
      isFeedId(id) && isPort(+port)
      && isHost(host)
    )
  }
}

//This is somewhat fragile, because maybe non-shs protocols get added...
//it would be better to treat all addresses as opaque or have multiserver handle
//extraction of a signing key from the address.
var getKeyFromAddress = exports.getKeyFromAddress = function (addr) {
  if(addr.key) return addr.key
  var data = MultiServerAddress.decode(addr)
  if(!data) return
  for(var k in data) {
    var address = data[k]
    for(var j in address) {
      var protocol = address[j]
      if(/^shs/.test(protocol.name)) //forwards compatible with future shs versions...
        return '@'+protocol.data[0]+'.ed25519'
    }
  }
}

function _parseAddress (e) {
  if(isString(e)) {
    if(~e.indexOf('~'))
      return _parseMultiServerAddress(e)
    var parts = e.split(':')
    var id = parts.pop(), port = parts.pop(), host = parts.join(':')
    var e = {
      host: host,
      port: +(port || DEFAULT_PORT),
      key: id
    }
    return e
  }
  return e
}

exports.parseAddress = deprecate('ssb-ref.parseAddress', _parseAddress)

var toAddress = exports.toAddress = function (e) {
  e = _parseAddress(e)
  e.port = e.port || DEFAULT_PORT
  e.host = e.host || 'localhost'
  return e
}

var legacyInviteRegex = /^[A-Za-z0-9\/+]{43}=$/
var legacyInviteFixerRegex = /#.*$/
var isLegacyInvite = exports.isLegacyInvite =
  function (data) {
    if(!isString(data)) return false
    data = data.replace(legacyInviteFixerRegex, '')
    var parts = data.split('~')
    return parts.length == 2 && isAddress(parts[0]) && legacyInviteRegex.test(parts[1])
  }

var isMultiServerInvite = exports.isMultiServerInvite =
  function (data) {
    if(!isString(data)) return false
    return !!_parseMultiServerInvite(data)
  }

var isInvite = exports.isInvite =
  function (data) {
    if(!isString(data)) return false
    return isLegacyInvite(data) || isMultiServerInvite(data)
  }

exports.parseLink = function parseBlob (ref) {
  var match = parseLinkRegex.exec(ref)
  if (match && match[1]) {
    if (match[3]) {
      return {link: match[1], query: Querystring.parse(match[4])}
    } else {
      return {link: match[1]}
    }
  }
}

function parseLegacyInvite (invite) {
  var redirect = invite.split('#')
  invite = redirect.shift()
  var parts = invite.split('~')
  var addr = toAddress(parts[0])//.split(':')
  //convert legacy code to multiserver invite code.
  invite = remote+':'+parts[1]
  var remote = toMultiServerAddress(addr)
  return {
    invite: remote + ':' + parts[1],
    key: addr.key,
    redirect: null,
    remote: remote,
    redirect: redirect.length ? '#' + redirect.join('#') : null
  }
}

function _parseMultiServerInvite (invite) {

  var redirect = invite.split('#')
  if(!redirect.length) return null

  invite = redirect.shift()
  var addr = _parseMultiServerAddress(invite)
  if(!addr) return null
  delete addr.seed
  return {
    invite: invite,
    remote: toMultiServerAddress(addr),
    key: addr.key,
    redirect: redirect.length ? '#' + redirect.join('#') : null
  }
}

exports.parseLegacyInvite = deprecate('ssb-ref.parseLegacyInvite', parseLegacyInvite)
exports.parseMultiServerInvite = deprecate('ssb-ref.parseMultiServerInvite', _parseMultiServerInvite)

exports.parseInvite = deprecate('ssb-ref.parseInvite', function (invite) {
  return (
    isLegacyInvite(invite)
  ? parseLegacyInvite(invite)
  : isMultiServerInvite(invite)
  ? _parseMultiServerInvite(invite)
  : null
  )
})

exports.type =
  function (id) {
    if(!isString(id)) return false
    var c = id.charAt(0)
    if (c == '@' && isFeedId(id))
      return 'feed'
    else if (c == '%' && isMsgId(id))
      return 'msg'
    else if (c == '&' && isBlobId(id))
      return 'blob'
    else if(isAddress(id)) return 'address'
    else if(isInvite(id)) return 'invite'
    else
    return false
  }

exports.extract =
  function (data) {
    if (!isString(data))
      return false

    var _data = data

    var res = extractRegex.exec(_data)
    if (res) {
      return res && res[0]
    } else {
      try { _data = decodeURIComponent(data) }
      catch (e) {} // this may fail if it's not encoded, so don't worry if it does
      _data = _data.replace(/&/g, '&')

      var res = extractRegex.exec(_data)
      return res && res[0]
    }
  }

<|endoftext|>