<commit_before>const R = require("ramda");
const RA = require("ramda-adjunct");
const immer = require("immer");
const YAML = require("yaml");
const Mustache = require("mustache");

const regex = {
  create: (pattern, flags) => new RegExp(pattern, flags),
  test: (re, str) => re.test(str),
};

const defaultLibraries = [
  R,
  RA,
  // These are libraries we want to name space
  {
    jq: require("jsonpath"),
    saunter: require("saunter"),
    regex,
  },
];

class Geneva {
  constructor(options = {}) {
    this.initial = options.initial || {};
    this.forms = options.forms || {};
    this.parser = options.parser || new ObjectParser();
    this.libraries = options.libraries || defaultLibraries;
    this.fs = options.fs || {};
  }

  static withObjectParser(options = {}) {
    options.parser = new ObjectParser();
    return new Geneva(options);
  }

  static withArrayParser(options = {}) {
    options.parser = new ArrayParser();
    return new Geneva(options);
  }

  // Builds a runtime from the initial options
  buildRuntime(givenScope) {
    const scope =
      givenScope || new Scope(this.initial, this.forms, this.libraries);
    const runtime = new Runtime(scope, this.parser, this.fs);
    return runtime;
  }

  run(code) {
    const runtime = this.buildRuntime();
    return runtime.run(code);
  }
}

class Runtime {
  constructor(scope, parser, fs) {
    // Global scope for entire runtime
    this.scope = scope || new Scope();
    this.parser = parser || new DefaultParser();
    this.fs = fs;
  }

  run(code) {
    if (this.parser.isRef(code)) {
      const refName = this.parser.getRefName(code);
      return this.scope.getRef(refName);
    }

    if (this.parser.isCallable(code)) {
      const { fnName, args } = this.parser.parseCode(code);
      const fn = this.scope.getRef(fnName);

      // Functions allow for interacting with normal JavaScript code This is
      // mostly used by the lambda special form
      if (RA.isFunction(fn)) {
        if (fn.isForm) {
          return fn(this, args);
        }
        const values = args.map(this.run.bind(this));
        return fn(...values);
      }
    }

    // Arrays with the first item as a function should be called by passing all
    // of the items after the function as arguments. This is so lambdas can be
    // defined and called directly.
    if (RA.isArray(code)) {
      const arr = code.map((item) => this.run(item));

      if (RA.isFunction(arr[0])) {
        return arr[0](...arr.slice(1));
      }

      return arr;
    }

    // Even objects can have code nested within them. However, order of
    // operation can't really be guaranteed.
    // TODO: this can be a map instead
    if (RA.isPlainObject(code)) {
      return R.reduce(
        (memo, [key, value]) => {
          memo[key] = this.run(value);
          return memo;
        },
        {},
        R.toPairs(code)
      );
    }

    // If we made it this far, it's just a normal value so we can return it.
    // This will work for numbers, strings, booleans, or any code that has
    // already been evaluated.
    return code;
  }
}

class ArrayParser {
  // Code that is callable looks like ['!fn', 'arg1', 'arg2']
  isCallable(code) {
    return code && RA.isArray(code) && code.length > 0 && code[0][0] == "!";
  }

  // References look like ~x
  isRef(code) {
    return code && RA.isString(code) && code[0] == "~";
  }

  // References start with ~, so we need to trim off that first character
  // here. That will give us the variable name.
  getRefName(code) {
    return code.slice(1);
  }

  // Function names start with a bang (!), so we strip it
  parseCode(code) {
    const [fnNameBang, ...args] = code;
    const fnName = fnNameBang.slice(1);
    return { fnName, args };
  }
}

function formWrapper(fn) {
  fn.isForm = true;
  return fn;
}

const defaultForms = {
  do: formWrapper((runtime, args) => {
    const evaledArgs = args.map((arg) => runtime.run(arg));
    return R.last(evaledArgs);
  }),

  def: formWrapper((runtime, args) => {
    const value = runtime.run(args[1]);
    runtime.scope.setRef(args[0], value);
    return value;
  }),

  if: formWrapper((runtime, args) => {
    const test = runtime.run(args[0]);
    if (test) {
      return runtime.run(args[1]);
    }
    if (args[2]) {
      return runtime.run(args[2]);
    }
    return null;
  }),

  lambda: formWrapper((parentRuntime, lambdaArgs) => {
    const argNames = lambdaArgs[0];
    // We create a copy of the scope when the lambda is created
    // We don't want this scope to change
    const frozenScope = parentRuntime.scope.copy();

    return (...args) => {
      // We create a new copy when we run the function
      // We don't want this function to affect other scopes
      const scope = frozenScope.copy();

      // Add the passed arguments into the scope
      // TODO: set refs all at once since using immer
      R.forEach(([index, value]) => {
        scope.setRef(argNames[index], value);
      }, R.toPairs(args.slice(0, argNames.length)));

      const runtime = new Runtime(scope, parentRuntime.parser);
      const results = R.map((expr) => {
        return runtime.run(expr);
      }, lambdaArgs.slice(1));

      return R.last(results);
    };
  }),

  defn: formWrapper((runtime, args) => {
    // Rewrite the function as a def call for simplicity
    // TODO: this is coupled to the syntax
    return runtime.run({
      "fn:def": [
        args[0],
        {
          "fn:lambda": [args[1], args[2]],
        },
      ],
    });
    // return runtime.run(["!def", args[0], ["!fn", args[1], args[2]]]);
  }),

  quote: formWrapper((runtime, args) => {
    // We don't run the code for quote, we just pass it through like it's normal
    // code
    return args[0];
  }),

  eval: formWrapper((runtime, args) => {
    // Evaluate quoted code
    const code = runtime.run(args[0]);
    return runtime.run(code);
  }),

  readString: formWrapper((runtime, args) => {
    // TODO: this is broken
    return runtime.run(["!quote", JSON.parse(args[0])]);
  }),

  template: formWrapper((runtime, args) => {
    const arg = args[0];
    // We run the first arg in case there is code in there
    const value = runtime.run(arg);
    return Mustache.render(value, runtime.scope.refs);
  }),

  templateFile: formWrapper((runtime, args) => {
    const arg = args[0];
    const filePath = runtime.run(arg);
    const template = runtime.fs.readFileSync(filePath, "utf-8");
    return Mustache.render(template, runtime.scope.refs);
  }),

  include: formWrapper((runtime, args) => {
    const arg = args[0];
    const filePath = runtime.run(arg);
    console.log("fasdf");
    const code = runtime.fs.readFileSync(filePath, "utf-8");
    return runtime.run(YAML.parse(code));
  }),

  readFile: formWrapper((runtime, args) => {
    const arg = args[0];
    const filePath = runtime.run(arg);
    return runtime.fs.readFileSync(filePath, "utf-8");
  }),
};

// Alias lambda as fn for fun
defaultForms["fn"] = defaultForms["lambda"];

class Scope {
  constructor(refs = {}, forms = {}, libraries = {}) {
    // User-defined references
    this.refs = refs;

    // Special forms passed in at load time
    this.forms = R.pipe(
      R.toPairs,
      R.map(([key, value]) => [key, formWrapper(value)]),
      R.fromPairs
    )(forms);

    this.libraries = libraries;
  }

  copy() {
    return new Scope(this.refs, this.forms, this.libraries);
  }

  getRef(refName) {
    let value;

    // Allow paths specified with periods
    const parts = R.split(".", refName);

    value = R.path(parts, defaultForms);
    if (value !== undefined) return value;

    value = R.path(parts, this.refs);
    if (value !== undefined) return value;

    value = R.path(parts, this.forms);
    if (value !== undefined) return value;

    for (let i = 0; i < this.libraries.length; i++) {
      value = R.path(parts, this.libraries[i]);
      if (value !== undefined) return value;
    }

    throw ReferenceError(`No reference '${refName}' found.`);
  }

  setRef(name, value) {
    this.refs = immer.produce(this.refs, (draft) => {
      draft[name] = value;
    });
  }
}

class ObjectParser {
  constructor({ functionPrefix = "fn:", codePrefix = "ref:" } = {}) {
    this.functionPrefix = functionPrefix;
    this.codePrefix = codePrefix;
  }

  // References look like ~x
  isRef(code) {
    return code && RA.isString(code) && code.startsWith(this.codePrefix);
  }

  // References start with ~, so we need to trim off that first character
  // here. That will give us the variable name.
  getRefName(code) {
    return code.slice(this.codePrefix.length);
  }

  isCallable(code) {
    if (RA.isPlainObject(code)) {
      const keys = R.keys(code);
      return (
        keys.length === 1 &&
        RA.isString(keys[0]) &&
        keys[0].startsWith(this.functionPrefix)
      );
    }
    return false;
  }

  // Function names start with a bang (!), so we strip it
  parseCode(code) {
    const fnWithChar = R.keys(code)[0];
    const argsValue = code[fnWithChar];
    // Allow a single value or an array of values
    // This means an array can't be considered a single value, though
    const args = RA.isArray(argsValue) ? argsValue : [argsValue];
    const fnName = fnWithChar.slice(this.functionPrefix.length);
    return { fnName, args };
  }
}

class ConfigBuilder {
  constructor({ config, initial = {}, forms = {}, fs } = {}) {
    this.config = config;
    this.geneva = new Geneva({ initial, forms, fs });
  }

