<commit_before>/*
 * Clara - music handler module
 *
 * Contributed by Ovvyerus
 */

const ytdl = require('youtube-dl');
const ytSearch = require('youtube-search');
const request = require('request');
let PassThrough = require('stream').PassThrough;
const utils = require(`${__baseDir}/lib/utils`);

var ytRegex = exports.ytRegex = str => /(https?:\/\/)?(www\.|m.)?youtube\.com\/watch\?v=.+(&.+)?/.test(str) || /(https?:\/\/)?youtu\.be\/.+/.test(str);
var scRegex = exports.scRegex = str => /(https?:\/\/)?soundcloud\.com\/.+\/.+/.test(str);
var clypRegex = exports.clypRegex = str => /(https?:\/\/)?clyp\.it\/.+/.test(str);
var allRegex = exports.allRegex = str => ytRegex(str) || scRegex(str) || clypRegex(str);
exports.exposed = {};

var search = exports.search = (msg, terms) => {
    return new Promise((resolve, reject) => {
        let bot = exports.exposed.bot;
        ytSearch(terms, {maxResults: 10, key: bot.config.ytSearchKey}, (err, res) => {
            if (err) {
                reject(err);
            } else {
                res = res.filter(r => r.kind === 'youtube#video');
                if (res.length > 0) {
                    let embed = {
                        title: 'Search Results',
                        description: 'Please choose one of the following choices below by just typing its corresponding number.',
                        fields: []
                    };

                    for (let i = 0; i <= 4; i++) {
                        if (!res[i]) break;
                        embed.fields.push({name: (i + 1) + ': ' + res[i].channelTitle, value: res[i].title});
                    }

                    let outerMsg;
                    msg.channel.createMessage({embed}).then(m => {
                        outerMsg = m;
                        return bot.awaitMessage(msg.channel.id, msg.author.id);
                    }).then(m => {
                        if (/^[1-5]$/.test(m.content.split(' ')[0])) {
                            let choice = Number(m.content.split(' ')[0]) - 1;
                            choice = res[choice];
                            outerMsg.delete();
                            return {msg: m, url: choice.link};
                        } else {
                            outerMsg.delete();
                            return m.channel.createMessage('Invalid response.');
                        }
                    }).then(resolve).catch(reject);
                } else {
                    msg.channel.createMessage('No results found.').then(resolve).catch(reject);
                }
            }
        });
    });
};

var play = exports.play = (msg, url) => {
    return new Promise((resolve, reject) => {
        let bot = exports.exposed.bot;
        if (!bot.music.connections.get(msg.channel.guild.id)) {
            queue(msg, url).then(() => {
                return bot.joinVoiceChannel(msg.member.voiceState.channelID);
            }).then(cnc => {
                if (!bot.music.channels.get(cnc.channelID)) bot.music.channels.add(msg.channel.guild.channels.get(cnc.channelID));
                if (!bot.music.guilds.get(bot.guilds.get(cnc.id))) bot.music.guilds.add(bot.guilds.get(cnc.id));
                bot.music.connections.add(cnc);
                return getStream(url);
            }).then(res => {
                let [stream, type] = res;
                let cnc = bot.music.connections.get(msg.channel.guild.id);
                let info = bot.music.queues.get(msg.channel.guild.id).q[0].info;
                bot.music.streams.add({id: msg.channel.guild.id, stream, type});
                cnc.play(stream, {encoderArgs: ['-af', 'volume=0.5']});

                stream.on('data', function infoSend() {
                    msg.channel.createMessage({embed: {
                        title: 'Now Playing',
                        description: `${info.title}\n[Link](${url}) **[${timeFormat(info.length)}]**`,
                        image: {url: info.thumbnail},
                        footer: {text: `Requested by ${utils.formatUsername(msg.member)} | ${type}`}
                    }});
                    this.removeListener('data', infoSend);
                });

                cnc.on('error', function onError(err) {
                    logger.customError('voiceConnection', `Error in voice connection in guild '${bot.guilds.get(cnc.id).name}' (${cnc.id}):\n${err}`);
                    msg.channel.createMessage(err);
                });

                cnc.on('end', function onMusicEnd() {
                    if (cnc.playing || !bot.music.queues.get(msg.channel.guild.id) || bot.music.stopped.indexOf(msg.channel.guild.id) > -1) return;
                    let q = bot.music.queues.get(msg.channel.guild.id).q;
                    if (q[0].url === url) {
                        q.splice(0, 1);
                        bot.music.streams.delete(msg.channel.guild.id);
                    }
                    if (q.length > 0) play(q[0].msg, q[0].url);
                    this.removeAllListeners('error');
                    this.removeListener('end', onMusicEnd);
                });
            }).then(resolve).catch(reject);
        } else if (bot.music.connections.get(msg.channel.guild.id) && (!bot.music.queues.get(msg.channel.guild.id) || bot.music.queues.get(msg.channel.guild.id).q.length === 0)) {
            queue(msg, url).then(() => {
                return getStream(url);
            }).then(res => {
                let [stream, type] = res;
                let cnc = bot.music.connections.get(msg.channel.guild.id);
                let info = bot.music.queues.get(msg.channel.guild.id).q[0].info;
                bot.music.streams.add({id: msg.channel.guild.id, stream, type});
                cnc.play(stream, {encoderArgs: ['-af', 'volume=0.5']});

                stream.on('data', function infoSend() {
                    msg.channel.createMessage({embed: {
                        title: 'Now Playing',
                        description: `${info.title}\n[Link](${url}) **[${timeFormat(info.length)}]**`,
                        image: {url: info.thumbnail},
                        footer: {text: `Requested by ${utils.formatUsername(msg.member)} | ${type}`}
                    }});
                    this.removeListener('data', infoSend);
                });

                cnc.on('error', function onError(err) {
                    logger.customError('voiceConnection', `Error in voice connection in guild '${bot.guilds.get(cnc.id).name}' (${cnc.id}):\n${err}`);
                    msg.channel.createMessage(err);
                });

                cnc.on('end', function onMusicEnd() {
                    if (cnc.playing || !bot.music.queues.get(msg.channel.guild.id) || bot.music.stopped.indexOf(msg.channel.guild.id) > -1) return;
                    let q = bot.music.queues.get(msg.channel.guild.id).q;
                    if (q[0].url === url) {
                        q.splice(0, 1);
                        bot.music.streams.delete(msg.channel.guild.id);
                    }
                    if (q.length > 0) play(q[0].msg, q[0].url);
                    this.removeAllListeners('error');
                    this.removeListener('end', onMusicEnd);
                });
            }).then(resolve).catch(reject);
        } else if (bot.music.connections.get(msg.channel.guild.id) && bot.music.queues.get(msg.channel.guild.id).q.length > 0 && bot.music.queues.get(msg.channel.guild.id).q[0].url === url) {
            getStream(url).then(res => {
                let [stream, type] = res;
                let cnc = bot.music.connections.get(msg.channel.guild.id);
                let info = bot.music.queues.get(msg.channel.guild.id).q[0].info;
                bot.music.streams.add({id: msg.channel.guild.id, stream, type: 'YouTubeVideo'});
                cnc.play(stream, {encoderArgs: ['-af', 'volume=0.5']});

                stream.on('data', function infoSend() {
                    msg.channel.createMessage({embed: {
                        title: 'Now Playing',
                        description: `${info.title}\n[Link](${url}) **[${timeFormat(info.length)}]**`,
                        image: {url: info.thumbnail},
                        footer: {text: `Requested by ${utils.formatUsername(msg.member)} | ${type}`}
                    }});
                    this.removeListener('data', infoSend);
                });

                cnc.on('error', function onError(err) {
                    logger.customError('voiceConnection', `Error in voice connection in guild '${bot.guilds.get(cnc.id).name}' (${cnc.id}):\n${err}`);
                    msg.channel.createMessage(err);
                });

                cnc.on('end', function onMusicEnd() {
                    if (cnc.playing || !bot.music.queues.get(msg.channel.guild.id) || bot.music.stopped.indexOf(msg.channel.guild.id) > -1) return;
                    let q = bot.music.queues.get(msg.channel.guild.id).q;
                    if (q[0].url === url) {
                        q.splice(0, 1);
                        bot.music.streams.delete(msg.channel.guild.id);
                    }
                    if (q.length > 0) play(q[0].msg, q[0].url);
                    this.removeAllListeners('error');
                    this.removeListener('end', onMusicEnd);
                });
            }).then(resolve).catch(reject);
        } else if (bot.music.connections.get(msg.channel.guild.id) && bot.music.queues.get(msg.channel.guild.id).q.length > 0 && bot.music.queues.get(msg.channel.guild.id).q[0].url !== url && bot.music.connections.get(msg.channel.guild.id).playing) {
            queue(msg, url).then(resolve).catch(reject);
        }
    });
};

var queue = exports.queue = (msg, url) => {
    return new Promise((resolve, reject) => {
        let bot = exports.exposed.bot;
        if (!bot.music.queues.get(msg.channel.guild.id)) bot.music.queues.add({id: msg.channel.guild.id, q: []});
        let q = bot.music.queues.get(msg.channel.guild.id).q;

        getSongInfo(url).then(info => {
            q.push({url, requestee: msg.author.id, msg, info, timestamp: Date.now()});
            return msg.channel.createMessage(`Queued **${info.title}** to position **${q.length}**.`);
        }).then(resolve).catch(reject);
    });
};

var getStream = exports.getStream = song => {
    return new Promise((resolve, reject) => {
        if (ytRegex(song)) {
            resolve([ytdl(song), 'YouTubeVideo']);
        } else if (scRegex(song)) {
            resolve([ytdl(song), 'SoundCloudTrack']);
        } else if (clypRegex(song)) {
            request(song, (err, resp, body) => {
                if (err) {
                    reject(err);
                } else if (resp.statusCode !== 200) {
                    reject(new Error(`Invalid status code: ${resp.statusCode}`));
                } else {
                    let stream = new PassThrough();
                    resolve([request(JSON.parse(body).Mp3Url).pipe(stream), 'ClypAudio']);
                }
            });
        } else {
            reject(new Error(`Could not get stream for ${song}`));
        }
    });
};

var getSongInfo = exports.getSongInfo = song => {
    return new Promise((resolve, reject) => {
        if (ytRegex(song)) {
            ytdl.getInfo(song, (err, info) => {
                if (err) {
                    reject(err);
                } else {
                    let res = {title: info.fulltitle, uploader: info.uploader, thumbnail: info.thumbnail, length: formatToSeconds(info.duration), type: 'YouTubeVideo'};
                    resolve(res);
                }
            });
        } else if (scRegex(song)) {
            ytdl.getInfo(song, (err, info) => {
                if (err) {
                    reject(err);
                } else {
                    let res = {title: info.title, uploader: info.uploader, thumbnail: info.thumbnail, length: formatToSeconds(info.duration), type: 'SoundCloudTrack'};
                    resolve(res);
                }
            });
        } else if (clypRegex(song)) {
            let id = song.split('/');
            id = id[id.length -1];
            request(`https://api.clyp.it/${id}`, (err, resp, body) => {
                if (err) {
                    reject(err);
                } else if (resp.statusCode !== 200) {
                    reject(new Error(`Invalid status code: ${resp.statusCode}`));
                } else {
                    let info = JSON.parse(body);
                    let res = {title: info.Title, uploader: 'N/A', thumbnail: info.ArtworkPictureURL || '', length: Math.floor(info.Duration), type: 'ClypAudio'};
                    resolve(res);
                }
            });
        }
    });
};

