<commit_before>module.exports = decodeURIComponent;
function decodeURIComponent(string) {
  let k = string.indexOf('%');
  if (k === -1) return string;

  const length = string.length;
  let decoded = '';
  let last = 0;
  let codepoint = 0;
  let startOfOctets = k;
  state = UTF8_ACCEPT;

  while (k > -1 && k < length - 2) {
    codepoint = decode(string, k, codepoint);

    switch (state) {
      case UTF8_ACCEPT:
        decoded += string.substring(last, startOfOctets);

        decoded += (codepoint <= 0xFFFF) ?
          String.fromCharCode(codepoint) :
          String.fromCharCode(
            (0xD7C0 + (codepoint >> 10)),
            (0xDC00 + (codepoint & 0x3FF))
          );

        codepoint = 0;
        last = k + 3;
        k = startOfOctets = string.indexOf('%', last);
        break;
      default:
        k += 3;
        if (k < length && string[k] === '%') break;

        // Intentional fall-through
      case UTF8_REJECT:
        state = UTF8_ACCEPT;
        k = startOfOctets = string.indexOf('%', k + 1);
        break;
    }
  }

  return decoded + string.substring(last);
}

const HEX = Object.assign(Object.create(null), {
  '0':  0, '1':  1,
  '2':  2, '3':  3,
  '4':  4, '5':  5,
  '6':  6, '7':  7,
  '8':  8, '9':  9,
  'a': 10, 'A': 10,
  'b': 11, 'B': 11,
  'c': 12, 'C': 12,
  'd': 13, 'D': 13,
  'e': 14, 'E': 14,
  'f': 15, 'F': 15,
});
function hexCodeToInt(c, badShift) {
  const i = HEX[c];
  return i === undefined ? 16 << badShift : i;
}

/**
 * The below algorithms are based on Bjoern Hoehrmann's DFA Unicode Decoder.
 * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
 * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
 */
const UTF8_ACCEPT = 0;
const UTF8_REJECT = 12;
const UTF8_DATA = [
  // The first part of the table maps bytes to character classes that
  // to reduce the size of the transition table and create bitmasks.
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
   8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
  10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
  // Special "bad" byte character classes, at all possible values of (X << 4 | 256) or (256 | Y),
  // where X and Y are 0 <= X <= 15.
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,

  // The second part is a transition table that maps a combination
  // of a state of the automaton and a character class to a state.
   0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
  12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
  12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
  12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
  12,36,12,12,12,12,12,12,12,12,12,12,

  // The third part maps the current character class to a mask that needs
  // to apply to it.
  0x7F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x07, 0x3F, 0x00, 0x3F, 0x0F, 0x07, 0x00, 0x00,
];

let state = UTF8_ACCEPT;
function decode(string, k, codepoint) {
  const high = hexCodeToInt(string[k + 1], 0);
  const low = hexCodeToInt(string[k + 2], 4);
  const byte = (high << 4) | low;

  const type = UTF8_DATA[byte];
  const mask = UTF8_DATA[605 + type];

  state = UTF8_DATA[497 + state + type];
  return (codepoint << 6) | (byte & mask);
}
<commit_msg>Don't forget to reset codepoint on rejection<commit_after>module.exports = decodeURIComponent;
function decodeURIComponent(string) {
  let k = string.indexOf('%');
  if (k === -1) return string;

  const length = string.length;
  let decoded = '';
  let last = 0;
  let codepoint = 0;
  let startOfOctets = k;
  state = UTF8_ACCEPT;

  while (k > -1 && k < length - 2) {
    codepoint = decode(string, k, codepoint);

    switch (state) {
      case UTF8_ACCEPT:
        decoded += string.substring(last, startOfOctets);

        decoded += (codepoint <= 0xFFFF) ?
          String.fromCharCode(codepoint) :
          String.fromCharCode(
            (0xD7C0 + (codepoint >> 10)),
            (0xDC00 + (codepoint & 0x3FF))
          );

        codepoint = 0;
        last = k + 3;
        k = startOfOctets = string.indexOf('%', last);
        break;
      default:
        k += 3;
        if (k < length && string[k] === '%') break;

        // Intentional fall-through
      case UTF8_REJECT:
        state = UTF8_ACCEPT;
        codepoint = 0;
        k = startOfOctets = string.indexOf('%', k + 1);
        break;
    }
  }

  return decoded + string.substring(last);
}

const HEX = Object.assign(Object.create(null), {
  '0':  0, '1':  1,
  '2':  2, '3':  3,
  '4':  4, '5':  5,
  '6':  6, '7':  7,
  '8':  8, '9':  9,
  'a': 10, 'A': 10,
  'b': 11, 'B': 11,
  'c': 12, 'C': 12,
  'd': 13, 'D': 13,
  'e': 14, 'E': 14,
  'f': 15, 'F': 15,
});
function hexCodeToInt(c, badShift) {
  const i = HEX[c];
  return i === undefined ? 16 << badShift : i;
}

/**
 * The below algorithms are based on Bjoern Hoehrmann's DFA Unicode Decoder.
 * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
 * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
 */
const UTF8_ACCEPT = 0;
const UTF8_REJECT = 12;
const UTF8_DATA = [
  // The first part of the table maps bytes to character classes that
  // to reduce the size of the transition table and create bitmasks.
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
   8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
  10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
  // Special "bad" byte character classes, at all possible values of (X << 4 | 256) or (256 | Y),
  // where X and Y are 0 <= X <= 15.
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,
  13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,

  // The second part is a transition table that maps a combination
  // of a state of the automaton and a character class to a state.
   0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
  12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
  12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
  12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
  12,36,12,12,12,12,12,12,12,12,12,12,

  // The third part maps the current character class to a mask that needs
  // to apply to it.
  0x7F, 0x3F, 0x1F, 0x0F, 0x0F, 0x07, 0x07, 0x3F, 0x00, 0x3F, 0x0F, 0x07, 0x00, 0x00,
];

let state = UTF8_ACCEPT;
function decode(string, k, codepoint) {
  const high = hexCodeToInt(string[k + 1], 0);
  const low = hexCodeToInt(string[k + 2], 4);
  const byte = (high << 4) | low;

  const type = UTF8_DATA[byte];
  const mask = UTF8_DATA[605 + type];

  state = UTF8_DATA[497 + state + type];
  return (codepoint << 6) | (byte & mask);
}
<|endoftext|>


<commit_before>// Imports
var Logger = require('./modules/Logger');
var Commands = require('./modules/CommandList');
var GameServer = require('./GameServer');
var figlet = require('figlet');

// Init variables
var showConsole = true;

// Start msg
Logger.start();

process.on('exit', function (code) {
    Logger.debug("process.exit(" + code + ")");
    Logger.shutdown();
});

process.on('uncaughtException', function (err) {
    Logger.fatal(err.stack);
    process.exit(1);
});

// Run Ogar
var gameServer = new GameServer();
Logger.info("\u001B[1m\u001B[32mMultiOgar-Edited " + gameServer.version + "\u001B[37m - An open source multi-protocol ogar server\u001B[0m");

// Handle arguments
process.argv.forEach(function (item) {

    switch (item){
        case "--help":
            console.log("Proper Usage: node index.js");
            console.log("    -n, --name             Set name");
            console.log("    -g, --gameport         Set game port");
            console.log("    -s, --statsport        Set stats port");
            console.log("    -m, --gamemode         Set game mode (id)");
            console.log("    -c, --connections      Set max connections limit");
            console.log("    -t, --tracker          Set serverTracker");
            console.log("    --noconsole            Disables the console");
            console.log("    --help                 Help menu");
            console.log("");
            break;

        case "-n":
        case "--name":
            setParam("serverName", getValue(item));
            break;

        case "-g":
        case "--gameport":
            setParam("serverPort", parseInt(getValue(item)));
            break;
        case "-s":
        case "--statsport":
            setParam("serverStatsPort", parseInt(getValue(item)));
            break;

        case "-m":
        case "--gamemode":
            setParam("serverGamemode", getValue(item));
            break;

        case "-c":
        case "--connections":
            setParam("serverMaxConnections", parseInt(getValue(item)));
            break;
        case "-t":
        case "--tracker":
            setParam("serverTracker", parseInt(getValue(item)));
            break;

        case "--noconsole":
            showConsole = false;
            break;
    }
});

function getValue(param){
    var ind = process.argv.indexOf(param);
    if (process.argv[ind + 1].indexOf('-') != -1){
        Logger.error("No value for " + param);
        return null;
    } else{
        return process.argv[ind + 1];
    }
}

function setParam(paramName, val){
    if (!gameServer.config.hasOwnProperty(paramName)){
        Logger.error("Wrong parameter");
    }
    if (val) {
        if (typeof val === 'string'){
            val = "'" + val + "'";
        }
        eval("gameServer.config." + paramName + "=" + val);
    }
}

gameServer.start();
figlet(('MultiOgar-Edited  ' + gameServer.version), function(err, data) {
     if (err) {
         console.log('Something went wrong...');
         console.dir(err);
         return;
     }
     console.log(data)
 });

// Add command handler
gameServer.commands = Commands.list;
// Initialize the server console
if (showConsole) {
    var readline = require('readline');
    var in_ = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    setTimeout(prompt, 100);
}

// Console functions

function prompt() {
    in_.question(">", function (str) {
        try {
            parseCommands(str);
        } catch (err) {
            Logger.error(err.stack);
        } finally {
            setTimeout(prompt, 0);
        }
    });
}

function parseCommands(str) {
    // Log the string
    Logger.write(">" + str);

    // Don't process ENTER
    if (str === '')
        return;

    // Splits the string
    var split = str.split(" ");

    // Process the first string value
    var first = split[0].toLowerCase();

    // Get command function
    var execute = gameServer.commands[first];
    if (typeof execute != 'undefined') {
        execute(gameServer, split);
    } else {
        Logger.warn("Invalid Command!");
    }
}
<commit_msg>Minor fixes<commit_after>// Imports
var Logger = require('./modules/Logger');
var Commands = require('./modules/CommandList');
var GameServer = require('./GameServer');
var figlet = require('figlet');

// Init variables
var showConsole = true;

// Start msg
Logger.start();

process.on('exit', function (code) {
    Logger.debug("process.exit(" + code + ")");
    Logger.shutdown();
});

process.on('uncaughtException', function (err) {
    Logger.fatal(err.stack);
    process.exit(1);
});

// Run Ogar
var gameServer = new GameServer();
Logger.info("\u001B[1m\u001B[32mMultiOgar-Edited " + gameServer.version + "\u001B[37m - An open source multi-protocol ogar server\u001B[0m");

