<commit_before>/* jshint esversion: 6 */
const myConfig = require('./config.json');
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const bodyParser = require('body-parser');
const Discord = require('discord.js');
const bot = new Discord.Client({autoReconnect:true});
app.set('serverPort', myConfig.serverPort);
app.use(bodyParser.urlencoded({
  extended: true
}));
let botInvites = [];
app.use(bodyParser.json());
const prefix = '!'
bot.on('message', message => {
  if (!message.content.startsWith(prefix) || message.author.bot) return;
  const args = message.content.slice(prefix.length).split(' ');
  const command = args.shift().toLowerCase();
  if (command === 'kern') {
    message.channel.sendMessage('https://clips.twitch.tv/MistyNiceBibimbapTheRinger');
  } else if (command === 'kern2') {
    message.channel.sendMessage('https://clips.twitch.tv/SarcasticConcernedHedgehogTebowing');
  } else if (command === 'varcster') {
    message.channel.sendMessage('https://www.twitch.tv/videos/68214198');
  } else if (command === 'zesty') {
    message.channel.sendMessage('https://clips.twitch.tv/SpoopyFreezingWrenOSsloth');
  } else if (command === 'sephyr') {
    message.channel.sendMessage('https://www.youtube.com/watch?v=p9UZnxDVxTY');
  } else if (command === 'dimi') {
    message.channel.sendMessage('https://clips.twitch.tv/PowerfulFancyOcelotAliens');
  } else if (command === 'fire') {
    message.channel.sendMessage('https://youtu.be/V552exgHaWU');
  } else if (command === 'stagger') {
    message.channel.sendMessage('When they nerf stagger \n https://cdn.discordapp.com/attachments/444357789025042432/507587021213007883/image0.jpg');
  } else if (command === 'mrblizzard') {
    message.channel.sendMessage('https://youtu.be/dstyQa9JRgY');
  } else if (command === 'seabreeze') {
    message.channel.sendMessage(`PriestBot: How good is Seabreeze for shadow? Is the haste proc worth the lack of secondaries?\n\nIts not awful but its not amazing either. Mostly it doesnt change your gear prioritization. Its worth a tiny bit less than staves with BIS stats at the same ilvl`);
  } else if (command === 'deism') {
    message.channel.sendMessage('https://clips.twitch.tv/BashfulCourageousDootHassaanChop');
  } else if (command === 'bobcommands') {
    message.channel.sendMessage(`!kern\n!varcster\n!zesty\n!sephyr\n!dimi\n!fire\n!stagger\n!mrblizzard\n!seabreeze\n!kern2\n!deism\n!bobcommands`);
  }
});

bot.on('ready', () => {
  console.log('I am ready!');
  let guild = bot.guilds.find(val => val.id === myConfig.guildID);
  if (guild.available) {
    guild.fetchInvites()
      .then(invites => {
        for (const invite of invites.array()) {
          if (invite.inviter && invite.inviter.id &&
            invite.inviter.id === myConfig.botID && invite.uses !== null &&
            invite.uses === 0)
            botInvites.push(invite.code);
        }
      })
      .catch(console.error);
  }
});
bot.on('guildMemberAdd', (member) => {
  let guild = bot.guilds.find(val => val.id === myConfig.guildID);
  if (guild.available) {
    guild.fetchInvites()
      .then(invites => {
        for (const invite of invites.array()) {
          for (var i = 0; i < botInvites.length; i++) {
            if (invite.code === botInvites[i] && invite.uses >= 1) {
              createApplicantUser(member, invite, i);
            }
          }
        }
      })
      .catch(console.error);
  }
});

const createApplicantUser = (member, invite, i) => {
  member.addRole(myConfig.applicantRoleID)
    .then((member) => {
      if (invite.channel) {
        invite.channel.overwritePermissions(member, {
         SEND_MESSAGES: true,
         READ_MESSAGES: true,
         EMBED_LINKS: true,
         READ_MESSAGE_HISTORY:true,
         ATTACH_FILES: true
        })
        .then(() => {
          member.setNickname(invite.channel.name)
            .then((applicant) => {
              invite.channel.sendMessage(`${applicant.user} Thanks for applying to revive. You can find your application here please feel free to ask any questions.`);
            })
            .catch(console.error);
        })
        .catch(console.error);
      }
    })
    .catch(console.error);
  invite.delete()
    .then(invite => {
      botInvites.splice(i,1);
    })
    .catch(console.error);
};

app.post('/discord/createGuildApp', (req,res) => {
  const applicationData = {
    name:req.body.name,
    loc:req.body.loc,
    tag:req.body.tag,
    armory:req.body.armory,
    sscombat:req.body.sscombat,
    headset:req.body.headset,
    logs:req.body.logs,
    role:req.body.role,
    awp:req.body.awp,
    prog:req.body.prog,
    left:req.body.left,
    rotation:req.body.rotation,
    resources:req.body.resources,
    hear:req.body.hear,
    crit:req.body.crit,
    sit:req.body.sit,
    schedule:req.body.schedule,
    beta:req.body.beta,
    about:req.body.about,
    joke:req.body.joke
  };
  const applicationText = `**What is your Name, Age and Sex?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.name}\n\n
**Provide the location you will primarily will be playing from and a speedtest.net screenshot from the Chicago server**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.loc}\n\n
**What is your Battle.net Tag**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.tag}\n\n
**Link to your main characters and alternate character's armory **\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.armory}\n\n
**Post a screenshot of your character in combat**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.sscombat}\n\n
**Do you have a functioning headset with microphone that you can use in Discord?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.headset}\n\n
**Post your recent warcraft logs**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.logs}\n\n
**What role and spec are you applying for**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.role}\n\n
**Neck Level **\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.awp}\n\n
**List Previous Mythic/Heroic Progression Experiences**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.prog}\n\n
**Name your last TWO raiding guilds and the reason for leaving them?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.left}\n\n
**Take us through your opening rotation on a SINGLE TARGET fight. (Healers Ignore this)**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.rotation}\n\n
**What resources do you use to improve yourself?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.resources}\n\n
**How did you hear about us?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.hear}\n\n
**How do you handle criticism and adult humor?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.crit}\n\n
**You may be asked to sit on some fights for composition, gear, or general skill level how do you feel about that?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.sit}\n\n
**Do you have any commitments or schedule changes that we should know about?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.schedule}\n\n
**Are you willing to test new content in PTR/Alpha/Beta?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.beta}\n\n
**Tell us a little bit about yourself. The more we know the better!**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.about}\n\n
**Tell us a joke**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
${applicationData.joke}\n\n`;
  let guild = bot.guilds.find(val => val.id === myConfig.guildID);
  let _this = this;
  if (guild.available) {
    const channel = applicationData.tag.replace('#','-');
    const permissions = [
      { id: '231944378577321984', type: 'role', deny: 0, allow: 3072 },
      { id: '291440686916698112', type: 'role', deny: 0, allow: 3072 },
      { id: '231933464201527298', type: 'role', deny: 1024, allow: 0 },
      {id: '231944387255336960',type: 'role',deny: 8192,allow: 3072 },
      {id: '231944378476658690',type:'role',deny:0,allow:3072},
      {id: '231948453737922580',type: 'role',deny: 8192,allow: 3072 }
    ];
    guild.createChannel(channel, 'text', permissions)
     .then(channel => {
       channel.createInvite({
         temporary: false,
         maxUses: 2
       }).then(invite => {
        botInvites.push(invite.code);
        res.send(invite.url);
       })
       .catch(console.error);
       channel.sendMessage(`@everyone New application please review.`).then(message => {
         channel.sendMessage(applicationText, {split:true})
         .then(message => {
         })
         .catch(console.error);
       }).catch(console.error());
     })
     .catch(console.error);
  }
});

app.get('/discord', (req,res) => {
  res.send("Revive-Discord-Bot");
});

app.get('/discord/callback', (req,res) => {
  //Handle Discord Request URI?
});

bot.login(myConfig.discordKey);

server.listen(app.get('serverPort'));
console.log('Listening on port', app.get('serverPort'));
<commit_msg>new commands try to fix the image issues on doc splits<commit_after>/* jshint esversion: 6 */
const myConfig = require('./config.json');
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const bodyParser = require('body-parser');
const Discord = require('discord.js');
const bot = new Discord.Client({autoReconnect:true});
app.set('serverPort', myConfig.serverPort);
app.use(bodyParser.urlencoded({
  extended: true
}));
let botInvites = [];
app.use(bodyParser.json());
const prefix = '!'
bot.on('message', message => {
  if (!message.content.startsWith(prefix) || message.author.bot) return;
  const args = message.content.slice(prefix.length).split(' ');
  const command = args.shift().toLowerCase();
  if (command === 'kern') {
    message.channel.sendMessage('https://clips.twitch.tv/MistyNiceBibimbapTheRinger');
  } else if (command === 'kern2') {
    message.channel.sendMessage('https://clips.twitch.tv/SarcasticConcernedHedgehogTebowing');
  } else if (command === 'varcster') {
    message.channel.sendMessage('https://www.twitch.tv/videos/68214198');
  } else if (command === 'zesty') {
    message.channel.sendMessage('https://clips.twitch.tv/SpoopyFreezingWrenOSsloth');
  } else if (command === 'sephyr') {
    message.channel.sendMessage('https://www.youtube.com/watch?v=p9UZnxDVxTY');
  } else if (command === 'dimi') {
    message.channel.sendMessage('https://clips.twitch.tv/PowerfulFancyOcelotAliens');
  } else if (command === 'fire') {
    message.channel.sendMessage('https://youtu.be/V552exgHaWU');
  } else if (command === 'stagger') {
    message.channel.sendMessage('When they nerf stagger \n https://cdn.discordapp.com/attachments/444357789025042432/507587021213007883/image0.jpg');
  } else if (command === 'mrblizzard') {
    message.channel.sendMessage('https://youtu.be/dstyQa9JRgY');
  } else if (command === 'diminudes') {
    message.channel.sendMessage('<https://www.youtube.com/watch?v=dQw4w9WgXcQ>');
  } else if (command === 'seabreeze') {
    message.channel.sendMessage(`PriestBot: How good is Seabreeze for shadow? Is the haste proc worth the lack of secondaries?\n\nIts not awful but its not amazing either. Mostly it doesnt change your gear prioritization. Its worth a tiny bit less than staves with BIS stats at the same ilvl`);
  } else if (command === 'deism') {
    message.channel.sendMessage('https://clips.twitch.tv/BashfulCourageousDootHassaanChop');
  } else if (command === 'bthelight') {
    message.channel.sendMessage('https://www.twitch.tv/videos/127633788');
  } else if (command === 'swampletics') {
    message.channel.sendMessage(`Meet Swampletics, my Morytania locked ultimate ironman. After recently maxing my ultimate ironman I decided to up the ante, and forge my own journey from scratch. No banking, no trading, but this time I can't leave Morytania.\n<https://youtu.be/rk5XuqLrf3U>`)
  } else if (command === 'bobcommands') {
    message.channel.sendMessage(`!kern\n!varcster\n!zesty\n!sephyr\n!dimi\n!fire\n!stagger\n!mrblizzard\n!diminudes\n!seabreeze\n!kern2\n!deism\n!bthelight\n!swampletics\n!bobcommands`);
  }
});

