<commit_before>const log = require('@cardstack/logger')('cardstack/auth');
const Error = require('@cardstack/plugin-utils/error');
const Session = require('@cardstack/plugin-utils/session');
const bearerTokenPattern = /bearer +(.*)$/i;
const compose = require('koa-compose');
const route = require('koa-better-route');
const koaJSONBody = require('koa-json-body');

const { declareInjections } = require('@cardstack/di');
const { withJsonErrorHandling } = Error;

function addCorsHeaders(response) {
  response.set('Access-Control-Allow-Origin', '*');
  response.set('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
  response.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');
}

function isPartialSession(doc) {
  return doc.meta && doc.meta['partial-session'];
}

module.exports = declareInjections({
  encryptor: 'hub:encryptor',
  sessions: 'hub:sessions',
  searcher: 'hub:searchers',
  writer: 'hub:writers',
  indexers: 'hub:indexers',
  sources: 'hub:data-sources',
  controllingBranch: 'hub:controlling-branch',
  ciSession: 'config:ci-session',
  currentSchema: 'hub:current-schema'
},

class Authentication {

  get userSearcher() {
    return {
      get: (type, userId) => {
        return this.searcher.get(Session.INTERNAL_PRIVILEGED, this.controllingBranch.name, type, userId);
      },
      search: (params) => {
        return this.searcher.search(Session.INTERNAL_PRIVILEGED, this.controllingBranch.name, params);
      }
    };
  }

  async createToken(sessionPayload, validSeconds) {
    let validUntil = Math.floor(Date.now()/1000 + validSeconds);
    return {
      token: this.encryptor.encryptAndSign([sessionPayload, validUntil]),
      validUntil
    };
  }

  async _tokenToSession(token, ctxt) {
    let ciSessionId = this.ciSession && this.ciSession.id;
    if (ciSessionId && token === ciSessionId) {
      return Session.INTERNAL_PRIVILEGED;
    }

    try {
      let [sessionPayload, validUntil] = this.encryptor.verifyAndDecrypt(token);
      if (validUntil <= Date.now()/1000) {
        log.debug("Ignoring expired token");
      } else {
        let sessionMeta = await this._sessionMeta(ctxt);
        return this.sessions.create(sessionPayload.type, sessionPayload.id, sessionMeta);
      }
    } catch (err) {
      if (/unable to authenticate data|invalid key length|Not a valid signed message/.test(err.message)) {
        log.warn("Ignoring invalid token");
      } else {
        throw err;
      }
    }
  }

  get category() {
    return 'authentication';
  }

  middleware() {
    const prefix = 'auth';
    return compose([
      this._tokenVerifier(),
      this._tokenIssuerPreflight(prefix),
      this._tokenIssuer(prefix),
      this._tokenStatusPreflight(prefix),
      this._tokenStatus(prefix),
      this._exposeConfiguration(prefix)
    ]);
  }

  _tokenVerifier() {
    return async (ctxt, next) => {
      log.trace(`client headers: ${JSON.stringify(ctxt.header)}`);
      log.trace(`client IP address: ${ctxt.ip}`);

      let m = bearerTokenPattern.exec(ctxt.header['authorization']);
      if (m) {
        let session = await this._tokenToSession(m[1], ctxt);
        if (session) {
          ctxt.state.cardstackSession = session;
        }
      }
      await next();
    };
  }

  _tokenIssuerPreflight(prefix) {
    return route.options(`/${prefix}/:module`,  async (ctxt) => {
      addCorsHeaders(ctxt.response);
      ctxt.status = 200;
    });
  }

  _tokenStatusPreflight(prefix) {
    return route.options(`/${prefix}/:module/status`,  async (ctxt) => {
      addCorsHeaders(ctxt.response);
      ctxt.status = 200;
    });
  }

  _tokenStatus(prefix){
    return route.get(`/${prefix}/:module/status`, compose([
      async (ctxt) => {
        addCorsHeaders(ctxt.response);
        await withJsonErrorHandling(ctxt, async () => {
          let session = ctxt.state.cardstackSession;
          if (!session) {
            ctxt.status = 401;
            ctxt.body = {
              errors: [{
                title: "Not authorized",
                detail: "The authentication plugin was unable to validate the session"
              }]
            };
            return;
          }
          let user = await this.userSearcher.get(session.type, session.id);

          if (!user) { throw new Error(`cant find user type ${session.type} id ${session.id}`); }

          await this._generateSession(ctxt, user);

          let m = bearerTokenPattern.exec(ctxt.header['authorization']);
          if (m) {
            ctxt.body.data.meta['prevToken'] = m[1];
          }
        });
      }
    ]));
  }

  async _locateAuthenticationSource(name) {
    let activeSources = await this.sources.active();
    let source = activeSources.get(name);
    if (source && source.authenticator) {
      return source;
    }
    log.warn('Did not locate authentication source "%s"', name);
    throw new Error(`No such authentication source "${name}"`, { status: 404 });
  }

  async _sessionMeta(ctxt) {
    let config, ip;

    try {
       config = await this.searcher.getFromControllingBranch(Session.INTERNAL_PRIVILEGED, 'plugin-configs', '@cardstack/authentication');
    } catch (e) {
      // no config for the authentication plugin, assume header should not be respected
    }

    if (config && config.data.attributes['plugin-config'] && config.data.attributes['plugin-config']['allow-x-forwarded-for']) {
      ip = ctxt.header['x-forwarded-for'] || ctxt.ip;
    } else {
      ip = ctxt && ctxt.ip;
    }

    return {ip};
  }

  async _generateSession(ctxt, user, tokenExpirySeconds=24*3600) {
    let sessionPayload = { id: user.data.id, type: user.data.type };
    let sessionMeta = await this._sessionMeta();
    let session = this.sessions.create(sessionPayload.type, sessionPayload.id, sessionMeta);

    let schema = await this.currentSchema.forControllingBranch();
    let canLogin = await schema.hasLoginAuthorization(session);
    let creatableTypes = await schema.authorizedCreatableContentTypes(session);

    if (!canLogin) {
      ctxt.status = 401;
      ctxt.body = {
        errors: [{
          title: "Not authorized",
          detail: "You do not posses a grant that authorizes you to login"
        }]
      };

      return;
    }

    let tokenMeta = await this.createToken(sessionPayload, tokenExpirySeconds);
    if (!user.data.meta) {
      user.data.meta = tokenMeta;
    } else {
      Object.assign(user.data.meta, tokenMeta);
    }
    if (creatableTypes.length) {
      user.data.meta.creatableTypes = creatableTypes;
    }

    let authorizedUser = await this._applyReadAuthorization(session, user);

    ctxt.body = authorizedUser;
    ctxt.status = 200;
  }

  async _invokeAuthenticationSource(ctxt, source) {
    let result = await source.authenticator.authenticate(ctxt.request.body, this.userSearcher);

    if (!result) {
      ctxt.status = 401;
      ctxt.body = {
        errors: [{
          title: "Not authorized",
          detail: "The authentication plugin did not approve your request"
        }]
      };
      return;
    }

    let user;
    if (result.meta && result.meta.preloaded) {
      delete result.meta;
      user = result;
    } else {
      let rewritten = await source.rewriteExternalUser(result);

      if (isPartialSession(rewritten)) {
        ctxt.body = rewritten;
        ctxt.status = 200;
        return;
      }
      user = await this._processExternalUser(rewritten, source);
    }

    if (!user || !user.data) {
      ctxt.status = 401;
      ctxt.body = {
        errors: [{
          title: "Not authorized",
          detail: "The authentication plugin attempted to approve you but we found no corresponding user record"
        }]
      };

      return;
    }

    await this._generateSession(ctxt, user, source.tokenExpirySeconds);
  }

  async _applyReadAuthorization(session, user) {
    let branch = this.controllingBranch.name;
    let schema = await this.currentSchema.forControllingBranch();
    let documentContext = this.searcher.createDocumentContext({
      branch,
      schema,
    });
    let authorizedUser = await documentContext.applyReadAuthorization(user, { session });
    if (!authorizedUser) {
      // User has no grant to even see that their own record
      // exists. But users necessarily know they exists if they're
      // able to log in, so we default to an empty document.
      authorizedUser = {
        data: {
          type: user.data.type,
          id: user.data.id,
          meta: user.data.meta
        }
      };
    }

    return authorizedUser;
  }

  async _processExternalUser(user, source) {
    if (!user.data || !user.data.type) { return; }

    let have;
    try {
      let query = await source.externalUserCorrelationQuery(user);
      if (query) {
        let searchResult = await this.userSearcher.search(query);
        if (searchResult.data.length > 0) {
          have = { data: searchResult.data[0] };
        }
      } else {
        have = await this.userSearcher.get(user.data.type, user.data.id);
      }
    } catch (err) {
      if (err.status !== 404) {
        throw err;
      }
    }

    if (!have && source.mayCreateUser) {
      have = await this.writer.create(this.controllingBranch.name, Session.INTERNAL_PRIVILEGED, user.data.type, user);
    }
    if (have && source.mayUpdateUser) {
      user.data.meta = have.data.meta;
      have = await this.writer.update(this.controllingBranch.name, Session.INTERNAL_PRIVILEGED, user.data.type, have.data.id, user);
    }

    return have;
  }

  _tokenIssuer(prefix){
    return route.post(`/${prefix}/:module`, compose([
      koaJSONBody({ limit: '1mb' }),
      async (ctxt) => {
        addCorsHeaders(ctxt.response);
        await withJsonErrorHandling(ctxt, async () => {
          let source = await this._locateAuthenticationSource(ctxt.routeParams.module);
          await this._invokeAuthenticationSource(ctxt, source);
        });
      }
    ]));
  }

  _exposeConfiguration(prefix) {
    return route.get(`/${prefix}/:module`, async (ctxt) => {
      addCorsHeaders(ctxt.response);
      await withJsonErrorHandling(ctxt, async () => {
        let source = await this._locateAuthenticationSource(ctxt.routeParams.module);
        if (source.authenticator.exposeConfig) {
          ctxt.body = await source.authenticator.exposeConfig();
        } else {
          ctxt.body = {};
        }
      });
    });
  }
});
<commit_msg>log more detail for invalid token<commit_after>const log = require('@cardstack/logger')('cardstack/auth');
const Error = require('@cardstack/plugin-utils/error');
const Session = require('@cardstack/plugin-utils/session');
const bearerTokenPattern = /bearer +(.*)$/i;
const compose = require('koa-compose');
const route = require('koa-better-route');
const koaJSONBody = require('koa-json-body');

const { declareInjections } = require('@cardstack/di');
const { withJsonErrorHandling } = Error;

function addCorsHeaders(response) {
  response.set('Access-Control-Allow-Origin', '*');
  response.set('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
  response.set('Access-Control-Allow-Headers', 'Content-Type,Authorization');
}

function isPartialSession(doc) {
  return doc.meta && doc.meta['partial-session'];
}

module.exports = declareInjections({
  encryptor: 'hub:encryptor',
  sessions: 'hub:sessions',
  searcher: 'hub:searchers',
  writer: 'hub:writers',
  indexers: 'hub:indexers',
  sources: 'hub:data-sources',
  controllingBranch: 'hub:controlling-branch',
  ciSession: 'config:ci-session',
  currentSchema: 'hub:current-schema'
},

class Authentication {

  get userSearcher() {
    return {
      get: (type, userId) => {
        return this.searcher.get(Session.INTERNAL_PRIVILEGED, this.controllingBranch.name, type, userId);
      },
      search: (params) => {
        return this.searcher.search(Session.INTERNAL_PRIVILEGED, this.controllingBranch.name, params);
      }
    };
  }

  async createToken(sessionPayload, validSeconds) {
    let validUntil = Math.floor(Date.now()/1000 + validSeconds);
    return {
      token: this.encryptor.encryptAndSign([sessionPayload, validUntil]),
      validUntil
    };
  }

  async _tokenToSession(token, ctxt) {
    let ciSessionId = this.ciSession && this.ciSession.id;
    if (ciSessionId && token === ciSessionId) {
      return Session.INTERNAL_PRIVILEGED;
    }

    try {
      let [sessionPayload, validUntil] = this.encryptor.verifyAndDecrypt(token);
      if (validUntil <= Date.now()/1000) {
        log.debug("Ignoring expired token");
      } else {
        let sessionMeta = await this._sessionMeta(ctxt);
        return this.sessions.create(sessionPayload.type, sessionPayload.id, sessionMeta);
      }
    } catch (err) {
      if (/unable to authenticate data|invalid key length|Not a valid signed message/.test(err.message)) {
        log.warn(`Ignoring invalid token for ${ctxt.request.method} ${ctxt.request.path} (${ctxt.header['authorization']})`);
      } else {
        throw err;
      }
    }
  }

  get category() {
    return 'authentication';
  }

  middleware() {
    const prefix = 'auth';
    return compose([
      this._tokenVerifier(),
      this._tokenIssuerPreflight(prefix),
      this._tokenIssuer(prefix),
      this._tokenStatusPreflight(prefix),
      this._tokenStatus(prefix),
      this._exposeConfiguration(prefix)
    ]);
  }

  _tokenVerifier() {
    return async (ctxt, next) => {
      log.trace(`client headers: ${JSON.stringify(ctxt.header)}`);
      log.trace(`client IP address: ${ctxt.ip}`);

      let m = bearerTokenPattern.exec(ctxt.header['authorization']);
      if (m) {
        let session = await this._tokenToSession(m[1], ctxt);
        if (session) {
          ctxt.state.cardstackSession = session;
        }
      }
      await next();
    };
  }

  _tokenIssuerPreflight(prefix) {
    return route.options(`/${prefix}/:module`,  async (ctxt) => {
      addCorsHeaders(ctxt.response);
      ctxt.status = 200;
    });
  }

  _tokenStatusPreflight(prefix) {
    return route.options(`/${prefix}/:module/status`,  async (ctxt) => {
      addCorsHeaders(ctxt.response);
      ctxt.status = 200;
    });
  }

  _tokenStatus(prefix){
    return route.get(`/${prefix}/:module/status`, compose([
      async (ctxt) => {
        addCorsHeaders(ctxt.response);
        await withJsonErrorHandling(ctxt, async () => {
          let session = ctxt.state.cardstackSession;
          if (!session) {
            ctxt.status = 401;
            ctxt.body = {
              errors: [{
                title: "Not authorized",
                detail: "The authentication plugin was unable to validate the session"
              }]
            };
            return;
          }
          let user = await this.userSearcher.get(session.type, session.id);

          if (!user) { throw new Error(`cant find user type ${session.type} id ${session.id}`); }

          await this._generateSession(ctxt, user);

          let m = bearerTokenPattern.exec(ctxt.header['authorization']);
          if (m) {
            ctxt.body.data.meta['prevToken'] = m[1];
          }
        });
      }
    ]));
  }

  async _locateAuthenticationSource(name) {
    let activeSources = await this.sources.active();
    let source = activeSources.get(name);
    if (source && source.authenticator) {
      return source;
    }
    log.warn('Did not locate authentication source "%s"', name);
    throw new Error(`No such authentication source "${name}"`, { status: 404 });
  }

  async _sessionMeta(ctxt) {
    let config, ip;

    try {
       config = await this.searcher.getFromControllingBranch(Session.INTERNAL_PRIVILEGED, 'plugin-configs', '@cardstack/authentication');
    } catch (e) {
      // no config for the authentication plugin, assume header should not be respected
    }

    if (config && config.data.attributes['plugin-config'] && config.data.attributes['plugin-config']['allow-x-forwarded-for']) {
      ip = ctxt.header['x-forwarded-for'] || ctxt.ip;
    } else {
      ip = ctxt && ctxt.ip;
    }

    return {ip};
  }

  async _generateSession(ctxt, user, tokenExpirySeconds=24*3600) {
    let sessionPayload = { id: user.data.id, type: user.data.type };
    let sessionMeta = await this._sessionMeta();
    let session = this.sessions.create(sessionPayload.type, sessionPayload.id, sessionMeta);

    let schema = await this.currentSchema.forControllingBranch();
    let canLogin = await schema.hasLoginAuthorization(session);
    let creatableTypes = await schema.authorizedCreatableContentTypes(session);

    if (!canLogin) {
      ctxt.status = 401;
      ctxt.body = {
        errors: [{
          title: "Not authorized",
          detail: "You do not posses a grant that authorizes you to login"
        }]
      };

      return;
    }

    let tokenMeta = await this.createToken(sessionPayload, tokenExpirySeconds);
    if (!user.data.meta) {
      user.data.meta = tokenMeta;
    } else {
      Object.assign(user.data.meta, tokenMeta);
    }
    if (creatableTypes.length) {
      user.data.meta.creatableTypes = creatableTypes;
    }

    let authorizedUser = await this._applyReadAuthorization(session, user);

    ctxt.body = authorizedUser;
    ctxt.status = 200;
  }

  async _invokeAuthenticationSource(ctxt, source) {
    let result = await source.authenticator.authenticate(ctxt.request.body, this.userSearcher);

    if (!result) {
      ctxt.status = 401;
      ctxt.body = {
        errors: [{
          title: "Not authorized",
          detail: "The authentication plugin did not approve your request"
        }]
      };
      return;
    }

    let user;
    if (result.meta && result.meta.preloaded) {
      delete result.meta;
      user = result;
    } else {
      let rewritten = await source.rewriteExternalUser(result);

      if (isPartialSession(rewritten)) {
        ctxt.body = rewritten;
        ctxt.status = 200;
        return;
      }
      user = await this._processExternalUser(rewritten, source);
    }

    if (!user || !user.data) {
      ctxt.status = 401;
      ctxt.body = {
        errors: [{
          title: "Not authorized",
          detail: "The authentication plugin attempted to approve you but we found no corresponding user record"
        }]
      };

      return;
    }

    await this._generateSession(ctxt, user, source.tokenExpirySeconds);
  }

  async _applyReadAuthorization(session, user) {
    let branch = this.controllingBranch.name;
    let schema = await this.currentSchema.forControllingBranch();
    let documentContext = this.searcher.createDocumentContext({
      branch,
      schema,
    });
    let authorizedUser = await documentContext.applyReadAuthorization(user, { session });
    if (!authorizedUser) {
      // User has no grant to even see that their own record
      // exists. But users necessarily know they exists if they're
      // able to log in, so we default to an empty document.
      authorizedUser = {
        data: {
          type: user.data.type,
          id: user.data.id,
          meta: user.data.meta
        }
      };
    }

    return authorizedUser;
  }

  async _processExternalUser(user, source) {
    if (!user.data || !user.data.type) { return; }

    let have;
    try {
      let query = await source.externalUserCorrelationQuery(user);
      if (query) {
        let searchResult = await this.userSearcher.search(query);
        if (searchResult.data.length > 0) {
          have = { data: searchResult.data[0] };
        }
      } else {
        have = await this.userSearcher.get(user.data.type, user.data.id);
      }
    } catch (err) {
      if (err.status !== 404) {
        throw err;
      }
    }

    if (!have && source.mayCreateUser) {
      have = await this.writer.create(this.controllingBranch.name, Session.INTERNAL_PRIVILEGED, user.data.type, user);
    }
    if (have && source.mayUpdateUser) {
      user.data.meta = have.data.meta;
      have = await this.writer.update(this.controllingBranch.name, Session.INTERNAL_PRIVILEGED, user.data.type, have.data.id, user);
    }

    return have;
  }

  _tokenIssuer(prefix){
    return route.post(`/${prefix}/:module`, compose([
      koaJSONBody({ limit: '1mb' }),
      async (ctxt) => {
        addCorsHeaders(ctxt.response);
        await withJsonErrorHandling(ctxt, async () => {
          let source = await this._locateAuthenticationSource(ctxt.routeParams.module);
          await this._invokeAuthenticationSource(ctxt, source);
        });
      }
    ]));
  }

  _exposeConfiguration(prefix) {
    return route.get(`/${prefix}/:module`, async (ctxt) => {
      addCorsHeaders(ctxt.response);
      await withJsonErrorHandling(ctxt, async () => {
        let source = await this._locateAuthenticationSource(ctxt.routeParams.module);
        if (source.authenticator.exposeConfig) {
          ctxt.body = await source.authenticator.exposeConfig();
        } else {
          ctxt.body = {};
        }
      });
    });
  }
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

import yargs    from 'yargs'
import log      from './commands/tick-log'
import login    from './commands/tick-login'
import list     from './commands/tick-list'
import register from './commands/tick-register'
import rm       from './commands/tick-rm'
import upgrade  from './commands/tick-upgrade'
import sync     from './commands/tick-sync'

yargs
.usage('Usage: tick <command> [options]')
.command('log', 'log a tick', log)
.command('login', 'login to your account', login)
.command('list', 'list your ticks', list)
.command('register', 'create an account', register)
.command('rm', 'delete a tick', rm)
.command('upgrade', 'upgrade your tickbin', upgrade)
.command('sync', 'sync your database with remotes', sync)
.check(checkCommand)
.alias('h', 'help')
.help('h')
.argv

function checkCommand (argv) {
  const validArgs = [ 'log', 'list', 'rm' ]
  if (validArgs.indexOf(argv._[0]) == -1) {
    throw new Error('you must provide a valid command')
  }
}
<commit_msg>Make tick.js executable<commit_after>#!/usr/bin/env node

import yargs    from 'yargs'
import log      from './commands/tick-log'
import login    from './commands/tick-login'
import list     from './commands/tick-list'
import register from './commands/tick-register'
import rm       from './commands/tick-rm'
import upgrade  from './commands/tick-upgrade'
import sync     from './commands/tick-sync'

yargs
.usage('Usage: tick <command> [options]')
.command('log', 'log a tick', log)
.command('login', 'login to your account', login)
.command('list', 'list your ticks', list)
.command('register', 'create an account', register)
.command('rm', 'delete a tick', rm)
.command('upgrade', 'upgrade your tickbin', upgrade)
.command('sync', 'sync your database with remotes', sync)
.check(checkCommand)
.alias('h', 'help')
.help('h')
.argv

function checkCommand (argv) {
  const validArgs = [ 'log', 'list', 'rm' ]
  if (validArgs.indexOf(argv._[0]) == -1) {
    throw new Error('you must provide a valid command')
  }
}
<|endoftext|>


<commit_before>// LICENSE : MIT
"use strict";
import {RuleHelper} from "textlint-rule-helper";

function countMatchContent(text, reg) {
    var count = 0;
    var matches = text.match(reg) || [];
    count += matches.length;
    return {count, matches};
}
function countMatchContentEnd(text, reg) {
    var count = 0;
    var lines = text.split(/\r\n|\r|\n|\u2028|\u2029/g);
    var matches = [];
    lines.forEach(line => {
        var match = line.match(reg) || [];
        matches = matches.concat(match);
        count += matches.length;
    });
    return {count, matches};
}
export default function noMixDearuDesumasu(context) {
    let {Syntax, RuleError, report, getSource} = context;
    let helper = new RuleHelper(context);
    // This RegExp come from https://github.com/recruit-tech/redpen/blob/master/redpen-core/src/main/java/cc/redpen/validator/sentence/JapaneseStyleValidator.java
    const DEARU_PATTERN = /のだが|したが|したので|ないかと|してきた|であるから/g;
    const DEARU_END_PATTERN = /(?:だ|である|った|ではない｜ないか|しろ|しなさい|いただきたい|いただく|ならない|あろう|られる)(?:[。]?)$/;

    const DESUMASU_PATTERN = /でしたが|でしたので|ですので|ですが/g;
    const DESUMASU_END_PATTERN = /(?:です|ます|ました|ません|ですね|でしょうか|ください|ませ)(?:[。]?)$/;

    let dearuCount = 0;
    let desumasuCount = 0;

    let dearuLastHit = null;
    let desumasuLastHit = null;

    return {
        [Syntax.Str](node){
            if (helper.isChildNode(node, [Syntax.Link, Syntax.Image, Syntax.BlockQuote, Syntax.Emphasis])) {
                return;
            }
            // 例外) 「です・ます」調の文中の「箇条書き」の部分に「である」調を使う場合
            // http://www.p-press.jp/correct/mailmagazine/mailmagazine24.html
            if (helper.isChildNode(node, [Syntax.ListItem])) {
                return;
            }
            let beforeDearuCount = dearuCount;
            let beforeDesumasuCount = desumasuCount;
            let text = getSource(node);
            let matchDearu = countMatchContent(text, DEARU_PATTERN);
            let matchDearuEnd = countMatchContentEnd(text, DEARU_END_PATTERN);
            let matchDesumasu = countMatchContent(text, DESUMASU_PATTERN);
            let matchDesumasuEnd = countMatchContentEnd(text, DESUMASU_END_PATTERN);
            dearuCount += matchDearu.count + matchDearuEnd.count;
            desumasuCount += matchDesumasu.count + matchDesumasuEnd.count;
            if (beforeDearuCount !== dearuCount) {
                dearuLastHit = {
                    node,
                    matches: matchDearu.count > 0 ? matchDearu.matches : matchDearuEnd.matches
                };
            }
            if (beforeDesumasuCount !== desumasuCount) {
                desumasuLastHit = {
                    node,
                    matches: matchDesumasu.count > 0 ? matchDesumasu.matches : matchDesumasuEnd.matches
                };
            }
        },
        [Syntax.Document + ":exit"](node){
            if (dearuCount === 0 || desumasuCount === 0) {
                // No problem
                return;
            }
            if (dearuCount > desumasuCount) {
                let ruleError = new RuleError(`"である"調 と "ですます"調 が混在
である  : ${dearuCount}
ですます: ${desumasuCount}
=> "${desumasuLastHit.matches.join("、")}" がですます調
`);
                // である優先 => 最後の"ですます"を表示
                report(desumasuLastHit.node, ruleError)
            } else {
                let ruleError = new RuleError(`"である"調 と "ですます"調 が混在
である  : ${dearuCount}
ですます: ${desumasuCount}
=> "${dearuLastHit.matches.join("、")}" がである調
`);

                // ですます優先 => 最後の"である"を表示
                report(dearuLastHit.node, ruleError);
            }
        }
    }
}<commit_msg>chore(rule): use let insteadof var<commit_after>// LICENSE : MIT
"use strict";
import {RuleHelper} from "textlint-rule-helper";

function countMatchContent(text, reg) {
    let count = 0;
    let matches = text.match(reg) || [];
    count += matches.length;
    return {count, matches};
}
function countMatchContentEnd(text, reg) {
    let count = 0;
    let lines = text.split(/\r\n|\r|\n|\u2028|\u2029/g);
    let matches = [];
    lines.forEach(line => {
        let match = line.match(reg) || [];
        matches = matches.concat(match);
        count += matches.length;
    });
    return {count, matches};
}
export default function noMixDearuDesumasu(context) {
    let {Syntax, RuleError, report, getSource} = context;
    let helper = new RuleHelper(context);
    // This RegExp come from https://github.com/recruit-tech/redpen/blob/master/redpen-core/src/main/java/cc/redpen/validator/sentence/JapaneseStyleValidator.java
    const DEARU_PATTERN = /のだが|したが|したので|ないかと|してきた|であるから/g;
    const DEARU_END_PATTERN = /(?:だ|である|った|ではない｜ないか|しろ|しなさい|いただきたい|いただく|ならない|あろう|られる)(?:[。]?)$/;

    const DESUMASU_PATTERN = /でしたが|でしたので|ですので|ですが/g;
    const DESUMASU_END_PATTERN = /(?:です|ます|ました|ません|ですね|でしょうか|ください|ませ)(?:[。]?)$/;

    let dearuCount = 0;
    let desumasuCount = 0;

    let dearuLastHit = null;
    let desumasuLastHit = null;

    return {
        [Syntax.Str](node){
            if (helper.isChildNode(node, [Syntax.Link, Syntax.Image, Syntax.BlockQuote, Syntax.Emphasis])) {
                return;
            }
            // 例外) 「です・ます」調の文中の「箇条書き」の部分に「である」調を使う場合
            // http://www.p-press.jp/correct/mailmagazine/mailmagazine24.html
            if (helper.isChildNode(node, [Syntax.ListItem])) {
                return;
            }
            let beforeDearuCount = dearuCount;
            let beforeDesumasuCount = desumasuCount;
            let text = getSource(node);
            let matchDearu = countMatchContent(text, DEARU_PATTERN);
            let matchDearuEnd = countMatchContentEnd(text, DEARU_END_PATTERN);
            let matchDesumasu = countMatchContent(text, DESUMASU_PATTERN);
            let matchDesumasuEnd = countMatchContentEnd(text, DESUMASU_END_PATTERN);
            dearuCount += matchDearu.count + matchDearuEnd.count;
            desumasuCount += matchDesumasu.count + matchDesumasuEnd.count;
            if (beforeDearuCount !== dearuCount) {
                dearuLastHit = {
                    node,
                    matches: matchDearu.count > 0 ? matchDearu.matches : matchDearuEnd.matches
                };
            }
            if (beforeDesumasuCount !== desumasuCount) {
                desumasuLastHit = {
                    node,
                    matches: matchDesumasu.count > 0 ? matchDesumasu.matches : matchDesumasuEnd.matches
                };
            }
        },
        [Syntax.Document + ":exit"](node){
            if (dearuCount === 0 || desumasuCount === 0) {
                // No problem
                return;
            }
            if (dearuCount > desumasuCount) {
                let ruleError = new RuleError(`"である"調 と "ですます"調 が混在
である  : ${dearuCount}
ですます: ${desumasuCount}
=> "${desumasuLastHit.matches.join("、")}" がですます調
`);
                // である優先 => 最後の"ですます"を表示
                report(desumasuLastHit.node, ruleError)
            } else {
                let ruleError = new RuleError(`"である"調 と "ですます"調 が混在
である  : ${dearuCount}
ですます: ${desumasuCount}
=> "${dearuLastHit.matches.join("、")}" がである調
`);

                // ですます優先 => 最後の"である"を表示
                report(dearuLastHit.node, ruleError);
            }
        }
    }
}<|endoftext|>


<commit_before>const { authorization, isAdmin } = require('./auth');
const bodyParser = require('body-parser');
const Logger = require('../logger');
const { saveOrUpdate, handleGenericError } = require('../db/mongoHelper');

const {
  HomeTile,
} = require('../db/models');

module.exports = {
  registerEndpoints(app) {
    app.get('/api/hometiles',
      (req, res) =>
        HomeTile.find().sort({ order: 1 })
          .then(tiles => res.send(tiles))
          .catch((err) => {
            Logger.error(err);
            res.status(500).send('Fetching home tiles failed!');
          })
    );

    app.put('/api/hometiles', authorization, isAdmin, bodyParser.json(),
      (req, res) =>
        saveOrUpdate(HomeTile, req.body)
          .then(() => res.sendStatus(200))
          .catch(error => handleGenericError(error, res))
    );

    app.post('/api/hometiles/order', authorization, isAdmin, bodyParser.json(),
      ({ body }, res) => Promise.all([
        HomeTile.findOne({ _id: body.element }),
        body.sibling ? HomeTile.findOne({ _id: body.sibling }) : HomeTile.count(),
      ])
        .then(([{ _id, order: originalPosition }, siblingTile]) => {
          let promise;
          let positionToTake = (typeof siblingTile === 'number') ? siblingTile : siblingTile.order;

          if (originalPosition < positionToTake) {
            positionToTake -= 1;
            promise = HomeTile.updateMany(
              { order: { $gt: originalPosition, $lte: positionToTake } },
              { $inc: { order: -1 } }
            );
          } else {
            promise = HomeTile.updateMany(
              { order: { $gte: positionToTake, $lt: originalPosition } },
              { $inc: { order: 1 } }
            );
          }

          return promise.then(() => HomeTile.update(
            { _id },
            { $set: { order: positionToTake } })
          );
        })
        .then(() => res.sendStatus(200), error => handleGenericError(error, res))
    );
  },
};
<commit_msg>set order correctly when adding a new home tile<commit_after>const { authorization, isAdmin } = require('./auth');
const bodyParser = require('body-parser');
const Logger = require('../logger');
const { saveOrUpdate, handleGenericError } = require('../db/mongoHelper');

const {
  HomeTile,
} = require('../db/models');

module.exports = {
  registerEndpoints(app) {
    app.get('/api/hometiles',
      (req, res) =>
        HomeTile.find().sort({ order: 1 })
          .then(tiles => res.send(tiles))
          .catch((err) => {
            Logger.error(err);
            res.status(500).send('Fetching home tiles failed!');
          })
    );

    app.put('/api/hometiles', authorization, isAdmin, bodyParser.json(),
      (req, res) =>
        HomeTile.count().then((count) => {
          const tile = req.body;
          tile.order = count;

          saveOrUpdate(HomeTile, tile)
            .then(() => res.sendStatus(200))
            .catch(error => handleGenericError(error, res));
        })
    );

    app.post('/api/hometiles/order', authorization, isAdmin, bodyParser.json(),
      ({ body }, res) => Promise.all([
        HomeTile.findOne({ _id: body.element }),
        body.sibling ? HomeTile.findOne({ _id: body.sibling }) : HomeTile.count(),
      ])
        .then(([{ _id, order: originalPosition }, siblingTile]) => {
          let promise;
          let positionToTake = (typeof siblingTile === 'number') ? siblingTile : siblingTile.order;

          if (originalPosition < positionToTake) {
            positionToTake -= 1;
            promise = HomeTile.updateMany(
              { order: { $gt: originalPosition, $lte: positionToTake } },
              { $inc: { order: -1 } }
            );
          } else {
            promise = HomeTile.updateMany(
              { order: { $gte: positionToTake, $lt: originalPosition } },
              { $inc: { order: 1 } }
            );
          }

          return promise.then(() => HomeTile.update(
            { _id },
            { $set: { order: positionToTake } })
          );
        })
        .then(() => res.sendStatus(200), error => handleGenericError(error, res))
    );
  },
};
<|endoftext|>


<commit_before>import { Context } from './context';
import { PreparedContext } from './prepared-context';

import {
  setPassword,
  getPassword,
  setProperty,
  removeSensibleValues
} from './util';
import { version } from '../package.json';
import { GithubProvider } from 'github-repository-provider';
import { BitbucketProvider } from 'bitbucket-repository-provider';
import { LocalProvider } from 'local-repository-provider';
import { AggregationProvider } from 'aggregation-repository-provider';
import { directory } from 'tempy';

const program = require('caporal'),
  prompt = require('prompt');

process.on('uncaughtException', err => console.error(err));
process.on('unhandledRejection', reason => console.error(reason));

const properties = {};

program
  .description('Keep npm package in sync with its template')
  .version(version)
  .option('--dry', 'do not create branch/pull request', program.BOOL, false)
  .option(
    '-k, --keystore <account/service>',
    'keystore',
    /^[\w\-]+\/.*/,
    'arlac77/GitHub for Mac SSH key passphrase — github.com'
  )
  .option('-s, --save', 'save keystore')
  .option('-d --define <key=value>', 'set provider option', values => {
    if (!Array.isArray(values)) {
      values = [values];
    }

    values.forEach(value => {
      const [k, v] = value.split(/=/);
      setProperty(properties, k, v);
    });
  })
  .option('--list-providers', 'list providers with options and exit')
  .option('--list-properties', 'list all properties and exit')
  .option(
    '-t, --template <identifier>',
    'template repository',
    /^([\w\-]+\/[\w\-]+)|((git|ssh|https?):\/\/.*)$/
  )
  .option(
    '--usage',
    'track packages using template in package.json',
    program.BOOL
  )
  .argument('[repos...]', 'repos to merge')
  .action(async (args, options, logger) => {
    if (options.save) {
      prompt.start();
      const schema = {
        properties: {
          password: {
            required: true,
            hidden: true
          }
        }
      };
      prompt.get(schema, async (err, result) => {
        if (err) {
          logger.error(err);
          return;
        }

        try {
          await setPassword(result.password, options);
        } catch (e) {
          logger.error(err);
          return;
        }
        logger.info('password set');
      });
    }

    try {
      const pass = await getPassword(options);
      const aggregationProvider = new AggregationProvider();

      if (pass !== null && pass !== undefined) {
        if (properties.GithubProvider === undefined) {
          properties.GithubProvider = {};
        }
        properties.GithubProvider.auth = pass;
      }

      [BitbucketProvider, GithubProvider, LocalProvider].forEach(provider => {
        let options = provider.optionsFromEnvironment(process.env);

        if (options !== undefined || properties[provider.name] !== undefined) {
          options = Object.assign({}, options, properties[provider.name]);
          aggregationProvider.providers.push(new provider(options));
        }
      });

      if (options.listProviders) {
        logger.info(
          Array.from(
            aggregationProvider.providers.map(
              p =>
                `${p.name}: ${JSON.stringify(removeSensibleValues(p.config))}`
            )
          ).join('\n')
        );

        return;
      }

      const context = new Context(aggregationProvider, {
        templateBranchName: options.template,
        dry: options.dry,
        trackUsedByModule: options.usage,
        logger,
        properties
      });

      if (options.listProperties) {
        logger.info(JSON.stringify(removeSensibleValues(context.properties)));
        return;
      }

      for (const repo of args.repos) {
        await PreparedContext.execute(context, repo);
      }
    } catch (err) {
      logger.error(err);
    }
  });

program.parse(process.argv);
<commit_msg>fix(cli): declare default --usage option as false<commit_after>import { Context } from './context';
import { PreparedContext } from './prepared-context';

import {
  setPassword,
  getPassword,
  setProperty,
  removeSensibleValues
} from './util';
import { version } from '../package.json';
import { GithubProvider } from 'github-repository-provider';
import { BitbucketProvider } from 'bitbucket-repository-provider';
import { LocalProvider } from 'local-repository-provider';
import { AggregationProvider } from 'aggregation-repository-provider';
import { directory } from 'tempy';

const program = require('caporal'),
  prompt = require('prompt');

process.on('uncaughtException', err => console.error(err));
process.on('unhandledRejection', reason => console.error(reason));

const properties = {};

program
  .description('Keep npm package in sync with its template')
  .version(version)
  .option('--dry', 'do not create branch/pull request', program.BOOL, false)
  .option(
    '-k, --keystore <account/service>',
    'keystore',
    /^[\w\-]+\/.*/,
    'arlac77/GitHub for Mac SSH key passphrase — github.com'
  )
  .option('-s, --save', 'save keystore')
  .option('-d --define <key=value>', 'set provider option', values => {
    if (!Array.isArray(values)) {
      values = [values];
    }

    values.forEach(value => {
      const [k, v] = value.split(/=/);
      setProperty(properties, k, v);
    });
  })
  .option('--list-providers', 'list providers with options and exit')
  .option('--list-properties', 'list all properties and exit')
  .option(
    '-t, --template <identifier>',
    'template repository',
    /^([\w\-]+\/[\w\-]+)|((git|ssh|https?):\/\/.*)$/
  )
  .option(
    '--usage',
    'track packages using template in package.json',
    program.BOOL,
    false
  )
  .argument('[repos...]', 'repos to merge')
  .action(async (args, options, logger) => {
    if (options.save) {
      prompt.start();
      const schema = {
        properties: {
          password: {
            required: true,
            hidden: true
          }
        }
      };
      prompt.get(schema, async (err, result) => {
        if (err) {
          logger.error(err);
          return;
        }

        try {
          await setPassword(result.password, options);
        } catch (e) {
          logger.error(err);
          return;
        }
        logger.info('password set');
      });
    }

    try {
      const pass = await getPassword(options);
      const aggregationProvider = new AggregationProvider();

      if (pass !== null && pass !== undefined) {
        if (properties.GithubProvider === undefined) {
          properties.GithubProvider = {};
        }
        properties.GithubProvider.auth = pass;
      }

      [BitbucketProvider, GithubProvider, LocalProvider].forEach(provider => {
        let options = provider.optionsFromEnvironment(process.env);

        if (options !== undefined || properties[provider.name] !== undefined) {
          options = Object.assign({}, options, properties[provider.name]);
          aggregationProvider.providers.push(new provider(options));
        }
      });

      if (options.listProviders) {
        logger.info(
          Array.from(
            aggregationProvider.providers.map(
              p =>
                `${p.name}: ${JSON.stringify(removeSensibleValues(p.config))}`
            )
          ).join('\n')
        );

        return;
      }

      const context = new Context(aggregationProvider, {
        templateBranchName: options.template,
        dry: options.dry,
        trackUsedByModule: options.usage,
        logger,
        properties
      });

      if (options.listProperties) {
        logger.info(JSON.stringify(removeSensibleValues(context.properties)));
        return;
      }

      for (const repo of args.repos) {
        await PreparedContext.execute(context, repo);
      }
    } catch (err) {
      logger.error(err);
    }
  });

program.parse(process.argv);
<|endoftext|>


<commit_before>import glob from 'glob';
import Boom from 'boom';

/**
 * For every js file defined in [rootDir]/src/api/routes, register the
 * Hapi.js routes exported by that file under the `routes` export.
 */
export default (config) => {
  const routesDir = config.apiRoutes;
  const apiExport = {};

  const routeFiles = glob.sync(`${routesDir}/**/*.js`);

  apiExport.register = (server, options, next) => {
    // TODO: Can we rewrite this using Redux compose/applyMiddleware patterns?
    const getTryCatchWrappedHandler = (handler) => {
      return async (request, reply) => {

        try {
          const response = await handler(request, reply);

          if (response === null) {
            // By convention, if the response is null, we'll return a 404
            reply(Boom.notFound());
          } else if (response._events) {
            // The handler called reply()
            // do nothing as the response is already set
          } else {
            reply(response);
          }
        } catch (e) {
          const errObj = (e instanceof Error) ? e : new Error(e);
          reply(Boom.wrap(errObj));
        }
      };
    };

    routeFiles.forEach((routeFile) => {
      const routes = require(routeFile).routes;
      const preparedRoutes = routes.map((route) => {
        const wrappedHandler = async (request, reply) => {
          return await getTryCatchWrappedHandler(route.handler)(request, reply);
        };

        // TODO: This needs to be expanded to capture all Hapi properties
        const preparedRoute = {
          path: route.path,
          method: route.method,
          handler: wrappedHandler,
          config: {
            auth: route.auth,
            validate: {
              query: route.query,
              params: route.params,
              payload: route.payload
            }
          }
        };

        return preparedRoute;
      });

      server.route(preparedRoutes);
    });

    next();
  };

  apiExport.register.attributes = {
    name: 'api',
    version: '0.1.0'
  };

  return apiExport;
};

<commit_msg>Provide additional Hapi route configuration options<commit_after>import glob from 'glob';
import Boom from 'boom';

/**
 * For every js file defined in [rootDir]/src/api/routes, register the
 * Hapi.js routes exported by that file under the `routes` export.
 */
export default (config) => {
  const routesDir = config.apiRoutes;
  const apiExport = {};

  const routeFiles = glob.sync(`${routesDir}/**/*.js`);

  apiExport.register = (server, options, next) => {
    // TODO: Can we rewrite this using Redux compose/applyMiddleware patterns?
    const getTryCatchWrappedHandler = (handler) => {
      return async (request, reply) => {

        try {
          const response = await handler(request, reply);

          if (response === null) {
            // By convention, if the response is null, we'll return a 404
            reply(Boom.notFound());
          } else if (response._events) {
            // The handler called reply()
            // do nothing as the response is already set
          } else {
            reply(response);
          }
        } catch (e) {
          const errObj = (e instanceof Error) ? e : new Error(e);
          reply(Boom.wrap(errObj));
        }
      };
    };

    routeFiles.forEach((routeFile) => {
      const routes = require(routeFile).routes;
      const preparedRoutes = routes.map((route) => {
        const wrappedHandler = async (request, reply) => {
          return await getTryCatchWrappedHandler(route.handler)(request, reply);
        };

        // TODO: This needs to be expanded to capture all Hapi properties
        const preparedRoute = {
          path: route.path,
          method: route.method,
          handler: wrappedHandler,
          config: {
            auth: route.auth,
            validate: {
              query: route.query,
              params: route.params,
              payload: route.payload
            }
          }
        };

        if (route.config) {
          preparedRoute.config = {
            ...preparedRoute.config,
            ...route.config
          };
        }

        return preparedRoute;
      });

      server.route(preparedRoutes);
    });

    next();
  };

  apiExport.register.attributes = {
    name: 'api',
    version: '0.1.0'
  };

  return apiExport;
};

<|endoftext|>


<commit_before>(function(){

    var _settings = { baseURL:'' };
    var _counter = 1;
    var _requests = {};

    function _registerRequest( request )
    {
        var overwrite = request.options.overwrite;

        if( overwrite == xhrm.overwrite.NONE )
        {
            _requests[ request.id ] = request;

            return true;
        }
        else {

            var overwriteSameState = (( overwrite & xhrm.overwrite.SAME_STATE ) == xhrm.overwrite.SAME_STATE );
            var overwriteSameURL = (( overwrite & xhrm.overwrite.SAME_URL ) == xhrm.overwrite.SAME_URL );

            var id, requestActive, requestsSameState, requestsSameURL;

            for( id in _requests )
            {
                requestActive = _requests[ id ];

                requestsSameState = (requestActive.options.state == request.options.state);
                requestsSameURL = (requestActive.options.url == request.options.url);

                if( overwriteSameState && overwriteSameURL )
                {
                    if( requestsSameState && requestsSameURL )
                    {
                        requestActive.cancel();
                    }
                }
                else if( overwriteSameState )
                {
                    if( requestsSameState )
                    {
                        requestActive.cancel();
                    }
                }
                else if( overwriteSameURL )
                {
                    if( requestsSameURL )
                    {
                        requestActive.cancel();
                    }
                }
            }

            _requests[ request.id ] = request;

            return true;
        }
    };

    function _unregisterRequest( request )
    {
        var registered = (_requests[ request.id ] != undefined);

        _requests[ request.id ] = null;
        delete _requests[ request.id ];

        return registered;
    };

    var xhrm = {

        overwrite: {

            //the request will persist until it will be completed or cancelled
            NONE:0,

            //allow only 1 request for the same state at time, new requests to the same state will override existing ones (if cancelable)
            //the state value should be provided within options and it value will be obtained doing String(options.state),
            //so you can pass also an object which implements its own toString() method
            SAME_STATE:(1 << 0),

            //allow only 1 request for the same url at time, new requests to the same url will override existing ones (if cancelable)
            SAME_URL:(1 << 1)
        },

        configRequests:function( settings )
        {
            _settings = $.extend({}, {

                baseURL:''

            }, settings );

            if( _settings.baseURL && _settings.baseURL.length > 0 && _settings.baseURL.charAt(_settings.baseURL.length - 1) != '/' ){
                _settings.baseURL += '/';
            }
        },

        createRequest:function( options )
        {
            if( options == undefined )
            {
                throw new Error('[xhrm] options cannot be undefined');
            }

            if( typeof(options) != 'object' )
            {
                throw new Error('[xhrm] options should be an {}');
            }

            if( typeof(options.url) != 'string' )
            {
                throw new Error('[xhrm] options.url cannot be undefined');
            }

            if( options.url == '' )
            {
                throw new Error('[xhrm] options.url cannot be empty');
            }
            else if( options.url.substring(0, 4).toLowerCase() != 'http' && _settings.baseURL != '' )
            {
                if( options.url.charAt(0) == '/' ){
                    options.url = options.url.substring(1);
                }

                options.url = (_settings.baseURL + options.url);
            }

            //easy contentType setting
            var contentTypes = {
                html: 'text/html; charset=utf-8',
                json: 'application/json; charset=utf-8',
                text: 'text/plain; charset=utf-8',
                vars: 'application/x-www-form-urlencoded; charset=utf-8'
            };

            var contentType = (contentTypes[ options.contentType ] || contentTypes[ options.requestType ] || contentTypes[ options.requestContentType ]);
            if( contentType ){
                contentType = contentType.toLowerCase()

                options.contentType = contentType;
            }

            var dataType = (options.dataType || options.responseType || options.responseContentType);
            if( dataType ){
                dataType = dataType.toLowerCase();

                if( dataType == 'jquery' ){
                    dataType = 'text';

                    options.dataTypeJQuery = true;
                }

                options.dataType = dataType;
            }

            var request = {};
            request.id = String(_counter++);
            request.options = $.extend({}, {

                data: {},
                crossDomain: true,
                timeout: (1000 * 15),
                overwrite: this.overwrite.NONE,
                state: ''

            }, options );

            if( request.options.state ){
                request.options.state = String(request.options.state);

                if( request.options.state == '' && ((request.options.overwrite & this.overwrite.SAME_STATE) == this.overwrite.SAME_STATE) )
                {
                    throw new Error('[xhrm] options.state cannot be "' + request.options.state + '" if the overwrite.SAME_STATE flag is present.')
                }
            }

            request.sent = false;
            request.send = function()
            {
                if(!request.sent && !request.cancelled && _registerRequest( request )){
                    request.sent = true;

                    request.xhr = $.ajax( request.options );

                    return true;
                }
                else {
                    return false;
                }
            };

            request.cancelable = (request.options.cancelable === false ? false : true);
            request.cancelled = false;
            request.cancel = function()
            {
                if(!request.cancelled && request.cancelable){
                    request.cancelled = true;

                    if( request.xhr && request.xhr.readystate != 4 ){
                        request.xhr.abort();
                    }

                    _unregisterRequest( request );

                    return true;
                }
                else {
                    return false;
                }
            };

            request.clone = function()
            {
                return xhrm.createRequest( $.extend({}, options ) );
            };

            request.toString = function()
            {
                return '[xhrm][request=' + this.id + ']';
            };

            request.options.successCallback = request.options.success;
            request.options.success = function( data, text )
            {
                if( request.cancelled ){
                    return;
                }

                _unregisterRequest( request );

                if( typeof(request.options.successCallback) === 'function' )
                {
                    try {
                        if( request.options.dataTypeJQuery ){
                            request.options.successCallback( $('

').append(data), text );
                        }
                        else {
                            request.options.successCallback( data, text );
                        }
                    }
                    catch(error){

                        if( typeof(request.options.errorCallback) === 'function' )
                        {
                            //parsing error
                            request.options.errorCallback( new Error( '[xhrm] parsing error - ' + error.message ) );
                        }
                    }
                }
            };

            request.options.errorCallback = request.options.error;
            request.options.error = function( jqXHR, textStatus, errorThrown )
            {
                if( request.cancelled || textStatus == 'abort' ){
                    return;
                }

                _unregisterRequest( request );

                if( typeof(request.options.errorCallback) === 'function' )
                {
                    request.options.errorCallback( new Error( jqXHR.statusText + ' ' + jqXHR.status ) );
                }
            };

            return request;
        },

        sendRequest:function( options )
        {
            var request = this.createRequest( options );
            request.send();
            return request;
        },

        cancelRequestByID:function( id )
        {
            var request = _requests[ String(id) ];

            if( request != undefined )
            {
                return request.cancel();
            }
            else {
                return false;
            }
        },

        cancelAllRequests:function()
        {
            var id, request;

            for( id in _requests )
            {
                this.cancelRequestByID( id );
            }
        }

    };

    document.xhrm = window.xhrm = xhrm;
    document.XHRM = window.XHRM = xhrm;
    document.XMLHttpRequestManager = window.XMLHttpRequestManager = xhrm;

})();

<commit_msg>Removed error catching from success callback execution.<commit_after>(function(){

    var _settings = { baseURL:'' };
    var _counter = 1;
    var _requests = {};

    function _registerRequest( request )
    {
        var overwrite = request.options.overwrite;

        if( overwrite == xhrm.overwrite.NONE )
        {
            _requests[ request.id ] = request;

            return true;
        }
        else {

            var overwriteSameState = (( overwrite & xhrm.overwrite.SAME_STATE ) == xhrm.overwrite.SAME_STATE );
            var overwriteSameURL = (( overwrite & xhrm.overwrite.SAME_URL ) == xhrm.overwrite.SAME_URL );

            var id, requestActive, requestsSameState, requestsSameURL;

            for( id in _requests )
            {
                requestActive = _requests[ id ];

                requestsSameState = (requestActive.options.state == request.options.state);
                requestsSameURL = (requestActive.options.url == request.options.url);

                if( overwriteSameState && overwriteSameURL )
                {
                    if( requestsSameState && requestsSameURL )
                    {
                        requestActive.cancel();
                    }
                }
                else if( overwriteSameState )
                {
                    if( requestsSameState )
                    {
                        requestActive.cancel();
                    }
                }
                else if( overwriteSameURL )
                {
                    if( requestsSameURL )
                    {
                        requestActive.cancel();
                    }
                }
            }

            _requests[ request.id ] = request;

            return true;
        }
    };

    function _unregisterRequest( request )
    {
        var registered = (_requests[ request.id ] != undefined);

        _requests[ request.id ] = null;
        delete _requests[ request.id ];

        return registered;
    };

    var xhrm = {

        overwrite: {

            //the request will persist until it will be completed or cancelled
            NONE:0,

            //allow only 1 request for the same state at time, new requests to the same state will override existing ones (if cancelable)
            //the state value should be provided within options and it value will be obtained doing String(options.state),
            //so you can pass also an object which implements its own toString() method
            SAME_STATE:(1 << 0),

            //allow only 1 request for the same url at time, new requests to the same url will override existing ones (if cancelable)
            SAME_URL:(1 << 1)
        },

        configRequests:function( settings )
        {
            _settings = $.extend({}, {

                baseURL:''

            }, settings );

            if( _settings.baseURL && _settings.baseURL.length > 0 && _settings.baseURL.charAt(_settings.baseURL.length - 1) != '/' ){
                _settings.baseURL += '/';
            }
        },

        createRequest:function( options )
        {
            if( options == undefined )
            {
                throw new Error('[xhrm] options cannot be undefined');
            }

            if( typeof(options) != 'object' )
            {
                throw new Error('[xhrm] options should be an {}');
            }

            if( typeof(options.url) != 'string' )
            {
                throw new Error('[xhrm] options.url cannot be undefined');
            }

            if( options.url == '' )
            {
                throw new Error('[xhrm] options.url cannot be empty');
            }
            else if( options.url.substring(0, 4).toLowerCase() != 'http' && _settings.baseURL != '' )
            {
                if( options.url.charAt(0) == '/' ){
                    options.url = options.url.substring(1);
                }

                options.url = (_settings.baseURL + options.url);
            }

            //easy contentType setting
            var contentTypes = {
                html: 'text/html; charset=utf-8',
                json: 'application/json; charset=utf-8',
                text: 'text/plain; charset=utf-8',
                vars: 'application/x-www-form-urlencoded; charset=utf-8'
            };

            var contentType = (contentTypes[ options.contentType ] || contentTypes[ options.requestType ] || contentTypes[ options.requestContentType ]);
            if( contentType ){
                contentType = contentType.toLowerCase()

                options.contentType = contentType;
            }

            var dataType = (options.dataType || options.responseType || options.responseContentType);
            if( dataType ){
                dataType = dataType.toLowerCase();

                if( dataType == 'jquery' ){
                    dataType = 'text';

                    options.dataTypeJQuery = true;
                }

                options.dataType = dataType;
            }

            var request = {};
            request.id = String(_counter++);
            request.options = $.extend({}, {

                data: {},
                crossDomain: true,
                timeout: (1000 * 15),
                overwrite: this.overwrite.NONE,
                state: ''

            }, options );

            if( request.options.state ){
                request.options.state = String(request.options.state);

                if( request.options.state == '' && ((request.options.overwrite & this.overwrite.SAME_STATE) == this.overwrite.SAME_STATE) )
                {
                    throw new Error('[xhrm] options.state cannot be "' + request.options.state + '" if the overwrite.SAME_STATE flag is present.')
                }
            }

            request.sent = false;
            request.send = function()
            {
                if(!request.sent && !request.cancelled && _registerRequest( request )){
                    request.sent = true;

                    request.xhr = $.ajax( request.options );

                    return true;
                }
                else {
                    return false;
                }
            };

            request.cancelable = (request.options.cancelable === false ? false : true);
            request.cancelled = false;
            request.cancel = function()
            {
                if(!request.cancelled && request.cancelable){
                    request.cancelled = true;

                    if( request.xhr && request.xhr.readystate != 4 ){
                        request.xhr.abort();
                    }

                    _unregisterRequest( request );

                    return true;
                }
                else {
                    return false;
                }
            };

            request.clone = function()
            {
                return xhrm.createRequest( $.extend({}, options ) );
            };

            request.toString = function()
            {
                return '[xhrm][request=' + this.id + ']';
            };

            request.options.successCallback = request.options.success;
            request.options.success = function( data, text )
            {
                if( request.cancelled ){
                    return;
                }

                _unregisterRequest( request );

                if( typeof(request.options.successCallback) === 'function' )
                {
                    if( request.options.dataTypeJQuery ){
                        request.options.successCallback( $('

').append(data), text );
                    }
                    else {
                        request.options.successCallback( data, text );
                    }
                }
            };

            request.options.errorCallback = request.options.error;
            request.options.error = function( jqXHR, textStatus, errorThrown )
            {
                if( request.cancelled || textStatus == 'abort' ){
                    return;
                }

                _unregisterRequest( request );

                if( typeof(request.options.errorCallback) === 'function' )
                {
                    request.options.errorCallback( new Error( jqXHR.statusText + ' ' + jqXHR.status ) );
                }
            };

            return request;
        },

        sendRequest:function( options )
        {
            var request = this.createRequest( options );
            request.send();
            return request;
        },

        cancelRequestByID:function( id )
        {
            var request = _requests[ String(id) ];

            if( request != undefined )
            {
                return request.cancel();
            }
            else {
                return false;
            }
        },

        cancelAllRequests:function()
        {
            var id, request;

            for( id in _requests )
            {
                this.cancelRequestByID( id );
            }
        }

    };

    document.xhrm = window.xhrm = xhrm;
    document.XHRM = window.XHRM = xhrm;
    document.XMLHttpRequestManager = window.XMLHttpRequestManager = xhrm;

})();

<|endoftext|>


<commit_before>var config = require('../config');
var eslintFriendlyFormatter = require('eslint-friendly-formatter');
var isProduction = process.env.NODE_ENV === 'production';
var path = require('path');
var utils = require('./utils');
var webpack = require('webpack');

function resolve (dir) {
    return path.join(__dirname, '..', dir);
};

module.exports = {
    entry: {
        app: './src/main.js',
    },
    output: {
        path: config.build.assetsRoot,
        filename: '[name].js',
        publicPath: isProduction
            ? config.build.assetsPublicPath
            : config.dev.assetsPublicPath,
    },
    resolve: {
        extensions: ['.js', '.vue', '.json', '.scss'],
        modules: [
            resolve('src'),
            resolve('scss'),
            resolve('node_modules'),
        ],
        alias: {
            'src': resolve('src'),
            'assets': resolve('src/assets'),
            'components': resolve('src/components'),
        },
    },
    module: {
        rules: [
            {
                test: /\.(js|vue)$/,
                loader: 'eslint-loader',
                enforce: "pre",
                include: [resolve('src'), resolve('test')],
                options: {
                    formatter: eslintFriendlyFormatter,
                },
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: utils.cssLoaders({
                        extract: isProduction,
                        sourceMap: isProduction
                            ? config.build.productionSourceMap
                            : config.dev.cssSourceMap,
                    }),
                    postcss: [
                        require('autoprefixer')({ browsers: ['last 2 versions'] }),
                    ],
                },
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('src'), resolve('test')],
            },
            {
                test: /\.json$/,
                loader: 'json-loader',
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                loader: 'url-loader',
                query: {
                    limit: 10000,
                    name: utils.assetsPath('img/[name].[hash:7].[ext]'),
                },
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader: 'url-loader',
                query: {
                    limit: 10000,
                    name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
                },
            },
        ],
    },
};
<commit_msg>Autofix js coding style<commit_after>var config = require('../config');
var eslintFriendlyFormatter = require('eslint-friendly-formatter');
var isProduction = process.env.NODE_ENV === 'production';
var path = require('path');
var utils = require('./utils');
var webpack = require('webpack');

function resolve (dir) {
    return path.join(__dirname, '..', dir);
};

module.exports = {
    entry: {
        app: './src/main.js',
    },
    output: {
        path: config.build.assetsRoot,
        filename: '[name].js',
        publicPath: isProduction
            ? config.build.assetsPublicPath
            : config.dev.assetsPublicPath,
    },
    resolve: {
        extensions: ['.js', '.vue', '.json', '.scss'],
        modules: [
            resolve('src'),
            resolve('scss'),
            resolve('node_modules'),
        ],
        alias: {
            'src': resolve('src'),
            'assets': resolve('src/assets'),
            'components': resolve('src/components'),
        },
    },
    module: {
        rules: [
            {
                test: /\.(js|vue)$/,
                loader: 'eslint-loader',
                enforce: "pre",
                include: [resolve('src'), resolve('test')],
                options: {
                    fix: true,
                    formatter: eslintFriendlyFormatter,
                },
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                options: {
                    loaders: utils.cssLoaders({
                        extract: isProduction,
                        sourceMap: isProduction
                            ? config.build.productionSourceMap
                            : config.dev.cssSourceMap,
                    }),
                    postcss: [
                        require('autoprefixer')({ browsers: ['last 2 versions'] }),
                    ],
                },
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: [resolve('src'), resolve('test')],
            },
            {
                test: /\.json$/,
                loader: 'json-loader',
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                loader: 'url-loader',
                query: {
                    limit: 10000,
                    name: utils.assetsPath('img/[name].[hash:7].[ext]'),
                },
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                loader: 'url-loader',
                query: {
                    limit: 10000,
                    name: utils.assetsPath('fonts/[name].[hash:7].[ext]'),
                },
            },
        ],
    },
};
<|endoftext|>


<commit_before>/**
 * @file MaterialLocalAutoComplete component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import LocalAutoComplete from '../LocalAutoComplete';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialLocalAutoComplete extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            filter: '',
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.popupClosedHandler = ::this.popupClosedHandler;
        this.triggerFilterChangeHandler = ::this.triggerFilterChangeHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;
        this.closePopup = ::this.closePopup;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    popupClosedHandler() {
        this.setState({
            isFocus: false
        });
    }

    triggerFilterChangeHandler(filter) {
        this.setState({
            filter
        }, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    closePopup() {
        this.refs.localAutoComplete && this.refs.localAutoComplete.closePopup();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimated, popupClassName,
                ...restProps
            } = this.props,
            {isFocus, isHover, value, filter} = this.state,

            wrapperClassName = (isLabelAnimated ? ' animated' : '') + (label ? ' has-label' : '')
                + (filter ? ' has-value' : '') + (isFocus ? ' focused' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <LocalAutoComplete {...restProps}
                                   ref="localAutoComplete"
                                   popupClassName={'material-local-auto-complete-popup ' + popupClassName}
                                   theme={theme}
                                   value={value}
                                   onFocus={this.triggerFocusHandler}
                                   onPopupClosed={this.popupClosedHandler}
                                   onTriggerMouseOver={this.triggerMouseOverHandler}
                                   onTriggerMouseOut={this.triggerMouseOutHandler}
                                   onFilterChange={this.triggerFilterChangeHandler}
                                   onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialLocalAutoComplete.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The name of the auto complete.
     */
    name: PropTypes.string,

    /**
     * The placeholder of the field.
     */
    placeholder: PropTypes.string,

    label: PropTypes.string,

    /**
     * Children passed into the List.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            itemRenderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * If true, the auto complete will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the popup list automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * Callback function fired when filter changed.
     */
    filterCallback: PropTypes.func,

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * The visiblity of no matched popup.
     */
    noMatchedPopupVisible: PropTypes.bool,

    /**
     * The message of no matched value.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * If true,the list data will be grouped.
     */
    isGrouped: PropTypes.bool,

    isLabelAnimated: PropTypes.bool,

    popupChildren: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The function that trigger when filter changes.
     */
    onFilterChange: PropTypes.func,

    /**
     * The function that trigger when press enter.
     */
    onFilterPressEnter: PropTypes.func,

    /**
     * The function that trigger when filter cleared.
     */
    onFilterClear: PropTypes.func,

    /**
     * The function that trigger when value changed.
     */
    onChange: PropTypes.func,

    /**
     * The function that trigger when touch-tap the list items.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete get focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete lose focus.
     */
    onBlur: PropTypes.func,

    onTriggerMouseOver: PropTypes.func,
    onTriggerMouseOut: PropTypes.func

};

MaterialLocalAutoComplete.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,
    theme: Theme.DEFAULT,

    name: '',
    placeholder: '',
    label: '',
    isLabelAnimated: true,
    data: [],
    disabled: false,
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    autoClose: false,
    iconCls: '',
    rightIconCls: 'fa fa-search',
    noMatchedPopupVisible: true,
    noMatchedMsg: '',
    isGrouped: false,

    popupChildren: null

};<commit_msg>update MaterialLocalAutoComplete<commit_after>/**
 * @file MaterialLocalAutoComplete component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import LocalAutoComplete from '../LocalAutoComplete';
import MaterialFieldSeparator from '../_MaterialFieldSeparator';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class MaterialLocalAutoComplete extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            value: '',
            filter: props.filterInitValue,
            isFocus: false,
            isHover: false
        };

        this.triggerFocusHandler = ::this.triggerFocusHandler;
        this.popupClosedHandler = ::this.popupClosedHandler;
        this.triggerFilterChangeHandler = ::this.triggerFilterChangeHandler;
        this.triggerChangeHandler = ::this.triggerChangeHandler;
        this.triggerMouseOverHandler = ::this.triggerMouseOverHandler;
        this.triggerMouseOutHandler = ::this.triggerMouseOutHandler;
        this.closePopup = ::this.closePopup;

    }

    triggerFocusHandler(...args) {
        this.setState({
            isFocus: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(...args);
        });
    }

    popupClosedHandler() {
        this.setState({
            isFocus: false
        });
    }

    triggerFilterChangeHandler(filter) {
        this.setState({
            filter
        }, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });
    }

    triggerChangeHandler(value) {
        this.setState({
            value
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });
    }

    triggerMouseOverHandler(...args) {
        this.setState({
            isHover: true
        }, () => {
            const {onMouseOver} = this.props;
            onMouseOver && onMouseOver(...args);
        });
    }

    triggerMouseOutHandler(...args) {
        this.setState({
            isHover: false
        }, () => {
            const {onMouseOut} = this.props;
            onMouseOut && onMouseOut(...args);
        });
    }

    closePopup() {
        this.refs.localAutoComplete && this.refs.localAutoComplete.closePopup();
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    componentDidMount() {
        this.setState({
            value: this.props.value
        });
    }

    render() {

        const {
                className, style, theme, label, isLabelAnimated, popupClassName,
                ...restProps
            } = this.props,
            {isFocus, isHover, value, filter} = this.state,

            wrapperClassName = (isLabelAnimated ? ' animated' : '') + (label ? ' has-label' : '')
                + (filter ? ' has-value' : '') + (isFocus ? ' focused' : '') + (className ? ' ' + className : '');

        return (

                {
                    label ?

                            {label}

                        :
                        null
                }

                <LocalAutoComplete {...restProps}
                                   ref="localAutoComplete"
                                   popupClassName={'material-local-auto-complete-popup ' + popupClassName}
                                   theme={theme}
                                   value={value}
                                   onFocus={this.triggerFocusHandler}
                                   onPopupClosed={this.popupClosedHandler}
                                   onTriggerMouseOver={this.triggerMouseOverHandler}
                                   onTriggerMouseOut={this.triggerMouseOutHandler}
                                   onFilterChange={this.triggerFilterChangeHandler}
                                   onChange={this.triggerChangeHandler}/>

                <MaterialFieldSeparator theme={theme}
                                        isHover={isHover}
                                        isFocus={isFocus}/>

        );

    }
};

MaterialLocalAutoComplete.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * The CSS class name of the popup element.
     */
    popupClassName: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * Override the styles of the popup element.
     */
    popupStyle: PropTypes.object,

    /**
     * The theme.
     */
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    /**
     * The name of the auto complete.
     */
    name: PropTypes.string,

    /**
     * The placeholder of the field.
     */
    placeholder: PropTypes.string,

    label: PropTypes.string,

    /**
     * Children passed into the List.
     */
    data: PropTypes.oneOfType([

        // not grouped
        PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

            /**
             * The CSS class name of the list button.
             */
            className: PropTypes.string,

            /**
             * Override the styles of the list button.
             */
            style: PropTypes.object,

            /**
             * The theme of the list button.
             */
            theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

            /**
             * The text value of the list button.Type can be string or number.
             */
            value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * The desc value of the list button. Type can be string or number.
             */
            desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

            /**
             * If true, the list button will be disabled.
             */
            disabled: PropTypes.bool,

            /**
             * If true,the button will be have loading effect.
             */
            isLoading: PropTypes.bool,

            /**
             * If true,the element's ripple effect will be disabled.
             */
            disableTouchRipple: PropTypes.bool,

            /**
             * Use this property to display an icon. It will display on the left.
             */
            iconCls: PropTypes.string,

            /**
             * Use this property to display an icon. It will display on the right.
             */
            rightIconCls: PropTypes.string,

            /**
             * You can create a complicated renderer callback instead of value and desc prop.
             */
            itemRenderer: PropTypes.func,

            /**
             * Callback function fired when a list item touch-tapped.
             */
            onTouchTap: PropTypes.func

        }), PropTypes.string, PropTypes.number])),

        // grouped
        PropTypes.array

    ]).isRequired,

    /**
     * If true, the auto complete will be disabled.
     */
    disabled: PropTypes.bool,

    /**
     * The value field name in data. (default: "value")
     */
    valueField: PropTypes.string,

    /**
     * The display field name in data. (default: "text")
     */
    displayField: PropTypes.string,

    /**
     * The description field name in data. (default: "desc")
     */
    descriptionField: PropTypes.string,

    /**
     * If true, the popup list automatically closed after selection.
     */
    autoClose: PropTypes.bool,

    /**
     * Callback function fired when filter changed.
     */
    filterCallback: PropTypes.func,

    /**
     * Use this property to display an icon.
     */
    iconCls: PropTypes.string,

    /**
     * Use this property to display an icon.
     */
    rightIconCls: PropTypes.string,

    /**
     * The visiblity of no matched popup.
     */
    noMatchedPopupVisible: PropTypes.bool,

    /**
     * The message of no matched value.
     */
    noMatchedMsg: PropTypes.string,

    /**
     * If true,the list data will be grouped.
     */
    isGrouped: PropTypes.bool,

    isLabelAnimated: PropTypes.bool,

    filterInitValue: PropTypes.string,

    popupChildren: PropTypes.any,

    /**
     * You can create a complicated renderer callback instead of value and desc prop.
     */
    renderer: PropTypes.func,

    /**
     * The function that trigger when filter changes.
     */
    onFilterChange: PropTypes.func,

    /**
     * The function that trigger when press enter.
     */
    onFilterPressEnter: PropTypes.func,

    /**
     * The function that trigger when filter cleared.
     */
    onFilterClear: PropTypes.func,

    /**
     * The function that trigger when value changed.
     */
    onChange: PropTypes.func,

    /**
     * The function that trigger when touch-tap the list items.
     */
    onItemTouchTap: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete get focus.
     */
    onFocus: PropTypes.func,

    /**
     * Callback function fired when LocalAutoComplete lose focus.
     */
    onBlur: PropTypes.func,

    onTriggerMouseOver: PropTypes.func,
    onTriggerMouseOut: PropTypes.func

};

