<commit_before>'use strict';

var Q = require('q');
var log = require('../../logger/index')('nodecg/lib/bundles/parser/npm');
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
var util = require('util');

exports = module.exports = function (bundle) {
    if (!bundle) return;

    var deferred = Q.defer();

    if (bundle.dependencies) {
        log.warn('Bundle "%s" has the "dependencies" property in its nodecg.json', bundle.name);
        log.warn('Per-bundle npm dependencies have been moved into package.json');
        log.warn('Please remove the "dependencies" field from this bundle\'s nodecg.json and ' +
        'move it into a package.json');
    }

    // Do nothing if package.json does not exist
    var packagejsonPath = path.join(bundle.dir, 'package.json');
    if (!fs.existsSync(packagejsonPath)) {
        log.trace('No npm dependencies to install for bundle', bundle.name);
        deferred.resolve();
        return deferred.promise;
    }

    exec('npm install', { cwd: bundle.dir }, function(err) {
        if (err) {
            deferred.reject(new Error(
                util.format('[%s] Failed to install npm dependencies:', bundle.name, err.message)
            ));
            return;
        }
        log.trace('Successfully installed npm dependencies for bundle', bundle.name);
        deferred.resolve();
    });

    return deferred.promise;
};
<commit_msg>add config boolean `asyncNpm` to control whether npm instances are spawned synchronously<commit_after>'use strict';

var Q = require('q');
var log = require('../../logger/index')('nodecg/lib/bundles/parser/npm');
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;
var execSync = require('child_process').execSync;
var util = require('util');
var os = require('os');
var format = require('util').format;
var config = require('../../config').getConfig();

exports = module.exports = function (bundle) {
    if (!bundle) return;

    var deferred = Q.defer();

    if (bundle.dependencies) {
        log.warn('Bundle "%s" has the "dependencies" property in its nodecg.json', bundle.name);
        log.warn('Per-bundle npm dependencies have been moved into package.json');
        log.warn('Please remove the "dependencies" field from this bundle\'s nodecg.json and ' +
        'move it into a package.json');
    }

    // Do nothing if package.json does not exist
    var packagejsonPath = path.join(bundle.dir, 'package.json');
    if (!fs.existsSync(packagejsonPath)) {
        log.trace('No npm dependencies to install for bundle', bundle.name);
        deferred.resolve();
        return deferred.promise;
    }

    if (config.asyncNpm === false) {
        try {
            process.stdout.write(format('Verifying/installing npm deps for bundle %s...', bundle.name));
            execSync('npm install', { cwd: bundle.dir, stdio: ['pipe', 'pipe', 'pipe'] });
            log.trace('Successfully installed npm dependencies for bundle', bundle.name);
            deferred.resolve();
            process.stdout.write(' done!' + os.EOL);
        } catch (e) {
            process.stdout.write(' failed!' + os.EOL);
            console.error(e.stack);
            deferred.reject(new Error(
                util.format('[%s] Failed to install npm dependencies:', bundle.name, e.message)
            ));
        }
    } else {
        exec('npm install', { cwd: bundle.dir }, function(err) {
            if (err) {
                deferred.reject(new Error(
                    util.format('[%s] Failed to install npm dependencies:', bundle.name, err.message)
                ));
                return;
            }
            log.trace('Successfully installed npm dependencies for bundle', bundle.name);
            deferred.resolve();
        });
    }

    return deferred.promise;
};
<|endoftext|>


<commit_before>const util = require('util')
const Stream = require('stream')
const sqlstring = require('../sqlstring')

const fmt = util.format.bind(util)

const JOIN_SEP = ':@:'

module.exports = {
  escape: function escape(value) {
    return sqlstring.escape(value)
  },
  escapeId: function escapeId(id) {
    return sqlstring.escapeId(id)
  },
  whereSql: function whereSql(table, conditions) {
    const hasConditons = conditions && keys(conditions).length
    if (!hasConditons) return ''

    const escape = this.escape
    const escapeId = this.escapeId
    var where = ' WHERE '
    var error

    var clauses = map(conditions, function (val, key) {
      const field = escapeId([table, key].join('.'))
      var cnd = conditions[key]

      if (Array.isArray(cnd)) {
        // if the condition is a simple array,
        // e.g { release_date: [2000,1996] }
        // use an `in` operator.
        if (typeof cnd[0] != 'object') {
          cnd = cnd.map(function (x) { return escape(x) })
          return fmt('%s IN (%s)', field, cnd.join(','))
        }

        return cnd.map(function (subcnd) {
          const operation = subcnd.op
          const value = escape(subcnd.value)
          return fmt('%s %s %s', field, operation, value)
        }).join(' AND ')
      }

      if (cnd !== null) {
        if (typeof cnd == 'undefined')
          return (error = new RangeError('Condition for `'+key+'` cannot be undefined'))

        const op = cnd.operation || cnd.op || '='
        if (cnd.value) cnd = cnd.value
        return fmt('%s %s %s', field, op, escape(cnd))
      }

      return fmt('%s IS NULL', field)
    })

    if (error) return error
    where += clauses.join(' AND ')
    return where
  },
  orderSql: function orderSql(table, order) {
    if (!order) return ''

    const escape = this.escape
    const escapeId = this.escapeId

    // order can be one of three styles:
    // * implicit ascending, single: 'title'
    // * implicit ascending, multi: ['release_date', 'title']
    // * explicit: { title: 'desc', release_date: 'asc' }

    if (typeof order == 'string') {
      return fmt(' ORDER BY %s ', escapeId(order))
    }

    if (Array.isArray(order))
      return fmt(' ORDER BY %s ', order.map(escapeId).join(','))

    // must be an object
    return fmt(' ORDER BY %s ', map(order, function (value, key) {
      return fmt('%s %s', escapeId(key), value.toUpperCase())
    }).join(','))
  },
  limitSql: function limitSql(limit, page) {
    if (!limit) return ''
    if (!page) return fmt(' LIMIT %s ', limit)
    const beginning = (page - 1) * limit
    return fmt(' LIMIT %s,%s ', beginning, limit)
  },

  selectSql: function selectSql(opts) {
    const escapeId = this.escapeId
    const escape = this.escape
    const table = opts.table
    const tables = opts.tables
    const fields = opts.fields
    const conds = opts.conditions
    const includeFields = opts.include
    const excludeFields = opts.exclude
    const relationships = opts.relationships
    const useRaw = Array.isArray(conds) || typeof conds === 'string'
    const useJoin = (relationships && keys(relationships).length)

    if (useRaw) return rawSql.call(this)
    if (useJoin) return withJoin.call(this)

    const fieldList = filterFields(fields, includeFields, excludeFields)
      .map(escapeId).join(',')

    const clauses = [
      fmt('SELECT %s FROM %s', fieldList, escapeId(table)),
      this.whereSql(table, conds),
      this.orderSql(table, opts.order),
      this.limitSql(opts.limit, opts.page)
    ]

    const error = firstErrorInArray(clauses)
    if (error) return error
    return clauses.join(' ')

    function rawSql() {
      const statementPair = typeof conds == 'string'
       ? [conds, []]
       : conds

      const sql = statementPair[0].replace(/\$table/i, escapeId(table))
      const values = statementPair[1].map(escape)
      const statement = values.reduce(function (sql, value) {
        return sql.replace('?', value)
      }, sql)
      return statement
    }

    function withJoin() {
      var allFields = fields
        .slice().map(function (field) {
          return [table, field].join('.')
        })

      var joinString = ''

      this.normalizeRelationships(table, relationships)

      function addRelationships (relationships, tableAlias) {
        // in the loop below, rel -->
        // { 'type': <string>,
        //   'local': <{table, key}>,
        //   'foreign': <{table, key, [as]}>,
        //   'relationships': <{name*: rel}>,
        //   'optional': <null | boolean>  }

        forEach(relationships, function (rel, name) {
          if (rel.type == 'hasMany') return
          var foreignAs = name
          var localTable = rel.local.table

          rel.foreign.as = tableAlias + JOIN_SEP + foreignAs
          rel.local.table = tableAlias

          allFields = allFields.concat(getFields(rel.foreign, tables))

          joinString += this.joinSql(rel)

          if (rel.relationships)
            addRelationships.call(this, rel.relationships, tableAlias + JOIN_SEP + name)

          rel.foreign.as = foreignAs
          rel.local.table = localTable
        }.bind(this))
      }

      addRelationships.call(this, relationships, table)

      const selectFields =
        filterFields(allFields, includeFields, excludeFields)

      const joinClause =
        fmt('SELECT %s FROM %s %s',
        selectAs(selectFields),
        escapeId(table),
        joinString)

      const clauses = [
        joinClause,
        this.whereSql(table, conds),
        this.orderSql(table, opts.order),
        this.limitSql(opts.limit, opts.page),
      ]

      const error = firstErrorInArray(clauses)
      if (error) return error
      return clauses.join(' ')
    }

    function selectAs(fields) {
      return fields.map(function (field) {
        return fmt(
          '%s AS %s',
          escapeId(field),
          escapeId(field.replace('.', JOIN_SEP)))
      }).join(',')
    }

    function getFields(tableWithAlias, tables) {
      // XXX: probably shouldn't throw
      const name = tableWithAlias.table
      const alias = tableWithAlias.as || name
      const tableDef = tables[name]
      if (!tableDef)
        throw new Error(fmt('table %s is not registered', escapeId(name)))
      return tableDef.fields.map(function (field) {
        return [alias, field].join('.')
      })
    }

  },
  parseJoinRow: function (row, table) {
    const fixed = {}

    forEach(row, function (value, key) {
      const parts = key.split(JOIN_SEP)
      const property = parts.pop()
      var target = fixed
      forEach(parts, function (part) {
        target = (target[part] || (target[part] = {}))
      })
      target[property] = value
    });

    return fixed[table] || {}
  },
  fixHasOne: function (row, opts) {
    const table = opts.table
    const relationships = opts.relationships
    const tableCache = opts.tableCache
    row = this.parseJoinRow(row, table)

    fixRelationships(row, relationships)

    function fixRelationships (target, relationships) {
      forEach(relationships, function (rel, relKey) {
        if (rel.type !== 'hasOne') return
        const foreign = tableCache[rel.foreign.table]
        target[relKey] = new foreign.constructor(target[relKey])

        if (rel.relationships)
          fixRelationships(target[relKey], rel.relationships)
      })
    }

    return row
  },

  fixHasMany: function (globalRow, opts, callback) {
    var globalError
    const table = opts.table
    const tableCache = opts.tableCache
    const hasMany = {}

    findRelationships(opts.relationships)

    if (!keys(hasMany).length)
      return deferCallback(callback, null, globalRow)

    this.normalizeRelationships(table, hasMany)

    var waiting = keys(hasMany).length

    forEach(hasMany, function (rel, relKey) {
      if (globalError) return

      function handleResult (err, rows) {
        if (globalError) return
        if (err) {
          globalError = err
          return callback(err, null)
        }

        const parts = relKey.split('.')
        const property = parts.pop()
        const context = parts.reduce(function (context, part) {
          if (!context[part])
            context[part] = []
          return context[part]
        }, globalRow);

        context[property] = rows.map(function (row) {
          return new rel.foreign.constructor(row)
        })

        return checkDone()
      }

      (rel.via ? fetchVia : fetchDirect)(rel, handleResult);
    })

    function fetchVia (rel, callback) {
      const cond = {}
      const table = tableCache[rel.via.table]
      const options = {relationships: {}}

      options.relationships[rel.foreign.table] = {
        type: 'hasOne',
        local: rel.via.foreign,
        foreign: rel.foreign
      }

      cond[rel.via.local] = globalRow[rel.local.key]

      table.get(cond, options, function (err, rows) {
        if (err)
          return callback(err);

        return callback(null, rows.map(function (row) {
          return row[rel.foreign.table];
        }));
      })
    }

    function fetchDirect (rel, callback) {
      const cond = {}
      const table = tableCache[rel.foreign.table]
      const options = {relationships: rel.relationships || {}}

      cond[rel.foreign.key] = globalRow[rel.local.key]

      table.get(cond, options, callback)
    }

    function findRelationships (relationships, prefix) {
      prefix = prefix || ''

      forEach(relationships, function (rel, relKey) {
        if (rel.type == 'hasMany')
          hasMany[prefix + relKey] = rel
        if (rel.relationships)
          findRelationships(rel.relationships, prefix + relKey + '.')
      })
    }

    function checkDone() {
      if (globalError) return
      if (--waiting > 0) return
      return callback(null, globalRow)
    }
  },
  hydrateRow: function (row, opts, callback) {
    if (!row || isEmpty(opts.relationships))
      return deferCallback(callback, null, row)

    row = this.fixHasOne(row, {
      table: opts.table,
      relationships: opts.relationships,
      tableCache: opts.tableCache,
    })

    this.fixHasMany(row, {
      table: opts.table,
      relationships: opts.relationships,
      tableCache: opts.tableCache,
    }, callback)
  },
  hydrateRows: function (rows, opts, callback) {
    const results = []
    var waiting = rows.length
    var globalError

    if (!rows.length || isEmpty(opts.relationships))
      return deferCallback(callback, null, rows)

    rows.forEach(function (row, idx) {
      this.hydrateRow(row, opts, function (err, result) {
        if (globalError) return
        if (err) {
          globalError = err
          return callback(err)
        }
        storeResult(result, idx)
      })
    }, this)

    function storeResult(result, pos) {
      results[pos] = result
      if (--waiting > 0) return
      callback(null, results)
    }
  },
  streamHandlers: function (stream, opts) {
    var waiting = 0
    var finishing = false

    const driver = this
    const table = opts.table
    const relationships = opts.relationships
    const tableCache = opts.tableCache
    const RowClass = tableCache[table].constructor

    const withRelationships =
      (relationships && keys(relationships).length)

    function onResult(row) {
      if (!withRelationships) {
        return stream.emit('data', new RowClass(row))
      }

      waiting ++

      // fulfills both hasOne and hasMany relationships
      driver.hydrateRow(row, {
        table: table,
        relationships: relationships,
        tableCache: tableCache,
      }, function (err, row) {
        waiting --
        if (err) return stream.emit(err)
        stream.emit('data', new RowClass(row))
        checkDone()
      })
    }

    function endCallback(err) {
      if (err) return stream.emit('error', err)
      if (waiting > 0) {
        finishing = true
        return false
      }
      stream.emit('end')
    }

    function checkDone() {
      if (finishing && !waiting)
        stream.emit('end')
    }

    return { row: onResult, done: endCallback }

  },
  normalizeRelationships: function (table, relationships) {
    forEach(relationships, function (rel, relKey) {
      if (!rel.local)
        rel.local = { table: table, key: relKey }

      if (typeof rel.local === 'string')
        rel.local = { table: table, key: rel.local }

      if (rel.relationships)
        this.normalizeRelationships(rel.foreign.table, rel.relationships)
    }.bind(this))
    return relationships
  },

  joinSql: function (opts) {
    // opts --> <see `withJoin` above>
    const local = opts.local
    const foreign = opts.foreign
    const joinType = opts.optional ? ' LEFT ' : ' INNER '
    const localKey = fmt('%s.%s', local.table, local.key)
    const foreignTableAlias = foreign.as || foreign.table
    const foreignKey = fmt('%s.%s', foreignTableAlias, foreign.key)

    const statement = joinType
      + ' JOIN ' + this.escapeId(foreign.table)
      + ' AS '   + this.escapeId(foreignTableAlias)
      + ' ON '   + this.escapeId(localKey)
      + ' = '    + this.escapeId(foreignKey)

    return statement
  },
  insertSql: function insertSql(table, row) {
    const columns = keys(row)
    const values = vals(row)

    const tpl = 'INSERT INTO %s (%s) VALUES (%s)'
    return fmt(tpl, this.escapeId(table),
               map(columns, this.escapeId),
               map(values, this.escape))
  },
  updateSql: function updateSql(table, row, conds) {
    const escape = this.escape
    const escapeId = this.escapeId
    const pairs = map(row, function (val, col) {
      return fmt('%s = %s', escapeId(col), escape(val))
    })
    return fmt('UPDATE %s SET %s %s',
               escapeId(table),
               pairs.join(','),
               this.whereSql(table, conds))
  },
  deleteSql: function deleteSql(opts) {
    const table = opts.table
    const conds = opts.conditions
    const limit = opts.limit

    const clauses = [
      fmt('DELETE FROM %s', this.escapeId(table)),
      this.whereSql(table, conds),
      this.limitSql(limit),
    ]

    const error = firstErrorInArray(clauses)
    if (error) return error
    return clauses.join(' ')
  },
}

