<commit_before>'use strict';

// Modules
const tokens = require('./../tokens.json');
const prefix = tokens.prefix;

const yt = require('ytdl-core');
const ytdl = require('youtube-dl');

// Definitions
const msgDeleteDelay = 15000;

let wrap = (content) => {
    return '```' + content + '```';
};

let msgFormats = {
    addSongFirst: wrap(`Add some songs to the queue first with ${prefix}add`),
    missingAddParam: wrap(`Add keywords, a url, or youtube video id after ${prefix}add`),
    emptyQueue: wrap('Song queue is empty'),
    addedSong: (song) => wrap(`Added ${song.title} to the queue\n`) +
        song.thumbnail,
    alreadyPlaying: wrap('Already Playing'),
    playError: 'Oops, an error occured. Resuming in 5s...',
    playing: (song) => wrap(
            `Now Playing:\n${song.title}\nas requested by: @${song.requester}`
        ),
    paused: (song) => wrap(
            `Paused: ${song.title}`
        ),
    resumed: (song) => wrap(
            `Playing: ${song.title}`
        ),
    skipped: (song) => wrap(
            `Skipped: ${song.title}`
        ),
    volume: (vol) => wrap(
            `Volume: ${vol}%`
        ),
    noVoiceChannel: wrap('I couldn\'t connect to your voice channel...'),
    searchVideoError: wrap('Oops, an error occured while searching the video'),
    invalidVideoLink: (err) => wrap('Invalid YouTube Link: ' + err)
};

let queue = {};
const addToQueue = (id, song) => {
    if (!queue.hasOwnProperty(id)) {
        queue[id] = {};
        queue[id].playing = false;
        queue[id].songs = [];
    }

    queue[id].songs.push(song);
};
const isPlaying = (id) => {
    return queue[id] && queue[id].playing;
};
const getQueueId = (msg) => {
    return msg.guild.id;
};
const getVoiceConnection = (msg) => {
    return msg.guild.voiceConnection;
};
const getVoiceChannel = (msg) => {
    return msg.member.voiceChannel;
};

const getUser = (msg, userId) => {
    return msg.client.fetchUser(userId);
};

const log = () => {
    console.log.apply(console, arguments);
};
const sendMessage = (msg, content, dontDelete)  => {
    return new Promise((resolve, reject) => {
        msg.channel.sendMessage(
            content
        ).then(
            message => {
                !dontDelete && message.delete(msgDeleteDelay);
                resolve(message);
            }
        ).catch(
            err => {
                log(
                    'Error sending msg:',
                    content,
                    '\n',
                    err
                );
                reject(err);
            }
        );
    });
};
const pmMessage = (user, content) => {
    return new Promise((resolve, reject) => {
        user.sendMessage(content).then(
            () => resolve(content)
        ).catch(
            (err) => reject(err)
        );
    });
};

// API
const commands = {
    'play': (msg) => {

        const queueId = getQueueId(msg);
        let currentQueue = queue[queueId];

        if (currentQueue === undefined) {
            return sendMessage(msg, msgFormats.addSongFirst);
        }

        if (currentQueue.playing) {
            return sendMessage(msg, msgFormats.alreadyPlaying);
        }

        let voiceConnection = getVoiceConnection(msg);

        if (!voiceConnection) {
            return commands.join(msg).then(
                connection => commands.play(msg)
            ).catch(
                err => {
                    log(
                        'Error on voiceConnection:\n',
                        err
                    );
                }
            );
        }

        (function play(song) {

            if (song === undefined) {

                sendMessage(msg, msgFormats.emptyQueue).then(
                    message => {
                        getVoiceChannel(msg).leave();
                    }
                );

            } else {

                queue[queueId].playing = true;

                sendMessage(msg, msgFormats.playing(song));

                getUser(msg, song.requesterId).then(
                    (user) => pmMessage(user, msgFormats.playing(song))
                );

                let dispatcher = voiceConnection.playStream(
                    yt(song.url, {quality: 'lowest', audioonly: true}),
                    {passes: tokens.passes}
                );

                let collector = msg.channel.createCollector(m => m);

                collector.on('message', m => {
                    if (m.content.startsWith(prefix + 'pause')) {
                        sendMessage(msg, msgFormats.paused(song)).then(
                            message => dispatcher.pause()
                        );
                    } else if (m.content.startsWith(prefix + 'resume')) {
                        sendMessage(msg, msgFormats.resumed(song)).then(
                            message => dispatcher.resume()
                        );
                    } else if (m.content.startsWith(prefix + 'skip')) {
                        sendMessage(msg, msgFormats.skipped(song)).then(
                            message => dispatcher.end()
                        );
                    } else if (m.content.startsWith('volume+')) {
                        const volume = Math.round(dispatcher.volume * 50);

                        if (volume < 100) {

                            dispatcher.setVolume(
                                Math.min(
                                    (dispatcher.volume * 50 + (2 * (m.content.split('+').length - 1))) / 50,
                                    2
                                )
                            );

                        }

                        sendMessage(msg, msgFormats.volume(volume));
                    } else if (m.content.startsWith('volume-')) {
                        const volume = Math.round(dispatcher.volume * 50);

                        if (volume > 0) {

                            dispatcher.setVolume(
                                Math.max(
                                    (dispatcher.volume * 50 - (2 * (m.content.split('-').length - 1))) / 50,
                                    0
                                )
                            );

                        }

                        sendMessage(msg, msgFormats.volume(volume));
                    } else if (m.content.startsWith(prefix + 'time')) {
                        let minutes = Math.floor(dispatcher.time / 60000);
                        let seconds = Math.floor((dispatcher.time % 60000) / 1000);

                        sendMessage(msg, `time: ${minutes}:${seconds < 10 ? '0' + seconds : seconds}`);
                    }
                });

                dispatcher.on('end', () => {
                    collector.stop();
                    queue[queueId].playing = false;
                    queue[queueId].songs.shift();
                    play(queue[queueId].songs[0]);
                });

                dispatcher.on('error', (err) => {
                    sendMessage(
                        msg,
                        msgFormats.playError
                    ).then(message => {
                        dispatcher.pause();
                        setTimeout(() => {
                            dispatcher.resume();
                        }, msgDeleteDelay);
                    });
                });

            }

        })(currentQueue.songs[0]);
    },
    'join': (msg) => {

        return new Promise((resolve, reject) => {
            const voiceChannel = getVoiceChannel(msg);

            if (!voiceChannel || voiceChannel.type !== 'voice') {

                msg.reply(msgFormats.noVoiceChannel);
                reject(msgFormats.noVoiceChannel);

            } else if (voiceChannel.connection) {

                resolve(voiceChannel.connection);

            } else {

                voiceChannel.join().then(
                    connection => resolve(connection)
                ).catch(
                    err => reject(err)
                );

            }
        });
    },
    'add': (msg, cmdArgs) => {

        let url = cmdArgs;

        if (url == '' || url === undefined) {

            sendMessage(msg, msgFormats.missingAddParam);

        } else if (!url.toLowerCase().startsWith('http')) {

            // If the suffix doesn't start with 'http',
            // assume it's a search.
            url = 'gvsearch1:' + url;

            sendMessage(
                msg,
                wrap('Searching ' + cmdArgs + ' ...'),
                true
            ).then(
                message => {

                    ytdl.getInfo(url, (err, info) => {
                        if (err) {

                            message.edit(msgFormats.searchVideoError);
                            message.delete(5000);

                        } else {

                            let queueId = getQueueId(msg);

                            addToQueue(
                                queueId,
                                {
                                    url: 'https://www.youtube.com/watch?v=' + info.id,
                                    title: info.title,
                                    requester: msg.author.username,
                                    requesterId: msg.author.id
                                }
                            );

                            message.edit(msgFormats.addedSong(info));
                            message.delete(5000);

                            !isPlaying(queueId) && commands.play(msg);
                        }
                    });
                }
            );

        } else if (url) {

            sendMessage(
                msg,
                wrap('Getting video info...'),
                true
            ).then(
                message => {

                    yt.getInfo(url, (err, info) => {
                        if (err) {

                            message.edit(msgFormats.invalidVideoLink(err));
                            message.delete(5000);

                        } else {

                            let queueId = getQueueId(msg);

                            addToQueue(
                                queueId,
                                {
                                    url: url,
                                    title: info.title,
                                    requester: msg.author.username,
                                    requesterId: msg.author.id
                                }
                            );

                            message.edit(msgFormats.addedSong(info));
                            message.delete(5000);

                            !isPlaying(queueId) && commands.play(msg);
                        }
                    });

                }
            );
        }
    },
    'queue': (msg) => {

        const queueId = getQueueId(msg);

        if (queue[queueId] === undefined || queue[queueId].songs.length === 0) {

            sendMessage(msg, msgFormats.addSongFirst);

        } else {

            let tosend = [];

            queue[queueId].songs.forEach((song, i) => {
                tosend.push(`${i+1}. ${song.title} - Requested by: ${song.requester}`);
            });

            sendMessage(
                msg,
                `__**${msg.guild.name}'s Music Queue:**__ **${tosend.length}** songs ${(tosend.length > 15 ? '*[Only next 15 shown]*' : '')}\n\`\`\`${tosend.slice(0,15).join('\n')}\`\`\``
            );
        }
    },
    'help': (cmdPrefix) => {

        return [
            '** MUSIC **',
            cmdPrefix + 'join : "Join Voice channel of msg sender"',
            cmdPrefix + 'add <keywords/url> : "Add a valid youtube link to the queue"',
            cmdPrefix + 'queue : "Shows the current queue, up to 15 songs shown."',
            cmdPrefix + 'play : "Play the music queue if already joined to a voice channel"',
            '',
            'the following commands only function while the play command is running:'.toUpperCase(),
            cmdPrefix + 'pause : "pauses the music"',
            cmdPrefix + 'resume : "resumes the music"',
            cmdPrefix + 'skip : "skips the playing song"',
            cmdPrefix + 'time : "Shows the playtime of the song."',
            'volume+(+++) : "increases volume by 2%/+"',
            'volume-(---) : "decreases volume by 2%/-"'
        ].join('\n');
    },
    'reboot': (msg) => {

        if (msg.author.id == tokens.adminID) {
            //Requires a node module like Forever to work.
            process.exit();
        }
    }
};

// Export Module
module.exports = commands;
<commit_msg>Add options to auto play song, repeat current song, and repeat last song in playlist<commit_after>'use strict';

// Modules
const tokens = require('./../tokens.json');
const prefix = tokens.prefix;

const yt = require('ytdl-core');
const ytdl = require('youtube-dl');

// Definitions
let autoPlay = true;
let repeat = false;
let repeatLast = true;
const msgDeleteDelay = 15000;

let wrap = (content) => {
    return '```' + content + '```';
};