// Handle arguments
process.argv.forEach(function (item) {

    switch (item){
        case "--help":
            console.log("Proper Usage: node index.js");
            console.log("    -n, --name             Set name");
            console.log("    -g, --gameport         Set game port");
            console.log("    -s, --statsport        Set stats port");
            console.log("    -m, --gamemode         Set game mode (id)");
            console.log("    -c, --connections      Set max connections limit");
            console.log("    -t, --tracker          Set serverTracker");
            console.log("    --noconsole            Disables the console");
            console.log("    --help                 Help menu");
            console.log("");
            break;

        case "-n":
        case "--name":
            setParam("serverName", getValue(item));
            break;

        case "-g":
        case "--gameport":
            setParam("serverPort", parseInt(getValue(item)));
            break;
        case "-s":
        case "--statsport":
            setParam("serverStatsPort", parseInt(getValue(item)));
            break;

        case "-m":
        case "--gamemode":
            setParam("serverGamemode", getValue(item));
            break;

        case "-c":
        case "--connections":
            setParam("serverMaxConnections", parseInt(getValue(item)));
            break;
        case "-t":
        case "--tracker":
            setParam("serverTracker", parseInt(getValue(item)));
            break;

        case "--noconsole":
            showConsole = false;
            break;
    }
});

function getValue(param){
    var ind = process.argv.indexOf(param);
    var item  = process.argv[ind + 1]
    if (!item || item.indexOf('-') != -1){
        Logger.error("No value for " + param);
        return null;
    } else{
        return item;
    }
}

function setParam(paramName, val){
    if (!gameServer.config.hasOwnProperty(paramName)){
        Logger.error("Wrong parameter");
    }
    if (val || val === 0) {
        if (typeof val === 'string'){
            val = "'" + val + "'";
        }
        eval("gameServer.config." + paramName + "=" + val);
    }
}

gameServer.start();
figlet(('MultiOgar-Edited  ' + gameServer.version), function(err, data) {
     if (err) {
         console.log('Something went wrong...');
         console.dir(err);
         return;
     }
     console.log(data)
 });

// Add command handler
gameServer.commands = Commands.list;
// Initialize the server console
if (showConsole) {
    var readline = require('readline');
    var in_ = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });
    setTimeout(prompt, 100);
}

// Console functions

function prompt() {
    in_.question(">", function (str) {
        try {
            parseCommands(str);
        } catch (err) {
            Logger.error(err.stack);
        } finally {
            setTimeout(prompt, 0);
        }
    });
}

function parseCommands(str) {
    // Log the string
    Logger.write(">" + str);

    // Don't process ENTER
    if (str === '')
        return;

    // Splits the string
    var split = str.split(" ");

    // Process the first string value
    var first = split[0].toLowerCase();

    // Get command function
    var execute = gameServer.commands[first];
    if (typeof execute != 'undefined') {
        execute(gameServer, split);
    } else {
        Logger.warn("Invalid Command!");
    }
}
<|endoftext|>


<commit_before>import Component from './component.js';
// import Listener from './listener.js';
import Batcher from './batcher.js';
import Fetcher from './fetcher.js';
import Binder from './binder.js';
import Loader from './loader.js';
import Router from './router.js';
import Style from './style.js';
import Path from './path.js';

document.head.insertAdjacentHTML('afterbegin', '<style>:not(:defined){visibility:hidden;}o-router,o-router>:first-child{display:block;}</style>');

const oSetup = document.querySelector('script[o-setup]');

if (oSetup) {
    Promise.resolve().then(function () {
        const attribute = oSetup.getAttribute('o-setup');

        if (!attribute) {
            throw new Error('Oxe - attribute o-setup requires arguments');
        }

        const options = attribute.split(/\s+|\s*,+\s*/);

        Loader.type = options[1] || 'esm';

        return Loader.load(options[0]);
    });
}

let SETUP = false;
const GLOBAL = {};

export default Object.freeze({

    global: GLOBAL,
    component: Component,
    batcher: Batcher,
    fetcher: Fetcher,
    binder: Binder,
    loader: Loader,
    router: Router,
    style: Style,
    path: Path,

    setup (options) {
        const self = this;

        if (SETUP) return;
        else SETUP = true;

        options = options || {};
        options.listener = options.listener || {};

        return Promise.all([
            self.path.setup(options.path),
            self.style.setup(options.style),
            self.binder.setup(options.binder),
            self.loader.setup(options.loader),
            self.fetcher.setup(options.fetcher),
        ]).then(function () {
            if (options.listener.before) {
                return options.listener.before();
            }
        }).then(function () {
            if (options.component) {
                return self.component.setup(options.component);
            }
        }).then(function () {
            if (options.router) {
                return self.router.setup(options.router);
            }
        }).then(function () {
            if (options.listener.after) {
                return options.listener.after();
            }
        });
    }

});
<commit_msg>removed comment<commit_after>import Component from './component.js';
import Batcher from './batcher.js';
import Fetcher from './fetcher.js';
import Binder from './binder.js';
import Loader from './loader.js';
import Router from './router.js';
import Style from './style.js';
import Path from './path.js';

document.head.insertAdjacentHTML('afterbegin', '<style>:not(:defined){visibility:hidden;}o-router,o-router>:first-child{display:block;}</style>');

const oSetup = document.querySelector('script[o-setup]');

if (oSetup) {
    Promise.resolve().then(function () {
        const attribute = oSetup.getAttribute('o-setup');

        if (!attribute) {
            throw new Error('Oxe - attribute o-setup requires arguments');
        }

        const options = attribute.split(/\s+|\s*,+\s*/);

        Loader.type = options[1] || 'esm';

        return Loader.load(options[0]);
    });
}

let SETUP = false;
const GLOBAL = {};

export default Object.freeze({

    global: GLOBAL,
    component: Component,
    batcher: Batcher,
    fetcher: Fetcher,
    binder: Binder,
    loader: Loader,
    router: Router,
    style: Style,
    path: Path,

    setup (options) {
        const self = this;

        if (SETUP) return;
        else SETUP = true;

        options = options || {};
        options.listener = options.listener || {};

        return Promise.all([
            self.path.setup(options.path),
            self.style.setup(options.style),
            self.binder.setup(options.binder),
            self.loader.setup(options.loader),
            self.fetcher.setup(options.fetcher),
        ]).then(function () {
            if (options.listener.before) {
                return options.listener.before();
            }
        }).then(function () {
            if (options.component) {
                return self.component.setup(options.component);
            }
        }).then(function () {
            if (options.router) {
                return self.router.setup(options.router);
            }
        }).then(function () {
            if (options.listener.after) {
                return options.listener.after();
            }
        });
    }

});
<|endoftext|>


<commit_before>import InputChildren from './InputChildren.js';
export default InputChildren;<commit_msg>code clean<commit_after>export default from './InputChildren.js';
<|endoftext|>


<commit_before>/**
 * @file react-anything-sortable
 * @author jasonslyvia
 */

/* eslint new-cap:0, consistent-return: 0, react/prefer-es6-class: 0, react/sort-comp: 0 */
/**
 * @dependency
 */
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { on, off, isFunction, isNumeric, position, closest, get,
        assign, findMostOften } from './utils';
import SortableItemMixin from './SortableItemMixin';

const STACK_SIZE = 6;

/**
 * @class Sortable
 */