var timeFormat = exports.timeFormat = secs => {
    let seconds = secs % 60;
    let minutes = (secs / 60) % 60;
    let hours = (minutes / 60) % 24;

    seconds = Math.floor(seconds);
    minutes = Math.floor(minutes);
    hours = Math.floor(hours);

    seconds.toString().length === 1 ? seconds = '0' + seconds.toString() : seconds = seconds.toString();
    minutes.toString().length === 1 && hours !== 0 ? minutes = '0' + minutes.toString() : minutes = minutes.toString();

    return `${hours === 0  ? '' : `${hours}:`}${minutes}:${seconds}`;
};

var formatToSeconds =  exports.formatToSeconds = time => {
    let splitted = time.split(':');
    return Number(splitted[0]) * 60 + Number(splitted[1]);
};
<commit_msg>Clyp fixes<commit_after>/*
 * Clara - music handler module
 *
 * Contributed by Ovvyerus
 */

const ytdl = require('youtube-dl');
const ytSearch = require('youtube-search');
const request = require('request');
let PassThrough = require('stream').PassThrough;
const utils = require(`${__baseDir}/lib/utils`);

var ytRegex = exports.ytRegex = str => /(https?:\/\/)?(www\.|m.)?youtube\.com\/watch\?v=.+(&.+)?/.test(str) || /(https?:\/\/)?youtu\.be\/.+/.test(str);
var scRegex = exports.scRegex = str => /(https?:\/\/)?soundcloud\.com\/.+\/.+/.test(str);
var clypRegex = exports.clypRegex = str => /(https?:\/\/)?clyp\.it\/.+/.test(str);
var allRegex = exports.allRegex = str => ytRegex(str) || scRegex(str) || clypRegex(str);
exports.exposed = {};

var search = exports.search = (msg, terms) => {
    return new Promise((resolve, reject) => {
        let bot = exports.exposed.bot;
        ytSearch(terms, {maxResults: 10, key: bot.config.ytSearchKey}, (err, res) => {
            if (err) {
                reject(err);
            } else {
                res = res.filter(r => r.kind === 'youtube#video');
                if (res.length > 0) {
                    let embed = {
                        title: 'Search Results',
                        description: 'Please choose one of the following choices below by just typing its corresponding number.',
                        fields: []
                    };

                    for (let i = 0; i <= 4; i++) {
                        if (!res[i]) break;
                        embed.fields.push({name: (i + 1) + ': ' + res[i].channelTitle, value: res[i].title});
                    }

                    let outerMsg;
                    msg.channel.createMessage({embed}).then(m => {
                        outerMsg = m;
                        return bot.awaitMessage(msg.channel.id, msg.author.id);
                    }).then(m => {
                        if (/^[1-5]$/.test(m.content.split(' ')[0])) {
                            let choice = Number(m.content.split(' ')[0]) - 1;
                            choice = res[choice];
                            outerMsg.delete();
                            return {msg: m, url: choice.link};
                        } else {
                            outerMsg.delete();
                            return m.channel.createMessage('Invalid response.');
                        }
                    }).then(resolve).catch(reject);
                } else {
                    msg.channel.createMessage('No results found.').then(resolve).catch(reject);
                }
            }
        });
    });
};

var play = exports.play = (msg, url) => {
    return new Promise((resolve, reject) => {
        let bot = exports.exposed.bot;
        if (!bot.music.connections.get(msg.channel.guild.id)) {
            queue(msg, url).then(() => {
                return bot.joinVoiceChannel(msg.member.voiceState.channelID);
            }).then(cnc => {
                if (!bot.music.channels.get(cnc.channelID)) bot.music.channels.add(msg.channel.guild.channels.get(cnc.channelID));
                if (!bot.music.guilds.get(bot.guilds.get(cnc.id))) bot.music.guilds.add(bot.guilds.get(cnc.id));
                bot.music.connections.add(cnc);
                return getStream(url);
            }).then(res => {
                let [stream, type] = res;
                let cnc = bot.music.connections.get(msg.channel.guild.id);
                let info = bot.music.queues.get(msg.channel.guild.id).q[0].info;
                bot.music.streams.add({id: msg.channel.guild.id, stream, type});
                cnc.play(stream, {encoderArgs: ['-af', 'volume=0.5']});

                stream.on('data', function infoSend() {
                    msg.channel.createMessage({embed: {
                        title: 'Now Playing',
                        description: `${info.title}\n[Link](${url}) **[${timeFormat(info.length)}]**`,
                        image: {url: info.thumbnail},
                        footer: {text: `Requested by ${utils.formatUsername(msg.member)} | ${type}`}
                    }});
                    this.removeListener('data', infoSend);
                });

                cnc.on('error', function onError(err) {
                    logger.customError('voiceConnection', `Error in voice connection in guild '${bot.guilds.get(cnc.id).name}' (${cnc.id}):\n${err}`);
                    msg.channel.createMessage(err);
                });

                cnc.on('end', function onMusicEnd() {
                    if (cnc.playing || !bot.music.queues.get(msg.channel.guild.id) || bot.music.stopped.indexOf(msg.channel.guild.id) > -1) return;
                    let q = bot.music.queues.get(msg.channel.guild.id).q;
                    if (q[0].url === url) {
                        q.splice(0, 1);
                        bot.music.streams.delete(msg.channel.guild.id);
                    }
                    if (q.length > 0) play(q[0].msg, q[0].url);
                    this.removeAllListeners('error');
                    this.removeListener('end', onMusicEnd);
                });
            }).then(resolve).catch(reject);
        } else if (bot.music.connections.get(msg.channel.guild.id) && (!bot.music.queues.get(msg.channel.guild.id) || bot.music.queues.get(msg.channel.guild.id).q.length === 0)) {
            queue(msg, url).then(() => {
                return getStream(url);
            }).then(res => {
                let [stream, type] = res;
                let cnc = bot.music.connections.get(msg.channel.guild.id);
                let info = bot.music.queues.get(msg.channel.guild.id).q[0].info;
                bot.music.streams.add({id: msg.channel.guild.id, stream, type});
                cnc.play(stream, {encoderArgs: ['-af', 'volume=0.5']});

                stream.on('data', function infoSend() {
                    msg.channel.createMessage({embed: {
                        title: 'Now Playing',
                        description: `${info.title}\n[Link](${url}) **[${timeFormat(info.length)}]**`,
                        image: {url: info.thumbnail},
                        footer: {text: `Requested by ${utils.formatUsername(msg.member)} | ${type}`}
                    }});
                    this.removeListener('data', infoSend);
                });

                cnc.on('error', function onError(err) {
                    logger.customError('voiceConnection', `Error in voice connection in guild '${bot.guilds.get(cnc.id).name}' (${cnc.id}):\n${err}`);
                    msg.channel.createMessage(err);
                });

                cnc.on('end', function onMusicEnd() {
                    if (cnc.playing || !bot.music.queues.get(msg.channel.guild.id) || bot.music.stopped.indexOf(msg.channel.guild.id) > -1) return;
                    let q = bot.music.queues.get(msg.channel.guild.id).q;
                    if (q[0].url === url) {
                        q.splice(0, 1);
                        bot.music.streams.delete(msg.channel.guild.id);
                    }
                    if (q.length > 0) play(q[0].msg, q[0].url);
                    this.removeAllListeners('error');
                    this.removeListener('end', onMusicEnd);
                });
            }).then(resolve).catch(reject);
        } else if (bot.music.connections.get(msg.channel.guild.id) && bot.music.queues.get(msg.channel.guild.id).q.length > 0 && bot.music.queues.get(msg.channel.guild.id).q[0].url === url) {
            getStream(url).then(res => {
                let [stream, type] = res;
                let cnc = bot.music.connections.get(msg.channel.guild.id);
                let info = bot.music.queues.get(msg.channel.guild.id).q[0].info;
                bot.music.streams.add({id: msg.channel.guild.id, stream, type: 'YouTubeVideo'});
                cnc.play(stream, {encoderArgs: ['-af', 'volume=0.5']});

                stream.on('data', function infoSend() {
                    msg.channel.createMessage({embed: {
                        title: 'Now Playing',
                        description: `${info.title}\n[Link](${url}) **[${timeFormat(info.length)}]**`,
                        image: {url: info.thumbnail},
                        footer: {text: `Requested by ${utils.formatUsername(msg.member)} | ${type}`}
                    }});
                    this.removeListener('data', infoSend);
                });

                cnc.on('error', function onError(err) {
                    logger.customError('voiceConnection', `Error in voice connection in guild '${bot.guilds.get(cnc.id).name}' (${cnc.id}):\n${err}`);
                    msg.channel.createMessage(err);
                });

                cnc.on('end', function onMusicEnd() {
                    if (cnc.playing || !bot.music.queues.get(msg.channel.guild.id) || bot.music.stopped.indexOf(msg.channel.guild.id) > -1) return;
                    let q = bot.music.queues.get(msg.channel.guild.id).q;
                    if (q[0].url === url) {
                        q.splice(0, 1);
                        bot.music.streams.delete(msg.channel.guild.id);
                    }
                    if (q.length > 0) play(q[0].msg, q[0].url);
                    this.removeAllListeners('error');
                    this.removeListener('end', onMusicEnd);
                });
            }).then(resolve).catch(reject);
        } else if (bot.music.connections.get(msg.channel.guild.id) && bot.music.queues.get(msg.channel.guild.id).q.length > 0 && bot.music.queues.get(msg.channel.guild.id).q[0].url !== url && bot.music.connections.get(msg.channel.guild.id).playing) {
            queue(msg, url).then(resolve).catch(reject);
        }
    });
};

var queue = exports.queue = (msg, url) => {
    return new Promise((resolve, reject) => {
        let bot = exports.exposed.bot;
        if (!bot.music.queues.get(msg.channel.guild.id)) bot.music.queues.add({id: msg.channel.guild.id, q: []});
        let q = bot.music.queues.get(msg.channel.guild.id).q;

        getSongInfo(url).then(info => {
            q.push({url, requestee: msg.author.id, msg, info, timestamp: Date.now()});
            return msg.channel.createMessage(`Queued **${info.title}** to position **${q.length}**.`);
        }).then(resolve).catch(reject);
    });
};

var getStream = exports.getStream = song => {
    return new Promise((resolve, reject) => {
        if (ytRegex(song)) {
            resolve([ytdl(song), 'YouTubeVideo']);
        } else if (scRegex(song)) {
            resolve([ytdl(song), 'SoundCloudTrack']);
        } else if (clypRegex(song)) {
            let id = song.split('/');
            id = id[id.length - 1];
            request(`https://api.clyp.it/${id}`, (err, resp, body) => {
                if (err) {
                    reject(err);
                } else if (resp.statusCode !== 200) {
                    reject(new Error(`Invalid status code: ${resp.statusCode}`));
                } else {
                    let stream = new PassThrough();
                    resolve([request(JSON.parse(body).Mp3Url).pipe(stream), 'ClypAudio']);
                }
            });
        } else {
            reject(new Error(`Could not get stream for ${song}`));
        }
    });
};