function appearsLast(string, sub) {
  const index = string.indexOf(sub)
  if (index == -1) return false
  return string.slice(index) == sub
}

function filterFields(fields, includeFields, excludeFields) {
  if (!includeFields && !excludeFields)
    return fields

  // we always include ids because they are needed for proper
  // relationship fulfillment
  if (includeFields)
    includeFields.push('id')

  return fields.filter(function (field) {
    function search(needle) {
      return appearsLast(field, needle)
    }
    if (includeFields)
      return includeFields.some(search)
    if (excludeFields)
      return !excludeFields.some(search)
    return field
  })
}

function map(obj, fn) {
  return Object.keys(obj).map(function (key) {
    return fn(obj[key], key)
  })
}

function forEach(obj, fn) {
  return Object.keys(obj).forEach(function (key) {
    fn(obj[key], key)
  })
}

function keys(o) {
  return Object.keys(o)
}

function vals(o) {
  return map(o, function (val) { return val })
}

function isEmpty(obj) {
  return (!obj || !keys(obj).length)
}

function deferCallback(callback, err, arg) {
  return setImmediate(function () {
    callback(null, arg)
  })
}

const errorRegExp = /Error$/
function isError(thing) {
  return (typeof thing == 'object' && errorRegExp.exec(thing.name))
}

function firstErrorInArray(array) {
  for (var idx = 0, item; idx < array.length; idx++) {
    item = array[idx]
    if (isError(item)) return item
  }
  return null
}
<commit_msg>Ensuring relationships are carried through<commit_after>const util = require('util')
const Stream = require('stream')
const sqlstring = require('../sqlstring')

const fmt = util.format.bind(util)

const JOIN_SEP = ':@:'