  static fromYAML(yamlConfig, options) {
    return new ConfigBuilder({ ...options, config: YAML.parse(yamlConfig) });
  }

  build(parameters) {
    const runtime = this.geneva.buildRuntime();

    // Check given parameters against checks if they exist
    R.forEach(({ name, check }) => {
      if (check === undefined) return;
      // We allow the person to reference a check or create their own
      // We pass it into the runtime to get it back out
      const checker = runtime.run(check);
      if (!RA.isFunction(checker)) {
        throw TypeError(`Check for ${name} must be a function`);
      }
      if (!checker(parameters[name])) throw TypeError("Wrong Type");
    }, this.config.parameters);

    // Check required fields
    R.pipe(
      R.filter(({ required }) => required === true),
      R.forEach(({ name }) => {
        if (!(name in parameters)) {
          throw TypeError(`Parameter ${name} is required`);
        }
      })
    )(this.config.parameters);

    // Load parameters into runtime
    R.pipe(
      R.toPairs,
      R.forEach(([key, value]) => {
        runtime.run({
          "fn:def": [key, value],
        });
      })
    )(parameters);

    // Compute computed properties
    R.forEach(({ name, compute }) => {
      runtime.run({
        "fn:def": [name, compute],
      });
    }, this.config.computed);

    return () => {
      return runtime.run(this.config.definition);
    };
  }

  run(parameters) {
    const runner = this.build(parameters);
    return runner();
  }
}

class SimpleFS {
  constructor(state) {
    this.state = state;
  }

  readFileSync(filename) {
    return this.state[filename];
  }
}

module.exports = {
  Geneva,
  Runtime,
  Scope,
  ArrayParser,
  ObjectParser,
  ConfigBuilder,
  defaultForms,
  defaultLibraries,
  SimpleFS,
};
<commit_msg>Remove diff<commit_after>const R = require("ramda");
const RA = require("ramda-adjunct");
const immer = require("immer");
const YAML = require("yaml");
const Mustache = require("mustache");

const regex = {
  create: (pattern, flags) => new RegExp(pattern, flags),
  test: (re, str) => re.test(str),
};

const defaultLibraries = [
  R,
  RA,
  // These are libraries we want to name space
  {
    jq: require("jsonpath"),
    saunter: require("saunter"),
    regex,
  },
];

class Geneva {
  constructor(options = {}) {
    this.initial = options.initial || {};
    this.forms = options.forms || {};
    this.parser = options.parser || new ObjectParser();
    this.libraries = options.libraries || defaultLibraries;
    this.fs = options.fs || {};
  }

  static withObjectParser(options = {}) {
    options.parser = new ObjectParser();
    return new Geneva(options);
  }

  static withArrayParser(options = {}) {
    options.parser = new ArrayParser();
    return new Geneva(options);
  }

  // Builds a runtime from the initial options
  buildRuntime(givenScope) {
    const scope =
      givenScope || new Scope(this.initial, this.forms, this.libraries);
    const runtime = new Runtime(scope, this.parser, this.fs);
    return runtime;
  }

  run(code) {
    const runtime = this.buildRuntime();
    return runtime.run(code);
  }
}

class Runtime {
  constructor(scope, parser, fs) {
    // Global scope for entire runtime
    this.scope = scope || new Scope();
    this.parser = parser || new DefaultParser();
    this.fs = fs;
  }

  run(code) {
    if (this.parser.isRef(code)) {
      const refName = this.parser.getRefName(code);
      return this.scope.getRef(refName);
    }

    if (this.parser.isCallable(code)) {
      const { fnName, args } = this.parser.parseCode(code);
      const fn = this.scope.getRef(fnName);

      // Functions allow for interacting with normal JavaScript code This is
      // mostly used by the lambda special form
      if (RA.isFunction(fn)) {
        if (fn.isForm) {
          return fn(this, args);
        }
        const values = args.map(this.run.bind(this));
        return fn(...values);
      }
    }

    // Arrays with the first item as a function should be called by passing all
    // of the items after the function as arguments. This is so lambdas can be
    // defined and called directly.
    if (RA.isArray(code)) {
      const arr = code.map((item) => this.run(item));

      if (RA.isFunction(arr[0])) {
        return arr[0](...arr.slice(1));
      }

      return arr;
    }

    // Even objects can have code nested within them. However, order of
    // operation can't really be guaranteed.
    // TODO: this can be a map instead
    if (RA.isPlainObject(code)) {
      return R.reduce(
        (memo, [key, value]) => {
          memo[key] = this.run(value);
          return memo;
        },
        {},
        R.toPairs(code)
      );
    }

    // If we made it this far, it's just a normal value so we can return it.
    // This will work for numbers, strings, booleans, or any code that has
    // already been evaluated.
    return code;
  }
}

class ArrayParser {
  // Code that is callable looks like ['!fn', 'arg1', 'arg2']
  isCallable(code) {
    return code && RA.isArray(code) && code.length > 0 && code[0][0] == "!";
  }

  // References look like ~x
  isRef(code) {
    return code && RA.isString(code) && code[0] == "~";
  }

  // References start with ~, so we need to trim off that first character
  // here. That will give us the variable name.
  getRefName(code) {
    return code.slice(1);
  }

  // Function names start with a bang (!), so we strip it
  parseCode(code) {
    const [fnNameBang, ...args] = code;
    const fnName = fnNameBang.slice(1);
    return { fnName, args };
  }
}

function formWrapper(fn) {
  fn.isForm = true;
  return fn;
}

const defaultForms = {
  do: formWrapper((runtime, args) => {
    const evaledArgs = args.map((arg) => runtime.run(arg));
    return R.last(evaledArgs);
  }),

  def: formWrapper((runtime, args) => {
    const value = runtime.run(args[1]);
    runtime.scope.setRef(args[0], value);
    return value;
  }),

  if: formWrapper((runtime, args) => {
    const test = runtime.run(args[0]);
    if (test) {
      return runtime.run(args[1]);
    }
    if (args[2]) {
      return runtime.run(args[2]);
    }
    return null;
  }),

  lambda: formWrapper((parentRuntime, lambdaArgs) => {
    const argNames = lambdaArgs[0];
    // We create a copy of the scope when the lambda is created
    // We don't want this scope to change
    const frozenScope = parentRuntime.scope.copy();

    return (...args) => {
      // We create a new copy when we run the function
      // We don't want this function to affect other scopes
      const scope = frozenScope.copy();

      // Add the passed arguments into the scope
      // TODO: set refs all at once since using immer
      R.forEach(([index, value]) => {
        scope.setRef(argNames[index], value);
      }, R.toPairs(args.slice(0, argNames.length)));

      const runtime = new Runtime(scope, parentRuntime.parser);
      const results = R.map((expr) => {
        return runtime.run(expr);
      }, lambdaArgs.slice(1));

      return R.last(results);
    };
  }),

  defn: formWrapper((runtime, args) => {
    // Rewrite the function as a def call for simplicity
    // TODO: this is coupled to the syntax
    return runtime.run({
      "fn:def": [
        args[0],
        {
          "fn:lambda": [args[1], args[2]],
        },
      ],
    });
    // return runtime.run(["!def", args[0], ["!fn", args[1], args[2]]]);
  }),

  quote: formWrapper((runtime, args) => {
    // We don't run the code for quote, we just pass it through like it's normal
    // code
    return args[0];
  }),

  eval: formWrapper((runtime, args) => {
    // Evaluate quoted code
    const code = runtime.run(args[0]);
    return runtime.run(code);
  }),

  readString: formWrapper((runtime, args) => {
    // TODO: this is broken
    return runtime.run(["!quote", JSON.parse(args[0])]);
  }),

  template: formWrapper((runtime, args) => {
    const arg = args[0];
    // We run the first arg in case there is code in there
    const value = runtime.run(arg);
    return Mustache.render(value, runtime.scope.refs);
  }),

  templateFile: formWrapper((runtime, args) => {
    const arg = args[0];
    const filePath = runtime.run(arg);
    const template = runtime.fs.readFileSync(filePath, "utf-8");
    return Mustache.render(template, runtime.scope.refs);
  }),

  include: formWrapper((runtime, args) => {
    const arg = args[0];
    const filePath = runtime.run(arg);
    const code = runtime.fs.readFileSync(filePath, "utf-8");
    return runtime.run(YAML.parse(code));
  }),

  readFile: formWrapper((runtime, args) => {
    const arg = args[0];
    const filePath = runtime.run(arg);
    return runtime.fs.readFileSync(filePath, "utf-8");
  }),
};

// Alias lambda as fn for fun
defaultForms["fn"] = defaultForms["lambda"];

class Scope {
  constructor(refs = {}, forms = {}, libraries = {}) {
    // User-defined references
    this.refs = refs;

    // Special forms passed in at load time
    this.forms = R.pipe(
      R.toPairs,
      R.map(([key, value]) => [key, formWrapper(value)]),
      R.fromPairs
    )(forms);

    this.libraries = libraries;
  }

  copy() {
    return new Scope(this.refs, this.forms, this.libraries);
  }

  getRef(refName) {
    let value;

    // Allow paths specified with periods
    const parts = R.split(".", refName);

    value = R.path(parts, defaultForms);
    if (value !== undefined) return value;

    value = R.path(parts, this.refs);
    if (value !== undefined) return value;

    value = R.path(parts, this.forms);
    if (value !== undefined) return value;

    for (let i = 0; i < this.libraries.length; i++) {
      value = R.path(parts, this.libraries[i]);
      if (value !== undefined) return value;
    }

    throw ReferenceError(`No reference '${refName}' found.`);
  }

