<commit_before>import _ from 'lodash'

const isPUT = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.PUT
const isTAKE = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.TAKE
const isCALL = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.CALL
const isRACE = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.RACE

export const matchers = {
  putAction: (action) => _.isString(action)
    ? effect => isPUT(effect) && effect.PUT.action.type === action
    : effect => isPUT(effect) && _.isEqual(effect.PUT.action, action),
  takeAction: pattern =>
    effect => isTAKE(effect) && effect.TAKE.pattern === pattern,
  effect: effectToMatch =>
    effect => _.isEqual(effect, effectToMatch),
  call: fn =>
    effect => isCALL(effect) && effect.CALL.fn === fn,
  callWithArgs: (fn, args) =>
    effect => isCALL(effect) && effect.CALL.fn === fn && _.isMatch(effect.CALL.args, args),
  callWithExactArgs: (fn, args) =>
    effect => isCALL(effect) && effect.CALL.fn === fn && _.isEqual(effect.CALL.args, args)
}

function recursive(matcher) {
  const rmatcher = (effect) => {
    if (matcher(effect)) return true
    else if (isRACE(effect)) {
      return !!_.find(effect.RACE, rmatcher)
    } else if (_.isArray(effect)) {
      return !!effect.find(rmatcher)
    }
  }
  return rmatcher
}

function findAllIndexes (array, matcher, fromPos=0) {
  const indexes = []
  for (let i=fromPos; i < array.length; i++) {
    if (matcher(array[i])) indexes.push(i)
  }
  return indexes
}

export function mockSaga (saga) {
  const GeneratorFunction = (function*(){}).constructor;
  if (!saga instanceof GeneratorFunction) throw new Error('saga must be a generator function')
  const effects = []
  const listeners = []
  const stubs = []
  const g = saga()
  const mock = function * () {
    let current = g.next()
    while (!current.done) {
      const effect = current.value
      console.log('>> effect:', effect)
      effects.push(effect)
      listeners.forEach((l) => recursive(l.match)(effect) && setTimeout(l.callback))
      const stub = stubs.find((s) => s.match(effect))
      try {
        const data = stub ? stub.stub() : (yield effect)
        current = g.next(data)
      } catch (error) {
        current = g.throw(error)
      }
    }
    return current.value
  }

  const findEffect = (effect, fromPos = 0) => findAllIndexes(effects, recursive(matchers.effect(effect)), fromPos)
  const findPuttedAction = (action, fromPos = 0) => findAllIndexes(effects, recursive(matchers.putAction(action)), fromPos)
  const findTakenAction = (pattern, fromPos = 0) => findAllIndexes(effects, recursive(matchers.takeAction(pattern)), fromPos)
  const findCall = (fn, fromPos = 0) => findAllIndexes(effects, recursive(matchers.call(fn)), fromPos)
  const findCallWithArgs = (fn, args, fromPos = 0) => findAllIndexes(effects, recursive(matchers.callWithArgs(fn, args)), fromPos)
  const findCallWithExactArgs = (fn, args, fromPos = 0) => findAllIndexes(effects, recursive(matchers.callWithExactArgs(fn, args)), fromPos)

  function createResult (indexes) {
    const isPresent = indexes.length > 0
    const filteredEffects = indexes.map(i => effects[i])
    const next = isPresent ? indexes[0] + 1 : 0
    const count = indexes.length
    return {
      indexes,
      effects: filteredEffects,
      isPresent,
      count,
      instance: number => createResult(number <= count ? [indexes[number]] : []),
      followedBy: {
        effect: effect => createResult(isPresent ? findEffect(effect, next) : []),
        puttedAction: action => createResult(isPresent ? findPuttedAction(action, next) : []),
        takenAction: pattern => createResult(isPresent ? findTakenAction(pattern, next) : []),
        call: fn => createResult(isPresent ? findCall(fn, next) : []),
        callWithArgs: (fn, ...args) => createResult(isPresent ? findCallWithArgs(fn, args, next) : []),
        callWithExactArgs: (fn, ...args) => createResult(isPresent ? findCallWithExactArgs(fn, args, next) : [])
      }
    }
  }

  function createListener (callback, matcher, ...args) {
    listeners.push({ match: matcher(...args), callback })
    return mock
  }

  function createStub (matcher, stub) {
    if (!_.isFunction(stub)) throw new Error('stub function required')
    stubs.push({ match: matcher, stub })
    return mock
  }

  return Object.assign(mock, {
    generatedEffect: (effect) => createResult(findEffect(effect)),
    puttedAction: (action) => createResult(findPuttedAction(action)),
    takenAction: (pattern) => createResult(findTakenAction(pattern)),
    called: (fn) => createResult(findCall(fn)),
    calledWithArgs: (fn, ...args) => createResult(findCallWithArgs(fn, args)),
    calledWithExactArgs: (fn, ...args) => createResult(findCallWithExactArgs(fn, args)),

    onEffect: (effect, callback) => createListener(callback, matchers.effect, effect),
    onTakeAction: (pattern, callback)  => createListener(callback, matchers.takeAction, pattern),
    onPuttedAction: (action, callback)  => createListener(callback, matchers.putAction, action),
    onCall: (fn, callback) => createListener(callback, matchers.call, fn),
    onCallWithArgs: (fn, args, callback) => createListener(callback, matchers.callWithArgs, fn, args),
    onCallWithExactArgs: (fn, args, callback) => createListener(callback, matchers.callWithExactArgs, fn, args),

    stubCall: (fn, stub) => createStub(matchers.call(fn), stub),
    stubCallWithArgs: (fn, args, stub) => createStub(matchers.callWithArgs(fn, args), stub),
    stubCallWithExactArgs: (fn, args, stub) => createStub(matchers.callWithExactArgs(fn, args), stub),
  })
}
<commit_msg>add precededBy<commit_after>import _ from 'lodash'

const isPUT = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.PUT
const isTAKE = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.TAKE
const isCALL = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.CALL
const isRACE = (effect) => _.isObject(effect) && effect['@@redux-saga/IO'] && effect.RACE

export const matchers = {
  putAction: (action) => _.isString(action)
    ? effect => isPUT(effect) && effect.PUT.action.type === action
    : effect => isPUT(effect) && _.isEqual(effect.PUT.action, action),
  takeAction: pattern =>
    effect => isTAKE(effect) && effect.TAKE.pattern === pattern,
  effect: effectToMatch =>
    effect => _.isEqual(effect, effectToMatch),
  call: fn =>
    effect => isCALL(effect) && effect.CALL.fn === fn,
  callWithArgs: (fn, args) =>
    effect => isCALL(effect) && effect.CALL.fn === fn && _.isMatch(effect.CALL.args, args),
  callWithExactArgs: (fn, args) =>
    effect => isCALL(effect) && effect.CALL.fn === fn && _.isEqual(effect.CALL.args, args)
}

function recursive(matcher) {
  const rmatcher = (effect) => {
    if (matcher(effect)) return true
    else if (isRACE(effect)) {
      return !!_.find(effect.RACE, rmatcher)
    } else if (_.isArray(effect)) {
      return !!effect.find(rmatcher)
    }
  }
  return rmatcher
}

function findAllIndexes (array, matcher, fromPos=0, last=(array.length-1)) {
  const indexes = []
  for (let i=fromPos; i <= last; i++) {
    if (matcher(array[i])) indexes.push(i)
  }
  return indexes
}