bot.on('ready', () => {
  console.log('I am ready!');
  let guild = bot.guilds.find(val => val.id === myConfig.guildID);
  if (guild.available) {
    guild.fetchInvites()
      .then(invites => {
        for (const invite of invites.array()) {
          if (invite.inviter && invite.inviter.id &&
            invite.inviter.id === myConfig.botID && invite.uses !== null &&
            invite.uses === 0)
            botInvites.push(invite.code);
        }
      })
      .catch(console.error);
  }
});
bot.on('guildMemberAdd', (member) => {
  let guild = bot.guilds.find(val => val.id === myConfig.guildID);
  if (guild.available) {
    guild.fetchInvites()
      .then(invites => {
        for (const invite of invites.array()) {
          for (var i = 0; i < botInvites.length; i++) {
            if (invite.code === botInvites[i] && invite.uses >= 1) {
              createApplicantUser(member, invite, i);
            }
          }
        }
      })
      .catch(console.error);
  }
});

const createApplicantUser = (member, invite, i) => {
  member.addRole(myConfig.applicantRoleID)
    .then((member) => {
      if (invite.channel) {
        invite.channel.overwritePermissions(member, {
         SEND_MESSAGES: true,
         READ_MESSAGES: true,
         EMBED_LINKS: true,
         READ_MESSAGE_HISTORY:true,
         ATTACH_FILES: true
        })
        .then(() => {
          member.setNickname(invite.channel.name)
            .then((applicant) => {
              invite.channel.sendMessage(`${applicant.user} Thanks for applying to revive. You can find your application here please feel free to ask any questions.`);
            })
            .catch(console.error);
        })
        .catch(console.error);
      }
    })
    .catch(console.error);
  invite.delete()
    .then(invite => {
      botInvites.splice(i,1);
    })
    .catch(console.error);
};

app.post('/discord/createGuildApp', (req,res) => {
  const applicationData = {
    name:req.body.name,
    loc:req.body.loc,
    tag:req.body.tag,
    armory:req.body.armory,
    sscombat:req.body.sscombat,
    headset:req.body.headset,
    logs:req.body.logs,
    role:req.body.role,
    awp:req.body.awp,
    prog:req.body.prog,
    left:req.body.left,
    rotation:req.body.rotation,
    resources:req.body.resources,
    hear:req.body.hear,
    crit:req.body.crit,
    sit:req.body.sit,
    schedule:req.body.schedule,
    beta:req.body.beta,
    about:req.body.about,
    joke:req.body.joke
  };
  const applicationText = `**What is your Name, Age and Sex?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.name}>\n\n
**Provide the location you will primarily will be playing from and a speedtest.net screenshot from the Chicago server**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.loc}>\n\n
**What is your Battle.net Tag**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.tag}>\n\n
**Link to your main characters and alternate character's armory **\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.armory}>\n\n
**Post a screenshot of your character in combat**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.sscombat}>\n\n
**Do you have a functioning headset with microphone that you can use in Discord?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.headset}>\n\n
**Post your recent warcraft logs**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.logs}>\n\n
**What role and spec are you applying for**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.role}>\n\n
**Neck Level **\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.awp}>\n\n
**List Previous Mythic/Heroic Progression Experiences**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.prog}>\n\n
**Name your last TWO raiding guilds and the reason for leaving them?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.left}>\n\n
**Take us through your opening rotation on a SINGLE TARGET fight. (Healers Ignore this)**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.rotation}>\n\n
**What resources do you use to improve yourself?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.resources}>\n\n
**How did you hear about us?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.hear}>\n\n
**How do you handle criticism and adult humor?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.crit}>\n\n
**You may be asked to sit on some fights for composition, gear, or general skill level how do you feel about that?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.sit}>\n\n
**Do you have any commitments or schedule changes that we should know about?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.schedule}>\n\n
**Are you willing to test new content in PTR/Alpha/Beta?**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.beta}>\n\n
**Tell us a little bit about yourself. The more we know the better!**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.about}>\n\n
**Tell us a joke**\n
━━━━━━━━━━━━━━━━━━━━━━━━\n
<${applicationData.joke}>\n\n`;
  let guild = bot.guilds.find(val => val.id === myConfig.guildID);
  let _this = this;
  if (guild.available) {
    const channel = applicationData.tag.replace('#','-');
    const permissions = [
      { id: '231944378577321984', type: 'role', deny: 0, allow: 3072 },
      { id: '291440686916698112', type: 'role', deny: 0, allow: 3072 },
      { id: '231933464201527298', type: 'role', deny: 1024, allow: 0 },
      {id: '231944387255336960',type: 'role',deny: 8192,allow: 3072 },
      {id: '231944378476658690',type:'role',deny:0,allow:3072},
      {id: '231948453737922580',type: 'role',deny: 8192,allow: 3072 }
    ];
    guild.createChannel(channel, 'text', permissions)
     .then(channel => {
       channel.createInvite({
         temporary: false,
         maxUses: 2
       }).then(invite => {
        botInvites.push(invite.code);
        res.send(invite.url);
       })
       .catch(console.error);
       channel.sendMessage(`@everyone New application please review.`).then(message => {
         channel.sendMessage(applicationText, {split:true})
         .then(message => {
         })
         .catch(console.error);
       }).catch(console.error());
     })
     .catch(console.error);
  }
});

app.get('/discord', (req,res) => {
  res.send("Revive-Discord-Bot");
});

app.get('/discord/callback', (req,res) => {
  //Handle Discord Request URI?
});

bot.login(myConfig.discordKey);

server.listen(app.get('serverPort'));
console.log('Listening on port', app.get('serverPort'));
<|endoftext|>


<commit_before>var config = module.exports = {};

config.path = {
  build:    './www',
  styles:   ['./src/**/*.less'],
  scripts:  ['./src/**/*.js'],
  views:    ['./src/**/*.jade'],
  fonts:    ['./src/fonts/**/*'],
  images:   ['./src/images/**/*'],
  manifest: ['./src/manifest.webapp']
};

config.vendor = {
  fonts: [
    'bower_components/building-blocks/fonts/**/*'
  ],
  scripts: [
    'bower_components/jquery/dist/jquery.js',
    'bower_components/lodash/dist/lodash.js',
    'bower_components/angular/angular.js',
    'bower_components/angular-animate/angular-animate.js',
    'bower_components/angular-sanitize/angular-sanitize.js',
    'bower_components/angular-touch/angular-touch.js',
    'bower_components/angular-ui-router/release/angular-ui-router.js',
    'bower_components/angular-cache/dist/angular-cache.js'
  ],
  styles: [
    'bower_components/building-blocks/util.css',
    'bower_components/building-blocks/fonts.css',
    'bower_components/building-blocks/transitions.css',
    'bower_components/building-blocks/icons/**/*.css',
    'bower_components/building-blocks/style/**/*.css',
    'bower_components/building-blocks/style_unstable/**/*.css'
  ],
  images: [
    'bower_components/building-blocks/icons/**/*.png',
    'bower_components/building-blocks/style/**/*.png',
    'bower_components/building-blocks/style_unstable/**/*.png'
  ]
};<commit_msg>Add config parameters for configScript task<commit_after>var config = module.exports = {};

config.constants = {
  BUFFER_API_URL: 'https://api.bufferapp.com/1/',
  BUFFER_CLIENT_ID: '1234567890',
  CONNECT_URL: 'https://bufferapp.com/oauth2/authorize',
  REDIRECT_URI: 'http://www.google.co.uk',
  EXCHANGE_OAUTH_CODE_URL: 'http://www.random.url',
  TOKEN_KEY: 'accessToken'
};

config.path = {
  build:          './www',
  styles:         ['./src/**/*.less'],
  scripts:        ['./src/**/*.js'],
  configScripts:  ['./src/**/*.cfg'],
  views:          ['./src/**/*.jade'],
  fonts:          ['./src/fonts/**/*'],
  images:         ['./src/images/**/*'],
  manifest:       ['./src/manifest.webapp']
};

config.vendor = {
  fonts: [
    'bower_components/building-blocks/fonts/**/*'
  ],
  scripts: [
    'bower_components/jquery/dist/jquery.js',
    'bower_components/lodash/dist/lodash.js',
    'bower_components/angular/angular.js',
    'bower_components/angular-animate/angular-animate.js',
    'bower_components/angular-sanitize/angular-sanitize.js',
    'bower_components/angular-touch/angular-touch.js',
    'bower_components/angular-ui-router/release/angular-ui-router.js',
    'bower_components/angular-cache/dist/angular-cache.js'
  ],
  styles: [
    'bower_components/building-blocks/util.css',
    'bower_components/building-blocks/fonts.css',
    'bower_components/building-blocks/transitions.css',
    'bower_components/building-blocks/icons/**/*.css',
    'bower_components/building-blocks/style/**/*.css',
    'bower_components/building-blocks/style_unstable/**/*.css'
  ],
  images: [
    'bower_components/building-blocks/icons/**/*.png',
    'bower_components/building-blocks/style/**/*.png',
    'bower_components/building-blocks/style_unstable/**/*.png'
  ]
};<|endoftext|>


