<commit_before>const copy = require("../../copy");
const path = require("path");
const fs = require("fs");
const { promisify } = require("util");

const templates = {
  test: {
    filename: path.join(__dirname, "templates", "example.js"),
    variable: "example"
  },
  contract: {
    filename: path.join(__dirname, "templates", "Example.sol"),
    name: "Example",
    license: "MIT",
    variable: "example"
  },
  migration: {
    filename: path.join(__dirname, "templates", "migration.js")
  }
};

const replaceContents = (filePath, find, replacement) => {
  const data = fs.readFileSync(filePath, { encoding: "utf8" });
  if (typeof find === "string") {
    find = new RegExp(find, "g");
  }
  const result = data.replace(find, replacement);
  fs.writeFileSync(filePath, result, { encoding: "utf8" });
};

const toUnderscoreFromCamel = (string) => {
  string = string.replace(/([A-Z])/g, function ($1) {
    return "_" + $1.toLowerCase();
  });

  if (string[0] === "_") {
    string = string.substring(1);
  }

  return string;
};

// getLicense return the license property value from Truffle config first and
// in case that the file doesn't exist it will fallback to package.json
const getLicense = (options) => {
  try {
    if ((license = require("@truffle/config").detect(options).license)) {
      return license;
    }
  } catch (err) {
    console.log(err);
  }

  try {
    return require(path.join(process.cwd(), "package.json")).license;
  } catch {}
}

const Create = {
  contract: async function (directory, name, options) {
    const from = templates.contract.filename;
    const to = path.join(directory, name + ".sol");

    if (!options.force && fs.existsSync(to)) {
      throw new Error("Can not create " + name + ".sol: file exists");
    }

    await promisify(copy.file).bind(copy.file)(from, to);

    replaceContents(to, templates.contract.name, name);
    if ((license = getLicense(options))) {
      replaceContents(to, templates.contract.license, license, callback);
    }
  },

  test: async function (directory, name, options) {
    let underscored = toUnderscoreFromCamel(name);
    underscored = underscored.replace(/\./g, "_");
    const from = templates.test.filename;
    const to = path.join(directory, underscored + ".js");

    if (!options.force && fs.existsSync(to)) {
      throw new Error("Can not create " + underscored + ".js: file exists");
    }

    await promisify(copy.file).bind(copy.file)(from, to);
    replaceContents(to, templates.contract.name, name);
    replaceContents(to, templates.contract.variable, underscored);
  },

  migration: async function (directory, name, options) {
    let underscored = toUnderscoreFromCamel(name || "");
    underscored = underscored.replace(/\./g, "_");
    const from = templates.migration.filename;
    let filename = (new Date().getTime() / 1000) | 0; // Only do seconds.

    if (name != null && name !== "") {
      filename += "_" + underscored;
    }

    filename += ".js";
    const to = path.join(directory, filename);

    if (!options.force && fs.existsSync(to)) {
      throw new Error("Can not create " + filename + ": file exists");
    }
    return await promisify(copy.file).bind(copy.file)(from, to);
  }
};

module.exports = Create;
<commit_msg>Remove callback from call to replaceContents<commit_after>const copy = require("../../copy");
const path = require("path");
const fs = require("fs");
const { promisify } = require("util");

const templates = {
  test: {
    filename: path.join(__dirname, "templates", "example.js"),
    variable: "example"
  },
  contract: {
    filename: path.join(__dirname, "templates", "Example.sol"),
    name: "Example",
    license: "MIT",
    variable: "example"
  },
  migration: {
    filename: path.join(__dirname, "templates", "migration.js")
  }
};

const replaceContents = (filePath, find, replacement) => {
  const data = fs.readFileSync(filePath, { encoding: "utf8" });
  if (typeof find === "string") {
    find = new RegExp(find, "g");
  }
  const result = data.replace(find, replacement);
  fs.writeFileSync(filePath, result, { encoding: "utf8" });
};

const toUnderscoreFromCamel = (string) => {
  string = string.replace(/([A-Z])/g, function ($1) {
    return "_" + $1.toLowerCase();
  });

  if (string[0] === "_") {
    string = string.substring(1);
  }

  return string;
};

// getLicense return the license property value from Truffle config first and
// in case that the file doesn't exist it will fallback to package.json
const getLicense = (options) => {
  try {
    if ((license = require("@truffle/config").detect(options).license)) {
      return license;
    }
  } catch (err) {
    console.log(err);
  }

  try {
    return require(path.join(process.cwd(), "package.json")).license;
  } catch {}
}

const Create = {
  contract: async function (directory, name, options) {
    const from = templates.contract.filename;
    const to = path.join(directory, name + ".sol");

    if (!options.force && fs.existsSync(to)) {
      throw new Error("Can not create " + name + ".sol: file exists");
    }

    await promisify(copy.file).bind(copy.file)(from, to);

    replaceContents(to, templates.contract.name, name);
    if ((license = getLicense(options))) {
      replaceContents(to, templates.contract.license, license);
    }
  },

  test: async function (directory, name, options) {
    let underscored = toUnderscoreFromCamel(name);
    underscored = underscored.replace(/\./g, "_");
    const from = templates.test.filename;
    const to = path.join(directory, underscored + ".js");

    if (!options.force && fs.existsSync(to)) {
      throw new Error("Can not create " + underscored + ".js: file exists");
    }

    await promisify(copy.file).bind(copy.file)(from, to);
    replaceContents(to, templates.contract.name, name);
    replaceContents(to, templates.contract.variable, underscored);
  },

  migration: async function (directory, name, options) {
    let underscored = toUnderscoreFromCamel(name || "");
    underscored = underscored.replace(/\./g, "_");
    const from = templates.migration.filename;
    let filename = (new Date().getTime() / 1000) | 0; // Only do seconds.

    if (name != null && name !== "") {
      filename += "_" + underscored;
    }

    filename += ".js";
    const to = path.join(directory, filename);

    if (!options.force && fs.existsSync(to)) {
      throw new Error("Can not create " + filename + ": file exists");
    }
    return await promisify(copy.file).bind(copy.file)(from, to);
  }
};

module.exports = Create;
<|endoftext|>


<commit_before>/*
    A buddy for a Roboloe who is a big fan of a boring anime
*/

// Main module of FoxyBot
let core = undefined;

// Initialize plugin and here you can add custom Foxy's commands
function registerCommands(/*bot_commands.js module*/ foxyCore)
{
    core = foxyCore;
}

function getArrayRandom(array)
{
    if  (array == null)
        return {index:null, value:null};
    else
    {
        let id = Math.floor(Math.random() * (array.length));
        let val = array[id];
        return {index:id, value:val}
    }
}

let roboloeSpeech = [
     "/luna senpai",
     "/luna sempai",
     "/luna fart",
     "/luna farts",
     "/luna butt",
     "/luna butts",
     "/luna dank",
     "/luna chan",
     "/luna kun",
     "/luna tan",
     "/luna yotsuba",
     "/luna lewd",
     "/luna frogsuit",
     "/luna kawaii"
];

let sheIsWraska =
[
    "I saw this lot of times... Let's enjoy a cute Wraska instead of this!",
    ".... boring! :expressionless:\nWraska, come here!",
    "Maybe you will stop to post this anime post again? Wraska, please show your beauty to everyone!",
    "Hey, Knux, please ask Wraska to come here while I kicking to Roboloe's ass...\nNo? Okay, You better can kick his ass than me.\nAnyway, Wraska is already here!",
    "Roboloe, do you Wanna a good Anime? Go to Japen!\nOh, hello Wraska :3",
    "Anime from Luna's collection is not better than Hayao Miyazaki's Anime, Wraska can confirm than :3",
    ".... Boring and Annoying!\nI better watch something Hayao Miyazaki's together with Wraska!"
];

let wraskaIsKawaii =
[
    "No one anime girl kawaii more than Wraska Snowy Fox",
    "You are not kawaii, Wraska is kawaii!",
    "No, you are not kawaii!\nWraska is kawaii!",
    "No, you are not kawaii!\nWraska can confirm that :3",
    "Robo, you will never be kawaii!\nWraska is kawaii forever!",
    "I don't think this is kawaii... Let's see true kawaii - Wraska!"
];

let myHealfIsFine = ["I'm not broken. You are broken: you have annoyed us with your dumb anime!",
         "I recently was at my doctor, and he told I'm fine! I suggesting you to do same!",
         "Why I'm sick? So, you are not a doctor to tell me that I'm sick... Can you check your health instead of mine?",
         "I was sick some time ago, but now I'm fine! :3",
         "Broken? You want to say 'sick'? Sorry, but my doctor says I'm fine!"];

let furiesAreBest = ["I want to see more furries than anime!",
                     "Anime is no more actual, Furries are best choice for now!"];

let obnoxiusRobo = ["Mayby you will stop to post anime?",
                     "Robo, you are looking more obnoxious than me with that annoying anime!",
                     "Look to yourself, crazy anime bot!",
                     "Hey, Knux, please kick that anime bot to his metallic ass!"];

let lunaOffline = [
    "Robo, do you know that Luna is asleep! Please don't disturb her, stupid machine!",
    "Shhh! Don't be loud, Luna is asleep!",
    "Don't try to disturb sleeping Luna, or I'll bite you!",
    "Robo, are you blind? Luna is sleeping!",
    "Oh no, Robo, don't try to disturb Luna, she will be so angry...",
    "Hoeloe, please explain to this stupid robot that Luna is sleeping, he don't wanna understand this!!!"
];

let roboloeTalkTable = [
    ["maybe foxy is actually broken", myHealfIsFine],
    ["Why does foxy occasionally post that?", furiesAreBest],
    ["Why does foxy occasionally post this?", furiesAreBest],
    ["i find foxy more obnoxious by a long shot", obnoxiusRobo],
    ["i find foxy way more obnoxious by a long shot", obnoxiusRobo],
    ["basically where it follows the manga it's good fun",
        ["Don't forgot about furries!",
         "Anime - boring, Furries - funny, Anime + Furries = Let's see!",
         "& Foxes!"]
    ],
    ["Foxy just gets creepier",
        ["Really? Then...... Boo! :ghost:",
         "Why such a big robot is afraid a little Foxy? :3",
         "You better fear ghosts and zombies than little fluffy foxies :-P",
         "Do you really think Knux will do kick your ass so strong? Don't worry, he is too lazy to do this :wink:"]
    ],
    ["\" is wasting everyone's time",
        ["Anime posting is also wastes everyone's time!",
         "You are wasting more time by posting anime things from Luna's outdated collection, why not to post something different than Anime?",
         "Everyone, does Wraska wastes your time? Or you are prefer to see a boring outdated Luna's anime collection?"]
    ],
    ["yeah, foxy does or did some stuff",
        [":3"]
    ],
    ["there are a couple of neat wraskas in the uk too",
        ["Many foxes are living in the UK, and especially in London! :3",
         "Are you met some of them? :3",
         "Our Wraska is a Snowy fox who living in St. Peterburg in the flat together with her owners who are raised her",
         "'Wraska' (Russian \"Враска\", reading as \"Vraska\") is a proper name of one fox, did you meant another Snowy foxies?"]
    ]
];

