<commit_before>// Native
const { createServer } = require('http')
const path = require('path')

// Packages
const next = require('next')

exports.devServer = async (app, dir, port) => {
  const nextApp = next({
    dev: true,
    dir: dir || path.join(process.cwd(), 'renderer')
  })

  const nextHandler = nextApp.getRequestHandler()

  // Build the renderer code and watch the files
  await nextApp.prepare()

  // But if developing the application, create a
  // new native HTTP server (which supports hot code reloading)
  const server = createServer(nextHandler)

  server.listen(port || 8000, () => {
    // Make sure to stop the server when the app closes
    // Otherwise it keeps running on its own
    app.on('before-quit', () => server.close())
  })
}

exports.adjustProtocol = protocol => {
  const paths = ['_next', 'static']

  protocol.interceptFileProtocol('file', (request, callback) => {
    let filePath = request.url.substr('file'.length + 1)

    for (const replacement of paths) {
      const wrongPath = '///' + replacement
      const rightPath = '//' + resolvePath('./renderer') + '/' + replacement

      filePath = filePath.replace(wrongPath, rightPath)
    }

    callback({ path: filePath })
  })
}
<commit_msg>Renamed `adjustProtocol` to `adjustRenderer`<commit_after>// Native
const { createServer } = require('http')
const path = require('path')

// Packages
const next = require('next')

exports.devServer = async (app, dir, port) => {
  const nextApp = next({
    dev: true,
    dir: dir || path.join(process.cwd(), 'renderer')
  })

  const nextHandler = nextApp.getRequestHandler()

  // Build the renderer code and watch the files
  await nextApp.prepare()

  // But if developing the application, create a
  // new native HTTP server (which supports hot code reloading)
  const server = createServer(nextHandler)

  server.listen(port || 8000, () => {
    // Make sure to stop the server when the app closes
    // Otherwise it keeps running on its own
    app.on('before-quit', () => server.close())
  })
}

exports.adjustRenderer = protocol => {
  const paths = ['_next', 'static']

  protocol.interceptFileProtocol('file', (request, callback) => {
    let filePath = request.url.substr('file'.length + 1)

    for (const replacement of paths) {
      const wrongPath = '///' + replacement
      const rightPath = '//' + resolvePath('./renderer') + '/' + replacement

      filePath = filePath.replace(wrongPath, rightPath)
    }

    callback({ path: filePath })
  })
}
<|endoftext|>


<commit_before>
//really simple data replication.

var EventEmitter = require('events').EventEmitter
var i = require('iterate')
var duplex = require('duplex')
var inherits = require('util').inherits
var serializer = require('stream-serializer')
var u = require('./util')
var ReadableStream = require('readable-stream')

exports =
module.exports = Scuttlebutt

exports.createID = u.createID
exports.updateIsRecent = u.filter
exports.filter = u.filter
exports.timestamp = u.timestamp

function dutyOfSubclass() {
  throw new Error('method must be implemented by subclass')
}

function validate (data) {
  var key = data[0], ts = data[2], source = data[3]

  if(  !Array.isArray(data)
    || data.length < 4
    || 'string'    !== typeof key
    || 'string'    !== typeof source
    || 'number'    !== typeof ts
  )
    return false

  return true
}

inherits (Scuttlebutt, EventEmitter)

function Scuttlebutt (opts) {

  if(!(this instanceof Scuttlebutt)) return new Scuttlebutt(opts)
  var id = 'string' === typeof opts ? opts : opts && opts.id
  this.sources = {}

  if(opts && opts.sign && opts.verify) {
    // id should be camelcased "Id" not "ID".
    // as it's a abbreviation, not an acronym.
    this.id      = opts.id || opts.createId()
    this._sign   = opts.sign
    this._verify = opts.verify
  } else {
    this.id = id || u.createId()
  }
}

var sb = Scuttlebutt.prototype

var emit = EventEmitter.prototype.emit

sb.applyUpdate = dutyOfSubclass
sb.history      = dutyOfSubclass

sb.localUpdate = function (key, value) {
  this._update([key, value, u.timestamp(), this.id])
  return this
}

//checks whether this update is valid.

sb._update = function (update) {
  var ts = update[2]
  var source = update[3]
  //if this message is old for it's source,
  //ignore it. it's out of order.
  //each node must emit it's changes in order!

  var latest = this.sources[source]
  if(latest && latest >= ts)
    return emit.call(this, 'old_data', update), false

  this.sources[source] = ts

  var self = this
  function didVerification (err, verified) {

    // I'm not sure how what should happen if a async verification
    // errors. if it's an key not found - that is a verification fail,
    // not a error. if it's genunie error, really you should queue and
    // try again? or replay the message later
    // -- this should be done my the security plugin though, not scuttlebutt.

    if(err)
      self.emit('error', err)

    if(!verified)
      return EventEmitter.prototype.emit.call(self, 'unverified_data', update)

    // check if this message is older than
    // the value we already have.
    // do nothing if so
    // emit an 'old-data' event because i'll want to track how many
    // unnecessary messages are sent.

    if(self.applyUpdate(update))
      emit.call(self, '_update', update)

  }

  if(source !== this.id) {
    if(this._verify)
      this._verify(update, didVerification)
    else
      didVerification(null, true)
  } else {
    if(this._sign) {
      //could make this async easily enough.
      update[4] = this._sign(update)
    }
    didVerification(null, true)
  }

  return true
}

sb.createStream = function (opts) {
  var self = this
  //the sources for the remote end.
  var sources = {}, other
  var syncSent = false, syncRecv = false
  var d = duplex()
  d.name = opts && opts.name
  var outer = serializer(opts && opts.wrapper)(d)
  outer.inner = d
  d
    .on('write', function (data) {
    //if it's an array, it's an update.
    //if it's an object, it's a scuttlebut digest.
      if(Array.isArray(data)) {
        if(validate(data))
          return self._update(data)
      }
      //suppose we want to disconnect after we have synced?
      else if('object' === typeof data && data) {
        //when the digest is recieved from the other end,
        //send the history.
        //merge with the current list of sources.
        sources = data
        i.each(self.history(sources), d.emitData.bind(d))

        d.emitData('SYNC')
        syncSent = true
        if(syncRecv) outer.emit('sync')
      }
      else if('string' === typeof data && data == 'SYNC') {
        syncRecv = true
        if(syncSent) outer.emit('sync')
      }
    }).on('ended', function () {
      //d.emitEnd()
    })
    .on('close', function () {
      self.removeListener('data', onUpdate)
    })

  if(opts && opts.end) {
    outer.on('sync', function () {
      process.nextTick(function () {
        d.emitEnd()
      })
    })
  }
  function onUpdate (update) { //key, value, source, ts
    if(!u.filter(update, sources))
      return

    //if I put this after source[source]= ... it breaks tests
    d.emitData(update)

    //really, this should happen before emitting.
    var ts = update[2]
    var source = update[3]
    sources[source] = ts
  }
  d.emitData(self.sources)
  outer.on('sync', function () {
    self.on('_update', onUpdate)
  })
  return outer
}

//createReadStream -- for persisting.

sb.createReadStream = function (opts) {
  opts = opts || {}

  //write this.id
  //then write the histroy.
  //then, if opts.tail
  //listen for updates, else emit 'end'

  var out = this.history()
  out.unshift(this.id)

  var wrapper = ({
    json: function (e) { return JSON.stringify(e) + '\n' },
    raw: function (e) { return e }
  }) [opts.wrapper || 'json']

  var rs = new ReadableStream()
  rs.read = function () {
    var data = out.shift()

    if(!data && !opts.tail)
      return this.emit('end'), null

    return wrapper(data)
  }

  if(opts.tail) {
    this.on('_update', function (update) {
      out.push(update)
      rs.emit('readable')
    })
  }

  return rs
}

sb.createWriteStream = function (opts) {
  opts = opts || {}
  var Stream = require('stream')
  var ws = new Stream()
  var self = this, first = true
  ws.writable = true
  ws.write = function (data) {
    if(!this.writable) return
    if('string' === typeof data)
      return self.id = data, true
    first = false
    self.applyUpdate(data)
    return true
  }
  ws.end = function () {
    this.writable = false
    self.sync = true
    self.emit('sync')
  }
  ws.destroy = function () {
    this.writable = false
    this.emit('close')
  }

  return serializer(opts.wrapper)(ws)
}
<commit_msg>synced event<commit_after>
//really simple data replication.

var EventEmitter = require('events').EventEmitter
var i = require('iterate')
var duplex = require('duplex')
var inherits = require('util').inherits
var serializer = require('stream-serializer')
var u = require('./util')
var ReadableStream = require('readable-stream')

exports =
module.exports = Scuttlebutt

exports.createID = u.createID
exports.updateIsRecent = u.filter
exports.filter = u.filter
exports.timestamp = u.timestamp

function dutyOfSubclass() {
  throw new Error('method must be implemented by subclass')
}

function validate (data) {
  var key = data[0], ts = data[2], source = data[3]

  if(  !Array.isArray(data)
    || data.length < 4
    || 'string'    !== typeof key
    || 'string'    !== typeof source
    || 'number'    !== typeof ts
  )
    return false

  return true
}

inherits (Scuttlebutt, EventEmitter)

function Scuttlebutt (opts) {

  if(!(this instanceof Scuttlebutt)) return new Scuttlebutt(opts)
  var id = 'string' === typeof opts ? opts : opts && opts.id
  this.sources = {}

  if(opts && opts.sign && opts.verify) {
    // id should be camelcased "Id" not "ID".
    // as it's a abbreviation, not an acronym.
    this.id      = opts.id || opts.createId()
    this._sign   = opts.sign
    this._verify = opts.verify
  } else {
    this.id = id || u.createId()
  }
}

var sb = Scuttlebutt.prototype

var emit = EventEmitter.prototype.emit

sb.applyUpdate = dutyOfSubclass
sb.history      = dutyOfSubclass

sb.localUpdate = function (key, value) {
  this._update([key, value, u.timestamp(), this.id])
  return this
}

//checks whether this update is valid.