module.exports = {
  escape: function escape(value) {
    return sqlstring.escape(value)
  },
  escapeId: function escapeId(id) {
    return sqlstring.escapeId(id)
  },
  whereSql: function whereSql(table, conditions) {
    const hasConditons = conditions && keys(conditions).length
    if (!hasConditons) return ''

    const escape = this.escape
    const escapeId = this.escapeId
    var where = ' WHERE '
    var error

    var clauses = map(conditions, function (val, key) {
      const field = escapeId([table, key].join('.'))
      var cnd = conditions[key]

      if (Array.isArray(cnd)) {
        // if the condition is a simple array,
        // e.g { release_date: [2000,1996] }
        // use an `in` operator.
        if (typeof cnd[0] != 'object') {
          cnd = cnd.map(function (x) { return escape(x) })
          return fmt('%s IN (%s)', field, cnd.join(','))
        }

        return cnd.map(function (subcnd) {
          const operation = subcnd.op
          const value = escape(subcnd.value)
          return fmt('%s %s %s', field, operation, value)
        }).join(' AND ')
      }

      if (cnd !== null) {
        if (typeof cnd == 'undefined')
          return (error = new RangeError('Condition for `'+key+'` cannot be undefined'))

        const op = cnd.operation || cnd.op || '='
        if (cnd.value) cnd = cnd.value
        return fmt('%s %s %s', field, op, escape(cnd))
      }

      return fmt('%s IS NULL', field)
    })

    if (error) return error
    where += clauses.join(' AND ')
    return where
  },
  orderSql: function orderSql(table, order) {
    if (!order) return ''

    const escape = this.escape
    const escapeId = this.escapeId

    // order can be one of three styles:
    // * implicit ascending, single: 'title'
    // * implicit ascending, multi: ['release_date', 'title']
    // * explicit: { title: 'desc', release_date: 'asc' }

    if (typeof order == 'string') {
      return fmt(' ORDER BY %s ', escapeId(order))
    }

    if (Array.isArray(order))
      return fmt(' ORDER BY %s ', order.map(escapeId).join(','))

    // must be an object
    return fmt(' ORDER BY %s ', map(order, function (value, key) {
      return fmt('%s %s', escapeId(key), value.toUpperCase())
    }).join(','))
  },
  limitSql: function limitSql(limit, page) {
    if (!limit) return ''
    if (!page) return fmt(' LIMIT %s ', limit)
    const beginning = (page - 1) * limit
    return fmt(' LIMIT %s,%s ', beginning, limit)
  },

  selectSql: function selectSql(opts) {
    const escapeId = this.escapeId
    const escape = this.escape
    const table = opts.table
    const tables = opts.tables
    const fields = opts.fields
    const conds = opts.conditions
    const includeFields = opts.include
    const excludeFields = opts.exclude
    const relationships = opts.relationships
    const useRaw = Array.isArray(conds) || typeof conds === 'string'
    const useJoin = (relationships && keys(relationships).length)

    if (useRaw) return rawSql.call(this)
    if (useJoin) return withJoin.call(this)

    const fieldList = filterFields(fields, includeFields, excludeFields)
      .map(escapeId).join(',')

    const clauses = [
      fmt('SELECT %s FROM %s', fieldList, escapeId(table)),
      this.whereSql(table, conds),
      this.orderSql(table, opts.order),
      this.limitSql(opts.limit, opts.page)
    ]

    const error = firstErrorInArray(clauses)
    if (error) return error
    return clauses.join(' ')

    function rawSql() {
      const statementPair = typeof conds == 'string'
       ? [conds, []]
       : conds

      const sql = statementPair[0].replace(/\$table/i, escapeId(table))
      const values = statementPair[1].map(escape)
      const statement = values.reduce(function (sql, value) {
        return sql.replace('?', value)
      }, sql)
      return statement
    }

    function withJoin() {
      var allFields = fields
        .slice().map(function (field) {
          return [table, field].join('.')
        })

      var joinString = ''

      this.normalizeRelationships(table, relationships)

      function addRelationships (relationships, tableAlias) {
        // in the loop below, rel -->
        // { 'type': <string>,
        //   'local': <{table, key}>,
        //   'foreign': <{table, key, [as]}>,
        //   'relationships': <{name*: rel}>,
        //   'optional': <null | boolean>  }

        forEach(relationships, function (rel, name) {
          if (rel.type == 'hasMany') return
          var foreignAs = name
          var localTable = rel.local.table

          rel.foreign.as = tableAlias + JOIN_SEP + foreignAs
          rel.local.table = tableAlias

          allFields = allFields.concat(getFields(rel.foreign, tables))

          joinString += this.joinSql(rel)

          if (rel.relationships)
            addRelationships.call(this, rel.relationships, tableAlias + JOIN_SEP + name)

          rel.foreign.as = foreignAs
          rel.local.table = localTable
        }.bind(this))
      }

      addRelationships.call(this, relationships, table)

      const selectFields =
        filterFields(allFields, includeFields, excludeFields)

      const joinClause =
        fmt('SELECT %s FROM %s %s',
        selectAs(selectFields),
        escapeId(table),
        joinString)

      const clauses = [
        joinClause,
        this.whereSql(table, conds),
        this.orderSql(table, opts.order),
        this.limitSql(opts.limit, opts.page),
      ]

      const error = firstErrorInArray(clauses)
      if (error) return error
      return clauses.join(' ')
    }

    function selectAs(fields) {
      return fields.map(function (field) {
        return fmt(
          '%s AS %s',
          escapeId(field),
          escapeId(field.replace('.', JOIN_SEP)))
      }).join(',')
    }

    function getFields(tableWithAlias, tables) {
      // XXX: probably shouldn't throw
      const name = tableWithAlias.table
      const alias = tableWithAlias.as || name
      const tableDef = tables[name]
      if (!tableDef)
        throw new Error(fmt('table %s is not registered', escapeId(name)))
      return tableDef.fields.map(function (field) {
        return [alias, field].join('.')
      })
    }

  },
  parseJoinRow: function (row, table) {
    const fixed = {}

    forEach(row, function (value, key) {
      const parts = key.split(JOIN_SEP)
      const property = parts.pop()
      var target = fixed
      forEach(parts, function (part) {
        target = (target[part] || (target[part] = {}))
      })
      target[property] = value
    });

    return fixed[table] || {}
  },
  fixHasOne: function (row, opts) {
    const table = opts.table
    const relationships = opts.relationships
    const tableCache = opts.tableCache
    row = this.parseJoinRow(row, table)

    fixRelationships(row, relationships)

    function fixRelationships (target, relationships) {
      forEach(relationships, function (rel, relKey) {
        if (rel.type !== 'hasOne') return
        const foreign = tableCache[rel.foreign.table]
        target[relKey] = new foreign.constructor(target[relKey])

        if (rel.relationships)
          fixRelationships(target[relKey], rel.relationships)
      })
    }

    return row
  },

  fixHasMany: function (globalRow, opts, callback) {
    var globalError
    const table = opts.table
    const tableCache = opts.tableCache
    const hasMany = {}

    findRelationships(opts.relationships)

    if (!keys(hasMany).length)
      return deferCallback(callback, null, globalRow)

    this.normalizeRelationships(table, hasMany)

    var waiting = keys(hasMany).length

    forEach(hasMany, function (rel, relKey) {
      if (globalError) return

      function handleResult (err, rows) {
        if (globalError) return
        if (err) {
          globalError = err
          return callback(err, null)
        }

        const parts = relKey.split('.')
        const property = parts.pop()
        const context = parts.reduce(function (context, part) {
          if (!context[part])
            context[part] = []
          return context[part]
        }, globalRow);

        context[property] = rows.map(function (row) {
          return new rel.foreign.constructor(row)
        })

        return checkDone()
      }

      (rel.via ? fetchVia : fetchDirect)(rel, handleResult);
    })

    function fetchVia (rel, callback) {
      const cond = {}
      const table = tableCache[rel.via.table]
      const options = {relationships: {}}

      options.relationships[rel.foreign.table] = {
        type: 'hasOne',
        local: rel.via.foreign,
        foreign: rel.foreign,
        relationships: rel.relationships || {}
      }

      cond[rel.via.local] = globalRow[rel.local.key]

      table.get(cond, options, function (err, rows) {
        if (err)
          return callback(err);

        return callback(null, rows.map(function (row) {
          return row[rel.foreign.table];
        }));
      })
    }

    function fetchDirect (rel, callback) {
      const cond = {}
      const table = tableCache[rel.foreign.table]
      const options = {relationships: rel.relationships || {}}

      cond[rel.foreign.key] = globalRow[rel.local.key]

      table.get(cond, options, callback)
    }

    function findRelationships (relationships, prefix) {
      prefix = prefix || ''

      forEach(relationships, function (rel, relKey) {
        if (rel.type == 'hasMany')
          hasMany[prefix + relKey] = rel
        if (rel.relationships)
          findRelationships(rel.relationships, prefix + relKey + '.')
      })
    }

    function checkDone() {
      if (globalError) return
      if (--waiting > 0) return
      return callback(null, globalRow)
    }
  },
  hydrateRow: function (row, opts, callback) {
    if (!row || isEmpty(opts.relationships))
      return deferCallback(callback, null, row)

    row = this.fixHasOne(row, {
      table: opts.table,
      relationships: opts.relationships,
      tableCache: opts.tableCache,
    })

    this.fixHasMany(row, {
      table: opts.table,
      relationships: opts.relationships,
      tableCache: opts.tableCache,
    }, callback)
  },
  hydrateRows: function (rows, opts, callback) {
    const results = []
    var waiting = rows.length
    var globalError

    if (!rows.length || isEmpty(opts.relationships))
      return deferCallback(callback, null, rows)

    rows.forEach(function (row, idx) {
      this.hydrateRow(row, opts, function (err, result) {
        if (globalError) return
        if (err) {
          globalError = err
          return callback(err)
        }
        storeResult(result, idx)
      })
    }, this)

    function storeResult(result, pos) {
      results[pos] = result
      if (--waiting > 0) return
      callback(null, results)
    }
  },
  streamHandlers: function (stream, opts) {
    var waiting = 0
    var finishing = false

    const driver = this
    const table = opts.table
    const relationships = opts.relationships
    const tableCache = opts.tableCache
    const RowClass = tableCache[table].constructor

    const withRelationships =
      (relationships && keys(relationships).length)

    function onResult(row) {
      if (!withRelationships) {
        return stream.emit('data', new RowClass(row))
      }

      waiting ++

      // fulfills both hasOne and hasMany relationships
      driver.hydrateRow(row, {
        table: table,
        relationships: relationships,
        tableCache: tableCache,
      }, function (err, row) {
        waiting --
        if (err) return stream.emit(err)
        stream.emit('data', new RowClass(row))
        checkDone()
      })
    }

    function endCallback(err) {
      if (err) return stream.emit('error', err)
      if (waiting > 0) {
        finishing = true
        return false
      }
      stream.emit('end')
    }

    function checkDone() {
      if (finishing && !waiting)
        stream.emit('end')
    }

    return { row: onResult, done: endCallback }

  },
  normalizeRelationships: function (table, relationships) {
    forEach(relationships, function (rel, relKey) {
      if (!rel.local)
        rel.local = { table: table, key: relKey }

      if (typeof rel.local === 'string')
        rel.local = { table: table, key: rel.local }

      if (rel.relationships)
        this.normalizeRelationships(rel.foreign.table, rel.relationships)
    }.bind(this))
    return relationships
  },

  joinSql: function (opts) {
    // opts --> <see `withJoin` above>
    const local = opts.local
    const foreign = opts.foreign
    const joinType = opts.optional ? ' LEFT ' : ' INNER '
    const localKey = fmt('%s.%s', local.table, local.key)
    const foreignTableAlias = foreign.as || foreign.table
    const foreignKey = fmt('%s.%s', foreignTableAlias, foreign.key)

    const statement = joinType
      + ' JOIN ' + this.escapeId(foreign.table)
      + ' AS '   + this.escapeId(foreignTableAlias)
      + ' ON '   + this.escapeId(localKey)
      + ' = '    + this.escapeId(foreignKey)

    return statement
  },
  insertSql: function insertSql(table, row) {
    const columns = keys(row)
    const values = vals(row)

    const tpl = 'INSERT INTO %s (%s) VALUES (%s)'
    return fmt(tpl, this.escapeId(table),
               map(columns, this.escapeId),
               map(values, this.escape))
  },
  updateSql: function updateSql(table, row, conds) {
    const escape = this.escape
    const escapeId = this.escapeId
    const pairs = map(row, function (val, col) {
      return fmt('%s = %s', escapeId(col), escape(val))
    })
    return fmt('UPDATE %s SET %s %s',
               escapeId(table),
               pairs.join(','),
               this.whereSql(table, conds))
  },
  deleteSql: function deleteSql(opts) {
    const table = opts.table
    const conds = opts.conditions
    const limit = opts.limit

    const clauses = [
      fmt('DELETE FROM %s', this.escapeId(table)),
      this.whereSql(table, conds),
      this.limitSql(limit),
    ]

    const error = firstErrorInArray(clauses)
    if (error) return error
    return clauses.join(' ')
  },
}

function appearsLast(string, sub) {
  const index = string.indexOf(sub)
  if (index == -1) return false
  return string.slice(index) == sub
}

function filterFields(fields, includeFields, excludeFields) {
  if (!includeFields && !excludeFields)
    return fields

  // we always include ids because they are needed for proper
  // relationship fulfillment
  if (includeFields)
    includeFields.push('id')

  return fields.filter(function (field) {
    function search(needle) {
      return appearsLast(field, needle)
    }
    if (includeFields)
      return includeFields.some(search)
    if (excludeFields)
      return !excludeFields.some(search)
    return field
  })
}

function map(obj, fn) {
  return Object.keys(obj).map(function (key) {
    return fn(obj[key], key)
  })
}

function forEach(obj, fn) {
  return Object.keys(obj).forEach(function (key) {
    fn(obj[key], key)
  })
}

function keys(o) {
  return Object.keys(o)
}

function vals(o) {
  return map(o, function (val) { return val })
}

function isEmpty(obj) {
  return (!obj || !keys(obj).length)
}

function deferCallback(callback, err, arg) {
  return setImmediate(function () {
    callback(null, arg)
  })
}

const errorRegExp = /Error$/
function isError(thing) {
  return (typeof thing == 'object' && errorRegExp.exec(thing.name))
}

function firstErrorInArray(array) {
  for (var idx = 0, item; idx < array.length; idx++) {
    item = array[idx]
    if (isError(item)) return item
  }
  return null
}
<|endoftext|>


<commit_before>'use strict'; // eslint-disable-line strict

const os = require('os');
const fsh = require('../lib/fsh');
const fs = require('fs');
const path = require('path');