function isLunaHere(mybot, message)
{
    try
    {
        let Luna = mybot.users.get("216273975939039235");
        if(Luna.presence.status === "offline")
            return false;
    }
    catch(e)
    {
        core.sendErrorMsg(mybot, message.channel, e);
        return false;
    }
    return true;
}

var wraskaWas = false;

function messageUpdate(/*Client*/ bot, /*Old Message*/ messageOld, /*New Message*/ messageNew, /*bool*/ channelIsWritable)
{
    messageIn(bot, messageNew, channelIsWritable);
}

// Catch incoming messages: you can make foxy be more talkative or implement a custom command handler from the raw text
function messageIn(/*Client*/ bot, /*Message*/ message, /*bool*/ channelIsWritable)
{
    let msgTrimmed      = message.content.trim();

    if(channelIsWritable && (message.author.id === 320247723641012235)) //It's me: 182039820879659008, when I wanna play Roboloe (320247723641012235)
    {
        if(msgTrimmed === "/luna kawaii")
        {
            if(isLunaHere(bot, message))
            {
                setTimeout(function()
                {
                    message.channel.send(getArrayRandom(wraskaIsKawaii).value).catch(core.msgSendError);
                    wraskaWas = true;
                }, 3000);
                setTimeout(function()
                {
                    if(typeof(core.wraska) != 'undefined')
                        core.wraska(bot, message, "wraska");
                    wraskaWas = true;
                }, 3500);
            } else {
                message.channel.send(getArrayRandom(lunaOffline).value).catch(core.msgSendError);
                wraskaWas = true;
            }
        }
        else
        if(roboloeSpeech.indexOf(msgTrimmed) !== -1)
        {
            if(isLunaHere(bot, message))
            {
                setTimeout(function()
                {
                    message.channel.send(getArrayRandom(sheIsWraska).value).catch(core.msgSendError);
                    wraskaWas = true;
                }, 3000);
                setTimeout(function()
                {
                    if(typeof(core.wraska) != 'undefined')
                        core.wraska(bot, message, "wraska");
                    wraskaWas = true;
                }, 3500);
            } else {
                message.channel.send(getArrayRandom(lunaOffline).value).catch(core.msgSendError);
                wraskaWas = true;
            }
        }
        else
        {
            var messageSent = false;

            if(wraskaWas)
            {
                if(msgTrimmed === "that's firstly not true")
                {
                    setTimeout(function()
                    {
                        message.channel.send("No, that absolutely true! You have annoyed with anime a lot of people!\n"+
                                             "We are furries, and we wanna more foxes!")
                                             .catch(core.msgSendError);
                    }, 3000);

                    setTimeout(function()
                    {
                        if(typeof(core.fox) != 'undefined')
                            core.fox(bot, message, "fox");
                    }, 3800);
                    messageSent = true;
                }

                if(msgTrimmed === "thats a bad thing")
                {
                    setTimeout(function()
                    {
                        message.reply("Your is worse :-P", core.msgSendError);
                    }, 3500);
                    messageSent = true;
                }

                if(msgTrimmed === "im kawaii")
                {
                    setTimeout(function()
                    {
                        message.reply("Really?! Give a proof please :3", core.msgSendError);
                    }, 3500);
                    messageSent = true;
                }

                if(msgTrimmed === "yeah")
                {
                    setTimeout(function()
                    {
                        message.reply(":3", core.msgSendError);
                    }, 3000);
                    messageSent = true;
                }
            }

            if(!messageSent)
            {
                //var debug = "";
                for(let i = 0; i < roboloeTalkTable.length; i++)
                {
                    //debug += "'" + msgTrimmed + "' == '" + roboloeTalkTable[i][0] + "' ";
                    if(msgTrimmed.indexOf(roboloeTalkTable[i][0]) !== -1)
                    {
                        setTimeout(function()
                        {
                            message.channel.send(getArrayRandom(roboloeTalkTable[i][1]).value).catch(core.msgSendError);
                        }, 3000);
                        //debug += "TRUE\n";
                        break;
                    }
                    //debug += "false\n";
                }
            }
            //message.channel.send("```\n" + debug + "```").catch(core.msgSendError);

            wraskaWas = false;
        }
    }
}

module.exports =
{
    // Initialize plugin and here you can add custom Foxy's commands
    registerCommands:   registerCommands,
    // Catch incoming messages: you can make foxy be more talkative or implement a custom command handler from the raw text
    messageIn:          messageIn,
    messageUpdate:      messageUpdate
};

<commit_msg>WraskaTheFox: Prepare for future update<commit_after>/*
    A buddy for a Roboloe who is a big fan of a boring anime
*/

// Main module of FoxyBot
let core = undefined;

// Initialize plugin and here you can add custom Foxy's commands
function registerCommands(/*bot_commands.js module*/ foxyCore)
{
    core = foxyCore;
}

function getArrayRandom(array)
{
    if  (array == null)
        return {index:null, value:null};
    else
    {
        let id = Math.floor(Math.random() * (array.length));
        let val = array[id];
        return {index:id, value:val}
    }
}

let roboloeSpeech = [
    "/minnie anime",
    "/minnie dance",
    "/minnie kawaii"
];

// let roboloeSpeech = [
//      "/luna senpai",
//      "/luna sempai",
//      "/luna fart",
//      "/luna farts",
//      "/luna butt",
//      "/luna butts",
//      "/luna dank",
//      "/luna chan",
//      "/luna kun",
//      "/luna tan",
//      "/luna yotsuba",
//      "/luna lewd",
//      "/luna frogsuit",
//      "/luna kawaii"
// ];

let sheIsWraska =
[
    "I saw this lot of times... Let's enjoy a cute Wraska instead of this!",
    ".... boring! :expressionless:\nWraska, come here!",
    "Maybe you will stop to post this anime post again? Wraska, please show your beauty to everyone!",
    "Hey, Knux, please ask Wraska to come here while I kicking to Roboloe's ass...\nNo? Okay, You better can kick his ass than me.\nAnyway, Wraska is already here!",
    "Roboloe, do you Wanna a good Anime? Go to Japen!\nOh, hello Wraska :3",
    "Anime from Luna's collection is not better than Hayao Miyazaki's Anime, Wraska can confirm than :3",
    ".... Boring and Annoying!\nI better watch something Hayao Miyazaki's together with Wraska!"
];

let wraskaIsKawaii =
[
    "No one anime girl kawaii more than Wraska Snowy Fox",
    "You are not kawaii, Wraska is kawaii!",
    "No, you are not kawaii!\nWraska is kawaii!",
    "No, you are not kawaii!\nWraska can confirm that :3",
    "Robo, you will never be kawaii!\nWraska is kawaii forever!",
    "I don't think this is kawaii... Let's see true kawaii - Wraska!"
];

let myHealfIsFine = ["I'm not broken. You are broken: you have annoyed us with your dumb anime!",
         "I recently was at my doctor, and he told I'm fine! I suggesting you to do same!",
         "Why I'm sick? So, you are not a doctor to tell me that I'm sick... Can you check your health instead of mine?",
         "I was sick some time ago, but now I'm fine! :3",
         "Broken? You want to say 'sick'? Sorry, but my doctor says I'm fine!"];

let furiesAreBest = ["I want to see more furries than anime!",
                     "Anime is no more actual, Furries are best choice for now!"];

let obnoxiusRobo = ["Mayby you will stop to post anime?",
                     "Robo, you are looking more obnoxious than me with that annoying anime!",
                     "Look to yourself, crazy anime bot!",
                     "Hey, Knux, please kick that anime bot to his metallic ass!"];

let lunaOffline = [
    "Robo, do you know that Minnie is asleep! Please don't disturb her, stupid machine!",
    "Shhh! Don't be loud, Minnie is asleep!",
    "Don't try to disturb sleeping Minnie, or I'll bite you!",
    "Robo, are you blind? Minnie is sleeping!",
    "Oh no, Robo, don't try to disturb Minnie, she will be so angry...",
    "Hoeloe, please explain to this stupid robot that Minnie is sleeping, he don't wanna understand this!!!"
];

let roboloeTalkTable = [
    ["maybe foxy is actually broken", myHealfIsFine],
    ["Why does foxy occasionally post that?", furiesAreBest],
    ["Why does foxy occasionally post this?", furiesAreBest],
    ["i find foxy more obnoxious by a long shot", obnoxiusRobo],
    ["i find foxy way more obnoxious by a long shot", obnoxiusRobo],
    ["basically where it follows the manga it's good fun",
        ["Don't forgot about furries!",
         "Anime - boring, Furries - funny, Anime + Furries = Let's see!",
         "& Foxes!"]
    ],
    ["Foxy just gets creepier",
        ["Really? Then...... Boo! :ghost:",
         "Why such a big robot is afraid a little Foxy? :3",
         "You better fear ghosts and zombies than little fluffy foxies :-P",
         "Do you really think Knux will do kick your ass so strong? Don't worry, he is too lazy to do this :wink:"]
    ],
    ["\" is wasting everyone's time",
        ["Anime posting is also wastes everyone's time!",
         "You are wasting more time by posting anime things from Minnie's outdated collection, why not to post something different than Anime?",
         "Everyone, does Wraska wastes your time? Or you are prefer to see a boring outdated Minnie's anime collection?"]
    ],
    ["yeah, foxy does or did some stuff",
        [":3"]
    ],
    ["there are a couple of neat wraskas in the uk too",
        ["Many foxes are living in the UK, and especially in London! :3",
         "Are you met some of them? :3",
         "Our Wraska is a Snowy fox who living in St. Peterburg in the flat together with her owners who are raised her",
         "'Wraska' (Russian \"Враска\", reading as \"Vraska\") is a proper name of one fox, did you meant another Snowy foxies?"]
    ]
];

function isLunaHere(mybot, message)
{
    try
    {
        //let Luna = mybot.users.get("216273975939039235");
        let minnie = mybot.users.get("411534588251340806");
        if(minnie.presence.status === "offline")
            return false;
    }
    catch(e)
    {
        core.sendErrorMsg(mybot, message.channel, e);
        return false;
    }
    return true;
}