sb._update = function (update) {
  var ts = update[2]
  var source = update[3]
  //if this message is old for it's source,
  //ignore it. it's out of order.
  //each node must emit it's changes in order!

  var latest = this.sources[source]
  if(latest && latest >= ts)
    return emit.call(this, 'old_data', update), false

  this.sources[source] = ts

  var self = this
  function didVerification (err, verified) {

    // I'm not sure how what should happen if a async verification
    // errors. if it's an key not found - that is a verification fail,
    // not a error. if it's genunie error, really you should queue and
    // try again? or replay the message later
    // -- this should be done my the security plugin though, not scuttlebutt.

    if(err)
      self.emit('error', err)

    if(!verified)
      return EventEmitter.prototype.emit.call(self, 'unverified_data', update)

    // check if this message is older than
    // the value we already have.
    // do nothing if so
    // emit an 'old-data' event because i'll want to track how many
    // unnecessary messages are sent.

    if(self.applyUpdate(update))
      emit.call(self, '_update', update)

  }

  if(source !== this.id) {
    if(this._verify)
      this._verify(update, didVerification)
    else
      didVerification(null, true)
  } else {
    if(this._sign) {
      //could make this async easily enough.
      update[4] = this._sign(update)
    }
    didVerification(null, true)
  }

  return true
}

sb.createStream = function (opts) {
  var self = this
  //the sources for the remote end.
  var sources = {}, other
  var syncSent = false, syncRecv = false
  var d = duplex()
  d.name = opts && opts.name
  var outer = serializer(opts && opts.wrapper)(d)
  outer.inner = d
  d
    .on('write', function (data) {
    //if it's an array, it's an update.
    //if it's an object, it's a scuttlebut digest.
      if(Array.isArray(data)) {
        if(validate(data))
          return self._update(data)
      }
      //suppose we want to disconnect after we have synced?
      else if('object' === typeof data && data) {
        //when the digest is recieved from the other end,
        //send the history.
        //merge with the current list of sources.
        sources = data
        i.each(self.history(sources), d.emitData.bind(d))

        d.emitData('SYNC')
        //when we have sent all history
        outer.emit('sync')
        syncSent = true
        //when we have recieved all histoyr
        if(syncRecv) outer.emit('synced')
      }
      else if('string' === typeof data && data == 'SYNC') {
        syncRecv = true
        if(syncSent) outer.emit('synced')
      }
    }).on('ended', function () {
      //d.emitEnd()
    })
    .on('close', function () {
      self.removeListener('data', onUpdate)
    })

  if(opts && opts.end) {
    outer.on('sync', function () {
      process.nextTick(function () {
        d.emitEnd()
      })
    })
  }
  function onUpdate (update) { //key, value, source, ts
    if(!u.filter(update, sources))
      return

    //if I put this after source[source]= ... it breaks tests
    d.emitData(update)

    //really, this should happen before emitting.
    var ts = update[2]
    var source = update[3]
    sources[source] = ts
  }
  d.emitData(self.sources)
  outer.on('sync', function () {
    self.on('_update', onUpdate)
  })
  return outer
}

//createReadStream -- for persisting.

sb.createReadStream = function (opts) {
  opts = opts || {}

  //write this.id
  //then write the histroy.
  //then, if opts.tail
  //listen for updates, else emit 'end'

  var out = this.history()
  out.unshift(this.id)

  var wrapper = ({
    json: function (e) { return JSON.stringify(e) + '\n' },
    raw: function (e) { return e }
  }) [opts.wrapper || 'json']

  var rs = new ReadableStream()
  rs.read = function () {
    var data = out.shift()

    if(!data && !opts.tail)
      return this.emit('end'), null

    return wrapper(data)
  }

  if(opts.tail) {
    this.on('_update', function (update) {
      out.push(update)
      rs.emit('readable')
    })
  }

  return rs
}

sb.createWriteStream = function (opts) {
  opts = opts || {}
  var Stream = require('stream')
  var ws = new Stream()
  var self = this, first = true
  ws.writable = true
  ws.write = function (data) {
    if(!this.writable) return
    if('string' === typeof data)
      return self.id = data, true
    first = false
    self.applyUpdate(data)
    return true
  }
  ws.end = function () {
    this.writable = false
    self.sync = true
    self.emit('sync')
  }
  ws.destroy = function () {
    this.writable = false
    this.emit('close')
  }

  return serializer(opts.wrapper)(ws)
}
<|endoftext|>


<commit_before>// @flow
"use strict";

function filter (template, obj, onException) {

	// exclude what's undefined in template
	if (typeof template === "undefined") {
		return undefined;
	}

	// only check Type
	if ( _isType(template) ) {
		if ( _sameType(template, obj) ) {
			return obj;
		}

		// type mismatch
		if (onException) {
			return onException(template, obj);
		}
		return undefined;
	}

	// handle Array as True
	if (template instanceof Array) {
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: Doesn't support Array in template yet. The meaning might differ in different context. Please use custom function instead.\nTreating as `true`"
			);
		}
		return obj;
	}

	// filtering
	if ( typeof template === "object" ){
		if (typeof obj === "object") {
			var result = {};
			Object.keys(template).forEach( function (key) {
				var tmp = filter(template[key], obj[key], onException);

				if (typeof tmp !== "undefined") {
					result[key] = tmp;
				}
			});
			return result;
		}

		// type mismatch
		if (onException) {
			return onException(template, obj);
		}
		return undefined;
	}

	if ( typeof template === "function" ) {
		return template(obj);
	}

	return obj;
}

filter.prototype.merge = merge;
function merge (template, obj, onException) {

	// exclude what's undefined in template
	if (typeof template === "undefined") {
		return undefined;
	}

	// only check Type
	if ( _isType(template) ) {
		if ( _sameType(template, obj) ) {
			return obj;
		}

		// type mismatch
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: merge: Using Type Checking in template but object target object doesn't match.\nReturning template Type as result."
			);
		}
		return template;
	}

	// handle Array as True
	if (template instanceof Array) {
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: merge: Doesn't support Array in template yet. The meaning might differ in different context. Please use custom function instead.\nTreating as `true`"
			);
		}
		return obj;
	}

	// obj ? obj : template ;
	if ( typeof template === "object" ){
		if (typeof obj === "object") {
			var result = {};
			Object.keys(template).forEach( function (key) {
				var ret = merge(template[key], obj[key], onException);

				if (typeof ret !== "undefined") {
					result[key] = ret;
				} else if (typeof template[key] !== "undefined") {
					result[key] = template[key];
				}
			});
			return result;
		} else {
			// type mismatch
			if (onException) {
				return onException(
					template,
					obj,
					"obj-filter: merge: template is object but target is not.\nReturning template as result."
				);
			}
			return template;
		}
	}

	// must before "undefined" handling, so user can handle undefined if they wanted
	if ( typeof template === "function" ) {
		return template(obj);
	}

	// must after typeof template === 'function', so user can handle it if they wanted
	if (typeof obj === "undefined") {
		return template;
	}

	return obj;
}

filter.prototype.exist = exist;
function exist (template, obj, onException) {

	// exclude what's undefined in template
	if (typeof template === "undefined") {
		return undefined;
	}

	// only check Type
	if ( _isType(template) ) {
		if ( _sameType(template, obj) ) {
			return obj;
		}

		// type mismatch
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: exist: Using Type Checking in template but object target object doesn't match.\nReturning undefined."
			);
		}

		return undefined;
	}

	// handle Array as True
	if (template instanceof Array) {
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: exist: Doesn't support Array in template yet. The meaning might differ in different context. Please use custom function instead.\nTreating as `true`"
			);
		}
		return obj;
	}

	// must before "undefined" handling, so user can handle undefined if they wanted
	if (typeof template === "function") {
		return template(obj);
	}

	// must after typeof template === 'function', so user can handle it if they wanted
	if (typeof obj === "undefined") {
		return undefined;
	}

	// check if all keys exists recursively
	if (typeof template === "object") {
		var result = {};

		for (const key in template) {
			if (template[key] === undefined) {
				// value 'undefined' means skip
				continue;
			}

			var tmp = exist(template[key], obj[key], onException);

			if (typeof tmp !== "undefined") {
				result[key] = tmp;
			} else {
				return undefined;
			}
		}

		return result;
	}

	// return whatever obj has
	return obj;
}

function _isType (template) {
	if (
		(template === String)
		|| (template === Number)
		|| (template === Boolean)
		|| (template === Array)
		|| (template === Symbol)
		|| (template === Map)
		|| (template === Set)
		|| (template === WeakMap)
		|| (template === WeakSet)
		|| (template === Object)
		|| (template === Function)
	) {
		return true;
	}

	return false;
}

function _sameType (template, obj) {
	if (
		(template === String && typeof obj === "string")
		|| (template === Number && typeof obj === "number")
		|| (template === Boolean && typeof obj === "boolean")
		|| (template === Array && Array.isArray(obj))
		|| (template === Symbol && obj instanceof Symbol)
		|| (template === Map && obj instanceof Map)
		|| (template === Set && obj instanceof Set)
		|| (template === WeakMap && obj instanceof WeakMap)
		|| (template === WeakSet && obj instanceof WeakSet)
		|| (template === Function && typeof obj === "function")
		|| (template === Object && typeof obj === "object")
	) {
		return true;
	}

	return false;
}

filter.prototype.ArrayIter = ArrayIter;
function ArrayIter (checker, template, {min = 0, onException} = {}) {

	if (typeof(checker) !== "function") {
		if (onException) {
			return onException(undefined, undefined, "obj-filter: First argument of ArrayIter must be a function of obj-filter");
		} else {
			throw new Error("obj-filter: First argument of ArrayIter must be a function of obj-filter");
		}
	}

	return function (array) {
		if (!(array instanceof Array)) {
			return undefined;
		}

		let result = array.map((value) => {
			return checker(template, value, onException);
		}).filter((x) => x !== undefined);

		return result.length >= min ? result : undefined;
	};
}

module.exports = filter;
module.exports.filter = filter;
module.exports.merge = merge;
module.exports.exist = exist;
module.exports.ArrayIter = ArrayIter;
<commit_msg>fix codacy warnings<commit_after>// @flow
"use strict";