<commit_before>/*
 * jitsu.js: Top-level include for the jitsu module.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

require.paths.unshift(__dirname);

var path = require('path'),
    fs = require('fs');

var jitsu = exports,
    auth = false,
    started = false;

// Failure HTTP Response codes based
// off of `/lib/broodmother/slave/service.js`
var failCodes = jitsu.failCodes = {
  400: "Bad Request",
  403: "Not Authorized",
  404: "Item not found",
  500: "Internal Server Error"
};

// Success HTTP Response codes based
// off of `/lib/broodmother/slave/service.js`
var successCodes = jitsu.successCodes = {
  200: "OK",
  201: "Created"
};

jitsu.utils         = require('jitsu/utils');
jitsu.package       = require('jitsu/package');
jitsu.log           = require('cliff');
jitsu.prompt        = require('prompt');
jitsu.config        = require('jitsu/config');
jitsu.commands      = require('jitsu/commands');
jitsu.version       = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'))).version;

//
// Add the known properties to `jitsu.prompt` for later use
//
jitsu.prompt.properties = require('jitsu/properties').properties;

//
// Lazy load the REST clients for the nodejitsu APIs.
//
jitsu.api = {};
['Client', 'Apps', 'Snapshots', 'Users'].forEach(function (api) {
  jitsu.api.__defineGetter__(api, function () {
    return require('jitsu/api/' + api.toLowerCase())[api];
  });
});

//
// Alias the appropriate commands for simplier CLI usage
//
jitsu.commands.alias('create', { resource: 'apps', command: 'create' });
jitsu.commands.alias('deploy', { resource: 'apps', command: 'deploy' });
jitsu.commands.alias('list',   { resource: 'apps', command: 'list' });
jitsu.commands.alias('conf',   { resource: 'config', command: 'list' });
jitsu.commands.alias('logout', { resource: 'users', command: 'logout' });

var eyes = require('eyes'),
    winston = require('winston'),
    colors = require('colors');

//
// ### function start (command, callback)
// #### @command {string} Command to execute once started
// #### @callback {function} Continuation to pass control to when complete.
// Starts the jitsu CLI and runs the specified command.
//
jitsu.start = function (argv, callback) {
  var command = argv._;

  if(argv.version || argv.v) {
    console.log('v' + jitsu.version.join('.'));
    process.exit(0);
  }

  // Setup the initial prompt but don't leave it open
  jitsu.prompt.start().pause();

  //
  // Default to the `help` command.
  //
  command[0] || (command[0] = 'help');

  winston.info('Welcome to ' + 'Nodejitsu'.grey);
  winston.info('It worked if it ends with ' + 'Nodejitsu'.grey + ' ok'.green.bold);
  jitsu.utils.checkVersion(function () {
    jitsu.config.load(function (err) {
      if (!jitsu.config.username && jitsu.commands.requiresAuth(command[0])) {
        return jitsu.setupUser(function (err) {
          if (err) {
            return callback(err);
          }

          winston.info('Successfully configured user ' + jitsu.config.username.magenta);
          return command.length > 0 ? jitsu.exec(command, callback) : callback();
        });
      }

      return err ? callback(err) : jitsu.exec(command, callback);
    });
  });
};

//
// ### function exec (command, callback)
// #### @command {string} Command to execute
// #### @callback {function} Continuation to pass control to when complete.
// Runs the specified command in the jitsu CLI.
//
jitsu.exec = function (command, callback) {
  function execCommand (err) {
    if (err) {
      return callback(err);
    }

    winston.info('Executing command ' + command.join(' ').magenta);
    jitsu.commands.run(command, function (err, shallow) {
      if (err) {
        return jitsu.showError(command.join(' '), err, shallow);
      }

      //
      // TODO (indexzero): Something here
      //
      callback();
    });
  }

  return !started ? jitsu.setup(execCommand) : execCommand();
};

//
// ### function setup (callback)
// #### @callback {function} Continuation to pass control to when complete.
// Sets up the instances of the Resource clients for jitsu and tests
// the username/password combo for the authenticated user.
//
jitsu.setup = function (callback) {
  if (!started) {
    jitsu.users = new jitsu.api.Users(jitsu.config.settings);
    jitsu.apps = new jitsu.api.Apps(jitsu.config.settings);
    jitsu.snapshots = new jitsu.api.Snapshots(jitsu.config.settings);
    started = true;
  }

  callback();
};

//
// ### function auth (callback)
// #### @callback {function} Continuation to pass control to when complete.
// Attempts to authenicate the user identified in `jitsu.config.settings.auth`
//
jitsu.auth = function (callback) {
  if (auth) {
    return callback();
  }

  winston.silly('Attempting to authenticate as ' + jitsu.config.username.magenta);
  jitsu.users.auth(function (err, success) {
    if (err || !success) {
      winston.error('Unable to Authenticate as ' + jitsu.config.username.magenta);
      winston.error(err.message);
      return callback(err);
    }

    auth = true;
    winston.info('Authenticated as ' + jitsu.config.username.magenta);
    callback();
  });
};

//
// ### function setupUser (callback)
// #### @callback {function} Continuation to pass control to when complete.
// Prompts the user for their username / password combo, then sets up the jitsu CLI,
// and saves the resulting configuration to disk.
//
jitsu.setupUser = function (callback) {
  winston.info('No user has been setup on this machine');
  jitsu.prompt.get(['username', 'password'], function (err, result) {
    jitsu.config.settings.auth = [result.username, result.password].join(':');
    jitsu.setup(function () {
      jitsu.auth(function (err) {
        if (err) {
          return callback(err);
        }

        jitsu.config.save(function (err) {
          return err ? callback(err) : callback();
        });
      });
    });
  });
};

jitsu.showError = function (command, err, shallow, skip) {
  if (err.statusCode !== '403' && !skip) {
    winston.error('Error running command ' + command.magenta);

    if (err.message) {
      winston.error(err.message);
    }

    if (err.result && err.result.error) {
      winston.error(err.result.error);
    }
    else {
      if (err.stack && !shallow) {
        err.stack.split('\n').forEach(function (trace) {
          winston.error(trace);
        });
      }
    }
  }

  winston.info('Nodejitsu '.grey + 'not ok'.red.bold);
};<commit_msg>[fix] Reprompt the user for password if incorrect. Fixes #26<commit_after>/*
 * jitsu.js: Top-level include for the jitsu module.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

require.paths.unshift(__dirname);

var path = require('path'),
    fs = require('fs');

var jitsu = exports,
    auth = false,
    started = false;

// Failure HTTP Response codes based
// off of `/lib/broodmother/slave/service.js`
var failCodes = jitsu.failCodes = {
  400: "Bad Request",
  403: "Not Authorized",
  404: "Item not found",
  500: "Internal Server Error"
};

// Success HTTP Response codes based
// off of `/lib/broodmother/slave/service.js`
var successCodes = jitsu.successCodes = {
  200: "OK",
  201: "Created"
};

jitsu.utils         = require('jitsu/utils');
jitsu.package       = require('jitsu/package');
jitsu.log           = require('cliff');
jitsu.prompt        = require('prompt');
jitsu.config        = require('jitsu/config');
jitsu.commands      = require('jitsu/commands');
jitsu.version       = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'))).version;

//
// Add the known properties to `jitsu.prompt` for later use
//
jitsu.prompt.properties = require('jitsu/properties').properties;

//
// Lazy load the REST clients for the nodejitsu APIs.
//
jitsu.api = {};
['Client', 'Apps', 'Snapshots', 'Users'].forEach(function (api) {
  jitsu.api.__defineGetter__(api, function () {
    return require('jitsu/api/' + api.toLowerCase())[api];
  });
});

//
// Alias the appropriate commands for simplier CLI usage
//
jitsu.commands.alias('create', { resource: 'apps', command: 'create' });
jitsu.commands.alias('deploy', { resource: 'apps', command: 'deploy' });
jitsu.commands.alias('list',   { resource: 'apps', command: 'list' });
jitsu.commands.alias('conf',   { resource: 'config', command: 'list' });
jitsu.commands.alias('logout', { resource: 'users', command: 'logout' });

var eyes = require('eyes'),
    winston = require('winston'),
    colors = require('colors');

//
// ### function start (command, callback)
// #### @command {string} Command to execute once started
// #### @callback {function} Continuation to pass control to when complete.
// Starts the jitsu CLI and runs the specified command.
//
jitsu.start = function (argv, callback) {
  var command = argv._;

  if(argv.version || argv.v) {
    console.log('v' + jitsu.version.join('.'));
    process.exit(0);
  }

  // Setup the initial prompt but don't leave it open
  jitsu.prompt.start().pause();

  //
  // Default to the `help` command.
  //
  command[0] || (command[0] = 'help');

  winston.info('Welcome to ' + 'Nodejitsu'.grey);
  winston.info('It worked if it ends with ' + 'Nodejitsu'.grey + ' ok'.green.bold);
  jitsu.utils.checkVersion(function () {
    jitsu.config.load(function (err) {
      if (!jitsu.config.username && jitsu.commands.requiresAuth(command[0])) {
        return jitsu.setupUser(function (err) {
          if (err) {
            return callback(err);
          }

          winston.info('Successfully configured user ' + jitsu.config.username.magenta);
          return command.length > 0 ? jitsu.exec(command, callback) : callback();
        });
      }

      return err ? callback(err) : jitsu.exec(command, callback);
    });
  });
};

//
// ### function exec (command, callback)
// #### @command {string} Command to execute
// #### @callback {function} Continuation to pass control to when complete.
// Runs the specified command in the jitsu CLI.
//
jitsu.exec = function (command, callback) {
  function execCommand (err) {
    if (err) {
      return callback(err);
    }

    winston.info('Executing command ' + command.join(' ').magenta);
    jitsu.commands.run(command, function (err, shallow) {
      if (err) {
        return jitsu.showError(command.join(' '), err, shallow);
      }

      //
      // TODO (indexzero): Something here
      //
      callback();
    });
  }

  return !started ? jitsu.setup(execCommand) : execCommand();
};

//
// ### function setup (callback)
// #### @callback {function} Continuation to pass control to when complete.
// Sets up the instances of the Resource clients for jitsu and tests
// the username/password combo for the authenticated user.
//
jitsu.setup = function (callback) {
  if (!started) {
    jitsu.users = new jitsu.api.Users(jitsu.config.settings);
    jitsu.apps = new jitsu.api.Apps(jitsu.config.settings);
    jitsu.snapshots = new jitsu.api.Snapshots(jitsu.config.settings);
    started = true;
  }

  callback();
};

//
// ### function auth (callback)
// #### @callback {function} Continuation to pass control to when complete.
// Attempts to authenicate the user identified in `jitsu.config.settings.auth`
//
jitsu.auth = function (callback) {
  if (auth) {
    return callback();
  }

  winston.silly('Attempting to authenticate as ' + jitsu.config.username.magenta);
  jitsu.users.auth(function (err, success) {
    if (err || !success) {
      winston.error('Unable to Authenticate as ' + jitsu.config.username.magenta);
      winston.error(err.message);
      return callback(err);
    }

    auth = true;
    winston.info('Authenticated as ' + jitsu.config.username.magenta);
    callback();
  });
};

//
// ### function setupUser (callback)
// #### @callback {function} Continuation to pass control to when complete.
// Prompts the user for their username / password combo, then sets up the jitsu CLI,
// and saves the resulting configuration to disk.
//
jitsu.setupUser = function (callback) {
  winston.info('No user has been setup on this machine');

  //
  // Attempt to get the password three times.
  //
  var tries = 0;

  function getPassword (username) {
    jitsu.prompt.get(['password'], function (err, result) {
      if (err) {
        return callback(err);
      }

      jitsu.config.settings.auth = [username, result.password].join(':');
      jitsu.setup(function () {
        jitsu.auth(function (err) {
          //
          // Increment the auth attempts
          //
          tries += 1;

          if (err) {
            if (tries >= 3) {
              winston.error('Exiting. Three failed attempts as ' + username.magenta);
              return callback(err);
            }

            return getPassword(username);
          }

          jitsu.config.save(function (err) {
            return err ? callback(err) : callback();
          });
        });
      });
    });
  }

  jitsu.prompt.get(['username'], function (err, result) {
    return err ? callback(err) : getPassword(result.username);
  });
};

jitsu.showError = function (command, err, shallow, skip) {
  if (err.statusCode !== '403' && !skip) {
    winston.error('Error running command ' + command.magenta);

    if (err.message) {
      winston.error(err.message);
    }

    if (err.result && err.result.error) {
      winston.error(err.result.error);
    }
    else {
      if (err.stack && !shallow) {
        err.stack.split('\n').forEach(function (trace) {
          winston.error(trace);
        });
      }
    }
  }

  winston.info('Nodejitsu '.grey + 'not ok'.red.bold);
};<|endoftext|>


<commit_before>//= require_tree ./templates

;(function(window, wysihtml5) {
  window.AA = (window.AA || {})
  var config

  var Editor = function(options, el) {
    config          = options
    var _this       = this
    this.$el        = $(el)
    this.$textarea  = this.$el.find('textarea')
    this.policy     = this.$el.data('policy')

    this._addToolbar()
    this._attachEditor()
  }

  /**
   * Returns the wysihtml5 editor instance for this editor.
   */
  Editor.prototype.editor = function() {
    return this._editor
  }

  /**
   * Adds the wysihtml5 toolbar. If uploads are enabled, also adds the
   * necessary file inputs for uploading.
   */
  Editor.prototype._addToolbar = function() {
    var template = JST['active_admin/editor/templates/toolbar']({
      id: this.$el.attr('id') + '-toolbar'
    })

    this.$toolbar = $(template)

    if (config.uploads_enabled) {
      var _this = this
      this.$toolbar.find('input.uploadable').each(function() {
        _this._addUploader(this)
      })
    }

    this.$el.find('.wrap').prepend(this.$toolbar)
  }

  /**
   * Adds a file input attached to the supplied text input. And upload is
   * triggered if the source of the input is changed.
   *
   * @input Text input to attach a file input to.
   */
  Editor.prototype._addUploader = function(input) {
    var $input = $(input)

    var template = JST['active_admin/editor/templates/uploader']({ spinner: config.spinner })
    var $uploader = $(template)

    var $dialog = $input.closest('[data-wysihtml5-dialog]')
    $dialog.append($uploader)

    var _this = this
    $uploader.find('input:file').on('change', function() {
      var file = this.files[0]
      if (file) {
        $input.val('')
        _this.upload(file, function(location) {
          $input.val(location)
        })
      }
    })
  }

  /**
   * Initializes the wysihtml5 editor for the textarea.
   */
  Editor.prototype._attachEditor = function() {
    this._editor = new wysihtml5.Editor(this.$textarea.attr('id'), {
      toolbar: this.$toolbar.attr('id'),
      stylesheets: config.stylesheets,
      parserRules: config.parserRules
    })
  }

  /**
   * Sets the internal uploading state to true or false. Adds the .uploading
   * class to the root element for stying.
   *
   * @uploading {Boolean} Whether or not something is being uploaded.
   */
  Editor.prototype._uploading = function(uploading) {
    this.__uploading = uploading
    this.$el.toggleClass('uploading', this.__uploading)
    return this.__uploading
  }

  /**
   * Uploads a file to S3 or an custom action.
   *
   * @file The file to upload
   * @callback A function to be called when the upload completes.
   */
  Editor.prototype.upload = function(file, callback) {
    if (config.uploader_action_path == null) {
      this.s3_upload(file, callback);
    } else {
      this.action_upload(file, callback);
    }
  }

  /**
   * Uploads a file to a confured action under config.uploader_action_path.
   * When the upload is complete, calls callback with the location of the uploaded file.
   *
   * @file The file to upload
   * @callback A function to be called when the upload completes.
   */
  Editor.prototype.action_upload = function(file, callback) {
    var _this = this
    _this._uploading(true)

    var xhr = new XMLHttpRequest()
      , fd = new FormData()

    fd.append('_method', 'POST')
    fd.append($('meta[name="csrf-param"]').attr('content'), $('meta[name="csrf-token"]').attr('content'))
    fd.append('file', file)

    xhr.action_upload.addEventListener('progress', function(e) {
      _this.loaded   = e.loaded
      _this.total    = e.total
      _this.progress = e.loaded / e.total
    }, false)

    xhr.onreadystatechange = function() {
      if (xhr.readyState != 4) { return }
      _this._uploading(false)
      if (xhr.status == 200) {
        callback(xhr.getResponseHeader('Location'))
      } else {
        alert('Failed to upload file. Have you implemented action "' + config.uploader_action_path + '" correctly?')
      }
    }

    action_url = window.location.protocol + '//' + window.location.host + config.uploader_action_path
    xhr.open('POST', action_url, true)
    xhr.send(fd)

    return xhr
  }

  /**

  /**
   * Uploads a file to S3. When the upload is complete, calls callback with the
   * location of the uploaded file.
   *
   * @file The file to upload
   * @callback A function to be called when the upload completes.
   */
  Editor.prototype.s3_upload = function(file, callback) {
    var _this = this
    _this._uploading(true)

    var xhr = new XMLHttpRequest()
      , fd = new FormData()
      , key = config.storage_dir + '/' + Date.now().toString() + '-' + file.name

    fd.append('key', key)
    fd.append('AWSAccessKeyId', config.aws_access_key_id)
    fd.append('acl', 'public-read')
    fd.append('policy', this.policy.document)
    fd.append('signature', this.policy.signature)
    fd.append('Content-Type', file.type)
    fd.append('file', file)

    xhr.s3_upload.addEventListener('progress', function(e) {
      _this.loaded   = e.loaded
      _this.total    = e.total
      _this.progress = e.loaded / e.total
    }, false)

    xhr.onreadystatechange = function() {
      if (xhr.readyState != 4) { return }
      _this._uploading(false)
      if (xhr.status == 204) {
        callback(xhr.getResponseHeader('Location'))
      } else {
        alert('Failed to upload file. Have you configured S3 properly?')
      }
    }

    xhr.open('POST', 'https://' + config.s3_bucket + '.s3.amazonaws.com', true)
    xhr.send(fd)

    return xhr
  }

  window.AA.Editor = Editor
})(window, wysihtml5)