describe('fsh', () => {
  it('dirExistsSync', () => {
    const tempFolder1 = fs.mkdtempSync(os.tmpdir() + path.sep);
    const tempFile1 = path.resolve(tempFolder1, 'delete.me');
    fs.writeFileSync(tempFile1, 'Hello, world');
    expect(fsh.dirExistsSync(tempFile1)).toBe(false);
    expect(fsh.dirExistsSync(tempFolder1)).toBe(true);
    fs.unlinkSync(tempFile1);
    fs.rmdirSync(tempFolder1);
    expect(fsh.dirExistsSync(tempFolder1)).toBe(false);
  });

  it('fileExistsSync', () => {
    const tempFolder2 = fs.mkdtempSync(os.tmpdir() + path.sep);
    const tempFile2 = path.resolve(tempFolder2, 'delete.me');
    fs.writeFileSync(tempFile2, 'Hello, world');
    expect(fsh.fileExistsSync(tempFile2)).toBe(true);
    expect(fsh.fileExistsSync(tempFolder2)).toBe(false);
    fs.unlinkSync(tempFile2);
    fs.rmdirSync(tempFolder2);
    expect(fsh.fileExistsSync(tempFile2)).toBe(false);
  });
});
<commit_msg>minor tweaks<commit_after>'use strict'; // eslint-disable-line strict

const fsh = require('../lib/fsh');
const os = require('os');
const fs = require('fs');
const path = require('path');

describe('fsh (fs helper):', () => {
  it('dirExistsSync', () => {
    const tempFolder1 = fs.mkdtempSync(os.tmpdir() + path.sep);
    const tempFile1 = path.resolve(tempFolder1, 'delete.me');
    fs.writeFileSync(tempFile1, 'Hello, world');
    expect(fsh.dirExistsSync(tempFile1)).toBe(false);
    expect(fsh.dirExistsSync(tempFolder1)).toBe(true);
    fs.unlinkSync(tempFile1);
    fs.rmdirSync(tempFolder1);
    expect(fsh.dirExistsSync(tempFolder1)).toBe(false);
  });

  it('fileExistsSync', () => {
    const tempFolder2 = fs.mkdtempSync(os.tmpdir() + path.sep);
    const tempFile2 = path.resolve(tempFolder2, 'delete.me');
    fs.writeFileSync(tempFile2, 'Hello, world');
    expect(fsh.fileExistsSync(tempFile2)).toBe(true);
    expect(fsh.fileExistsSync(tempFolder2)).toBe(false);
    fs.unlinkSync(tempFile2);
    expect(fsh.fileExistsSync(tempFile2)).toBe(false);
    fs.rmdirSync(tempFolder2);
  });
});
<|endoftext|>


<commit_before><commit_msg>Add "is functional variable" mutation to functional procedures<commit_after><|endoftext|>


<commit_before>/* ============================================================
 * bootstrap-tokenfield.js v0.6
 * ============================================================
 *
 * Copyright 2013 Sliptree
 * ============================================================ */

!function ($) {

  "use strict"; // jshint ;_;

 /* TOKENFIELD PUBLIC CLASS DEFINITION
  * ============================== */

  var Tokenfield = function (element, options) {
    var _self = this

    this.$input = $(element).addClass('token-input')
    this.$element = $('
')
    this.$helper = $('<textarea class="token-helper" tabindex="-1" style="position: absolute; left: -10000px;" />').appendTo(this.$element)
    this.$mirror = $('');

    this.options = $.extend({}, $.fn.tokenfield.defaults, { tokens: this.$input.data('tokens') }, options)

    // Copy some styles
    this.$element.width( this.$input.width() )

    // Set up mirror for input auto-sizing
    this.$input.css('min-width', this.options.minWidth + 'px')
    $.each([
        'fontFamily',
        'fontSize',
        'fontWeight',
        'fontStyle',
        'letterSpacing',
        'textTransform',
        'wordSpacing',
        'textIndent'
    ], function (i, val) {
        _self.$mirror[0].style[val] = _self.$input.css(val);
    });
    this.$mirror.appendTo( 'body' )

    // Insert tokenfield to HTML
    this.$element.insertBefore( this.$input )
    this.$input.appendTo( this.$element )

    this.setTokens(this.options.tokens)

    this.listen()
  }

  Tokenfield.prototype = {

    constructor: Tokenfield

  , createToken: function (attrs) {
      if (typeof attrs === 'string') {
        attrs = { value: attrs, label: attrs }
      }

      var _self = this
        , value = $.trim(attrs.value)
        , label = attrs.label.length ? $.trim(attrs.label) : value

      if (!value.length || !label.length || value.length < this.options.minLength) return

      var token = $('
')
            .attr('data-value', value)
            .append('')
            .append('×')

      this.$input.before( token )
      this.$input.css('width', this.options.minWidth + 'px')

      var tokenLabel = token.find('.token-label')
        , closeButton = token.find('.close')

      // Determine maximum possible token label width
      if (!this.maxTokenWidth) {
        this.maxTokenWidth =
          this.$element.width() - closeButton.outerWidth() -
          parseInt(closeButton.css('margin-left')) -
          parseInt(closeButton.css('margin-right')) -
          parseInt(token.css('border-left-width')) -
          parseInt(token.css('border-right-width')) -
          parseInt(token.css('padding-left')) -
          parseInt(token.css('padding-right'))
          parseInt(tokenLabel.css('border-left-width')) -
          parseInt(tokenLabel.css('border-right-width')) -
          parseInt(tokenLabel.css('padding-left')) -
          parseInt(tokenLabel.css('padding-right'))
          parseInt(tokenLabel.css('margin-left')) -
          parseInt(tokenLabel.css('margin-right'))
      }

      tokenLabel
        .text(label)
        .css('max-width', this.maxTokenWidth)

      // Listen to events
      token
        .on('click',    function (e) {
          _self.activate( token )
        })
        .on('dblclick', function (e) {
          _self.edit( token )
        })

      closeButton
          .on('click',  $.proxy(this.remove, this))

      this.update()

      return this.$input.get(0)
    }

  , setTokens: function (tokens, add) {
      if (!tokens) return

      if (!add) this.$element.find('.token').remove()

      if (typeof tokens === 'string') {
        tokens = tokens.split(',')
      }

      var _self = this
      $.each(tokens, function (i, token) {
        _self.createToken(token)
      })

      return this.$input.get(0)
    }

  , getTokens: function() {
      var tokens = []
      this.$element.find('.token').each( function() {
        tokens.push({
          value: $(this).data('value') || $(this).find('.token-label').text(),
          label: $(this).find('.token-label').text()
        })
      })
      return tokens
  }

  , getTokensList: function() {
      return $.map( this.getTokens(), function (token) {
        return token.value
      }).join(', ')
  }

  , listen: function () {
      this.$element
        .on('click',    $.proxy(this.focusInput, this))

      this.$input
        .on('focus',    $.proxy(this.focus, this))
        .on('blur',     $.proxy(this.blur, this))
        .on('paste',    $.proxy(this.paste, this))
        .on('keydown',  $.proxy(this.keydown, this))
        .on('keyup',    $.proxy(this.keyup, this))

      this.$helper
        .on('focus',    $.proxy(this.focus, this))
        .on('blur',     $.proxy(this.blur, this))
        .on('keydown',  $.proxy(this.keydown, this))
        .on('keyup',    $.proxy(this.keyup, this))

      // Secondary listeners for input width calculation
      this.$input.on('keydown, keypress, keyup',  $.proxy(this.update, this))
    }

  , keydown: function (e) {

      if (!this.focused) return

      this.lastKeyDown = e.keyCode

      switch(e.keyCode) {
        case 8: // backspace
          if (!this.$input.is(':focus')) return
          this.lastInputValue = this.$input.val()
          break

        case 37: // left arrow
          if (this.$input.is(':focus')) {
            if (this.$input.val().length > 0) return
            this.activate( this.$input.prev('.token') )
            e.preventDefault()
          } else {
            this.prev()
            e.preventDefault()
          }
          break

        case 39: // right arrow
          if (this.$input.is(':focus')) return
          this.next()
          e.preventDefault()
          break

        case 65: // a (to handle ctrl + a)
          if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) return
          this.activateAll()
          e.preventDefault()
          break

        case 9: // tab
        case 13: // enter
        case 188: // comma
          if (this.$input.is(':focus') && this.$input.val()) {
            this.createTokensFromInput(e)
          }
          if (e.keyCode === 13) {
            if (!this.$helper.is(':focus') || this.$element.find('.token.active').length !== 1) return
            this.edit( this.$element.find('.token.active') )
          }
      }
    }

  , keyup: function (e) {
      if (!this.focused) return
      switch(e.keyCode) {
        case 8: // backspace
          if (this.$input.is(':focus')) {
            if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) return
            this.activate( this.$input.prev('.token') )
          } else {
            this.remove(e)
          }
          break

        case 46: // delete
          this.remove(e, 'next')
          break
      }
    }

  , focus: function (e) {
      this.focused = true
      this.$element.addClass('focus')
      this.$element.find('.active').removeClass('active')
    }

  , blur: function (e) {
      this.focused = false
      this.$element.removeClass('focus')
      this.$element.find('.active').removeClass('active')

      if (this.$input.data('edit')) {
        this.createTokensFromInput(e)
      }
    }

  , paste: function (e) {
      var _self = this

      // Add tokens to existing ones
      setTimeout(function () {
        _self.createTokensFromInput(e)
      }, 1)
    }

  , createTokensFromInput: function (e) {
      this.setTokens( this.$input.val(), true )
      this.$input.val('')

      if (this.$input.data( 'edit' )) {
        this.$input
          .appendTo( this.$element )
          .data( 'edit', false )
          .css( 'width', this.options.minWidth + 'px' )

        var _self = this
        setTimeout(function () {
          _self.$input.focus()
        }, 1)

        this.$element.css( 'width', this.$element.data('prev-width') )
      }

      e.preventDefault()
      e.stopPropagation()
    }

  , next: function () {
      var active = this.$element.find('.active:last')
        , next = active.next('.token')

      if (!next.length) {
        this.$input.focus()
        return
      }

      this.activate(next)
    }

  , prev: function () {
      var active = this.$element.find('.active:first')
        , prev = active.prev('.token')

      if (!prev.length) {
        prev = this.$element.find('.token:first')
      }

      if (!prev.length) {
        this.$input.focus()
        return
      }

      this.activate( prev )
    }

  , activate: function (token) {
      if (!token) return

      this.$helper.focus()

      this.$element.find('.active').removeClass('active')
      token.addClass('active')
      this.$helper.val( token.data('value') ).select()
    }

  , activateAll: function() {
      this.$helper.focus()

      this.$element.find('.token').addClass('active')
      this.$helper.val( this.getTokensList() ).select()
    }

  , edit: function (token) {
      if (!token) return

      var value = token.data('value')
        , label = token.find('.token-label').text()
        , tokenWidth = token.outerWidth()

      this.$element
        .data( 'prev-width', this.$element.width() )
        .width( this.$element.width() )

      token.replaceWith( this.$input )
      this.$input.focus()
                .val( value )
                .select()
                .data( 'edit', true )
                .width( tokenWidth )
    }

  , remove: function (e, direction) {
      if (this.$input.is(':focus')) return

      var token = (e.type === 'click') ? $(e.target).closest('.token') : this.$element.find('.token.active')
      if (!direction) direction = 'prev'

      this[direction]()
      token.remove()

      if (!this.$element.find('.token').length || e.type === 'click') this.$input.focus()

      this.$input.css('width', this.options.minWidth + 'px')
      this.update()

      e.preventDefault()
      e.stopPropagation()
    }

  , update: function () {
      var value = this.$input.val()

      if (this.$input.data('edit')) {
        if (!value) {
          value = this.$input.prop("placeholder")
        }
        if (value === this.$mirror.text()) return

        this.$mirror.text(value)

        this.$input.width(this.$mirror.width() + 10)
      }
      else {
        this.$input.width( this.$element.offset().left + this.$element.width() - this.$input.offset().left )
      }
    }

  , focusInput: function (e) {
      if ($(e.target).closest('.token').length) return
      this.$input.focus()
    }

  }

 /* TOKENFIELD PLUGIN DEFINITION
  * ======================== */

  var old = $.fn.tokenfield

  $.fn.tokenfield = function (option, param) {
    return this.map(function () {
      var $this = $(this)
        , data = $this.data('tokenfield')
        , options = typeof option == 'object' && option
      if (!data) $this.data('tokenfield', (data = new Tokenfield(this, options)))
      if (typeof option == 'string') {
        return data[option](param)
      }
      return this
    })
  }

  $.fn.tokenfield.defaults = {
    minWidth: 60,
    minLength: 0
  }

  $.fn.tokenfield.Constructor = Tokenfield

 /* TOKENFIELD NO CONFLICT
  * ================== */

  $.fn.tokenfield.noConflict = function () {
    $.fn.tokenfield = old
    return this
  }

}(window.jQuery);<commit_msg>Add option to disable duplicates<commit_after>/* ============================================================
 * bootstrap-tokenfield.js v0.6
 * ============================================================
 *
 * Copyright 2013 Sliptree
 * ============================================================ */