  setRef(name, value) {
    this.refs = immer.produce(this.refs, (draft) => {
      draft[name] = value;
    });
  }
}

class ObjectParser {
  constructor({ functionPrefix = "fn:", codePrefix = "ref:" } = {}) {
    this.functionPrefix = functionPrefix;
    this.codePrefix = codePrefix;
  }

  // References look like ~x
  isRef(code) {
    return code && RA.isString(code) && code.startsWith(this.codePrefix);
  }

  // References start with ~, so we need to trim off that first character
  // here. That will give us the variable name.
  getRefName(code) {
    return code.slice(this.codePrefix.length);
  }

  isCallable(code) {
    if (RA.isPlainObject(code)) {
      const keys = R.keys(code);
      return (
        keys.length === 1 &&
        RA.isString(keys[0]) &&
        keys[0].startsWith(this.functionPrefix)
      );
    }
    return false;
  }

  // Function names start with a bang (!), so we strip it
  parseCode(code) {
    const fnWithChar = R.keys(code)[0];
    const argsValue = code[fnWithChar];
    // Allow a single value or an array of values
    // This means an array can't be considered a single value, though
    const args = RA.isArray(argsValue) ? argsValue : [argsValue];
    const fnName = fnWithChar.slice(this.functionPrefix.length);
    return { fnName, args };
  }
}

class ConfigBuilder {
  constructor({ config, initial = {}, forms = {}, fs } = {}) {
    this.config = config;
    this.geneva = new Geneva({ initial, forms, fs });
  }

  static fromYAML(yamlConfig, options) {
    return new ConfigBuilder({ ...options, config: YAML.parse(yamlConfig) });
  }

  build(parameters) {
    const runtime = this.geneva.buildRuntime();

    // Check given parameters against checks if they exist
    R.forEach(({ name, check }) => {
      if (check === undefined) return;
      // We allow the person to reference a check or create their own
      // We pass it into the runtime to get it back out
      const checker = runtime.run(check);
      if (!RA.isFunction(checker)) {
        throw TypeError(`Check for ${name} must be a function`);
      }
      if (!checker(parameters[name])) throw TypeError("Wrong Type");
    }, this.config.parameters);

    // Check required fields
    R.pipe(
      R.filter(({ required }) => required === true),
      R.forEach(({ name }) => {
        if (!(name in parameters)) {
          throw TypeError(`Parameter ${name} is required`);
        }
      })
    )(this.config.parameters);

    // Load parameters into runtime
    R.pipe(
      R.toPairs,
      R.forEach(([key, value]) => {
        runtime.run({
          "fn:def": [key, value],
        });
      })
    )(parameters);

    // Compute computed properties
    R.forEach(({ name, compute }) => {
      runtime.run({
        "fn:def": [name, compute],
      });
    }, this.config.computed);

    return () => {
      return runtime.run(this.config.definition);
    };
  }

  run(parameters) {
    const runner = this.build(parameters);
    return runner();
  }
}

class SimpleFS {
  constructor(state) {
    this.state = state;
  }

  readFileSync(filename) {
    return this.state[filename];
  }
}

module.exports = {
  Geneva,
  Runtime,
  Scope,
  ArrayParser,
  ObjectParser,
  ConfigBuilder,
  defaultForms,
  defaultLibraries,
  SimpleFS,
};
<|endoftext|>


<commit_before>'use strict';

var isDate        = require('es5-ext/lib/Date/is-date')
  , isFunction    = require('es5-ext/lib/Function/is-function')
  , validFunction = require('es5-ext/lib/Function/valid-function')
  , copy          = require('es5-ext/lib/Object/copy')
  , d             = require('es5-ext/lib/Object/descriptor')
  , extend        = require('es5-ext/lib/Object/extend')
  , forEach       = require('es5-ext/lib/Object/for-each')
  , validValue    = require('es5-ext/lib/Object/valid-value')
  , isString      = require('es5-ext/lib/String/is-string')
  , startsWith    = require('es5-ext/lib/String/prototype/starts-with')
  , now           = require('time-uuid/lib/time')
  , Property      = require('./_property')

  , defineProperty = Object.defineProperty
  , defineProperties = Object.defineProperties
  , getOwnPropertyNames = Object.getOwnPropertyNames
  , getPrototypeOf = Object.getPrototypeOf
  , isProperty = Property.isProperty
  , functionRe = new RegExp('^\\s*function[\\0-\'\\)-\\uffff]*' +
		'\\(([\\0-\\(\\*-\\uffff]*)\\)\\s*\\{([\\0-\\uffff]*)\\}\\s*$')
  , nameRe = /^[a-zA-Z][0-9a-zA-Z]*$/

  , define, PropertyExt, requiredOpts, isNamespace, base, boolean, number
  , string, dateTime, regExp, functionType;

PropertyExt = function (value, ns, ext) {
	this.value = value;
	this.ns = ns;
	this.ext = ext;
};
PropertyExt.prototype.__propertyExtension = true;

requiredOpts = { required: true };

define = function (obj, name, value, attr) {
	var ns, get, set, prop, ext, propName;
	if (value && value.__propertyExtension) {
		ns = value.ns;
		ext = value.ext;
		value = value.value;
	} else if (isNamespace(value)) {
		ns = value;
		value = null;
	}
	prop = new Property(obj, value, ns, name);
	if (ext) extend(prop, ext);
	propName = '_$' + name;

	defineProperty(obj, propName, d('', prop));
	defineProperty(obj, '_' + name, d.gs('', function () {
		return this.hasOwnProperty(propName) ? this[propName] : undefined;
	}));
	defineProperty(obj, name, d.gs(attr || '', get = function () {
		var prop = this[propName], value = prop.value, ns = prop.ns;
		if ((value == null) || !ns || (ns._$async.value && !prop.resolved)) {
			return value;
		}
		return ns._$normalize.value(value);
	}, set = function (value) {
		var ns, ext;
		if (value && value.__propertyExtension) {
			ns = value.ns;
			ext = value.ext;
			value = value.value;
		} else if (isNamespace(value)) {
			ns = value;
			value = null;
		}
		if (this.hasOwnProperty(propName)) {
			this[propName].set(value, ns);
		} else {
			defineProperty(this, propName,
				d('', this[propName].create(this, value, ns)));
			defineProperty(this, name, d.gs(attr || '', get, set));
		}
		if (ext) extend(this[propName], ext);
		return this[propName].value;
	}));
	return prop.value;
};

module.exports = base = defineProperties(function self(value) {
	return self.validate(value);
}, {
	__id: d('base'),
	lock: d(null),
	isNamespace: d(function (value) {
		return value && value.__id && !value.__ns;
	}),
	create: d('c', function (name, constructor, properties) {
		name = String(name);
		if (!nameRe.test(name)) {
			throw new Error(name + "is not valid namespace name");
		}

		if ((properties == null) && !isFunction(constructor)) {
			properties = constructor;
			constructor = eval('(' + String(this) + ')');
		}
		constructor.__proto__ = this;

		base.transaction(function () {
			constructor.__create(properties);
			defineProperties(constructor, {
				__id:      d(name),
				__created: d(base.lock)
			});
		});

		defineProperty(this, name, d(constructor));
		return constructor;
	}),
	__create: d('c', function (properties, isSchema, attr) {
		var proto, prop, errors, error, done, iterate;
		if (properties) {
			forEach(properties, function (value, name) {
				try {
					this.set(name, value, attr);
				} catch (e) {
					if (!errors) (errors = {});
					errors[name] = e;
				}
			}, this);
		}
		if (!isSchema) {
			proto = getPrototypeOf(this);
			done = {};
			iterate = function (name) {
				if (done[name]) return;
				done[name] = true;
				if (!startsWith.call(name, '_$') || this.hasOwnProperty(name) ||
					 (errors && errors[name.slice(2)])) {
					return;
				}
				prop = proto[name];
				if (!isProperty(prop)) return;
				try {
					prop.validateUndefinedExt();
				} catch (e) {
					if (!errors) (errors = {});
					errors[name.slice(2)] = e;
				}
			};
			while (proto !== Object.prototype) {
				getOwnPropertyNames(proto).forEach(iterate, this);
				proto = getPrototypeOf(proto);
			}
		}
		if (errors) {
			error = new TypeError("Could not define properties");
			error.subErrors = errors;
			throw error;
		}
	}),
	set: d('c', function (name, value, attr) {
		if (!nameRe.test(name)) {
			throw new Error("'" + name + "' is not valid property name");
		}

		if ((name in this) && (('_$' + name) in this)) { //jslint: skip
			// Defined value, set directly
			return (this[name] = value);
		}

		return define(this, name, value, attr);
	}),
	rel: d('c', function (data) {
		var value;
		validValue(data);
		if (data.default) {
			data = copy(data);
			value = data.default;
			delete data.default;
		}
		return new PropertyExt(value, this, data);
	}),
	required: d.gs('c', function () {
		return new PropertyExt(null, this, requiredOpts);
	}),
	transaction: d('c', function (fn) {
		if (base.lock) return fn();
		base.lock = now();
		try {
			return fn();
		} finally {
			base.lock = null;
		}
	}),
	serialize: d('c', function (value) {
		var type;
		if (value === null) return null;
		type = typeof value;
		if (type === 'object') {
			return isDate(value) ? value.toISOString() : String(value);
		} else if (type === 'function') {
			return String(value);
		} else {
			return value;
		}
	})
});