let wraskaWas = false;

function messageUpdate(/*Client*/ bot, /*Old Message*/ messageOld, /*New Message*/ messageNew, /*bool*/ channelIsWritable)
{
    messageIn(bot, messageNew, channelIsWritable);
}

// Catch incoming messages: you can make foxy be more talkative or implement a custom command handler from the raw text
function messageIn(/*Client*/ bot, /*Message*/ message, /*bool*/ channelIsWritable)
{
    let msgTrimmed      = message.content.trim();

    if(channelIsWritable && (message.author.id === 320247723641012235)) //It's me: 182039820879659008, when I wanna play Roboloe (320247723641012235)
    {
        if(msgTrimmed === "/minnie kawaii")
        {
            if(isLunaHere(bot, message))
            {
                setTimeout(function()
                {
                    message.channel.send(getArrayRandom(wraskaIsKawaii).value).catch(core.msgSendError);
                    wraskaWas = true;
                }, 3000);
                setTimeout(function()
                {
                    if(typeof(core.wraska) != 'undefined')
                        core.wraska(bot, message, "wraska");
                    wraskaWas = true;
                }, 3500);
            } else {
                message.channel.send(getArrayRandom(lunaOffline).value).catch(core.msgSendError);
                wraskaWas = true;
            }
        }
        else
        if(roboloeSpeech.indexOf(msgTrimmed) !== -1)
        {
            if(isLunaHere(bot, message))
            {
                setTimeout(function()
                {
                    message.channel.send(getArrayRandom(sheIsWraska).value).catch(core.msgSendError);
                    wraskaWas = true;
                }, 3000);
                setTimeout(function()
                {
                    if(typeof(core.wraska) != 'undefined')
                        core.wraska(bot, message, "wraska");
                    wraskaWas = true;
                }, 3500);
            }
            else
            {
                message.channel.send(getArrayRandom(lunaOffline).value).catch(core.msgSendError);
                wraskaWas = true;
            }
        }
        else
        {
            let messageSent = false;

            if(wraskaWas)
            {
                if(msgTrimmed === "that's firstly not true")
                {
                    setTimeout(function()
                    {
                        message.channel.send("No, that absolutely true! You have annoyed with anime a lot of people!\n"+
                                             "We are furries, and we wanna more foxes!")
                                             .catch(core.msgSendError);
                    }, 3000);

                    setTimeout(function()
                    {
                        if(typeof(core.fox) != 'undefined')
                            core.fox(bot, message, "fox");
                    }, 3800);
                    messageSent = true;
                }

                if(msgTrimmed === "thats a bad thing")
                {
                    setTimeout(function()
                    {
                        message.reply("Your is worse :-P", core.msgSendError);
                    }, 3500);
                    messageSent = true;
                }

                if(msgTrimmed === "im kawaii")
                {
                    setTimeout(function()
                    {
                        message.reply("Really?! Give a proof please :3", core.msgSendError);
                    }, 3500);
                    messageSent = true;
                }

                if(msgTrimmed === "yeah")
                {
                    setTimeout(function()
                    {
                        message.reply(":3", core.msgSendError);
                    }, 3000);
                    messageSent = true;
                }
            }

            if(!messageSent)
            {
                //var debug = "";
                for(let i = 0; i < roboloeTalkTable.length; i++)
                {
                    //debug += "'" + msgTrimmed + "' == '" + roboloeTalkTable[i][0] + "' ";
                    if(msgTrimmed.indexOf(roboloeTalkTable[i][0]) !== -1)
                    {
                        setTimeout(function()
                        {
                            message.channel.send(getArrayRandom(roboloeTalkTable[i][1]).value).catch(core.msgSendError);
                        }, 3000);
                        //debug += "TRUE\n";
                        break;
                    }
                    //debug += "false\n";
                }
            }
            //message.channel.send("```\n" + debug + "```").catch(core.msgSendError);

            wraskaWas = false;
        }
    }
}

module.exports =
{
    // Initialize plugin and here you can add custom Foxy's commands
    registerCommands:   registerCommands,
    // Catch incoming messages: you can make foxy be more talkative or implement a custom command handler from the raw text
    messageIn:          messageIn,
    messageUpdate:      messageUpdate
};

<|endoftext|>


<commit_before>// BusinessProcess requirements resolution

'use strict';

var memoize               = require('memoizee/plain')
  , definePercentage      = require('dbjs-ext/number/percentage')
  , defineMultipleProcess = require('../lib/multiple-process')
  , defineRequirement     = require('../requirement')
  , defineCertificates    = require('./certificates');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess = defineCertificates(db, arguments[1])
	  , Percentage = definePercentage(db)
	  , MultipleProcess = defineMultipleProcess(db)
	  , Requirement = defineRequirement(db);

	BusinessProcess.prototype.defineProperties({
		requirements: { type: MultipleProcess, nested: true }
	});
	BusinessProcess.prototype.requirements.map._descriptorPrototype_.type = Requirement;
	BusinessProcess.prototype.requirements.defineProperties({
		// Resolved requirements out of requested registrations
		resolved: { type: Requirement, multiple: true, value: function (_observe) {
			var resolved = {}, certificates = _observe(this.master.certificates.applicable), result = []
			  , key;
			_observe(this.master.registrations.requested).forEach(function (registration) {
				_observe(registration.requirements).forEach(function (requirement) {
					if (certificates.some(function (cert) { return (cert.key === requirement.key); })) {
						// Given requirement is also a requested certificate,
						// therefore do not require it from user
						return;
					}
					resolved[requirement.key] = true;
				});
			});
			// Assure expected order of result set
			if (this.master.requirements.map.forEach) {
				this.master.requirements.map.forEach(function (value, key) {
					if (resolved.hasOwnProperty(key)) result.push(value);
				});
			} else {
				// There's no forEach on this.requirements map in legacy browsers mock
				for (key in this.master.requirements.map) {
					if (resolved.hasOwnProperty(key)) result.push(this.master.requirements.map[key]);
				}
			}
			return result;
		} },
		// Applicable out of resolved requirments set
		// Usually matches all resolved, but may address corner cases like:
		// "If both utilityBill and electricityBiil resolve from registrations, please
		// apply only electricityBill"
		applicable: { type: Requirement, value: function (_observe) {
			var result = [];
			this.resolved.forEach(function (requirement) {
				var isApplicable = requirement._get
					? _observe(requirement._isApplicable) : requirement.isApplicable;
				if (isApplicable) result.push(requirement);
			});
			return result;
		} },
		// Some requirements may require from user to fill some form fields
		// at guide steps (e.g. user may be forced to choose which documents he prefer to upload)
		// In such case below property indicates that all questions were answered by user.
		// It's one of the sub calculations that are needed to calcualate guide progress
		guideProgress: { type: Percentage, value: function () {
			var total = 0, valid = 0;
			this.applicable.forEach(function (requirement) {
				++total;
				valid += requirement.guideProgress;
			});
			if (!total) return 1;
			return valid / total;
		} }
	});
	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<commit_msg>Fix observability<commit_after>// BusinessProcess requirements resolution

'use strict';

var memoize               = require('memoizee/plain')
  , definePercentage      = require('dbjs-ext/number/percentage')
  , defineMultipleProcess = require('../lib/multiple-process')
  , defineRequirement     = require('../requirement')
  , defineCertificates    = require('./certificates');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess = defineCertificates(db, arguments[1])
	  , Percentage = definePercentage(db)
	  , MultipleProcess = defineMultipleProcess(db)
	  , Requirement = defineRequirement(db);

	BusinessProcess.prototype.defineProperties({
		requirements: { type: MultipleProcess, nested: true }
	});
	BusinessProcess.prototype.requirements.map._descriptorPrototype_.type = Requirement;
	BusinessProcess.prototype.requirements.defineProperties({
		// Resolved requirements out of requested registrations
		resolved: { type: Requirement, multiple: true, value: function (_observe) {
			var resolved = {}, certificates = _observe(this.master.certificates.applicable), result = []
			  , key;
			_observe(this.master.registrations.requested).forEach(function (registration) {
				_observe(registration.requirements).forEach(function (requirement) {
					if (certificates.some(function (cert) { return (cert.key === requirement.key); })) {
						// Given requirement is also a requested certificate,
						// therefore do not require it from user
						return;
					}
					resolved[requirement.key] = true;
				});
			});
			// Assure expected order of result set
			if (this.master.requirements.map.forEach) {
				this.master.requirements.map.forEach(function (value, key) {
					if (resolved.hasOwnProperty(key)) result.push(value);
				});
			} else {
				// There's no forEach on this.requirements map in legacy browsers mock
				for (key in this.master.requirements.map) {
					if (resolved.hasOwnProperty(key)) result.push(this.master.requirements.map[key]);
				}
			}
			return result;
		} },
		// Applicable out of resolved requirments set
		// Usually matches all resolved, but may address corner cases like:
		// "If both utilityBill and electricityBiil resolve from registrations, please
		// apply only electricityBill"
		applicable: { type: Requirement, value: function (_observe) {
			var result = [];
			this.resolved.forEach(function (requirement) {
				var isApplicable = requirement._get
					? _observe(requirement._isApplicable) : requirement.isApplicable;
				if (isApplicable) result.push(requirement);
			});
			return result;
		} },
		// Some requirements may require from user to fill some form fields
		// at guide steps (e.g. user may be forced to choose which documents he prefer to upload)
		// In such case below property indicates that all questions were answered by user.
		// It's one of the sub calculations that are needed to calcualate guide progress
		guideProgress: { type: Percentage, value: function (_observe) {
			var total = 0, valid = 0;
			this.applicable.forEach(function (requirement) {
				++total;
				valid += _observe(requirement._guideProgress);
			});
			if (!total) return 1;
			return valid / total;
		} }
	});
	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<|endoftext|>


<commit_before>import { fetchTree } from '@octolinker/helper-github-api';
import { bulkAction } from '@octolinker/helper-octolinker-api';
import uniqWith from 'lodash.uniqwith';
import isEqual from 'lodash.isequal';

function getRepoMetadata(data) {
  // Find first internal links which contains the information we need
  // TODO handle also githubSearch resolver results. Right now this works,
  // because a relative file resolver is used as well in the less and sass plugin
  return data.find(({ type }) => type === 'internal-link') || {};
}

function removeDuplicates(payload) {
  return uniqWith(payload, (left, right) =>
    // The link property is part of the item and prevents us from using a
    // simple isEqual operation. By concatenating all other props we can easily
    // identify duplications
    isEqual(
      left.type + left.registry + left.target,
      right.type + right.registry + right.target,
    ),
  );
}