function _isType (template) {
	if (
		(template === String)
		|| (template === Number)
		|| (template === Boolean)
		|| (template === Array)
		|| (template === Symbol)
		|| (template === Map)
		|| (template === Set)
		|| (template === WeakMap)
		|| (template === WeakSet)
		|| (template === Object)
		|| (template === Function)
	) {
		return true;
	}

	return false;
}

function _sameType (template, obj) {
	if (
		(template === String && typeof obj === "string")
		|| (template === Number && typeof obj === "number")
		|| (template === Boolean && typeof obj === "boolean")
		|| (template === Array && Array.isArray(obj))
		|| (template === Symbol && obj instanceof Symbol)
		|| (template === Map && obj instanceof Map)
		|| (template === Set && obj instanceof Set)
		|| (template === WeakMap && obj instanceof WeakMap)
		|| (template === WeakSet && obj instanceof WeakSet)
		|| (template === Function && typeof obj === "function")
		|| (template === Object && typeof obj === "object")
	) {
		return true;
	}

	return false;
}

function filter (template, obj, onException) {

	// exclude what's undefined in template
	if (typeof template === "undefined") {
		return undefined;
	}

	// only check Type
	if ( _isType(template) ) {
		if ( _sameType(template, obj) ) {
			return obj;
		}

		// type mismatch
		if (onException) {
			return onException(template, obj);
		}
		return undefined;
	}

	// handle Array as True
	if (template instanceof Array) {
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: Doesn't support Array in template yet. The meaning might differ in different context. Please use custom function instead.\nTreating as `true`"
			);
		}
		return obj;
	}

	// filtering
	if ( typeof template === "object" ){
		if (typeof obj === "object") {
			var result = {};
			Object.keys(template).forEach( function (key) {
				var tmp = filter(template[key], obj[key], onException);

				if (typeof tmp !== "undefined") {
					result[key] = tmp;
				}
			});
			return result;
		}

		// type mismatch
		if (onException) {
			return onException(template, obj);
		}
		return undefined;
	}

	if ( typeof template === "function" ) {
		return template(obj);
	}

	return obj;
}

function merge (template, obj, onException) {

	// exclude what's undefined in template
	if (typeof template === "undefined") {
		return undefined;
	}

	// only check Type
	if ( _isType(template) ) {
		if ( _sameType(template, obj) ) {
			return obj;
		}

		// type mismatch
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: merge: Using Type Checking in template but object target object doesn't match.\nReturning template Type as result."
			);
		}
		return template;
	}

	// handle Array as True
	if (template instanceof Array) {
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: merge: Doesn't support Array in template yet. The meaning might differ in different context. Please use custom function instead.\nTreating as `true`"
			);
		}
		return obj;
	}

	// obj ? obj : template ;
	if ( typeof template === "object" ){
		if (typeof obj === "object") {
			var result = {};
			Object.keys(template).forEach( function (key) {
				var ret = merge(template[key], obj[key], onException);

				if (typeof ret !== "undefined") {
					result[key] = ret;
				} else if (typeof template[key] !== "undefined") {
					result[key] = template[key];
				}
			});
			return result;
		} else {
			// type mismatch
			if (onException) {
				return onException(
					template,
					obj,
					"obj-filter: merge: template is object but target is not.\nReturning template as result."
				);
			}
			return template;
		}
	}

	// must before "undefined" handling, so user can handle undefined if they wanted
	if ( typeof template === "function" ) {
		return template(obj);
	}

	// must after typeof template === 'function', so user can handle it if they wanted
	if (typeof obj === "undefined") {
		return template;
	}

	return obj;
}

function exist (template, obj, onException) {

	// exclude what's undefined in template
	if (typeof template === "undefined") {
		return undefined;
	}

	// only check Type
	if ( _isType(template) ) {
		if ( _sameType(template, obj) ) {
			return obj;
		}

		// type mismatch
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: exist: Using Type Checking in template but object target object doesn't match.\nReturning undefined."
			);
		}

		return undefined;
	}

	// handle Array as True
	if (template instanceof Array) {
		if (onException) {
			return onException(
				template,
				obj,
				"obj-filter: exist: Doesn't support Array in template yet. The meaning might differ in different context. Please use custom function instead.\nTreating as `true`"
			);
		}
		return obj;
	}

	// must before "undefined" handling, so user can handle undefined if they wanted
	if (typeof template === "function") {
		return template(obj);
	}

	// must after typeof template === 'function', so user can handle it if they wanted
	if (typeof obj === "undefined") {
		return undefined;
	}

	// check if all keys exists recursively
	if (typeof template === "object") {
		var result = {};

		for (const key in template) {
			if (template[key] === undefined) {
				// value 'undefined' means skip
				continue;
			}

			var tmp = exist(template[key], obj[key], onException);

			if (typeof tmp !== "undefined") {
				result[key] = tmp;
			} else {
				return undefined;
			}
		}

		return result;
	}

	// return whatever obj has
	return obj;
}

function ArrayIter (checker, template, {min = 0, onException} = {}) {

	if (typeof(checker) !== "function") {
		if (onException) {
			return onException(undefined, undefined, "obj-filter: First argument of ArrayIter must be a function of obj-filter");
		} else {
			throw new Error("obj-filter: First argument of ArrayIter must be a function of obj-filter");
		}
	}

	return function (array) {
		if (!(array instanceof Array)) {
			return undefined;
		}

		let result = array.map((value) => {
			return checker(template, value, onException);
		}).filter((x) => x !== undefined);

		return result.length >= min ? result : undefined;
	};
}

filter.prototype.merge = merge;
filter.prototype.exist = exist;
filter.prototype.ArrayIter = ArrayIter;

module.exports = filter;
module.exports.filter = filter;
module.exports.merge = merge;
module.exports.exist = exist;
module.exports.ArrayIter = ArrayIter;
<|endoftext|>


<commit_before>'use strict';
const path = require('path');
const childProcess = require('child_process');
const isWsl = require('is-wsl');