isNamespace = base.isNamespace;

// function type (we need it before it can be created)
functionType = defineProperties(function self(value) {
	return self.validate(value);
}, {
	__id:      d('function'),
	__created: d(0)
});
functionType.__proto__ = base;
define(functionType, 'validate',
	new PropertyExt(validFunction, null, requiredOpts));
define(functionType, 'normalize', new PropertyExt(function (value) {
	var match;
	if (isFunction(value)) {
		return value;
	} else if (isString(value)) {
		match = value.match(functionRe);
		if (match) {
			try {
				return new Function(match[1], match[2]);
			} catch (e) {}
		}
	}
	return null;
}, null, requiredOpts));
define(base, 'validate', new PropertyExt(null, functionType, requiredOpts));
define(base, 'normalize', new PropertyExt(null, functionType, requiredOpts));
defineProperty(base, 'function', d(functionType));

boolean      = require('./boolean');
define(base, 'async', new PropertyExt(null, boolean, requiredOpts));
base.async = false;

number       = require('./number');
regExp       = require('./reg-exp');
string       = require('./string');
dateTime     = require('./date-time');
<commit_msg>Improve handling property attributes by set/define<commit_after>'use strict';

var isDate        = require('es5-ext/lib/Date/is-date')
  , isFunction    = require('es5-ext/lib/Function/is-function')
  , validFunction = require('es5-ext/lib/Function/valid-function')
  , copy          = require('es5-ext/lib/Object/copy')
  , d             = require('es5-ext/lib/Object/descriptor')
  , extend        = require('es5-ext/lib/Object/extend')
  , forEach       = require('es5-ext/lib/Object/for-each')
  , validValue    = require('es5-ext/lib/Object/valid-value')
  , isString      = require('es5-ext/lib/String/is-string')
  , startsWith    = require('es5-ext/lib/String/prototype/starts-with')
  , now           = require('time-uuid/lib/time')
  , Property      = require('./_property')

  , defineProperty = Object.defineProperty
  , defineProperties = Object.defineProperties
  , getOwnPropertyNames = Object.getOwnPropertyNames
  , getPrototypeOf = Object.getPrototypeOf
  , isProperty = Property.isProperty
  , functionRe = new RegExp('^\\s*function[\\0-\'\\)-\\uffff]*' +
		'\\(([\\0-\\(\\*-\\uffff]*)\\)\\s*\\{([\\0-\\uffff]*)\\}\\s*$')
  , nameRe = /^[a-zA-Z][0-9a-zA-Z]*$/

  , define, PropertyExt, requiredOpts, isNamespace, base, boolean, number
  , string, dateTime, regExp, functionType;

PropertyExt = function (value, ns, ext) {
	this.value = value;
	this.ns = ns;
	this.ext = ext;
};
PropertyExt.prototype.__propertyExtension = true;

requiredOpts = { required: true };

define = function (obj, name, value, attr) {
	var ns, get, set, prop, ext, propName;
	if (value && value.__propertyExtension) {
		ns = value.ns;
		ext = value.ext;
		value = value.value;
	} else if (isNamespace(value)) {
		ns = value;
		value = null;
	}
	prop = new Property(obj, value, ns, name);
	if (ext) extend(prop, ext);
	propName = '_$' + name;

	defineProperty(obj, propName, d('', prop));
	defineProperty(obj, '_' + name, d.gs('', function () {
		return this.hasOwnProperty(propName) ? this[propName] : undefined;
	}));
	defineProperty(obj, name, d.gs(attr, get = function () {
		var prop = this[propName], value = prop.value, ns = prop.ns;
		if ((value == null) || !ns || (ns._$async.value && !prop.resolved)) {
			return value;
		}
		return ns._$normalize.value(value);
	}, set = function (value) {
		var ns, ext;
		if (value && value.__propertyExtension) {
			ns = value.ns;
			ext = value.ext;
			value = value.value;
		} else if (isNamespace(value)) {
			ns = value;
			value = null;
		}
		if (this.hasOwnProperty(propName)) {
			this[propName].set(value, ns);
		} else {
			defineProperty(this, propName,
				d('', this[propName].create(this, value, ns)));
			defineProperty(this, name, d.gs(attr, get, set));
		}
		if (ext) extend(this[propName], ext);
		return this[propName].value;
	}));
	return prop.value;
};

module.exports = base = defineProperties(function self(value) {
	return self.validate(value);
}, {
	__id: d('base'),
	lock: d(null),
	isNamespace: d(function (value) {
		return value && value.__id && !value.__ns;
	}),
	create: d('c', function (name, constructor, properties) {
		name = String(name);
		if (!nameRe.test(name)) {
			throw new Error(name + "is not valid namespace name");
		}

		if ((properties == null) && !isFunction(constructor)) {
			properties = constructor;
			constructor = eval('(' + String(this) + ')');
		}
		constructor.__proto__ = this;

		base.transaction(function () {
			constructor.__create(properties, false, '');
			defineProperties(constructor, {
				__id:      d(name),
				__created: d(base.lock)
			});
		});

		defineProperty(this, name, d(constructor));
		return constructor;
	}),
	__create: d('c', function (properties, isSchema, attr) {
		var proto, prop, errors, error, done, iterate;
		if (properties) {
			forEach(properties, function (value, name) {
				try {
					this.set(name, value, attr);
				} catch (e) {
					if (!errors) (errors = {});
					errors[name] = e;
				}
			}, this);
		}
		if (!isSchema) {
			proto = getPrototypeOf(this);
			done = {};
			iterate = function (name) {
				if (done[name]) return;
				done[name] = true;
				if (!startsWith.call(name, '_$') || this.hasOwnProperty(name) ||
					 (errors && errors[name.slice(2)])) {
					return;
				}
				prop = proto[name];
				if (!isProperty(prop)) return;
				try {
					prop.validateUndefinedExt();
				} catch (e) {
					if (!errors) (errors = {});
					errors[name.slice(2)] = e;
				}
			};
			while (proto !== Object.prototype) {
				getOwnPropertyNames(proto).forEach(iterate, this);
				proto = getPrototypeOf(proto);
			}
		}
		if (errors) {
			error = new TypeError("Could not define properties");
			error.subErrors = errors;
			throw error;
		}
	}),
	set: d('c', function (name, value, attr) {
		if (!nameRe.test(name)) {
			throw new Error("'" + name + "' is not valid property name");
		}

		if ((name in this) && (('_$' + name) in this)) { //jslint: skip
			// Defined value, set directly
			return (this[name] = value);
		}

		return define(this, name, value, (attr == null) ? 'ce' : attr);
	}),
	rel: d('c', function (data) {
		var value;
		validValue(data);
		if (data.default) {
			data = copy(data);
			value = data.default;
			delete data.default;
		}
		return new PropertyExt(value, this, data);
	}),
	required: d.gs('c', function () {
		return new PropertyExt(null, this, requiredOpts);
	}),
	transaction: d('c', function (fn) {
		if (base.lock) return fn();
		base.lock = now();
		try {
			return fn();
		} finally {
			base.lock = null;
		}
	}),
	serialize: d('c', function (value) {
		var type;
		if (value === null) return null;
		type = typeof value;
		if (type === 'object') {
			return isDate(value) ? value.toISOString() : String(value);
		} else if (type === 'function') {
			return String(value);
		} else {
			return value;
		}
	})
});

isNamespace = base.isNamespace;

// function type (we need it before it can be created)
functionType = defineProperties(function self(value) {
	return self.validate(value);
}, {
	__id:      d('function'),
	__created: d(0)
});
functionType.__proto__ = base;
define(functionType, 'validate',
	new PropertyExt(validFunction, null, requiredOpts), '');
define(functionType, 'normalize', new PropertyExt(function (value) {
	var match;
	if (isFunction(value)) {
		return value;
	} else if (isString(value)) {
		match = value.match(functionRe);
		if (match) {
			try {
				return new Function(match[1], match[2]);
			} catch (e) {}
		}
	}
	return null;
}, null, requiredOpts), '');
define(base, 'validate', new PropertyExt(null, functionType, requiredOpts), '');
define(base, 'normalize', new PropertyExt(null, functionType, requiredOpts),
	'');
defineProperty(base, 'function', d(functionType));

boolean      = require('./boolean');
define(base, 'async', new PropertyExt(null, boolean, requiredOpts), '');
base.async = false;

number       = require('./number');
regExp       = require('./reg-exp');
string       = require('./string');
dateTime     = require('./date-time');
<|endoftext|>


<commit_before>/**
 * base.js
 * @author kazuya kawaguchi <kawakazu80@gmail.com>
 */

//
// import(s)
//

var EventEmitter = require('events').EventEmitter;
var util = require('util');
var net = require('net');
var common = require('./common');

//
// constant(s)
//
var REGEX_HOSTNAME = /^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/;
var REGEX_IPADDRESS = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;

//
// clsss(es)
//

function Base () {
  EventEmitter.call(this);
  this.port = null;
  this.host = null;
  this.socket = null;
  this.cmds = {};

  var self = this;
  this.cmds[common.CMD_JOIN] = function (res, callback) { // JOIN
    if (!res.stat || res.stat === 'NG') {
      self.socket.destroy();
      self.emit('error', new Error()); // TODO: error message
    } else {
      callback ? callback() : self.emit('connect');
    }
  };
}