!function ($) {

  "use strict"; // jshint ;_;

 /* TOKENFIELD PUBLIC CLASS DEFINITION
  * ============================== */

  var Tokenfield = function (element, options) {
    var _self = this

    this.$input = $(element).addClass('token-input')
    this.$element = $('
')
    this.$helper = $('<textarea class="token-helper" tabindex="-1" style="position: absolute; left: -10000px;" />').appendTo(this.$element)
    this.$mirror = $('');

    this.options = $.extend({}, $.fn.tokenfield.defaults, { tokens: this.$input.data('tokens') }, options)

    // Copy some styles
    this.$element.width( this.$input.width() )

    // Set up mirror for input auto-sizing
    this.$input.css('min-width', this.options.minWidth + 'px')
    $.each([
        'fontFamily',
        'fontSize',
        'fontWeight',
        'fontStyle',
        'letterSpacing',
        'textTransform',
        'wordSpacing',
        'textIndent'
    ], function (i, val) {
        _self.$mirror[0].style[val] = _self.$input.css(val);
    });
    this.$mirror.appendTo( 'body' )

    // Insert tokenfield to HTML
    this.$element.insertBefore( this.$input )
    this.$input.appendTo( this.$element )

    this.setTokens(this.options.tokens)

    this.listen()
  }

  Tokenfield.prototype = {

    constructor: Tokenfield

  , createToken: function (attrs) {
      if (typeof attrs === 'string') {
        attrs = { value: attrs, label: attrs }
      }

      var _self = this
        , value = $.trim(attrs.value)
        , label = attrs.label.length ? $.trim(attrs.label) : value

      if (!value.length || !label.length || value.length < this.options.minLength) return

      if (!this.options.allowDuplicates && $.grep(this.getTokens(), function (token) {
        return token.value === value
      }).length) return

      var token = $('
')
            .attr('data-value', value)
            .append('')
            .append('×')

      this.$input.before( token )
      this.$input.css('width', this.options.minWidth + 'px')

      var tokenLabel = token.find('.token-label')
        , closeButton = token.find('.close')

      // Determine maximum possible token label width
      if (!this.maxTokenWidth) {
        this.maxTokenWidth =
          this.$element.width() - closeButton.outerWidth() -
          parseInt(closeButton.css('margin-left')) -
          parseInt(closeButton.css('margin-right')) -
          parseInt(token.css('border-left-width')) -
          parseInt(token.css('border-right-width')) -
          parseInt(token.css('padding-left')) -
          parseInt(token.css('padding-right'))
          parseInt(tokenLabel.css('border-left-width')) -
          parseInt(tokenLabel.css('border-right-width')) -
          parseInt(tokenLabel.css('padding-left')) -
          parseInt(tokenLabel.css('padding-right'))
          parseInt(tokenLabel.css('margin-left')) -
          parseInt(tokenLabel.css('margin-right'))
      }

      tokenLabel
        .text(label)
        .css('max-width', this.maxTokenWidth)

      // Listen to events
      token
        .on('click',    function (e) {
          _self.activate( token )
        })
        .on('dblclick', function (e) {
          _self.edit( token )
        })

      closeButton
          .on('click',  $.proxy(this.remove, this))

      this.update()

      return this.$input.get(0)
    }

  , setTokens: function (tokens, add) {
      if (!tokens) return

      if (!add) this.$element.find('.token').remove()

      if (typeof tokens === 'string') {
        tokens = tokens.split(',')
      }

      var _self = this
      $.each(tokens, function (i, token) {
        _self.createToken(token)
      })

      return this.$input.get(0)
    }

  , getTokens: function() {
      var tokens = []
      this.$element.find('.token').each( function() {
        tokens.push({
          value: $(this).data('value') || $(this).find('.token-label').text(),
          label: $(this).find('.token-label').text()
        })
      })
      return tokens
  }

  , getTokensList: function() {
      return $.map( this.getTokens(), function (token) {
        return token.value
      }).join(', ')
  }

  , listen: function () {
      this.$element
        .on('click',    $.proxy(this.focusInput, this))

      this.$input
        .on('focus',    $.proxy(this.focus, this))
        .on('blur',     $.proxy(this.blur, this))
        .on('paste',    $.proxy(this.paste, this))
        .on('keydown',  $.proxy(this.keydown, this))
        .on('keyup',    $.proxy(this.keyup, this))

      this.$helper
        .on('focus',    $.proxy(this.focus, this))
        .on('blur',     $.proxy(this.blur, this))
        .on('keydown',  $.proxy(this.keydown, this))
        .on('keyup',    $.proxy(this.keyup, this))

      // Secondary listeners for input width calculation
      this.$input.on('keydown, keypress, keyup',  $.proxy(this.update, this))
    }

  , keydown: function (e) {

      if (!this.focused) return

      this.lastKeyDown = e.keyCode

      switch(e.keyCode) {
        case 8: // backspace
          if (!this.$input.is(':focus')) return
          this.lastInputValue = this.$input.val()
          break

        case 37: // left arrow
          if (this.$input.is(':focus')) {
            if (this.$input.val().length > 0) return
            this.activate( this.$input.prev('.token') )
            e.preventDefault()
          } else {
            this.prev()
            e.preventDefault()
          }
          break

        case 39: // right arrow
          if (this.$input.is(':focus')) return
          this.next()
          e.preventDefault()
          break

        case 65: // a (to handle ctrl + a)
          if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) return
          this.activateAll()
          e.preventDefault()
          break

        case 9: // tab
        case 13: // enter
        case 188: // comma
          if (this.$input.is(':focus') && this.$input.val()) {
            this.createTokensFromInput(e)
          }
          if (e.keyCode === 13) {
            if (!this.$helper.is(':focus') || this.$element.find('.token.active').length !== 1) return
            this.edit( this.$element.find('.token.active') )
          }
      }
    }

  , keyup: function (e) {
      if (!this.focused) return
      switch(e.keyCode) {
        case 8: // backspace
          if (this.$input.is(':focus')) {
            if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) return
            this.activate( this.$input.prev('.token') )
          } else {
            this.remove(e)
          }
          break

        case 46: // delete
          this.remove(e, 'next')
          break
      }
    }

  , focus: function (e) {
      this.focused = true
      this.$element.addClass('focus')
      this.$element.find('.active').removeClass('active')
    }

  , blur: function (e) {
      this.focused = false
      this.$element.removeClass('focus')
      this.$element.find('.active').removeClass('active')

      if (this.$input.data('edit')) {
        this.createTokensFromInput(e)
      }
    }

  , paste: function (e) {
      var _self = this

      // Add tokens to existing ones
      setTimeout(function () {
        _self.createTokensFromInput(e)
      }, 1)
    }

  , createTokensFromInput: function (e) {
      this.setTokens( this.$input.val(), true )
      this.$input.val('')

      if (this.$input.data( 'edit' )) {
        this.$input
          .appendTo( this.$element )
          .data( 'edit', false )
          .css( 'width', this.options.minWidth + 'px' )

        var _self = this
        setTimeout(function () {
          _self.$input.focus()
        }, 1)

        this.$element.css( 'width', this.$element.data('prev-width') )
      }

      e.preventDefault()
      e.stopPropagation()
    }

  , next: function () {
      var active = this.$element.find('.active:last')
        , next = active.next('.token')

      if (!next.length) {
        this.$input.focus()
        return
      }

      this.activate(next)
    }

  , prev: function () {
      var active = this.$element.find('.active:first')
        , prev = active.prev('.token')

      if (!prev.length) {
        prev = this.$element.find('.token:first')
      }

      if (!prev.length) {
        this.$input.focus()
        return
      }

      this.activate( prev )
    }

  , activate: function (token) {
      if (!token) return

      this.$helper.focus()

      this.$element.find('.active').removeClass('active')
      token.addClass('active')
      this.$helper.val( token.data('value') ).select()
    }

  , activateAll: function() {
      this.$helper.focus()

      this.$element.find('.token').addClass('active')
      this.$helper.val( this.getTokensList() ).select()
    }

  , edit: function (token) {
      if (!token) return

      var value = token.data('value')
        , label = token.find('.token-label').text()
        , tokenWidth = token.outerWidth()

      this.$element
        .data( 'prev-width', this.$element.width() )
        .width( this.$element.width() )

      token.replaceWith( this.$input )
      this.$input.focus()
                .val( value )
                .select()
                .data( 'edit', true )
                .width( tokenWidth )
    }

  , remove: function (e, direction) {
      if (this.$input.is(':focus')) return

      var token = (e.type === 'click') ? $(e.target).closest('.token') : this.$element.find('.token.active')
      if (!direction) direction = 'prev'

      this[direction]()
      token.remove()

      if (!this.$element.find('.token').length || e.type === 'click') this.$input.focus()

      this.$input.css('width', this.options.minWidth + 'px')
      this.update()

      e.preventDefault()
      e.stopPropagation()
    }

  , update: function () {
      var value = this.$input.val()

      if (this.$input.data('edit')) {
        if (!value) {
          value = this.$input.prop("placeholder")
        }
        if (value === this.$mirror.text()) return

        this.$mirror.text(value)

        this.$input.width(this.$mirror.width() + 10)
      }
      else {
        this.$input.width( this.$element.offset().left + this.$element.width() - this.$input.offset().left )
      }
    }

  , focusInput: function (e) {
      if ($(e.target).closest('.token').length) return
      this.$input.focus()
    }

  }

 /* TOKENFIELD PLUGIN DEFINITION
  * ======================== */

  var old = $.fn.tokenfield

  $.fn.tokenfield = function (option, param) {
    return this.map(function () {
      var $this = $(this)
        , data = $this.data('tokenfield')
        , options = typeof option == 'object' && option
      if (!data) $this.data('tokenfield', (data = new Tokenfield(this, options)))
      if (typeof option == 'string') {
        return data[option](param)
      }
      return this
    })
  }

  $.fn.tokenfield.defaults = {
    minWidth: 60,
    minLength: 0,
    allowDuplicates: false
  }

  $.fn.tokenfield.Constructor = Tokenfield

 /* TOKENFIELD NO CONFLICT
  * ================== */

  $.fn.tokenfield.noConflict = function () {
    $.fn.tokenfield = old
    return this
  }

}(window.jQuery);<|endoftext|>


