<commit_before>import invariant from 'invariant';
import deepEqual from 'deep-equal';
import { PUSH, REPLACE } from './Actions';
import createLocation from './createLocation';

function createRandomKey(length) {
  return Math.random().toString(36).substr(2, length);
}

function locationsAreEqual(a, b) {
  return a.pathname === b.pathname &&
    a.search === b.search &&
    //a.action === b.action && // Different action !== location change.
    a.key === b.key &&
    deepEqual(a.state, b.state);
}

var DefaultKeyLength = 6;

function createHistory(options={}) {
  var { getCurrentLocation, finishTransition, cancelTransition, go, keyLength, getUserConfirmation } = options;

  if (typeof keyLength !== 'number')
    keyLength = DefaultKeyLength;

  var transitionHooks = [];
  var transitionMiddlewareHandlers = [];
  var changeListeners = [];
  var pendingLocation;
  var location;

  function updateLocation(newLocation) {
    location = newLocation;

    changeListeners.forEach(function (listener) {
      listener(location);
    });
  }

  function addChangeListener(listener) {
    changeListeners.push(listener);
  }

  function removeChangeListener(listener) {
    changeListeners = changeListeners.filter(item => item !== listener);
  }

  function listen(listener) {
    addChangeListener(listener);

    if (location) {
      listener(location);
    } else {
      var currentLocation = getCurrentLocation();
      pendingLocation = currentLocation;
      processMiddleware(function(ok) {
        if (pendingLocation !== currentLocation) {
          return; // An intercepting transition has occured.
        }
        pendingLocation = null;
        if (ok) {
          updateLocation(currentLocation);
        } else {
          invariant(false, 'The initial route location transition was aborted by middleware.');
        }
      });
    }

    return function () {
      removeChangeListener(listener);
    };
  }

  function registerTransitionHook(hook) {
    if (transitionHooks.indexOf(hook) === -1)
      transitionHooks.push(hook);
  }

  function unregisterTransitionHook(hook) {
    transitionHooks = transitionHooks.filter(item => item !== hook);
  }

  function registerTransitionMiddleware(handler) {
    if (transitionMiddlewareHandlers.indexOf(handler) === -1)
      transitionMiddlewareHandlers.push(handler);
    return function() {
      unregisterTransitionMiddleware(handler);
    }
  }

  function unregisterTransitionMiddleware(handler) {
    transitionMiddlewareHandlers = transitionMiddlewareHandlers.filter(item => item !== handler);
  }

  function processMiddleware(callback) {
    let numCompletions = 0;
    let numHandlers = transitionMiddlewareHandlers.length;
    if (numHandlers === 0) {
      callback(true);
    } else {
      transitionMiddlewareHandlers.forEach(handler => {
        handler(pendingLocation, location,
          () => {
            numCompletions++;
            if (numCompletions === numHandlers) {
              callback(true);
            }
          },
          () => {
            callback(false);
          }
        )
      });
    }
  }

  function getTransitionConfirmationMessage() {
    var message = null;

    for (var i = 0, len = transitionHooks.length; i < len && message == null; ++i)
      message = transitionHooks[i].call(this);

    return message;
  }

  function confirmTransition(callback) {
    var message = getTransitionConfirmationMessage();

    if (getUserConfirmation && typeof message === 'string') {
      getUserConfirmation(message, function (ok) {
        callback(ok !== false);
      });
    } else {
      callback(message !== false);
    }
  }

  function transitionTo(nextLocation) {
    if (location && locationsAreEqual(location, nextLocation))
      return; // Nothing to do.

    pendingLocation = nextLocation;

    processMiddleware(function(ok) {
      if (pendingLocation !== nextLocation) {
        return; // An interecepting transition has occurred.
      }
      if (ok) {
        confirmTransition(function (ok) {
          if (pendingLocation !== nextLocation) {
            return; // An interecepting transition has occurred.
          }
          pendingLocation = null;
          if (ok) {
            finishTransition(nextLocation);
            updateLocation(nextLocation);
          } else if (cancelTransition) {
            cancelTransition(nextLocation);
          }
        });
      } else if (cancelTransition) {
        pendingLocation = null;
        cancelTransition(nextLocation);
      }
    });
  }

  function pushState(state, path) {
    transitionTo(
      createLocation(path, state, PUSH, createKey())
    );
  }

  function replaceState(state, path) {
    transitionTo(
      createLocation(path, state, REPLACE, createKey())
    );
  }

  function goBack() {
    go(-1);
  }

  function goForward() {
    go(1);
  }

  function createKey() {
    return createRandomKey(keyLength);
  }

  function createHref(path) {
    return path;
  }

  return {
    listen,
    registerTransitionHook,
    unregisterTransitionHook,
    registerTransitionMiddleware,
    unregisterTransitionMiddleware,
    getTransitionConfirmationMessage,
    transitionTo,
    pushState,
    replaceState,
    go,
    goBack,
    goForward,
    createKey,
    createHref
  };
}

export default createHistory;
<commit_msg>Run middleware serially.<commit_after>import invariant from 'invariant';
import deepEqual from 'deep-equal';
import { PUSH, REPLACE } from './Actions';
import createLocation from './createLocation';

function createRandomKey(length) {
  return Math.random().toString(36).substr(2, length);
}