;(function(window, $) {
  if ($.widget) {
    $.widget.bridge('editor', window.AA.Editor)
  }
})(window, jQuery)
<commit_msg>Fixes Editor.upload so that it returns the xhr<commit_after>//= require_tree ./templates

;(function(window, wysihtml5) {
  window.AA = (window.AA || {})
  var config

  var Editor = function(options, el) {
    config          = options
    var _this       = this
    this.$el        = $(el)
    this.$textarea  = this.$el.find('textarea')
    this.policy     = this.$el.data('policy')

    this._addToolbar()
    this._attachEditor()
  }

  /**
   * Returns the wysihtml5 editor instance for this editor.
   */
  Editor.prototype.editor = function() {
    return this._editor
  }

  /**
   * Adds the wysihtml5 toolbar. If uploads are enabled, also adds the
   * necessary file inputs for uploading.
   */
  Editor.prototype._addToolbar = function() {
    var template = JST['active_admin/editor/templates/toolbar']({
      id: this.$el.attr('id') + '-toolbar'
    })

    this.$toolbar = $(template)

    if (config.uploads_enabled) {
      var _this = this
      this.$toolbar.find('input.uploadable').each(function() {
        _this._addUploader(this)
      })
    }

    this.$el.find('.wrap').prepend(this.$toolbar)
  }

  /**
   * Adds a file input attached to the supplied text input. And upload is
   * triggered if the source of the input is changed.
   *
   * @input Text input to attach a file input to.
   */
  Editor.prototype._addUploader = function(input) {
    var $input = $(input)

    var template = JST['active_admin/editor/templates/uploader']({ spinner: config.spinner })
    var $uploader = $(template)

    var $dialog = $input.closest('[data-wysihtml5-dialog]')
    $dialog.append($uploader)

    var _this = this
    $uploader.find('input:file').on('change', function() {
      var file = this.files[0]
      if (file) {
        $input.val('')
        _this.upload(file, function(location) {
          $input.val(location)
        })
      }
    })
  }

  /**
   * Initializes the wysihtml5 editor for the textarea.
   */
  Editor.prototype._attachEditor = function() {
    this._editor = new wysihtml5.Editor(this.$textarea.attr('id'), {
      toolbar: this.$toolbar.attr('id'),
      stylesheets: config.stylesheets,
      parserRules: config.parserRules
    })
  }

  /**
   * Sets the internal uploading state to true or false. Adds the .uploading
   * class to the root element for stying.
   *
   * @uploading {Boolean} Whether or not something is being uploaded.
   */
  Editor.prototype._uploading = function(uploading) {
    this.__uploading = uploading
    this.$el.toggleClass('uploading', this.__uploading)
    return this.__uploading
  }

  /**
   * Uploads a file to S3 or an custom action.
   *
   * @file The file to upload
   * @callback A function to be called when the upload completes.
   */
  Editor.prototype.upload = function(file, callback) {
    if (config.uploader_action_path == null) {
      return this.s3_upload(file, callback)
    } else {
      return this.action_upload(file, callback)
    }
  }

  /**
   * Uploads a file to a confured action under config.uploader_action_path.
   * When the upload is complete, calls callback with the location of the uploaded file.
   *
   * @file The file to upload
   * @callback A function to be called when the upload completes.
   */
  Editor.prototype.action_upload = function(file, callback) {
    var _this = this
    _this._uploading(true)

    var xhr = new XMLHttpRequest()
      , fd = new FormData()

    fd.append('_method', 'POST')
    fd.append($('meta[name="csrf-param"]').attr('content'), $('meta[name="csrf-token"]').attr('content'))
    fd.append('file', file)

    xhr.action_upload.addEventListener('progress', function(e) {
      _this.loaded   = e.loaded
      _this.total    = e.total
      _this.progress = e.loaded / e.total
    }, false)

    xhr.onreadystatechange = function() {
      if (xhr.readyState != 4) { return }
      _this._uploading(false)
      if (xhr.status == 200) {
        callback(xhr.getResponseHeader('Location'))
      } else {
        alert('Failed to upload file. Have you implemented action "' + config.uploader_action_path + '" correctly?')
      }
    }

    action_url = window.location.protocol + '//' + window.location.host + config.uploader_action_path
    xhr.open('POST', action_url, true)
    xhr.send(fd)

    return xhr
  }

  /**

  /**
   * Uploads a file to S3. When the upload is complete, calls callback with the
   * location of the uploaded file.
   *
   * @file The file to upload
   * @callback A function to be called when the upload completes.
   */
  Editor.prototype.s3_upload = function(file, callback) {
    var _this = this
    _this._uploading(true)

    var xhr = new XMLHttpRequest()
      , fd = new FormData()
      , key = config.storage_dir + '/' + Date.now().toString() + '-' + file.name

    fd.append('key', key)
    fd.append('AWSAccessKeyId', config.aws_access_key_id)
    fd.append('acl', 'public-read')
    fd.append('policy', this.policy.document)
    fd.append('signature', this.policy.signature)
    fd.append('Content-Type', file.type)
    fd.append('file', file)

    xhr.s3_upload.addEventListener('progress', function(e) {
      _this.loaded   = e.loaded
      _this.total    = e.total
      _this.progress = e.loaded / e.total
    }, false)

    xhr.onreadystatechange = function() {
      if (xhr.readyState != 4) { return }
      _this._uploading(false)
      if (xhr.status == 204) {
        callback(xhr.getResponseHeader('Location'))
      } else {
        alert('Failed to upload file. Have you configured S3 properly?')
      }
    }

    xhr.open('POST', 'https://' + config.s3_bucket + '.s3.amazonaws.com', true)
    xhr.send(fd)

    return xhr
  }

  window.AA.Editor = Editor
})(window, wysihtml5)