module.exports = (target, opts) => {
	if (typeof target !== 'string') {
		return Promise.reject(new Error('Expected a `target`'));
	}

	opts = Object.assign({wait: true}, opts);

	let cmd;
	let appArgs = [];
	let args = [];
	const cpOpts = {};

	if (Array.isArray(opts.app)) {
		appArgs = opts.app.slice(1);
		opts.app = opts.app[0];
	}

	if (process.platform === 'darwin') {
		cmd = 'open';

		if (opts.wait) {
			args.push('-W');
		}

		if (opts.app) {
			args.push('-a', opts.app);
		}
	} else if (process.platform === 'win32' || isWsl) {
		cmd = 'cmd' + (isWsl ? '.exe' : '');
		args.push('/c', 'start', '""', '/b');
		target = target.replace(/&/g, '^&');

		if (opts.wait) {
			args.push('/wait');
		}

		if (opts.app) {
			args.push(opts.app);
		}

		if (appArgs.length > 0) {
			args = args.concat(appArgs);
		}
	} else {
		if (opts.app) {
			cmd = opts.app;
		} else {
			cmd = path.join(__dirname, 'xdg-open');
		}

		if (appArgs.length > 0) {
			args = args.concat(appArgs);
		}

		if (!opts.wait) {
			// `xdg-open` will block the process unless
			// stdio is ignored even if it's unref'd
			cpOpts.stdio = 'ignore';
		}
	}

	args.push(target);

	if (process.platform === 'darwin' && appArgs.length > 0) {
		args.push('--args');
		args = args.concat(appArgs);
	}

	const cp = childProcess.spawn(cmd, args, cpOpts);

	if (opts.wait) {
		return new Promise((resolve, reject) => {
			cp.once('error', reject);

			cp.once('close', code => {
				if (code > 0) {
					reject(new Error('Exited with code ' + code));
					return;
				}

				resolve(cp);
			});
		});
	}

	cp.unref();

	return Promise.resolve(cp);
};
<commit_msg>Run process detached from its parent on Linux (#42)<commit_after>'use strict';
const path = require('path');
const childProcess = require('child_process');
const isWsl = require('is-wsl');

module.exports = (target, opts) => {
	if (typeof target !== 'string') {
		return Promise.reject(new Error('Expected a `target`'));
	}

	opts = Object.assign({wait: true}, opts);

	let cmd;
	let appArgs = [];
	let args = [];
	const cpOpts = {};

	if (Array.isArray(opts.app)) {
		appArgs = opts.app.slice(1);
		opts.app = opts.app[0];
	}

	if (process.platform === 'darwin') {
		cmd = 'open';

		if (opts.wait) {
			args.push('-W');
		}

		if (opts.app) {
			args.push('-a', opts.app);
		}
	} else if (process.platform === 'win32' || isWsl) {
		cmd = 'cmd' + (isWsl ? '.exe' : '');
		args.push('/c', 'start', '""', '/b');
		target = target.replace(/&/g, '^&');

		if (opts.wait) {
			args.push('/wait');
		}

		if (opts.app) {
			args.push(opts.app);
		}

		if (appArgs.length > 0) {
			args = args.concat(appArgs);
		}
	} else {
		if (opts.app) {
			cmd = opts.app;
		} else {
			cmd = path.join(__dirname, 'xdg-open');
		}

		if (appArgs.length > 0) {
			args = args.concat(appArgs);
		}

		if (!opts.wait) {
			// `xdg-open` will block the process unless
			// stdio is ignored and it's detached from the parent
			// even if it's unref'd
			cpOpts.stdio = 'ignore';
			cpOpts.detached = true;
		}
	}

	args.push(target);

	if (process.platform === 'darwin' && appArgs.length > 0) {
		args.push('--args');
		args = args.concat(appArgs);
	}

	const cp = childProcess.spawn(cmd, args, cpOpts);

	if (opts.wait) {
		return new Promise((resolve, reject) => {
			cp.once('error', reject);

			cp.once('close', code => {
				if (code > 0) {
					reject(new Error('Exited with code ' + code));
					return;
				}

				resolve(cp);
			});
		});
	}

	cp.unref();

	return Promise.resolve(cp);
};
<|endoftext|>


<commit_before>/**
 * Created by Kyriakos Barbounakis<k.barbounakis@gmail.com> on 26/11/2014.
 *
 * Copyright (c) 2014, Kyriakos Barbounakis k.barbounakis@gmail.com
 Anthi Oikonomou anthioikonomou@gmail.com
 All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.
 * Neither the name of MOST Web Framework nor the names of its
 contributors may be used to endorse or promote products derived from
 this software without specific prior written permission.
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
var async = require('async'),
    util = require('util'),
    qry = require('most-query'),
    sqlite3 = require('sqlite3').verbose();
/**
 * @class SQLiteAdapter
 * @augments DataAdapter
 * @param {*} options
 * @constructor
 */
function SQLiteAdapter(options) {
    /**
     * @type {{database: string}}
     */
    this.options = options || { database: ':memory:' };
    /**
     * Represents the database raw connection associated with this adapter
     * @type {*}
     */
    this.rawConnection = null;
}

SQLiteAdapter.prototype.open = function(callback) {
    var self = this;
    callback = callback || function() {};
    if (self.rawConnection) {
        callback();
    }
    else {
        //try to open or create database
        self.rawConnection = new sqlite3.Database(self.options.database,6, function(err) {
            if (err) {
                self.rawConnection = null;;
            }
            callback(err);

        });
    }
};

SQLiteAdapter.prototype.close = function(callback) {
    var self = this;
    callback = callback || function() {};
    try {
        if (self.rawConnection)
        {
            //close connection
            self.rawConnection.close(function() {
                //and finally return
                callback();
            });
        }
        else {
            callback();
        }

    }
    catch (e) {
        console.log('An error occured while closing database.');
        console.log(e.message);
        //call callback without error
        callback();
    }
};

/**
 * @param {string} query
 * @param {*=} values
 */
SQLiteAdapter.prototype.prepare = function(query,values) {
    return qry.prepare(query,values)
};

SQLiteAdapter.formatType = function(field)
{
    var size = parseInt(field.size);
    switch (field.type)
    {
        case 'Boolean':
            s = 'INTEGER(1)';
            break;
        case 'Byte':
            s = 'INTEGER(1)';
            break;
        case 'Number':
        case 'Float':
            s = 'REAL';
            break;
        case 'Counter':
            return 'INTEGER PRIMARY KEY';
        case 'Currency':
        case 'Decimal':
            s =  'NUMERIC';
            break;
        case 'Date':
        case 'Time':
        case 'DateTime':
            s = 'NUMERIC';
            break;
        case 'Long':
        case 'Integer':
        case 'Duration':
            s = 'INTEGER';
            break;
        case 'URL':
        case 'Text':
        case 'Note':
            s =field.size ? util.format('TEXT(%s)', field.size) : 'TEXT';
            break;
        case 'Image':
        case 'Binary':
            s ='BLOB';
            break;
        case 'Guid':
            s = 'TEXT(36)';
            break;
        case 'Short':
            s = 'INTEGER';
            break;
        default:
            s = 'INTEGER';
            break;
    }
    s += field.nullable===undefined ? ' NULL': field.nullable ? ' NULL': ' NOT NULL';
    return s;
};

SQLiteAdapter.prototype.table = function(name) {
    var self = this;
    return {
        /**
         * @param {function(Error,Boolean=)} callback
         */
        exists:function(callback) {
            self.execute('SELECT COUNT(*) count FROM sqlite_master WHERE name=? AND type=\'table\';', [name], function(err, result) {
                if (err) { callback(err); return; }
                callback(null, (result[0].count>0));
            });
        },
        /**
         * @param {function(Error,string=)} callback
         */
        version:function(callback) {
            self.execute('SELECT MAX(version) AS version FROM migrations WHERE appliesTo=?',
                [name], function(err, result) {
                    if (err) { cb(err); return; }
                    if (result.length==0)
                        callback(null, '0.0');
                    else
                        callback(null, result[0].version || '0.0');
                });
        },
        /**
         * @param {function(Error,Boolean=)} callback
         */
        has_sequence:function(callback) {
            callback = callback || function() {};
            self.execute('SELECT COUNT(*) count FROM sqlite_sequence WHERE name=?',
                [name], function(err, result) {
                    if (err) { callback(err); return; }
                    callback(null, (result[0].count>0));
                });
        },
        /**
         * @param {function(Error,{columnName:string,ordinal:number,dataType:*, maxLength:number,isNullable:number }[]=)} callback
         */
        columns:function(callback) {
            callback = callback || function() {};
            self.execute('PRAGMA table_info(?)',
                [name], function(err, result) {
                    if (err) { callback(err); return; }
                    var arr = [];
                    result.forEach(function(x) {
                        arr.push({ columnName: x.name, ordinal: x.cid, dataType: x.type,isNullable: x.notnull ? true : false });
                    });
                    callback(null, arr);
                });
        }
    }

}
/**
 * Executes a query against the underlying database
 * @param query {QueryExpression|string|*}
 * @param values {*=}
 * @param {function(Error=,*=)} callback
 */
SQLiteAdapter.prototype.execute = function(query, values, callback) {
    var self = this, sql = null;
    try {

        if (typeof query == 'string') {
            //get raw sql statement
            //todo: this operation may be obsolete (for security reasons)
            sql = query;
        }
        else {
            //format query expression or any object that may be act as query expression
            var formatter = new SqliteFormatter();
            sql = formatter.format(query);
        }
        //validate sql statement
        if (typeof sql !== 'string') {
            callback.call(self, new Error('The executing command is of the wrong type or empty.'));
            return;
        }
        //ensure connection
        self.open(function(err) {
            if (err) {
                callback.call(self, err);
            }
            else {
                //log statement (optional)
                if (process.env.NODE_ENV==='development')
                    console.log(util.format('SQL:%s, Parameters:%s', sql, JSON.stringify(values)));
                //prepare statement - the traditional way
                var prepared = self.prepare(sql, values), params, fn;
                //validate statement
                if (/^(SELECT|PRAGMA)/ig.test(prepared)) {
                    //prepare for select
                    fn = self.rawConnection.all;
                }
                else {
                    //otherwise prepare for run
                    fn = self.rawConnection.run;
                }
                //execute raw command
                fn.call(self.rawConnection, prepared, params , function(err, result) {
                    if (err) {
                        //log sql
                        console.log(util.format('SQL Error:%s', prepared));
                        callback(err);
                    }
                    else {
                        if (result)
                            callback(null, result);
                        else
                            callback();
                    }
                });
            }
        });
    }
    catch (e) {
        callback.call(self, e);
    }

};

/**
 * @class PGSqlFormatter
 * @constructor
 * @augments {SqlFormatter}
 */
function SqliteFormatter() {
    this.settings = {
        nameFormat:SqliteFormatter.NAME_FORMAT
    }
}
util.inherits(SqliteFormatter, qry.classes.SqlFormatter);

SqliteFormatter.NAME_FORMAT = '$1';

var sqli = {
    /**
     * @constructs SQLiteAdapter
     * */
    SQLiteAdapter : SQLiteAdapter,
    /**
     * Creates an instance of SQLiteAdapter object that represents a sqlite database connection.
     * @param {*} options An object that represents the properties of the underlying database connection.
     * @returns {DataAdapter|*}
     */
    createInstance: function(options) {
        return new SQLiteAdapter(options);
    }
}

if (typeof exports !== 'undefined')
{
    module.exports = sqli;
}

<commit_msg>sqlite adapter update<commit_after>/**
 * Created by Kyriakos Barbounakis<k.barbounakis@gmail.com> on 26/11/2014.
 *
 * Copyright (c) 2014, Kyriakos Barbounakis k.barbounakis@gmail.com
 Anthi Oikonomou anthioikonomou@gmail.com
 All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following disclaimer in the documentation
 and/or other materials provided with the distribution.
 * Neither the name of MOST Web Framework nor the names of its
 contributors may be used to endorse or promote products derived from
 this software without specific prior written permission.
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
var async = require('async'),
    util = require('util'),
    qry = require('most-query'),
    sqlite3 = require('sqlite3').verbose();
/**
 * @class SQLiteAdapter
 * @augments DataAdapter
 * @param {*} options
 * @constructor
 */
function SQLiteAdapter(options) {
    /**
     * @type {{database: string}}
     */
    this.options = options || { database: ':memory:' };
    /**
     * Represents the database raw connection associated with this adapter
     * @type {*}
     */
    this.rawConnection = null;
}

SQLiteAdapter.prototype.open = function(callback) {
    var self = this;
    callback = callback || function() {};
    if (self.rawConnection) {
        callback();
    }
    else {
        //try to open or create database
        self.rawConnection = new sqlite3.Database(self.options.database,6, function(err) {
            if (err) {
                self.rawConnection = null;;
            }
            callback(err);

        });
    }
};

SQLiteAdapter.prototype.close = function(callback) {
    var self = this;
    callback = callback || function() {};
    try {
        if (self.rawConnection)
        {
            //close connection
            self.rawConnection.close(function() {
                //and finally return
                callback();
            });
        }
        else {
            callback();
        }

    }
    catch (e) {
        console.log('An error occured while closing database.');
        console.log(e.message);
        //call callback without error
        callback();
    }
};

/**
 * @param {string} query
 * @param {*=} values
 */
SQLiteAdapter.prototype.prepare = function(query,values) {
    return qry.prepare(query,values)
};

SQLiteAdapter.formatType = function(field)
{
    var size = parseInt(field.size);
    switch (field.type)
    {
        case 'Boolean':
            s = 'INTEGER(1,0)';
            break;
        case 'Byte':
            s = 'INTEGER(1,0)';
            break;
        case 'Number':
        case 'Float':
            s = 'REAL';
            break;
        case 'Counter':
            return 'INTEGER PRIMARY KEY AUTOINCREMENT';
        case 'Currency':
        case 'Decimal':
            s =  'NUMERIC';
            break;
        case 'Date':
        case 'Time':
        case 'DateTime':
            s = 'NUMERIC';
            break;
        case 'Long':
        case 'Integer':
        case 'Duration':
            s = 'INTEGER';
            break;
        case 'URL':
        case 'Text':
        case 'Note':
            s =field.size ? util.format('TEXT(%s,0)', field.size) : 'TEXT';
            break;
        case 'Image':
        case 'Binary':
            s ='BLOB';
            break;
        case 'Guid':
            s = 'TEXT(36,0)';
            break;
        case 'Short':
            s = 'INTEGER(2,0)';
            break;
        default:
            s = 'INTEGER';
            break;
    }
    s += field.nullable===undefined ? ' NULL': field.nullable ? ' NULL': ' NOT NULL';
    return s;
};

/**
 * Begins a transactional operation by executing the given function
 * @param fn {function} The function to execute
 * @param callback {function(Error=)} The callback that contains the error -if any- and the results of the given operation
 */
SQLiteAdapter.prototype.executeInTransaction = function(fn, callback) {
    var self = this;
    //ensure parameters
    fn = fn || function() {}; callback = callback || function() {};
    self.open(function(err) {
        if (err) {
            callback(err);
        }
        else {
            if (self.transaction) {
                fn.call(self, function(err) {
                    callback(err);
                });
            }
            else {
                //begin transaction
                self.rawConnection.run('BEGIN TRANSACTION;', undefined, function(err) {
                    if (err) {
                        callback(err);
                        return;
                    }
                    //initialize dummy transaction object (for future use)
                    self.transaction = { };
                    //execute function
                    fn.call(self, function(err) {
                        if (err) {
                            //rollback transaction
                            self.rawConnection.run('ROLLBACK;', undefined, function() {
                                self.transaction = null;
                                callback(err);
                            });
                        }
                        else {
                            //commit transaction
                            self.rawConnection.run('COMMIT;', undefined, function(err) {
                                self.transaction = null;
                                callback(err);
                            });
                        }
                    });
                });
            }
        }
    });
};

/**
 *
 * @param {string} name
 * @param {QueryExpression|*} query
 * @param {function(Error=)} callback
 */
SQLiteAdapter.prototype.createView = function(name, query, callback) {
    this.view(name).create(query, callback);
};

/*
 * @param {DataModelMigration|*} obj An Object that represents the data model scheme we want to migrate
 * @param {function(Error=)} callback
 */
SQLiteAdapter.prototype.migrate = function(obj, callback) {
    var self = this;
    callback = callback || function() {};
    if (typeof obj === 'undefined' || obj == null) { callback(); return; }
    /**
     * @type {DataModelMigration|*}
     */
    var migration = obj;

    var format = function(format, obj)
    {
        var result = format;
        if (/%t/.test(format))
            result = result.replace(/%t/g,SQLiteAdapter.formatType(obj));
        if (/%f/.test(format))
            result = result.replace(/%f/g,obj.name);
        return result;
    }

    async.waterfall([
        //1. Check migrations table existence
        function(cb) {
            if (SQLiteAdapter.supportMigrations) {
                cb(null, true);
                return;
            }
            self.table('migrations').exists(function(err, exists) {
                if (err) { cb(err); return; }
                cb(null, exists);
            });
        },
        //2. Create migrations table, if it does not exist
        function(arg, cb) {
            if (arg) { cb(null, 0); return; }
            //create migrations table
            self.execute('CREATE TABLE migrations("id" INTEGER PRIMARY KEY AUTOINCREMENT, ' +
                '"appliesTo" TEXT NOT NULL, "model" TEXT NULL, "description" TEXT,"version" TEXT NOT NULL)',
                [], function(err) {
                    if (err) { cb(err); return; }
                    SQLiteAdapter.supportMigrations=true;
                    cb(null, 0);
                });
        },
        //3. Check if migration has already been applied (true=Table version is equal to migration version, false=Table version is older from migration version)
        function(arg, cb) {
            self.table(migration.appliesTo).version(function(err, version) {
                if (err) { cb(err); return; }
                cb(null, (version>=migration.version));
            });
        },
        //4a. Check table existence (-1=Migration has already been applied, 0=Table does not exist, 1=Table exists)
        function(arg, cb) {
            //migration has already been applied (set migration.updated=true)
            if (arg) {
                migration['updated']=true;
                cb(null, -1);
            }
            else {
                self.table(migration.appliesTo).exists(function(err, exists) {
                    if (err) { cb(err); return; }
                    cb(null, exists ? 1 : 0);
                });

            }
        },
        //4. Get table columns
        function(arg, cb) {
            //migration has already been applied
            if (arg<0) { cb(null, [arg, null]); return; }
            self.table(migration.appliesTo).columns(function(err, columns) {
                if (err) { cb(err); return; }
                cb(null, [arg, columns]);
            });
        },
        //5. Migrate target table (create or alter)
        function(args, cb) {
            //migration has already been applied (args[0]=-1)
            if (args[0] < 0) {
                cb(null, args[0]);
            }
            else if (args[0] == 0) {
                //create table
                var strFields = migration.add.filter(function(x) {
                    return !x['oneToMany']
                }).map(
                    function(x) {
                        return format('"%f" %t', x);
                    }).join(', ');
                var sql = util.format('CREATE TABLE "%s" (%s)', migration.appliesTo, strFields);
                self.execute(sql, null, function(err) {
                    if (err) { cb(err); return; }
                    cb(null, 1);
                });
            }
            else if (args[0] == 1) {
                var expressions = [];
                //alter table
                if (util.isArray(migration.remove)) {
                    if (migration.remove>0) {
                        //todo::support drop column (rename table, create new and copy data)
                        cb(new Error('Drop column is not supported. This operation is not yet implemented.'));
                        return;
                    }
                }
                if (util.isArray(migration.change)) {
                    if (migration.change>0) {
                        //todo::support alter column (rename table, create new and copy data)
                        cb(new Error('Alter column is not supported. This operation is not yet implemented.'));
                        return;
                    }
                }
                if (util.isArray(migration.add)) {
                    migration.add.forEach(function(x) {
                        expressions.push(util.format('ALTER TABLE "%s" ADD COLUMN "%s" %s', migration.appliesTo, x.name, SQLiteAdapter.formatType(x)));
                    });
                }
                if (expressions.length>0) {
                    self.execute(expressions.join(';'), [], function(err)
                    {
                        if (err) { cb(err); return; }
                        cb(null, 1);
                    });
                }
                else {
                    cb(null, 2);
                }
            }
            else {
                cb(new Error('Invalid table status.'));
            }
        },
        function(arg, cb) {
            if (arg>0) {
                //log migration to database
                self.execute('INSERT INTO migrations("appliesTo", "model", "version", "description") VALUES (?,?,?,?)', [migration.appliesTo,
                    migration.model,
                    migration.version,
                    migration.description ], function(err, result) {
                    if (err)  {
                        cb(err);
                        return;
                    }
                    cb(null, 1);
                });
            }
            else {
                migration['updated'] = true;
                cb(null, arg);
            }
        }
    ], function(err) {
        callback(err);
    })

};

/**
 * Produces a new identity value for the given entity and attribute.
 * @param entity {String} The target entity name
 * @param attribute {String} The target attribute
 * @param callback {Function=}
 */
SQLiteAdapter.prototype.selectIdentity = function(entity, attribute , callback) {

    var self = this;

    var migration = {
        appliesTo:'increment_id',
        model:'increments',
        description:'Increments migration (version 1.0)',
        version:'1.0',
        add:[
            { name:'id', type:'Counter', primary:true },
            { name:'entity', type:'Text', size:120 },
            { name:'attribute', type:'Text', size:120 },
            { name:'value', type:'Integer' }
        ]
    }
    //ensure increments entity
    self.migrate(migration, function(err)
    {
        //throw error if any
        if (err) { callback.call(self,err); return; }
        self.execute('SELECT * FROM increment_id WHERE entity=? AND attribute=?', [entity, attribute], function(err, result) {
            if (err) { callback.call(self,err); return; }
            if (result.length==0) {
                //get max value by querying the given entity
                var q = qry.query(entity).select([qry.fields.max(attribute)]);
                self.execute(q,null, function(err, result) {
                    if (err) { callback.call(self, err); return; }
                    var value = 1;
                    if (result.length>0) {
                        value = parseInt(result[0][attribute]) + 1;
                    }
                    self.execute('INSERT INTO increment_id(entity, attribute, value) VALUES (?,?,?)',[entity, attribute, value], function(err) {
                        //throw error if any
                        if (err) { callback.call(self, err); return; }
                        //return new increment value
                        callback.call(self, err, value);
                    });
                });
            }
            else {
                //get new increment value
                var value = parseInt(result[0].value) + 1;
                self.execute('UPDATE increment_id SET value=? WHERE id=?',[value, result[0].id], function(err) {
                    //throw error if any
                    if (err) { callback.call(self, err); return; }
                    //return new increment value
                    callback.call(self, err, value);
                });
            }
        });
    });
};

/**
 * Executes an operation against database and returns the results.
 * @param {DataModelBatch} batch
 * @param {function(Error=)} callback
 */
SQLiteAdapter.prototype.executeBatch = function(batch, callback) {
    callback = callback || function() {};
    callback(new Error('DataAdapter.executeBatch() is obsolete. Use DataAdapter.executeInTransaction() instead.'));
};

SQLiteAdapter.prototype.table = function(name) {
    var self = this;
    return {
        /**
         * @param {function(Error,Boolean=)} callback
         */
        exists:function(callback) {
            self.execute('SELECT COUNT(*) count FROM sqlite_master WHERE name=? AND type=\'table\';', [name], function(err, result) {
                if (err) { callback(err); return; }
                callback(null, (result[0].count>0));
            });
        },
        /**
         * @param {function(Error,string=)} callback
         */
        version:function(callback) {
            self.execute('SELECT MAX(version) AS version FROM migrations WHERE appliesTo=?',
                [name], function(err, result) {
                    if (err) { cb(err); return; }
                    if (result.length==0)
                        callback(null, '0.0');
                    else
                        callback(null, result[0].version || '0.0');
                });
        },
        /**
         * @param {function(Error,Boolean=)} callback
         */
        has_sequence:function(callback) {
            callback = callback || function() {};
            self.execute('SELECT COUNT(*) count FROM sqlite_sequence WHERE name=?',
                [name], function(err, result) {
                    if (err) { callback(err); return; }
                    callback(null, (result[0].count>0));
                });
        },
        /**
         * @param {function(Error,{columnName:string,ordinal:number,dataType:*, maxLength:number,isNullable:number }[]=)} callback
         */
        columns:function(callback) {
            callback = callback || function() {};
            self.execute('PRAGMA table_info(?)',
                [name], function(err, result) {
                    if (err) { callback(err); return; }
                    var arr = [];
                    /**
                     * enumerates table columns
                     * @param {{name:string},{cid:number},{type:string},{notnull:number}} x
                     */
                    var iterator = function(x) {
                        arr.push({ columnName: x.name, ordinal: x.cid, dataType: x.type,isNullable: x.notnull ? true : false });
                    };
                    result.forEach(iterator);
                    callback(null, arr);
                });
        }
    }

};

SQLiteAdapter.prototype.view = function(name) {
    var self = this;
    return {
        /**
         * @param {function(Error,Boolean=)} callback
         */
        exists:function(callback) {
            self.execute('SELECT COUNT(*) count FROM sqlite_master WHERE name=? AND type=\'view\';', [name], function(err, result) {
                if (err) { callback(err); return; }
                callback(null, (result[0].count>0));
            });
        },
        /**
         * @param {function(Error=)} callback
         */
        drop:function(callback) {
            callback = callback || function() {};
            self.open(function(err) {
               if (err) { callback(err); return; }
                var sql = util.format("DROP VIEW IF EXISTS %s",name);
                self.execute(sql, undefined, function(err) {
                    if (err) { callback(err); return; }
                    callback();
                });
            });
        },
        /**
         * @param {QueryExpression|*} q
         * @param {function(Error=)} callback
         */
        create:function(q, callback) {
            var thisArg = this;
            self.executeInTransaction(function(tr) {
                thisArg.drop(function(err) {
                    if (err) { tr(err); return; }
                    try {
                        var sql = util.format("CREATE VIEW %s AS ",name);
                        var formatter = new SqliteFormatter();
                        sql += formatter.format(q);
                        self.execute(sql, undefined, tr);
                    }
                    catch(e) {
                        tr(e);
                    }
                });
            }, function(err) {
                callback(err);
            });

        }
    };
};

/**
 * Executes a query against the underlying database
 * @param query {QueryExpression|string|*}
 * @param values {*=}
 * @param {function(Error=,*=)} callback
 */
SQLiteAdapter.prototype.execute = function(query, values, callback) {
    var self = this, sql = null;
    try {

        if (typeof query == 'string') {
            //get raw sql statement
            sql = query;
        }
        else {
            //format query expression or any object that may be act as query expression
            var formatter = new SqliteFormatter();
            sql = formatter.format(query);
        }
        //validate sql statement
        if (typeof sql !== 'string') {
            callback.call(self, new Error('The executing command is of the wrong type or empty.'));
            return;
        }
        //ensure connection
        self.open(function(err) {
            if (err) {
                callback.call(self, err);
            }
            else {
                //log statement (optional)
                if (process.env.NODE_ENV==='development')
                    console.log(util.format('SQL:%s, Parameters:%s', sql, JSON.stringify(values)));
                //prepare statement - the traditional way
                var prepared = self.prepare(sql, values), params, fn;
                //validate statement
                if (/^(SELECT|PRAGMA)/ig.test(prepared)) {
                    //prepare for select
                    fn = self.rawConnection.all;
                }
                else {
                    //otherwise prepare for run
                    fn = self.rawConnection.run;
                }
                //execute raw command
                fn.call(self.rawConnection, prepared, [] , function(err, result) {
                    if (err) {
                        //log sql
                        console.log(util.format('SQL Error:%s', prepared));
                        callback(err);
                    }
                    else {
                        if (result)
                            callback(null, result);
                        else
                            callback();
                    }
                });
            }
        });
    }
    catch (e) {
        callback.call(self, e);
    }

};

SQLiteAdapter.prototype.lastIdentity = function(callback) {
    var self = this;
    self.open(function(err) {
        if (err) {
            callback(err);
        }
        else {
            //execute lastval (for sequence)
            self.execute('SELECT last_insert_rowid() as lastval', [], function(err, lastval) {
                if (err) {
                    callback(null, { insertId: null });
                }
                else {
                    lastval.rows = lastval.rows || [];
                    if (lastval.rows.length>0)
                        callback(null, { insertId:lastval.rows[0]['lastval'] });
                    else
                        callback(null, { insertId: null });
                }
            });
        }
    });
};

/**
 * @class PGSqlFormatter
 * @constructor
 * @augments {SqlFormatter}
 */
function SqliteFormatter() {
    this.settings = {
        nameFormat:SqliteFormatter.NAME_FORMAT,
        forceAlias:true
    }
}
util.inherits(SqliteFormatter, qry.classes.SqlFormatter);

SqliteFormatter.NAME_FORMAT = '$1';

SqliteFormatter.prototype.escapeName = function(name) {
    if (typeof name === 'string')
        return name.replace(/(\w+)/ig, this.settings.nameFormat);
    return name;
};

var sqli = {
    /**
     * @constructs SQLiteAdapter
     * */
    SQLiteAdapter : SQLiteAdapter,
    /**
     * Creates an instance of SQLiteAdapter object that represents a sqlite database connection.
     * @param {*} options An object that represents the properties of the underlying database connection.
     * @returns {DataAdapter|*}
     */
    createInstance: function(options) {
        return new SQLiteAdapter(options);
    }
}

if (typeof exports !== 'undefined')
{
    module.exports = sqli;
}

<|endoftext|>


<commit_before>/**
 * gemini-scrollbar
 * @version v0.0.1
 * @link http://noeldelgado.github.io/gemini-scrollbar/
 * @license MIT
 */
(function() {
    var SCROLLBAR_WIDTH, CLASSNAMES;

    SCROLLBAR_WIDTH = (function() {
        /* http://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript#answer-13382873 */
        var tmpElement = document.createElement("div");
        tmpElement.style.visibility = "hidden";
        tmpElement.style.width = "100px";
        tmpElement.style.msOverflowStyle = "scrollbar";
        document.body.appendChild(tmpElement);

        var widthNoScroll = tmpElement.offsetWidth;
        var inner = document.createElement("div");
        tmpElement.style.overflow = "scroll";
        inner.style.width = "100%";
        tmpElement.appendChild(inner);
        var widthWithScroll = inner.offsetWidth;
        tmpElement.parentNode.removeChild(tmpElement);

        return (widthNoScroll - widthWithScroll);
    }());

    CLASSNAMES = {
        element: 'gm-scrollbar-container',
        verticalScrollbar: 'gm-scrollbar -vertical',
        horizontalScrollbar: 'gm-scrollbar -horizontal',
        thumb: 'thumb',
        view: 'gm-scroll-view',
        autoshow: 'gm-autoshow',
        disable: 'gm-scrollbar-disable-selection'
    };

    function GeminiScrollbar(config) {
        this.element = null;
        this.autoshow = false;

        Object.keys(config || {}).forEach(function (propertyName) {
            this[propertyName] = config[propertyName];
        }, this);

        this._cache = {
            events: {},
            dom: {}
        };
        this._cursorDown = false;
        this._prevPageX = 0;
        this._prevPageY = 0;

        this._document = null;
        this._window = null;
        this._viewElement = null;
        this._scrollbarVerticalElement = null;
        this._thumbVerticalElement = null;
        this._scrollbarHorizontalElement = null;
        this._scrollbarHorizontalElement = null;
    }

    GeminiScrollbar.prototype.create = function create() {
        if (SCROLLBAR_WIDTH === 0) return this;

        if (this.autoshow) {
            this.element.classList.add(CLASSNAMES.autoshow);
        }

        this._document = document;
        this._window = window;
        this._viewElement = document.createElement('div');
        this._scrollbarVerticalElement = document.createElement('div');
        this._thumbVerticalElement = document.createElement('div');
        this._scrollbarHorizontalElement = document.createElement('div');
        this._thumbHorizontalElement = document.createElement('div');
        while(this.element.childNodes.length > 0) {
            this._viewElement.appendChild(this.element.childNodes[0]);
        }

        this.element.classList.add(CLASSNAMES.element);
        this._viewElement.className = CLASSNAMES.view;
        this._scrollbarVerticalElement.className = CLASSNAMES.verticalScrollbar;
        this._scrollbarHorizontalElement.className = CLASSNAMES.horizontalScrollbar;
        this._thumbVerticalElement.className = CLASSNAMES.thumb;
        this._thumbHorizontalElement.className = CLASSNAMES.thumb;

        this._scrollbarVerticalElement.appendChild(this._thumbVerticalElement);
        this._scrollbarHorizontalElement.appendChild(this._thumbHorizontalElement);
        this.element.appendChild(this._scrollbarVerticalElement);
        this.element.appendChild(this._scrollbarHorizontalElement);
        this.element.appendChild(this._viewElement);

        return this._bindEvents().update();
    };

    GeminiScrollbar.prototype.update = function update() {
        if (SCROLLBAR_WIDTH === 0) return this;

        var heightPercentage, widthPercentage;

        this._viewElement.style.width = ((this.element.offsetWidth + SCROLLBAR_WIDTH).toString() + 'px');
        this._viewElement.style.height = ((this.element.offsetHeight + SCROLLBAR_WIDTH).toString() + 'px');

        heightPercentage = (this._viewElement.clientHeight * 100 / this._viewElement.scrollHeight);
        widthPercentage = (this._viewElement.clientWidth * 100 / this._viewElement.scrollWidth);

        this._thumbVerticalElement.style.height = (heightPercentage < 100) ? (heightPercentage + '%') : '';
        this._thumbHorizontalElement.style.width = (widthPercentage < 100) ? (widthPercentage + '%') : '';

        return this;
    };

    GeminiScrollbar.prototype.destroy = function destroy() {
        if (SCROLLBAR_WIDTH === 0) return this;

        this._unbinEvents();

        this.element.classList.remove(CLASSNAMES.element, CLASSNAMES.autoshow);
        this.element.removeChild(this._scrollbarVerticalElement);
        this.element.removeChild(this._scrollbarHorizontalElement);
        while(this._viewElement.childNodes.length > 0) {
            this.element.appendChild(this._viewElement.childNodes[0]);
        }
        this.element.removeChild(this._viewElement);

        return null;
    };

    GeminiScrollbar.prototype._bindEvents = function() {
        this._cache.events.scrollHandler = this._scrollHandler.bind(this);
        this._cache.events.clickVerticalTrackHandler = this._clickVerticalTrackHandler.bind(this);
        this._cache.events.clickHorizontalTrackHandler = this._clickHorizontalTrackHandler.bind(this);
        this._cache.events.clickVerticalThumbHandler = this._clickVerticalThumbHandler.bind(this);
        this._cache.events.clickHorizontalThumbHandler = this._clickHorizontalThumbHandler.bind(this);
        this._cache.events.mouseUpDocumentHandler = this._mouseUpDocumentHandler.bind(this);
        this._cache.events.mouseMoveDocumentHandler = this._mouseMoveDocumentHandler.bind(this);
        this._cache.events.resizeWindowHandler = this.update.bind(this);

        this._viewElement.addEventListener('scroll', this._cache.events.scrollHandler);
        this._scrollbarVerticalElement.addEventListener('mousedown', this._cache.events.clickVerticalTrackHandler);
        this._scrollbarHorizontalElement.addEventListener('mousedown', this._cache.events.clickHorizontalTrackHandler);
        this._thumbVerticalElement.addEventListener('mousedown', this._cache.events.clickVerticalThumbHandler);
        this._thumbHorizontalElement.addEventListener('mousedown', this._cache.events.clickHorizontalThumbHandler);
        this._document.addEventListener('mouseup', this._cache.events.mouseUpDocumentHandler);
        this._window.addEventListener('resize', this._cache.events.resizeWindowHandler);

        return this;
    };

    GeminiScrollbar.prototype._unbinEvents = function() {
        this._viewElement.removeEventListener('scroll', this._cache.events.scrollHandler);
        this._scrollbarVerticalElement.removeEventListener('mousedown', this._cache.events.clickVerticalTrackHandler);
        this._scrollbarHorizontalElement.removeEventListener('mousedown', this._cache.events.clickHorizontalTrackHandler);
        this._thumbVerticalElement.removeEventListener('mousedown', this._cache.events.clickVerticalThumbHandler);
        this._thumbHorizontalElement.removeEventListener('mousedown', this._cache.events.clickHorizontalThumbHandler);
        this._document.removeEventListener('mouseup', this._cache.events.mouseUpDocumentHandler);
        this._document.removeEventListener('mousemove', this._cache.events.mouseMoveDocumentHandler);
        this._window.removeEventListener('resize', this._cache.events.resizeWindowHandler);

        return this;
    };

    GeminiScrollbar.prototype._scrollHandler = function(e) {
        var y = (e.target.scrollTop * 100 / this._viewElement.clientHeight);
        var x = (e.target.scrollLeft * 100 / this._viewElement.clientWidth);

        this._thumbVerticalElement.style.transform = 'translateY(' + y + '%)';
        this._thumbHorizontalElement.style.transform = 'translateX(' + x + '%)';
    };

    GeminiScrollbar.prototype._clickVerticalTrackHandler = function(e) {
        var offset = Math.abs(e.target.getBoundingClientRect().top - e.clientY);
        var thumbHalf = (this._thumbVerticalElement.offsetHeight / 2);
        var thumbPositionPercentage = ((offset - thumbHalf) * 100 / this._viewElement.clientHeight);

        this._viewElement.scrollTop = (thumbPositionPercentage * this._viewElement.scrollHeight / 100);
    };

    GeminiScrollbar.prototype._clickHorizontalTrackHandler = function(e) {
        var offset = Math.abs(e.target.getBoundingClientRect().left - e.clientX);
        var thumbHalf = (this._thumbHorizontalElement.offsetWidth / 2);
        var thumbPositionPercentage = ((offset - thumbHalf) * 100 / this._viewElement.clientWidth);

        this._viewElement.scrollLeft = (thumbPositionPercentage * this._viewElement.scrollWidth / 100);
    };

    GeminiScrollbar.prototype._clickVerticalThumbHandler = function(e) {
        this._startDrag(e);
        this._prevPageY = (e.currentTarget.offsetHeight - (e.clientY - e.currentTarget.getBoundingClientRect().top));
    };

    GeminiScrollbar.prototype._clickHorizontalThumbHandler = function(e) {
        this._startDrag(e);
        this._prevPageX = (e.currentTarget.offsetWidth - (e.clientX - e.currentTarget.getBoundingClientRect().left));
    };

    GeminiScrollbar.prototype._startDrag = function(e) {
        e.stopImmediatePropagation();
        this._cursorDown = true;
        document.body.classList.add(CLASSNAMES.disable);
        this._document.addEventListener('mousemove', this._cache.events.mouseMoveDocumentHandler);
    };

    GeminiScrollbar.prototype._mouseUpDocumentHandler = function(e) {
        this._cursorDown = false;
        this._prevPageX = 0;
        this._prevPageY = 0;
        document.body.classList.remove(CLASSNAMES.disable);
        this._document.removeEventListener('mousemove', this._cache.events.mouseMoveDocumentHandler);
    };

    GeminiScrollbar.prototype._mouseMoveDocumentHandler = function(e) {
        if (this._cursorDown) {
            var offset, thumbClickPosition, thumbPositionPercentage;

            if (this._prevPageY) {
                offset = ((this._scrollbarVerticalElement.getBoundingClientRect().top - e.clientY) * -1);
                thumbClickPosition = (this._thumbVerticalElement.offsetHeight - this._prevPageY);
                thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / this._viewElement.clientHeight);

                return this._viewElement.scrollTop = (thumbPositionPercentage * this._viewElement.scrollHeight / 100);
            }

            if (this._prevPageX) {
                offset = ((this._scrollbarHorizontalElement.getBoundingClientRect().left - e.clientX) * -1);
                thumbClickPosition = (this._thumbHorizontalElement.offsetWidth - this._prevPageX);
                thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / this._viewElement.clientWidth);

                return this._viewElement.scrollLeft = (thumbPositionPercentage * this._viewElement.scrollWidth / 100);
            }
        }
    };

    if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
        module.exports = GeminiScrollbar;
    } else window.GeminiScrollbar = GeminiScrollbar;
})();
<commit_msg>add / _created  property to hold the created status of the scrollbar This property help us prevent creating multiple/repetable elements if the scrollbar was already created. Ex. calling the 'create' method several times in a row, or calling the 'update' or 'destroy' method without calling the 'create' method first.<commit_after>/**
 * gemini-scrollbar
 * @version v0.0.1
 * @link http://noeldelgado.github.io/gemini-scrollbar/
 * @license MIT
 */