export function mockSaga (saga) {
  const GeneratorFunction = (function*(){}).constructor;
  if (!saga instanceof GeneratorFunction) throw new Error('saga must be a generator function')
  const effects = []
  const listeners = []
  const stubs = []
  const g = saga()
  const mock = function * () {
    let current = g.next()
    while (!current.done) {
      const effect = current.value
      console.log('>> effect:', effect)
      effects.push(effect)
      listeners.forEach((l) => recursive(l.match)(effect) && setTimeout(l.callback))
      const stub = stubs.find((s) => s.match(effect))
      try {
        const data = stub ? stub.stub() : (yield effect)
        current = g.next(data)
      } catch (error) {
        current = g.throw(error)
      }
    }
    return current.value
  }

  const findEffect = (effect, fromPos = 0, last) => findAllIndexes(effects, recursive(matchers.effect(effect)), fromPos, last)
  const findPuttedAction = (action, fromPos = 0, last) => findAllIndexes(effects, recursive(matchers.putAction(action)), fromPos, last)
  const findTakenAction = (pattern, fromPos = 0, last) => findAllIndexes(effects, recursive(matchers.takeAction(pattern)), fromPos, last)
  const findCall = (fn, fromPos = 0, last) => findAllIndexes(effects, recursive(matchers.call(fn)), fromPos, last)
  const findCallWithArgs = (fn, args, fromPos = 0, last) => findAllIndexes(effects, recursive(matchers.callWithArgs(fn, args)), fromPos, last)
  const findCallWithExactArgs = (fn, args, fromPos = 0, last) => findAllIndexes(effects, recursive(matchers.callWithExactArgs(fn, args)), fromPos, last)

  function createResult (indexes) {
    const isPresent = indexes.length > 0
    const filteredEffects = indexes.map(i => effects[i])
    const count = indexes.length
    const next = isPresent ? indexes[0] + 1 : 0
    const prev = isPresent ? indexes[count - 1] - 1 : 0
    return {
      indexes,
      effects: filteredEffects,
      isPresent,
      count,
      instance: number => createResult(number <= count ? [indexes[number]] : []),
      first: () => createResult(isPresent ? [indexes[0]] : []),
      last: () => createResult(isPresent ? [indexes[count - 1]] : []),
      followedBy: {
        effect: effect => createResult(isPresent ? findEffect(effect, next) : []),
        puttedAction: action => createResult(isPresent ? findPuttedAction(action, next) : []),
        takenAction: pattern => createResult(isPresent ? findTakenAction(pattern, next) : []),
        call: fn => createResult(isPresent ? findCall(fn, next) : []),
        callWithArgs: (fn, ...args) => createResult(isPresent ? findCallWithArgs(fn, args, next) : []),
        callWithExactArgs: (fn, ...args) => createResult(isPresent ? findCallWithExactArgs(fn, args, next) : [])
      },
      precededBy: {
        effect: effect => createResult(isPresent ? findEffect(effect, 0, prev) : []),
        puttedAction: action => createResult(isPresent ? findPuttedAction(action, 0, prev) : []),
        takenAction: pattern => createResult(isPresent ? findTakenAction(pattern, 0, prev) : []),
        call: fn => createResult(isPresent ? findCall(fn, 0, prev) : []),
        callWithArgs: (fn, ...args) => createResult(isPresent ? findCallWithArgs(fn, args, 0, prev) : []),
        callWithExactArgs: (fn, ...args) => createResult(isPresent ? findCallWithExactArgs(fn, args, 0, prev) : [])
      }
    }
  }

  function createListener (callback, matcher, ...args) {
    listeners.push({ match: matcher(...args), callback })
    return mock
  }

  function createStub (matcher, stub) {
    if (!_.isFunction(stub)) throw new Error('stub function required')
    stubs.push({ match: matcher, stub })
    return mock
  }

  return Object.assign(mock, {
    generatedEffect: (effect) => createResult(findEffect(effect)),
    puttedAction: (action) => createResult(findPuttedAction(action)),
    takenAction: (pattern) => createResult(findTakenAction(pattern)),
    called: (fn) => createResult(findCall(fn)),
    calledWithArgs: (fn, ...args) => createResult(findCallWithArgs(fn, args)),
    calledWithExactArgs: (fn, ...args) => createResult(findCallWithExactArgs(fn, args)),

    onEffect: (effect, callback) => createListener(callback, matchers.effect, effect),
    onTakeAction: (pattern, callback)  => createListener(callback, matchers.takeAction, pattern),
    onPuttedAction: (action, callback)  => createListener(callback, matchers.putAction, action),
    onCall: (fn, callback) => createListener(callback, matchers.call, fn),
    onCallWithArgs: (fn, args, callback) => createListener(callback, matchers.callWithArgs, fn, args),
    onCallWithExactArgs: (fn, args, callback) => createListener(callback, matchers.callWithExactArgs, fn, args),

    stubCall: (fn, stub) => createStub(matchers.call(fn), stub),
    stubCallWithArgs: (fn, args, stub) => createStub(matchers.callWithArgs(fn, args), stub),
    stubCallWithExactArgs: (fn, args, stub) => createStub(matchers.callWithExactArgs(fn, args), stub),
  })
}
<|endoftext|>


<commit_before>var path = require('path')
var http = require('http')
var jDad = require('jdad')
var merge = require('lodash.merge')
var debug = require('debug')('micromono:router')
var assign = require('lodash.assign')
var argsNames = require('js-args-names')
var httpProxy = require('http-proxy')

/**
 * Create route definition object from route name.
 *
 * @param  {String}    routeName Path of the route
 * @return {Object}    Route definition object
 */
exports.generateRouteByName = function(routeName, defaultMethod) {
  if ('string' === typeof routeName) {
    var _path = routeName.split('::')
    var method = defaultMethod || 'get'
    var route = {
      name: routeName
    }

    if (2 === _path.length) {
      method = _path[0]
      _path = _path[1]
    } else if (3 === _path.length) {
      // This is GET route with page definition in format of:
      // `[method]::[routeUrl]::[templatePath]`
      // `get::/abc::public/abc.jsx!`
      method = _path[0]
      route.page = _path[2]
      _path = _path[1]
    } else {
      _path = routeName
    }

    route.path = _path
    route.method = method

    return route
  } else {
    console.error('Route name must be a string.')
    process.exit(-1)
  }
}

/**
 * Normalize route definition to a portable format which could be easily used
 * by different web frameworks.
 *
 * ```javascript
 * route: {
 *   'get::/user/:name': function(req, res) {...}
 * }
 * ```
 *
 * will be formatted into:
 *
 * ```javascript
 * {
 *   name: 'get::/user/:name',
 *   method: 'get',
 *   path: '/user/:name',
 *   handler: [Function],
 *   args: ['req', 'res'],
 *   middleware: null
 * }
 * ```
 *
 * Example with route middleware:
 *
 * ```javascript
 * route: {
 *   'get::/user/:name': [function(req, res, next) {...}, function(req, res) {...}]
 * }
 * ```
 *
 * will be formatted into:
 *
 * ```javascript
 * {
 *   name: 'get::/user/:name',
 *   method: 'get',
 *   path: '/user/:name',
 *   handler: Function,
 *   args: ['req', 'res'],
 *   middleware: [Function]
 * }
 * ```
 *
 * @param {Object}  route   Route definition object.
 * @param {Service} service Instance of service.
 * @return {Object}         Formatted routes object.
 */
exports.normalizeRoutes = function(route, page, pageApiBaseUrl) {
  var _routes = {}

  Object.keys(route).forEach(function(routePath) {
    var middleware
    var routeHandler = route[routePath]
    var _route = exports.generateRouteByName(routePath)

    // Page contains mapping between url and template for client side routing
    // and rendering. e.g.:
    //
    // {`/abc`: 'public/abc.mustache'}
    //
    // The above tells url `/abc` uses template `public/abc.mustache` for client
    // side rendering.
    //
    if (page && page.hasOwnProperty(routePath))
      _route.page = page[routePath]
    if (Array.isArray(routeHandler)) {
      middleware = routeHandler
      routeHandler = middleware.pop()
    }

    _route.args = argsNames(routeHandler)
    _route.handler = routeHandler
    _route.middleware = middleware || null

    if (_route.page) {
      // Add the page api route
      var apiRoutePath = path.join(pageApiBaseUrl, _route.path)

      if (apiRoutePath.length > 1 && '/' === apiRoutePath[apiRoutePath.length - 1])
        apiRoutePath = apiRoutePath.slice(0, -1)

      if (!_routes[apiRoutePath]) {
        _route.api = apiRoutePath
        _routes[apiRoutePath] = {
          name: apiRoutePath,
          method: _route.method,
          path: apiRoutePath,
          args: _route.args,
          handler: _route.handler,
          middleware: _route.middleware
        }
      }
    }

    _routes[routePath] = _route
  })

  return _routes
}

exports.normalizeUses = function(use) {
  var _uses = {}

  Object.keys(use).forEach(function(name) {
    var _use = use[name]
    if (!Array.isArray(_use))
      _use = [_use]

    _use = _use.map(function(url) {
      if ('string' === typeof url)
        url = exports.generateRouteByName(url, 'default')
      return url
    })
    _uses[name] = _use
  })

  return _uses
}

exports.normalizeMiddlewares = function(middleware, middlewareBaseUrl) {
  var _middlewares = {}

  Object.keys(middleware).forEach(function(name) {
    _middlewares[name] = {
      name: name,
      path: path.join(middlewareBaseUrl, name),
      handler: middleware[name]
    }
  })

  return _middlewares
}

// Static function for processing remote middleware. It rebuilds the remote
// middleware handler so it could be used locally.
exports.rebuildRemoteMiddlewares = function(middlewares, service) {
  Object.keys(middlewares).forEach(function(mName) {
    var middleware = middlewares[mName]
    var _path = middleware.path
    debug('[%s] rebuild remote middleware handler "%s" url "%s"', service.name, middleware.name, _path)

    var handler = function() {
      return function(req, res, next) {
        var provider = service.scheduler.get()
        if (!provider) {
          exports.noProviderAvailable(req, res, next)
          return
        }

        var headers = assign({}, req.headers)
        delete headers['content-length']
        delete headers['x-micromono-req']
        var proxyReq = http.request({
          host: provider.host,
          port: provider.web.port,
          path: _path,
          method: req.method,
          headers: headers
        }, function(proxyRes) {
          if (103 === proxyRes.statusCode) {
            var reqMerge = proxyRes.headers['x-micromono-req']
            if (reqMerge) {
              try {
                reqMerge = jDad.parse(reqMerge, {
                  decycle: true
                })
                merge(req, reqMerge)
              } catch (e) {
                debug('[%s] Failed to merge request from remote middleware', service.name, e)
              }
            }
            next()
          } else {
            res.set(proxyRes.headers)
            res.statusCode = proxyRes.statusCode
            proxyRes.pipe(res)
          }
        })

        proxyReq.on('error', function(err, req, res) {
          if (res) {
            res.writeHead(500, {
              'Content-Type': 'text/plain'
            })
            res.end('Service error')
          }
          debug('[%s] middleware "%s" proxy error', service.name, mName, err.stack)
        })

        proxyReq.end()
      }
    }

    service.middleware[middleware.name] = handler
  })
}

/**
 * Get a function which proxy the requests to the real services.
 *
 * @param  {String} baseUrl      The base url for the target endpoint of the service.
 * @param  {String} upgradeUrl   The url for upgrade request (websockets).
 * @param  {Object} httpServer
 * @return {Function}            The proxy handler function.
 */