;(function(window, $) {
  if ($.widget) {
    $.widget.bridge('editor', window.AA.Editor)
  }
})(window, jQuery)
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/*jslint node: true, nomen: true, white: true*/
"use strict";

var crypto = require('crypto'),
    format = require('./compat').format,
    fs = require('fs'),
    path = require('path'),
    und = require('underscore');

var opts,
    _assets,
    _cache = {};

exports.setup = function (assets, options) {
  _assets = assets;
  if (!options) options = {};
  opts = options;
  if (options.production === undefined) {
    opts.production = true;
  }
  if (!options.root) opts.root = '.';
  if (!options.prefix) {
    opts.prefix = '';
  // Must end with a '/'
  } else if (options.prefix.slice(-1) !== '/') {
    opts.prefix += '/';
  }
  // Don't lead with a '/'
  if (opts.prefix.charAt(0) === '/') {
    opts.prefix = opts.prefix.slice(1);
  }
  if (options.control_headers === undefined) {
    opts.control_headers = false;
  }
  if (options.debug === undefined) {
    opts.debug = false;
  }

  return function (req, resp, next) {
    // If the first path element is a md5 hash AND one of the following:
    // * the path is opts.prefix
    // * the filename is a key in our opts[family]
    // * or the file exists on the filesystem
    var prefix = escape_regex(no_url(opts.prefix)),
        fmt = format('^\\/%s[a-f0-9]{10}', prefix),
        m = req.url.match(new RegExp(fmt)),
        true_path, exists;

    if (typeof resp.local === 'function') {
      resp.local('cachify_js', cachify_js);
      resp.local('cachify_css', cachify_css);
      resp.local('cachify', cachify);
    }
    else if (typeof resp.locals === 'function') {
      resp.locals({
        cachify_js: cachify_js,
        cachify_css: cachify_css,
        cachify: cachify
      });
    }
    if (m && m.index === 0) {
      // 10 first characters of md5 + 1 for slash
      true_path = req.url.slice(prefix.length + 11);
      exists = false;

      if (opts.prefix !== '' &&
          req.url.indexOf('/' + opts.prefix) === 0) {
        exists = true;
      } else if (assets[true_path]) {
        exists = true;
      }

      if (exists === false) {
        if (_cache[true_path]) {
          if (_cache[true_path].exists === true) {
            exists = true;
          }
        } else {
          // Would we ever want to use cachify for non-file resources?
          if (path.existsSync(path.join(opts.root, true_path))) {
            exists = true;
            _cache[true_path] = {exists: true};
          } else {
            // Hmm potential DOS Attack here... maybe we shouldn't cache
            // checking disk every time isn't that bad - just like static
            // middleware
            // Or use an LRU?
            console.warn('Cachify like URL, but no file found');
            _cache[true_path] = {exists: false};
          }
        }
      }
      if (exists === true) {
        resp.setHeader('Cache-Control', 'public, max-age=31536000');
        req.url = true_path;
        if (opts.control_headers === true) {
          resp.on('header', function () {
            // Relax other middleware... I got this one
            ['ETag', 'Last-Modified'].forEach(function (header) {
              if (resp.getHeader(header)) resp.removeHeader(header);
              if (resp.getHeader(header)) resp.removeHeader(header);
            });
          });
        }
      }
    }
    next();
  };
};

/**
 * If filename is an absolute path (starts with a '/') The hash
 * will be pre-pended with be slashified.
 * Examples:
 * /foo.js                   -> /a998d8e98f/foo.js
 * foo.js                    -> a998d8e98f/foo.js
 * http://example.com/foo.js -> http://example.com/foo.js
 */
var hashify = function (filename, hash) {
  if (typeof filename !== 'string')
    throw "cachify ERROR, expected string for filename, got " + filename;
  if (filename.indexOf('://') !== -1) {
    return filename;
  }
  if (! hash && opts.global_hash) {
    hash = opts.global_hash;
  }

  if (opts.production !== true &&
      opts.debug === false) {
    return filename;
  }
  if (hash) {
    // No-op, specifing a hash skips all this craziness
  } else if (_cache[filename] && _cache[filename].hash) {
    hash = _cache[filename].hash;
    //console.info('cache hit ', filename);
  } else {
    //console.info('cache miss ', filename);
    var md5 = crypto.createHash('md5');
    try {
      var data = fs.readFileSync(path.join(opts.root, filename));
      md5.update(data);
      // Expensive, maintain in-memory cache
      if (! _cache[filename]) _cache[filename] = {exists: true};
      hash = _cache[filename].hash = md5.digest('hex').slice(0, 10);
    } catch (e) {
      // Not intersting to cache, programmer error?
      console.error('Cachify bailing on hash... no such file ' + filename );
      console.error(e);
      return filename;
    }
  }
  if (opts.prefix.indexOf('://') === -1 &&
      filename[0] === '/') {
    return format('/%s%s%s', opts.prefix, hash, filename);
  } else {
    return format('%s%s%s%s', opts.prefix, hash, filename[0] === '/' ? '' : '/', filename);
  }
};

var prod_or_dev_tags = function (filename, link_fmt, hash) {
  if (opts.production !== true &&
      _assets[filename]) {
        return und.map(_assets[filename], function (f) {
          return format(link_fmt, hashify(f, hash));
        }).join('\n');
  }
  return format(link_fmt, hashify(filename, hash));
};

var cachify_js = exports.cachify_js = function (filename, options) {
  if (! options) options = {};
  return prod_or_dev_tags(filename,
                          '<script src="%s"></script>',
                          options.hash);
};

var cachify_css = exports.cachify_css = function (filename, options) {
  if (! options) options = {};
  return prod_or_dev_tags(filename,
                          '<link href="%s" rel="stylesheet" type="text/css">',
                          options.hash);
};

var cachify = exports.cachify = function (filename, options) {
  var tag_format;
  if (! options) options = {};
  if (options.tag_format)
      tag_format = options.tag_format;
  else
      tag_format = '%s';
  return prod_or_dev_tags(filename, tag_format, options.hash);
};

var no_url = function (prefix) {
  if (prefix.indexOf('://') === -1) return prefix;
  var m = prefix.match(/^[a-z]{3,5}:\/\/[a-z0-9\-_.]*(?:\:[0-9]*)?\/(.*)$/i);
  if (m) return m[1];
  else return prefix;
};

var escape_regex = function (str) {
  return str.replace('/', '\/');
};
<commit_msg>Add a new cachify_prefetch helper<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/*jslint node: true, nomen: true, white: true*/
"use strict";