let msgFormats = {
    addSongFirst: wrap(`Add some songs to the queue first with ${prefix}add`),
    missingAddParam: wrap(`Add keywords, a url, or youtube video id after ${prefix}add`),
    emptyQueue: wrap('Song queue is empty'),
    addedSong: (song) => wrap(`Added ${song.title} to the queue\n`) +
        song.thumbnail,
    alreadyPlaying: wrap('Already Playing'),
    playError: 'Oops, an error occured. Resuming in 5s...',
    playing: (song) => wrap(
            `Now Playing:\n${song.title}\nas requested by: @${song.requester}`
        ),
    paused: (song) => wrap(
            `Paused: ${song.title}`
        ),
    resumed: (song) => wrap(
            `Playing: ${song.title}`
        ),
    skipped: (song) => wrap(
            `Skipped: ${song.title}`
        ),
    volume: (vol) => wrap(
            `Volume: ${vol}%`
        ),
    noVoiceChannel: wrap('I couldn\'t connect to your voice channel...'),
    searchVideoError: wrap('Oops, an error occured while searching the video'),
    invalidVideoLink: (err) => wrap('Invalid YouTube Link: ' + err)
};

let queue = {};
const addToQueue = (id, song) => {
    if (!queue.hasOwnProperty(id)) {
        queue[id] = {};
        queue[id].playing = false;
        queue[id].songs = [];
    }

    queue[id].songs.push(song);
};
const isPlaying = (id) => {
    return queue[id] && queue[id].playing;
};
const getQueueId = (msg) => {
    return msg.guild.id;
};
const getVoiceConnection = (msg) => {
    return msg.guild.voiceConnection;
};
const getVoiceChannel = (msg) => {
    return msg.member.voiceChannel;
};

const getUser = (msg, userId) => {
    return msg.client.fetchUser(userId);
};

const log = () => {
    console.log.apply(console, arguments);
};
const sendMessage = (msg, content, dontDelete)  => {
    return new Promise((resolve, reject) => {
        msg.channel.sendMessage(
            content
        ).then(
            message => {
                !dontDelete && message.delete(msgDeleteDelay);
                resolve(message);
            }
        ).catch(
            err => {
                log(
                    'Error sending msg:',
                    content,
                    '\n',
                    err
                );
                reject(err);
            }
        );
    });
};
const pmMessage = (user, content) => {
    return new Promise((resolve, reject) => {
        user.sendMessage(content).then(
            () => resolve(content)
        ).catch(
            (err) => reject(err)
        );
    });
};

// API
const commands = {
    'autoplay': () => {
        autoPlay = !autoPlay;
    },
    'repeat': () => {
        repeat = !repeat;
    },
    'repeatlast': () => {
        repeatLast = !repeatLast;
    },
    'play': (msg) => {

        const queueId = getQueueId(msg);
        let currentQueue = queue[queueId];

        if (currentQueue === undefined) {
            return sendMessage(msg, msgFormats.addSongFirst);
        }

        if (currentQueue.playing) {
            return sendMessage(msg, msgFormats.alreadyPlaying);
        }

        let voiceConnection = getVoiceConnection(msg);

        if (!voiceConnection) {
            return commands.join(msg).then(
                connection => commands.play(msg)
            ).catch(
                err => {
                    log(
                        'Error on voiceConnection:\n',
                        err
                    );
                }
            );
        }

        (function play(song) {

            if (song === undefined) {

                sendMessage(msg, msgFormats.emptyQueue).then(
                    message => {
                        getVoiceChannel(msg).leave();
                    }
                );

            } else {

                queue[queueId].playing = true;

                sendMessage(msg, msgFormats.playing(song));

                getUser(msg, song.requesterId).then(
                    (user) => pmMessage(user, msgFormats.playing(song))
                );

                let dispatcher = voiceConnection.playStream(
                    yt(song.url, {quality: 'lowest', audioonly: true}),
                    {passes: tokens.passes}
                );

                let collector = msg.channel.createCollector(m => m);

                collector.on('message', m => {
                    if (m.content.startsWith(prefix + 'pause')) {
                        sendMessage(msg, msgFormats.paused(song)).then(
                            message => dispatcher.pause()
                        );
                    } else if (m.content.startsWith(prefix + 'resume')) {
                        sendMessage(msg, msgFormats.resumed(song)).then(
                            message => dispatcher.resume()
                        );
                    } else if (m.content.startsWith(prefix + 'skip')) {
                        sendMessage(msg, msgFormats.skipped(song)).then(
                            message => dispatcher.end()
                        );
                    } else if (m.content.startsWith('volume+')) {
                        const volume = Math.round(dispatcher.volume * 50);

                        if (volume < 100) {

                            dispatcher.setVolume(
                                Math.min(
                                    (dispatcher.volume * 50 + (2 * (m.content.split('+').length - 1))) / 50,
                                    2
                                )
                            );

                        }

                        sendMessage(msg, msgFormats.volume(volume));
                    } else if (m.content.startsWith('volume-')) {
                        const volume = Math.round(dispatcher.volume * 50);

                        if (volume > 0) {

                            dispatcher.setVolume(
                                Math.max(
                                    (dispatcher.volume * 50 - (2 * (m.content.split('-').length - 1))) / 50,
                                    0
                                )
                            );

                        }

                        sendMessage(msg, msgFormats.volume(volume));
                    } else if (m.content.startsWith(prefix + 'time')) {
                        let minutes = Math.floor(dispatcher.time / 60000);
                        let seconds = Math.floor((dispatcher.time % 60000) / 1000);

                        sendMessage(msg, `time: ${minutes}:${seconds < 10 ? '0' + seconds : seconds}`);
                    }
                });

                dispatcher.on('end', () => {
                    collector.stop();

                    queue[queueId].playing = false;
                    // Repeat if:
                    // only 1 song is left and repeatLast is enabled (default)
                    // repeat is enabled
                    (
                        (queue[queueId].songs.length > 1 && repeatLast) ||
                        repeat
                    ) && queue[queueId].songs.shift();

                    play(queue[queueId].songs[0]);
                });

                dispatcher.on('error', (err) => {
                    sendMessage(
                        msg,
                        msgFormats.playError
                    ).then(message => {
                        dispatcher.pause();
                        setTimeout(() => {
                            dispatcher.resume();
                        }, msgDeleteDelay);
                    });
                });

            }

        })(currentQueue.songs[0]);
    },
    'join': (msg) => {

        return new Promise((resolve, reject) => {
            const voiceChannel = getVoiceChannel(msg);

            if (!voiceChannel || voiceChannel.type !== 'voice') {

                msg.reply(msgFormats.noVoiceChannel);
                reject(msgFormats.noVoiceChannel);

            } else if (voiceChannel.connection) {

                resolve(voiceChannel.connection);

            } else {

                voiceChannel.join().then(
                    connection => resolve(connection)
                ).catch(
                    err => reject(err)
                );

            }
        });
    },
    'add': (msg, cmdArgs) => {

        let url = cmdArgs;

        if (url == '' || url === undefined) {

            sendMessage(msg, msgFormats.missingAddParam);

        } else if (!url.toLowerCase().startsWith('http')) {

            // If the suffix doesn't start with 'http',
            // assume it's a search.
            url = 'gvsearch1:' + url;

            sendMessage(
                msg,
                wrap('Searching ' + cmdArgs + ' ...'),
                true
            ).then(
                message => {

                    ytdl.getInfo(url, (err, info) => {
                        if (err) {

                            message.edit(msgFormats.searchVideoError);
                            message.delete(5000);

                        } else {

                            let queueId = getQueueId(msg);

                            addToQueue(
                                queueId,
                                {
                                    url: 'https://www.youtube.com/watch?v=' + info.id,
                                    title: info.title,
                                    requester: msg.author.username,
                                    requesterId: msg.author.id
                                }
                            );

                            message.edit(msgFormats.addedSong(info));
                            message.delete(5000);

                            !isPlaying(queueId) && autoPlay && commands.play(msg);
                        }
                    });
                }
            );

        } else if (url) {

            sendMessage(
                msg,
                wrap('Getting video info...'),
                true
            ).then(
                message => {

                    yt.getInfo(url, (err, info) => {
                        if (err) {

                            message.edit(msgFormats.invalidVideoLink(err));
                            message.delete(5000);

                        } else {

                            let queueId = getQueueId(msg);

                            addToQueue(
                                queueId,
                                {
                                    url: url,
                                    title: info.title,
                                    requester: msg.author.username,
                                    requesterId: msg.author.id
                                }
                            );

                            message.edit(msgFormats.addedSong(info));
                            message.delete(5000);

                            !isPlaying(queueId) && autoPlay && commands.play(msg);
                        }
                    });

                }
            );
        }
    },
    'queue': (msg) => {

        const queueId = getQueueId(msg);

        if (queue[queueId] === undefined || queue[queueId].songs.length === 0) {

            sendMessage(msg, msgFormats.addSongFirst);

        } else {

            let tosend = [];

            queue[queueId].songs.forEach((song, i) => {
                tosend.push(`${i+1}. ${song.title} - Requested by: ${song.requester}`);
            });

            sendMessage(
                msg,
                `__**${msg.guild.name}'s Music Queue:**__ **${tosend.length}** songs ${(tosend.length > 15 ? '*[Only next 15 shown]*' : '')}\n\`\`\`${tosend.slice(0,15).join('\n')}\`\`\``
            );
        }
    },
    'help': (cmdPrefix) => {

        return [
            '** MUSIC **',
            cmdPrefix + 'join : "Join Voice channel of msg sender"',
            cmdPrefix + 'add <keywords/url> : "Add a valid youtube link to the queue"',
            cmdPrefix + 'queue : "Shows the current queue, up to 15 songs shown."',
            cmdPrefix + 'play : "Play the music queue if already joined to a voice channel"',
            cmdPrefix + 'autoplay : "Toggle auto play when adding a song"',
            cmdPrefix + 'repeat : "Toggle repeat of current song"',
            cmdPrefix + 'repeatlast : "Toggle repeat of last song"',
            '',
            'the following commands only function while the play command is running:'.toUpperCase(),
            cmdPrefix + 'pause : "pauses the music"',
            cmdPrefix + 'resume : "resumes the music"',
            cmdPrefix + 'skip : "skips the playing song"',
            cmdPrefix + 'time : "Shows the playtime of the song."',
            'volume+(+++) : "increases volume by 2%/+"',
            'volume-(---) : "decreases volume by 2%/-"'
        ].join('\n');
    },
    'reboot': (msg) => {

        if (msg.author.id == tokens.adminID) {
            //Requires a node module like Forever to work.
            process.exit();
        }
    }
};

// Export Module
module.exports = commands;
<|endoftext|>