<commit_before>(function(exports) {

  var KadOH = exports;

  KadOH.util = KadOH.util ? KadOH.util : {};

  var Crypto = KadOH.util.Crypto = {
    // Bit-wise rotate left
    rotl: function (n, b) {
      return (n << b) | (n >>> (32 - b));
    },

    // Bit-wise rotate right
    rotr: function (n, b) {
      return (n << (32 - b)) | (n >>> b);
    },

    // Swap big-endian to little-endian and vice versa
    endian: function (n) {
      // If number given, swap endian
      if (n.constructor == Number) {
        return Crypto.rotl(n,  8) & 0x00FF00FF | Crypto.rotl(n, 24) & 0xFF00FF00;
      }

      // Else, assume array and swap all items
      for (var i = 0; i < n.length; i++)
        n[i] = Crypto.endian(n[i]);
      return n;
    },

    // Generate an array of any length of random bytes
    randomBytes: function (n) {
      for (var bytes = []; n > 0; n--)
      bytes.push(Math.floor(Math.random() * 256));
      return bytes;
    },

    // Convert a byte array to big-endian 32-bit words
    bytesToWords: function (bytes) {
      for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
        words[b >>> 5] |= bytes[i] << (24 - b % 32);
      return words;
    },

    // Convert big-endian 32-bit words to a byte array
    wordsToBytes: function (words) {
      for (var bytes = [], b = 0; b < words.length * 32; b += 8)
        bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
      return bytes;
    },

    // Convert a hex string to an ascii string
    hexToAscii: function(hex) {
      var str = '';
      for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
      return str;
    },

    asciiToHex: function(str) {
      for (var hex = '', i = 0; i < str.length; i++) {
        hex += str.charCodeAt(i).toString(16);
      }
      return hex;
    },

    // Convert a byte array to a hex string
    bytesToHex: function (bytes) {
      for (var hex = [], i = 0; i < bytes.length; i++) {
        hex.push((bytes[i] >>> 4).toString(16));
        hex.push((bytes[i] & 0xF).toString(16));
      }
      return hex.join("");
    },

    // Convert a hex string to a byte array
    hexToBytes: function (hex) {
      for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
      return bytes;
    },

    /**
     * Return the bytes XOR of two hexadecimal strings
     *
     * @param {String} hex1 the first hexadecimal string
     * @param {String} hex2 the second hexadecimal string
     * @return {Array} the bytes `Array` of the XOR
     */
    XOR: function(hex1, hex2) {
      if (hex1.length != hex2.length)
        return;

      if ('string' === typeof hex1)
        hex1 = Crypto.hexToBytes(hex1);
      if ('string' === typeof hex2)
        hex2 = Crypto.hexToBytes(hex2);

      var xor = [];
      for (var i = 0; i < hex1.length; i++) {
        xor.push(hex1[i] ^ hex2[i]);
      }
      return xor;
    },

    /**
     * Compares two bytes array and tell which
     * one is greater than the other.
     * It is possible to use this function with
     * `Array.prototype.sort`
     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort
     *
     * @param  {Array} a Bytes array
     * @param  {Array} b Bytes array
     * @return {-1|0|1}
     */
    compareBytes: function(a, b) {
      for (var i = 0; i < a.length; i++) {
        if (a[i] > b[i]) {
          return 1;
        }
        else if (a[i] < b[i]) {
          return -1;
        }
      }
      return 0;
    },

    /**
     * Return the position of the first different bit
     * between two hexadecimal strings
     *
     * @param {String} hex1 the first hexadecimal string
     * @param {String} hex2 the second hexadecimal string
     * @return {Integer} the position of the bit
     */
    distance: function(hex1, hex2) {
      if (hex1 === hex2)
        return 0;

      if ('string' === typeof hex1)
        hex1 = Crypto.hexToBytes(hex1);
      if ('string' === typeof hex2)
        hex2 = Crypto.hexToBytes(hex2);

      var length = hex1.length;
      if (length != hex2.length) {
        return -1;
      }

      var max_dist = 8*length;

      for (var i = 0; i < length; i++) {
        var diff = hex1[i] ^ hex2[i];

        if (diff) {
          for (var j = 0; j < 7; j++) {
            if (diff >>> (7 - j))
              break;
          }
          return max_dist - 8*i - j;
        }
      }
      return 0;
    }
  };

  Crypto.charenc = {};
  Crypto.charenc.Binary = {

    // Convert a string to a byte array
    stringToBytes: function (str) {
      for (var bytes = [], i = 0; i < str.length; i++)
      bytes.push(str.charCodeAt(i) & 0xFF);
      return bytes;
    },

    // Convert a byte array to a string
    bytesToString: function (bytes) {
      for (var str = [], i = 0; i < bytes.length; i++)
      str.push(String.fromCharCode(bytes[i]));
      return str.join("");
    }

  };

  Crypto.charenc.UTF8 = {

    // Convert a string to a byte array
    stringToBytes: function (str) {
      return Crypto.charenc.Binary.stringToBytes(unescape(encodeURIComponent(str)));
    },

    // Convert a byte array to a string
    bytesToString: function (bytes) {
      return decodeURIComponent(escape(Crypto.charenc.Binary.bytesToString(bytes)));
    }

  };

  // Digest (SHA1)

  Crypto.digest = {

    SHA1: function(message, options) {
      var digestbytes = Crypto.wordsToBytes(Crypto.digest._sha1(message));
      return options && options.asBytes ? digestbytes : options && options.asString ? Crypto.charenc.Binary.bytesToString(digestbytes) : Crypto.bytesToHex(digestbytes);
    },

    randomSHA1: function(message) {
      var bytes = [];
      for (var i = 0; i < 40; i++) {
        bytes.push(Math.floor(Math.random() * 256));
      }
      return Crypto.digest.SHA1(Crypto.bytesToHex(bytes));
    },

    _sha1: function (message) {
      // Convert to byte array
      if (message.constructor == String) message = Crypto.charenc.UTF8.stringToBytes(message);

      /* else, assume byte array already */
      var m  = Crypto.bytesToWords(message),
      l  = message.length * 8,
      w  =  [],
      H0 =  1732584193,
      H1 = -271733879,
      H2 = -1732584194,
      H3 =  271733878,
      H4 = -1009589776;

      // Padding
      m[l >> 5] |= 0x80 << (24 - l % 32);
      m[((l + 64 >>> 9) << 4) + 15] = l;

      for (var i = 0; i < m.length; i += 16) {

        var a = H0,
        b = H1,
        c = H2,
        d = H3,
        e = H4;

        for (var j = 0; j < 80; j++) {

          if (j < 16) w[j] = m[i + j];
          else {
            var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];
            w[j] = (n << 1) | (n >>> 31);
          }

          var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
            j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
            j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
            j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
            (H1 ^ H2 ^ H3) - 899497514);

          H4 =  H3;
          H3  =  H2;
          H2 = (H1 << 30) | (H1 >>> 2);
          H1 =  H0;
          H0 =  t;

        }

        H0 += a;
        H1 += b;
        H2 += c;
        H3 += d;
        H4 += e;

      }

      return [H0, H1, H2, H3, H4];
    },

    // Package private blocksize
    _blocksize: 16,

    _digestsize: 20
  };
})('object' === typeof module ? module.exports : (this.KadOH = this.KadOH || {}));
<commit_msg>copyright for crypto-js in crypto.js<commit_after>/*
 * Crypto-JS v2.3.0
 * http://code.google.com/p/crypto-js/
 * Copyright (c) 2011, Jeff Mott. All rights reserved.
 * http://code.google.com/p/crypto-js/wiki/License
 */