var crypto = require('crypto'),
    format = require('./compat').format,
    fs = require('fs'),
    path = require('path'),
    und = require('underscore');

var opts,
    _assets,
    _cache = {};

exports.setup = function (assets, options) {
  _assets = assets;
  if (!options) options = {};
  opts = options;
  if (options.production === undefined) {
    opts.production = true;
  }
  if (!options.root) opts.root = '.';
  if (!options.prefix) {
    opts.prefix = '';
  // Must end with a '/'
  } else if (options.prefix.slice(-1) !== '/') {
    opts.prefix += '/';
  }
  // Don't lead with a '/'
  if (opts.prefix.charAt(0) === '/') {
    opts.prefix = opts.prefix.slice(1);
  }
  if (options.control_headers === undefined) {
    opts.control_headers = false;
  }
  if (options.debug === undefined) {
    opts.debug = false;
  }

  return function (req, resp, next) {
    // If the first path element is a md5 hash AND one of the following:
    // * the path is opts.prefix
    // * the filename is a key in our opts[family]
    // * or the file exists on the filesystem
    var prefix = escape_regex(no_url(opts.prefix)),
        fmt = format('^\\/%s[a-f0-9]{10}', prefix),
        m = req.url.match(new RegExp(fmt)),
        true_path, exists;

    if (typeof resp.local === 'function') {
      resp.local('cachify_prefetch', cachify_prefetch);
      resp.local('cachify_js', cachify_js);
      resp.local('cachify_css', cachify_css);
      resp.local('cachify', cachify);
    }
    else if (typeof resp.locals === 'function') {
      resp.locals({
        cachify_prefetch: cachify_prefetch,
        cachify_js: cachify_js,
        cachify_css: cachify_css,
        cachify: cachify
      });
    }
    if (m && m.index === 0) {
      // 10 first characters of md5 + 1 for slash
      true_path = req.url.slice(prefix.length + 11);
      exists = false;

      if (opts.prefix !== '' &&
          req.url.indexOf('/' + opts.prefix) === 0) {
        exists = true;
      } else if (assets[true_path]) {
        exists = true;
      }

      if (exists === false) {
        if (_cache[true_path]) {
          if (_cache[true_path].exists === true) {
            exists = true;
          }
        } else {
          // Would we ever want to use cachify for non-file resources?
          if (path.existsSync(path.join(opts.root, true_path))) {
            exists = true;
            _cache[true_path] = {exists: true};
          } else {
            // Hmm potential DOS Attack here... maybe we shouldn't cache
            // checking disk every time isn't that bad - just like static
            // middleware
            // Or use an LRU?
            console.warn('Cachify like URL, but no file found');
            _cache[true_path] = {exists: false};
          }
        }
      }
      if (exists === true) {
        resp.setHeader('Cache-Control', 'public, max-age=31536000');
        req.url = true_path;
        if (opts.control_headers === true) {
          resp.on('header', function () {
            // Relax other middleware... I got this one
            ['ETag', 'Last-Modified'].forEach(function (header) {
              if (resp.getHeader(header)) resp.removeHeader(header);
              if (resp.getHeader(header)) resp.removeHeader(header);
            });
          });
        }
      }
    }
    next();
  };
};

/**
 * If filename is an absolute path (starts with a '/') The hash
 * will be pre-pended with be slashified.
 * Examples:
 * /foo.js                   -> /a998d8e98f/foo.js
 * foo.js                    -> a998d8e98f/foo.js
 * http://example.com/foo.js -> http://example.com/foo.js
 */
var hashify = function (filename, hash) {
  if (typeof filename !== 'string')
    throw "cachify ERROR, expected string for filename, got " + filename;
  if (filename.indexOf('://') !== -1) {
    return filename;
  }
  if (! hash && opts.global_hash) {
    hash = opts.global_hash;
  }

  if (opts.production !== true &&
      opts.debug === false) {
    return filename;
  }
  if (hash) {
    // No-op, specifing a hash skips all this craziness
  } else if (_cache[filename] && _cache[filename].hash) {
    hash = _cache[filename].hash;
    //console.info('cache hit ', filename);
  } else {
    //console.info('cache miss ', filename);
    var md5 = crypto.createHash('md5');
    try {
      var data = fs.readFileSync(path.join(opts.root, filename));
      md5.update(data);
      // Expensive, maintain in-memory cache
      if (! _cache[filename]) _cache[filename] = {exists: true};
      hash = _cache[filename].hash = md5.digest('hex').slice(0, 10);
    } catch (e) {
      // Not intersting to cache, programmer error?
      console.error('Cachify bailing on hash... no such file ' + filename );
      console.error(e);
      return filename;
    }
  }
  if (opts.prefix.indexOf('://') === -1 &&
      filename[0] === '/') {
    return format('/%s%s%s', opts.prefix, hash, filename);
  } else {
    return format('%s%s%s%s', opts.prefix, hash, filename[0] === '/' ? '' : '/', filename);
  }
};

var prod_or_dev_tags = function (filename, link_fmt, hash) {
  if (opts.production !== true &&
      _assets[filename]) {
        return und.map(_assets[filename], function (f) {
          return format(link_fmt, hashify(f, hash));
        }).join('\n');
  }
  return format(link_fmt, hashify(filename, hash));
};

var cachify_js = exports.cachify_js = function (filename, options) {
  if (! options) options = {};
  return prod_or_dev_tags(filename,
                          '<script src="%s"></script>',
                          options.hash);
};

var cachify_css = exports.cachify_css = function (filename, options) {
  if (! options) options = {};
  return prod_or_dev_tags(filename,
                          '<link href="%s" rel="stylesheet" type="text/css">',
                          options.hash);
};

var cachify_prefetch = exports.cachify_prefetch = function (filename, options) {
  if (! options) options = {};
  return prod_or_dev_tags(filename,
                          '<link rel="prefetch" href="%s">',
                          options.hash);
};

var cachify = exports.cachify = function (filename, options) {
  var tag_format;
  if (! options) options = {};
  if (options.tag_format)
      tag_format = options.tag_format;
  else
      tag_format = '%s';
  return prod_or_dev_tags(filename, tag_format, options.hash);
};

var no_url = function (prefix) {
  if (prefix.indexOf('://') === -1) return prefix;
  var m = prefix.match(/^[a-z]{3,5}:\/\/[a-z0-9\-_.]*(?:\:[0-9]*)?\/(.*)$/i);
  if (m) return m[1];
  else return prefix;
};

var escape_regex = function (str) {
  return str.replace('/', '\/');
};
<|endoftext|>


<commit_before>var gulp = require("gulp"),
	replace = require("gulp-replace"),
	rename = require("gulp-rename"),
	sourcemaps = require("gulp-sourcemaps"),
	filter = require("gulp-filter"),
	changed = require("gulp-changed"),
	jasmine = require("gulp-jasmine"),
	common = require("./buildutils/gulp-common"),
	logging = require("./buildutils/logger-loader"),
	istanbul = require('gulp-istanbul'),
	gulpif = require("gulp-if"),
	minimist = require("minimist"),
	eslint = require('gulp-eslint');

var availableOptions = {
	'boolean': [ 'verbose', 'skipSourcemaps' ],
	'default': {
		'verbose': false,
		'skipSourcemaps': false
	}
}
var options = minimist(process.argv.slice(2), availableOptions);

function shouldSourcemap () {
	return !options.skipSourcemaps;
}
function isVerbose () {
	return !!options.verbose;
}

var src = ["core/**/*", "core/**/*"];

function compile(serverSide) {
	var codeFilter = filter(["**/*.js", "**/*.jsx"]);
	var dest = 'target/' + (serverSide ? "server" : "client");
	return gulp.src(src)
		.pipe(codeFilter)
			.pipe(changed(dest, {extension: '.js'}))
			.pipe(logging())
			.pipe(replace("SERVER_SIDE", serverSide ? "true" : "false"))
			.pipe(gulpif(shouldSourcemap(), sourcemaps.init()))
			.pipe(common.es6Transform())
			.pipe(gulpif(shouldSourcemap(), sourcemaps.write()))
			.pipe(rename(function (path) {
				path.extname = ".js";
			}))
		.pipe(codeFilter.restore())
		.pipe(gulp.dest(dest));
}

gulp.task('compile', ["compileClient", "compileServer"]);

gulp.task("compileClient", function() {
	return compile(false);
});

gulp.task("compileServer", function() {
	return compile(true);
});

gulp.task("build", ["compile"]);

gulp.task('watch', function () {
   gulp.watch(src, ["build"]);
});

gulp.task("test-coverage", ["compileServer", "compileClient"], function(cb) {
	gulp.src(['target/server/**/*.js', "!target/server/test/**/*.js", "!target/server/test-temp/**/*.js"])
		.pipe(istanbul({includeUntested:true})) // Covering files
		.pipe(gulp.dest("target/server-covered")) // copy covered files to a parallel directory
		.on('finish', function () {
			gulp.src("target/server/test/**/*.js")
				.pipe(gulp.dest("target/server-covered/test"))
				.on("finish", function() {
					gulp.src(['target/server-covered/test/**/*[Ss]pec.js'])
						.pipe(jasmine())
						.pipe(istanbul.writeReports({dir: './target/coverage'})) // Creating the reports after tests runned
						.on('end', cb);
    			});
    	});
});

gulp.task("test", ["compileServer", "compileClient"], function() {
	return gulp.src("target/server/test/**/*[Ss]pec.js")
		.pipe(jasmine(isVerbose() ? {verbose:true, includeStackTrace: true} : {}));
});

gulp.task("eslint", [], function() {
	return gulp.src(src)
		.pipe(codeFilter)
        // eslint() attaches the lint output to the eslint property
        // of the file object so it can be used by other modules.
        .pipe(eslint({
        	reset: true
        }))
        // eslint.format() outputs the lint results to the console.
        // Alternatively use eslint.formatEach() (see Docs).
        .pipe(eslint.format());
        // To have the process exit with an error code (1) on
        // lint error, return the stream and pipe to failOnError last.
        //.pipe(eslint.failWithError());
});

// todo: where should tests go?