<commit_before>/* jshint asi: false */
steal("can/map/define", "can/test", function () {

	module('can/map/define');

	// remove, type, default
	test('basics set', function () {
		var Defined = can.Map.extend({
			define: {
				prop: {
					set: function (newVal) {
						return "foo" + newVal;
					}
				}
			}
		});

		var def = new Defined();
		def.attr("prop", "bar");

		equal(def.attr("prop"), "foobar", "setter works");

		Defined = can.Map.extend({
			define: {
				prop: {
					set: function (newVal, setter) {
						setter("foo" + newVal);
					}
				}
			}
		});

		def = new Defined();
		def.attr("prop", "bar");

		equal(def.attr("prop"), "foobar", "setter callback works");

	});

	test("basics remove", function () {
		var ViewModel = can.Map.extend({
			define: {
				makeId: {
					remove: function () {
						this.removeAttr("models");
					}
				},
				models: {
					remove: function () {
						this.removeAttr("modelId");
					}
				},
				modelId: {
					remove: function () {
						this.removeAttr("years");
					}
				},
				years: {
					remove: function () {
						this.removeAttr("year");
					}
				}
			}
		});

		var mmy = new ViewModel({
			makes: [
				{id: 1}
			],
			makeId: 1,
			models: [
				{id: 2}
			],
			modelId: 2,
			years: [2010],
			year: 2010
		});

		var events = ["year", "years", "modelId", "models", "makeId"],
			eventCount = 0,
			batchNum;

		mmy.bind("change", function (ev, attr) {
			if (batchNum === undefined) {
				batchNum = ev.batchNum;
			}
			equal(attr, events[eventCount++], "got correct attribute");
			ok(ev.batchNum && ev.batchNum === batchNum, "batched");
		});

		mmy.removeAttr("makeId");

	});

	test("basics get", function () {

		var Person = can.Map.extend({
			define: {
				fullName: {
					get: function () {
						return this.attr("first") + " " + this.attr("last");
					}
				}
			}
		});

		var p = new Person({first: "Justin", last: "Meyer"});

		equal(p.attr("fullName"), "Justin Meyer", "sync getter works");

		var Adder = can.Map.extend({
			define: {
				more: {
					get: function (curVal, setVal) {
						var num = this.attr("num");
						setTimeout(function () {
							setVal(num + 1);
						}, 10);
					}
				}
			}
		});

		var a = new Adder({num: 1}),
			callbackVals = [
				[2, undefined, function () {
					a.attr("num", 2);
				}],
				[3, 2, function () {
					start();
				}]
			],
			callbackCount = 0;

		a.bind("more", function (ev, newVal, oldVal) {
			var vals = callbackVals[callbackCount++];
			equal(newVal, vals[0], "newVal is correct");
			equal(a.attr("more"), vals[0], "attr value is correct");

			equal(oldVal, vals[1], "oldVal is correct");

			setTimeout(vals[2], 10);
		});

		stop();
	});

	test("basic type", function () {

		expect(6);

		var Typer = can.Map.extend({
			define: {
				arrayWithAddedItem: {
					type: function (value) {
						if (value && value.push) {
							value.push("item");
						}
						return value;
					}
				},
				listWithAddedItem: {
					type: function (value) {
						if (value && value.push) {
							value.push("item");
						}
						return value;
					},
					Type: can.List
				}
			}
		});

		var t = new Typer();
		deepEqual(can.Map.keys(t), [], "no keys");

		var array = [];
		t.attr("arrayWithAddedItem", array);

		deepEqual(array, ["item"], "updated array");
		equal(t.attr("arrayWithAddedItem"), array, "leave value as array");

		t.attr("listWithAddedItem", []);

		ok(t.attr("listWithAddedItem") instanceof can.List, "convert to can.List");
		equal(t.attr("listWithAddedItem").attr(0), "item", "has item in it");

		t.bind("change", function (ev, attr) {
			equal(attr, "listWithAddedItem.1", "got a bubbling event");
		});

		t.attr("listWithAddedItem").push("another item");

	});

	test("basic Type", function () {
		var Foo = function (name) {
			this.name = name;
		};
		Foo.prototype.getName = function () {
			return this.name;
		};

		var Typer = can.Map.extend({
			define: {
				foo: {
					Type: Foo
				}
			}
		});

		var t = new Typer({foo: "Justin"});
		equal(t.attr("foo").getName(), "Justin", "correctly created an instance");

		var brian = new Foo("brian");

		t.attr("foo", brian);

		equal(t.attr("foo"), brian, "same instances");

	});

	test("type converters", function () {

		var Typer = can.Map.extend({
			define: {
				date: {  type: 'date' },
				string: {type: 'string'},
				number: {  type: 'number' },
				'boolean': {  type: 'boolean' },
				leaveAlone: {  type: '*' }
			}
		});
		var obj = {};

		var t = new Typer({
			date: 1395896701516,
			string: 5,
			number: '5',
			'boolean': 'false',
			leaveAlone: obj
		});

		ok(t.attr("date") instanceof Date, "converted to date");

		equal(t.attr("string"), '5', "converted to string");

		equal(t.attr("number"), 5, "converted to number");

		equal(t.attr("boolean"), false, "converted to boolean");

		equal(t.attr("leaveAlone"), obj, "left as object");
		t.attr({
			'number': '15'
		});
		ok(t.attr("number") === 15, "converted to number");

	});

	test("basics value", function () {
		var Typer = can.Map.extend({
			define: {
				prop: { value: 'foo' }
			}
		});

		equal(new Typer().attr('prop'), "foo", "value is used as default value");

		var Typer2 = can.Map.extend({
			define: {
				prop: {
					value: function () {
						return [];
					},
					type: "*"
				}
			}
		});

		var t1 = new Typer2(),
			t2 = new Typer2();
		ok(t1.attr("prop") !== t2.attr("prop"), "different array instances");
		ok(can.isArray(t1.attr("prop")), "its an array");

	});

	test("basics Value", function () {

		var Typer = can.Map.extend({
			define: {
				prop: {
					Value: Array,
					type: "*"
				}
			}
		});

		var t1 = new Typer(),
			t2 = new Typer();
		ok(t1.attr("prop") !== t2.attr("prop"), "different array instances");
		ok(can.isArray(t1.attr("prop")), "its an array");

	});

	test("setter with no arguments and returns undefined does the default behavior, the setter is for side effects only", function () {

		var Typer = can.Map.extend({
			define: {
				prop: {
					set: function () {
						this.attr("foo", "bar");
					}
				}
			}
		});

		var t = new Typer();

		t.attr("prop", false);

		deepEqual(t.attr(), { foo: "bar", prop: false });

	});

	test("type happens before the set", function () {
		var MyMap = can.Map.extend({
			define: {
				prop: {
					type: "number",
					set: function (newValue) {
						equal(typeof newValue, "number", "got a number");
						return newValue + 1;
					}
				}
			}
		});

		var map = new MyMap();
		map.attr("prop", "5");

		equal(map.attr("prop"), 6, "number");
	});

	test("getter and setter work", function () {
		expect(5);
		var Paginate = can.Map.extend({
			define: {
				page: {
					set: function (newVal) {
						this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit'));
					},
					get: function () {
						return Math.floor(this.attr('offset') / this.attr('limit')) + 1;
					}
				}
			}
		});

		var p = new Paginate({limit: 10, offset: 20});

		equal(p.attr("page"), 3, "page get right");

		p.bind("page", function (ev, newValue, oldValue) {
			equal(newValue, 2, "got new value event");
			equal(oldValue, 3, "got old value event");
		});

		p.attr("page", 2);

		equal(p.attr("page"), 2, "page set right");

		equal(p.attr("offset"), 10, "page offset set");

	});

	test("getter with initial value", function(){

		var compute = can.compute(1);

		var Grabber = can.Map.extend({
			define: {
				vals: {
					type: "*",
					Value: Array,
					get: function(current, setVal){
						if(setVal){
							current.push( compute() );
						}
						return current;
					}
				}
			}
		});

		var g = new Grabber();
		// This assertion doesn't mean much.  It's mostly testing
		// that there were no errors.
		equal(g.attr("vals").length,0,"zero items in array" );

	});

	test("serialize basics", function(){
		var MyMap = can.Map.extend({
			define: {
				name: {
					serialize: function(){
						return;
					}
				},
				locations: {
					serialize: false
				},
				locationIds: {
					get: function(){
						var ids = [];
						this.attr('locations').each(function(location){
							ids.push(location.id);
						});
						return ids;
					},
					serialize: function(locationIds){
						return locationIds.join(',');
					}
				},
				bared: {
					get: function(){
						return this.attr("name")+"+bar";
					},
					serialize: true
				},
				ignored: {
					get: function(){
						return this.attr("name")+"+ignored";
					}
				}
			}
		});

		var map = new MyMap({name: "foo"});
		map.attr("locations", [{id: 1, name: "Chicago"}, {id: 2, name: "LA"}]);
		equal(map.attr("locationIds").length, 2, "get locationIds");
		equal(map.attr("locationIds")[0], 1, "get locationIds index 0");
		equal(map.attr("locations")[0].id, 1, "get locations index 0");

		var serialized = map.serialize();
		equal(serialized.locations, undefined, "locations doesn't serialize");
		equal(serialized.locationIds, "1,2", "locationIds serializes");
		equal(serialized.name, undefined, "name doesn't serialize");

		equal(serialized.bared, "foo+bar", "true adds computed props");
		equal(serialized.ignored, undefined, "computed props are not serialized by default");

	});

    test("serialize context", function(){
        var context
        var MyMap = can.Map.extend({
            define: {
                name: {
                    serialize: function(obj){
                        context = this
                        return obj;
                    }
                }

            }
        });

        var map = new MyMap();
        map.serialize()
        equal(context, map);
    });

    test("methods contexts", function(){
        var contexts = {}
        var MyMap = can.Map.extend({
            define: {
                name: {
                    value: 'John Galt',

                    get: function(obj){
                        contexts['get'] = this
                        return obj;
                    },

                    remove: function(obj){
                        contexts['remove'] = this
                        return obj;
                    },

                    set: function(obj){
                        contexts['set'] = this
                        return obj;
                    },

                    serialize: function(obj){
                        contexts['serialize'] = this
                        return obj;
                    },

                    type: function(val){
                        contexts['type'] = this
                        return val;
                    }
                }

            }
        });

        var map = new MyMap();
        map.serialize()
        map.removeAttr('name')

        equal(contexts['get'], map);
        equal(contexts['remove'], map);
        equal(contexts['set'], map);
        equal(contexts['serialize'], map);
        equal(contexts['type'], map);
    });

});<commit_msg>fixed jshint #959<commit_after>/* jshint asi: false */
steal("can/map/define", "can/test", function () {

	module('can/map/define');

	// remove, type, default
	test('basics set', function () {
		var Defined = can.Map.extend({
			define: {
				prop: {
					set: function (newVal) {
						return "foo" + newVal;
					}
				}
			}
		});

		var def = new Defined();
		def.attr("prop", "bar");

		equal(def.attr("prop"), "foobar", "setter works");

		Defined = can.Map.extend({
			define: {
				prop: {
					set: function (newVal, setter) {
						setter("foo" + newVal);
					}
				}
			}
		});

		def = new Defined();
		def.attr("prop", "bar");

		equal(def.attr("prop"), "foobar", "setter callback works");

	});

	test("basics remove", function () {
		var ViewModel = can.Map.extend({
			define: {
				makeId: {
					remove: function () {
						this.removeAttr("models");
					}
				},
				models: {
					remove: function () {
						this.removeAttr("modelId");
					}
				},
				modelId: {
					remove: function () {
						this.removeAttr("years");
					}
				},
				years: {
					remove: function () {
						this.removeAttr("year");
					}
				}
			}
		});

		var mmy = new ViewModel({
			makes: [
				{id: 1}
			],
			makeId: 1,
			models: [
				{id: 2}
			],
			modelId: 2,
			years: [2010],
			year: 2010
		});

		var events = ["year", "years", "modelId", "models", "makeId"],
			eventCount = 0,
			batchNum;

		mmy.bind("change", function (ev, attr) {
			if (batchNum === undefined) {
				batchNum = ev.batchNum;
			}
			equal(attr, events[eventCount++], "got correct attribute");
			ok(ev.batchNum && ev.batchNum === batchNum, "batched");
		});

		mmy.removeAttr("makeId");

	});

	test("basics get", function () {

		var Person = can.Map.extend({
			define: {
				fullName: {
					get: function () {
						return this.attr("first") + " " + this.attr("last");
					}
				}
			}
		});

		var p = new Person({first: "Justin", last: "Meyer"});

		equal(p.attr("fullName"), "Justin Meyer", "sync getter works");

		var Adder = can.Map.extend({
			define: {
				more: {
					get: function (curVal, setVal) {
						var num = this.attr("num");
						setTimeout(function () {
							setVal(num + 1);
						}, 10);
					}
				}
			}
		});

		var a = new Adder({num: 1}),
			callbackVals = [
				[2, undefined, function () {
					a.attr("num", 2);
				}],
				[3, 2, function () {
					start();
				}]
			],
			callbackCount = 0;

		a.bind("more", function (ev, newVal, oldVal) {
			var vals = callbackVals[callbackCount++];
			equal(newVal, vals[0], "newVal is correct");
			equal(a.attr("more"), vals[0], "attr value is correct");

			equal(oldVal, vals[1], "oldVal is correct");

			setTimeout(vals[2], 10);
		});

		stop();
	});

	test("basic type", function () {

		expect(6);

		var Typer = can.Map.extend({
			define: {
				arrayWithAddedItem: {
					type: function (value) {
						if (value && value.push) {
							value.push("item");
						}
						return value;
					}
				},
				listWithAddedItem: {
					type: function (value) {
						if (value && value.push) {
							value.push("item");
						}
						return value;
					},
					Type: can.List
				}
			}
		});

		var t = new Typer();
		deepEqual(can.Map.keys(t), [], "no keys");

		var array = [];
		t.attr("arrayWithAddedItem", array);

		deepEqual(array, ["item"], "updated array");
		equal(t.attr("arrayWithAddedItem"), array, "leave value as array");

		t.attr("listWithAddedItem", []);

		ok(t.attr("listWithAddedItem") instanceof can.List, "convert to can.List");
		equal(t.attr("listWithAddedItem").attr(0), "item", "has item in it");

		t.bind("change", function (ev, attr) {
			equal(attr, "listWithAddedItem.1", "got a bubbling event");
		});

		t.attr("listWithAddedItem").push("another item");

	});

	test("basic Type", function () {
		var Foo = function (name) {
			this.name = name;
		};
		Foo.prototype.getName = function () {
			return this.name;
		};

		var Typer = can.Map.extend({
			define: {
				foo: {
					Type: Foo
				}
			}
		});

		var t = new Typer({foo: "Justin"});
		equal(t.attr("foo").getName(), "Justin", "correctly created an instance");

		var brian = new Foo("brian");

		t.attr("foo", brian);

		equal(t.attr("foo"), brian, "same instances");

	});

	test("type converters", function () {

		var Typer = can.Map.extend({
			define: {
				date: {  type: 'date' },
				string: {type: 'string'},
				number: {  type: 'number' },
				'boolean': {  type: 'boolean' },
				leaveAlone: {  type: '*' }
			}
		});
		var obj = {};

		var t = new Typer({
			date: 1395896701516,
			string: 5,
			number: '5',
			'boolean': 'false',
			leaveAlone: obj
		});

		ok(t.attr("date") instanceof Date, "converted to date");

		equal(t.attr("string"), '5', "converted to string");

		equal(t.attr("number"), 5, "converted to number");

		equal(t.attr("boolean"), false, "converted to boolean");

		equal(t.attr("leaveAlone"), obj, "left as object");
		t.attr({
			'number': '15'
		});
		ok(t.attr("number") === 15, "converted to number");

	});

	test("basics value", function () {
		var Typer = can.Map.extend({
			define: {
				prop: { value: 'foo' }
			}
		});

		equal(new Typer().attr('prop'), "foo", "value is used as default value");

		var Typer2 = can.Map.extend({
			define: {
				prop: {
					value: function () {
						return [];
					},
					type: "*"
				}
			}
		});

		var t1 = new Typer2(),
			t2 = new Typer2();
		ok(t1.attr("prop") !== t2.attr("prop"), "different array instances");
		ok(can.isArray(t1.attr("prop")), "its an array");

	});

	test("basics Value", function () {

		var Typer = can.Map.extend({
			define: {
				prop: {
					Value: Array,
					type: "*"
				}
			}
		});

		var t1 = new Typer(),
			t2 = new Typer();
		ok(t1.attr("prop") !== t2.attr("prop"), "different array instances");
		ok(can.isArray(t1.attr("prop")), "its an array");

	});

	test("setter with no arguments and returns undefined does the default behavior, the setter is for side effects only", function () {

		var Typer = can.Map.extend({
			define: {
				prop: {
					set: function () {
						this.attr("foo", "bar");
					}
				}
			}
		});

		var t = new Typer();

		t.attr("prop", false);

		deepEqual(t.attr(), { foo: "bar", prop: false });

	});

	test("type happens before the set", function () {
		var MyMap = can.Map.extend({
			define: {
				prop: {
					type: "number",
					set: function (newValue) {
						equal(typeof newValue, "number", "got a number");
						return newValue + 1;
					}
				}
			}
		});

		var map = new MyMap();
		map.attr("prop", "5");

		equal(map.attr("prop"), 6, "number");
	});

	test("getter and setter work", function () {
		expect(5);
		var Paginate = can.Map.extend({
			define: {
				page: {
					set: function (newVal) {
						this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit'));
					},
					get: function () {
						return Math.floor(this.attr('offset') / this.attr('limit')) + 1;
					}
				}
			}
		});

		var p = new Paginate({limit: 10, offset: 20});

		equal(p.attr("page"), 3, "page get right");

		p.bind("page", function (ev, newValue, oldValue) {
			equal(newValue, 2, "got new value event");
			equal(oldValue, 3, "got old value event");
		});

		p.attr("page", 2);

		equal(p.attr("page"), 2, "page set right");

		equal(p.attr("offset"), 10, "page offset set");

	});

	test("getter with initial value", function(){

		var compute = can.compute(1);

		var Grabber = can.Map.extend({
			define: {
				vals: {
					type: "*",
					Value: Array,
					get: function(current, setVal){
						if(setVal){
							current.push( compute() );
						}
						return current;
					}
				}
			}
		});

		var g = new Grabber();
		// This assertion doesn't mean much.  It's mostly testing
		// that there were no errors.
		equal(g.attr("vals").length,0,"zero items in array" );

	});

	test("serialize basics", function(){
		var MyMap = can.Map.extend({
			define: {
				name: {
					serialize: function(){
						return;
					}
				},
				locations: {
					serialize: false
				},
				locationIds: {
					get: function(){
						var ids = [];
						this.attr('locations').each(function(location){
							ids.push(location.id);
						});
						return ids;
					},
					serialize: function(locationIds){
						return locationIds.join(',');
					}
				},
				bared: {
					get: function(){
						return this.attr("name")+"+bar";
					},
					serialize: true
				},
				ignored: {
					get: function(){
						return this.attr("name")+"+ignored";
					}
				}
			}
		});

		var map = new MyMap({name: "foo"});
		map.attr("locations", [{id: 1, name: "Chicago"}, {id: 2, name: "LA"}]);
		equal(map.attr("locationIds").length, 2, "get locationIds");
		equal(map.attr("locationIds")[0], 1, "get locationIds index 0");
		equal(map.attr("locations")[0].id, 1, "get locations index 0");

		var serialized = map.serialize();
		equal(serialized.locations, undefined, "locations doesn't serialize");
		equal(serialized.locationIds, "1,2", "locationIds serializes");
		equal(serialized.name, undefined, "name doesn't serialize");

		equal(serialized.bared, "foo+bar", "true adds computed props");
		equal(serialized.ignored, undefined, "computed props are not serialized by default");

	});

	test("serialize context", function(){
		var context, serializeContext;
		var MyMap = can.Map.extend({
			define: {
				name: {
					serialize: function(obj){
						context = this;
						return obj;
					}
				}
			},
			serialize: function(){
				serializeContext = this;
				can.Map.prototype.serialize.apply(this, arguments);

			}
		});

		var map = new MyMap();
		map.serialize();
		equal(context, map);
		equal(serializeContext, map);
	});

	test("methods contexts", function(){
		var contexts = {};
		var MyMap = can.Map.extend({
			define: {
				name: {
					value: 'John Galt',

					get: function(obj){
						contexts.get = this;
						return obj;
					},

					remove: function(obj){
						contexts.remove = this;
						return obj;
					},

					set: function(obj){
						contexts.set = this;
						return obj;
					},

					serialize: function(obj){
						contexts.serialize = this;
						return obj;
					},

					type: function(val){
						contexts.type = this;
						return val;
					}
				}

			}
		});

		var map = new MyMap();
		map.serialize();
		map.removeAttr('name');

		equal(contexts.get, map);
		equal(contexts.remove, map);
		equal(contexts.set, map);
		equal(contexts.serialize, map);
		equal(contexts.type, map);
	});

});<|endoftext|>