exports.getProxyHandler = function(scheduler, httpServer, upgradeUrl) {
  var proxy = httpProxy.createProxyServer({})

  if (httpServer && upgradeUrl) {
    var re = new RegExp('^' + upgradeUrl)
    httpServer.on('upgrade', function(req, socket, head) {
      if (re.test(req.url)) {
        var provider = scheduler.get()
        if (!provider) {
          exports.noProviderAvailable(req, socket)
        } else {
          var target = 'http://' + provider.host + ':' + provider.web.port
          proxy.ws(req, socket, head, {
            target: target
          })
        }
      }
    })
  }

  proxy.on('error', function(err, req, res) {
    debug('Proxy error, request from %s target %s\n %s',
      res && res.remoteAddress || req.hostname,
      req.method,
      err.stack)

    if (/^Error: socket hang up/.test(err.stack))
      // Ignore socket hang up error.
      return

    if (res && res.writeHead) {
      res.writeHead(500, {
        'Content-Type': 'text/plain'
      })
      res.end('Service error')
    }
  })

  return function(req, res) {
    var provider = scheduler.get()
    if (!provider) {
      exports.noProviderAvailable(req, res)
    } else {
      var target = 'http://' + provider.host + ':' + provider.web.port
      if (upgradeUrl)
        target += '/' === upgradeUrl[0] ? upgradeUrl : '/' + upgradeUrl

      proxy.web(req, res, {
        target: target
      })
    }
  }
}

exports.noProviderAvailable = function(req, res) {
  res.writeHead(503)
  res.end('Service Unavailable')
}
<commit_msg>Fix undefined `res.set` <commit_after>var path = require('path')
var http = require('http')
var jDad = require('jdad')
var merge = require('lodash.merge')
var debug = require('debug')('micromono:router')
var assign = require('lodash.assign')
var argsNames = require('js-args-names')
var httpProxy = require('http-proxy')

/**
 * Create route definition object from route name.
 *
 * @param  {String}    routeName Path of the route
 * @return {Object}    Route definition object
 */
exports.generateRouteByName = function(routeName, defaultMethod) {
  if ('string' === typeof routeName) {
    var _path = routeName.split('::')
    var method = defaultMethod || 'get'
    var route = {
      name: routeName
    }

    if (2 === _path.length) {
      method = _path[0]
      _path = _path[1]
    } else if (3 === _path.length) {
      // This is GET route with page definition in format of:
      // `[method]::[routeUrl]::[templatePath]`
      // `get::/abc::public/abc.jsx!`
      method = _path[0]
      route.page = _path[2]
      _path = _path[1]
    } else {
      _path = routeName
    }

    route.path = _path
    route.method = method

    return route
  } else {
    console.error('Route name must be a string.')
    process.exit(-1)
  }
}

/**
 * Normalize route definition to a portable format which could be easily used
 * by different web frameworks.
 *
 * ```javascript
 * route: {
 *   'get::/user/:name': function(req, res) {...}
 * }
 * ```
 *
 * will be formatted into:
 *
 * ```javascript
 * {
 *   name: 'get::/user/:name',
 *   method: 'get',
 *   path: '/user/:name',
 *   handler: [Function],
 *   args: ['req', 'res'],
 *   middleware: null
 * }
 * ```
 *
 * Example with route middleware:
 *
 * ```javascript
 * route: {
 *   'get::/user/:name': [function(req, res, next) {...}, function(req, res) {...}]
 * }
 * ```
 *
 * will be formatted into:
 *
 * ```javascript
 * {
 *   name: 'get::/user/:name',
 *   method: 'get',
 *   path: '/user/:name',
 *   handler: Function,
 *   args: ['req', 'res'],
 *   middleware: [Function]
 * }
 * ```
 *
 * @param {Object}  route   Route definition object.
 * @param {Service} service Instance of service.
 * @return {Object}         Formatted routes object.
 */
exports.normalizeRoutes = function(route, page, pageApiBaseUrl) {
  var _routes = {}

  Object.keys(route).forEach(function(routePath) {
    var middleware
    var routeHandler = route[routePath]
    var _route = exports.generateRouteByName(routePath)

    // Page contains mapping between url and template for client side routing
    // and rendering. e.g.:
    //
    // {`/abc`: 'public/abc.mustache'}
    //
    // The above tells url `/abc` uses template `public/abc.mustache` for client
    // side rendering.
    //
    if (page && page.hasOwnProperty(routePath))
      _route.page = page[routePath]
    if (Array.isArray(routeHandler)) {
      middleware = routeHandler
      routeHandler = middleware.pop()
    }

    _route.args = argsNames(routeHandler)
    _route.handler = routeHandler
    _route.middleware = middleware || null

    if (_route.page) {
      // Add the page api route
      var apiRoutePath = path.join(pageApiBaseUrl, _route.path)

      if (apiRoutePath.length > 1 && '/' === apiRoutePath[apiRoutePath.length - 1])
        apiRoutePath = apiRoutePath.slice(0, -1)

      if (!_routes[apiRoutePath]) {
        _route.api = apiRoutePath
        _routes[apiRoutePath] = {
          name: apiRoutePath,
          method: _route.method,
          path: apiRoutePath,
          args: _route.args,
          handler: _route.handler,
          middleware: _route.middleware
        }
      }
    }

    _routes[routePath] = _route
  })

  return _routes
}

exports.normalizeUses = function(use) {
  var _uses = {}

  Object.keys(use).forEach(function(name) {
    var _use = use[name]
    if (!Array.isArray(_use))
      _use = [_use]

    _use = _use.map(function(url) {
      if ('string' === typeof url)
        url = exports.generateRouteByName(url, 'default')
      return url
    })
    _uses[name] = _use
  })

  return _uses
}

exports.normalizeMiddlewares = function(middleware, middlewareBaseUrl) {
  var _middlewares = {}

  Object.keys(middleware).forEach(function(name) {
    _middlewares[name] = {
      name: name,
      path: path.join(middlewareBaseUrl, name),
      handler: middleware[name]
    }
  })

  return _middlewares
}

// Static function for processing remote middleware. It rebuilds the remote
// middleware handler so it could be used locally.
exports.rebuildRemoteMiddlewares = function(middlewares, service) {
  Object.keys(middlewares).forEach(function(mName) {
    var middleware = middlewares[mName]
    var _path = middleware.path
    debug('[%s] rebuild remote middleware handler "%s" url "%s"', service.name, middleware.name, _path)

    var handler = function() {
      return function(req, res, next) {
        var provider = service.scheduler.get()
        if (!provider) {
          exports.noProviderAvailable(req, res, next)
          return
        }

        var headers = assign({}, req.headers)
        delete headers['content-length']
        delete headers['x-micromono-req']
        var proxyReq = http.request({
          host: provider.host,
          port: provider.web.port,
          path: _path,
          method: req.method,
          headers: headers
        }, function(proxyRes) {
          if (103 === proxyRes.statusCode) {
            var reqMerge = proxyRes.headers['x-micromono-req']
            if (reqMerge) {
              try {
                reqMerge = jDad.parse(reqMerge, {
                  decycle: true
                })
                merge(req, reqMerge)
              } catch (e) {
                debug('[%s] Failed to merge request from remote middleware', service.name, e)
              }
            }
            next()
          } else {
            if (res.set) {
              res.set(proxyRes.headers)
            } else {
              res.headers = res.headers || {}
              assign(res.headers, proxyRes.headers)
            }
            res.statusCode = proxyRes.statusCode
            proxyRes.pipe(res)
          }
        })

        proxyReq.on('error', function(err, req, res) {
          if (res) {
            res.writeHead(500, {
              'Content-Type': 'text/plain'
            })
            res.end('Service error')
          }
          debug('[%s] middleware "%s" proxy error', service.name, mName, err.stack)
        })

        proxyReq.end()
      }
    }

    service.middleware[middleware.name] = handler
  })
}

/**
 * Get a function which proxy the requests to the real services.
 *
 * @param  {String} baseUrl      The base url for the target endpoint of the service.
 * @param  {String} upgradeUrl   The url for upgrade request (websockets).
 * @param  {Object} httpServer
 * @return {Function}            The proxy handler function.
 */
exports.getProxyHandler = function(scheduler, httpServer, upgradeUrl) {
  var proxy = httpProxy.createProxyServer({})

  if (httpServer && upgradeUrl) {
    var re = new RegExp('^' + upgradeUrl)
    httpServer.on('upgrade', function(req, socket, head) {
      if (re.test(req.url)) {
        var provider = scheduler.get()
        if (!provider) {
          exports.noProviderAvailable(req, socket)
        } else {
          var target = 'http://' + provider.host + ':' + provider.web.port
          proxy.ws(req, socket, head, {
            target: target
          })
        }
      }
    })
  }

  proxy.on('error', function(err, req, res) {
    debug('Proxy error, request from %s target %s\n %s',
      res && res.remoteAddress || req.hostname,
      req.method,
      err.stack)

    if (/^Error: socket hang up/.test(err.stack))
      // Ignore socket hang up error.
      return

    if (res && res.writeHead) {
      res.writeHead(500, {
        'Content-Type': 'text/plain'
      })
      res.end('Service error')
    }
  })

  return function(req, res) {
    var provider = scheduler.get()
    if (!provider) {
      exports.noProviderAvailable(req, res)
    } else {
      var target = 'http://' + provider.host + ':' + provider.web.port
      if (upgradeUrl)
        target += '/' === upgradeUrl[0] ? upgradeUrl : '/' + upgradeUrl

      proxy.web(req, res, {
        target: target
      })
    }
  }
}