// todo: add clean<commit_msg>Exclude the test folder<commit_after>var gulp = require("gulp"),
	replace = require("gulp-replace"),
	rename = require("gulp-rename"),
	sourcemaps = require("gulp-sourcemaps"),
	filter = require("gulp-filter"),
	changed = require("gulp-changed"),
	jasmine = require("gulp-jasmine"),
	common = require("./buildutils/gulp-common"),
	logging = require("./buildutils/logger-loader"),
	istanbul = require('gulp-istanbul'),
	gulpif = require("gulp-if"),
	minimist = require("minimist"),
	eslint = require('gulp-eslint');

var availableOptions = {
	'boolean': [ 'verbose', 'skipSourcemaps' ],
	'default': {
		'verbose': false,
		'skipSourcemaps': false
	}
}
var options = minimist(process.argv.slice(2), availableOptions);

function shouldSourcemap () {
	return !options.skipSourcemaps;
}
function isVerbose () {
	return !!options.verbose;
}

var src = ["core/**/*", "core/**/*"];

function compile(serverSide) {
	var codeFilter = filter(["**/*.js", "**/*.jsx"]);
	var dest = 'target/' + (serverSide ? "server" : "client");
	return gulp.src(src)
		.pipe(codeFilter)
			.pipe(changed(dest, {extension: '.js'}))
			.pipe(logging())
			.pipe(replace("SERVER_SIDE", serverSide ? "true" : "false"))
			.pipe(gulpif(shouldSourcemap(), sourcemaps.init()))
			.pipe(common.es6Transform())
			.pipe(gulpif(shouldSourcemap(), sourcemaps.write()))
			.pipe(rename(function (path) {
				path.extname = ".js";
			}))
		.pipe(codeFilter.restore())
		.pipe(gulp.dest(dest));
}

gulp.task('compile', ["compileClient", "compileServer"]);

gulp.task("compileClient", function() {
	return compile(false);
});

gulp.task("compileServer", function() {
	return compile(true);
});

gulp.task("build", ["compile"]);

gulp.task('watch', function () {
   gulp.watch(src, ["build"]);
});

gulp.task("test-coverage", ["compileServer", "compileClient"], function(cb) {
	gulp.src(['target/server/**/*.js', "!target/server/test/**/*.js", "!target/server/test-temp/**/*.js"])
		.pipe(istanbul({includeUntested:true})) // Covering files
		.pipe(gulp.dest("target/server-covered")) // copy covered files to a parallel directory
		.on('finish', function () {
			gulp.src("target/server/test/**/*.js")
				.pipe(gulp.dest("target/server-covered/test"))
				.on("finish", function() {
					gulp.src(['target/server-covered/test/**/*[Ss]pec.js'])
						.pipe(jasmine())
						.pipe(istanbul.writeReports({dir: './target/coverage'})) // Creating the reports after tests runned
						.on('end', cb);
    			});
    	});
});

gulp.task("test", ["compileServer", "compileClient"], function() {
	return gulp.src("target/server/test/**/*[Ss]pec.js")
		.pipe(jasmine(isVerbose() ? {verbose:true, includeStackTrace: true} : {}));
});

gulp.task("eslint", [], function() {
	var srcMinusTest = src;
	srcMinusTest.push("!core/test/**/*");
	return gulp.src(srcMinusTest)
        // eslint() attaches the lint output to the eslint property
        // of the file object so it can be used by other modules.
        .pipe(eslint())
        // eslint.format() outputs the lint results to the console.
        // Alternatively use eslint.formatEach() (see Docs).
        .pipe(eslint.format());
        // To have the process exit with an error code (1) on
        // lint error, return the stream and pipe to failOnError last.
        //.pipe(eslint.failWithError());
});

// todo: where should tests go?

// todo: add clean<|endoftext|>


<commit_before>import jsan from 'jsan';
import throttle from 'lodash/throttle';
import seralizeImmutable from 'remotedev-serialize/immutable/serialize';
import { getLocalFilter, isFiltered } from './filters';
import importState from './importState';
import generateId from './generateInstanceId';

const listeners = {};
export const source = '@devtools-page';

function tryCatchStringify(obj) {
  try {
    return JSON.stringify(obj);
  } catch (err) {
    /* eslint-disable no-console */
    if (process.env.NODE_ENV !== 'production') console.log('Failed to stringify', err);
    /* eslint-enable no-console */
    return jsan.stringify(obj, null, null, { circular: '[CIRCULAR]' });
  }
}

function stringify(obj, serialize) {
  if (typeof serialize === 'undefined') {
    return tryCatchStringify(obj);
  }
  return jsan.stringify(obj, serialize.replacer, null, serialize.options);
}

export function getSeralizeParameter(config, param) {
  const serialize = config.serialize;
  if (serialize) {
    if (serialize === true) return { options: true };
    if (serialize.immutable) {
      return {
        replacer: seralizeImmutable(serialize.immutable, serialize.refs).replacer,
        options: serialize.options || true
      };
    }
    if (!serialize.replacer) return { options: serialize.options };
    return { replacer: serialize.replacer, options: serialize.options || true };
  }

  const value = config[param];
  if (typeof value === 'undefined') return undefined;
  console.warn(`\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`); // eslint-disable-line

  if (typeof serializeState === 'boolean') return { options: value };
  if (typeof serializeState === 'function') return { replacer: value };
  return value;
}

function post(message) {
  window.postMessage(message, '*');
}

function amendActionType(action, serialize) {
  if (typeof action === 'string') return { action: { type: action }, timestamp: Date.now() };
  if (!action.type) return { action: { type: 'update' }, timestamp: Date.now() };
  if (action.action) return action;
  return {
    action: serialize ? action : { ...action, type: action.type.toString() },
    timestamp: Date.now()
  };
}

export function toContentScript(message, serializeState, serializeAction) {
  if (message.type === 'ACTION') {
    message.action = stringify(message.action, serializeAction);
    message.payload = stringify(message.payload, serializeState);
  } else if (message.type === 'STATE' || message.type === 'PARTIAL_STATE') {
    const { actionsById, computedStates, committedState, ...rest } = message.payload;
    message.payload = rest;
    message.actionsById = stringify(actionsById, serializeAction);
    message.computedStates = stringify(computedStates, serializeState);
    message.committedState = typeof committedState !== 'undefined';
  } else if (message.type === 'EXPORT') {
    message.payload = stringify(message.payload, serializeAction);
    if (typeof message.committedState !== 'undefined') {
      message.committedState = stringify(message.committedState, serializeState);
    }
  }
  post(message);
}

export function sendMessage(action, state, config, instanceId, name) {
  let amendedAction = action;
  if (typeof config !== 'object') {
    // Legacy: sending actions not from connected part
    config = {}; // eslint-disable-line no-param-reassign
    if (action) amendedAction = amendActionType(action);
  }
  const message = {
    type: action ? 'ACTION' : 'STATE',
    action: amendedAction,
    payload: state,
    maxAge: config.maxAge,
    source,
    name: config.name || name,
    instanceId: config.instanceId || instanceId || 1
  };
  toContentScript(message, config.serialize, config.serialize);
}

function handleMessages(event) {
  if (process.env.BABEL_ENV !== 'test' && (!event || event.source !== window)) return;
  const message = event.data;
  if (!message || message.source !== '@devtools-extension') return;
  Object.keys(listeners).forEach(id => {
    if (message.id && id !== message.id) return;
    if (typeof listeners[id] === 'function') listeners[id](message);
    else listeners[id].forEach(fn => { fn(message); });
  });
}

export function setListener(onMessage, instanceId) {
  listeners[instanceId] = onMessage;
  window.addEventListener('message', handleMessages, false);
}

const liftListener = (listener, config) => message => {
  let data = {};
  if (message.type === 'IMPORT') {
    data.type = 'DISPATCH';
    data.payload = {
      type: 'IMPORT_STATE',
      ...importState(message.state, config)
    };
  } else {
    data = message;
  }
  listener(data);
};

export function disconnect() {
  window.removeEventListener('message', handleMessages);
  post({ type: 'DISCONNECT', source });
}

export function connect(preConfig) {
  const config = preConfig || {};
  const id = generateId(config.instanceId);
  if (!config.instanceId) config.instanceId = id;
  if (!config.name) config.name = document.title && id === 1 ? document.title : `Instance ${id}`;
  if (config.serialize) config.serialize = getSeralizeParameter(config);
  const latency = config.latency;
  const predicate = config.predicate;
  const localFilter = getLocalFilter(config);
  const autoPause = config.autoPause;
  let isPaused = autoPause;
  let delayedActions = [];
  let delayedStates = [];

  const rootListiner = action => {
    if (autoPause) {
      if (action.type === 'START') isPaused = false;
      else if (action.type === 'STOP') isPaused = true;
    }
    if (action.type === 'DISPATCH') {
      const payload = action.payload;
      if (payload.type === 'PAUSE_RECORDING') {
        isPaused = payload.status;
        toContentScript({
          type: 'LIFTED',
          liftedState: { isPaused },
          instanceId: id,
          source
        });
      }
    }
  };

  listeners[id] = [rootListiner];

  const subscribe = (listener) => {
    if (!listener) return undefined;
    const liftedListener = liftListener(listener, config);
    listeners[id].push(liftedListener);

    return function unsubscribe() {
      const index = listeners[id].indexOf(liftedListener);
      listeners[id].splice(index, 1);
    };
  };

  const unsubscribe = () => {
    delete listeners[id];
  };

  const sendDelayed = throttle(() => {
    sendMessage(delayedActions, delayedStates, config);
    delayedActions = [];
    delayedStates = [];
  }, latency);

  const send = (action, state) => {
    if (isPaused || isFiltered(action, localFilter) || predicate && !predicate(state, action)) {
      return;
    }

    let amendedAction = action;
    const amendedState = config.stateSanitizer ? config.stateSanitizer(state) : state;
    if (action) {
      if (config.getActionType) {
        amendedAction = config.getActionType(action);
        if (typeof amendedAction !== 'object') {
          amendedAction = { action: { type: amendedAction }, timestamp: Date.now() };
        }
      }
      else if (config.actionSanitizer) amendedAction = config.actionSanitizer(action);
      amendedAction = amendActionType(amendedAction, config.serialize);
      if (latency) {
        delayedActions.push(amendedAction);
        delayedStates.push(amendedState);
        sendDelayed();
        return;
      }
    }
    sendMessage(amendedAction, amendedState, config);
  };

  const init = (state, liftedData) => {
    const message = {
      type: 'INIT',
      payload: stringify(state),
      instanceId: id,
      source
    };
    if (liftedData && Array.isArray(liftedData)) { // Legacy
      message.action = stringify(liftedData);
      message.name = config.name;
    } else {
      if (liftedData) {
        message.liftedState = liftedData;
        if (liftedData.isPaused) isPaused = true;
      }
      message.libConfig = {
        name: config.name || document.title,
        features: config.features,
        serialize: !!config.serialize,
        type: config.type
      };
      // TODO: add actionCreators
    }
    post(message);
  };

  const error = (payload) => {
    post({ type: 'ERROR', payload, id, source });
  };

  window.addEventListener('message', handleMessages, false);

  post({ type: 'INIT_INSTANCE', instanceId: id, source });

  return {
    init,
    subscribe,
    unsubscribe,
    send,
    error
  };
}