<commit_before>/*
   Licensed under the Apache License v2.0.

   A copy of which can be found at the root of this distrubution in
   the file LICENSE-2.0 or at http://www.apache.org/licenses/LICENSE-2.0
*/

(function (window) {

  var jetzt = window.jetzt;
  var H = jetzt.helpers;
  var config = jetzt.config;

  // splitting long words. Used by the Instructionator.

  function wordShouldBeSplitUp(word) {
    return word.length > 13 || word.length > 9 && word.indexOf("-") > -1;
  }

  function _maybeSplitLongWord (word) {
    if (wordShouldBeSplitUp(word)) {
      var result = [];

      var dashIdx = word.indexOf("-");
      if (dashIdx > 0 && dashIdx < word.length - 1) {
        result.push(word.substr(0, dashIdx));
        result.push(word.substr(dashIdx + 1));
        return H.flatten(result.map(_maybeSplitLongWord));
      } else {
        var partitions = Math.ceil(word.length / 8);
        var partitionLength = Math.ceil(word.length / partitions);
        while (partitions--) {
          result.push(word.substr(0, partitionLength));
          word = word.substr(partitionLength);
        }
        return result;
      }
    } else {
      return [word];
    }
  }

  // split a long word into sensible sections
  function splitLongWord (word) {
    var result = _maybeSplitLongWord(word);
    if (result.length > 1) {
      for (var i=0; i<result.length-1; i++) {
        result[i] += "-";
      }
    }
    return result;
  }

  /**
   * Helper class for generating jetzt instructions.
   * Very subject to change.
   */
  function Instructionator () {
    // state
    var instructions = []
      , modifier = "normal"
      , wraps = []
      , spacerInstruction = null
      , done = false;

    // add a modifier to the next token
    this.modNext = function (mod) {
      modifier = config.maxModifier(modifier, mod);
    };

    // add a modifier to the previous token
    this.modPrev = function (mod) {
      if (instructions.length > 0) {
        var current = instructions[instructions.length-1].modifier;
        instructions[instructions.length-1].modifier = config.maxModifier(current, mod);
      }
    };

    // start a wrap on the next token
    this.pushWrap = function (wrap) {
      wraps.push(wrap);
    };

    // stop the specified wrap before the next token.
    // Pops off any wraps in the way
    this.popWrap = function (wrap) {
      var idx = wraps.lastIndexOf(wrap);
      if (idx > -1)
        wraps.splice(wraps.lastIndexOf(wrap), wraps.length);
    };

    // pop all wraps
    this.clearWrap = function (wrap) {
      wraps = [];
    };

    var _addWraps = function (instr) {
      instr.leftWrap = wraps.map(function (w) { return w.left; }).join("");
      instr.rightWrap = wraps.map(function (w) { return w.right; }).reverse().join("");
      return instr;
    }

    // put a spacer before the next token
    this.spacer = function () {
      if (spacerInstruction) {
        spacerInstruction.modifier = "long_space";
      } else {
        spacerInstruction = _addWraps({
          token: "   ",
          modifier: "short_space"
        });
      }
    };

    var _emit = function (token) {
      if (spacerInstruction) {
        instructions.push(spacerInstruction);
      }

      instructions.push(_addWraps({
        token: token,
        modifier: modifier
      }));

      modifier = "normal";
      spacerInstruction = null;
    };

    // add the token
    this.token = function (tkn) {
      if (wordShouldBeSplitUp(tkn)) {
        splitLongWord(tkn).forEach(_emit);
      } else {
        _emit(tkn);
      }
    };

    // amend the last token, cb(old_tkn) returns new_tkn
    this.amend = function (cb) {
      if (instructions.length > 0) {
        var inst = instructions[instructions.length-1];
        inst.token = cb(inst.token);
      }
    };

    this.getInstructions = function () {
      return instructions;
    };
  }

  var wraps = {
    guillemot: {left: "«", right: "»"},
    double_quote: {left: "“", right: "”"},
    parens: {left: "(", right: ")"},
    heading1: {left: "H1", right: ""}
  };

  function parseDom(topnode,$instructionator) {
    var inst =  ($instructionator) ? $instructionator :  new Instructionator();
    var node=null;

    for(var i=0;i<topnode.childNodes.length;i++) {
        node=topnode.childNodes[i];

        //TODO add modifiers, e.g. based on node.nodeName
        switch(node.nodeName) {
          case "H1":
            //commented out until view for headings is implemented
            //inst.pushWrap(wraps.heading1);
            inst.modNext("start_paragraph");
            parseDom(node,inst);
            inst.spacer();
            inst.clearWrap();
            inst.modPrev("end_paragraph");
            break;
          case "SCRIPT":
            break;
          case "#text":
            if(node.textContent.trim().length > 0) parseText(node.textContent.trim(),inst);
            break;
          case "P":
            inst.clearWrap();
            inst.modNext("start_paragraph");
            parseDom(node, inst)
            inst.modPrev("end_paragraph");
            inst.clearWrap();
            break;
          case "#comment":
            break;
          default:
            parseDom(node,inst);
        }
    }

    return inst.getInstructions();
  }

  // convert raw text into instructions
  function parseText (text,$instructionator) {
                        // long dashes ↓
    var tokens = text.match(/["«»“”\(\)\/–—]|--+|\n+|[^\s"“«»”\(\)\/–—]+/g);

    var $ = ($instructionator) ? $instructionator :  new Instructionator();

    // doesn't handle nested double quotes, but that junk is *rare*;
    var double_quote_state = false;

    for (var i=0; i<tokens.length; i++) {
      var tkn = tokens[i];

      switch (tkn) {
        case "“":
          $.spacer();
          $.pushWrap(wraps.double_quote);
          $.modNext("start_clause");
          break;
        case "”":
          $.popWrap(wraps.double_quote);
          $.modPrev("end_clause");
          $.spacer();
          break;
        case "«":
          $.spacer();
          $.pushWrap(wraps.guillemot);
          $.modNext("start_clause");
          break;
        case "»":
          $.popWrap(wraps.guillemot);
          $.modPrev("end_clause");
          $.spacer();
          break;
        case "\"":
          if (double_quote_state) {
            $.popWrap(wraps.double_quote)
            $.spacer();
            $.modNext("start_clause");
          } else {
            $.spacer();
            $.pushWrap(wraps.double_quote);
            $.modPrev("end_clause");
          }
          double_quote_state = !double_quote_state;
          break;
        case "(":
          $.spacer();
          $.pushWrap(wraps.parens);
          $.modNext("start_clause");
          break;
        case ")":
          $.popWrap(wraps.parens);
          $.modPrev("end_clause");
          $.spacer();
          break;
        default:
          if (tkn.match(/^(\/|--+|—|–)$/)) {
            $.modNext("start_clause");
            $.token(tkn);
            $.modNext("start_clause");
          } else if (tkn.match(/^[.?!…]+$/)) {
            $.modPrev("end_sentence");
            $.amend(function (old) {return old + tkn;});
            $.modNext("start_sentence");
          } else if (tkn.match(/[.?!…]+$/)) {
            $.modNext("end_sentence");
            $.token(tkn);
            $.modNext("start_sentence");
          } else if (tkn.match(/^[,;:]$/)) {
            $.modNext("end_clause");
            $.amend(function (old) {return old + tkn;});
            $.modNext("start_clause");
          } else if (tkn.match(/[,;:]$/)) {
            $.modNext("end_clause");
            $.token(tkn);
            $.modNext("start_clause");
          } else if (tkn.match(/\n+/)) {
            if (tkn.length > 1
                // hack for linefeed-based word wrapping. Ugly. So ugly.
                || (i > 0 && tokens[i - 1].match(/[.?!…'"”]+$/))) {

              $.clearWrap();
              $.modPrev("end_paragraph");
              $.spacer();
              $.modNext("start_paragraph");
              double_quote_state = false;
            }
          } else if (tkn.match(/^".+$/)) {
            double_quote_state = true;
            $.modNext("start_clause");
            $.token(tkn.substr(1));
          } else {
            $.token(tkn);
          }
      }
    }

    return $.getInstructions();
  }

  function parseStuff (parser, content) {
    var instr = new Instructionator();
    if (H.realTypeOf(content) === "Array") {
      content.forEach(function (item) {
        instr.modNext("start_paragraph");
        parser(item, instr);
        instr.clearWrap();
        instr.modPrev("end_paragraph");
      });
    } else {
      parser(content, instr);
    }

    return instr.getInstructions();
  }

  jetzt.parse = {
    /**
     * Read the given string, or array of strings.
     */
    string: function (str) {
      return parseStuff(parseText, str);
    },

    /**
     * Read the given DOM element, or array of DOM elements.
     */
    dom: function (dom) {
      return parseStuff(parseDom, dom);
    }
  };

})(this);

<commit_msg>concatenate inline nodes' text in parser<commit_after>/*
   Licensed under the Apache License v2.0.

   A copy of which can be found at the root of this distrubution in
   the file LICENSE-2.0 or at http://www.apache.org/licenses/LICENSE-2.0
*/

(function (window) {

  var jetzt = window.jetzt;
  var H = jetzt.helpers;
  var config = jetzt.config;

  // splitting long words. Used by the Instructionator.

  function wordShouldBeSplitUp(word) {
    return word.length > 13 || word.length > 9 && word.indexOf("-") > -1;
  }

  function _maybeSplitLongWord (word) {
    if (wordShouldBeSplitUp(word)) {
      var result = [];

      var dashIdx = word.indexOf("-");
      if (dashIdx > 0 && dashIdx < word.length - 1) {
        result.push(word.substr(0, dashIdx));
        result.push(word.substr(dashIdx + 1));
        return H.flatten(result.map(_maybeSplitLongWord));
      } else {
        var partitions = Math.ceil(word.length / 8);
        var partitionLength = Math.ceil(word.length / partitions);
        while (partitions--) {
          result.push(word.substr(0, partitionLength));
          word = word.substr(partitionLength);
        }
        return result;
      }
    } else {
      return [word];
    }
  }

  // split a long word into sensible sections
  function splitLongWord (word) {
    var result = _maybeSplitLongWord(word);
    if (result.length > 1) {
      for (var i=0; i<result.length-1; i++) {
        result[i] += "-";
      }
    }
    return result;
  }

  /**
   * Helper class for generating jetzt instructions.
   * Very subject to change.
   */
  function Instructionator () {
    // state
    var instructions = []
      , modifier = "normal"
      , wraps = []
      , spacerInstruction = null
      , done = false;

    // add a modifier to the next token
    this.modNext = function (mod) {
      modifier = config.maxModifier(modifier, mod);
    };

    // add a modifier to the previous token
    this.modPrev = function (mod) {
      if (instructions.length > 0) {
        var current = instructions[instructions.length-1].modifier;
        instructions[instructions.length-1].modifier = config.maxModifier(current, mod);
      }
    };

    // start a wrap on the next token
    this.pushWrap = function (wrap) {
      wraps.push(wrap);
    };

    // stop the specified wrap before the next token.
    // Pops off any wraps in the way
    this.popWrap = function (wrap) {
      var idx = wraps.lastIndexOf(wrap);
      if (idx > -1)
        wraps.splice(wraps.lastIndexOf(wrap), wraps.length);
    };

    // pop all wraps
    this.clearWrap = function (wrap) {
      wraps = [];
    };

    var _addWraps = function (instr) {
      instr.leftWrap = wraps.map(function (w) { return w.left; }).join("");
      instr.rightWrap = wraps.map(function (w) { return w.right; }).reverse().join("");
      return instr;
    }

    // put a spacer before the next token
    this.spacer = function () {
      if (spacerInstruction) {
        spacerInstruction.modifier = "long_space";
      } else {
        spacerInstruction = _addWraps({
          token: "   ",
          modifier: "short_space"
        });
      }
    };

    var _emit = function (token) {
      if (spacerInstruction) {
        instructions.push(spacerInstruction);
      }

      instructions.push(_addWraps({
        token: token,
        modifier: modifier
      }));

      modifier = "normal";
      spacerInstruction = null;
    };

    // add the token
    this.token = function (tkn) {
      if (wordShouldBeSplitUp(tkn)) {
        splitLongWord(tkn).forEach(_emit);
      } else {
        _emit(tkn);
      }
    };

    this.getInstructions = function () {
      return instructions;
    };
  }

  var wraps = {
    guillemot: {left: "«", right: "»"},
    double_quote: {left: "“", right: "”"},
    parens: {left: "(", right: ")"},
    heading1: {left: "H1", right: ""}
  };

  function parseDom(topnode,$instructionator) {
    var inst =  ($instructionator) ? $instructionator :  new Instructionator();

    var all_inline = [].reduce.call(
      topnode.childNodes,
      function(val, node) {
        return val && (node.nodeType !== 1 ||
          !!window.getComputedStyle(node).display.match(/^inline/));
      },
      true
    );
    if (all_inline) {
      var text = topnode.textContent.trim();
      if (text.length > 0) parseText(text, inst);
      return inst.getInstructions();
    }

    var node=null;
    for(var i=0;i<topnode.childNodes.length;i++) {
        node=topnode.childNodes[i];

        //TODO add modifiers, e.g. based on node.nodeName
        switch(node.nodeName) {
          case "H1":
            //commented out until view for headings is implemented
            //inst.pushWrap(wraps.heading1);
            inst.modNext("start_paragraph");
            parseDom(node,inst);
            inst.spacer();
            inst.clearWrap();
            inst.modPrev("end_paragraph");
            break;
          case "SCRIPT":
            break;
          case "#text":
            if(node.textContent.trim().length > 0) parseText(node.textContent.trim(),inst);
            break;
          case "P":
            inst.clearWrap();
            inst.modNext("start_paragraph");
            parseDom(node, inst)
            inst.modPrev("end_paragraph");
            inst.clearWrap();
            break;
          case "#comment":
            break;
          default:
            parseDom(node,inst);
        }
    }

    return inst.getInstructions();
  }

  // convert raw text into instructions
  function parseText (text,$instructionator) {
                        // long dashes ↓
    var tokens = text.match(/["«»“”\(\)\/–—]|--+|\n+|[^\s"“«»”\(\)\/–—]+/g);

    var $ = ($instructionator) ? $instructionator :  new Instructionator();

    // doesn't handle nested double quotes, but that junk is *rare*;
    var double_quote_state = false;

    for (var i=0; i<tokens.length; i++) {
      var tkn = tokens[i];

      switch (tkn) {
        case "“":
          $.spacer();
          $.pushWrap(wraps.double_quote);
          $.modNext("start_clause");
          break;
        case "”":
          $.popWrap(wraps.double_quote);
          $.modPrev("end_clause");
          $.spacer();
          break;
        case "«":
          $.spacer();
          $.pushWrap(wraps.guillemot);
          $.modNext("start_clause");
          break;
        case "»":
          $.popWrap(wraps.guillemot);
          $.modPrev("end_clause");
          $.spacer();
          break;
        case "\"":
          if (double_quote_state) {
            $.popWrap(wraps.double_quote)
            $.spacer();
            $.modNext("start_clause");
          } else {
            $.spacer();
            $.pushWrap(wraps.double_quote);
            $.modPrev("end_clause");
          }
          double_quote_state = !double_quote_state;
          break;
        case "(":
          $.spacer();
          $.pushWrap(wraps.parens);
          $.modNext("start_clause");
          break;
        case ")":
          $.popWrap(wraps.parens);
          $.modPrev("end_clause");
          $.spacer();
          break;
        default:
          if (tkn.match(/^(\/|--+|—|–)$/)) {
            $.modNext("start_clause");
            $.token(tkn);
            $.modNext("start_clause");
          } else if (tkn.match(/[.?!…]+$/)) {
            $.modNext("end_sentence");
            $.token(tkn);
            $.modNext("start_sentence");
          } else if (tkn.match(/[,;:]$/)) {
            $.modNext("end_clause");
            $.token(tkn);
            $.modNext("start_clause");
          } else if (tkn.match(/\n+/)) {
            if (tkn.length > 1
                // hack for linefeed-based word wrapping. Ugly. So ugly.
                || (i > 0 && tokens[i - 1].match(/[.?!…'"”]+$/))) {

              $.clearWrap();
              $.modPrev("end_paragraph");
              $.spacer();
              $.modNext("start_paragraph");
              double_quote_state = false;
            }
          } else if (tkn.match(/^".+$/)) {
            double_quote_state = true;
            $.modNext("start_clause");
            $.token(tkn.substr(1));
          } else {
            $.token(tkn);
          }
      }
    }

    return $.getInstructions();
  }

  function parseStuff (parser, content) {
    var instr = new Instructionator();
    if (H.realTypeOf(content) === "Array") {
      content.forEach(function (item) {
        instr.modNext("start_paragraph");
        parser(item, instr);
        instr.clearWrap();
        instr.modPrev("end_paragraph");
      });
    } else {
      parser(content, instr);
    }

    return instr.getInstructions();
  }

  jetzt.parse = {
    /**
     * Read the given string, or array of strings.
     */
    string: function (str) {
      return parseStuff(parseText, str);
    },

    /**
     * Read the given DOM element, or array of DOM elements.
     */
    dom: function (dom) {
      return parseStuff(parseDom, dom);
    }
  };

})(this);

<|endoftext|>


<commit_before>/* jslint node: true, esnext: true */

'use strict';

const path = require('path'),
	process = require('process'),
	ssh2 = require('ssh2'),
	mat = require('model-attributes'),
	endpoint = require('kronos-endpoint'),
	service = require('kronos-service');

/**
 *
 */
class ServiceAdmin extends service.Service {
	static get name() {
		return 'admin';
	}
	static get configurationAttributes() {
		return Object.assign(mat.createAttributes({
			ssh: {
				description: 'ssh admin interface',
				attributes: {
					port: {
						description: 'listen port',
						needsRestart: true,
						type: 'integer'
					},
					hostKeys: {
						description: 'server ssh private host key(s)',
						needsRestart: true
					}
				}
			}
		}), service.Service.configurationAttributes);
	}

	constructor(config, owner) {
		super(config, owner);

		this.addEndpoint(new endpoint.ReceiveEndpoint('software', this)).receive = request => Promise.resolve({
			versions: process.versions,
			platform: process.platform
		});
	}

	get autostart() {
		return true;
	}

	_start() {
		return super._start().then(() => {
			return service.ServiceConsumerMixin.defineServiceConsumerProperties(this, {
				listener: {
					name: 'koa-admin',
					type: 'koa'
				}
			}, this.owner, true).then(() => {
				if (this.config.ssh) {
					this.sshServer = require('./ssh')(this, this.config.ssh);
				}

				return this.owner.loadFlowFromFile(path.join(__dirname, '..', 'admin.flow'));
			});
		});
	}

	_stop() {
		if (this.sshServer) {
			return super._stop().then(new Promise((fullfill, reject) => {
				this.sshServer.close((err) => {
					if (err) {
						reject(err);
						return;
					}
					delete this.sshServer;
					fullfill();
				});
			}));
		}

		return super._stop();
	}
}

module.exports.registerWithManager = manager =>
	manager.registerServiceFactory(ServiceAdmin).then(admin =>
		manager.declareService({
			type: admin.name,
			name: admin.name
		}));
<commit_msg>fix(config): declare all attribute types<commit_after>/* jslint node: true, esnext: true */

'use strict';

const path = require('path'),
	process = require('process'),
	ssh2 = require('ssh2'),
	mat = require('model-attributes'),
	endpoint = require('kronos-endpoint'),
	service = require('kronos-service');

/**
 *
 */
class ServiceAdmin extends service.Service {
	static get name() {
		return 'admin';
	}
	static get configurationAttributes() {
		return Object.assign(mat.createAttributes({
			ssh: {
				description: 'ssh admin interface',
				attributes: {
					port: {
						description: 'listen port',
						needsRestart: true,
						type: 'ip-port'
					},
					hostKeys: {
						description: 'server ssh private host key(s)',
						type: 'blob',
						needsRestart: true
					}
				}
			}
		}), service.Service.configurationAttributes);
	}

	constructor(config, owner) {
		super(config, owner);

		this.addEndpoint(new endpoint.ReceiveEndpoint('software', this)).receive = request => Promise.resolve({
			versions: process.versions,
			platform: process.platform
		});
	}

	get autostart() {
		return true;
	}

	_start() {
		return super._start().then(() => {
			return service.ServiceConsumerMixin.defineServiceConsumerProperties(this, {
				listener: {
					name: 'koa-admin',
					type: 'koa'
				}
			}, this.owner, true).then(() => {
				if (this.config.ssh) {
					this.sshServer = require('./ssh')(this, this.config.ssh);
				}

				return this.owner.loadFlowFromFile(path.join(__dirname, '..', 'admin.flow'));
			});
		});
	}

	_stop() {
		if (this.sshServer) {
			return super._stop().then(new Promise((fullfill, reject) => {
				this.sshServer.close((err) => {
					if (err) {
						reject(err);
						return;
					}
					delete this.sshServer;
					fullfill();
				});
			}));
		}

		return super._stop();
	}
}

module.exports.registerWithManager = manager =>
	manager.registerServiceFactory(ServiceAdmin).then(admin =>
		manager.declareService({
			type: admin.name,
			name: admin.name
		}));
<|endoftext|>


<commit_before>#!/usr/bin/env node

var express = require('express');
var cors = require('cors');
var fs = require('fs');
const path = require('path');
var app = express();
app.use(express.static(path.join(__dirname, 'quick-bench-front-end', 'build')));
var exec = require('child_process').exec;
var sha1 = require('sha1');
var bodyParser = require('body-parser');
var multer = require('multer');

var upload = multer();

const MAX_CODE_LENGTH = 20000;
const WRITE_PATH = '/data';
const PREFIX_CODE_1 = `#include <benchmark/benchmark_api.h>
`;
const SUFFIX_CODE_1 = `

static void Noop(benchmark::State& state) {
  while (state.KeepRunning());
}
BENCHMARK(Noop);
BENCHMARK_MAIN()`;
const PREFIX_CODE_2 = `#include <benchmark/benchmark.h>
`;
const SUFFIX_CODE_2 = `

static void Noop(benchmark::State& state) {
  for (auto _ : state) benchmark::DoNotOptimize(0);
}
BENCHMARK(Noop);
BENCHMARK_MAIN()`;
const SUFFIX_CODE_3 = `

static void Noop(benchmark::State& state) {
  for (auto _ : state) benchmark::DoNotOptimize(0);
}
BENCHMARK(Noop);
BENCHMARK_MAIN();`;

const NOOP_BUILD = 'int main() {return 0;}';

app.use(bodyParser.json());
app.use(cors());

function write(fileName, code) {
    return new Promise((resolve, reject) => {
        fs.writeFile(fileName, code, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

function read(fileName, acceptMissing) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, 'utf8', (err, data) => {
            if (err) {
                if (acceptMissing && err.code === 'ENOENT') {
                    resolve(null);
                } else {
                    reject(err);
                }
            } else {
                resolve(data);
            }
        });
    });
}

function runDockerCommand(fileName, request, force) {
    return './run-docker-builder ' + fileName + ' ' + request.compiler + ' ' + request.optim + ' ' + request.cppVersion + ' ' + (request.isAnnotated || false) + ' ' + (force || false) + ' ' + (request.lib || 'gnu');
}

function optionsToString(request, protocolVersion) {
    let options = {
        "protocolVersion": protocolVersion,
        "compiler": request.compiler,
        "optim": request.optim,
        "cppVersion": request.cppVersion,
        "isAnnotated": request.isAnnotated,
        "lib": request.lib
    };
    return JSON.stringify(options);
}

function execute(fileName, request, protocolVersion, force) {
    let options = {
        timeout: 60000,
        killSignal: 'SIGKILL'
    };
    return new Promise((resolve, reject) => {
        console.time(fileName);
        return exec(runDockerCommand(fileName, request, force), options, function (err, stdout, stderr) {
            if (err) {
                console.timeEnd(fileName);
                console.log('Bench failed ' + fileName);
                exec("./kill-docker " + fileName);
                reject("\u001b[0m\u001b[0;1;31mError or timeout\u001b[0m\u001b[1m
" + stdout + "
" + stderr);
            } else {
                console.timeEnd(fileName);
                console.log('Bench done ' + fileName + (stderr.indexOf('cached results') > -1 ? ' from cache' : ''));
                resolve({
                    res: fs.readFileSync(fileName + '.out'),
                    stdout: stderr,
                    id: encodeName(makeCodeName(request, protocolVersion))
                });
            }
        });
    });
}

function groupResults(results, id) {
    let code = results[0];
    let options = results[1];
    let graph = results[2];
    return { code: code, options: JSON.parse(options), graph: graph, id: id };
}

function makeCodeName(tab, protocolVersion) {
    return sha1(tab.code + tab.compiler + tab.optim + tab.cppVersion + tab.lib + protocolVersion);
}
function makeName(request) {
    return sha1(request.tabs.reduce(u => makeCodeName(u, request.protocolVersion)) + request.protocolVersion);
}

function wrapCode(inputCode) {
    return PREFIX_CODE_2 + inputCode + SUFFIX_CODE_3;
}

function unwrapCode(inputCode) {
    if (inputCode.startsWith(PREFIX_CODE_1)) {
        inputCode = inputCode.slice(PREFIX_CODE_1.length);
    }
    if (inputCode.endsWith(SUFFIX_CODE_1)) {
        inputCode = inputCode.slice(0, -SUFFIX_CODE_1.length);
    }
    if (inputCode.startsWith(PREFIX_CODE_2)) {
        inputCode = inputCode.slice(PREFIX_CODE_2.length);
    }
    if (inputCode.endsWith(SUFFIX_CODE_2)) {
        inputCode = inputCode.slice(0, -SUFFIX_CODE_2.length);
    }
    if (inputCode.endsWith(SUFFIX_CODE_3)) {
        inputCode = inputCode.slice(0, -SUFFIX_CODE_3.length);
    }
    return inputCode;
}

function encodeName(id) {
    let short = new Buffer(id, 'hex').toString('base64');
    short = short.replace(new RegExp('/', 'g'), '-').replace(new RegExp('\\+', 'g'), '_');
    return short.slice(0, -1);
}

function decodeName(short) {
    short = short.replace(new RegExp('\\-', 'g'), '/').replace(new RegExp('_', 'g'), '+ ') + '=';
    return new Buffer(short, 'base64').toString('hex');
}

function getFunctions(code) {
    RE = /BENCHMARK\s*\(\s*([A-Za-z0-9_]+)\s*\)/g;
    let content='';
    let res;
    while ((res = RE.exec(code)) !== null) {
        content+= res[1] + '\n';
    }
    return content;
}

function filename(name) {
    let dir = WRITE_PATH + '/' + name.substr(0, 2);
    return  dir + '/' + name;
}

async function benchmarkOneBuild(tab, protocolVersion, force) {
    try {
	if (tab.code.length > MAX_CODE_LENGTH) {
            return Promise.reject(`\u001b[0m\u001b[0;1;31mError: Unauthorized code length in {$unit.title}.\u001b[0m\u001b[1m`);
	}
	let name = makeCodeName(tab, protocolVersion);
	console.log('Bench ' + name + ' < ' + optionsToString(tab, protocolVersion));
	let fileName = filename(name);
	await write(fileName + '.cpp', tab.code);
	await write(fileName + '.opt', optionsToString(tab, protocolVersion));
	return await execute(fileName, tab, protocolVersion, force);
    } catch (e) {
	return { stdout: e };
    }
}

async function benchmark(request, header) {
    return await Promise.all(request.tabs.map(u => benchmarkOneBuild(u, request.protocolVersion, request.force)));
}

async function reloadOne(id) {
    const fileName = filename(id);
    const values = await Promise.all([read(fileName + '.cpp'), read(fileName + '.opt'), read(fileName + '.out')]);
    return groupResults(values, id);
}

async function reload(encodedName) {
    let fileName = filename(decodeName(encodedName));
    const ids = await read(fileName + '.res');
    return await Promise.all(ids.split("\n").map(id => reloadOne(decodeName(id))));
}

function readBuildResults(values) {
    if (values.res == null) return {};
    let results = values.res.toString().split('\n');
    let times = [];
    let memories = [];
    for (let i = 0; i < results.length; i++) {
	let s = results[i].split('\t');
	if (s.length === 2) {
	    times.push(s[0]);
	    memories.push(s[1]);
	}
    }
    return {
	times: times,
	memories: memories,
    };
}

function makeBuildGraphResult(values) {
    let result = values.map(v => readBuildResults(v));
    let message = values.map(v => v.stdout).reduce((r, v) => r + '\n' + v);
    let idsList = values.map(v => v.id).reduce((r, v) => r + '\n' + v);
    let id = sha1(idsList);
    write(filename(id) + '.res', idsList);
    return {
        result: result,
        message: message,
        id: encodeName(id)
    };
}

function makeOneResult(done) {
    return {
        code: done.code,
        compiler: done.options.compiler,
        optim: done.options.optim,
        cppVersion: done.options.cppVersion,
        lib: done.options.lib,
        protocolVersion: done.options.protocolVersion
    };
}

function makeWholeResult(done) {
    return Object.assign(done.map(d => makeOneResult(d)), makeBuildGraphResult(done.map(d => {
        return {
            result: d.graph,
            stdout: '',
            id: encodeName(d.id)
        };
    })));
}

app.post('/', upload.array(), function (req, res) {
    Promise.resolve(benchmark(req.body, req.headers))
        .then((done) => res.json(makeBuildGraphResult(done)))
        .catch((err) => res.json({ message: err }));
});

app.get('/get/:id', upload.array(), function (req, res) {
    console.log('Get ' + req.params.id + ' ' + JSON.stringify(req.headers));
    Promise.resolve(reload(req.params.id))
        .then((done) => res.json(makeWholeResult(done)))
        .catch(() => res.json({ message: 'Could not load given id' }));
});

app.listen(3000, function () {
    console.log('Listening to commands');
});

exports.makeName = makeName;
exports.encodeName = encodeName;
exports.decodeName = decodeName;
exports.wrapCode = wrapCode;
exports.unwrapCode = unwrapCode;
exports.groupResults = groupResults;
exports.getFunctions = getFunctions;
exports.optionsToString = optionsToString;
exports.execute = execute;
<commit_msg>Fixes on get id.<commit_after>#!/usr/bin/env node

var express = require('express');
var cors = require('cors');
var fs = require('fs');
const path = require('path');
var app = express();
app.use(express.static(path.join(__dirname, 'quick-bench-front-end', 'build')));
var exec = require('child_process').exec;
var sha1 = require('sha1');
var bodyParser = require('body-parser');
var multer = require('multer');

var upload = multer();

const MAX_CODE_LENGTH = 20000;
const WRITE_PATH = '/data';
const PREFIX_CODE_1 = `#include <benchmark/benchmark_api.h>
`;
const SUFFIX_CODE_1 = `

static void Noop(benchmark::State& state) {
  while (state.KeepRunning());
}
BENCHMARK(Noop);
BENCHMARK_MAIN()`;
const PREFIX_CODE_2 = `#include <benchmark/benchmark.h>
`;
const SUFFIX_CODE_2 = `

static void Noop(benchmark::State& state) {
  for (auto _ : state) benchmark::DoNotOptimize(0);
}
BENCHMARK(Noop);
BENCHMARK_MAIN()`;
const SUFFIX_CODE_3 = `

static void Noop(benchmark::State& state) {
  for (auto _ : state) benchmark::DoNotOptimize(0);
}
BENCHMARK(Noop);
BENCHMARK_MAIN();`;

const NOOP_BUILD = 'int main() {return 0;}';

app.use(bodyParser.json());
app.use(cors());

function write(fileName, code) {
    return new Promise((resolve, reject) => {
        fs.writeFile(fileName, code, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

function read(fileName, acceptMissing) {
    return new Promise((resolve, reject) => {
        fs.readFile(fileName, 'utf8', (err, data) => {
            if (err) {
                if (acceptMissing && err.code === 'ENOENT') {
                    resolve(null);
                } else {
                    reject(err);
                }
            } else {
                resolve(data);
            }
        });
    });
}

function runDockerCommand(fileName, request, force) {
    return './run-docker-builder ' + fileName + ' ' + request.compiler + ' ' + request.optim + ' ' + request.cppVersion + ' ' + (request.isAnnotated || false) + ' ' + (force || false) + ' ' + (request.lib || 'gnu');
}

function optionsToString(request, protocolVersion) {
    let options = {
        "protocolVersion": protocolVersion,
        "compiler": request.compiler,
        "optim": request.optim,
        "cppVersion": request.cppVersion,
        "isAnnotated": request.isAnnotated,
        "lib": request.lib
    };
    return JSON.stringify(options);
}

function execute(fileName, request, protocolVersion, force) {
    let options = {
        timeout: 60000,
        killSignal: 'SIGKILL'
    };
    return new Promise((resolve, reject) => {
        console.time(fileName);
        return exec(runDockerCommand(fileName, request, force), options, function (err, stdout, stderr) {
            if (err) {
                console.timeEnd(fileName);
                console.log('Bench failed ' + fileName);
                exec("./kill-docker " + fileName);
                reject("\u001b[0m\u001b[0;1;31mError or timeout\u001b[0m\u001b[1m
" + stdout + "
" + stderr);
            } else {
                console.timeEnd(fileName);
                console.log('Bench done ' + fileName + (stderr.indexOf('cached results') > -1 ? ' from cache' : ''));
                resolve({
                    res: fs.readFileSync(fileName + '.out'),
                    stdout: stderr,
                    id: encodeName(makeCodeName(request, protocolVersion))
                });
            }
        });
    });
}

function groupResults(results, id) {
    let code = results[0];
    let options = results[1];
    let graph = results[2];
    return { code: code, options: JSON.parse(options), graph: graph, id: id };
}

function makeCodeName(tab, protocolVersion) {
    return sha1(tab.code + tab.compiler + tab.optim + tab.cppVersion + tab.lib + protocolVersion);
}
function makeName(request) {
    return sha1(request.tabs.reduce(u => makeCodeName(u, request.protocolVersion)) + request.protocolVersion);
}

function wrapCode(inputCode) {
    return PREFIX_CODE_2 + inputCode + SUFFIX_CODE_3;
}

function unwrapCode(inputCode) {
    if (inputCode.startsWith(PREFIX_CODE_1)) {
        inputCode = inputCode.slice(PREFIX_CODE_1.length);
    }
    if (inputCode.endsWith(SUFFIX_CODE_1)) {
        inputCode = inputCode.slice(0, -SUFFIX_CODE_1.length);
    }
    if (inputCode.startsWith(PREFIX_CODE_2)) {
        inputCode = inputCode.slice(PREFIX_CODE_2.length);
    }
    if (inputCode.endsWith(SUFFIX_CODE_2)) {
        inputCode = inputCode.slice(0, -SUFFIX_CODE_2.length);
    }
    if (inputCode.endsWith(SUFFIX_CODE_3)) {
        inputCode = inputCode.slice(0, -SUFFIX_CODE_3.length);
    }
    return inputCode;
}

function encodeName(id) {
    let short = new Buffer(id, 'hex').toString('base64');
    short = short.replace(new RegExp('/', 'g'), '-').replace(new RegExp('\\+', 'g'), '_');
    return short.slice(0, -1);
}

function decodeName(short) {
    short = short.replace(new RegExp('\\-', 'g'), '/').replace(new RegExp('_', 'g'), '+ ') + '=';
    return new Buffer(short, 'base64').toString('hex');
}

function getFunctions(code) {
    RE = /BENCHMARK\s*\(\s*([A-Za-z0-9_]+)\s*\)/g;
    let content='';
    let res;
    while ((res = RE.exec(code)) !== null) {
        content+= res[1] + '\n';
    }
    return content;
}

function filename(name) {
    let dir = WRITE_PATH + '/' + name.substr(0, 2);
    return  dir + '/' + name;
}

async function benchmarkOneBuild(tab, protocolVersion, force) {
    try {
	if (tab.code.length > MAX_CODE_LENGTH) {
            return Promise.reject(`\u001b[0m\u001b[0;1;31mError: Unauthorized code length in {$unit.title}.\u001b[0m\u001b[1m`);
	}
	let name = makeCodeName(tab, protocolVersion);
	console.log('Bench ' + name + ' < ' + optionsToString(tab, protocolVersion));
	let fileName = filename(name);
	await write(fileName + '.cpp', tab.code);
	await write(fileName + '.opt', optionsToString(tab, protocolVersion));
	return await execute(fileName, tab, protocolVersion, force);
    } catch (e) {
	return { stdout: e };
    }
}

async function benchmark(request, header) {
    return await Promise.all(request.tabs.map(u => benchmarkOneBuild(u, request.protocolVersion, request.force)));
}

async function reloadOne(id) {
    const fileName = filename(id);
    const values = await Promise.all([read(fileName + '.cpp'), read(fileName + '.opt'), read(fileName + '.out')]);
    return groupResults(values, id);
}

async function reload(encodedName) {
    let fileName = filename(decodeName(encodedName));
    const ids = await read(fileName + '.res');
    return await Promise.all(ids.split("\n").map(id => reloadOne(decodeName(id))));
}

function readBuildResults(values) {
    if (values.res == null) return {};
    let results = values.res.toString().split('\n');
    let times = [];
    let memories = [];
    for (let i = 0; i < results.length; i++) {
	let s = results[i].split('\t');
	if (s.length === 2) {
	    times.push(s[0]);
	    memories.push(s[1]);
	}
    }
    return {
	times: times,
	memories: memories,
    };
}

function makeBuildGraphResult(values) {
    let result = values.map(v => readBuildResults(v));
    let message = values.map(v => v.stdout).reduce((r, v) => r + '\n' + v);
    let idsList = values.map(v => v.id).reduce((r, v) => r + '\n' + v);
    let id = sha1(idsList);
    write(filename(id) + '.res', idsList);
    return {
        result: result,
        message: message,
        id: encodeName(id)
    };
}

function makeOneResult(done) {
    return {
        code: done.code,
        compiler: done.options.compiler,
        optim: done.options.optim,
        cppVersion: done.options.cppVersion,
        lib: done.options.lib,
        protocolVersion: done.options.protocolVersion
    };
}

function makeWholeResult(done) {
    return Object.assign({ tabs: done.map(d => makeOneResult(d)) }, makeBuildGraphResult(done.map(d => {
        return {
            res: d.graph,
            stdout: '',
            id: encodeName(d.id)
        };
    })));
}

app.post('/', upload.array(), function (req, res) {
    Promise.resolve(benchmark(req.body, req.headers))
        .then((done) => res.json(makeBuildGraphResult(done)))
        .catch((err) => res.json({ message: err }));
});

app.get('/get/:id', upload.array(), function (req, res) {
    console.log('Get ' + req.params.id + ' ' + JSON.stringify(req.headers));
    Promise.resolve(reload(req.params.id))
        .then((done) => res.json(makeWholeResult(done)))
        .catch(() => res.json({ message: 'Could not load given id' }));
});

app.listen(3000, function () {
    console.log('Listening to commands');
});

exports.makeName = makeName;
exports.encodeName = encodeName;
exports.decodeName = decodeName;
exports.wrapCode = wrapCode;
exports.unwrapCode = unwrapCode;
exports.groupResults = groupResults;
exports.getFunctions = getFunctions;
exports.optionsToString = optionsToString;
exports.execute = execute;
<|endoftext|>


<commit_before>// *********************************
// Libraries
// *********************************

var U               = require('util');
var Base            = require('./base');
var C               = require('./common').common();
var Configurator    = require('./configurator');
var LL              = require('./local_listen');

// *********************************
// Admin commands
// *********************************

var ___admin_command = exports.___admin_command = function( cmd ) {
    C._debug( "Got admin command: " + cmd );

    var stats   = this.stats_object();
    var state   = this.state_object();
    var config  = this.config_object();

    // Recompute uptime
    stats.uptime            = process.uptime();

    // Recompute idle time - if we don't have a last item, idle time == uptime
    stats.connections.idle  = stats.connections.last
                                ? C._now() - stats.connections.last
                                : stats.uptime;

    // just in case /anything/ goes wrong, wrap this in a try/catch.
    try {
        switch(cmd) {

            case "stats":
                var idx;
                var map = { };
                var cur = [ ];

                // Map state to something consumable by a client
                for( idx in state.all_servers ) {
                    map[ state.all_servers[idx].name ] = state.all_servers[idx].stats();
                }

                // List current active servers we are sending to
                for( idx = 0; idx < state.current_servers.length; idx++ ) {
                    cur.push( state.current_servers[idx].name );
                }

                if( config.overflow_stream ) {
                    map[ config.overflow_stream.name ] = config.overflow_stream.stats();
                }

                // return that and stats back
                return C._json_pp(
                    { stats: stats, active_servers: cur, all_servers: map });

            case "config":
                return C._json_pp( config );

            case "ping":
                return "pong";

            case "__dump":
                C._debug( state );
                return "OK";

            case "help":
                return "Try any of: config, ping, stats, help\n"

            default:
                var out = "ADMIN ERROR: UNKNOWN COMMAND " +  cmd + "\n";
                C._debug( out );
                return out;
        }
    } catch(e) {
        var out = "ADMIN ERROR on '" + cmd + "': " + U.inspect( e );
        C._log( out );
        return out;
    }
}

// ****************************************
// Admin Server object
// ****************************************

var AdminServer = exports.AdminServer = function( port, address ) {
    C._debug( U.format( "Starting admin server on tcp://%s:%s", address, port ) );

    // On connection, dispatch to our callback
    new LL.Stream( 'admin', port, address, function(ll, conn) {

        // These are line based commands
        conn.setEncoding('ascii');

        // Dispatch the command
        conn.on( 'data', function (data) {
            var cmd     = data.trim();

            conn.write( ___admin_command.bind( ll )( cmd ) );
        });
    });
}

<commit_msg>* prettier admin server diagnostic<commit_after>// *********************************
// Libraries
// *********************************

var U               = require('util');
var Base            = require('./base');
var C               = require('./common').common();
var Configurator    = require('./configurator');
var LL              = require('./local_listen');

// *********************************
// Admin commands
// *********************************

var ___admin_command = exports.___admin_command = function( cmd ) {
    C._debug( "Got admin command: " + cmd );

    var stats   = this.stats_object();
    var state   = this.state_object();
    var config  = this.config_object();

    // Recompute uptime
    stats.uptime            = process.uptime();

    // Recompute idle time - if we don't have a last item, idle time == uptime
    stats.connections.idle  = stats.connections.last
                                ? C._now() - stats.connections.last
                                : stats.uptime;

    // just in case /anything/ goes wrong, wrap this in a try/catch.
    try {
        switch(cmd) {

            case "stats":
                var idx;
                var map = { };
                var cur = [ ];

                // Map state to something consumable by a client
                for( idx in state.all_servers ) {
                    map[ state.all_servers[idx].name ] = state.all_servers[idx].stats();
                }

                // List current active servers we are sending to
                for( idx = 0; idx < state.current_servers.length; idx++ ) {
                    cur.push( state.current_servers[idx].name );
                }

                if( config.overflow_stream ) {
                    map[ config.overflow_stream.name ] = config.overflow_stream.stats();
                }

                // return that and stats back
                return C._json_pp(
                    { stats: stats, active_servers: cur, all_servers: map });

            case "config":
                return C._json_pp( config );

            case "ping":
                return "pong";

            case "__dump":
                C._debug( state );
                return "OK";

            case "help":
                return "Try any of: config, ping, stats, help\n"

            default:
                var out = "ADMIN ERROR: UNKNOWN COMMAND " +  cmd + "\n";
                C._debug( out );
                return out;
        }
    } catch(e) {
        var out = "ADMIN ERROR on '" + cmd + "': " + U.inspect( e );
        C._log( out );
        return out;
    }
}

// ****************************************
// Admin Server object
// ****************************************

var AdminServer = exports.AdminServer = function( port, address ) {

    // This could be a unix socket; if it has ANY non digits, it's
    // basically a socket. LL.Stream will do the right thing, but
    // we want to get a pretty diagnostic too.
    if( port.match(/\D/) ) {
        C._debug( "Starting admin server on socket://" + port );
    } else {
        C._debug( U.format( "Starting admin server on tcp://%s:%s", address, port ) );
    }

    // On connection, dispatch to our callback
    new LL.Stream( 'admin', port, address, function(ll, conn) {

        // These are line based commands
        conn.setEncoding('ascii');

        // Dispatch the command
        conn.on( 'data', function (data) {
            var cmd     = data.trim();

            conn.write( ___admin_command.bind( ll )( cmd ) );
        });
    });
}

<|endoftext|>


<commit_before>// variables in global scope so available to console
video = document.querySelector("video");
constraints = {audio: false, video: true};

function successCallback(stream){
  window.stream = stream; // stream available to console
	video = attachMediaStream(video, stream);
}

function errorCallback(error){
  console.log("navigator.getUserMedia error: ", error);
}

function TemPluginLoaded() {
navigator.getUserMedia(constraints, successCallback, errorCallback);
}
<commit_msg>STYLE: indentation<commit_after>// variables in global scope so available to console
video = document.querySelector("video");
constraints = {audio: false, video: true};

function successCallback(stream){
  window.stream = stream; // stream available to console
	video = attachMediaStream(video, stream);
}

function errorCallback(error){
  console.log("navigator.getUserMedia error: ", error);
}

function TemPluginLoaded() {
	navigator.getUserMedia(constraints, successCallback, errorCallback);
}
<|endoftext|>


<commit_before>Package.describe({
  version: '1.0.2',
  summary: 'Meteor bundle analysis and visualization.',
  documentation: 'README.md',
});

Npm.depends({
  "d3-selection": "1.0.5",
  "d3-shape": "1.0.6",
  "d3-hierarchy": "1.1.4",
  "d3-transition": "1.0.4",
  "pretty-bytes": "4.0.2",
});

Package.onUse(function(api) {
  api.use('isobuild:dynamic-import@1.5.0');
  api.use([
    'ecmascript',
    'dynamic-import',
  ]);
  api.mainModule('server.js', 'server');
  api.mainModule('client.js', 'client');
});
<commit_msg>Bump version of `bundle-visualizer` to 1.0.3.<commit_after>Package.describe({
  version: '1.0.3',
  summary: 'Meteor bundle analysis and visualization.',
  documentation: 'README.md',
});

Npm.depends({
  "d3-selection": "1.0.5",
  "d3-shape": "1.0.6",
  "d3-hierarchy": "1.1.4",
  "d3-transition": "1.0.4",
  "pretty-bytes": "4.0.2",
});

Package.onUse(function(api) {
  api.use('isobuild:dynamic-import@1.5.0');
  api.use([
    'ecmascript',
    'dynamic-import',
  ]);
  api.mainModule('server.js', 'server');
  api.mainModule('client.js', 'client');
});
<|endoftext|>