const Sortable = React.createClass({
  propTypes: {
    /**
     * callback fires after sort operation finish
     * function (dataSet){
     *   //dataSet sorted
     * }
     */
    onSort: PropTypes.func,
    className: PropTypes.string,
    sortHandle: PropTypes.string,
    containment: PropTypes.bool,
    children: PropTypes.arrayOf(PropTypes.node)
  },

  getInitialState() {
    // keep tracking the dimension and coordinates of all children
    this._dimensionArr = this.props.children ?
                          Array.isArray(this.props.children) ?
                            this.props.children.map(() => ({})) :
                            [{}] :
                         [];

    // keep tracking the order of all children
    this._orderArr = [];
    let i = 0;
    while (i < this._dimensionArr.length) {
      this._orderArr.push(i++);
    }

    return {
      isDragging: false,
      placeHolderIndex: null,
      left: null,
      top: null
    };
  },

  componentDidUpdate() {
    const container = ReactDOM.findDOMNode(this);
    const rect = container.getBoundingClientRect();

    this._top = rect.top + document.body.scrollTop;
    this._left = rect.left + document.body.scrollLeft;
    this._bottom = this._top + rect.height;
    this._right = this._left + rect.width;
  },

  componentWillUnmount() {
    this.unbindEvent();
  },

  bindEvent() {
    // so that the focus won't be lost if cursor moving too fast
    this.__mouseMoveHandler = (e) => {
      /**
       * Since Chrome may trigger redundant mousemove event evne if
       * we didn't really move the mouse, we should make sure that
       * mouse coordinates really changed then respond to mousemove
       * event
       * @see https://code.google.com/p/chromium/issues/detail?id=327114
       */
      if (((e.pageX || e.clientX) === this._prevX && (e.pageY || e.clientY) === this._prevY) ||
          (this._prevX === null && this._prevY === null)) {
        return false;
      }

      this.handleMouseMove.call(this, e);
    };

    this.__mouseUpHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    this.__touchMoveHandler = (e) => {
      // blocks the default scrolling as we sort an element
      e.preventDefault();

      this.handleMouseMove.call(this, {
        target: e.target,
        clientX: e.touches[0].clientX,
        clientY: e.touches[0].clientY,
        pageX: e.touches[0].pageX,
        pageY: e.touches[0].pageY
      });
    };

    this.__touchEndOrCancelHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    on(document, 'touchmove', this.__touchMoveHandler);
    on(document, 'touchend', this.__touchEndOrCancelHandler);
    on(document, 'touchcancel', this.__touchEndOrCancelHandler);
    on(document, 'mousemove', this.__mouseMoveHandler);
    on(document, 'mouseup', this.__mouseUpHandler);
  },

  unbindEvent() {
    off(document, 'touchmove', this.__touchMoveHandler);
    off(document, 'touchend', this.__touchEndOrCancelHandler);
    off(document, 'touchcancel', this.__touchEndOrCancelHandler);
    off(document, 'mousemove', this.__mouseMoveHandler);
    off(document, 'mouseup', this.__mouseUpHandler);

    this.__mouseMoveHandler = null;
    this.__mouseUpHandler = null;
    this.__touchMoveHandler = null;
    this.__touchEndOrCancelHandler = null;
  },

  /**
   * getting ready for dragging
   * @param  {object} e     React event
   * @param  {numbner} index index of pre-dragging item
   */
  handleMouseDown(e, index) {
    this._draggingIndex = index;
    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
    this._initOffset = e.offset;
    this._isReadyForDragging = true;
    this._hasInitDragging = false;

    // start listening mousemove and mouseup
    this.bindEvent();
  },

  /**
   * `add` a dragging item and re-calculating position of placeholder
   * @param  {object} e     React event
   */
  handleMouseMove(e) {
    this._isMouseMoving = true;

    if (!this._isReadyForDragging) {
      return false;
    }

    if (!this._hasInitDragging) {
      this._dimensionArr[this._draggingIndex].isPlaceHolder = true;
      this._hasInitDragging = true;
    }

    if (this.props.containment) {
      const x = e.pageX || e.clientX;
      const y = e.pageY || e.clientY;

      if (x < this._left || x > this._right || y < this._top || y > this._bottom) {
        return false;
      }
    }

    const newOffset = this.calculateNewOffset(e);
    const newIndex = this.calculateNewIndex(e);

    this._draggingIndex = newIndex;

    this.setState({
      isDragging: true,
      top: newOffset.top,
      left: newOffset.left,
      placeHolderIndex: newIndex
    });

    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
  },

  /**
   * replace placeholder with dragging item
   * @param  {object} e     React event
   */
  handleMouseUp() {
    const _hasMouseMoved = this._isMouseMoving;
    this.unbindEvent();

    const draggingIndex = this._draggingIndex;
    // reset temp lets
    this._draggingIndex = null;
    this._isReadyForDragging = false;
    this._isMouseMoving = false;
    this._initOffset = null;
    this._prevX = null;
    this._prevY = null;

    if (this.state.isDragging) {
      this._dimensionArr[this.state.placeHolderIndex].isPlaceHolder = false;

      // sort finished, callback fires
      if (isFunction(this.props.onSort)) {
        const sortData = this.getSortData();
        this.props.onSort(sortData, sortData[draggingIndex], draggingIndex);
      }
    }

    if (_hasMouseMoved) {
      this.setState({
        isDragging: false,
        placeHolderIndex: null,
        left: null,
        top: null
      });
    }
  },

  /**
   * when children mounted, return its size(handled by SortableItemMixin)
   * @param  {object} offset {top:1, left:2}
   * @param  {number} width
   * @param  {number} height
   * @param  {number} fullWidth  (with margin)
   * @param  {number} fullHeight (with margin)
   * @param  {number} index
   */
  handleChildUpdate(offset, width, height, fullWidth, fullHeight, index) {
    assign(this._dimensionArr[index], {
      top: offset.top,
      left: offset.left,
      width,
      height,
      fullWidth,
      fullHeight
    });
  },

  /**
   * get new index of all items by cursor position
   * @param {object} offset {left: 12, top: 123}
   * @param {string} direction cursor moving direction, used to aviod blinking when
   *                 interchanging position of different width elements
   * @return {number}
   */
  getIndexByOffset(offset, direction) {
    if (!offset || !isNumeric(offset.top) || !isNumeric(offset.left)) {
      return 0;
    }

    const _dimensionArr = this._dimensionArr;
    const offsetX = offset.left;
    const offsetY = offset.top;
    const prevIndex = this.state.placeHolderIndex !== null ?
                   this.state.placeHolderIndex :
                   this._draggingIndex;
    let newIndex;

    _dimensionArr.every((item, index) => {
      const relativeLeft = offsetX - item.left;
      const relativeTop = offsetY - item.top;

      if (relativeLeft < item.fullWidth && relativeTop < item.fullHeight) {
        if (relativeLeft < item.fullWidth / 2 && direction === 'left') {
          newIndex = index;
        } else if (relativeLeft > item.fullWidth / 2 && direction === 'right'){
          newIndex = Math.min(index + 1, _dimensionArr.length - 1);
        } else if (relativeTop < item.fullHeight / 2 && direction === 'up') {
          newIndex = index;
        } else if (relativeTop > item.fullHeight / 2 && direction === 'down') {
          newIndex = index;
        } else {
          return true;
        }

        return false;
      }
      return true;
    });

    return newIndex !== undefined ? newIndex : prevIndex;
  },

  /**
   * untility function
   * @param  {array} arr
   * @param  {number} src
   * @param  {number} to
   * @return {array}
   */
  swapArrayItemPosition(arr, src, to) {
    if (!arr || !isNumeric(src) || !isNumeric(to)) {
      return arr;
    }

    const srcEl = arr.splice(src, 1)[0];
    arr.splice(to, 0, srcEl);
    return arr;
  },

  /**
   * calculate new offset
   * @param  {object} e MouseMove event
   * @return {object}   {left: 1, top: 1}
   */
  calculateNewOffset(e) {
    const deltaX = this._prevX - (e.pageX || e.clientX);
    const deltaY = this._prevY - (e.pageY || e.clientY);

    const prevLeft = this.state.left !== null ? this.state.left : this._initOffset.left;
    const prevTop = this.state.top !== null ? this.state.top : this._initOffset.top;
    const newLeft = prevLeft - deltaX;
    const newTop = prevTop - deltaY;

    return {
      left: newLeft,
      top: newTop
    };
  },

  /**
   * calculate new index and do swapping
   * @param  {object} e MouseMove event
   * @return {number}
   */
  calculateNewIndex(e) {
    const placeHolderIndex = this.state.placeHolderIndex !== null ?
                           this.state.placeHolderIndex :
                           this._draggingIndex;

    // Since `mousemove` is listened on document, when cursor move too fast,
    // `e.target` may be `body` or some other stuff instead of
    // `.ui-sortable-item`
    const target = get('.ui-sortable-dragging') ||
                   closest((e.target || e.srcElement), '.ui-sortable-item');
    const offset = position(target);

    const currentX = e.pageX || e.clientX;
    const currentY = e.pageY || e.clientY;

    const deltaX = Math.abs(this._prevX - currentX);
    const deltaY = Math.abs(this._prevY - currentY);

    let direction;
    if (deltaX > deltaY) {
      // tend to move left/right
      direction = this._prevX > currentX ? 'left' : 'right';
    } else {
      // tend to move up/down
      direction = this._prevY > currentY ? 'up' : 'down';
    }

    const newIndex = this.getIndexByOffset(offset, this.getPossibleDirection(direction));
    if (newIndex !== placeHolderIndex) {
      this._dimensionArr = this.swapArrayItemPosition(this._dimensionArr,
                                                      placeHolderIndex,
                                                      newIndex);
      this._orderArr = this.swapArrayItemPosition(this._orderArr, placeHolderIndex, newIndex);
    }

    return newIndex;
  },

  getSortData() {
    return this._orderArr.map((itemIndex) => {
      const item = Array.isArray(this.props.children) ?
                   this.props.children[itemIndex] :
                   this.props.children;
      if (!item) {
        return undefined;
      }

      return item.props.sortData;
    });
  },

  getPossibleDirection(direction) {
    this._stack = this._stack || [];
    this._stack.push(direction);
    if (this._stack.length > STACK_SIZE) {
      this._stack.shift();
    }

    if (this._stack.length < STACK_SIZE) {
      return direction;
    }

    return findMostOften(this._stack);
  },

  /**
   * render all sortable children which mixined with SortableItemMixin
   */
  renderItems() {
    const { _dimensionArr, _orderArr } = this;
    let draggingItem;

    const items = _orderArr.map((itemIndex, index) => {
      const item = Array.isArray(this.props.children) ?
                 this.props.children[itemIndex] :
                 this.props.children;
      if (!item) {
        return undefined;
      }
      if (index === this._draggingIndex) {
        draggingItem = this.renderDraggingItem(item);
      }

      const isPlaceHolder = _dimensionArr[index].isPlaceHolder;
      const itemClassName = `ui-sortable-item
                             ${isPlaceHolder && 'ui-sortable-placeholder'}
                             ${this.state.isDragging && isPlaceHolder && 'visible'}`;

      return React.cloneElement(item, {
        key: index,
        sortableClassName: `${item.props.className} ${itemClassName}`,
        sortableIndex: index,
        onSortableItemReadyToMove: isPlaceHolder ? undefined : (e) => {
          this.handleMouseDown.call(this, e, index);
        },
        onSortableItemMount: this.handleChildUpdate,
        sortHandle: this.props.sortHandle
      });
    });

    return items.concat([draggingItem]);
  },

  /**
   * render the item that being dragged
   * @param  {object} item a reference of this.props.children
   */
  renderDraggingItem(item) {
    if (!item) {
      return;
    }

    const style = {
      top: this.state.top,
      left: this.state.left,
      width: this._dimensionArr[this._draggingIndex].width,
      height: this._dimensionArr[this._draggingIndex].height
    };
    return React.cloneElement(item, {
      sortableClassName: `${item.props.className} ui-sortable-item ui-sortable-dragging`,
      key: this._dimensionArr.length,
      sortableStyle: style,
      isDragging: true,
      sortHandle: this.props.sortHandle
    });
  },

  render() {
    const className = `ui-sortable ${this.props.className || ''}`;

    return (
      <div className={className}>
        {this.renderItems()}
      </div>
    );
  }
});

Sortable.SortableItemMixin = SortableItemMixin();
Sortable.sortable = SortableItemMixin;

export default Sortable;
<commit_msg>fix: reset before onSort called to prevent calling setState in a unmounted component<commit_after>/**
 * @file react-anything-sortable
 * @author jasonslyvia
 */

/* eslint new-cap:0, consistent-return: 0, react/prefer-es6-class: 0, react/sort-comp: 0 */
/**
 * @dependency
 */
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { on, off, isFunction, isNumeric, position, closest, get,
        assign, findMostOften } from './utils';
import SortableItemMixin from './SortableItemMixin';

const STACK_SIZE = 6;

/**
 * @class Sortable
 */