function injectLiveDemoUrl(url) {
  return url.replace('https://github.com', process.env.OCTOLINKER_LIVE_DEMO);
}

function groupMatchesByType(matches) {
  const flattenUrls = removeDuplicates(
    [].concat(
      ...matches.map(match =>
        match.urls.map(url => ({
          ...url,
          link: match.link,
        })),
      ),
    ),
  );

  const apiItems = flattenUrls.filter(({ type }) =>
    ['registry', 'ping'].includes(type),
  );

  const internalItems = flattenUrls.filter(({ type }) =>
    ['internal-link', 'github-search'].includes(type),
  );

  const trustedItems = flattenUrls.filter(({ type }) =>
    ['trusted-url'].includes(type),
  );

  return {
    apiItems,
    internalItems,
    trustedItems,
  };
}

function insertLinks({
  matches,
  githubTree,
  octolinkerApiResponse,
  user,
  repo,
  branch,
}) {
  matches.forEach(({ urls, link }) => {
    if (link.href) {
      // Return early if link is already set
      return;
    }

    for (const item of urls) {
      if (item.type === 'internal-link') {
        if (githubTree.includes(item.path)) {
          link.href = item.url;
          if (process.env.OCTOLINKER_LIVE_DEMO) {
            link.href = injectLiveDemoUrl(link.href);
          }
          break;
        }
      } else if (item.type === 'github-search') {
        const allMatches = githubTree.filter(path =>
          path.endsWith(item.target),
        );

        if (allMatches.length === 1) {
          link.href = `https://github.com/${user}/${repo}/blob/${branch}/${allMatches[0]}`;
          if (process.env.OCTOLINKER_LIVE_DEMO) {
            link.href = injectLiveDemoUrl(link.href);
          }
          break;
        }
        // TODO implement https://www.npmjs.com/package/fast-levenshtein
      } else if (['registry', 'ping'].includes(item.type)) {
        const finalUrl = octolinkerApiResponse.find(
          ({ type, target }) =>
            (type === item.registry || type === item.type) &&
            target === item.target,
        );

        if (finalUrl && finalUrl.result) {
          link.href = finalUrl.result;
          if (process.env.OCTOLINKER_LIVE_DEMO) {
            link.href = injectLiveDemoUrl(link.href);
          }
          break;
        }
      }
    }
  });
}