exports.noProviderAvailable = function(req, res) {
  res.writeHead(503)
  res.end('Service Unavailable')
}
<|endoftext|>


<commit_before>define(function (require, exports, module) {
"use strict";

var SimpleEventEmitter = require("./lib/simple_event_emitter").SimpleEventEmitter;
var validate = require("salve/validate");
var $ = require("jquery");
var util = require("./util");
var oop = require("./oop");

// parsing_stage values

var START_TAG = 1;
var CONTENTS = 2;
var END_TAG = 3;

// Working state values

var INCOMPLETE = 1;
var WORKING = 2;
var INVALID = 3;
var VALID = 4;

exports.INCOMPLETE = INCOMPLETE;
exports.WORKING = WORKING;
exports.INVALID = INVALID;
exports.VALID = VALID;

function Parser(schema, resolver, root) {
    // Call the constructor for our mixin
    SimpleEventEmitter.call(this);

    this.schema = schema;
    this.resolver = resolver;

    this.root = root;
    this._timeout = 200;
    // _max_cycles is meant to be used only in testing.
    this._max_cycles = 0;
    this._max_timespan = 100;
    this._timeout_id = undefined;
    this._initialized = false;
    this._errors = [];
    this._tree = undefined;
    this._bound_wrapper = this._workWrapper.bind(this);

    // Parsing state
    this._events = [];
    this._parsing_walker = undefined;
    this._working_state = undefined;
    this._part_done = 0;
    this._parsing_stage = CONTENTS;
    this._previous_child = null;
    this._parsing_stack = [new ParsingState(0, 1)];
    this._element_to_parse = null;

    this._setWorkingState(INCOMPLETE);

}

oop.implement(Parser, SimpleEventEmitter);

(function () {
    this.start = function () {
        if (this._timeout_id !== undefined)
            this.stop();

        if (!this._initialized)
            this._initialize(function () {
                this._timeout_id = window.setTimeout(this._bound_wrapper, this._timeout);
            }.bind(this));
        else
            this._timeout_id = window.setTimeout(this._bound_wrapper, this._timeout);
        this._setWorkingState(WORKING, this._part_done);
        this._restarting = false;
    };

    this._initialize = function (done) {
        require(["requirejs/text!" + this.schema], function(x) {
            this._tree = validate.constructTree(x);
            this._parsing_walker = this._tree.newWalker();
            this._element_to_parse = this.root;
            this._initialized = true;
            done();
        }.bind(this));
    };

    this._workWrapper = function () {
        if (this._work())
            this._timeout_id = window.setTimeout(this._bound_wrapper, this._timeout);
    };

    this._work = function () {
        if (!this._initialized)
            return true;

        var event_result;

        var start_date = new Date();
        var cycles = 0;
        state_change:
        while (true) {

            if ((this._max_cycles > 0) && cycles >= this._max_cycles)
                return true;

            cycles++;

            // Give a chance to other operations to work.
            if ((this._max_timespan > 0) && (new Date() - start_date) >= this._max_timespan)
                return true;

            var walker = this._parsing_walker;

            var portion = this._parsing_stack[0].portion;

            stage_change:
            while (true) {
                var $element_to_parse = $(this._element_to_parse);
                switch(this._parsing_stage) {
                case START_TAG:
                    // These are currently not needed:
                    // $element_to_parse.data("wed_state_index_before", this._events.length - 1);
                    // $element_to_parse.data("wed_event_index_before", this._events.length - 1);

                    // Phantoms do not fire events, nor do "_real
                    // _text" elements, nor do placeholders
                    if (!$element_to_parse.hasClass("_phantom_wrap") &&
                        !$element_to_parse.is("._real._text, ._placeholder")) {
                        // Enter the start tag
                        // The 1st class name is always the name of the tag in the
                        // original XML.
                        var name = this._element_to_parse.className.split(" ", 1)[0];
                        var ename = this.resolver.resolveName(name);
                        this._fireAndProcessEvent(
                            walker,
                            new validate.Event("enterStartTag",
                                               ename.ns,
                                               ename.name),
                            this._element_to_parse);

                        // Find all attributes, fire events for
                        // them. We should not get any errors here,
                        // since the user does not get to set these
                        // manually. An error here would indicate an
                        // internal error.
                        var attr_ix_lim = this._element_to_parse.attributes.length
                        for(var attr_ix = 0; attr_ix < attr_ix_lim; ++attr_ix) {
                            var attr = this._element_to_parse.attributes[attr_ix];
                            if (attr.name.lastIndexOf("data-wed-", 0) !== 0)
                                continue;
                            var attr_name = util.decodeAttrName(attr.name);
                            var ename = this.resolver.resolveName(attr_name, true);
                            this._fireAndProcessEvent(
                                walker,
                                new validate.Event("attributeName",
                                                   ename.ns,
                                                   ename.name),
                                this._element_to_parse);
                            this._fireAndProcessEvent(
                                walker,
                                new validate.Event("attributeValue",
                                                   attr.value),
                                this._element_to_parse);
                        }

                        // Leave the start tag.
                        this._fireAndProcessEvent(
                            walker,
                            new validate.Event("leaveStartTag"),
                            this._element_to_parse);
                    }

                    this._parsing_stage = CONTENTS;
                    $element_to_parse.data("wed_event_index_after_start", this._events.length - 1);
                    continue state_change;
                    // break would be unreachable.
                case CONTENTS:
                    var node = (this._previous_child === null)  ?
                        // starting from scratch
                        this._element_to_parse.firstChild :
                        // already parsing contents
                        this._previous_child.nextSibling;

                    while (node !== null) {
                        switch(node.nodeType) {
                        case Node.TEXT_NODE:
                            // We want to fire an event only if the
                            // text node is not just white space or if
                            // text is possible.
                            event_result = this._fireTextEventIfNeeded(node, walker);
                            if (event_result !== true)
                                // XXX
                                console.log("XXX text error");
                            break;
                        case Node.ELEMENT_NODE:
                            if (!$(node).hasClass("_phantom")) {
                                portion /= $element_to_parse.children().length;
                                this._parsing_stack.unshift(new ParsingState(this._part_done, portion));
                                this._element_to_parse = node;
                                this._parsing_stage = START_TAG;
                                this._previous_child = null;
                                continue stage_change;
                            }
                            // else we do nothing because it is phantom.
                            break;
                        default:
                            throw new Error("unexpected node type: " + node.nodeType);
                        }
                        node = node.nextSibling;
                    }

                    if (node === null)
                        this._parsing_stage = END_TAG;
                    break;
                case END_TAG:

                    // We've reached the end...
                    if (this._element_to_parse === this.root) {
                        event_result = walker.end();
                        if (event_result !== true)
                            this._processTagEventResult(event_result, this._element_to_parse);
                        this._setWorkingState(this._errors.length > 0 ? INVALID : VALID);
                        this.stop();
                        return false;
                    }

                    var original_element = this._element_to_parse; // we need it later
                    // Phantoms do not generate events, nor do "_real
                    // _text" elements.
                    if (!$element_to_parse.hasClass("_phantom_wrap") &&
                        !$element_to_parse.is("._real._text, ._placeholder")) {
                        // The 1st class name is always the name of the tag in the
                        // original XML.
                        var name = this._element_to_parse.className.split(" ", 1)[0];
                        var ename = this.resolver.resolveName(name);
                        this._fireAndProcessEvent(
                            walker,
                            new validate.Event("endTag",
                                               ename.ns,
                                               ename.name),
                            this._element_to_parse);
                    }

                    // Go back to the parent
                    this._previous_child = this._element_to_parse;
                    this._element_to_parse = this._element_to_parse.parentNode;

                    if (this._element_to_parse !== this.root) {
                        this._parsing_stack.shift();
                        this._part_done = this._parsing_stack[0].part_done += portion;
                        portion = this._parsing_stack[0].portion;
                    }

                    this._setWorkingState(WORKING, this._part_done);

                    $(original_element).data("wed_event_index_after",
                                             this._events.length - 1);
                    this._parsing_stage = CONTENTS;
                    continue state_change;

                    // break; would be unreachable
                default:
                    throw new Error("unexpected state");
                }
            }
        }
        return true;
    };

    this.stop = function () {
        if (this._timeout_id !== undefined)
            window.clearTimeout(this._timeout_id);
        this._timeout_id = undefined;

        // We are stopping prematurely, update the state
        if (this._working_state === WORKING)
            this._setWorkingState(INCOMPLETE);
    };

    this.restartAt = function (element) {
        if (this._working_state === WORKING)
            this.stop();
        if (this._initialized && !this._restarting) {
            $(this.root).findAndSelf("*").removeData(
                ["wed_event_index_after", "wed_event_index_after_start"]);
            this._parsing_stage = CONTENTS;
            this._previous_child = null;
            this._parsing_walker = this._tree.newWalker();
            this._events = [];
            this._element_to_parse = this.root;
            this._part_done = 0;
            this._errors = [];
            this._emit("reset-errors", { at: 0 });
        }
        this._restarting = true;
        this.start();
    };

    this._setWorkingState = function (new_state) {
        if (this._working_state !== new_state || new_state === WORKING) {
            this._working_state = new_state;
            this._emit("state-update");
        }
    };

    this.getWorkingState = function () {
        return {
            state: this._working_state,
            part_done: this._part_done
        };
    };

    this._processTagEventResult = function (result, element) {
        this._errors.push(result);
        this._emit("error", { error: result,
                              element: element});
    };

    this._fireAndProcessEvent = function (walker, event, element_to_parse) {
        this._events.push(event);
        var event_result = walker.fireEvent(event);
        if (event_result !== true)
            this._processTagEventResult(event_result, element_to_parse);

    };

    this._fireTextEventIfNeeded = function (text_node, walker) {
        if (($.trim(text_node.nodeValue).length > 0) ||
            (walker.possible().filter(function (x) {
                return x[0] === 'text';
            }).length > 0)) {
            var event = new validate.Event("text");
            this._events.push(event);
            return walker.fireEvent(event);
        }

        // Behave the same as when an event has been fired without error.
        return true;
    };

    // The meaning of container and index is the same as for selection
    // ranges.
    this.possibleAt = function (container, index) {

        // This function could be called with container === root if
        // the document is empty or if the user has the caret before
        // the start tag of the first element of the actual structure
        // we want to parse or after the end tag of that element.

        if (container === this.root) {
            var walker = this._tree.newWalker();

            // We're before the top element, no events to fire.
            if (index === 0)
                return walker.possible();

            // We're past the top element, fire all events.
            for(var ix= 0; ix < this._events.length; ++ix)
                walker.fireEvent(this._events[ix]);
            return walker.possible();
        }

        var evs = [];
        switch(container.nodeType) {
        case Node.TEXT_NODE:
            var $prev = $(container).prev("._real, ._phantom_wrap");

            var event_index =
                ($prev.length > 0) ? $prev.data("wed_event_index_after") :
                $(container.parentNode).data("wed_event_index_after_start");

            var walker = this._tree.newWalker();
            for(var ix = 0; ix <= event_index; ++ix) {
                walker.fireEvent(this._events[ix]);
            }

            // We have will attempt to fire a text event either if our
            // location is inside the current text node or if another
            // text node appeared between this text node and the
            // previous element found.
            if (index > 0 ||
                 $prev.nextUntil(container).filter(function (x) {
                return this.nodeType === Node.TEXT_NODE ||
                    $(this).hasClass("_text");
                 }).length > 0)
                fireTextEventIfNeeded(container, walker);

            evs = walker.possible();
            break;
        case Node.ELEMENT_NODE:
            var node = container.childNodes[index];

            var $prev = (node === undefined) ?
                $(container).children("._real, ._phantom_wrap").last() :
                $(node).prev("._real, ._phantom_wrap");

            var event_index =
                ($prev.length > 0) ? $prev.data("wed_event_index_after") :
                $(container).data("wed_event_index_after_start");

            var walker = this._tree.newWalker();
            for(var ix = 0; ix <= event_index; ++ix) {
                walker.fireEvent(this._events[ix]);
            }

            // We will attempt to fire a text event if another
            // text node appeared between this text node and the
            // previous element found.
            if (((node === undefined) ? $prev.nextAll() : $prev.nextUntil(node)).filter(function (x) {
                return this.nodeType === Node.TEXT_NODE ||
                    $(this).hasClass("_text");
                 }).length > 0) {
                if (fireTextEventIfNeeded(container, walker) === true)
                    evs = walker.possible();
            }
            else
                evs = walker.possible();
            break;
        default:
            throw new Error("unexpected node type: " + container.nodeType);
        }

        return evs;
    };
}).call(Parser.prototype);

//
// Private helper functions and classes
//

/*

Elements of interest are those which are of the "_real" class Text
nodes are of interest. Nothing else is of interest.

*/

function ParsingState(part_done, portion) {
    this.part_done = part_done;
    this.portion = portion;
}

function fireTextEventIfNeeded(text_node, walker) {
    if (($.trim(text_node.nodeValue).length > 0) ||
        (walker.possible().filter(function (x) {
            return x[0] === 'text';
        }).length > 0)) {

        return walker.fireEvent(new validate.Event("text"));
    }

    // Behave the same as when an event has been fired without error.
    return true;
}

// Exports

exports.Parser = Parser;

});
<commit_msg>_restarting flag was not set properly.<commit_after>define(function (require, exports, module) {
"use strict";

var SimpleEventEmitter = require("./lib/simple_event_emitter").SimpleEventEmitter;
var validate = require("salve/validate");
var $ = require("jquery");
var util = require("./util");
var oop = require("./oop");

// parsing_stage values

var START_TAG = 1;
var CONTENTS = 2;
var END_TAG = 3;

// Working state values

var INCOMPLETE = 1;
var WORKING = 2;
var INVALID = 3;
var VALID = 4;

exports.INCOMPLETE = INCOMPLETE;
exports.WORKING = WORKING;
exports.INVALID = INVALID;
exports.VALID = VALID;

function Parser(schema, resolver, root) {
    // Call the constructor for our mixin
    SimpleEventEmitter.call(this);

    this.schema = schema;
    this.resolver = resolver;

    this.root = root;
    this._timeout = 200;
    // _max_cycles is meant to be used only in testing.
    this._max_cycles = 0;
    this._max_timespan = 100;
    this._timeout_id = undefined;
    this._initialized = false;
    this._errors = [];
    this._tree = undefined;
    this._bound_wrapper = this._workWrapper.bind(this);

    // Parsing state
    this._events = [];
    this._parsing_walker = undefined;
    this._working_state = undefined;
    this._part_done = 0;
    this._parsing_stage = CONTENTS;
    this._previous_child = null;
    this._parsing_stack = [new ParsingState(0, 1)];
    this._element_to_parse = null;

    this._setWorkingState(INCOMPLETE);

}

oop.implement(Parser, SimpleEventEmitter);

(function () {
    this.start = function () {
        if (this._timeout_id !== undefined)
            this.stop();

        if (!this._initialized)
            this._initialize(function () {
                this._timeout_id = window.setTimeout(this._bound_wrapper, this._timeout);
            }.bind(this));
        else
            this._timeout_id = window.setTimeout(this._bound_wrapper, this._timeout);
        this._setWorkingState(WORKING, this._part_done);
    };

    this._initialize = function (done) {
        require(["requirejs/text!" + this.schema], function(x) {
            this._tree = validate.constructTree(x);
            this._parsing_walker = this._tree.newWalker();
            this._element_to_parse = this.root;
            this._initialized = true;
            done();
        }.bind(this));
    };

    this._workWrapper = function () {
        this._restarting = false;
        if (this._work())
            this._timeout_id = window.setTimeout(this._bound_wrapper, this._timeout);
    };

    this._work = function () {
        if (!this._initialized)
            return true;

        var event_result;

        var start_date = new Date();
        var cycles = 0;
        state_change:
        while (true) {

            if ((this._max_cycles > 0) && cycles >= this._max_cycles)
                return true;

            cycles++;

            // Give a chance to other operations to work.
            if ((this._max_timespan > 0) && (new Date() - start_date) >= this._max_timespan)
                return true;

            var walker = this._parsing_walker;

            var portion = this._parsing_stack[0].portion;

            stage_change:
            while (true) {
                var $element_to_parse = $(this._element_to_parse);
                switch(this._parsing_stage) {
                case START_TAG:
                    // These are currently not needed:
                    // $element_to_parse.data("wed_state_index_before", this._events.length - 1);
                    // $element_to_parse.data("wed_event_index_before", this._events.length - 1);

                    // Phantoms do not fire events, nor do "_real
                    // _text" elements, nor do placeholders
                    if (!$element_to_parse.hasClass("_phantom_wrap") &&
                        !$element_to_parse.is("._real._text, ._placeholder")) {
                        // Enter the start tag
                        // The 1st class name is always the name of the tag in the
                        // original XML.
                        var name = this._element_to_parse.className.split(" ", 1)[0];
                        var ename = this.resolver.resolveName(name);
                        this._fireAndProcessEvent(
                            walker,
                            new validate.Event("enterStartTag",
                                               ename.ns,
                                               ename.name),
                            this._element_to_parse);

                        // Find all attributes, fire events for
                        // them. We should not get any errors here,
                        // since the user does not get to set these
                        // manually. An error here would indicate an
                        // internal error.
                        var attr_ix_lim = this._element_to_parse.attributes.length
                        for(var attr_ix = 0; attr_ix < attr_ix_lim; ++attr_ix) {
                            var attr = this._element_to_parse.attributes[attr_ix];
                            if (attr.name.lastIndexOf("data-wed-", 0) !== 0)
                                continue;
                            var attr_name = util.decodeAttrName(attr.name);
                            var ename = this.resolver.resolveName(attr_name, true);
                            this._fireAndProcessEvent(
                                walker,
                                new validate.Event("attributeName",
                                                   ename.ns,
                                                   ename.name),
                                this._element_to_parse);
                            this._fireAndProcessEvent(
                                walker,
                                new validate.Event("attributeValue",
                                                   attr.value),
                                this._element_to_parse);
                        }

                        // Leave the start tag.
                        this._fireAndProcessEvent(
                            walker,
                            new validate.Event("leaveStartTag"),
                            this._element_to_parse);
                    }

                    this._parsing_stage = CONTENTS;
                    $element_to_parse.data("wed_event_index_after_start", this._events.length - 1);
                    continue state_change;
                    // break would be unreachable.
                case CONTENTS:
                    var node = (this._previous_child === null)  ?
                        // starting from scratch
                        this._element_to_parse.firstChild :
                        // already parsing contents
                        this._previous_child.nextSibling;

                    while (node !== null) {
                        switch(node.nodeType) {
                        case Node.TEXT_NODE:
                            // We want to fire an event only if the
                            // text node is not just white space or if
                            // text is possible.
                            event_result = this._fireTextEventIfNeeded(node, walker);
                            if (event_result !== true)
                                // XXX
                                console.log("XXX text error");
                            break;
                        case Node.ELEMENT_NODE:
                            if (!$(node).hasClass("_phantom")) {
                                portion /= $element_to_parse.children().length;
                                this._parsing_stack.unshift(new ParsingState(this._part_done, portion));
                                this._element_to_parse = node;
                                this._parsing_stage = START_TAG;
                                this._previous_child = null;
                                continue stage_change;
                            }
                            // else we do nothing because it is phantom.
                            break;
                        default:
                            throw new Error("unexpected node type: " + node.nodeType);
                        }
                        node = node.nextSibling;
                    }

                    if (node === null)
                        this._parsing_stage = END_TAG;
                    break;
                case END_TAG:
                    // We've reached the end...
                    if (this._element_to_parse === this.root) {
                        event_result = walker.end();
                        if (event_result !== true)
                            this._processTagEventResult(event_result, this._element_to_parse);
                        this._part_done = 1;
                        this._setWorkingState(this._errors.length > 0 ? INVALID : VALID);
                        this.stop();
                        return false;
                    }

                    var original_element = this._element_to_parse; // we need it later
                    // Phantoms do not generate events, nor do "_real
                    // _text" elements.
                    if (!$element_to_parse.hasClass("_phantom_wrap") &&
                        !$element_to_parse.is("._real._text, ._placeholder")) {
                        // The 1st class name is always the name of the tag in the
                        // original XML.
                        var name = this._element_to_parse.className.split(" ", 1)[0];
                        var ename = this.resolver.resolveName(name);
                        this._fireAndProcessEvent(
                            walker,
                            new validate.Event("endTag",
                                               ename.ns,
                                               ename.name),
                            this._element_to_parse);
                    }

                    // Go back to the parent
                    this._previous_child = this._element_to_parse;
                    this._element_to_parse = this._element_to_parse.parentNode;

                    if (this._element_to_parse !== this.root) {
                        this._parsing_stack.shift();
                        this._part_done = this._parsing_stack[0].part_done += portion;
                        portion = this._parsing_stack[0].portion;
                    }

                    this._setWorkingState(WORKING, this._part_done);

                    $(original_element).data("wed_event_index_after",
                                             this._events.length - 1);
                    this._parsing_stage = CONTENTS;
                    continue state_change;

                    // break; would be unreachable
                default:
                    throw new Error("unexpected state");
                }
            }
        }
        return true;
    };

    this.stop = function () {
        if (this._timeout_id !== undefined)
            window.clearTimeout(this._timeout_id);
        this._timeout_id = undefined;

        // We are stopping prematurely, update the state
        if (this._working_state === WORKING)
            this._setWorkingState(INCOMPLETE);
    };

    this.restartAt = function (element) {
        if (this._working_state === WORKING)
            this.stop();
        if (this._initialized && !this._restarting) {
            this._restarting = true;
            $(this.root).findAndSelf("*").removeData(
                ["wed_event_index_after", "wed_event_index_after_start"]);
            this._parsing_stage = CONTENTS;
            this._previous_child = null;
            this._parsing_walker = this._tree.newWalker();
            this._events = [];
            this._element_to_parse = this.root;
            this._part_done = 0;
            this._errors = [];
            this._emit("reset-errors", { at: 0 });
        }
        this.start();
    };

    this._setWorkingState = function (new_state) {
        if (this._working_state !== new_state || new_state === WORKING) {
            this._working_state = new_state;
            this._emit("state-update");
        }
    };

    this.getWorkingState = function () {
        return {
            state: this._working_state,
            part_done: this._part_done
        };
    };

    this._processTagEventResult = function (result, element) {
        this._errors.push(result);
        this._emit("error", { error: result,
                              element: element});
    };

    this._fireAndProcessEvent = function (walker, event, element_to_parse) {
        this._events.push(event);
        var event_result = walker.fireEvent(event);
        if (event_result !== true)
            this._processTagEventResult(event_result, element_to_parse);

    };

    this._fireTextEventIfNeeded = function (text_node, walker) {
        if (($.trim(text_node.nodeValue).length > 0) ||
            (walker.possible().filter(function (x) {
                return x[0] === 'text';
            }).length > 0)) {
            var event = new validate.Event("text");
            this._events.push(event);
            return walker.fireEvent(event);
        }

        // Behave the same as when an event has been fired without error.
        return true;
    };

    // The meaning of container and index is the same as for selection
    // ranges.
    this.possibleAt = function (container, index) {

        // This function could be called with container === root if
        // the document is empty or if the user has the caret before
        // the start tag of the first element of the actual structure
        // we want to parse or after the end tag of that element.

        if (container === this.root) {
            var walker = this._tree.newWalker();

            // We're before the top element, no events to fire.
            if (index === 0)
                return walker.possible();

            // We're past the top element, fire all events.
            for(var ix= 0; ix < this._events.length; ++ix)
                walker.fireEvent(this._events[ix]);
            return walker.possible();
        }

        var evs = [];
        switch(container.nodeType) {
        case Node.TEXT_NODE:
            var $prev = $(container).prev("._real, ._phantom_wrap");

            var event_index =
                ($prev.length > 0) ? $prev.data("wed_event_index_after") :
                $(container.parentNode).data("wed_event_index_after_start");

            var walker = this._tree.newWalker();
            for(var ix = 0; ix <= event_index; ++ix) {
                walker.fireEvent(this._events[ix]);
            }

            // We have will attempt to fire a text event either if our
            // location is inside the current text node or if another
            // text node appeared between this text node and the
            // previous element found.
            if (index > 0 ||
                 $prev.nextUntil(container).filter(function (x) {
                return this.nodeType === Node.TEXT_NODE ||
                    $(this).hasClass("_text");
                 }).length > 0)
                fireTextEventIfNeeded(container, walker);

            evs = walker.possible();
            break;
        case Node.ELEMENT_NODE:
            var node = container.childNodes[index];

            var $prev = (node === undefined) ?
                $(container).children("._real, ._phantom_wrap").last() :
                $(node).prev("._real, ._phantom_wrap");

            var event_index =
                ($prev.length > 0) ? $prev.data("wed_event_index_after") :
                $(container).data("wed_event_index_after_start");

            var walker = this._tree.newWalker();
            for(var ix = 0; ix <= event_index; ++ix) {
                walker.fireEvent(this._events[ix]);
            }

            // We will attempt to fire a text event if another
            // text node appeared between this text node and the
            // previous element found.
            if (((node === undefined) ? $prev.nextAll() : $prev.nextUntil(node)).filter(function (x) {
                return this.nodeType === Node.TEXT_NODE ||
                    $(this).hasClass("_text");
                 }).length > 0) {
                if (fireTextEventIfNeeded(container, walker) === true)
                    evs = walker.possible();
            }
            else
                evs = walker.possible();
            break;
        default:
            throw new Error("unexpected node type: " + container.nodeType);
        }

        return evs;
    };
}).call(Parser.prototype);

//
// Private helper functions and classes
//

/*

Elements of interest are those which are of the "_real" class Text
nodes are of interest. Nothing else is of interest.

*/

function ParsingState(part_done, portion) {
    this.part_done = part_done;
    this.portion = portion;
}

function fireTextEventIfNeeded(text_node, walker) {
    if (($.trim(text_node.nodeValue).length > 0) ||
        (walker.possible().filter(function (x) {
            return x[0] === 'text';
        }).length > 0)) {

        return walker.fireEvent(new validate.Event("text"));
    }

    // Behave the same as when an event has been fired without error.
    return true;
}

// Exports

exports.Parser = Parser;

});
<|endoftext|>