(function() {
    var SCROLLBAR_WIDTH, CLASSNAMES;

    SCROLLBAR_WIDTH = (function() {
        /* http://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript#answer-13382873 */
        var tmpElement = document.createElement("div");
        tmpElement.style.visibility = "hidden";
        tmpElement.style.width = "100px";
        tmpElement.style.msOverflowStyle = "scrollbar";
        document.body.appendChild(tmpElement);

        var widthNoScroll = tmpElement.offsetWidth;
        var inner = document.createElement("div");
        tmpElement.style.overflow = "scroll";
        inner.style.width = "100%";
        tmpElement.appendChild(inner);
        var widthWithScroll = inner.offsetWidth;
        tmpElement.parentNode.removeChild(tmpElement);

        return (widthNoScroll - widthWithScroll);
    }());

    CLASSNAMES = {
        element: 'gm-scrollbar-container',
        verticalScrollbar: 'gm-scrollbar -vertical',
        horizontalScrollbar: 'gm-scrollbar -horizontal',
        thumb: 'thumb',
        view: 'gm-scroll-view',
        autoshow: 'gm-autoshow',
        disable: 'gm-scrollbar-disable-selection'
    };

    function GeminiScrollbar(config) {
        this.element = null;
        this.autoshow = false;

        Object.keys(config || {}).forEach(function (propertyName) {
            this[propertyName] = config[propertyName];
        }, this);

        this._cache = {events: {}};
        this._created = false;
        this._cursorDown = false;
        this._prevPageX = 0;
        this._prevPageY = 0;

        this._document = null;
        this._window = null;
        this._viewElement = null;
        this._scrollbarVerticalElement = null;
        this._thumbVerticalElement = null;
        this._scrollbarHorizontalElement = null;
        this._scrollbarHorizontalElement = null;
    }

    GeminiScrollbar.prototype.create = function create() {
        if (SCROLLBAR_WIDTH === 0) return this;

        if (this._created === true) {
            console.warn('calling on a already-created object');
            return this;
        }

        if (this.autoshow) {
            this.element.classList.add(CLASSNAMES.autoshow);
        }

        this._document = document;
        this._window = window;
        this._viewElement = document.createElement('div');
        this._scrollbarVerticalElement = document.createElement('div');
        this._thumbVerticalElement = document.createElement('div');
        this._scrollbarHorizontalElement = document.createElement('div');
        this._thumbHorizontalElement = document.createElement('div');
        while(this.element.childNodes.length > 0) {
            this._viewElement.appendChild(this.element.childNodes[0]);
        }

        this.element.classList.add(CLASSNAMES.element);
        this._viewElement.className = CLASSNAMES.view;
        this._scrollbarVerticalElement.className = CLASSNAMES.verticalScrollbar;
        this._scrollbarHorizontalElement.className = CLASSNAMES.horizontalScrollbar;
        this._thumbVerticalElement.className = CLASSNAMES.thumb;
        this._thumbHorizontalElement.className = CLASSNAMES.thumb;

        this._scrollbarVerticalElement.appendChild(this._thumbVerticalElement);
        this._scrollbarHorizontalElement.appendChild(this._thumbHorizontalElement);
        this.element.appendChild(this._scrollbarVerticalElement);
        this.element.appendChild(this._scrollbarHorizontalElement);
        this.element.appendChild(this._viewElement);

        this._created = true;

        return this._bindEvents().update();
    };

    GeminiScrollbar.prototype.update = function update() {
        if (SCROLLBAR_WIDTH === 0) return this;

        if (this._created === false) {
            console.warn('calling on a not-yet-created object');
            return this;
        }

        var heightPercentage, widthPercentage;

        this._viewElement.style.width = ((this.element.offsetWidth + SCROLLBAR_WIDTH).toString() + 'px');
        this._viewElement.style.height = ((this.element.offsetHeight + SCROLLBAR_WIDTH).toString() + 'px');

        heightPercentage = (this._viewElement.clientHeight * 100 / this._viewElement.scrollHeight);
        widthPercentage = (this._viewElement.clientWidth * 100 / this._viewElement.scrollWidth);

        this._thumbVerticalElement.style.height = (heightPercentage < 100) ? (heightPercentage + '%') : '';
        this._thumbHorizontalElement.style.width = (widthPercentage < 100) ? (widthPercentage + '%') : '';

        return this;
    };

    GeminiScrollbar.prototype.destroy = function destroy() {
        if (SCROLLBAR_WIDTH === 0) return this;

        if (this._created === false) {
            console.warn('calling on a not-yet-created object');
            return this;
        }

        this._unbinEvents();

        this.element.classList.remove(CLASSNAMES.element, CLASSNAMES.autoshow);
        this.element.removeChild(this._scrollbarVerticalElement);
        this.element.removeChild(this._scrollbarHorizontalElement);
        while(this._viewElement.childNodes.length > 0) {
            this.element.appendChild(this._viewElement.childNodes[0]);
        }
        this.element.removeChild(this._viewElement);

        this._created = false;

        return null;
    };

    GeminiScrollbar.prototype._bindEvents = function() {
        this._cache.events.scrollHandler = this._scrollHandler.bind(this);
        this._cache.events.clickVerticalTrackHandler = this._clickVerticalTrackHandler.bind(this);
        this._cache.events.clickHorizontalTrackHandler = this._clickHorizontalTrackHandler.bind(this);
        this._cache.events.clickVerticalThumbHandler = this._clickVerticalThumbHandler.bind(this);
        this._cache.events.clickHorizontalThumbHandler = this._clickHorizontalThumbHandler.bind(this);
        this._cache.events.mouseUpDocumentHandler = this._mouseUpDocumentHandler.bind(this);
        this._cache.events.mouseMoveDocumentHandler = this._mouseMoveDocumentHandler.bind(this);
        this._cache.events.resizeWindowHandler = this.update.bind(this);

        this._viewElement.addEventListener('scroll', this._cache.events.scrollHandler);
        this._scrollbarVerticalElement.addEventListener('mousedown', this._cache.events.clickVerticalTrackHandler);
        this._scrollbarHorizontalElement.addEventListener('mousedown', this._cache.events.clickHorizontalTrackHandler);
        this._thumbVerticalElement.addEventListener('mousedown', this._cache.events.clickVerticalThumbHandler);
        this._thumbHorizontalElement.addEventListener('mousedown', this._cache.events.clickHorizontalThumbHandler);
        this._document.addEventListener('mouseup', this._cache.events.mouseUpDocumentHandler);
        this._window.addEventListener('resize', this._cache.events.resizeWindowHandler);

        return this;
    };

    GeminiScrollbar.prototype._unbinEvents = function() {
        this._viewElement.removeEventListener('scroll', this._cache.events.scrollHandler);
        this._scrollbarVerticalElement.removeEventListener('mousedown', this._cache.events.clickVerticalTrackHandler);
        this._scrollbarHorizontalElement.removeEventListener('mousedown', this._cache.events.clickHorizontalTrackHandler);
        this._thumbVerticalElement.removeEventListener('mousedown', this._cache.events.clickVerticalThumbHandler);
        this._thumbHorizontalElement.removeEventListener('mousedown', this._cache.events.clickHorizontalThumbHandler);
        this._document.removeEventListener('mouseup', this._cache.events.mouseUpDocumentHandler);
        this._document.removeEventListener('mousemove', this._cache.events.mouseMoveDocumentHandler);
        this._window.removeEventListener('resize', this._cache.events.resizeWindowHandler);

        return this;
    };

    GeminiScrollbar.prototype._scrollHandler = function(e) {
        var y = (e.target.scrollTop * 100 / this._viewElement.clientHeight);
        var x = (e.target.scrollLeft * 100 / this._viewElement.clientWidth);

        this._thumbVerticalElement.style.transform = 'translateY(' + y + '%)';
        this._thumbHorizontalElement.style.transform = 'translateX(' + x + '%)';
    };

    GeminiScrollbar.prototype._clickVerticalTrackHandler = function(e) {
        var offset = Math.abs(e.target.getBoundingClientRect().top - e.clientY);
        var thumbHalf = (this._thumbVerticalElement.offsetHeight / 2);
        var thumbPositionPercentage = ((offset - thumbHalf) * 100 / this._viewElement.clientHeight);

        this._viewElement.scrollTop = (thumbPositionPercentage * this._viewElement.scrollHeight / 100);
    };

    GeminiScrollbar.prototype._clickHorizontalTrackHandler = function(e) {
        var offset = Math.abs(e.target.getBoundingClientRect().left - e.clientX);
        var thumbHalf = (this._thumbHorizontalElement.offsetWidth / 2);
        var thumbPositionPercentage = ((offset - thumbHalf) * 100 / this._viewElement.clientWidth);

        this._viewElement.scrollLeft = (thumbPositionPercentage * this._viewElement.scrollWidth / 100);
    };

    GeminiScrollbar.prototype._clickVerticalThumbHandler = function(e) {
        this._startDrag(e);
        this._prevPageY = (e.currentTarget.offsetHeight - (e.clientY - e.currentTarget.getBoundingClientRect().top));
    };

    GeminiScrollbar.prototype._clickHorizontalThumbHandler = function(e) {
        this._startDrag(e);
        this._prevPageX = (e.currentTarget.offsetWidth - (e.clientX - e.currentTarget.getBoundingClientRect().left));
    };

    GeminiScrollbar.prototype._startDrag = function(e) {
        e.stopImmediatePropagation();
        this._cursorDown = true;
        document.body.classList.add(CLASSNAMES.disable);
        this._document.addEventListener('mousemove', this._cache.events.mouseMoveDocumentHandler);
    };

    GeminiScrollbar.prototype._mouseUpDocumentHandler = function(e) {
        this._cursorDown = false;
        this._prevPageX = 0;
        this._prevPageY = 0;
        document.body.classList.remove(CLASSNAMES.disable);
        this._document.removeEventListener('mousemove', this._cache.events.mouseMoveDocumentHandler);
    };

    GeminiScrollbar.prototype._mouseMoveDocumentHandler = function(e) {
        if (this._cursorDown) {
            var offset, thumbClickPosition, thumbPositionPercentage;

            if (this._prevPageY) {
                offset = ((this._scrollbarVerticalElement.getBoundingClientRect().top - e.clientY) * -1);
                thumbClickPosition = (this._thumbVerticalElement.offsetHeight - this._prevPageY);
                thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / this._viewElement.clientHeight);

                return this._viewElement.scrollTop = (thumbPositionPercentage * this._viewElement.scrollHeight / 100);
            }

            if (this._prevPageX) {
                offset = ((this._scrollbarHorizontalElement.getBoundingClientRect().left - e.clientX) * -1);
                thumbClickPosition = (this._thumbHorizontalElement.offsetWidth - this._prevPageX);
                thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / this._viewElement.clientWidth);

                return this._viewElement.scrollLeft = (thumbPositionPercentage * this._viewElement.scrollWidth / 100);
            }
        }
    };

    if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
        module.exports = GeminiScrollbar;
    } else window.GeminiScrollbar = GeminiScrollbar;
})();
<|endoftext|>