export default async function(matches) {
  const { apiItems, internalItems, trustedItems } = groupMatchesByType(matches);

  let octolinkerApiResponsePromise = [];
  let githubTreePromise = [];

  if (apiItems.length) {
    octolinkerApiResponsePromise = bulkAction(apiItems);
  }

  let user;
  let repo;
  let branch;
  if (internalItems.length) {
    ({ user, repo, branch } = getRepoMetadata(internalItems));

    if (user && repo && branch) {
      githubTreePromise = fetchTree({ user, repo, branch });
    }
  }

  trustedItems.forEach(item => {
    item.link.href = item.target;
  });

  const octolinkerApiResponse = await octolinkerApiResponsePromise;
  const githubTree = await githubTreePromise;

  // TODO explore resolving on the fly so we don't have to wait for both calls to finish

  insertLinks({
    matches,
    octolinkerApiResponse,
    githubTree,
    user,
    repo,
    branch,
  });

  if (process.env.OCTOLINKER_LIVE_DEMO) {
    let parentElement = document;

    const activeLineNumber = window.location.hash.match(/#LO([0-9]+)/);
    if (activeLineNumber && activeLineNumber[1]) {
      parentElement = document.getElementById(`LC${activeLineNumber[1]}`);
    }

    const el = parentElement.querySelector('.octolinker-link[href]');
    if (el) {
      el.classList.add('octospotlight');

      const container = document.createElement('div');
      container.innerHTML = `<div class="octospotlight-inner">${el.innerHTML}</div>`;
      el.innerHTML = container.innerHTML;

      const spot = document.createElement('div');
      spot.classList.add('octospotlight-dot');
      el.querySelector('.octospotlight-inner').appendChild(spot);
    }
  }
}
<commit_msg>Deduplicate matches for OctoLinker API only (#811)<commit_after>import { fetchTree } from '@octolinker/helper-github-api';
import { bulkAction } from '@octolinker/helper-octolinker-api';
import uniqWith from 'lodash.uniqwith';
import isEqual from 'lodash.isequal';

function getRepoMetadata(data) {
  // Find first internal links which contains the information we need
  // TODO handle also githubSearch resolver results. Right now this works,
  // because a relative file resolver is used as well in the less and sass plugin
  return data.find(({ type }) => type === 'internal-link') || {};
}

function removeDuplicates(payload) {
  return uniqWith(payload, (left, right) =>
    // The link property is part of the item and prevents us from using a
    // simple isEqual operation. By concatenating all other props we can easily
    // identify duplications
    isEqual(
      left.type + left.registry + left.target,
      right.type + right.registry + right.target,
    ),
  );
}

function injectLiveDemoUrl(url) {
  return url.replace('https://github.com', process.env.OCTOLINKER_LIVE_DEMO);
}

function groupMatchesByType(matches) {
  const flattenUrls = [].concat(
    ...matches.map(match =>
      match.urls.map(url => ({
        ...url,
        link: match.link,
      })),
    ),
  );

  const apiItems = removeDuplicates(flattenUrls).filter(({ type }) =>
    ['registry', 'ping'].includes(type),
  );

  const internalItems = flattenUrls.filter(({ type }) =>
    ['internal-link', 'github-search'].includes(type),
  );

  const trustedItems = flattenUrls.filter(({ type }) =>
    ['trusted-url'].includes(type),
  );

  return {
    apiItems,
    internalItems,
    trustedItems,
  };
}

function insertLinks({
  matches,
  githubTree,
  octolinkerApiResponse,
  user,
  repo,
  branch,
}) {
  matches.forEach(({ urls, link }) => {
    if (link.href) {
      // Return early if link is already set
      return;
    }

    for (const item of urls) {
      if (item.type === 'internal-link') {
        if (githubTree.includes(item.path)) {
          link.href = item.url;
          if (process.env.OCTOLINKER_LIVE_DEMO) {
            link.href = injectLiveDemoUrl(link.href);
          }
          break;
        }
      } else if (item.type === 'github-search') {
        const allMatches = githubTree.filter(path =>
          path.endsWith(item.target),
        );

        if (allMatches.length === 1) {
          link.href = `https://github.com/${user}/${repo}/blob/${branch}/${allMatches[0]}`;
          if (process.env.OCTOLINKER_LIVE_DEMO) {
            link.href = injectLiveDemoUrl(link.href);
          }
          break;
        }
        // TODO implement https://www.npmjs.com/package/fast-levenshtein
      } else if (['registry', 'ping'].includes(item.type)) {
        const finalUrl = octolinkerApiResponse.find(
          ({ type, target }) =>
            (type === item.registry || type === item.type) &&
            target === item.target,
        );

        if (finalUrl && finalUrl.result) {
          link.href = finalUrl.result;
          if (process.env.OCTOLINKER_LIVE_DEMO) {
            link.href = injectLiveDemoUrl(link.href);
          }
          break;
        }
      }
    }
  });
}

export default async function(matches) {
  const { apiItems, internalItems, trustedItems } = groupMatchesByType(matches);

  let octolinkerApiResponsePromise = [];
  let githubTreePromise = [];

  if (apiItems.length) {
    octolinkerApiResponsePromise = bulkAction(apiItems);
  }

  let user;
  let repo;
  let branch;
  if (internalItems.length) {
    ({ user, repo, branch } = getRepoMetadata(internalItems));

    if (user && repo && branch) {
      githubTreePromise = fetchTree({ user, repo, branch });
    }
  }

  trustedItems.forEach(item => {
    item.link.href = item.target;
  });

  const octolinkerApiResponse = await octolinkerApiResponsePromise;
  const githubTree = await githubTreePromise;

  // TODO explore resolving on the fly so we don't have to wait for both calls to finish

  insertLinks({
    matches,
    octolinkerApiResponse,
    githubTree,
    user,
    repo,
    branch,
  });

  if (process.env.OCTOLINKER_LIVE_DEMO) {
    let parentElement = document;

    const activeLineNumber = window.location.hash.match(/#LO([0-9]+)/);
    if (activeLineNumber && activeLineNumber[1]) {
      parentElement = document.getElementById(`LC${activeLineNumber[1]}`);
    }

    const el = parentElement.querySelector('.octolinker-link[href]');
    if (el) {
      el.classList.add('octospotlight');

      const container = document.createElement('div');
      container.innerHTML = `<div class="octospotlight-inner">${el.innerHTML}</div>`;
      el.innerHTML = container.innerHTML;

      const spot = document.createElement('div');
      spot.classList.add('octospotlight-dot');
      el.querySelector('.octospotlight-inner').appendChild(spot);
    }
  }
}
<|endoftext|>


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

/**
 * @fileoverview Definitions for ECMAScript 5.
 * @see http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
 * @externs
 */

/**
 * @param {Object} selfObj Specifies the object to which |this| should point
 *     when the function is run. If the value is null or undefined, it will
 *     default to the global object.
 * @param {...*} var_args Additional arguments that are partially
 *     applied to fn.
 * @return {!Function} A partially-applied form of the Function on which
 *     bind() was invoked as a method.
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
 */
Function.prototype.bind = function(selfObj, var_args) {};

/**
 * @return {string}
 * @nosideeffects
 * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim
 */
String.prototype.trim = function() {};

/**
 * @return {string}
 * @nosideeffects
 * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimLeft
 */
String.prototype.trimLeft = function() {};

/**
 * @return {string}
 * @nosideeffects
 * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimRight
 */
String.prototype.trimRight = function() {};

/**
 * A object property descriptor used by Object.create, Object.defineProperty,
 * Object.defineProperties, Object.getOwnPropertyDescriptor.
 *
 * Note: not a real constructor.
 * @constructor
 */
var ObjectPropertyDescriptor = function(){};

/** @type {*} */
ObjectPropertyDescriptor.prototype.value;

/** @type {(function():?)||undefined} */
ObjectPropertyDescriptor.prototype.get;

/** @type {(function(?):void)||undefined} */
ObjectPropertyDescriptor.prototype.set;

/** @type {boolean|undefined} */
ObjectPropertyDescriptor.prototype.writable;

/** @type {boolean|undefined} */
ObjectPropertyDescriptor.prototype.enumerable;

/** @type {boolean|undefined} */
ObjectPropertyDescriptor.prototype.configurable;

/**
 * @param {Object} proto
 * @param {Object=} opt_properties  A map of ObjectPropertyDescriptors.
 * @return {!Object}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create
 */
Object.create = function(proto, opt_properties) {};

/**
 * @param {!Object} obj
 * @param {string} prop
 * @param {!Object} descriptor A ObjectPropertyDescriptor.
 * @return {!Object}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
 */
Object.defineProperty = function(obj, prop, descriptor) {};

/**
 * @param {!Object} obj
 * @param {!Object} props A map of ObjectPropertyDescriptors.
 * @return {!Object}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperties
 */
Object.defineProperties = function(obj, props) {};

/**
 * @param {!Object} obj
 * @param {string} prop
 * @return {Object.<ObjectPropertyDescriptor>}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
 */
Object.getOwnPropertyDescriptor = function(obj, prop) {};

/**
 * @param {!Object} obj
 * @return {Array.<string>}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
 */
Object.keys = function(obj) {};

/**
 * @param {!Object} obj
 * @return {Array.<string>}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
 */
Object.getOwnPropertyNames = function(obj) {};

/**
 * @param {!Object} obj
 * @return {Object}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf
 */
Object.getPrototypeOf = function(obj) {};

/**
 * @param {!T} obj
 * @return {!T}
 * @template T
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/preventExtensions
 */
Object.preventExtensions = function(obj) {};

/**
 * @param {!T} obj
 * @return {!T}
 * @template T
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/seal
 */
Object.seal = function(obj) {};

/**
 * @param {!T} obj
 * @return {!T}
 * @template T
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze
 */
Object.freeze = function(obj) {};

/**
 * @param {!Object} obj
 * @return {boolean}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isExtensible
 */
Object.isExtensible = function(obj) {};

/**
 * @param {!Object} obj
 * @return {boolean}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isSealed
 */
Object.isSealed = function(obj) {};

/**
 * @param {!Object} obj
 * @return {boolean}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isFrozen
 */
Object.isFrozen = function(obj) {};

/**
 * As per ECMAScript 5, 15.12.3.
 * @param {string=} opt_key The JSON key for this object.
 * @return {*} The serializable representation of this object. Note that this
 *     need not be a string. See http://goo.gl/PEUvs.
 */
Object.prototype.toJSON = function(opt_key) {};

/**
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString
 * @return {string}
 */
Date.prototype.toISOString = function() {};

/**
 * @param {*=} opt_ignoredKey
 * @return {string}
 * @override
 */
Date.prototype.toJSON = function(opt_ignoredKey) {};
<commit_msg><commit_after>/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Definitions for ECMAScript 5.
 * @see http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf
 * @externs
 */

/**
 * @param {Object} selfObj Specifies the object to which |this| should point
 *     when the function is run. If the value is null or undefined, it will
 *     default to the global object.
 * @param {...*} var_args Additional arguments that are partially
 *     applied to fn.
 * @return {!Function} A partially-applied form of the Function on which
 *     bind() was invoked as a method.
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
 */
Function.prototype.bind = function(selfObj, var_args) {};

/**
 * @return {string}
 * @nosideeffects
 * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim
 */
String.prototype.trim = function() {};

/**
 * @return {string}
 * @nosideeffects
 * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimLeft
 */
String.prototype.trimLeft = function() {};

/**
 * @return {string}
 * @nosideeffects
 * @see http://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/TrimRight
 */
String.prototype.trimRight = function() {};

/**
 * A object property descriptor used by Object.create, Object.defineProperty,
 * Object.defineProperties, Object.getOwnPropertyDescriptor.
 *
 * Note: not a real constructor.
 * @constructor
 */
var ObjectPropertyDescriptor = function(){};

/** @type {*} */
ObjectPropertyDescriptor.prototype.value;

/** @type {(function():?)||undefined} */
ObjectPropertyDescriptor.prototype.get;

/** @type {(function(?):void)||undefined} */
ObjectPropertyDescriptor.prototype.set;

/** @type {boolean|undefined} */
ObjectPropertyDescriptor.prototype.writable;

/** @type {boolean|undefined} */
ObjectPropertyDescriptor.prototype.enumerable;

/** @type {boolean|undefined} */
ObjectPropertyDescriptor.prototype.configurable;

/**
 * @param {Object} proto
 * @param {Object=} opt_properties  A map of ObjectPropertyDescriptors.
 * @return {!Object}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create
 */
Object.create = function(proto, opt_properties) {};

/**
 * @param {!Object} obj
 * @param {string} prop
 * @param {!Object} descriptor A ObjectPropertyDescriptor.
 * @return {!Object}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
 */
Object.defineProperty = function(obj, prop, descriptor) {};

/**
 * @param {!Object} obj
 * @param {!Object} props A map of ObjectPropertyDescriptors.
 * @return {!Object}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperties
 */
Object.defineProperties = function(obj, props) {};

/**
 * @param {!Object} obj
 * @param {string} prop
 * @return {Object.<ObjectPropertyDescriptor>}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
 */
Object.getOwnPropertyDescriptor = function(obj, prop) {};

/**
 * @param {!Object} obj
 * @return {Array.<string>}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
 */
Object.keys = function(obj) {};

/**
 * @param {!Object} obj
 * @return {Array.<string>}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames
 */
Object.getOwnPropertyNames = function(obj) {};

/**
 * @param {!Object} obj
 * @return {Object}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf
 */
Object.getPrototypeOf = function(obj) {};

/**
 * @param {!Object} obj
 * @return {void}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/preventExtensions
 */
Object.preventExtensions = function(obj) {};

/**
 * @param {!Object} obj
 * @return {void}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/seal
 */
Object.seal = function(obj) {};

/**
 * @param {!Object} obj
 * @return {void}
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze
 */
Object.freeze = function(obj) {};

/**
 * @param {!Object} obj
 * @return {boolean}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isExtensible
 */
Object.isExtensible = function(obj) {};

/**
 * @param {!Object} obj
 * @return {boolean}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isSealed
 */
Object.isSealed = function(obj) {};

/**
 * @param {!Object} obj
 * @return {boolean}
 * @nosideeffects
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/isFrozen
 */
Object.isFrozen = function(obj) {};

/**
 * As per ECMAScript 5, 15.12.3.
 * @param {string=} opt_key The JSON key for this object.
 * @return {*} The serializable representation of this object. Note that this
 *     need not be a string. See http://goo.gl/PEUvs.
 */
Object.prototype.toJSON = function(opt_key) {};

/**
 * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/toISOString
 * @return {string}
 */
Date.prototype.toISOString = function() {};

/**
 * @param {*=} opt_ignoredKey
 * @return {string}
 * @override
 */
Date.prototype.toJSON = function(opt_ignoredKey) {};
<|endoftext|>


<commit_before>
Sky = global.Sky || {};

Sky._publishes = {}; // XXX namespace pollution

Sky._poll_subscriptions = function (socket) { // XXX namespace pollution
  Fiber(function () {
    // what we send to the client.
    // {collection_name:
    //   {inserted: [objects], updated: [objects], removed: [ids]}}
    var results = {};
    // set of keys we touched in the update. things in the cache that
    // are not touched are removed.
    var touched_keys = {};

    var add_to_results = function (collection, which, id) {
      if (!(collection in results))
        results[collection] = {};
      if (!(which in results[collection]))
        results[collection][which] = [];
      results[collection][which].push(id);
    };

    // setup a channel object
    var channel = {
      send: function(collection_name, obj) {
        if (!(obj instanceof Array))
          obj = [obj];
        if (obj.length === 0)
          return;

        _.each(obj, function (o) {
          if (!o._id) {
            console.log("WARNING trying to send object without _id"); // XXX
            return;
          }

          // | not allowed in collection name?
          var key = collection_name + "|" + o._id;

          var cached = socket.sky.cache[key];
          socket.sky.cache[key] = o;
          touched_keys[key] = true;

          if (!cached)
            add_to_results(collection_name, 'inserted', o);
          else if (JSON.stringify(o) !== JSON.stringify(cached))
            // Not canonical order comparison or anything, but close
            // enough I hope. We may send some spurious updates?
            add_to_results(collection_name, 'updated', o);
          else {
            // cache hit. do nothing.
          }
        });
      }
    };

    // actually run the subscriptions.
    _.each(socket.sky.subs, function (sub) {
      var pub = Sky._publishes[sub.name];
      if (!pub) {
        // XXX error unknown publish
        console.log("ERROR UNKNOWN PUBLISH " + sub.name);
        return;
      }

      pub(channel, sub.args);
    });

    // compute the removed keys.
    var removed_keys = _.difference(_.keys(socket.sky.cache),
                                    _.keys(touched_keys));
    _.each(removed_keys, function (key) {
      // XXX parsing from the string is so ugly.
      var parts = key.split("|");
      if (!parts || parts.length !== 2) return;
      var collection_name = parts[0];
      var id = parts[1];

      add_to_results(collection_name, 'removed', id);
      delete socket.sky.cache[key];
    });

    // if (and only if) any changes, send to client
    for (var x in results) {
      socket.emit('published', results);
      break;
    }

    // inform the client that the subscription is ready to go
    _.each(socket.sky.subs, function (sub) {
      if (!sub.ready) {
        socket.emit('subscription_ready', sub._id);
        sub.ready = true;
      }
    });

  }).run();

};

_.extend(Sky, {
  is_server: true,
  is_client: false,

  /**
   * Returns a unique identifier as a string.
   *
   * XXX copied from minimongo/genid.js
   */
  _genId_counter:0, // XXX namespace pollution
  genId: function () {
    var key = (Sky._genId_counter++) + "#";
    key += (new Date()).getTime() + "#";
    key += Math.random() + "#";
    key += Math.random() + "#";
    key += Math.random() + "#";
    // XXX XXX eliminate require (easy, in this case..)
    var md5 = __skybreak_bootstrap__.require('crypto').createHash('md5');
    md5.update(key);
    var id = md5.digest('hex');
    return id;
  },

  /**
   * Defines a live query.
   *
   * @param name {String} identifier for query
   * @param callback {Function<channel, args>} OR
   *                 {Object<mongodb selector>}
   *
   * If callback is an object, not a function, this will automatically run
   * the selector through db.find and send the results to the client. The
   * 'name' argument is used as the colleciton name on both the server and
   * the client. This is just sugar.
   */
  publish: function (name, callback) {
    if (name in Sky._publishes) {
      // XXX error duplicate publish
      console.log("ERROR DUPLICATE PUBLISH " + name);
      return;
    }

    var func = callback;
    if (typeof callback !== 'function' &&
        callback instanceof Object) {
      var collection = Sky._collections[name];
      if (!collection)
        // XXX confusing error message
        throw new Error("No such collection " + JSON.stringify(name) +
                        " (while generating default publish function)");
      func = function (channel) {
        channel.send(name, collection.find(callback));
      };
    }

    Sky._publishes[name] = func;
  },

  subscribe: function () {
    // ignored on server
  },

  startup: function (callback) {
    __skybreak_bootstrap__.startup_hooks.push(callback);
  }
});

Sky._collections = {};
Sky.Collection = function (name) {
  if (!name)
    // XXX maybe support this using minimongo?
    throw new Error("Anonymous collections aren't allowed on the server");

  var ret = {
    _name: name,
    _api: {},

    // XXX there are probably a lot of little places where this API
    // and minimongo diverge. we should track each of those down and
    // kill it.

    insert: function (doc) {
      // do id allocation here, so we never end up with an ObjectID.
      // This only happens if some calls this directly on the server,
      // since normally ids are allocated on the client and sent over
      // the wire to us.
      if (! doc._id) {
        // copy doc because we mess with it. only shallow copy.
        new_doc = {};
        _.extend(new_doc, doc);
        doc = new_doc;
        doc._id = Sky.genId();
      }

      Sky._mongo_driver.insert(this._name, doc);

      // return the doc w/ _id, so we can use it.
      return doc;
    },

    find: function (selector, options) {
      return Sky._mongo_driver.find(this._name, selector, options);
    },

    findLive: function () {
      throw new Error("findLive isn't supported on the server");
    },

    update: function (selector, mod, options) {
      return Sky._mongo_driver.update(this._name, selector, mod, options);
    },

    remove: function (selector) {
      return Sky._mongo_driver.remove(this._name, selector);
    },

    schema: function () {
      // XXX not implemented yet
    },

    api: function (methods) {
      for (var method in methods) {
        this[method] = _.bind(methods[method], null);
        this._api[method] = methods[method];
      }
    }
  }

  if (name)
    Sky._collections[name] = ret;

  return ret;
};

__skybreak_bootstrap__.register_socket = function (socket) {
  socket.sky = {};
  socket.sky.subs = [];
  socket.sky.cache = {};

  // 5/sec updates tops, once every 10sec min.
  socket.sky.throttled_poll = _.throttle(function () {
    Sky._poll_subscriptions(socket)
  }, 50); // XXX only 50ms! for great speed. might want higher in prod.
  socket.sky.timer = setInterval(socket.sky.throttled_poll, 10000);
};

__skybreak_bootstrap__.register_subscription = function (socket, data) {
  socket.sky.subs.push(data);
  Sky._poll_subscriptions(socket);
};

__skybreak_bootstrap__.unregister_subscription = function (socket, data) {
  socket.sky.subs = _.filter(socket.sky.subs, function (x) {
    return x._id !== data._id;
  });
  Sky._poll_subscriptions(socket);
};

__skybreak_bootstrap__.run_handler = function (socket, data, other_sockets) {
  // XXX note that running this in a fiber means that two serial
  // requests from the client can try to execute in parallel.. we're
  // going to have to think that through at some point. also, consider
  // races against Sky.Collection(), though this shouldn't happen in
  // most normal use cases
  Fiber(function () {
    if (!('collection' in data) || !(data.collection in Sky._collections))
      // XXX gracefully report over the wire
      throw new Error("No such collection "+ JSON.stringify(data.collection));
    var collection = Sky._collections[data.collection];

    // XXX obviously, we're going to add validation and authentication
    // somewhere around here (and probably the ability to disable the
    // automatic mutators completely, too)
    if (data.type === 'insert')
      collection.insert(data.args);
    else if (data.type === 'update')
      collection.update(data.selector, data.mutator, data.options);
    else if (data.type === 'remove')
      collection.remove(data.selector);
    else if (data.type === 'method') {
      var func = collection._api[data.method];
      if (!func)
        throw new Error("No API method " + JSON.stringify(data.method) +
                        " on collection " + data.collection);
      func.apply(null, data.args);
    } else
      throw new Error("Bad handler type " + JSON.stringify(data.type));

    // XXX XXX should emit some kind of success/failure indication

    // after the handler, rerun all the subscriptions as stuff may have
    // changed.
    // XXX potential fast path for 'remove' -- we know which sockets
    // need the removal message; it's exactly the sockets that have
    // the item in their cache
    _.each(other_sockets, function(x) {
      if (x && x.sky) {
        x.sky.throttled_poll(); } });

  }).run();
};
<commit_msg>simplify publish API for tp0<commit_after>
Sky = global.Sky || {};

Sky._publishes = {}; // XXX namespace pollution

Sky._poll_subscriptions = function (socket) { // XXX namespace pollution
  Fiber(function () {
    // what we send to the client.
    // {collection_name:
    //   {inserted: [objects], updated: [objects], removed: [ids]}}
    var results = {};
    // set of keys we touched in the update. things in the cache that
    // are not touched are removed.
    var touched_keys = {};

    var add_to_results = function (collection, which, id) {
      if (!(collection in results))
        results[collection] = {};
      if (!(which in results[collection]))
        results[collection][which] = [];
      results[collection][which].push(id);
    };

    // setup a channel object
    var channel = {
      send: function(collection_name, obj) {
        if (!(obj instanceof Array))
          obj = [obj];
        if (obj.length === 0)
          return;

        _.each(obj, function (o) {
          if (!o._id) {
            console.log("WARNING trying to send object without _id"); // XXX
            return;
          }

          // | not allowed in collection name?
          var key = collection_name + "|" + o._id;

          var cached = socket.sky.cache[key];
          socket.sky.cache[key] = o;
          touched_keys[key] = true;

          if (!cached)
            add_to_results(collection_name, 'inserted', o);
          else if (JSON.stringify(o) !== JSON.stringify(cached))
            // Not canonical order comparison or anything, but close
            // enough I hope. We may send some spurious updates?
            add_to_results(collection_name, 'updated', o);
          else {
            // cache hit. do nothing.
          }
        });
      }
    };

    // actually run the subscriptions.
    _.each(socket.sky.subs, function (sub) {
      var pub = Sky._publishes[sub.name];
      if (!pub) {
        // XXX error unknown publish
        console.log("ERROR UNKNOWN PUBLISH " + sub.name);
        return;
      }

      pub(channel, sub.args);
    });

    // compute the removed keys.
    var removed_keys = _.difference(_.keys(socket.sky.cache),
                                    _.keys(touched_keys));
    _.each(removed_keys, function (key) {
      // XXX parsing from the string is so ugly.
      var parts = key.split("|");
      if (!parts || parts.length !== 2) return;
      var collection_name = parts[0];
      var id = parts[1];

      add_to_results(collection_name, 'removed', id);
      delete socket.sky.cache[key];
    });

    // if (and only if) any changes, send to client
    for (var x in results) {
      socket.emit('published', results);
      break;
    }

    // inform the client that the subscription is ready to go
    _.each(socket.sky.subs, function (sub) {
      if (!sub.ready) {
        socket.emit('subscription_ready', sub._id);
        sub.ready = true;
      }
    });

  }).run();

};

_.extend(Sky, {
  is_server: true,
  is_client: false,

  /**
   * Returns a unique identifier as a string.
   *
   * XXX copied from minimongo/genid.js
   */
  _genId_counter:0, // XXX namespace pollution
  genId: function () {
    var key = (Sky._genId_counter++) + "#";
    key += (new Date()).getTime() + "#";
    key += Math.random() + "#";
    key += Math.random() + "#";
    key += Math.random() + "#";
    // XXX XXX eliminate require (easy, in this case..)
    var md5 = __skybreak_bootstrap__.require('crypto').createHash('md5');
    md5.update(key);
    var id = md5.digest('hex');
    return id;
  },

  /**
   * Defines a live dataset that clients can subscribe to.
   *
   * @param name {String} identifier for query
   * @param options {Object}
   *
   * options to contain:
   *  - collection {Collection} collection; defaults to the collection
   *    named 'name' on disk in mongodb
   *  - selector {Function<args> OR Object} either a mongodb selector,
   *    or a function that takes the argument object passed to
   *    Sky.subscribe and returns a mongodb selector. default {}
   */
  publish: function (name, options) {
    if (name in Sky._publishes) {
      // XXX error duplicate publish
      console.log("ERROR DUPLICATE PUBLISH " + name);
      return;
    }

    options = options || {};
    var collection = options.collection || Sky._collections[name];
    if (!collection)
      throw new Error("No collection '" + name + "' found to publish. " +
                      "You can specify the collection explicitly with the " +
                      "'collection' option.");
    var selector = options.selector || {};
    var func = function (channel, args) {
      var s = selector;
      if (s instanceof Function)
        s = s(args);
      channel.send(name, collection.find(s));
    };

    Sky._publishes[name] = func;
  },

  subscribe: function () {
    // ignored on server
  },

  startup: function (callback) {
    __skybreak_bootstrap__.startup_hooks.push(callback);
  }
});

Sky._collections = {};
Sky.Collection = function (name) {
  if (!name)
    // XXX maybe support this using minimongo?
    throw new Error("Anonymous collections aren't allowed on the server");

  var ret = {
    _name: name,
    _api: {},

    // XXX there are probably a lot of little places where this API
    // and minimongo diverge. we should track each of those down and
    // kill it.

    insert: function (doc) {
      // do id allocation here, so we never end up with an ObjectID.
      // This only happens if some calls this directly on the server,
      // since normally ids are allocated on the client and sent over
      // the wire to us.
      if (! doc._id) {
        // copy doc because we mess with it. only shallow copy.
        new_doc = {};
        _.extend(new_doc, doc);
        doc = new_doc;
        doc._id = Sky.genId();
      }

      Sky._mongo_driver.insert(this._name, doc);

      // return the doc w/ _id, so we can use it.
      return doc;
    },

    find: function (selector, options) {
      return Sky._mongo_driver.find(this._name, selector, options);
    },

    findLive: function () {
      throw new Error("findLive isn't supported on the server");
    },

    update: function (selector, mod, options) {
      return Sky._mongo_driver.update(this._name, selector, mod, options);
    },

    remove: function (selector) {
      return Sky._mongo_driver.remove(this._name, selector);
    },

    schema: function () {
      // XXX not implemented yet
    },

    api: function (methods) {
      for (var method in methods) {
        this[method] = _.bind(methods[method], null);
        this._api[method] = methods[method];
      }
    }
  }

  if (name)
    Sky._collections[name] = ret;

  return ret;
};

__skybreak_bootstrap__.register_socket = function (socket) {
  socket.sky = {};
  socket.sky.subs = [];
  socket.sky.cache = {};

  // 5/sec updates tops, once every 10sec min.
  socket.sky.throttled_poll = _.throttle(function () {
    Sky._poll_subscriptions(socket)
  }, 50); // XXX only 50ms! for great speed. might want higher in prod.
  socket.sky.timer = setInterval(socket.sky.throttled_poll, 10000);
};

__skybreak_bootstrap__.register_subscription = function (socket, data) {
  socket.sky.subs.push(data);
  Sky._poll_subscriptions(socket);
};

__skybreak_bootstrap__.unregister_subscription = function (socket, data) {
  socket.sky.subs = _.filter(socket.sky.subs, function (x) {
    return x._id !== data._id;
  });
  Sky._poll_subscriptions(socket);
};

__skybreak_bootstrap__.run_handler = function (socket, data, other_sockets) {
  // XXX note that running this in a fiber means that two serial
  // requests from the client can try to execute in parallel.. we're
  // going to have to think that through at some point. also, consider
  // races against Sky.Collection(), though this shouldn't happen in
  // most normal use cases
  Fiber(function () {
    if (!('collection' in data) || !(data.collection in Sky._collections))
      // XXX gracefully report over the wire
      throw new Error("No such collection "+ JSON.stringify(data.collection));
    var collection = Sky._collections[data.collection];

    // XXX obviously, we're going to add validation and authentication
    // somewhere around here (and probably the ability to disable the
    // automatic mutators completely, too)
    if (data.type === 'insert')
      collection.insert(data.args);
    else if (data.type === 'update')
      collection.update(data.selector, data.mutator, data.options);
    else if (data.type === 'remove')
      collection.remove(data.selector);
    else if (data.type === 'method') {
      var func = collection._api[data.method];
      if (!func)
        throw new Error("No API method " + JSON.stringify(data.method) +
                        " on collection " + data.collection);
      func.apply(null, data.args);
    } else
      throw new Error("Bad handler type " + JSON.stringify(data.type));

    // XXX XXX should emit some kind of success/failure indication

    // after the handler, rerun all the subscriptions as stuff may have
    // changed.
    // XXX potential fast path for 'remove' -- we know which sockets
    // need the removal message; it's exactly the sockets that have
    // the item in their cache
    _.each(other_sockets, function(x) {
      if (x && x.sky) {
        x.sky.throttled_poll(); } });

  }).run();
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */
import rndstr from 'rndstr';
const crypto = require('crypto');
import App from '../../models/app';
import AuthSess from '../../models/auth-session';
import AccessToken from '../../models/access-token';

/**
 * @swagger
 * /auth/accept:
 *   post:
 *     summary: Accept a session
 *     parameters:
 *       - $ref: "#/parameters/NativeToken"
 *       -
 *         name: token
 *         description: Session Token
 *         in: formData
 *         required: true
 *         type: string
 *     responses:
 *       204:
 *         description: OK
 *
 *       default:
 *         description: Failed
 *         schema:
 *           $ref: "#/definitions/Error"
 */

/**
 * Accept
 *
 * @param {Object} params
 * @param {Object} user
 * @return {Promise<object>}
 */
module.exports = (params, user) =>
	new Promise(async (res, rej) =>
{
	// Get 'token' parameter
	const sesstoken = params.token;
	if (sesstoken == null) {
		return rej('token is required');
	}

	// Fetch token
	const session = await AuthSess
		.findOne({ token: sesstoken });

	if (session === null) {
		return rej('session not found');
	}

	// Generate access token
	const token = rndstr('a-zA-Z0-9', 32);

	// Fetch exist access token
	const exist = await AccessToken.findOne({
		app_id: session.app_id,
		user_id: user._id,
	});

	if (exist === null) {
		// Lookup app
		const app = await App.findOne({
			_id: session.app_id
		});

		// Generate Hash
		const sha256 = crypto.createHash('sha256');
		sha256.update(token + app.secret);
		const hash = sha256.digest('hex');

		// Insert access token doc
		await AccessToken.insert({
			created_at: new Date(),
			app_id: session.app_id,
			user_id: user._id,
			token: token,
			hash: hash
		});
	}

	// Update session
	await AuthSess.update(session._id, {
		user_id: user._id
	});

	// Response
	res();
});
<commit_msg>[API] Fix bug<commit_after>'use strict';

/**
 * Module dependencies
 */
import rndstr from 'rndstr';
const crypto = require('crypto');
import App from '../../models/app';
import AuthSess from '../../models/auth-session';
import AccessToken from '../../models/access-token';

/**
 * @swagger
 * /auth/accept:
 *   post:
 *     summary: Accept a session
 *     parameters:
 *       - $ref: "#/parameters/NativeToken"
 *       -
 *         name: token
 *         description: Session Token
 *         in: formData
 *         required: true
 *         type: string
 *     responses:
 *       204:
 *         description: OK
 *
 *       default:
 *         description: Failed
 *         schema:
 *           $ref: "#/definitions/Error"
 */

/**
 * Accept
 *
 * @param {Object} params
 * @param {Object} user
 * @return {Promise<object>}
 */
module.exports = (params, user) =>
	new Promise(async (res, rej) =>
{
	// Get 'token' parameter
	const sesstoken = params.token;
	if (sesstoken == null) {
		return rej('token is required');
	}

	// Fetch token
	const session = await AuthSess
		.findOne({ token: sesstoken });

	if (session === null) {
		return rej('session not found');
	}

	// Generate access token
	const token = rndstr('a-zA-Z0-9', 32);

	// Fetch exist access token
	const exist = await AccessToken.findOne({
		app_id: session.app_id,
		user_id: user._id,
	});

	if (exist === null) {
		// Lookup app
		const app = await App.findOne({
			_id: session.app_id
		});

		// Generate Hash
		const sha256 = crypto.createHash('sha256');
		sha256.update(token + app.secret);
		const hash = sha256.digest('hex');

		// Insert access token doc
		await AccessToken.insert({
			created_at: new Date(),
			app_id: session.app_id,
			user_id: user._id,
			token: token,
			hash: hash
		});
	}

	// Update session
	await AuthSess.update(session._id, {
		$set: {
			user_id: user._id
		}
	});

	// Response
	res();
});
<|endoftext|>


<commit_before>/**
 * _scripts/smooth-scrolling.js
 * Makes any same-page link animate instead of snapping to new point
 */

import jQuery from '~/lib/jquery'

jQuery.then(($) => {
    $(function () {
        $(document).on('click', 'a', function (event) {
            // Event already handled?
            if (event.isDefaultPrevented()) {
                return
            }

            // Get link href
            var $anchor = $(this)
            var href = $anchor.attr('href')
            if (href.indexOf('#') === -1) {
                return
            }

            // This handles /path/current-page#element
            href = href.split('#').pop()

            // Get offset
            var scrollTop
            if (href === '') {
                scrollTop = 0
            } else {
                var $target = $('#' + href)
                if (!$target.length) { // Anchor target not in this page
                    return
                }

                scrollTop = $target.offset().top
            }

            // Smooth scrolling
            $('html, body').stop().animate({
                scrollTop: scrollTop
            }, 'normal', function () {
                window.location.hash = href
            })

            event.preventDefault()
        })

        // Fix anchors
        // Let other scripts add links to DOM before fixing them
        setTimeout(function () {
            $('a[href^="#"]').each(function () {
                if (!$(this).hasClass('open-modal')) {
                    var href = $(this).attr('href')
                    $(this).attr('href', window.location.pathname + href)
                }
            })
        }, 0)
    })
})
<commit_msg>Fix hash external (#1408)<commit_after>/**
 * _scripts/smooth-scrolling.js
 * Makes any same-page link animate instead of snapping to new point
 */

import jQuery from '~/lib/jquery'

jQuery.then(($) => {
    $(function () {
        $(document).on('click', 'a', function (event) {
            // Event already handled?
            if (event.isDefaultPrevented()) return

            // Get link href
            var $anchor = $(this)
            var href = $anchor.attr('href')
            if (href.indexOf('#') === -1) return

            // This handles /path/current-page#element
            href = href.split('#').pop()
            if (href[0] === '!') return

            // Get offset
            var scrollTop
            if (href === '') {
                scrollTop = 0
            } else {
                var $target = $('#' + href)
                if (!$target.length) { // Anchor target not in this page
                    return
                }

                scrollTop = $target.offset().top
            }

            // Smooth scrolling
            $('html, body').stop().animate({
                scrollTop: scrollTop
            }, 'normal', function () {
                window.location.hash = href
            })

            event.preventDefault()
        })

        // Fix anchors
        // Let other scripts add links to DOM before fixing them
        setTimeout(function () {
            $('a[href^="#"]').each(function () {
                if (!$(this).hasClass('open-modal')) {
                    var href = $(this).attr('href')
                    $(this).attr('href', window.location.pathname + href)
                }
            })
        }, 0)
    })
})
<|endoftext|>


<commit_before>/*jshint globalstrict:false, strict:false, maxlen: 5000 */
/*global fail, assertTrue, assertFalse, assertEqual, assertNotEqual, assertTypeOf */

////////////////////////////////////////////////////////////////////////////////
/// @brief test the binary document interface
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB GmbH, Cologne, Germany
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2017, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

let jsunity = require("jsunity");
let arangodb = require("@arangodb");
let ERRORS = arangodb.errors;
let db = arangodb.db;
let internal = require("internal");
let fs = require("fs");
let wait = internal.wait;

function CollectionBinaryDocumentSuite () {
  'use strict';
  let cn = "UnitTestsCollectionBinary";
  let collection = null;

  return {
    setUp : function () {
      db._drop(cn);
      collection = db._create(cn, { waitForSync : false });
    },

    tearDown : function () {
      if (collection) {
        collection.unload();
        collection.drop();
        collection = null;
      }
      wait(0.0);
    },

    testBinaryDocument : function () {
      let text = "HALLO";
      let filename1 = fs.getTempFile('binary', false);

      fs.write(filename1, text);

      let d1 = collection._binaryInsert({_key: "test", meta: "hallo"}, filename1);

      assertTypeOf("string", d1._id);
      assertTypeOf("string", d1._key);
      assertTypeOf("string", d1._rev);
      assertEqual("UnitTestsCollectionBinary/test", d1._id);
      assertEqual("test", d1._key);

      let d3 = collection.document("test");

      assertTypeOf("string", d3._id);
      assertTypeOf("string", d3._key);
      assertTypeOf("string", d3._rev);
      assertTypeOf("string", d3._attachment);
      assertTypeOf("string", d3.meta);
      assertEqual("hallo", d3.meta);
      assertEqual("SEFMTE8=", d3._attachment);

      let filename2 = fs.getTempFile('binary', false);
      let d2 = collection._binaryDocument("test", filename2);

      assertTypeOf("string", d2._id);
      assertTypeOf("string", d2._key);
      assertTypeOf("string", d2._rev);
      assertTypeOf("string", d2.meta);
      assertEqual("hallo", d2.meta);

      let s = fs.read(filename2);
      assertEqual(text, s);
    }
  };
}

jsunity.run(CollectionBinaryDocumentSuite);

return jsunity.done();
<commit_msg>More realistic binary document test<commit_after>/*jshint globalstrict:false, strict:false, maxlen: 5000 */
/*global fail, assertTrue, assertFalse, assertEqual, assertNotEqual, assertTypeOf */

////////////////////////////////////////////////////////////////////////////////
/// @brief test the binary document interface
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2017 ArangoDB GmbH, Cologne, Germany
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2017, ArangoDB GmbH, Cologne, Germany
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

let jsunity = require("jsunity");
let arangodb = require("@arangodb");
let ERRORS = arangodb.errors;
let db = arangodb.db;
let internal = require("internal");
let fs = require("fs");
let path = require("path");
let wait = internal.wait;

function CollectionBinaryDocumentSuite () {
  'use strict';
  let cn = "UnitTestsCollectionBinary";
  let collection = null;

  return {
    setUp : function () {
      db._drop(cn);
      collection = db._create(cn, { waitForSync : false });
    },

    tearDown : function () {
      if (collection) {
        collection.unload();
        collection.drop();
        collection = null;
      }
      wait(0.0);
    },

    testBinaryDocument : function () {
      const filename1 = path.resolve('js/apps/system/_admin/aardvark/APP/frontend/img/arangodb_logo_letter.png'.split('/').join(path.sep));
      const content = fs.readFileSync(filename1);

      const d1 = collection._binaryInsert({_key: "test", meta: "hallo"}, filename1);

      assertTypeOf("string", d1._id);
      assertTypeOf("string", d1._key);
      assertTypeOf("string", d1._rev);
      assertEqual("UnitTestsCollectionBinary/test", d1._id);
      assertEqual("test", d1._key);

      const d3 = collection.document("test");

      assertTypeOf("string", d3._id);
      assertTypeOf("string", d3._key);
      assertTypeOf("string", d3._rev);
      assertTypeOf("string", d3._attachment);
      assertTypeOf("string", d3.meta);
      assertEqual("hallo", d3.meta);
      assertEqual(content.toString('base64'), d3._attachment);

      const filename2 = fs.getTempFile('binary', false);
      const d2 = collection._binaryDocument("test", filename2);

      assertTypeOf("string", d2._id);
      assertTypeOf("string", d2._key);
      assertTypeOf("string", d2._rev);
      assertTypeOf("string", d2.meta);
      assertEqual("hallo", d2.meta);

      const s = fs.readFileSync(filename2);
      assertEqual(content, s);
    }
  };
}

jsunity.run(CollectionBinaryDocumentSuite);

return jsunity.done();
<|endoftext|>


<commit_before>'use strict';

module.exports =

/*@ngInject*/
function SentencePlayCtrl (
  $scope, $state, _,
  SentenceLocalStorage,
  AnalyticsService, finalizeService,
  GrammarActivity
) {
  $scope.number = 0;

  //If we have a student param, then we have a valid session
  if ($state.params.student) {
    $scope.sessionId = $state.params.student;
  }

  //This is what we need to do after a student has completed the set
  $scope.finish = function () {
    var passageId = $state.params.passageId;
    if (passageId) { // Prevent explosions when there is no passage ID (started 'Sentence Writing' activity).
      var tempResults = SentenceLocalStorage.saveResults(passageId);
      AnalyticsService.trackSentenceWritingSubmission(tempResults, passageId);
    } else {
      passageId = null;
    }
    return finalizeService($scope.sessionId, passageId).then(function () {
      $state.go('.results', {
        student: $state.params.student
      });
    });
  };

  /*
   * Next question is a scope function that is called
   * when the student presses the next button. It advances
   * the pointer on the list of questions.
   */
  $scope.nextQuestion = function () {
    var nextQuestion = $scope.questions[_.indexOf($scope.questions, $scope.currentQuestion) + 1];
    if (!nextQuestion) {
      $scope.currentQuestion = null;
      $scope.currentConcept = null;
      $scope.number = $scope.number + 1;
      $scope.finish();
      return;
    }
    $scope.number = $scope.number + 1;
    $scope.currentQuestion = nextQuestion;
    $scope.currentConcept = $scope.grammarActivity.getConceptForQuestion($scope.currentQuestion);
  };

  /*
   * Here, we check for the **All Correct from PF Flag**
   */

  $scope.checkIfAllPfCorrect = function () {
    if ($state.params.pfAllCorrect) {
      return $scope.finish();
    }
  };
  $scope.checkIfAllPfCorrect();

  /*
   * Function execution stops here if the **All Correct PF Flag
   * is true.
   */

  /*
   * If we have a uid of a sentence writing activity, we fetch,
   * then build a list of rule ids with their needed quantity.
   *
   * If we have ids of rules, we default to a quantity of 3
   * for the max number of rule questions to retrieve.
   */
  var loadPromise;
  if ($state.params.uid) {
    loadPromise = GrammarActivity.getById($state.params.uid);
  } else if ($state.params.ids) {
    var ids = _.map(_.uniq($state.params.ids.split(',')), parseInt);
    loadPromise = GrammarActivity.fromPassageResults(ids, $state.params.passageId);
  } else if (!$state.params.pfAllCorrect) {
    throw new Error('Unable to load sentence writing. Please provide an activity ID or a set of rule IDs.');
  }
  if (loadPromise) {
    loadPromise.then(function (grammarActivity) {
      $scope.grammarActivity = grammarActivity;
      // FIXME: Get rid of this scope assignment and just use activity.selectedRuleQuestions.
      $scope.questions = grammarActivity.questions;
      $scope.currentQuestion = grammarActivity.questions[0];
      $scope.currentConcept = $scope.grammarActivity.getConceptForQuestion($scope.currentQuestion);
      $scope.showNextQuestion = false;
    });
  }

  /*
   * Format Description
   * This function takes a description and splits the sentences
   * into a unordered list of phrases and example sentences.
   * These two lists are divided by a horizontal rule bar.
   */
  $scope.formatDescription = function (des) {
    if (!des) {
      return;
    }
    var entries = des.split('.');
    var phrases = [];
    var sentences = [];
    _.each(entries, function (e) {
      e = '
' + e + '.
';
      if (e.indexOf(':') !== -1) {
        phrases.push(e);
      } else {
        sentences.push(e);
      }
    });
    var html = '
' + phrases.join('') + '

' + sentences.join('') + '
';
    return html;
  };
};
<commit_msg>Use number constructor instead of parseInt<commit_after>'use strict';

module.exports =

/*@ngInject*/
function SentencePlayCtrl (
  $scope, $state, _,
  SentenceLocalStorage,
  AnalyticsService, finalizeService,
  GrammarActivity
) {
  $scope.number = 0;

  //If we have a student param, then we have a valid session
  if ($state.params.student) {
    $scope.sessionId = $state.params.student;
  }

  //This is what we need to do after a student has completed the set
  $scope.finish = function () {
    var passageId = $state.params.passageId;
    if (passageId) { // Prevent explosions when there is no passage ID (started 'Sentence Writing' activity).
      var tempResults = SentenceLocalStorage.saveResults(passageId);
      AnalyticsService.trackSentenceWritingSubmission(tempResults, passageId);
    } else {
      passageId = null;
    }
    return finalizeService($scope.sessionId, passageId).then(function () {
      $state.go('.results', {
        student: $state.params.student
      });
    });
  };

  /*
   * Next question is a scope function that is called
   * when the student presses the next button. It advances
   * the pointer on the list of questions.
   */
  $scope.nextQuestion = function () {
    var nextQuestion = $scope.questions[_.indexOf($scope.questions, $scope.currentQuestion) + 1];
    if (!nextQuestion) {
      $scope.currentQuestion = null;
      $scope.currentConcept = null;
      $scope.number = $scope.number + 1;
      $scope.finish();
      return;
    }
    $scope.number = $scope.number + 1;
    $scope.currentQuestion = nextQuestion;
    $scope.currentConcept = $scope.grammarActivity.getConceptForQuestion($scope.currentQuestion);
  };

  /*
   * Here, we check for the **All Correct from PF Flag**
   */

  $scope.checkIfAllPfCorrect = function () {
    if ($state.params.pfAllCorrect) {
      return $scope.finish();
    }
  };
  $scope.checkIfAllPfCorrect();

  /*
   * Function execution stops here if the **All Correct PF Flag
   * is true.
   */

  /*
   * If we have a uid of a sentence writing activity, we fetch,
   * then build a list of rule ids with their needed quantity.
   *
   * If we have ids of rules, we default to a quantity of 3
   * for the max number of rule questions to retrieve.
   */
  var loadPromise;
  if ($state.params.uid) {
    loadPromise = GrammarActivity.getById($state.params.uid);
  } else if ($state.params.ids) {
    var ids = _.map(_.uniq($state.params.ids.split(',')), Number);
    loadPromise = GrammarActivity.fromPassageResults(ids, $state.params.passageId);
  } else if (!$state.params.pfAllCorrect) {
    throw new Error('Unable to load sentence writing. Please provide an activity ID or a set of rule IDs.');
  }
  if (loadPromise) {
    loadPromise.then(function (grammarActivity) {
      $scope.grammarActivity = grammarActivity;
      // FIXME: Get rid of this scope assignment and just use activity.selectedRuleQuestions.
      $scope.questions = grammarActivity.questions;
      $scope.currentQuestion = grammarActivity.questions[0];
      $scope.currentConcept = $scope.grammarActivity.getConceptForQuestion($scope.currentQuestion);
      $scope.showNextQuestion = false;
    });
  }

  /*
   * Format Description
   * This function takes a description and splits the sentences
   * into a unordered list of phrases and example sentences.
   * These two lists are divided by a horizontal rule bar.
   */
  $scope.formatDescription = function (des) {
    if (!des) {
      return;
    }
    var entries = des.split('.');
    var phrases = [];
    var sentences = [];
    _.each(entries, function (e) {
      e = '
' + e + '.
';
      if (e.indexOf(':') !== -1) {
        phrases.push(e);
      } else {
        sentences.push(e);
      }
    });
    var html = '
' + phrases.join('') + '

' + sentences.join('') + '
';
    return html;
  };
};
<|endoftext|>