const Sortable = React.createClass({
  propTypes: {
    /**
     * callback fires after sort operation finish
     * function (dataSet){
     *   //dataSet sorted
     * }
     */
    onSort: PropTypes.func,
    className: PropTypes.string,
    sortHandle: PropTypes.string,
    containment: PropTypes.bool,
    children: PropTypes.arrayOf(PropTypes.node)
  },

  getInitialState() {
    // keep tracking the dimension and coordinates of all children
    this._dimensionArr = this.props.children ?
                          Array.isArray(this.props.children) ?
                            this.props.children.map(() => ({})) :
                            [{}] :
                         [];

    // keep tracking the order of all children
    this._orderArr = [];
    let i = 0;
    while (i < this._dimensionArr.length) {
      this._orderArr.push(i++);
    }

    return {
      isDragging: false,
      placeHolderIndex: null,
      left: null,
      top: null
    };
  },

  componentDidUpdate() {
    const container = ReactDOM.findDOMNode(this);
    const rect = container.getBoundingClientRect();

    this._top = rect.top + document.body.scrollTop;
    this._left = rect.left + document.body.scrollLeft;
    this._bottom = this._top + rect.height;
    this._right = this._left + rect.width;
  },

  componentWillUnmount() {
    this.unbindEvent();
  },

  bindEvent() {
    // so that the focus won't be lost if cursor moving too fast
    this.__mouseMoveHandler = (e) => {
      /**
       * Since Chrome may trigger redundant mousemove event evne if
       * we didn't really move the mouse, we should make sure that
       * mouse coordinates really changed then respond to mousemove
       * event
       * @see https://code.google.com/p/chromium/issues/detail?id=327114
       */
      if (((e.pageX || e.clientX) === this._prevX && (e.pageY || e.clientY) === this._prevY) ||
          (this._prevX === null && this._prevY === null)) {
        return false;
      }

      this.handleMouseMove.call(this, e);
    };

    this.__mouseUpHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    this.__touchMoveHandler = (e) => {
      // blocks the default scrolling as we sort an element
      e.preventDefault();

      this.handleMouseMove.call(this, {
        target: e.target,
        clientX: e.touches[0].clientX,
        clientY: e.touches[0].clientY,
        pageX: e.touches[0].pageX,
        pageY: e.touches[0].pageY
      });
    };

    this.__touchEndOrCancelHandler = (e) => {
      this.handleMouseUp.call(this, e);
    };

    on(document, 'touchmove', this.__touchMoveHandler);
    on(document, 'touchend', this.__touchEndOrCancelHandler);
    on(document, 'touchcancel', this.__touchEndOrCancelHandler);
    on(document, 'mousemove', this.__mouseMoveHandler);
    on(document, 'mouseup', this.__mouseUpHandler);
  },

  unbindEvent() {
    off(document, 'touchmove', this.__touchMoveHandler);
    off(document, 'touchend', this.__touchEndOrCancelHandler);
    off(document, 'touchcancel', this.__touchEndOrCancelHandler);
    off(document, 'mousemove', this.__mouseMoveHandler);
    off(document, 'mouseup', this.__mouseUpHandler);

    this.__mouseMoveHandler = null;
    this.__mouseUpHandler = null;
    this.__touchMoveHandler = null;
    this.__touchEndOrCancelHandler = null;
  },

  /**
   * getting ready for dragging
   * @param  {object} e     React event
   * @param  {numbner} index index of pre-dragging item
   */
  handleMouseDown(e, index) {
    this._draggingIndex = index;
    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
    this._initOffset = e.offset;
    this._isReadyForDragging = true;
    this._hasInitDragging = false;

    // start listening mousemove and mouseup
    this.bindEvent();
  },

  /**
   * `add` a dragging item and re-calculating position of placeholder
   * @param  {object} e     React event
   */
  handleMouseMove(e) {
    this._isMouseMoving = true;

    if (!this._isReadyForDragging) {
      return false;
    }

    if (!this._hasInitDragging) {
      this._dimensionArr[this._draggingIndex].isPlaceHolder = true;
      this._hasInitDragging = true;
    }

    if (this.props.containment) {
      const x = e.pageX || e.clientX;
      const y = e.pageY || e.clientY;

      if (x < this._left || x > this._right || y < this._top || y > this._bottom) {
        return false;
      }
    }

    const newOffset = this.calculateNewOffset(e);
    const newIndex = this.calculateNewIndex(e);

    this._draggingIndex = newIndex;

    this.setState({
      isDragging: true,
      top: newOffset.top,
      left: newOffset.left,
      placeHolderIndex: newIndex
    });

    this._prevX = (e.pageX || e.clientX);
    this._prevY = (e.pageY || e.clientY);
  },

  /**
   * replace placeholder with dragging item
   * @param  {object} e     React event
   */
  handleMouseUp() {
    const _hasMouseMoved = this._isMouseMoving;
    this.unbindEvent();

    const draggingIndex = this._draggingIndex;
    // reset temp lets
    this._draggingIndex = null;
    this._isReadyForDragging = false;
    this._isMouseMoving = false;
    this._initOffset = null;
    this._prevX = null;
    this._prevY = null;

    if (this.state.isDragging) {
      this._dimensionArr[this.state.placeHolderIndex].isPlaceHolder = false;

      if (_hasMouseMoved) {
        this.setState({
          isDragging: false,
          placeHolderIndex: null,
          left: null,
          top: null
        });
      }

      // sort finished, callback fires
      if (isFunction(this.props.onSort)) {
        const sortData = this.getSortData();
        this.props.onSort(sortData, sortData[draggingIndex], draggingIndex);
      }
    }
  },

  /**
   * when children mounted, return its size(handled by SortableItemMixin)
   * @param  {object} offset {top:1, left:2}
   * @param  {number} width
   * @param  {number} height
   * @param  {number} fullWidth  (with margin)
   * @param  {number} fullHeight (with margin)
   * @param  {number} index
   */
  handleChildUpdate(offset, width, height, fullWidth, fullHeight, index) {
    assign(this._dimensionArr[index], {
      top: offset.top,
      left: offset.left,
      width,
      height,
      fullWidth,
      fullHeight
    });
  },

  /**
   * get new index of all items by cursor position
   * @param {object} offset {left: 12, top: 123}
   * @param {string} direction cursor moving direction, used to aviod blinking when
   *                 interchanging position of different width elements
   * @return {number}
   */
  getIndexByOffset(offset, direction) {
    if (!offset || !isNumeric(offset.top) || !isNumeric(offset.left)) {
      return 0;
    }

    const _dimensionArr = this._dimensionArr;
    const offsetX = offset.left;
    const offsetY = offset.top;
    const prevIndex = this.state.placeHolderIndex !== null ?
                   this.state.placeHolderIndex :
                   this._draggingIndex;
    let newIndex;

    _dimensionArr.every((item, index) => {
      const relativeLeft = offsetX - item.left;
      const relativeTop = offsetY - item.top;

      if (relativeLeft < item.fullWidth && relativeTop < item.fullHeight) {
        if (relativeLeft < item.fullWidth / 2 && direction === 'left') {
          newIndex = index;
        } else if (relativeLeft > item.fullWidth / 2 && direction === 'right'){
          newIndex = Math.min(index + 1, _dimensionArr.length - 1);
        } else if (relativeTop < item.fullHeight / 2 && direction === 'up') {
          newIndex = index;
        } else if (relativeTop > item.fullHeight / 2 && direction === 'down') {
          newIndex = index;
        } else {
          return true;
        }

        return false;
      }
      return true;
    });

    return newIndex !== undefined ? newIndex : prevIndex;
  },

  /**
   * untility function
   * @param  {array} arr
   * @param  {number} src
   * @param  {number} to
   * @return {array}
   */
  swapArrayItemPosition(arr, src, to) {
    if (!arr || !isNumeric(src) || !isNumeric(to)) {
      return arr;
    }

    const srcEl = arr.splice(src, 1)[0];
    arr.splice(to, 0, srcEl);
    return arr;
  },

  /**
   * calculate new offset
   * @param  {object} e MouseMove event
   * @return {object}   {left: 1, top: 1}
   */
  calculateNewOffset(e) {
    const deltaX = this._prevX - (e.pageX || e.clientX);
    const deltaY = this._prevY - (e.pageY || e.clientY);

    const prevLeft = this.state.left !== null ? this.state.left : this._initOffset.left;
    const prevTop = this.state.top !== null ? this.state.top : this._initOffset.top;
    const newLeft = prevLeft - deltaX;
    const newTop = prevTop - deltaY;

    return {
      left: newLeft,
      top: newTop
    };
  },

  /**
   * calculate new index and do swapping
   * @param  {object} e MouseMove event
   * @return {number}
   */
  calculateNewIndex(e) {
    const placeHolderIndex = this.state.placeHolderIndex !== null ?
                           this.state.placeHolderIndex :
                           this._draggingIndex;

    // Since `mousemove` is listened on document, when cursor move too fast,
    // `e.target` may be `body` or some other stuff instead of
    // `.ui-sortable-item`
    const target = get('.ui-sortable-dragging') ||
                   closest((e.target || e.srcElement), '.ui-sortable-item');
    const offset = position(target);

    const currentX = e.pageX || e.clientX;
    const currentY = e.pageY || e.clientY;

    const deltaX = Math.abs(this._prevX - currentX);
    const deltaY = Math.abs(this._prevY - currentY);

    let direction;
    if (deltaX > deltaY) {
      // tend to move left/right
      direction = this._prevX > currentX ? 'left' : 'right';
    } else {
      // tend to move up/down
      direction = this._prevY > currentY ? 'up' : 'down';
    }

    const newIndex = this.getIndexByOffset(offset, this.getPossibleDirection(direction));
    if (newIndex !== placeHolderIndex) {
      this._dimensionArr = this.swapArrayItemPosition(this._dimensionArr,
                                                      placeHolderIndex,
                                                      newIndex);
      this._orderArr = this.swapArrayItemPosition(this._orderArr, placeHolderIndex, newIndex);
    }

    return newIndex;
  },

  getSortData() {
    return this._orderArr.map((itemIndex) => {
      const item = Array.isArray(this.props.children) ?
                   this.props.children[itemIndex] :
                   this.props.children;
      if (!item) {
        return undefined;
      }

      return item.props.sortData;
    });
  },

  getPossibleDirection(direction) {
    this._stack = this._stack || [];
    this._stack.push(direction);
    if (this._stack.length > STACK_SIZE) {
      this._stack.shift();
    }

    if (this._stack.length < STACK_SIZE) {
      return direction;
    }

    return findMostOften(this._stack);
  },

  /**
   * render all sortable children which mixined with SortableItemMixin
   */
  renderItems() {
    const { _dimensionArr, _orderArr } = this;
    let draggingItem;

    const items = _orderArr.map((itemIndex, index) => {
      const item = Array.isArray(this.props.children) ?
                 this.props.children[itemIndex] :
                 this.props.children;
      if (!item) {
        return undefined;
      }
      if (index === this._draggingIndex) {
        draggingItem = this.renderDraggingItem(item);
      }

      const isPlaceHolder = _dimensionArr[index].isPlaceHolder;
      const itemClassName = `ui-sortable-item
                             ${isPlaceHolder && 'ui-sortable-placeholder'}
                             ${this.state.isDragging && isPlaceHolder && 'visible'}`;

      return React.cloneElement(item, {
        key: index,
        sortableClassName: `${item.props.className} ${itemClassName}`,
        sortableIndex: index,
        onSortableItemReadyToMove: isPlaceHolder ? undefined : (e) => {
          this.handleMouseDown.call(this, e, index);
        },
        onSortableItemMount: this.handleChildUpdate,
        sortHandle: this.props.sortHandle
      });
    });

    return items.concat([draggingItem]);
  },

  /**
   * render the item that being dragged
   * @param  {object} item a reference of this.props.children
   */
  renderDraggingItem(item) {
    if (!item) {
      return;
    }

    const style = {
      top: this.state.top,
      left: this.state.left,
      width: this._dimensionArr[this._draggingIndex].width,
      height: this._dimensionArr[this._draggingIndex].height
    };
    return React.cloneElement(item, {
      sortableClassName: `${item.props.className} ui-sortable-item ui-sortable-dragging`,
      key: this._dimensionArr.length,
      sortableStyle: style,
      isDragging: true,
      sortHandle: this.props.sortHandle
    });
  },

  render() {
    const className = `ui-sortable ${this.props.className || ''}`;

    return (
      <div className={className}>
        {this.renderItems()}
      </div>
    );
  }
});

Sortable.SortableItemMixin = SortableItemMixin();
Sortable.sortable = SortableItemMixin;

export default Sortable;
<|endoftext|>


<commit_before>import React, { Component, createElement } from 'react' // eslint-disable-line no-unused-vars
import PropTypes from 'prop-types'
import { observe, unobserve } from './intersection'

const isFunction = func => typeof func === 'function'

/**
 * Monitors scroll, and triggers the children function with updated props
 *
 <Observer>
 {inView => (

{`${inView}`}

 )}
 </Observer>
 */
class Observer extends Component {
  static propTypes = {
    /** Element tag to use for the wrapping */
    tag: PropTypes.node,
    /** Children should be either a function or a node */
    children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
    /** Only trigger this method once */
    triggerOnce: PropTypes.bool,
    /** Number between 0 and 1 indicating the the percentage that should be visible before triggering */
    threshold: PropTypes.number,
    /** Call this function whenever the in view state changes */
    onChange: PropTypes.func,
    /** Use render method to only render content when inView */
    render: PropTypes.func,
  }