var getSongInfo = exports.getSongInfo = song => {
    return new Promise((resolve, reject) => {
        if (ytRegex(song)) {
            ytdl.getInfo(song, (err, info) => {
                if (err) {
                    reject(err);
                } else {
                    let res = {title: info.fulltitle, uploader: info.uploader, thumbnail: info.thumbnail, length: formatToSeconds(info.duration), type: 'YouTubeVideo'};
                    resolve(res);
                }
            });
        } else if (scRegex(song)) {
            ytdl.getInfo(song, (err, info) => {
                if (err) {
                    reject(err);
                } else {
                    let res = {title: info.title, uploader: info.uploader, thumbnail: info.thumbnail, length: formatToSeconds(info.duration), type: 'SoundCloudTrack'};
                    resolve(res);
                }
            });
        } else if (clypRegex(song)) {
            let id = song.split('/');
            id = id[id.length -1];
            request(`https://api.clyp.it/${id}`, (err, resp, body) => {
                if (err) {
                    reject(err);
                } else if (resp.statusCode !== 200) {
                    reject(new Error(`Invalid status code: ${resp.statusCode}`));
                } else {
                    let info = JSON.parse(body);
                    let res = {title: info.Title, uploader: 'N/A', thumbnail: info.ArtworkPictureURL || '', length: Math.floor(info.Duration), type: 'ClypAudio'};
                    resolve(res);
                }
            });
        }
    });
};

var timeFormat = exports.timeFormat = secs => {
    let seconds = secs % 60;
    let minutes = (secs / 60) % 60;
    let hours = (minutes / 60) % 24;

    seconds = Math.floor(seconds);
    minutes = Math.floor(minutes);
    hours = Math.floor(hours);

    seconds.toString().length === 1 ? seconds = '0' + seconds.toString() : seconds = seconds.toString();
    minutes.toString().length === 1 && hours !== 0 ? minutes = '0' + minutes.toString() : minutes = minutes.toString();

    return `${hours === 0  ? '' : `${hours}:`}${minutes}:${seconds}`;
};

var formatToSeconds =  exports.formatToSeconds = time => {
    let splitted = time.split(':');
    return Number(splitted[0]) * 60 + Number(splitted[1]);
};
<|endoftext|>


<commit_before>/* global Cache */
const Command = require('../Command')
const DiscordServer = require('../../DiscordServer')
const VirtualGroups = require('../../VirtualGroups')
const request = require('request-promise')

const Contributors = require('../../Contributors.json')

module.exports =
class WhoisCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'whois',
      properName: 'Whois',
      aliases: ['roblox'],
      userPermissions: [],
      description: "`<user>` Get a user's Roblox profile link.",

      args: [
        {
          key: 'member',
          label: 'member',
          prompt: 'What is their username?',
          type: 'member',
          default: false
        }
      ]
    })
  }

  // This is probably the worst file in the entire project, so prepare yourself.
  // TODO: DRY this up and make the method that gets user data a method in DiscordMember
  async fn (msg, args) {
    let member = args.member
    let data = {}
    if (member) { // If the member specified exists,
      let editMessage = await msg.reply(`:mag: Looking up ${member.displayName.replace('@', '')}`)

      let id = member.user.id
      try {
        // Read user data from memory, or request it if there isn't any cached.
        data = await Cache.get('users', id)

        if (!data) {
          data = await request({
            uri: `https://verify.eryn.io/api/user/${id}`,
            json: true,
            simple: false
          })
        }
      } catch (e) {
        return editMessage.edit("An error occured while fetching that user's data.")
      }

      if (data.status === 'ok') {
        let apiUserData = {}
        try {
          apiUserData = await request({
            uri: `http://api.roblox.com/users/${data.robloxId}`,
            json: true,
            simple: false
          })
        } catch (e) {
          return false
        }

        if (apiUserData.Username) {
          data.robloxUsername = apiUserData.Username
        }
        let profileLink = `https://www.roblox.com/users/${data.robloxId}/profile`
        let avatarURL = `https://assetgame.roblox.com/Thumbs/Avatar.ashx?username=${encodeURIComponent(data.robloxUsername)}`

        let bio = 'Bio failed to load'
        let joinDate = 'Unknown'
        let pastNames = 'Unknown'
        try {
          let profileSource = await request({
            uri: profileLink
          })

          joinDate = profileSource.match(/Join Date
(.*?)<li/)[1]
          bio = profileSource.match(/<meta name=description content=".*? is one of the millions playing, creating and exploring the endless possibilities of Roblox. Join .*? on Roblox and explore together! ?((?:.|\n)*?)"/m)[1]
          pastNames = profileSource.match(//)[1].substr(0, 1024)
        } catch (e) {}

        let bc = 'Unknown'
        try {
          bc = await Cache.get(`bindings.${data.robloxId}`, 'bc')
          if (!bc) {
            let response = await request({
              uri: `https://www.roblox.com/Thumbs/BCOverlay.ashx?username=${encodeURIComponent(data.robloxUsername)}`,
              simple: false,
              resolveWithFullResponse: true
            })

            let url = response.request.uri.href
            bc = 'NBC'

            if (url.includes('overlay_obcOnly')) {
              bc = 'OBC'
            } else if (url.includes('overlay_tbcOnly')) {
              bc = 'TBC'
            } else if (url.includes('overlay_bcOnly')) {
              bc = 'BC'
            }

            Cache.set(`bindings.${data.robloxId}`, 'bc', bc)
          }
        } catch (e) {}

        // Make sure the data is cached so we don't have to use the API in the future
        Cache.set('users', id, data)

        // Remove excess new lines in the bio
        while ((bio.match(/\n/mg) || []).length > 3) {
          const lastN = bio.lastIndexOf('\n')
          bio = bio.slice(0, lastN) + bio.slice(lastN + 1)
        }

        // Truncate bio if it's too long
        if (bio.length > 500) {
          bio = bio.substr(0, 500) + '...'
        }

        // Add a space after any @ symbols to prevent tagging @everyone, @here, and @anything else Discord adds
        bio = bio.replace('@', '@ ')

        let embed = {
          title: 'View Profile',
          url: profileLink,
          author: {
            name: data.robloxUsername,
            url: profileLink,
            icon_url: avatarURL
          },
          color: args.member.displayColor,
          thumbnail: {
            url: avatarURL
          },
          description: bio,
          fields: [
            { name: 'Join Date', value: joinDate, inline: true },
            { name: 'Builders Club', value: bc, inline: true },
            { name: 'Past Usernames', value: pastNames, inline: true }
          ]
        }

        // Nickname Group rank display
        const nicknameGroup = this.server.getSetting('nicknameGroup')
        if (nicknameGroup) {
          const userGroups = await DiscordServer.getRobloxMemberGroups(data.robloxId)

          const serverGroup = userGroups.find(group => group.Id === parseInt(nicknameGroup))

          let isAlly = false
          if (!serverGroup) {
            isAlly = await VirtualGroups._Relationship({ id: data.robloxId }, parseInt(nicknameGroup), DiscordServer, 'allies')
          }

          embed.fields.push({
            name: `Group Rank`,
            value: isAlly ? 'Ally' : (serverGroup ? serverGroup.Role : 'Guest')
          })
        }

        if (Contributors.includes(id)) embed.fields.push({ name: 'User Tags', value: 'RoVer Contributor', inline: true })

        editMessage.edit({embed}).catch(console.error)
      } else {
        editMessage.edit(`${member.displayName} doesn't seem to be verified.`)
      }
    }
  }
}
<commit_msg>Acknowledge the error <commit_after>/* global Cache */
const Command = require('../Command')
const DiscordServer = require('../../DiscordServer')
const VirtualGroups = require('../../VirtualGroups')
const request = require('request-promise')

const Contributors = require('../../Contributors.json')

module.exports =
class WhoisCommand extends Command {
  constructor (client) {
    super(client, {
      name: 'whois',
      properName: 'Whois',
      aliases: ['roblox'],
      userPermissions: [],
      description: "`<user>` Get a user's Roblox profile link.",

      args: [
        {
          key: 'member',
          label: 'member',
          prompt: 'What is their username?',
          type: 'member',
          default: false
        }
      ]
    })
  }