util.inherits(Base, EventEmitter);

/**
 * Connect to hellowork server.
 * @method connect
 * @param {Object} [options] an option parameters.
 * @param {Number} [options.port] a port number.
 * @param {String} [options.hsot] a hostname.
 * @param {Function} [callback] a callback function.
 */
Base.prototype.connect = function (/*options, callback*/) {

  var self = this;

  var options;
  var callback;
  if (typeof arguments[0] === 'object') {
    options = arguments[0];
    callback = arguments[1];
  } else {
    callback = arguments[0];
  }

  if (this.socket) {
    var err = new Error('AlreadyConnect');
    callback ? callback(err) : this.emit('connect', err);
    return;
  }

  var port = 20000;
  if (options && (options.port !== undefined)) {
    port = options.port;
  }
  if (port <= 1024 || port >= 65536) {
    var err = new Error('InvalidParameter');
    callback ? callback(err) : this.emit('connect', err);
    return;
  }

  var host = 'localhost';
  if (options && (options.host !== undefined)) {
    host = options.host;
  }
  if ((!REGEX_HOSTNAME.test(host) & !REGEX_IPADDRESS.test(host))) {
    var err = new Error('InvalidParameter');
    callback ? callback(err) : this.emit('connect', err);
    return;
  }

  var socket = new net.Socket({
    allowHalfOpen: true
  });
  socket.on('error', function (err) {
    if (self.socket) { // connected
      self.emit('error', err);
    } else { // close
      callback ? callback(err) : self.emit('connect', err);
    }
  });

  socket.connect(port, host, function () {

    self.socket = socket;
    self.host = host;
    self.port = port;

    socket.on('data', function (data) {
      var res = JSON.parse(data);
      var cmd = self.cmds[res.cmd];
      if (cmd) {
        cmd(res, callback);
      }
    });

    // `connect` negociation
    var request = {
      cmd: common.CMD_JOIN,
      type: this.type
    };
    this.write(JSON.stringify(request));

  }); // end of connect

};

Base.prototype.disconnect = function (callback) {

  var self = this;

  if (!this.socket) {
    var err = new Error('NotConnect');
    callback ? callback(err) : this.emit('disconnect', err);
    return;
  }

  this.socket.on('end', function () {
    self.host = null;
    self.port = null;
    self.socket.destroy();
    self.socket = null;
    callback ? callback() : self.emit('disconnect');
  });

  this.socket.on('close', function (has_err) {
    console.log('con close event : ' + has_err);
  });

  var request = {
    cmd: common.CMD_LEAVE,
    type: this.type
  };
  this.socket.end(JSON.stringify(request));

};

//
// export(s)
//

exports.Base = Base;

<commit_msg>Change 'data' event recieving method.<commit_after>/**
 * base.js
 * @author kazuya kawaguchi <kawakazu80@gmail.com>
 */

//
// import(s)
//

var EventEmitter = require('events').EventEmitter;
var util = require('util');
var net = require('net');
var common = require('./common');

//
// constant(s)
//
var REGEX_HOSTNAME = /^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/;
var REGEX_IPADDRESS = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;

//
// clsss(es)
//

function Base () {
  EventEmitter.call(this);
  this.port = null;
  this.host = null;
  this.socket = null;
  this.cmds = {};

  var self = this;
  this.cmds[common.CMD_JOIN] = function (res, callback) { // JOIN
    if (!res.stat || res.stat === 'NG') {
      self.socket.destroy();
      self.emit('error', new Error()); // TODO: error message
    } else {
      callback ? callback() : self.emit('connect');
    }
  };
}

util.inherits(Base, EventEmitter);

/**
 * Connect to hellowork server.
 * @method connect
 * @param {Object} [options] an option parameters.
 * @param {Number} [options.port] a port number.
 * @param {String} [options.hsot] a hostname.
 * @param {Function} [callback] a callback function.
 */
Base.prototype.connect = function (/*options, callback*/) {

  var self = this;

  var options;
  var callback;
  if (typeof arguments[0] === 'object') {
    options = arguments[0];
    callback = arguments[1];
  } else {
    callback = arguments[0];
  }

  if (this.socket) {
    var err = new Error('AlreadyConnect');
    callback ? callback(err) : this.emit('connect', err);
    return;
  }

  var port = 20000;
  if (options && (options.port !== undefined)) {
    port = options.port;
  }
  if (port <= 1024 || port >= 65536) {
    var err = new Error('InvalidParameter');
    callback ? callback(err) : this.emit('connect', err);
    return;
  }

  var host = 'localhost';
  if (options && (options.host !== undefined)) {
    host = options.host;
  }
  if ((!REGEX_HOSTNAME.test(host) & !REGEX_IPADDRESS.test(host))) {
    var err = new Error('InvalidParameter');
    callback ? callback(err) : this.emit('connect', err);
    return;
  }

  var socket = new net.Socket({
    allowHalfOpen: true
  });
  socket.on('error', function (err) {
    if (self.socket) { // connected
      self.emit('error', err);
    } else { // close
      callback ? callback(err) : self.emit('connect', err);
    }
  });

  socket.connect(port, host, function () {

    self.socket = socket;
    self.host = host;
    self.port = port;

    socket.once('data', function (data) {
      var res = JSON.parse(data);
      var cmd = self.cmds[res.cmd];
      if (cmd) {
        cmd(res, callback);
      }
    });

    // `connect` negociation
    var request = {
      cmd: common.CMD_JOIN,
      type: this.type
    };
    this.write(JSON.stringify(request));

  }); // end of connect

};

Base.prototype.disconnect = function (callback) {

  var self = this;

  if (!this.socket) {
    var err = new Error('NotConnect');
    callback ? callback(err) : this.emit('disconnect', err);
    return;
  }

  this.socket.on('end', function () {
    self.host = null;
    self.port = null;
    self.socket.destroy();
    self.socket = null;
    callback ? callback() : self.emit('disconnect');
  });

  this.socket.on('close', function (has_err) {
    console.log('con close event : ' + has_err);
  });

  var request = {
    cmd: common.CMD_LEAVE,
    type: this.type
  };
  this.socket.end(JSON.stringify(request));

};

//
// export(s)
//

exports.Base = Base;

<|endoftext|>