<commit_before>'use strict';
/*
 *@author xtx1130
 *@description 处理基础routes和基本controller绑定，抛出扩展后的koa构造函数
 */
const Koa = require('koa');
const Routes = require('./app/routes/koas-router');
const Controller = require('./app/controllers/controller');

const syncRouteController = Symbol.for('koas#syncRouteController')

class Koas extends Koa{
	constructor(){
		super();
		this.routes = new Routes();
		this.controller = new Controller();
	}
	get routesMap() {
		return this.routes.jsonMap;
	}
	get controlMap() {
		return this.controller.jsonMap;
	}
	//同步注册routes，controllers，通过key来寻找对应关系
	[syncRouteController](){
		console.log(this.routesMap)
		console.log(this.controlMap)
	}
}
let s = new Koas();
console.dir(s[syncRouteController]())<commit_msg>add relative for router and controller<commit_after>'use strict';
/*
 *@author xtx1130
 *@description 处理基础routes和基本controller绑定，抛出扩展后的koa构造函数
 */
const Koa = require('koa');
const Routes = require('./app/routes/koas-router');
const Controller = require('./app/controllers/controller');
const Assert = require('assert');

const syncRouteController = Symbol.for('koas#syncRouteController')

class Koas extends Koa{
	constructor(){
		super();
		this.routes = new Routes();
		this.controller = new Controller();
	}
	get routesMap() {
		return this.routes.jsonMap;
	}
	get controlMap() {
		return this.controller.jsonMap;
	}
	//同步注册routes，controllers，通过key来寻找对应关系
	[syncRouteController](){
		for(let i in this.routesMap){
			for(let j in this.routesMap[i]){
				if(j == 'baseRouter'){
					console.log('todo index')
				}else{
					let temroute = this.routesMap[i][j];
					let meth = temroute.method.split(',')||['get','post'];
					if(temroute.status === 1){
						for(let k = 0;k<meth.length;k++){
							Assert(this.routes.methods.join('').match(meth[k].toUpperCase()),'only support HEAD,OPTIONS,GET,PUT,PATCH,POST,DELETE')
							this.routes[meth[k]](temroute.url,this.controlMap[i][j])
						}
					}
				}
			}
		}
		console.log(this.routes.routes())
	}
}
let s = new Koas();
s[syncRouteController]()<|endoftext|>