function locationsAreEqual(a, b) {
  return a.pathname === b.pathname &&
    a.search === b.search &&
    //a.action === b.action && // Different action !== location change.
    a.key === b.key &&
    deepEqual(a.state, b.state);
}

var DefaultKeyLength = 6;

function createHistory(options={}) {
  var { getCurrentLocation, finishTransition, cancelTransition, go, keyLength, getUserConfirmation } = options;

  if (typeof keyLength !== 'number')
    keyLength = DefaultKeyLength;

  var transitionHooks = [];
  var transitionMiddlewareHandlers = [];
  var changeListeners = [];
  var pendingLocation;
  var location;

  function updateLocation(newLocation) {
    location = newLocation;

    changeListeners.forEach(function (listener) {
      listener(location);
    });
  }

  function addChangeListener(listener) {
    changeListeners.push(listener);
  }

  function removeChangeListener(listener) {
    changeListeners = changeListeners.filter(item => item !== listener);
  }

  function listen(listener) {
    addChangeListener(listener);

    if (location) {
      listener(location);
    } else {
      var currentLocation = getCurrentLocation();
      pendingLocation = currentLocation;
      processMiddleware(function(ok) {
        if (pendingLocation !== currentLocation) {
          return; // An intercepting transition has occured.
        }
        pendingLocation = null;
        if (ok) {
          updateLocation(currentLocation);
        } else {
          invariant(false, 'The initial route location transition was aborted by middleware.');
        }
      });
    }

    return function () {
      removeChangeListener(listener);
    };
  }

  function registerTransitionHook(hook) {
    if (transitionHooks.indexOf(hook) === -1)
      transitionHooks.push(hook);
  }

  function unregisterTransitionHook(hook) {
    transitionHooks = transitionHooks.filter(item => item !== hook);
  }

  function registerTransitionMiddleware(handler) {
    if (transitionMiddlewareHandlers.indexOf(handler) === -1)
      transitionMiddlewareHandlers.push(handler);
    return function() {
      unregisterTransitionMiddleware(handler);
    }
  }

  function unregisterTransitionMiddleware(handler) {
    transitionMiddlewareHandlers = transitionMiddlewareHandlers.filter(item => item !== handler);
  }

  function processMiddleware(callback, optionalMiddleware) {
    let handlers = optionalMiddleware || transitionMiddlewareHandlers;

    if (handlers.length === 0) {
      // All handlers have been processed w/out aborting. Callback w/ OK status.
      callback(true);
      return;
    }

    // Cache pending location to detect if an intercepting transition occurs.
    let pendingLocationCache = pendingLocation;

    handlers[0](pendingLocation, location,
      function() {
        if (pendingLocationCache === pendingLocation) {
          processMiddleware(callback, handlers.slice(1));
        }
      },
      function() {
        if (pendingLocationCache === pendingLocation) {
          callback(false);
        }
      }
    );
  }

  function getTransitionConfirmationMessage() {
    var message = null;

    for (var i = 0, len = transitionHooks.length; i < len && message == null; ++i)
      message = transitionHooks[i].call(this);

    return message;
  }

  function confirmTransition(callback) {
    var message = getTransitionConfirmationMessage();

    if (getUserConfirmation && typeof message === 'string') {
      getUserConfirmation(message, function (ok) {
        callback(ok !== false);
      });
    } else {
      callback(message !== false);
    }
  }

  function transitionTo(nextLocation) {
    if (location && locationsAreEqual(location, nextLocation))
      return; // Nothing to do.

    pendingLocation = nextLocation;

    processMiddleware(function(ok) {
      if (pendingLocation !== nextLocation) {
        return; // An interecepting transition has occurred.
      }
      if (ok) {
        confirmTransition(function (ok) {
          if (pendingLocation !== nextLocation) {
            return; // An interecepting transition has occurred.
          }
          pendingLocation = null;
          if (ok) {
            finishTransition(nextLocation);
            updateLocation(nextLocation);
          } else if (cancelTransition) {
            cancelTransition(nextLocation);
          }
        });
      } else if (cancelTransition) {
        pendingLocation = null;
        cancelTransition(nextLocation);
      }
    });
  }

  function pushState(state, path) {
    transitionTo(
      createLocation(path, state, PUSH, createKey())
    );
  }

  function replaceState(state, path) {
    transitionTo(
      createLocation(path, state, REPLACE, createKey())
    );
  }

  function goBack() {
    go(-1);
  }

  function goForward() {
    go(1);
  }

  function createKey() {
    return createRandomKey(keyLength);
  }

  function createHref(path) {
    return path;
  }

  return {
    listen,
    registerTransitionHook,
    unregisterTransitionHook,
    registerTransitionMiddleware,
    unregisterTransitionMiddleware,
    getTransitionConfirmationMessage,
    transitionTo,
    pushState,
    replaceState,
    go,
    goBack,
    goForward,
    createKey,
    createHref
  };
}

export default createHistory;

{'fraction_non_alphanumeric': 0.10935924466037217, 'fraction_numerical': 0.002108350902924191, 'mean_word_length': 2.874289772727273, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4977352', 'n_tokens_mistral': 2950, 'n_tokens_neox': 2915, 'n_words': 653}