<commit_before>(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports      = SmallTalk();
    else
        global.smalltalk    = SmallTalk();

    function SmallTalk(callback) {
        if (!(this instanceof SmallTalk))
            return new SmallTalk(callback);

        let remove              = bind(removeEl, '.smalltalk');

        const BUTTON_OK         = ['OK'];
        const BUTTON_OK_CANCEL  = ['OK', 'Cancel'];

        this.alert = (title, msg, options) => {
            return showDialog(title, msg, '', BUTTON_OK, options);
        };

        this.prompt = (title, msg, value, options) => {
            let val         = value || '';
            let valueStr    = `<input type="text" value="${ val }" data-name="js-input">`;

            return showDialog(title, msg, valueStr, BUTTON_OK_CANCEL, options);
        };

        this.confirm = (title, msg, options) => {
            return showDialog(title, msg, '', BUTTON_OK_CANCEL, options);
        };

        function getTemplate(title, msg, value, buttons) {
            if (!Array.isArray(buttons))
                throw Error('buttons should be array!');

            return `

                ${ title }

                    ${ msg }
                    ${ value }

 ${
                        buttons.map((name, i) =>
                            `<button tabindex=${ i } data-name="js-${ name.toLowerCase() }">${ name }</button>`
                        ).join('')
                    }

`;
        }

        function showDialog(title, msg, value, buttons, options) {
            let dialog  = document.createElement('div'),

                closeButtons    = [
                    'cancel',
                    'close',
                    'ok'
                ],

                ok, cancel,

                promise = new Promise((resolve, reject) => {
                    let noCancel    = options && !options.cancel;
                    let empty       = () => {};

                    ok      = resolve;
                    cancel  = reject;

                    if (noCancel)
                        cancel = empty;
                }),

                tmpl    = getTemplate(title, msg, value, buttons);

            dialog.innerHTML = tmpl;
            dialog.className = 'smalltalk';

            document.body.appendChild(dialog);

            find(dialog, ['ok']).forEach(el =>
                el.focus()
            );

            find(dialog, ['input']).forEach(el =>
                el.setSelectionRange(0, value.length)
            );

            addListeterAll('click', dialog, closeButtons, event =>
                closeDialog(event.target, dialog, ok, cancel)
            );

            ['click', 'contextmenu'].forEach(event =>
                dialog.addEventListener(event, () =>
                    find(dialog, ['ok', 'input']).forEach(el =>
                        el.focus()
                    )
                )
            );

            dialog.addEventListener('keydown', keyDown(dialog, ok, cancel));

            return promise;
        }

        function keyDown(dialog, ok, cancel) {
            return event => {
                const KEY   = {
                    ENTER : 13,
                    ESC   : 27,
                    TAB   : 9,
                    LEFT  : 37,
                    UP    : 38,
                    RIGHT : 39,
                    DOWN  : 40
                };

                let keyCode     = event.keyCode,
                    el          = event.target;

                let namesAll    = ['ok', 'cancel', 'input'],
                    names       = find(dialog, namesAll).map(el =>
                        getDataName(el)
                    );

                switch(keyCode) {
                case KEY.ENTER:
                    closeDialog(el, dialog, ok, cancel);
                    event.preventDefault();
                    break;

                case KEY.ESC:
                    remove();
                    cancel();
                    break;

                case KEY.TAB:
                    if (event.shiftKey)
                        tab(dialog, names);

                    tab(dialog, names);
                    event.preventDefault();
                    break;

                default:
                    let is = ['left', 'right', 'up', 'down'].some(name =>
                        keyCode === KEY[name.toUpperCase()]
                    );

                    if (is)
                        changeButtonFocus(dialog, names);

                    break;
                }

                event.stopPropagation();
            };
        }

        function getDataName(el) {
            return el
                .getAttribute('data-name')
                .replace('js-', '');
        }

        function changeButtonFocus(dialog, names) {
            let name        = '',
                active      = document.activeElement,
                activeName  = getDataName(active),
                isButton    = /ok|cancel/.test(activeName),
                count       = names.length - 1;

            if (activeName !== 'input' && count && isButton) {
                if (activeName === 'cancel')
                    name = 'ok';
                else
                    name = 'cancel';

                find(dialog, [name]).forEach(el =>
                    el.focus()
                );
            }
        }

        function tab(dialog, names) {
            let active      = document.activeElement,
                activeName  = getDataName(active),

                count       = names.length - 1,
                index       = names.indexOf(activeName);

            if (index === count)
                index = 0;
            else if (index < count)
                ++index;

            let name = names[index];

            find(dialog, [name]).forEach(el =>
                el.focus()
            );
        }

        function closeDialog(el, dialog, ok, cancel) {
            let value,
                name = el
                    .getAttribute('data-name')
                    .replace('js-', '');

            if (/close|cancel/.test(name)) {
                cancel();
            } else {
                value = find(dialog, ['input']).reduce((value, el) => {
                    return el.value;
                }, null);

                ok(value);
            }

            remove();
        }

        function find(element, names) {
            let elements = names.map(name =>
                element.querySelector(`[data-name="js-${ name }"]`)
            ).filter(el =>
                el
            );

            return elements;
        }

        function addListeterAll(event, parent, elements, fn) {
            find(parent, elements).forEach(el =>
                el.addEventListener(event, event =>
                    fn(event)
                )
            );
        }

        function removeEl(name) {
            var el = document.querySelector(name);

            el.parentElement.removeChild(el);
        }

        function bind(fn) {
            var args = [].slice.call(arguments, 1);

            return fn.bind(null, args);
        }
    }

})(typeof window !== 'undefined' && window);
<commit_msg>fix(smalltalk) prompt: focus on ok -> focus on input<commit_after>(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports      = SmallTalk();
    else
        global.smalltalk    = SmallTalk();

    function SmallTalk(callback) {
        if (!(this instanceof SmallTalk))
            return new SmallTalk(callback);

        let remove              = bind(removeEl, '.smalltalk');

        const BUTTON_OK         = ['OK'];
        const BUTTON_OK_CANCEL  = ['OK', 'Cancel'];

        this.alert = (title, msg, options) => {
            return showDialog(title, msg, '', BUTTON_OK, options);
        };

        this.prompt = (title, msg, value, options) => {
            let val         = value || '';
            let valueStr    = `<input type="text" value="${ val }" data-name="js-input">`;

            return showDialog(title, msg, valueStr, BUTTON_OK_CANCEL, options);
        };

        this.confirm = (title, msg, options) => {
            return showDialog(title, msg, '', BUTTON_OK_CANCEL, options);
        };

        function getTemplate(title, msg, value, buttons) {
            if (!Array.isArray(buttons))
                throw Error('buttons should be array!');

            return `

                ${ title }

                    ${ msg }
                    ${ value }

 ${
                        buttons.map((name, i) =>
                            `<button tabindex=${ i } data-name="js-${ name.toLowerCase() }">${ name }</button>`
                        ).join('')
                    }

`;
        }

        function showDialog(title, msg, value, buttons, options) {
            let dialog  = document.createElement('div'),

                closeButtons    = [
                    'cancel',
                    'close',
                    'ok'
                ],

                ok, cancel,

                promise = new Promise((resolve, reject) => {
                    let noCancel    = options && !options.cancel;
                    let empty       = () => {};

                    ok      = resolve;
                    cancel  = reject;

                    if (noCancel)
                        cancel = empty;
                }),

                tmpl    = getTemplate(title, msg, value, buttons);

            dialog.innerHTML = tmpl;
            dialog.className = 'smalltalk';

            document.body.appendChild(dialog);

            find(dialog, ['ok', 'input']).forEach(el =>
                el.focus()
            );

            find(dialog, ['input']).forEach(el => {
                el.setSelectionRange(0, value.length);
            });

            addListeterAll('click', dialog, closeButtons, event =>
                closeDialog(event.target, dialog, ok, cancel)
            );

            ['click', 'contextmenu'].forEach(event =>
                dialog.addEventListener(event, () =>
                    find(dialog, ['ok', 'input']).forEach(el =>
                        el.focus()
                    )
                )
            );

            dialog.addEventListener('keydown', keyDown(dialog, ok, cancel));

            return promise;
        }

        function keyDown(dialog, ok, cancel) {
            return event => {
                const KEY   = {
                    ENTER : 13,
                    ESC   : 27,
                    TAB   : 9,
                    LEFT  : 37,
                    UP    : 38,
                    RIGHT : 39,
                    DOWN  : 40
                };

                let keyCode     = event.keyCode,
                    el          = event.target;

                let namesAll    = ['ok', 'cancel', 'input'],
                    names       = find(dialog, namesAll).map(el =>
                        getDataName(el)
                    );

                switch(keyCode) {
                case KEY.ENTER:
                    closeDialog(el, dialog, ok, cancel);
                    event.preventDefault();
                    break;

                case KEY.ESC:
                    remove();
                    cancel();
                    break;

                case KEY.TAB:
                    if (event.shiftKey)
                        tab(dialog, names);

                    tab(dialog, names);
                    event.preventDefault();
                    break;

                default:
                    let is = ['left', 'right', 'up', 'down'].some(name =>
                        keyCode === KEY[name.toUpperCase()]
                    );

                    if (is)
                        changeButtonFocus(dialog, names);

                    break;
                }

                event.stopPropagation();
            };
        }

        function getDataName(el) {
            return el
                .getAttribute('data-name')
                .replace('js-', '');
        }

        function changeButtonFocus(dialog, names) {
            let name        = '',
                active      = document.activeElement,
                activeName  = getDataName(active),
                isButton    = /ok|cancel/.test(activeName),
                count       = names.length - 1;

            if (activeName !== 'input' && count && isButton) {
                if (activeName === 'cancel')
                    name = 'ok';
                else
                    name = 'cancel';

                find(dialog, [name]).forEach(el =>
                    el.focus()
                );
            }
        }

        function tab(dialog, names) {
            let active      = document.activeElement,
                activeName  = getDataName(active),

                count       = names.length - 1,
                index       = names.indexOf(activeName);

            if (index === count)
                index = 0;
            else if (index < count)
                ++index;

            let name = names[index];

            find(dialog, [name]).forEach(el =>
                el.focus()
            );
        }

        function closeDialog(el, dialog, ok, cancel) {
            let value,
                name = el
                    .getAttribute('data-name')
                    .replace('js-', '');

            if (/close|cancel/.test(name)) {
                cancel();
            } else {
                value = find(dialog, ['input']).reduce((value, el) => {
                    return el.value;
                }, null);

                ok(value);
            }

            remove();
        }

        function find(element, names) {
            let elements = names.map(name =>
                element.querySelector(`[data-name="js-${ name }"]`)
            ).filter(el =>
                el
            );

            return elements;
        }

        function addListeterAll(event, parent, elements, fn) {
            find(parent, elements).forEach(el =>
                el.addEventListener(event, event =>
                    fn(event)
                )
            );
        }

        function removeEl(name) {
            var el = document.querySelector(name);

            el.parentElement.removeChild(el);
        }

        function bind(fn) {
            var args = [].slice.call(arguments, 1);

            return fn.bind(null, args);
        }
    }

})(typeof window !== 'undefined' && window);
<|endoftext|>


<commit_before>var Lang = A.Lang;

var DEFAULT_ARGS = [];

A.debounce = function(fn, delay, context, args) {
	var id;

	delay = delay || 0;

	args = !Lang.isUndefined(args) ? A.Array(args) : DEFAULT_ARGS;

	var clearFn = function() {
		clearInterval(id);

		id = null;
	};

	var base = function() {
		clearFn();

		return fn.apply(context, args || DEFAULT_ARGS);
	};

	var delayFn = function(delayTime, newArgs, newContext, newFn) {
		wrapped.cancel();

		delayTime = !Lang.isUndefined(delayTime) ? delayTime : delay;

		fn = newFn || fn;
		context = newContext || context;

		args = !Lang.isUndefined(newArgs) ? A.Array(newArgs) : args;

		if (delayTime > 0) {
			id = setInterval(base, delayTime);
		}
		else {
			return base();
		}
	};

	var cancelFn = function() {
		if (id) {
			clearFn();
		}
	};

	var wrapped = function() {
		var currentArgs = arguments.length ? arguments : args;

		return wrapped.delay(delay, currentArgs, context || this);
	};

	wrapped.cancel = cancelFn;
	wrapped.delay = delayFn;

	return wrapped;
};<commit_msg>AUI-362 - Changing so args passed into constructor match the rbind syntax<commit_after>var Lang = A.Lang;