<commit_before>define(function (require) {
    'use strict';
    var routeHash = {},
        routeRegExps = {},
        routeInstalled = false,
        noPushState = !window.history || !window.history.pushState,
        stateCounter = 0;

    return withRoute;

    function convertRoute(route) {
        var index = 0,
            readyToRegExp = route,
            hashKeys = [],
            firstIndex = route.indexOf(':');

        if (firstIndex !== -1) {
            if (firstIndex !== 0) {
                readyToRegExp = route.slice(0, firstIndex);
            } else {
                readyToRegExp = '';
            }
            while((index = route.indexOf(':', index)) !== -1 ) {
                var routeFragment = route.slice(++index),
                    i = routeFragment.search(/\W/),
                    routeFragmentBegining = '',
                    routeFragmentLast = '';
                if (i === -1) {
                    i = routeFragment.length;
                }
                routeFragmentBegining = routeFragment.slice(0, i);
                if (routeFragment[i] !== ':') {
                    var slicer = routeFragment.indexOf(':');
                    routeFragmentLast = routeFragment.slice(routeFragmentBegining.length, slicer);
                }
                hashKeys.push(routeFragmentBegining);
                readyToRegExp += '([\\w\\sа-яА-Я\\-%@!$;]+\)' + routeFragmentLast;
            }
        }
        return [new RegExp('^' + readyToRegExp.replace('?', '\\?') + '/?$'), hashKeys, route];
    }

    function listenRoutes(routes, context) {
        for (var n in routes) {
            if (routes.hasOwnProperty(n)) {
                singleRoute(n, routes[n], context);
            }
        }
    }

    function singleRoute(route, callback, context) {
        route = route.slice(1);
        if (!$.isFunction(callback)) {
            callback = context[callback];
            if (!callback) {
                throw new Error('no callback for route: ' + route);
            }
        }
        if (!routeRegExps[route]) {
            routeRegExps[route] = convertRoute(route);
        }
        if (!routeHash[route]) {
            routeHash[route] = [];
        }
        routeHash[route].push([callback, context]);
    }

    function checkRoutes(route, data) {
        for (var n in routeHash) {
            if (routeHash.hasOwnProperty(n) && routeRegExps.hasOwnProperty(n)) {
                var regAndKeys = routeRegExps[n],
                    initialRouter = regAndKeys[2],
                    reg = regAndKeys[0],
                    keys = regAndKeys[1],
                    args = {};
                var ready = false;
                if (initialRouter === route) {
                    ready = true;
                } else if (initialRouter.length === 0) {
                    ready = false;
                } else {
                    var res = reg.exec(route);
                    if (res) {
                        ready = true;
                        res.shift();
                        for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
                            args[keys[keyIndex]] = res.shift();
                        }
                    }
                }
                if (ready) {
                    routeHash[n].forEach(function(routeCallback) {
                        routeCallback[0].call(routeCallback[1], args, data);
                    });
                }
            }
        }
    }

    function navigateTo(route, data, replaceState) {
        if (noPushState) {
            return false;
        }
        if (!replaceState) {
            window.history.pushState({
                stateCounter: ++stateCounter,
                data: data
            }, '', route);
        } else {
            window.history.replaceState({
                stateCounter: ++stateCounter,
                data: data
            }, '', route);
        }
        return true;
    }

    function getCurrentURL() {
        return document.location.pathname + document.location.search + document.location.hash;
    }

    function withRoute() {
        this.defineRoute = function(routes, callback) {
            if (arguments.length === 1) {
                listenRoutes(routes, this);
            } else {
                singleRoute(routes, callback, this);
            }
        };

        this.navigate = function(route, args) {
            var currentPath = getCurrentURL();
            if (!args) {
                args = {};
            }
            if (!args.data) {
                args.data = {};
            }
            if (!args.forced && route === currentPath) {
                return false;
            }
            if (navigateTo(route, args.data, args.replaceState)) {
                route = route.slice(1);
                checkRoutes(route, args.data);
                this.trigger(document, 'route:change', {
                    route: route,
                    data: args.data
                });
                return true;
            } else {
                document.location.href = route;
                return false;
            }
        };

        this.after('initialize', function() {
            if (!routeInstalled) {
                routeInstalled = true;
                this.on(window, 'popstate', function(event) {
                    if (!stateCounter) {
                        return;
                    }
                    if (!event.originalEvent.state || event.originalEvent.state.stateCounter !== stateCounter) {
                        var path = getCurrentURL();
                        this.navigate(path, {
                            data: event.data,
                            forced: true,
                            replaceState: true
                        });
                    }
                });
            }
        });
    }
});<commit_msg>say no to forEach ftw<commit_after>define(function (require) {
    'use strict';
    var routeHash = {},
        routeRegExps = {},
        routeInstalled = false,
        noPushState = !window.history || !window.history.pushState,
        stateCounter = 0;

    return withRoute;

    function convertRoute(route) {
        var index = 0,
            readyToRegExp = route,
            hashKeys = [],
            firstIndex = route.indexOf(':');

        if (firstIndex !== -1) {
            if (firstIndex !== 0) {
                readyToRegExp = route.slice(0, firstIndex);
            } else {
                readyToRegExp = '';
            }
            while((index = route.indexOf(':', index)) !== -1 ) {
                var routeFragment = route.slice(++index),
                    i = routeFragment.search(/\W/),
                    routeFragmentBegining = '',
                    routeFragmentLast = '';
                if (i === -1) {
                    i = routeFragment.length;
                }
                routeFragmentBegining = routeFragment.slice(0, i);
                if (routeFragment[i] !== ':') {
                    var slicer = routeFragment.indexOf(':');
                    routeFragmentLast = routeFragment.slice(routeFragmentBegining.length, slicer);
                }
                hashKeys.push(routeFragmentBegining);
                readyToRegExp += '([\\w\\sа-яА-Я\\-%@!$;]+\)' + routeFragmentLast;
            }
        }
        return [new RegExp('^' + readyToRegExp.replace('?', '\\?') + '/?$'), hashKeys, route];
    }

    function listenRoutes(routes, context) {
        for (var n in routes) {
            if (routes.hasOwnProperty(n)) {
                singleRoute(n, routes[n], context);
            }
        }
    }

    function singleRoute(route, callback, context) {
        route = route.slice(1);
        if (!$.isFunction(callback)) {
            callback = context[callback];
            if (!callback) {
                throw new Error('no callback for route: ' + route);
            }
        }
        if (!routeRegExps[route]) {
            routeRegExps[route] = convertRoute(route);
        }
        if (!routeHash[route]) {
            routeHash[route] = [];
        }
        routeHash[route].push([callback, context]);
    }

    function checkRoutes(route, data) {
        for (var n in routeHash) {
            if (routeHash.hasOwnProperty(n) && routeRegExps.hasOwnProperty(n)) {
                var regAndKeys = routeRegExps[n],
                    initialRouter = regAndKeys[2],
                    reg = regAndKeys[0],
                    keys = regAndKeys[1],
                    args = {};
                var ready = false;
                if (initialRouter === route) {
                    ready = true;
                } else if (initialRouter.length === 0) {
                    ready = false;
                } else {
                    var res = reg.exec(route);
                    if (res) {
                        ready = true;
                        res.shift();
                        for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
                            args[keys[keyIndex]] = res.shift();
                        }
                    }
                }
                if (ready) {
                    for (var i = 0; i < routeHash[n].length; i++) {
                        var routeCallback = routeHash[n][i];
                        routeCallback[0].call(routeCallback[1], args, data);
                    }
                }
            }
        }
    }

    function navigateTo(route, data, replaceState) {
        if (noPushState) {
            return false;
        }
        if (!replaceState) {
            window.history.pushState({
                stateCounter: ++stateCounter,
                data: data
            }, '', route);
        } else {
            window.history.replaceState({
                stateCounter: ++stateCounter,
                data: data
            }, '', route);
        }
        return true;
    }

    function getCurrentURL() {
        return document.location.pathname + document.location.search + document.location.hash;
    }

    function withRoute() {
        this.defineRoute = function(routes, callback) {
            if (arguments.length === 1) {
                listenRoutes(routes, this);
            } else {
                singleRoute(routes, callback, this);
            }
        };

        this.navigate = function(route, args) {
            var currentPath = getCurrentURL();
            if (!args) {
                args = {};
            }
            if (!args.data) {
                args.data = {};
            }
            if (!args.forced && route === currentPath) {
                return false;
            }
            if (navigateTo(route, args.data, args.replaceState)) {
                route = route.slice(1);
                checkRoutes(route, args.data);
                this.trigger(document, 'route:change', {
                    route: route,
                    data: args.data
                });
                return true;
            } else {
                document.location.href = route;
                return false;
            }
        };

        this.after('initialize', function() {
            if (!routeInstalled) {
                routeInstalled = true;
                this.on(window, 'popstate', function(event) {
                    if (!stateCounter) {
                        return;
                    }
                    if (!event.originalEvent.state || event.originalEvent.state.stateCounter !== stateCounter) {
                        var path = getCurrentURL();
                        this.navigate(path, {
                            data: event.data,
                            forced: true,
                            replaceState: true
                        });
                    }
                });
            }
        });
    }
});<|endoftext|>