(function(exports) {

  var KadOH = exports;

  KadOH.util = KadOH.util ? KadOH.util : {};

  var Crypto = KadOH.util.Crypto = {
    // Bit-wise rotate left
    rotl: function (n, b) {
      return (n << b) | (n >>> (32 - b));
    },

    // Bit-wise rotate right
    rotr: function (n, b) {
      return (n << (32 - b)) | (n >>> b);
    },

    // Swap big-endian to little-endian and vice versa
    endian: function (n) {
      // If number given, swap endian
      if (n.constructor == Number) {
        return Crypto.rotl(n,  8) & 0x00FF00FF | Crypto.rotl(n, 24) & 0xFF00FF00;
      }

      // Else, assume array and swap all items
      for (var i = 0; i < n.length; i++)
        n[i] = Crypto.endian(n[i]);
      return n;
    },

    // Generate an array of any length of random bytes
    randomBytes: function (n) {
      for (var bytes = []; n > 0; n--)
      bytes.push(Math.floor(Math.random() * 256));
      return bytes;
    },

    // Convert a byte array to big-endian 32-bit words
    bytesToWords: function (bytes) {
      for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
        words[b >>> 5] |= bytes[i] << (24 - b % 32);
      return words;
    },

    // Convert big-endian 32-bit words to a byte array
    wordsToBytes: function (words) {
      for (var bytes = [], b = 0; b < words.length * 32; b += 8)
        bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
      return bytes;
    },

    // Convert a hex string to an ascii string
    hexToAscii: function(hex) {
      var str = '';
      for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
      return str;
    },

    asciiToHex: function(str) {
      for (var hex = '', i = 0; i < str.length; i++) {
        hex += str.charCodeAt(i).toString(16);
      }
      return hex;
    },

    // Convert a byte array to a hex string
    bytesToHex: function (bytes) {
      for (var hex = [], i = 0; i < bytes.length; i++) {
        hex.push((bytes[i] >>> 4).toString(16));
        hex.push((bytes[i] & 0xF).toString(16));
      }
      return hex.join("");
    },

    // Convert a hex string to a byte array
    hexToBytes: function (hex) {
      for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
      return bytes;
    },

    /**
     * Return the bytes XOR of two hexadecimal strings
     *
     * @param {String} hex1 the first hexadecimal string
     * @param {String} hex2 the second hexadecimal string
     * @return {Array} the bytes `Array` of the XOR
     */
    XOR: function(hex1, hex2) {
      if (hex1.length != hex2.length)
        return;

      if ('string' === typeof hex1)
        hex1 = Crypto.hexToBytes(hex1);
      if ('string' === typeof hex2)
        hex2 = Crypto.hexToBytes(hex2);

      var xor = [];
      for (var i = 0; i < hex1.length; i++) {
        xor.push(hex1[i] ^ hex2[i]);
      }
      return xor;
    },

    /**
     * Compares two bytes array and tell which
     * one is greater than the other.
     * It is possible to use this function with
     * `Array.prototype.sort`
     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort
     *
     * @param  {Array} a Bytes array
     * @param  {Array} b Bytes array
     * @return {-1|0|1}
     */
    compareBytes: function(a, b) {
      for (var i = 0; i < a.length; i++) {
        if (a[i] > b[i]) {
          return 1;
        }
        else if (a[i] < b[i]) {
          return -1;
        }
      }
      return 0;
    },

    /**
     * Return the position of the first different bit
     * between two hexadecimal strings
     *
     * @param {String} hex1 the first hexadecimal string
     * @param {String} hex2 the second hexadecimal string
     * @return {Integer} the position of the bit
     */
    distance: function(hex1, hex2) {
      if (hex1 === hex2)
        return 0;

      if ('string' === typeof hex1)
        hex1 = Crypto.hexToBytes(hex1);
      if ('string' === typeof hex2)
        hex2 = Crypto.hexToBytes(hex2);

      var length = hex1.length;
      if (length != hex2.length) {
        return -1;
      }

      var max_dist = 8*length;

      for (var i = 0; i < length; i++) {
        var diff = hex1[i] ^ hex2[i];

        if (diff) {
          for (var j = 0; j < 7; j++) {
            if (diff >>> (7 - j))
              break;
          }
          return max_dist - 8*i - j;
        }
      }
      return 0;
    }
  };

  Crypto.charenc = {};
  Crypto.charenc.Binary = {

    // Convert a string to a byte array
    stringToBytes: function (str) {
      for (var bytes = [], i = 0; i < str.length; i++)
      bytes.push(str.charCodeAt(i) & 0xFF);
      return bytes;
    },

    // Convert a byte array to a string
    bytesToString: function (bytes) {
      for (var str = [], i = 0; i < bytes.length; i++)
      str.push(String.fromCharCode(bytes[i]));
      return str.join("");
    }

  };

  Crypto.charenc.UTF8 = {

    // Convert a string to a byte array
    stringToBytes: function (str) {
      return Crypto.charenc.Binary.stringToBytes(unescape(encodeURIComponent(str)));
    },

    // Convert a byte array to a string
    bytesToString: function (bytes) {
      return decodeURIComponent(escape(Crypto.charenc.Binary.bytesToString(bytes)));
    }

  };

  // Digest (SHA1)

  Crypto.digest = {

    SHA1: function(message, options) {
      var digestbytes = Crypto.wordsToBytes(Crypto.digest._sha1(message));
      return options && options.asBytes ? digestbytes : options && options.asString ? Crypto.charenc.Binary.bytesToString(digestbytes) : Crypto.bytesToHex(digestbytes);
    },

    randomSHA1: function(message) {
      var bytes = [];
      for (var i = 0; i < 40; i++) {
        bytes.push(Math.floor(Math.random() * 256));
      }
      return Crypto.digest.SHA1(Crypto.bytesToHex(bytes));
    },

    _sha1: function (message) {
      // Convert to byte array
      if (message.constructor == String) message = Crypto.charenc.UTF8.stringToBytes(message);

      /* else, assume byte array already */
      var m  = Crypto.bytesToWords(message),
      l  = message.length * 8,
      w  =  [],
      H0 =  1732584193,
      H1 = -271733879,
      H2 = -1732584194,
      H3 =  271733878,
      H4 = -1009589776;

      // Padding
      m[l >> 5] |= 0x80 << (24 - l % 32);
      m[((l + 64 >>> 9) << 4) + 15] = l;

      for (var i = 0; i < m.length; i += 16) {

        var a = H0,
        b = H1,
        c = H2,
        d = H3,
        e = H4;

        for (var j = 0; j < 80; j++) {

          if (j < 16) w[j] = m[i + j];
          else {
            var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];
            w[j] = (n << 1) | (n >>> 31);
          }

          var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
            j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
            j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
            j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
            (H1 ^ H2 ^ H3) - 899497514);

          H4 =  H3;
          H3  =  H2;
          H2 = (H1 << 30) | (H1 >>> 2);
          H1 =  H0;
          H0 =  t;

        }

        H0 += a;
        H1 += b;
        H2 += c;
        H3 += d;
        H4 += e;

      }

      return [H0, H1, H2, H3, H4];
    },

    // Package private blocksize
    _blocksize: 16,

    _digestsize: 20
  };
})('object' === typeof module ? module.exports : (this.KadOH = this.KadOH || {}));
<|endoftext|>


<commit_before>import Vue from 'vue';
import VueI18n from 'vue-i18n';
import VueRouter from 'vue-router';
import 'highlight.js/styles/github-gist.css';
import App from './App.vue';

import utils from 'hey-utils';

import HeyUI from './../src/index';
import heyuiConfig from './js/config/heyui-config';
import VueHighlightJS from './js/vuehighlight';

import example from './components/common/example.vue';
import codes from './components/common/codes.vue';
import exampleEn from './components_en/common/example.vue';
import codesEn from './components_en/common/codes.vue';
import routerParam from './js/config/router-config';
import en from '../src/locale/lang/en-US';
import zh from '../src/locale/lang/zh-CN';

require('./css/index.less');

let language = (navigator.language || navigator.browserLanguage).toLowerCase();

let lang = window.location.pathname.indexOf('/en') == -1 ? 'zh' : 'en';

if (utils.getLocal('LANGUAGE') == null && window.location.pathname.indexOf('/en') == -1) {
  if (language.indexOf('zh') == -1) {
    window.location.href = '/en';
  }
}

Vue.use(VueHighlightJS);

Vue.use(VueI18n);
const messages = {
  en: Object.assign({ message: 'hello' }, en),
  zh: Object.assign({ message: '你好' }, zh)
};
const i18n = new VueI18n({
  locale: lang, // set locale
  fallbackLocale: 'en',
  messages // set locale messages
});
HeyUI.i18n(i18n);

heyuiConfig();
Vue.use(VueRouter);
Vue.use(HeyUI);

Vue.component('example', example);
Vue.component('codes', codes);
Vue.component('exampleEn', exampleEn);
Vue.component('codesEn', codesEn);

const router = new VueRouter(routerParam);
router.beforeEach((to, from, next) => {
  if (from.name == to.name) return;
  HeyUI.$LoadingBar.start();
  if (to.meta.title && to.name != 'Home') {
    document.title = `${to.meta.title} - HeyUI: A high quality UI Toolkit based on Vue.js`;
  } else {
    document.title = 'HeyUI: A high quality UI Toolkit based on Vue.js';
  }
  next();
});
router.afterEach(() => {
  HeyUI.$LoadingBar.success();
  Vue.nextTick(() => {
    $('.right-frame').scrollTop(0);
  });
  if (window.location.hostname != 'localhost' && window._hmt) {
    window._hmt.push(['_trackPageview', window.location.pathname]);
  }
});
const app = new Vue({
  i18n,
  router,
  el: '#app',
  render: h => h(App)
});

export default app;
<commit_msg>docs: update console<commit_after>import Vue from 'vue';
import VueI18n from 'vue-i18n';
import VueRouter from 'vue-router';
import 'highlight.js/styles/github-gist.css';
import App from './App.vue';

import utils from 'hey-utils';

import HeyUI from './../src/index';
import heyuiConfig from './js/config/heyui-config';
import VueHighlightJS from './js/vuehighlight';

import example from './components/common/example.vue';
import codes from './components/common/codes.vue';
import exampleEn from './components_en/common/example.vue';
import codesEn from './components_en/common/codes.vue';
import routerParam from './js/config/router-config';
import en from '../src/locale/lang/en-US';
import zh from '../src/locale/lang/zh-CN';

require('./css/index.less');

let language = (navigator.language || navigator.browserLanguage).toLowerCase();

let lang = window.location.pathname.indexOf('/en') == -1 ? 'zh' : 'en';

if (utils.getLocal('LANGUAGE') == null && window.location.pathname.indexOf('/en') == -1) {
  if (language.indexOf('zh') == -1) {
    window.location.href = '/en';
  }
}

Vue.use(VueHighlightJS);

Vue.use(VueI18n);
const messages = {
  en: Object.assign({ message: 'hello' }, en),
  zh: Object.assign({ message: '你好' }, zh)
};
const i18n = new VueI18n({
  locale: lang, // set locale
  fallbackLocale: 'en',
  messages // set locale messages
});
HeyUI.i18n(i18n);

heyuiConfig();
Vue.use(VueRouter);
Vue.use(HeyUI);

Vue.component('example', example);
Vue.component('codes', codes);
Vue.component('exampleEn', exampleEn);
Vue.component('codesEn', codesEn);

const router = new VueRouter(routerParam);
router.beforeEach((to, from, next) => {
  if (from.name == to.name) return;
  HeyUI.$LoadingBar.start();
  if (to.meta.title && to.name != 'Home') {
    document.title = `${to.meta.title} - HeyUI: A high quality UI Toolkit based on Vue.js`;
  } else {
    document.title = 'HeyUI: A high quality UI Toolkit based on Vue.js';
  }
  next();
});
router.afterEach(() => {
  HeyUI.$LoadingBar.success();
  Vue.nextTick(() => {
    $('.right-frame').scrollTop(0);
  });
  if (window.location.hostname != 'localhost' && window._hmt) {
    window._hmt.push(['_trackPageview', window.location.pathname]);
  }
});
const app = new Vue({
  i18n,
  router,
  el: '#app',
  render: h => h(App)
});

console.log(` %c
 _    _  ______ __     __ _    _  _____
| |  | ||  ____|\\ \\   / /| |  | ||_   _|
| |__| || |__    \\ \\_/ / | |  | |  | |
|  __  ||  __|    \\   /  | |  | |  | |
| |  | || |____    | |   | |__| | _| |_
|_|  |_||______|   |_|    \\____/ |_____|

             一个基于Vue.js的高质量UI组件库
`, 'color: #45b984');
export default app;
<|endoftext|>


<commit_before>import React from "react";
import {isDataURL} from "../utils";

class Cropper extends React.Component {

  constructor () {
    super();

    // getInitialState
    this.state = {
      dragging: false,
      image: {},
      mouse: {
        x: null,
        y: null
      },
      preview: null
    };

    // getDefaultProps
    this.defaultProps = {
      zoom: 1
    }
  }