var AArray = A.Array;
var isArray = Lang.isArray;
var isUndefined = Lang.isUndefined;

var DEFAULT_ARGS = [];

var toArray = function(arr, fallback, index, arrayLike) {
	return !isUndefined(arr) ? AArray(arr, index || 0, (arrayLike !== false)) : fallback;
};

A.debounce = function(fn, delay, context, args) {
	var id;
	var tempArgs;

	delay = delay || 0;

	args = toArray(arguments, DEFAULT_ARGS, 3);

	var clearFn = function() {
		clearInterval(id);

		id = null;
	};

	var base = function() {
		clearFn();

		var result = fn.apply(context, tempArgs || args || DEFAULT_ARGS);

		tempArgs = null;

		return result;
	};

	var delayFn = function(delayTime, newArgs, newContext, newFn) {
		wrapped.cancel();

		delayTime = !isUndefined(delayTime) ? delayTime : delay;

		fn = newFn || fn;
		context = newContext || context;

		if (newArgs != args) {
			tempArgs = toArray(newArgs, DEFAULT_ARGS, 0, false).concat(args);
		}

		if (delayTime > 0) {
			id = setInterval(base, delayTime);
		}
		else {
			return base();
		}
	};

	var cancelFn = function() {
		if (id) {
			clearFn();
		}
	};

	var wrapped = function() {
		var currentArgs = arguments.length ? arguments : args;

		return wrapped.delay(delay, currentArgs, context || this);
	};

	wrapped.cancel = cancelFn;
	wrapped.delay = delayFn;

	return wrapped;
};<|endoftext|>


<commit_before>var mongoose;

mongoose  = require('mongoose');

var Schema = mongoose.Schema;

var entrySchema = new Schema({
  title:  String,
  author: String,
  url:    String,
  body:   String,
  tags: [{name: String}],
  category: String,
  comments: [{ body: String, date: Date }],
  creationDate: { type: Date, default: Date.now },
  updateDate: { type: Date, default: Date.now },
  published: Boolean,
}, {collection: 'entries'});

var Entry = mongoose.model('Entry', entrySchema);

var categorySchema = new Schema({
  name: String
}, {collection: 'categories'});

var Category = mongoose.model('Category', categorySchema);

module.exports = {

  /*
  `getEntries(limit, skip, callback)`
  ----------------------------
  */

  getEntries: function(limit, skip, callback) {
    Entry.find({}, null, { skip: skip, limit: limit }).sort({creationDate: 'desc'}).exec(function(err, result) {
      if(!err) {
        err = null;
      }
      callback(err, result);
    });
  },

  /*
  `getEntry(param, callback)`
  ----------------------------
  */

  getEntry: function(param, callback) {
    Entry.find(param, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    })
  },

  /*
  `createEntry(entry, callback)`
  ----------------------------
  */
  createEntry: function(entry, callback) {
    var newEntry = new Entry(entry);
    newEntry.save(function(err, newEntry, numberAffected) {
      if(!err) {
        err = null;
      }
      callback(err, newEntry);
    })
  },

  /*
  `removeEntry(id, callback)`
  ----------------------------

  */
  removeEntry: function(id, callback) {
    Entry.remove({'_id' : id}, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    }).remove();
  },

  /*
  `editEntry(modifiedEntry, callback)`
  ----------------------------

  */
  editEntry: function(id, modifiedEntry, callback) {
    if(modifiedEntry.hasOwnProperty('_id')) {
      delete modifiedEntry._id;
    }
    Entry.findOneAndUpdate({_id: id}, modifiedEntry, {new: true}, function(err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    });
  },

  /*
  `getCategories(callback)`
  ----------------------------
  */

  getCategories: function(callback) {
    Category.find({}, function(err, result) {
      if(!err) {
        err = null;
      }
      callback(err, result);
    });
  },

  /*
  `getCategory(id, callback)`
  TODO change id by {id: id, name: name} as options
  ----------------------------
  */

  getCategory: function(id, callback) {
    Category.find({'_id': id}, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    })
  },

  /*
  `createCategory(category, callback)`
  ----------------------------

  */
  createCategory: function(category, callback) {
    var newCategory = new Category(category);
    newCategory.save(function(err, newCategory, numberAffected) {
      if(!err) {
        err = null;
      }
      callback(err, newCategory);
    })
  },

  /*
  `removecategory(id, callback)`
  ----------------------------

  */
  removeCategory: function(id, callback) {
    Category.remove({'_id' : id}, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    }).remove();
  },

  /*
  `editCategory(id, newCategory, callback)`
  ----------------------------

  */
  editCategory: function(id, modifiedCategory, callback) {
    if(modifiedCategory.hasOwnProperty('_id')) {
      delete modifiedCategory._id;
    }
    Category.findOneAndUpdate({_id: id}, modifiedCategory, {new: true}, function(err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    });
  }
};
<commit_msg>add 2 functions count entries & count pages<commit_after>var mongoose;

mongoose  = require('mongoose');

var Schema = mongoose.Schema;

var entrySchema = new Schema({
  title:  String,
  author: String,
  url:    String,
  body:   String,
  tags: [{name: String}],
  category: String,
  comments: [{ body: String, date: Date }],
  creationDate: { type: Date, default: Date.now },
  updateDate: { type: Date, default: Date.now },
  published: Boolean,
}, {collection: 'entries'});

var Entry = mongoose.model('Entry', entrySchema);

var categorySchema = new Schema({
  name: String
}, {collection: 'categories'});

var Category = mongoose.model('Category', categorySchema);

module.exports = {

  /*
  `getEntries(limit, skip, callback)`
  ----------------------------
  */

  getEntries: function(limit, skip, callback) {
    Entry.find({}, null, { skip: skip, limit: limit }).sort({creationDate: 'desc'}).exec(function(err, result) {
      if(!err) {
        err = null;
      }
      callback(err, result);
    });
  },

    /*
  `countEntries(callback)`
  ----------------------------
  */

  countEntries: function(callback) {
    Entry.count({}, function(err, count){
      callback(err, count);
    });
  }

  /*
  `countEntries(callback)`
  ----------------------------
  */

  countPages: function(limit, callback) {
    Entry.count({}, function(err, count){
      callback(err, Math.ceil(count/limit));
    });
  }

  /*
  `getEntry(param, callback)`
  ----------------------------
  */

  getEntry: function(param, callback) {
    Entry.find(param, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    })
  },

  /*
  `createEntry(entry, callback)`
  ----------------------------
  */
  createEntry: function(entry, callback) {
    var newEntry = new Entry(entry);
    newEntry.save(function(err, newEntry, numberAffected) {
      if(!err) {
        err = null;
      }
      callback(err, newEntry);
    })
  },

  /*
  `removeEntry(id, callback)`
  ----------------------------

  */
  removeEntry: function(id, callback) {
    Entry.remove({'_id' : id}, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    }).remove();
  },

  /*
  `editEntry(modifiedEntry, callback)`
  ----------------------------

  */
  editEntry: function(id, modifiedEntry, callback) {
    if(modifiedEntry.hasOwnProperty('_id')) {
      delete modifiedEntry._id;
    }
    Entry.findOneAndUpdate({_id: id}, modifiedEntry, {new: true}, function(err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    });
  },

  /*
  `getCategories(callback)`
  ----------------------------
  */

  getCategories: function(callback) {
    Category.find({}, function(err, result) {
      if(!err) {
        err = null;
      }
      callback(err, result);
    });
  },

  /*
  `getCategory(id, callback)`
  TODO change id by {id: id, name: name} as options
  ----------------------------
  */

  getCategory: function(id, callback) {
    Category.find({'_id': id}, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    })
  },

  /*
  `createCategory(category, callback)`
  ----------------------------

  */
  createCategory: function(category, callback) {
    var newCategory = new Category(category);
    newCategory.save(function(err, newCategory, numberAffected) {
      if(!err) {
        err = null;
      }
      callback(err, newCategory);
    })
  },

  /*
  `removecategory(id, callback)`
  ----------------------------

  */
  removeCategory: function(id, callback) {
    Category.remove({'_id' : id}, function (err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    }).remove();
  },

  /*
  `editCategory(id, newCategory, callback)`
  ----------------------------

  */
  editCategory: function(id, modifiedCategory, callback) {
    if(modifiedCategory.hasOwnProperty('_id')) {
      delete modifiedCategory._id;
    }
    Category.findOneAndUpdate({_id: id}, modifiedCategory, {new: true}, function(err, data) {
      if(!err) {
        err = null;
      }
      callback(err, data);
    });
  }
};
<|endoftext|>


<commit_before>'use strict';

const bodyParser = require('body-parser'),
    passport = require('passport'),
    authService = require('../controllers/auth/auth.service'),
    cors = require('cors');

module.exports = (app) => app
    .use(cors())
    .use(bodyParser.json())
    .use(authService.validateJwt)
    .use(passport.initialize())
    .use(authService.initRequest);
<commit_msg>adjust file size limit<commit_after>'use strict';

const bodyParser = require('body-parser'),
    passport = require('passport'),
    authService = require('../controllers/auth/auth.service'),
    cors = require('cors');