  // This is probably the worst file in the entire project, so prepare yourself.
  // TODO: DRY this up and make the method that gets user data a method in DiscordMember
  async fn (msg, args) {
    let member = args.member
    let data = {}
    if (member) { // If the member specified exists,
      let editMessage = await msg.reply(`:mag: Looking up ${member.displayName.replace('@', '')}`)

      let id = member.user.id
      try {
        // Read user data from memory, or request it if there isn't any cached.
        data = await Cache.get('users', id)

        if (!data) {
          data = await request({
            uri: `https://verify.eryn.io/api/user/${id}`,
            json: true,
            simple: false
          })
        }
      } catch (e) {
        return editMessage.edit("An error occured while fetching that user's data.")
      }

      if (data.status === 'ok') {
        let apiUserData = {}
        try {
          apiUserData = await request({
            uri: `http://api.roblox.com/users/${data.robloxId}`,
            json: true,
            simple: false
          })
        } catch (e) {
          return editMessage.edit("An error occured while fetching that user's data.")
        }

        if (apiUserData.Username) {
          data.robloxUsername = apiUserData.Username
        }
        let profileLink = `https://www.roblox.com/users/${data.robloxId}/profile`
        let avatarURL = `https://assetgame.roblox.com/Thumbs/Avatar.ashx?username=${encodeURIComponent(data.robloxUsername)}`

        let bio = 'Bio failed to load'
        let joinDate = 'Unknown'
        let pastNames = 'Unknown'
        try {
          let profileSource = await request({
            uri: profileLink
          })

          joinDate = profileSource.match(/Join Date
(.*?)<li/)[1]
          bio = profileSource.match(/<meta name=description content=".*? is one of the millions playing, creating and exploring the endless possibilities of Roblox. Join .*? on Roblox and explore together! ?((?:.|\n)*?)"/m)[1]
          pastNames = profileSource.match(//)[1].substr(0, 1024)
        } catch (e) {}

        let bc = 'Unknown'
        try {
          bc = await Cache.get(`bindings.${data.robloxId}`, 'bc')
          if (!bc) {
            let response = await request({
              uri: `https://www.roblox.com/Thumbs/BCOverlay.ashx?username=${encodeURIComponent(data.robloxUsername)}`,
              simple: false,
              resolveWithFullResponse: true
            })

            let url = response.request.uri.href
            bc = 'NBC'

            if (url.includes('overlay_obcOnly')) {
              bc = 'OBC'
            } else if (url.includes('overlay_tbcOnly')) {
              bc = 'TBC'
            } else if (url.includes('overlay_bcOnly')) {
              bc = 'BC'
            }

            Cache.set(`bindings.${data.robloxId}`, 'bc', bc)
          }
        } catch (e) {}

        // Make sure the data is cached so we don't have to use the API in the future
        Cache.set('users', id, data)

        // Remove excess new lines in the bio
        while ((bio.match(/\n/mg) || []).length > 3) {
          const lastN = bio.lastIndexOf('\n')
          bio = bio.slice(0, lastN) + bio.slice(lastN + 1)
        }

        // Truncate bio if it's too long
        if (bio.length > 500) {
          bio = bio.substr(0, 500) + '...'
        }

        // Add a space after any @ symbols to prevent tagging @everyone, @here, and @anything else Discord adds
        bio = bio.replace('@', '@ ')

        let embed = {
          title: 'View Profile',
          url: profileLink,
          author: {
            name: data.robloxUsername,
            url: profileLink,
            icon_url: avatarURL
          },
          color: args.member.displayColor,
          thumbnail: {
            url: avatarURL
          },
          description: bio,
          fields: [
            { name: 'Join Date', value: joinDate, inline: true },
            { name: 'Builders Club', value: bc, inline: true },
            { name: 'Past Usernames', value: pastNames, inline: true }
          ]
        }

        // Nickname Group rank display
        const nicknameGroup = this.server.getSetting('nicknameGroup')
        if (nicknameGroup) {
          const userGroups = await DiscordServer.getRobloxMemberGroups(data.robloxId)

          const serverGroup = userGroups.find(group => group.Id === parseInt(nicknameGroup))

          let isAlly = false
          if (!serverGroup) {
            isAlly = await VirtualGroups._Relationship({ id: data.robloxId }, parseInt(nicknameGroup), DiscordServer, 'allies')
          }

          embed.fields.push({
            name: `Group Rank`,
            value: isAlly ? 'Ally' : (serverGroup ? serverGroup.Role : 'Guest')
          })
        }

        if (Contributors.includes(id)) embed.fields.push({ name: 'User Tags', value: 'RoVer Contributor', inline: true })

        editMessage.edit({embed}).catch(console.error)
      } else {
        editMessage.edit(`${member.displayName} doesn't seem to be verified.`)
      }
    }
  }
}
<|endoftext|>


<commit_before>import { keys, bindAll, each, isUndefined, debounce } from 'underscore';
import Dragger from 'utils/Dragger';

const evName = 'dmode';

export default {
  run(editor, sender, opts = {}) {
    bindAll(
      this,
      'setPosition',
      'onStart',
      'onDrag',
      'onEnd',
      'getPosition',
      'getGuidesStatic',
      'renderGuide',
      'getGuidesTarget'
    );
    const { target, event, mode, dragger = {} } = opts;
    const el = target.getEl();
    const config = {
      doc: el.ownerDocument,
      onStart: this.onStart,
      onEnd: this.onEnd,
      onDrag: this.onDrag,
      getPosition: this.getPosition,
      setPosition: this.setPosition,
      guidesStatic: () => this.guidesStatic,
      guidesTarget: () => this.guidesTarget,
      ...dragger
    };
    this.setupGuides();
    this.opts = opts;
    this.editor = editor;
    this.em = editor.getModel();
    this.target = target;
    this.isTran = mode == 'translate';
    this.guidesContainer = this.getGuidesContainer();
    this.guidesTarget = this.getGuidesTarget();
    this.guidesStatic = this.getGuidesStatic();
    let drg = this.dragger;

    if (!drg) {
      drg = new Dragger(config);
      this.dragger = drg;
    } else {
      drg.setOptions(config);
    }

    event && drg.start(event);
    this.toggleDrag(1);
    this.em.trigger(`${evName}:start`, this.getEventOpts());

    return drg;
  },

  getEventOpts() {
    return {
      mode: this.opts.mode,
      target: this.target,
      guidesTarget: this.guidesTarget,
      guidesStatic: this.guidesStatic
    };
  },

  stop() {
    this.toggleDrag();
  },

  setupGuides() {
    (this.guides || []).forEach(item => {
      const { guide } = item;
      guide && guide.parentNode.removeChild(guide);
    });
    this.guides = [];
  },

  getGuidesContainer() {
    let { guidesEl } = this;

    if (!guidesEl) {
      const { editor, em, opts } = this;
      const pfx = editor.getConfig('stylePrefix');
      const elInfoX = document.createElement('div');
      const elInfoY = document.createElement('div');
      const guideContent = `

`;
      guidesEl = document.createElement('div');
      guidesEl.className = `${pfx}guides`;
      elInfoX.className = `${pfx}guide-info ${pfx}guide-info__x`;
      elInfoY.className = `${pfx}guide-info ${pfx}guide-info__y`;
      elInfoX.innerHTML = guideContent;
      elInfoY.innerHTML = guideContent;
      guidesEl.appendChild(elInfoX);
      guidesEl.appendChild(elInfoY);
      editor.Canvas.getGlobalToolsEl().appendChild(guidesEl);
      this.guidesEl = guidesEl;
      this.elGuideInfoX = elInfoX;
      this.elGuideInfoY = elInfoY;
      this.elGuideInfoContentX = elInfoX.querySelector(
        `.${pfx}guide-info__content`
      );
      this.elGuideInfoContentY = elInfoY.querySelector(
        `.${pfx}guide-info__content`
      );

      em.on(
        'canvas:update frame:scroll',
        debounce(() => {
          this.updateGuides();
          opts.debug && this.guides.forEach(item => this.renderGuide(item));
        }, 200)
      );
    }

    return guidesEl;
  },

  getGuidesStatic() {
    let result = [];
    const el = this.target.getEl();
    const { parentNode = {} } = el;
    each(
      parentNode.children,
      item =>
        (result = result.concat(el !== item ? this.getElementGuides(item) : []))
    );

    return result.concat(this.getElementGuides(parentNode));
  },

  getGuidesTarget() {
    return this.getElementGuides(this.target.getEl());
  },

  updateGuides(guides) {
    let lastEl, lastPos;
    (guides || this.guides).forEach(item => {
      const { origin } = item;
      const pos = lastEl === origin ? lastPos : this.getElementPos(origin);
      lastEl = origin;
      lastPos = pos;
      each(this.getGuidePosUpdate(item, pos), (val, key) => (item[key] = val));
      item.originRect = pos;
    });
  },

  getGuidePosUpdate(item, rect) {
    const result = {};
    const { top, height, left, width } = rect;

    switch (item.type) {
      case 't':
        result.y = top;
        break;
      case 'b':
        result.y = top + height;
        break;
      case 'l':
        result.x = left;
        break;
      case 'r':
        result.x = left + width;
        break;
      case 'x':
        result.x = left + width / 2;
        break;
      case 'y':
        result.y = top + height / 2;
        break;
    }

    return result;
  },

  renderGuide(item = {}) {
    const el = item.guide || document.createElement('div');
    const un = 'px';
    const guideSize = item.active ? 2 : 1;
    let numEl = el.children[0];
    el.style = `position: absolute; background-color: ${
      item.active ? 'green' : 'red'
    };`;

    if (!el.children.length) {
      numEl = document.createElement('div');
      numEl.style =
        'position: absolute; color: red; padding: 5px; top: 0; left: 0;';
      el.appendChild(numEl);
    }

    if (item.y) {
      el.style.width = '100%';
      el.style.height = `${guideSize}${un}`;
      el.style.top = `${item.y}${un}`;
      el.style.left = 0;
    } else {
      el.style.width = `${guideSize}${un}`;
      el.style.height = '100%';
      el.style.left = `${item.x}${un}`;
      el.style.top = `0${un}`;
    }

    !item.guide && this.guidesContainer.appendChild(el);
    return el;
  },

  getElementPos(el) {
    return this.editor.Canvas.getElementPos(el, { noScroll: 1 });
  },

  getElementGuides(el) {
    const { opts } = this;
    const originRect = this.getElementPos(el);
    const { top, height, left, width } = originRect;
    const guides = [
      { type: 't', y: top }, // Top
      { type: 'b', y: top + height }, // Bottom
      { type: 'l', x: left }, // Left
      { type: 'r', x: left + width }, // Right
      { type: 'x', x: left + width / 2 }, // Mid x
      { type: 'y', y: top + height / 2 } // Mid y
    ].map(item => ({
      ...item,
      origin: el,
      originRect,
      guide: opts.debug && this.renderGuide(item)
    }));
    guides.forEach(item => this.guides.push(item));

    return guides;
  },

  getTranslate(transform, axis = 'x') {
    let result = 0;
    (transform || '').split(' ').forEach(item => {
      const itemStr = item.trim();
      const fn = `translate${axis.toUpperCase()}(`;
      if (itemStr.indexOf(fn) === 0)
        result = parseFloat(itemStr.replace(fn, ''));
    });
    return result;
  },

  setTranslate(transform, axis, value) {
    const fn = `translate${axis.toUpperCase()}(`;
    const val = `${fn}${value})`;
    let result = (transform || '')
      .split(' ')
      .map(item => {
        const itemStr = item.trim();
        if (itemStr.indexOf(fn) === 0) item = val;
        return item;
      })
      .join(' ');
    if (result.indexOf(fn) < 0) result += ` ${val}`;

    return result;
  },

  getPosition() {
    const { target, isTran } = this;
    const { left, top, transform } = target.getStyle();
    let x = 0;
    let y = 0;

    if (isTran) {
      x = this.getTranslate(transform);
      y = this.getTranslate(transform, 'y');
    } else {
      x = parseFloat(left);
      y = parseFloat(top);
    }

    return { x, y };
  },

  setPosition({ x, y, end, position, width, height }) {
    const { target, isTran, em } = this;
    const unit = 'px';
    const en = !end ? 1 : ''; // this will trigger the final change
    const left = `${x}${unit}`;
    const top = `${y}${unit}`;
    let styleUp = {};

    if (isTran) {
      let transform = target.getStyle()['transform'] || '';
      transform = this.setTranslate(transform, 'x', left);
      transform = this.setTranslate(transform, 'y', top);
      styleUp = { transform, en };
      target.addStyle(styleUp, { avoidStore: !end });
    } else {
      const adds = { position, width, height };
      const style = { left, top, en };
      keys(adds).forEach(add => {
        const prop = adds[add];
        if (prop) style[add] = prop;
      });
      styleUp = style;
      target.addStyle(styleUp, { avoidStore: !end });
    }

    // Update StyleManager properties
    em.getSelected() &&
      keys(styleUp).forEach(i => em.trigger(`update:component:style:${i}`));
  },

  _getDragData() {
    const { target } = this;
    return {
      target,
      parent: target.parent(),
      index: target.index()
    };
  },

  onStart(event) {
    const { target, editor, isTran, opts } = this;
    const { center, onStart } = opts;
    const { Canvas } = editor;
    const style = target.getStyle();
    const position = 'absolute';
    onStart && onStart(this._getDragData());
    if (isTran) return;

    if (style.position !== position) {
      let { left, top, width, height } = Canvas.offset(target.getEl());

      // Check if to center the target to the pointer position
      if (center) {
        const { x, y } = Canvas.getMouseRelativeCanvas(event);
        left = x;
        top = y;
      }

      this.setPosition({
        x: left,
        y: top,
        width: `${width}px`,
        height: `${height}px`,
        position
      });
    }
  },

  onDrag(...args) {
    const { guidesTarget, opts } = this;
    const { onDrag } = opts;
    this.updateGuides(guidesTarget);
    opts.debug && guidesTarget.forEach(item => this.renderGuide(item));
    opts.guidesInfo &&
      this.renderGuideInfo(guidesTarget.filter(item => item.active));
    onDrag && onDrag(this._getDragData());
  },

  onEnd(ev, dragger, opt = {}) {
    const { editor, opts, id } = this;
    const { onEnd } = opts;
    onEnd && onEnd(ev, opt, { event: ev, ...opt, ...this._getDragData() });
    editor.stopCommand(id);
    this.hideGuidesInfo();
    this.em.trigger(`${evName}:end`, this.getEventOpts());
  },

  hideGuidesInfo() {
    ['X', 'Y'].forEach(item => {
      const guide = this[`elGuideInfo${item}`];
      if (guide) guide.style.display = 'none';
    });
  },

  /**
   * Render guides with spacing information
   */
  renderGuideInfo(guides = []) {
    const { guidesStatic } = this;
    this.hideGuidesInfo();
    guides.forEach(item => {
      const { origin, x } = item;
      const rectOrigin = this.getElementPos(origin);
      const axis = isUndefined(x) ? 'y' : 'x';
      const isY = axis === 'y';
      const origEdge1 = rectOrigin[isY ? 'left' : 'top'];
      const origEdge1Raw = rectOrigin.rect[isY ? 'left' : 'top'];
      const origEdge2 = isY
        ? origEdge1 + rectOrigin.width
        : origEdge1 + rectOrigin.height;
      const origEdge2Raw = isY
        ? origEdge1Raw + rectOrigin.rect.width
        : origEdge1Raw + rectOrigin.rect.height;
      const elGuideInfo = this[`elGuideInfo${axis.toUpperCase()}`];
      const elGuideInfoCnt = this[`elGuideInfoContent${axis.toUpperCase()}`];
      const guideInfoStyle = elGuideInfo.style;

      // Find the nearest element
      const res = guidesStatic
        .filter(stat => stat.type === item.type)
        .map(stat => {
          const { left, width, top, height } = stat.originRect;
          const statEdge1 = isY ? left : top;
          const statEdge2 = isY ? left + width : top + height;
          return {
            gap:
              statEdge2 < origEdge1
                ? origEdge1 - statEdge2
                : statEdge1 - origEdge2,
            guide: stat
          };
        })
        .filter(item => item.gap > 0)
        .sort((a, b) => a.gap - b.gap)
        .map(item => item.guide)[0];

      if (res) {
        const { left, width, top, height, rect } = res.originRect;
        const isEdge1 = isY ? left < rectOrigin.left : top < rectOrigin.top;
        const statEdge1 = isY ? left : top;
        const statEdge1Raw = isY ? rect.left : rect.top;
        const statEdge2 = isY ? left + width : top + height;
        const statEdge2Raw = isY
          ? rect.left + rect.width
          : rect.top + rect.height;
        const posFirst = isY ? item.y : item.x;
        const posSecond = isEdge1 ? statEdge2 : origEdge2;
        const pos2 = `${posFirst}px`;
        const size = isEdge1 ? origEdge1 - statEdge2 : statEdge1 - origEdge2;
        const sizeRaw = isEdge1
          ? origEdge1Raw - statEdge2Raw
          : statEdge1Raw - origEdge2Raw;
        guideInfoStyle.display = '';
        guideInfoStyle[isY ? 'top' : 'left'] = pos2;
        guideInfoStyle[isY ? 'left' : 'top'] = `${posSecond}px`;
        guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`;
        elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`;
        this.em.trigger(`${evName}:active`, {
          ...this.getEventOpts(),
          guide: item,
          guidesStatic,
          matched: res,
          posFirst,
          posSecond,
          size,
          sizeRaw,
          elGuideInfo,
          elGuideInfoCnt
        });
      }
    });
  },

  toggleDrag(enable) {
    const { ppfx, editor } = this;
    const methodCls = enable ? 'add' : 'remove';
    const classes = [`${ppfx}is__grabbing`];
    const { Canvas } = editor;
    const body = Canvas.getBody();
    classes.forEach(cls => body.classList[methodCls](cls));
    Canvas[enable ? 'startAutoscroll' : 'stopAutoscroll']();
  }
};
<commit_msg>Add default top left in absolute mode. Closes #2547<commit_after>import { keys, bindAll, each, isUndefined, debounce } from 'underscore';
import Dragger from 'utils/Dragger';

const evName = 'dmode';

export default {
  run(editor, sender, opts = {}) {
    bindAll(
      this,
      'setPosition',
      'onStart',
      'onDrag',
      'onEnd',
      'getPosition',
      'getGuidesStatic',
      'renderGuide',
      'getGuidesTarget'
    );
    const { target, event, mode, dragger = {} } = opts;
    const el = target.getEl();
    const config = {
      doc: el.ownerDocument,
      onStart: this.onStart,
      onEnd: this.onEnd,
      onDrag: this.onDrag,
      getPosition: this.getPosition,
      setPosition: this.setPosition,
      guidesStatic: () => this.guidesStatic,
      guidesTarget: () => this.guidesTarget,
      ...dragger
    };
    this.setupGuides();
    this.opts = opts;
    this.editor = editor;
    this.em = editor.getModel();
    this.target = target;
    this.isTran = mode == 'translate';
    this.guidesContainer = this.getGuidesContainer();
    this.guidesTarget = this.getGuidesTarget();
    this.guidesStatic = this.getGuidesStatic();
    let drg = this.dragger;

    if (!drg) {
      drg = new Dragger(config);
      this.dragger = drg;
    } else {
      drg.setOptions(config);
    }

    event && drg.start(event);
    this.toggleDrag(1);
    this.em.trigger(`${evName}:start`, this.getEventOpts());

    return drg;
  },

  getEventOpts() {
    return {
      mode: this.opts.mode,
      target: this.target,
      guidesTarget: this.guidesTarget,
      guidesStatic: this.guidesStatic
    };
  },

  stop() {
    this.toggleDrag();
  },

  setupGuides() {
    (this.guides || []).forEach(item => {
      const { guide } = item;
      guide && guide.parentNode.removeChild(guide);
    });
    this.guides = [];
  },

  getGuidesContainer() {
    let { guidesEl } = this;

    if (!guidesEl) {
      const { editor, em, opts } = this;
      const pfx = editor.getConfig('stylePrefix');
      const elInfoX = document.createElement('div');
      const elInfoY = document.createElement('div');
      const guideContent = `

`;
      guidesEl = document.createElement('div');
      guidesEl.className = `${pfx}guides`;
      elInfoX.className = `${pfx}guide-info ${pfx}guide-info__x`;
      elInfoY.className = `${pfx}guide-info ${pfx}guide-info__y`;
      elInfoX.innerHTML = guideContent;
      elInfoY.innerHTML = guideContent;
      guidesEl.appendChild(elInfoX);
      guidesEl.appendChild(elInfoY);
      editor.Canvas.getGlobalToolsEl().appendChild(guidesEl);
      this.guidesEl = guidesEl;
      this.elGuideInfoX = elInfoX;
      this.elGuideInfoY = elInfoY;
      this.elGuideInfoContentX = elInfoX.querySelector(
        `.${pfx}guide-info__content`
      );
      this.elGuideInfoContentY = elInfoY.querySelector(
        `.${pfx}guide-info__content`
      );

      em.on(
        'canvas:update frame:scroll',
        debounce(() => {
          this.updateGuides();
          opts.debug && this.guides.forEach(item => this.renderGuide(item));
        }, 200)
      );
    }

    return guidesEl;
  },

  getGuidesStatic() {
    let result = [];
    const el = this.target.getEl();
    const { parentNode = {} } = el;
    each(
      parentNode.children,
      item =>
        (result = result.concat(el !== item ? this.getElementGuides(item) : []))
    );

    return result.concat(this.getElementGuides(parentNode));
  },

  getGuidesTarget() {
    return this.getElementGuides(this.target.getEl());
  },

  updateGuides(guides) {
    let lastEl, lastPos;
    (guides || this.guides).forEach(item => {
      const { origin } = item;
      const pos = lastEl === origin ? lastPos : this.getElementPos(origin);
      lastEl = origin;
      lastPos = pos;
      each(this.getGuidePosUpdate(item, pos), (val, key) => (item[key] = val));
      item.originRect = pos;
    });
  },

  getGuidePosUpdate(item, rect) {
    const result = {};
    const { top, height, left, width } = rect;

    switch (item.type) {
      case 't':
        result.y = top;
        break;
      case 'b':
        result.y = top + height;
        break;
      case 'l':
        result.x = left;
        break;
      case 'r':
        result.x = left + width;
        break;
      case 'x':
        result.x = left + width / 2;
        break;
      case 'y':
        result.y = top + height / 2;
        break;
    }

    return result;
  },

  renderGuide(item = {}) {
    const el = item.guide || document.createElement('div');
    const un = 'px';
    const guideSize = item.active ? 2 : 1;
    let numEl = el.children[0];
    el.style = `position: absolute; background-color: ${
      item.active ? 'green' : 'red'
    };`;

    if (!el.children.length) {
      numEl = document.createElement('div');
      numEl.style =
        'position: absolute; color: red; padding: 5px; top: 0; left: 0;';
      el.appendChild(numEl);
    }

    if (item.y) {
      el.style.width = '100%';
      el.style.height = `${guideSize}${un}`;
      el.style.top = `${item.y}${un}`;
      el.style.left = 0;
    } else {
      el.style.width = `${guideSize}${un}`;
      el.style.height = '100%';
      el.style.left = `${item.x}${un}`;
      el.style.top = `0${un}`;
    }

    !item.guide && this.guidesContainer.appendChild(el);
    return el;
  },

  getElementPos(el) {
    return this.editor.Canvas.getElementPos(el, { noScroll: 1 });
  },

  getElementGuides(el) {
    const { opts } = this;
    const originRect = this.getElementPos(el);
    const { top, height, left, width } = originRect;
    const guides = [
      { type: 't', y: top }, // Top
      { type: 'b', y: top + height }, // Bottom
      { type: 'l', x: left }, // Left
      { type: 'r', x: left + width }, // Right
      { type: 'x', x: left + width / 2 }, // Mid x
      { type: 'y', y: top + height / 2 } // Mid y
    ].map(item => ({
      ...item,
      origin: el,
      originRect,
      guide: opts.debug && this.renderGuide(item)
    }));
    guides.forEach(item => this.guides.push(item));

    return guides;
  },

  getTranslate(transform, axis = 'x') {
    let result = 0;
    (transform || '').split(' ').forEach(item => {
      const itemStr = item.trim();
      const fn = `translate${axis.toUpperCase()}(`;
      if (itemStr.indexOf(fn) === 0)
        result = parseFloat(itemStr.replace(fn, ''));
    });
    return result;
  },

  setTranslate(transform, axis, value) {
    const fn = `translate${axis.toUpperCase()}(`;
    const val = `${fn}${value})`;
    let result = (transform || '')
      .split(' ')
      .map(item => {
        const itemStr = item.trim();
        if (itemStr.indexOf(fn) === 0) item = val;
        return item;
      })
      .join(' ');
    if (result.indexOf(fn) < 0) result += ` ${val}`;

    return result;
  },

  getPosition() {
    const { target, isTran } = this;
    const { left, top, transform } = target.getStyle();
    let x = 0;
    let y = 0;

    if (isTran) {
      x = this.getTranslate(transform);
      y = this.getTranslate(transform, 'y');
    } else {
      x = parseFloat(left || 0);
      y = parseFloat(top || 0);
    }

    return { x, y };
  },

  setPosition({ x, y, end, position, width, height }) {
    const { target, isTran, em } = this;
    const unit = 'px';
    const en = !end ? 1 : ''; // this will trigger the final change
    const left = `${x}${unit}`;
    const top = `${y}${unit}`;
    let styleUp = {};

    if (isTran) {
      let transform = target.getStyle()['transform'] || '';
      transform = this.setTranslate(transform, 'x', left);
      transform = this.setTranslate(transform, 'y', top);
      styleUp = { transform, en };
      target.addStyle(styleUp, { avoidStore: !end });
    } else {
      const adds = { position, width, height };
      const style = { left, top, en };
      keys(adds).forEach(add => {
        const prop = adds[add];
        if (prop) style[add] = prop;
      });
      styleUp = style;
      target.addStyle(styleUp, { avoidStore: !end });
    }

    // Update StyleManager properties
    em.getSelected() &&
      keys(styleUp).forEach(i => em.trigger(`update:component:style:${i}`));
  },

  _getDragData() {
    const { target } = this;
    return {
      target,
      parent: target.parent(),
      index: target.index()
    };
  },

  onStart(event) {
    const { target, editor, isTran, opts } = this;
    const { center, onStart } = opts;
    const { Canvas } = editor;
    const style = target.getStyle();
    const position = 'absolute';
    onStart && onStart(this._getDragData());
    if (isTran) return;

    if (style.position !== position) {
      let { left, top, width, height } = Canvas.offset(target.getEl());

      // Check if to center the target to the pointer position
      if (center) {
        const { x, y } = Canvas.getMouseRelativeCanvas(event);
        left = x;
        top = y;
      }

      this.setPosition({
        x: left,
        y: top,
        width: `${width}px`,
        height: `${height}px`,
        position
      });
    }
  },

  onDrag(...args) {
    const { guidesTarget, opts } = this;
    const { onDrag } = opts;
    this.updateGuides(guidesTarget);
    opts.debug && guidesTarget.forEach(item => this.renderGuide(item));
    opts.guidesInfo &&
      this.renderGuideInfo(guidesTarget.filter(item => item.active));
    onDrag && onDrag(this._getDragData());
  },

  onEnd(ev, dragger, opt = {}) {
    const { editor, opts, id } = this;
    const { onEnd } = opts;
    onEnd && onEnd(ev, opt, { event: ev, ...opt, ...this._getDragData() });
    editor.stopCommand(id);
    this.hideGuidesInfo();
    this.em.trigger(`${evName}:end`, this.getEventOpts());
  },

  hideGuidesInfo() {
    ['X', 'Y'].forEach(item => {
      const guide = this[`elGuideInfo${item}`];
      if (guide) guide.style.display = 'none';
    });
  },

  /**
   * Render guides with spacing information
   */
  renderGuideInfo(guides = []) {
    const { guidesStatic } = this;
    this.hideGuidesInfo();
    guides.forEach(item => {
      const { origin, x } = item;
      const rectOrigin = this.getElementPos(origin);
      const axis = isUndefined(x) ? 'y' : 'x';
      const isY = axis === 'y';
      const origEdge1 = rectOrigin[isY ? 'left' : 'top'];
      const origEdge1Raw = rectOrigin.rect[isY ? 'left' : 'top'];
      const origEdge2 = isY
        ? origEdge1 + rectOrigin.width
        : origEdge1 + rectOrigin.height;
      const origEdge2Raw = isY
        ? origEdge1Raw + rectOrigin.rect.width
        : origEdge1Raw + rectOrigin.rect.height;
      const elGuideInfo = this[`elGuideInfo${axis.toUpperCase()}`];
      const elGuideInfoCnt = this[`elGuideInfoContent${axis.toUpperCase()}`];
      const guideInfoStyle = elGuideInfo.style;

      // Find the nearest element
      const res = guidesStatic
        .filter(stat => stat.type === item.type)
        .map(stat => {
          const { left, width, top, height } = stat.originRect;
          const statEdge1 = isY ? left : top;
          const statEdge2 = isY ? left + width : top + height;
          return {
            gap:
              statEdge2 < origEdge1
                ? origEdge1 - statEdge2
                : statEdge1 - origEdge2,
            guide: stat
          };
        })
        .filter(item => item.gap > 0)
        .sort((a, b) => a.gap - b.gap)
        .map(item => item.guide)[0];

      if (res) {
        const { left, width, top, height, rect } = res.originRect;
        const isEdge1 = isY ? left < rectOrigin.left : top < rectOrigin.top;
        const statEdge1 = isY ? left : top;
        const statEdge1Raw = isY ? rect.left : rect.top;
        const statEdge2 = isY ? left + width : top + height;
        const statEdge2Raw = isY
          ? rect.left + rect.width
          : rect.top + rect.height;
        const posFirst = isY ? item.y : item.x;
        const posSecond = isEdge1 ? statEdge2 : origEdge2;
        const pos2 = `${posFirst}px`;
        const size = isEdge1 ? origEdge1 - statEdge2 : statEdge1 - origEdge2;
        const sizeRaw = isEdge1
          ? origEdge1Raw - statEdge2Raw
          : statEdge1Raw - origEdge2Raw;
        guideInfoStyle.display = '';
        guideInfoStyle[isY ? 'top' : 'left'] = pos2;
        guideInfoStyle[isY ? 'left' : 'top'] = `${posSecond}px`;
        guideInfoStyle[isY ? 'width' : 'height'] = `${size}px`;
        elGuideInfoCnt.innerHTML = `${Math.round(sizeRaw)}px`;
        this.em.trigger(`${evName}:active`, {
          ...this.getEventOpts(),
          guide: item,
          guidesStatic,
          matched: res,
          posFirst,
          posSecond,
          size,
          sizeRaw,
          elGuideInfo,
          elGuideInfoCnt
        });
      }
    });
  },

  toggleDrag(enable) {
    const { ppfx, editor } = this;
    const methodCls = enable ? 'add' : 'remove';
    const classes = [`${ppfx}is__grabbing`];
    const { Canvas } = editor;
    const body = Canvas.getBody();
    classes.forEach(cls => body.classList[methodCls](cls));
    Canvas[enable ? 'startAutoscroll' : 'stopAutoscroll']();
  }
};
<|endoftext|>


<commit_before>/**
 * @file Common to atob & btoa polyfills
 * @since 0.2.9
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfALPHA*/ // Letters (uppercase)
/*global _gpfAlpha*/ // Letters (lowercase)
/*global _gpfDigit*/ // Digits
/*exported _GPF_12_BITS*/ // 12
/*exported _GPF_16_BITS*/ // 16
/*exported _GPF_18_BITS*/ // 18
/*exported _GPF_1_BYTE*/ // 1
/*exported _GPF_2_BYTES*/ // 2
/*exported _GPF_6_BITS*/ // 6
/*exported _GPF_6_BITS_MASK*/ // 63
/*exported _GPF_PADDING_CHAR*/ // 64
/*exported _gpfBase64*/ // Base64 encoding string
/*#endif*/

var _gpfBase64 = _gpfALPHA + _gpfAlpha + _gpfDigit + "+/=",
    _GPF_6_BITS_MASK = 63,
    _GPF_18_BITS = 18,
    _GPF_16_BITS = 18,
    _GPF_12_BITS = 12,
    _GPF_6_BITS = 6,
    _GPF_1_BYTE = 8,
    _GPF_2_BYTES = 16,
    _GPF_PADDING_CHAR = 64;

/*#ifndef(UMD)*/

// Generates an empty function to reflect the null complexity of this module
function _gpfCompatibilityBase64Common () {}
/*exported _gpfCompatibilityBase64Common*/

/*#endif*/

<commit_msg>Also consider coverage (#240)<commit_after>/**
 * @file Common to atob & btoa polyfills
 * @since 0.2.9
 */
/*#ifndef(UMD)*/
"use strict";
/*global _gpfALPHA*/ // Letters (uppercase)
/*global _gpfAlpha*/ // Letters (lowercase)
/*global _gpfDigit*/ // Digits
/*exported _GPF_12_BITS*/ // 12
/*exported _GPF_16_BITS*/ // 16
/*exported _GPF_18_BITS*/ // 18
/*exported _GPF_1_BYTE*/ // 1
/*exported _GPF_2_BYTES*/ // 2
/*exported _GPF_6_BITS*/ // 6
/*exported _GPF_6_BITS_MASK*/ // 63
/*exported _GPF_PADDING_CHAR*/ // 64
/*exported _gpfBase64*/ // Base64 encoding string
/*#endif*/

var _gpfBase64 = _gpfALPHA + _gpfAlpha + _gpfDigit + "+/=",
    _GPF_6_BITS_MASK = 63,
    _GPF_18_BITS = 18,
    _GPF_16_BITS = 18,
    _GPF_12_BITS = 12,
    _GPF_6_BITS = 6,
    _GPF_1_BYTE = 8,
    _GPF_2_BYTES = 16,
    _GPF_PADDING_CHAR = 64;

/*#ifndef(UMD)*/

// Generates an empty function to reflect the null complexity of this module
(function _gpfCompatibilityBase64Common () {}());

/*#endif*/
<|endoftext|>


<commit_before>const readFileSync = require("fs").readFileSync;
const { resolve } = require("url");
const { rollup } = require("rollup");
const typescript = require("typescript");

const { packageConfig } = require("./rollup-base.config");

// globals
const packagesRoot = resolve(__dirname, ".");
const buildRoot = resolve(__dirname, "scripts/");

function packagePath(name) {
	return resolve(packagesRoot, name + "/");
}

function packageBuildTempPath(name) {
	return resolve(buildRoot, `temp/${name}`);
}

function packageJSONPath(name) {
	return resolve(packagesRoot, `${name}/package.json`);
}

function createTSConfigForPackage(packageName) {
	const packageDir = packagePath(packageName);
	const buildDir = packageBuildTempPath(packageName);

	const result = typescript.readConfigFile(resolve(buildRoot, "tsconfig-packages.json"), path => readFileSync(path, "utf8"));
	if (result.error) {
		console.info(typescript.flattenDiagnosticMessageText(result.error.messageText, "\n"));
		return undefined;
	}

	return {
		...result.config.compilerOptions || {},
		...{
			// this is not handled by readConfigFile for some reason
			target: typescript.ScriptTarget.ES2017,
			module: typescript.ModuleKind.ES2015,
			moduleResolution: typescript.ModuleResolutionKind.NodeJs,

			rootDir: `${packageDir}/src`,
			outDir: buildDir,
			declarationDir: `${packageDir}/dist`,

			// why do _I_ need to do this?
			lib: (result.config.compilerOptions.lib || []).map(lf => `lib.${lf}.d.ts`)
		}
	};
}

function compile(packageName) {
	const packageDir = packagePath(packageName);
	const realPackageName = packageName.split("/")[1];
	const indexFileName = resolve(packageDir + "/", `src/${realPackageName}.ts`);

	const compilerOptions = createTSConfigForPackage(packageName);
	if (! compilerOptions) {
		return false;
	}

	const program = typescript.createProgram([indexFileName], compilerOptions);
	const emitResult = program.emit();

    const allDiagnostics = typescript.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);

	for (const diagnostic of allDiagnostics) {
        if (diagnostic.file) {
            const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
			const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
            console.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
        }
        else {
            console.error(typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
        }
    }

    return !emitResult.emitSkipped;
}

async function roll(packageName) {
	const packageDir = packagePath(packageName);
	const buildDir = packageBuildTempPath(packageName);
	const packageJSON = require(packageJSONPath(packageName));
	const rollupConfig = packageConfig(packageJSON, buildDir, packageDir);

	const { output } = rollupConfig;
	delete rollupConfig.output;

	const bundle = await rollup(rollupConfig);
	for (const out of output) {
		await bundle.write(out);
	}
}

async function run() {
	const packageList = process.argv.slice(2);
	if (packageList.length !== 1) {
		console.info("Usage: build-package <group-name>/<package-name>");
		return;
	}
	const packageToBuild = packageList[0];

	if (compile(packageToBuild)) {
		await roll(packageToBuild);
	}
	else {
		throw new Error("compilation failed");
	}
}

run().catch(
	err => {
		console.error("Error: " + err.message);
		console.info(err);
		process.exit(1);
	}
);
<commit_msg>fix(build): supply global names of deps in rollup UMD config<commit_after>const readFileSync = require("fs").readFileSync;
const { resolve } = require("url");
const { rollup } = require("rollup");
const typescript = require("typescript");

const { packageConfig } = require("./rollup-base.config");

// globals
const packagesRoot = resolve(__dirname, ".");
const buildRoot = resolve(__dirname, "scripts/");

function packagePath(name) {
	return resolve(packagesRoot, name + "/");
}

function packageBuildTempPath(name) {
	return resolve(buildRoot, `temp/${name}`);
}

function packageJSONPath(name) {
	return resolve(packagesRoot, `${name}/package.json`);
}

function createTSConfigForPackage(packageName) {
	const packageDir = packagePath(packageName);
	const buildDir = packageBuildTempPath(packageName);

	const result = typescript.readConfigFile(resolve(buildRoot, "tsconfig-packages.json"), path => readFileSync(path, "utf8"));
	if (result.error) {
		console.info(typescript.flattenDiagnosticMessageText(result.error.messageText, "\n"));
		return undefined;
	}

	return {
		...result.config.compilerOptions || {},
		...{
			// this is not handled by readConfigFile for some reason
			target: typescript.ScriptTarget.ES2017,
			module: typescript.ModuleKind.ES2015,
			moduleResolution: typescript.ModuleResolutionKind.NodeJs,

			rootDir: `${packageDir}/src`,
			outDir: buildDir,
			declarationDir: `${packageDir}/dist`,

			// why do _I_ need to do this?
			lib: (result.config.compilerOptions.lib || []).map(lf => `lib.${lf}.d.ts`)
		}
	};
}

function compile(packageName) {
	const packageDir = packagePath(packageName);
	const realPackageName = packageName.split("/")[1];
	const indexFileName = resolve(packageDir + "/", `src/${realPackageName}.ts`);

	const compilerOptions = createTSConfigForPackage(packageName);
	if (! compilerOptions) {
		return false;
	}

	const program = typescript.createProgram([indexFileName], compilerOptions);
	const emitResult = program.emit();

    const allDiagnostics = typescript.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);

	for (const diagnostic of allDiagnostics) {
        if (diagnostic.file) {
            const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
			const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
            console.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
        }
        else {
            console.error(typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
        }
    }

    return !emitResult.emitSkipped;
}

async function roll(packageName) {
	const packageDir = packagePath(packageName);
	const buildDir = packageBuildTempPath(packageName);
	const packageJSON = require(packageJSONPath(packageName));
	const rollupConfig = packageConfig(packageJSON, buildDir, packageDir);

	const globals = {};
	for (const depName in packageJSON.dependencies) {
		if (depName.startsWith("@stardazed/")) {
			const pascal = depName.replace("@stardazed/", "").split("-").map(
				s => s[0].toUpperCase() + s.substr(1)
			).join("");
			globals[depName] = `sd${pascal}`;
		}
	}

	const { output } = rollupConfig;
	delete rollupConfig.output;

	const bundle = await rollup(rollupConfig);
	for (const out of output) {
		if (out.format === "umd") {
			out.globals = globals;
		}
		await bundle.write(out);
	}
}

async function run() {
	const packageList = process.argv.slice(2);
	if (packageList.length !== 1) {
		console.info("Usage: build-package <group-name>/<package-name>");
		return;
	}
	const packageToBuild = packageList[0];

	if (compile(packageToBuild)) {
		await roll(packageToBuild);
	}
	else {
		throw new Error("compilation failed");
	}
}

run().catch(
	err => {
		console.error("Error: " + err.message);
		console.info(err);
		process.exit(1);
	}
);
<|endoftext|>


<commit_before>import React from 'react';
import AltContainer from 'alt-container';

import styles from './css/media.module.css';

import TXIDSearch from './search';

import InfoBox from './InfoBox';
import AlbumCover from './AlbumCover';
import PlayList from './PlayList';
import PWYW from './PWYW';

import testData from './tests/data.json';

import alt from './alt';

const Actions = {
    PWYWActions: alt.generateActions('showPWYW', 'hidePWYW'),
    TXIDActions: alt.generateActions('loadingTXID', 'recievedTXID', 'failedTXID'),
    FormatActions: alt.generateActions('setFormat')
}

const AlexandriaSource = {
    fetchTXID: {
        // remotely fetch something (required)
        remote(state, txid) {
            return TXIDSearch(state.server, txid)
        },

        local(state, txid) {
            return state.results[txid] ? state.results : null;
        },

        loading: Actions.TXIDActions.loadingTXID,
        success: Actions.TXIDActions.recievedTXID,
        error:   Actions.TXIDActions.failedTXID,

        shouldFetch(state) {
            return true
        }
    }
};

class StoreModel {
    constructor() {
        Object.keys(Actions).map(k => this.bindActions(Actions[k]));

        this.state = {
            server: 'libraryd.alexandria.media',
            results: {},
            data: null,
            mediaInfo: {
                artist: null,
                title: null,
            },
            formats: {
                options: [],
                selected: null
            },
            PWYW: {
                shown: false,
                type: null
            },
            cover: null,
            price: null
        };

        this.registerAsync(AlexandriaSource);
    }

    onRecievedTXID(e) {
        let media = e['media-data']['alexandria-media'];
        let info = media.info;
        let xinfo= info['extra-info'];
        let formats = Object.keys(xinfo.element1).filter(k => (k !== 'name'));

        console.log (media);
        this.setState({
            mediaInfo: {
                title: info.title,
                artist: xinfo.artist,
            },
            price: info.price,
            formats: {
                options: formats,
                selected: formats[0]
            },
            data: info
        });
    }

    onLoadingTXID(e) {
        console.error('loading TXID', e)
    }
    onFailedTXID(e) {
        console.error('failed TXID', e)
    }

    onShowPWYW(type) {
        console.log ('show', type)
        this.setState({
            PWYW: {
                shown: true,
                type: type
            }
        })
    }

    onHidePWYW() {
        this.state.PWYW.shown = false
    }

    onSetFormat(format) {
        this.state.formats.selected = format
    }
}

const Store = alt.createStore(StoreModel);

export default class Component extends React.Component {
    static defaultProps = {
        txid: 'fc9220025df5f2bf76fcda8f66bced4c95846e136ff3f5ff4c36fede3a5e3fc5'
    }

    constructor(props) {
        super(props);

        Store.fetchTXID(props.txid);
//        console.error ('test data', testData)
//        this.state = testData['media-data']['alexandria-media']['info']
    }

    render() {
        return (
                <AltContainer stores={{state: Store}} actions={Actions}
                render={props => {
                        if (Store.isLoading())
                            return
Loading Please Wait...

                       return <Alexandria {...props}/>
                    }}/>
                )
    }
}

export class Alexandria extends React.Component {
    render() {
        let {state, PWYWActions} = this.props;
        console.log (this.props, this.state)
        let xinfo = state.data['extra-info'];

        return (

                {state.PWYW.shown?
                 <PWYW {...state.PWYW} actions={PWYWActions} />:null
                }

                        <InfoBox {...this.props} data={state.data}/>
                        <AlbumCover {...xinfo}/>

                        <PlayList tracks={xinfo.elements} author={xinfo.author}/>

        )
    }
}

<commit_msg>start hooking up services logic<commit_after>import React from 'react';
import AltContainer from 'alt-container';

import styles from './css/media.module.css';

import TXIDSearch from './search';

import InfoBox from './InfoBox';
import AlbumCover from './AlbumCover';
import PlayList from './PlayList';
import PWYW from './PWYW';

import testData from './tests/data.json';

import alt from './alt';

const Actions = {
    PWYWActions: alt.generateActions('showPWYW', 'hidePWYW'),
    TXIDActions: alt.generateActions('loadingTXID', 'recievedTXID', 'failedTXID'),
    FormatActions: alt.generateActions('setFormat')
}

const AlexandriaSource = {
    fetchTXID: {
        // remotely fetch something (required)
        remote(state, txid) {
            return TXIDSearch(state.servers.libraryd, txid)
        },

        local(state, txid) {
            return state.results[txid] ? state.results : null;
        },

        loading: Actions.TXIDActions.loadingTXID,
        success: Actions.TXIDActions.recievedTXID,
        error:   Actions.TXIDActions.failedTXID,

        shouldFetch(state) {
            return true
        }
    }
};

class StoreModel {
    constructor() {
        Object.keys(Actions).map(k => this.bindActions(Actions[k]));

        this.state = {
            servers: {
                libraryd: 'libraryd.alexandria.media',
                ipfs: 'ipfs.alexandria.media'
            },
            results: {},
            data: null,
            mediaInfo: {
                artist: null,
                title: null,
            },
            formats: {
                options: [],
                selected: null
            },
            PWYW: {
                shown: false,
                type: null
            },
            cover: null,
            price: null
        };

        this.registerAsync(AlexandriaSource);
    }

    onRecievedTXID(e) {
        let media = e['media-data']['alexandria-media'];
        let info = media.info;
        let xinfo= info['extra-info'];
        let formats = Object.keys(xinfo.element1).filter(k => (k !== 'name'));

        console.log (media);
        this.setState({
            mediaInfo: {
                title: info.title,
                artist: xinfo.artist,
            },
            price: info.price,
            formats: {
                options: formats,
                selected: formats[0]
            },
            data: info
        });
    }

    onLoadingTXID(e) {
        console.error('loading TXID', e)
    }
    onFailedTXID(e) {
        console.error('failed TXID', e)
    }

    onShowPWYW(type) {
        console.log ('show', type)
        this.setState({
            PWYW: {
                shown: true,
                type: type
            }
        })
    }

    onHidePWYW() {
        this.state.PWYW.shown = false
    }

    onSetFormat(format) {
        this.state.formats.selected = format
    }
}

const Store = alt.createStore(StoreModel);

export default class Component extends React.Component {
    static defaultProps = {
        txid: 'fc9220025df5f2bf76fcda8f66bced4c95846e136ff3f5ff4c36fede3a5e3fc5'
    }

    constructor(props) {
        super(props);

        Store.fetchTXID(props.txid);
//        console.error ('test data', testData)
//        this.state = testData['media-data']['alexandria-media']['info']
    }

    render() {
        return (
                <AltContainer stores={{state: Store}} actions={Actions}
                render={props => {
                        if (Store.isLoading())
                            return
Loading Please Wait...

                       return <Alexandria {...props}/>
                    }}/>
                )
    }
}

export class Alexandria extends React.Component {
    render() {
        let {state, PWYWActions} = this.props;
        console.log (this.props, this.state)
        let xinfo = state.data['extra-info'];

        return (

                {state.PWYW.shown?
                 <PWYW {...state.PWYW} actions={PWYWActions} />:null
                }

                        <InfoBox {...this.props} data={state.data}/>
                        <AlbumCover {...xinfo}/>

                        <PlayList tracks={xinfo.elements} author={xinfo.author}/>

        )
    }
}

<|endoftext|>


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

//
//
//function toEnglish(a) {
//    if(a == 1) {
//    return "one";
//    } else {
//        return "this is not number 1";
//    }
//
//};
//
//it ('should equal the english word "one" instead of "1"', function() {
//assert.equal(toEnglish(1), "one");
//});

//function toEnglish (a) {
//    if (a == 2) {
//        return "two"
//    } else {
//        return "this is not number 2";
//    }
//};
//
//it ('should equal the english word "two" instead of "2"', function (){
//    assert.equal(toEnglish(2), "two");
//});

//function toEnglish (a) {
//    if (a == 3) {
//        return "three"
//    } else {
//        return "this is not number 3";
//    }
//}
//
//it ('should equal the english word "three" instead of "3"', function () {
//    assert.equal(toEnglish(3), "three");
//});

//function toEnglish (a) {
//    if (a == 4) {
//        return "four"
//    }   else {
//        return "this is not numnber 4";
//    }
//}
//
//it ('should equal the english word "four" instead of "4"', function() {
//    assert.equal(toEnglish(4), "four");
//});
//
//function toEnglish (a) {
//    if (a==5) {
//        return "five"
//    } else {
//        return "this is not number 5";
//    }
//}
//
//it ('should equal the english word "five" instead of "5"', function() {
//    assert.equal(toEnglish(5), "five");
//});
//
//function toEnglish (a) {
//    if (a==6) {
//        return "six"
//    } else {
//        return "this is not number 6";
//    }
//}
//
//it ('should equal the english word "six" instead of "6"', function (){
//    assert.equal(toEnglish(6), "six");
//});

//function toEnglish (a) {
//    if (a==7) {
//        return "seven"
//    } else {
//        return "this is not number 7";
//    }
//}
//
//it ('should equal the english word "seven" instead of "seven"', function() {
//    assert.equal(toEnglish(7), "seven");
//});

//function toEnglish (a) {
//    if (a==8) {
//        return "eight"
//    } else {
//        return "this is not  number 8";
//    }
//}
//
//it ('should equal the english word "eight" instead of "8"', function () {
//    assert.equal(toEnglish(8), "eight");
//});
//
//function toEnglish (a) {
//    if (a==9) {
//        return "nine"
//    } else {
//        return "this is not a number 9";
//    }
//}
//
//it ('should equal the english word "nine" instead of "nine"', function () {
//    assert.equal(toEnglish(9), "nine");
//});

//function toEnglish (a) {
//    if (a==10) {
//        return "ten"
//    } else {
//        return "this is not number 10";
//    }
//}
//
//it ('should equal the english word "ten" instead of "ten"', function () {
//    assert.equal(toEnglish(10), "ten");
//});

function toEnglish(a) {
    if (a == 1) {
        return "one";
    } else if (a== 2) {
        return "two";
    } else if (a==3) {
        return "three";
    } else if (a==4) {
        return "four";
    } else if (a==5) {
        return "five";
    } else if (a==6) {
        return "six";
    } else if (a==7){
        return "seven";
    } else if (a==8){
        return "eight";
    } else if (a==9) {
        return "nine";
    } else if (a==10) {
        return "ten";
    }
<<<<<<< HEAD
};
=======

}

var toEnglish=["zero","one","two","three","four","five","six","seven","eight","nine","ten"]

function print(n){
    return toEnglish[n];
}
>>>>>>> aacd087a802504c4c6ebf72db3ed265e72f26caf

it('should print numbers from string"', function (){
    assert.equal(print(4),"four");
})

it('should return 1 as string "one"', function() {
    assert.equal(toEnglish(1), "one");
})

it('should return "two" when 2 is entered and "not two" if any other number is entered', function () {
    assert.equal(toEnglish(2), "two");

} )

it('should return "three" when 3 is entered and "not the number 3" is any other number is entered', function() {
    assert.equal(toEnglish(3), "three");
})

it('should return 4 as string "four"', function(){
   assert.equal(toEnglish(4),"four");
})

it('should return 5 as string "five"', function(){
    assert.equal(toEnglish(5),"five");
})

it('should return 6 as string "six"', function (){
    assert.equal(toEnglish(6),"six");
})

it('should return 7 as string "seven"', function(){
    assert.equal(toEnglish(7),"seven");
})

it('should return 8 as string "eight"', function(){
    assert.equal(toEnglish(8),"eight");
})

it('should return 9 as string "nine"', function(){
    assert.equal(toEnglish(9),"nine");
})

it('should return 10 as strign "ten"', function () {
    assert.equal(toEnglish(10),"ten");
})<commit_msg>check-writing?<commit_after>var assert = require('assert');

//
//
//function toEnglish(a) {
//    if(a == 1) {
//    return "one";
//    } else {
//        return "this is not number 1";
//    }
//
//};
//
//it ('should equal the english word "one" instead of "1"', function() {
//assert.equal(toEnglish(1), "one");
//});

//function toEnglish (a) {
//    if (a == 2) {
//        return "two"
//    } else {
//        return "this is not number 2";
//    }
//};
//
//it ('should equal the english word "two" instead of "2"', function (){
//    assert.equal(toEnglish(2), "two");
//});

//function toEnglish (a) {
//    if (a == 3) {
//        return "three"
//    } else {
//        return "this is not number 3";
//    }
//}
//
//it ('should equal the english word "three" instead of "3"', function () {
//    assert.equal(toEnglish(3), "three");
//});

//function toEnglish (a) {
//    if (a == 4) {
//        return "four"
//    }   else {
//        return "this is not numnber 4";
//    }
//}
//
//it ('should equal the english word "four" instead of "4"', function() {
//    assert.equal(toEnglish(4), "four");
//});
//
//function toEnglish (a) {
//    if (a==5) {
//        return "five"
//    } else {
//        return "this is not number 5";
//    }
//}
//
//it ('should equal the english word "five" instead of "5"', function() {
//    assert.equal(toEnglish(5), "five");
//});
//
//function toEnglish (a) {
//    if (a==6) {
//        return "six"
//    } else {
//        return "this is not number 6";
//    }
//}
//
//it ('should equal the english word "six" instead of "6"', function (){
//    assert.equal(toEnglish(6), "six");
//});

//function toEnglish (a) {
//    if (a==7) {
//        return "seven"
//    } else {
//        return "this is not number 7";
//    }
//}
//
//it ('should equal the english word "seven" instead of "seven"', function() {
//    assert.equal(toEnglish(7), "seven");
//});

//function toEnglish (a) {
//    if (a==8) {
//        return "eight"
//    } else {
//        return "this is not  number 8";
//    }
//}
//
//it ('should equal the english word "eight" instead of "8"', function () {
//    assert.equal(toEnglish(8), "eight");
//});
//
//function toEnglish (a) {
//    if (a==9) {
//        return "nine"
//    } else {
//        return "this is not a number 9";
//    }
//}
//
//it ('should equal the english word "nine" instead of "nine"', function () {
//    assert.equal(toEnglish(9), "nine");
//});

//function toEnglish (a) {
//    if (a==10) {
//        return "ten"
//    } else {
//        return "this is not number 10";
//    }
//}
//
//it ('should equal the english word "ten" instead of "ten"', function () {
//    assert.equal(toEnglish(10), "ten");
//});

//
//function toEnglish(a) {
//    if (a == 1) {
//        return "one";
//    } else if (a== 2) {
//        return "two";
//    } else if (a==3) {
//        return "three";
//    } else if (a==4) {
//        return "four";
//    } else if (a==5) {
//        return "five";
//    } else if (a==6) {
//        return "six";
//    } else if (a==7){
//        return "seven";
//    } else if (a==8){
//        return "eight";
//    } else if (a==9) {
//        return "nine";
//    } else if (a==10) {
//        return "ten";
//    }
//<<<<<<< HEAD
//};
//=======
//
//}

var toEnglish=["zero","one","two","three","four","five","six","seven","eight","nine","ten"]

function print(n){
    return toEnglish[n];
}
>>>>>>> aacd087a802504c4c6ebf72db3ed265e72f26caf

it('should print numbers from string"', function (){
    assert.equal(print(4),"four");
})

it('should return 1 as string "one"', function() {
    assert.equal(toEnglish(1), "one");
})

it('should return "two" when 2 is entered and "not two" if any other number is entered', function () {
    assert.equal(toEnglish(2), "two");

} )

it('should return "three" when 3 is entered and "not the number 3" is any other number is entered', function() {
    assert.equal(toEnglish(3), "three");
})

it('should return 4 as string "four"', function(){
   assert.equal(toEnglish(4),"four");
})

it('should return 5 as string "five"', function(){
    assert.equal(toEnglish(5),"five");
})

it('should return 6 as string "six"', function (){
    assert.equal(toEnglish(6),"six");
})

it('should return 7 as string "seven"', function(){
    assert.equal(toEnglish(7),"seven");
})

it('should return 8 as string "eight"', function(){
    assert.equal(toEnglish(8),"eight");
})

it('should return 9 as string "nine"', function(){
    assert.equal(toEnglish(9),"nine");
})

it('should return 10 as strign "ten"', function () {
    assert.equal(toEnglish(10),"ten");
})<|endoftext|>