<commit_before>var zeller = require('./zeller');

var export_obj = {};
var j = 1;
var monthIs;
var yearIs;
var numDays;
var firstIs;

// Y is a flag that the writeMonth function will check; if it's false (default),
// that means that it's going to write its output directly into the terminal window
// via console logs. If it's true, it will dump its output into the buckets array
// (below). The idea is that writeYear will run writeMonth 12 times, so that
// we wind up with the first bucket full of all the first lines of each month,
// second bucket with all of the second lines, etc. We then do some looping in makeYear
// to put those lines together via concatenation to make the lines we print for the year's
// calendar (three months per row until they're done, then the next three, etc.)
export_obj.Y = false;
// buckets is outside of the scope of writeMonth and module is a singleton,
// so buckets will all get filled up when running writeMonth 12 times (as desired);
// writeYear is able to use them by accessing the buckets attribute of
// this single (and inherently only) instantiation
// of the singleton writeMonth module.
export_obj.buckets = [[],[],[],[],[],[],[],[]];

var month_name = {1: "January", 2: "February", 3: "March",
                  4: "April",   5: "May",      6: "June",
                  7: "July",    8: "August",   9: "September",
                 10: "October",11: "November",12: "December"};

var days_in_month = {1: 31,  2: 28,  3: 31,  4: 30,  5: 31,  6: 30,
                     7: 31,  8: 31,  9: 30, 10: 31, 11: 30, 12: 31};