  static defaultProps = {
    tag: 'div',
    threshold: 0,
    triggerOnce: false,
  }

  state = {
    inView: false,
  }

  componentWillUpdate(nextProps, nextState) {
    if (!!this.props.onChange && nextState.inView !== this.state.inView) {
      this.props.onChange(nextState.inView)
      if (nextState.inView && nextProps.unobserve) {
      }
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.inView !== this.state.inView) {
      if (this.state.inView && this.props.triggerOnce) {
        unobserve(this.node)
        this.node = null
      }
    }
  }

  componentWillUnmount() {
    if (this.node) {
      unobserve(this.node)
      this.node = null
    }
  }

  node = null

  handleNode = node => {
    if (this.node) unobserve(this.node)
    if (node) {
      observe(
        node,
        this.handleChange,
        this.props.triggerOnce,
        this.props.threshold,
      )
    }
    this.node = node
  }

  handleChange = inView => this.setState({ inView })

  render() {
    const {
      children,
      render,
      tag,
      triggerOnce,
      threshold,
      ...props
    } = this.props
    const { inView } = this.state

    return createElement(
      tag,
      {
        ...props,
        ref: this.handleNode,
      },
      // If render is a function, use it to render content when in view
      inView && isFunction(render) ? render() : null,
      // If children is a function, render it with the current inView status.
      // Otherwise always render children. Assume onChange is being used outside, to control the the state of children.
      isFunction(children) ? children(inView) : children,
    )
  }
}

export default Observer
<commit_msg>Remove triggerOnce from observe<commit_after>import React, { Component, createElement } from 'react' // eslint-disable-line no-unused-vars
import PropTypes from 'prop-types'
import { observe, unobserve } from './intersection'

const isFunction = func => typeof func === 'function'

/**
 * Monitors scroll, and triggers the children function with updated props
 *
 <Observer>
 {inView => (

{`${inView}`}

 )}
 </Observer>
 */
class Observer extends Component {
  static propTypes = {
    /** Element tag to use for the wrapping */
    tag: PropTypes.node,
    /** Children should be either a function or a node */
    children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
    /** Only trigger this method once */
    triggerOnce: PropTypes.bool,
    /** Number between 0 and 1 indicating the the percentage that should be visible before triggering */
    threshold: PropTypes.number,
    /** Call this function whenever the in view state changes */
    onChange: PropTypes.func,
    /** Use render method to only render content when inView */
    render: PropTypes.func,
  }

  static defaultProps = {
    tag: 'div',
    threshold: 0,
    triggerOnce: false,
  }

  state = {
    inView: false,
  }

  componentWillUpdate(nextProps, nextState) {
    if (!!this.props.onChange && nextState.inView !== this.state.inView) {
      this.props.onChange(nextState.inView)
      if (nextState.inView && nextProps.unobserve) {
      }
    }
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.inView !== this.state.inView) {
      if (this.state.inView && this.props.triggerOnce) {
        unobserve(this.node)
        this.node = null
      }
    }
  }

  componentWillUnmount() {
    if (this.node) {
      unobserve(this.node)
      this.node = null
    }
  }

  node = null

  handleNode = node => {
    if (this.node) unobserve(this.node)
    if (node) {
      observe(node, this.handleChange, this.props.threshold)
    }
    this.node = node
  }

  handleChange = inView => this.setState({ inView })

  render() {
    const {
      children,
      render,
      tag,
      triggerOnce,
      threshold,
      ...props
    } = this.props
    const { inView } = this.state

    return createElement(
      tag,
      {
        ...props,
        ref: this.handleNode,
      },
      // If render is a function, use it to render content when in view
      inView && isFunction(render) ? render() : null,
      // If children is a function, render it with the current inView status.
      // Otherwise always render children. Assume onChange is being used outside, to control the the state of children.
      isFunction(children) ? children(inView) : children,
    )
  }
}

export default Observer
<|endoftext|>


<commit_before>function h(type, props) {
  var node
  var stack = []
  var children = []

  for (var i = arguments.length; i-- > 2; ) {
    stack.push(arguments[i])
  }

  while (stack.length) {
    if (Array.isArray((node = stack.pop()))) {
      for (i = node.length; i--; ) {
        stack.push(node[i])
      }
    } else if (node != null && node !== true && node !== false) {
      children.push(typeof node === "number" ? (node = node + "") : node)
    }
  }

  return typeof type === "string"
    ? {
        type: type,
        props: props || {},
        children: children
      }
    : type(props || {}, children)
}

function vnode(element, map) {
  return (
    element &&
    h(
      element.tagName.toLowerCase(),
      {},
      map.call(element.childNodes, function(element) {
        return element.nodeType === 3 ? element.nodeValue : vnode(element, map)
      })
    )
  )
}

function app(props, container) {
  var root = (container = container || document.body).children[0]
  var node = vnode(root, [].map)
  var appState = props.state || {}
  var appActions = {}
  var lifecycle = []
  var lock

  function set(to, from) {
    for (var i in from) {
      to[i] = from[i]
    }
    return to
  }

  function merge(to, from) {
    return set(set({}, to), from)
  }

  function setDeep(path, value, from) {
    var to = {}
    return path.length === 0
      ? value
      : ((to[path[0]] =
          1 < path.length
            ? setDeep(path.slice(1), value, from[path[0]])
            : value),
        merge(from, to))
  }

  function get(path, from) {
    for (var i = 0; i < path.length; i++) {
      from = from[path[i]]
    }
    return from
  }

  function setElementProp(element, name, value, oldValue) {
    if (name === "key") {
    } else if (name === "style") {
      for (var name in merge(oldValue, (value = value || {}))) {
        element.style[name] = value[name] || ""
      }
    } else {
      try {
        element[name] = null == value ? "" : value
      } catch (_) {}

      if (typeof value !== "function") {
        if (null == value || false === value) {
          element.removeAttribute(name)
        } else {
          element.setAttribute(name, value)
        }
      }
    }
  }

  function createElement(node, isSVG) {
    if (typeof node === "string") {
      var element = document.createTextNode(node)
    } else {
      var element = (isSVG = isSVG || node.type === "svg")
        ? document.createElementNS("http://www.w3.org/2000/svg", node.type)
        : document.createElement(node.type)

      if (node.props.oncreate) {
        lifecycle.push(function() {
          node.props.oncreate(element)
        })
      }

      for (var i = 0; i < node.children.length; i++) {
        element.appendChild(createElement(node.children[i], isSVG))
      }

      for (var i in node.props) {
        setElementProp(element, i, node.props[i])
      }
    }
    return element
  }

  function updateElement(element, oldProps, props) {
    for (var i in merge(oldProps, props)) {
      var value = props[i]
      var oldValue = i === "value" || i === "checked" ? element[i] : oldProps[i]

      if (value !== oldValue) {
        setElementProp(element, i, value, oldValue)
      }
    }

    if (props.onupdate) {
      lifecycle.push(function() {
        props.onupdate(element, oldProps)
      })
    }
  }

  function removeElement(parent, element, props) {
    function done() {
      parent.removeChild(element)
    }

    if (props && props.onremove) {
      props.onremove(element, done)
    } else {
      done()
    }
  }

  function getKey(node) {
    if (node && node.props) {
      return node.props.key
    }
  }

  function patch(parent, element, oldNode, node, isSVG, nextSibling) {
    if (oldNode === node) {
    } else if (oldNode == null) {
      element = parent.insertBefore(createElement(node, isSVG), element)
    } else if (node.type != null && node.type === oldNode.type) {
      updateElement(element, oldNode.props, node.props)

      isSVG = isSVG || node.type === "svg"

      var len = node.children.length
      var oldLen = oldNode.children.length
      var oldKeyed = {}
      var oldElements = []
      var keyed = {}

      for (var i = 0; i < oldLen; i++) {
        var oldElement = (oldElements[i] = element.childNodes[i])
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)

        if (null != oldKey) {
          oldKeyed[oldKey] = [oldElement, oldChild]
        }
      }

      var i = 0
      var j = 0

      while (j < len) {
        var oldElement = oldElements[i]
        var oldChild = oldNode.children[i]
        var newChild = node.children[j]

        var oldKey = getKey(oldChild)
        if (keyed[oldKey]) {
          i++
          continue
        }

        var newKey = getKey(newChild)
        var keyedNode = oldKeyed[newKey] || []

        if (null == newKey) {
          if (null == oldKey) {
            patch(element, oldElement, oldChild, newChild, isSVG)
            j++
          }
          i++
        } else {
          if (oldKey === newKey) {
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
            i++
          } else if (keyedNode[0]) {
            element.insertBefore(keyedNode[0], oldElement)
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
          } else {
            patch(element, oldElement, null, newChild, isSVG)
          }

          j++
          keyed[newKey] = newChild
        }
      }

      while (i < oldLen) {
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)
        if (null == oldKey) {
          removeElement(element, oldElements[i], oldChild.props)
        }
        i++
      }

      for (var i in oldKeyed) {
        var keyedNode = oldKeyed[i]
        var reusableNode = keyedNode[1]
        if (!keyed[reusableNode.props.key]) {
          removeElement(element, keyedNode[0], reusableNode.props)
        }
      }
    } else if (element && node !== element.nodeValue) {
      if (typeof node === "string" && typeof oldNode === "string") {
        element.nodeValue = node
      } else {
        element = parent.insertBefore(
          createElement(node, isSVG),
          (nextSibling = element)
        )
        removeElement(parent, nextSibling, oldNode.props)
      }
    }

    return element
  }

  function render(next) {
    lock = !lock
    if ((next = props.view(appState, appActions)) && !lock) {
      root = patch(container, root, node, (node = next))
    }
    while ((next = lifecycle.pop())) next()
  }

  function repaint() {
    if (props.view && !lock) {
      setTimeout(render, (lock = !lock))
    }
  }

  function init(state, actions, from, path) {
    for (var key in from) {
      typeof from[key] === "function"
        ? (function(key) {
            actions[key] = function(data) {
              var result = from[key]((state = get(path, appState)), actions)

              if (typeof result === "function") {
                result = result(data)
              }

              if (result && result !== state && !result.then) {
                repaint(
                  (appState = setDeep(path, merge(state, result), appState))
                )
              }

              return result
            }
          })(key)
        : init(
            state[key] || (state[key] = {}),
            (actions[key] = {}),
            from[key],
            path.concat(key)
          )
    }
  }

  repaint(init(appState, appActions, props.actions, []))

  return appActions
}

export { h, app }
<commit_msg>Woof woof.<commit_after>export { h, app }