  fitImageToCanvas (width, height) {
    var scaledHeight, scaledWidth;

    var canvasAspectRatio = this.props.height / this.props.width;
    var imageAspectRatio = height / width;

    if (canvasAspectRatio > imageAspectRatio) {
      scaledHeight = this.props.height;
      var scaleRatio = scaledHeight / height;
      scaledWidth = width * scaleRatio;
    } else {
      scaledWidth = this.props.width;
      var scaleRatio = scaledWidth / width;
      scaledHeight = height * scaleRatio;
    }

    return { width: scaledWidth, height: scaledHeight };
  }

  prepareImage (imageUri) {
    var img = new Image();
    if (!isDataURL(imageUri)) img.crossOrigin = 'anonymous';
    img.onload = () => {
      var scaledImage = this.fitImageToCanvas(img.width, img.height);
      scaledImage.resource = img;
      scaledImage.x = 0;
      scaledImage.y = 0;
      this.setState({dragging: false, image: scaledImage});
    }
    img.src = imageUri;
  }

  mouseDownListener (e) {
    this.setState({
      image: this.state.image,
      dragging: true,
      mouse: {
        x: null,
        y: null
      }
    });
    e.stopPropagation()
  }

  mouseUpListener (e) {
    this.setState({ dragging: false, preview: this.toDataURL() });
  }

  mouseMoveListener (e) {
    if (!this.state.dragging) return;

    var mouseX = e.clientX;
    var mouseY = e.clientY;
    var imageX = this.state.image.x;
    var imageY = this.state.image.y;

    var newImage = this.state.image;

    if (this.state.mouse.x && this.state.mouse.y) {
      var dx = this.state.mouse.x - mouseX;
      var dy = this.state.mouse.y - mouseY;

      var bounded = this.boundedCoords(imageX, imageY, dx, dy);

      newImage.x = bounded.x;
      newImage.y = bounded.y;
    }

    this.setState({
      image: this.state.image,
      mouse: {
        x: mouseX,
        y: mouseY
      }
    });
  }

  boundedCoords (x, y, dx, dy) {
    var newX = x - dx;
    var newY = y - dy;

    var scaledWidth = this.state.image.width * this.props.zoom;
    var dw = (scaledWidth - this.state.image.width) / 2;
    var imageLeftEdge = this.state.image.x - dw;
    var imageRightEdge = (imageLeftEdge + scaledWidth);

    var rightEdge = this.props.width;
    var leftEdge = 0;

    if (newX - dw > 0) { x = dw; }
    else if (newX < (-scaledWidth + rightEdge)) { x = rightEdge - scaledWidth; }
    else {
      x = newX;
    }

    var scaledHeight = this.state.image.height * this.props.zoom;
    var dh = (scaledHeight - this.state.image.height) / 2;
    var imageTopEdge = this.state.image.y - dh;
    var imageBottomEdge = imageTopEdge + scaledHeight;

    var bottomEdge = this.props.height
    var topEdge = 0;
    if (newY - dw > 0) { y = dh; }
    else if (newY < (-scaledHeight + bottomEdge)) { y = bottomEdge - scaledHeight; }
    else {
      y = newY;
    }

    return { x: x, y: y };
  }

  componentDidMount () {
    var canvas = React.findDOMNode(this.refs.canvas);
    var context = canvas.getContext("2d");
    this.prepareImage(this.props.image);
    window.addEventListener("mousemove", this.mouseMoveListener.bind(this), false);
    window.addEventListener("mouseup", this.mouseUpListener.bind(this), false);
  }

  componentWillUnmount () {
    window.removeEventListener("mousemove", this.mouseMoveListener);
    window.removeEventListener("mouseup", this.mouseMoveListener);
  }

  componentDidUpdate () {
    var context = React.findDOMNode(this.refs.canvas).getContext("2d");
    context.clearRect(0, 0, this.props.width, this.props.height);
    this.addImageToCanvas(context, this.state.image);
  }

  addImageToCanvas (context, image) {
    if (!image.resource) return;
    context.save();
    context.globalCompositeOperation = "destination-over";
    var scaledWidth = this.state.image.width * this.props.zoom;
    var scaledHeight = this.state.image.height * this.props.zoom;

    var x = image.x - (scaledWidth - this.state.image.width) / 2;
    var y = image.y - (scaledHeight - this.state.image.height) / 2;

    // need to make sure we aren't going out of bounds here...
    x = Math.min(x, 0);
    y = Math.min(y, 0);
    y = scaledHeight + y >= this.props.height ? y : (y + (this.props.height - (scaledHeight + y)));
    x = scaledWidth + x >= this.props.width ? x : (x + (this.props.width - (scaledWidth + x)));

    context.drawImage( image.resource, x, y, image.width * this.props.zoom, image.height * this.props.zoom);
    context.restore();
  }

  toDataURL () {
    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");

    canvas.width = this.props.width;
    canvas.height = this.props.height;

    this.addImageToCanvas(context, {
      resource: this.state.image.resource,
      x: this.state.image.x,
      y: this.state.image.y,
      height: this.state.image.height,
      width: this.state.image.width
    });

    return canvas.toDataURL();
  }

  render () {
    return (

        <canvas
          ref="canvas"
          onMouseDown={this.mouseDownListener.bind(this)}
          width={this.props.width}
          height={this.props.height}
        ></canvas>

        {this.state.preview &&

        }

    );
  }
};

export default Cropper;

<commit_msg>event listener paradise<commit_after>import React from "react";
import {isDataURL} from "../utils";

class Cropper extends React.Component {

  constructor () {
    super();

    // getInitialState
    this.state = {
      dragging: false,
      image: {},
      mouse: {
        x: null,
        y: null
      },
      preview: null
    };

    // getDefaultProps
    this.defaultProps = {
      zoom: 1
    }
  }

  fitImageToCanvas (width, height) {
    var scaledHeight, scaledWidth;

    var canvasAspectRatio = this.props.height / this.props.width;
    var imageAspectRatio = height / width;

    if (canvasAspectRatio > imageAspectRatio) {
      scaledHeight = this.props.height;
      var scaleRatio = scaledHeight / height;
      scaledWidth = width * scaleRatio;
    } else {
      scaledWidth = this.props.width;
      var scaleRatio = scaledWidth / width;
      scaledHeight = height * scaleRatio;
    }

    return { width: scaledWidth, height: scaledHeight };
  }

  prepareImage (imageUri) {
    var img = new Image();
    if (!isDataURL(imageUri)) img.crossOrigin = 'anonymous';
    img.onload = () => {
      var scaledImage = this.fitImageToCanvas(img.width, img.height);
      scaledImage.resource = img;
      scaledImage.x = 0;
      scaledImage.y = 0;
      this.setState({dragging: false, image: scaledImage});
    }
    img.src = imageUri;
  }

  mouseDownListener (e) {
    this.setState({
      image: this.state.image,
      dragging: true,
      mouse: {
        x: null,
        y: null
      }
    });
  }

  preventSelection (e) {
    if (this.state.dragging) {
      e.preventDefault();
      return false;
    }
  }

  mouseUpListener (e) {
    this.setState({ dragging: false, preview: this.toDataURL() });
  }

  mouseMoveListener (e) {
    if (!this.state.dragging) return;

    var mouseX = e.clientX;
    var mouseY = e.clientY;
    var imageX = this.state.image.x;
    var imageY = this.state.image.y;

    var newImage = this.state.image;

    if (this.state.mouse.x && this.state.mouse.y) {
      var dx = this.state.mouse.x - mouseX;
      var dy = this.state.mouse.y - mouseY;

      var bounded = this.boundedCoords(imageX, imageY, dx, dy);

      newImage.x = bounded.x;
      newImage.y = bounded.y;
    }

    this.setState({
      image: this.state.image,
      mouse: {
        x: mouseX,
        y: mouseY
      }
    });
  }

  boundedCoords (x, y, dx, dy) {
    var newX = x - dx;
    var newY = y - dy;

    var scaledWidth = this.state.image.width * this.props.zoom;
    var dw = (scaledWidth - this.state.image.width) / 2;
    var imageLeftEdge = this.state.image.x - dw;
    var imageRightEdge = (imageLeftEdge + scaledWidth);

    var rightEdge = this.props.width;
    var leftEdge = 0;

    if (newX - dw > 0) { x = dw; }
    else if (newX < (-scaledWidth + rightEdge)) { x = rightEdge - scaledWidth; }
    else {
      x = newX;
    }

    var scaledHeight = this.state.image.height * this.props.zoom;
    var dh = (scaledHeight - this.state.image.height) / 2;
    var imageTopEdge = this.state.image.y - dh;
    var imageBottomEdge = imageTopEdge + scaledHeight;

    var bottomEdge = this.props.height
    var topEdge = 0;
    if (newY - dw > 0) { y = dh; }
    else if (newY < (-scaledHeight + bottomEdge)) { y = bottomEdge - scaledHeight; }
    else {
      y = newY;
    }

    return { x: x, y: y };
  }

  componentDidMount () {
    var canvas = React.findDOMNode(this.refs.canvas);
    var context = canvas.getContext("2d");
    this.prepareImage(this.props.image);
    window.addEventListener("mousemove", e => this.mouseMoveListener(e), false);
    window.addEventListener("mouseup", e => this.mouseUpListener(e), false);
    canvas.addEventListener("mousedown", e => this.mouseDownListener(e), false);
    document.onselectstart = e => this.preventSelection(e);
  }

  componentWillUnmount () {
    var canvas = React.findDOMNode(this.refs.canvas);
    window.removeEventListener("mousemove", this.mouseMoveListener);
    window.removeEventListener("mouseup", this.mouseMoveListener);
    canvas.removeEventListener("mousedown", this.mouseDownListener);
  }

  componentDidUpdate () {
    var context = React.findDOMNode(this.refs.canvas).getContext("2d");
    context.clearRect(0, 0, this.props.width, this.props.height);
    this.addImageToCanvas(context, this.state.image);
  }

  addImageToCanvas (context, image) {
    if (!image.resource) return;
    context.save();
    context.globalCompositeOperation = "destination-over";
    var scaledWidth = this.state.image.width * this.props.zoom;
    var scaledHeight = this.state.image.height * this.props.zoom;

    var x = image.x - (scaledWidth - this.state.image.width) / 2;
    var y = image.y - (scaledHeight - this.state.image.height) / 2;

    // need to make sure we aren't going out of bounds here...
    x = Math.min(x, 0);
    y = Math.min(y, 0);
    y = scaledHeight + y >= this.props.height ? y : (y + (this.props.height - (scaledHeight + y)));
    x = scaledWidth + x >= this.props.width ? x : (x + (this.props.width - (scaledWidth + x)));

    context.drawImage( image.resource, x, y, image.width * this.props.zoom, image.height * this.props.zoom);
    context.restore();
  }

  toDataURL () {
    var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");

    canvas.width = this.props.width;
    canvas.height = this.props.height;

    this.addImageToCanvas(context, {
      resource: this.state.image.resource,
      x: this.state.image.x,
      y: this.state.image.y,
      height: this.state.image.height,
      width: this.state.image.width
    });

    return canvas.toDataURL();
  }

  render () {
    return (

        <canvas
          ref="canvas"
          width={this.props.width}
          height={this.props.height}
        ></canvas>

        {this.state.preview &&

        }

    );
  }
};

export default Cropper;

<|endoftext|>