export_obj.numDays = function(month, year){
  return (month === 2 && this.isLeap(year)) ? 29 : days_in_month[month];
}

export_obj.isLeap = function(year) {
  if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {return true;}
  return false;
}

export_obj.writeMonth = function(month, year){
  j = 1;
  monthIs = parseInt(month);
  yearIs = parseInt(year);
  numDays = this.numDays(month, year);
  // let's get the first day into format: 0 is Sun, 1 is Mon, 6 is Sat.
  firstIs = (zeller.calculate(month, 1, year) + 6) % 7;
  if(!this.Y) {
    console.log(this.makeHeader());
    console.log("Su Mo Tu We Th Fr Sa");
    console.log(this.makeFirstLine());
    for (var i = 0; i < 5; i++) {
      console.log(this.makeOtherLine());
    }
  } else {
    this.buckets[0].push(this.makeHeader());
    monthIs % 3 !== 0 ? this.buckets[1].push("Su Mo Tu We Th Fr Sa "): this.buckets[1].push("Su Mo Tu We Th Fr Sa");
    this.buckets[2].push(this.makeFirstLine());
    for (var i = 3; i < 8; i++) {
      this.buckets[i].push(this.makeOtherLine());
    }
  }
  return "worked";
}

export_obj.makeHeader = function() {
  var line = "";
  var spaces = "";
  var monthTitle = month_name[monthIs];
  var content = monthTitle;
  if (!this.Y) {content = content + " " + yearIs;}
  var spaces_num = Math.floor((20 - content.length) / 2);
  for (var i = 0; i < spaces_num; i++) {
    spaces += " ";
  }
  line = spaces + content + spaces;
  for (var i = 0; i < (20 - line.length) + 2; i++) {
    line += " ";
  }
  return line;
}