function app(props, container) {
  var root = (container = container || document.body).children[0]
  var node = vnode(root, [].map)
  var appState = props.state || {}
  var appActions = {}
  var lifecycle = []
  var lock

  function set(to, from) {
    for (var i in from) {
      to[i] = from[i]
    }
    return to
  }

  function merge(to, from) {
    return set(set({}, to), from)
  }

  function setDeep(path, value, from) {
    var to = {}
    return path.length === 0
      ? value
      : ((to[path[0]] =
          1 < path.length
            ? setDeep(path.slice(1), value, from[path[0]])
            : value),
        merge(from, to))
  }

  function get(path, from) {
    for (var i = 0; i < path.length; i++) {
      from = from[path[i]]
    }
    return from
  }

  function setElementProp(element, name, value, oldValue) {
    if (name === "key") {
    } else if (name === "style") {
      for (var name in merge(oldValue, (value = value || {}))) {
        element.style[name] = value[name] || ""
      }
    } else {
      try {
        element[name] = null == value ? "" : value
      } catch (_) {}

      if (typeof value !== "function") {
        if (null == value || false === value) {
          element.removeAttribute(name)
        } else {
          element.setAttribute(name, value)
        }
      }
    }
  }

  function createElement(node, isSVG) {
    if (typeof node === "string") {
      var element = document.createTextNode(node)
    } else {
      var element = (isSVG = isSVG || node.type === "svg")
        ? document.createElementNS("http://www.w3.org/2000/svg", node.type)
        : document.createElement(node.type)

      if (node.props.oncreate) {
        lifecycle.push(function() {
          node.props.oncreate(element)
        })
      }

      for (var i = 0; i < node.children.length; i++) {
        element.appendChild(createElement(node.children[i], isSVG))
      }

      for (var i in node.props) {
        setElementProp(element, i, node.props[i])
      }
    }
    return element
  }

  function updateElement(element, oldProps, props) {
    for (var i in merge(oldProps, props)) {
      var value = props[i]
      var oldValue = i === "value" || i === "checked" ? element[i] : oldProps[i]

      if (value !== oldValue) {
        setElementProp(element, i, value, oldValue)
      }
    }

    if (props.onupdate) {
      lifecycle.push(function() {
        props.onupdate(element, oldProps)
      })
    }
  }

  function removeElement(parent, element, props) {
    function done() {
      parent.removeChild(element)
    }

    if (props && props.onremove) {
      props.onremove(element, done)
    } else {
      done()
    }
  }

  function getKey(node) {
    if (node && node.props) {
      return node.props.key
    }
  }

  function patch(parent, element, oldNode, node, isSVG, nextSibling) {
    if (oldNode === node) {
    } else if (oldNode == null) {
      element = parent.insertBefore(createElement(node, isSVG), element)
    } else if (node.type != null && node.type === oldNode.type) {
      updateElement(element, oldNode.props, node.props)

      isSVG = isSVG || node.type === "svg"

      var len = node.children.length
      var oldLen = oldNode.children.length
      var oldKeyed = {}
      var oldElements = []
      var keyed = {}

      for (var i = 0; i < oldLen; i++) {
        var oldElement = (oldElements[i] = element.childNodes[i])
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)

        if (null != oldKey) {
          oldKeyed[oldKey] = [oldElement, oldChild]
        }
      }

      var i = 0
      var j = 0

      while (j < len) {
        var oldElement = oldElements[i]
        var oldChild = oldNode.children[i]
        var newChild = node.children[j]

        var oldKey = getKey(oldChild)
        if (keyed[oldKey]) {
          i++
          continue
        }

        var newKey = getKey(newChild)
        var keyedNode = oldKeyed[newKey] || []

        if (null == newKey) {
          if (null == oldKey) {
            patch(element, oldElement, oldChild, newChild, isSVG)
            j++
          }
          i++
        } else {
          if (oldKey === newKey) {
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
            i++
          } else if (keyedNode[0]) {
            element.insertBefore(keyedNode[0], oldElement)
            patch(element, keyedNode[0], keyedNode[1], newChild, isSVG)
          } else {
            patch(element, oldElement, null, newChild, isSVG)
          }

          j++
          keyed[newKey] = newChild
        }
      }

      while (i < oldLen) {
        var oldChild = oldNode.children[i]
        var oldKey = getKey(oldChild)
        if (null == oldKey) {
          removeElement(element, oldElements[i], oldChild.props)
        }
        i++
      }

      for (var i in oldKeyed) {
        var keyedNode = oldKeyed[i]
        var reusableNode = keyedNode[1]
        if (!keyed[reusableNode.props.key]) {
          removeElement(element, keyedNode[0], reusableNode.props)
        }
      }
    } else if (element && node !== element.nodeValue) {
      if (typeof node === "string" && typeof oldNode === "string") {
        element.nodeValue = node
      } else {
        element = parent.insertBefore(
          createElement(node, isSVG),
          (nextSibling = element)
        )
        removeElement(parent, nextSibling, oldNode.props)
      }
    }

    return element
  }

  function render(next) {
    lock = !lock
    if ((next = props.view(appState, appActions)) && !lock) {
      root = patch(container, root, node, (node = next))
    }
    while ((next = lifecycle.pop())) next()
  }

  function repaint() {
    if (props.view && !lock) {
      setTimeout(render, (lock = !lock))
    }
  }

  function init(state, actions, from, path) {
    for (var key in from) {
      typeof from[key] === "function"
        ? (function(key) {
            actions[key] = function(data) {
              var result = from[key]((state = get(path, appState)), actions)

              if (typeof result === "function") {
                result = result(data)
              }

              if (result && result !== state && !result.then) {
                repaint(
                  (appState = setDeep(path, merge(state, result), appState))
                )
              }

              return result
            }
          })(key)
        : init(
            state[key] || (state[key] = {}),
            (actions[key] = {}),
            from[key],
            path.concat(key)
          )
    }
  }

  repaint(init(appState, appActions, props.actions, []))

  return appActions
}

function h(type, props) {
  var node
  var stack = []
  var children = []

  for (var i = arguments.length; i-- > 2; ) {
    stack.push(arguments[i])
  }

  while (stack.length) {
    if (Array.isArray((node = stack.pop()))) {
      for (i = node.length; i--; ) {
        stack.push(node[i])
      }
    } else if (node != null && node !== true && node !== false) {
      children.push(typeof node === "number" ? (node = node + "") : node)
    }
  }

  return typeof type === "string"
    ? {
        type: type,
        props: props || {},
        children: children
      }
    : type(props || {}, children)
}

function vnode(element, map) {
  return (
    element &&
    h(
      element.tagName.toLowerCase(),
      {},
      map.call(element.childNodes, function(element) {
        return element.nodeType === 3 ? element.nodeValue : vnode(element, map)
      })
    )
  )
}
<|endoftext|>


<commit_before>import * as utils from './utils';

// https://gist.github.com/Xeoncross/7663273
function ajax(url, options, callback, data, cache) {
  try {
    var x = new (XMLHttpRequest || ActiveXObject)('MSXML2.XMLHTTP.3.0');
    x.open(data ? 'POST' : 'GET', url, 1);
    if (!options.crossDomain) {
      x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    }
    if (options.authorize && options.apiKey) {
      x.setRequestHeader('Authorization', options.apiKey);
    }
    if (data || options.setContentTypeJSON) {
      x.setRequestHeader('Content-type', 'application/json');
    }
    x.onreadystatechange = function() {
      x.readyState > 3 && callback && callback(x.responseText, x);
    };
    x.send(JSON.stringify(data));
  } catch (e) {
    typeof window !== 'undefined' && window.console && console.log(e);
  }
}

function getDefaults() {
  return {
    loadPath: 'https://api.locize.io/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    privatePath:
      'https://api.locize.io/private/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    pullPath:
      'https://api.locize.io/pull/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    getLanguagesPath: 'https://api.locize.io/languages/{{projectId}}',
    addPath:
      'https://api.locize.io/missing/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    updatePath:
      'https://api.locize.io/update/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    referenceLng: 'en',
    crossDomain: true,
    setContentTypeJSON: false,
    version: 'latest',
    pull: false,
    private: false,
    whitelistThreshold: 0.9,
    failLoadingOnEmptyJSON: false, // useful if using chained backend
    allowedAddOrUpdateHosts: ['localhost'],
    onSaved: false
  };
}

class I18NextLocizeBackend {
  constructor(services, options, callback) {
    if (services && services.projectId) {
      this.init(null, services, {}, options);
    } else {
      this.init(null, options, {}, callback);
    }

    this.type = 'backend';
  }

  init(services, options = {}, i18nextOptions, callback) {
    this.options = { ...getDefaults(), ...this.options, ...options }; // initial
    this.services = services;

    if (this.options.pull)
      console.warn(
        'deprecated: pull will be removed in future versions and should be replaced with locize private versions'
      );

    const hostname = typeof window !== 'undefined' && window.location && window.location.hostname;
    if (hostname) {
      this.isAddOrUpdateAllowed = typeof this.options.allowedAddOrUpdateHosts === 'function' ?this.options.allowedAddOrUpdateHosts(hostname) : this.options.allowedAddOrUpdateHosts.indexOf(hostname) > -1;

      if (i18nextOptions.saveMissing && !this.isAddOrUpdateAllowed)
        services &&
          services.logger &&
          services.logger.warn(
            `locize-backend: will not save missings because the host "${hostname}" was not in the list of allowedAddOrUpdateHosts: ${this.options.allowedAddOrUpdateHosts.join(
              ', '
            )} (matches need to be exact).`
          );
    } else {
      this.isAddOrUpdateAllowed = true;
    }

    if (typeof callback === 'function') {
      this.getOptions((err, opts) => {
        if (err) return callback(err);

        this.options.referenceLng =
          options.referenceLng ||
          opts.referenceLng ||
          this.options.referenceLng;
        callback(null, opts);
      });
    }

    this.queuedWrites = {};
    this.debouncedProcess = utils.debounce(this.process, 10000);
  }

  getLanguages(callback) {
    const isMissing = utils.isMissingOption(this.options, ['projectId'])
    if (isMissing) return callback(new Error(isMissing));

    let url = utils.interpolate(this.options.getLanguagesPath, {
      projectId: this.options.projectId
    });

    this.loadUrl(url, {}, callback);
  }

  getOptions(callback) {
    this.getLanguages((err, data) => {
      if (err) return callback(err);
      const keys = Object.keys(data);
      if (!keys.length)
        return callback(new Error('was unable to load languages via API'));

      const referenceLng = keys.reduce((mem, k) => {
        const item = data[k];
        if (item.isReferenceLanguage) mem = k;
        return mem;
      }, '');

      const whitelist = keys.reduce((mem, k) => {
        const item = data[k];
        if (
          item.translated[this.options.version] &&
          item.translated[this.options.version] >=
            this.options.whitelistThreshold
        )
          mem.push(k);
        return mem;
      }, []);

      const hasRegion = keys.reduce((mem, k) => {
        if (k.indexOf('-') > -1) return true;
        return mem;
      }, false);

      callback(null, {
        fallbackLng: referenceLng,
        referenceLng,
        whitelist,
        load: hasRegion ? 'all' : 'languageOnly'
      });
    });
  }