module.exports = (app) => app
    .use(cors())
    .use(bodyParser.json({ limit: '15mb' }))
    .use(authService.validateJwt)
    .use(passport.initialize())
    .use(authService.initRequest);
<|endoftext|>


<commit_before>import { PropTypes } from 'react';
import mapValues from 'lodash/mapValues';
import Styled from 'rsg-components/Styled';
import { styles as linkStyles } from 'rsg-components/Link';
import renderMarkdown from '../../utils/markdown-to-jsx';

// We’re explicitly specifying Webpack loaders here so we could skip specifying them in Webpack configuration.
// That way we could avoid clashes between our loaders and user loaders.
require('!!../../../loaders/style-loader!../../../loaders/css-loader!highlight.js/styles/tomorrow.css');

const styles = ({ font, monospace, link, linkHover, border, codeBackground }) => ({
	base: {
		fontFamily: font,
		fontSize: 'inherit',
	},
	para: {
		fontFamily: font,
		fontSize: 'inherit',
		margin: [[0, 0, 15, 0]],
	},
	a: linkStyles({ link, linkHover }).link,
	h3: {
		composes: '$para',
		fontSize: 26,
		fontWeight: 'normal',
	},
	h4: {
		composes: '$para',
		fontSize: 21,
		fontWeight: 'normal',
	},
	h5: {
		composes: '$para',
		fontSize: 16,
		fontWeight: 'normal',
	},
	h6: {
		composes: '$para',
		fontSize: 16,
		fontWeight: 'normal',
		fontStyle: 'italic',
	},
	p: {
		composes: '$para',
	},
	ul: {
		composes: '$para',
	},
	ol: {
		composes: '$para',
		listStyleType: 'decimal',
	},
	li: {
		composes: '$base',
		listStyleType: 'inherit',
	},
	blockquote: {
		composes: '$para',
		fontSize: 13,
		margin: [[0, 50]],
		padding: 0,
	},
	hr: {
		composes: '$para',
		borderWidth: [[0, 0, 1, 0]],
		borderColor: border,
		borderStyle: 'solid',
	},
	em: {
		composes: '$base',
		fontStyle: 'italic',
	},
	strong: {
		composes: '$base',
		fontWeight: 'bold',
	},
	code: {
		display: 'inline',
		fontFamily: monospace,
		fontSize: 'inherit',
		color: 'inherit',
		background: 'transparent',
	},
	pre: {
		backgroundColor: codeBackground,
		border: [[1, border, 'solid']],
		padding: [[12, 15]],
		borderRadius: 3,
		'& code': {
			fontFamily: monospace,
			fontSize: 12,
			color: 'inherit',
			background: 'transparent',
			whiteSpace: 'pre',
		},
	},
	table: {
		composes: '$para',
		borderCollapse: 'collapse',
	},
	thead: {
		composes: '$hr',
	},
	td: {
		paddingRight: 15,
		paddingTop: 6,
		fontSize: 13,
	},
	th: {
		paddingRight: 15,
		paddingBottom: 6,
		textAlign: 'left',
		fontSize: 13,
	},
	img: {},
	tr: {},
	tbody: {},
});

function Markdown({
	classes,
	text,
	inline,
}) {
	// Custom CSS classes for each tag:  → .
	const overrides = mapValues(classes, value => ({
		props: {
			className: value,
		},
	}));

	// Inline mode: replace
 (usual root component) with
	const overridesInline = {
		...overrides,
		p: {
			component: 'span',
			props: {
				className: classes.base,
			},
		},
	};

	const options = { overrides: inline ? overridesInline : overrides };
	return renderMarkdown(text, options);
}

Markdown.propTypes = {
	classes: PropTypes.object.isRequired,
	text: PropTypes.string.isRequired,
	inline: PropTypes.bool,
};

export default Styled(styles)(Markdown);
<commit_msg>Fix: Tweak text styles a bit<commit_after>import { PropTypes } from 'react';
import mapValues from 'lodash/mapValues';
import Styled from 'rsg-components/Styled';
import { styles as linkStyles } from 'rsg-components/Link';
import renderMarkdown from '../../utils/markdown-to-jsx';

// We’re explicitly specifying Webpack loaders here so we could skip specifying them in Webpack configuration.
// That way we could avoid clashes between our loaders and user loaders.
require('!!../../../loaders/style-loader!../../../loaders/css-loader!highlight.js/styles/tomorrow.css');

const styles = ({ font, monospace, link, linkHover, border, codeBackground }) => ({
	base: {
		fontFamily: font,
		fontSize: 'inherit',
	},
	para: {
		fontFamily: font,
		fontSize: 'inherit',
		margin: [[0, 0, 15, 0]],
		lineHeight: 1.5,
	},
	a: linkStyles({ link, linkHover }).link,
	h3: {
		composes: '$para',
		fontSize: 26,
		fontWeight: 'normal',
	},
	h4: {
		composes: '$para',
		fontSize: 21,
		fontWeight: 'normal',
	},
	h5: {
		composes: '$para',
		fontSize: 16,
		fontWeight: 'normal',
	},
	h6: {
		composes: '$para',
		fontSize: 16,
		fontWeight: 'normal',
		fontStyle: 'italic',
	},
	p: {
		composes: '$para',
	},
	ul: {
		composes: '$para',
	},
	ol: {
		composes: '$para',
		listStyleType: 'decimal',
	},
	li: {
		composes: '$base',
		listStyleType: 'inherit',
	},
	blockquote: {
		composes: '$para',
		fontSize: 14,
		margin: [[15, 30]],
		padding: 0,
	},
	hr: {
		composes: '$para',
		borderWidth: [[0, 0, 1, 0]],
		borderColor: border,
		borderStyle: 'solid',
	},
	em: {
		composes: '$base',
		fontStyle: 'italic',
	},
	strong: {
		composes: '$base',
		fontWeight: 'bold',
	},
	code: {
		display: 'inline',
		fontFamily: monospace,
		fontSize: 'inherit',
		color: 'inherit',
		background: 'transparent',
	},
	pre: {
		backgroundColor: codeBackground,
		border: [[1, border, 'solid']],
		padding: [[12, 15]],
		borderRadius: 3,
		'& code': {
			fontFamily: monospace,
			fontSize: 12,
			color: 'inherit',
			background: 'transparent',
			whiteSpace: 'pre',
		},
	},
	table: {
		composes: '$para',
		borderCollapse: 'collapse',
	},
	thead: {
		composes: '$hr',
	},
	td: {
		paddingRight: 15,
		paddingTop: 6,
		fontSize: 13,
	},
	th: {
		paddingRight: 15,
		paddingBottom: 6,
		textAlign: 'left',
		fontSize: 13,
	},
	img: {},
	tr: {},
	tbody: {},
});

function Markdown({
	classes,
	text,
	inline,
}) {
	// Custom CSS classes for each tag:  → .
	const overrides = mapValues(classes, value => ({
		props: {
			className: value,
		},
	}));

	// Inline mode: replace
 (usual root component) with
	const overridesInline = {
		...overrides,
		p: {
			component: 'span',
			props: {
				className: classes.base,
			},
		},
	};

	const options = { overrides: inline ? overridesInline : overrides };
	return renderMarkdown(text, options);
}

Markdown.propTypes = {
	classes: PropTypes.object.isRequired,
	text: PropTypes.string.isRequired,
	inline: PropTypes.bool,
};

export default Styled(styles)(Markdown);
<|endoftext|>


<commit_before>'use strict';

exports = module.exports = boil;

// ## boil(path [, options])
// > normalize a path
//
// arguments
//  - path, type `string`
//  - options, type `object` holding all extra information
//
// returns
//  - true for supported and non-empty types
//  - null for non supported types
//
// --
// api.private
// --
//
var urlRE = /\/?[?#][^!=:][^ ]+/g;
var boilRE = /(\/[^\/ ]+|\.[^\.\/ ]+)/g;

function boil(path, o){
  if(typeof path !== 'string'){ return null; }

  o = o || {};
  o.path = path.replace(/[ ]+/, ' ').trim();
  var url = o.path.match(/[^\/ ]*\/\S*/g);

  if(url){
    url = url[0];
    o.path = o.path.replace(url, url.replace(urlRE, '') || '/');
    if(o.notFound){ o.url = url; }
  }

  return o;
}

// separated for paths that were already boiled before
//
boil.argv = function(path){
  return path.replace(boilRE, ' $&').trim().split(/[ ]+/);
};
<commit_msg>bug in boil<commit_after>'use strict';

exports = module.exports = boil;

// ## boil(path [, options])
// > normalize a path
//
// arguments
//  - path, type `string`
//  - options, type `object` holding all extra information
//
// returns
//  - true for supported and non-empty types
//  - null for non supported types
//
// --
// api.private
// --
//
var boilRE = /([/.][(:\w])/g;
var urlRE = /\/?[?#][^!=:][^ ]+/g;

function boil(path, o){
  if(typeof path !== 'string'){ return null; }

  o = o || {};
  o.path = path.replace(/[ ]+/, ' ').trim();
  var url = o.path.match(/[^\/ ]*\/\S*/g);

  if(url){
    url = url[0];
    o.path = o.path.replace(url, url.replace(urlRE, '') || '/');
    if(o.notFound){ o.url = url; }
  }

  return o;
}

// separated for paths that were already boiled before
//
boil.argv = function(path){
  return path.replace(boilRE, ' $&').trim().split(/[ ]+/);
};
<|endoftext|>