// export_obj.makeHeader = function() {
//   var line = "";
//   var spaces = "";
//   var monthTitle = month_name[monthIs];
//   var content = monthTitle;
//   if (!this.Y) {content = content + " " + yearIs;}
//   var spaces_num = Math.floor((20 - content.length) / 2);
//   for (var i = 0; i < spaces_num; i++) {
//     spaces += " ";
//   }

//   // need to make sure that there aren't any trailing spaces if
//   // this is going to be a line build for a single month instead of a year
//   // also edge months (March, June, etc.) need to have no trailing spaces.
//   if (this.Y && (monthIs % 3 !== 0)) {
//     line = spaces + content + spaces;
//     for (var i = 0; i < (20 - line.length) + 2; i++) {
//       line += " ";
//     }
//   } else {
//     line = spaces + content;
//   }

//   return line;
// }

export_obj.makeFirstLine = function() {
  var line = "";
  for (var i = 0; i < firstIs; i++) {
    line += "   ";
  }
  for (var i = firstIs; i <= 6; i++) {
    if (i.toString().length === 1) {line += " ";}
    line += j
    // if there are days yet to write to the month, and we're only writing a month and we aren't on the last day of the row, add a space.
    // or, if we're writing a year and we aren't on an edge month (March, June, etc.) OR we are on an edge month but aren't on its last column, add a space.
    if (j < numDays && ((!this.Y && i !== 6) || this.Y && (monthIs % 3 !== 0 || i !== 6))) {line += " ";}
    j++;
  }
  return line;
}

export_obj.makeOtherLine = function() {
  var line = "";
  for (var i = 0; i <= 6; i++) {
    if (j <= numDays && j.toString().length === 1) {
      line += " " + j;
    } else if (j <= numDays) {
      line += j;
    } else if (this.Y && monthIs % 3 !== 0) {
      line += "  ";
    }
    // if we're writing just a month, we have days left to write and we aren't on the last column, add a space.
    // Or if we're writing a year, and either we aren't on an edge month (March, June, etc.) or we have days left to write & we aren't
    // on the last column, add a space.
    if ((!this.Y && j < numDays && i !== 6) || this.Y && (monthIs % 3 !== 0 || (i !== 6 && j < numDays))) {line += " ";}
    j++
  }
  return line;
}

module.exports = export_obj;
<commit_msg>is the linux version really the same as what I had before?<commit_after>var zeller = require('./zeller');

var export_obj = {};
var j = 1;
var monthIs;
var yearIs;
var numDays;
var firstIs;

// Y is a flag that the writeMonth function will check; if it's false (default),
// that means that it's going to write its output directly into the terminal window
// via console logs. If it's true, it will dump its output into the buckets array
// (below). The idea is that writeYear will run writeMonth 12 times, so that
// we wind up with the first bucket full of all the first lines of each month,
// second bucket with all of the second lines, etc. We then do some looping in makeYear
// to put those lines together via concatenation to make the lines we print for the year's
// calendar (three months per row until they're done, then the next three, etc.)
export_obj.Y = false;
// buckets is outside of the scope of writeMonth and module is a singleton,
// so buckets will all get filled up when running writeMonth 12 times (as desired);
// writeYear is able to use them by accessing the buckets attribute of
// this single (and inherently only) instantiation
// of the singleton writeMonth module.
export_obj.buckets = [[],[],[],[],[],[],[],[]];

var month_name = {1: "January", 2: "February", 3: "March",
                  4: "April",   5: "May",      6: "June",
                  7: "July",    8: "August",   9: "September",
                 10: "October",11: "November",12: "December"};

var days_in_month = {1: 31,  2: 28,  3: 31,  4: 30,  5: 31,  6: 30,
                     7: 31,  8: 31,  9: 30, 10: 31, 11: 30, 12: 31};

export_obj.numDays = function(month, year){
  return (month === 2 && this.isLeap(year)) ? 29 : days_in_month[month];
}

export_obj.isLeap = function(year) {
  if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {return true;}
  return false;
}

export_obj.writeMonth = function(month, year){
  j = 1;
  monthIs = parseInt(month);
  yearIs = parseInt(year);
  numDays = this.numDays(month, year);
  // let's get the first day into format: 0 is Sun, 1 is Mon, 6 is Sat.
  firstIs = (zeller.calculate(month, 1, year) + 6) % 7;
  if(!this.Y) {
    console.log(this.makeHeader());
    console.log("Su Mo Tu We Th Fr Sa");
    console.log(this.makeFirstLine());
    for (var i = 0; i < 5; i++) {
      console.log(this.makeOtherLine());
    }
  } else {
    this.buckets[0].push(this.makeHeader());
    monthIs % 3 !== 0 ? this.buckets[1].push("Su Mo Tu We Th Fr Sa "): this.buckets[1].push("Su Mo Tu We Th Fr Sa");
    this.buckets[2].push(this.makeFirstLine());
    for (var i = 3; i < 8; i++) {
      this.buckets[i].push(this.makeOtherLine());
    }
  }
  return "worked";
}

export_obj.makeHeader = function() {
  var line = "";
  var spaces = "";
  var monthTitle = month_name[monthIs];
  var content = monthTitle;
  if (!this.Y) {content = content + " " + yearIs;}
  var spaces_num = Math.floor((20 - content.length) / 2);
  for (var i = 0; i < spaces_num; i++) {
    spaces += " ";
  }
  line = spaces + content + spaces;
  for (var i = 0; i < (20 - line.length) + 2; i++) {
    line += " ";
  }
  return line;
}

export_obj.makeFirstLine = function() {
  var line = "";
  for (var i = 0; i < firstIs; i++) {
    line += "   ";
  }
  for (var i = firstIs; i <= 6; i++) {
    if (i.toString().length === 1) {line += " ";}
    line += j
    if (i !== 7) {line += " ";}
    j++;
  }
  return line;
}

export_obj.makeOtherLine = function() {
  var line = "";
  for (var i = 0; i < 7; i++) {
    if (j <= numDays && j.toString().length === 1) {
      line += " " + j;
    } else if (j <= numDays) {
      line += j;
    } else {
      line += "  ";
    }
    if (i !== 7) {line += " ";}
    j++
  }
  return line;
}

// export_obj.makeHeader = function() {
//   var line = "";
//   var spaces = "";
//   var monthTitle = month_name[monthIs];
//   var content = monthTitle;
//   if (!this.Y) {content = content + " " + yearIs;}
//   var spaces_num = Math.floor((20 - content.length) / 2);
//   for (var i = 0; i < spaces_num; i++) {
//     spaces += " ";
//   }

//   // need to make sure that there aren't any trailing spaces if
//   // this is going to be a line build for a single month instead of a year
//   // also edge months (March, June, etc.) need to have no trailing spaces.
//   if (this.Y && (monthIs % 3 !== 0)) {
//     line = spaces + content + spaces;
//     for (var i = 0; i < (20 - line.length) + 2; i++) {
//       line += " ";
//     }
//   } else {
//     line = spaces + content;
//   }

//   return line;
// }

// export_obj.makeFirstLine = function() {
//   var line = "";
//   for (var i = 0; i < firstIs; i++) {
//     line += "   ";
//   }
//   for (var i = firstIs; i <= 6; i++) {
//     if (i.toString().length === 1) {line += " ";}
//     line += j
//     // if there are days yet to write to the month, and we're only writing a month and we aren't on the last day of the row, add a space.
//     // or, if we're writing a year and we aren't on an edge month (March, June, etc.) OR we are on an edge month but aren't on its last column, add a space.
//     if (j < numDays && ((!this.Y && i !== 6) || this.Y && (monthIs % 3 !== 0 || i !== 6))) {line += " ";}
//     j++;
//   }
//   return line;
// }

// export_obj.makeOtherLine = function() {
//   var line = "";
//   for (var i = 0; i <= 6; i++) {
//     if (j <= numDays && j.toString().length === 1) {
//       line += " " + j;
//     } else if (j <= numDays) {
//       line += j;
//     } else if (this.Y && monthIs % 3 !== 0) {
//       line += "  ";
//     }
//     // if we're writing just a month, we have days left to write and we aren't on the last column, add a space.
//     // Or if we're writing a year, and either we aren't on an edge month (March, June, etc.) or we have days left to write & we aren't
//     // on the last column, add a space.
//     if ((!this.Y && j < numDays && i !== 6) || this.Y && (monthIs % 3 !== 0 || (i !== 6 && j < numDays))) {line += " ";}
//     j++
//   }
//   return line;
// }

module.exports = export_obj;
<|endoftext|>