  read(language, namespace, callback) {
    let url;
    let options = {};
    if (this.options.private) {
      const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey'])
      if (isMissing) return callback(new Error(isMissing), false);

      url = utils.interpolate(this.options.privatePath, {
        lng: language,
        ns: namespace,
        projectId: this.options.projectId,
        version: this.options.version
      });
      options = { authorize: true };
    } else if (this.options.pull) {
      const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey'])
      if (isMissing) return callback(new Error(isMissing), false);

      url = utils.interpolate(this.options.pullPath, {
        lng: language,
        ns: namespace,
        projectId: this.options.projectId,
        version: this.options.version
      });
      options = { authorize: true };
    } else {
      const isMissing = utils.isMissingOption(this.options, ['projectId', 'version'])
      if (isMissing) return callback(new Error(isMissing), false);

      url = utils.interpolate(this.options.loadPath, {
        lng: language,
        ns: namespace,
        projectId: this.options.projectId,
        version: this.options.version
      });
    }

    this.loadUrl(url, options, callback);
  }

  loadUrl(url, options, callback) {
    ajax(url, { ...this.options, ...options }, (data, xhr) => {
      if (xhr.status >= 500 && xhr.status < 600)
        return callback('failed loading ' + url, true /* retry */);
      if (xhr.status >= 400 && xhr.status < 500)
        return callback('failed loading ' + url, false /* no retry */);

      let ret, err;
      try {
        ret = JSON.parse(data);
      } catch (e) {
        err = 'failed parsing ' + url + ' to json';
      }
      if (err) return callback(err, false);
      if (this.options.failLoadingOnEmptyJSON && !Object.keys(ret).length)
        return callback('loaded result empty for ' + url, false);
      callback(null, ret);
    });
  }

  create(languages, namespace, key, fallbackValue, callback, options) {
    if (!callback) callback = () => {};

    // missing options
    const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey'])
    if (isMissing) return callback(new Error(isMissing));

    // unallowed host
    if (!this.isAddOrUpdateAllowed)
      return callback('host is not allowed to create key.');

    if (typeof languages === 'string') languages = [languages];

    if (languages.filter(l => l === this.options.referenceLng).length < 1) {
      this.services &&
        this.services.logger &&
        this.services.logger.warn(
          `locize-backend: will not save missings because the reference language "${
            this.options.referenceLng
          }" was not in the list of to save languages: ${languages.join(
            ', '
          )} (open your site in the reference language to save missings).`
        );
    }

    languages.forEach(lng => {
      if (lng === this.options.referenceLng)
        this.queue.call(
          this,
          this.options.referenceLng,
          namespace,
          key,
          fallbackValue,
          callback,
          options
        );
    });
  }

  update(languages, namespace, key, fallbackValue, callback, options) {
    if (!callback) callback = () => {};
    if (!this.isAddOrUpdateAllowed)
      return callback('host is not allowed to update key.');
    if (!options) options = {};
    if (typeof languages === 'string') languages = [languages];

    // mark as update
    options.isUpdate = true;

    languages.forEach(lng => {
      if (lng === this.options.referenceLng)
        this.queue.call(
          this,
          this.options.referenceLng,
          namespace,
          key,
          fallbackValue,
          callback,
          options
        );
    });
  }

  write(lng, namespace) {
    let lock = utils.getPath(this.queuedWrites, ['locks', lng, namespace]);
    if (lock) return;

    let missingUrl = utils.interpolate(this.options.addPath, {
      lng: lng,
      ns: namespace,
      projectId: this.options.projectId,
      version: this.options.version
    });
    let updatesUrl = utils.interpolate(this.options.updatePath, {
      lng: lng,
      ns: namespace,
      projectId: this.options.projectId,
      version: this.options.version
    });

    let missings = utils.getPath(this.queuedWrites, [lng, namespace]);
    utils.setPath(this.queuedWrites, [lng, namespace], []);

    if (missings.length) {
      // lock
      utils.setPath(this.queuedWrites, ['locks', lng, namespace], true);

      let hasMissing = false;
      let hasUpdates = false;
      const payloadMissing = {};
      const payloadUpdate = {};

      missings.forEach(item => {
        const value =
          item.options && item.options.tDescription
            ? {
                value: item.fallbackValue || '',
                context: { text: item.options.tDescription }
              }
            : item.fallbackValue || '';
        if (item.options && item.options.isUpdate) {
          if (!hasUpdates) hasUpdates = true;
          payloadUpdate[item.key] = value;
        } else {
          if (!hasMissing) hasMissing = true;
          payloadMissing[item.key] = value;
        }
      });

      let todo = 0;
      if (hasMissing) todo++;
      if (hasUpdates) todo++;
      const doneOne = () => {
        todo--;

        if (!todo) {
          // unlock
          utils.setPath(this.queuedWrites, ['locks', lng, namespace], false);

          missings.forEach(missing => {
            if (missing.callback) missing.callback();
          });

          // emit notification onSaved
          if (this.options.onSaved) this.options.onSaved(lng, namespace);

          // rerun
          this.debouncedProcess(lng, namespace);
        }
      };

      if (!todo) doneOne();

      if (hasMissing) {
        ajax(
          missingUrl,
          { ...{ authorize: true }, ...this.options },
          (data, xhr) => {
            //const statusCode = xhr.status.toString();
            // TODO: if statusCode === 4xx do log

            doneOne();
          },
          payloadMissing
        );
      }

      if (hasUpdates) {
        ajax(
          updatesUrl,
          { ...{ authorize: true }, ...this.options },
          (data, xhr) => {
            //const statusCode = xhr.status.toString();
            // TODO: if statusCode === 4xx do log

            doneOne();
          },
          payloadUpdate
        );
      }
    }
  }

  process() {
    Object.keys(this.queuedWrites).forEach(lng => {
      if (lng === 'locks') return;
      Object.keys(this.queuedWrites[lng]).forEach(ns => {
        const todo = this.queuedWrites[lng][ns];
        if (todo.length) {
          this.write(lng, ns);
        }
      });
    });
  }

  queue(lng, namespace, key, fallbackValue, callback, options) {
    utils.pushPath(this.queuedWrites, [lng, namespace], {
      key: key,
      fallbackValue: fallbackValue || '',
      callback: callback,
      options
    });

    this.debouncedProcess();
  }
}

I18NextLocizeBackend.type = 'backend';

export default I18NextLocizeBackend;
<commit_msg>warn for missing configuration<commit_after>import * as utils from './utils';

// https://gist.github.com/Xeoncross/7663273
function ajax(url, options, callback, data, cache) {
  try {
    var x = new (XMLHttpRequest || ActiveXObject)('MSXML2.XMLHTTP.3.0');
    x.open(data ? 'POST' : 'GET', url, 1);
    if (!options.crossDomain) {
      x.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    }
    if (options.authorize && options.apiKey) {
      x.setRequestHeader('Authorization', options.apiKey);
    }
    if (data || options.setContentTypeJSON) {
      x.setRequestHeader('Content-type', 'application/json');
    }
    x.onreadystatechange = function() {
      x.readyState > 3 && callback && callback(x.responseText, x);
    };
    x.send(JSON.stringify(data));
  } catch (e) {
    typeof window !== 'undefined' && window.console && console.log(e);
  }
}

function getDefaults() {
  return {
    loadPath: 'https://api.locize.io/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    privatePath:
      'https://api.locize.io/private/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    pullPath:
      'https://api.locize.io/pull/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    getLanguagesPath: 'https://api.locize.io/languages/{{projectId}}',
    addPath:
      'https://api.locize.io/missing/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    updatePath:
      'https://api.locize.io/update/{{projectId}}/{{version}}/{{lng}}/{{ns}}',
    referenceLng: 'en',
    crossDomain: true,
    setContentTypeJSON: false,
    version: 'latest',
    pull: false,
    private: false,
    whitelistThreshold: 0.9,
    failLoadingOnEmptyJSON: false, // useful if using chained backend
    allowedAddOrUpdateHosts: ['localhost'],
    onSaved: false
  };
}

class I18NextLocizeBackend {
  constructor(services, options, callback) {
    if (services && services.projectId) {
      this.init(null, services, {}, options);
    } else {
      this.init(null, options, {}, callback);
    }

    this.type = 'backend';
  }

  init(services, options = {}, i18nextOptions, callback) {
    this.options = { ...getDefaults(), ...this.options, ...options }; // initial
    this.services = services;

    if (this.options.pull)
      console.warn(
        'deprecated: pull will be removed in future versions and should be replaced with locize private versions'
      );

    const hostname = typeof window !== 'undefined' && window.location && window.location.hostname;
    if (hostname) {
      this.isAddOrUpdateAllowed = typeof this.options.allowedAddOrUpdateHosts === 'function' ?this.options.allowedAddOrUpdateHosts(hostname) : this.options.allowedAddOrUpdateHosts.indexOf(hostname) > -1;

      if (i18nextOptions.saveMissing && !this.isAddOrUpdateAllowed)
        services &&
          services.logger &&
          services.logger.warn(
            `locize-backend: will not save missings because the host "${hostname}" was not in the list of allowedAddOrUpdateHosts: ${this.options.allowedAddOrUpdateHosts.join(
              ', '
            )} (matches need to be exact).`
          );
    } else {
      this.isAddOrUpdateAllowed = true;
    }

    if (typeof callback === 'function') {
      this.getOptions((err, opts) => {
        if (err) return callback(err);

        this.options.referenceLng =
          options.referenceLng ||
          opts.referenceLng ||
          this.options.referenceLng;
        callback(null, opts);
      });
    }

    this.queuedWrites = {};
    this.debouncedProcess = utils.debounce(this.process, 10000);
  }

  getLanguages(callback) {
    const isMissing = utils.isMissingOption(this.options, ['projectId'])
    if (isMissing) return callback(new Error(isMissing));

    let url = utils.interpolate(this.options.getLanguagesPath, {
      projectId: this.options.projectId
    });

    this.loadUrl(url, {}, callback);
  }

  getOptions(callback) {
    this.getLanguages((err, data) => {
      if (err) return callback(err);
      const keys = Object.keys(data);
      if (!keys.length)
        return callback(new Error('was unable to load languages via API'));

      const referenceLng = keys.reduce((mem, k) => {
        const item = data[k];
        if (item.isReferenceLanguage) mem = k;
        return mem;
      }, '');

      const whitelist = keys.reduce((mem, k) => {
        const item = data[k];
        if (
          item.translated[this.options.version] &&
          item.translated[this.options.version] >=
            this.options.whitelistThreshold
        )
          mem.push(k);
        return mem;
      }, []);

      const hasRegion = keys.reduce((mem, k) => {
        if (k.indexOf('-') > -1) return true;
        return mem;
      }, false);

      callback(null, {
        fallbackLng: referenceLng,
        referenceLng,
        whitelist,
        load: hasRegion ? 'all' : 'languageOnly'
      });
    });
  }