MaterialLocalAutoComplete.defaultProps = {

    className: '',
    popupClassName: '',
    style: null,
    popupStyle: null,
    theme: Theme.DEFAULT,

    name: '',
    placeholder: '',
    label: '',
    isLabelAnimated: true,
    data: [],
    disabled: false,
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',
    autoClose: false,
    iconCls: '',
    rightIconCls: 'fa fa-search',
    noMatchedPopupVisible: true,
    noMatchedMsg: '',
    isGrouped: false,
    filterInitValue: '',

    popupChildren: null

};<|endoftext|>


<commit_before>var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src')
    }
  },
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}
<commit_msg>[webpack] add alias in webpack.config/resolve & add modules in webpack.config/resolve<commit_after>var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'store': resolve('src/app/config/store'),
      'App': resolve('src/app/screens/App')
    },
    modules: ['node_modules', 'shared']
  },
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: 'pre',
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}
<|endoftext|>


<commit_before>var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
var isRelease = process.env.NODE_ENV === 'release'

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    app: isRelease ? './src/main.js' : './src/dev.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: "pre",
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        exclude: [resolve('src/assets/icons/')],
        query: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}
<commit_msg><commit_after>var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
var isRelease = process.env.NODE_ENV === 'release'

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

module.exports = {
  entry: {
    app: isRelease ? './src/main.js' : './src/dev.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
    }
  },
  module: {
    rules: [
      {
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        enforce: "pre",
        include: [resolve('src'), resolve('test')],
        options: {
          formatter: require('eslint-friendly-formatter')
        }
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        query: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
}
<|endoftext|>