export function updateStore(stores) {
  return function(newStore, instanceId) {
    /* eslint-disable no-console */
    console.warn('`devToolsExtension.updateStore` is deprecated, remove it and just use ' +
      '`__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` instead of the extension\'s store enhancer: ' +
      'https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup');
    /* eslint-enable no-console */
    const store = stores[instanceId || Object.keys(stores)[0]];
    // Mutate the store in order to keep the reference
    store.liftedStore = newStore.liftedStore;
    store.getState = newStore.getState;
    store.dispatch = newStore.dispatch;
  };
}

export function isInIframe() {
  try {
    return window.self !== window.top;
  } catch (e) {
    return true;
  }
}
<commit_msg>Stringify only symbols<commit_after>import jsan from 'jsan';
import throttle from 'lodash/throttle';
import seralizeImmutable from 'remotedev-serialize/immutable/serialize';
import { getLocalFilter, isFiltered } from './filters';
import importState from './importState';
import generateId from './generateInstanceId';

const listeners = {};
export const source = '@devtools-page';

function tryCatchStringify(obj) {
  try {
    return JSON.stringify(obj);
  } catch (err) {
    /* eslint-disable no-console */
    if (process.env.NODE_ENV !== 'production') console.log('Failed to stringify', err);
    /* eslint-enable no-console */
    return jsan.stringify(obj, null, null, { circular: '[CIRCULAR]' });
  }
}

function stringify(obj, serialize) {
  if (typeof serialize === 'undefined') {
    return tryCatchStringify(obj);
  }
  return jsan.stringify(obj, serialize.replacer, null, serialize.options);
}

export function getSeralizeParameter(config, param) {
  const serialize = config.serialize;
  if (serialize) {
    if (serialize === true) return { options: true };
    if (serialize.immutable) {
      return {
        replacer: seralizeImmutable(serialize.immutable, serialize.refs).replacer,
        options: serialize.options || true
      };
    }
    if (!serialize.replacer) return { options: serialize.options };
    return { replacer: serialize.replacer, options: serialize.options || true };
  }

  const value = config[param];
  if (typeof value === 'undefined') return undefined;
  console.warn(`\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`); // eslint-disable-line

  if (typeof serializeState === 'boolean') return { options: value };
  if (typeof serializeState === 'function') return { replacer: value };
  return value;
}

function post(message) {
  window.postMessage(message, '*');
}

function amendActionType(action, serialize) {
  if (typeof action === 'string') return { action: { type: action }, timestamp: Date.now() };
  if (!action.type) return { action: { type: 'update' }, timestamp: Date.now() };
  if (action.action) return action;
  return {
    action: !serialize && typeof action.type === 'symbol' ?
      { ...action, type: action.type.toString() } : action,
    timestamp: Date.now()
  };
}

export function toContentScript(message, serializeState, serializeAction) {
  if (message.type === 'ACTION') {
    message.action = stringify(message.action, serializeAction);
    message.payload = stringify(message.payload, serializeState);
  } else if (message.type === 'STATE' || message.type === 'PARTIAL_STATE') {
    const { actionsById, computedStates, committedState, ...rest } = message.payload;
    message.payload = rest;
    message.actionsById = stringify(actionsById, serializeAction);
    message.computedStates = stringify(computedStates, serializeState);
    message.committedState = typeof committedState !== 'undefined';
  } else if (message.type === 'EXPORT') {
    message.payload = stringify(message.payload, serializeAction);
    if (typeof message.committedState !== 'undefined') {
      message.committedState = stringify(message.committedState, serializeState);
    }
  }
  post(message);
}

export function sendMessage(action, state, config, instanceId, name) {
  let amendedAction = action;
  if (typeof config !== 'object') {
    // Legacy: sending actions not from connected part
    config = {}; // eslint-disable-line no-param-reassign
    if (action) amendedAction = amendActionType(action);
  }
  const message = {
    type: action ? 'ACTION' : 'STATE',
    action: amendedAction,
    payload: state,
    maxAge: config.maxAge,
    source,
    name: config.name || name,
    instanceId: config.instanceId || instanceId || 1
  };
  toContentScript(message, config.serialize, config.serialize);
}

function handleMessages(event) {
  if (process.env.BABEL_ENV !== 'test' && (!event || event.source !== window)) return;
  const message = event.data;
  if (!message || message.source !== '@devtools-extension') return;
  Object.keys(listeners).forEach(id => {
    if (message.id && id !== message.id) return;
    if (typeof listeners[id] === 'function') listeners[id](message);
    else listeners[id].forEach(fn => { fn(message); });
  });
}

export function setListener(onMessage, instanceId) {
  listeners[instanceId] = onMessage;
  window.addEventListener('message', handleMessages, false);
}

const liftListener = (listener, config) => message => {
  let data = {};
  if (message.type === 'IMPORT') {
    data.type = 'DISPATCH';
    data.payload = {
      type: 'IMPORT_STATE',
      ...importState(message.state, config)
    };
  } else {
    data = message;
  }
  listener(data);
};

export function disconnect() {
  window.removeEventListener('message', handleMessages);
  post({ type: 'DISCONNECT', source });
}

export function connect(preConfig) {
  const config = preConfig || {};
  const id = generateId(config.instanceId);
  if (!config.instanceId) config.instanceId = id;
  if (!config.name) config.name = document.title && id === 1 ? document.title : `Instance ${id}`;
  if (config.serialize) config.serialize = getSeralizeParameter(config);
  const latency = config.latency;
  const predicate = config.predicate;
  const localFilter = getLocalFilter(config);
  const autoPause = config.autoPause;
  let isPaused = autoPause;
  let delayedActions = [];
  let delayedStates = [];

  const rootListiner = action => {
    if (autoPause) {
      if (action.type === 'START') isPaused = false;
      else if (action.type === 'STOP') isPaused = true;
    }
    if (action.type === 'DISPATCH') {
      const payload = action.payload;
      if (payload.type === 'PAUSE_RECORDING') {
        isPaused = payload.status;
        toContentScript({
          type: 'LIFTED',
          liftedState: { isPaused },
          instanceId: id,
          source
        });
      }
    }
  };

  listeners[id] = [rootListiner];

  const subscribe = (listener) => {
    if (!listener) return undefined;
    const liftedListener = liftListener(listener, config);
    listeners[id].push(liftedListener);

    return function unsubscribe() {
      const index = listeners[id].indexOf(liftedListener);
      listeners[id].splice(index, 1);
    };
  };

  const unsubscribe = () => {
    delete listeners[id];
  };

  const sendDelayed = throttle(() => {
    sendMessage(delayedActions, delayedStates, config);
    delayedActions = [];
    delayedStates = [];
  }, latency);

  const send = (action, state) => {
    if (isPaused || isFiltered(action, localFilter) || predicate && !predicate(state, action)) {
      return;
    }

    let amendedAction = action;
    const amendedState = config.stateSanitizer ? config.stateSanitizer(state) : state;
    if (action) {
      if (config.getActionType) {
        amendedAction = config.getActionType(action);
        if (typeof amendedAction !== 'object') {
          amendedAction = { action: { type: amendedAction }, timestamp: Date.now() };
        }
      }
      else if (config.actionSanitizer) amendedAction = config.actionSanitizer(action);
      amendedAction = amendActionType(amendedAction, config.serialize);
      if (latency) {
        delayedActions.push(amendedAction);
        delayedStates.push(amendedState);
        sendDelayed();
        return;
      }
    }
    sendMessage(amendedAction, amendedState, config);
  };

  const init = (state, liftedData) => {
    const message = {
      type: 'INIT',
      payload: stringify(state),
      instanceId: id,
      source
    };
    if (liftedData && Array.isArray(liftedData)) { // Legacy
      message.action = stringify(liftedData);
      message.name = config.name;
    } else {
      if (liftedData) {
        message.liftedState = liftedData;
        if (liftedData.isPaused) isPaused = true;
      }
      message.libConfig = {
        name: config.name || document.title,
        features: config.features,
        serialize: !!config.serialize,
        type: config.type
      };
      // TODO: add actionCreators
    }
    post(message);
  };

  const error = (payload) => {
    post({ type: 'ERROR', payload, id, source });
  };

  window.addEventListener('message', handleMessages, false);

  post({ type: 'INIT_INSTANCE', instanceId: id, source });

  return {
    init,
    subscribe,
    unsubscribe,
    send,
    error
  };
}

export function updateStore(stores) {
  return function(newStore, instanceId) {
    /* eslint-disable no-console */
    console.warn('`devToolsExtension.updateStore` is deprecated, remove it and just use ' +
      '`__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` instead of the extension\'s store enhancer: ' +
      'https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup');
    /* eslint-enable no-console */
    const store = stores[instanceId || Object.keys(stores)[0]];
    // Mutate the store in order to keep the reference
    store.liftedStore = newStore.liftedStore;
    store.getState = newStore.getState;
    store.dispatch = newStore.dispatch;
  };
}

export function isInIframe() {
  try {
    return window.self !== window.top;
  } catch (e) {
    return true;
  }
}
<|endoftext|>