  read(language, namespace, callback) {
    let url;
    let options = {};
    if (this.options.private) {
      const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey'])
      if (isMissing) return callback(new Error(isMissing), false);

      url = utils.interpolate(this.options.privatePath, {
        lng: language,
        ns: namespace,
        projectId: this.options.projectId,
        version: this.options.version
      });
      options = { authorize: true };
    } else if (this.options.pull) {
      const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey'])
      if (isMissing) return callback(new Error(isMissing), false);

      url = utils.interpolate(this.options.pullPath, {
        lng: language,
        ns: namespace,
        projectId: this.options.projectId,
        version: this.options.version
      });
      options = { authorize: true };
    } else {
      const isMissing = utils.isMissingOption(this.options, ['projectId', 'version'])
      if (isMissing) return callback(new Error(isMissing), false);

      url = utils.interpolate(this.options.loadPath, {
        lng: language,
        ns: namespace,
        projectId: this.options.projectId,
        version: this.options.version
      });
    }

    this.loadUrl(url, options, callback);
  }

  loadUrl(url, options, callback) {
    ajax(url, { ...this.options, ...options }, (data, xhr) => {
      if (xhr.status >= 500 && xhr.status < 600)
        return callback('failed loading ' + url, true /* retry */);
      if (xhr.status >= 400 && xhr.status < 500)
        return callback('failed loading ' + url, false /* no retry */);

      let ret, err;
      try {
        ret = JSON.parse(data);
      } catch (e) {
        err = 'failed parsing ' + url + ' to json';
      }
      if (err) return callback(err, false);
      if (this.options.failLoadingOnEmptyJSON && !Object.keys(ret).length)
        return callback('loaded result empty for ' + url, false);
      callback(null, ret);
    });
  }

  create(languages, namespace, key, fallbackValue, callback, options) {
    if (!callback) callback = () => {};

    // missing options
    const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey', 'referenceLng'])
    if (isMissing) return callback(new Error(isMissing));

    // unallowed host
    if (!this.isAddOrUpdateAllowed)
      return callback('host is not allowed to create key.');

    if (typeof languages === 'string') languages = [languages];

    if (languages.filter(l => l === this.options.referenceLng).length < 1) {
      this.services &&
        this.services.logger &&
        this.services.logger.warn(
          `locize-backend: will not save missings because the reference language "${
            this.options.referenceLng
          }" was not in the list of to save languages: ${languages.join(
            ', '
          )} (open your site in the reference language to save missings).`
        );
    }

    languages.forEach(lng => {
      if (lng === this.options.referenceLng)
        this.queue.call(
          this,
          this.options.referenceLng,
          namespace,
          key,
          fallbackValue,
          callback,
          options
        );
    });
  }

  update(languages, namespace, key, fallbackValue, callback, options) {
    if (!callback) callback = () => {};

    // missing options
    const isMissing = utils.isMissingOption(this.options, ['projectId', 'version', 'apiKey', 'referenceLng'])
    if (isMissing) return callback(new Error(isMissing));

    if (!this.isAddOrUpdateAllowed)
      return callback('host is not allowed to update key.');

    if (!options) options = {};
    if (typeof languages === 'string') languages = [languages];

    // mark as update
    options.isUpdate = true;

    languages.forEach(lng => {
      if (lng === this.options.referenceLng)
        this.queue.call(
          this,
          this.options.referenceLng,
          namespace,
          key,
          fallbackValue,
          callback,
          options
        );
    });
  }

  write(lng, namespace) {
    let lock = utils.getPath(this.queuedWrites, ['locks', lng, namespace]);
    if (lock) return;

    let missingUrl = utils.interpolate(this.options.addPath, {
      lng: lng,
      ns: namespace,
      projectId: this.options.projectId,
      version: this.options.version
    });
    let updatesUrl = utils.interpolate(this.options.updatePath, {
      lng: lng,
      ns: namespace,
      projectId: this.options.projectId,
      version: this.options.version
    });

    let missings = utils.getPath(this.queuedWrites, [lng, namespace]);
    utils.setPath(this.queuedWrites, [lng, namespace], []);

    if (missings.length) {
      // lock
      utils.setPath(this.queuedWrites, ['locks', lng, namespace], true);

      let hasMissing = false;
      let hasUpdates = false;
      const payloadMissing = {};
      const payloadUpdate = {};

      missings.forEach(item => {
        const value =
          item.options && item.options.tDescription
            ? {
                value: item.fallbackValue || '',
                context: { text: item.options.tDescription }
              }
            : item.fallbackValue || '';
        if (item.options && item.options.isUpdate) {
          if (!hasUpdates) hasUpdates = true;
          payloadUpdate[item.key] = value;
        } else {
          if (!hasMissing) hasMissing = true;
          payloadMissing[item.key] = value;
        }
      });

      let todo = 0;
      if (hasMissing) todo++;
      if (hasUpdates) todo++;
      const doneOne = () => {
        todo--;

        if (!todo) {
          // unlock
          utils.setPath(this.queuedWrites, ['locks', lng, namespace], false);

          missings.forEach(missing => {
            if (missing.callback) missing.callback();
          });

          // emit notification onSaved
          if (this.options.onSaved) this.options.onSaved(lng, namespace);

          // rerun
          this.debouncedProcess(lng, namespace);
        }
      };

      if (!todo) doneOne();

      if (hasMissing) {
        ajax(
          missingUrl,
          { ...{ authorize: true }, ...this.options },
          (data, xhr) => {
            //const statusCode = xhr.status.toString();
            // TODO: if statusCode === 4xx do log

            doneOne();
          },
          payloadMissing
        );
      }

      if (hasUpdates) {
        ajax(
          updatesUrl,
          { ...{ authorize: true }, ...this.options },
          (data, xhr) => {
            //const statusCode = xhr.status.toString();
            // TODO: if statusCode === 4xx do log

            doneOne();
          },
          payloadUpdate
        );
      }
    }
  }

  process() {
    Object.keys(this.queuedWrites).forEach(lng => {
      if (lng === 'locks') return;
      Object.keys(this.queuedWrites[lng]).forEach(ns => {
        const todo = this.queuedWrites[lng][ns];
        if (todo.length) {
          this.write(lng, ns);
        }
      });
    });
  }

  queue(lng, namespace, key, fallbackValue, callback, options) {
    utils.pushPath(this.queuedWrites, [lng, namespace], {
      key: key,
      fallbackValue: fallbackValue || '',
      callback: callback,
      options
    });

    this.debouncedProcess();
  }
}

I18NextLocizeBackend.type = 'backend';

export default I18NextLocizeBackend;
<|endoftext|>


<commit_before>/* global document */
import React from 'react';
import { render } from 'react-dom';
import Root from './js/Root';

render(<Root />,
  document.getElementById('root'),
);
<commit_msg>Format<commit_after>/* global document */
import React from 'react';
import { render } from 'react-dom';
import Root from './js/Root';

render(<Root />, document.getElementById('root'));
<|endoftext|>


<commit_before>import puppeteer from 'puppeteer';
import colors from 'colors';
import filterEmulateInfos from './filterEmulateInfos';
import initPage2imageKits from './kits';

colors.setTheme({
  info: 'green',
  data: 'grey',
  warn: 'yellow',
  debug: 'blue',
  error: 'red',
  path: 'white',
});

function checkBeforeRun(config, callback) {
  if (config) return callback(config);
  return Promise.resolve('skip');
}

class Screenshot {

  static async init(config) {
    Object.assign(this.config, config);
    if (!this.browser) this.browser = await puppeteer.launch(this.config.launchConfig);
  }

  static async takeScreenshot(url) {
    if (!url) return null;

    await checkBeforeRun(!this.browser, this.init);

    const page = await this.browser.newPage();
    const {
      waitForFunction, waitUntil,
      screenshotConfig, viewportConfig, emulateConfig,
      disableJS, waitFor,
    } = this.config;

    if (screenshotConfig.path) screenshotConfig.path = screenshotConfig.path.replace(/\?.*\./, '.'); // ? Symbol will cause windows user cannot save file

    await checkBeforeRun(viewportConfig, page.setViewport.bind(page));
    await checkBeforeRun(filterEmulateInfos(emulateConfig), page.emulate.bind(page));
    await checkBeforeRun(disableJS, page.setJavaScriptEnabled.bind(page, false));

    await page.goto(url, { waitUntil });

    await page.evaluate(initPage2imageKits);
    await checkBeforeRun(waitForFunction, page.waitForFunction.bind(page));
    await checkBeforeRun(waitFor, page.waitFor.bind(page));

    const pic = await page.screenshot(screenshotConfig);

    page.close();
    return pic;
  }

  constructor(config) {
    this.init = Screenshot.init.bind(this);
    this.takeScreenshot = Screenshot.takeScreenshot.bind(this);
    this.config = Object.assign({
      waitForFunction: null,
      waitUntil: null,
      waitFor: null,
      viewportConfig: null,
      screenshotConfig: {
        quality: 80,
        type: 'jpeg',
        fullPage: true,
      },
    }, config);
  }

}

export default Screenshot;
<commit_msg>feature - take screenshot from page element<commit_after>import puppeteer from 'puppeteer';
import colors from 'colors';
import filterEmulateInfos from './filterEmulateInfos';
import initPage2imageKits from './kits';

colors.setTheme({
  info: 'green',
  data: 'grey',
  warn: 'yellow',
  debug: 'blue',
  error: 'red',
  path: 'white',
});

function checkBeforeRun(config, callback) {
  if (config) return callback(config);
  return Promise.resolve('skip');
}

class Screenshot {

  static async init(config) {
    Object.assign(this.config, config);
    if (!this.browser) this.browser = await puppeteer.launch(this.config.launchConfig);
  }

  static async takeScreenshot(url) {
    if (!url) return null;

    await checkBeforeRun(!this.browser, this.init);

    const page = await this.browser.newPage();
    const {
      waitForFunction, waitUntil,
      screenshotConfig, viewportConfig, emulateConfig,
      disableJS, waitFor, selector,
    } = this.config;

    if (screenshotConfig.path) screenshotConfig.path = screenshotConfig.path.replace(/\?.*\./, '.'); // ? Symbol will cause windows user cannot save file

    await checkBeforeRun(viewportConfig, page.setViewport.bind(page));
    await checkBeforeRun(filterEmulateInfos(emulateConfig), page.emulate.bind(page));
    await checkBeforeRun(disableJS, page.setJavaScriptEnabled.bind(page, false));

    await page.goto(url, { waitUntil });

    await page.evaluate(initPage2imageKits);
    await checkBeforeRun(waitForFunction, page.waitForFunction.bind(page));
    await checkBeforeRun(waitFor, page.waitFor.bind(page));

    async function takeScreenshot() {
      if (selector) {
        delete screenshotConfig.fullPage;
        const element = await page.$(selector);

        if (!element) throw new Error(`element selector "${selector}" can not find any element`);

        return element.screenshot(screenshotConfig);
      } else {
        return page.screenshot(screenshotConfig);
      }
    }

    const pic = await takeScreenshot();

    page.close();

    return pic;
  }

  constructor(config) {
    this.init = Screenshot.init.bind(this);
    this.takeScreenshot = Screenshot.takeScreenshot.bind(this);
    this.config = Object.assign({
      waitForFunction: null,
      waitUntil: null,
      waitFor: null,
      viewportConfig: null,
      selector: null,
      screenshotConfig: {
        quality: 80,
        type: 'jpeg',
        fullPage: true,
      },
    }, config);
  }

}

export default Screenshot;
<|endoftext|>